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:
parent
35eada4d8d
commit
9e785a4ce0
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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:]))
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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..
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue