diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py index 5af4336b26..4c002d3d1e 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/network.py @@ -345,8 +345,26 @@ class NetworkController(rest.RestController): pecan.request.rpcapi.reconfigure_service_endpoints( pecan.request.context, chosts[0]) + # After the initial configration completed, we can still delete/add + # the system controller networks in a subcloud's controller to + # re-home a subcloud to a new central cloud. In this case, we want + # to update the related services configurations in runtime. + if cutils.is_initial_config_complete() and \ + network['type'] in [constants.NETWORK_TYPE_SYSTEM_CONTROLLER, + constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM]: + self._update_system_controller_network_config(network['type']) return Network.convert_with_links(result) + def _update_system_controller_network_config(self, type): + """ Update related services configurations after updating system + controller networks""" + if type == constants.NETWORK_TYPE_SYSTEM_CONTROLLER: + pecan.request.rpcapi.update_ldap_client_config( + pecan.request.context) + elif type == constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM: + pecan.request.rpcapi.update_dnsmasq_config( + pecan.request.context) + @wsme_pecan.wsexpose(NetworkCollection, types.uuid, int, wtypes.text, wtypes.text) def get_all(self, marker=None, limit=None, sort_key='id', sort_dir='asc'): diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index be5f429f2f..c8b5ad3c55 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -10821,6 +10821,26 @@ class ConductorManager(service.PeriodicService): } self._config_apply_runtime_manifest(context, config_uuid, config_dict) + def update_dnsmasq_config(self, context): + """Update the dnsmasq configuration""" + personalities = [constants.CONTROLLER] + config_uuid = self._config_update_hosts(context, personalities) + config_dict = { + "personalities": personalities, + "classes": ['platform::dns::dnsmasq::runtime'], + } + self._config_apply_runtime_manifest(context, config_uuid, config_dict) + + def update_ldap_client_config(self, context): + """Update the LDAP client configuration""" + personalities = [constants.CONTROLLER] + config_uuid = self._config_update_hosts(context, personalities) + config_dict = { + "personalities": personalities, + "classes": ['platform::ldap::client::runtime'], + } + self._config_apply_runtime_manifest(context, config_uuid, config_dict) + def get_ceph_pools_config(self, context): return self._ceph.get_pools_config() diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py index 3b07e3a680..a3412185f2 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/rpcapi.py @@ -2186,3 +2186,25 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy): :param context: request context. """ return self.call(context, self.make_msg('get_restore_state')) + + def update_ldap_client_config(self, context): + """Synchronously, have a conductor configure LDAP client configureation + + Does the following tasks: + - Update puppet hiera configuration file and apply run time manifest. + + :param context: request context. + """ + return self.call(context, + self.make_msg('update_ldap_client_config')) + + def update_dnsmasq_config(self, context): + """Synchronously, have a conductor configure the DNS configuration + + Does the following tasks: + - Update puppet hiera configuration file and apply run time manifest. + + :param context: request context. + """ + return self.call(context, + self.make_msg('update_dnsmasq_config')) diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py index 2c4b262ad9..6798a014cb 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_network.py @@ -120,6 +120,14 @@ class NetworkTestCase(base.FunctionalTest, dbbase.BaseHostTestCase): hostnames, self.storage_subnet, constants.NETWORK_TYPE_STORAGE) + self._create_test_addresses( + hostnames, self.system_controller_subnet, + constants.NETWORK_TYPE_SYSTEM_CONTROLLER) + + self._create_test_addresses( + hostnames, self.system_controller_oam_subnet, + constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM) + class TestPostMixin(NetworkTestCase): @@ -175,6 +183,38 @@ class TestPostMixin(NetworkTestCase): self.assertIn("Network of type %s already exists." % network_type, response.json['error_message']) + def test_create_success_system_controller_oam(self): + self._create_test_host(constants.CONTROLLER) + m = mock.Mock() + update_dnsmasq_config = "sysinv.conductor.rpcapi." \ + "ConductorAPI." \ + "update_dnsmasq_config" + with mock.patch('sysinv.common.utils.is_initial_config_complete', + lambda: True), \ + mock.patch(update_dnsmasq_config, + m.update_dnsmasq_config): + self._test_create_network_success( + 'system-controller-oam', + constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM, + self.system_controller_oam_subnet) + m.update_dnsmasq_config.assert_called_once() + + def test_create_success_system_controller(self): + self._create_test_host(constants.CONTROLLER) + m = mock.Mock() + update_ldap_client_config = "sysinv.conductor.rpcapi." \ + "ConductorAPI." \ + "update_ldap_client_config" + with mock.patch('sysinv.common.utils.is_initial_config_complete', + lambda: True), \ + mock.patch(update_ldap_client_config, + m.update_ldap_client_config): + self._test_create_network_success( + 'system-controller', + constants.NETWORK_TYPE_SYSTEM_CONTROLLER, + self.system_controller_subnet) + m.update_ldap_client_config.assert_called_once() + def test_create_success_pxeboot(self): self._test_create_network_success( 'pxeboot', diff --git a/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_manager.py b/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_manager.py index 6c47c1882d..4ed5c4b4ab 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_manager.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_manager.py @@ -1901,6 +1901,44 @@ class ManagerTestCase(base.DbTestCase): ihost = self.dbapi.ihost_get(host_id) self.assertEqual(ihost.reboot_needed, True) + def test_update_dnsmasq_config(self): + mock_config_update_hosts = mock.MagicMock() + mock_config_apply_runtime_manifest = mock.MagicMock() + p = mock.patch('sysinv.conductor.manager.ConductorManager._config_update_hosts', + mock_config_update_hosts) + p.start().return_value = '1234' + self.addCleanup(p.stop) + p2 = mock.patch('sysinv.conductor.manager.ConductorManager._config_apply_runtime_manifest', + mock_config_apply_runtime_manifest) + p2.start() + self.addCleanup(p2.stop) + self.service.update_dnsmasq_config(self.context) + personalities = [constants.CONTROLLER] + config_dict = { + "personalities": personalities, + "classes": ['platform::dns::dnsmasq::runtime'], + } + mock_config_apply_runtime_manifest.assert_called_with(mock.ANY, '1234', config_dict) + + def test_update_ldap_client_config(self): + mock_config_update_hosts = mock.MagicMock() + mock_config_apply_runtime_manifest = mock.MagicMock() + p = mock.patch('sysinv.conductor.manager.ConductorManager._config_update_hosts', + mock_config_update_hosts) + p.start().return_value = '1234' + self.addCleanup(p.stop) + p2 = mock.patch('sysinv.conductor.manager.ConductorManager._config_apply_runtime_manifest', + mock_config_apply_runtime_manifest) + p2.start() + self.addCleanup(p2.stop) + self.service.update_ldap_client_config(self.context) + personalities = [constants.CONTROLLER] + config_dict = { + "personalities": personalities, + "classes": ['platform::ldap::client::runtime'], + } + mock_config_apply_runtime_manifest.assert_called_with(mock.ANY, '1234', config_dict) + class ManagerTestCaseInternal(base.BaseHostTestCase): diff --git a/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_rpcapi.py b/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_rpcapi.py index 7983d8bda0..18f6062255 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_rpcapi.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/conductor/test_rpcapi.py @@ -95,3 +95,9 @@ class RPCAPITestCase(base.DbTestCase): 'call', host=self.fake_ihost, do_worker_apply=False) + + def test_update_ldap_client_config(self): + self._test_rpcapi('update_ldap_client_config', 'call') + + def test_update_dnsmasq_config(self): + self._test_rpcapi('update_dnsmasq_config', 'call') diff --git a/sysinv/sysinv/sysinv/sysinv/tests/db/base.py b/sysinv/sysinv/sysinv/sysinv/tests/db/base.py index af2c89e3d6..225d886791 100644 --- a/sysinv/sysinv/sysinv/sysinv/tests/db/base.py +++ b/sysinv/sysinv/sysinv/sysinv/tests/db/base.py @@ -48,6 +48,8 @@ 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') + system_controller_subnet = netaddr.IPNetwork('192.168.104.0/24') + system_controller_oam_subnet = netaddr.IPNetwork('10.10.50.0/24') nameservers = ['8.8.8.8', '8.8.4.4'] @@ -65,6 +67,8 @@ 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') + system_controller_subnet = netaddr.IPNetwork('fd07::/64') + system_controller_oam_subnet = netaddr.IPNetwork('fd06::/64') nameservers = ['2001:4860:4860::8888', '2001:4860:4860::8844'] @@ -240,6 +244,14 @@ class BaseSystemTestCase(BaseIPv4Mixin, DbTestCase): constants.NETWORK_TYPE_STORAGE, self.storage_subnet) + self._create_test_network('system-controller', + constants.NETWORK_TYPE_SYSTEM_CONTROLLER, + self.system_controller_subnet) + + self._create_test_network('system-controller-oam', + constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM, + self.system_controller_oam_subnet) + def _create_test_addresses(self, hostnames, subnet, network_type, start=1, stop=None): ips = itertools.islice(subnet, start, stop) @@ -286,6 +298,14 @@ class BaseSystemTestCase(BaseIPv4Mixin, DbTestCase): hostnames, self.storage_subnet, constants.NETWORK_TYPE_STORAGE) + self._create_test_addresses( + hostnames, self.system_controller_subnet, + constants.NETWORK_TYPE_SYSTEM_CONTROLLER) + + self._create_test_addresses( + hostnames, self.system_controller_oam_subnet, + constants.NETWORK_TYPE_SYSTEM_CONTROLLER_OAM) + def _create_test_oam(self): self.oam = dbutils.create_test_oam()