diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 9976828bba4..67467bfc99f 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -1431,7 +1431,9 @@ def get_devices_info(namespace, **kwargs): for device in retval.values(): if device.get('parent_index'): - device['parent_name'] = retval[device['parent_index']]['name'] + parent_device = retval.get(device['parent_index']) + if parent_device: + device['parent_name'] = parent_device['name'] elif device.get('vxlan_link_index'): device['vxlan_link_name'] = ( retval[device['vxlan_link_index']]['name']) diff --git a/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py b/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py index 7bcdc67cf37..e0d32e256b2 100644 --- a/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py @@ -183,6 +183,47 @@ class GetDevicesInfoTestCase(functional_base.BaseSudoTestCase): self.assertEqual(sorted(interfaces_tested), sorted(self.interfaces + vxlan_interfaces)) + def test_get_devices_info_veth_different_namespaces(self): + namespace2 = 'ns_test-' + uuidutils.generate_uuid() + priv_ip_lib.create_netns(namespace2) + self.addCleanup(self._remove_ns, namespace2) + ip_wrapper = ip_lib.IPWrapper(self.namespace) + ip_wrapper.add_veth('veth1_1', 'veth1_2', namespace2) + + devices = priv_ip_lib.get_link_devices(self.namespace) + for device in devices: + name = ip_lib.get_attr(device, 'IFLA_IFNAME') + if name == 'veth1_1': + veth1_1 = device + break + else: + self.fail('Interface "veth1_1" not found') + + ifla_linkinfo = ip_lib.get_attr(veth1_1, 'IFLA_LINKINFO') + self.assertEqual(ip_lib.get_attr(ifla_linkinfo, 'IFLA_INFO_KIND'), + 'veth') + self.assertIsNone(ip_lib.get_attr(veth1_1, 'IFLA_LINK')) + + def test_get_devices_info_veth_same_namespaces(self): + ip_wrapper = ip_lib.IPWrapper(self.namespace) + ip_wrapper.add_veth('veth1_1', 'veth1_2') + + devices = priv_ip_lib.get_link_devices(self.namespace) + veth1_1 = veth1_2 = None + for device in devices: + name = ip_lib.get_attr(device, 'IFLA_IFNAME') + if name == 'veth1_1': + veth1_1 = device + elif name == 'veth1_2': + veth1_2 = device + + self.assertIsNotNone(veth1_1) + self.assertIsNotNone(veth1_2) + veth1_1_link = ip_lib.get_attr(veth1_1, 'IFLA_LINK') + veth1_2_link = ip_lib.get_attr(veth1_2, 'IFLA_LINK') + self.assertEqual(veth1_1['index'], veth1_2_link) + self.assertEqual(veth1_2['index'], veth1_1_link) + class ListIpRulesTestCase(functional_base.BaseSudoTestCase): diff --git a/neutron/tests/unit/agent/linux/test_ip_lib.py b/neutron/tests/unit/agent/linux/test_ip_lib.py index 2d027efae1c..93dc23aa523 100644 --- a/neutron/tests/unit/agent/linux/test_ip_lib.py +++ b/neutron/tests/unit/agent/linux/test_ip_lib.py @@ -1957,6 +1957,18 @@ class GetDevicesInfoTestCase(base.BaseTestCase): )})) } + DEVICE_VETH = { + 'index': 11, + 'attrs': (('IFLA_IFNAME', 'int_04'), ('IFLA_OPERSTATE', 'UP'), + ('IFLA_LINKMODE', 0), ('IFLA_MTU', 900), + ('IFLA_PROMISCUITY', 0), + ('IFLA_ADDRESS', '5a:76:ed:cc:ce:93'), + ('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f3'), + ('IFLA_LINK', 30), + ('IFLA_LINKINFO', { + 'attrs': (('IFLA_INFO_KIND', 'veth'), )})) + } + def setUp(self): super(GetDevicesInfoTestCase, self).setUp() self.mock_getdevs = mock.patch.object(priv_lib, @@ -2031,3 +2043,22 @@ class GetDevicesInfoTestCase(base.BaseTestCase): break else: self.fail('No VXLAN device found') + + def test_get_devices_info_veth(self): + self.mock_getdevs.return_value = (self.DEVICE_VETH, self.DEVICE_DUMMY) + ret = ip_lib.get_devices_info('namespace') + expected = {'index': 11, + 'name': 'int_04', + 'operstate': 'UP', + 'linkmode': 0, + 'mtu': 900, + 'promiscuity': 0, + 'mac': '5a:76:ed:cc:ce:93', + 'broadcast': 'ff:ff:ff:ff:ff:f3', + 'kind': 'veth', + 'parent_index': 30} + for device in (device for device in ret if device['kind'] == 'veth'): + self.assertEqual(expected, device) + break + else: + self.fail('No VETH device found')