Adding VLAN support and other configs for sriov_vf

Allow additional settings for configuring sriov_vf types
such as vlan, qos, spoofcheck, trust mode, state control,
mac address, promisc.

Implements: blueprint sriov-vfs-as-network-interface
Change-Id: I078a202b1f5769d8b18db2618914edb5ad836919
Signed-off-by: Karthik S <ksundara@redhat.com>
This commit is contained in:
Karthik S 2018-02-07 04:00:20 -05:00
parent 1bbbb583b6
commit dbeb6fe87b
13 changed files with 834 additions and 119 deletions

View File

@ -4,7 +4,8 @@
"type": "sriov_pf", "type": "sriov_pf",
"name": "p2p1", "name": "p2p1",
"numvfs": 10, "numvfs": 10,
"use_dhcp": false "use_dhcp": false,
"promisc": on
}, },
{ {
"type": "sriov_vf", "type": "sriov_vf",
@ -14,7 +15,31 @@
{ {
"ip_netmask": "192.0.2.1/24" "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
} }
] ]
} }

View File

@ -13,6 +13,9 @@ network_config:
numvfs: 10 numvfs: 10
# Dont set the IP address on the PF # Dont set the IP address on the PF
use_dhcp: false 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. # 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 # It requires the PF devices to be configured via sriov_pf types
@ -27,3 +30,56 @@ network_config:
vfid: 5 vfid: 5
addresses: addresses:
- ip_netmask: 192.0.2.1/24 - 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

View File

@ -249,9 +249,16 @@ def main(argv=sys.argv):
# Look for the presence of SriovPF types in the first parse of the json # 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 # if SriovPFs exists then PF devices needs to be configured so that the VF
# devices are created. # 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 # In the second parse, all other objects shall be added
for iface_json in iface_array: 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): if isinstance(obj, objects.SriovPF):
configure_sriov = True configure_sriov = True
provider.add_object(obj) provider.add_object(obj)
@ -263,11 +270,20 @@ def main(argv=sys.argv):
# All objects other than the sriov_pf will be added here. # All objects other than the sriov_pf will be added here.
# The VFs are expected to be available now and an exception # The VFs are expected to be available now and an exception
# SriovVfNotFoundException shall be raised if not available. # 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): if not isinstance(obj, objects.SriovPF):
provider.add_object(obj) provider.add_object(obj)
if configure_sriov and not opts.noop:
utils.configure_sriov_vfs()
files_changed = provider.apply(cleanup=opts.cleanup, files_changed = provider.apply(cleanup=opts.cleanup,
activate=not opts.no_activate) activate=not opts.no_activate)
if opts.noop: if opts.noop:
for location, data in files_changed.items(): for location, data in files_changed.items():
print("File: %s\n" % location) print("File: %s\n" % location)

View File

@ -140,7 +140,6 @@ class IfcfgNetConfig(os_net_config.NetConfig):
def _add_common(self, base_opt): def _add_common(self, base_opt):
ovs_extra = [] ovs_extra = []
data = "# This file is autogenerated by os-net-config\n" data = "# This file is autogenerated by os-net-config\n"
data += "DEVICE=%s\n" % base_opt.name data += "DEVICE=%s\n" % base_opt.name
if base_opt.onboot: if base_opt.onboot:
@ -678,7 +677,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
logger.info('adding sriov pf: %s' % sriov_pf.name) logger.info('adding sriov pf: %s' % sriov_pf.name)
data = self._add_common(sriov_pf) data = self._add_common(sriov_pf)
logger.debug('sriov pf data: %s' % data) 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 self.interface_data[sriov_pf.name] = data
def add_sriov_vf(self, sriov_vf): 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 :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' logger.info('adding sriov vf: %s for pf: %s, vfid: %d'
% (sriov_vf.name, sriov_vf.device, sriov_vf.vfid)) % (sriov_vf.name, sriov_vf.device, sriov_vf.vfid))
data = self._add_common(sriov_vf) data = self._add_common(sriov_vf)

View File

@ -1106,7 +1106,8 @@ class SriovVF(_BaseOpts):
addresses=None, routes=None, mtu=None, primary=False, addresses=None, routes=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True, nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False, 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 [] addresses = addresses or []
routes = routes or [] routes = routes or []
dns_servers = dns_servers or [] dns_servers = dns_servers or []
@ -1116,13 +1117,38 @@ class SriovVF(_BaseOpts):
# Empty strings are set for the name field. # Empty strings are set for the name field.
# The provider shall identify the VF name from the PF device name # The provider shall identify the VF name from the PF device name
# (device) and the VF id. # (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, routes, mtu, primary, nic_mapping,
persist_mapping, defroute, persist_mapping, defroute,
dhclient_args, dns_servers, dhclient_args, dns_servers,
nm_controlled, onboot) nm_controlled, onboot)
self.vfid = vfid self.vfid = int(vfid)
self.device = device 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 @staticmethod
def from_json(json): def from_json(json):
@ -1131,7 +1157,22 @@ class SriovVF(_BaseOpts):
# Get the PF device name # Get the PF device name
device = _get_required_field(json, 'device', 'SriovVF') device = _get_required_field(json, 'device', 'SriovVF')
opts = _BaseOpts.base_opts_from_json(json) 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): class SriovPF(_BaseOpts):
@ -1141,7 +1182,7 @@ class SriovPF(_BaseOpts):
addresses=None, routes=None, mtu=None, primary=False, addresses=None, routes=None, mtu=None, primary=False,
nic_mapping=None, persist_mapping=False, defroute=True, nic_mapping=None, persist_mapping=False, defroute=True,
dhclient_args=None, dns_servers=None, nm_controlled=False, dhclient_args=None, dns_servers=None, nm_controlled=False,
onboot=True, members=None): onboot=True, members=None, promisc=None):
addresses = addresses or [] addresses = addresses or []
routes = routes or [] routes = routes or []
dns_servers = dns_servers or [] dns_servers = dns_servers or []
@ -1150,19 +1191,25 @@ class SriovPF(_BaseOpts):
persist_mapping, defroute, persist_mapping, defroute,
dhclient_args, dns_servers, dhclient_args, dns_servers,
nm_controlled, onboot) nm_controlled, onboot)
self.numvfs = numvfs self.numvfs = int(numvfs)
mapped_nic_names = mapped_nics(nic_mapping) mapped_nic_names = mapped_nics(nic_mapping)
if name in mapped_nic_names: if name in mapped_nic_names:
self.name = mapped_nic_names[name] self.name = mapped_nic_names[name]
else: else:
self.name = name self.name = name
self.promisc = promisc
@staticmethod @staticmethod
def from_json(json): def from_json(json):
name = _get_required_field(json, 'name', 'SriovPF') name = _get_required_field(json, 'name', 'SriovPF')
numvfs = _get_required_field(json, 'numvfs', '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) opts = _BaseOpts.base_opts_from_json(json)
return SriovPF(name, numvfs, *opts) return SriovPF(name, numvfs, *opts, promisc=promisc)
class OvsDpdkBond(_BaseOpts): class OvsDpdkBond(_BaseOpts):

View File

@ -30,7 +30,13 @@ definitions:
type: string type: string
pattern: "(?i)^(t|true|on|y|yes|1|f|false|off|n|no|0)$" pattern: "(?i)^(t|true|on|y|yes|1|f|false|off|n|no|0)$"
- $ref: "#/definitions/param" - $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 # IP address and address+prefix types
ipv4_address_string: ipv4_address_string:
type: string type: string
@ -243,6 +249,8 @@ definitions:
$ref: "#/definitions/bool_or_param" $ref: "#/definitions/bool_or_param"
numvfs: numvfs:
$ref: "#/definitions/int_or_param" $ref: "#/definitions/int_or_param"
promisc:
$ref: "#/definitions/bool_or_param"
hotplug: hotplug:
$ref: "#/definitions/bool_or_param" $ref: "#/definitions/bool_or_param"
# common options: # common options:
@ -287,8 +295,22 @@ definitions:
$ref: "#/definitions/string_or_param" $ref: "#/definitions/string_or_param"
vfid: vfid:
$ref: "#/definitions/int_or_param" $ref: "#/definitions/int_or_param"
vlan_id:
$ref: "#/definitions/int_or_param"
qos:
$ref: "#/definitions/int_or_param"
hotplug: hotplug:
$ref: "#/definitions/bool_or_param" $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: # common options:
use_dhcp: use_dhcp:
$ref: "#/definitions/bool_or_param" $ref: "#/definitions/bool_or_param"
@ -378,6 +400,7 @@ definitions:
- $ref: "#/definitions/ovs_bond" - $ref: "#/definitions/ovs_bond"
- $ref: "#/definitions/ovs_tunnel" - $ref: "#/definitions/ovs_tunnel"
- $ref: "#/definitions/ovs_patch_port" - $ref: "#/definitions/ovs_patch_port"
- $ref: "#/definitions/sriov_vf"
ovs_options: ovs_options:
$ref: "#/definitions/ovs_options_string_or_param" $ref: "#/definitions/ovs_options_string_or_param"
ovs_extra: ovs_extra:

View File

@ -21,29 +21,53 @@
# it for the first time configuration. # it for the first time configuration.
# An entry point os-net-config-sriov is added for invocation of this module. # An entry point os-net-config-sriov is added for invocation of this module.
import argparse
import logging import logging
import os import os
import pyudev
from six.moves import queue as Queue
import sys import sys
import time
import yaml 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 # Format of the file shall be
# # - device_type: pf
# - name: eth1 # name: <pf name>
# numvfs: 5 # numvfs: <number of VFs>
_SRIOV_PF_CONFIG_FILE = '/var/lib/os-net-config/sriov_pf.yaml' # promisc: "on"/"off"
_SYS_CLASS_NET = '/sys/class/net' # - device_type: vf
# maximum retries for checking the creation of VFs # device:
_MAX_SRIOV_VFS_CONFIG_RETRIES = 60 # name: <pf name>
# vfid: <VF id>
# name: <vf name>
# vlan_id: <vlan>
# qos: <qos>
# spoofcheck: "on"/"off"
# trust: "on"/"off"
# state: "auto"/"enable"/"disable"
# macaddr: <mac address>
# promisc: "on"/"off"
_SRIOV_CONFIG_FILE = '/var/lib/os-net-config/sriov_config.yaml'
class SRIOVNumvfsException(ValueError): class SRIOVNumvfsException(ValueError):
pass 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): def get_file_data(filename):
if not os.path.exists(filename): if not os.path.exists(filename):
return '' return ''
@ -55,55 +79,161 @@ def get_file_data(filename):
return '' return ''
def _get_sriov_pf_map(): def _get_sriov_map():
contents = get_file_data(_SRIOV_PF_CONFIG_FILE) contents = get_file_data(_SRIOV_CONFIG_FILE)
sriov_pf_map = yaml.load(contents) if contents else [] sriov_map = yaml.load(contents) if contents else []
return sriov_pf_map return sriov_map
def _configure_sriov_pf(): def configure_sriov_pf():
sriov_pf_map = _get_sriov_pf_map() # Create a context for pyudev and observe udev events for network
for item in sriov_pf_map: context = pyudev.Context()
try: monitor = pyudev.Monitor.from_netlink(context)
sriov_numvfs_path = ("/sys/class/net/%s/device/sriov_numvfs" monitor.filter_by('net')
% item['name']) observer = pyudev.MonitorObserver(monitor, udev_event_handler)
with open(sriov_numvfs_path, 'w') as f: observer.start()
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)
sriov_map = _get_sriov_map()
def _wait_for_vf_creation():
sriov_map = _get_sriov_pf_map()
for item in sriov_map: for item in sriov_map:
count = 0 if item['device_type'] == 'pf':
while count < _MAX_SRIOV_VFS_CONFIG_RETRIES: _pf_interface_up(item)
pf = item['name'] try:
numvfs = item['numvfs'] sriov_numvfs_path = ("/sys/class/net/%s/device/sriov_numvfs"
vf_path = os.path.join(_SYS_CLASS_NET, pf, % item['name'])
"device/virtfn%d/net" % (numvfs - 1)) with open(sriov_numvfs_path, 'w') as f:
if os.path.isdir(vf_path): f.write("%d" % item['numvfs'])
vf_nic = os.listdir(vf_path) except IOError as exc:
if len(vf_nic) == 1 and pf in vf_nic[0]: msg = ("Unable to configure pf: %s with numvfs: %d\n%s"
logger.info("VFs created for PF: %s" % pf) % (item['name'], item['numvfs'], exc))
break 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: else:
logger.debug("VF device name not present for PF %s" % pf) logger.error("Unable to parse event %s" % event["device"])
else: else:
logger.info("Attempt#%d, VFs for PF %s is not yet created" logger.error("%s is not a directory" % pf_path)
% (count + 1, pf)) except Queue.Empty:
time.sleep(1) logger.info("Timeout in the creation of VFs for PF %s" % pf_name)
count += 1 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 the PF's
_configure_sriov_pf() configure_sriov_pf()
# Wait for the VF's to get created # Configure the VFs
_wait_for_vf_creation() configure_sriov_vf()
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main(sys.argv[1:])) sys.exit(main(sys.argv))

