Merge "Use pyroute2 for SRIOV VF commands" into stable/train

This commit is contained in:
Zuul 2020-06-23 02:19:33 +00:00 committed by Gerrit Code Review
commit 4744f37662
18 changed files with 237 additions and 673 deletions

View File

@ -28,7 +28,6 @@ from pyroute2.netlink import exceptions as netlink_exceptions
from pyroute2.netlink import rtnl from pyroute2.netlink import rtnl
from pyroute2.netlink.rtnl import ifaddrmsg from pyroute2.netlink.rtnl import ifaddrmsg
from pyroute2.netlink.rtnl import ifinfmsg from pyroute2.netlink.rtnl import ifinfmsg
from pyroute2 import NetlinkError
from pyroute2 import netns from pyroute2 import netns
from neutron._i18n import _ from neutron._i18n import _
@ -100,9 +99,7 @@ class AddressNotReady(exceptions.NeutronException):
"become ready: %(reason)s") "become ready: %(reason)s")
class InvalidArgument(exceptions.NeutronException): InvalidArgument = privileged.InvalidArgument
message = _("Invalid value %(value)s for parameter %(parameter)s "
"provided.")
class SubProcessBase(object): class SubProcessBase(object):
@ -442,13 +439,8 @@ class IpLinkCommand(IpDeviceCommandBase):
self.name, self._parent.namespace, ifinfmsg.IFF_ALLMULTI) self.name, self._parent.namespace, ifinfmsg.IFF_ALLMULTI)
def set_mtu(self, mtu_size): def set_mtu(self, mtu_size):
try: privileged.set_link_attribute(
privileged.set_link_attribute( self.name, self._parent.namespace, mtu=mtu_size)
self.name, self._parent.namespace, mtu=mtu_size)
except NetlinkError as e:
if e.code == errno.EINVAL:
raise InvalidArgument(parameter="MTU", value=mtu_size)
raise
def set_up(self): def set_up(self):
privileged.set_link_attribute( privileged.set_link_attribute(
@ -520,6 +512,13 @@ class IpLinkCommand(IpDeviceCommandBase):
def exists(self): def exists(self):
return privileged.interface_exists(self.name, self._parent.namespace) return privileged.interface_exists(self.name, self._parent.namespace)
def get_vfs(self):
return privileged.get_link_vfs(self.name, self._parent.namespace)
def set_vf_feature(self, vf_config):
return privileged.set_link_vf_feature(
self.name, self._parent.namespace, vf_config)
class IpAddrCommand(IpDeviceCommandBase): class IpAddrCommand(IpDeviceCommandBase):
COMMAND = 'addr' COMMAND = 'addr'

View File

@ -1,108 +0,0 @@
# Copyright 2014 Mellanox Technologies, Ltd
#
# 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.
import re
from neutron_lib import exceptions as n_exc
from oslo_log import log as logging
from neutron._i18n import _
from neutron.agent.linux import utils
LOG = logging.getLogger(__name__)
class IpLinkSupportError(n_exc.NeutronException):
pass
class UnsupportedIpLinkCommand(IpLinkSupportError):
message = _("ip link command is not supported: %(reason)s")
class InvalidIpLinkCapability(IpLinkSupportError):
message = _("ip link capability %(capability)s is not supported")
class IpLinkConstants(object):
IP_LINK_CAPABILITY_STATE = "state"
IP_LINK_CAPABILITY_VLAN = "vlan"
IP_LINK_CAPABILITY_RATE = "rate"
IP_LINK_CAPABILITY_MIN_TX_RATE = "min_tx_rate"
IP_LINK_CAPABILITY_SPOOFCHK = "spoofchk"
IP_LINK_SUB_CAPABILITY_QOS = "qos"
class IpLinkSupport(object):
VF_BLOCK_REGEX = r"\[ vf NUM(?P<vf_block>.*) \] \]"
CAPABILITY_REGEX = r"\[ %s (.*)"
SUB_CAPABILITY_REGEX = r"\[ %(cap)s (.*) \[ %(subcap)s (.*)"
@classmethod
def get_vf_mgmt_section(cls):
"""Parses ip link help output, and gets vf block"""
output = cls._get_ip_link_output()
vf_block_pattern = re.search(cls.VF_BLOCK_REGEX,
output,
re.DOTALL | re.MULTILINE)
if vf_block_pattern:
return vf_block_pattern.group("vf_block")
@classmethod
def vf_mgmt_capability_supported(cls, vf_section, capability,
subcapability=None):
"""Validate vf capability support
Checks if given vf capability (and sub capability
if given) supported
:param vf_section: vf Num block content
:param capability: for example: vlan, rate, spoofchk, state
:param subcapability: for example: qos
"""
if not vf_section:
return False
if subcapability:
regex = cls.SUB_CAPABILITY_REGEX % {"cap": capability,
"subcap": subcapability}
else:
regex = cls.CAPABILITY_REGEX % capability
pattern_match = re.search(regex, vf_section,
re.DOTALL | re.MULTILINE)
return pattern_match is not None
@classmethod
def _get_ip_link_output(cls):
"""Gets the output of the ip link help command
Runs ip link help command and stores its output
Note: ip link help return error and writes its output to stderr
so we get the output from there. however, if this issue
will be solved and the command will write to stdout, we
will get the output from there too.
"""
try:
ip_cmd = ['ip', 'link', 'help']
_stdout, _stderr = utils.execute(
ip_cmd,
check_exit_code=False,
return_stderr=True,
log_fail_as_error=False)
except Exception as e:
LOG.exception("Failed executing ip command")
raise UnsupportedIpLinkCommand(reason=e)
return _stdout or _stderr

View File

@ -29,7 +29,6 @@ from neutron.agent.l3 import ha_router
from neutron.agent.l3 import namespaces from neutron.agent.l3 import namespaces
from neutron.agent.linux import external_process from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
from neutron.agent.linux import ip_link_support
from neutron.agent.linux import keepalived from neutron.agent.linux import keepalived
from neutron.agent.linux import utils as agent_utils from neutron.agent.linux import utils as agent_utils
from neutron.cmd import runtime_checks from neutron.cmd import runtime_checks
@ -146,41 +145,6 @@ def icmpv6_header_match_supported():
actions="NORMAL") actions="NORMAL")
def _vf_management_support(required_caps):
is_supported = True
try:
vf_section = ip_link_support.IpLinkSupport.get_vf_mgmt_section()
for cap in required_caps:
if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported(
vf_section, cap):
is_supported = False
LOG.debug("ip link command does not support "
"vf capability '%(cap)s'", {'cap': cap})
except ip_link_support.UnsupportedIpLinkCommand:
LOG.exception("Unexpected exception while checking supported "
"ip link command")
return False
return is_supported
def vf_management_supported():
required_caps = (
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE)
return _vf_management_support(required_caps)
def vf_extended_management_supported():
required_caps = (
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE,
)
return _vf_management_support(required_caps)
def netns_read_requires_helper(): def netns_read_requires_helper():
nsname = "netnsreadtest-" + uuidutils.generate_uuid() nsname = "netnsreadtest-" + uuidutils.generate_uuid()
ip_lib.create_network_namespace(nsname) ip_lib.create_network_namespace(nsname)

