From 433a0f3cbe59fd5da183245bf7e6dcd4af4b8c88 Mon Sep 17 00:00:00 2001 From: Brent Eagles Date: Mon, 7 Nov 2016 17:57:02 -0330 Subject: Add support for enabling hotplug on interfaces This patch adds support for enabling hotplugging on interfaces (disabled by default). This is useful for configuring SR-IOV root devices so that they "return" to the system when no longer used by a VM. Note: also updates an invalid value in the interface and ib_interface sample files. Partial-Bug: #1639901 Change-Id: Idfc17d6f20bb306271838895bc53f4b109dd664d --- etc/os-net-config/samples/ib_interface.json | 2 +- etc/os-net-config/samples/ib_interface.yaml | 2 +- etc/os-net-config/samples/interface.json | 8 +++++++- etc/os-net-config/samples/interface.yaml | 7 ++++++- os_net_config/impl_eni.py | 5 ++++- os_net_config/impl_ifcfg.py | 5 ++++- os_net_config/objects.py | 7 +++++-- os_net_config/tests/test_impl_eni.py | 17 +++++++++++++++-- os_net_config/tests/test_impl_ifcfg.py | 14 ++++++++++++++ os_net_config/tests/test_objects.py | 21 +++++++++++++++++++++ 10 files changed, 78 insertions(+), 10 deletions(-) diff --git a/etc/os-net-config/samples/ib_interface.json b/etc/os-net-config/samples/ib_interface.json index 4e42867..f552af3 100644 --- a/etc/os-net-config/samples/ib_interface.json +++ b/etc/os-net-config/samples/ib_interface.json @@ -20,7 +20,7 @@ "type": "ib_interface", "name": "ib1", "use_dhcp": true, - "defroute": no + "defroute": false } ] } diff --git a/etc/os-net-config/samples/ib_interface.yaml b/etc/os-net-config/samples/ib_interface.yaml index f930471..6210f0a 100644 --- a/etc/os-net-config/samples/ib_interface.yaml +++ b/etc/os-net-config/samples/ib_interface.yaml @@ -15,4 +15,4 @@ network_config: type: interface name: ib1 use_dhcp: true - defroute: no \ No newline at end of file + defroute: false diff --git a/etc/os-net-config/samples/interface.json b/etc/os-net-config/samples/interface.json index 6eb8f62..7b70e05 100644 --- a/etc/os-net-config/samples/interface.json +++ b/etc/os-net-config/samples/interface.json @@ -25,8 +25,14 @@ "type": "interface", "name": "em2", "use_dhcp": true, - "defroute": no, + "defroute": false, "ethtool_opts": "speed 1000 duplex full" + }, + { + "type": "interface", + "name": "em3", + "use_dhcp": true, + "hotplug": true } ] } diff --git a/etc/os-net-config/samples/interface.yaml b/etc/os-net-config/samples/interface.yaml index 725091b..4c4269e 100644 --- a/etc/os-net-config/samples/interface.yaml +++ b/etc/os-net-config/samples/interface.yaml @@ -19,5 +19,10 @@ network_config: type: interface name: em2 use_dhcp: true - defroute: no + defroute: false ethtool_opts: "speed 1000 duplex full" + - + type: interface + name: em3 + use_dhcp: true + hotplug: true diff --git a/os_net_config/impl_eni.py b/os_net_config/impl_eni.py index 5b91270..c6252ce 100644 --- a/os_net_config/impl_eni.py +++ b/os_net_config/impl_eni.py @@ -127,7 +127,10 @@ class ENINetConfig(os_net_config.NetConfig): data += address_data data += " vlan-raw-device %s\n" % interface.device else: - data += "auto %s\n" % interface.name + if isinstance(interface, objects.Interface) and interface.hotplug: + data += "allow-hotplug %s\n" % interface.name + else: + data += "auto %s\n" % interface.name data += _iface data += address_data if interface.mtu: diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index 41d5f8c..bcfc4dc 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -131,7 +131,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): data = "# This file is autogenerated by os-net-config\n" data += "DEVICE=%s\n" % base_opt.name data += "ONBOOT=yes\n" - data += "HOTPLUG=no\n" + if isinstance(base_opt, objects.Interface) and base_opt.hotplug: + data += "HOTPLUG=yes\n" + else: + data += "HOTPLUG=no\n" data += "NM_CONTROLLED=no\n" if not base_opt.dns_servers and not base_opt.use_dhcp: data += "PEERDNS=no\n" diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 8d52e73..741f304 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -287,7 +287,7 @@ class Interface(_BaseOpts): 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, ethtool_opts=None): + dns_servers=None, ethtool_opts=None, hotplug=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -296,13 +296,16 @@ class Interface(_BaseOpts): persist_mapping, defroute, dhclient_args, dns_servers) self.ethtool_opts = ethtool_opts + self.hotplug = hotplug @staticmethod def from_json(json): name = _get_required_field(json, 'name', 'Interface') + hotplug = strutils.bool_from_string(str(json.get('hotplug', False))) opts = _BaseOpts.base_opts_from_json(json) ethtool_opts = json.get('ethtool_opts', None) - return Interface(name, *opts, ethtool_opts=ethtool_opts) + return Interface(name, *opts, ethtool_opts=ethtool_opts, + hotplug=hotplug) class Vlan(_BaseOpts): diff --git a/os_net_config/tests/test_impl_eni.py b/os_net_config/tests/test_impl_eni.py index e445ed4..4911cb9 100644 --- a/os_net_config/tests/test_impl_eni.py +++ b/os_net_config/tests/test_impl_eni.py @@ -32,6 +32,12 @@ _V4_IFACE_STATIC_IP = _AUTO + """iface eth0 inet static netmask 255.255.255.0 """ +_IFACE_HOTPLUG = """allow-hotplug eth0 +iface eth0 inet static + address 192.168.1.2 + netmask 255.255.255.0 +""" + _V4_IFACE_STATIC_IP_MULTIPLE = (_V4_IFACE_STATIC_IP + _AUTO + """iface eth0 inet static address 10.0.0.2 @@ -117,8 +123,9 @@ class TestENINetConfig(base.TestCase): def get_route_config(self): return self.provider.routes[self.if_name] - def _default_interface(self, addr=[], rts=[]): - return objects.Interface(self.if_name, addresses=addr, routes=rts) + def _default_interface(self, addr=[], rts=[], hotplug=False): + return objects.Interface(self.if_name, addresses=addr, routes=rts, + hotplug=hotplug) def test_interface_no_ip(self): interface = self._default_interface() @@ -131,6 +138,12 @@ class TestENINetConfig(base.TestCase): self.provider.add_interface(interface) self.assertEqual(_V4_IFACE_STATIC_IP, self.get_interface_config()) + def test_add_interface_with_hotplug(self): + v4_addr = objects.Address('192.168.1.2/24') + interface = self._default_interface(addr=[v4_addr], hotplug=True) + self.provider.add_interface(interface) + self.assertEqual(_IFACE_HOTPLUG, self.get_interface_config()) + def test_add_interface_with_v4_multiple(self): v4_addresses = [objects.Address('192.168.1.2/24'), objects.Address('10.0.0.2/8')] diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index 3a5a7db..9621b8c 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -33,6 +33,15 @@ NM_CONTROLLED=no PEERDNS=no """ +_HOTPLUG = """# This file is autogenerated by os-net-config +DEVICE=em1 +ONBOOT=yes +HOTPLUG=yes +NM_CONTROLLED=no +PEERDNS=no +BOOTPROTO=none +""" + _NO_IP = _BASE_IFCFG + "BOOTPROTO=none\n" _V4_IFCFG = _BASE_IFCFG + """BOOTPROTO=static @@ -386,6 +395,11 @@ class TestIfcfgNetConfig(base.TestCase): self.provider.add_interface(interface) self.assertEqual(_NO_IP, self.get_interface_config()) + def test_add_interface_with_hotplug(self): + interface = objects.Interface('em1', hotplug=True) + self.provider.add_interface(interface) + self.assertEqual(_HOTPLUG, self.get_interface_config()) + def test_add_base_interface_vlan(self): interface = objects.Interface('em1.120') self.provider.add_interface(interface) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index ca2dd47..23a3bbe 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -98,6 +98,27 @@ class TestInterface(base.TestCase): self.assertEqual("em1", interface.name) self.assertTrue(interface.use_dhcp) + def test_from_json_hotplug(self): + data = """{ +"type": "interface", +"name": "em1", +"hotplug": true +} +""" + interface = objects.object_from_json(json.loads(data)) + self.assertEqual("em1", interface.name) + self.assertTrue(interface.hotplug) + + def test_from_json_hotplug_off_by_default(self): + data = """{ +"type": "interface", +"name": "em1" +} +""" + interface = objects.object_from_json(json.loads(data)) + self.assertEqual("em1", interface.name) + self.assertFalse(interface.hotplug) + def test_from_json_defroute(self): data = '{"type": "interface", "name": "em1", "use_dhcp": true}' interface1 = objects.object_from_json(json.loads(data)) -- cgit 1.2.3-korg