Add IPWrapper.get_devices_info using PyRoute2
This function returns the attributes of a list of devices. Change-Id: I322fc7db9c71e7c21fd03d616937d172da856428 Related-Bug: #1804274
This commit is contained in:
parent
56dfd7291d
commit
e7a2b6d179
|
@ -144,6 +144,19 @@ class IPWrapper(SubProcessBase):
|
||||||
def device(self, name):
|
def device(self, name):
|
||||||
return IPDevice(name, namespace=self.namespace)
|
return IPDevice(name, namespace=self.namespace)
|
||||||
|
|
||||||
|
def get_devices_info(self, exclude_loopback=True,
|
||||||
|
exclude_fb_tun_devices=True):
|
||||||
|
devices = get_devices_info(self.namespace)
|
||||||
|
|
||||||
|
retval = []
|
||||||
|
for device in devices:
|
||||||
|
if (exclude_loopback and device['name'] == LOOPBACK_DEVNAME or
|
||||||
|
exclude_fb_tun_devices and
|
||||||
|
device['name'] in FB_TUNNEL_DEVICE_NAMES):
|
||||||
|
continue
|
||||||
|
retval.append(device)
|
||||||
|
return retval
|
||||||
|
|
||||||
def get_devices(self, exclude_loopback=True, exclude_fb_tun_devices=True):
|
def get_devices(self, exclude_loopback=True, exclude_fb_tun_devices=True):
|
||||||
retval = []
|
retval = []
|
||||||
try:
|
try:
|
||||||
|
@ -1330,6 +1343,13 @@ def delete_ip_rule(namespace, ip, iif=None, table=None, priority=None,
|
||||||
privileged.delete_ip_rule(namespace, **cmd_args)
|
privileged.delete_ip_rule(namespace, **cmd_args)
|
||||||
|
|
||||||
|
|
||||||
|
def get_attr(pyroute2_obj, attr_name):
|
||||||
|
"""Get an attribute from a PyRoute2 object"""
|
||||||
|
rule_attrs = pyroute2_obj.get('attrs', [])
|
||||||
|
for attr in (attr for attr in rule_attrs if attr[0] == attr_name):
|
||||||
|
return attr[1]
|
||||||
|
|
||||||
|
|
||||||
def _parse_link_device(namespace, device, **kwargs):
|
def _parse_link_device(namespace, device, **kwargs):
|
||||||
"""Parse pytoute2 link device information
|
"""Parse pytoute2 link device information
|
||||||
|
|
||||||
|
@ -1337,12 +1357,6 @@ def _parse_link_device(namespace, device, **kwargs):
|
||||||
in a dictionary.
|
in a dictionary.
|
||||||
IP address scope: http://linux-ip.net/html/tools-ip-address.html
|
IP address scope: http://linux-ip.net/html/tools-ip-address.html
|
||||||
"""
|
"""
|
||||||
def get_attr(pyroute2_obj, attr_name):
|
|
||||||
rule_attrs = pyroute2_obj.get('attrs', [])
|
|
||||||
for attr in (attr for attr in rule_attrs if attr[0] == attr_name):
|
|
||||||
return attr[1]
|
|
||||||
return
|
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
name = get_attr(device, 'IFLA_IFNAME')
|
name = get_attr(device, 'IFLA_IFNAME')
|
||||||
ip_addresses = privileged.get_ip_addresses(namespace,
|
ip_addresses = privileged.get_ip_addresses(namespace,
|
||||||
|
@ -1377,3 +1391,29 @@ def get_devices_with_ip(namespace, name=None, **kwargs):
|
||||||
for device in devices):
|
for device in devices):
|
||||||
retval += parsed_ips
|
retval += parsed_ips
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
|
||||||
|
def get_devices_info(namespace, **kwargs):
|
||||||
|
devices = privileged.get_link_devices(namespace, **kwargs)
|
||||||
|
retval = []
|
||||||
|
for device in devices:
|
||||||
|
ret = {'index': device['index'],
|
||||||
|
'name': get_attr(device, 'IFLA_IFNAME'),
|
||||||
|
'operstate': get_attr(device, 'IFLA_OPERSTATE'),
|
||||||
|
'linkmode': get_attr(device, 'IFLA_LINKMODE'),
|
||||||
|
'mtu': get_attr(device, 'IFLA_MTU'),
|
||||||
|
'promiscuity': get_attr(device, 'IFLA_PROMISCUITY'),
|
||||||
|
'mac': get_attr(device, 'IFLA_ADDRESS'),
|
||||||
|
'broadcast': get_attr(device, 'IFLA_BROADCAST')}
|
||||||
|
ifla_linkinfo = get_attr(device, 'IFLA_LINKINFO')
|
||||||
|
if ifla_linkinfo:
|
||||||
|
ret['kind'] = get_attr(ifla_linkinfo, 'IFLA_INFO_KIND')
|
||||||
|
ifla_data = get_attr(ifla_linkinfo, 'IFLA_INFO_DATA')
|
||||||
|
if ret['kind'] == 'vxlan':
|
||||||
|
ret['vxlan_id'] = get_attr(ifla_data, 'IFLA_VXLAN_ID')
|
||||||
|
ret['vxlan_group'] = get_attr(ifla_data, 'IFLA_VXLAN_GROUP')
|
||||||
|
elif ret['kind'] == 'vlan':
|
||||||
|
ret['vlan_id'] = get_attr(ifla_data, 'IFLA_VLAN_ID')
|
||||||
|
retval.append(ret)
|
||||||
|
|
||||||
|
return retval
|
||||||
|
|
|
@ -21,13 +21,6 @@ from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
|
||||||
from neutron.tests.functional import base as functional_base
|
from neutron.tests.functional import base as functional_base
|
||||||
|
|
||||||
|
|
||||||
def _get_attr(pyroute2_obj, attr_name):
|
|
||||||
rule_attrs = pyroute2_obj.get('attrs', [])
|
|
||||||
for attr in (attr for attr in rule_attrs if attr[0] == attr_name):
|
|
||||||
return attr[1]
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class GetDeviceNamesTestCase(functional_base.BaseSudoTestCase):
|
class GetDeviceNamesTestCase(functional_base.BaseSudoTestCase):
|
||||||
|
|
||||||
def _remove_ns(self, namespace):
|
def _remove_ns(self, namespace):
|
||||||
|
@ -57,6 +50,121 @@ class GetDeviceNamesTestCase(functional_base.BaseSudoTestCase):
|
||||||
self.assertNotIn(name, interfaces)
|
self.assertNotIn(name, interfaces)
|
||||||
|
|
||||||
|
|
||||||
|
class GetDevicesInfoTestCase(functional_base.BaseSudoTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(GetDevicesInfoTestCase, self).setUp()
|
||||||
|
self.namespace = 'ns_test-' + uuidutils.generate_uuid()
|
||||||
|
priv_ip_lib.create_netns(self.namespace)
|
||||||
|
self.addCleanup(self._remove_ns, self.namespace)
|
||||||
|
self.interfaces = ['int_01', 'int_02']
|
||||||
|
self.interfaces_to_exclude = (ip_lib.FB_TUNNEL_DEVICE_NAMES +
|
||||||
|
[ip_lib.LOOPBACK_DEVNAME])
|
||||||
|
|
||||||
|
def _remove_ns(self, namespace):
|
||||||
|
priv_ip_lib.remove_netns(namespace)
|
||||||
|
|
||||||
|
def test_get_devices_info_lo(self):
|
||||||
|
devices = priv_ip_lib.get_link_devices(self.namespace)
|
||||||
|
self.assertGreater(len(devices), 0)
|
||||||
|
for device in devices:
|
||||||
|
if ip_lib.get_attr(device, 'IFLA_IFNAME') != 'lo':
|
||||||
|
continue
|
||||||
|
self.assertIsNone(ip_lib.get_attr(device, 'IFLA_LINKINFO'))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.fail('Device "lo" not found')
|
||||||
|
|
||||||
|
def test_get_devices_info_dummy(self):
|
||||||
|
interfaces_tested = []
|
||||||
|
for interface in self.interfaces:
|
||||||
|
priv_ip_lib.create_interface(interface, self.namespace, 'dummy')
|
||||||
|
|
||||||
|
devices = priv_ip_lib.get_link_devices(self.namespace)
|
||||||
|
self.assertGreater(len(devices), 0)
|
||||||
|
for device in devices:
|
||||||
|
name = ip_lib.get_attr(device, 'IFLA_IFNAME')
|
||||||
|
if name in self.interfaces_to_exclude:
|
||||||
|
continue
|
||||||
|
self.assertIn(name, self.interfaces)
|
||||||
|
ifla_linkinfo = ip_lib.get_attr(device, 'IFLA_LINKINFO')
|
||||||
|
self.assertEqual(ip_lib.get_attr(ifla_linkinfo, 'IFLA_INFO_KIND'),
|
||||||
|
'dummy')
|
||||||
|
interfaces_tested.append(name)
|
||||||
|
self.assertEqual(sorted(interfaces_tested), sorted(self.interfaces))
|
||||||
|
|
||||||
|
def test_get_devices_info_vlan(self):
|
||||||
|
interfaces_tested = []
|
||||||
|
vlan_interfaces = []
|
||||||
|
vlan_id = 1000
|
||||||
|
for interface in self.interfaces:
|
||||||
|
priv_ip_lib.create_interface(interface, self.namespace, 'dummy')
|
||||||
|
vlan_interface = interface + '_' + str(vlan_id)
|
||||||
|
vlan_interfaces.append(vlan_interface)
|
||||||
|
priv_ip_lib.create_interface(
|
||||||
|
vlan_interface, self.namespace, 'vlan',
|
||||||
|
physical_interface=interface, vlan_id=vlan_id)
|
||||||
|
vlan_id += 1
|
||||||
|
|
||||||
|
devices = priv_ip_lib.get_link_devices(self.namespace)
|
||||||
|
self.assertGreater(len(devices), 0)
|
||||||
|
for device in devices:
|
||||||
|
name = ip_lib.get_attr(device, 'IFLA_IFNAME')
|
||||||
|
if name in self.interfaces_to_exclude:
|
||||||
|
continue
|
||||||
|
self.assertIn(name, self.interfaces + vlan_interfaces)
|
||||||
|
ifla_linkinfo = ip_lib.get_attr(device, 'IFLA_LINKINFO')
|
||||||
|
if name in vlan_interfaces:
|
||||||
|
self.assertEqual(
|
||||||
|
ip_lib.get_attr(ifla_linkinfo, 'IFLA_INFO_KIND'), 'vlan')
|
||||||
|
ifla_infodata = ip_lib.get_attr(ifla_linkinfo,
|
||||||
|
'IFLA_INFO_DATA')
|
||||||
|
vlan_id = int(name.split('_')[-1])
|
||||||
|
self.assertEqual(
|
||||||
|
ip_lib.get_attr(ifla_infodata, 'IFLA_VLAN_ID'), vlan_id)
|
||||||
|
interfaces_tested.append(name)
|
||||||
|
self.assertEqual(sorted(interfaces_tested),
|
||||||
|
sorted(self.interfaces + vlan_interfaces))
|
||||||
|
|
||||||
|
def test_get_devices_info_vxlan(self):
|
||||||
|
interfaces_tested = []
|
||||||
|
vxlan_interfaces = []
|
||||||
|
vxlan_id = 1000
|
||||||
|
for interface in self.interfaces:
|
||||||
|
priv_ip_lib.create_interface(interface, self.namespace, 'dummy')
|
||||||
|
vxlan_interface = interface + '_' + str(vxlan_id)
|
||||||
|
vxlan_interfaces.append(vxlan_interface)
|
||||||
|
priv_ip_lib.create_interface(
|
||||||
|
vxlan_interface, self.namespace, 'vxlan',
|
||||||
|
physical_interface=interface, vxlan_id=vxlan_id,
|
||||||
|
vxlan_group='239.1.1.1')
|
||||||
|
vxlan_id += 1
|
||||||
|
|
||||||
|
devices = priv_ip_lib.get_link_devices(self.namespace)
|
||||||
|
self.assertGreater(len(devices), 0)
|
||||||
|
for device in devices:
|
||||||
|
name = ip_lib.get_attr(device, 'IFLA_IFNAME')
|
||||||
|
if name in self.interfaces_to_exclude:
|
||||||
|
continue
|
||||||
|
self.assertIn(name, self.interfaces + vxlan_interfaces)
|
||||||
|
ifla_linkinfo = ip_lib.get_attr(device, 'IFLA_LINKINFO')
|
||||||
|
if name in vxlan_interfaces:
|
||||||
|
self.assertEqual(
|
||||||
|
ip_lib.get_attr(ifla_linkinfo, 'IFLA_INFO_KIND'),
|
||||||
|
'vxlan')
|
||||||
|
ifla_infodata = ip_lib.get_attr(ifla_linkinfo,
|
||||||
|
'IFLA_INFO_DATA')
|
||||||
|
vxlan_id = int(name.split('_')[-1])
|
||||||
|
self.assertEqual(
|
||||||
|
ip_lib.get_attr(ifla_infodata, 'IFLA_VXLAN_ID'), vxlan_id)
|
||||||
|
self.assertEqual(
|
||||||
|
ip_lib.get_attr(ifla_infodata, 'IFLA_VXLAN_GROUP'),
|
||||||
|
'239.1.1.1')
|
||||||
|
interfaces_tested.append(name)
|
||||||
|
self.assertEqual(sorted(interfaces_tested),
|
||||||
|
sorted(self.interfaces + vxlan_interfaces))
|
||||||
|
|
||||||
|
|
||||||
class ListIpRulesTestCase(functional_base.BaseSudoTestCase):
|
class ListIpRulesTestCase(functional_base.BaseSudoTestCase):
|
||||||
|
|
||||||
RULE_TABLES = {'default': 253, 'main': 254, 'local': 255}
|
RULE_TABLES = {'default': 253, 'main': 254, 'local': 255}
|
||||||
|
@ -276,7 +384,7 @@ class GetIpAddressesTestCase(functional_base.BaseSudoTestCase):
|
||||||
ip_addresses = priv_ip_lib.get_ip_addresses(namespace)
|
ip_addresses = priv_ip_lib.get_ip_addresses(namespace)
|
||||||
for ip_address in ip_addresses:
|
for ip_address in ip_addresses:
|
||||||
int_name = str(ip_address['index'])
|
int_name = str(ip_address['index'])
|
||||||
ip = _get_attr(ip_address, 'IFA_ADDRESS')
|
ip = ip_lib.get_attr(ip_address, 'IFA_ADDRESS')
|
||||||
mask = ip_address['prefixlen']
|
mask = ip_address['prefixlen']
|
||||||
cidr = common_utils.ip_to_cidr(ip, mask)
|
cidr = common_utils.ip_to_cidr(ip, mask)
|
||||||
self.assertEqual(interfaces[int_name]['cidr'], cidr)
|
self.assertEqual(interfaces[int_name]['cidr'], cidr)
|
||||||
|
|
|
@ -1906,3 +1906,115 @@ class ParseLinkDeviceTestCase(base.BaseTestCase):
|
||||||
'dynamic': False, 'dadfailed': False, 'name': 'int_name',
|
'dynamic': False, 'dadfailed': False, 'name': 'int_name',
|
||||||
'broadcast': None, 'tentative': False}]
|
'broadcast': None, 'tentative': False}]
|
||||||
self.assertEqual(expected, retval)
|
self.assertEqual(expected, retval)
|
||||||
|
|
||||||
|
|
||||||
|
class GetDevicesInfoTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
|
DEVICE_LO = {
|
||||||
|
'index': 2,
|
||||||
|
'attrs': (('IFLA_IFNAME', 'lo'), ('IFLA_OPERSTATE', 'UP'),
|
||||||
|
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1000),
|
||||||
|
('IFLA_PROMISCUITY', 0),
|
||||||
|
('IFLA_ADDRESS', '5a:76:ed:cc:ce:90'),
|
||||||
|
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f0'), )
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVICE_DUMMY = {
|
||||||
|
'index': 2,
|
||||||
|
'attrs': (('IFLA_IFNAME', 'int_01'), ('IFLA_OPERSTATE', 'DOWN'),
|
||||||
|
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1500),
|
||||||
|
('IFLA_PROMISCUITY', 0),
|
||||||
|
('IFLA_ADDRESS', '5a:76:ed:cc:ce:90'),
|
||||||
|
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f0'),
|
||||||
|
('IFLA_LINKINFO', {
|
||||||
|
'attrs': (('IFLA_INFO_KIND', 'dummy'), )}))
|
||||||
|
}
|
||||||
|
DEVICE_VLAN = {
|
||||||
|
'index': 5,
|
||||||
|
'attrs': (('IFLA_IFNAME', 'int_02'), ('IFLA_OPERSTATE', 'DOWN'),
|
||||||
|
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1400),
|
||||||
|
('IFLA_PROMISCUITY', 0),
|
||||||
|
('IFLA_ADDRESS', '5a:76:ed:cc:ce:91'),
|
||||||
|
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f1'),
|
||||||
|
('IFLA_LINKINFO', {'attrs': (
|
||||||
|
('IFLA_INFO_KIND', 'vlan'),
|
||||||
|
('IFLA_INFO_DATA', {'attrs': (('IFLA_VLAN_ID', 1000), )})
|
||||||
|
)}))
|
||||||
|
}
|
||||||
|
DEVICE_VXLAN = {
|
||||||
|
'index': 9,
|
||||||
|
'attrs': (('IFLA_IFNAME', 'int_03'), ('IFLA_OPERSTATE', 'UP'),
|
||||||
|
('IFLA_LINKMODE', 0), ('IFLA_MTU', 1300),
|
||||||
|
('IFLA_PROMISCUITY', 0),
|
||||||
|
('IFLA_ADDRESS', '5a:76:ed:cc:ce:92'),
|
||||||
|
('IFLA_BROADCAST', 'ff:ff:ff:ff:ff:f2'),
|
||||||
|
('IFLA_LINKINFO', {'attrs': (
|
||||||
|
('IFLA_INFO_KIND', 'vxlan'),
|
||||||
|
('IFLA_INFO_DATA', {'attrs': (
|
||||||
|
('IFLA_VXLAN_ID', 1001),
|
||||||
|
('IFLA_VXLAN_GROUP', '239.1.1.1'))})
|
||||||
|
)}))
|
||||||
|
}
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(GetDevicesInfoTestCase, self).setUp()
|
||||||
|
self.mock_getdevs = mock.patch.object(priv_lib,
|
||||||
|
'get_link_devices').start()
|
||||||
|
|
||||||
|
def test_get_devices_info_lo(self):
|
||||||
|
self.mock_getdevs.return_value = (self.DEVICE_LO, )
|
||||||
|
ret = ip_lib.get_devices_info('namespace')
|
||||||
|
expected = {'index': 2,
|
||||||
|
'name': 'lo',
|
||||||
|
'operstate': 'UP',
|
||||||
|
'linkmode': 0,
|
||||||
|
'mtu': 1000,
|
||||||
|
'promiscuity': 0,
|
||||||
|
'mac': '5a:76:ed:cc:ce:90',
|
||||||
|
'broadcast': 'ff:ff:ff:ff:ff:f0'}
|
||||||
|
self.assertEqual(expected, ret[0])
|
||||||
|
|
||||||
|
def test_get_devices_info_dummy(self):
|
||||||
|
self.mock_getdevs.return_value = (self.DEVICE_DUMMY, )
|
||||||
|
ret = ip_lib.get_devices_info('namespace')
|
||||||
|
expected = {'index': 2,
|
||||||
|
'name': 'int_01',
|
||||||
|
'operstate': 'DOWN',
|
||||||
|
'linkmode': 0,
|
||||||
|
'mtu': 1500,
|
||||||
|
'promiscuity': 0,
|
||||||
|
'mac': '5a:76:ed:cc:ce:90',
|
||||||
|
'broadcast': 'ff:ff:ff:ff:ff:f0',
|
||||||
|
'kind': 'dummy'}
|
||||||
|
self.assertEqual(expected, ret[0])
|
||||||
|
|
||||||
|
def test_get_devices_info_vlan(self):
|
||||||
|
self.mock_getdevs.return_value = (self.DEVICE_VLAN, )
|
||||||
|
ret = ip_lib.get_devices_info('namespace')
|
||||||
|
expected = {'index': 5,
|
||||||
|
'name': 'int_02',
|
||||||
|
'operstate': 'DOWN',
|
||||||
|
'linkmode': 0,
|
||||||
|
'mtu': 1400,
|
||||||
|
'promiscuity': 0,
|
||||||
|
'mac': '5a:76:ed:cc:ce:91',
|
||||||
|
'broadcast': 'ff:ff:ff:ff:ff:f1',
|
||||||
|
'kind': 'vlan',
|
||||||
|
'vlan_id': 1000}
|
||||||
|
self.assertEqual(expected, ret[0])
|
||||||
|
|
||||||
|
def test_get_devices_info_vxlan(self):
|
||||||
|
self.mock_getdevs.return_value = (self.DEVICE_VXLAN, )
|
||||||
|
ret = ip_lib.get_devices_info('namespace')
|
||||||
|
expected = {'index': 9,
|
||||||
|
'name': 'int_03',
|
||||||
|
'operstate': 'UP',
|
||||||
|
'linkmode': 0,
|
||||||
|
'mtu': 1300,
|
||||||
|
'promiscuity': 0,
|
||||||
|
'mac': '5a:76:ed:cc:ce:92',
|
||||||
|
'broadcast': 'ff:ff:ff:ff:ff:f2',
|
||||||
|
'kind': 'vxlan',
|
||||||
|
'vxlan_id': 1001,
|
||||||
|
'vxlan_group': '239.1.1.1'}
|
||||||
|
self.assertEqual(expected, ret[0])
|
||||||
|
|
Loading…
Reference in New Issue