From c7eb48feeae5f61068453195b68dc8f35526f74a Mon Sep 17 00:00:00 2001 From: Sam Betts Date: Wed, 4 Feb 2015 15:58:42 +0000 Subject: [PATCH] Cisco Nexus ML2 Vendor decomposition This adds a thin shim replacing the Cisco Nexus mech driver so that it may use the code now stored in the networking-cisco stackforge. The code for this driver that has been moved into the stackforge is also removed by this patch. Partial-Implements: blueprint core-vendor-decomposition Change-Id: Ib88fd121509e96be42fb0bd22422ccaa7d4eb0f1 Closes-Bug: 1419010 --- .../plugins/ml2/drivers/cisco/nexus/README | 19 - .../plugins/ml2/drivers/cisco/nexus/config.py | 65 -- .../ml2/drivers/cisco/nexus/constants.py | 24 - .../ml2/drivers/cisco/nexus/exceptions.py | 84 -- .../drivers/cisco/nexus/mech_cisco_nexus.py | 224 +---- .../ml2/drivers/cisco/nexus/nexus_db_v2.py | 144 --- .../cisco/nexus/nexus_network_driver.py | 203 ----- .../ml2/drivers/cisco/nexus/nexus_snippets.py | 195 ----- .../ml2/drivers/cisco/nexus/requirements.txt | 1 + .../unit/ml2/drivers/cisco/nexus/__init__.py | 0 .../drivers/cisco/nexus/test_cisco_config.py | 75 -- .../drivers/cisco/nexus/test_cisco_mech.py | 819 ------------------ .../drivers/cisco/nexus/test_cisco_nexus.py | 216 ----- .../cisco/nexus/test_cisco_nexus_db.py | 200 ----- 14 files changed, 4 insertions(+), 2265 deletions(-) delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/README delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/config.py delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/constants.py delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/exceptions.py delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/nexus_db_v2.py delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/nexus_network_driver.py delete mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/nexus_snippets.py create mode 100644 neutron/plugins/ml2/drivers/cisco/nexus/requirements.txt delete mode 100644 neutron/tests/unit/ml2/drivers/cisco/nexus/__init__.py delete mode 100644 neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_config.py delete mode 100644 neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py delete mode 100644 neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py delete mode 100644 neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus_db.py diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/README b/neutron/plugins/ml2/drivers/cisco/nexus/README deleted file mode 100644 index 21905b03692..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/README +++ /dev/null @@ -1,19 +0,0 @@ -Neutron ML2 Cisco Nexus Mechanism Driver README - - -Notes: - -The initial version of this driver supports only a single physical -network. - -For provider networks, extended configuration options are not -currently supported. - -This driver's database may have duplicate entries also found in the -core ML2 database. Since the Cisco Nexus DB code is a port from the -plugins/cisco implementation this duplication will remain until the -plugins/cisco code is deprecated. - - -For more details on using Cisco Nexus switches under ML2 please refer to: -http://wiki.openstack.org/wiki/Neutron/ML2/MechCiscoNexus diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/config.py b/neutron/plugins/ml2/drivers/cisco/nexus/config.py deleted file mode 100644 index ceeb60cf5a6..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/config.py +++ /dev/null @@ -1,65 +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. - -from oslo_config import cfg - - -ml2_cisco_opts = [ - cfg.StrOpt('vlan_name_prefix', default='q-', - help=_("VLAN Name prefix")), - cfg.BoolOpt('svi_round_robin', default=False, - help=_("Distribute SVI interfaces over all switches")), - cfg.StrOpt('managed_physical_network', - help=_("The physical network managed by the switches.")), -] - - -cfg.CONF.register_opts(ml2_cisco_opts, "ml2_cisco") - -# -# Format for ml2_conf_cisco.ini 'ml2_mech_cisco_nexus' is: -# {('', ''): '', ...} -# -# Example: -# {('1.1.1.1', 'username'): 'admin', -# ('1.1.1.1', 'password'): 'mySecretPassword', -# ('1.1.1.1', 'compute1'): '1/1', ...} -# - - -class ML2MechCiscoConfig(object): - """ML2 Mechanism Driver Cisco Configuration class.""" - nexus_dict = {} - - def __init__(self): - self._create_ml2_mech_device_cisco_dictionary() - - def _create_ml2_mech_device_cisco_dictionary(self): - """Create the ML2 device cisco dictionary. - - Read data from the ml2_conf_cisco.ini device supported sections. - """ - multi_parser = cfg.MultiConfigParser() - read_ok = multi_parser.read(cfg.CONF.config_file) - - if len(read_ok) != len(cfg.CONF.config_file): - raise cfg.Error(_("Some config files were not parsed properly")) - - 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() == 'ml2_mech_cisco_nexus': - for dev_key, value in parsed_file[parsed_item].items(): - self.nexus_dict[dev_ip, dev_key] = value[0] diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/constants.py b/neutron/plugins/ml2/drivers/cisco/nexus/constants.py deleted file mode 100644 index f3191b0b2d4..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/constants.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2011 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. -# - - -CREDENTIAL_USERNAME = 'user_name' -CREDENTIAL_PASSWORD = 'password' - -USERNAME = 'username' -PASSWORD = 'password' - -NETWORK_ADMIN = 'network_admin' diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/exceptions.py b/neutron/plugins/ml2/drivers/cisco/nexus/exceptions.py deleted file mode 100644 index 9302f30de28..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/exceptions.py +++ /dev/null @@ -1,84 +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. - -"""Exceptions used by Cisco Nexus ML2 mechanism driver.""" - -from neutron.common import exceptions - - -class CredentialNotFound(exceptions.NeutronException): - """Credential with this ID cannot be found.""" - message = _("Credential %(credential_id)s could not be found.") - - -class CredentialNameNotFound(exceptions.NeutronException): - """Credential Name could not be found.""" - message = _("Credential %(credential_name)s could not be found.") - - -class CredentialAlreadyExists(exceptions.NeutronException): - """Credential name already exists.""" - message = _("Credential %(credential_name)s already exists " - "for tenant %(tenant_id)s.") - - -class NexusComputeHostNotConfigured(exceptions.NeutronException): - """Connection to compute host is not configured.""" - message = _("Connection to %(host)s is not configured.") - - -class NexusConnectFailed(exceptions.NeutronException): - """Failed to connect to Nexus switch.""" - message = _("Unable to connect to Nexus %(nexus_host)s. Reason: %(exc)s.") - - -class NexusConfigFailed(exceptions.NeutronException): - """Failed to configure Nexus switch.""" - message = _("Failed to configure Nexus: %(config)s. Reason: %(exc)s.") - - -class NexusPortBindingNotFound(exceptions.NeutronException): - """NexusPort Binding is not present.""" - message = _("Nexus Port Binding (%(filters)s) is not present") - - def __init__(self, **kwargs): - filters = ','.join('%s=%s' % i for i in kwargs.items()) - super(NexusPortBindingNotFound, self).__init__(filters=filters) - - -class NexusMissingRequiredFields(exceptions.NeutronException): - """Missing required fields to configure nexus switch.""" - message = _("Missing required field(s) to configure nexus switch: " - "%(fields)s") - - -class NoNexusSviSwitch(exceptions.NeutronException): - """No usable nexus switch found.""" - message = _("No usable Nexus switch found to create SVI interface.") - - -class SubnetNotSpecified(exceptions.NeutronException): - """Subnet id not specified.""" - message = _("No subnet_id specified for router gateway.") - - -class SubnetInterfacePresent(exceptions.NeutronException): - """Subnet SVI interface already exists.""" - message = _("Subnet %(subnet_id)s has an interface on %(router_id)s.") - - -class PortIdForNexusSvi(exceptions.NeutronException): - """Port Id specified for Nexus SVI.""" - message = _('Nexus hardware router gateway only uses Subnet Ids.') diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py b/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py index 35c86040d07..fac9503df8c 100644 --- a/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py +++ b/neutron/plugins/ml2/drivers/cisco/nexus/mech_cisco_nexus.py @@ -17,226 +17,8 @@ ML2 Mechanism Driver for Cisco Nexus platforms. """ -from oslo_config import cfg - -from neutron.common import constants as n_const -from neutron.extensions import portbindings -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as p_const -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2.drivers.cisco.nexus import config as conf -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as excep -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_db_v2 as nxos_db -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_network_driver - -LOG = logging.getLogger(__name__) +from networking_cisco.plugins.ml2.drivers.cisco.nexus import mech_cisco_nexus -class CiscoNexusMechanismDriver(api.MechanismDriver): - - """Cisco Nexus ML2 Mechanism Driver.""" - - def initialize(self): - # Create ML2 device dictionary from ml2_conf.ini entries. - conf.ML2MechCiscoConfig() - - # Extract configuration parameters from the configuration file. - self._nexus_switches = conf.ML2MechCiscoConfig.nexus_dict - LOG.debug("nexus_switches found = %s", self._nexus_switches) - - self.driver = nexus_network_driver.CiscoNexusDriver() - - def _valid_network_segment(self, segment): - return (cfg.CONF.ml2_cisco.managed_physical_network is None or - cfg.CONF.ml2_cisco.managed_physical_network == - segment[api.PHYSICAL_NETWORK]) - - def _get_vlanid(self, segment): - if (segment and segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN and - self._valid_network_segment(segment)): - return segment.get(api.SEGMENTATION_ID) - - def _is_deviceowner_compute(self, port): - return port['device_owner'].startswith('compute') - - def _is_status_active(self, port): - return port['status'] == n_const.PORT_STATUS_ACTIVE - - def _get_switch_info(self, host_id): - host_connections = [] - for switch_ip, attr in self._nexus_switches: - if str(attr) == str(host_id): - for port_id in ( - self._nexus_switches[switch_ip, attr].split(',')): - if ':' in port_id: - intf_type, port = port_id.split(':') - else: - intf_type, port = 'ethernet', port_id - host_connections.append((switch_ip, intf_type, port)) - - if host_connections: - return host_connections - else: - raise excep.NexusComputeHostNotConfigured(host=host_id) - - def _configure_nxos_db(self, vlan_id, device_id, host_id): - """Create the nexus database entry. - - Called during update precommit port event. - """ - host_connections = self._get_switch_info(host_id) - for switch_ip, intf_type, nexus_port in host_connections: - port_id = '%s:%s' % (intf_type, nexus_port) - nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip, - device_id) - - def _configure_switch_entry(self, vlan_id, device_id, host_id): - """Create a nexus switch entry. - - if needed, create a VLAN in the appropriate switch/port and - configure the appropriate interfaces for this VLAN. - - Called during update postcommit port event. - """ - vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id) - host_connections = self._get_switch_info(host_id) - - # (nexus_port,switch_ip) will be unique in each iteration. - # But switch_ip will repeat if host has >1 connection to same switch. - # So track which switch_ips already have vlan created in this loop. - vlan_already_created = [] - for switch_ip, intf_type, nexus_port in host_connections: - - # The VLAN needs to be created on the switch if no other - # instance has been placed in this VLAN on a different host - # attached to this switch. Search the existing bindings in the - # database. If all the instance_id in the database match the - # current device_id, then create the VLAN, but only once per - # switch_ip. Otherwise, just trunk. - all_bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - previous_bindings = [row for row in all_bindings - if row.instance_id != device_id] - if previous_bindings or (switch_ip in vlan_already_created): - LOG.debug("Nexus: trunk vlan %s", vlan_name) - self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id, - intf_type, nexus_port) - else: - vlan_already_created.append(switch_ip) - LOG.debug("Nexus: create & trunk vlan %s", vlan_name) - self.driver.create_and_trunk_vlan( - switch_ip, vlan_id, vlan_name, intf_type, nexus_port) - - def _delete_nxos_db(self, vlan_id, device_id, host_id): - """Delete the nexus database entry. - - Called during delete precommit port event. - """ - try: - rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id) - for row in rows: - nxos_db.remove_nexusport_binding( - row.port_id, row.vlan_id, row.switch_ip, row.instance_id) - except excep.NexusPortBindingNotFound: - return - - def _delete_switch_entry(self, vlan_id, device_id, host_id): - """Delete the nexus switch entry. - - By accessing the current db entries determine if switch - configuration can be removed. - - Called during update postcommit port event. - """ - host_connections = self._get_switch_info(host_id) - - # (nexus_port,switch_ip) will be unique in each iteration. - # But switch_ip will repeat if host has >1 connection to same switch. - # So track which switch_ips already have vlan removed in this loop. - vlan_already_removed = [] - for switch_ip, intf_type, nexus_port in host_connections: - - # if there are no remaining db entries using this vlan on this - # nexus switch port then remove vlan from the switchport trunk. - port_id = '%s:%s' % (intf_type, nexus_port) - try: - nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, - switch_ip) - except excep.NexusPortBindingNotFound: - self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id, - intf_type, nexus_port) - - # if there are no remaining db entries using this vlan on this - # nexus switch then remove the vlan. - try: - nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) - except excep.NexusPortBindingNotFound: - - # Do not perform a second time on same switch - if switch_ip not in vlan_already_removed: - self.driver.delete_vlan(switch_ip, vlan_id) - vlan_already_removed.append(switch_ip) - - def _is_vm_migration(self, context): - if (not context.bottom_bound_segment and - context.original_bottom_bound_segment): - return context.host != context.original_host - - def _port_action(self, port, segment, func): - """Verify configuration and then process event.""" - device_id = port.get('device_id') - host_id = port.get(portbindings.HOST_ID) - vlan_id = self._get_vlanid(segment) - - if vlan_id and device_id and host_id: - func(vlan_id, device_id, host_id) - else: - fields = "vlan_id " if not vlan_id else "" - fields += "device_id " if not device_id else "" - fields += "host_id" if not host_id else "" - raise excep.NexusMissingRequiredFields(fields=fields) - - def update_port_precommit(self, context): - """Update port pre-database transaction commit event.""" - - # if VM migration is occurring then remove previous database entry - # else process update event. - if self._is_vm_migration(context): - self._port_action(context.original, - context.original_bottom_bound_segment, - self._delete_nxos_db) - else: - if (self._is_deviceowner_compute(context.current) and - self._is_status_active(context.current)): - self._port_action(context.current, - context.bottom_bound_segment, - self._configure_nxos_db) - - def update_port_postcommit(self, context): - """Update port non-database commit event.""" - - # if VM migration is occurring then remove previous nexus switch entry - # else process update event. - if self._is_vm_migration(context): - self._port_action(context.original, - context.original_bottom_bound_segment, - self._delete_switch_entry) - else: - if (self._is_deviceowner_compute(context.current) and - self._is_status_active(context.current)): - self._port_action(context.current, - context.bottom_bound_segment, - self._configure_switch_entry) - - def delete_port_precommit(self, context): - """Delete port pre-database commit event.""" - if self._is_deviceowner_compute(context.current): - self._port_action(context.current, - context.bottom_bound_segment, - self._delete_nxos_db) - - def delete_port_postcommit(self, context): - """Delete port non-database commit event.""" - if self._is_deviceowner_compute(context.current): - self._port_action(context.current, - context.bottom_bound_segment, - self._delete_switch_entry) +class CiscoNexusMechanismDriver(mech_cisco_nexus.CiscoNexusMechanismDriver): + pass diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/nexus_db_v2.py b/neutron/plugins/ml2/drivers/cisco/nexus/nexus_db_v2.py deleted file mode 100644 index 467434c1cd2..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/nexus_db_v2.py +++ /dev/null @@ -1,144 +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 sqlalchemy.orm.exc as sa_exc - -import neutron.db.api as db -from neutron.i18n import _LW -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as c_exc -from neutron.plugins.ml2.drivers.cisco.nexus 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(_LW("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_bindings() called") - return _lookup_all_nexus_bindings(instance_id=instance_id, - vlan_id=vlan_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 _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/ml2/drivers/cisco/nexus/nexus_network_driver.py b/neutron/plugins/ml2/drivers/cisco/nexus/nexus_network_driver.py deleted file mode 100644 index 68b2a26f4db..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/nexus_network_driver.py +++ /dev/null @@ -1,203 +0,0 @@ -# Copyright 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. - -""" -Implements a Nexus-OS NETCONF over SSHv2 API Client -""" - -from oslo_utils import excutils -from oslo_utils import importutils - -from neutron.openstack.common import log as logging -from neutron.plugins.ml2.drivers.cisco.nexus import config as conf -from neutron.plugins.ml2.drivers.cisco.nexus import constants as const -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as cexc -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_snippets as snipp - -LOG = logging.getLogger(__name__) - - -class CiscoNexusDriver(object): - """Nexus Driver Main Class.""" - def __init__(self): - self.ncclient = None - self.nexus_switches = conf.ML2MechCiscoConfig.nexus_dict - self.connections = {} - - def _import_ncclient(self): - """Import the NETCONF client (ncclient) module. - - The ncclient module is not installed as part of the normal Neutron - distributions. It is imported dynamically in this module so that - the import can be mocked, allowing unit testing without requiring - the installation of ncclient. - - """ - return importutils.import_module('ncclient.manager') - - 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 - - :returns None: if config was edited successfully - Exception object: if _edit_config() encountered an exception - containing one of allowed_exc_strs - - :raises: NexusConfigFailed: if _edit_config() encountered an exception - not containing one of allowed_exc_strs - - """ - if not allowed_exc_strs: - allowed_exc_strs = [] - mgr = self.nxos_connect(nexus_host) - try: - LOG.debug("NexusDriver config: %s", config) - mgr.edit_config(target=target, config=config) - except Exception as e: - for exc_str in allowed_exc_strs: - if exc_str in str(e): - return e - # Raise a Neutron exception. Include a description of - # the original ncclient exception. - raise cexc.NexusConfigFailed(config=config, exc=e) - - 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] - - if not self.ncclient: - self.ncclient = self._import_ncclient() - nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port']) - nexus_user = self.nexus_switches[nexus_host, const.USERNAME] - nexus_password = self.nexus_switches[nexus_host, const.PASSWORD] - try: - try: - # With new ncclient version, we can pass device_params... - man = self.ncclient.connect(host=nexus_host, - port=nexus_ssh_port, - username=nexus_user, - password=nexus_password, - device_params={"name": "nexus"}) - except TypeError: - # ... but if that causes an error, we appear to have the old - # ncclient installed, which doesn't understand this parameter. - man = self.ncclient.connect(host=nexus_host, - port=nexus_ssh_port, - username=nexus_user, - password=nexus_password) - except Exception as e: - # Raise a Neutron exception. Include a description of - # the original ncclient exception. - raise cexc.NexusConnectFailed(nexus_host=nexus_host, exc=e) - - self.connections[nexus_host] = man - return self.connections[nexus_host] - - def create_xml_snippet(self, customized_config): - """Create XML snippet. - - Creates the Proper XML structure for the Nexus Switch Configuration. - """ - conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (customized_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). - for snippet in [snipp.CMD_VLAN_ACTIVE_SNIPPET, - snipp.CMD_VLAN_NO_SHUTDOWN_SNIPPET]: - 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 build_intf_confstr(self, snippet, intf_type, interface, vlanid): - """Build the VLAN config string xml snippet to be used.""" - confstr = snippet % (intf_type, interface, vlanid, intf_type) - confstr = self.create_xml_snippet(confstr) - LOG.debug("NexusDriver: %s", confstr) - return confstr - - def enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type, - interface): - """Enable a VLAN on a trunk interface.""" - # Configure a new VLAN into the interface using 'ADD' keyword - confstr = self.build_intf_confstr( - snippet=snipp.CMD_INT_VLAN_ADD_SNIPPET, - intf_type=intf_type, - interface=interface, - vlanid=vlanid - ) - exc_str = ["switchport trunk allowed vlan list is empty"] - ret_exc = self._edit_config(nexus_host, target='running', - config=confstr, - allowed_exc_strs=exc_str) - if ret_exc: - # If no switchports have been configured on the switch - # before the new 'ADD', configure the VLAN into the - # interface without the keyword so as to create a vlan list - confstr = self.build_intf_confstr( - snippet=snipp.CMD_INT_VLAN_SNIPPET, - intf_type=intf_type, - interface=interface, - vlanid=vlanid - ) - self._edit_config(nexus_host, target='running', - config=confstr) - - def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type, - interface): - """Disable a VLAN on a trunk interface.""" - confstr = (snipp.CMD_NO_VLAN_INT_SNIPPET % - (intf_type, interface, vlanid, intf_type)) - confstr = self.create_xml_snippet(confstr) - self._edit_config(nexus_host, target='running', config=confstr) - - def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name, - intf_type, 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, intf_type, - nexus_port) diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/nexus_snippets.py b/neutron/plugins/ml2/drivers/cisco/nexus/nexus_snippets.py deleted file mode 100644 index 99f38251bfc..00000000000 --- a/neutron/plugins/ml2/drivers/cisco/nexus/nexus_snippets.py +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright 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. - - -""" -Cisco 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_PORT_TRUNK = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - - - - -""" - -CMD_NO_SWITCHPORT = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - -""" - -CMD_NO_VLAN_INT_SNIPPET = """ - - <%s> - %s - <__XML__MODE_if-ethernet-switch> - - - - - - - %s - - - - - - - - -""" - -CMD_VLAN_SVI_SNIPPET = """ - - - %s - <__XML__MODE_vlan> - - - - -
-
%s
-
-
- -
-
-""" - -CMD_NO_VLAN_SVI_SNIPPET = """ - - - - %s - - - -""" diff --git a/neutron/plugins/ml2/drivers/cisco/nexus/requirements.txt b/neutron/plugins/ml2/drivers/cisco/nexus/requirements.txt new file mode 100644 index 00000000000..ef631a3f2b9 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/nexus/requirements.txt @@ -0,0 +1 @@ +networking-cisco diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/__init__.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_config.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_config.py deleted file mode 100644 index 01f249517d4..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_config.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2014 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. - -import mock -from oslo_config import cfg - -from neutron.plugins.ml2.drivers.cisco.nexus import config as cisco_config -from neutron.tests import base - - -class TestCiscoNexusPluginConfig(base.BaseTestCase): - - def setUp(self): - 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.ML2MechCiscoConfig) - - def test_create_device_dictionary(self): - """Test creation of the device dictionary based on nexus config.""" - test_config = { - 'ml2_mech_cisco_nexus:1.1.1.1': { - 'username': ['admin'], - 'password': ['mySecretPassword'], - 'ssh_port': [22], - 'compute1': ['1/1'], - 'compute2': ['1/2'], - 'compute5': ['1/3,1/4'] - }, - 'ml2_mech_cisco_nexus:2.2.2.2': { - 'username': ['admin'], - 'password': ['mySecretPassword'], - 'ssh_port': [22], - 'compute3': ['1/1'], - 'compute4': ['1/2'], - 'compute5': ['portchannel:20,portchannel:30'] - }, - } - expected_dev_dict = { - ('1.1.1.1', 'username'): 'admin', - ('1.1.1.1', 'password'): 'mySecretPassword', - ('1.1.1.1', 'ssh_port'): 22, - ('1.1.1.1', 'compute1'): '1/1', - ('1.1.1.1', 'compute2'): '1/2', - ('1.1.1.1', 'compute5'): '1/3,1/4', - ('2.2.2.2', 'username'): 'admin', - ('2.2.2.2', 'password'): 'mySecretPassword', - ('2.2.2.2', 'ssh_port'): 22, - ('2.2.2.2', 'compute3'): '1/1', - ('2.2.2.2', 'compute4'): '1/2', - ('2.2.2.2', 'compute5'): 'portchannel:20,portchannel:30', - } - 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.ML2MechCiscoConfig() - self.assertEqual(expected_dev_dict, - cisco_config.ML2MechCiscoConfig.nexus_dict) diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py deleted file mode 100644 index 329209a8949..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_mech.py +++ /dev/null @@ -1,819 +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 - -import webob.exc as wexc - -from neutron.api.v2 import base -from neutron import context -from neutron.extensions import portbindings -from neutron import manager -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as p_const -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2 import driver_context -from neutron.plugins.ml2.drivers.cisco.nexus import config as cisco_config -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as c_exc -from neutron.plugins.ml2.drivers.cisco.nexus import mech_cisco_nexus -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_db_v2 -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_network_driver -from neutron.tests.unit.ml2 import test_ml2_plugin - - -LOG = logging.getLogger(__name__) -PHYS_NET = 'physnet1' -COMP_HOST_NAME = 'testhost' -COMP_HOST_NAME_2 = 'testhost_2' -VLAN_START = 1000 -VLAN_END = 1100 -NEXUS_IP_ADDR = '1.1.1.1' -NETWORK_NAME = 'test_network' -NETWORK_NAME_2 = 'test_network_2' -NEXUS_INTERFACE = '1/1' -NEXUS_INTERFACE_2 = '1/2' -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' -BOUND_SEGMENT1 = {api.NETWORK_TYPE: p_const.TYPE_VLAN, - api.PHYSICAL_NETWORK: PHYS_NET, - api.SEGMENTATION_ID: VLAN_START} -BOUND_SEGMENT2 = {api.NETWORK_TYPE: p_const.TYPE_VLAN, - api.PHYSICAL_NETWORK: PHYS_NET, - api.SEGMENTATION_ID: VLAN_START + 1} - - -class CiscoML2MechanismTestCase(test_ml2_plugin.Ml2PluginV2TestCase): - _mechanism_drivers = ['cisco_nexus'] - - def setUp(self): - """Configure for end-to-end neutron testing using a mock ncclient. - - This setup includes: - - Configure the ML2 plugin to use VLANs in the range of 1000-1100. - - Configure the Cisco mechanism driver to use an imaginary switch - at NEXUS_IP_ADDR. - - Create a mock NETCONF client (ncclient) for the Cisco mechanism - driver - - """ - - # Configure the Cisco Nexus mechanism driver - nexus_config = { - (NEXUS_IP_ADDR, 'username'): 'admin', - (NEXUS_IP_ADDR, 'password'): 'mySecretPassword', - (NEXUS_IP_ADDR, 'ssh_port'): 22, - (NEXUS_IP_ADDR, COMP_HOST_NAME): NEXUS_INTERFACE, - (NEXUS_IP_ADDR, COMP_HOST_NAME_2): NEXUS_INTERFACE_2} - nexus_patch = mock.patch.dict( - cisco_config.ML2MechCiscoConfig.nexus_dict, - nexus_config) - nexus_patch.start() - self.addCleanup(nexus_patch.stop) - - # The NETCONF client module is not included in the DevStack - # distribution, so mock this module for unit testing. - self.mock_ncclient = mock.Mock() - mock.patch.object(nexus_network_driver.CiscoNexusDriver, - '_import_ncclient', - return_value=self.mock_ncclient).start() - - # Mock port context values for bound_segments and 'status'. - self.mock_bound_segment = mock.patch.object( - driver_context.PortContext, - 'bottom_bound_segment', - new_callable=mock.PropertyMock).start() - self.mock_bound_segment.return_value = BOUND_SEGMENT1 - - self.mock_original_bound_segment = mock.patch.object( - driver_context.PortContext, - 'original_bottom_bound_segment', - new_callable=mock.PropertyMock).start() - self.mock_original_bound_segment.return_value = None - - # Use _is_status_active method to determine bind state. - def _mock_check_bind_state(port_context): - if (port_context[portbindings.VIF_TYPE] != - portbindings.VIF_TYPE_UNBOUND): - return True - else: - return False - - self.mock_status = mock.patch.object( - mech_cisco_nexus.CiscoNexusMechanismDriver, - '_is_status_active').start() - self.mock_status.side_effect = _mock_check_bind_state - - super(CiscoML2MechanismTestCase, self).setUp() - - self.port_create_status = 'DOWN' - - def _create_deviceowner_mock(self): - # Mock deviceowner method for UT's that expect update precommit - # failures. This allows control of delete_port_pre/postcommit() - # actions. - mock_deviceowner = mock.patch.object( - mech_cisco_nexus.CiscoNexusMechanismDriver, - '_is_deviceowner_compute').start() - mock_deviceowner.return_value = False - self.addCleanup(mock_deviceowner.stop) - - @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.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): - """Confirm last config sent to Nexus contains specified keywords.""" - last_cfg = (self.mock_ncclient.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, - first_vlan_addition=False): - """Confirm if VLAN was configured or not.""" - vlan_created = self._is_in_nexus_cfg(['vlan', 'vlan-name']) - add_appears = self._is_in_last_nexus_cfg(['add']) - # The first VLAN being configured should be done without the - # ADD keyword. Thereafter additional VLANs to be configured - # should be done with the ADD keyword. - add_keyword_expected = not first_vlan_addition - 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_deleted = self._is_in_last_nexus_cfg( - ['no', 'vlan', 'vlan-id-create-delete']) - return (self._is_in_nexus_cfg(['allowed', 'vlan', 'remove']) and - vlan_deleted == vlan_deletion_expected) - - -class TestCiscoBasicGet(CiscoML2MechanismTestCase, - test_ml2_plugin.TestMl2BasicGet): - - pass - - -class TestCiscoV2HTTPResponse(CiscoML2MechanismTestCase, - test_ml2_plugin.TestMl2V2HTTPResponse): - - pass - - -class TestCiscoPortsV2(CiscoML2MechanismTestCase, - test_ml2_plugin.TestMl2PortsV2): - - @contextlib.contextmanager - def _create_resources(self, name=NETWORK_NAME, cidr=CIDR_1, - device_id=DEVICE_ID_1, - host_id=COMP_HOST_NAME, - expected_failure=False): - """Create network, subnet, and port resources for test cases. - - Create a network, subnet, port and then update the 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 device_id: Device ID to use for port to be created/updated. - :param host_id: Host ID to use for port create/update. - :param expected_failure: Set to True when an update_port_precommit - failure is expected. Results in no actions being taken in - delete_port_pre/postcommit() methods. - """ - with self.network(name=name) as network: - with self.subnet(network=network, cidr=cidr) as subnet: - with self.port(subnet=subnet, cidr=cidr) as port: - - data = {'port': {portbindings.HOST_ID: host_id, - 'device_id': device_id, - 'device_owner': DEVICE_OWNER, - 'admin_state_up': True}} - req = self.new_update_request('ports', data, - port['port']['id']) - yield req.get_response(self.api) - if expected_failure: - self._create_deviceowner_mock() - self._delete('ports', port['port']['id']) - self._delete('networks', network['network']['id']) - - 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) - - def _mock_config_first_trunk(self): - """Mock the behavior for the first VLAN addition. - - When the first VLAN is being added to the interface the usage of - the ADD keyword should raise an exception specifying that the ADD - keyword cannot be used till a VLAN list is created and to create - a VLAN list the configuration should not contain the ADD keyword. - - """ - config = "switchport trunk allowed vlan add" - exc = Exception("switchport trunk allowed vlan list is empty, " - "please specify a list using " - "'switchport trunk allowed vlan X' " - "before using the add option") - return (self._patch_ncclient( - 'connect.return_value.edit_config.side_effect', - self._config_dependent_side_effect(config, exc))) - - def test_create_ports_bulk_emulated_plugin_failure(self): - real_has_attr = hasattr - - #ensures the API chooses 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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_port - with mock.patch.object(plugin_obj, - '_create_port_db') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._fail_second_call(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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_port - with mock.patch.object(plugin_obj, - '_create_port_db') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._fail_second_call(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_on_same_host(self): - """Verify the syntax of the command to enable a vlan on an intf. - - Test of the following ml2_conf_cisco_ini config: - [ml2_mech_cisco_nexus:1.1.1.1] - Resource A on host=COMP_HOST_NAME with vlan_id = 1000 - Resource B on host=COMP_HOST_NAME with vlan_id = 1001 - - Confirm that when configuring the first VLAN on a Nexus interface, - the final command string sent to the switch does not contain the - keyword 'add'. The initial attempt will contain 'add' but when - the switch rejects it, the re-attempt shouldn't contain the 'add'. - - Confirm that for the second VLAN configured on a Nexus interface, - the command string sent to the switch contains the keyword 'add' - since it is on the same host. - - """ - # First vlan should be configured without 'add' keyword and an - # exception should be raised when it is done with the 'add' - # thereby triggering a re-attempt without the 'add'. - with self._mock_config_first_trunk(): - with self._create_resources(): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - first_vlan_addition=True)) - self.mock_ncclient.reset_mock() - self.mock_bound_segment.return_value = BOUND_SEGMENT2 - - # Second vlan should be configured with the 'add' keyword - # when on first host. - with(self._patch_ncclient( - 'connect.return_value.edit_config.side_effect', - None)): - with self._create_resources(name=NETWORK_NAME_2, - device_id=DEVICE_ID_2, - cidr=CIDR_2, - host_id=COMP_HOST_NAME): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - first_vlan_addition=False - )) - - # Return to first segment for delete port calls. - self.mock_bound_segment.return_value = BOUND_SEGMENT1 - - def test_nexus_enable_vlan_cmd_on_different_hosts(self): - """Verify the syntax of the command to enable a vlan on an intf. - - Test of the following ml2_conf_cisco_ini config: - [ml2_mech_cisco_nexus:1.1.1.1] - Resource A on host=COMP_HOST_NAME with vlan_id = 1000 - Resource B on host=COMP_HOST_NAME_2 with vlan_id = 1001 - - Confirm that when configuring the first VLAN on a Nexus interface, - the final command string sent to the switch does not contain the - keyword 'add'. The initial attempt will contain 'add' but when - the switch rejects it, the re-attempt shouldn't contain the 'add'. - - Confirm that for the second VLAN configured on a Nexus interface, - the command string sent to the switch does not contain the - keyword 'add' since it is on a different host. - - """ - # First vlan should be configured without 'add' keyword and an - # exception should be raised when it is done with the 'add' - # thereby triggering a re-attempt without the 'add'. - with self._mock_config_first_trunk(): - with self._create_resources(): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - first_vlan_addition=True)) - self.mock_ncclient.reset_mock() - self.mock_bound_segment.return_value = BOUND_SEGMENT2 - - # Second vlan should be configured without the 'add' keyword - # when on second host. - with self._create_resources(name=NETWORK_NAME_2, - device_id=DEVICE_ID_2, - cidr=CIDR_2, - host_id=COMP_HOST_NAME_2): - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=True, - first_vlan_addition=True - )) - - # Return to first segment for delete port calls. - self.mock_bound_segment.return_value = BOUND_SEGMENT1 - - def test_ncclient_version_detect(self): - """Test ability to handle connection to old and new-style ncclient. - - We used to require a custom version of the ncclient library. However, - recent contributions to the ncclient make this unnecessary. Our - driver was modified to be able to establish a connection via both - the old and new type of ncclient. - - The new style ncclient.connect() function takes one additional - parameter. - - The ML2 driver uses this to detect whether we are dealing with an - old or new ncclient installation. - - """ - # The code we are exercising calls connect() twice, if there is a - # TypeError on the first call (if the old ncclient is installed). - # The second call should succeed. That's what we are simulating here. - orig_connect_return_val = self.mock_ncclient.connect.return_value - with self._patch_ncclient('connect.side_effect', - [TypeError, orig_connect_return_val]): - with self._create_resources() as result: - self.assertEqual(result.status_int, - wexc.HTTPOk.code) - - def test_ncclient_fail_on_second_connect(self): - """Test that other errors during connect() sequences are still handled. - - If the old ncclient is installed, we expect to get a TypeError first, - but should still handle other errors in the usual way, whether they - appear on the first or second call to connect(). - - """ - with self._patch_ncclient('connect.side_effect', - [TypeError, IOError]): - with self._create_resources() as result: - self._assertExpectedHTTP(result.status_int, - c_exc.NexusConnectFailed) - - 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('connect.side_effect', - AttributeError): - with self._create_resources() as result: - self._assertExpectedHTTP(result.status_int, - c_exc.NexusConnectFailed) - - 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): - with self.port(subnet=subnet, fmt=self.fmt) as port: - data = {'port': {portbindings.HOST_ID: comp_host_name, - 'device_id': device_id, - 'device_owner': DEVICE_OWNER, - 'admin_state_up': True}} - req = self.new_update_request('ports', data, - port['port']['id']) - req.get_response(self.api) - self.assertTrue(self._is_vlan_configured( - vlan_creation_expected=vlan_creation_expected, - first_vlan_addition=True)) - self.mock_ncclient.reset_mock() - yield - self._delete('ports', port['port']['id']) - - # Create network and subnet - with self._mock_config_first_trunk(): - 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_vm_migration(self): - """Verify VM (live) migration. - - Simulate the following: - Nova informs neutron of live-migration with port-update(new host). - This should trigger two update_port_pre/postcommit() calls. - - The first one should only change the current host_id and remove the - binding resulting in the mechanism drivers receiving: - PortContext.original['binding:host_id']: previous value - PortContext.original_bottom_bound_segment: previous value - PortContext.current['binding:host_id']: current (new) value - PortContext.bottom_bound_segment: None - - The second one binds the new host resulting in the mechanism - drivers receiving: - PortContext.original['binding:host_id']: previous value - PortContext.original_bottom_bound_segment: None - PortContext.current['binding:host_id']: previous value - PortContext.bottom_bound_segment: new value - """ - - # Create network, subnet and port. - with self._create_resources() as result: - # Verify initial database entry. - # Use port_id to verify that 1st host name was used. - binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START, - DEVICE_ID_1)[0] - intf_type, nexus_port = binding.port_id.split(':') - self.assertEqual(nexus_port, NEXUS_INTERFACE) - - port = self.deserialize(self.fmt, result) - port_id = port['port']['id'] - - # Trigger update event to unbind segment. - # Results in port being deleted from nexus DB and switch. - data = {'port': {portbindings.HOST_ID: COMP_HOST_NAME_2}} - self.mock_bound_segment.return_value = None - self.mock_original_bound_segment.return_value = BOUND_SEGMENT1 - self.new_update_request('ports', data, - port_id).get_response(self.api) - - # Verify that port entry has been deleted. - self.assertRaises(c_exc.NexusPortBindingNotFound, - nexus_db_v2.get_nexusvm_bindings, - VLAN_START, DEVICE_ID_1) - - # Trigger update event to bind segment with new host. - self.mock_bound_segment.return_value = BOUND_SEGMENT1 - self.mock_original_bound_segment.return_value = None - self.new_update_request('ports', data, - port_id).get_response(self.api) - - # Verify that port entry has been added using new host name. - # Use port_id to verify that 2nd host name was used. - binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START, - DEVICE_ID_1)[0] - intf_type, nexus_port = binding.port_id.split(':') - self.assertEqual(nexus_port, NEXUS_INTERFACE_2) - - 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( - 'connect.return_value.edit_config.side_effect', - AttributeError): - with self._create_resources() as result: - self._assertExpectedHTTP(result.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. - - """ - def mock_edit_config_a(target, config): - if all(word in config for word in ['state', 'active']): - raise Exception("Can't modify state for extended") - - with self._patch_ncclient( - 'connect.return_value.edit_config.side_effect', - mock_edit_config_a): - with self._create_resources() as result: - self.assertEqual(result.status_int, wexc.HTTPOk.code) - - def mock_edit_config_b(target, config): - if all(word in config for word in ['no', 'shutdown']): - raise Exception("Command is only allowed on VLAN") - - with self._patch_ncclient( - 'connect.return_value.edit_config.side_effect', - mock_edit_config_b): - with self._create_resources() as result: - self.assertEqual(result.status_int, wexc.HTTPOk.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( - 'connect.return_value.edit_config.side_effect', - self._config_dependent_side_effect(config, ValueError)): - with self._create_resources() as result: - # 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(result.status_int, - c_exc.NexusConfigFailed) - - def test_nexus_host_not_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_resources(host_id='fake_host', - expected_failure=True) as result: - self._assertExpectedHTTP(result.status_int, - c_exc.NexusComputeHostNotConfigured) - - def test_nexus_missing_fields(self): - """Test handling of a NexusMissingRequiredFields exception. - - Test the Cisco NexusMissingRequiredFields exception by using - empty device_id value during port creation. - - """ - with self._create_resources(device_id='', - expected_failure=True) as result: - self._assertExpectedHTTP(result.status_int, - c_exc.NexusMissingRequiredFields) - - def test_update_port_mac(self): - # REVISIT: test passes, but is back-end OK? - host_arg = { - portbindings.HOST_ID: COMP_HOST_NAME, - 'device_id': DEVICE_ID_1, - } - arg_list = (portbindings.HOST_ID, 'device_id',) - self.check_update_port_mac(host_arg=host_arg, arg_list=arg_list) - - -class TestCiscoNetworksV2(CiscoML2MechanismTestCase, - test_ml2_plugin.TestMl2NetworksV2): - - 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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_network - #ensures the API choose the emulation code path - with mock.patch('__builtin__.hasattr', - new=fakehasattr): - with mock.patch.object(plugin_obj, - '_create_network_db') as patched_plugin: - def side_effect(*args, **kwargs): - return self._fail_second_call(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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_network - with mock.patch.object(plugin_obj, - '_create_network_db') as patched_plugin: - - def side_effect(*args, **kwargs): - return self._fail_second_call(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) - - -class TestCiscoSubnetsV2(CiscoML2MechanismTestCase, - test_ml2_plugin.TestMl2SubnetsV2): - - 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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_subnet - with mock.patch.object(plugin_obj, - '_create_subnet_db') as patched_plugin: - - def side_effect(*args, **kwargs): - self._fail_second_call(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_obj = manager.NeutronManager.get_plugin() - orig = plugin_obj.create_subnet - with mock.patch.object(plugin_obj, - '_create_subnet_db') as patched_plugin: - def side_effect(*args, **kwargs): - return self._fail_second_call(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) diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py deleted file mode 100644 index e8be98c6e83..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus.py +++ /dev/null @@ -1,216 +0,0 @@ -# Copyright (c) 2013 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 collections -import mock -from oslo_utils import importutils -import testtools - -from neutron.common import constants as n_const -from neutron.extensions import portbindings -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2.drivers.cisco.nexus import constants -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions -from neutron.plugins.ml2.drivers.cisco.nexus import mech_cisco_nexus -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_db_v2 -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_network_driver -from neutron.tests.unit import testlib_api - - -NEXUS_IP_ADDRESS = '1.1.1.1' -NEXUS_IP_ADDRESS_PC = '2.2.2.2' -NEXUS_IP_ADDRESS_DUAL = '3.3.3.3' -HOST_NAME_1 = 'testhost1' -HOST_NAME_2 = 'testhost2' -HOST_NAME_PC = 'testpchost' -HOST_NAME_DUAL = 'testdualhost' -INSTANCE_1 = 'testvm1' -INSTANCE_2 = 'testvm2' -INSTANCE_PC = 'testpcvm' -INSTANCE_DUAL = 'testdualvm' -NEXUS_PORT_1 = 'ethernet:1/10' -NEXUS_PORT_2 = 'ethernet:1/20' -NEXUS_PORTCHANNELS = 'portchannel:2' -NEXUS_DUAL = 'ethernet:1/3,portchannel:2' -VLAN_ID_1 = 267 -VLAN_ID_2 = 265 -VLAN_ID_PC = 268 -VLAN_ID_DUAL = 269 -DEVICE_OWNER = 'compute:test' -NEXUS_SSH_PORT = '22' -PORT_STATE = n_const.PORT_STATUS_ACTIVE -NETWORK_TYPE = 'vlan' -NEXUS_DRIVER = ('neutron.plugins.ml2.drivers.cisco.nexus.' - 'nexus_network_driver.CiscoNexusDriver') - - -class FakeNetworkContext(object): - - """Network context for testing purposes only.""" - - def __init__(self, segment_id): - self._network_segments = {api.SEGMENTATION_ID: segment_id, - api.NETWORK_TYPE: NETWORK_TYPE} - - @property - def network_segments(self): - return self._network_segments - - -class FakePortContext(object): - - """Port context for testing purposes only.""" - - def __init__(self, device_id, host_name, network_context): - self._port = { - 'status': PORT_STATE, - 'device_id': device_id, - 'device_owner': DEVICE_OWNER, - portbindings.HOST_ID: host_name, - portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS - } - self._network = network_context - self._segment = network_context.network_segments - - @property - def current(self): - return self._port - - @property - def network(self): - return self._network - - @property - def bottom_bound_segment(self): - return self._segment - - -class TestCiscoNexusDevice(testlib_api.SqlTestCase): - - """Unit tests for Cisco ML2 Nexus device driver.""" - - TestConfigObj = collections.namedtuple( - 'TestConfigObj', - 'nexus_ip_addr host_name nexus_port instance_id vlan_id') - - test_configs = { - 'test_config1': TestConfigObj( - NEXUS_IP_ADDRESS, - HOST_NAME_1, - NEXUS_PORT_1, - INSTANCE_1, - VLAN_ID_1), - 'test_config2': TestConfigObj( - NEXUS_IP_ADDRESS, - HOST_NAME_2, - NEXUS_PORT_2, - INSTANCE_2, - VLAN_ID_2), - 'test_config_portchannel': TestConfigObj( - NEXUS_IP_ADDRESS_PC, - HOST_NAME_PC, - NEXUS_PORTCHANNELS, - INSTANCE_PC, - VLAN_ID_PC), - 'test_config_dual': TestConfigObj( - NEXUS_IP_ADDRESS_DUAL, - HOST_NAME_DUAL, - NEXUS_DUAL, - INSTANCE_DUAL, - VLAN_ID_DUAL), - } - - def setUp(self): - """Sets up mock ncclient, and switch and credentials dictionaries.""" - super(TestCiscoNexusDevice, self).setUp() - - # Use a mock netconf client - mock_ncclient = mock.Mock() - mock.patch.object(nexus_network_driver.CiscoNexusDriver, - '_import_ncclient', - return_value=mock_ncclient).start() - - def new_nexus_init(mech_instance): - mech_instance.driver = importutils.import_object(NEXUS_DRIVER) - - mech_instance._nexus_switches = {} - for name, config in TestCiscoNexusDevice.test_configs.items(): - ip_addr = config.nexus_ip_addr - host_name = config.host_name - nexus_port = config.nexus_port - mech_instance._nexus_switches[(ip_addr, - host_name)] = nexus_port - mech_instance._nexus_switches[(ip_addr, - 'ssh_port')] = NEXUS_SSH_PORT - mech_instance._nexus_switches[(ip_addr, - constants.USERNAME)] = 'admin' - mech_instance._nexus_switches[(ip_addr, - constants.PASSWORD)] = 'password' - mech_instance.driver.nexus_switches = ( - mech_instance._nexus_switches) - - mock.patch.object(mech_cisco_nexus.CiscoNexusMechanismDriver, - '__init__', new=new_nexus_init).start() - self._cisco_mech_driver = (mech_cisco_nexus. - CiscoNexusMechanismDriver()) - - def _create_delete_port(self, port_config): - """Tests creation and deletion of a virtual port.""" - nexus_ip_addr = port_config.nexus_ip_addr - host_name = port_config.host_name - nexus_port = port_config.nexus_port - instance_id = port_config.instance_id - vlan_id = port_config.vlan_id - - network_context = FakeNetworkContext(vlan_id) - port_context = FakePortContext(instance_id, host_name, - network_context) - - self._cisco_mech_driver.update_port_precommit(port_context) - self._cisco_mech_driver.update_port_postcommit(port_context) - for port_id in nexus_port.split(','): - bindings = nexus_db_v2.get_nexusport_binding(port_id, - vlan_id, - nexus_ip_addr, - instance_id) - self.assertEqual(len(bindings), 1) - - self._cisco_mech_driver.delete_port_precommit(port_context) - self._cisco_mech_driver.delete_port_postcommit(port_context) - for port_id in nexus_port.split(','): - with testtools.ExpectedException( - exceptions.NexusPortBindingNotFound): - nexus_db_v2.get_nexusport_binding(port_id, - vlan_id, - nexus_ip_addr, - instance_id) - - def test_create_delete_ports(self): - """Tests creation and deletion of two new virtual Ports.""" - self._create_delete_port( - TestCiscoNexusDevice.test_configs['test_config1']) - - self._create_delete_port( - TestCiscoNexusDevice.test_configs['test_config2']) - - def test_create_delete_portchannel(self): - """Tests creation of a port over a portchannel.""" - self._create_delete_port( - TestCiscoNexusDevice.test_configs['test_config_portchannel']) - - def test_create_delete_dual(self): - """Tests creation and deletion of dual ports for single server""" - self._create_delete_port( - TestCiscoNexusDevice.test_configs['test_config_dual']) diff --git a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus_db.py b/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus_db.py deleted file mode 100644 index 12789701a80..00000000000 --- a/neutron/tests/unit/ml2/drivers/cisco/nexus/test_cisco_nexus_db.py +++ /dev/null @@ -1,200 +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 testtools - -from neutron.plugins.ml2.drivers.cisco.nexus import exceptions -from neutron.plugins.ml2.drivers.cisco.nexus import nexus_db_v2 -from neutron.tests.unit import testlib_api - - -class CiscoNexusDbTest(testlib_api.SqlTestCase): - - """Unit tests for Cisco mechanism driver's Nexus port binding database.""" - - NpbObj = collections.namedtuple('NpbObj', 'port vlan switch instance') - - def _npb_test_obj(self, pnum, vnum, switch='10.9.8.7', instance=None): - """Creates a Nexus port binding test object from a pair of numbers.""" - if pnum is 'router': - port = pnum - else: - port = '1/%s' % pnum - if instance is None: - instance = 'instance_%s_%s' % (pnum, vnum) - return self.NpbObj(port, vnum, switch, instance) - - def _assert_bindings_match(self, npb, npb_obj): - """Asserts that a port binding matches a port binding test obj.""" - self.assertEqual(npb.port_id, npb_obj.port) - self.assertEqual(npb.vlan_id, npb_obj.vlan) - self.assertEqual(npb.switch_ip, npb_obj.switch) - self.assertEqual(npb.instance_id, npb_obj.instance) - - def _add_binding_to_db(self, npb): - """Adds a port binding to the Nexus database.""" - return nexus_db_v2.add_nexusport_binding( - npb.port, npb.vlan, npb.switch, npb.instance) - - def _add_bindings_to_db(self, npbs): - """Adds a list of port bindings to the Nexus database.""" - for npb in npbs: - nexus_db_v2.add_nexusport_binding( - npb.port, npb.vlan, npb.switch, npb.instance) - - def _remove_binding_from_db(self, npb): - """Removes a port binding from the Nexus database.""" - return nexus_db_v2.remove_nexusport_binding( - npb.port, npb.vlan, npb.switch, npb.instance) - - def _get_nexusport_binding(self, npb): - """Gets a port binding based on port, vlan, switch, and instance.""" - return nexus_db_v2.get_nexusport_binding( - npb.port, npb.vlan, npb.switch, npb.instance) - - def _get_nexusvlan_binding(self, npb): - """Gets port bindings based on vlan and switch.""" - return nexus_db_v2.get_nexusvlan_binding(npb.vlan, npb.switch) - - def _get_nexusvm_binding(self, npb): - """Gets port binding based on vlan and instance.""" - return nexus_db_v2.get_nexusvm_bindings(npb.vlan, npb.instance)[0] - - def _get_port_vlan_switch_binding(self, npb): - """Gets port bindings based on port, vlan, and switch.""" - return nexus_db_v2.get_port_vlan_switch_binding( - npb.port, npb.vlan, npb.switch) - - def _get_port_switch_bindings(self, npb): - """Get port bindings based on port and switch.""" - return nexus_db_v2.get_port_switch_bindings(npb.port, npb.switch) - - def test_nexusportbinding_add_remove(self): - """Tests add and removal of port bindings from the Nexus database.""" - npb11 = self._npb_test_obj(10, 100) - npb = self._add_binding_to_db(npb11) - self._assert_bindings_match(npb, npb11) - npb = self._remove_binding_from_db(npb11) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb11) - with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): - self._remove_binding_from_db(npb11) - - def test_nexusportbinding_get(self): - """Tests get of specific port bindings from the database.""" - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_bindings_to_db([npb11, npb21, npb22]) - - npb = self._get_nexusport_binding(npb11) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb11) - npb = self._get_nexusport_binding(npb21) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb21) - npb = self._get_nexusport_binding(npb22) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb22) - - with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): - nexus_db_v2.get_nexusport_binding( - npb21.port, npb21.vlan, npb21.switch, "dummyInstance") - - def test_nexusvlanbinding_get(self): - """Test get of port bindings based on vlan and switch.""" - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_bindings_to_db([npb11, npb21, npb22]) - - npb_all_v100 = self._get_nexusvlan_binding(npb11) - self.assertEqual(len(npb_all_v100), 2) - npb_v200 = self._get_nexusvlan_binding(npb22) - self.assertEqual(len(npb_v200), 1) - self._assert_bindings_match(npb_v200[0], npb22) - - with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): - nexus_db_v2.get_nexusvlan_binding(npb21.vlan, "dummySwitch") - - def test_nexusvmbinding_get(self): - """Test get of port bindings based on vlan and instance.""" - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - npb22 = self._npb_test_obj(20, 200) - self._add_bindings_to_db([npb11, npb21, npb22]) - - npb = self._get_nexusvm_binding(npb21) - self._assert_bindings_match(npb, npb21) - npb = self._get_nexusvm_binding(npb22) - self._assert_bindings_match(npb, npb22) - - with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): - nexus_db_v2.get_nexusvm_bindings(npb21.vlan, "dummyInstance")[0] - - def test_nexusportvlanswitchbinding_get(self): - """Tests get of port bindings based on port, vlan, and switch.""" - npb11 = self._npb_test_obj(10, 100) - npb21 = self._npb_test_obj(20, 100) - self._add_bindings_to_db([npb11, npb21]) - - npb = self._get_port_vlan_switch_binding(npb11) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb11) - - with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): - nexus_db_v2.get_port_vlan_switch_binding( - npb21.port, npb21.vlan, "dummySwitch") - - def test_nexusportswitchbinding_get(self): - """Tests get of port bindings based on port and switch.""" - 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_bindings_to_db([npb11, npb21, npb22]) - - npb = self._get_port_switch_bindings(npb11) - self.assertEqual(len(npb), 1) - self._assert_bindings_match(npb[0], npb11) - npb_all_p20 = self._get_port_switch_bindings(npb21) - self.assertEqual(len(npb_all_p20), 2) - - npb = nexus_db_v2.get_port_switch_bindings(npb21.port, "dummySwitch") - self.assertIsNone(npb) - - def test_nexusbinding_update(self): - """Tests update of vlan IDs for port bindings.""" - 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_bindings_to_db([npb11, npb21]) - - npb_all_v100 = nexus_db_v2.get_nexusvlan_binding(100, '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 = nexus_db_v2.update_nexusport_binding(npb21.port, 200) - self._assert_bindings_match(npb, npb22) - - npb_all_v100 = nexus_db_v2.get_nexusvlan_binding(100, '1.1.1.1') - self.assertEqual(len(npb_all_v100), 1) - self._assert_bindings_match(npb_all_v100[0], npb11) - - npb = nexus_db_v2.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(exceptions.NexusPortBindingNotFound): - nexus_db_v2.update_nexusport_binding(npb33.port, 200)