From e445001d000123b3b717973a66d46c611eee369b Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Thu, 23 Jan 2025 21:42:12 +0000 Subject: [PATCH 1/4] Add ImageService.set_next_boot for GNOI Activate OS. --- host_modules/image_service.py | 23 ++++++++++ tests/host_modules/image_service_test.py | 56 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/host_modules/image_service.py b/host_modules/image_service.py index c52c6de9..a6a1f0a9 100644 --- a/host_modules/image_service.py +++ b/host_modules/image_service.py @@ -160,6 +160,29 @@ def list_images(self): logger.error(msg) return e.returncode, msg + @host_service.method( + host_service.bus_name(MOD_NAME), in_signature="s", out_signature="is" + ) + def set_next_boot(self, image): + """ + Set the image to be used for the next boot. + + Args: + image: The name of the image to set for the next boot. + """ + logger.info("Setting the next boot image to {}".format(image)) + cmd = ["/usr/local/bin/sonic-installer", "set-next-boot", image] + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + msg = "" + if result.returncode: + lines = result.stderr.decode().split("\n") + for line in lines: + if "Error" in line: + msg = line + break + return result.returncode, msg + + def _parse_sonic_installer_list(self, output): """ Parse the output of the sonic-installer list command. diff --git a/tests/host_modules/image_service_test.py b/tests/host_modules/image_service_test.py index bc941778..aeeecf21 100644 --- a/tests/host_modules/image_service_test.py +++ b/tests/host_modules/image_service_test.py @@ -568,3 +568,59 @@ def test_list_images_failed(self, mock_check_output, MockInit, MockBusName, Mock stderr=subprocess.STDOUT, ) + @mock.patch("dbus.SystemBus") + @mock.patch("dbus.service.BusName") + @mock.patch("dbus.service.Object.__init__") + @mock.patch("subprocess.run") + def test_image_set_next_boot_success(self, mock_run, MockInit, MockBusName, MockSystemBus): + """ + Test that the `set_next_boot` method successfully sets the next boot image. + """ + # Arrange + image_service = ImageService(mod_name="image_service") + image = "sonic_image" + mock_result = mock.Mock() + mock_result.returncode = 0 + mock_result.stderr = b"" + mock_run.return_value = mock_result + + # Act + rc, msg = image_service.set_next_boot(image) + + # Assert + assert rc == 0, "wrong return value" + assert msg == "", "message should be empty on success" + mock_run.assert_called_once_with( + ["/usr/local/bin/sonic-installer", "set-next-boot", image], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + @mock.patch("dbus.SystemBus") + @mock.patch("dbus.service.BusName") + @mock.patch("dbus.service.Object.__init__") + @mock.patch("subprocess.run") + def test_image_set_next_boot_fail_not_exists(self, mock_run, MockInit, MockBusName, MockSystemBus): + """ + Test that the `set_next_boot` method fails when the image does not exist. + """ + # Arrange + image_service = ImageService(mod_name="image_service") + image = "nonexistent_image" + mock_result = mock.Mock() + mock_result.returncode = 1 + mock_result.stderr = b"Error: Image does not exist" + mock_run.return_value = mock_result + + # Act + rc, msg = image_service.set_next_boot(image) + + # Assert + assert rc != 0, "wrong return value" + assert "Error: Image does not exist" in msg, "message should contain 'Error: Image does not exist'" + mock_run.assert_called_once_with( + ["/usr/local/bin/sonic-installer", "set-next-boot", image], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + From 098adeb3095b94b7957d6bef468b778f37d19771 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Thu, 23 Jan 2025 22:37:13 +0000 Subject: [PATCH 2/4] address copilot comment. --- host_modules/image_service.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/host_modules/image_service.py b/host_modules/image_service.py index a6a1f0a9..9e92bd6e 100644 --- a/host_modules/image_service.py +++ b/host_modules/image_service.py @@ -175,11 +175,7 @@ def set_next_boot(self, image): result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) msg = "" if result.returncode: - lines = result.stderr.decode().split("\n") - for line in lines: - if "Error" in line: - msg = line - break + msg = result.stderr.decode() return result.returncode, msg From 848a541699f2ef9c927cc0ba31d9650e3b288cd7 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Fri, 24 Jan 2025 16:17:12 +0000 Subject: [PATCH 3/4] more logs. --- host_modules/image_service.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/host_modules/image_service.py b/host_modules/image_service.py index 9e92bd6e..cfe2c786 100644 --- a/host_modules/image_service.py +++ b/host_modules/image_service.py @@ -173,8 +173,10 @@ def set_next_boot(self, image): logger.info("Setting the next boot image to {}".format(image)) cmd = ["/usr/local/bin/sonic-installer", "set-next-boot", image] result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - msg = "" + msg = "Boot image set to {}".format(image) + logger.info(msg) if result.returncode: + logger.error("Failed to set next boot image: {}".format(result.stderr.decode())) msg = result.stderr.decode() return result.returncode, msg From a784a00fa072ac7251d2c04ef507d71edc39c008 Mon Sep 17 00:00:00 2001 From: Dawei Huang Date: Fri, 24 Jan 2025 17:07:43 +0000 Subject: [PATCH 4/4] update unit test. --- tests/host_modules/image_service_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/host_modules/image_service_test.py b/tests/host_modules/image_service_test.py index aeeecf21..686971f2 100644 --- a/tests/host_modules/image_service_test.py +++ b/tests/host_modules/image_service_test.py @@ -589,7 +589,7 @@ def test_image_set_next_boot_success(self, mock_run, MockInit, MockBusName, Mock # Assert assert rc == 0, "wrong return value" - assert msg == "", "message should be empty on success" + assert image in msg, "message should contain the name of the new image" mock_run.assert_called_once_with( ["/usr/local/bin/sonic-installer", "set-next-boot", image], stdout=subprocess.PIPE,