Add network objects sriov_vf and sriov_pf

sriov_pf: configure numvfs of the device, create and enable
sriov_numvfs service for the persistence of the numvfs
configuration across reboots.
sriov_vf: configure the ifcfgs for the Vfs

Change-Id: I0e55d1556328fcb90b66c898c534b76e41ca9618
Implements: blueprint sriov-vfs-as-network-interface
This commit is contained in:
Karthik S 2017-12-12 23:44:26 -05:00
parent 35eada4d8d
commit 9e785a4ce0
15 changed files with 659 additions and 9 deletions

View File

@ -0,0 +1,20 @@
{
"network_config": [
{
"type": "sriov_pf",
"name": "p2p1",
"numvfs": 10,
"use_dhcp": false
},
{
"type": "sriov_vf",
"device": "p2p1",
"vfid": 5,
"addresses": [
{
"ip_netmask": "192.0.2.1/24"
}
]
}
]
}

View File

@ -0,0 +1,29 @@
network_config:
# sriov_pf type shall be used to configure the PF's of NICs.
# The numvfs configured for the PF's shall be set on the sriov_numvfs of the
# sysfs for the corresponding NIC and the persistence of the same across reboots
# shall be handled
-
type: sriov_pf
# nic name or nic number of the NIC that needs to be configured for SRIOV
name: p2p1
# number of VFs required on the particular NIC
numvfs: 10
# Dont set the IP address on the PF
use_dhcp: false
# 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
# "name" parameter is not required for VF's. It'll be derived from the VF id
# and the PF device name.
- type: sriov_vf
# The PF device needs to be configured via the sriov_pf type
# the PF device could be a nic number (ex: nic5) or nic name (ex: ens20f0)
device: p2p1
# The VF id shall be the VF number between 0 to (numvfs-1), where numvfs
# is the member of the sriov_pf type
vfid: 5
addresses:
- ip_netmask: 192.0.2.1/24

View File

@ -100,6 +100,10 @@ class NetConfig(object):
self.add_ovs_dpdk_port(obj)
elif isinstance(obj, objects.OvsDpdkBond):
self.add_ovs_dpdk_bond(obj)
elif isinstance(obj, objects.SriovPF):
self.add_sriov_pf(obj)
elif isinstance(obj, objects.SriovVF):
self.add_sriov_vf(obj)
elif isinstance(obj, objects.VppInterface):
self.add_vpp_interface(obj)
elif isinstance(obj, objects.VppBond):
@ -216,6 +220,20 @@ class NetConfig(object):
"""
raise NotImplementedError("add_ovs_dpdk_bond is not implemented.")
def add_sriov_pf(self, sriov_pf):
"""Add a SriovPF object to the net config object.
:param sriov_pf: The SriovPF object to add.
"""
raise NotImplementedError("add_sriov_pf is not implemented.")
def add_sriov_vf(self, sriov_vf):
"""Add a SriovVF object to the net config object.
:param sriov_vf: The SriovVF object to add.
"""
raise NotImplementedError("add_sriov_vf is not implemented.")
def add_vpp_interface(self, vpp_interface):
"""Add a VppInterface object to the net config object.

View File

@ -25,10 +25,10 @@ from os_net_config import impl_eni
from os_net_config import impl_ifcfg
from os_net_config import impl_iproute
from os_net_config import objects
from os_net_config import utils
from os_net_config import validator
from os_net_config import version
logger = logging.getLogger(__name__)
@ -138,6 +138,7 @@ def main(argv=sys.argv):
configure_logger(opts.verbose, opts.debug)
logger.info('Using config file at: %s' % opts.config_file)
iface_array = []
configure_sriov = False
provider = None
if opts.provider:
@ -245,9 +246,26 @@ def main(argv=sys.argv):
else:
logger.warning('\n'.join(validation_errors))
# 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.
# In the second parse, all other objects shall be added
for iface_json in iface_array:
obj = objects.object_from_json(iface_json)
provider.add_object(obj)
if isinstance(obj, objects.SriovPF):
configure_sriov = True
provider.add_object(obj)
if configure_sriov and not opts.noop:
utils.configure_sriov_pfs()
for iface_json in iface_array:
# 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)
if not isinstance(obj, objects.SriovPF):
provider.add_object(obj)
files_changed = provider.apply(cleanup=opts.cleanup,
activate=not opts.no_activate)
if opts.noop:

View File

@ -163,9 +163,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
data += "VLAN=yes\n"
if base_opt.device:
data += "PHYSDEV=%s\n" % base_opt.device
else:
if base_opt.linux_bond_name:
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
elif base_opt.linux_bond_name:
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
elif isinstance(base_opt, objects.IvsInterface):
data += "TYPE=IVSIntPort\n"
elif isinstance(base_opt, objects.NfvswitchInternal):
@ -670,6 +669,37 @@ class IfcfgNetConfig(os_net_config.NetConfig):
if ovs_dpdk_bond.routes:
self._add_routes(ovs_dpdk_bond.name, ovs_dpdk_bond.routes)
def add_sriov_pf(self, sriov_pf):
"""Add a SriovPF object to the net config object
:param sriov_pf: The SriovPF object to add
"""
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)
self.interface_data[sriov_pf.name] = data
def add_sriov_vf(self, sriov_vf):
"""Add a SriovVF object to the net config object
: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)
logger.debug('sriov vf data: %s' % data)
self.interface_data[sriov_vf.name] = data
if sriov_vf.routes:
self._add_routes(sriov_vf.name, sriov_vf.routes)
def add_vpp_interface(self, vpp_interface):
"""Add a VppInterface object to the net config object

View File

@ -29,7 +29,6 @@ from os_net_config import utils
logger = logging.getLogger(__name__)
_MAPPED_NICS = None
STANDALONE_FAIL_MODE = 'standalone'
DEFAULT_OVS_BRIDGE_FAIL_MODE = STANDALONE_FAIL_MODE
@ -82,6 +81,10 @@ def object_from_json(json):
return ContrailVrouter.from_json(json)
elif obj_type == "contrail_vrouter_dpdk":
return ContrailVrouterDpdk.from_json(json)
elif obj_type == "sriov_pf":
return SriovPF.from_json(json)
elif obj_type == "sriov_vf":
return SriovVF.from_json(json)
def _get_required_field(json, name, object_name):
@ -407,7 +410,6 @@ class Vlan(_BaseOpts):
persist_mapping, defroute, dhclient_args,
dns_servers, nm_controlled, onboot)
self.vlan_id = int(vlan_id)
mapped_nic_names = mapped_nics(nic_mapping)
if device in mapped_nic_names:
self.device = mapped_nic_names[device]
@ -1097,6 +1099,72 @@ class OvsDpdkPort(_BaseOpts):
ovs_extra=ovs_extra, rx_queue=rx_queue)
class SriovVF(_BaseOpts):
"""Base class for SR-IOV VF."""
def __init__(self, device, vfid, use_dhcp=False, use_dhcpv6=False,
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):
addresses = addresses or []
routes = routes or []
dns_servers = dns_servers or []
mapped_nic_names = mapped_nics(nic_mapping)
if device in mapped_nic_names:
device = mapped_nic_names[device]
# 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,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.vfid = vfid
self.device = device
@staticmethod
def from_json(json):
# Get the VF id
vfid = _get_required_field(json, 'vfid', 'SriovVF')
# Get the PF device name
device = _get_required_field(json, 'device', 'SriovVF')
opts = _BaseOpts.base_opts_from_json(json)
return SriovVF(device, vfid, *opts)
class SriovPF(_BaseOpts):
"""Base class for SR-IOV PF."""
def __init__(self, name, numvfs, use_dhcp=False, use_dhcpv6=False,
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):
addresses = addresses or []
routes = routes or []
dns_servers = dns_servers or []
super(SriovPF, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping,
persist_mapping, defroute,
dhclient_args, dns_servers,
nm_controlled, onboot)
self.numvfs = numvfs
mapped_nic_names = mapped_nics(nic_mapping)
if name in mapped_nic_names:
self.name = mapped_nic_names[name]
else:
self.name = name
@staticmethod
def from_json(json):
name = _get_required_field(json, 'name', 'SriovPF')
numvfs = _get_required_field(json, 'numvfs', 'SriovPF')
opts = _BaseOpts.base_opts_from_json(json)
return SriovPF(name, numvfs, *opts)
class OvsDpdkBond(_BaseOpts):
"""Base class for OVS DPDK bonds."""

View File

@ -232,6 +232,94 @@ definitions:
- name
additionalProperties: False
sriov_pf:
type: object
properties:
type:
enum: ["sriov_pf"]
name:
$ref: "#/definitions/string_or_param"
primary:
$ref: "#/definitions/bool_or_param"
numvfs:
$ref: "#/definitions/int_or_param"
hotplug:
$ref: "#/definitions/bool_or_param"
# common options:
use_dhcp:
$ref: "#/definitions/bool_or_param"
use_dhcp6:
$ref: "#/definitions/bool_or_param"
addresses:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
$ref: "#/definitions/nic_mapping"
persist_mapping:
$ref: "#/definitions/bool_or_param"
defroute:
$ref: "#/definitions/bool_or_param"
dhclient_args:
$ref: "#/definitions/string_or_param"
dns_servers:
$ref: "#/definitions/list_of_ip_address_string_or_param"
nm_controlled:
$ref: "#/definitions/bool_or_param"
onboot:
$ref: "#/definitions/bool_or_param"
required:
- type
- name
- numvfs
additionalProperties: False
sriov_vf:
type: object
properties:
type:
enum: ["sriov_vf"]
primary:
$ref: "#/definitions/bool_or_param"
device:
$ref: "#/definitions/string_or_param"
vfid:
$ref: "#/definitions/int_or_param"
hotplug:
$ref: "#/definitions/bool_or_param"
# common options:
use_dhcp:
$ref: "#/definitions/bool_or_param"
use_dhcp6:
$ref: "#/definitions/bool_or_param"
addresses:
$ref: "#/definitions/list_of_address"
routes:
$ref: "#/definitions/list_of_route"
mtu:
$ref: "#/definitions/int_or_param"
nic_mapping:
$ref: "#/definitions/nic_mapping"
persist_mapping:
$ref: "#/definitions/bool_or_param"
defroute:
$ref: "#/definitions/bool_or_param"
dhclient_args:
$ref: "#/definitions/string_or_param"
dns_servers:
$ref: "#/definitions/list_of_ip_address_string_or_param"
nm_controlled:
$ref: "#/definitions/bool_or_param"
onboot:
$ref: "#/definitions/bool_or_param"
required:
- type
- device
- vfid
additionalProperties: False
vlan:
type: object
properties:
@ -394,6 +482,7 @@ definitions:
items:
oneOf:
- $ref: "#/definitions/interface"
- $ref: "#/definitions/sriov_vf"
- $ref: "#/definitions/vlan"
minItems: 1
ovs_options:
@ -1166,6 +1255,8 @@ type: array
items:
oneOf:
- $ref: "#/definitions/interface"
- $ref: "#/definitions/sriov_pf"
- $ref: "#/definitions/sriov_vf"
- $ref: "#/definitions/vlan"
- $ref: "#/definitions/ovs_bridge"
- $ref: "#/definitions/ovs_user_bridge"

View File

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# The sriov_config.py module does the SR-IOV PF configuration.
# It'll be invoked by the sriov_config systemd service for the persistence of
# the SR-IOV configuration across reboots. And os-net-config:utils also invokes
# it for the first time configuration.
# An entry point os-net-config-sriov is added for invocation of this module.
import logging
import os
import sys
import time
import yaml
logger = logging.getLogger(__name__)
# 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'
_SYS_CLASS_NET = '/sys/class/net'
# maximum retries for checking the creation of VFs
_MAX_SRIOV_VFS_CONFIG_RETRIES = 60
class SRIOVNumvfsException(ValueError):
pass
def get_file_data(filename):
if not os.path.exists(filename):
return ''
try:
with open(filename, 'r') as f:
return f.read()
except IOError:
logger.error("Error reading file: %s" % 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 _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 _wait_for_vf_creation():
sriov_map = _get_sriov_pf_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
else:
logger.debug("VF device name not present for PF %s" % pf)
else:
logger.info("Attempt#%d, VFs for PF %s is not yet created"
% (count + 1, pf))
time.sleep(1)
count += 1
def main(argv=None):
# Configure the PF's
_configure_sriov_pf()
# Wait for the VF's to get created
_wait_for_vf_creation()
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -193,6 +193,23 @@ class TestCli(base.TestCase):
'-c %s --detailed-exit-codes'
% interface_yaml, exitcodes=(0,))
def test_sriov_noop_output(self):
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 '
'--exit-on-validation-errors '
'-c %s' % ivs_yaml)
self.assertEqual('', stderr)
stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
'--exit-on-validation-errors '
'-c %s' % ivs_json)
self.assertEqual('', stderr)
sanity_devices = ['DEVICE=p2p1',
'DEVICE=p2p1_5']
for dev in sanity_devices:
self.assertIn(dev, stdout_yaml)
self.assertEqual(stdout_yaml, stdout_json)
def test_ovs_dpdk_bond_noop_output(self):
ivs_yaml = os.path.join(SAMPLE_BASE, 'ovs_dpdk_bond.yaml')
ivs_json = os.path.join(SAMPLE_BASE, 'ovs_dpdk_bond.json')

View File

@ -1096,6 +1096,53 @@ 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):
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')
self.assertEqual(vfid, 7)
return 'eth2_7'
self.stubs.Set(utils, 'get_vf_devname',
test_get_vf_devname)
self.provider.add_sriov_vf(vf)
vf_config = """# This file is autogenerated by os-net-config
DEVICE=eth2_7
HOTPLUG=no
ONBOOT=yes
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(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):
self.assertEqual(name, 'eth2')
self.assertEqual(numvfs, 10)
self.stubs.Set(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
HOTPLUG=no
ONBOOT=yes
NM_CONTROLLED=no
PEERDNS=no
BOOTPROTO=none
"""
self.assertEqual(pf_config, self.get_interface_config('eth2'))
def test_network_ovs_dpdk_bridge_and_port(self):
nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'}
self.stubbed_mapped_nics = nic_mapping

View File

@ -1187,6 +1187,63 @@ class TestNicMapping(base.TestCase):
self.assertEqual("bar", interface2.name)
class TestSriovPF(base.TestCase):
def test_from_json_numvfs(self):
data = '{"type": "sriov_pf", "name": "em1", "numvfs": 16,' \
'"use_dhcp": false}'
pf = objects.object_from_json(json.loads(data))
self.assertEqual("em1", pf.name)
self.assertEqual(16, pf.numvfs)
self.assertFalse(pf.use_dhcp)
def test_from_json_numvfs_nic1(self):
def dummy_mapped_nics(nic_mapping=None):
return {"nic1": "em4"}
self.stubs.Set(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)
class TestSriovVF(base.TestCase):
def test_from_json_vfid(self):
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)
def test_from_json_name_ignored(self):
data = '{"type": "sriov_vf", "device": "em1", "vfid": 16,' \
'"use_dhcp": false, "name": "em1_16"}'
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)
def test_from_json_vfid_nic1(self):
def dummy_mapped_nics(nic_mapping=None):
return {"nic1": "em4"}
self.stubs.Set(objects, 'mapped_nics', dummy_mapped_nics)
data = '{"type": "sriov_vf", "device": "nic1", "vfid": 16,' \
'"use_dhcp": 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("", vf.name)
class TestOvsDpdkBond(base.TestCase):
# We want to test the function, not the dummy..

View File

@ -81,11 +81,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'
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)
def test_ordered_active_nics(self):
@ -113,6 +116,56 @@ class TestUtils(base.TestCase):
shutil.rmtree(tmpdir)
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)
sriov_pf_map = yaml.load(contents) if contents else []
self.assertEqual(1, len(sriov_pf_map))
test_sriov_pf_map = [{'name': 'eth1', 'numvfs': 10}]
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)
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_map = yaml.load(contents) if contents else []
self.assertEqual(1, len(pf_map))
self.assertListEqual(pf_final, pf_map)
def test_get_vf_devname_net_dir_not_found(self):
tmpdir = tempfile.mkdtemp()
self.stubs.Set(utils, '_SYS_CLASS_NET', tmpdir)
self.assertRaises(utils.SriovVfNotFoundException,
utils.get_vf_devname, "eth1", 1, False)
shutil.rmtree(tmpdir)
def test_get_vf_devname_vf_dir_not_found(self):
tmpdir = tempfile.mkdtemp()
self.stubs.Set(utils, '_SYS_CLASS_NET', tmpdir)
vf_path = os.path.join(utils._SYS_CLASS_NET, 'eth1/device/virtfn1/net')
os.makedirs(vf_path)
self.assertRaises(utils.SriovVfNotFoundException,
utils.get_vf_devname, "eth1", 1, False)
shutil.rmtree(tmpdir)
def test_get_vf_devname_vf_dir_found(self):
tmpdir = tempfile.mkdtemp()
self.stubs.Set(utils, '_SYS_CLASS_NET', tmpdir)
vf_path = os.path.join(utils._SYS_CLASS_NET,
'eth1/device/virtfn1/net/eth1_1')
os.makedirs(vf_path)
self.assertEqual(utils.get_vf_devname("eth1", 1, False), "eth1_1")
shutil.rmtree(tmpdir)
def test_get_pci_address_success(self):
def test_execute(name, dummy1, dummy2=None, dummy3=None):
if 'ethtool' in name:

View File

@ -21,9 +21,9 @@ import re
import time
import yaml
from os_net_config import sriov_config
from oslo_concurrency import processutils
logger = logging.getLogger(__name__)
_SYS_CLASS_NET = '/sys/class/net'
# File to contain the DPDK mapped nics, as nic name will not be available after
@ -36,6 +36,29 @@ _SYS_CLASS_NET = '/sys/class/net'
# 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_FILE = "/etc/systemd/system/sriov_config.service"
_SRIOV_CONFIG_DEVICE_CONTENT = """[Unit]
Description=SR-IOV numvfs configuration
After=systemd-udev-settle.service
Before=openvswitch.service
[Service]
Type=oneshot
ExecStart=os-net-config-sriov
[Install]
WantedBy=multi-user.target
"""
# VPP startup operational configuration file. The content of this file will
# be executed when VPP starts as if typed from CLI.
_VPP_EXEC_FILE = '/etc/vpp/vpp-exec'
@ -53,6 +76,10 @@ class ContrailVrouterException(ValueError):
pass
class SriovVfNotFoundException(ValueError):
pass
def write_config(filename, data):
with open(filename, 'w') as f:
f.write(str(data))
@ -347,6 +374,64 @@ def _get_dpdk_mac_address(name):
return item['mac_address']
def update_sriov_pf_map(ifname, numvfs, noop):
if not noop:
sriov_map = _get_sriov_pf_map()
for item in sriov_map:
if item['name'] == ifname:
item['numvfs'] = numvfs
break
else:
new_item = {}
new_item['name'] = ifname
new_item['numvfs'] = numvfs
sriov_map.append(new_item)
write_yaml_config(_SRIOV_PF_CONFIG_FILE, sriov_map)
def _get_sriov_pf_map():
contents = get_file_data(_SRIOV_PF_CONFIG_FILE)
sriov_map = yaml.load(contents) if contents else []
return sriov_map
def _configure_sriov_config_service():
"""Generate the sriov_config.service
sriov_config service shall configure the numvfs for the SriovPF nics
during reboot of the nodes.
"""
with open(_SRIOV_CONFIG_SERVICE_FILE, 'w') as f:
f.write(_SRIOV_CONFIG_DEVICE_CONTENT)
processutils.execute('systemctl', 'enable', 'sriov_config')
def configure_sriov_pfs():
logger.info("Configuring PFs now")
sriov_config.main()
_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)
vf_path = os.path.join(_SYS_CLASS_NET, pf_name, "device/virtfn%d/net"
% vfid)
if os.path.isdir(vf_path):
vf_nic = os.listdir(vf_path)
else:
msg = "NIC %s with VF id: %d could not be found" % (pf_name, vfid)
raise SriovVfNotFoundException(msg)
if len(vf_nic) != 1:
msg = "VF name could not be identified in %s" % vf_path
raise SriovVfNotFoundException(msg)
# The VF's actual device name shall be the only directory seen in the path
# /sys/class/net/<pf_name>/device/virtfn<vfid>/net
return vf_nic[0]
def restart_vpp(vpp_interfaces):
for vpp_int in vpp_interfaces:
if 'vfio-pci' in vpp_int.uio_driver:

View File

@ -0,0 +1,7 @@
---
features:
- |
sriov_pf and sriov_vf object types are added to allow configuration and
usage of the SR-IOV PF and VF devices in the host machine. The sriov_config
service file shall be used to reconfigure the SR-IOV devices after reboots.
VLAN suport for SR-IOV VF device is not available in this patch.

View File

@ -26,6 +26,7 @@ setup-hooks =
[entry_points]
console_scripts =
os-net-config = os_net_config.cli:main
os-net-config-sriov = os_net_config.sriov_config:main
[build_sphinx]
source-dir = doc/source
@ -36,6 +37,6 @@ all_files = 1
upload-dir = doc/build/html
[egg_info]
tag_build =
tag_build =
tag_date = 0
tag_svn_revision = 0