View File

@ -15,11 +15,13 @@
# under the License. # under the License.
import os.path import os.path
import random
import sys import sys
import yaml import yaml
import os_net_config import os_net_config
from os_net_config import cli from os_net_config import cli
from os_net_config import sriov_config
from os_net_config.tests import base from os_net_config.tests import base
import six import six
@ -31,6 +33,16 @@ SAMPLE_BASE = os.path.join(REALPATH, '../../', 'etc',
class TestCli(base.TestCase): 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,)): def run_cli(self, argstr, exitcodes=(0,)):
orig = sys.stdout orig = sys.stdout
orig_stderr = sys.stderr orig_stderr = sys.stderr
@ -191,6 +203,10 @@ class TestCli(base.TestCase):
% interface_yaml, exitcodes=(0,)) % interface_yaml, exitcodes=(0,))
def test_sriov_noop_output(self): 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_yaml = os.path.join(SAMPLE_BASE, 'sriov_pf.yaml')
ivs_json = os.path.join(SAMPLE_BASE, 'sriov_pf.json') ivs_json = os.path.join(SAMPLE_BASE, 'sriov_pf.json')
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop ' stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
@ -202,7 +218,8 @@ class TestCli(base.TestCase):
'-c %s' % ivs_json) '-c %s' % ivs_json)
self.assertEqual('', stderr) self.assertEqual('', stderr)
sanity_devices = ['DEVICE=p2p1', sanity_devices = ['DEVICE=p2p1',
'DEVICE=p2p1_5'] 'DEVICE=p2p1_5',
'DEVICE=p2p1_1']
for dev in sanity_devices: for dev in sanity_devices:
self.assertIn(dev, stdout_yaml) self.assertIn(dev, stdout_yaml)
self.assertEqual(stdout_yaml, stdout_json) self.assertEqual(stdout_yaml, stdout_json)

