diff options
-rw-r--r-- | etc/os-net-config/samples/ib_interface.json | 26 | ||||
-rw-r--r-- | etc/os-net-config/samples/ib_interface.yaml | 18 | ||||
-rw-r--r-- | os_net_config/__init__.py | 9 | ||||
-rw-r--r-- | os_net_config/impl_ifcfg.py | 46 | ||||
-rw-r--r-- | os_net_config/objects.py | 24 | ||||
-rw-r--r-- | os_net_config/tests/test_impl_ifcfg.py | 51 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 89 |
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.. |