diff options
-rw-r--r-- | os_net_config/objects.py | 34 | ||||
-rw-r--r-- | os_net_config/tests/base.py | 7 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 75 | ||||
-rw-r--r-- | os_net_config/tests/test_utils.py | 47 | ||||
-rw-r--r-- | os_net_config/utils.py | 49 |
5 files changed, 206 insertions, 6 deletions
diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 14ecfc6..b73122c 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -14,9 +14,17 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import netaddr from openstack.common import strutils +from os_net_config import utils + + +logger = logging.getLogger(__name__) + +_NUMBERED_NICS = None + class InvalidConfigException(ValueError): pass @@ -43,6 +51,19 @@ def _get_required_field(json, name, object_name): return field +def _numbered_nics(): + global _NUMBERED_NICS + if _NUMBERED_NICS: + return _NUMBERED_NICS + _NUMBERED_NICS = {} + count = 0 + for nic in utils.ordered_active_nics(): + count += 1 + _NUMBERED_NICS["nic%i" % count] = nic + logger.info("nic%i mapped to: %s" % (count, nic)) + return _NUMBERED_NICS + + class Route(object): """Base class for network routes.""" @@ -80,7 +101,11 @@ class _BaseOpts(object): def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], routes=[], mtu=1500, primary=False): - self.name = name + numbered_nic_names = _numbered_nics() + if name in numbered_nic_names: + self.name = numbered_nic_names[name] + else: + self.name = name self.mtu = mtu self.use_dhcp = use_dhcp self.use_dhcpv6 = use_dhcpv6 @@ -171,7 +196,12 @@ class Vlan(_BaseOpts): super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary) self.vlan_id = int(vlan_id) - self.device = device + + numbered_nic_names = _numbered_nics() + if device in numbered_nic_names: + self.device = numbered_nic_names[device] + else: + self.device = device @staticmethod def from_json(json): diff --git a/os_net_config/tests/base.py b/os_net_config/tests/base.py index c8ad7c4..1e87ba2 100644 --- a/os_net_config/tests/base.py +++ b/os_net_config/tests/base.py @@ -21,6 +21,8 @@ import fixtures import stubout import testtools +from os_net_config import objects + _TRUE_VALUES = ('True', 'true', '1', 'yes') @@ -33,6 +35,11 @@ class TestCase(testtools.TestCase): super(TestCase, self).setUp() self.stubs = stubout.StubOutForTesting() + + def test_numbered_nics(): + return {} + self.stubs.Set(objects, '_numbered_nics', test_numbered_nics) + test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) try: test_timeout = int(test_timeout) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 830a809..2f43997 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -92,6 +92,16 @@ class TestInterface(base.TestCase): self.assertEqual("em1", interface.name) self.assertEqual(True, interface.use_dhcp) + def test_from_json_dhcp_nic1(self): + def test_numbered_nics(): + return {"nic1": "em3"} + self.stubs.Set(objects, '_numbered_nics', test_numbered_nics) + + data = '{"type": "interface", "name": "nic1", "use_dhcp": true}' + interface = objects.object_from_json(json.loads(data)) + self.assertEqual("em3", interface.name) + self.assertEqual(True, interface.use_dhcp) + def test_from_json_with_addresses(self): data = """{ "type": "interface", @@ -130,6 +140,18 @@ class TestVlan(base.TestCase): self.assertEqual(16, vlan.vlan_id) self.assertEqual(True, vlan.use_dhcp) + def test_from_json_dhcp_nic1(self): + def test_numbered_nics(): + return {"nic1": "em4"} + self.stubs.Set(objects, '_numbered_nics', test_numbered_nics) + + data = '{"type": "vlan", "device": "nic1", "vlan_id": 16,' \ + '"use_dhcp": true}' + vlan = objects.object_from_json(json.loads(data)) + self.assertEqual("em4", vlan.device) + self.assertEqual(16, vlan.vlan_id) + self.assertEqual(True, vlan.use_dhcp) + class TestBridge(base.TestCase): @@ -152,6 +174,29 @@ class TestBridge(base.TestCase): self.assertEqual(True, interface1.ovs_port) self.assertEqual("br-foo", interface1.bridge_name) + def test_from_json_dhcp_with_nic1(self): + def test_numbered_nics(): + return {"nic1": "em5"} + self.stubs.Set(objects, '_numbered_nics', test_numbered_nics) + + data = """{ +"type": "ovs_bridge", +"name": "br-foo", +"use_dhcp": true, +"members": [{ + "type": "interface", + "name": "nic1" +}] +} +""" + bridge = objects.object_from_json(json.loads(data)) + self.assertEqual("br-foo", bridge.name) + self.assertEqual(True, bridge.use_dhcp) + interface1 = bridge.members[0] + self.assertEqual("em5", interface1.name) + self.assertEqual(True, interface1.ovs_port) + self.assertEqual("br-foo", interface1.bridge_name) + def test_from_json_primary_interface(self): data = """{ "type": "ovs_bridge", @@ -210,3 +255,33 @@ class TestBond(base.TestCase): self.assertEqual("em1", interface1.name) interface2 = bridge.members[1] self.assertEqual("em2", interface2.name) + + def test_from_json_dhcp_with_nic1_nic2(self): + + def test_numbered_nics(): + return {"nic1": "em1", "nic2": "em2"} + self.stubs.Set(objects, '_numbered_nics', test_numbered_nics) + + data = """{ +"type": "ovs_bond", +"name": "bond1", +"use_dhcp": true, +"members": [ + { + "type": "interface", + "name": "nic1" + }, + { + "type": "interface", + "name": "nic2" + } +] +} +""" + bridge = objects.object_from_json(json.loads(data)) + self.assertEqual("bond1", bridge.name) + self.assertEqual(True, bridge.use_dhcp) + interface1 = bridge.members[0] + self.assertEqual("em1", interface1.name) + interface2 = bridge.members[1] + self.assertEqual("em2", interface2.name) diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py new file mode 100644 index 0000000..e7326d3 --- /dev/null +++ b/os_net_config/tests/test_utils.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +# Copyright 2014 Red Hat, Inc. +# +# 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 shutil +import tempfile + +from os_net_config.tests import base +from os_net_config import utils + + +class TestUtils(base.TestCase): + + def test_ordered_active_nics(self): + + tmpdir = tempfile.mkdtemp() + self.stubs.Set(utils, '_SYS_CLASS_NET', tmpdir) + + def test_is_active_nic(interface_name): + return True + self.stubs.Set(utils, '_is_active_nic', test_is_active_nic) + + for nic in ['a1', 'em1', 'em2', 'eth2', 'z1']: + with open(os.path.join(tmpdir, nic), 'w') as f: + f.write(nic) + + nics = utils.ordered_active_nics() + self.assertEqual('em1', nics[0]) + self.assertEqual('em2', nics[1]) + self.assertEqual('eth2', nics[2]) + self.assertEqual('a1', nics[3]) + self.assertEqual('z1', nics[4]) + + shutil.rmtree(tmpdir) diff --git a/os_net_config/utils.py b/os_net_config/utils.py index cd7ee65..e51d197 100644 --- a/os_net_config/utils.py +++ b/os_net_config/utils.py @@ -14,35 +14,76 @@ # License for the specific language governing permissions and limitations # under the License. +import glob import logging logger = logging.getLogger(__name__) +_SYS_CLASS_NET = '/sys/class/net' def write_config(filename, data): - with open(filename, "w") as f: + with open(filename, 'w') as f: f.write(str(data)) def get_file_data(filename): try: - with open(filename, "r") as f: + with open(filename, 'r') as f: return f.read() except IOError: logger.error("Error reading file: %s" % filename) - return "" + return '' def interface_mac(name): try: - with open('/sys/class/net/%s/address' % name, "r") as f: + with open('/sys/class/net/%s/address' % name, 'r') as f: return f.read().rstrip() except IOError: logger.error("Unable to read file: %s" % name) raise +def _is_active_nic(interface_name): + try: + if interface_name == 'lo': + return False + + addr_assign_type = None + with open(_SYS_CLASS_NET + '/%s/addr_assign_type' % interface_name, + 'r') as f: + addr_assign_type = int(f.read().rstrip()) + + carrier = None + with open(_SYS_CLASS_NET + '/%s/carrier' % interface_name, 'r') as f: + carrier = int(f.read().rstrip()) + + address = None + with open(_SYS_CLASS_NET + '/%s/address' % interface_name, 'r') as f: + address = f.read().rstrip() + + if addr_assign_type == 0 and carrier == 1 and address: + return True + else: + return False + except IOError: + return False + + +def ordered_active_nics(): + embedded_nics = [] + nics = [] + for name in glob.iglob(_SYS_CLASS_NET + '/*'): + nic = name[(len(_SYS_CLASS_NET) + 1):] + if _is_active_nic(nic): + if nic.startswith('em') or nic.startswith('eth'): + embedded_nics.append(nic) + else: + nics.append(nic) + return sorted(embedded_nics) + sorted(nics) + + def diff(filename, data): file_data = get_file_data(filename) logger.debug("Diff file data:\n%s" % file_data) |