Update subcloud admin parameters in db

The current restrictions for network reconfiguration post-install is problematic for correcting or updating network configuration for Subcloud deployments or supporting Subcloud rehoming procedures.

The proposed solution is to decouple the Distributed Cloud networking operations from the management network by introducing a new admin network type that will be used for the addressing and routing between the central System Controller and the Subcloud.

Currently, network parameters are not able to be modified via the dcmanager subcloud update CLI.  This commit allows a subcloud to modify the current network parameters  via the dcmanager subcloud update CLI and have that be reflected in the dcmanager DB (subcloud entity).

Test-plan: Debian

dcmanager subcloud update --admin-subnet --admin-gateway-ip --admin-node-0-address --admin-node-1-address
dcmanager subcloud show

Negative Testing :

1. Test dcmanager subcloud update  command with invalid admin-subnet .
Example:
dcmanager subcloud update --admin-subnet 254.254.254.2 --admin-gateway-ip 192.168.101.1 --admin-node-0-address 192.168.101.5 --admin-node-1-address 192.168.101.49 subcloud1
dcmanager subcloud show subcloud1
Result:  dcmanager subcloud update  should not update invalid admin-subnet  IP .

2. Test dcmanager subcloud update  command  with invalid admin-gateway-ip , admin-node-0-address & admin-node-1-address . It should not allow subcloud update with invalid admin IP.

3. Test dcmanager subcloud update --description without updating admin parameters in a subcloud .
Example:
dcmanager subcloud update --description mks subcloud1
dcmanager subcloud show
Expected result : It should not update any admin parameters (admin-subnet, admin-gateway-ip , admin-node-0-address & admin-node-1-address)  in a subcloud . It should only update subcloud description.

Story: 2010319
Task: 46962

Signed-off-by: rsujay <ramkrishna.sujay@windriver.com>
Change-Id: I4cbaab5add873eb4264182645042bff1ee7f2456
This commit is contained in:
rsujay 2022-11-30 04:19:23 -05:00 committed by Ram Sujay
parent fd05375f84
commit 8ca98d3ee1
10 changed files with 204 additions and 13 deletions

View File

