aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Prince <dprince@redhat.com>2014-08-20 13:10:40 -0400
committerDan Prince <dprince@redhat.com>2014-08-20 13:10:40 -0400
commitb3e74611befb157dc4bf992f23aaba346e655a86 (patch)
tree30807f86f1e54e10576780611772ec790807f729
parent2d3af95651223ca142e99c9c2bbe284481e3f33d (diff)
Add nic1, nic2 naming abstraction
Implements a new active NIC abstraction and naming convention that allows nic1, nic2, etc. to be translated to actual (active) network device names like em1, em2 (or eth0, eth1). This includes some logic to map ordered active nics to the nic1, nic2 naming scheme. Embedded nics are always listed first (in sort order) followed by any other active Nics on the system. With the new code: {"type": "interface", "name": "nic1" } is automatically translated (internally) to: {"type": "interface", "name": "em1" } This works for all top level "interface" devices, vlans, bonds, and bridges alike. For vlans the 'device' name is translated instead of the device name per vlan object conventions.
-rw-r--r--os_net_config/objects.py34
-rw-r--r--os_net_config/tests/base.py7
-rw-r--r--os_net_config/tests/test_objects.py75
-rw-r--r--os_net_config/tests/test_utils.py47
-rw-r--r--os_net_config/utils.py49
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)