diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index e46c7b4809..aa44b1e299 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -29,6 +29,11 @@ OPTS = [ LOOPBACK_DEVNAME = 'lo' +# NOTE(ethuleau): depend of the version of iproute2, the vlan +# interface details vary. +VLAN_INTERFACE_DETAIL = ['vlan protocol 802.1q', + 'vlan protocol 802.1Q', + 'vlan id'] class SubProcessBase(object): @@ -87,14 +92,18 @@ class IPWrapper(SubProcessBase): def get_devices(self, exclude_loopback=False): retval = [] - output = self._execute('o', 'link', ('list',), + output = self._execute(['o', 'd'], 'link', ('list',), self.root_helper, self.namespace) for line in output.split('\n'): if '<' not in line: continue - tokens = line.split(':', 2) - if len(tokens) >= 3: - name = tokens[1].split('@', 1)[0].strip() + tokens = line.split(' ', 2) + if len(tokens) == 3: + if any(v in tokens[2] for v in VLAN_INTERFACE_DETAIL): + delimiter = '@' + else: + delimiter = ':' + name = tokens[1].rpartition(delimiter)[0].strip() if exclude_loopback and name == LOOPBACK_DEVNAME: continue diff --git a/neutron/tests/unit/test_linux_ip_lib.py b/neutron/tests/unit/test_linux_ip_lib.py index e17dc8baf0..7ae10670bf 100644 --- a/neutron/tests/unit/test_linux_ip_lib.py +++ b/neutron/tests/unit/test_linux_ip_lib.py @@ -28,17 +28,51 @@ NETNS_SAMPLE = [ LINK_SAMPLE = [ '1: lo: mtu 16436 qdisc noqueue state UNKNOWN \\' - 'link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00', + 'link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0', '2: eth0: mtu 1500 qdisc mq state UP ' 'qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff' '\ alias openvswitch', '3: br-int: mtu 1500 qdisc noop state DOWN ' - '\ link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff', + '\ link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff promiscuity 0', '4: gw-ddc717df-49: mtu 1500 qdisc noop ' - 'state DOWN \ link/ether fe:dc:ba:fe:dc:ba brd ff:ff:ff:ff:ff:ff', - '5: eth0.50@eth0: mtu 1500 qdisc ' + 'state DOWN \ link/ether fe:dc:ba:fe:dc:ba brd ff:ff:ff:ff:ff:ff ' + 'promiscuity 0', + '5: foo:foo: mtu 1500 qdisc mq state ' + 'UP qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff ' + 'promiscuity 0', + '6: foo@foo: mtu 1500 qdisc mq state ' + 'UP qlen 1000\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff ' + 'promiscuity 0', + '7: foo:foo@foo: mtu 1500 qdisc mq ' + 'state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0', + '8: foo@foo:foo: mtu 1500 qdisc mq ' + 'state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0', + '9: bar.9@eth0: mtu 1500 qdisc ' ' noqueue master brq0b24798c-07 state UP mode DEFAULT' - '\ link/ether ab:04:49:b6:ab:a0 brd ff:ff:ff:ff:ff:ff'] + '\ link/ether ab:04:49:b6:ab:a0 brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan protocol 802.1q id 9 ', + '10: bar@eth0: mtu 1500 qdisc ' + ' noqueue master brq0b24798c-07 state UP mode DEFAULT' + '\ link/ether ab:04:49:b6:ab:a0 brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan protocol 802.1Q id 10 ', + '11: bar:bar@eth0: mtu 1500 qdisc mq ' + 'state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan id 11 ', + '12: bar@bar@eth0: mtu 1500 qdisc mq ' + 'state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan id 12 ', + '13: bar:bar@bar@eth0: mtu 1500 ' + 'qdisc mq state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan protocol 802.1q id 13 ', + '14: bar@bar:bar@eth0: mtu 1500 ' + 'qdisc mq state UP qlen 1000' + '\ link/ether cc:dd:ee:ff:ab:cd brd ff:ff:ff:ff:ff:ff promiscuity 0' + '\ vlan protocol 802.1Q id 14 '] ADDR_SAMPLE = (""" 2: eth0: mtu 1500 qdisc mq state UP qlen 1000 @@ -168,9 +202,18 @@ class TestIpWrapper(base.BaseTestCase): ip_lib.IPDevice('eth0'), ip_lib.IPDevice('br-int'), ip_lib.IPDevice('gw-ddc717df-49'), - ip_lib.IPDevice('eth0.50')]) + ip_lib.IPDevice('foo:foo'), + ip_lib.IPDevice('foo@foo'), + ip_lib.IPDevice('foo:foo@foo'), + ip_lib.IPDevice('foo@foo:foo'), + ip_lib.IPDevice('bar.9'), + ip_lib.IPDevice('bar'), + ip_lib.IPDevice('bar:bar'), + ip_lib.IPDevice('bar@bar'), + ip_lib.IPDevice('bar:bar@bar'), + ip_lib.IPDevice('bar@bar:bar')]) - self.execute.assert_called_once_with('o', 'link', ('list',), + self.execute.assert_called_once_with(['o', 'd'], 'link', ('list',), 'sudo', None) def test_get_devices_malformed_line(self): @@ -181,9 +224,18 @@ class TestIpWrapper(base.BaseTestCase): ip_lib.IPDevice('eth0'), ip_lib.IPDevice('br-int'), ip_lib.IPDevice('gw-ddc717df-49'), - ip_lib.IPDevice('eth0.50')]) + ip_lib.IPDevice('foo:foo'), + ip_lib.IPDevice('foo@foo'), + ip_lib.IPDevice('foo:foo@foo'), + ip_lib.IPDevice('foo@foo:foo'), + ip_lib.IPDevice('bar.9'), + ip_lib.IPDevice('bar'), + ip_lib.IPDevice('bar:bar'), + ip_lib.IPDevice('bar@bar'), + ip_lib.IPDevice('bar:bar@bar'), + ip_lib.IPDevice('bar@bar:bar')]) - self.execute.assert_called_once_with('o', 'link', ('list',), + self.execute.assert_called_once_with(['o', 'd'], 'link', ('list',), 'sudo', None) def test_get_namespaces(self):