Recent changes in some versions of iproute2 CLI output (v4.18), have invalidated the regular expression used to parse the "ip link" output. To solve this problem and avoid future ones, pyroute2 is used to retrieve the virtual functions information and set the VF attributes (spoofcheck, min_tx_rate, max_tx_rate and link_state). pyroute2 extended the "ip link" support to retrieve this information, adding "ext_mask=1" in the get command. If no virtual functions are present in this particular network interface, the added method, "get_link_vfs", will return an empty list. The set commands can return a "InterfaceOperationNotSupported" in case the operation is not supported. For min_tx_rate, if the driver does not support to set a minimum bandwidth, an "InvalidArgument" (from a pyroute2.NetlinkError(22)) exception will be raised. Change-Id: I680da4f64bd114f1caecaaeedbf8a4b1915a0849 Closes-Bug: #1878042changes/18/726918/11
parent
08e9ec1b56
commit
c5d8fd6329
@ -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
|
@ -1,181 +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.
|
||||
|
||||
from unittest 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)
|