View File

@ -15,6 +15,7 @@
# under the License. # under the License.
import os.path import os.path
import random
import tempfile import tempfile
from oslo_concurrency import processutils from oslo_concurrency import processutils
@ -22,6 +23,7 @@ from oslo_concurrency import processutils
import os_net_config import os_net_config
from os_net_config import impl_ifcfg from os_net_config import impl_ifcfg
from os_net_config import objects from os_net_config import objects
from os_net_config import sriov_config
from os_net_config.tests import base from os_net_config.tests import base
from os_net_config import utils from os_net_config import utils
@ -475,14 +477,17 @@ CPU_LIST=2,3
class TestIfcfgNetConfig(base.TestCase): class TestIfcfgNetConfig(base.TestCase):
def setUp(self): def setUp(self):
super(TestIfcfgNetConfig, self).setUp() 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() self.provider = impl_ifcfg.IfcfgNetConfig()
def tearDown(self): def tearDown(self):
super(TestIfcfgNetConfig, self).tearDown() 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'): def get_interface_config(self, name='em1'):
return self.provider.interface_data[name] return self.provider.interface_data[name]
@ -1120,18 +1125,31 @@ DNS2=5.6.7.8
bond_data = self.get_linux_bond_config('bond1') bond_data = self.get_linux_bond_config('bond1')
self.assertEqual(_NM_CONTROLLED_BOND, bond_data) 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'} nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'}
self.stubbed_mapped_nics = nic_mapping self.stubbed_mapped_nics = nic_mapping
addresses = [objects.Address('10.0.0.30/24')] 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): def test_update_sriov_vf_map(pf_name, vfid, vf_name, vlan_id=None,
self.assertEqual(device, 'eth2') qos=None, spoofcheck=None, trust=None,
state=None, macaddr=None, promisc=None):
self.assertEqual(pf_name, 'eth2')
self.assertEqual(vfid, 7) 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', self.stub_out('os_net_config.utils.get_vf_devname',
test_get_vf_devname) test_get_vf_devname)
vf = objects.SriovVF(device='nic3', vfid=7, addresses=addresses)
self.provider.add_sriov_vf(vf) self.provider.add_sriov_vf(vf)
vf_config = """# This file is autogenerated by os-net-config vf_config = """# This file is autogenerated by os-net-config
DEVICE=eth2_7 DEVICE=eth2_7
@ -1145,15 +1163,152 @@ NETMASK=255.255.255.0
""" """
self.assertEqual(vf_config, self.get_interface_config('eth2_7')) 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'} nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'}
self.stubbed_mapped_nics = nic_mapping self.stubbed_mapped_nics = nic_mapping
pf = objects.SriovPF(name='nic3', numvfs=10) 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(name, 'eth2')
self.assertEqual(numvfs, 10) 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', self.stub_out('os_net_config.utils.update_sriov_pf_map',
test_update_sriov_pf_map) test_update_sriov_pf_map)
self.provider.add_sriov_pf(pf) self.provider.add_sriov_pf(pf)