View File

@ -182,25 +182,6 @@ def check_icmpv6_header_match():
return result return result
def check_vf_management():
result = checks.vf_management_supported()
if not result:
LOG.error('Check for VF management support failed. '
'Please ensure that the version of ip link '
'being used has VF support.')
return result
def check_vf_extended_management():
result = checks.vf_extended_management_supported()
if not result:
LOG.error('Check for VF extended management support failed. '
'Please ensure that the version of ip link '
'being used has VF extended support: version '
'"iproute2-ss140804", git tag "v3.16.0"')
return result
def check_ovsdb_native(): def check_ovsdb_native():
result = checks.ovsdb_native_supported() result = checks.ovsdb_native_supported()
if not result: if not result:
@ -315,10 +296,6 @@ OPTS = [
help=_('Check for ARP header match support')), help=_('Check for ARP header match support')),
BoolOptCallback('icmpv6_header_match', check_icmpv6_header_match, BoolOptCallback('icmpv6_header_match', check_icmpv6_header_match,
help=_('Check for ICMPv6 header match support')), help=_('Check for ICMPv6 header match support')),
BoolOptCallback('vf_management', check_vf_management,
help=_('Check for VF management support')),
BoolOptCallback('vf_extended_management', check_vf_extended_management,
help=_('Check for VF extended management support')),
BoolOptCallback('read_netns', check_read_netns, BoolOptCallback('read_netns', check_read_netns,
help=_('Check netns permission settings')), help=_('Check netns permission settings')),
BoolOptCallback('dnsmasq_local_service_supported', BoolOptCallback('dnsmasq_local_service_supported',

View File

@ -26,13 +26,5 @@ class InvalidDeviceError(SriovNicError):
message = _("Invalid Device %(dev_name)s: %(reason)s") message = _("Invalid Device %(dev_name)s: %(reason)s")
class IpCommandError(SriovNicError):
message = _("ip command failed on device %(dev_name)s: %(reason)s")
class IpCommandOperationNotSupportedError(SriovNicError):
message = _("Operation not supported on device %(dev_name)s")
class InvalidPciSlotError(SriovNicError): class InvalidPciSlotError(SriovNicError):
message = _("Invalid pci slot %(pci_slot)s") message = _("Invalid pci slot %(pci_slot)s")

View File

@ -21,7 +21,6 @@ from neutron_lib.utils import helpers
from oslo_log import log as logging from oslo_log import log as logging
from neutron._i18n import _ from neutron._i18n import _
from neutron.agent.linux import ip_link_support
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
@ -29,6 +28,14 @@ from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
IP_LINK_CAPABILITY_STATE = 'state'
IP_LINK_CAPABILITY_VLAN = 'vlan'
IP_LINK_CAPABILITY_RATE = 'max_tx_rate'
IP_LINK_CAPABILITY_MIN_TX_RATE = 'min_tx_rate'
IP_LINK_CAPABILITY_SPOOFCHK = 'spoofchk'
IP_LINK_SUB_CAPABILITY_QOS = 'qos'
class PciOsWrapper(object): class PciOsWrapper(object):
"""OS wrapper for checking virtual functions""" """OS wrapper for checking virtual functions"""
@ -200,11 +207,11 @@ class EmbSwitch(object):
auto=propagate_uplink_state) auto=propagate_uplink_state)
def set_device_rate(self, pci_slot, rate_type, rate_kbps): def set_device_rate(self, pci_slot, rate_type, rate_kbps):
"""Set device rate: rate (max_tx_rate), min_tx_rate """Set device rate: max_tx_rate, min_tx_rate
@param pci_slot: Virtual Function address @param pci_slot: Virtual Function address
@param rate_type: device rate name type. Could be 'rate' and @param rate_type: device rate name type. Could be 'max_tx_rate' and
'min_tx_rate'. 'min_tx_rate' ('rate' is not supported anymore).
@param rate_kbps: device rate in kbps @param rate_kbps: device rate in kbps
""" """
vf_index = self._get_vf_index(pci_slot) vf_index = self._get_vf_index(pci_slot)
@ -349,7 +356,7 @@ class ESwitchManager(object):
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch: if embedded_switch:
return embedded_switch.get_device_state(pci_slot) return embedded_switch.get_device_state(pci_slot)
return pci_lib.LinkState.DISABLE return pci_lib.LinkState.disable.name
def set_device_max_rate(self, device_mac, pci_slot, max_kbps): def set_device_max_rate(self, device_mac, pci_slot, max_kbps):
"""Set device max rate """Set device max rate
@ -362,9 +369,7 @@ class ESwitchManager(object):
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch: if embedded_switch:
embedded_switch.set_device_rate( embedded_switch.set_device_rate(
pci_slot, pci_slot, IP_LINK_CAPABILITY_RATE, max_kbps)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE,
max_kbps)
def set_device_min_tx_rate(self, device_mac, pci_slot, min_kbps): def set_device_min_tx_rate(self, device_mac, pci_slot, min_kbps):
"""Set device min_tx_rate """Set device min_tx_rate
@ -377,9 +382,7 @@ class ESwitchManager(object):
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot) embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
if embedded_switch: if embedded_switch:
embedded_switch.set_device_rate( embedded_switch.set_device_rate(
pci_slot, pci_slot, IP_LINK_CAPABILITY_MIN_TX_RATE, min_kbps)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE,
min_kbps)
def set_device_state(self, device_mac, pci_slot, admin_state_up, def set_device_state(self, device_mac, pci_slot, admin_state_up,
propagate_uplink_state): propagate_uplink_state):
@ -498,9 +501,7 @@ class ESwitchManager(object):
Clear the "rate" configuration from VF by setting it to 0. Clear the "rate" configuration from VF by setting it to 0.
@param pci_slot: VF PCI slot @param pci_slot: VF PCI slot
""" """
self._clear_rate( self._clear_rate(pci_slot, IP_LINK_CAPABILITY_RATE)
pci_slot,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE)
def clear_min_tx_rate(self, pci_slot): def clear_min_tx_rate(self, pci_slot):
"""Clear the VF "min_tx_rate" parameter """Clear the VF "min_tx_rate" parameter
@ -508,16 +509,14 @@ class ESwitchManager(object):
Clear the "min_tx_rate" configuration from VF by setting it to 0. Clear the "min_tx_rate" configuration from VF by setting it to 0.
@param pci_slot: VF PCI slot @param pci_slot: VF PCI slot
""" """
self._clear_rate( self._clear_rate(pci_slot, IP_LINK_CAPABILITY_MIN_TX_RATE)
pci_slot,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE)
def _clear_rate(self, pci_slot, rate_type): def _clear_rate(self, pci_slot, rate_type):
"""Clear the VF rate parameter specified in rate_type """Clear the VF rate parameter specified in rate_type
Clear the rate configuration from VF by setting it to 0. Clear the rate configuration from VF by setting it to 0.
@param pci_slot: VF PCI slot @param pci_slot: VF PCI slot
@param rate_type: rate to clear ('rate', 'min_tx_rate') @param rate_type: rate to clear ('max_tx_rate', 'min_tx_rate')
""" """
# NOTE(Moshe Levi): we don't use the self._get_emb_eswitch here, # NOTE(Moshe Levi): we don't use the self._get_emb_eswitch here,
# because when clearing the VF it may be not assigned. This happens # because when clearing the VF it may be not assigned. This happens

