--- /dev/null
+# 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.agent.linux import utils
+from neutron.common import exceptions as n_exc
+from neutron.openstack.common.gettextutils import _LE
+from neutron.openstack.common import log as logging
+
+
+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_SPOOFCHK = "spoofchk"
+ IP_LINK_SUB_CAPABILITY_QOS = "qos"
+
+
+class IpLinkSupport(object):
+ VF_BLOCK_REGEX = "\[ vf NUM(?P<vf_block>.*) \] \]"
+
+ CAPABILITY_REGEX = "\[ %s (.*)"
+ SUB_CAPABILITY_REGEX = "\[ %(cap)s (.*) \[ %(subcap)s (.*)"
+
+ @classmethod
+ def get_vf_mgmt_section(cls, root_helper=None):
+ """Parses ip link help output, and gets vf block
+
+ :param root_helper: root permission helper
+
+ """
+ output = cls._get_ip_link_output(root_helper)
+ 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, root_helper):
+ """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, root_helper,
+ check_exit_code=False,
+ return_stderr=True,
+ log_fail_as_error=False)
+ except Exception as e:
+ LOG.exception(_LE("Failed executing ip command"))
+ raise UnsupportedIpLinkCommand(reason=e)
+ return _stdout or _stderr
import netaddr
+from neutron.agent.linux import ip_link_support
from neutron.agent.linux import ovs_lib
from neutron.agent.linux import utils as agent_utils
from neutron.common import utils
dl_vlan=42,
nw_dst='%s' % ip,
actions=actions)
+
+
+def vf_management_supported(root_helper):
+ try:
+ vf_section = ip_link_support.IpLinkSupport.get_vf_mgmt_section(
+ root_helper)
+ if not ip_link_support.IpLinkSupport.vf_mgmt_capability_supported(
+ vf_section,
+ ip_link_support.IpLinkConstants.IP_LINK_CAPABILITY_STATE):
+ LOG.debug("ip link command does not support vf capability")
+ return False
+ except ip_link_support.UnsupportedIpLinkCommand:
+ LOG.exception(_("Unexpected exception while checking supported "
+ "ip link command"))
+ return False
+ return True
LOG = logging.getLogger(__name__)
cfg.CONF.import_group('AGENT', 'neutron.plugins.openvswitch.common.config')
cfg.CONF.import_group('OVS', 'neutron.plugins.openvswitch.common.config')
+cfg.CONF.import_group('ml2_sriov',
+ 'neutron.plugins.ml2.drivers.mech_sriov.mech_driver')
class BoolOptCallback(cfg.BoolOpt):
return result
+def check_vf_management():
+ result = checks.vf_management_supported(
+ root_helper=cfg.CONF.AGENT.root_helper)
+ if not result:
+ LOG.error(_LE('Check for VF management support failed. '
+ 'Please ensure that the version of ip link '
+ 'being used has VF support.'))
+ return result
+
+
# Define CLI opts to test specific features, with a calback for the test
OPTS = [
BoolOptCallback('ovs_vxlan', check_ovs_vxlan, default=False,
help=_('Check for nova notification support')),
BoolOptCallback('arp_responder', check_arp_responder, default=False,
help=_('Check for ARP responder support')),
+ BoolOptCallback('vf_management', check_vf_management, default=False,
+ help=_('Check for VF management support')),
+
]
cfg.CONF.set_override('nova_notify', True)
if cfg.CONF.AGENT.arp_responder:
cfg.CONF.set_override('arp_responder', True)
+ if cfg.CONF.ml2_sriov.agent_required:
+ cfg.CONF.set_override('vf_management', True)
def all_tests_passed():
def test_arp_responder_runs(self):
checks.arp_responder_supported(self.root_helper)
+
+ def test_vf_management_runs(self):
+ checks.vf_management_supported(self.root_helper)
--- /dev/null
+# 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_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'],
+ None,
+ 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_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)