View File

@ -1192,10 +1192,11 @@ class TestSriovPF(base.TestCase):
def test_from_json_numvfs(self): def test_from_json_numvfs(self):
data = '{"type": "sriov_pf", "name": "em1", "numvfs": 16,' \ data = '{"type": "sriov_pf", "name": "em1", "numvfs": 16,' \
'"use_dhcp": false}' '"use_dhcp": false, "promisc": false}'
pf = objects.object_from_json(json.loads(data)) pf = objects.object_from_json(json.loads(data))
self.assertEqual("em1", pf.name) self.assertEqual("em1", pf.name)
self.assertEqual(16, pf.numvfs) self.assertEqual(16, pf.numvfs)
self.assertEqual("off", pf.promisc)
self.assertFalse(pf.use_dhcp) self.assertFalse(pf.use_dhcp)
def test_from_json_numvfs_nic1(self): def test_from_json_numvfs_nic1(self):
@ -1203,37 +1204,148 @@ class TestSriovPF(base.TestCase):
return {"nic1": "em4"} return {"nic1": "em4"}
self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics) 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,' \ data = '{"type": "sriov_pf", "name": "nic1", "numvfs": 16,' \
'"use_dhcp": false}' '"use_dhcp": false}'
pf = objects.object_from_json(json.loads(data)) pf = objects.object_from_json(json.loads(data))
self.assertEqual("em4", pf.name) self.assertEqual("em4", pf.name)
self.assertEqual(16, pf.numvfs) self.assertEqual(16, pf.numvfs)
self.assertFalse(pf.use_dhcp) self.assertFalse(pf.use_dhcp)
self.assertEqual(None, pf.promisc)
class TestSriovVF(base.TestCase): 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_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,' \ data = '{"type": "sriov_vf", "device": "em1", "vfid": 16,' \
'"use_dhcp": false}' '"use_dhcp": false}'
vf = objects.object_from_json(json.loads(data)) vf = objects.object_from_json(json.loads(data))
self.assertEqual("em1", vf.device) self.assertEqual("em1", vf.device)
self.assertEqual(16, vf.vfid) self.assertEqual(16, vf.vfid)
self.assertFalse(vf.use_dhcp) self.assertFalse(vf.use_dhcp)
self.assertEqual("", vf.name) self.assertEqual("em1_16", vf.name)
def test_from_json_name_ignored(self): 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,' \ 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)) vf = objects.object_from_json(json.loads(data))
self.assertEqual("em1", vf.device) self.assertEqual("em1", vf.device)
self.assertEqual(16, vf.vfid) self.assertEqual(16, vf.vfid)
self.assertFalse(vf.use_dhcp) 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_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): def dummy_mapped_nics(nic_mapping=None):
return {"nic1": "em4"} return {"nic1": "em4"}
self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics) self.stub_out('os_net_config.objects.mapped_nics', dummy_mapped_nics)
data = '{"type": "sriov_vf", "device": "nic1", "vfid": 16,' \ data = '{"type": "sriov_vf", "device": "nic1", "vfid": 16,' \
@ -1242,7 +1354,7 @@ class TestSriovVF(base.TestCase):
self.assertEqual("em4", vf.device) self.assertEqual("em4", vf.device)
self.assertEqual(16, vf.vfid) self.assertEqual(16, vf.vfid)
self.assertFalse(vf.use_dhcp) self.assertFalse(vf.use_dhcp)
self.assertEqual("", vf.name) self.assertEqual("em4_16", vf.name)
class TestOvsDpdkBond(base.TestCase): class TestOvsDpdkBond(base.TestCase):