View File

@ -15,9 +15,8 @@
from oslo_log import log as logging from oslo_log import log as logging
from neutron.agent.l2.extensions import qos_linux as qos from neutron.agent.l2.extensions import qos_linux as qos
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import (
exceptions as exc)
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
from neutron.services.qos.drivers.sriov import driver from neutron.services.qos.drivers.sriov import driver
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -55,9 +54,9 @@ class QosSRIOVAgentDriver(qos.QosLinuxAgentDriver):
try: try:
self.eswitch_mgr.set_device_max_rate( self.eswitch_mgr.set_device_max_rate(
device, pci_slot, max_kbps) device, pci_slot, max_kbps)
except exc.SriovNicError: except (priv_ip_lib.InterfaceOperationNotSupported,
LOG.exception( priv_ip_lib.InvalidArgument):
"Failed to set device %s max rate", device) LOG.exception("Failed to set device %s max rate", device)
else: else:
LOG.info("No device with MAC %s defined on agent.", device) LOG.info("No device with MAC %s defined on agent.", device)
@ -97,8 +96,8 @@ class QosSRIOVAgentDriver(qos.QosLinuxAgentDriver):
try: try:
self.eswitch_mgr.set_device_min_tx_rate( self.eswitch_mgr.set_device_min_tx_rate(
device, pci_slot, min_tx_kbps) device, pci_slot, min_tx_kbps)
except exc.SriovNicError: except (priv_ip_lib.InterfaceOperationNotSupported,
LOG.exception( priv_ip_lib.InvalidArgument):
"Failed to set device %s min_tx_rate", device) LOG.exception("Failed to set device %s min_tx_rate", device)
else: else:
LOG.info("No device with MAC %s defined on agent.", device) LOG.info("No device with MAC %s defined on agent.", device)

View File

