From 790819c23776f91ae6778a33697ea88b5d39bcbd Mon Sep 17 00:00:00 2001 From: James Page Date: Fri, 11 Nov 2016 17:20:58 +0000 Subject: [PATCH] Update SR-IOV support for >= Mitaka SR-IOV network for OpenStack release later than Mitaka requires the use of the neutron-sriov-agent to support management of SR-IOV PF and VF interface state by Neutron - said interfaces are still consumed directly by nova-compute/libvirt via PCI device allocation scheduling for instances. Add new configuration options to the neutron-openvswitch charm to support enablement of the SR-IOV agent; this could have been done automatically from data presented from neutron-api, but its possible that cloud deployments may only have subsets of compute nodes that are SR-IOV enabled in terms of hardware. Enabling this option ('enable-sriov') will install and configure the neutron-sriov-agent; configuration of SR-IOV PF's are made using the 'sriov-numvfs', which by default automatically configures all SR-IOV devices on every machine to the maximum number of VF's supported by the device. This option can be used to configure devices at an individual level as well. Finally, neutron needs to understand what underlying provider network each SR-IOV device maps to - this is configured using the sriov-device-mappings configuration option. Change-Id: Ie185fd347ddc1b11e9ed13cefaf44fb7c8546ab0 --- config.yaml | 31 +++++ hooks/neutron_ovs_context.py | 6 + hooks/neutron_ovs_hooks.py | 2 + hooks/neutron_ovs_utils.py | 75 +++++++++++ hooks/pci.py | 186 +++++++++++++++++++++------ templates/mitaka/sriov_agent.ini | 12 ++ unit_tests/test_neutron_ovs_hooks.py | 2 + unit_tests/test_neutron_ovs_utils.py | 98 ++++++++++++++ unit_tests/test_pci.py | 56 ++++---- unit_tests/test_pci_helper.py | 4 +- 10 files changed, 405 insertions(+), 67 deletions(-) create mode 100644 templates/mitaka/sriov_agent.ini diff --git a/config.yaml b/config.yaml index 3182a8a2..36e5298d 100644 --- a/config.yaml +++ b/config.yaml @@ -172,3 +172,34 @@ options: uio_pci_generic . Only used when DPDK is enabled. + enable-sriov: + type: boolean + default: false + description: | + Enable SR-IOV NIC agent on deployed units; use with sriov-device-mappings to + map SR-IOV devices to underlying provider networks. Enabling this option + allows instances to be plugged into directly into SR-IOV VF devices connected + to underlying provider networks alongside the default Open vSwitch networking + options. + sriov-device-mappings: + type: string + default: + description: | + Space-delimited list of SR-IOV device mappings with format + . + : + . + Multiple mappings can be provided, delimited by spaces. + sriov-numvfs: + type: string + default: auto + description: | + Number of VF's to configure each PF with; by default, each SR-IOV PF will be + configured with the maximum number of VF's it can support. Either use a + single integer to apply the same VF configuration to all detected SR-IOV + devices or use a per-device configuration in the following format + . + : + . + Multiple devices can be configured by providing multi values delimited by + spaces. diff --git a/hooks/neutron_ovs_context.py b/hooks/neutron_ovs_context.py index 56b5863d..08d793b0 100644 --- a/hooks/neutron_ovs_context.py +++ b/hooks/neutron_ovs_context.py @@ -103,6 +103,12 @@ class OVSPluginContext(context.NeutronContext): if mappings: ovs_ctxt['bridge_mappings'] = ','.join(mappings.split()) + sriov_mappings = config('sriov-device-mappings') + if sriov_mappings: + ovs_ctxt['sriov_device_mappings'] = ( + ','.join(sriov_mappings.split()) + ) + flat_providers = config('flat-network-providers') if flat_providers: ovs_ctxt['network_providers'] = ','.join(flat_providers.split()) diff --git a/hooks/neutron_ovs_hooks.py b/hooks/neutron_ovs_hooks.py index d897ff72..d82774ab 100755 --- a/hooks/neutron_ovs_hooks.py +++ b/hooks/neutron_ovs_hooks.py @@ -39,6 +39,7 @@ from neutron_ovs_utils import ( DVR_PACKAGES, METADATA_PACKAGES, configure_ovs, + configure_sriov, git_install, get_topics, get_shared_secret, @@ -72,6 +73,7 @@ def config_changed(): git_install(config('openstack-origin-git')) configure_ovs() + configure_sriov() CONFIGS.write_all() for rid in relation_ids('zeromq-configuration'): zeromq_configuration_relation_joined(rid) diff --git a/hooks/neutron_ovs_utils.py b/hooks/neutron_ovs_utils.py index f24076d1..24b4b5df 100644 --- a/hooks/neutron_ovs_utils.py +++ b/hooks/neutron_ovs_utils.py @@ -33,6 +33,7 @@ from charmhelpers.contrib.openstack.utils import ( make_assess_status_func, is_unit_paused_set, os_application_version_set, + remote_restart, ) from collections import OrderedDict from charmhelpers.contrib.openstack.utils import ( @@ -48,6 +49,7 @@ from charmhelpers.core.hookenv import ( charm_dir, config, status_set, + log, ) from charmhelpers.contrib.openstack.neutron import ( parse_bridge_mappings, @@ -79,6 +81,9 @@ from charmhelpers.fetch import ( filter_installed_packages, ) +from pci import PCINetDevices + + # The interface is said to be satisfied if anyone of the interfaces in the # list has a complete context. # LY: Note the neutron-plugin is always present since that is the relation @@ -134,6 +139,8 @@ PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf' TEMPLATES = 'templates/' OVS_DEFAULT = '/etc/default/openvswitch-switch' DPDK_INTERFACES = '/etc/dpdk/interfaces' +NEUTRON_SRIOV_AGENT_CONF = os.path.join(NEUTRON_CONF_DIR, + 'plugins/ml2/sriov_agent.ini') BASE_RESOURCE_MAP = OrderedDict([ (NEUTRON_CONF, { @@ -195,6 +202,12 @@ DVR_RESOURCE_MAP = OrderedDict([ 'contexts': [context.ExternalPortContext()], }), ]) +SRIOV_RESOURCE_MAP = OrderedDict([ + (NEUTRON_SRIOV_AGENT_CONF, { + 'services': ['neutron-sriov-agent'], + 'contexts': [neutron_ovs_context.OVSPluginContext()], + }), +]) TEMPLATES = 'templates/' INT_BRIDGE = "br-int" @@ -254,6 +267,9 @@ def determine_packages(): if use_dpdk(): pkgs.append('openvswitch-switch-dpdk') + if enable_sriov_agent(): + pkgs.append('neutron-sriov-agent') + return pkgs @@ -296,6 +312,10 @@ def resource_map(): ) if not use_dpdk(): drop_config.append(DPDK_INTERFACES) + if enable_sriov_agent(): + resource_map.update(SRIOV_RESOURCE_MAP) + resource_map[NEUTRON_CONF]['services'].append( + 'neutron-sriov-agent') else: drop_config.extend([OVS_CONF, DPDK_INTERFACES]) @@ -409,6 +429,53 @@ def configure_ovs(): service_restart('os-charm-phy-nic-mtu') +def configure_sriov(): + '''Configure SR-IOV devices based on provided configuration options''' + charm_config = config() + enable_sriov = charm_config.get('enable-sriov') + if enable_sriov and charm_config.changed('sriov-numvfs'): + devices = PCINetDevices() + sriov_numvfs = charm_config.get('sriov-numvfs') + + # automatic configuration of all SR-IOV devices + if sriov_numvfs == 'auto': + log('Configuring SR-IOV device VF functions in auto mode') + for device in devices.pci_devices: + if device and device.sriov: + log("Configuring SR-IOV device" + " {} with {} VF's".format(device.interface_name, + device.sriov_totalvfs)) + device.set_sriov_numvfs(device.sriov_totalvfs) + else: + # Single int blanket configuration + try: + sriov_numvfs = int(sriov_numvfs) + log('Configuring SR-IOV device VF functions' + ' with blanket setting') + for device in devices.pci_devices: + if device and device.sriov: + log("Configuring SR-IOV device" + " {} with {} VF's".format(device.interface_name, + sriov_numvfs)) + device.set_sriov_numvfs(sriov_numvfs) + except ValueError: + # :[ :numvfs] configuration + sriov_numvfs = sriov_numvfs.split() + for device_config in sriov_numvfs: + log('Configuring SR-IOV device VF functions per interface') + interface_name, numvfs = device_config.split(':') + device = devices.get_device_from_interface_name( + interface_name) + if device and device.sriov: + log("Configuring SR-IOV device" + " {} with {} VF's".format(device.interface_name, + numvfs)) + device.set_sriov_numvfs(int(numvfs)) + + # Trigger remote restart in parent application + remote_restart('neutron-plugin', 'nova-compute') + + def get_shared_secret(): ctxt = neutron_ovs_context.SharedSecretContext()() if 'shared_secret' in ctxt: @@ -438,6 +505,14 @@ def use_dpdk(): return False +def enable_sriov_agent(): + '''Determine with SR-IOV agent should be used''' + release = os_release('neutron-common', base='icehouse') + if (release >= 'mitaka' and config('enable-sriov')): + return True + return False + + # TODO: update into charm-helpers to add port_type parameter def dpdk_add_bridge_port(name, port, promisc=False, port_type=None): ''' Add a port to the named openvswitch bridge ''' diff --git a/hooks/pci.py b/hooks/pci.py index 90299d18..034ac21f 100644 --- a/hooks/pci.py +++ b/hooks/pci.py @@ -18,9 +18,6 @@ import os import glob import subprocess import shlex -from charmhelpers.core.hookenv import( - log, -) def format_pci_addr(pci_addr): @@ -30,6 +27,126 @@ def format_pci_addr(pci_addr): func) +def get_sysnet_interfaces_and_macs(): + '''Catalog interface information from local system + + each device dict contains: + + interface: logical name + mac_address: MAC address + pci_address: PCI address + state: Current interface state (up/down) + sriov: Boolean indicating whether inteface is an SR-IOV + capable device. + sriov_totalvfs: Total VF capacity of device + sriov_numvfs: Configured VF capacity of device + + :returns: array: of dict objects containing details of each interface + ''' + net_devs = [] + for sdir in glob.glob('/sys/class/net/*'): + sym_link = sdir + "/device" + if os.path.islink(sym_link): + fq_path = os.path.realpath(sym_link) + path = fq_path.split('/') + if 'virtio' in path[-1]: + pci_address = path[-2] + else: + pci_address = path[-1] + device = { + 'interface': get_sysnet_interface(sdir), + 'mac_address': get_sysnet_mac(sdir), + 'pci_address': pci_address, + 'state': get_sysnet_device_state(sdir), + 'sriov': is_sriov(sdir) + } + if device['sriov']: + device['sriov_totalvfs'] = \ + get_sriov_totalvfs(sdir) + device['sriov_numvfs'] = \ + get_sriov_numvfs(sdir) + net_devs.append(device) + + return net_devs + + +def get_sysnet_mac(sysdir): + '''Read MAC address for a device + + :sysdir: string: path to device /sys directory + + :returns: string: MAC address of device + ''' + mac_addr_file = sysdir + '/address' + with open(mac_addr_file, 'r') as f: + read_data = f.read() + mac = read_data.strip() + return mac + + +def get_sysnet_device_state(sysdir): + '''Read operational state of a device + + :sysdir: string: path to device /sys directory + + :returns: string: current device state + ''' + state_file = sysdir + '/operstate' + with open(state_file, 'r') as f: + read_data = f.read() + state = read_data.strip() + return state + + +def is_sriov(sysdir): + '''Determine whether a device is SR-IOV capable + + :sysdir: string: path to device /sys directory + + :returns: boolean: indicating whether device is SR-IOV + capable or not. + ''' + return os.path.exists(os.path.join(sysdir, + 'device', + 'sriov_totalvfs')) + + +def get_sriov_totalvfs(sysdir): + '''Read total VF capacity for a device + + :sysdir: string: path to device /sys directory + + :returns: int: number of VF's the device supports + ''' + sriov_totalvfs_file = os.path.join(sysdir, + 'device', + 'sriov_totalvfs') + with open(sriov_totalvfs_file, 'r') as f: + read_data = f.read() + sriov_totalvfs = int(read_data.strip()) + return sriov_totalvfs + + +def get_sriov_numvfs(sysdir): + '''Read configured VF capacity for a device + + :sysdir: string: path to device /sys directory + + :returns: int: number of VF's the device is configured for + ''' + sriov_numvfs_file = os.path.join(sysdir, + 'device', + 'sriov_numvfs') + with open(sriov_numvfs_file, 'r') as f: + read_data = f.read() + sriov_numvfs = int(read_data.strip()) + return sriov_numvfs + + +def get_sysnet_interface(sysdir): + return sysdir.split('/')[-1] + + class PCINetDevice(object): def __init__(self, pci_address): @@ -37,59 +154,38 @@ class PCINetDevice(object): self.interface_name = None self.mac_address = None self.state = None + self.sriov = False self.update_attributes() def update_attributes(self): self.update_interface_info() def update_interface_info(self): - self.update_interface_info_eth() - - def update_interface_info_eth(self): - net_devices = self.get_sysnet_interfaces_and_macs() + net_devices = get_sysnet_interfaces_and_macs() for interface in net_devices: if self.pci_address == interface['pci_address']: self.interface_name = interface['interface'] self.mac_address = interface['mac_address'] self.state = interface['state'] + self.sriov = interface['sriov'] + if self.sriov: + self.sriov_totalvfs = interface['sriov_totalvfs'] + self.sriov_numvfs = interface['sriov_numvfs'] - def get_sysnet_interfaces_and_macs(self): - net_devs = [] - for sdir in glob.glob('/sys/class/net/*'): - sym_link = sdir + "/device" - if os.path.islink(sym_link): - fq_path = os.path.realpath(sym_link) - path = fq_path.split('/') - if 'virtio' in path[-1]: - pci_address = path[-2] - else: - pci_address = path[-1] - net_devs.append({ - 'interface': self.get_sysnet_interface(sdir), - 'mac_address': self.get_sysnet_mac(sdir), - 'pci_address': pci_address, - 'state': self.get_sysnet_device_state(sdir), - }) - return net_devs + def set_sriov_numvfs(self, numvfs): + '''Set the number of VF devices for a SR-IOV PF - def get_sysnet_mac(self, sysdir): - mac_addr_file = sysdir + '/address' - with open(mac_addr_file, 'r') as f: - read_data = f.read() - mac = read_data.strip() - log('mac from {} is {}'.format(mac_addr_file, mac)) - return mac + Assuming the device is an SR-IOV device, this function will attempt + to change the number of VF's created by the PF. - def get_sysnet_device_state(self, sysdir): - state_file = sysdir + '/operstate' - with open(state_file, 'r') as f: - read_data = f.read() - state = read_data.strip() - log('state from {} is {}'.format(state_file, state)) - return state - - def get_sysnet_interface(self, sysdir): - return sysdir.split('/')[-1] + @param numvfs: integer to set the current number of VF's to + ''' + if self.sriov: + sdevice = os.path.join('/sys/class/net', + self.interface_name, + 'device', 'sriov_numvfs') + with open(sdevice, 'w') as sh: + sh.write(str(numvfs)) class PCINetDevices(object): @@ -131,3 +227,9 @@ class PCINetDevices(object): if pcidev.pci_address == pci_addr: return pcidev return None + + def get_device_from_interface_name(self, interface_name): + for pcidev in self.pci_devices: + if pcidev.interface_name == interface_name: + return pcidev + return None diff --git a/templates/mitaka/sriov_agent.ini b/templates/mitaka/sriov_agent.ini new file mode 100644 index 00000000..3c7b1383 --- /dev/null +++ b/templates/mitaka/sriov_agent.ini @@ -0,0 +1,12 @@ +# mitaka +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +# Config managed by neutron-openvswitch charm +############################################################################### +[securitygroup] +firewall_driver = neutron.agent.firewall.NoopFirewallDriver + +[sriov_nic] +physical_device_mappings = {{ sriov_device_mappings }} +exclude_devices = diff --git a/unit_tests/test_neutron_ovs_hooks.py b/unit_tests/test_neutron_ovs_hooks.py index 76f9f9c9..26b596c7 100644 --- a/unit_tests/test_neutron_ovs_hooks.py +++ b/unit_tests/test_neutron_ovs_hooks.py @@ -41,6 +41,7 @@ TO_PATCH = [ 'relation_ids', 'relation_set', 'configure_ovs', + 'configure_sriov', 'use_dvr', 'install_packages', 'purge_packages', @@ -99,6 +100,7 @@ class NeutronOVSHooksTests(CharmTestCase): self.assertTrue(self.CONFIGS.write_all.called) self.assertTrue(_zmq_joined.called_with('relid')) self.configure_ovs.assert_called_with() + self.configure_sriov.assert_called_with() @patch.object(hooks, 'git_install_requested') @patch.object(hooks, 'config_value_changed') diff --git a/unit_tests/test_neutron_ovs_utils.py b/unit_tests/test_neutron_ovs_utils.py index 75a4f78c..aef7b3d3 100644 --- a/unit_tests/test_neutron_ovs_utils.py +++ b/unit_tests/test_neutron_ovs_utils.py @@ -51,6 +51,8 @@ TO_PATCH = [ 'status_set', 'use_dpdk', 'os_application_version_set', + 'remote_restart', + 'PCINetDevices', ] head_pkg = 'linux-headers-3.15.0-5-generic' @@ -596,3 +598,99 @@ class TestNeutronOVSUtils(CharmTestCase): asf.assert_called_once_with('some-config') # ports=None whilst port checks are disabled. f.assert_called_once_with('assessor', services='s1', ports=None) + + def _configure_sriov_base(self, config, + changed=False): + self.mock_config = MagicMock() + self.config.side_effect = None + self.config.return_value = self.mock_config + self.mock_config.get.side_effect = lambda x: config.get(x) + self.mock_config.changed.return_value = changed + + self.mock_eth_device = MagicMock() + self.mock_eth_device.sriov = False + self.mock_eth_device.interface_name = 'eth0' + self.mock_eth_device.sriov_totalvfs = 0 + + self.mock_sriov_device = MagicMock() + self.mock_sriov_device.sriov = True + self.mock_sriov_device.interface_name = 'ens0' + self.mock_sriov_device.sriov_totalvfs = 64 + + self.mock_sriov_device2 = MagicMock() + self.mock_sriov_device2.sriov = True + self.mock_sriov_device2.interface_name = 'ens49' + self.mock_sriov_device2.sriov_totalvfs = 64 + + self.pci_devices = { + 'eth0': self.mock_eth_device, + 'ens0': self.mock_sriov_device, + 'ens49': self.mock_sriov_device2, + } + + mock_pci_devices = MagicMock() + mock_pci_devices.pci_devices = [ + self.mock_eth_device, + self.mock_sriov_device, + self.mock_sriov_device2 + ] + self.PCINetDevices.return_value = mock_pci_devices + + mock_pci_devices.get_device_from_interface_name.side_effect = \ + lambda x: self.pci_devices.get(x) + + def test_configure_sriov_no_changes(self): + _config = { + 'enable-sriov': True, + 'sriov-numvfs': 'auto' + } + self._configure_sriov_base(_config, False) + + nutils.configure_sriov() + + self.assertFalse(self.remote_restart.called) + + def test_configure_sriov_auto(self): + _config = { + 'enable-sriov': True, + 'sriov-numvfs': 'auto' + } + self._configure_sriov_base(_config, True) + + nutils.configure_sriov() + + self.mock_sriov_device.set_sriov_numvfs.assert_called_with( + self.mock_sriov_device.sriov_totalvfs + ) + self.mock_sriov_device2.set_sriov_numvfs.assert_called_with( + self.mock_sriov_device2.sriov_totalvfs + ) + self.assertTrue(self.remote_restart.called) + + def test_configure_sriov_numvfs(self): + _config = { + 'enable-sriov': True, + 'sriov-numvfs': '32', + } + self._configure_sriov_base(_config, True) + + nutils.configure_sriov() + + self.mock_sriov_device.set_sriov_numvfs.assert_called_with(32) + self.mock_sriov_device2.set_sriov_numvfs.assert_called_with(32) + + self.assertTrue(self.remote_restart.called) + + def test_configure_sriov_numvfs_per_device(self): + _config = { + 'enable-sriov': True, + 'sriov-numvfs': 'ens0:32 sriov23:64' + } + self._configure_sriov_base(_config, True) + + nutils.configure_sriov() + + self.mock_sriov_device.set_sriov_numvfs.assert_called_with(32) + self.mock_sriov_device2.set_sriov_numvfs.assert_not_called() + + self.assertTrue(self.remote_restart.called) diff --git a/unit_tests/test_pci.py b/unit_tests/test_pci.py index 4a99b984..a5043b35 100644 --- a/unit_tests/test_pci.py +++ b/unit_tests/test_pci.py @@ -26,7 +26,6 @@ import pci TO_PATCH = [ 'glob', - 'log', 'subprocess', ] NOT_JSON = "Im not json" @@ -73,43 +72,48 @@ class PCINetDeviceTest(CharmTestCase): 'mac_address': 'a8:9d:21:cf:93:fc', 'pci_address': '0000:10:00.0', 'state': 'up', + 'sriov': False, } self.assertTrue(check_device(net, expect)) - @patch('pci.PCINetDevice.get_sysnet_interfaces_and_macs') + @patch('pci.get_sysnet_interfaces_and_macs') @patch('pci.PCINetDevice.update_attributes') - def test_update_interface_info_eth(self, _update, _sysnet_ints): + def test_update_interface_info(self, _update, _sysnet_ints): dev = pci.PCINetDevice('0000:10:00.0') _sysnet_ints.return_value = [ { 'interface': 'eth2', 'mac_address': 'a8:9d:21:cf:93:fc', 'pci_address': '0000:10:00.0', - 'state': 'up' + 'state': 'up', + 'sriov': False, }, { 'interface': 'eth3', 'mac_address': 'a8:9d:21:cf:93:fd', 'pci_address': '0000:10:00.1', - 'state': 'down' + 'state': 'down', + 'sriov': False, } ] - dev.update_interface_info_eth() + dev.update_interface_info() self.assertEqual(dev.interface_name, 'eth2') @patch('os.path.islink') @patch('os.path.realpath') - @patch('pci.PCINetDevice.get_sysnet_device_state') - @patch('pci.PCINetDevice.get_sysnet_mac') - @patch('pci.PCINetDevice.get_sysnet_interface') + @patch('pci.is_sriov') + @patch('pci.get_sysnet_device_state') + @patch('pci.get_sysnet_mac') + @patch('pci.get_sysnet_interface') @patch('pci.PCINetDevice.update_attributes') def test_get_sysnet_interfaces_and_macs(self, _update, _interface, _mac, - _state, _osrealpath, _osislink): - dev = pci.PCINetDevice('0000:06:00.0') + _state, _sriov, _osrealpath, + _osislink): self.glob.glob.return_value = ['/sys/class/net/eth2'] _interface.return_value = 'eth2' _mac.return_value = 'a8:9d:21:cf:93:fc' _state.return_value = 'up' + _sriov.return_value = False _osrealpath.return_value = ('/sys/devices/pci0000:00/0000:00:02.0/' '0000:02:00.0/0000:03:00.0/0000:04:00.0/' '0000:05:01.0/0000:07:00.0') @@ -118,23 +122,26 @@ class PCINetDeviceTest(CharmTestCase): 'mac_address': 'a8:9d:21:cf:93:fc', 'pci_address': '0000:07:00.0', 'state': 'up', + 'sriov': False, } - self.assertEqual(dev.get_sysnet_interfaces_and_macs(), [expect]) + self.assertEqual(pci.get_sysnet_interfaces_and_macs(), [expect]) @patch('os.path.islink') @patch('os.path.realpath') - @patch('pci.PCINetDevice.get_sysnet_device_state') - @patch('pci.PCINetDevice.get_sysnet_mac') - @patch('pci.PCINetDevice.get_sysnet_interface') + @patch('pci.is_sriov') + @patch('pci.get_sysnet_device_state') + @patch('pci.get_sysnet_mac') + @patch('pci.get_sysnet_interface') @patch('pci.PCINetDevice.update_attributes') def test_get_sysnet_interfaces_and_macs_virtio(self, _update, _interface, - _mac, _state, _osrealpath, + _mac, _state, _sriov, + _osrealpath, _osislink): - dev = pci.PCINetDevice('0000:06:00.0') self.glob.glob.return_value = ['/sys/class/net/eth2'] _interface.return_value = 'eth2' _mac.return_value = 'a8:9d:21:cf:93:fc' _state.return_value = 'up' + _sriov.return_value = False _osrealpath.return_value = ('/sys/devices/pci0000:00/0000:00:07.0/' 'virtio5') expect = { @@ -142,36 +149,34 @@ class PCINetDeviceTest(CharmTestCase): 'mac_address': 'a8:9d:21:cf:93:fc', 'pci_address': '0000:00:07.0', 'state': 'up', + 'sriov': False, } - self.assertEqual(dev.get_sysnet_interfaces_and_macs(), [expect]) + self.assertEqual(pci.get_sysnet_interfaces_and_macs(), [expect]) @patch('pci.PCINetDevice.update_attributes') def test_get_sysnet_mac(self, _update): - device = pci.PCINetDevice('0000:10:00.1') with patch_open() as (_open, _file): super_fh = mocked_filehandle() _file.readlines = MagicMock() _open.side_effect = super_fh._setfilename _file.read.side_effect = super_fh._getfilecontents_read - macaddr = device.get_sysnet_mac('/sys/class/net/eth3') + macaddr = pci.get_sysnet_mac('/sys/class/net/eth3') self.assertEqual(macaddr, 'a8:9d:21:cf:93:fd') @patch('pci.PCINetDevice.update_attributes') def test_get_sysnet_device_state(self, _update): - device = pci.PCINetDevice('0000:10:00.1') with patch_open() as (_open, _file): super_fh = mocked_filehandle() _file.readlines = MagicMock() _open.side_effect = super_fh._setfilename _file.read.side_effect = super_fh._getfilecontents_read - state = device.get_sysnet_device_state('/sys/class/net/eth3') + state = pci.get_sysnet_device_state('/sys/class/net/eth3') self.assertEqual(state, 'down') @patch('pci.PCINetDevice.update_attributes') def test_get_sysnet_interface(self, _update): - device = pci.PCINetDevice('0000:10:00.1') self.assertEqual( - device.get_sysnet_interface('/sys/class/net/eth3'), 'eth3') + pci.get_sysnet_interface('/sys/class/net/eth3'), 'eth3') class PCINetDevicesTest(CharmTestCase): @@ -208,12 +213,14 @@ class PCINetDevicesTest(CharmTestCase): 'mac_address': 'a8:9d:21:cf:93:fc', 'pci_address': '0000:10:00.0', 'state': 'up', + 'sriov': False, }, '0000:10:00.1': { 'interface_name': 'eth3', 'mac_address': 'a8:9d:21:cf:93:fd', 'pci_address': '0000:10:00.1', 'state': 'down', + 'sriov': False, }, } for device in devices.pci_devices: @@ -244,6 +251,7 @@ class PCINetDevicesTest(CharmTestCase): 'mac_address': 'a8:9d:21:cf:93:fd', 'pci_address': '0000:10:00.1', 'state': 'down', + 'sriov': False, }, } self.assertTrue(check_device( diff --git a/unit_tests/test_pci_helper.py b/unit_tests/test_pci_helper.py index 1c21ebe9..5ef9497c 100644 --- a/unit_tests/test_pci_helper.py +++ b/unit_tests/test_pci_helper.py @@ -86,12 +86,14 @@ def mocked_realpath(link): return pci_responses.SYS_TREE[resolved_link] +@patch('pci.cached') @patch('pci.log') @patch('pci.subprocess.Popen') @patch('pci.subprocess.check_output') @patch('pci.glob.glob') @patch('pci.os.path.islink') -def pci_devs(_osislink, _glob, _check_output, _Popen, _log, subproc_map=None): +def pci_devs(_osislink, _glob, _check_output, _Popen, _log, + _cached, subproc_map=None): _glob.side_effect = mocked_globs _osislink.side_effect = mocked_islink _check_output.side_effect = mocked_subprocess(