View File

@ -23,6 +23,7 @@ import tempfile
import yaml import yaml
from os_net_config import objects from os_net_config import objects
from os_net_config import sriov_config
from os_net_config.tests import base from os_net_config.tests import base
from os_net_config import utils from os_net_config import utils
@ -81,14 +82,14 @@ class TestUtils(base.TestCase):
super(TestUtils, self).setUp() super(TestUtils, self).setUp()
rand = str(int(random.random() * 100000)) rand = str(int(random.random() * 100000))
utils._DPDK_MAPPING_FILE = '/tmp/dpdk_mapping_' + rand + '.yaml' 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): def tearDown(self):
super(TestUtils, self).tearDown() super(TestUtils, self).tearDown()
if os.path.isfile(utils._DPDK_MAPPING_FILE): if os.path.isfile(utils._DPDK_MAPPING_FILE):
os.remove(utils._DPDK_MAPPING_FILE) os.remove(utils._DPDK_MAPPING_FILE)
if os.path.isfile(utils._SRIOV_PF_CONFIG_FILE): if os.path.isfile(sriov_config._SRIOV_CONFIG_FILE):
os.remove(utils._SRIOV_PF_CONFIG_FILE) os.remove(sriov_config._SRIOV_CONFIG_FILE)
def test_ordered_active_nics(self): def test_ordered_active_nics(self):
@ -119,30 +120,126 @@ class TestUtils(base.TestCase):
def test_update_sriov_pf_map_new(self): def test_update_sriov_pf_map_new(self):
utils.update_sriov_pf_map('eth1', 10, False) 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 [] sriov_pf_map = yaml.load(contents) if contents else []
self.assertEqual(1, len(sriov_pf_map)) 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) self.assertListEqual(test_sriov_pf_map, sriov_pf_map)
def test_update_sriov_pf_map_exist(self): def test_update_sriov_pf_map_exist(self):
pf_initial = [{'name': 'eth1', 'numvfs': 10}] pf_initial = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 10}]
utils.write_yaml_config(utils._SRIOV_PF_CONFIG_FILE, pf_initial) utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, pf_initial)
utils.update_sriov_pf_map('eth1', 20, False) utils.update_sriov_pf_map('eth1', 20, False)
pf_final = [{'name': 'eth1', 'numvfs': 20}] pf_final = [{'device_type': 'pf', 'name': 'eth1', 'numvfs': 20}]
contents = utils.get_file_data(utils._SRIOV_PF_CONFIG_FILE) contents = utils.get_file_data(sriov_config._SRIOV_CONFIG_FILE)
pf_map = yaml.load(contents) if contents else [] pf_map = yaml.load(contents) if contents else []
self.assertEqual(1, len(pf_map)) self.assertEqual(1, len(pf_map))
self.assertListEqual(pf_final, 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): def test_get_vf_devname_net_dir_not_found(self):
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
self.stub_out('os_net_config.utils._SYS_CLASS_NET', tmpdir) self.stub_out('os_net_config.utils._SYS_CLASS_NET', tmpdir)
self.assertRaises(utils.SriovVfNotFoundException, self.assertRaises(utils.SriovVfNotFoundException,
utils.get_vf_devname, "eth1", 1, False) utils.get_vf_devname, "eth1", 1)
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
def test_get_vf_devname_vf_dir_not_found(self): def test_get_vf_devname_vf_dir_not_found(self):
@ -153,7 +250,7 @@ class TestUtils(base.TestCase):
os.makedirs(vf_path) os.makedirs(vf_path)
self.assertRaises(utils.SriovVfNotFoundException, self.assertRaises(utils.SriovVfNotFoundException,
utils.get_vf_devname, "eth1", 1, False) utils.get_vf_devname, "eth1", 1)
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
def test_get_vf_devname_vf_dir_found(self): def test_get_vf_devname_vf_dir_found(self):
@ -164,7 +261,7 @@ class TestUtils(base.TestCase):
'eth1/device/virtfn1/net/eth1_1') 'eth1/device/virtfn1/net/eth1_1')
os.makedirs(vf_path) 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) shutil.rmtree(tmpdir)
def test_get_pci_address_success(self): def test_get_pci_address_success(self):

