705 lines
25 KiB
Python
705 lines
25 KiB
Python
# -*- 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 argparse
|
|
import logging
|
|
import os
|
|
import pyudev
|
|
import re
|
|
from six.moves import queue as Queue
|
|
import sys
|
|
import time
|
|
import yaml
|
|
|
|
from os_net_config import sriov_bind_config
|
|
from oslo_concurrency import processutils
|
|
|
|
logger = logging.getLogger(__name__)
|
|
_SYS_CLASS_NET = '/sys/class/net'
|
|
_UDEV_RULE_FILE = '/etc/udev/rules.d/80-persistent-os-net-config.rules'
|
|
_UDEV_LEGACY_RULE_FILE = '/etc/udev/rules.d/70-os-net-config-sriov.rules'
|
|
_IFUP_LOCAL_FILE = '/sbin/ifup-local'
|
|
_RESET_SRIOV_RULES_FILE = '/etc/udev/rules.d/70-tripleo-reset-sriov.rules'
|
|
_ALLOCATE_VFS_FILE = '/etc/sysconfig/allocate_vfs'
|
|
_MLNX_DRIVER = "mlx5_core"
|
|
MLNX_VENDOR_ID = "0x15b3"
|
|
|
|
MAX_RETRIES = 10
|
|
PF_FUNC_RE = re.compile(r"\.(\d+)$", 0)
|
|
# In order to keep VF representor name consistent specially after the upgrade
|
|
# proccess, we should have a udev rule to handle that.
|
|
# The udev rule will rename the VF representor as "<sriov_pf_name>_<vf_num>"
|
|
_REP_LINK_NAME_FILE = "/etc/udev/rep-link-name.sh"
|
|
_REP_LINK_NAME_DATA = '''#!/bin/bash
|
|
# This file is autogenerated by os-net-config
|
|
set -x
|
|
PORT="$1"
|
|
echo "NUMBER=${PORT##pf*vf}"
|
|
'''
|
|
|
|
# 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
|
|
# - 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(
|
|
f"Received udev event {event['action']} for {event['device']}"
|
|
)
|
|
vf_queue.put(event)
|
|
|
|
|
|
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(f"Error reading file: {filename}")
|
|
return ''
|
|
|
|
|
|
def _get_sriov_map():
|
|
contents = get_file_data(_SRIOV_CONFIG_FILE)
|
|
sriov_map = yaml.safe_load(contents) if contents else []
|
|
return sriov_map
|
|
|
|
|
|
def _get_dev_path(ifname, path=None):
|
|
path = f"device/{path}" if path else "device"
|
|
return os.path.join(_SYS_CLASS_NET, ifname, path)
|
|
|
|
|
|
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(
|
|
f"VF: {vf_name} created for PF: {pf_name}"
|
|
)
|
|
vf_count = vf_count + 1
|
|
else:
|
|
logger.warning(f"Unable to parse event {event['device']}")
|
|
else:
|
|
logger.warning(f"{pf_path} is not a directory")
|
|
except Queue.Empty:
|
|
logger.info(f"Timeout in the creation of VFs for PF {pf_name}")
|
|
return
|
|
logger.info(f"Required VFs are created for PF {pf_name}")
|
|
|
|
|
|
def get_numvfs(ifname):
|
|
"""Getting sriov_numvfs for PF
|
|
|
|
Wrapper that will get the sriov_numvfs file for a PF.
|
|
|
|
:param ifname: interface name (ie: p1p1)
|
|
:returns: int -- the number of current VFs on ifname
|
|
:raises: SRIOVNumvfsException
|
|
"""
|
|
sriov_numvfs_path = _get_dev_path(ifname, "sriov_numvfs")
|
|
logger.debug(f"Getting numvfs for interface {ifname}")
|
|
try:
|
|
with open(sriov_numvfs_path, 'r') as f:
|
|
curr_numvfs = int(f.read())
|
|
except IOError as exc:
|
|
msg = f"Unable to read numvfs for {ifname}: {exc}"
|
|
raise SRIOVNumvfsException(msg)
|
|
logger.debug(f"Interface {ifname} has {curr_numvfs} configured")
|
|
return curr_numvfs
|
|
|
|
|
|
def set_numvfs(ifname, numvfs):
|
|
"""Setting sriov_numvfs for PF
|
|
|
|
Wrapper that will set the sriov_numvfs file for a PF.
|
|
|
|
After numvfs has been set for an interface, _wait_for_vf_creation will be
|
|
called to monitor the creation.
|
|
|
|
Some restrictions:
|
|
- if current number of VF is already defined, we can't change numvfs
|
|
- if sriov_numvfs doesn't exist for an interface, we can't create it
|
|
|
|
:param ifname: interface name (ie: p1p1)
|
|
:param numvfs: an int that represents the number of VFs to be created.
|
|
:returns: int -- the number of current VFs on ifname
|
|
:raises: SRIOVNumvfsException
|
|
"""
|
|
curr_numvfs = get_numvfs(ifname)
|
|
logger.debug(f"Interface {ifname} has {curr_numvfs} configured, setting "
|
|
f"to {numvfs}")
|
|
if not isinstance(numvfs, int):
|
|
msg = (f"Unable to configure pf: {ifname} with numvfs: {numvfs}\n"
|
|
f"numvfs must be an integer")
|
|
raise SRIOVNumvfsException(msg)
|
|
|
|
if numvfs != curr_numvfs:
|
|
if curr_numvfs != 0:
|
|
logger.warning(f"Numvfs already configured to {curr_numvfs} for "
|
|
f"{ifname}")
|
|
return curr_numvfs
|
|
|
|
sriov_numvfs_path = _get_dev_path(ifname, "sriov_numvfs")
|
|
try:
|
|
with open(sriov_numvfs_path, 'w') as f:
|
|
f.write("%d" % numvfs)
|
|
except IOError as exc:
|
|
msg = (f"Unable to configure pf: {ifname} with numvfs: {numvfs}\n"
|
|
f"{exc}")
|
|
raise SRIOVNumvfsException(msg)
|
|
|
|
_wait_for_vf_creation(ifname, numvfs)
|
|
curr_numvfs = get_numvfs(ifname)
|
|
if curr_numvfs != numvfs:
|
|
msg = (f"Unable to configure pf: {ifname} with numvfs: {numvfs}\n"
|
|
"sriov_numvfs file is not set to the targeted number of "
|
|
"vfs")
|
|
raise SRIOVNumvfsException(msg)
|
|
return curr_numvfs
|
|
|
|
|
|
def restart_ovs_and_pfs_netdevs():
|
|
sriov_map = _get_sriov_map()
|
|
processutils.execute('/usr/bin/systemctl', 'restart', 'openvswitch')
|
|
for item in sriov_map:
|
|
if item['device_type'] == 'pf':
|
|
if_down_interface(item['name'])
|
|
if_up_interface(item['name'])
|
|
|
|
|
|
def cleanup_puppet_config():
|
|
file_contents = ""
|
|
if os.path.exists(_RESET_SRIOV_RULES_FILE):
|
|
os.remove(_RESET_SRIOV_RULES_FILE)
|
|
if os.path.exists(_ALLOCATE_VFS_FILE):
|
|
os.remove(_ALLOCATE_VFS_FILE)
|
|
if os.path.exists(_IFUP_LOCAL_FILE):
|
|
# Remove the invocation of allocate_vfs script generated by puppet
|
|
# After the removal of allocate_vfs, if the ifup-local file has just
|
|
# "#!/bin/bash" left, then remove the file as well.
|
|
with open(_IFUP_LOCAL_FILE) as oldfile:
|
|
for line in oldfile:
|
|
if "/etc/sysconfig/allocate_vfs" not in line:
|
|
file_contents = file_contents + line
|
|
if file_contents.strip() == "#!/bin/bash":
|
|
os.remove(_IFUP_LOCAL_FILE)
|
|
else:
|
|
with open(_IFUP_LOCAL_FILE, 'w') as newfile:
|
|
newfile.write(file_contents)
|
|
|
|
|
|
def udev_monitor_setup():
|
|
# 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)
|
|
return observer
|
|
|
|
|
|
def udev_monitor_start(observer):
|
|
observer.start()
|
|
|
|
|
|
def udev_monitor_stop(observer):
|
|
observer.stop()
|
|
|
|
|
|
def is_partitioned_pf(dev_name: str) -> bool:
|
|
"""Check if any nic-partition(VF) is already used
|
|
|
|
Given a PF device, returns True if any VFs of this
|
|
device are in-use.
|
|
"""
|
|
sriov_map = _get_sriov_map()
|
|
for config in sriov_map:
|
|
devtype = config.get('device_type', None)
|
|
if devtype == 'vf':
|
|
name = config.get('device', {}).get('name')
|
|
vf_name = config.get('name')
|
|
if dev_name == name:
|
|
logger.warning("%s has VF(%s) used by host" % (name, vf_name))
|
|
return True
|
|
return False
|
|
|
|
|
|
def configure_sriov_pf(execution_from_cli=False, restart_openvswitch=False):
|
|
observer = udev_monitor_setup()
|
|
udev_monitor_start(observer)
|
|
|
|
sriov_map = _get_sriov_map()
|
|
MLNX_UNBIND_FILE_PATH = "/sys/bus/pci/drivers/mlx5_core/unbind"
|
|
mlnx_vfs_pcis_list = []
|
|
trigger_udev_rule = False
|
|
|
|
# Cleanup the previous config by puppet-tripleo
|
|
cleanup_puppet_config()
|
|
|
|
for item in sriov_map:
|
|
if item['device_type'] == 'pf':
|
|
_pf_interface_up(item)
|
|
if item.get('link_mode') == "legacy":
|
|
# Add a udev rule to configure the VF's when PF's are
|
|
# released by a guest
|
|
if not is_partitioned_pf(item['name']):
|
|
add_udev_rule_for_legacy_sriov_pf(item['name'],
|
|
item['numvfs'])
|
|
set_numvfs(item['name'], item['numvfs'])
|
|
vendor_id = get_vendor_id(item['name'])
|
|
|
|
if (item.get('link_mode') == "switchdev" and
|
|
vendor_id == MLNX_VENDOR_ID):
|
|
logger.info(f"{item['name']}: Mellanox card")
|
|
vf_pcis_list = get_vf_pcis_list(item['name'])
|
|
mlnx_vfs_pcis_list += vf_pcis_list
|
|
for vf_pci in vf_pcis_list:
|
|
vf_pci_path = f"/sys/bus/pci/devices/{vf_pci}/driver"
|
|
if os.path.exists(vf_pci_path):
|
|
logger.info(f"Unbinding {vf_pci}")
|
|
with open(MLNX_UNBIND_FILE_PATH, 'w') as f:
|
|
f.write(vf_pci)
|
|
|
|
logger.info(f"{item['name']}: Adding udev rules")
|
|
# Adding a udev rule to make vf-representors unmanaged by
|
|
# NetworkManager
|
|
add_udev_rule_to_unmanage_vf_representors_by_nm()
|
|
|
|
# Adding a udev rule to save the sriov_pf name
|
|
trigger_udev_rule = add_udev_rule_for_sriov_pf(item['name'])\
|
|
or trigger_udev_rule
|
|
|
|
configure_smfs_software_steering(item['name'])
|
|
|
|
# Configure switchdev mode
|
|
configure_switchdev(item['name'])
|
|
|
|
# Adding a udev rule to rename vf-representors
|
|
trigger_udev_rule = add_udev_rule_for_vf_representors(
|
|
item['name']) or trigger_udev_rule
|
|
|
|
# Moving the sriov-PFs to switchdev mode will put the netdev
|
|
# interfaces in down state.
|
|
# In case we are running during initial deployment,
|
|
# bring the interfaces up.
|
|
# In case we are running as part of the sriov_config service
|
|
# after reboot, net config scripts, which run after
|
|
# sriov_config service will bring the interfaces up.
|
|
if execution_from_cli:
|
|
if_up_interface(item['name'])
|
|
|
|
if mlnx_vfs_pcis_list:
|
|
sriov_bind_pcis_map = {_MLNX_DRIVER: mlnx_vfs_pcis_list}
|
|
if not execution_from_cli:
|
|
sriov_bind_config.update_sriov_bind_pcis_map(sriov_bind_pcis_map)
|
|
else:
|
|
sriov_bind_config.configure_sriov_bind_service()
|
|
sriov_bind_config.bind_vfs(sriov_bind_pcis_map)
|
|
|
|
# Trigger udev rules if there is new rules written
|
|
if trigger_udev_rule:
|
|
trigger_udev_rules()
|
|
|
|
udev_monitor_stop(observer)
|
|
if restart_openvswitch:
|
|
restart_ovs_and_pfs_netdevs()
|
|
|
|
|
|
def _wait_for_uplink_rep_creation(pf_name):
|
|
uplink_rep_phys_switch_id_path = f"/sys/class/net/{pf_name}/phys_switch_id"
|
|
|
|
for i in range(MAX_RETRIES):
|
|
if get_file_data(uplink_rep_phys_switch_id_path):
|
|
logger.info(f"Uplink representor {pf_name} ready")
|
|
break
|
|
time.sleep(1)
|
|
else:
|
|
raise RuntimeError(f"Timeout while waiting for uplink representor "
|
|
f"{pf_name}.")
|
|
|
|
|
|
def create_rep_link_name_script():
|
|
with open(_REP_LINK_NAME_FILE, "w") as f:
|
|
f.write(_REP_LINK_NAME_DATA)
|
|
# Make the _REP_LINK_NAME_FILE executable
|
|
os.chmod(_REP_LINK_NAME_FILE, 0o755)
|
|
|
|
|
|
def add_udev_rule_for_sriov_pf(pf_name):
|
|
logger.info(f"adding udev rules for sriov {pf_name}")
|
|
pf_pci = get_pf_pci(pf_name)
|
|
udev_data_line = 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '\
|
|
f'KERNELS=="{pf_pci}", NAME="{pf_name}"'
|
|
return add_udev_rule(udev_data_line, _UDEV_RULE_FILE)
|
|
|
|
|
|
def add_udev_rule_for_legacy_sriov_pf(pf_name, numvfs):
|
|
logger.info(f"adding udev rules for legacy sriov {pf_name}: {numvfs}")
|
|
udev_line = f'KERNEL=="{pf_name}", '\
|
|
f'RUN+="/bin/os-net-config-sriov -n %k:{numvfs}"'
|
|
pattern = f'KERNEL=="{pf_name}", RUN+="/bin/os-net-config-sriov -n'
|
|
return add_udev_rule(udev_line, _UDEV_LEGACY_RULE_FILE, pattern)
|
|
|
|
|
|
def add_udev_rule_for_vf_representors(pf_name):
|
|
logger.info(f"adding udev rules for vf representators {pf_name}")
|
|
phys_switch_id_path = os.path.join(_SYS_CLASS_NET, pf_name,
|
|
"phys_switch_id")
|
|
phys_switch_id = get_file_data(phys_switch_id_path).strip()
|
|
pf_pci = get_pf_pci(pf_name)
|
|
pf_fun_num_match = PF_FUNC_RE.search(pf_pci)
|
|
if pf_fun_num_match:
|
|
pf_fun_num = pf_fun_num_match.group(1)
|
|
else:
|
|
logger.error(f"Failed to get function number for {pf_name} \n"
|
|
"and so failed to create a udev rule for renaming "
|
|
"its' vf-represent")
|
|
return
|
|
|
|
udev_data_line = 'SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}'\
|
|
'=="%s", ATTR{phys_port_name}=="pf%svf*", '\
|
|
'IMPORT{program}="%s $attr{phys_port_name}", '\
|
|
'NAME="%s_$env{NUMBER}"' % (phys_switch_id,
|
|
pf_fun_num,
|
|
_REP_LINK_NAME_FILE,
|
|
pf_name)
|
|
create_rep_link_name_script()
|
|
return add_udev_rule(udev_data_line, _UDEV_RULE_FILE)
|
|
|
|
|
|
def add_udev_rule_to_unmanage_vf_representors_by_nm():
|
|
logger.info(f"adding udev rules to unmanage vf representators")
|
|
udev_data_line = 'SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}'\
|
|
'!="", ATTR{phys_port_name}=="pf*vf*", '\
|
|
'ENV{NM_UNMANAGED}="1"'
|
|
return add_udev_rule(udev_data_line, _UDEV_RULE_FILE)
|
|
|
|
|
|
def add_udev_rule(udev_data, udev_file, pattern=None):
|
|
logger.debug(f"adding udev rule to {udev_file}: {udev_data}")
|
|
trigger_udev_rule = False
|
|
udev_data = udev_data.strip()
|
|
if not pattern:
|
|
pattern = udev_data
|
|
if not os.path.exists(udev_file):
|
|
with open(udev_file, "w") as f:
|
|
data = "# This file is autogenerated by os-net-config\n"\
|
|
f"{udev_data}\n"
|
|
f.write(data)
|
|
else:
|
|
file_data = get_file_data(udev_file)
|
|
udev_lines = file_data.splitlines()
|
|
if pattern in file_data:
|
|
if udev_data in udev_lines:
|
|
return trigger_udev_rule
|
|
with open(udev_file, "w") as f:
|
|
for line in udev_lines:
|
|
if pattern in line:
|
|
f.write(udev_data + "\n")
|
|
else:
|
|
f.write(line + "\n")
|
|
else:
|
|
with open(udev_file, "a") as f:
|
|
f.write(udev_data + "\n")
|
|
|
|
reload_udev_rules()
|
|
trigger_udev_rule = True
|
|
return trigger_udev_rule
|
|
|
|
|
|
def reload_udev_rules():
|
|
try:
|
|
processutils.execute('/usr/sbin/udevadm', 'control', '--reload-rules')
|
|
logger.info("udev rules reloaded successfully")
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error(f"Failed to reload udev rules: {exc}")
|
|
raise
|
|
|
|
|
|
def trigger_udev_rules():
|
|
try:
|
|
processutils.execute('/usr/sbin/udevadm', 'trigger', '--action=add',
|
|
'--attr-match=subsystem=net')
|
|
logger.info("udev rules triggered successfully")
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error(f"Failed to trigger udev rules: {exc}")
|
|
raise
|
|
|
|
|
|
def configure_switchdev(pf_name):
|
|
pf_pci = get_pf_pci(pf_name)
|
|
pf_device_id = get_pf_device_id(pf_name)
|
|
if pf_device_id == "0x1013" or pf_device_id == "0x1015":
|
|
try:
|
|
processutils.execute('/usr/sbin/devlink', 'dev', 'eswitch', 'set',
|
|
f'pci/{pf_pci}', 'inline-mode', 'transport')
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error(f"Failed to set inline-mode to transport for "
|
|
f"{pf_pci}: {exc}")
|
|
raise
|
|
try:
|
|
processutils.execute('/usr/sbin/devlink', 'dev', 'eswitch', 'set',
|
|
f'pci/{pf_pci}', 'mode', 'switchdev')
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error(f"Failed to set mode to switchdev for {pf_pci}: {exc}")
|
|
raise
|
|
logger.info(f"Device pci/{pf_pci} set to switchdev mode.")
|
|
|
|
# WA to make sure that the uplink_rep is ready after moving to switchdev,
|
|
# as moving to switchdev will remove the sriov_pf and create uplink
|
|
# representor, so we need to make sure that uplink representor is ready
|
|
# before proceed
|
|
_wait_for_uplink_rep_creation(pf_name)
|
|
|
|
try:
|
|
processutils.execute('/usr/sbin/ethtool', '-K', pf_name,
|
|
'hw-tc-offload', 'on')
|
|
logger.info(f"Enabled \"hw-tc-offload\" for PF {pf_name}.")
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error(f"Failed to enable hw-tc-offload on {pf_name}: {exc}")
|
|
raise
|
|
|
|
|
|
def configure_smfs_software_steering(pf_name):
|
|
pf_pci = get_pf_pci(pf_name)
|
|
try:
|
|
processutils.execute('/usr/sbin/devlink', 'dev', 'param', 'set',
|
|
f'pci/{pf_pci}', 'name', 'flow_steering_mode',
|
|
'value', 'smfs', 'cmode', 'runtime')
|
|
logger.info(f"Device pci/{pf_pci} is set to smfs steering mode.")
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.warning(f"Could not set pci/{pf_pci} to smfs steering mode: "
|
|
f"{exc}")
|
|
|
|
|
|
def run_ip_config_cmd(*cmd, **kwargs):
|
|
logger.info("Running %s" % ' '.join(cmd))
|
|
try:
|
|
processutils.execute(*cmd, delay_on_retry=True, attempts=10, **kwargs)
|
|
except processutils.ProcessExecutionError as exc:
|
|
logger.error("Failed to execute %s: %s" % (' '.join(cmd), exc))
|
|
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(f"Bringing up PF: {pf_device['name']}")
|
|
run_ip_config_cmd('ip', 'link', 'set', 'dev', pf_device['name'], 'up')
|
|
|
|
|
|
def get_vendor_id(ifname):
|
|
try:
|
|
with open(_get_dev_path(ifname, "vendor"), 'r') as f:
|
|
out = f.read().strip()
|
|
return out
|
|
except IOError:
|
|
return
|
|
|
|
|
|
def get_pf_pci(pf_name):
|
|
pf_pci_path = _get_dev_path(pf_name, "uevent")
|
|
pf_info = get_file_data(pf_pci_path)
|
|
pf_pci = re.search(r'PCI_SLOT_NAME=(.*)', pf_info, re.MULTILINE).group(1)
|
|
return pf_pci
|
|
|
|
|
|
def get_pf_device_id(pf_name):
|
|
pf_device_path = _get_dev_path(pf_name, "device")
|
|
pf_device_id = get_file_data(pf_device_path).strip()
|
|
return pf_device_id
|
|
|
|
|
|
def get_vf_pcis_list(pf_name):
|
|
vf_pcis_list = []
|
|
listOfPfFiles = os.listdir(os.path.join(_SYS_CLASS_NET, pf_name,
|
|
"device"))
|
|
for pf_file in listOfPfFiles:
|
|
if pf_file.startswith("virtfn"):
|
|
vf_info = get_file_data(_get_dev_path(pf_name,
|
|
f"{pf_file}/uevent"))
|
|
vf_pcis_list.append(re.search(r'PCI_SLOT_NAME=(.*)',
|
|
vf_info, re.MULTILINE).group(1))
|
|
return vf_pcis_list
|
|
|
|
|
|
def if_down_interface(device):
|
|
logger.info(f"Running /sbin/ifdown {device}")
|
|
try:
|
|
processutils.execute('/sbin/ifdown', device)
|
|
except processutils.ProcessExecutionError:
|
|
logger.error(f"Failed to ifdown {device}")
|
|
raise
|
|
|
|
|
|
def if_up_interface(device):
|
|
logger.info(f"Running /sbin/ifup {device}")
|
|
try:
|
|
processutils.execute('/sbin/ifup', device)
|
|
except processutils.ProcessExecutionError:
|
|
logger.error(f"Failed to ifup {device}")
|
|
raise
|
|
|
|
|
|
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(f"Configuring settings for PF: {pf_name} VF: {vfid} "
|
|
f"VF name: {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 'max_tx_rate' in item:
|
|
cmd = base_cmd + ('max_tx_rate', str(item['max_tx_rate']))
|
|
run_ip_config_cmd(*cmd)
|
|
if 'min_tx_rate' in item:
|
|
cmd = base_cmd + ('min_tx_rate', str(item['min_tx_rate']))
|
|
run_ip_config_cmd(*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)
|
|
|
|
parser.add_argument(
|
|
'-n', '--numvfs',
|
|
dest="numvfs",
|
|
action='store',
|
|
help="Provide the numvfs for device in the format <device>:<numvfs>",
|
|
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)
|
|
|
|
if opts.numvfs:
|
|
if re.match(r"^\w+:\d+$", opts.numvfs):
|
|
device_name, numvfs = opts.numvfs.split(':')
|
|
set_numvfs(device_name, int(numvfs))
|
|
else:
|
|
logging.error(f"Invalid arguments for --numvfs {opts.numvfs}")
|
|
return 1
|
|
else:
|
|
# Configure the PF's
|
|
configure_sriov_pf()
|
|
# Configure the VFs
|
|
configure_sriov_vf()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv))
|