From 3ebffb5de7f3c92cacb20767e0d53168518a01d6 Mon Sep 17 00:00:00 2001 From: Mayank Patel Date: Mon, 31 Oct 2022 07:53:26 -0400 Subject: [PATCH] Create the admin network in sysinv / 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. Test Plan: Debian PASS: - system network-add - system network-delete - system addrpool-add - system addrpool-modify - system interface-nework-assign - system host-addr-list controller-0 - system interface-network-assign controller-1 (AIO-DX) Story: 2010319 Task: 46688 Signed-off-by: Mayank Patel Change-Id: I1e7cb782e683daeb847e4855aeb3922a7655b2e9 --- .../sysinv/api/controllers/v1/address.py | 8 +++--- .../sysinv/api/controllers/v1/interface.py | 5 ++-- .../api/controllers/v1/interface_network.py | 26 +++++++++++++++++-- .../sysinv/api/controllers/v1/network.py | 18 ++++++++++--- .../sysinv/sysinv/api/controllers/v1/route.py | 5 +++- .../sysinv/sysinv/sysinv/common/constants.py | 4 ++- .../sysinv/sysinv/sysinv/puppet/interface.py | 18 ++++++++++++- .../sysinv/sysinv/sysinv/puppet/networking.py | 10 +++++++ .../sysinv/sysinv/tests/api/test_network.py | 25 +++++++++++++++++- sysinv/sysinv/sysinv/sysinv/tests/db/base.py | 22 ++++++++++++++-- 10 files changed, 125 insertions(+), 16 deletions(-) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py index 635d4ad256..59e5d628cd 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/address.py @@ -15,9 +15,10 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2015 Wind River Systems, Inc. +# Copyright (c) 2015-2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 # - import netaddr import pecan @@ -46,7 +47,8 @@ ALLOWED_NETWORK_TYPES = [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_DATA, constants.NETWORK_TYPE_IRONIC, - constants.NETWORK_TYPE_STORAGE] + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN] class Address(base.APIBase): diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py index d6f3ae7720..ba9f608d9b 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface.py @@ -16,7 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2021 Wind River Systems, Inc. +# Copyright (c) 2013-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -70,7 +70,8 @@ VALID_NETWORK_TYPES = [constants.NETWORK_TYPE_NONE, constants.NETWORK_TYPE_PCI_PASSTHROUGH, constants.NETWORK_TYPE_PCI_SRIOV, constants.NETWORK_TYPE_IRONIC, - constants.NETWORK_TYPE_STORAGE] + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN] VALID_INTERFACE_CLASS = [constants.INTERFACE_CLASS_PLATFORM, constants.INTERFACE_CLASS_DATA, diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py index e13679e958..aaec2f3dd8 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/interface_network.py @@ -16,7 +16,9 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2013-2021 Wind River Systems, Inc. +# Copyright (c) 2013-2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 # import os @@ -52,7 +54,8 @@ NONDUPLICATE_NETWORK_TYPES = (constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_OAM, constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_PXEBOOT, - constants.NETWORK_TYPE_STORAGE) + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN) class InterfaceNetwork(base.APIBase): @@ -413,6 +416,8 @@ class InterfaceNetworkController(rest.RestController): def _update_host_address(host, interface, network_type): if network_type == constants.NETWORK_TYPE_MGMT: _update_host_mgmt_address(host, interface) + elif network_type == constants.NETWORK_TYPE_ADMIN: + _update_host_admin_address(host, interface) elif network_type == constants.NETWORK_TYPE_CLUSTER_HOST: _update_host_cluster_address(host, interface) elif network_type == constants.NETWORK_TYPE_IRONIC: @@ -458,6 +463,23 @@ def _update_host_mgmt_address(host, interface): _allocate_pool_address(interface['id'], mgmt_pool_uuid, address_name) +def _update_host_admin_address(host, interface): + address_name = cutils.format_address_name(host.hostname, + constants.NETWORK_TYPE_ADMIN) + try: + address = pecan.request.dbapi.address_get_by_name(address_name) + updates = {'interface_id': interface['id']} + pecan.request.dbapi.address_update(address.uuid, updates) + except exception.AddressNotFoundByName: + # For non-controller hosts, allocate address from pool if dynamic + admin_network = pecan.request.dbapi.network_get_by_type( + constants.NETWORK_TYPE_ADMIN) + if admin_network.dynamic: + _allocate_pool_address(interface['id'], + admin_network.pool_uuid, + address_name) + + def _update_host_oam_address(host, interface): if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: address_name = cutils.format_address_name(constants.CONTROLLER_HOSTNAME, diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py index af5348cb4c..0758931ed3 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py @@ -15,7 +15,9 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2015 Wind River Systems, Inc. +# Copyright (c) 2015-2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 # import collections @@ -52,7 +54,7 @@ ALLOWED_NETWORK_TYPES = [constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_IRONIC, constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM, constants.NETWORK_TYPE_STORAGE, - ] + constants.NETWORK_TYPE_ADMIN] class Network(base.APIBase): @@ -173,6 +175,8 @@ class NetworkController(rest.RestController): def _create_network_addresses(self, pool, network): if network['type'] == constants.NETWORK_TYPE_MGMT: addresses = self._create_mgmt_network_address(pool) + elif network['type'] == constants.NETWORK_TYPE_ADMIN: + addresses = self._create_admin_network_address() elif network['type'] == constants.NETWORK_TYPE_PXEBOOT: addresses = self._create_pxeboot_network_address() elif network['type'] == constants.NETWORK_TYPE_CLUSTER_HOST: @@ -209,6 +213,13 @@ class NetworkController(rest.RestController): pool.gateway_address return addresses + def _create_admin_network_address(self): + addresses = collections.OrderedDict() + addresses[constants.CONTROLLER_HOSTNAME] = None + addresses[constants.CONTROLLER_0_HOSTNAME] = None + addresses[constants.CONTROLLER_1_HOSTNAME] = None + return addresses + def _create_pxeboot_network_address(self): addresses = collections.OrderedDict() addresses[constants.CONTROLLER_HOSTNAME] = None @@ -395,7 +406,8 @@ class NetworkController(rest.RestController): constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_CLUSTER_POD, constants.NETWORK_TYPE_CLUSTER_SERVICE, - constants.NETWORK_TYPE_STORAGE]: + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN]: msg = _("Cannot delete type {} network {} after initial " "configuration completion" .format(network['type'], network_uuid)) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py index 60e0b6c730..95809bba02 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/route.py @@ -17,6 +17,8 @@ # # Copyright (c) 2015-2022 Wind River Systems, Inc. # +# SPDX-License-Identifier: Apache-2.0 +# import netaddr import pecan @@ -46,7 +48,8 @@ ALLOWED_NETWORK_TYPES = [constants.NETWORK_TYPE_DATA, constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_STORAGE, - constants.NETWORK_TYPE_OAM] + constants.NETWORK_TYPE_OAM, + constants.NETWORK_TYPE_ADMIN] class Route(base.APIBase): diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 798c08a7db..448b8bc2fb 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -696,6 +696,7 @@ CONTROLLER_AUDIT_REQUESTS = [DISK_AUDIT_REQUEST, # Interface definitions NETWORK_TYPE_NONE = 'none' NETWORK_TYPE_MGMT = 'mgmt' +NETWORK_TYPE_ADMIN = 'admin' NETWORK_TYPE_OAM = 'oam' NETWORK_TYPE_BM = 'bm' NETWORK_TYPE_MULTICAST = 'multicast' @@ -717,7 +718,8 @@ PLATFORM_NETWORK_TYPES = [NETWORK_TYPE_PXEBOOT, NETWORK_TYPE_OAM, NETWORK_TYPE_CLUSTER_HOST, NETWORK_TYPE_IRONIC, - NETWORK_TYPE_STORAGE] + NETWORK_TYPE_STORAGE, + NETWORK_TYPE_ADMIN] PCI_NETWORK_TYPES = [NETWORK_TYPE_PCI_PASSTHROUGH, NETWORK_TYPE_PCI_SRIOV] diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py index a925b98151..91f5884940 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/interface.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/interface.py @@ -31,7 +31,8 @@ PLATFORM_NETWORK_TYPES = [constants.NETWORK_TYPE_PXEBOOT, constants.NETWORK_TYPE_CLUSTER_HOST, constants.NETWORK_TYPE_OAM, constants.NETWORK_TYPE_IRONIC, - constants.NETWORK_TYPE_STORAGE] + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN] DATA_NETWORK_TYPES = [constants.NETWORK_TYPE_DATA] @@ -315,6 +316,19 @@ class InterfacePuppet(base.BasePuppet): except exception.AddressNotFoundByName: pass + try: + admin_address = self._get_address_by_name( + constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_ADMIN) + + admin_floating_ip = (str(admin_address.address) + '/' + + str(admin_address.prefix)) + + floating_ips.update({ + constants.NETWORK_TYPE_ADMIN: admin_floating_ip, + }) + except exception.AddressNotFoundByName: + pass + return floating_ips def _get_datanetworks(self, host): @@ -724,6 +738,8 @@ def get_interface_address_method(context, iface, network_id=None): return STATIC_METHOD elif networktype == constants.NETWORK_TYPE_STORAGE: return STATIC_METHOD + elif networktype == constants.NETWORK_TYPE_ADMIN: + return STATIC_METHOD elif networktype == constants.NETWORK_TYPE_PXEBOOT: # All pxeboot interfaces that exist on non-controller nodes are set # to manual as they are not needed/used once the install is done. diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/networking.py b/sysinv/sysinv/sysinv/sysinv/puppet/networking.py index 8500f7eb2e..6691a992b5 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/networking.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/networking.py @@ -24,6 +24,7 @@ class NetworkingPuppet(base.BasePuppet): config.update(self._get_oam_network_config()) config.update(self._get_cluster_network_config()) config.update(self._get_ironic_network_config()) + config.update(self._get_admin_network_config()) config.update(self._get_storage_network_config()) return config @@ -34,6 +35,7 @@ class NetworkingPuppet(base.BasePuppet): config.update(self._get_cluster_interface_config()) config.update(self._get_ironic_interface_config()) config.update(self._get_storage_interface_config()) + config.update(self._get_admin_interface_config()) config.update(self._get_instance_ptp_config(host)) if host.personality == constants.CONTROLLER: config.update(self._get_oam_interface_config()) @@ -93,6 +95,11 @@ class NetworkingPuppet(base.BasePuppet): config = self._get_network_config(networktype) return config + def _get_admin_network_config(self): + networktype = constants.NETWORK_TYPE_ADMIN + config = self._get_network_config(networktype) + return config + def _get_network_config(self, networktype): try: network = self.dbapi.network_get_by_type(networktype) @@ -181,6 +188,9 @@ class NetworkingPuppet(base.BasePuppet): def _get_storage_interface_config(self): return self._get_interface_config(constants.NETWORK_TYPE_STORAGE) + def _get_admin_interface_config(self): + return self._get_interface_config(constants.NETWORK_TYPE_ADMIN) + def _set_ptp_instance_global_parameters(self, ptp_instances, ptp_parameters_instance): default_global_parameters = { diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py index 4c1f38cd6d..9dd3dfa58d 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2020 Wind River Systems, Inc. +# Copyright (c) 2020-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -116,6 +116,10 @@ class NetworkTestCase(base.FunctionalTest, dbbase.BaseHostTestCase): hostnames, self.storage_subnet, constants.NETWORK_TYPE_STORAGE) + self._create_test_addresses( + hostnames, self.admin_subnet, + constants.NETWORK_TYPE_ADMIN) + self._create_test_addresses( hostnames, self.system_controller_subnet, constants.NETWORK_TYPE_SYSTEM_CONTROLLER) @@ -267,6 +271,12 @@ class TestPostMixin(NetworkTestCase): constants.NETWORK_TYPE_STORAGE, self.storage_subnet) + def test_create_success_admin(self): + self._test_create_network_success( + 'admin', + constants.NETWORK_TYPE_ADMIN, + self.admin_subnet) + def test_create_fail_duplicate_pxeboot(self): self._test_create_network_fail_duplicate( 'pxeboot', @@ -309,6 +319,12 @@ class TestPostMixin(NetworkTestCase): constants.NETWORK_TYPE_STORAGE, self.storage_subnet) + def test_create_fail_duplicate_admin(self): + self._test_create_network_fail_duplicate( + 'admin', + constants.NETWORK_TYPE_ADMIN, + self.admin_subnet) + def test_create_with_invalid_type(self): # Test creation with an invalid type address_pool_id = self._create_test_address_pool( @@ -456,6 +472,13 @@ class TestDelete(NetworkTestCase): constants.NETWORK_TYPE_STORAGE ) + def test_delete_admin_subnet(self): + self._test_delete_allowed(constants.NETWORK_TYPE_ADMIN) + + def test_delete_admin_subnet_after_initial_config(self): + self._test_delete_after_initial_config_not_allowed( + constants.NETWORK_TYPE_ADMIN) + def test_delete_data(self): self._test_delete_allowed(constants.NETWORK_TYPE_DATA) diff --git a/sysinv/sysinv/sysinv/sysinv/tests/db/base.py b/sysinv/sysinv/sysinv/sysinv/tests/db/base.py index 8d384bcc1f..e3ec37d033 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/db/base.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/db/base.py @@ -1,3 +1,5 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + # Copyright (c) 2012 NTT DOCOMO, INC. # All Rights Reserved. # @@ -12,6 +14,11 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +# +# Copyright (c) 2013-2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# """Sysinv DB test base class.""" @@ -50,6 +57,7 @@ class BaseIPv4Mixin(object): cluster_service_subnet = netaddr.IPNetwork('10.96.0.0/12') multicast_subnet = netaddr.IPNetwork('239.1.1.0/28') storage_subnet = netaddr.IPNetwork('10.10.20.0/24') + admin_subnet = netaddr.IPNetwork('10.10.30.0/24') system_controller_subnet = netaddr.IPNetwork('192.168.104.0/24') system_controller_oam_subnet = netaddr.IPNetwork('10.10.50.0/24') @@ -69,6 +77,7 @@ class BaseIPv6Mixin(object): cluster_service_subnet = netaddr.IPNetwork('fd04::/112') multicast_subnet = netaddr.IPNetwork('ff08::1:1:0/124') storage_subnet = netaddr.IPNetwork('fd05::/64') + admin_subnet = netaddr.IPNetwork('fd09::/64') system_controller_subnet = netaddr.IPNetwork('fd07::/64') system_controller_oam_subnet = netaddr.IPNetwork('fd06::/64') @@ -265,6 +274,10 @@ class BaseSystemTestCase(BaseIPv4Mixin, DbTestCase): constants.NETWORK_TYPE_STORAGE, self.storage_subnet) + self._create_test_network('admin', + constants.NETWORK_TYPE_ADMIN, + self.admin_subnet) + self._create_test_network('system-controller', constants.NETWORK_TYPE_SYSTEM_CONTROLLER, self.system_controller_subnet) @@ -323,6 +336,10 @@ class BaseSystemTestCase(BaseIPv4Mixin, DbTestCase): hostnames, self.storage_subnet, constants.NETWORK_TYPE_STORAGE) + self._create_test_addresses( + hostnames, self.admin_subnet, + constants.NETWORK_TYPE_ADMIN) + self._create_test_addresses( hostnames, self.system_controller_subnet, constants.NETWORK_TYPE_SYSTEM_CONTROLLER) @@ -444,8 +461,9 @@ class BaseHostTestCase(BaseSystemTestCase): network_types = [constants.NETWORK_TYPE_OAM, constants.NETWORK_TYPE_MGMT, constants.NETWORK_TYPE_CLUSTER_HOST, - constants.NETWORK_TYPE_STORAGE] - ifnames = ['oam', 'mgmt', 'cluster', 'storage'] + constants.NETWORK_TYPE_STORAGE, + constants.NETWORK_TYPE_ADMIN] + ifnames = ['oam', 'mgmt', 'cluster', 'storage', 'admin'] index = 0 ifaces = [] for nt, name in zip(network_types, ifnames):