diff --git a/etc/os-net-config/samples/sriov_pf.json b/etc/os-net-config/samples/sriov_pf.json index f2235fc6..f3d0f155 100644 --- a/etc/os-net-config/samples/sriov_pf.json +++ b/etc/os-net-config/samples/sriov_pf.json @@ -4,7 +4,8 @@ "type": "sriov_pf", "name": "p2p1", "numvfs": 10, - "use_dhcp": false + "use_dhcp": false, + "promisc": on }, { "type": "sriov_vf", @@ -14,7 +15,31 @@ { "ip_netmask": "192.0.2.1/24" } - ] + ], + "vlan_id": 100, + "qos": 2, + "spoofcheck": true, + "macaddr": "00:78:90:80:cc:30", + "trust": true, + "state": auto, + "promisc": false + }, + { + "type": "ovs_bridge", + "name": "br-vfs", + "members": [ + { + "type": "sriov_vf", + "vfid": 1, + "trust": true, + "device": "p2p1", + "promisc": true, + "vlan_id": 116, + "qos": 2, + "spoofcheck": false + } + ], + "use_dhcp": true } ] } diff --git a/etc/os-net-config/samples/sriov_pf.yaml b/etc/os-net-config/samples/sriov_pf.yaml index 7458e4eb..28c1c8d7 100644 --- a/etc/os-net-config/samples/sriov_pf.yaml +++ b/etc/os-net-config/samples/sriov_pf.yaml @@ -13,6 +13,9 @@ network_config: numvfs: 10 # Dont set the IP address on the PF use_dhcp: false + # Allow all the traffic received. It might be needed when one of its VF + # is attached to a ovs bridge + promisc: on # sriov_vf type shall be used to configure the VF's of NICs. # It requires the PF devices to be configured via sriov_pf types @@ -27,3 +30,56 @@ network_config: vfid: 5 addresses: - ip_netmask: 192.0.2.1/24 + # When specified, all traffic sent from the VF will be tagged with the + # specified VLAN ID. Incoming traffic will be filtered for the specified + # VLAN ID, and will have all VLAN tags stripped before being passed to the + # VF. Setting this parameter to 0 disables VLAN tagging and filtering. + vlan_id: 100 + # VLAN-Quality Of Service (priority) bits for the VLAN tag. + # When specified, all VLAN tags transmitted by the VF will include the + # specified priority bits in the VLAN tag. Requires vlan_id + # Default value is 0. + qos: 2 + # MAC spoofing is a method of altering the MAC address + # The MAC address anti-spoofing when enabled protects from malicious MAC + # address spoofing. It should be disabled for 802.3ad bonds. + spoofcheck: on + # Change the MAC address of the VF. + macaddr: 00:78:90:80:cc:30 + # Enabling trust (true) for VF allows enabling multicast/promiscuous mode + # on the VF. The default value is off. + trust: on + # Link state seen by the VF + # - auto: a reflection of the PF link state (default) + # - enable: lets the VF to communicate with other VFs on this host even + # if the PF link state is down + # - disable: causes the HW to drop any packets sent by the VF. + state: auto + # Enabling promisc mode allows the traffic originally targeted to go to the + # VF and it will also receive the unmatched traffic and all the multicast + # traffic received in the physical port. Note that all traffic that has + # destination mac that does not match any of the VFs/PF MAC addresses is + # referred to as unmatched traffic. + # The default value is off. Requires the enabling of trust mode + promisc: off + # Attach a SR-IOV VF to a ovs bridge + - type: ovs_bridge + name: br-vfs + use_dhcp: true + members: + # Specify the type + - type: sriov_vf + # Required field + device: p2p1 + # Required field + vfid: 1 + # Optional field + vlan_id: 116 + # Optional field, but requires vlan_id + qos: 3 + # Set trust to 'on' when attaching to ovs_bridge + trust: on + # Set promisc to 'on' when attaching to ovs_bridge + promisc: on + # Set spoofcheck to 'off' when attaching to ovs_bridge + spoofcheck: off diff --git a/os_net_config/cli.py b/os_net_config/cli.py index f0678efb..4b729f7c 100644 --- a/os_net_config/cli.py +++ b/os_net_config/cli.py @@ -249,9 +249,16 @@ def main(argv=sys.argv): # Look for the presence of SriovPF types in the first parse of the json # if SriovPFs exists then PF devices needs to be configured so that the VF # devices are created. + # The VFs will not be available now and an exception + # SriovVfNotFoundException will be raised while fetching the device name. + # After the first parse the SR-IOV PF devices would be configured and the + # VF devices would be created. # In the second parse, all other objects shall be added for iface_json in iface_array: - obj = objects.object_from_json(iface_json) + try: + obj = objects.object_from_json(iface_json) + except utils.SriovVfNotFoundException: + continue if isinstance(obj, objects.SriovPF): configure_sriov = True provider.add_object(obj) @@ -263,11 +270,20 @@ def main(argv=sys.argv): # All objects other than the sriov_pf will be added here. # The VFs are expected to be available now and an exception # SriovVfNotFoundException shall be raised if not available. - obj = objects.object_from_json(iface_json) + try: + obj = objects.object_from_json(iface_json) + except utils.SriovVfNotFoundException: + if not opts.noop: + raise if not isinstance(obj, objects.SriovPF): provider.add_object(obj) + + if configure_sriov and not opts.noop: + utils.configure_sriov_vfs() + files_changed = provider.apply(cleanup=opts.cleanup, activate=not opts.no_activate) + if opts.noop: for location, data in files_changed.items(): print("File: %s\n" % location) diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index e4cc98fa..00f5530a 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -140,7 +140,6 @@ class IfcfgNetConfig(os_net_config.NetConfig): def _add_common(self, base_opt): ovs_extra = [] - data = "# This file is autogenerated by os-net-config\n" data += "DEVICE=%s\n" % base_opt.name if base_opt.onboot: @@ -678,7 +677,8 @@ class IfcfgNetConfig(os_net_config.NetConfig): logger.info('adding sriov pf: %s' % sriov_pf.name) data = self._add_common(sriov_pf) logger.debug('sriov pf data: %s' % data) - utils.update_sriov_pf_map(sriov_pf.name, sriov_pf.numvfs, self.noop) + utils.update_sriov_pf_map(sriov_pf.name, sriov_pf.numvfs, + self.noop, promisc=sriov_pf.promisc) self.interface_data[sriov_pf.name] = data def add_sriov_vf(self, sriov_vf): @@ -686,13 +686,6 @@ class IfcfgNetConfig(os_net_config.NetConfig): :param sriov_vf: The SriovVF object to add """ - # Retrieve the VF's name, using its PF device name and VF id. - # Note: The VF's name could be read only after setting the numvfs of - # the corresponding parent PF device. Untill this point the name field - # for VFs will be a empty string. An exception SriovVfNotFoundException - # shall be raised when the VF could not be found - sriov_vf.name = utils.get_vf_devname(sriov_vf.device, sriov_vf.vfid, - self.noop) logger.info('adding sriov vf: %s for pf: %s, vfid: %d' % (sriov_vf.name, sriov_vf.device, sriov_vf.vfid)) data = self._add_common(sriov_vf) diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 5e070f28..86ac1f48 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -1121,7 +1121,8 @@ class SriovVF(_BaseOpts): addresses=None, routes=None, mtu=None, primary=False, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, dns_servers=None, nm_controlled=False, - onboot=True): + onboot=True, vlan_id=0, qos=0, spoofcheck=None, + trust=None, state=None, macaddr=None, promisc=None): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -1131,13 +1132,38 @@ class SriovVF(_BaseOpts): # Empty strings are set for the name field. # The provider shall identify the VF name from the PF device name # (device) and the VF id. - super(SriovVF, self).__init__("", use_dhcp, use_dhcpv6, addresses, + name = utils.get_vf_devname(device, vfid) + super(SriovVF, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled, onboot) - self.vfid = vfid + self.vfid = int(vfid) self.device = device + self.vlan_id = int(vlan_id) + self.qos = int(qos) + self.spoofcheck = spoofcheck + self.trust = trust + self.state = state + self.macaddr = macaddr + self.promisc = promisc + utils.update_sriov_vf_map(device, self.vfid, name, + vlan_id=self.vlan_id, + qos=self.qos, + spoofcheck=spoofcheck, + trust=trust, + state=state, + macaddr=macaddr, + promisc=promisc) + + @staticmethod + def get_on_off(config): + rval = None + if config: + rval = "on" + elif config is False: + rval = "off" + return rval @staticmethod def from_json(json): @@ -1146,7 +1172,22 @@ class SriovVF(_BaseOpts): # Get the PF device name device = _get_required_field(json, 'device', 'SriovVF') opts = _BaseOpts.base_opts_from_json(json) - return SriovVF(device, vfid, *opts) + vlan_id = json.get('vlan_id', 0) + qos = json.get('qos', 0) + if qos != 0 and vlan_id == 0: + msg = "Vlan tag not set for QOS - VF: %s:%d" % (device, vfid) + raise InvalidConfigException(msg) + spoofcheck = SriovVF.get_on_off(json.get('spoofcheck')) + trust = SriovVF.get_on_off(json.get('trust')) + promisc = SriovVF.get_on_off(json.get('promisc')) + state = json.get('state') + if state not in [None, 'auto', 'enable', 'disable']: + msg = 'Expecting state to match auto/enable/disable' + raise InvalidConfigException(msg) + macaddr = json.get('macaddr') + return SriovVF(device, vfid, *opts, vlan_id=vlan_id, qos=qos, + spoofcheck=spoofcheck, trust=trust, state=state, + macaddr=macaddr, promisc=promisc) class SriovPF(_BaseOpts): @@ -1156,7 +1197,7 @@ class SriovPF(_BaseOpts): addresses=None, routes=None, mtu=None, primary=False, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, dns_servers=None, nm_controlled=False, - onboot=True, members=None): + onboot=True, members=None, promisc=None): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -1165,19 +1206,25 @@ class SriovPF(_BaseOpts): persist_mapping, defroute, dhclient_args, dns_servers, nm_controlled, onboot) - self.numvfs = numvfs + self.numvfs = int(numvfs) mapped_nic_names = mapped_nics(nic_mapping) if name in mapped_nic_names: self.name = mapped_nic_names[name] else: self.name = name + self.promisc = promisc @staticmethod def from_json(json): name = _get_required_field(json, 'name', 'SriovPF') numvfs = _get_required_field(json, 'numvfs', 'SriovPF') + promisc = json.get('promisc', None) + if promisc is True: + promisc = "on" + elif promisc is False: + promisc = "off" opts = _BaseOpts.base_opts_from_json(json) - return SriovPF(name, numvfs, *opts) + return SriovPF(name, numvfs, *opts, promisc=promisc) class OvsDpdkBond(_BaseOpts): diff --git a/os_net_config/schema.yaml b/os_net_config/schema.yaml index ac61a302..657c4977 100644 --- a/os_net_config/schema.yaml +++ b/os_net_config/schema.yaml @@ -30,7 +30,13 @@ definitions: type: string pattern: "(?i)^(t|true|on|y|yes|1|f|false|off|n|no|0)$" - $ref: "#/definitions/param" - + sriov_vf_state_string: + type: string + pattern: "^(auto|enable|disable)$" + # MAC address type + mac_address_string: + type: string + pattern: "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" # IP address and address+prefix types ipv4_address_string: type: string @@ -256,6 +262,8 @@ definitions: $ref: "#/definitions/bool_or_param" numvfs: $ref: "#/definitions/int_or_param" + promisc: + $ref: "#/definitions/bool_or_param" hotplug: $ref: "#/definitions/bool_or_param" # common options: @@ -300,8 +308,22 @@ definitions: $ref: "#/definitions/string_or_param" vfid: $ref: "#/definitions/int_or_param" + vlan_id: + $ref: "#/definitions/int_or_param" + qos: + $ref: "#/definitions/int_or_param" hotplug: $ref: "#/definitions/bool_or_param" + spoofcheck: + $ref: "#/definitions/bool_or_param" + trust: + $ref: "#/definitions/bool_or_param" + promisc: + $ref: "#/definitions/bool_or_param" + macaddr: + $ref: "#/definitions/mac_address_string" + state: + $ref: "#/definitions/sriov_vf_state_string" # common options: use_dhcp: $ref: "#/definitions/bool_or_param" @@ -391,6 +413,7 @@ definitions: - $ref: "#/definitions/ovs_bond" - $ref: "#/definitions/ovs_tunnel" - $ref: "#/definitions/ovs_patch_port" + - $ref: "#/definitions/sriov_vf" ovs_options: $ref: "#/definitions/ovs_options_string_or_param" ovs_extra: diff --git a/os_net_config/sriov_config.py b/os_net_config/sriov_config.py index dece520a..8e867101 100644 --- a/os_net_config/sriov_config.py +++ b/os_net_config/sriov_config.py @@ -21,29 +21,53 @@ # it for the first time configuration. # An entry point os-net-config-sriov is added for invocation of this module. +import argparse import logging import os +import pyudev +from six.moves import queue as Queue import sys -import time import yaml -logger = logging.getLogger(__name__) +from oslo_concurrency import processutils -# File to contain the list of SR-IOV nics and the numvfs +logger = logging.getLogger(__name__) +# Create a queue for passing the udev network events +vf_queue = Queue.Queue() + + +# File to contain the list of SR-IOV PF, VF and their configurations # Format of the file shall be -# -# - name: eth1 -# numvfs: 5 -_SRIOV_PF_CONFIG_FILE = '/var/lib/os-net-config/sriov_pf.yaml' -_SYS_CLASS_NET = '/sys/class/net' -# maximum retries for checking the creation of VFs -_MAX_SRIOV_VFS_CONFIG_RETRIES = 60 +# - device_type: pf +# name: +# numvfs: +# promisc: "on"/"off" +# - device_type: vf +# device: +# name: +# vfid: +# name: +# vlan_id: +# qos: +# spoofcheck: "on"/"off" +# trust: "on"/"off" +# state: "auto"/"enable"/"disable" +# macaddr: +# promisc: "on"/"off" +_SRIOV_CONFIG_FILE = '/var/lib/os-net-config/sriov_config.yaml' class SRIOVNumvfsException(ValueError): pass +def udev_event_handler(action, device): + event = {"action": action, "device": device.sys_path} + logger.info("Received udev event %s for %s" + % (event["action"], event["device"])) + vf_queue.put(event) + + def get_file_data(filename): if not os.path.exists(filename): return '' @@ -55,55 +79,161 @@ def get_file_data(filename): return '' -def _get_sriov_pf_map(): - contents = get_file_data(_SRIOV_PF_CONFIG_FILE) - sriov_pf_map = yaml.load(contents) if contents else [] - return sriov_pf_map +def _get_sriov_map(): + contents = get_file_data(_SRIOV_CONFIG_FILE) + sriov_map = yaml.load(contents) if contents else [] + return sriov_map -def _configure_sriov_pf(): - sriov_pf_map = _get_sriov_pf_map() - for item in sriov_pf_map: - try: - sriov_numvfs_path = ("/sys/class/net/%s/device/sriov_numvfs" - % item['name']) - with open(sriov_numvfs_path, 'w') as f: - f.write("%d" % item['numvfs']) - except IOError as exc: - msg = ("Unable to configure pf: %s with numvfs: %d\n%s" - % (item['name'], item['numvfs'], exc)) - raise SRIOVNumvfsException(msg) +def configure_sriov_pf(): + # Create a context for pyudev and observe udev events for network + context = pyudev.Context() + monitor = pyudev.Monitor.from_netlink(context) + monitor.filter_by('net') + observer = pyudev.MonitorObserver(monitor, udev_event_handler) + observer.start() - -def _wait_for_vf_creation(): - sriov_map = _get_sriov_pf_map() + sriov_map = _get_sriov_map() for item in sriov_map: - count = 0 - while count < _MAX_SRIOV_VFS_CONFIG_RETRIES: - pf = item['name'] - numvfs = item['numvfs'] - vf_path = os.path.join(_SYS_CLASS_NET, pf, - "device/virtfn%d/net" % (numvfs - 1)) - if os.path.isdir(vf_path): - vf_nic = os.listdir(vf_path) - if len(vf_nic) == 1 and pf in vf_nic[0]: - logger.info("VFs created for PF: %s" % pf) - break + if item['device_type'] == 'pf': + _pf_interface_up(item) + try: + sriov_numvfs_path = ("/sys/class/net/%s/device/sriov_numvfs" + % item['name']) + with open(sriov_numvfs_path, 'w') as f: + f.write("%d" % item['numvfs']) + except IOError as exc: + msg = ("Unable to configure pf: %s with numvfs: %d\n%s" + % (item['name'], item['numvfs'], exc)) + raise SRIOVNumvfsException(msg) + # Wait for the creation of VFs for each PF + _wait_for_vf_creation(item['name'], item['numvfs']) + observer.stop() + + +def _wait_for_vf_creation(pf_name, numvfs): + vf_count = 0 + vf_list = [] + while vf_count < numvfs: + try: + # wait for 5 seconds after every udev event + event = vf_queue.get(True, 5) + vf_name = os.path.basename(event["device"]) + pf_path = os.path.normpath(os.path.join(event["device"], + "../../physfn/net")) + if os.path.isdir(pf_path): + pf_nic = os.listdir(pf_path) + if len(pf_nic) == 1 and pf_name == pf_nic[0]: + if vf_name not in vf_list: + vf_list.append(vf_name) + logger.info("VF: %s created for PF: %s" + % (vf_name, pf_name)) + vf_count = vf_count + 1 else: - logger.debug("VF device name not present for PF %s" % pf) + logger.error("Unable to parse event %s" % event["device"]) else: - logger.info("Attempt#%d, VFs for PF %s is not yet created" - % (count + 1, pf)) - time.sleep(1) - count += 1 + logger.error("%s is not a directory" % pf_path) + except Queue.Empty: + logger.info("Timeout in the creation of VFs for PF %s" % pf_name) + return + logger.info("Required VFs are created for PF %s" % pf_name) -def main(argv=None): +def run_ip_config_cmd(*cmd, **kwargs): + logger.info("Running %s" % ' '.join(cmd)) + try: + processutils.execute(*cmd, **kwargs) + except processutils.ProcessExecutionError: + logger.error("Failed to execute %s" % ' '.join(cmd)) + raise + + +def _pf_interface_up(pf_device): + if 'promisc' in pf_device: + run_ip_config_cmd('ip', 'link', 'set', 'dev', pf_device['name'], + 'promisc', pf_device['promisc']) + logger.info("Bringing up PF: %s" % pf_device['name']) + run_ip_config_cmd('ip', 'link', 'set', 'dev', pf_device['name'], 'up') + + +def configure_sriov_vf(): + sriov_map = _get_sriov_map() + for item in sriov_map: + if item['device_type'] == 'vf': + pf_name = item['device']['name'] + vfid = item['device']['vfid'] + base_cmd = ('ip', 'link', 'set', 'dev', pf_name, 'vf', str(vfid)) + logger.info("Configuring settings for PF: %s VF :%d VF name : %s" + % (pf_name, vfid, item['name'])) + if 'macaddr' in item: + cmd = base_cmd + ('mac', item['macaddr']) + run_ip_config_cmd(*cmd) + if 'vlan_id' in item: + vlan_cmd = base_cmd + ('vlan', str(item['vlan_id'])) + if 'qos' in item: + vlan_cmd = vlan_cmd + ('qos', str(item['qos'])) + run_ip_config_cmd(*vlan_cmd) + if 'spoofcheck' in item: + cmd = base_cmd + ('spoofchk', item['spoofcheck']) + run_ip_config_cmd(*cmd) + if 'state' in item: + cmd = base_cmd + ('state', item['state']) + run_ip_config_cmd(*cmd) + if 'trust' in item: + cmd = base_cmd + ('trust', item['trust']) + run_ip_config_cmd(*cmd) + if 'promisc' in item: + run_ip_config_cmd('ip', 'link', 'set', 'dev', item['name'], + 'promisc', item['promisc']) + + +def parse_opts(argv): + + parser = argparse.ArgumentParser( + description='Configure SR-IOV PF and VF interfaces using a YAML' + ' config file format.') + + parser.add_argument( + '-d', '--debug', + dest="debug", + action='store_true', + help="Print debugging output.", + required=False) + + parser.add_argument( + '-v', '--verbose', + dest="verbose", + action='store_true', + help="Print verbose output.", + required=False) + + opts = parser.parse_args(argv[1:]) + + return opts + + +def configure_logger(verbose=False, debug=False): + LOG_FORMAT = '[%(asctime)s] [%(levelname)s] %(message)s' + DATE_FORMAT = '%Y/%m/%d %I:%M:%S %p' + log_level = logging.WARN + + if debug: + log_level = logging.DEBUG + elif verbose: + log_level = logging.INFO + + logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT, + level=log_level) + + +def main(argv=sys.argv): + opts = parse_opts(argv) + configure_logger(opts.verbose, opts.debug) # Configure the PF's - _configure_sriov_pf() - # Wait for the VF's to get created - _wait_for_vf_creation() + configure_sriov_pf() + # Configure the VFs + configure_sriov_vf() if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv)) diff --git a/os_net_config/tests/test_cli.py b/os_net_config/tests/test_cli.py index c862d924..f9b9756e 100644 --- a/os_net_config/tests/test_cli.py +++ b/os_net_config/tests/test_cli.py @@ -15,11 +15,13 @@ # under the License. import os.path +import random import sys import yaml import os_net_config from os_net_config import cli +from os_net_config import sriov_config from os_net_config.tests import base import six @@ -31,6 +33,16 @@ SAMPLE_BASE = os.path.join(REALPATH, '../../', 'etc', class TestCli(base.TestCase): + def setUp(self): + super(TestCli, self).setUp() + rand = str(int(random.random() * 100000)) + sriov_config._SRIOV_CONFIG_FILE = '/tmp/sriov_config_' + rand + '.yaml' + + def tearDown(self): + super(TestCli, self).tearDown() + if os.path.isfile(sriov_config._SRIOV_CONFIG_FILE): + os.remove(sriov_config._SRIOV_CONFIG_FILE) + def run_cli(self, argstr, exitcodes=(0,)): orig = sys.stdout orig_stderr = sys.stderr @@ -191,6 +203,10 @@ class TestCli(base.TestCase): % interface_yaml, exitcodes=(0,)) def test_sriov_noop_output(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) ivs_yaml = os.path.join(SAMPLE_BASE, 'sriov_pf.yaml') ivs_json = os.path.join(SAMPLE_BASE, 'sriov_pf.json') stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop ' @@ -202,7 +218,8 @@ class TestCli(base.TestCase): '-c %s' % ivs_json) self.assertEqual('', stderr) sanity_devices = ['DEVICE=p2p1', - 'DEVICE=p2p1_5'] + 'DEVICE=p2p1_5', + 'DEVICE=p2p1_1'] for dev in sanity_devices: self.assertIn(dev, stdout_yaml) self.assertEqual(stdout_yaml, stdout_json) diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index ce7e1fc8..b6f9fe5e 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -15,6 +15,7 @@ # under the License. import os.path +import random import tempfile from oslo_concurrency import processutils @@ -22,6 +23,7 @@ from oslo_concurrency import processutils import os_net_config from os_net_config import impl_ifcfg from os_net_config import objects +from os_net_config import sriov_config from os_net_config.tests import base from os_net_config import utils @@ -475,14 +477,17 @@ CPU_LIST=2,3 class TestIfcfgNetConfig(base.TestCase): - def setUp(self): super(TestIfcfgNetConfig, self).setUp() + rand = str(int(random.random() * 100000)) + sriov_config._SRIOV_CONFIG_FILE = '/tmp/sriov_config_' + rand + '.yaml' self.provider = impl_ifcfg.IfcfgNetConfig() def tearDown(self): super(TestIfcfgNetConfig, self).tearDown() + if os.path.isfile(sriov_config._SRIOV_CONFIG_FILE): + os.remove(sriov_config._SRIOV_CONFIG_FILE) def get_interface_config(self, name='em1'): return self.provider.interface_data[name] @@ -1120,18 +1125,31 @@ DNS2=5.6.7.8 bond_data = self.get_linux_bond_config('bond1') self.assertEqual(_NM_CONTROLLED_BOND, bond_data) - def test_network_sriov_vf(self): + def test_network_sriov_vf_without_config(self): nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} self.stubbed_mapped_nics = nic_mapping addresses = [objects.Address('10.0.0.30/24')] - vf = objects.SriovVF(device='nic3', vfid=7, addresses=addresses) - def test_get_vf_devname(device, vfid, noop): - self.assertEqual(device, 'eth2') + def test_update_sriov_vf_map(pf_name, vfid, vf_name, vlan_id=None, + qos=None, spoofcheck=None, trust=None, + state=None, macaddr=None, promisc=None): + self.assertEqual(pf_name, 'eth2') self.assertEqual(vfid, 7) - return 'eth2_7' + self.assertEqual(vlan_id, 0) + self.assertEqual(qos, 0) + self.assertEqual(spoofcheck, None) + self.assertEqual(trust, None) + self.assertEqual(state, None) + self.assertEqual(macaddr, None) + self.stub_out('os_net_config.utils.update_sriov_vf_map', + test_update_sriov_vf_map) + + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', test_get_vf_devname) + vf = objects.SriovVF(device='nic3', vfid=7, addresses=addresses) self.provider.add_sriov_vf(vf) vf_config = """# This file is autogenerated by os-net-config DEVICE=eth2_7 @@ -1145,15 +1163,152 @@ NETMASK=255.255.255.0 """ self.assertEqual(vf_config, self.get_interface_config('eth2_7')) - def test_network_sriov_pf(self): + def test_network_sriov_vf_true(self): + nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} + self.stubbed_mapped_nics = nic_mapping + addresses = [objects.Address('10.0.0.30/24')] + + def test_update_sriov_vf_map(pf_name, vfid, vf_name, vlan_id=None, + qos=None, spoofcheck=None, trust=None, + state=None, macaddr=None, promisc=None): + self.assertEqual(pf_name, 'eth2') + self.assertEqual(vf_name, 'eth2_7') + self.assertEqual(vfid, 7) + self.assertEqual(vlan_id, 100) + self.assertEqual(qos, 10) + self.assertTrue(spoofcheck) + self.assertTrue(trust) + self.assertEqual(state, "auto") + self.assertEqual(macaddr, "AA:BB:CC:DD:EE:FF") + self.assertTrue(promisc) + self.stub_out('os_net_config.utils.update_sriov_vf_map', + test_update_sriov_vf_map) + + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + vf = objects.SriovVF(device='nic3', vfid=7, addresses=addresses, + vlan_id='100', qos='10', spoofcheck=True, + trust=True, state="auto", + macaddr="AA:BB:CC:DD:EE:FF", promisc=True) + + self.provider.add_sriov_vf(vf) + vf_config = """# This file is autogenerated by os-net-config +DEVICE=eth2_7 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +BOOTPROTO=static +IPADDR=10.0.0.30 +NETMASK=255.255.255.0 +""" + self.assertEqual(vf_config, self.get_interface_config('eth2_7')) + + def test_network_sriov_vf_config_false(self): + nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} + self.stubbed_mapped_nics = nic_mapping + addresses = [objects.Address('10.0.0.30/24')] + + def test_update_sriov_vf_map(pf_name, vfid, vf_name, vlan_id=None, + qos=None, spoofcheck=None, trust=None, + state=None, macaddr=None, promisc=None): + self.assertEqual(pf_name, 'eth2') + self.assertEqual(vf_name, 'eth2_7') + self.assertEqual(vfid, 7) + self.assertEqual(vlan_id, 100) + self.assertEqual(qos, 10) + self.assertFalse(spoofcheck) + self.assertFalse(trust) + self.assertEqual(state, "enable") + self.assertEqual(macaddr, "AA:BB:CC:DD:EE:FF") + self.assertFalse(promisc) + self.stub_out('os_net_config.utils.update_sriov_vf_map', + test_update_sriov_vf_map) + + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + vf = objects.SriovVF(device='nic3', vfid=7, addresses=addresses, + vlan_id='100', qos='10', spoofcheck=False, + trust=False, state="enable", + macaddr="AA:BB:CC:DD:EE:FF", promisc=False) + + self.provider.add_sriov_vf(vf) + vf_config = """# This file is autogenerated by os-net-config +DEVICE=eth2_7 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +BOOTPROTO=static +IPADDR=10.0.0.30 +NETMASK=255.255.255.0 +""" + self.assertEqual(vf_config, self.get_interface_config('eth2_7')) + + def test_network_sriov_pf_without_promisc(self): nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} self.stubbed_mapped_nics = nic_mapping pf = objects.SriovPF(name='nic3', numvfs=10) - def test_update_sriov_pf_map(name, numvfs, noop): + def test_update_sriov_pf_map(name, numvfs, noop, promisc=None): self.assertEqual(name, 'eth2') self.assertEqual(numvfs, 10) + self.assertEqual(promisc, None) + self.stub_out('os_net_config.utils.update_sriov_pf_map', + test_update_sriov_pf_map) + self.provider.add_sriov_pf(pf) + pf_config = """# This file is autogenerated by os-net-config +DEVICE=eth2 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +BOOTPROTO=none +""" + self.assertEqual(pf_config, self.get_interface_config('eth2')) + + def test_network_sriov_pf_with_promisc_on(self): + nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} + self.stubbed_mapped_nics = nic_mapping + + pf = objects.SriovPF(name='nic3', numvfs=10, promisc=True) + + def test_update_sriov_pf_map(name, numvfs, noop, promisc=None): + self.assertEqual(name, 'eth2') + self.assertEqual(numvfs, 10) + self.assertTrue(promisc) + self.stub_out('os_net_config.utils.update_sriov_pf_map', + test_update_sriov_pf_map) + self.provider.add_sriov_pf(pf) + pf_config = """# This file is autogenerated by os-net-config +DEVICE=eth2 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=no +PEERDNS=no +BOOTPROTO=none +""" + self.assertEqual(pf_config, self.get_interface_config('eth2')) + + def test_network_sriov_pf_with_promisc_off(self): + nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} + self.stubbed_mapped_nics = nic_mapping + + pf = objects.SriovPF(name='nic3', numvfs=10, promisc=False) + + def test_update_sriov_pf_map(name, numvfs, noop, promisc=None): + self.assertEqual(name, 'eth2') + self.assertEqual(numvfs, 10) + self.assertFalse(promisc) self.stub_out('os_net_config.utils.update_sriov_pf_map', test_update_sriov_pf_map) self.provider.add_sriov_pf(pf) diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 3616951c..a7ee6b5a 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -1210,10 +1210,11 @@ class TestSriovPF(base.TestCase): def test_from_json_numvfs(self): data = '{"type": "sriov_pf", "name": "em1", "numvfs": 16,' \ - '"use_dhcp": false}' + '"use_dhcp": false, "promisc": false}' pf = objects.object_from_json(json.loads(data)) self.assertEqual("em1", pf.name) self.assertEqual(16, pf.numvfs) + self.assertEqual("off", pf.promisc) self.assertFalse(pf.use_dhcp) def test_from_json_numvfs_nic1(self): @@ -1221,37 +1222,148 @@ class TestSriovPF(base.TestCase): return {"nic1": "em4"} self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics) + data = '{"type": "sriov_pf", "name": "nic1", "numvfs": 16,' \ + '"use_dhcp": false, "promisc": true}' + pf = objects.object_from_json(json.loads(data)) + self.assertEqual("em4", pf.name) + self.assertEqual(16, pf.numvfs) + self.assertFalse(pf.use_dhcp) + self.assertEqual('on', pf.promisc) + + def test_from_json_without_promisc(self): + def dummy_mapped_nics(nic_mapping=None): + return {"nic1": "em4"} + self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics) + data = '{"type": "sriov_pf", "name": "nic1", "numvfs": 16,' \ '"use_dhcp": false}' pf = objects.object_from_json(json.loads(data)) self.assertEqual("em4", pf.name) self.assertEqual(16, pf.numvfs) self.assertFalse(pf.use_dhcp) + self.assertEqual(None, pf.promisc) class TestSriovVF(base.TestCase): + def setUp(self): + super(TestSriovVF, self).setUp() + + def tearDown(self): + super(TestSriovVF, self).tearDown() + def test_from_json_vfid(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) data = '{"type": "sriov_vf", "device": "em1", "vfid": 16,' \ '"use_dhcp": false}' vf = objects.object_from_json(json.loads(data)) self.assertEqual("em1", vf.device) self.assertEqual(16, vf.vfid) self.assertFalse(vf.use_dhcp) - self.assertEqual("", vf.name) + self.assertEqual("em1_16", vf.name) def test_from_json_name_ignored(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) data = '{"type": "sriov_vf", "device": "em1", "vfid": 16,' \ - '"use_dhcp": false, "name": "em1_16"}' + '"use_dhcp": false, "name": "em1_7"}' vf = objects.object_from_json(json.loads(data)) self.assertEqual("em1", vf.device) self.assertEqual(16, vf.vfid) self.assertFalse(vf.use_dhcp) - self.assertEqual("", vf.name) + self.assertEqual("em1_16", vf.name) + + def test_from_json_vfid_configs_enabled(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + data = '{"type": "sriov_vf", "device": "em4", "vfid": 16,' \ + '"use_dhcp": false, "vlan_id": 100, "qos": 2, "trust": true,' \ + '"state": "auto", "spoofcheck": true,' \ + '"macaddr":"AA:BB:CC:DD:EE:FF", "promisc": true}' + vf = objects.object_from_json(json.loads(data)) + self.assertEqual("em4", vf.device) + self.assertEqual(16, vf.vfid) + self.assertFalse(vf.use_dhcp) + self.assertEqual("em4_16", vf.name) + self.assertEqual(100, vf.vlan_id) + self.assertEqual(2, vf.qos) + self.assertEqual("on", vf.spoofcheck) + self.assertEqual("on", vf.trust) + self.assertEqual("auto", vf.state) + self.assertEqual("AA:BB:CC:DD:EE:FF", vf.macaddr) + self.assertEqual("on", vf.promisc) + + def test_from_json_vfid_configs_disabled(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + data = '{"type": "sriov_vf", "device": "em4", "vfid": 16,' \ + '"use_dhcp": false, "vlan_id": 0, "qos": 0, "trust": false,' \ + '"state": "disable", "spoofcheck": false,' \ + '"promisc": false}' + vf = objects.object_from_json(json.loads(data)) + self.assertEqual("em4", vf.device) + self.assertEqual(16, vf.vfid) + self.assertFalse(vf.use_dhcp) + self.assertEqual("em4_16", vf.name) + self.assertEqual(0, vf.vlan_id) + self.assertEqual(0, vf.qos) + self.assertEqual("off", vf.spoofcheck) + self.assertEqual("off", vf.trust) + self.assertEqual("disable", vf.state) + self.assertEqual(None, vf.macaddr) + self.assertEqual("off", vf.promisc) + + def test_from_json_vfid_invalid_state(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + data = '{"type": "sriov_vf", "device": "em4", "vfid": 16,' \ + '"use_dhcp": false, "vlan_id": 0, "qos": 0, "trust": false,' \ + '"state": "disabled", ' \ + '"promisc": false}' + err = self.assertRaises(objects.InvalidConfigException, + objects.object_from_json, + json.loads(data)) + expected = 'Expecting state to match auto/enable/disable' + self.assertIn(expected, six.text_type(err)) + + def test_from_json_vfid_invalid_qos(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + + data = '{"type": "sriov_vf", "device": "em4", "vfid": 16,' \ + '"use_dhcp": false, "vlan_id": 0, "qos": 10, "trust": false,' \ + '"promisc": false}' + err = self.assertRaises(objects.InvalidConfigException, + objects.object_from_json, + json.loads(data)) + expected = 'Vlan tag not set for QOS - VF: em4:16' + self.assertIn(expected, six.text_type(err)) def test_from_json_vfid_nic1(self): + def test_get_vf_devname(device, vfid): + return device + '_' + str(vfid) + self.stub_out('os_net_config.utils.get_vf_devname', + test_get_vf_devname) + def dummy_mapped_nics(nic_mapping=None): return {"nic1": "em4"} + self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics) data = '{"type": "sriov_vf", "device": "nic1", "vfid": 16,' \ @@ -1260,7 +1372,7 @@ class TestSriovVF(base.TestCase): self.assertEqual("em4", vf.device) self.assertEqual(16, vf.vfid) self.assertFalse(vf.use_dhcp) - self.assertEqual("", vf.name) + self.assertEqual("em4_16", vf.name) class TestOvsDpdkBond(base.TestCase): diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py index 29811f79..308f4834 100644 --- a/os_net_config/tests/test_utils.py +++ b/os_net_config/tests/test_utils.py @@ -23,6 +23,7 @@ import tempfile import yaml from os_net_config import objects +from os_net_config import sriov_config from os_net_config.tests import base from os_net_config import utils @@ -81,14 +82,14 @@ class TestUtils(base.TestCase): super(TestUtils, self).setUp() rand = str(int(random.random() * 100000)) utils._DPDK_MAPPING_FILE = '/tmp/dpdk_mapping_' + rand + '.yaml' - utils._SRIOV_PF_CONFIG_FILE = '/tmp/sriov_pf_' + rand + '.yaml' + sriov_config._SRIOV_CONFIG_FILE = '/tmp/sriov_config_' + rand + '.yaml' def tearDown(self): super(TestUtils, self).tearDown() if os.path.isfile(utils._DPDK_MAPPING_FILE): os.remove(utils._DPDK_MAPPING_FILE) - if os.path.isfile(utils._SRIOV_PF_CONFIG_FILE): - os.remove(utils._SRIOV_PF_CONFIG_FILE) + if os.path.isfile(sriov_config._SRIOV_CONFIG_FILE): + os.remove(sriov_config._SRIOV_CONFIG_FILE) def test_ordered_active_nics(self): @@ -119,30 +120,126 @@ class TestUtils(base.TestCase): def test_update_sriov_pf_map_new(self): utils.update_sriov_pf_map('eth1', 10, False) - contents = utils.get_file_data(utils._SRIOV_PF_CONFIG_FILE) + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) sriov_pf_map = yaml.load(contents) if contents else [] self.assertEqual(1, len(sriov_pf_map)) - test_sriov_pf_map = [{'name': 'eth1', 'numvfs': 10}] + test_sriov_pf_map = [{'device_type': 'pf', 'name': 'eth1', + 'numvfs': 10}] + self.assertListEqual(test_sriov_pf_map, sriov_pf_map) + + def test_update_sriov_pf_map_new_with_promisc(self): + utils.update_sriov_pf_map('eth1', 10, False, promisc='off') + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + sriov_pf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(sriov_pf_map)) + test_sriov_pf_map = [{'device_type': 'pf', 'name': 'eth1', + 'numvfs': 10, 'promisc': 'off'}] self.assertListEqual(test_sriov_pf_map, sriov_pf_map) def test_update_sriov_pf_map_exist(self): - pf_initial = [{'name': 'eth1', 'numvfs': 10}] - utils.write_yaml_config(utils._SRIOV_PF_CONFIG_FILE, pf_initial) + pf_initial = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 10}] + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, pf_initial) utils.update_sriov_pf_map('eth1', 20, False) - pf_final = [{'name': 'eth1', 'numvfs': 20}] - contents = utils.get_file_data(utils._SRIOV_PF_CONFIG_FILE) + pf_final = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 20}] + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) pf_map = yaml.load(contents) if contents else [] self.assertEqual(1, len(pf_map)) self.assertListEqual(pf_final, pf_map) + def test_update_sriov_pf_map_exist_with_promisc(self): + pf_initial = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 10, + 'promisc': 'on'}] + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, pf_initial) + + utils.update_sriov_pf_map('eth1', 20, False) + pf_final = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 20, + 'promisc': 'on'}] + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + + pf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(pf_map)) + self.assertListEqual(pf_final, pf_map) + + def test_update_sriov_vf_map_minimal_new(self): + utils.update_sriov_vf_map('eth1', 2, 'eth1_2') + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + sriov_vf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(sriov_vf_map)) + test_sriov_vf_map = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {"name": "eth1", "vfid": 2}}] + self.assertListEqual(test_sriov_vf_map, sriov_vf_map) + + def test_update_sriov_vf_map_complete_new(self): + utils.update_sriov_vf_map('eth1', 2, 'eth1_2', vlan_id=10, qos=5, + spoofcheck="on", trust="on", state="enable", + macaddr="AA:BB:CC:DD:EE:FF", promisc="off") + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + sriov_vf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(sriov_vf_map)) + test_sriov_vf_map = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {'name': 'eth1', 'vfid': 2}, + 'vlan_id': 10, 'qos': 5, + 'spoofcheck': 'on', 'trust': 'on', + 'state': 'enable', + 'macaddr': 'AA:BB:CC:DD:EE:FF', + 'promisc': 'off'}] + self.assertListEqual(test_sriov_vf_map, sriov_vf_map) + + def test_update_sriov_vf_map_exist(self): + vf_initial = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {"name": "eth1", "vfid": 2}}] + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, vf_initial) + + utils.update_sriov_vf_map('eth1', 2, 'eth1_2', vlan_id=10, qos=5, + spoofcheck="on", trust="on", state="enable", + macaddr="AA:BB:CC:DD:EE:FF", promisc="off") + vf_final = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {'name': 'eth1', 'vfid': 2}, + 'vlan_id': 10, 'qos': 5, + 'spoofcheck': 'on', 'trust': 'on', + 'state': 'enable', + 'macaddr': 'AA:BB:CC:DD:EE:FF', + 'promisc': 'off'}] + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + + vf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(vf_map)) + self.assertListEqual(vf_final, vf_map) + + def test_update_sriov_vf_map_exist_complete(self): + vf_initial = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {'name': 'eth1', 'vfid': 2}, + 'vlan_id': 10, 'qos': 5, + 'spoofcheck': 'on', 'trust': 'on', + 'state': 'enable', + 'macaddr': 'AA:BB:CC:DD:EE:FF', + 'promisc': 'off'}] + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, vf_initial) + + utils.update_sriov_vf_map('eth1', 2, 'eth1_2', vlan_id=100, qos=15, + spoofcheck="off", trust="off", state="auto", + macaddr="BB:BB:CC:DD:EE:FF", promisc="on") + vf_final = [{'device_type': 'vf', 'name': 'eth1_2', + 'device': {'name': 'eth1', 'vfid': 2}, + 'vlan_id': 100, 'qos': 15, + 'spoofcheck': 'off', 'trust': 'off', + 'state': 'auto', + 'macaddr': 'BB:BB:CC:DD:EE:FF', + 'promisc': 'on'}] + contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE) + + vf_map = yaml.load(contents) if contents else [] + self.assertEqual(1, len(vf_map)) + self.assertListEqual(vf_final, vf_map) + def test_get_vf_devname_net_dir_not_found(self): tmpdir = tempfile.mkdtemp() self.stub_out('os_net_config.utils._SYS_CLASS_NET', tmpdir) self.assertRaises(utils.SriovVfNotFoundException, - utils.get_vf_devname, "eth1", 1, False) + utils.get_vf_devname, "eth1", 1) shutil.rmtree(tmpdir) def test_get_vf_devname_vf_dir_not_found(self): @@ -153,7 +250,7 @@ class TestUtils(base.TestCase): os.makedirs(vf_path) self.assertRaises(utils.SriovVfNotFoundException, - utils.get_vf_devname, "eth1", 1, False) + utils.get_vf_devname, "eth1", 1) shutil.rmtree(tmpdir) def test_get_vf_devname_vf_dir_found(self): @@ -164,7 +261,7 @@ class TestUtils(base.TestCase): 'eth1/device/virtfn1/net/eth1_1') os.makedirs(vf_path) - self.assertEqual(utils.get_vf_devname("eth1", 1, False), "eth1_1") + self.assertEqual(utils.get_vf_devname("eth1", 1), "eth1_1") shutil.rmtree(tmpdir) def test_get_pci_address_success(self): diff --git a/os_net_config/utils.py b/os_net_config/utils.py index 1613f7e3..466601b6 100644 --- a/os_net_config/utils.py +++ b/os_net_config/utils.py @@ -35,16 +35,9 @@ _SYS_CLASS_NET = '/sys/class/net' # mac_address: 01:02:03:04:05:06 # driver: vfio-pci _DPDK_MAPPING_FILE = '/var/lib/os-net-config/dpdk_mapping.yaml' - -# File to contain the list of SR-IOV nics and the numvfs -# Format of the file shall be -# -# - name: eth1 -# numvfs: 5 -_SRIOV_PF_CONFIG_FILE = '/var/lib/os-net-config/sriov_pf.yaml' - -# sriov_numvfs service shall be configured so that the numvfs for each of the -# SR-IOV PF device shall be configured during reboot as well +# sriov_config service shall be created and enabled so that the various +# SR-IOV PF and VF configurations shall be done during reboot as well using +# sriov_config.py installed in path /usr/bin/os-net-config-sriov _SRIOV_CONFIG_SERVICE_FILE = "/etc/systemd/system/sriov_config.service" _SRIOV_CONFIG_DEVICE_CONTENT = """[Unit] Description=SR-IOV numvfs configuration @@ -420,28 +413,76 @@ def _get_dpdk_mac_address(name): return item['mac_address'] -def update_sriov_pf_map(ifname, numvfs, noop): +def update_sriov_pf_map(ifname, numvfs, noop, promisc=None): if not noop: - sriov_map = _get_sriov_pf_map() + sriov_map = _get_sriov_map() for item in sriov_map: - if item['name'] == ifname: + if item['device_type'] == 'pf' and item['name'] == ifname: item['numvfs'] = numvfs + if promisc is not None: + item['promisc'] = promisc break else: new_item = {} + new_item['device_type'] = 'pf' new_item['name'] = ifname new_item['numvfs'] = numvfs + if promisc is not None: + new_item['promisc'] = promisc sriov_map.append(new_item) - write_yaml_config(_SRIOV_PF_CONFIG_FILE, sriov_map) + write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, sriov_map) -def _get_sriov_pf_map(): - contents = get_file_data(_SRIOV_PF_CONFIG_FILE) +def _get_sriov_map(): + contents = get_file_data(sriov_config._SRIOV_CONFIG_FILE) sriov_map = yaml.load(contents) if contents else [] return sriov_map +def _set_vf_fields(vf_name, vlan_id, qos, spoofcheck, trust, state, macaddr, + promisc): + vf_configs = {} + vf_configs['name'] = vf_name + if vlan_id != 0: + vf_configs['vlan_id'] = vlan_id + if qos != 0: + vf_configs['qos'] = qos + if spoofcheck is not None: + vf_configs['spoofcheck'] = spoofcheck + if trust is not None: + vf_configs['trust'] = trust + if state is not None: + vf_configs['state'] = state + if macaddr is not None: + vf_configs['macaddr'] = macaddr + if promisc is not None: + vf_configs['promisc'] = promisc + return vf_configs + + +def update_sriov_vf_map(pf_name, vfid, vf_name, vlan_id=0, qos=0, + spoofcheck=None, trust=None, state=None, macaddr=None, + promisc=None): + sriov_map = _get_sriov_map() + for item in sriov_map: + if (item['device_type'] == 'vf' and + item['device'].get('name') == pf_name and + item['device'].get('vfid') == vfid): + item.update(_set_vf_fields(vf_name, vlan_id, qos, spoofcheck, + trust, state, macaddr, promisc)) + break + else: + new_item = {} + new_item['device_type'] = 'vf' + new_item['device'] = {"name": pf_name, "vfid": vfid} + new_item.update(_set_vf_fields(vf_name, vlan_id, qos, spoofcheck, + trust, state, macaddr, promisc)) + sriov_map.append(new_item) + + write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, sriov_map) + + def _configure_sriov_config_service(): """Generate the sriov_config.service @@ -455,14 +496,16 @@ def _configure_sriov_config_service(): def configure_sriov_pfs(): logger.info("Configuring PFs now") - sriov_config.main() + sriov_config.configure_sriov_pf() _configure_sriov_config_service() -def get_vf_devname(pf_name, vfid, noop): - if noop: - logger.info("NOOP: returning VF name as %s_%d" % (pf_name, vfid)) - return "%s_%d" % (pf_name, vfid) +def configure_sriov_vfs(): + logger.info("Configuring VFs now") + sriov_config.configure_sriov_vf() + + +def get_vf_devname(pf_name, vfid): vf_path = os.path.join(_SYS_CLASS_NET, pf_name, "device/virtfn%d/net" % vfid) if os.path.isdir(vf_path): diff --git a/requirements.txt b/requirements.txt index dbf76d96..baebf9ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,8 +6,9 @@ anyjson>=0.3.3 # BSD six>=1.9.0 # MIT eventlet!=0.18.3,>=0.18.2 # MIT iso8601>=0.1.11 # MIT -netaddr>=0.7.13 #BSD +netaddr>=0.7.13 # BSD oslo.concurrency>=3.8.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 PyYAML>=3.10.0 # MIT jsonschema>=2.0.0,<3.0.0 # MIT +pyudev>=0.15