From 39330d3bba021b3792eb5760e01daf3b527b1dba Mon Sep 17 00:00:00 2001 From: Sandhya Dasu Date: Tue, 7 Mar 2017 11:04:57 -0500 Subject: [PATCH] Accept ucsm_host_list as part of Multi-UCSM config The plugin has been modified to parse ucsm_host_list specified as part of Multi-UCSM config. If that config is missing, it will try to read Service Profile information directly from the UCSM. Change-Id: Idd306d994bebbec2ef4ecf625bfea47be057c1ce Closes-Bug: #1648849 --- etc/neutron/plugins/ml2/ml2_conf_cisco.ini | 5 ++ .../plugins/ml2/drivers/cisco/ucsm/config.py | 68 ++++++++++--------- .../drivers/cisco/ucsm/ucsm_network_driver.py | 11 ++- .../cisco/ucsm/test_cisco_ucsm_common.py | 9 +++ .../cisco/ucsm/test_cisco_ucsm_driver.py | 14 ++-- 5 files changed, 70 insertions(+), 37 deletions(-) diff --git a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini index 82b687e..85e6ee0 100644 --- a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini +++ b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini @@ -225,6 +225,11 @@ # ucsm_password = password # ucsm_virtio_eth_ports = eth0, eth1 +# Hostname to Service Profile mapping for Compute hosts managed by +# this UCS Manager. This config should be specified for hosts configured +# with only Service Profiles and not Service Profile Templates. +# ucsm_host_list=Hostname1:Serviceprofile1, Hostname2:Serviceprofile2 + # Service Profile Template config per UCSM. This is a mapping of Service Profile # Template to the list of UCS Servers controlled by this template. # sp_template_list = SP_Template1_path:SP_Template1:S1,S2 SP_Template2_path:SP_Template2:S3,S4 diff --git a/networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py b/networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py index bcc4500..71f1ca3 100644 --- a/networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py +++ b/networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import debtcollector - from oslo_config import cfg from oslo_log import log as logging @@ -82,38 +80,35 @@ def parse_pci_vendor_config(): return vendor_list -@debtcollector.removals.remove(message=DEPRECATION_MESSAGE) -def parse_ucsm_host_config(): +def parse_ucsm_host_config(ucsm_ip, ucsm_host_list): sp_dict = {} host_dict = {} - if cfg.CONF.ml2_cisco_ucsm.ucsm_host_list: - host_config_list = cfg.CONF.ml2_cisco_ucsm.ucsm_host_list - for host in host_config_list: - hostname, sep, service_profile = host.partition(':') - if not sep or not service_profile: - raise cfg.Error(_("UCS Mech Driver: Invalid Host Service " - "Profile config: %s") % host) - key = (cfg.CONF.ml2_cisco_ucsm.ucsm_ip, hostname) - if '/' not in service_profile: - # Assuming the service profile is at the root level - # and the path is not specified. This option - # allows backward compatability with earlier config - # format - sp_dict[key] = (const.SERVICE_PROFILE_PATH_PREFIX + - service_profile.strip()) - else: - # Assuming the complete path to Service Profile has - # been provided in the config. The Service Profile - # could be in an sub-org. - sp_dict[key] = service_profile.strip() + for host in ucsm_host_list: + host = host.strip() + hostname, sep, service_profile = host.partition(':') + if not sep or not service_profile: + raise cfg.Error(_("UCS Mech Driver: Invalid Host Service " + "Profile config: %s") % host) + key = (ucsm_ip, hostname) + if '/' not in service_profile: + # Assuming the service profile is at the root level + # and the path is not specified. This option + # allows backward compatability with earlier config + # format + sp_dict[key] = (const.SERVICE_PROFILE_PATH_PREFIX + + service_profile.strip()) + else: + # Assuming the complete path to Service Profile has + # been provided in the config. The Service Profile + # could be in an sub-org. + sp_dict[key] = service_profile.strip() - LOG.debug('Service Profile for %s is %s', - hostname, sp_dict.get(key)) - host_dict[hostname] = cfg.CONF.ml2_cisco_ucsm.ucsm_ip - return sp_dict, host_dict + LOG.debug('Service Profile for %s is %s', + hostname, sp_dict.get(key)) + host_dict[hostname] = ucsm_ip + return sp_dict, host_dict -@debtcollector.removals.remove(message=DEPRECATION_MESSAGE) def parse_virtio_eth_ports(): eth_port_list = [] if not cfg.CONF.ml2_cisco_ucsm.ucsm_virtio_eth_ports: @@ -130,6 +125,8 @@ def parse_virtio_eth_ports(): class UcsmConfig(object): """ML2 Cisco UCSM Mechanism Driver Configuration class.""" ucsm_dict = {} + ucsm_sp_dict = {} + ucsm_host_dict = {} ucsm_port_dict = {} sp_template_dict = {} vnic_template_dict = {} @@ -149,7 +146,6 @@ class UcsmConfig(object): raise cfg.Error(_('Insufficient UCS Manager configuration has ' 'been provided to the plugin')) - @debtcollector.removals.remove(message=DEPRECATION_MESSAGE) def _create_single_ucsm_dicts(self): """Creates a dictionary of UCSM data for 1 UCS Manager.""" ucsm_info = [] @@ -166,6 +162,8 @@ class UcsmConfig(object): """Creates a dictionary of all UCS Manager data from config.""" username = None password = None + local_sp_dict = {} + local_host_dict = {} multi_parser = cfg.MultiConfigParser() read_ok = multi_parser.read(cfg.CONF.config_file) @@ -181,7 +179,13 @@ class UcsmConfig(object): eth_port_list = [] for dev_key, value in parsed_file[parsed_item].items(): config_item = dev_key.lower() - if config_item == 'ucsm_virtio_eth_ports': + if config_item == 'ucsm_host_list': + local_sp_dict, local_host_dict = ( + parse_ucsm_host_config(dev_ip, + value[0].split(','))) + self.ucsm_sp_dict.update(local_sp_dict) + self.ucsm_host_dict.update(local_host_dict) + elif config_item == 'ucsm_virtio_eth_ports': for eth_port in value[0].split(','): eth_port_list.append( const.ETH_PREFIX + str(eth_port).strip()) @@ -197,7 +201,7 @@ class UcsmConfig(object): self.sriov_qos_policy[dev_ip] = value[0].strip() elif dev_key.lower() == 'ucsm_username': username = value[0].strip() - else: + elif dev_key.lower() == 'ucsm_password': password = value[0].strip() ucsm_info = (username, password) self.ucsm_dict[dev_ip] = ucsm_info diff --git a/networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_network_driver.py b/networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_network_driver.py index f98cdc1..d094635 100644 --- a/networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_network_driver.py +++ b/networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_network_driver.py @@ -104,10 +104,19 @@ class CiscoUcsmDriver(object): # Check if Service Profile to Hostname mapping config has been provided if cfg.CONF.ml2_cisco_ucsm.ucsm_host_list: self.ucsm_sp_dict, self.ucsm_host_dict = ( - config.parse_ucsm_host_config()) + config.parse_ucsm_host_config( + cfg.CONF.ml2_cisco_ucsm.ucsm_ip, + cfg.CONF.ml2_cisco_ucsm.ucsm_host_list)) + elif self.ucsm_conf.multi_ucsm_mode: + self.ucsm_sp_dict.update(self.ucsm_conf.ucsm_sp_dict) + self.ucsm_host_dict.update(self.ucsm_conf.ucsm_host_dict) else: self._create_ucsm_host_to_service_profile_mapping() + if not self.ucsm_sp_dict: + LOG.error(_LE('UCS Manager network driver failed to get Service ' + 'Profile information for any of its nodes.')) + @contextmanager def ucsm_connect_disconnect(self, ucsm_ip): handle = self.ucs_manager_connect(ucsm_ip) diff --git a/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_common.py b/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_common.py index 2690b2e..5812301 100644 --- a/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_common.py +++ b/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_common.py @@ -29,6 +29,7 @@ UCSM_IP_ADDRESS_1 = '1.1.1.1' UCSM_USERNAME_1 = 'username1' UCSM_PASSWORD_1 = 'password1' UCSM_VIRTIO_ETH_PORTS_1 = ['eth0, eth1'] +UCSM_HOST_LIST_1 = ['UCS-1:UCS-1-SP, UCS-2:org-root/test/ls-UCS-2-SP'] UCSM_IP_ADDRESS_2 = '2.2.2.2' UCSM_USERNAME_2 = 'username2' @@ -63,6 +64,7 @@ class ConfigMixin(object): 'ml2_cisco_ucsm_ip: 1.1.1.1': { 'ucsm_username': [UCSM_USERNAME_1], 'ucsm_password': [UCSM_PASSWORD_1], + 'ucsm_host_list': UCSM_HOST_LIST_1, 'ucsm_virtio_eth_ports': UCSM_VIRTIO_ETH_PORTS_1, 'vnic_template_list': ['test-physnet:org-root:Test-VNIC'], 'sriov_qos_policy': ['Test'] @@ -103,6 +105,11 @@ class ConfigMixin(object): 507, 508, 509, 700] } + expected_sp_dict = { + ('1.1.1.1', 'UCS-1'): ('org-root/ls-UCS-1-SP'), + ('1.1.1.1', 'UCS-2'): ('org-root/test/ls-UCS-2-SP'), + } + self.mocked_parser = mock.patch.object(cfg, 'MultiConfigParser').start() self.mocked_parser.return_value.read.return_value = [ucsm_test_config] @@ -118,3 +125,5 @@ class ConfigMixin(object): ucsm_config.UcsmConfig.sriov_qos_policy) self.assertEqual(expected_multivlan_trunk_dict, ucsm_config.UcsmConfig.multivlan_trunk_dict) + self.assertEqual(expected_sp_dict, + ucsm_config.UcsmConfig.ucsm_sp_dict) diff --git a/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_driver.py b/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_driver.py index d066a89..272546a 100644 --- a/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_driver.py +++ b/networking_cisco/tests/unit/ml2/drivers/cisco/ucsm/test_cisco_ucsm_driver.py @@ -762,7 +762,9 @@ class TestCiscoUcsmMechDriver(testlib_api.SqlTestCase, expected_ip = '1.1.1.1' expected_sp1 = "org-root/ls-SP1" expected_sp2 = "org-root/ls-SP2" - ucsm_sp_dict, ucsm_host_dict = conf.parse_ucsm_host_config() + ucsm_sp_dict, ucsm_host_dict = conf.parse_ucsm_host_config( + cfg.CONF.ml2_cisco_ucsm.ucsm_ip, + cfg.CONF.ml2_cisco_ucsm.ucsm_host_list) key = (cfg.CONF.ml2_cisco_ucsm.ucsm_ip, 'Host1') self.assertIn(key, ucsm_sp_dict) @@ -783,11 +785,13 @@ class TestCiscoUcsmMechDriver(testlib_api.SqlTestCase, """Verifies malformed ucsm_host_list raises an error.""" cfg.CONF.ml2_cisco_ucsm.ucsm_host_list = ['Host1:', 'Host2:SP2'] self.assertRaisesRegex(cfg.Error, "Host1:", - conf.parse_ucsm_host_config) + conf.parse_ucsm_host_config, UCSM_IP_ADDRESS_1, + cfg.CONF.ml2_cisco_ucsm.ucsm_host_list) cfg.CONF.ml2_cisco_ucsm.ucsm_host_list = ['Host1:SP1', 'Host2'] self.assertRaisesRegex(cfg.Error, "Host2", - conf.parse_ucsm_host_config) + conf.parse_ucsm_host_config, UCSM_IP_ADDRESS_1, + cfg.CONF.ml2_cisco_ucsm.ucsm_host_list) def test_parse_virtio_eth_ports(self): cfg.CONF.ml2_cisco_ucsm.ucsm_virtio_eth_ports = ['test-eth1', @@ -804,7 +808,9 @@ class TestCiscoUcsmMechDriver(testlib_api.SqlTestCase, cfg.CONF.ml2_cisco_ucsm.ucsm_host_list = ['Host1:SP1', 'Host2:org-root/sub-org1/ls-SP2'] - ucsm_sp_dict, ucsm_host_dict = conf.parse_ucsm_host_config() + ucsm_sp_dict, ucsm_host_dict = conf.parse_ucsm_host_config( + cfg.CONF.ml2_cisco_ucsm.ucsm_ip, + cfg.CONF.ml2_cisco_ucsm.ucsm_host_list) key = ('1.1.1.1', 'Host1') actual_service_profile1 = ucsm_sp_dict.get(key)