From b34da2223c185c576949637a782e0fb8e71ef4aa Mon Sep 17 00:00:00 2001 From: Xinyuan Huang Date: Wed, 10 Jun 2015 01:13:18 +0800 Subject: [PATCH] Add util method for getting rack config into scheduler from a config file This adds a utility module to solver-scheduler's solver directory, together with a method to get rack configurations from an external config file. This will be needed by rack based constraints for solver scheduler. Change-Id: I2f81fc47f374cb90d19dc1a1cafcb3beb76d4bb5 --- .../scheduler/solvers/utils.py | 87 +++++++++++++++++++ .../tests/scheduler/solvers/test_utils.py | 69 +++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 nova_solverscheduler/scheduler/solvers/utils.py create mode 100644 nova_solverscheduler/tests/scheduler/solvers/test_utils.py diff --git a/nova_solverscheduler/scheduler/solvers/utils.py b/nova_solverscheduler/scheduler/solvers/utils.py new file mode 100644 index 0000000..180c325 --- /dev/null +++ b/nova_solverscheduler/scheduler/solvers/utils.py @@ -0,0 +1,87 @@ +# Copyright 2015 Cisco Systems, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Utility methods for scheduler solvers.""" + +import os.path + +from oslo_config import cfg +from oslo_log import log as logging + +from nova.i18n import _LE + +rack_config_file_opts = [ + cfg.StrOpt('rack_config', + default='', + help='The config file specifying physical rack configuration. By ' + 'default Cisco\'s Neutron ML2 plugin config is supported, ' + 'otherwise the format of the config file needs to be ' + 'compatible with Cisco\'s Neutron ML2 plugin config file.'), + cfg.StrOpt('rack_config_prefix', + default='', + help='The section name in rack config file should start with the ' + 'prefix, so the config parser can recognize the section as ' + 'information of a specific rack or ToR switch. For example, ' + 'in Cisco\'s Neutron ML2 plugin config a section name like ' + '[ml2_mech_cisco_nexus:1.1.1.1] identifies a specific ToR ' + 'switch, then the prefix is \'ml2_mech_cisco_nexus\'.') +] + +CONF = cfg.CONF +CONF.register_opts(rack_config_file_opts) + +LOG = logging.getLogger(__name__) + + +def get_host_racks_config(): + """Read the rack config file to get physical rack configurations.""" + # Example section in the file: + # [ml2_mech_cisco_nexus:1.1.1.1] + # compute1=1/1 + # compute2=1/2 + # ... + + host_racks_map = {} + sections = {} + + filepath = CONF.rack_config + if not filepath: + return host_racks_map + + if not os.path.exists(filepath): + LOG.error(_LE("The rack config file is not found: %s"), filepath) + return host_racks_map + + prefix = CONF.rack_config_prefix + if not prefix: + LOG.error(_LE("Rack config prefix is not set.")) + return host_racks_map + + try: + rack_config_parser = cfg.ConfigParser(filepath, sections) + rack_config_parser.parse() + + for section_name in sections.keys(): + if section_name.startswith(prefix): + # section_name: rack id + for key, value in sections[section_name].items(): + # key: host name, value: port id + host_racks_map.setdefault(key, set([])) + host_racks_map[key].add(section_name) + except Exception as e: + LOG.error(_LE("The rack config file is not parsed properly: %s"), + str(e)) + + return host_racks_map diff --git a/nova_solverscheduler/tests/scheduler/solvers/test_utils.py b/nova_solverscheduler/tests/scheduler/solvers/test_utils.py new file mode 100644 index 0000000..e126b68 --- /dev/null +++ b/nova_solverscheduler/tests/scheduler/solvers/test_utils.py @@ -0,0 +1,69 @@ +# Copyright 2015 Cisco Systems, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os.path +import tempfile + +from oslo_config import cfg + +from nova import test +from nova_solverscheduler.scheduler.solvers import utils + +CONF = cfg.CONF + + +class TestRackConfigLoader(test.NoDBTestCase): + """Test case for rack config file loading.""" + + _rack_config = ''' +[ml2_mech_cisco_nexus:1.1.1.1] +compute1=1/1 +compute2=1/2 +[other-section] +k=bla + ''' + + def setUp(self): + super(TestRackConfigLoader, self).setUp() + self.config = tempfile.NamedTemporaryFile(mode="w+t") + self.config.write(self._rack_config.lstrip()) + self.config.seek(0) + self.config.flush() + + self.config_path = None + if not os.path.isabs(self.config.name): + self.config_path = CONF.find_file(self.config.name) + elif os.path.exists(self.config.name): + self.config_path = self.config.name + + def test_load_rack_config_happy_day(self): + self.flags(rack_config=self.config_path, + rack_config_prefix='ml2_mech_cisco_nexus') + rackcfg = utils.get_host_racks_config() + + ref_rackcfg = { + 'compute1': set(['ml2_mech_cisco_nexus:1.1.1.1']), + 'compute2': set(['ml2_mech_cisco_nexus:1.1.1.1']) + } + self.assertEqual(ref_rackcfg, rackcfg) + + def test_load_rack_config_no_found(self): + self.flags(rack_config='non_existing_file') + rackcfg = utils.get_host_racks_config() + self.assertEqual({}, rackcfg) + + def tearDown(self): + self.config.close() + super(TestRackConfigLoader, self).tearDown()