@ -13,86 +13,42 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import re import enum
from oslo_log import log as logging from oslo_log import log as logging
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class LinkState(object): class LinkState(enum.Enum):
ENABLE = "enable" auto = 0
DISABLE = "disable" enable = 1
AUTO = "auto" disable = 2
class PciDeviceIPWrapper(ip_lib.IPWrapper): class PciDeviceIPWrapper(ip_lib.IPWrapper):
"""Wrapper class for ip link commands. """Wrapper class for ip link commands related to virtual functions."""
wrapper for getting/setting pci device details using ip link...
"""
VF_PATTERN = r"^vf\s+(?P<vf_index>\d+)\s+"
MAC_PATTERN = r"MAC\s+(?P<mac>[a-fA-F0-9:]+),"
STATE_PATTERN = r"\s+link-state\s+(?P<state>\w+)"
ANY_PATTERN = ".*,"
VF_LINE_FORMAT = VF_PATTERN + MAC_PATTERN + ANY_PATTERN + STATE_PATTERN
VF_DETAILS_REG_EX = re.compile(VF_LINE_FORMAT)
IP_LINK_OP_NOT_SUPPORTED = 'RTNETLINK answers: Operation not supported'
def __init__(self, dev_name): def __init__(self, dev_name):
super(PciDeviceIPWrapper, self).__init__() super(PciDeviceIPWrapper, self).__init__()
self.dev_name = dev_name self.dev_name = dev_name
def _set_feature(self, vf_index, feature, value):
"""Sets vf feature
Checks if the feature is not supported or there's some
general error during ip link invocation and raises
exception accordingly.
:param vf_index: vf index
:param feature: name of a feature to be passed to ip link,
such as 'state' or 'spoofchk'
:param value: value of the feature setting
"""
try:
self._as_root([], "link", ("set", self.dev_name, "vf",
str(vf_index), feature, value))
except Exception as e:
if self.IP_LINK_OP_NOT_SUPPORTED in str(e):
raise exc.IpCommandOperationNotSupportedError(
dev_name=self.dev_name)
else:
raise exc.IpCommandError(dev_name=self.dev_name,
reason=str(e))
def get_assigned_macs(self, vf_list): def get_assigned_macs(self, vf_list):
"""Get assigned mac addresses for vf list. """Get assigned mac addresses for vf list.
@param vf_list: list of vf indexes @param vf_list: list of vf indexes
@return: dict mapping of vf to mac @return: dict mapping of vf to mac
""" """
try: ip = self.device(self.dev_name)
out = self._as_root([], "link", ("show", self.dev_name)) vfs = ip.link.get_vfs()
except Exception as e:
LOG.exception("Failed executing ip command")
raise exc.IpCommandError(dev_name=self.dev_name,
reason=e)
vf_to_mac_mapping = {} vf_to_mac_mapping = {}
vf_lines = self._get_vf_link_show(vf_list, out) for vf_num in vf_list:
if vf_lines: if vfs.get(vf_num):
for vf_line in vf_lines: vf_to_mac_mapping[vf_num] = vfs[vf_num]['mac']
vf_details = self._parse_vf_link_show(vf_line)
if vf_details:
vf_num = vf_details.get('vf')
vf_mac = vf_details.get("MAC")
vf_to_mac_mapping[vf_num] = vf_mac
return vf_to_mac_mapping return vf_to_mac_mapping
def get_vf_state(self, vf_index): def get_vf_state(self, vf_index):
@ -100,88 +56,48 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
@param vf_index: vf index @param vf_index: vf index
""" """
try: ip = self.device(self.dev_name)
out = self._as_root([], "link", ("show", self.dev_name)) vfs = ip.link.get_vfs()
except Exception as e: vf = vfs.get(vf_index)
LOG.exception("Failed executing ip command") if vf:
raise exc.IpCommandError(dev_name=self.dev_name, return LinkState(int(vf['link_state'])).name
reason=e)
vf_lines = self._get_vf_link_show([vf_index], out) return LinkState.disable.name
if vf_lines:
vf_details = self._parse_vf_link_show(vf_lines[0])
if vf_details:
state = vf_details.get("link-state",
LinkState.DISABLE)
if state in (LinkState.AUTO, LinkState.ENABLE):
return state
return LinkState.DISABLE
def set_vf_state(self, vf_index, state, auto=False): def set_vf_state(self, vf_index, state, auto=False):
"""sets vf state. """sets vf state.
@param vf_index: vf index @param vf_index: vf index
@param state: required state {True/False} @param state: required state {True: enable (1)
False: disable (2)}
@param auto: set link_state to auto (0)
""" """
ip = self.device(self.dev_name)
if auto: if auto:
status_str = LinkState.AUTO link_state = 0
else: else:
status_str = LinkState.ENABLE if state else \ link_state = 1 if state else 2
LinkState.DISABLE vf_config = {'vf': vf_index, 'link_state': link_state}
self._set_feature(vf_index, "state", status_str) ip.link.set_vf_feature(vf_config)
def set_vf_spoofcheck(self, vf_index, enabled): def set_vf_spoofcheck(self, vf_index, enabled):
"""sets vf spoofcheck """sets vf spoofcheck
@param vf_index: vf index @param vf_index: vf index
@param enabled: True to enable spoof checking, @param enabled: True to enable (1) spoof checking,
False to disable False to disable (0)
""" """
setting = "on" if enabled else "off" ip = self.device(self.dev_name)
self._set_feature(vf_index, "spoofchk", setting) vf_config = {'vf': vf_index, 'spoofchk': int(enabled)}
ip.link.set_vf_feature(vf_config)
def set_vf_rate(self, vf_index, rate_type, rate_value): def set_vf_rate(self, vf_index, rate_type, rate_value):
"""sets vf rate. """sets vf rate.
@param vf_index: vf index @param vf_index: vf index
@param rate_type: vf rate type ('rate', 'min_tx_rate') @param rate_type: vf rate type ('max_tx_rate', 'min_tx_rate')
@param rate_value: vf rate in Mbps @param rate_value: vf rate in Mbps
""" """
self._set_feature(vf_index, rate_type, str(rate_value)) ip = self.device(self.dev_name)
vf_config = {'vf': vf_index, 'rate': {rate_type: int(rate_value)}}
def _get_vf_link_show(self, vf_list, link_show_out): ip.link.set_vf_feature(vf_config)
"""Get link show output for VFs
get vf link show command output filtered by given vf list
@param vf_list: list of vf indexes
@param link_show_out: link show command output
@return: list of output rows regarding given vf_list
"""
vf_lines = []
for line in link_show_out.split("\n"):
line = line.strip()
if line.startswith("vf"):
details = line.split()
index = int(details[1])
if index in vf_list:
vf_lines.append(line)
if not vf_lines:
LOG.warning("Cannot find vfs %(vfs)s in device %(dev_name)s",
{'vfs': vf_list, 'dev_name': self.dev_name})
return vf_lines
def _parse_vf_link_show(self, vf_line):
"""Parses vf link show command output line.
@param vf_line: link show vf line
"""
vf_details = {}
pattern_match = self.VF_DETAILS_REG_EX.match(vf_line)
if pattern_match:
vf_details["vf"] = int(pattern_match.group("vf_index"))
vf_details["MAC"] = pattern_match.group("mac")
vf_details["link-state"] = pattern_match.group("state")
else:
LOG.warning("failed to parse vf link show line %(line)s: "
"for %(device)s",
{'line': vf_line, 'device': self.dev_name})
return vf_details

View File

@ -31,6 +31,7 @@ from oslo_log import log as logging
import oslo_messaging import oslo_messaging
from oslo_service import loopingcall from oslo_service import loopingcall
from osprofiler import profiler from osprofiler import profiler
import pyroute2
import six import six
from neutron._i18n import _ from neutron._i18n import _
@ -48,6 +49,7 @@ from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -292,10 +294,9 @@ class SriovNicSwitchAgent(object):
self.eswitch_mgr.set_device_state(device, pci_slot, self.eswitch_mgr.set_device_state(device, pci_slot,
admin_state_up, admin_state_up,
propagate_uplink_state) propagate_uplink_state)
except exc.IpCommandOperationNotSupportedError: except priv_ip_lib.InterfaceOperationNotSupported:
LOG.warning("Device %s does not support state change", LOG.warning("Device %s does not support state change", device)
device) except pyroute2.NetlinkError:
except exc.SriovNicError:
LOG.warning("Failed to set device %s state", device) LOG.warning("Failed to set device %s state", device)
return False return False
else: else:

View File

@ -86,6 +86,8 @@ def _get_scope_name(scope):
return rtnl.rt_scope.get(scope, scope) return rtnl.rt_scope.get(scope, scope)
# TODO(ralonsoh): move those exceptions out of priv_ip_lib to avoid other
# modules to import this one.
class NetworkNamespaceNotFound(RuntimeError): class NetworkNamespaceNotFound(RuntimeError):
message = _("Network namespace %(netns_name)s could not be found.") message = _("Network namespace %(netns_name)s could not be found.")
@ -124,6 +126,21 @@ class InterfaceOperationNotSupported(RuntimeError):
super(InterfaceOperationNotSupported, self).__init__(message) super(InterfaceOperationNotSupported, self).__init__(message)
class InvalidArgument(RuntimeError):
message = _("Invalid parameter/value used on interface %(device)s, "
"namespace %(namespace)s.")
def __init__(self, message=None, device=None, namespace=None):
# NOTE(slaweq): 'message' can be passed as an optional argument
# because of how privsep daemon works. If exception is raised in
# function called by privsep daemon, it will then try to reraise it
# and will call it always with passing only message from originally
# raised exception.
message = message or self.message % {'device': device,
'namespace': namespace}
super(InvalidArgument, self).__init__(message)
class IpAddressAlreadyExists(RuntimeError): class IpAddressAlreadyExists(RuntimeError):
message = _("IP address %(ip)s already configured on %(device)s.") message = _("IP address %(ip)s already configured on %(device)s.")
@ -258,6 +275,8 @@ def _translate_ip_device_exception(e, device=None, namespace=None):
if e.code == errno.EOPNOTSUPP: if e.code == errno.EOPNOTSUPP:
raise InterfaceOperationNotSupported(device=device, raise InterfaceOperationNotSupported(device=device,
namespace=namespace) namespace=namespace)
if e.code == errno.EINVAL:
raise InvalidArgument(device=device, namespace=namespace)
def get_link_id(device, namespace, raise_exception=True): def get_link_id(device, namespace, raise_exception=True):
@ -424,6 +443,11 @@ def set_link_attribute(device, namespace, **attributes):
@_sync @_sync
@privileged.default.entrypoint
def set_link_vf_feature(device, namespace, vf_config):
return _run_iproute_link("set", device, namespace=namespace, vf=vf_config)
@privileged.default.entrypoint @privileged.default.entrypoint
def get_link_attributes(device, namespace): def get_link_attributes(device, namespace):
link = _run_iproute_link("get", device, namespace)[0] link = _run_iproute_link("get", device, namespace)[0]
@ -441,6 +465,24 @@ def get_link_attributes(device, namespace):
@_sync @_sync
@privileged.default.entrypoint
def get_link_vfs(device, namespace):
link = _run_iproute_link('get', device, namespace=namespace, ext_mask=1)[0]
num_vfs = link.get_attr('IFLA_NUM_VF')
vfs = {}
if not num_vfs:
return vfs
vfinfo_list = link.get_attr('IFLA_VFINFO_LIST')
for vinfo in vfinfo_list.get_attrs('IFLA_VF_INFO'):
mac = vinfo.get_attr('IFLA_VF_MAC')
link_state = vinfo.get_attr('IFLA_VF_LINK_STATE')
vfs[mac['vf']] = {'mac': mac['mac'],
'link_state': link_state['link_state']}
return vfs
@privileged.default.entrypoint @privileged.default.entrypoint
def add_neigh_entry(ip_version, ip_address, mac_address, device, namespace, def add_neigh_entry(ip_version, ip_address, mac_address, device, namespace,
**kwargs): **kwargs):

View File

@ -77,12 +77,6 @@ class SanityTestCaseRoot(base.BaseSudoTestCase):
def test_icmpv6_header_match_runs(self): def test_icmpv6_header_match_runs(self):
checks.icmpv6_header_match_supported() checks.icmpv6_header_match_supported()
def test_vf_management_runs(self):
checks.vf_management_supported()
def test_vf_extended_management_runs(self):
checks.vf_extended_management_supported()
def test_namespace_root_read_detection_runs(self): def test_namespace_root_read_detection_runs(self):
checks.netns_read_requires_helper() checks.netns_read_requires_helper()

View File

@ -1,182 +0,0 @@
# Copyright 2014 Mellanox Technologies, Ltd
#
# 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.
import mock
from neutron.agent.linux import ip_link_support as ip_link
from neutron.tests import base
class TestIpLinkSupport(base.BaseTestCase):
IP_LINK_HELP = """Usage: ip link add [link DEV] [ name ] NAME
[ txqueuelen PACKETS ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ] [index IDX ]
[ numtxqueues QUEUE_COUNT ]
[ numrxqueues QUEUE_COUNT ]
type TYPE [ ARGS ]
ip link delete DEV type TYPE [ ARGS ]
ip link set { dev DEVICE | group DEVGROUP } [ { up | down } ]
[ arp { on | off } ]
[ dynamic { on | off } ]
[ multicast { on | off } ]
[ allmulticast { on | off } ]
[ promisc { on | off } ]
[ trailers { on | off } ]
[ txqueuelen PACKETS ]
[ name NEWNAME ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ]
[ netns PID ]
[ netns NAME ]
[ alias NAME ]
[ vf NUM [ mac LLADDR ]
[ vlan VLANID [ qos VLAN-QOS ] ]
[ rate TXRATE ] ]
[ spoofchk { on | off} ] ]
[ state { auto | enable | disable} ] ]
[ master DEVICE ]
[ nomaster ]
ip link show [ DEVICE | group GROUP ] [up]
TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
can | bridge | bond | ipoib | ip6tnl | ipip | sit |
vxlan | gre | gretap | ip6gre | ip6gretap | vti }
"""
IP_LINK_HELP_NO_STATE = """Usage: ip link add link DEV [ name ] NAME
[ txqueuelen PACKETS ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ]
type TYPE [ ARGS ]
ip link delete DEV type TYPE [ ARGS ]
ip link set DEVICE [ { up | down } ]
[ arp { on | off } ]
[ dynamic { on | off } ]
[ multicast { on | off } ]
[ allmulticast { on | off } ]
[ promisc { on | off } ]
[ trailers { on | off } ]
[ txqueuelen PACKETS ]
[ name NEWNAME ]
[ address LLADDR ]
[ broadcast LLADDR ]
[ mtu MTU ]
[ netns PID ]
[ alias NAME ]
[ vf NUM [ mac LLADDR ]
[ vlan VLANID [ qos VLAN-QOS ] ]
[ rate TXRATE ] ]
ip link show [ DEVICE ]
TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | can }
"""
IP_LINK_HELP_NO_SPOOFCHK = IP_LINK_HELP_NO_STATE
IP_LINK_HELP_NO_VF = """Usage: ip link set DEVICE { up | down |
arp { on | off } |
dynamic { on | off } |
multicast { on | off } |
allmulticast { on | off } |
promisc { on | off } |
trailers { on | off } |
txqueuelen PACKETS |
name NEWNAME |
address LLADDR | broadcast LLADDR |
mtu MTU }
ip link show [ DEVICE ]
"""
def _test_capability(self, capability, subcapability=None,
expected=True, stdout="", stderr=""):
with mock.patch("neutron.agent.linux.utils.execute") as mock_exec:
mock_exec.return_value = (stdout, stderr)
vf_section = ip_link.IpLinkSupport.get_vf_mgmt_section()
capable = ip_link.IpLinkSupport.vf_mgmt_capability_supported(
vf_section, capability, subcapability)
self.assertEqual(expected, capable)
mock_exec.assert_called_once_with(['ip', 'link', 'help'],
check_exit_code=False,
return_stderr=True,
log_fail_as_error=False)
def test_vf_mgmt(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
stderr=self.IP_LINK_HELP)
def test_execute_with_stdout(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
stdout=self.IP_LINK_HELP)
def test_vf_mgmt_no_state(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
expected=False,
stderr=self.IP_LINK_HELP_NO_STATE)
def test_vf_mgmt_no_spoofchk(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_SPOOFCHK,
expected=False,
stderr=self.IP_LINK_HELP_NO_SPOOFCHK)
def test_vf_mgmt_no_vf(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
expected=False,
stderr=self.IP_LINK_HELP_NO_VF)
def test_vf_mgmt_unknown_capability(self):
self._test_capability(
"state1",
expected=False,
stderr=self.IP_LINK_HELP)
def test_vf_mgmt_sub_capability(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_VLAN,
ip_link.IpLinkConstants.IP_LINK_SUB_CAPABILITY_QOS,
stderr=self.IP_LINK_HELP)
def test_vf_mgmt_sub_capability_mismatch(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_STATE,
ip_link.IpLinkConstants.IP_LINK_SUB_CAPABILITY_QOS,
expected=False,
stderr=self.IP_LINK_HELP)
def test_vf_mgmt_sub_capability_invalid(self):
self._test_capability(
ip_link.IpLinkConstants.IP_LINK_CAPABILITY_VLAN,
"qos1",
expected=False,
stderr=self.IP_LINK_HELP)
def test_vf_mgmt_error(self):
with mock.patch("neutron.agent.linux.utils.execute") as mock_exec:
mock_exec.side_effect = Exception()
self.assertRaises(
ip_link.UnsupportedIpLinkCommand,
ip_link.IpLinkSupport.get_vf_mgmt_section)