View File

@ -35,16 +35,9 @@ _SYS_CLASS_NET = '/sys/class/net'
# mac_address: 01:02:03:04:05:06 # mac_address: 01:02:03:04:05:06
# driver: vfio-pci # driver: vfio-pci
_DPDK_MAPPING_FILE = '/var/lib/os-net-config/dpdk_mapping.yaml' _DPDK_MAPPING_FILE = '/var/lib/os-net-config/dpdk_mapping.yaml'
# sriov_config service shall be created and enabled so that the various
# File to contain the list of SR-IOV nics and the numvfs # SR-IOV PF and VF configurations shall be done during reboot as well using
# Format of the file shall be # sriov_config.py installed in path /usr/bin/os-net-config-sriov
#
# - 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_FILE = "/etc/systemd/system/sriov_config.service" _SRIOV_CONFIG_SERVICE_FILE = "/etc/systemd/system/sriov_config.service"
_SRIOV_CONFIG_DEVICE_CONTENT = """[Unit] _SRIOV_CONFIG_DEVICE_CONTENT = """[Unit]
Description=SR-IOV numvfs configuration Description=SR-IOV numvfs configuration
@ -420,28 +413,76 @@ def _get_dpdk_mac_address(name):
return item['mac_address'] 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: if not noop:
sriov_map = _get_sriov_pf_map() sriov_map = _get_sriov_map()
for item in sriov_map: for item in sriov_map:
if item['name'] == ifname: if item['device_type'] == 'pf' and item['name'] == ifname:
item['numvfs'] = numvfs item['numvfs'] = numvfs
if promisc is not None:
item['promisc'] = promisc
break break
else: else:
new_item = {} new_item = {}
new_item['device_type'] = 'pf'
new_item['name'] = ifname new_item['name'] = ifname
new_item['numvfs'] = numvfs new_item['numvfs'] = numvfs
if promisc is not None:
new_item['promisc'] = promisc
sriov_map.append(new_item) 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(): def _get_sriov_map():
contents = get_file_data(_SRIOV_PF_CONFIG_FILE) contents = get_file_data(sriov_config._SRIOV_CONFIG_FILE)
sriov_map = yaml.load(contents) if contents else [] sriov_map = yaml.load(contents) if contents else []
return sriov_map 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(): def _configure_sriov_config_service():
"""Generate the sriov_config.service """Generate the sriov_config.service
@ -455,14 +496,16 @@ def _configure_sriov_config_service():
def configure_sriov_pfs(): def configure_sriov_pfs():
logger.info("Configuring PFs now") logger.info("Configuring PFs now")
sriov_config.main() sriov_config.configure_sriov_pf()
_configure_sriov_config_service() _configure_sriov_config_service()
def get_vf_devname(pf_name, vfid, noop): def configure_sriov_vfs():
if noop: logger.info("Configuring VFs now")
logger.info("NOOP: returning VF name as %s_%d" % (pf_name, vfid)) sriov_config.configure_sriov_vf()
return "%s_%d" % (pf_name, vfid)
def get_vf_devname(pf_name, vfid):
vf_path = os.path.join(_SYS_CLASS_NET, pf_name, "device/virtfn%d/net" vf_path = os.path.join(_SYS_CLASS_NET, pf_name, "device/virtfn%d/net"
% vfid) % vfid)
if os.path.isdir(vf_path): if os.path.isdir(vf_path):

View File

@ -6,8 +6,9 @@ anyjson>=0.3.3 # BSD
six>=1.9.0 # MIT six>=1.9.0 # MIT
eventlet!=0.18.3,>=0.18.2 # MIT eventlet!=0.18.3,>=0.18.2 # MIT
iso8601>=0.1.11 # 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.concurrency>=3.8.0 # Apache-2.0
oslo.utils>=3.20.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0
PyYAML>=3.10.0 # MIT PyYAML>=3.10.0 # MIT
jsonschema>=2.0.0,<3.0.0 # MIT jsonschema>=2.0.0,<3.0.0 # MIT
pyudev>=0.15