From fc0a5f0e53ccac983bbd7ffc681b807e364ced53 Mon Sep 17 00:00:00 2001 From: Salman Rana Date: Fri, 16 Feb 2024 15:00:25 -0500 Subject: [PATCH] Restrict subcloud deploy configure to primary site Restrict subcloud's secondary sites (the peer site where subcloud in SPG can be migrated to) from reconfiguring the subcloud. This is necessary because subcloud reconfig may update the OpenStack endpoints/routes/network IPs and impact the ability to migrate the subcloud back to the primary site. - Introduced a check in "deploy configure" to ensure that the target subcloud is managed by its primary site. The site status is inferred from the subcloud's peer group priority on the site. Test Plan: Setup a DC system with GR configuration (2 sites + subclouds) and verify the "subcloud deploy config" command with the following arrangements: 1. PASS: For a subcloud that's not part of protection group (not in any SPG / not part of GR), ensure that the operation is not restricted. 2. PASS: Add subcloud to SPG and keep it managed by primary site, ensure that the operation is not restricted. 3. PASS: Add system peer, create SPG association with a secondary site, and migrate the subcloud from #2. Verify that "deploy config" is restricted and it fails with following error message: "Subcloud must be managed by its primary site" 4. PASS: Using the subcloud from #3, migrate it back to the primary site. Attempt to deploy config and ensure that the operation is not restricted. Closes-Bug: 2054161 Change-Id: I7e986771a32882bfad917c85439e6f6b99dfe173 Signed-off-by: Salman Rana --- .../controllers/v1/peer_group_association.py | 7 ++- .../controllers/v1/phased_subcloud_deploy.py | 11 +++++ distributedcloud/dcmanager/common/consts.py | 2 + .../test_phased_subcloud_deploy.py | 44 +++++++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/distributedcloud/dcmanager/api/controllers/v1/peer_group_association.py b/distributedcloud/dcmanager/api/controllers/v1/peer_group_association.py index f4c849234..d13d8e09f 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/peer_group_association.py +++ b/distributedcloud/dcmanager/api/controllers/v1/peer_group_association.py @@ -31,7 +31,6 @@ from dcmanager.rpc import client as rpc_client CONF = cfg.CONF LOG = logging.getLogger(__name__) -PEER_GROUP_PRIMARY_PRIORITY = 0 MIN_PEER_GROUP_ASSOCIATION_PRIORITY = 1 MAX_PEER_GROUP_ASSOCIATION_PRIORITY = 65536 ASSOCIATION_SYNC_STATUS_LIST = \ @@ -191,9 +190,9 @@ class PeerGroupAssociationsController(restcomm.GenericPathController): pecan.abort(httpclient.BAD_REQUEST, _('Invalid peer_group_priority')) - if (peer_group.group_priority == PEER_GROUP_PRIMARY_PRIORITY and + if (peer_group.group_priority == consts.PEER_GROUP_PRIMARY_PRIORITY and peer_group_priority is None) or ( - peer_group.group_priority > PEER_GROUP_PRIMARY_PRIORITY and + peer_group.group_priority > consts.PEER_GROUP_PRIMARY_PRIORITY and peer_group_priority is not None): pecan.abort(httpclient.BAD_REQUEST, _('Peer Group Association create is not allowed when ' @@ -201,7 +200,7 @@ class PeerGroupAssociationsController(restcomm.GenericPathController): 'and it is required when the subcloud peer group ' 'priority is 0.')) - is_primary = peer_group.group_priority == PEER_GROUP_PRIMARY_PRIORITY + is_primary = peer_group.group_priority == consts.PEER_GROUP_PRIMARY_PRIORITY # only one combination of peer_group_id + system_peer_id can exists association = None diff --git a/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py b/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py index 10727aa18..27b8744f0 100644 --- a/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py +++ b/distributedcloud/dcmanager/api/controllers/v1/phased_subcloud_deploy.py @@ -309,6 +309,17 @@ class PhasedSubcloudDeployController(object): pecan.abort(400, _('Subcloud prestage is ongoing %s') % subcloud.prestage_status) + # If the subcloud belongs to a peer group, ensure that + # it's not being configured in a secondary site. + if subcloud.peer_group_id is not None: + peer_group = utils.subcloud_peer_group_get_by_ref( + context, str(subcloud.peer_group_id)) + if peer_group is not None: + if peer_group.group_priority != consts.PEER_GROUP_PRIMARY_PRIORITY: + pecan.abort(400, + _('Subcloud can only be configured in' + ' its primary site.')) + psd_common.populate_payload_with_pre_existing_data( payload, subcloud, SUBCLOUD_CONFIG_GET_FILE_CONTENTS) diff --git a/distributedcloud/dcmanager/common/consts.py b/distributedcloud/dcmanager/common/consts.py index 501d33c65..23d8afebc 100644 --- a/distributedcloud/dcmanager/common/consts.py +++ b/distributedcloud/dcmanager/common/consts.py @@ -461,6 +461,8 @@ PEER_GROUP_MIGRATING = 'migrating' PEER_GROUP_MIGRATION_COMPLETE = 'complete' PEER_GROUP_MIGRATION_NONE = 'none' +PEER_GROUP_PRIMARY_PRIORITY = 0 + # Peer group association type ASSOCIATION_TYPE_PRIMARY = 'primary' ASSOCIATION_TYPE_NON_PRIMARY = 'non-primary' diff --git a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_phased_subcloud_deploy.py b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_phased_subcloud_deploy.py index 9a3509cce..f223da350 100644 --- a/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_phased_subcloud_deploy.py +++ b/distributedcloud/dcmanager/tests/unit/api/v1/controllers/test_phased_subcloud_deploy.py @@ -28,6 +28,8 @@ from dcmanager.tests.unit.api.v1.controllers.test_subclouds import \ from dcmanager.tests.unit.api.v1.controllers.test_subclouds import \ TestSubcloudPost from dcmanager.tests.unit.common import fake_subcloud +from dcmanager.tests.unit.manager.test_system_peer_manager import \ + TestSystemPeerManager from dcmanager.tests import utils FAKE_URL = '/v1.0/phased-subcloud-deploy' @@ -345,6 +347,48 @@ class TestSubcloudDeployConfig(testroot.DCManagerApiTest): str(subcloud.id) + '/configure', headers=FAKE_HEADERS, params=data) + def test_configure_primary_subcloud_peer_group(self): + data_install = copy.copy(FAKE_SUBCLOUD_INSTALL_VALUES) + data_install.pop('software_version') + + fake_password = \ + (base64.b64encode('testpass'.encode("utf-8"))).decode('ascii') + data = {'sysadmin_password': fake_password} + + self.mock_rpc_client().subcloud_deploy_config.return_value = True + self.mock_get_request_data.return_value = data + + # Create a subcloud and add it to SPG with primary priority + peer_group = TestSystemPeerManager.create_subcloud_peer_group_static( + self.ctx, + group_priority=consts.PEER_GROUP_PRIMARY_PRIORITY, + peer_group_name='SubcloudPeerGroup1') + + subcloud = TestSystemPeerManager.create_subcloud_with_pg_static( + self.ctx, + peer_group_id=peer_group.id, + name='subcloud1', + data_install=json.dumps(data_install)) + + response = self.app.patch_json(FAKE_URL + '/' + str(subcloud.id) + + '/configure', + headers=FAKE_HEADERS, + params=data) + self.mock_rpc_client().subcloud_deploy_config.assert_called_once_with( + mock.ANY, subcloud.id, data, initial_deployment=True) + self.assertEqual(response.status_int, 200) + + # Change the SPG as if it was on peer site + db_api.subcloud_peer_group_update( + self.ctx, + peer_group.id, + group_priority=consts.PEER_GROUP_PRIMARY_PRIORITY + 1) + + six.assertRaisesRegex(self, webtest.app.AppError, "400 *", + self.app.patch_json, FAKE_URL + '/' + + str(subcloud.id) + '/configure', + headers=FAKE_HEADERS, params=data) + class TestSubcloudDeployInstall(testroot.DCManagerApiTest): def setUp(self):