View File

@ -454,7 +454,7 @@ class TestLinuxBridgeManager(base.BaseTestCase):
return_value=vxlan_dev) as add_vxlan_fn,\ return_value=vxlan_dev) as add_vxlan_fn,\
mock.patch.object(vxlan_dev.link, 'set_mtu', mock.patch.object(vxlan_dev.link, 'set_mtu',
side_effect=ip_lib.InvalidArgument( side_effect=ip_lib.InvalidArgument(
parameter="MTU", value=mtu)),\ device='device_exists', namespace='ns')),\
mock.patch.object(ip_lib, 'get_device_mtu', mock.patch.object(ip_lib, 'get_device_mtu',
return_value=physical_mtu),\ return_value=physical_mtu),\
mock.patch.object(vxlan_dev.link, 'delete') as delete_dev: mock.patch.object(vxlan_dev.link, 'delete') as delete_dev:

View File

@ -21,9 +21,9 @@ from oslo_utils import uuidutils
from neutron.objects.qos import policy from neutron.objects.qos import policy
from neutron.objects.qos import rule from neutron.objects.qos import rule
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions
from neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers import ( from neutron.plugins.ml2.drivers.mech_sriov.agent.extension_drivers import (
qos_driver) qos_driver)
from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
from neutron.tests import base from neutron.tests import base
@ -115,8 +115,16 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
self.clear_max_rate_mock.assert_called_once_with(self.PCI_SLOT) self.clear_max_rate_mock.assert_called_once_with(self.PCI_SLOT)
def test__set_vf_max_rate_captures_sriov_failure(self): def test__set_vf_max_rate_captures_sriov_failure(self):
self.max_rate_mock.side_effect = exceptions.SriovNicError() msg = 'Failed to set device %s max rate'
self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC, self.PCI_SLOT) with mock.patch.object(qos_driver, 'LOG') as mock_log:
for exc in (priv_ip_lib.InterfaceOperationNotSupported(),
priv_ip_lib.InvalidArgument()):
self.max_rate_mock.side_effect = exc
self.qos_driver._set_vf_max_rate(self.ASSIGNED_MAC,
self.PCI_SLOT)
mock_log.exception.assert_called_once_with(msg,
self.ASSIGNED_MAC)
mock_log.exception.reset_mock()
def test__set_vf_max_rate_unknown_device(self): def test__set_vf_max_rate_unknown_device(self):
with mock.patch.object(self.qos_driver.eswitch_mgr, 'device_exists', with mock.patch.object(self.qos_driver.eswitch_mgr, 'device_exists',

View File

@ -18,7 +18,6 @@ import os
import mock import mock
from neutron.agent.linux import ip_link_support
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
@ -72,8 +71,8 @@ class TestESwitchManagerApi(base.BaseTestCase):
PCI_SLOT = '0000:06:00.1' PCI_SLOT = '0000:06:00.1'
WRONG_MAC = '00:00:00:00:00:67' WRONG_MAC = '00:00:00:00:00:67'
WRONG_PCI = "0000:06:00.6" WRONG_PCI = "0000:06:00.6"
MAX_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE MAX_RATE = esm.IP_LINK_CAPABILITY_RATE
MIN_RATE = ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE MIN_RATE = esm.IP_LINK_CAPABILITY_MIN_TX_RATE
def setUp(self): def setUp(self):
super(TestESwitchManagerApi, self).setUp() super(TestESwitchManagerApi, self).setUp()
@ -526,55 +525,48 @@ class TestEmbSwitch(base.BaseTestCase):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 2000)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2000)
pci_lib_mock.assert_called_with( pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2) 0, esm.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_ok2(self): def test_set_device_max_rate_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 99)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 99)
pci_lib_mock.assert_called_with( pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1) 0, esm.IP_LINK_CAPABILITY_RATE, 1)
def test_set_device_max_rate_rounded_ok(self): def test_set_device_max_rate_rounded_ok(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 2001)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2001) pci_lib_mock.assert_called_with(0, esm.IP_LINK_CAPABILITY_RATE, 2)
pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_rounded_ok2(self): def test_set_device_max_rate_rounded_ok2(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 2499)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2499)
pci_lib_mock.assert_called_with( pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2) 0, esm.IP_LINK_CAPABILITY_RATE, 2)
def test_set_device_max_rate_rounded_ok3(self): def test_set_device_max_rate_rounded_ok3(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 2500)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 2500)
pci_lib_mock.assert_called_with( pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 3) 0, esm.IP_LINK_CAPABILITY_RATE, 3)
def test_set_device_max_rate_disable(self): def test_set_device_max_rate_disable(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock: "PciDeviceIPWrapper.set_vf_rate") as pci_lib_mock:
self.emb_switch.set_device_rate( self.emb_switch.set_device_rate(
self.PCI_SLOT, self.PCI_SLOT, esm.IP_LINK_CAPABILITY_RATE, 0)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0)
pci_lib_mock.assert_called_with( pci_lib_mock.assert_called_with(
0, ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 0) 0, esm.IP_LINK_CAPABILITY_RATE, 0)
def test_set_device_max_rate_fail(self): def test_set_device_max_rate_fail(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
@ -583,7 +575,7 @@ class TestEmbSwitch(base.BaseTestCase):
exc.InvalidPciSlotError, exc.InvalidPciSlotError,
self.emb_switch.set_device_rate, self.emb_switch.set_device_rate,
self.WRONG_PCI_SLOT, self.WRONG_PCI_SLOT,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE, 1000) esm.IP_LINK_CAPABILITY_RATE, 1000)
def test_get_pci_device(self): def test_get_pci_device(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."

View File

@ -16,28 +16,18 @@
import mock import mock
from neutron.agent.linux import ip_link_support from neutron.agent.linux import ip_lib
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
from neutron.tests import base from neutron.tests import base
class TestPciLib(base.BaseTestCase): class TestPciLib(base.BaseTestCase):
DEV_NAME = "p7p1" DEV_NAME = "p7p1"
VF_INDEX = 1 VF_INDEX = 1
VF_INDEX_DISABLE = 0 VFS_LIST = {0: {'mac': 'fa:16:3e:b4:81:ac', 'link_state': 2},
PF_LINK_SHOW = ('122: p7p1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop' 1: {'mac': '00:00:00:00:00:11', 'link_state': 1},
' state DOWN mode DEFAULT group default qlen 1000') 2: {'mac': 'fa:16:3e:68:4e:79', 'link_state': 0}}
PF_MAC = ' link/ether f4:52:14:2a:3e:c0 brd ff:ff:ff:ff:ff:ff'
VF_0_LINK_SHOW = (' vf 0 MAC fa:16:3e:b4:81:ac, vlan 4095, spoof'
' checking off, link-state disable')
VF_1_LINK_SHOW = (' vf 1 MAC 00:00:00:00:00:11, vlan 4095, spoof'
' checking off, link-state enable')
VF_2_LINK_SHOW = (' vf 2 MAC fa:16:3e:68:4e:79, vlan 4095, spoof'
' checking off, link-state enable')
VF_LINK_SHOW = '\n'.join((PF_LINK_SHOW, PF_MAC, VF_0_LINK_SHOW,
VF_1_LINK_SHOW, VF_2_LINK_SHOW))
MAC_MAPPING = { MAC_MAPPING = {
0: "fa:16:3e:b4:81:ac", 0: "fa:16:3e:b4:81:ac",
@ -45,121 +35,69 @@ class TestPciLib(base.BaseTestCase):
2: "fa:16:3e:68:4e:79", 2: "fa:16:3e:68:4e:79",
} }
STATE_MAPPING = { # VF index: state (string), according to VFS_LIST
0: pci_lib.LinkState.disable.name,
1: pci_lib.LinkState.enable.name,
2: pci_lib.LinkState.auto.name,
}
def setUp(self): def setUp(self):
super(TestPciLib, self).setUp() super(TestPciLib, self).setUp()
self.pci_wrapper = pci_lib.PciDeviceIPWrapper(self.DEV_NAME) self.pci_wrapper = pci_lib.PciDeviceIPWrapper(self.DEV_NAME)
self.mock_ip_device = mock.Mock()
self.mock_ip_device.link.get_vfs.return_value = self.VFS_LIST
mock.patch.object(ip_lib, 'IPDevice',
return_value=self.mock_ip_device).start()
def test_get_assigned_macs(self): def test_get_assigned_macs(self):
with mock.patch.object(self.pci_wrapper, for idx in range(len(self.VFS_LIST)):
"_as_root") as mock_as_root: result = self.pci_wrapper.get_assigned_macs([idx])
mock_as_root.return_value = self.VF_LINK_SHOW self.assertEqual({idx: self.MAC_MAPPING[idx]}, result)
result = self.pci_wrapper.get_assigned_macs([self.VF_INDEX])
self.assertEqual(
{self.VF_INDEX: self.MAC_MAPPING[self.VF_INDEX]}, result)
def test_get_assigned_macs_fail(self): def test_get_assigned_macs_not_present(self):
with mock.patch.object(self.pci_wrapper, result = self.pci_wrapper.get_assigned_macs([1000])
"_as_root") as mock_as_root: self.assertEqual({}, result)
mock_as_root.side_effect = Exception()
self.assertRaises(exc.IpCommandError,
self.pci_wrapper.get_assigned_macs,
[self.VF_INDEX])
def test_get_vf_state_enable(self): def test_get_vf_state(self):
with mock.patch.object(self.pci_wrapper, for idx in range(len(self.VFS_LIST)):
"_as_root") as mock_as_root: result = self.pci_wrapper.get_vf_state(idx)
mock_as_root.return_value = self.VF_LINK_SHOW self.assertEqual(self.STATE_MAPPING[idx], result)
result = self.pci_wrapper.get_vf_state(self.VF_INDEX)
self.assertEqual('enable', result)
def test_get_vf_state_disable(self): def test_get_vf_state_not_present(self):
with mock.patch.object(self.pci_wrapper, result = self.pci_wrapper.get_vf_state(1000)
"_as_root") as mock_as_root: self.assertEqual(pci_lib.LinkState.disable.name, result)
mock_as_root.return_value = self.VF_LINK_SHOW
result = self.pci_wrapper.get_vf_state(self.VF_INDEX_DISABLE)
self.assertEqual('disable', result)
def test_get_vf_state_fail(self):
with mock.patch.object(self.pci_wrapper,
"_as_root") as mock_as_root:
mock_as_root.side_effect = Exception()
self.assertRaises(exc.IpCommandError,
self.pci_wrapper.get_vf_state,
self.VF_INDEX)
def test_set_vf_state(self): def test_set_vf_state(self):
with mock.patch.object(self.pci_wrapper, "_as_root"): self.pci_wrapper.set_vf_state(self.VF_INDEX, True)
result = self.pci_wrapper.set_vf_state(self.VF_INDEX, vf = {'vf': self.VF_INDEX, 'link_state': 1}
True) self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
self.assertIsNone(result)
def test_set_vf_state_fail(self): self.mock_ip_device.link.set_vf_feature.reset_mock()
with mock.patch.object(self.pci_wrapper, self.pci_wrapper.set_vf_state(self.VF_INDEX, False)
"_as_root") as mock_as_root: vf = {'vf': self.VF_INDEX, 'link_state': 2}
mock_as_root.side_effect = Exception() self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
self.assertRaises(exc.IpCommandError,
self.pci_wrapper.set_vf_state, self.mock_ip_device.link.set_vf_feature.reset_mock()
self.VF_INDEX, self.pci_wrapper.set_vf_state(self.VF_INDEX, False, auto=True)
True) vf = {'vf': self.VF_INDEX, 'link_state': 0}
self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
def test_set_vf_spoofcheck(self): def test_set_vf_spoofcheck(self):
with mock.patch.object(self.pci_wrapper, "_as_root"): self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, True)
result = self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, vf = {'vf': self.VF_INDEX, 'spoofchk': 1}
True) self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
self.assertIsNone(result)
def test_set_vf_spoofcheck_fail(self): self.mock_ip_device.link.set_vf_feature.reset_mock()
with mock.patch.object(self.pci_wrapper, self.pci_wrapper.set_vf_spoofcheck(self.VF_INDEX, False)
"_as_root") as mock_as_root: vf = {'vf': self.VF_INDEX, 'spoofchk': 0}
mock_as_root.side_effect = Exception() self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
self.assertRaises(exc.IpCommandError,
self.pci_wrapper.set_vf_spoofcheck,
self.VF_INDEX,
True)
def _set_vf_rate(self, rate, passed=True): def test_set_vf_rate(self):
if passed: self.pci_wrapper.set_vf_rate(self.VF_INDEX, 'max_tx_rate', 20)
with mock.patch.object(self.pci_wrapper, "_as_root") \ vf = {'vf': self.VF_INDEX, 'rate': {'max_tx_rate': 20}}
as mock_as_root: self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
result = self.pci_wrapper.set_vf_rate(
self.VF_INDEX,
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE,
1000)
self.assertIsNone(result)
mock_as_root.assert_called_once_with(
[], "link", ("set", self.DEV_NAME, "vf",
str(self.VF_INDEX), "rate", '1000'))
else:
with mock.patch.object(self.pci_wrapper, "_as_root",
side_effect=Exception()):
self.assertRaises(exc.IpCommandError,
self.pci_wrapper.set_vf_rate,
self.VF_INDEX,
rate,
1000)
def test_set_vf_rate_max_rate(self): self.mock_ip_device.link.set_vf_feature.reset_mock()
self._set_vf_rate( self.pci_wrapper.set_vf_rate(self.VF_INDEX, 'min_tx_rate', 10)
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_RATE) vf = {'vf': self.VF_INDEX, 'rate': {'min_tx_rate': 10}}
self.mock_ip_device.link.set_vf_feature.assert_called_once_with(vf)
def test_set_vf_rate_max_rate_fail(self):
self._set_vf_rate('rate', passed=False)
def test_set_vf_rate_min_tx_rate(self):
self._set_vf_rate(
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE)
def test_set_vf_rate_min_tx_rate_fail(self):
self._set_vf_rate(
ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_MIN_TX_RATE,
passed=False)
def test_set_vf_state_not_supported(self):
with mock.patch.object(self.pci_wrapper,
"_as_root") as mock_as_root:
mock_as_root.side_effect = Exception(
pci_lib.PciDeviceIPWrapper.IP_LINK_OP_NOT_SUPPORTED)
self.assertRaises(exc.IpCommandOperationNotSupportedError,
self.pci_wrapper.set_vf_state,
self.VF_INDEX,
state=True)

