aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-07-25 14:41:18 +0000
committerGerrit Code Review <review@openstack.org>2016-07-25 14:41:18 +0000
commitf17add2e8e3c40f79cf211b0cb82a359104ad675 (patch)
treeec8eab3c2078d418fd917f095e3952fd03ee85a6
parenta08e230b00c32a475d0f2f9826dabaa822153ad6 (diff)
parent78e1b65d93a1c16d58fd72cb65f8e2adf1762854 (diff)
Merge "Add support for Infiniband interfaces"
-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..