Merge "Adding VLAN support and other configs for sriov_vf"
This commit is contained in:
commit
420cdede7a
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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: <pf name>
|
||||
# numvfs: <number of VFs>
|
||||
# promisc: "on"/"off"
|
||||
# - device_type: vf
|
||||
# device:
|
||||
# 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):
|
||||
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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue