aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Sneddon <dsneddon@redhat.com>2016-04-13 01:17:16 -0700
committerDan Sneddon <dsneddon@redhat.com>2016-07-05 10:27:45 -0700
commit78e1b65d93a1c16d58fd72cb65f8e2adf1762854 (patch)
tree150a8e2a68c41b0bd76dc17829181e4b4842b1de
parent6bb8412ef3d3f163d91f2884081b743f07a78f18 (diff)
Add support for Infiniband interfaces
This patch adds support for Infiniband interfaces. The only difference between Inifiniband and regular interfaces at this time is that an interface with type "ib_interface" will have "TYPE=Infiniband" added to the ifcfg file. However, the Infiniband interface is implemented as a full new class, so in the future we can add script functions or additional config options to the Infiniband interface config if needed. Unit tests for both the object and the ifcfg implementation are included. This patch does not include an implementation for systems that use /etc/network/interfaces (Debian-based systems). Note that this change has not yet been tested on bare metal with Infiniband hardware. Fixes bug: https://bugzilla.redhat.com/show_bug.cgi?id=1326616 Change-Id: Iaeaca9cd71e2cea6147951d49aecc7458be4ca0b
-rw-r--r--etc/os-net-config/samples/ib_interface.json26
-rw-r--r--etc/os-net-config/samples/ib_interface.yaml18
-rw-r--r--os_net_config/__init__.py9
-rw-r--r--os_net_config/impl_ifcfg.py46
-rw-r--r--os_net_config/objects.py24
-rw-r--r--os_net_config/tests/test_impl_ifcfg.py51
-rw-r--r--os_net_config/tests/test_objects.py89
7 files changed, 263 insertions, 0 deletions
diff --git a/etc/os-net-config/samples/ib_interface.json b/etc/os-net-config/samples/ib_interface.json
new file mode 100644
index 0000000..4e42867
--- /dev/null
+++ b/etc/os-net-config/samples/ib_interface.json
@@ -0,0 +1,26 @@
+{"network_config": [
+ {
+ "type": "ib_interface",
+ "name": "ib0",
+ "use_dhcp": false,
+ "addresses": [
+ {
+ "ip_netmask": "192.0.2.1/24"
+ }
+ ],
+ "routes": [
+ {
+ "ip_netmask": "0.0.0.0/0",
+ "next_hop": "192.0.2.254",
+ "default": "true"
+ }
+ ]
+ },
+ {
+ "type": "ib_interface",
+ "name": "ib1",
+ "use_dhcp": true,
+ "defroute": no
+ }
+ ]
+}
diff --git a/etc/os-net-config/samples/ib_interface.yaml b/etc/os-net-config/samples/ib_interface.yaml
new file mode 100644
index 0000000..f930471
--- /dev/null
+++ b/etc/os-net-config/samples/ib_interface.yaml
@@ -0,0 +1,18 @@
+network_config:
+ -
+ type: ib_interface
+ name: ib0
+ use_dhcp: false
+ addresses:
+ -
+ ip_netmask: 192.0.2.1/24
+ routes:
+ -
+ ip_netmask: 0.0.0.0/0
+ next_hop: 192.0.2.254
+ default: true
+ -
+ type: interface
+ name: ib1
+ use_dhcp: true
+ defroute: no \ No newline at end of file
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py
index 88e8900..fc1a38a 100644
--- a/os_net_config/__init__.py
+++ b/os_net_config/__init__.py
@@ -75,6 +75,8 @@ class NetConfig(object):
self.add_ovs_tunnel(obj)
elif isinstance(obj, objects.OvsPatchPort):
self.add_ovs_patch_port(obj)
+ elif isinstance(obj, objects.IbInterface):
+ self.add_ib_interface(obj)
def add_interface(self, interface):
"""Add an Interface object to the net config object.
@@ -139,6 +141,13 @@ class NetConfig(object):
"""
raise NotImplemented("add_ovs_patch_port is not implemented.")
+ def add_ib_interface(self, ib_interface):
+ """Add an InfiniBand Interface object to the net config object.
+
+ :param interface: The InfiniBand Interface object to add.
+ """
+ raise NotImplemented("add_ib_interface is not implemented.")
+
def apply(self, cleanup=False):
"""Apply the network configuration.
diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py
index fbd1c3a..22aef22 100644
--- a/os_net_config/impl_ifcfg.py
+++ b/os_net_config/impl_ifcfg.py
@@ -64,6 +64,7 @@ class IfcfgNetConfig(os_net_config.NetConfig):
self.bridge_data = {}
self.linuxbridge_data = {}
self.linuxbond_data = {}
+ self.ib_interface_data = {}
self.member_names = {}
self.renamed_interfaces = {}
self.bond_primary_ifaces = {}
@@ -101,6 +102,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
elif isinstance(base_opt, objects.IvsInterface):
data += "TYPE=IVSIntPort\n"
+ elif isinstance(base_opt, objects.IbInterface):
+ data += "TYPE=Infiniband\n"
elif re.match('\w+\.\d+$', base_opt.name):
data += "VLAN=yes\n"
if base_opt.linux_bond_name:
@@ -391,6 +394,23 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.debug('ovs patch port data: %s' % data)
self.interface_data[ovs_patch_port.name] = data
+ def add_ib_interface(self, ib_interface):
+ """Add an InfiniBand interface object to the net config object.
+
+ :param ib_interface: The InfiniBand interface object to add.
+ """
+ logger.info('adding ib_interface: %s' % ib_interface.name)
+ data = self._add_common(ib_interface)
+ logger.debug('ib_interface data: %s' % data)
+ self.ib_interface_data[ib_interface.name] = data
+ if ib_interface.routes:
+ self._add_routes(ib_interface.name, ib_interface.routes)
+
+ if ib_interface.renamed:
+ logger.info("InfiniBand interface %s being renamed to %s"
+ % (ib_interface.hwname, ib_interface.name))
+ self.renamed_interfaces[ib_interface.hwname] = ib_interface.name
+
def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
"""Generate configuration content for ivs."""
@@ -562,6 +582,32 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('No changes required for linux bond: %s' %
bond_name)
+ # Infiniband interfaces are handled similarly to Ethernet interfaces
+ for interface_name, iface_data in self.ib_interface_data.iteritems():
+ route_data = self.route_data.get(interface_name, '')
+ route6_data = self.route6_data.get(interface_name, '')
+ interface_path = self.root_dir + ifcfg_config_path(interface_name)
+ route_path = self.root_dir + route_config_path(interface_name)
+ route6_path = self.root_dir + route6_config_path(interface_name)
+ all_file_names.append(interface_path)
+ all_file_names.append(route_path)
+ all_file_names.append(route6_path)
+ # TODO(dsneddon) determine if InfiniBand can be used with IVS
+ if "IVS_BRIDGE" in iface_data:
+ ivs_uplinks.append(interface_name)
+ all_file_names.append(route6_path)
+ if (utils.diff(interface_path, iface_data) or
+ utils.diff(route_path, route_data) or
+ utils.diff(route6_path, route6_data)):
+ restart_interfaces.append(interface_name)
+ restart_interfaces.extend(self.child_members(interface_name))
+ update_files[interface_path] = iface_data
+ update_files[route_path] = route_data
+ update_files[route6_path] = route6_data
+ else:
+ logger.info('No changes required for InfiniBand iface: %s' %
+ interface_name)
+
if cleanup:
for ifcfg_file in glob.iglob(cleanup_pattern()):
if ifcfg_file not in all_file_names:
diff --git a/os_net_config/objects.py b/os_net_config/objects.py
index 9815832..289a0da 100644
--- a/os_net_config/objects.py
+++ b/os_net_config/objects.py
@@ -52,6 +52,8 @@ def object_from_json(json):
return OvsTunnel.from_json(json)
elif obj_type == "ovs_patch_port":
return OvsPatchPort.from_json(json)
+ elif obj_type == "ib_interface":
+ return IbInterface.from_json(json)
def _get_required_field(json, name, object_name):
@@ -689,3 +691,25 @@ class OvsPatchPort(_BaseOpts):
opts = _BaseOpts.base_opts_from_json(json)
return OvsPatchPort(name, *opts, bridge_name=bridge_name, peer=peer,
ovs_options=ovs_options, ovs_extra=ovs_extra)
+
+
+class IbInterface(_BaseOpts):
+ """Base class for InfiniBand network interfaces."""
+
+ def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
+ routes=None, mtu=None, primary=False, nic_mapping=None,
+ persist_mapping=False, defroute=True, dhclient_args=None,
+ dns_servers=None):
+ addresses = addresses or []
+ routes = routes or []
+ dns_servers = dns_servers or []
+ super(IbInterface, self).__init__(name, use_dhcp, use_dhcpv6,
+ addresses, routes, mtu, primary,
+ nic_mapping, persist_mapping,
+ defroute, dhclient_args, dns_servers)
+
+ @staticmethod
+ def from_json(json):
+ name = _get_required_field(json, 'name', 'IbInterface')
+ opts = _BaseOpts.base_opts_from_json(json)
+ return IbInterface(name, *opts)
diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py
index 71ad9ae..f30eb89 100644
--- a/os_net_config/tests/test_impl_ifcfg.py
+++ b/os_net_config/tests/test_impl_ifcfg.py
@@ -60,12 +60,33 @@ BOOTPROTO=none
_V4_IFCFG_MAPPED = _V4_IFCFG.replace('em1', 'nic1') + "HWADDR=a1:b2:c3:d4:e5\n"
+
+_BASE_IB_IFCFG = """# This file is autogenerated by os-net-config
+DEVICE=ib0
+ONBOOT=yes
+HOTPLUG=no
+NM_CONTROLLED=no
+PEERDNS=no
+TYPE=Infiniband
+"""
+
+_V4_IB_IFCFG = _BASE_IB_IFCFG + """BOOTPROTO=static
+IPADDR=192.168.1.2
+NETMASK=255.255.255.0
+"""
+
_V4_IFCFG_MULTIPLE = _V4_IFCFG + """IPADDR1=192.168.1.3
NETMASK1=255.255.255.255
IPADDR2=10.0.0.2
NETMASK2=255.0.0.0
"""
+_IB_V4_IFCFG_MULTIPLE = _V4_IB_IFCFG + """IPADDR1=192.168.1.3
+NETMASK1=255.255.255.255
+IPADDR2=10.0.0.2
+NETMASK2=255.0.0.0
+"""
+
_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
IPV6_AUTOCONF=no
IPV6ADDR=2001:abc:a::/64
@@ -505,6 +526,16 @@ class TestIfcfgNetConfig(base.TestCase):
data = self.provider.generate_ivs_config(['em1'], ['storage5'])
self.assertEqual(_IVS_CONFIG, data)
+ def test_add_ib_interface_with_v4_multiple(self):
+ addresses = [objects.Address('192.168.1.2/24'),
+ objects.Address('192.168.1.3/32'),
+ objects.Address('10.0.0.2/8')]
+ ib_interface = objects.IbInterface('ib0', addresses=addresses)
+ self.provider.add_interface(ib_interface)
+ self.assertEqual(_IB_V4_IFCFG_MULTIPLE,
+ self.get_interface_config('ib0'))
+ self.assertEqual('', self.get_route_config())
+
def test_add_vlan(self):
vlan = objects.Vlan('em1', 5)
self.provider.add_vlan(vlan)
@@ -775,6 +806,26 @@ class TestIfcfgNetConfigApply(base.TestCase):
self.provider.add_interface(interface)
self.provider.add_bridge(bridge)
self.provider.apply()
+ self.assertIn('em1', self.ifup_interface_names)
+
+ # test infiniband interfaces act as proper bridge members
+ ib_interface = objects.IbInterface('ib0')
+ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
+ members=[ib_interface])
+ self.provider.add_interface(ib_interface)
+ self.provider.add_bridge(bridge)
+ self.provider.apply()
+ self.assertIn('ib0', self.ifup_interface_names)
+ self.assertIn('br-ctlplane', self.ifup_interface_names)
+
+ # changing the bridge should restart the interface too
+ self.ifup_interface_names = []
+ bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
+ members=[ib_interface])
+ self.provider.add_interface(interface)
+ self.provider.add_bridge(bridge)
+ self.provider.apply()
+ self.assertIn('ib0', self.ifup_interface_names)
# setup and apply a bond on a bridge
self.ifup_interface_names = []
diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py
index 7d8d3b3..51119ec 100644
--- a/os_net_config/tests/test_objects.py
+++ b/os_net_config/tests/test_objects.py
@@ -564,6 +564,95 @@ class TestOvsPatchPort(base.TestCase):
self.assertEqual("br-ex-patch", patch_port.peer)
+class TestIbInterface(base.TestCase):
+
+ def test_ib_interface_addresses(self):
+ v4_addr = objects.Address('192.168.1.1/24')
+ v6_addr = objects.Address('2001:abc:a::/64')
+ ib_interface = objects.IbInterface('foo', addresses=[v4_addr, v6_addr])
+ self.assertEqual("192.168.1.1", ib_interface.v4_addresses()[0].ip)
+ self.assertEqual("2001:abc:a::", ib_interface.v6_addresses()[0].ip)
+
+ def test_from_json_dhcp(self):
+ data = '{"type": "ib_interface", "name": "ib0", "use_dhcp": true}'
+ ib_interface = objects.object_from_json(json.loads(data))
+ self.assertEqual("ib0", ib_interface.name)
+ self.assertTrue(ib_interface.use_dhcp)
+
+ def test_from_json_defroute(self):
+ data = '{"type": "ib_interface", "name": "ib0", "use_dhcp": true}'
+ ib_interface1 = objects.object_from_json(json.loads(data))
+ data = """{
+"type": "ib_interface",
+"name": "ib0",
+"use_dhcp": true,
+"defroute": false
+}
+"""
+ ib_interface2 = objects.object_from_json(json.loads(data))
+ self.assertTrue(ib_interface1.defroute)
+ self.assertFalse(ib_interface2.defroute)
+
+ def test_from_json_dhclient_args(self):
+ data = """{
+"type": "ib_interface",
+"name": "ib0",
+"use_dhcp": true,
+"dhclient_args": "--foobar"
+}
+"""
+ ib_interface1 = objects.object_from_json(json.loads(data))
+ self.assertEqual("--foobar", ib_interface1.dhclient_args)
+
+ def test_from_json_dns_servers(self):
+ data = """{
+"type": "ib_interface",
+"name": "ib0",
+"use_dhcp": true,
+"dns_servers": ["1.2.3.4"]
+}
+"""
+ ib_interface1 = objects.object_from_json(json.loads(data))
+ self.assertEqual(["1.2.3.4"], ib_interface1.dns_servers)
+
+ def test_from_json_dhcp_nic1(self):
+ def dummy_numbered_nics(nic_mapping=None):
+ return {"nic1": "ib0"}
+ self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
+
+ data = '{"type": "ib_interface", "name": "nic1", "use_dhcp": true}'
+ ib_interface = objects.object_from_json(json.loads(data))
+ self.assertEqual("ib0", ib_interface.name)
+ self.assertTrue(ib_interface.use_dhcp)
+
+ def test_from_json_with_addresses(self):
+ data = """{
+"type": "ib_interface",
+"name": "ib0",
+"use_dhcp": false,
+"mtu": 1501,
+"addresses": [{
+ "ip_netmask": "192.0.2.1/24"
+}],
+"routes": [{
+ "next_hop": "192.0.2.1",
+ "ip_netmask": "192.0.2.1/24"
+}]
+}
+"""
+ ib_interface = objects.object_from_json(json.loads(data))
+ self.assertEqual("ib0", ib_interface.name)
+ self.assertFalse(ib_interface.use_dhcp)
+ self.assertFalse(ib_interface.use_dhcpv6)
+ self.assertEqual(1501, ib_interface.mtu)
+ address1 = ib_interface.v4_addresses()[0]
+ self.assertEqual("192.0.2.1", address1.ip)
+ self.assertEqual("255.255.255.0", address1.netmask)
+ route1 = ib_interface.routes[0]
+ self.assertEqual("192.0.2.1", route1.next_hop)
+ self.assertEqual("192.0.2.1/24", route1.ip_netmask)
+
+
class TestNumberedNicsMapping(base.TestCase):
# We want to test the function, not the dummy..