View File

@ -19,12 +19,13 @@ from neutron_lib.api.definitions import portbindings
from neutron_lib import constants from neutron_lib import constants
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils from oslo_utils import uuidutils
import pyroute2
from neutron.agent.l2 import l2_agent_extensions_manager as l2_ext_manager from neutron.agent.l2 import l2_agent_extensions_manager as l2_ext_manager
from neutron.agent import rpc as agent_rpc from neutron.agent import rpc as agent_rpc
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions
from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent
from neutron.privileged.agent.linux import ip_lib as priv_ip_lib
from neutron.tests import base from neutron.tests import base
DEVICE_MAC = '11:22:33:44:55:66' DEVICE_MAC = '11:22:33:44:55:66'
@ -423,8 +424,7 @@ class TestSriovAgent(base.BaseTestCase):
agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True agent.eswitch_mgr.device_exists.return_value = True
agent.eswitch_mgr.set_device_state.side_effect = ( agent.eswitch_mgr.set_device_state.side_effect = (
exceptions.IpCommandOperationNotSupportedError( priv_ip_lib.InterfaceOperationNotSupported())
dev_name='aa:bb:cc:dd:ee:ff'))
self.assertTrue(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', self.assertTrue(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0',
admin_state_up=True)) admin_state_up=True))
@ -435,7 +435,7 @@ class TestSriovAgent(base.BaseTestCase):
agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True agent.eswitch_mgr.device_exists.return_value = True
agent.eswitch_mgr.set_device_state.side_effect = ( agent.eswitch_mgr.set_device_state.side_effect = (
exceptions.SriovNicError()) pyroute2.NetlinkError(22))
self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0',
admin_state_up=True)) admin_state_up=True))

