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] + 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}) - 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")) - - 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)