From e53cf0cc2cb4490a93405789faf8fe497d8894ba Mon Sep 17 00:00:00 2001 From: Melissa Wang Date: Fri, 23 Apr 2021 16:47:43 -0400 Subject: [PATCH] SX-to-DX: Check host administrative state This update adds semantic checks to ensure that the controller is locked before starting simplex-to-duplex migration. Story: 2008587 Task: 42369 Change-Id: I1ebf3bb531073344b4d17a00cd1ee480051f3897 Signed-off-by: Melissa Wang --- .../sysinv/api/controllers/v1/system.py | 10 ++ .../sysinv/sysinv/tests/api/test_system.py | 103 +++++++++++++++--- 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py index 8836b44842..9a83645ddd 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/system.py @@ -312,6 +312,15 @@ class SystemController(rest.RestController): % system_mode) raise wsme.exc.ClientSideError(msg) + def _check_controller_locked(self): + controller = api_utils.HostHelper.get_active_controller() + if controller is None: + return + if controller.administrative != constants.ADMIN_LOCKED: + msg = _("Cannot modify system mode if host '%s' is not " + "locked." % controller.hostname) + raise wsme.exc.ClientSideError(msg) + def _get_isystem_collection(self, marker, limit, sort_key, sort_dir, expand=False, resource_url=None): limit = api_utils.validate_limit(limit) @@ -433,6 +442,7 @@ class SystemController(rest.RestController): "set to %s." % rpc_isystem.system_mode) raise wsme.exc.ClientSideError(msg) elif new_system_mode != constants.SYSTEM_MODE_SIMPLEX: + self._check_controller_locked() self._check_interfaces(new_system_mode) else: system_mode_options.append(constants.SYSTEM_MODE_SIMPLEX) diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_system.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_system.py index 892997cbae..56710e450e 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_system.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_system.py @@ -98,6 +98,7 @@ class TestSystemUpdateModeFromSimplex(TestSystem): personality=constants.CONTROLLER, subfunctions=constants.CONTROLLER, invprovision=constants.PROVISIONED, + administrative=constants.ADMIN_LOCKED ) def _create_mgmt_interface_network(self, interface='mgmt'): @@ -152,8 +153,12 @@ class TestSystemUpdateModeFromSimplex(TestSystem): interface_id=self.cluster_host_interface.id, network_id=self.cluster_host_network.id) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_duplex(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_duplex(self, mock_init_config, + mock_controller): self._create_mgmt_interface_network() self._create_cluster_host_interface_network() update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} @@ -163,8 +168,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_duplex_mgmt_on_lo(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_duplex_mgmt_on_lo(self, + mock_init_config, + mock_controller): self._create_mgmt_interface_network(interface=constants.LOOPBACK_IFNAME) self._create_cluster_host_interface_network() update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} @@ -174,8 +184,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_duplex_no_mgmt_if(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_duplex_no_mgmt_if(self, + mock_init_config, + mock_controller): self._create_cluster_host_interface_network() update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} self._patch_and_check(self._get_path(self.system.uuid), @@ -184,8 +199,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_duplex_cluster_host_on_lo(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_duplex_cluster_host_on_lo(self, + mock_init_config, + mock_controller): self._create_mgmt_interface_network() self._create_cluster_host_interface_network(interface=constants.LOOPBACK_IFNAME) update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} @@ -195,8 +215,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_duplex_no_cluster_host_if(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_duplex_no_cluster_host_if(self, + mock_init_config, + mock_controller): self._create_mgmt_interface_network() update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} self._patch_and_check(self._get_path(self.system.uuid), @@ -205,8 +230,12 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=True) - def test_update_system_mode_simplex_to_simplex(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_simplex_to_simplex(self, mock_init_config, + mock_controller): update = {"system_mode": constants.SYSTEM_MODE_SIMPLEX} self._patch_and_check(self._get_path(self.system.uuid), update) @@ -214,8 +243,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=False) - def test_update_system_mode_before_initial_config_complete(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=False) + def test_update_system_mode_before_initial_config_complete(self, + mock_init_config, + mock_controller): update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} self._patch_and_check(self._get_path(self.system.uuid), update) @@ -223,8 +257,13 @@ class TestSystemUpdateModeFromSimplex(TestSystem): system_dict = system.as_dict() self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) - @mock.patch('sysinv.common.utils.is_initial_config_complete', return_value=False) - def test_update_system_mode_before_initial_config_complete_only_mgmt_if(self, mock_exists): + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=False) + def test_update_system_mode_before_initial_config_complete_only_mgmt_if(self, + mock_init_config, + mock_controller): self._create_mgmt_interface_network() update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} self._patch_and_check(self._get_path(self.system.uuid), @@ -234,6 +273,38 @@ class TestSystemUpdateModeFromSimplex(TestSystem): self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) +class TestSystemUpdateModeUnlockedController(TestSystem): + + def setUp(self): + super(TestSystemUpdateModeUnlockedController, self).setUp() + self.dbapi = db_api.get_instance() + self.system = dbutils.create_test_isystem(system_type=constants.TIS_AIO_BUILD, + system_mode=constants.SYSTEM_MODE_SIMPLEX) + self.controller = dbutils.create_test_ihost( + id='1', + uuid=None, + forisystemid=self.system.id, + hostname='controller-0', + personality=constants.CONTROLLER, + subfunctions=constants.CONTROLLER, + invprovision=constants.PROVISIONED, + administrative=constants.ADMIN_UNLOCKED + ) + + @mock.patch('socket.gethostname', + return_value='controller-0') + @mock.patch('sysinv.common.utils.is_initial_config_complete', + return_value=True) + def test_update_system_mode_on_unlocked_controller(self, mock_init_config, + mock_controller): + update = {"system_mode": constants.SYSTEM_MODE_DUPLEX} + self._patch_and_check(self._get_path(self.system.uuid), + update, expect_errors=True) + system = self.dbapi.isystem_get_one() + system_dict = system.as_dict() + self.assertNotIn('simplex_to_duplex_migration', system_dict['capabilities']) + + class TestSystemUpdateModeFromDuplex(TestSystem): def setUp(self):