View File

@ -213,6 +213,39 @@ class IpLibTestCase(base.BaseTestCase):
except OSError as e: except OSError as e:
self.assertEqual(errno.EINVAL, e.errno) self.assertEqual(errno.EINVAL, e.errno)
def _clean(self, client_mode):
priv_lib.privileged.default.client_mode = client_mode
def test_get_link_vfs(self):
# NOTE(ralonsoh): there should be a functional test checking this
# method, but this is not possible due to the lack of SR-IOV capable
# NICs in the CI servers.
vf_info = []
for idx in range(3):
vf_info.append(pyroute2.netlink.nlmsg_base())
mac_info = {'mac': 'mac_%s' % idx, 'vf': idx}
link_state = {'link_state': idx} # see SR-IOV pci_lib.LinkState
vf_info[idx].setvalue(
{'attrs': [('IFLA_VF_MAC', mac_info),
('IFLA_VF_LINK_STATE', link_state)]})
vfinfo_list = pyroute2.netlink.nlmsg_base()
vfinfo_list.setvalue({'attrs': [('IFLA_VF_INFO', vf_info[0]),
('IFLA_VF_INFO', vf_info[1]),
('IFLA_VF_INFO', vf_info[2])]})
value = pyroute2.netlink.nlmsg_base()
value.setvalue({'attrs': [('IFLA_NUM_VF', 3),
('IFLA_VFINFO_LIST', vfinfo_list)]})
client_mode = priv_lib.privileged.default.client_mode
priv_lib.privileged.default.client_mode = False
self.addCleanup(self._clean, client_mode)
with mock.patch.object(priv_lib, '_run_iproute_link') as mock_iplink:
mock_iplink.return_value = [value]
result = priv_lib.get_link_vfs('device', 'namespace')
self.assertEqual({0: {'mac': 'mac_0', 'link_state': 0},
1: {'mac': 'mac_1', 'link_state': 1},
2: {'mac': 'mac_2', 'link_state': 2}},
result)
class MakeSerializableTestCase(base.BaseTestCase): class MakeSerializableTestCase(base.BaseTestCase):