From: Pritesh Kothari Date: Thu, 21 Aug 2014 06:51:16 +0000 (-0700) Subject: Remove the Cisco Nexus monolithic plugin X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=825a90bdb4ff2b6c08ef6717d350cc8b9d654120;p=openstack-build%2Fneutron-build.git Remove the Cisco Nexus monolithic plugin The Cisco Nexus monolithic plugin does not work without the Open vSwitch plugin. The Open vSwitch plugin is scheduled to be removed as per #1323729. This patch removes the Nexus Hardware switch related plugin code. The N1KV virtual switch related code will still remain in the tree as it doesn't depend on Open vSwitch plugin. Closes-Bug: #1350387 Change-Id: I2542e92c25e9280e975c3903afb9114e850a966a --- diff --git a/etc/neutron/plugins/cisco/cisco_plugins.ini b/etc/neutron/plugins/cisco/cisco_plugins.ini index 1bfb46dc8..17eae7378 100644 --- a/etc/neutron/plugins/cisco/cisco_plugins.ini +++ b/etc/neutron/plugins/cisco/cisco_plugins.ini @@ -1,16 +1,3 @@ -[cisco_plugins] - -# (StrOpt) Period-separated module path to the plugin class to use for -# the Cisco Nexus switches. -# -# nexus_plugin = neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin - -# (StrOpt) Period-separated module path to the plugin class to use for -# the virtual switches on compute nodes. -# -# vswitch_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2 - - [cisco] # (StrOpt) A short prefix to prepend to the VLAN number when creating a @@ -48,14 +35,6 @@ # # model_class = neutron.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2 -# (StrOpt) Period-separated module path to the driver class to use for -# the Cisco Nexus switches. -# -# If no value is configured, a fake driver will be used. -# nexus_driver = neutron.plugins.cisco.test.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver -# With real hardware, use the CiscoNEXUSDriver class: -# nexus_driver = neutron.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver - # (BoolOpt) A flag to enable Layer 3 support on the Nexus switches. # Note: This feature is not supported on all models/versions of Cisco # Nexus switches. To use this feature, all of the Nexus switches in the @@ -67,29 +46,6 @@ # Cisco Nexus Switch configurations. # Each switch to be managed by Openstack Neutron must be configured here. -# -# Cisco Nexus Switch Format. -# [NEXUS_SWITCH:] -# = (1) -# ssh_port= (2) -# username= (3) -# password= (4) -# -# (1) For each host connected to a port on the switch, specify the hostname -# and the Nexus physical port (interface) it is connected to. -# (2) The TCP port for connecting via SSH to manage the switch. This is -# port number 22 unless the switch has been configured otherwise. -# (3) The username for logging into the switch to manage it. -# (4) The password for logging into the switch to manage it. -# -# Example: -# [NEXUS_SWITCH:1.1.1.1] -# compute1=1/1 -# compute2=1/2 -# ssh_port=22 -# username=admin -# password=mySecretPassword - # # N1KV Format. # [N1KV:] diff --git a/neutron/db/migration/alembic_migrations/versions/1680e1f0c4dc_remove_cisco_nexus_plugin.py b/neutron/db/migration/alembic_migrations/versions/1680e1f0c4dc_remove_cisco_nexus_plugin.py new file mode 100644 index 000000000..49040c071 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/1680e1f0c4dc_remove_cisco_nexus_plugin.py @@ -0,0 +1,53 @@ +# Copyright 2014 OpenStack Foundation +# +# 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. +# + +"""Remove Cisco Nexus Monolithic Plugin + +Revision ID: 1680e1f0c4dc +Revises: 3c346828361e +Create Date: 2014-08-31 08:58:37.123992 + +""" + +# revision identifiers, used by Alembic. +revision = '1680e1f0c4dc' +down_revision = '3c346828361e' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(active_plugins=None, options=None): + op.execute('INSERT INTO cisco_ml2_nexusport_bindings (port_id, ' + 'vlan_id, switch_ip, instance_id) SELECT ' + 'port_id, vlan_id, switch_ip, instance_id FROM ' + 'cisco_nexusport_bindings') + op.drop_table('cisco_nexusport_bindings') + + +def downgrade(active_plugins=None, options=None): + op.create_table( + 'cisco_nexusport_bindings', + sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True), + sa.Column('port_id', sa.String(255)), + sa.Column('vlan_id', sa.Integer(), nullable=False), + sa.Column('switch_ip', sa.String(255), nullable=False), + sa.Column('instance_id', sa.String(255), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.execute('INSERT INTO cisco_nexusport_bindings (port_id, ' + 'vlan_id, switch_ip, instance_id) SELECT ' + 'port_id, vlan_id, switch_ip, instance_id FROM ' + 'cisco_ml2_nexusport_bindings') diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index f0d646b0b..aa8f506d6 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -3c346828361e +1680e1f0c4dc diff --git a/neutron/db/migration/models/head.py b/neutron/db/migration/models/head.py index 47cb2630b..da206bc34 100644 --- a/neutron/db/migration/models/head.py +++ b/neutron/db/migration/models/head.py @@ -53,7 +53,6 @@ from neutron.plugins.brocade.db import models as brocade_models # noqa from neutron.plugins.cisco.db.l3 import l3_models # noqa from neutron.plugins.cisco.db import n1kv_models_v2 # noqa from neutron.plugins.cisco.db import network_models_v2 # noqa -from neutron.plugins.cisco.db import nexus_models_v2 # noqa from neutron.plugins.hyperv import model # noqa from neutron.plugins.linuxbridge.db import l2network_models_v2 # noqa from neutron.plugins.metaplugin import meta_models_v2 # noqa diff --git a/neutron/plugins/cisco/common/cisco_constants.py b/neutron/plugins/cisco/common/cisco_constants.py index eebf138d8..10d0a3bbd 100644 --- a/neutron/plugins/cisco/common/cisco_constants.py +++ b/neutron/plugins/cisco/common/cisco_constants.py @@ -40,7 +40,6 @@ PASSWORD = 'password' LOGGER_COMPONENT_NAME = "cisco_plugin" -NEXUS_PLUGIN = 'nexus_plugin' VSWITCH_PLUGIN = 'vswitch_plugin' DEVICE_IP = 'device_ip' @@ -101,11 +100,11 @@ ENCAPSULATION_PROFILE_SUFFIX = '_profile' UUID_LENGTH = 36 -# Nexus vlan and vxlan segment range -NEXUS_VLAN_RESERVED_MIN = 3968 -NEXUS_VLAN_RESERVED_MAX = 4047 -NEXUS_VXLAN_MIN = 4096 -NEXUS_VXLAN_MAX = 16000000 +# N1KV vlan and vxlan segment range +N1KV_VLAN_RESERVED_MIN = 3968 +N1KV_VLAN_RESERVED_MAX = 4047 +N1KV_VXLAN_MIN = 4096 +N1KV_VXLAN_MAX = 16000000 # Type and topic for Cisco cfg agent # ================================== diff --git a/neutron/plugins/cisco/common/config.py b/neutron/plugins/cisco/common/config.py index 1e844e5df..dcba6b64d 100644 --- a/neutron/plugins/cisco/common/config.py +++ b/neutron/plugins/cisco/common/config.py @@ -17,17 +17,6 @@ from oslo.config import cfg from neutron.agent.common import config -cisco_plugins_opts = [ - cfg.StrOpt('vswitch_plugin', - default='neutron.plugins.openvswitch.ovs_neutron_plugin.' - 'OVSNeutronPluginV2', - help=_("Virtual Switch to use")), - cfg.StrOpt('nexus_plugin', - default='neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.' - 'NexusPlugin', - help=_("Nexus Switch to use")), -] - cisco_opts = [ cfg.StrOpt('vlan_name_prefix', default='q-', help=_("VLAN Name prefix")), @@ -47,10 +36,6 @@ cisco_opts = [ default='neutron.plugins.cisco.models.virt_phy_sw_v2.' 'VirtualPhysicalSwitchModelV2', help=_("Model Class")), - cfg.StrOpt('nexus_driver', - default='neutron.plugins.cisco.test.nexus.' - 'fake_nexus_driver.CiscoNEXUSFakeDriver', - help=_("Nexus Driver Name")), ] cisco_n1k_opts = [ @@ -89,14 +74,12 @@ cisco_n1k_opts = [ cfg.CONF.register_opts(cisco_opts, "CISCO") cfg.CONF.register_opts(cisco_n1k_opts, "CISCO_N1K") -cfg.CONF.register_opts(cisco_plugins_opts, "CISCO_PLUGINS") config.register_root_helper(cfg.CONF) # shortcuts CONF = cfg.CONF CISCO = cfg.CONF.CISCO CISCO_N1K = cfg.CONF.CISCO_N1K -CISCO_PLUGINS = cfg.CONF.CISCO_PLUGINS # # device_dictionary - Contains all external device configuration. @@ -143,7 +126,7 @@ class CiscoConfigOptions(): for parsed_file in multi_parser.parsed: for parsed_item in parsed_file.keys(): dev_id, sep, dev_ip = parsed_item.partition(':') - if dev_id.lower() in ['nexus_switch', 'n1kv']: + if dev_id.lower() == 'n1kv': for dev_key, value in parsed_file[parsed_item].items(): if dev_ip and not first_device_ip: first_device_ip = dev_ip diff --git a/neutron/plugins/cisco/db/n1kv_db_v2.py b/neutron/plugins/cisco/db/n1kv_db_v2.py index 83f62e9bd..bfdd62c30 100644 --- a/neutron/plugins/cisco/db/n1kv_db_v2.py +++ b/neutron/plugins/cisco/db/n1kv_db_v2.py @@ -1362,18 +1362,18 @@ class NetworkProfile_db_mixin(object): if segment_type == c_const.NETWORK_TYPE_VLAN: if not ((seg_min <= seg_max) and ((seg_min in range(constants.MIN_VLAN_TAG, - c_const.NEXUS_VLAN_RESERVED_MIN) and + c_const.N1KV_VLAN_RESERVED_MIN) and seg_max in range(constants.MIN_VLAN_TAG, - c_const.NEXUS_VLAN_RESERVED_MIN)) or - (seg_min in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1, + c_const.N1KV_VLAN_RESERVED_MIN)) or + (seg_min in range(c_const.N1KV_VLAN_RESERVED_MAX + 1, constants.MAX_VLAN_TAG) and - seg_max in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1, + seg_max in range(c_const.N1KV_VLAN_RESERVED_MAX + 1, constants.MAX_VLAN_TAG)))): msg = (_("Segment range is invalid, select from " "%(min)s-%(nmin)s, %(nmax)s-%(max)s") % {"min": constants.MIN_VLAN_TAG, - "nmin": c_const.NEXUS_VLAN_RESERVED_MIN - 1, - "nmax": c_const.NEXUS_VLAN_RESERVED_MAX + 1, + "nmin": c_const.N1KV_VLAN_RESERVED_MIN - 1, + "nmax": c_const.N1KV_VLAN_RESERVED_MAX + 1, "max": constants.MAX_VLAN_TAG - 1}) LOG.error(msg) raise n_exc.InvalidInput(error_message=msg) @@ -1385,12 +1385,12 @@ class NetworkProfile_db_mixin(object): c_const.NETWORK_TYPE_MULTI_SEGMENT, c_const.NETWORK_TYPE_TRUNK]: if (seg_min > seg_max or - seg_min < c_const.NEXUS_VXLAN_MIN or - seg_max > c_const.NEXUS_VXLAN_MAX): + seg_min < c_const.N1KV_VXLAN_MIN or + seg_max > c_const.N1KV_VXLAN_MAX): msg = (_("segment range is invalid. Valid range is : " "%(min)s-%(max)s") % - {"min": c_const.NEXUS_VXLAN_MIN, - "max": c_const.NEXUS_VXLAN_MAX}) + {"min": c_const.N1KV_VXLAN_MIN, + "max": c_const.N1KV_VXLAN_MAX}) LOG.error(msg) raise n_exc.InvalidInput(error_message=msg) profiles = _get_network_profiles(db_session=context.session) diff --git a/neutron/plugins/cisco/db/network_db_v2.py b/neutron/plugins/cisco/db/network_db_v2.py index 53cfb17aa..03f350a03 100644 --- a/neutron/plugins/cisco/db/network_db_v2.py +++ b/neutron/plugins/cisco/db/network_db_v2.py @@ -22,7 +22,6 @@ from neutron.openstack.common import uuidutils from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.db import network_models_v2 -from neutron.plugins.openvswitch import ovs_models_v2 LOG = logging.getLogger(__name__) @@ -236,13 +235,6 @@ def is_provider_vlan(vlan_id): return True -def get_ovs_vlans(): - session = db.get_session() - bindings = (session.query(ovs_models_v2.VlanAllocation.vlan_id). - filter_by(allocated=True)) - return [binding.vlan_id for binding in bindings] - - class Credential_db_mixin(object): """Mixin class for Cisco Credentials as a resource.""" diff --git a/neutron/plugins/cisco/db/nexus_db_v2.py b/neutron/plugins/cisco/db/nexus_db_v2.py deleted file mode 100644 index 78ac51d89..000000000 --- a/neutron/plugins/cisco/db/nexus_db_v2.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright 2012, 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. -# -# @author: Rohit Agarwalla, Cisco Systems, Inc. -# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) -# - -import sqlalchemy.orm.exc as sa_exc - -import neutron.db.api as db -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.db import nexus_models_v2 - - -LOG = logging.getLogger(__name__) - - -def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Lists a nexusport binding.""" - LOG.debug(_("get_nexusport_binding() called")) - return _lookup_all_nexus_bindings(port_id=port_id, - vlan_id=vlan_id, - switch_ip=switch_ip, - instance_id=instance_id) - - -def get_nexusvlan_binding(vlan_id, switch_ip): - """Lists a vlan and switch binding.""" - LOG.debug(_("get_nexusvlan_binding() called")) - return _lookup_all_nexus_bindings(vlan_id=vlan_id, switch_ip=switch_ip) - - -def add_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Adds a nexusport binding.""" - LOG.debug(_("add_nexusport_binding() called")) - session = db.get_session() - binding = nexus_models_v2.NexusPortBinding(port_id=port_id, - vlan_id=vlan_id, - switch_ip=switch_ip, - instance_id=instance_id) - session.add(binding) - session.flush() - return binding - - -def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id): - """Removes a nexusport binding.""" - LOG.debug(_("remove_nexusport_binding() called")) - session = db.get_session() - binding = _lookup_all_nexus_bindings(session=session, - vlan_id=vlan_id, - switch_ip=switch_ip, - port_id=port_id, - instance_id=instance_id) - for bind in binding: - session.delete(bind) - session.flush() - return binding - - -def update_nexusport_binding(port_id, new_vlan_id): - """Updates nexusport binding.""" - if not new_vlan_id: - LOG.warning(_("update_nexusport_binding called with no vlan")) - return - LOG.debug(_("update_nexusport_binding called")) - session = db.get_session() - binding = _lookup_one_nexus_binding(session=session, port_id=port_id) - binding.vlan_id = new_vlan_id - session.merge(binding) - session.flush() - return binding - - -def get_nexusvm_bindings(vlan_id, instance_id): - """Lists nexusvm bindings.""" - LOG.debug(_("get_nexusvm_binding() called")) - - return _lookup_all_nexus_bindings(vlan_id=vlan_id, - instance_id=instance_id) - - -def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip): - """Lists nexusvm bindings.""" - LOG.debug(_("get_port_vlan_switch_binding() called")) - return _lookup_all_nexus_bindings(port_id=port_id, - switch_ip=switch_ip, - vlan_id=vlan_id) - - -def get_port_switch_bindings(port_id, switch_ip): - """List all vm/vlan bindings on a Nexus switch port.""" - LOG.debug(_("get_port_switch_bindings() called, " - "port:'%(port_id)s', switch:'%(switch_ip)s'"), - {'port_id': port_id, 'switch_ip': switch_ip}) - try: - return _lookup_all_nexus_bindings(port_id=port_id, - switch_ip=switch_ip) - except c_exc.NexusPortBindingNotFound: - pass - - -def get_nexussvi_bindings(): - """Lists nexus svi bindings.""" - LOG.debug(_("get_nexussvi_bindings() called")) - return _lookup_all_nexus_bindings(port_id='router') - - -def _lookup_nexus_bindings(query_type, session=None, **bfilter): - """Look up 'query_type' Nexus bindings matching the filter. - - :param query_type: 'all', 'one' or 'first' - :param session: db session - :param bfilter: filter for bindings query - :return: bindings if query gave a result, else - raise NexusPortBindingNotFound. - """ - if session is None: - session = db.get_session() - query_method = getattr(session.query( - nexus_models_v2.NexusPortBinding).filter_by(**bfilter), query_type) - try: - bindings = query_method() - if bindings: - return bindings - except sa_exc.NoResultFound: - pass - raise c_exc.NexusPortBindingNotFound(**bfilter) - - -def _lookup_all_nexus_bindings(session=None, **bfilter): - return _lookup_nexus_bindings('all', session, **bfilter) - - -def _lookup_one_nexus_binding(session=None, **bfilter): - return _lookup_nexus_bindings('one', session, **bfilter) - - -def _lookup_first_nexus_binding(session=None, **bfilter): - return _lookup_nexus_bindings('first', session, **bfilter) diff --git a/neutron/plugins/cisco/db/nexus_models_v2.py b/neutron/plugins/cisco/db/nexus_models_v2.py deleted file mode 100644 index cf0fd7b39..000000000 --- a/neutron/plugins/cisco/db/nexus_models_v2.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2012, 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. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - -import sqlalchemy as sa - -from neutron.db import model_base - - -class NexusPortBinding(model_base.BASEV2): - """Represents a binding of VM's to nexus ports.""" - - __tablename__ = "cisco_nexusport_bindings" - - id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) - port_id = sa.Column(sa.String(255)) - vlan_id = sa.Column(sa.Integer, nullable=False) - switch_ip = sa.Column(sa.String(255), nullable=False) - instance_id = sa.Column(sa.String(255), nullable=False) - - def __repr__(self): - """Just the binding, without the id key.""" - return ("" % - (self.port_id, self.vlan_id, self.switch_ip, self.instance_id)) - - def __eq__(self, other): - """Compare only the binding, without the id key.""" - return ( - self.port_id == other.port_id and - self.vlan_id == other.vlan_id and - self.switch_ip == other.switch_ip and - self.instance_id == other.instance_id - ) diff --git a/neutron/plugins/cisco/models/virt_phy_sw_v2.py b/neutron/plugins/cisco/models/virt_phy_sw_v2.py index 53fd6e211..9162384d2 100644 --- a/neutron/plugins/cisco/models/virt_phy_sw_v2.py +++ b/neutron/plugins/cisco/models/virt_phy_sw_v2.py @@ -18,20 +18,19 @@ # import inspect -import sys from neutron.api.v2 import attributes from neutron.extensions import portbindings from neutron.extensions import providernet as provider from neutron import neutron_plugin_base_v2 +from neutron.openstack.common import excutils +from neutron.openstack.common.gettextutils import _LE from neutron.openstack.common import importutils from neutron.openstack.common import log as logging from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_credentials_v2 as cred -from neutron.plugins.cisco.common import cisco_exceptions as cexc from neutron.plugins.cisco.common import config as conf from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.openvswitch import ovs_db_v2 as odb LOG = logging.getLogger(__name__) @@ -40,9 +39,9 @@ LOG = logging.getLogger(__name__) class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): """Virtual Physical Switch Model. - This implementation works with OVS and Nexus plugin for the + This implementation works with n1kv sub-plugin for the following topology: - One or more servers to a nexus switch. + One or more servers to a n1kv switch. """ __native_bulk_support = True supported_extension_aliases = ["provider", "binding"] @@ -64,12 +63,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): conf.CiscoConfigOptions() self._plugins = {} - for key in conf.CISCO_PLUGINS.keys(): - plugin_obj = conf.CISCO_PLUGINS[key] - if plugin_obj is not None: - self._plugins[key] = importutils.import_object(plugin_obj) - LOG.debug(_("Loaded device plugin %s"), - conf.CISCO_PLUGINS[key]) + self._plugins['vswitch_plugin'] = importutils.import_object( + 'neutron.plugins.cisco.n1kv.n1kv_neutron_plugin.' + 'N1kvNeutronPluginV2') if ((const.VSWITCH_PLUGIN in self._plugins) and hasattr(self._plugins[const.VSWITCH_PLUGIN], @@ -84,20 +80,14 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): {'module': __name__, 'name': self.__class__.__name__}) - # Check whether we have a valid Nexus driver loaded - self.is_nexus_plugin = False - nexus_driver = conf.CISCO.nexus_driver - if nexus_driver.endswith('CiscoNEXUSDriver'): - self.is_nexus_plugin = True - def __getattribute__(self, name): - """Delegate calls to OVS sub-plugin. + """Delegate calls to sub-plugin. - This delegates the calls to the methods implemented only by the OVS + This delegates the calls to the methods implemented by the sub-plugin. Note: Currently, bulking is handled by the caller (PluginV2), and this model class expects to receive only non-bulking calls. If, however, a bulking call is made, this will method will - delegate the call to the OVS plugin. + delegate the call to the sub-plugin. """ super_getattribute = super(VirtualPhysicalSwitchModelV2, self).__getattribute__ @@ -137,12 +127,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): func = getattr(self._plugins[plugin_key], function_name) return func(*args, **kwargs) - def _get_segmentation_id(self, network_id): - binding_seg_id = odb.get_network_binding(None, network_id) - if not binding_seg_id: - raise cexc.NetworkSegmentIDNotFound(net_id=network_id) - return binding_seg_id.segmentation_id - def _get_provider_vlan_id(self, network): if (all(attributes.is_attr_set(network.get(attr)) for attr in (provider.NETWORK_TYPE, @@ -161,34 +145,26 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): LOG.debug(_("create_network() called")) provider_vlan_id = self._get_provider_vlan_id(network[const.NETWORK]) args = [context, network] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) + switch_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, + self._func_name(), + args) # The vswitch plugin did all the verification. If it's a provider - # vlan network, save it for the nexus plugin to use later. + # vlan network, save it for the sub-plugin to use later. if provider_vlan_id: - network_id = ovs_output[const.NET_ID] + network_id = switch_output[const.NET_ID] cdb.add_provider_network(network_id, const.NETWORK_TYPE_VLAN, provider_vlan_id) LOG.debug(_("Provider network added to DB: %(network_id)s, " "%(vlan_id)s"), {'network_id': network_id, 'vlan_id': provider_vlan_id}) - return ovs_output + return switch_output def update_network(self, context, id, network): """Update network. Perform this operation in the context of the configured device plugins. - - Note that the Nexus sub-plugin does not need to be notified - (and the Nexus switch does not need to be [re]configured) - for an update network operation because the Nexus sub-plugin - is agnostic of all network-level attributes except the - segmentation ID. Furthermore, updating of the segmentation ID - is not supported by the OVS plugin since it is considered a - provider attribute, so it is not supported by this method. """ LOG.debug(_("update_network() called")) @@ -210,12 +186,12 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): plugins. """ args = [context, id] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) + switch_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, + self._func_name(), + args) if cdb.remove_provider_network(id): LOG.debug(_("Provider network removed from DB: %s"), id) - return ovs_output + return switch_output def get_network(self, context, id, fields=None): """Get network. This method is delegated to the vswitch plugin. @@ -232,30 +208,10 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): """ pass # pragma no cover - def _invoke_nexus_for_net_create(self, context, tenant_id, net_id, - instance_id, host_id): - if not self.is_nexus_plugin: - return False - - network = self.get_network(context, net_id) - vlan_id = self._get_segmentation_id(net_id) - vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id) - network[const.NET_VLAN_ID] = vlan_id - network[const.NET_VLAN_NAME] = vlan_name - attachment = { - const.TENANT_ID: tenant_id, - const.INSTANCE_ID: instance_id, - const.HOST_NAME: host_id, - } - self._invoke_plugin_per_device( - const.NEXUS_PLUGIN, - 'create_network', - [network, attachment]) - def _check_valid_port_device_owner(self, port): """Check the port for valid device_owner. - Don't call the nexus plugin for router and dhcp + Don't call the sub-plugin for router and dhcp port owners. """ return port['device_owner'].startswith('compute') @@ -278,36 +234,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): """ LOG.debug(_("create_port() called")) args = [context, port] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - instance_id = port['port']['device_id'] - - # Only call nexus plugin if there's a valid instance_id, host_id - # and device_owner - try: - host_id = self._get_port_host_id_from_bindings(port['port']) - if (instance_id and host_id and - self._check_valid_port_device_owner(port['port'])): - net_id = port['port']['network_id'] - tenant_id = port['port']['tenant_id'] - self._invoke_nexus_for_net_create( - context, tenant_id, net_id, instance_id, host_id) - except Exception: - # Create network on the Nexus plugin has failed, so we need - # to rollback the port creation on the VSwitch plugin. - exc_info = sys.exc_info() - try: - id = ovs_output['id'] - args = [context, id] - ovs_output = self._invoke_plugin_per_device( - const.VSWITCH_PLUGIN, - 'delete_port', - args) - finally: - # Re-raise the original exception - raise exc_info[0], exc_info[1], exc_info[2] - return ovs_output + return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, + self._func_name(), + args) def get_port(self, context, id, fields=None): """Get port. This method is delegated to the vswitch plugin. @@ -323,47 +252,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): """ pass # pragma no cover - def _check_nexus_net_create_needed(self, new_port, old_port): - """Check if nexus plugin should be invoked for net_create. - - In the following cases, the plugin should be invoked: - -- a port is attached to a VM instance. The old host id is None - -- VM migration. The old host id has a valid value - - When the plugin needs to be invoked, return the old_host_id, - and a list of calling arguments. - Otherwise, return '' for old host id and an empty list - """ - old_device_id = old_port['device_id'] - new_device_id = new_port.get('device_id') - new_host_id = self._get_port_host_id_from_bindings(new_port) - tenant_id = old_port['tenant_id'] - net_id = old_port['network_id'] - old_host_id = self._get_port_host_id_from_bindings(old_port) - - LOG.debug(_("tenant_id: %(tid)s, net_id: %(nid)s, " - "old_device_id: %(odi)s, new_device_id: %(ndi)s, " - "old_host_id: %(ohi)s, new_host_id: %(nhi)s, " - "old_device_owner: %(odo)s, new_device_owner: %(ndo)s"), - {'tid': tenant_id, 'nid': net_id, - 'odi': old_device_id, 'ndi': new_device_id, - 'ohi': old_host_id, 'nhi': new_host_id, - 'odo': old_port.get('device_owner'), - 'ndo': new_port.get('device_owner')}) - - # A port is attached to an instance - if (new_device_id and not old_device_id and new_host_id and - self._check_valid_port_device_owner(new_port)): - return '', [tenant_id, net_id, new_device_id, new_host_id] - - # An instance is being migrated - if (old_device_id and old_host_id and new_host_id != old_host_id and - self._check_valid_port_device_owner(old_port)): - return old_host_id, [tenant_id, net_id, old_device_id, new_host_id] - - # no need to invoke the plugin - return '', [] - def update_port(self, context, id, port): """Update port. @@ -371,44 +259,10 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): plugins. """ LOG.debug(_("update_port() called")) - old_port = self.get_port(context, id) args = [context, id, port] - ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - args) - try: - # Check if the nexus plugin needs to be invoked - old_host_id, create_args = self._check_nexus_net_create_needed( - port['port'], old_port) - - # In the case of migration, invoke it to remove - # the previous port binding - if old_host_id: - vlan_id = self._get_segmentation_id(old_port['network_id']) - delete_args = [old_port['device_id'], vlan_id] - self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - "delete_port", - delete_args) - - # Invoke the Nexus plugin to create a net and/or new port binding - if create_args: - self._invoke_nexus_for_net_create(context, *create_args) - - return ovs_output - except Exception: - exc_info = sys.exc_info() - LOG.error(_("Unable to update port '%s' on Nexus switch"), - old_port['name'], exc_info=exc_info) - try: - # Roll back vSwitch plugin to original port attributes. - args = [context, id, {'port': old_port}] - self._invoke_plugin_per_device( - const.VSWITCH_PLUGIN, - self._func_name(), - args) - finally: - # Re-raise the original exception - raise exc_info[0], exc_info[1], exc_info[2] + return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, + self._func_name(), + args) def delete_port(self, context, id, l3_port_check=True): """Delete port. @@ -419,94 +273,18 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2): LOG.debug(_("delete_port() called")) port = self.get_port(context, id) - host_id = self._get_port_host_id_from_bindings(port) - - if (self.is_nexus_plugin and host_id and - self._check_valid_port_device_owner(port)): - vlan_id = self._get_segmentation_id(port['network_id']) - n_args = [port['device_id'], vlan_id] - self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) try: args = [context, id] - ovs_output = self._invoke_plugin_per_device( + switch_output = self._invoke_plugin_per_device( const.VSWITCH_PLUGIN, self._func_name(), args, l3_port_check=l3_port_check) - except Exception: - exc_info = sys.exc_info() - # Roll back the delete port on the Nexus plugin - try: - tenant_id = port['tenant_id'] - net_id = port['network_id'] - instance_id = port['device_id'] - host_id = port[portbindings.HOST_ID] - self._invoke_nexus_for_net_create(context, tenant_id, net_id, - instance_id, host_id) - finally: - # Raise the original exception. - raise exc_info[0], exc_info[1], exc_info[2] - - return ovs_output - - def add_router_interface(self, context, router_id, interface_info): - """Add a router interface on a subnet. - - Only invoke the Nexus plugin to create SVI if L3 support on - the Nexus switches is enabled and a Nexus plugin is loaded, - otherwise send it to the vswitch plugin - """ - if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin): - LOG.debug(_("L3 enabled on Nexus plugin, create SVI on switch")) - if 'subnet_id' not in interface_info: - raise cexc.SubnetNotSpecified() - if 'port_id' in interface_info: - raise cexc.PortIdForNexusSvi() - subnet = self.get_subnet(context, interface_info['subnet_id']) - gateway_ip = subnet['gateway_ip'] - # Get gateway IP address and netmask - cidr = subnet['cidr'] - netmask = cidr.split('/', 1)[1] - gateway_ip = gateway_ip + '/' + netmask - network_id = subnet['network_id'] - vlan_id = self._get_segmentation_id(network_id) - vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id) - - n_args = [vlan_name, vlan_id, subnet['id'], gateway_ip, router_id] - return self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) - else: - LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch")) - n_args = [context, router_id, interface_info] - return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - n_args) - - def remove_router_interface(self, context, router_id, interface_info): - """Remove a router interface. - - Only invoke the Nexus plugin to delete SVI if L3 support on - the Nexus switches is enabled and a Nexus plugin is loaded, - otherwise send it to the vswitch plugin - """ - if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin): - LOG.debug(_("L3 enabled on Nexus plugin, delete SVI from switch")) + except Exception as e: + with excutils.save_and_reraise_exception(): + LOG.error(_LE("Unable to delete port '%(pname)s' on switch. " + "Exception: %(exp)s"), {'pname': port['name'], + 'exp': e}) - subnet = self.get_subnet(context, interface_info['subnet_id']) - network_id = subnet['network_id'] - vlan_id = self._get_segmentation_id(network_id) - n_args = [vlan_id, router_id] - - return self._invoke_plugin_per_device(const.NEXUS_PLUGIN, - self._func_name(), - n_args) - else: - LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch")) - n_args = [context, router_id, interface_info] - return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, - self._func_name(), - n_args) + return switch_output def create_subnet(self, context, subnet): """Create subnet. This method is delegated to the vswitch plugin. diff --git a/neutron/plugins/cisco/nexus/__init__.py b/neutron/plugins/cisco/nexus/__init__.py deleted file mode 100644 index b66a37fca..000000000 --- a/neutron/plugins/cisco/nexus/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2011 Cisco Systems, Inc. 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems, Inc. -""" -Init module for Nexus Driver -""" diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py deleted file mode 100644 index 9ee95bf36..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright 2011 Cisco Systems, Inc. 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. -# -# @author: Debojyoti Dutta, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems Inc. -# -""" -Implements a Nexus-OS NETCONF over SSHv2 API Client -""" - - -from ncclient import manager - -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_credentials_v2 as cred -from neutron.plugins.cisco.common import cisco_exceptions as cexc -from neutron.plugins.cisco.common import config as conf -from neutron.plugins.cisco.db import nexus_db_v2 -from neutron.plugins.cisco.nexus import cisco_nexus_snippets as snipp - -LOG = logging.getLogger(__name__) - - -class CiscoNEXUSDriver(): - """Nexus Driver Main Class.""" - def __init__(self): - cisco_switches = conf.get_device_dictionary() - self.nexus_switches = dict(((key[1], key[2]), val) - for key, val in cisco_switches.items() - if key[0] == 'NEXUS_SWITCH') - self.credentials = {} - self.connections = {} - - def _edit_config(self, nexus_host, target='running', config='', - allowed_exc_strs=None): - """Modify switch config for a target config type. - - :param nexus_host: IP address of switch to configure - :param target: Target config type - :param config: Configuration string in XML format - :param allowed_exc_strs: Exceptions which have any of these strings - as a subset of their exception message - (str(exception)) can be ignored - - :raises: NexusConfigFailed - - """ - if not allowed_exc_strs: - allowed_exc_strs = [] - mgr = self.nxos_connect(nexus_host) - try: - mgr.edit_config(target, config=config) - except Exception as e: - for exc_str in allowed_exc_strs: - if exc_str in str(e): - break - else: - # Raise a Neutron exception. Include a description of - # the original ncclient exception. No need to preserve T/B - raise cexc.NexusConfigFailed(config=config, exc=e) - - def get_credential(self, nexus_ip): - if nexus_ip not in self.credentials: - nexus_username = cred.Store.get_username(nexus_ip) - nexus_password = cred.Store.get_password(nexus_ip) - self.credentials[nexus_ip] = { - const.USERNAME: nexus_username, - const.PASSWORD: nexus_password - } - return self.credentials[nexus_ip] - - def nxos_connect(self, nexus_host): - """Make SSH connection to the Nexus Switch.""" - if getattr(self.connections.get(nexus_host), 'connected', None): - return self.connections[nexus_host] - - nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port']) - nexus_creds = self.get_credential(nexus_host) - nexus_user = nexus_creds[const.USERNAME] - nexus_password = nexus_creds[const.PASSWORD] - try: - man = manager.connect(host=nexus_host, - port=nexus_ssh_port, - username=nexus_user, - password=nexus_password) - self.connections[nexus_host] = man - except Exception as e: - # Raise a Neutron exception. Include a description of - # the original ncclient exception. No need to preserve T/B. - raise cexc.NexusConnectFailed(nexus_host=nexus_host, exc=e) - - return self.connections[nexus_host] - - def create_xml_snippet(self, cutomized_config): - """Create XML snippet. - - Creates the Proper XML structure for the Nexus Switch Configuration. - """ - conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config) - return conf_xml_snippet - - def create_vlan(self, nexus_host, vlanid, vlanname): - """Create a VLAN on Nexus Switch given the VLAN ID and Name.""" - confstr = self.create_xml_snippet( - snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname)) - self._edit_config(nexus_host, target='running', config=confstr) - - # Enable VLAN active and no-shutdown states. Some versions of - # Nexus switch do not allow state changes for the extended VLAN - # range (1006-4094), but these errors can be ignored (default - # values are appropriate). - state_config = [snipp.CMD_VLAN_ACTIVE_SNIPPET, - snipp.CMD_VLAN_NO_SHUTDOWN_SNIPPET] - for snippet in state_config: - try: - confstr = self.create_xml_snippet(snippet % vlanid) - self._edit_config( - nexus_host, - target='running', - config=confstr, - allowed_exc_strs=["Can't modify state for extended", - "Command is only allowed on VLAN"]) - except cexc.NexusConfigFailed: - with excutils.save_and_reraise_exception(): - self.delete_vlan(nexus_host, vlanid) - - def delete_vlan(self, nexus_host, vlanid): - """Delete a VLAN on Nexus Switch given the VLAN ID.""" - confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid - confstr = self.create_xml_snippet(confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def enable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface): - """Enable a VLAN on a trunk interface.""" - # If one or more VLANs are already configured on this interface, - # include the 'add' keyword. - if nexus_db_v2.get_port_switch_bindings('%s:%s' % (etype, interface), - nexus_host): - snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET - else: - snippet = snipp.CMD_INT_VLAN_SNIPPET - confstr = snippet % (etype, interface, vlanid, etype) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def disable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface): - """Disable a VLAN on a trunk interface.""" - confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (etype, interface, - vlanid, etype) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name, - etype, nexus_port): - """Create VLAN and trunk it on the specified ports.""" - self.create_vlan(nexus_host, vlan_id, vlan_name) - LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id) - if nexus_port: - self.enable_vlan_on_trunk_int(nexus_host, vlan_id, - etype, nexus_port) - - def delete_and_untrunk_vlan(self, nexus_host, vlan_id, etype, nexus_port): - """Delete VLAN and untrunk it from the specified ports.""" - self.delete_vlan(nexus_host, vlan_id) - if nexus_port: - self.disable_vlan_on_trunk_int(nexus_host, vlan_id, - etype, nexus_port) - - def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip): - confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip) - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def delete_vlan_svi(self, nexus_host, vlan_id): - confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id - confstr = self.create_xml_snippet(confstr) - LOG.debug(_("NexusDriver: %s"), confstr) - self._edit_config(nexus_host, target='running', config=confstr) diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py deleted file mode 100644 index a0fc4d4bb..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ /dev/null @@ -1,345 +0,0 @@ -# Copyright 2012 Cisco Systems, Inc. -# 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Edgar Magana, Cisco Systems, Inc. -# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com) -# - -""" -PlugIn for Nexus OS driver -""" - - -from neutron.openstack.common import excutils -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc -from neutron.plugins.cisco.common import config as conf -from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.cisco.db import nexus_db_v2 as nxos_db -from neutron.plugins.cisco import l2device_plugin_base - - -LOG = logging.getLogger(__name__) - - -class NexusPlugin(l2device_plugin_base.L2DevicePluginBase): - """Nexus PlugIn Main Class.""" - _networks = {} - - def __init__(self): - """Extract configuration parameters from the configuration file.""" - self._client = importutils.import_object(conf.CISCO.nexus_driver) - LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver) - self._nexus_switches = conf.get_device_dictionary() - - def create_network(self, network, attachment): - """Create or update a network when an attachment is changed. - - This method is not invoked at the usual plugin create_network() time. - Instead, it is invoked on create/update port. - - :param network: Network on which the port operation is happening - :param attachment: Details about the owner of the port - - Create a VLAN in the appropriate switch/port, and configure the - appropriate interfaces for this VLAN. - """ - LOG.debug(_("NexusPlugin:create_network() called")) - # Grab the switch IPs and ports for this host - host_connections = [] - host = attachment['host_name'] - for switch_type, switch_ip, attr in self._nexus_switches: - if str(attr) == str(host): - port = self._nexus_switches[switch_type, switch_ip, attr] - # Get ether type for port, assume an ethernet type - # if none specified. - if ':' in port: - etype, port_id = port.split(':') - else: - etype, port_id = 'ethernet', port - host_connections.append((switch_ip, etype, port_id)) - if not host_connections: - raise cisco_exc.NexusComputeHostNotConfigured(host=host) - - vlan_id = network[const.NET_VLAN_ID] - vlan_name = network[const.NET_VLAN_NAME] - auto_create = True - auto_trunk = True - if cdb.is_provider_vlan(vlan_id): - vlan_name = ''.join([conf.CISCO.provider_vlan_name_prefix, - str(vlan_id)]) - auto_create = conf.CISCO.provider_vlan_auto_create - auto_trunk = conf.CISCO.provider_vlan_auto_trunk - - # Check if this network is already in the DB - for switch_ip, etype, port_id in host_connections: - vlan_created = False - vlan_trunked = False - eport_id = '%s:%s' % (etype, port_id) - # Check for switch vlan bindings - try: - # This vlan has already been created on this switch - # via another operation, like SVI bindings. - nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - vlan_created = True - auto_create = False - except cisco_exc.NexusPortBindingNotFound: - # No changes, proceed as normal - pass - - try: - nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id, - switch_ip) - except cisco_exc.NexusPortBindingNotFound: - if auto_create and auto_trunk: - # Create vlan and trunk vlan on the port - LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name) - self._client.create_and_trunk_vlan( - switch_ip, vlan_id, vlan_name, etype, port_id) - vlan_created = True - vlan_trunked = True - elif auto_create: - # Create vlan but do not trunk it on the port - LOG.debug(_("Nexus: create vlan %s"), vlan_name) - self._client.create_vlan(switch_ip, vlan_id, vlan_name) - vlan_created = True - elif auto_trunk: - # Only trunk vlan on the port - LOG.debug(_("Nexus: trunk vlan %s"), vlan_name) - self._client.enable_vlan_on_trunk_int( - switch_ip, vlan_id, etype, port_id) - vlan_trunked = True - - try: - instance = attachment[const.INSTANCE_ID] - nxos_db.add_nexusport_binding(eport_id, str(vlan_id), - switch_ip, instance) - except Exception: - with excutils.save_and_reraise_exception(): - # Add binding failed, roll back any vlan creation/enabling - if vlan_created and vlan_trunked: - LOG.debug(_("Nexus: delete & untrunk vlan %s"), - vlan_name) - self._client.delete_and_untrunk_vlan(switch_ip, - vlan_id, - etype, port_id) - elif vlan_created: - LOG.debug(_("Nexus: delete vlan %s"), vlan_name) - self._client.delete_vlan(switch_ip, vlan_id) - elif vlan_trunked: - LOG.debug(_("Nexus: untrunk vlan %s"), vlan_name) - self._client.disable_vlan_on_trunk_int(switch_ip, - vlan_id, - etype, - port_id) - - net_id = network[const.NET_ID] - new_net_dict = {const.NET_ID: net_id, - const.NET_NAME: network[const.NET_NAME], - const.NET_PORTS: {}, - const.NET_VLAN_NAME: vlan_name, - const.NET_VLAN_ID: vlan_id} - self._networks[net_id] = new_net_dict - return new_net_dict - - def add_router_interface(self, vlan_name, vlan_id, subnet_id, - gateway_ip, router_id): - """Create VLAN SVI on the Nexus switch.""" - # Find a switch to create the SVI on - switch_ip = self._find_switch_for_svi() - if not switch_ip: - raise cisco_exc.NoNexusSviSwitch() - - # Check if this vlan exists on the switch already - try: - nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - except cisco_exc.NexusPortBindingNotFound: - # Create vlan and trunk vlan on the port - self._client.create_and_trunk_vlan( - switch_ip, vlan_id, vlan_name, etype=None, nexus_port=None) - # Check if a router interface has already been created - try: - nxos_db.get_nexusvm_bindings(vlan_id, router_id) - raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id, - router_id=router_id) - except cisco_exc.NexusPortBindingNotFound: - self._client.create_vlan_svi(switch_ip, vlan_id, gateway_ip) - nxos_db.add_nexusport_binding('router', str(vlan_id), - switch_ip, router_id) - - return True - - def remove_router_interface(self, vlan_id, router_id): - """Remove VLAN SVI from the Nexus Switch.""" - # Grab switch_ip from database - switch_ip = nxos_db.get_nexusvm_bindings(vlan_id, - router_id)[0].switch_ip - - # Delete the SVI interface from the switch - self._client.delete_vlan_svi(switch_ip, vlan_id) - - # Invoke delete_port to delete this row - # And delete vlan if required - return self.delete_port(router_id, vlan_id) - - def _find_switch_for_svi(self): - """Get a switch to create the SVI on.""" - LOG.debug(_("Grabbing a switch to create SVI")) - nexus_switches = self._client.nexus_switches - if conf.CISCO.svi_round_robin: - LOG.debug(_("Using round robin to create SVI")) - switch_dict = dict( - (switch_ip, 0) for switch_ip, _ in nexus_switches) - try: - bindings = nxos_db.get_nexussvi_bindings() - # Build a switch dictionary with weights - for binding in bindings: - switch_ip = binding.switch_ip - if switch_ip not in switch_dict: - switch_dict[switch_ip] = 1 - else: - switch_dict[switch_ip] += 1 - # Search for the lowest value in the dict - if switch_dict: - switch_ip = min(switch_dict, key=switch_dict.get) - return switch_ip - except cisco_exc.NexusPortBindingNotFound: - pass - - LOG.debug(_("No round robin or zero weights, using first switch")) - # Return the first switch in the config - return conf.first_device_ip - - def delete_network(self, tenant_id, net_id, **kwargs): - """Delete network. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:delete_network() called")) # pragma no cover - - def update_network(self, tenant_id, net_id, **kwargs): - """Update the properties of a particular Virtual Network. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:update_network() called")) # pragma no cover - - def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs): - """Create port. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:create_port() called")) # pragma no cover - - def delete_port(self, device_id, vlan_id): - """Delete port. - - Delete port bindings from the database and scan whether the network - is still required on the interfaces trunked. - """ - LOG.debug(_("NexusPlugin:delete_port() called")) - # Delete DB row(s) for this port - try: - rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id) - except cisco_exc.NexusPortBindingNotFound: - return - - auto_delete = True - auto_untrunk = True - if cdb.is_provider_vlan(vlan_id): - auto_delete = conf.CISCO.provider_vlan_auto_create - auto_untrunk = conf.CISCO.provider_vlan_auto_trunk - LOG.debug(_("delete_network(): provider vlan %s"), vlan_id) - - instance_id = False - for row in rows: - instance_id = row['instance_id'] - switch_ip = row.switch_ip - etype, nexus_port = '', '' - if row['port_id'] == 'router': - etype, nexus_port = 'vlan', row['port_id'] - auto_untrunk = False - else: - etype, nexus_port = row['port_id'].split(':') - - nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id, - row.switch_ip, - row.instance_id) - # Check whether there are any remaining instances using this - # vlan on this Nexus port. - try: - nxos_db.get_port_vlan_switch_binding(row.port_id, - row.vlan_id, - row.switch_ip) - except cisco_exc.NexusPortBindingNotFound: - try: - if nexus_port and auto_untrunk: - # Untrunk the vlan from this Nexus interface - self._client.disable_vlan_on_trunk_int( - switch_ip, row.vlan_id, etype, nexus_port) - - # Check whether there are any remaining instances - # using this vlan on the Nexus switch. - if auto_delete: - try: - nxos_db.get_nexusvlan_binding(row.vlan_id, - row.switch_ip) - except cisco_exc.NexusPortBindingNotFound: - # Delete this vlan from this switch - self._client.delete_vlan(switch_ip, row.vlan_id) - except Exception: - # The delete vlan operation on the Nexus failed, - # so this delete_port request has failed. For - # consistency, roll back the Nexus database to what - # it was before this request. - with excutils.save_and_reraise_exception(): - nxos_db.add_nexusport_binding(row.port_id, - row.vlan_id, - row.switch_ip, - row.instance_id) - - return instance_id - - def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs): - """Update port. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:update_port() called")) # pragma no cover - - def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, - **kwargs): - """Plug interfaces. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:plug_interface() called")) # pragma no cover - - def unplug_interface(self, tenant_id, net_id, port_id, **kwargs): - """Unplug interface. - - Not applicable to Nexus plugin. Defined here to satisfy abstract - method requirements. - """ - LOG.debug(_("NexusPlugin:unplug_interface() called") - ) # pragma no cover diff --git a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py b/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py deleted file mode 100644 index e8c8e2633..000000000 --- a/neutron/plugins/cisco/nexus/cisco_nexus_snippets.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2011 Cisco Systems, Inc. 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. -# -# @author: Edgar Magana, Cisco Systems, Inc. -# @author: Arvind Somya (asomya@cisco.com) Cisco Systems, Inc. - -""" -Nexus-OS XML-based configuration snippets -""" - - -# The following are standard strings, messages used to communicate with Nexus, -EXEC_CONF_SNIPPET = """ - - - <__XML__MODE__exec_configure>%s - - - -""" - -CMD_VLAN_CONF_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - %s - - - - -""" - -CMD_VLAN_ACTIVE_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - active - - - - -""" - -CMD_VLAN_NO_SHUTDOWN_SNIPPET = """ - - - <__XML__PARAM_value>%s - <__XML__MODE_vlan> - - - - - - -""" - -CMD_NO_VLAN_CONF_SNIPPET = """ - - - - <__XML__PARAM_value>%s - - - -""" - -CMD_INT_VLAN_HEADER = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - """ - -CMD_VLAN_ID = """ - %s""" - -CMD_VLAN_ADD_ID = """ - %s - """ % CMD_VLAN_ID - -CMD_INT_VLAN_TRAILER = """ - - - - - - - -""" - -CMD_INT_VLAN_SNIPPET = (CMD_INT_VLAN_HEADER + - CMD_VLAN_ID + - CMD_INT_VLAN_TRAILER) - -CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER + - CMD_VLAN_ADD_ID + - CMD_INT_VLAN_TRAILER) - -CMD_NO_VLAN_INT_SNIPPET = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - %s - - - - - - - - -""" - -FILTER_SHOW_VLAN_BRIEF_SNIPPET = """ - - - - - -""" - -CMD_VLAN_SVI_SNIPPET = """ - - - %s - <__XML__MODE_vlan> - - - - -
-
%s
-
-
- -
-
-""" - -CMD_NO_VLAN_SVI_SNIPPET = """ - - - - %s - - - -""" diff --git a/neutron/plugins/cisco/test/__init__.py b/neutron/plugins/cisco/test/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/plugins/cisco/test/nexus/__init__.py b/neutron/plugins/cisco/test/nexus/__init__.py deleted file mode 100644 index 4ee6bc9cc..000000000 --- a/neutron/plugins/cisco/test/nexus/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2012 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 __builtin__ -setattr(__builtin__, '_', lambda x: x) diff --git a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py b/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py deleted file mode 100644 index d9ca848a4..000000000 --- a/neutron/plugins/cisco/test/nexus/fake_nexus_driver.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2012 Cisco Systems, Inc. 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. -# -# @author: Sumit Naiksatam, Cisco Systems, Inc. -# @author: Rohit Agarwalla, Cisco Systems, Inc. - - -class CiscoNEXUSFakeDriver(): - """Nexus Driver Fake Class.""" - - def __init__(self): - pass - - def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user, - nexus_password): - """Make the fake connection to the Nexus Switch.""" - pass - - def create_xml_snippet(self, cutomized_config): - """Create XML snippet. - - Creates the Proper XML structure for the Nexus Switch - Configuration. - """ - pass - - def enable_vlan(self, mgr, vlanid, vlanname): - """Create a VLAN on Nexus Switch given the VLAN ID and Name.""" - pass - - def disable_vlan(self, mgr, vlanid): - """Delete a VLAN on Nexus Switch given the VLAN ID.""" - pass - - def disable_switch_port(self, mgr, interface): - """Disable trunk mode an interface on Nexus Switch.""" - pass - - def enable_vlan_on_trunk_int(self, mgr, etype, interface, vlanid): - """Enable vlan on trunk interface. - - Enable trunk mode vlan access an interface on Nexus Switch given - VLANID. - """ - pass - - def disable_vlan_on_trunk_int(self, mgr, interface, vlanid): - """Disables vlan in trunk interface. - - Enables trunk mode vlan access an interface on Nexus Switch given - VLANID. - """ - pass - - def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user, - nexus_password, nexus_ports, nexus_ssh_port, vlan_ids): - """Create VLAN and enable it on interface. - - Creates a VLAN and Enable on trunk mode an interface on Nexus Switch - given the VLAN ID and Name and Interface Number. - """ - pass - - def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port): - """Delete VLAN. - - Delete a VLAN and Disables trunk mode an interface on Nexus Switch - given the VLAN ID and Interface Number. - """ - pass - - def build_vlans_cmd(self): - """Build a string with all the VLANs on the same Switch.""" - pass - - def add_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port, vlan_ids=None): - """Add a vlan from interfaces on the Nexus switch given the VLAN ID.""" - pass - - def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password, - nexus_ports, nexus_ssh_port): - """Remove vlan from interfaces. - - Removes a vlan from interfaces on the Nexus switch given the VLAN ID. - """ - pass diff --git a/neutron/tests/unit/cisco/test_config.py b/neutron/tests/unit/cisco/test_config.py deleted file mode 100644 index 7104ed06a..000000000 --- a/neutron/tests/unit/cisco/test_config.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2013 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. - -import mock -from oslo.config import cfg - -from neutron.plugins.cisco.common import config as cisco_config -from neutron.tests import base - - -class TestCiscoNexusPluginConfig(base.BaseTestCase): - - def setUp(self): - # Point neutron config file to: neutron/tests/etc/neutron.conf.test - self.config_parse() - - super(TestCiscoNexusPluginConfig, self).setUp() - - def test_config_parse_error(self): - """Check that config error is raised upon config parser failure.""" - with mock.patch.object(cfg, 'MultiConfigParser') as parser: - parser.return_value.read.return_value = [] - self.assertRaises(cfg.Error, cisco_config.CiscoConfigOptions) - - def test_create_device_dictionary(self): - """Test creation of the device dictionary based on nexus config.""" - test_config = { - 'NEXUS_SWITCH:1.1.1.1': { - 'username': ['admin'], - 'password': ['mySecretPassword'], - 'ssh_port': [22], - 'compute1': ['1/1'], - 'compute2': ['1/2'], - }, - 'NEXUS_SWITCH:2.2.2.2': { - 'username': ['admin'], - 'password': ['mySecretPassword'], - 'ssh_port': [22], - 'compute3': ['1/1'], - 'compute4': ['1/2'], - }, - } - expected_dev_dict = { - ('NEXUS_SWITCH', '1.1.1.1', 'username'): 'admin', - ('NEXUS_SWITCH', '1.1.1.1', 'password'): 'mySecretPassword', - ('NEXUS_SWITCH', '1.1.1.1', 'ssh_port'): 22, - ('NEXUS_SWITCH', '1.1.1.1', 'compute1'): '1/1', - ('NEXUS_SWITCH', '1.1.1.1', 'compute2'): '1/2', - ('NEXUS_SWITCH', '2.2.2.2', 'username'): 'admin', - ('NEXUS_SWITCH', '2.2.2.2', 'password'): 'mySecretPassword', - ('NEXUS_SWITCH', '2.2.2.2', 'ssh_port'): 22, - ('NEXUS_SWITCH', '2.2.2.2', 'compute3'): '1/1', - ('NEXUS_SWITCH', '2.2.2.2', 'compute4'): '1/2', - } - with mock.patch.object(cfg, 'MultiConfigParser') as parser: - parser.return_value.read.return_value = cfg.CONF.config_file - parser.return_value.parsed = [test_config] - cisco_config.CiscoConfigOptions() - self.assertEqual(cisco_config.device_dictionary, - expected_dev_dict) diff --git a/neutron/tests/unit/cisco/test_network_plugin.py b/neutron/tests/unit/cisco/test_network_plugin.py deleted file mode 100644 index 582859ac8..000000000 --- a/neutron/tests/unit/cisco/test_network_plugin.py +++ /dev/null @@ -1,1190 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation. -# -# 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 contextlib -import copy -import inspect -import logging as std_logging -import mock - -import six -import webob.exc as wexc - -from neutron.api import extensions -from neutron.api.v2 import attributes -from neutron.api.v2 import base -from neutron.common import exceptions as n_exc -from neutron import context -from neutron.db import db_base_plugin_v2 as base_plugin -from neutron.db import l3_db -from neutron.extensions import portbindings -from neutron.extensions import providernet as provider -from neutron import manager -from neutron.openstack.common import gettextutils -from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.common import config as cisco_config -from neutron.plugins.cisco.db import network_db_v2 -from neutron.plugins.cisco.db import nexus_db_v2 -from neutron.plugins.cisco.models import virt_phy_sw_v2 -from neutron.plugins.openvswitch.common import config as ovs_config -from neutron.plugins.openvswitch import ovs_db_v2 -from neutron.tests.unit import _test_extension_portbindings as test_bindings -from neutron.tests.unit import test_db_plugin -from neutron.tests.unit import test_extensions - -LOG = logging.getLogger(__name__) -CORE_PLUGIN = 'neutron.plugins.cisco.network_plugin.PluginV2' -NEXUS_PLUGIN = 'neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin' -NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.' - 'cisco_nexus_network_driver_v2.CiscoNEXUSDriver') -PHYS_NET = 'physnet1' -BRIDGE_NAME = 'br-eth1' -VLAN_START = 1000 -VLAN_END = 1100 -COMP_HOST_NAME = 'testhost' -COMP_HOST_NAME_2 = 'testhost_2' -NEXUS_IP_ADDR = '1.1.1.1' -NEXUS_DEV_ID = 'NEXUS_SWITCH' -NEXUS_USERNAME = 'admin' -NEXUS_PASSWORD = 'mySecretPassword' -NEXUS_SSH_PORT = 22 -NEXUS_INTERFACE = '1/1' -NEXUS_INTERFACE_2 = '1/2' -NEXUS_PORT_1 = 'ethernet:1/1' -NEXUS_PORT_2 = 'ethernet:1/2' -NETWORK_NAME = 'test_network' -CIDR_1 = '10.0.0.0/24' -CIDR_2 = '10.0.1.0/24' -DEVICE_ID_1 = '11111111-1111-1111-1111-111111111111' -DEVICE_ID_2 = '22222222-2222-2222-2222-222222222222' -DEVICE_OWNER = 'compute:None' - - -class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): - - def setUp(self): - """Configure for end-to-end neutron testing using a mock ncclient. - - This setup includes: - - Configure the OVS plugin to use VLANs in the range of - VLAN_START-VLAN_END. - - Configure the Cisco plugin model to use the Nexus driver. - - Configure the Nexus driver to use an imaginary switch - at NEXUS_IP_ADDR. - - """ - # Configure the OVS and Cisco plugins - phys_bridge = ':'.join([PHYS_NET, BRIDGE_NAME]) - phys_vlan_range = ':'.join([PHYS_NET, str(VLAN_START), str(VLAN_END)]) - config = { - ovs_config: { - 'OVS': {'bridge_mappings': phys_bridge, - 'network_vlan_ranges': [phys_vlan_range], - 'tenant_network_type': 'vlan'} - }, - cisco_config: { - 'CISCO': {'nexus_driver': NEXUS_DRIVER}, - 'CISCO_PLUGINS': {'nexus_plugin': NEXUS_PLUGIN}, - } - } - for module in config: - for group in config[module]: - for opt, val in config[module][group].items(): - module.cfg.CONF.set_override(opt, val, group) - - # Configure the Nexus switch dictionary - # TODO(Henry): add tests for other devices - nexus_config = { - (NEXUS_DEV_ID, NEXUS_IP_ADDR, 'username'): NEXUS_USERNAME, - (NEXUS_DEV_ID, NEXUS_IP_ADDR, 'password'): NEXUS_PASSWORD, - (NEXUS_DEV_ID, NEXUS_IP_ADDR, 'ssh_port'): NEXUS_SSH_PORT, - (NEXUS_DEV_ID, NEXUS_IP_ADDR, COMP_HOST_NAME): NEXUS_INTERFACE, - (NEXUS_DEV_ID, NEXUS_IP_ADDR, COMP_HOST_NAME_2): NEXUS_INTERFACE_2, - } - nexus_patch = mock.patch.dict(cisco_config.device_dictionary, - nexus_config) - nexus_patch.start() - self.addCleanup(nexus_patch.stop) - - # Use a mock netconf client - self.mock_ncclient = mock.Mock() - ncclient_patch = mock.patch.dict('sys.modules', - {'ncclient': self.mock_ncclient}) - ncclient_patch.start() - self.addCleanup(ncclient_patch.stop) - - # Call the parent setUp, start the core plugin - super(CiscoNetworkPluginV2TestCase, self).setUp(CORE_PLUGIN) - self.port_create_status = 'DOWN' - - # Set Cisco config module's first configured Nexus IP address. - # Used for SVI placement when round-robin placement is disabled. - mock.patch.object(cisco_config, 'first_device_ip', - new=NEXUS_IP_ADDR).start() - - def _get_plugin_ref(self): - return getattr(manager.NeutronManager.get_plugin(), - "_model")._plugins[const.VSWITCH_PLUGIN] - - @contextlib.contextmanager - def _patch_ncclient(self, attr, value): - """Configure an attribute on the mock ncclient module. - - This method can be used to inject errors by setting a side effect - or a return value for an ncclient method. - - :param attr: ncclient attribute (typically method) to be configured. - :param value: Value to be configured on the attribute. - - """ - # Configure attribute. - config = {attr: value} - self.mock_ncclient.configure_mock(**config) - # Continue testing - yield - # Unconfigure attribute - config = {attr: None} - self.mock_ncclient.configure_mock(**config) - - @staticmethod - def _config_dependent_side_effect(match_config, exc): - """Generates a config-dependent side effect for ncclient edit_config. - - This method generates a mock side-effect function which can be - configured on the mock ncclient module for the edit_config method. - This side effect will cause a given exception to be raised whenever - the XML config string that is passed to edit_config contains all - words in a given match config string. - - :param match_config: String containing keywords to be matched - :param exc: Exception to be raised when match is found - :return: Side effect function for the mock ncclient module's - edit_config method. - - """ - keywords = match_config.split() - - def _side_effect_function(target, config): - if all(word in config for word in keywords): - raise exc - return _side_effect_function - - def _is_in_nexus_cfg(self, words): - """Check if any config sent to Nexus contains all words in a list.""" - for call in (self.mock_ncclient.manager.connect.return_value. - edit_config.mock_calls): - configlet = call[2]['config'] - if all(word in configlet for word in words): - return True - return False - - def _is_in_last_nexus_cfg(self, words): - """Check if last config sent to Nexus contains all words in a list.""" - last_cfg = (self.mock_ncclient.manager.connect.return_value. - edit_config.mock_calls[-1][2]['config']) - return all(word in last_cfg for word in words) - - def _is_vlan_configured(self, vlan_creation_expected=True, - add_keyword_expected=False): - vlan_created = self._is_in_nexus_cfg(['vlan', 'vlan-name']) - add_appears = self._is_in_last_nexus_cfg(['add']) - return (self._is_in_last_nexus_cfg(['allowed', 'vlan']) and - vlan_created == vlan_creation_expected and - add_appears == add_keyword_expected) - - def _is_vlan_unconfigured(self, vlan_deletion_expected=True, - vlan_untrunk_expected=True): - vlan_deleted = self._is_in_nexus_cfg( - ['no', 'vlan', 'vlan-id-create-delete']) - vlan_untrunked = self._is_in_nexus_cfg(['allowed', 'vlan', 'remove']) - return (vlan_deleted == vlan_deletion_expected and - vlan_untrunked == vlan_untrunk_expected) - - def _assertExpectedHTTP(self, status, exc): - """Confirm that an HTTP status corresponds to an expected exception. - - Confirm that an HTTP status which has been returned for an - neutron API request matches the HTTP status corresponding - to an expected exception. - - :param status: HTTP status - :param exc: Expected exception - - """ - if exc in base.FAULT_MAP: - expected_http = base.FAULT_MAP[exc].code - else: - expected_http = wexc.HTTPInternalServerError.code - self.assertEqual(status, expected_http) - - -class TestCiscoGetAttribute(CiscoNetworkPluginV2TestCase): - - def test_get_unsupported_attr_in_lazy_gettext_mode(self): - """Test get of unsupported attribute in lazy gettext mode. - - This test also checks that this operation does not cause - excessive nesting of calls to deepcopy. - """ - plugin = manager.NeutronManager.get_plugin() - - def _lazy_gettext(msg): - return gettextutils.Message(msg, domain='neutron') - - with mock.patch.dict(six.moves.builtins.__dict__, - {'_': _lazy_gettext}): - self.nesting_count = 0 - - def _count_nesting(*args, **kwargs): - self.nesting_count += 1 - - with mock.patch.object(copy, 'deepcopy', - side_effect=_count_nesting, - wraps=copy.deepcopy): - self.assertRaises(AttributeError, getattr, plugin, - 'an_unsupported_attribute') - # If there were no nested calls to deepcopy, then the total - # number of calls to deepcopy should be 2 (1 call for - # each mod'd field in the AttributeError message raised - # by the plugin). - self.assertEqual(self.nesting_count, 2) - - -class TestCiscoBasicGet(CiscoNetworkPluginV2TestCase, - test_db_plugin.TestBasicGet): - pass - - -class TestCiscoV2HTTPResponse(CiscoNetworkPluginV2TestCase, - test_db_plugin.TestV2HTTPResponse): - pass - - -class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase, - test_db_plugin.TestPortsV2, - test_bindings.PortBindingsHostTestCaseMixin): - - @contextlib.contextmanager - def _create_port_res(self, name=NETWORK_NAME, cidr=CIDR_1, - do_delete=True, host_id=COMP_HOST_NAME): - """Create a network, subnet, and port and yield the result. - - Create a network, subnet, and port, yield the result, - then delete the port, subnet, and network. - - :param name: Name of network to be created - :param cidr: cidr address of subnetwork to be created - :param do_delete: If set to True, delete the port at the - end of testing - :param host_id: Name of compute host to use for testing - - """ - ctx = context.get_admin_context() - with self.network(name=name) as network: - with self.subnet(network=network, cidr=cidr) as subnet: - net_id = subnet['subnet']['network_id'] - args = (portbindings.HOST_ID, 'device_id', 'device_owner') - port_dict = {portbindings.HOST_ID: host_id, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER} - res = self._create_port(self.fmt, net_id, arg_list=args, - context=ctx, **port_dict) - port = self.deserialize(self.fmt, res) - yield res - if do_delete: - self._delete('ports', port['port']['id']) - self._delete('subnets', subnet['subnet']['id']) - self._delete('networks', network['network']['id']) - - def test_create_ports_bulk_emulated_plugin_failure(self): - real_has_attr = hasattr - - #ensures the API choose the emulation code path - def fakehasattr(item, attr): - if attr.endswith('__native_bulk_support'): - return False - return real_has_attr(item, attr) - - with mock.patch('__builtin__.hasattr', - new=fakehasattr): - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_port - with mock.patch.object(plugin_ref, - 'create_port') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - - patched_plugin.side_effect = side_effect - with self.network() as net: - res = self._create_port_bulk(self.fmt, 2, - net['network']['id'], - 'test', - True) - # Expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'ports', - wexc.HTTPInternalServerError.code) - - def test_create_ports_bulk_native(self): - if self._skip_native_bulk: - self.skipTest("Plugin does not support native bulk port create") - - def test_create_ports_bulk_emulated(self): - if self._skip_native_bulk: - self.skipTest("Plugin does not support native bulk port create") - - def test_create_ports_bulk_native_plugin_failure(self): - if self._skip_native_bulk: - self.skipTest("Plugin does not support native bulk port create") - ctx = context.get_admin_context() - with self.network() as net: - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_port - with mock.patch.object(plugin_ref, - 'create_port') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - - patched_plugin.side_effect = side_effect - res = self._create_port_bulk(self.fmt, 2, - net['network']['id'], - 'test', True, context=ctx) - # We expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'ports', - wexc.HTTPInternalServerError.code) - - def test_nexus_enable_vlan_cmd(self): - """Verify the syntax of the command to enable a vlan on an intf.""" - - # First vlan should be configured without 'add' keyword - with self._create_port_res(name='net1', cidr=CIDR_1): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - add_keyword_expected=False)) - self.mock_ncclient.reset_mock() - - # Second vlan should be configured with 'add' keyword - with self._create_port_res(name='net2', cidr=CIDR_2): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - add_keyword_expected=True)) - - def test_nexus_vlan_config_two_hosts(self): - """Verify config/unconfig of vlan on two compute hosts.""" - - @contextlib.contextmanager - def _create_port_check_vlan(comp_host_name, device_id, - vlan_creation_expected=True): - arg_list = (portbindings.HOST_ID,) - port_dict = {portbindings.HOST_ID: comp_host_name, - 'device_id': device_id, - 'device_owner': DEVICE_OWNER} - with self.port(subnet=subnet, fmt=self.fmt, - arg_list=arg_list, **port_dict) as port: - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=vlan_creation_expected, - add_keyword_expected=False)) - self.mock_ncclient.reset_mock() - yield - self._delete('ports', port['port']['id']) - - # Create network and subnet - with self.network(name=NETWORK_NAME) as network: - with self.subnet(network=network, cidr=CIDR_1) as subnet: - - # Create an instance on first compute host - with _create_port_check_vlan( - COMP_HOST_NAME, DEVICE_ID_1, vlan_creation_expected=True): - - # Create an instance on second compute host - with _create_port_check_vlan( - COMP_HOST_NAME_2, DEVICE_ID_2, - vlan_creation_expected=False): - pass - - # Instance on second host is now terminated. - # Vlan should be untrunked from port, but vlan should - # still exist on the switch. - self.assertTrue(self._is_vlan_unconfigured( - vlan_deletion_expected=False)) - self.mock_ncclient.reset_mock() - - # Instance on first host is now terminated. - # Vlan should be untrunked from port and vlan should have - # been deleted from the switch. - self.assertTrue(self._is_vlan_unconfigured( - vlan_deletion_expected=True)) - - def test_nexus_connect_fail(self): - """Test failure to connect to a Nexus switch. - - While creating a network, subnet, and port, simulate a connection - failure to a nexus switch. Confirm that the expected HTTP code - is returned for the create port operation. - - """ - with self._patch_ncclient('manager.connect.side_effect', - AttributeError): - with self._create_port_res(do_delete=False) as res: - self._assertExpectedHTTP(res.status_int, - c_exc.NexusConnectFailed) - - def test_nexus_config_fail(self): - """Test a Nexus switch configuration failure. - - While creating a network, subnet, and port, simulate a nexus - switch configuration error. Confirm that the expected HTTP code - is returned for the create port operation. - - """ - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - AttributeError): - with self._create_port_res(do_delete=False) as res: - self._assertExpectedHTTP(res.status_int, - c_exc.NexusConfigFailed) - - def test_nexus_extended_vlan_range_failure(self): - """Test that extended VLAN range config errors are ignored. - - Some versions of Nexus switch do not allow state changes for - the extended VLAN range (1006-4094), but these errors can be - ignored (default values are appropriate). Test that such errors - are ignored by the Nexus plugin. - - """ - config_err_strings = { - "state active": "Can't modify state for extended", - "no shutdown": "Command is only allowed on VLAN", - } - for config, err_string in config_err_strings.items(): - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - self._config_dependent_side_effect(config, - Exception(err_string))): - with self._create_port_res() as res: - self.assertEqual(res.status_int, wexc.HTTPCreated.code) - - def test_nexus_vlan_config_rollback(self): - """Test rollback following Nexus VLAN state config failure. - - Test that the Cisco Nexus plugin correctly deletes the VLAN - on the Nexus switch when the 'state active' command fails (for - a reason other than state configuration change is rejected - for the extended VLAN range). - - """ - vlan_state_configs = ['state active', 'no shutdown'] - for config in vlan_state_configs: - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - self._config_dependent_side_effect(config, ValueError)): - with self._create_port_res(do_delete=False) as res: - # Confirm that the last configuration sent to the Nexus - # switch was deletion of the VLAN. - self.assertTrue( - self._is_in_last_nexus_cfg(['', '']) - ) - self._assertExpectedHTTP(res.status_int, - c_exc.NexusConfigFailed) - - def test_get_seg_id_fail(self): - """Test handling of a NetworkSegmentIDNotFound exception. - - Test the Cisco NetworkSegmentIDNotFound exception by simulating - a return of None by the OVS DB get_network_binding method - during port creation. - - """ - orig = ovs_db_v2.get_network_binding - - def _return_none_if_nexus_caller(self, *args, **kwargs): - def _calling_func_name(offset=0): - """Get name of the calling function 'offset' frames back.""" - return inspect.stack()[1 + offset][3] - if (_calling_func_name(1) == '_get_segmentation_id' and - _calling_func_name(2) == '_invoke_nexus_for_net_create'): - return None - else: - return orig(self, *args, **kwargs) - - with mock.patch.object(ovs_db_v2, 'get_network_binding', - new=_return_none_if_nexus_caller): - with self._create_port_res(do_delete=False) as res: - self._assertExpectedHTTP(res.status_int, - c_exc.NetworkSegmentIDNotFound) - - def test_nexus_host_non_configured(self): - """Test handling of a NexusComputeHostNotConfigured exception. - - Test the Cisco NexusComputeHostNotConfigured exception by using - a fictitious host name during port creation. - - """ - with self._create_port_res(do_delete=False, - host_id='fakehost') as res: - self._assertExpectedHTTP(res.status_int, - c_exc.NexusComputeHostNotConfigured) - - def _check_rollback_on_bind_failure(self, - vlan_deletion_expected, - vlan_untrunk_expected): - """Test for proper rollback following add Nexus DB binding failure. - - Test that the Cisco Nexus plugin correctly rolls back the vlan - configuration on the Nexus switch when add_nexusport_binding fails - within the plugin's create_port() method. - - """ - inserted_exc = KeyError - with mock.patch.object(nexus_db_v2, 'add_nexusport_binding', - side_effect=inserted_exc): - with self._create_port_res(do_delete=False) as res: - # Confirm that the configuration sent to the Nexus - # switch includes deletion of the vlan (if expected) - # and untrunking of the vlan from the ethernet interface - # (if expected). - self.assertTrue(self._is_vlan_unconfigured( - vlan_deletion_expected=vlan_deletion_expected, - vlan_untrunk_expected=vlan_untrunk_expected)) - self._assertExpectedHTTP(res.status_int, inserted_exc) - - def test_nexus_rollback_on_bind_failure_non_provider_vlan(self): - """Test rollback upon DB binding failure for non-provider vlan.""" - self._check_rollback_on_bind_failure(vlan_deletion_expected=True, - vlan_untrunk_expected=True) - - def test_nexus_rollback_on_bind_failure_prov_vlan_no_auto_create(self): - """Test rollback on bind fail for prov vlan w auto-create disabled.""" - with mock.patch.object(network_db_v2, 'is_provider_vlan', - return_value=True): - # Disable auto-create. This config change will be cleared based - # on cleanup scheduled in the CiscoNetworkPluginV2TestCase - # class' setUp() method. - cisco_config.CONF.set_override('provider_vlan_auto_create', - False, 'CISCO') - self._check_rollback_on_bind_failure(vlan_deletion_expected=False, - vlan_untrunk_expected=True) - - def test_nexus_rollback_on_bind_failure_prov_vlan_no_auto_trunk(self): - """Test rollback on bind fail for prov vlan w auto-trunk disabled.""" - with mock.patch.object(network_db_v2, 'is_provider_vlan', - return_value=True): - # Disable auto-trunk. This config change will be cleared - # based on post-test cleanup scheduled in the - # CiscoNetworkPluginV2TestCase class' setUp() method. - cisco_config.CONF.set_override('provider_vlan_auto_trunk', - False, 'CISCO') - self._check_rollback_on_bind_failure(vlan_deletion_expected=True, - vlan_untrunk_expected=False) - - def test_model_update_port_rollback(self): - """Test for proper rollback for Cisco model layer update port failure. - - Test that the vSwitch plugin port configuration is rolled back - (restored) by the Cisco plugin model layer when there is a - failure in the Nexus sub-plugin for an update port operation. - - The update port operation simulates a port attachment scenario: - first a port is created with no instance (null device_id), - and then a port update is requested with a non-null device_id - to simulate the port attachment. - - """ - with self.port(fmt=self.fmt, device_id='', - device_owner=DEVICE_OWNER) as orig_port: - - inserted_exc = ValueError - with mock.patch.object( - virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_nexus_for_net_create', - side_effect=inserted_exc): - - # Send an update port request including a non-null device ID - data = {'port': {'device_id': DEVICE_ID_2, - 'device_owner': DEVICE_OWNER, - portbindings.HOST_ID: COMP_HOST_NAME}} - port_id = orig_port['port']['id'] - req = self.new_update_request('ports', data, port_id) - res = req.get_response(self.api) - - # Sanity check failure result code - self._assertExpectedHTTP(res.status_int, inserted_exc) - - # Check that the port still has the original device ID - plugin = base_plugin.NeutronDbPluginV2() - ctx = context.get_admin_context() - db_port = plugin._get_port(ctx, port_id) - self.assertEqual(db_port['device_id'], - orig_port['port']['device_id']) - - def test_model_delete_port_rollback(self): - """Test for proper rollback for OVS plugin delete port failure. - - Test that the nexus port configuration is rolled back (restored) - by the Cisco model plugin when there is a failure in the OVS - plugin for a delete port operation. - - """ - with self._create_port_res() as res: - - # After port is created, we should have one binding for this - # vlan/nexus switch. - port = self.deserialize(self.fmt, res) - start_rows = nexus_db_v2.get_nexusvlan_binding(VLAN_START, - NEXUS_IP_ADDR) - self.assertEqual(len(start_rows), 1) - - # Inject an exception in the OVS plugin delete_port - # processing, and attempt a port deletion. - inserted_exc = n_exc.Conflict - expected_http = base.FAULT_MAP[inserted_exc].code - with mock.patch.object(l3_db.L3_NAT_db_mixin, - 'disassociate_floatingips', - side_effect=inserted_exc): - self._delete('ports', port['port']['id'], - expected_code=expected_http) - - # Confirm that the Cisco model plugin has restored - # the nexus configuration for this port after deletion failure. - end_rows = nexus_db_v2.get_nexusvlan_binding(VLAN_START, - NEXUS_IP_ADDR) - self.assertEqual(start_rows, end_rows) - - def test_nexus_delete_port_rollback(self): - """Test for proper rollback for nexus plugin delete port failure. - - Test for rollback (i.e. restoration) of a VLAN entry in the - nexus database whenever the nexus plugin fails to reconfigure the - nexus switch during a delete_port operation. - - """ - with self._create_port_res() as res: - - port = self.deserialize(self.fmt, res) - - # Check that there is only one binding in the nexus database - # for this VLAN/nexus switch. - start_rows = nexus_db_v2.get_nexusvlan_binding(VLAN_START, - NEXUS_IP_ADDR) - self.assertEqual(len(start_rows), 1) - - # Simulate a Nexus switch configuration error during - # port deletion. - with self._patch_ncclient( - 'manager.connect.return_value.edit_config.side_effect', - AttributeError): - self._delete('ports', port['port']['id'], - base.FAULT_MAP[c_exc.NexusConfigFailed].code) - - # Confirm that the binding has been restored (rolled back). - end_rows = nexus_db_v2.get_nexusvlan_binding(VLAN_START, - NEXUS_IP_ADDR) - self.assertEqual(start_rows, end_rows) - - def test_model_update_port_attach(self): - """Test the model for update_port in attaching to an instance. - - Mock the routines that call into the plugin code, and make sure they - are called with correct arguments. - - """ - with contextlib.nested( - self.port(), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_plugin_per_device'), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_nexus_for_net_create') - ) as (port, invoke_plugin_per_device, invoke_nexus_for_net_create): - data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER}} - - req = self.new_update_request('ports', data, port['port']['id']) - # Note, due to mocking out the two model routines, response won't - # contain any useful data - req.get_response(self.api) - - # Note that call_args_list is used instead of - # assert_called_once_with which requires exact match of arguments. - # This is because the mocked routines contain variable number of - # arguments and/or dynamic objects. - self.assertEqual(invoke_plugin_per_device.call_count, 1) - self.assertEqual( - invoke_plugin_per_device.call_args_list[0][0][0:2], - (const.VSWITCH_PLUGIN, 'update_port')) - self.assertEqual(invoke_nexus_for_net_create.call_count, 1) - self.assertEqual( - invoke_nexus_for_net_create.call_args_list[0][0][1:], - (port['port']['tenant_id'], port['port']['network_id'], - data['port']['device_id'], - data['port'][portbindings.HOST_ID],)) - - def test_model_update_port_migrate(self): - """Test the model for update_port in migrating an instance. - - Mock the routines that call into the plugin code, and make sure they - are called with correct arguments. - - """ - arg_list = (portbindings.HOST_ID,) - data = {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER} - - with contextlib.nested( - self.port(arg_list=arg_list, **data), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_plugin_per_device'), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_nexus_for_net_create') - ) as (port, invoke_plugin_per_device, invoke_nexus_for_net_create): - data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME_2}} - req = self.new_update_request('ports', data, port['port']['id']) - # Note, due to mocking out the two model routines, response won't - # contain any useful data - req.get_response(self.api) - - # Note that call_args_list is used instead of - # assert_called_once_with which requires exact match of arguments. - # This is because the mocked routines contain variable number of - # arguments and/or dynamic objects. - self.assertEqual(invoke_plugin_per_device.call_count, 2) - self.assertEqual( - invoke_plugin_per_device.call_args_list[0][0][0:2], - (const.VSWITCH_PLUGIN, 'update_port')) - self.assertEqual( - invoke_plugin_per_device.call_args_list[1][0][0:2], - (const.NEXUS_PLUGIN, 'delete_port')) - self.assertEqual(invoke_nexus_for_net_create.call_count, 1) - self.assertEqual( - invoke_nexus_for_net_create.call_args_list[0][0][1:], - (port['port']['tenant_id'], port['port']['network_id'], - port['port']['device_id'], - data['port'][portbindings.HOST_ID],)) - - def test_model_update_port_net_create_not_needed(self): - """Test the model for update_port when no action is needed. - - Mock the routines that call into the plugin code, and make sure that - VSWITCH plugin is called with correct arguments, while NEXUS plugin is - not called at all. - - """ - arg_list = (portbindings.HOST_ID,) - data = {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER} - - with contextlib.nested( - self.port(arg_list=arg_list, **data), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_plugin_per_device'), - mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2, - '_invoke_nexus_for_net_create') - ) as (port, invoke_plugin_per_device, invoke_nexus_for_net_create): - data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER}} - req = self.new_update_request('ports', data, port['port']['id']) - # Note, due to mocking out the two model routines, response won't - # contain any useful data - req.get_response(self.api) - - # Note that call_args_list is used instead of - # assert_called_once_with which requires exact match of arguments. - # This is because the mocked routines contain variable number of - # arguments and/or dynamic objects. - self.assertEqual(invoke_plugin_per_device.call_count, 1) - self.assertEqual( - invoke_plugin_per_device.call_args_list[0][0][0:2], - (const.VSWITCH_PLUGIN, 'update_port')) - self.assertFalse(invoke_nexus_for_net_create.called) - - def verify_portbinding(self, host_id1, host_id2, - vlan, device_id, binding_port): - """Verify a port binding entry in the DB is correct.""" - self.assertEqual(host_id1, host_id2) - pb = nexus_db_v2.get_nexusvm_bindings(vlan, device_id) - self.assertEqual(len(pb), 1) - self.assertEqual(pb[0].port_id, binding_port) - self.assertEqual(pb[0].switch_ip, NEXUS_IP_ADDR) - - def test_db_update_port_attach(self): - """Test DB for update_port in attaching to an instance. - - Query DB for the port binding entry corresponding to the search key - (vlan, device_id), and make sure that it's bound to correct switch port - - """ - with self.port() as port: - data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER}} - - req = self.new_update_request('ports', data, port['port']['id']) - res = self.deserialize(self.fmt, req.get_response(self.api)) - ctx = context.get_admin_context() - net = self._show('networks', res['port']['network_id'], - neutron_context=ctx)['network'] - self.assertTrue(attributes.is_attr_set( - net.get(provider.SEGMENTATION_ID))) - vlan = net[provider.SEGMENTATION_ID] - self.assertEqual(vlan, VLAN_START) - self.verify_portbinding(res['port'][portbindings.HOST_ID], - data['port'][portbindings.HOST_ID], - vlan, - data['port']['device_id'], - NEXUS_PORT_1) - - def test_db_update_port_migrate(self): - """Test DB for update_port in migrating an instance. - - Query DB for the port binding entry corresponding to the search key - (vlan, device_id), and make sure that it's bound to correct switch port - before and after the migration. - - """ - arg_list = (portbindings.HOST_ID,) - data = {portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - 'device_owner': DEVICE_OWNER} - - with self.port(arg_list=arg_list, **data) as port: - ctx = context.get_admin_context() - net = self._show('networks', port['port']['network_id'], - neutron_context=ctx)['network'] - self.assertTrue(attributes.is_attr_set( - net.get(provider.SEGMENTATION_ID))) - vlan = net[provider.SEGMENTATION_ID] - self.assertEqual(vlan, VLAN_START) - self.verify_portbinding(port['port'][portbindings.HOST_ID], - data[portbindings.HOST_ID], - vlan, - data['device_id'], - NEXUS_PORT_1) - - new_data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME_2}} - req = self.new_update_request('ports', - new_data, port['port']['id']) - res = self.deserialize(self.fmt, req.get_response(self.api)) - self.verify_portbinding(res['port'][portbindings.HOST_ID], - new_data['port'][portbindings.HOST_ID], - vlan, - data['device_id'], - NEXUS_PORT_2) - - def test_delete_ports_by_device_id_second_call_failure(self): - plugin_ref = self._get_plugin_ref() - self._test_delete_ports_by_device_id_second_call_failure(plugin_ref) - - def test_delete_ports_ignores_port_not_found(self): - plugin_ref = self._get_plugin_ref() - self._test_delete_ports_ignores_port_not_found(plugin_ref) - - -class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase, - test_db_plugin.TestNetworksV2): - - def test_create_networks_bulk_emulated_plugin_failure(self): - real_has_attr = hasattr - - def fakehasattr(item, attr): - if attr.endswith('__native_bulk_support'): - return False - return real_has_attr(item, attr) - - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_network - #ensures the API choose the emulation code path - with mock.patch('__builtin__.hasattr', - new=fakehasattr): - with mock.patch.object(plugin_ref, - 'create_network') as patched_plugin: - def side_effect(*args, **kwargs): - return self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - patched_plugin.side_effect = side_effect - res = self._create_network_bulk(self.fmt, 2, 'test', True) - LOG.debug('response is %s', res) - # We expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'networks', - wexc.HTTPInternalServerError.code) - - def test_create_networks_bulk_native_plugin_failure(self): - if self._skip_native_bulk: - self.skipTest("Plugin does not support native bulk network create") - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_network - with mock.patch.object(plugin_ref, - 'create_network') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - - patched_plugin.side_effect = side_effect - res = self._create_network_bulk(self.fmt, 2, 'test', True) - # We expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'networks', - wexc.HTTPInternalServerError.code) - - @contextlib.contextmanager - def _provider_vlan_network(self, phys_net, segment_id, net_name): - provider_attrs = {provider.NETWORK_TYPE: 'vlan', - provider.PHYSICAL_NETWORK: phys_net, - provider.SEGMENTATION_ID: segment_id} - arg_list = tuple(provider_attrs.keys()) - res = self._create_network(self.fmt, net_name, True, - arg_list=arg_list, **provider_attrs) - network = self.deserialize(self.fmt, res)['network'] - yield network - req = self.new_delete_request('networks', network['id']) - req.get_response(self.api) - - def test_create_provider_vlan_network(self): - with self._provider_vlan_network(PHYS_NET, '1234', - 'pvnet1') as network: - expected = [('name', 'pvnet1'), - ('admin_state_up', True), - ('status', 'ACTIVE'), - ('shared', False), - (provider.NETWORK_TYPE, 'vlan'), - (provider.PHYSICAL_NETWORK, PHYS_NET), - (provider.SEGMENTATION_ID, 1234)] - for k, v in expected: - self.assertEqual(network[k], v) - self.assertTrue(network_db_v2.is_provider_network(network['id'])) - - def test_delete_provider_vlan_network(self): - with self._provider_vlan_network(PHYS_NET, '1234', - 'pvnet1') as network: - network_id = network['id'] - # Provider network should now be deleted - self.assertFalse(network_db_v2.is_provider_network(network_id)) - - -class TestCiscoSubnetsV2(CiscoNetworkPluginV2TestCase, - test_db_plugin.TestSubnetsV2): - - def test_create_subnets_bulk_emulated_plugin_failure(self): - real_has_attr = hasattr - - #ensures the API choose the emulation code path - def fakehasattr(item, attr): - if attr.endswith('__native_bulk_support'): - return False - return real_has_attr(item, attr) - - with mock.patch('__builtin__.hasattr', - new=fakehasattr): - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_subnet - with mock.patch.object(plugin_ref, - 'create_subnet') as patched_plugin: - - def side_effect(*args, **kwargs): - self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - - patched_plugin.side_effect = side_effect - with self.network() as net: - res = self._create_subnet_bulk(self.fmt, 2, - net['network']['id'], - 'test') - # We expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'subnets', - wexc.HTTPInternalServerError.code) - - def test_create_subnets_bulk_native_plugin_failure(self): - if self._skip_native_bulk: - self.skipTest("Plugin does not support native bulk subnet create") - plugin_ref = self._get_plugin_ref() - orig = plugin_ref.create_subnet - with mock.patch.object(plugin_ref, - 'create_subnet') as patched_plugin: - def side_effect(*args, **kwargs): - return self._do_side_effect(patched_plugin, orig, - *args, **kwargs) - - patched_plugin.side_effect = side_effect - with self.network() as net: - res = self._create_subnet_bulk(self.fmt, 2, - net['network']['id'], - 'test') - - # We expect an internal server error as we injected a fault - self._validate_behavior_on_bulk_failure( - res, - 'subnets', - wexc.HTTPInternalServerError.code) - - -class TestCiscoRouterInterfacesV2(CiscoNetworkPluginV2TestCase): - - def setUp(self): - """Configure a log exception counter and an API extension manager.""" - self.log_exc_count = 0 - - def _count_exception_logs(*args, **kwargs): - self.log_exc_count += 1 - - mock.patch.object(std_logging.LoggerAdapter, 'exception', - autospec=True, - side_effect=_count_exception_logs, - wraps=std_logging.LoggerAdapter.exception).start() - super(TestCiscoRouterInterfacesV2, self).setUp() - ext_mgr = extensions.PluginAwareExtensionManager.get_instance() - self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) - - @contextlib.contextmanager - def _network_subnet_router(self): - """Context mgr for creating/deleting a net, subnet, and router.""" - with self.network() as network: - with self.subnet(network=network) as subnet: - data = {'router': {'tenant_id': 'test_tenant_id'}} - request = self.new_create_request('routers', data, self.fmt) - response = request.get_response(self.ext_api) - router = self.deserialize(self.fmt, response) - yield network, subnet, router - self._delete('routers', router['router']['id']) - - @contextlib.contextmanager - def _router_interface(self, router, subnet, **kwargs): - """Create a router interface, yield the response, then delete it.""" - interface_data = {} - if subnet: - interface_data['subnet_id'] = subnet['subnet']['id'] - interface_data.update(kwargs) - request = self.new_action_request('routers', interface_data, - router['router']['id'], - 'add_router_interface') - response = request.get_response(self.ext_api) - - yield response - - # If router interface was created successfully, delete it now. - if response.status_int == wexc.HTTPOk.code: - request = self.new_action_request('routers', interface_data, - router['router']['id'], - 'remove_router_interface') - request.get_response(self.ext_api) - - @contextlib.contextmanager - def _network_subnet_router_interface(self, **kwargs): - """Context mgr for create/deleting a net, subnet, router and intf.""" - with self._network_subnet_router() as (network, subnet, router): - with self._router_interface(router, subnet, - **kwargs) as response: - yield response - - def test_port_list_filtered_by_router_id(self): - """Test port list command filtered by router ID.""" - with self._network_subnet_router() as (network, subnet, router): - with self._router_interface(router, subnet): - query_params = "device_id=%s" % router['router']['id'] - req = self.new_list_request('ports', self.fmt, query_params) - res = self.deserialize(self.fmt, req.get_response(self.api)) - self.assertEqual(len(res['ports']), 1) - self.assertEqual(res['ports'][0]['device_id'], - router['router']['id']) - self.assertFalse(self.log_exc_count) - - def test_add_remove_router_intf_with_nexus_l3_enabled(self): - """Verifies proper add/remove intf operation with Nexus L3 enabled. - - With 'nexus_l3_enable' configured to True, confirm that a switched - virtual interface (SVI) is created/deleted on the Nexus switch when - a virtual router interface is created/deleted. - """ - cisco_config.CONF.set_override('nexus_l3_enable', True, 'CISCO') - with self._network_subnet_router_interface(): - self.assertTrue(self._is_in_last_nexus_cfg( - ['interface', 'vlan', 'ip', 'address'])) - # Clear list of calls made to mock ncclient - self.mock_ncclient.reset() - # Router interface is now deleted. Confirm that SVI - # has been deleted from the Nexus switch. - self.assertTrue(self._is_in_nexus_cfg(['no', 'interface', 'vlan'])) - self.assertTrue(self._is_in_last_nexus_cfg(['no', 'vlan'])) - - def test_add_remove_router_intf_with_nexus_l3_disabled(self): - """Verifies proper add/remove intf operation with Nexus L3 disabled. - - With 'nexus_l3_enable' configured to False, confirm that no changes - are made to the Nexus switch running configuration when a virtual - router interface is created and then deleted. - """ - cisco_config.CONF.set_override('nexus_l3_enable', False, 'CISCO') - with self._network_subnet_router_interface(): - self.assertFalse(self.mock_ncclient.manager.connect. - return_value.edit_config.called) - - def test_create_svi_but_subnet_not_specified_exception(self): - """Tests raising of SubnetNotSpecified exception. - - Tests that a SubnetNotSpecified exception is raised when an - add_router_interface request is made for creating a switch virtual - interface (SVI), but the request does not specify a subnet. - """ - cisco_config.CONF.set_override('nexus_l3_enable', True, 'CISCO') - with self._network_subnet_router() as (network, subnet, router): - with self._router_interface(router, subnet=None) as response: - self._assertExpectedHTTP(response.status_int, - c_exc.SubnetNotSpecified) - - def test_create_svi_but_port_id_included_exception(self): - """Tests raising of PortIdForNexusSvi exception. - - Tests that a PortIdForNexusSvi exception is raised when an - add_router_interface request is made for creating a switch virtual - interface (SVI), but the request includes a virtual port ID. - """ - cisco_config.CONF.set_override('nexus_l3_enable', True, 'CISCO') - with self._network_subnet_router_interface( - port_id='my_port_id') as response: - self._assertExpectedHTTP(response.status_int, - c_exc.PortIdForNexusSvi) - - -class TestCiscoPortsV2XML(TestCiscoPortsV2): - fmt = 'xml' - - -class TestCiscoNetworksV2XML(TestCiscoNetworksV2): - fmt = 'xml' - - -class TestCiscoSubnetsV2XML(TestCiscoSubnetsV2): - fmt = 'xml' - - -class TestCiscoRouterInterfacesV2XML(TestCiscoRouterInterfacesV2): - fmt = 'xml' diff --git a/neutron/tests/unit/cisco/test_nexus_db.py b/neutron/tests/unit/cisco/test_nexus_db.py deleted file mode 100644 index 2d7ba213b..000000000 --- a/neutron/tests/unit/cisco/test_nexus_db.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright (c) 2013 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 collections -import mock -import testtools - -from neutron.db import api as db -from neutron.plugins.cisco.common import cisco_exceptions as c_exc -from neutron.plugins.cisco.common import config -from neutron.plugins.cisco.db import nexus_db_v2 as nxdb -from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2 -from neutron.tests.unit import testlib_api - - -class CiscoNexusDbTest(testlib_api.SqlTestCase): - - """Unit tests for cisco.db.nexus_models_v2.NexusPortBinding model.""" - - NpbObj = collections.namedtuple('NpbObj', 'port vlan switch instance') - - def setUp(self): - super(CiscoNexusDbTest, self).setUp() - self.session = db.get_session() - - def _npb_test_obj(self, pnum, vnum, switch=None, instance=None): - """Create a Nexus port binding test object from a pair of numbers.""" - if pnum is 'router': - port = pnum - else: - port = '1/%s' % str(pnum) - vlan = str(vnum) - if switch is None: - switch = '10.9.8.7' - if instance is None: - instance = 'instance_%s_%s' % (str(pnum), str(vnum)) - return self.NpbObj(port, vlan, switch, instance) - - def _assert_equal(self, npb, npb_obj): - self.assertEqual(npb.port_id, npb_obj.port) - self.assertEqual(int(npb.vlan_id), int(npb_obj.vlan)) - self.assertEqual(npb.switch_ip, npb_obj.switch) - self.assertEqual(npb.instance_id, npb_obj.instance) - - def _add_to_db(self, npbs): - for npb in npbs: - nxdb.add_nexusport_binding( - npb.port, npb.vlan, npb.switch, npb.instance) - - def test_nexusportbinding_add_remove(self): - npb11 = self._npb_test_obj(10, 100) - npb = nxdb.add_nexusport_binding( - npb11.port, npb11.vlan, npb11.switch, npb11.instance) - self._assert_equal(npb, npb11) - npb = nxdb.remove_nexusport_binding( - npb11.port, npb11.vlan, npb11.switch, npb11.instance) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb11) - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.remove_nexusport_binding( - npb11.port, npb11.vlan, npb11.switch, npb11.instance) - - def test_nexusportbinding_get(self): - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_to_db([npb11, npb21, npb22]) - - npb = nxdb.get_nexusport_binding( - npb11.port, npb11.vlan, npb11.switch, npb11.instance) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb11) - npb = nxdb.get_nexusport_binding( - npb21.port, npb21.vlan, npb21.switch, npb21.instance) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb21) - npb = nxdb.get_nexusport_binding( - npb22.port, npb22.vlan, npb22.switch, npb22.instance) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb22) - - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.get_nexusport_binding( - npb21.port, npb21.vlan, npb21.switch, "dummyInstance") - - def test_nexusvlanbinding_get(self): - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_to_db([npb11, npb21, npb22]) - - npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, npb11.switch) - self.assertEqual(len(npb_all_v100), 2) - npb_v200 = nxdb.get_nexusvlan_binding(npb22.vlan, npb22.switch) - self.assertEqual(len(npb_v200), 1) - self._assert_equal(npb_v200[0], npb22) - - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.get_nexusvlan_binding(npb21.vlan, "dummySwitch") - - def test_nexusvmbinding_get(self): - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_to_db([npb11, npb21, npb22]) - - npb = nxdb.get_nexusvm_bindings(npb21.vlan, npb21.instance)[0] - self._assert_equal(npb, npb21) - npb = nxdb.get_nexusvm_bindings(npb22.vlan, npb22.instance)[0] - self._assert_equal(npb, npb22) - - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.get_nexusvm_bindings(npb21.vlan, "dummyInstance") - - def test_nexusportvlanswitchbinding_get(self): - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - self._add_to_db([npb11, npb21]) - - npb = nxdb.get_port_vlan_switch_binding( - npb11.port, npb11.vlan, npb11.switch) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb11) - - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.get_port_vlan_switch_binding( - npb21.port, npb21.vlan, "dummySwitch") - - def test_nexusportswitchbinding_get(self): - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100, switch='2.2.2.2') - npb22 = self._npb_test_obj(20, 200, switch='2.2.2.2') - self._add_to_db([npb11, npb21, npb22]) - - npb = nxdb.get_port_switch_bindings(npb11.port, npb11.switch) - self.assertEqual(len(npb), 1) - self._assert_equal(npb[0], npb11) - npb_all_p20 = nxdb.get_port_switch_bindings(npb21.port, npb21.switch) - self.assertEqual(len(npb_all_p20), 2) - - npb = nxdb.get_port_switch_bindings(npb21.port, "dummySwitch") - self.assertIsNone(npb) - - def test_nexussvibinding_get(self): - npbr1 = self._npb_test_obj('router', 100) - npb21 = self._npb_test_obj(20, 100) - self._add_to_db([npbr1, npb21]) - - npb_svi = nxdb.get_nexussvi_bindings() - self.assertEqual(len(npb_svi), 1) - self._assert_equal(npb_svi[0], npbr1) - - npbr2 = self._npb_test_obj('router', 200) - self._add_to_db([npbr2]) - npb_svi = nxdb.get_nexussvi_bindings() - self.assertEqual(len(npb_svi), 2) - - def test_nexussviswitch_find(self): - """Test Nexus switch selection for SVI placement.""" - # Configure 2 Nexus switches - nexus_switches = { - ('1.1.1.1', 'username'): 'admin', - ('1.1.1.1', 'password'): 'password1', - ('1.1.1.1', 'host1'): '1/1', - ('2.2.2.2', 'username'): 'admin', - ('2.2.2.2', 'password'): 'password2', - ('2.2.2.2', 'host2'): '1/1', - } - nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin() - nexus_plugin._client = mock.Mock() - nexus_plugin._client.nexus_switches = nexus_switches - - # Set the Cisco config module's first configured device IP address - # according to the preceding switch config - with mock.patch.object(config, 'first_device_ip', new='1.1.1.1'): - - # Enable round-robin mode with no SVIs configured on any of the - # Nexus switches (i.e. no entries in the SVI database). The - # plugin should select the first switch in the configuration. - config.CONF.set_override('svi_round_robin', True, 'CISCO') - switch_ip = nexus_plugin._find_switch_for_svi() - self.assertEqual(switch_ip, '1.1.1.1') - - # Keep round-robin mode enabled, and add entries to the SVI - # database. The plugin should select the switch with the least - # number of entries in the SVI database. - vlan = 100 - npbr11 = self._npb_test_obj('router', vlan, switch='1.1.1.1', - instance='instance11') - npbr12 = self._npb_test_obj('router', vlan, switch='1.1.1.1', - instance='instance12') - npbr21 = self._npb_test_obj('router', vlan, switch='2.2.2.2', - instance='instance21') - self._add_to_db([npbr11, npbr12, npbr21]) - switch_ip = nexus_plugin._find_switch_for_svi() - self.assertEqual(switch_ip, '2.2.2.2') - - # Disable round-robin mode. The plugin should select the - # first switch in the configuration. - config.CONF.clear_override('svi_round_robin', 'CISCO') - switch_ip = nexus_plugin._find_switch_for_svi() - self.assertEqual(switch_ip, '1.1.1.1') - - def test_nexusbinding_update(self): - npb11 = self._npb_test_obj(10, 100, switch='1.1.1.1', instance='test') - npb21 = self._npb_test_obj(20, 100, switch='1.1.1.1', instance='test') - self._add_to_db([npb11, npb21]) - - npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, '1.1.1.1') - self.assertEqual(len(npb_all_v100), 2) - - npb22 = self._npb_test_obj(20, 200, switch='1.1.1.1', instance='test') - npb = nxdb.update_nexusport_binding(npb21.port, 200) - self._assert_equal(npb, npb22) - - npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, '1.1.1.1') - self.assertEqual(len(npb_all_v100), 1) - self._assert_equal(npb_all_v100[0], npb11) - - npb = nxdb.update_nexusport_binding(npb21.port, 0) - self.assertIsNone(npb) - - npb33 = self._npb_test_obj(30, 300, switch='1.1.1.1', instance='test') - with testtools.ExpectedException(c_exc.NexusPortBindingNotFound): - nxdb.update_nexusport_binding(npb33.port, 200) diff --git a/neutron/tests/unit/cisco/test_nexus_plugin.py b/neutron/tests/unit/cisco/test_nexus_plugin.py deleted file mode 100644 index 4262b8187..000000000 --- a/neutron/tests/unit/cisco/test_nexus_plugin.py +++ /dev/null @@ -1,297 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation. -# -# 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 contextlib -import mock - -from oslo.config import cfg - -from neutron.extensions import providernet as provider -from neutron.openstack.common import importutils -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc -from neutron.plugins.cisco.common import config as cisco_config -from neutron.plugins.cisco.db import network_db_v2 as cdb -from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2 -from neutron.tests.unit import testlib_api - -NEXUS_IP_ADDRESS = '1.1.1.1' -HOSTNAME1 = 'testhost1' -HOSTNAME2 = 'testhost2' -HOSTNAME3 = 'testhost3' -INSTANCE1 = 'testvm1' -INSTANCE2 = 'testvm2' -INSTANCE3 = 'testvm3' -NEXUS_PORT1 = '1/10' -NEXUS_PORT2 = '1/20' -NEXUS_PC_IP_ADDRESS = '2.2.2.2' -NEXUS_PORTCHANNELS = 'portchannel:2' -PC_HOSTNAME = 'testpchost' -NEXUS_SSH_PORT = '22' -NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.' - 'cisco_nexus_network_driver_v2.CiscoNEXUSDriver') -NET_ATTRS = [const.NET_ID, - const.NET_NAME, - const.NET_VLAN_NAME, - const.NET_VLAN_ID] - - -class TestCiscoNexusPlugin(testlib_api.SqlTestCase): - - def setUp(self): - """Set up function.""" - super(TestCiscoNexusPlugin, self).setUp() - self.tenant_id = "test_tenant_cisco1" - self.net_name = "test_network_cisco1" - self.net_id = 7 - self.vlan_name = "q-" + str(self.net_id) + "vlan" - self.vlan_id = 267 - self.second_tenant_id = "test_tenant_2" - self.second_net_name = "test_network_cisco2" - self.second_net_id = 5 - self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan" - self.second_vlan_id = 265 - self._pchostname = PC_HOSTNAME - - self.attachment1 = { - const.TENANT_ID: self.tenant_id, - const.INSTANCE_ID: INSTANCE1, - const.HOST_NAME: HOSTNAME1, - } - self.attachment2 = { - const.TENANT_ID: self.second_tenant_id, - const.INSTANCE_ID: INSTANCE2, - const.HOST_NAME: HOSTNAME2, - } - self.attachment3 = { - const.TENANT_ID: self.second_tenant_id, - const.INSTANCE_ID: INSTANCE3, - const.HOST_NAME: HOSTNAME3, - } - self.network1 = { - const.NET_ID: self.net_id, - const.NET_NAME: self.net_name, - const.NET_VLAN_NAME: self.vlan_name, - const.NET_VLAN_ID: self.vlan_id, - } - self.network2 = { - const.NET_ID: self.second_net_id, - const.NET_NAME: self.second_net_name, - const.NET_VLAN_NAME: self.second_vlan_name, - const.NET_VLAN_ID: self.second_vlan_id, - } - self.network3 = { - const.NET_ID: 8, - const.NET_NAME: 'vpc_net', - const.NET_VLAN_NAME: 'q-268', - const.NET_VLAN_ID: '268', - } - self.delete_port_args_1 = [ - self.attachment1[const.INSTANCE_ID], - self.network1[const.NET_VLAN_ID], - ] - self.providernet = { - const.NET_ID: 9, - const.NET_NAME: 'pnet1', - const.NET_VLAN_NAME: 'p-300', - const.NET_VLAN_ID: 300, - provider.NETWORK_TYPE: 'vlan', - provider.PHYSICAL_NETWORK: self.net_name + '200:299', - provider.SEGMENTATION_ID: 300, - } - - def new_nexus_init(self): - self._client = importutils.import_object(NEXUS_DRIVER) - self._client.nexus_switches = { - (NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1, - (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT, - (NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2, - (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT, - (NEXUS_PC_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT, - } - self._nexus_switches = { - ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1, - ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2, - ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, HOSTNAME3): - NEXUS_PORTCHANNELS, - ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, 'ssh_port'): - NEXUS_SSH_PORT, - ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME3): - NEXUS_PORTCHANNELS, - ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT, - } - self._client.credentials = { - NEXUS_IP_ADDRESS: { - 'username': 'admin', - 'password': 'pass1234' - }, - NEXUS_PC_IP_ADDRESS: { - 'username': 'admin', - 'password': 'password' - }, - } - - # Use a mock netconf client - self.mock_ncclient = mock.Mock() - - with contextlib.nested( - mock.patch.dict('sys.modules', {'ncclient': self.mock_ncclient}), - mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, - '__init__', new=new_nexus_init) - ): - self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin() - - # Set the Cisco config module's first configured device IP address - # according to the preceding switch config. - mock.patch.object(cisco_config, 'first_device_ip', - new=NEXUS_IP_ADDRESS).start() - - def test_create_delete_networks(self): - """Tests creation of two new Virtual Networks.""" - new_net_dict = self._cisco_nexus_plugin.create_network( - self.network1, self.attachment1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.network1[attr]) - - expected_instance_id = self._cisco_nexus_plugin.delete_port( - INSTANCE1, self.vlan_id) - - self.assertEqual(expected_instance_id, INSTANCE1) - - new_net_dict = self._cisco_nexus_plugin.create_network( - self.network2, self.attachment1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.network2[attr]) - - expected_instance_id = self._cisco_nexus_plugin.delete_port( - INSTANCE1, self.second_vlan_id) - - self.assertEqual(expected_instance_id, INSTANCE1) - - def _create_delete_providernet(self, auto_create, auto_trunk): - cfg.CONF.set_override( - 'provider_vlan_auto_create', auto_create, 'CISCO') - cfg.CONF.set_override( - 'provider_vlan_auto_trunk', auto_trunk, 'CISCO') - with mock.patch.object(cdb, 'is_provider_vlan', - return_value=True) as mock_db: - # Create a provider network - new_net_dict = self._cisco_nexus_plugin.create_network( - self.providernet, self.attachment1) - self.assertEqual(mock_db.call_count, 1) - for attr in NET_ATTRS: - self.assertEqual(new_net_dict[attr], self.providernet[attr]) - # Delete the provider network - instance_id = self._cisco_nexus_plugin.delete_port( - self.attachment1[const.INSTANCE_ID], - self.providernet[const.NET_VLAN_ID]) - self.assertEqual(instance_id, - self.attachment1[const.INSTANCE_ID]) - - def test_create_delete_providernet(self): - self._create_delete_providernet(auto_create=True, auto_trunk=True) - - def test_create_delete_provider_vlan_network_cfg_auto_man(self): - self._create_delete_providernet(auto_create=True, auto_trunk=False) - - def test_create_delete_provider_vlan_network_cfg_man_auto(self): - self._create_delete_providernet(auto_create=False, auto_trunk=True) - - def test_create_delete_provider_vlan_network_cfg_man_man(self): - self._create_delete_providernet(auto_create=False, auto_trunk=False) - - def test_create_delete_network_portchannel(self): - """Tests creation of a network over a portchannel.""" - new_net_dict = self._cisco_nexus_plugin.create_network( - self.network3, self.attachment3) - self.assertEqual(new_net_dict[const.NET_ID], - self.network3[const.NET_ID]) - self.assertEqual(new_net_dict[const.NET_NAME], - self.network3[const.NET_NAME]) - self.assertEqual(new_net_dict[const.NET_VLAN_NAME], - self.network3[const.NET_VLAN_NAME]) - self.assertEqual(new_net_dict[const.NET_VLAN_ID], - self.network3[const.NET_VLAN_ID]) - - self._cisco_nexus_plugin.delete_port( - INSTANCE3, self.network3[const.NET_VLAN_ID] - ) - - def _add_router_interface(self): - """Add a router interface using fixed (canned) parameters.""" - vlan_name = self.vlan_name - vlan_id = self.vlan_id - gateway_ip = '10.0.0.1/24' - router_id = '00000R1' - subnet_id = '00001' - return self._cisco_nexus_plugin.add_router_interface( - vlan_name, vlan_id, subnet_id, gateway_ip, router_id) - - def _remove_router_interface(self): - """Remove a router interface created with _add_router_interface.""" - vlan_id = self.vlan_id - router_id = '00000R1' - return self._cisco_nexus_plugin.remove_router_interface(vlan_id, - router_id) - - def test_nexus_add_remove_router_interface(self): - """Tests addition of a router interface.""" - self.assertTrue(self._add_router_interface()) - self.assertEqual(self._remove_router_interface(), '00000R1') - - def test_nexus_dup_add_router_interface(self): - """Tests a duplicate add of a router interface.""" - self._add_router_interface() - try: - self.assertRaises( - cisco_exc.SubnetInterfacePresent, - self._add_router_interface) - finally: - self._remove_router_interface() - - def test_nexus_no_svi_switch_exception(self): - """Tests failure to find a Nexus switch for SVI placement.""" - # Clear the Nexus switches dictionary. - with mock.patch.dict(self._cisco_nexus_plugin._client.nexus_switches, - {}, clear=True): - # Clear the first Nexus IP address discovered in config - with mock.patch.object(cisco_config, 'first_device_ip', - new=None): - self.assertRaises(cisco_exc.NoNexusSviSwitch, - self._add_router_interface) - - def test_nexus_add_port_after_router_interface(self): - """Tests creating a port after a router interface. - - Test creating a port after an SVI router interface has - been created. Only a trunk call should be invoked and the - plugin should not attempt to recreate the vlan. - """ - self._add_router_interface() - # Create a network on the switch - self._cisco_nexus_plugin.create_network( - self.network1, self.attachment1) - - # Grab a list of all mock calls from ncclient - last_cfgs = (self.mock_ncclient.manager.connect.return_value. - edit_config.mock_calls) - - # The last ncclient call should be for trunking and the second - # to last call should be creating the SVI interface - last_cfg = last_cfgs[-1][2]['config'] - self.assertIn('allowed', last_cfg) - - slast_cfg = last_cfgs[-2][2]['config'] - self.assertIn('10.0.0.1/24', slast_cfg) diff --git a/neutron/tests/unit/cisco/test_plugin_model.py b/neutron/tests/unit/cisco/test_plugin_model.py deleted file mode 100755 index 7a99d30b5..000000000 --- a/neutron/tests/unit/cisco/test_plugin_model.py +++ /dev/null @@ -1,63 +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. - -import sys - -import mock - -from neutron import context -from neutron.plugins.cisco.common import cisco_constants as const -from neutron.plugins.cisco.common import config as cisco_config -from neutron.plugins.cisco.models import virt_phy_sw_v2 -from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2 -from neutron.tests.unit import testlib_api - - -class TestCiscoPluginModel(testlib_api.SqlTestCase): - - def setUp(self): - # Point config file to: neutron/tests/etc/neutron.conf.test - self.config_parse() - - super(TestCiscoPluginModel, self).setUp() - - def test_non_nexus_device_driver(self): - """Tests handling of an non-Nexus device driver being configured.""" - with mock.patch.dict(sys.modules, {'mock_driver': mock.Mock()}): - cisco_config.CONF.set_override('nexus_driver', - 'mock_driver.Non_Nexus_Driver', - 'CISCO') - # Plugin model instance should have is_nexus_plugin set to False - model = virt_phy_sw_v2.VirtualPhysicalSwitchModelV2() - self.assertFalse(model.is_nexus_plugin) - - # Model's _invoke_nexus_for_net_create should just return False - user_id = 'user_id' - tenant_id = 'tenant_id' - ctx = context.Context(user_id, tenant_id) - self.assertFalse(model._invoke_nexus_for_net_create( - ctx, tenant_id, net_id='net_id', - instance_id='instance_id', host_id='host_id')) - - def test_nexus_plugin_calls_ignored_if_plugin_not_loaded(self): - """Verifies Nexus plugin calls are ignored if plugin is not loaded.""" - cisco_config.CONF.set_override(const.NEXUS_PLUGIN, - None, 'CISCO_PLUGINS') - with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, - 'create_network') as mock_create_network: - model = virt_phy_sw_v2.VirtualPhysicalSwitchModelV2() - model._invoke_plugin_per_device(model, const.NEXUS_PLUGIN, - 'create_network') - self.assertFalse(mock_create_network.called)