diff --git a/neutron/agent/linux/ovs_lib.py b/neutron/agent/linux/ovs_lib.py index d5bf42e0b5b..508f7df5a82 100644 --- a/neutron/agent/linux/ovs_lib.py +++ b/neutron/agent/linux/ovs_lib.py @@ -13,10 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import distutils.version as dist_version -import os -import re - from oslo.config import cfg from neutron.agent.linux import ip_lib @@ -176,7 +172,14 @@ class OVSBridge(BaseOVS): self.run_ofctl("del-flows", []) def get_port_ofport(self, port_name): - return self.db_get_val("Interface", port_name, "ofport") + ofport = self.db_get_val("Interface", port_name, "ofport") + # This can return a non-integer string, like '[]' so ensure a + # common failure case + try: + int(ofport) + return ofport + except ValueError: + return constants.INVALID_OFPORT def get_datapath_id(self): return self.db_get_val('Bridge', @@ -248,7 +251,13 @@ class OVSBridge(BaseOVS): "options:in_key=flow", "options:out_key=flow"]) self.run_vsctl(vsctl_command) - return self.get_port_ofport(port_name) + ofport = self.get_port_ofport(port_name) + if (tunnel_type == p_const.TYPE_VXLAN and + ofport == constants.INVALID_OFPORT): + LOG.error(_('Unable to create VXLAN tunnel port. Please ensure ' + 'that an openvswitch version that supports VXLAN is ' + 'installed.')) + return ofport def add_patch_port(self, local_name, remote_name): self.run_vsctl(["add-port", self.br_name, local_name, @@ -451,6 +460,13 @@ class OVSBridge(BaseOVS): msg = _('Unable to determine mac address for %s') % self.br_name raise Exception(msg) + def __enter__(self): + self.create() + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.destroy() + def get_bridge_for_iface(root_helper, iface): args = ["ovs-vsctl", "--timeout=%d" % cfg.CONF.ovs_vsctl_timeout, @@ -472,35 +488,6 @@ def get_bridges(root_helper): LOG.exception(_("Unable to retrieve bridges. Exception: %s"), e) -def get_installed_ovs_usr_version(root_helper): - args = ["ovs-vsctl", "--version"] - try: - cmd = utils.execute(args, root_helper=root_helper) - ver = re.findall("\d+\.\d+", cmd)[0] - return ver - except Exception: - LOG.exception(_("Unable to retrieve OVS userspace version.")) - - -def get_installed_ovs_klm_version(): - args = ["modinfo", "openvswitch"] - try: - cmd = utils.execute(args) - for line in cmd.split('\n'): - if 'version: ' in line and not 'srcversion' in line: - ver = re.findall("\d+\.\d+", line) - return ver[0] - except Exception: - LOG.exception(_("Unable to retrieve OVS kernel module version.")) - - -def get_installed_kernel_version(): - try: - return os.uname()[2].split('-', 1)[0] - except IndexError: - LOG.exception(_("Unable to retrieve installed Linux kernel version.")) - - def get_bridge_external_bridge_id(root_helper, bridge): args = ["ovs-vsctl", "--timeout=2", "br-get-external-id", bridge, "bridge-id"] @@ -511,57 +498,6 @@ def get_bridge_external_bridge_id(root_helper, bridge): return None -def _compare_installed_and_required_version( - installed_kernel_version, installed_version, required_version, - check_type, version_type): - if installed_kernel_version: - if dist_version.StrictVersion( - installed_kernel_version) >= dist_version.StrictVersion( - constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN): - return - if installed_version: - if dist_version.StrictVersion( - installed_version) < dist_version.StrictVersion( - required_version): - msg = (_('Failed %(ctype)s version check for Open ' - 'vSwitch with %(vtype)s support. To use ' - '%(vtype)s tunnels with OVS, please ensure ' - 'the OVS version is %(required)s or newer!') % - {'ctype': check_type, 'vtype': version_type, - 'required': required_version}) - raise SystemError(msg) - else: - msg = (_('Unable to determine %(ctype)s version for Open ' - 'vSwitch with %(vtype)s support. To use ' - '%(vtype)s tunnels with OVS, please ensure ' - 'that the version is %(required)s or newer!') % - {'ctype': check_type, 'vtype': version_type, - 'required': required_version}) - raise SystemError(msg) - - -def check_ovs_vxlan_version(root_helper): - min_required_version = constants.MINIMUM_OVS_VXLAN_VERSION - installed_klm_version = get_installed_ovs_klm_version() - installed_kernel_version = get_installed_kernel_version() - installed_usr_version = get_installed_ovs_usr_version(root_helper) - LOG.debug(_("Checking OVS version for VXLAN support " - "installed klm version is %(klm)s, installed Linux version is " - "%(kernel)s, installed user version is %(usr)s ") % - {'klm': installed_klm_version, - 'kernel': installed_kernel_version, - 'usr': installed_usr_version}) - # First check the userspace version - _compare_installed_and_required_version(None, installed_usr_version, - min_required_version, - 'userspace', 'VXLAN') - # Now check the kernel version - _compare_installed_and_required_version(installed_kernel_version, - installed_klm_version, - min_required_version, - 'kernel', 'VXLAN') - - def _build_flow_expr_str(flow_dict, cmd): flow_expr_arr = [] actions = None diff --git a/neutron/cmd/sanity/__init__.py b/neutron/cmd/sanity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/cmd/sanity/checks.py b/neutron/cmd/sanity/checks.py new file mode 100644 index 00000000000..0dc05f7d39e --- /dev/null +++ b/neutron/cmd/sanity/checks.py @@ -0,0 +1,28 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2014 OpenStack Foundation. +# All Rights Reserved. +# +# 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 neutron.agent.linux import ovs_lib +from neutron.common import utils +from neutron.plugins.common import constants as const +from neutron.plugins.openvswitch.common import constants as ovs_const + + +def vxlan_supported(root_helper, from_ip='192.0.2.1', to_ip='192.0.2.2'): + name = "vxlantest-" + utils.get_random_string(6) + with ovs_lib.OVSBridge(name, root_helper) as br: + port = br.add_tunnel_port(from_ip, to_ip, const.TYPE_VXLAN) + return port != ovs_const.INVALID_OFPORT diff --git a/neutron/cmd/sanity_check.py b/neutron/cmd/sanity_check.py new file mode 100644 index 00000000000..715d994524e --- /dev/null +++ b/neutron/cmd/sanity_check.py @@ -0,0 +1,79 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2014 OpenStack Foundation. +# All Rights Reserved. +# +# 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 sys + +from neutron.cmd.sanity import checks +from neutron.common import config +from neutron.openstack.common import log as logging +from oslo.config import cfg + + +LOG = logging.getLogger(__name__) +cfg.CONF.import_group('AGENT', 'neutron.plugins.openvswitch.common.config') + + +class BoolOptCallback(cfg.BoolOpt): + def __init__(self, name, callback, **kwargs): + self.callback = callback + super(BoolOptCallback, self).__init__(name, **kwargs) + + +def check_ovs_vxlan(): + result = checks.vxlan_supported(root_helper=cfg.CONF.AGENT.root_helper) + if not result: + LOG.error(_('Check for Open vSwitch VXLAN support failed. ' + 'Please ensure that the version of openvswitch ' + 'being used has VXLAN 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 vxlan support')), +] + + +def enable_tests_from_config(): + """If a test can depend on configuration, use this function to set the + appropriate CLI option to enable that test. It will then be possible to + run all necessary tests, just by passing in the appropriate configs. + """ + + if 'vxlan' in cfg.CONF.AGENT.tunnel_types: + cfg.CONF.set_override('ovs_vxlan', True) + + +def all_tests_passed(): + res = True + for opt in OPTS: + if cfg.CONF.get(opt.name): + res &= opt.callback() + return res + + +def main(): + cfg.CONF.register_cli_opts(OPTS) + cfg.CONF.set_override('use_stderr', True) + config.setup_logging(cfg.CONF) + config.parse(sys.argv[1:], default_config_files=[]) + + if cfg.CONF.config_file: + enable_tests_from_config() + + return 0 if all_tests_passed() else 1 diff --git a/neutron/common/config.py b/neutron/common/config.py index 06610dcdde8..19a388b0df2 100644 --- a/neutron/common/config.py +++ b/neutron/common/config.py @@ -133,9 +133,10 @@ db_options.set_defaults(sql_connection=_SQL_CONNECTION_DEFAULT, max_overflow=20, pool_timeout=10) -def parse(args): +def parse(args, **kwargs): cfg.CONF(args=args, project='neutron', - version='%%prog %s' % version.version_info.release_string()) + version='%%prog %s' % version.version_info.release_string(), + **kwargs) # Validate that the base_mac is of the correct format msg = attributes._validate_regex(cfg.CONF.base_mac, diff --git a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py index c531bcaa07c..32b1ad7f45f 100644 --- a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py +++ b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py @@ -267,7 +267,6 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): self.local_ip = local_ip self.tunnel_count = 0 self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port - self._check_ovs_version() if self.enable_tunneling: self.setup_tunnel_br(tun_br) # Collect additional bridges to monitor @@ -280,14 +279,6 @@ class OFANeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin): # Initialize iteration counter self.iter_num = 0 - def _check_ovs_version(self): - if p_const.TYPE_VXLAN in self.tunnel_types: - try: - ovs_lib.check_ovs_vxlan_version(self.root_helper) - except SystemError: - LOG.exception(_("Agent terminated")) - raise SystemExit(1) - def _report_state(self): # How many devices are likely used by a VM self.agent_state.get('configurations')['devices'] = ( diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index c93e872c3e5..9c2f146effc 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -223,7 +223,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.local_ip = local_ip self.tunnel_count = 0 self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port - self._check_ovs_version() self.tun_br = None if self.enable_tunneling: self.setup_tunnel_br(tun_br) @@ -237,14 +236,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # Initialize iteration counter self.iter_num = 0 - def _check_ovs_version(self): - if p_const.TYPE_VXLAN in self.tunnel_types: - try: - ovs_lib.check_ovs_vxlan_version(self.root_helper) - except SystemError: - LOG.exception(_("Agent terminated")) - raise SystemExit(1) - def _check_arp_responder_support(self): '''Check if OVS supports to modify ARP headers. diff --git a/neutron/plugins/openvswitch/common/constants.py b/neutron/plugins/openvswitch/common/constants.py index 0d3877515e3..57f0863258d 100644 --- a/neutron/plugins/openvswitch/common/constants.py +++ b/neutron/plugins/openvswitch/common/constants.py @@ -30,12 +30,6 @@ VXLAN_UDP_PORT = 4789 VETH_INTEGRATION_PREFIX = 'int-' VETH_PHYSICAL_PREFIX = 'phy-' -# The minimum version of OVS which supports VXLAN tunneling -MINIMUM_OVS_VXLAN_VERSION = "1.10" - -# The first version of the Linux kernel with converged VXLAN code for OVS -MINIMUM_LINUX_KERNEL_OVS_VXLAN = "3.13.0" - # The different types of tunnels TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN] @@ -55,3 +49,6 @@ TUN_TABLE = {p_const.TYPE_GRE: GRE_TUN_TO_LV, # The default respawn interval for the ovsdb monitor DEFAULT_OVSDBMON_RESPAWN = 30 + +# Special return value for an invalid OVS ofport +INVALID_OFPORT = '-1' diff --git a/neutron/tests/functional/agent/linux/test_ovs_vxlan.py b/neutron/tests/functional/agent/linux/test_ovs_vxlan.py deleted file mode 100644 index fbff6a5a2a1..00000000000 --- a/neutron/tests/functional/agent/linux/test_ovs_vxlan.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright 2014 Cisco Systems, 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. - -from neutron.agent.linux import ovs_lib -from neutron.plugins.common import constants as p_const -from neutron.tests.functional.agent.linux import base as base_agent - - -PORT_PREFIX = 'testp-' -INVALID_OFPORT_ID = '-1' - - -class TestOVSAgentVXLAN(base_agent.BaseOVSLinuxTestCase): - - def setUp(self): - super(TestOVSAgentVXLAN, self).setUp() - - self._check_test_requirements() - - def _check_test_requirements(self): - self.check_sudo_enabled() - self.check_command(['which', 'ovs-vsctl'], - 'Exit code: 1', 'ovs-vsctl is not installed') - self.check_command(['sudo', '-n', 'ovs-vsctl', 'show'], - 'Exit code: 1', - 'password-less sudo not granted for ovs-vsctl') - - def test_ovs_lib_vxlan_version_check(self): - """Verify VXLAN versions match - - This function compares the return values of functionally checking if - VXLAN is supported with the ovs_lib programmatic check. It will fail - if the two do not align. - """ - expected = self.is_vxlan_supported() - actual = self.is_ovs_lib_vxlan_supported() - self.assertEqual(actual, expected) - - def is_ovs_lib_vxlan_supported(self): - try: - ovs_lib.check_ovs_vxlan_version(self.root_helper) - except SystemError: - return False - else: - return True - - def is_vxlan_supported(self): - bridge = self.create_ovs_bridge() - vxlan_port = self.create_resource( - PORT_PREFIX, - bridge.add_tunnel_port, - "10.10.10.10", - "10.10.10.20", - p_const.TYPE_VXLAN) - - return vxlan_port != INVALID_OFPORT_ID diff --git a/neutron/tests/functional/sanity/__init__.py b/neutron/tests/functional/sanity/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/tests/functional/sanity/test_ovs_sanity.py b/neutron/tests/functional/sanity/test_ovs_sanity.py new file mode 100644 index 00000000000..8195a35d9b4 --- /dev/null +++ b/neutron/tests/functional/sanity/test_ovs_sanity.py @@ -0,0 +1,39 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2014 OpenStack Foundation. +# All Rights Reserved. +# +# 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 os + +from neutron.cmd.sanity import checks +from neutron.tests import base + + +class OVSSanityTestCase(base.BaseTestCase): + def setUp(self): + super(OVSSanityTestCase, self).setUp() + + self.root_helper = 'sudo' + + def check_sudo_enabled(self): + if os.environ.get('OS_SUDO_TESTING') not in base.TRUE_STRING: + self.skipTest('testing with sudo is not enabled') + + def test_ovs_vxlan_support_runs(self): + """This test just ensures that the test in neutron-sanity-check + can run through without error, without mocking anything out + """ + self.check_sudo_enabled() + checks.vxlan_supported(self.root_helper) diff --git a/neutron/tests/unit/agent/linux/test_ovs_lib.py b/neutron/tests/unit/agent/linux/test_ovs_lib.py index a1daa22a01b..1aff6c3e429 100644 --- a/neutron/tests/unit/agent/linux/test_ovs_lib.py +++ b/neutron/tests/unit/agent/linux/test_ovs_lib.py @@ -22,7 +22,7 @@ from neutron.agent.linux import utils from neutron.common import exceptions from neutron.openstack.common import jsonutils from neutron.openstack.common import uuidutils -from neutron.plugins.openvswitch.common import constants +from neutron.plugins.openvswitch.common import constants as const from neutron.tests import base from neutron.tests import tools @@ -321,6 +321,15 @@ class OVS_Lib_Test(base.BaseTestCase): ["ovs-vsctl", self.TO, "get", "Interface", pname, "ofport"], root_helper=self.root_helper) + def test_get_port_ofport_non_int(self): + pname = "tap99" + ofport = "[]" + self.execute.return_value = ofport + self.assertEqual(self.br.get_port_ofport(pname), const.INVALID_OFPORT) + self.execute.assert_called_once_with( + ["ovs-vsctl", self.TO, "get", "Interface", pname, "ofport"], + root_helper=self.root_helper) + def test_get_datapath_id(self): datapath_id = '"0000b67f4fbcc149"' self.execute.return_value = datapath_id @@ -854,68 +863,6 @@ class OVS_Lib_Test(base.BaseTestCase): self.assertIsNone(self._test_get_vif_port_by_id('tap99id', data, "br-ext")) - def _check_ovs_vxlan_version(self, installed_usr_version, - installed_klm_version, - installed_kernel_version, - expecting_ok): - with mock.patch( - 'neutron.agent.linux.ovs_lib.get_installed_ovs_klm_version' - ) as klm_cmd: - with mock.patch( - 'neutron.agent.linux.ovs_lib.get_installed_ovs_usr_version' - ) as usr_cmd: - with mock.patch( - 'neutron.agent.linux.ovs_lib.get_installed_kernel_version' - ) as kernel_cmd: - try: - klm_cmd.return_value = installed_klm_version - usr_cmd.return_value = installed_usr_version - kernel_cmd.return_value = installed_kernel_version - ovs_lib.check_ovs_vxlan_version(root_helper='sudo') - version_ok = True - except SystemError: - version_ok = False - self.assertEqual(version_ok, expecting_ok) - - def test_check_minimum_version(self): - min_vxlan_ver = constants.MINIMUM_OVS_VXLAN_VERSION - min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN - self._check_ovs_vxlan_version(min_vxlan_ver, min_vxlan_ver, - min_kernel_ver, expecting_ok=True) - - def test_check_future_version(self): - install_ver = str(float(constants.MINIMUM_OVS_VXLAN_VERSION) + 0.01) - min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN - self._check_ovs_vxlan_version(install_ver, install_ver, - min_kernel_ver, expecting_ok=True) - - def test_check_fail_version(self): - install_ver = str(float(constants.MINIMUM_OVS_VXLAN_VERSION) - 0.01) - min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN - self._check_ovs_vxlan_version(install_ver, install_ver, - min_kernel_ver, expecting_ok=False) - - def test_check_fail_no_version(self): - min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN - self._check_ovs_vxlan_version(None, None, - min_kernel_ver, - expecting_ok=False) - - def test_check_fail_klm_version(self): - min_vxlan_ver = constants.MINIMUM_OVS_VXLAN_VERSION - min_kernel_ver = OVS_LINUX_KERN_VERS_WITHOUT_VXLAN - install_ver = str(float(min_vxlan_ver) - 0.01) - self._check_ovs_vxlan_version(min_vxlan_ver, - install_ver, - min_kernel_ver, - expecting_ok=False) - - def test_check_pass_kernel_version(self): - min_vxlan_ver = constants.MINIMUM_OVS_VXLAN_VERSION - min_kernel_ver = constants.MINIMUM_LINUX_KERNEL_OVS_VXLAN - self._check_ovs_vxlan_version(min_vxlan_ver, min_vxlan_ver, - min_kernel_ver, expecting_ok=True) - def test_ofctl_arg_supported(self): with mock.patch('neutron.common.utils.get_random_string') as utils: utils.return_value = 'test' diff --git a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py index 81757837efa..55afce3aa79 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py +++ b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py @@ -291,24 +291,12 @@ class TunnelTest(base.BaseTestCase): self._verify_mock_calls() def test_construct_vxlan(self): - with mock.patch.object(ovs_lib, 'get_installed_ovs_klm_version', - return_value="1.10") as klm_ver: - with mock.patch.object(ovs_lib, 'get_installed_ovs_usr_version', - return_value="1.10") as usr_ver: - with mock.patch.object(ovs_lib, 'get_installed_kernel_version', - return_value=( - constants. - MINIMUM_LINUX_KERNEL_OVS_VXLAN - )) as kernel_ver: - ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE, - self.TUN_BRIDGE, - '10.0.0.1', - self.NET_MAPPING, - 'sudo', 2, ['vxlan'], - self.VETH_MTU) - klm_ver.assert_called_once_with() - kernel_ver.assert_called_once_with() - usr_ver.assert_called_once_with('sudo') + ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE, + self.TUN_BRIDGE, + '10.0.0.1', + self.NET_MAPPING, + 'sudo', 2, ['vxlan'], + self.VETH_MTU) self._verify_mock_calls() def test_provision_local_vlan(self): diff --git a/setup.cfg b/setup.cfg index b44e1b3eabc..ded3eb4eb62 100644 --- a/setup.cfg +++ b/setup.cfg @@ -109,6 +109,7 @@ console_scripts = neutron-vpn-agent = neutron.services.vpn.agent:main neutron-metering-agent = neutron.services.metering.agents.metering_agent:main neutron-ofagent-agent = ryu.cmd.ofa_neutron_agent:main + neutron-sanity-check = neutron.cmd.sanity_check:main neutron.core_plugins = bigswitch = neutron.plugins.bigswitch.plugin:NeutronRestProxyV2 brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2