@ -572,6 +572,17 @@ class SubcloudsController(object):
LOG.exception(e)
pecan.abort(400, _("admin_gateway_address invalid: %s") % e)
subcloud_admin_address_start = IPAddress(admin_start_address_str)
subcloud_admin_address_end = IPAddress(admin_end_address_str)
subcloud_admin_gw_ip = IPAddress(admin_gateway_address_str)
if ((subcloud_admin_gw_ip >= subcloud_admin_address_start) and
(subcloud_admin_gw_ip <= subcloud_admin_address_end)):
pecan.abort(400, _("admin_gateway_address invalid, "
"is within admin pool: %(start)s - "
"%(end)s") %
{'start': subcloud_admin_address_start,
'end': subcloud_admin_address_end})
def _format_ip_address(self, payload):
"""Format IP addresses in 'bootstrap_values' and 'install_values'.
@ -1174,6 +1185,29 @@ class SubcloudsController(object):
location = payload.get('location')
group_id = payload.get('group_id')
admin_subnet_str = payload.get('admin_subnet')
admin_start_ip_str = payload.get('admin_node_0_address')
admin_end_ip_str = payload.get('admin_node_1_address')
admin_gateway_ip_str = payload.get('admin_gateway_ip')
admin_address_configured = False
# Syntax checking
if (admin_subnet_str and admin_gateway_ip_str and
admin_start_ip_str and admin_end_ip_str):
# Parse/validate the admin subnet
subcloud_subnets = []
subclouds = db_api.subcloud_get_all(context)
for subcloud in subclouds:
subcloud_subnets.append(IPNetwork(subcloud.management_subnet))
admin_address_configured = True
self._validate_admin_network_config(admin_subnet_str,
admin_start_ip_str,
admin_end_ip_str,
admin_gateway_ip_str,
subcloud_subnets)
# Syntax checking
if management_state and \
management_state not in [dccommon_consts.MANAGEMENT_UNMANAGED,
@ -1211,10 +1245,22 @@ class SubcloudsController(object):
try:
# Inform dcmanager that subcloud has been updated.
# It will do all the real work...
subcloud = self.dcmanager_rpc_client.update_subcloud(
context, subcloud_id, management_state=management_state,
description=description, location=location, group_id=group_id,
data_install=data_install, force=force_flag)
if admin_address_configured:
subcloud = self.dcmanager_rpc_client.update_subcloud(
context, subcloud_id, management_state=management_state,
description=description, management_subnet=payload.get('admin_subnet'),
management_gateway_ip=payload.get('admin_gateway_ip'),
management_start_ip=payload.get('admin_node_0_address'),
management_end_ip=payload.get('admin_node_1_address'),
location=location, group_id=group_id,
data_install=data_install, force=force_flag)
else:
subcloud = self.dcmanager_rpc_client.update_subcloud(
context, subcloud_id, management_state=management_state,
description=description, location=location,
group_id=group_id, data_install=data_install,
force=force_flag)
return subcloud
except RemoteError as e:
pecan.abort(422, e.value)

View File

@ -168,7 +168,9 @@ def subcloud_get_all_with_status(context):
def subcloud_update(context, subcloud_id, management_state=None,
availability_status=None, software_version=None,
description=None, location=None, audit_fail_count=None,
description=None, management_subnet=None, management_gateway_ip=None,
management_start_ip=None, management_end_ip=None,
location=None, audit_fail_count=None,
deploy_status=None, backup_status=None,
backup_datetime=None, error_description=None,
openstack_installed=None, group_id=None,
@ -176,9 +178,10 @@ def subcloud_update(context, subcloud_id, management_state=None,
"""Update a subcloud or raise if it does not exist."""
return IMPL.subcloud_update(context, subcloud_id, management_state,
availability_status, software_version,
description, location, audit_fail_count,
deploy_status, backup_status, backup_datetime,
error_description, openstack_installed,
description, management_subnet, management_gateway_ip,
management_start_ip, management_end_ip, location,
audit_fail_count, deploy_status, backup_status,
backup_datetime, error_description, openstack_installed,
group_id, data_install, data_upgrade)

View File

@ -374,7 +374,9 @@ def subcloud_create(context, name, description, location, software_version,
@require_admin_context
def subcloud_update(context, subcloud_id, management_state=None,
availability_status=None, software_version=None,
description=None, location=None, audit_fail_count=None,
description=None, management_subnet=None,
management_gateway_ip=None, management_start_ip=None,
management_end_ip=None, location=None, audit_fail_count=None,
deploy_status=None, backup_status=None,
backup_datetime=None, error_description=None,
openstack_installed=None,
@ -391,6 +393,14 @@ def subcloud_update(context, subcloud_id, management_state=None,
subcloud_ref.software_version = software_version
if description is not None:
subcloud_ref.description = description
if management_subnet is not None:
subcloud_ref.management_subnet = management_subnet
if management_gateway_ip is not None:
subcloud_ref.management_gateway_ip = management_gateway_ip
if management_start_ip is not None:
subcloud_ref.management_start_ip = management_start_ip
if management_end_ip is not None:
subcloud_ref.management_end_ip = management_end_ip
if location is not None:
subcloud_ref.location = location
if audit_fail_count is not None:

View File

@ -107,13 +107,19 @@ class DCManagerService(service.Service):
@request_context
def update_subcloud(self, context, subcloud_id, management_state=None,
description=None, location=None, group_id=None,
data_install=None, force=None):
description=None, management_subnet=None,
management_gateway_ip=None, management_start_ip=None,
management_end_ip=None, location=None,
group_id=None, data_install=None, force=None):
# Updates a subcloud
LOG.info("Handling update_subcloud request for: %s" % subcloud_id)
subcloud = self.subcloud_manager.update_subcloud(context, subcloud_id,
management_state,
description,
management_subnet,
management_gateway_ip,
management_start_ip,
management_end_ip,
location,
group_id,
data_install,

View File

@ -1584,6 +1584,10 @@ class SubcloudManager(manager.Manager):
subcloud_id,
management_state=None,
description=None,
management_subnet=None,
management_gateway_ip=None,
management_start_ip=None,
management_end_ip=None,
location=None,
group_id=None,
data_install=None,
@ -1641,6 +1645,10 @@ class SubcloudManager(manager.Manager):
subcloud_id,
management_state=management_state,
description=description,
management_subnet=management_subnet,
management_gateway_ip=management_gateway_ip,
management_start_ip=management_start_ip,
management_end_ip=management_end_ip,
location=location,
group_id=group_id,
data_install=data_install)

View File

@ -129,12 +129,18 @@ class ManagerClient(RPCClient):
subcloud_id=subcloud_id))
def update_subcloud(self, ctxt, subcloud_id, management_state=None,
description=None, location=None, group_id=None,
description=None, management_subnet=None,
management_gateway_ip=None, management_start_ip=None,
management_end_ip=None, location=None, group_id=None,
data_install=None, force=None):
return self.call(ctxt, self.make_msg('update_subcloud',
subcloud_id=subcloud_id,
management_state=management_state,
description=description,
management_subnet=management_subnet,
management_gateway_ip=management_gateway_ip,
management_start_ip=management_start_ip,
management_end_ip=management_end_ip,
location=location,
group_id=group_id,
data_install=data_install,

View File

@ -1041,6 +1041,40 @@ class TestSubcloudAPIOther(testroot.DCManagerApiTest):
force=None)
self.assertEqual(response.status_int, 200)
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_validate_admin_network_config')
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
def test_patch_subcloud_admin_values(self, mock_get_patch_data,
mock_validate_admin_network_config,
mock_rpc_client):
subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
data = {'admin_subnet': "192.168.102.0/24",
'admin_node_0_address': "192.168.102.5",
'admin_node_1_address': "192.168.102.49",
'admin_gateway_ip': "192.168.102.1"}
mock_rpc_client().update_subcloud.return_value = True
mock_get_patch_data.return_value = data
response = self.app.patch_json(FAKE_URL + '/' + str(subcloud.id),
headers=FAKE_HEADERS,
params=data)
self.assertEqual(response.status_int, 200)
mock_validate_admin_network_config.assert_called_once()
mock_rpc_client().update_subcloud.assert_called_once_with(
mock.ANY,
subcloud.id,
management_state=None,
description=None,
management_subnet=data['admin_subnet'],
management_gateway_ip=data['admin_gateway_ip'],
management_start_ip=data['admin_node_0_address'],
management_end_ip=data['admin_node_1_address'],
location=None,
group_id=None,
data_install=None,
force=None)
self.assertEqual(response.status_int, 200)
@mock.patch.object(rpc_client, 'ManagerClient')
@mock.patch.object(subclouds.SubcloudsController, '_get_patch_data')
def test_patch_subcloud_install_values(self, mock_get_patch_data,

View File

@ -178,15 +178,27 @@ class DBAPISubcloudTest(base.DCManagerTestCase):
management_state = 'testmanagementstate'
availability_status = 'testavailabilitystatus'
software_version = 'testversion'
admin_subnet = '192.168.102.0/24'
admin_start_ip = '192.168.102.5'
admin_end_ip = '192.168.102.49'
admin_gateway_ip = '192.168.102.1'
updated = db_api.subcloud_update(
self.ctx, subcloud.id,
management_state=management_state,
availability_status=availability_status,
software_version=software_version)
software_version=software_version,
management_subnet=admin_subnet,
management_start_ip=admin_start_ip,
management_end_ip=admin_end_ip,
management_gateway_ip=admin_gateway_ip)
self.assertIsNotNone(updated)
self.assertEqual(management_state, updated.management_state)
self.assertEqual(availability_status, updated.availability_status)
self.assertEqual(software_version, updated.software_version)
self.assertEqual(admin_subnet, updated.management_subnet)
self.assertEqual(admin_start_ip, updated.management_start_ip)
self.assertEqual(admin_end_ip, updated.management_end_ip)
self.assertEqual(admin_gateway_ip, updated.management_gateway_ip)
updated_subcloud = db_api.subcloud_get(self.ctx, subcloud.id)
self.assertEqual(management_state,
@ -195,6 +207,14 @@ class DBAPISubcloudTest(base.DCManagerTestCase):
updated_subcloud.availability_status)
self.assertEqual(software_version,
updated_subcloud.software_version)
self.assertEqual(admin_subnet,
updated_subcloud.management_subnet)
self.assertEqual(admin_start_ip,
updated_subcloud.management_start_ip)
self.assertEqual(admin_end_ip,
updated_subcloud.management_end_ip)
self.assertEqual(admin_gateway_ip,
updated_subcloud.management_gateway_ip)
def test_delete_subcloud(self):
fake_subcloud = utils.create_subcloud_dict(base.SUBCLOUD_SAMPLE_DATA_0)

View File

@ -97,6 +97,8 @@ class TestDCManagerService(base.DCManagerTestCase):
self.context, subcloud_id=1, management_state='testmgmtstatus')
mock_subcloud_manager().update_subcloud.\
assert_called_once_with(self.context, mock.ANY,
mock.ANY, mock.ANY,
mock.ANY, mock.ANY,
mock.ANY, mock.ANY,
mock.ANY, mock.ANY,
mock.ANY, mock.ANY)

View File

@ -647,6 +647,62 @@ class TestSubcloudManager(base.DCManagerTestCase):
self.assertEqual("subcloud new location",
updated_subcloud.location)
def test_update_subcloud_with_admin_values(self):
subcloud = self.create_subcloud_static(
self.ctx,
name='subcloud1',
deploy_status=consts.DEPLOY_STATE_DONE)
db_api.subcloud_update(self.ctx,
subcloud.id,
availability_status=dccommon_consts.AVAILABILITY_ONLINE)
data = {'admin_subnet': "192.168.102.0/24",
'admin_node_0_address': "192.168.102.5",
'admin_node_1_address': "192.168.102.49",
'admin_gateway_ip': "192.168.102.1"}
fake_dcmanager_notification = FakeDCManagerNotifications()
p = mock.patch('dcmanager.rpc.client.DCManagerNotifications')
mock_dcmanager_api = p.start()
mock_dcmanager_api.return_value = fake_dcmanager_notification
sm = subcloud_manager.SubcloudManager()
sm.update_subcloud(self.ctx,
subcloud.id,
management_state=dccommon_consts.MANAGEMENT_MANAGED,
description="subcloud new description",
location="subcloud new location",
management_subnet=data['admin_subnet'],
management_gateway_ip=data['admin_gateway_ip'],
management_start_ip=data['admin_node_0_address'],
management_end_ip=data['admin_node_1_address'])
fake_dcmanager_notification.subcloud_managed.assert_called_once_with(
self.ctx, subcloud.name)
exclude_endpoints = [dccommon_consts.ENDPOINT_TYPE_PATCHING,
dccommon_consts.ENDPOINT_TYPE_LOAD]
self.fake_dcmanager_audit_api.trigger_subcloud_audits.\
assert_called_once_with(self.ctx, subcloud.id, exclude_endpoints)
# Verify subcloud was updated with correct values
updated_subcloud = db_api.subcloud_get_by_name(self.ctx, subcloud.name)
self.assertEqual(dccommon_consts.MANAGEMENT_MANAGED,
updated_subcloud.management_state)
self.assertEqual("subcloud new description",
updated_subcloud.description)
self.assertEqual("subcloud new location",
updated_subcloud.location)
self.assertEqual(data['admin_subnet'],
updated_subcloud.management_subnet)
self.assertEqual(data['admin_gateway_ip'],
updated_subcloud.management_gateway_ip)
self.assertEqual(data['admin_node_0_address'],
updated_subcloud.management_start_ip)
self.assertEqual(data['admin_node_1_address'],
updated_subcloud.management_end_ip)
def test_update_subcloud_with_install_values(self):
subcloud = self.create_subcloud_static(
self.ctx,