]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Adding multi switch support to the Cisco Nexus plugin
authorArvind Somya <asomya@cisco.com>
Mon, 7 Jan 2013 23:40:59 +0000 (18:40 -0500)
committerArvind Somya <asomya@cisco.com>
Mon, 14 Jan 2013 18:04:14 +0000 (13:04 -0500)
This commit adds intelligent multiple nexus physical switch support for the Cisco plugin.
The plugin also has been modified to scan for the host when an instance is created and
selectively trunk VLAN's for the port for that host only. It also deletes VLANs from nexus
switches when no longer required.

Implements: blueprint cisco-plugin-enhancements

Change-Id: I6275eb1815310d0d5a8123ca2edbc0a0937718e9

12 files changed:
etc/quantum/plugins/cisco/credentials.ini
etc/quantum/plugins/cisco/l2network_plugin.ini
etc/quantum/plugins/cisco/nexus.ini
quantum/plugins/cisco/db/nexus_db_v2.py
quantum/plugins/cisco/db/nexus_models_v2.py
quantum/plugins/cisco/l2network_plugin_configuration.py
quantum/plugins/cisco/models/virt_phy_sw_v2.py
quantum/plugins/cisco/nexus/cisco_nexus_configuration.py
quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py
quantum/tests/unit/cisco/test_nexus_plugin.py
tools/pip-requires

index 4eef1d99233fde837f3ab663ccaa669aa9b0909d..a33e16c35b1aa5d1436ebbd2f1291d81e07ddf8d 100644 (file)
@@ -4,7 +4,13 @@ username=<put_user_name_here>
 password=<put_password_here>
 
 #Provide the Nexus credentials, if you are using Nexus
-[1.1.1.1]
-username=abc
-password=def
+[<put_nexus_switch_ip_address_here>]
+username=<put_user_name_here>
+password=<put_password_here>
 
+# Provide credentials and endpoint
+# for keystone here
+[keystone]
+auth_url=<put_keystone_endpoint_here>
+username=<put_user_name_here>
+password=<put_password_here>
index 848ab21836427a59b286f93dac160270aa5d163c..501ff3814ecead98d7ef1144dc11b31eb68c1b7f 100644 (file)
@@ -18,3 +18,7 @@ model_class=quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchMod
 
 [SEGMENTATION]
 manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr
+
+# IMPORTANT: Comment the following lines for production deployments
+[TEST]
+host=testhost
index 54bb967e012d2c8b1db874e49b37835b1a88a0c8..b01cd0aa81cc96ee9c4c86bad716d5f77daad7a2 100644 (file)
@@ -1,10 +1,13 @@
 [SWITCH]
-# Change the following to reflect the Nexus switch details
-nexus_ip_address=<put_nexus_switch_ip_address_here>
-#Interfaces connected from the Nexus Switch to the compute hosts ports, e.g.: 1/10 and 1/11
-ports=<put_interfaces_names_here_separated_by_commas>
-#Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default) 
-nexus_ssh_port=22
+# Ip address of the switch
+[[<put_nexus_switch_ip_address_here>]]
+# Hostname of the node
+[[[<put_hostname_here>]]]
+# Port this node is connected to on the nexus switch
+ports=<put_port_id_here>
+# Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default)
+[[[ssh_port]]]
+ssh_port=<put_port_number_here>
 
 [DRIVER]
 #name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver
index a61ffc4ae578853fb4012cc045e2f281252edb37..b5f35dd8fac1940ca6225ed5c138fd566dc9debb 100644 (file)
 #    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 logging as LOG
+# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com)
+#
 
 from sqlalchemy.orm import exc
 
 import quantum.db.api as db
-
+from quantum.openstack.common import log as logging
 from quantum.plugins.cisco.common import cisco_exceptions as c_exc
 from quantum.plugins.cisco.db import nexus_models_v2
 
 
+LOG = logging.getLogger(__name__)
+
+
 def get_all_nexusport_bindings():
     """Lists all the nexusport bindings"""
     LOG.debug("get_all_nexusport_bindings() called")
@@ -36,35 +40,54 @@ def get_all_nexusport_bindings():
         return []
 
 
-def get_nexusport_binding(vlan_id):
+def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
     """Lists a nexusport binding"""
     LOG.debug("get_nexusport_binding() called")
     session = db.get_session()
     try:
         binding = (session.query(nexus_models_v2.NexusPortBinding).
-                   filter_by(vlan_id=vlan_id).all())
+                   filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
+                   filter_by(port_id=port_id).
+                   filter_by(instance_id=instance_id).all())
         return binding
     except exc.NoResultFound:
         raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
 
 
-def add_nexusport_binding(port_id, vlan_id):
+def get_nexusvlan_binding(vlan_id, switch_ip):
+    """Lists a vlan and switch binding"""
+    LOG.debug("get_nexusvlan_binding() called")
+    session = db.get_session()
+    try:
+        binding = (session.query(nexus_models_v2.NexusPortBinding).
+                   filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
+                   all())
+        return binding
+    except exc.NoResultFound:
+        raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
+
+
+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, vlan_id)
+    binding = nexus_models_v2.NexusPortBinding(
+        port_id, vlan_id, switch_ip, instance_id)
     session.add(binding)
     session.flush()
     return binding
 
 
-def remove_nexusport_binding(vlan_id):
+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()
     try:
         binding = (session.query(nexus_models_v2.NexusPortBinding).
-                   filter_by(vlan_id=vlan_id).all())
+                   filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
+                   filter_by(port_id=port_id).
+                   filter_by(instance_id=instance_id).all())
+
         for bind in binding:
             session.delete(bind)
         session.flush()
@@ -87,3 +110,29 @@ def update_nexusport_binding(port_id, new_vlan_id):
         return binding
     except exc.NoResultFound:
         raise c_exc.NexusPortBindingNotFound()
+
+
+def get_nexusvm_binding(vlan_id, instance_id):
+    """Lists nexusvm bindings"""
+    LOG.debug("get_nexusvm_binding() called")
+    session = db.get_session()
+    try:
+        binding = (session.query(nexus_models_v2.NexusPortBinding).
+                   filter_by(instance_id=instance_id).
+                   filter_by(vlan_id=vlan_id).first())
+        return binding
+    except exc.NoResultFound:
+        raise c_exc.NexusPortBindingNotFound(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")
+    session = db.get_session()
+    try:
+        binding = (session.query(nexus_models_v2.NexusPortBinding).
+                   filter_by(port_id=port_id).filter_by(switch_ip=switch_ip).
+                   filter_by(vlan_id=vlan_id).all())
+        return binding
+    except exc.NoResultFound:
+        raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
index 975d270cd9210de9e1c9081fed898a75b8d2e8af..e1cfd4b3c98770487e142e2108e61931b2305756 100644 (file)
@@ -22,16 +22,21 @@ from quantum.plugins.cisco.db.l2network_models import L2NetworkBase
 
 
 class NexusPortBinding(model_base.BASEV2, L2NetworkBase):
-    """Represents a binding of nexus port to vlan_id"""
-    __tablename__ = 'nexusport_bindings'
+    """Represents a binding of VM's to nexus ports"""
+    __tablename__ = "nexusport_bindings"
 
     id = Column(Integer, primary_key=True, autoincrement=True)
     port_id = Column(String(255))
     vlan_id = Column(Integer, nullable=False)
+    switch_ip = Column(String(255))
+    instance_id = Column(String(255))
 
-    def __init__(self, port_id, vlan_id):
+    def __init__(self, port_id, vlan_id, switch_ip, instance_id):
         self.port_id = port_id
         self.vlan_id = vlan_id
+        self.switch_ip = switch_ip
+        self.instance_id = instance_id
 
     def __repr__(self):
-        return "<NexusPortBinding (%s,%d)>" % (self.port_id, self.vlan_id)
+        return "<NexusPortBinding (%s,%d, %s, %s)>" % \
+            (self.port_id, self.vlan_id, self.switch_ip, self.instance_id)
index fdd4191bc58a1a77478d028be77c0354feb36326..a1c35f059a8f61018a60596292a25bb0e1c25379 100644 (file)
@@ -43,6 +43,9 @@ MAX_NETWORKS = SECTION_CONF['max_networks']
 SECTION_CONF = CONF_PARSER_OBJ['MODEL']
 MODEL_CLASS = SECTION_CONF['model_class']
 
+if 'TEST' in CONF_PARSER_OBJ.keys():
+    TEST = CONF_PARSER_OBJ['TEST']
+
 CONF_FILE = find_config_file({'plugin': 'cisco'}, "cisco_plugins.ini")
 
 SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION']
@@ -51,7 +54,6 @@ MANAGER_CLASS = SECTION_CONF['manager_class']
 
 CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
 
-
 # Read the config for the device plugins
 PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy)
 
index b46aa4bc174f1a8a2c609a44ccddcf16ddd69a4d..49c26b5a338b581f574fb43c228901ed6952ea7f 100644 (file)
@@ -21,6 +21,10 @@ from copy import deepcopy
 import inspect
 import logging
 
+from keystoneclient.v2_0 import client as keystone_client
+from novaclient.v1_1 import client as nova_client
+
+from quantum.db import l3_db
 from quantum.manager import QuantumManager
 from quantum.openstack.common import importutils
 from quantum.plugins.cisco.common import cisco_constants as const
@@ -30,7 +34,6 @@ from quantum.plugins.cisco import l2network_plugin_configuration as conf
 from quantum.plugins.openvswitch import ovs_db_v2 as odb
 from quantum import quantum_plugin_base_v2
 
-
 LOG = logging.getLogger(__name__)
 
 
@@ -46,11 +49,11 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
     _plugins = {}
     _inventory = {}
     _methods_to_delegate = ['get_network', 'get_networks',
-                            'create_port', 'create_port_bulk', 'delete_port',
-                            'update_port', 'get_port', 'get_ports',
+                            'create_port_bulk', 'update_port',
+                            'get_port', 'get_ports',
                             'create_subnet', 'create_subnet_bulk',
-                            'delete_subnet', 'update_subnet', 'get_subnet',
-                            'get_subnets', ]
+                            'delete_subnet', 'update_subnet',
+                            'get_subnet', 'get_subnets', ]
 
     def __init__(self):
         """
@@ -181,6 +184,25 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
         else:
             return False
 
+    def _get_instance_host(self, tenant_id, instance_id):
+        keystone = cred._creds_dictionary['keystone']
+        kc = keystone_client.Client(username=keystone['username'],
+                                    password=keystone['password'],
+                                    tenant_id=tenant_id,
+                                    auth_url=keystone['auth_url'])
+        tenant = kc.tenants.get(tenant_id)
+        tenant_name = tenant.name
+
+        nc = nova_client.Client(keystone['username'],
+                                keystone['password'],
+                                tenant_name,
+                                keystone['auth_url'],
+                                no_cache=True)
+        serv = nc.servers.get(instance_id)
+        host = serv.__getattr__('OS-EXT-SRV-ATTR:host')
+
+        return host
+
     def create_network(self, context, network):
         """
         Perform this operation in the context of the configured device
@@ -200,9 +222,6 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
             args = [ovs_output[0]['tenant_id'], ovs_output[0]['name'],
                     ovs_output[0]['id'], vlan_name, vlan_id,
                     {'vlan_ids': vlanids}]
-            nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
-                                                          self._func_name(),
-                                                          args)
             return ovs_output[0]
         except:
             # TODO (Sumit): Check if we need to perform any rollback here
@@ -221,14 +240,6 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
             LOG.debug("ovs_output: %s\n " % ovs_output)
             vlanids = self._get_all_segmentation_ids()
             ovs_networks = ovs_output
-            for ovs_network in ovs_networks:
-                vlan_id = self._get_segmentation_id(ovs_network['id'])
-                vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id)
-                args = [ovs_network['tenant_id'], ovs_network['name'],
-                        ovs_network['id'], vlan_name, vlan_id,
-                        {'vlan_ids': vlanids}]
-                nexus_output = self._invoke_plugin_per_device(
-                    const.NEXUS_PLUGIN, "create_network", args)
             return ovs_output
         except:
             # TODO (Sumit): Check if we need to perform any rollback here
@@ -289,8 +300,41 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
         pass
 
     def create_port(self, context, port):
-        """For this model this method will be delegated to vswitch plugin"""
-        pass
+        """
+        Perform this operation in the context of the configured device
+        plugins.
+        """
+        LOG.debug("create_port() called\n")
+        try:
+            args = [context, port]
+            ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+                                                        self._func_name(),
+                                                        args)
+            net_id = port['port']['network_id']
+            instance_id = port['port']['device_id']
+            tenant_id = port['port']['tenant_id']
+
+            net_dict = self.get_network(context, net_id)
+            net_name = net_dict['name']
+
+            vlan_id = self._get_segmentation_id(net_id)
+            host = ''
+            if hasattr(conf, 'TEST'):
+                host = conf.TEST['host']
+            else:
+                host = self._get_instance_host(tenant_id, instance_id)
+
+            # Trunk segmentation id for only this host
+            vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id)
+            n_args = [tenant_id, net_name, net_id,
+                      vlan_name, vlan_id, host, instance_id]
+            nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+                                                          'create_network',
+                                                          n_args)
+            return ovs_output[0]
+        except:
+            # TODO (asomya): Check if we need to perform any rollback here
+            raise
 
     def get_port(self, context, id, fields=None):
         """For this model this method will be delegated to vswitch plugin"""
@@ -304,9 +348,27 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
         """For this model this method will be delegated to vswitch plugin"""
         pass
 
-    def delete_port(self, context, id, kwargs):
-        """For this model this method will be delegated to vswitch plugin"""
-        pass
+    def delete_port(self, context, id):
+        """
+        Perform this operation in the context of the configured device
+        plugins.
+        """
+        LOG.debug("delete_port() called\n")
+        try:
+            args = [context, id]
+            port = self.get_port(context, id)
+            vlan_id = self._get_segmentation_id(port['network_id'])
+            n_args = [port['device_id'], vlan_id]
+            ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+                                                        self._func_name(),
+                                                        args)
+            nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+                                                          self._func_name(),
+                                                          n_args)
+            return ovs_output[0]
+        except:
+            # TODO (asomya): Check if we need to perform any rollback here
+            raise
 
     def create_subnet(self, context, subnet):
         """For this model this method will be delegated to vswitch plugin"""
index 4d648d93ddd63ce5e7b144964c6081231c1e4903..d862a39a1414cc9c2c8512be71dcc79f0b55b17c 100644 (file)
@@ -30,10 +30,7 @@ from quantum.plugins.cisco.common import cisco_configparser as confp
 CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
                              "nexus.ini"))
 
-SECTION = CP['SWITCH']
-NEXUS_IP_ADDRESS = SECTION['nexus_ip_address']
-NEXUS_PORTS = SECTION['ports']
-NEXUS_SSH_PORT = SECTION['nexus_ssh_port']
+NEXUS_DETAILS = CP['SWITCH']
 
 SECTION = CP['DRIVER']
 NEXUS_DRIVER = SECTION['name']
index 6f30c80bf62df4823515cff807857469fa576570..0e10f025bd41f4eb8a78d700f891b0eb7167f4f3 100644 (file)
@@ -21,6 +21,7 @@
 Implements a Nexus-OS NETCONF over SSHv2 API Client
 """
 
+import eventlet
 import logging
 
 from ncclient import manager
@@ -116,14 +117,14 @@ class CiscoNEXUSDriver():
         Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
         given the VLAN ID and Name and Interface Number
         """
-        with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
-                               nexus_password) as man:
-            self.enable_vlan(man, vlan_id, vlan_name)
-            if vlan_ids is '':
-                vlan_ids = self.build_vlans_cmd()
-            LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids)
-            for ports in nexus_ports:
-                self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
+        man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
+                                nexus_user, nexus_password)
+        self.enable_vlan(man, vlan_id, vlan_name)
+        if vlan_ids is '':
+            vlan_ids = self.build_vlans_cmd()
+        LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids)
+        for ports in nexus_ports:
+            self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
 
     def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
                     nexus_ports, nexus_ssh_port):
@@ -131,11 +132,11 @@ class CiscoNEXUSDriver():
         Delete a VLAN and Disables trunk mode an interface on Nexus Switch
         given the VLAN ID and Interface Number
         """
-        with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
-                               nexus_password) as man:
-            self.disable_vlan(man, vlan_id)
-            for ports in nexus_ports:
-                self.disable_vlan_on_trunk_int(man, ports, vlan_id)
+        man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
+                                nexus_user, nexus_password)
+        self.disable_vlan(man, vlan_id)
+        for ports in nexus_ports:
+            self.disable_vlan_on_trunk_int(man, ports, vlan_id)
 
     def build_vlans_cmd(self):
         """
@@ -154,19 +155,19 @@ class CiscoNEXUSDriver():
         """
         Adds a vlan from interfaces on the Nexus switch given the VLAN ID
         """
-        with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
-                               nexus_password) as man:
-            if not vlan_ids:
-                vlan_ids = self.build_vlans_cmd()
-            for ports in nexus_ports:
-                self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
+        man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
+                                nexus_user, nexus_password)
+        if not vlan_ids:
+            vlan_ids = self.build_vlans_cmd()
+        for ports in nexus_ports:
+            self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
 
     def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
                         nexus_ports, nexus_ssh_port):
         """
         Removes a vlan from interfaces on the Nexus switch given the VLAN ID
         """
-        with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
-                               nexus_password) as man:
-            for ports in nexus_ports:
-                self.disable_vlan_on_trunk_int(man, ports, vlan_id)
+        man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
+                                nexus_user, nexus_password)
+        for ports in nexus_ports:
+            self.disable_vlan_on_trunk_int(man, ports, vlan_id)
index 447faf466b8684f10d7095f8b9b154ddb48153fc..57bc3c3a22ed686760fbfd4b23071059667b435e 100644 (file)
@@ -16,6 +16,7 @@
 #
 # @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
@@ -48,11 +49,18 @@ class NexusPlugin(L2DevicePluginBase):
         """
         self._client = importutils.import_object(conf.NEXUS_DRIVER)
         LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER)
-        self._nexus_ip = conf.NEXUS_IP_ADDRESS
-        self._nexus_username = cred.Store.get_username(conf.NEXUS_IP_ADDRESS)
-        self._nexus_password = cred.Store.get_password(conf.NEXUS_IP_ADDRESS)
-        self._nexus_ports = conf.NEXUS_PORTS
-        self._nexus_ssh_port = conf.NEXUS_SSH_PORT
+        self._nexus_switches = conf.NEXUS_DETAILS
+        self.credentials = {}
+
+    def get_credential(self, nexus_ip):
+        if not nexus_ip in self.credentials.keys():
+            _nexus_username = cred.Store.get_username(nexus_ip)
+            _nexus_password = cred.Store.get_password(nexus_ip)
+            self.credentials[nexus_ip] = {
+                'username': _nexus_username,
+                'password': _nexus_password
+            }
+        return self.credentials[nexus_ip]
 
     def get_all_networks(self, tenant_id):
         """
@@ -64,26 +72,52 @@ class NexusPlugin(L2DevicePluginBase):
         return self._networks.values()
 
     def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
-                       **kwargs):
+                       host, instance):
         """
-        Create a VLAN in the switch, and configure the appropriate interfaces
+        Create a VLAN in the appropriate switch/port,
+        and configure the appropriate interfaces
         for this VLAN
         """
         LOG.debug("NexusPlugin:create_network() called\n")
-        vlan_ids = ''
-        for key in kwargs:
-            if key == 'vlan_ids':
-                vlan_ids = kwargs['vlan_ids']
-        self._client.create_vlan(
-            vlan_name, str(vlan_id), self._nexus_ip,
-            self._nexus_username, self._nexus_password,
-            self._nexus_ports, self._nexus_ssh_port, vlan_ids)
-        for ports in self._nexus_ports:
-            try:
-                nxos_db.add_nexusport_binding(ports, str(vlan_id))
-            except:
-                raise excep.NexusPortBindingAlreadyExists(port_id=ports)
-
+        # Grab the switch IP and port for this host
+        switch_ip = ''
+        port_id = ''
+        for switch in self._nexus_switches.keys():
+            for hostname in self._nexus_switches[switch].keys():
+                if str(hostname) == str(host):
+                    switch_ip = switch
+                    port_id = self._nexus_switches[switch][hostname]['ports']
+        # Check if this network is already in the DB
+        binding = nxos_db.get_port_vlan_switch_binding(
+            port_id, vlan_id, switch_ip)
+        if not binding:
+            _nexus_ip = switch_ip
+            _nexus_ports = (port_id,)
+            _nexus_ssh_port = \
+                self._nexus_switches[switch_ip]['ssh_port']['ssh_port']
+            _nexus_creds = self.get_credential(_nexus_ip)
+            _nexus_username = _nexus_creds['username']
+            _nexus_password = _nexus_creds['password']
+            # Check for vlan/switch binding
+            vbinding = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+            if not vbinding:
+                # Create vlan and trunk vlan on the port
+                self._client.create_vlan(
+                    vlan_name, str(vlan_id), _nexus_ip,
+                    _nexus_username, _nexus_password,
+                    _nexus_ports, _nexus_ssh_port, vlan_id)
+            else:
+                # Only trunk vlan on the port
+                man = self._client.nxos_connect(_nexus_ip,
+                                                int(_nexus_ssh_port),
+                                                _nexus_username,
+                                                _nexus_password)
+                self._client.enable_vlan_on_trunk_int(man,
+                                                      port_id,
+                                                      vlan_id)
+
+        nxos_db.add_nexusport_binding(port_id, str(vlan_id),
+                                      switch_ip, instance)
         new_net_dict = {const.NET_ID: net_id,
                         const.NET_NAME: net_name,
                         const.NET_PORTS: {},
@@ -94,32 +128,10 @@ class NexusPlugin(L2DevicePluginBase):
 
     def delete_network(self, tenant_id, net_id, **kwargs):
         """
-        Deletes a VLAN in the switch, and removes the VLAN configuration
+        Deletes the VLAN in all switches, and removes the VLAN configuration
         from the relevant interfaces
         """
         LOG.debug("NexusPlugin:delete_network() called\n")
-        vlan_id = None
-        for key in kwargs:
-            if key == const.CONTEXT:
-                context = kwargs[const.CONTEXT]
-            elif key == const.BASE_PLUGIN_REF:
-                base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
-            elif key == 'vlan_id':
-                vlan_id = kwargs['vlan_id']
-        if vlan_id is None:
-            vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
-                                                    context, base_plugin_ref)
-        ports_id = nxos_db.get_nexusport_binding(vlan_id)
-        LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
-        nxos_db.remove_nexusport_binding(vlan_id)
-        if net_id:
-            self._client.delete_vlan(
-                str(vlan_id), self._nexus_ip,
-                self._nexus_username, self._nexus_password,
-                self._nexus_ports, self._nexus_ssh_port)
-            return net_id
-        # Network not found
-        raise exc.NetworkNotFound(net_id=net_id)
 
     def get_network_details(self, tenant_id, net_id, **kwargs):
         """
@@ -135,22 +147,6 @@ class NexusPlugin(L2DevicePluginBase):
         Virtual Network.
         """
         LOG.debug("NexusPlugin:update_network() called\n")
-        if 'net_admin_state' in kwargs:
-            net_admin_state = kwargs['net_admin_state']
-            vlan_id = kwargs['vlan_id']
-            vlan_ids = kwargs['vlan_ids']
-            if not net_admin_state:
-                self._client.remove_vlan_int(
-                    str(vlan_id), self._nexus_ip,
-                    self._nexus_username, self._nexus_password,
-                    self._nexus_ports, self._nexus_ssh_port)
-            else:
-                self._client.add_vlan_int(
-                    str(vlan_id), self._nexus_ip,
-                    self._nexus_username, self._nexus_password,
-                    self._nexus_ports, self._nexus_ssh_port,
-                    vlan_ids)
-        return net_id
 
     def get_all_ports(self, tenant_id, net_id, **kwargs):
         """
@@ -166,12 +162,38 @@ class NexusPlugin(L2DevicePluginBase):
         """
         LOG.debug("NexusPlugin:create_port() called\n")
 
-    def delete_port(self, tenant_id, net_id, port_id, **kwargs):
+    def delete_port(self, device_id, vlan_id):
         """
-        This is probably not applicable to the Nexus plugin.
-        Delete if not required.
+        Delete port bindings from the database and scan
+        whether the network is still required on
+        the interfaces trunked
         """
         LOG.debug("NexusPlugin:delete_port() called\n")
+        # Delete DB row for this port
+        row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
+        if row:
+            nxos_db.remove_nexusport_binding(row['port_id'], row['vlan_id'],
+                                             row['switch_ip'],
+                                             row['instance_id'])
+            # Check for any other bindings with the same vlan_id and switch_ip
+            bindings = nxos_db.get_nexusvlan_binding(
+                row['vlan_id'], row['switch_ip'])
+
+            if not bindings:
+                # Delete this vlan from this switch
+                _nexus_ip = row['switch_ip']
+                _nexus_ports = (row['port_id'],)
+                _nexus_ssh_port = \
+                    self._nexus_switches[_nexus_ip]['ssh_port']['ssh_port']
+                _nexus_creds = self.get_credential(_nexus_ip)
+                _nexus_username = _nexus_creds['username']
+                _nexus_password = _nexus_creds['password']
+                self._client.delete_vlan(
+                    str(row['vlan_id']), _nexus_ip,
+                    _nexus_username, _nexus_password,
+                    _nexus_ports, _nexus_ssh_port)
+
+            return row['instance_id']
 
     def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
         """
index 74ab64fb939f12428984caf2daeccb19a7c24ee8..f4d9d206d85cd1ad17c9fb8ca894c0e3582b5a33 100644 (file)
@@ -27,7 +27,9 @@ from quantum.plugins.cisco.nexus import cisco_nexus_plugin_v2
 NEXUS_IP_ADDRESS = '1.1.1.1'
 NEXUS_USERNAME = 'username'
 NEXUS_PASSWORD = 'password'
-NEXUS_PORTS = ['1/10']
+HOSTNAME = 'testhost'
+INSTANCE = 'testvm'
+NEXUS_PORTS = '1/10'
 NEXUS_SSH_PORT = '22'
 NEXUS_DRIVER = ('quantum.plugins.cisco.tests.unit.v2.nexus.'
                 'fake_nexus_driver.CiscoNEXUSFakeDriver')
@@ -48,6 +50,17 @@ class TestCiscoNexusPlugin(unittest.TestCase):
         self.second_net_id = 000005
         self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
         self.second_vlan_id = 265
+        self._nexus_switches = {
+            NEXUS_IP_ADDRESS: {
+                HOSTNAME: {
+                    'ports': NEXUS_PORTS,
+                },
+                'ssh_port': {
+                    'ssh_port': NEXUS_SSH_PORT
+                }
+            }
+        }
+        self._hostname = HOSTNAME
 
         def new_cdb_init():
             db.configure_db()
@@ -59,14 +72,21 @@ class TestCiscoNexusPlugin(unittest.TestCase):
             self._nexus_password = NEXUS_PASSWORD
             self._nexus_ports = NEXUS_PORTS
             self._nexus_ssh_port = NEXUS_SSH_PORT
+            self.credentials = {
+                self._nexus_ip: {
+                    'username': self._nexus_username,
+                    'password': self._nexus_password
+                }
+            }
 
         with mock.patch.object(cdb, 'initialize', new=new_cdb_init):
             cdb.initialize()
             with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin,
                                    '__init__', new=new_nexus_init):
                 self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin()
+                self._cisco_nexus_plugin._nexus_switches = self._nexus_switches
 
-    def test_a_create_delete_network(self):
+    def test_a_create_network(self):
         """
         Tests creation of two new Virtual Network.
         Tests deletion of one Virtual Network.
@@ -90,18 +110,16 @@ class TestCiscoNexusPlugin(unittest.TestCase):
 
         new_net_dict = self._cisco_nexus_plugin.create_network(
             tenant_id, net_name, net_id,
-            vlan_name, vlan_id, vlan_ids=str(vlan_id))
-
+            vlan_name, vlan_id, self._hostname, INSTANCE)
         self.assertEqual(new_net_dict[const.NET_ID], net_id)
         self.assertEqual(new_net_dict[const.NET_NAME], self.net_name)
         self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
         self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
 
-        vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
         new_net_dict = self._cisco_nexus_plugin.create_network(
             tenant_id, second_net_name, second_net_id,
-            second_vlan_name, second_vlan_id,
-            vlan_ids=vlan_ids)
+            second_vlan_name, second_vlan_id, self._hostname,
+            INSTANCE)
 
         self.assertEqual(new_net_dict[const.NET_ID], second_net_id)
         self.assertEqual(new_net_dict[const.NET_NAME], self.second_net_name)
@@ -109,106 +127,20 @@ class TestCiscoNexusPlugin(unittest.TestCase):
                          self.second_vlan_name)
         self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.second_vlan_id)
 
-        expected_net_id = self._cisco_nexus_plugin.delete_network(
-            tenant_id, net_id, vlan_id=str(vlan_id))
-
-        self.assertEqual(expected_net_id, net_id)
-
-    def test_b_nexus_clear_vlan(self):
+    def test_b_nexus_delete_port(self):
         """
         Test to clean up second vlan of nexus device
         created by test_create_delete_network. This
         test will fail if it is run individually.
         """
-        tenant_id = self.tenant_id
-        second_net_id = self.second_net_id
-        second_vlan_id = self.second_vlan_id
-
-        expected_second_net_id = self._cisco_nexus_plugin.delete_network(
-            tenant_id, second_net_id,
-            vlan_id=str(second_vlan_id))
-
-        self.assertEqual(expected_second_net_id, second_net_id)
-
-    def test_c_update_network_False(self):
-        """
-        Test to update a network state to False
-        resulting in disabling a vlan corresponding to
-        that network from the configured nexus interfaces
-        """
-        tenant_id = self.tenant_id
-        net_name = self.net_name
-        net_id = self.net_id
-        vlan_name = self.vlan_name
-        vlan_id = self.vlan_id
-        second_net_name = self.second_net_name
-        second_net_id = self.second_net_id
-        second_vlan_name = self.second_vlan_name
-        second_vlan_id = self.second_vlan_id
-
-        new_net_dict = self._cisco_nexus_plugin.create_network(
-            tenant_id, net_name, net_id,
-            vlan_name, vlan_id, vlan_ids=str(vlan_id))
-
-        vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
-        new_net_dict = self._cisco_nexus_plugin.create_network(
-            tenant_id, second_net_name, second_net_id,
-            second_vlan_name, second_vlan_id,
-            vlan_ids=vlan_ids)
-
-        expected_net_id = self._cisco_nexus_plugin.update_network(
-            tenant_id, net_id, net_admin_state=False,
-            vlan_id=vlan_id, vlan_ids=str(vlan_id))
-
-        self.assertEqual(expected_net_id, net_id)
-
-    def test_d_nexus_clean_vlan_update(self):
-        """
-        Cleans up vlans on the nexus for the two
-        created networks
-        """
-        tenant_id = self.tenant_id
-        net_id = self.net_id
-        vlan_id = self.vlan_id
-        second_net_id = self.second_net_id
-        second_vlan_id = self.second_vlan_id
-
-        netid = self._cisco_nexus_plugin.delete_network(
-            tenant_id, net_id, vlan_id=str(vlan_id))
-
-        self.assertEqual(netid, net_id)
-
-        expected_second_net_id = self._cisco_nexus_plugin.delete_network(
-            tenant_id, second_net_id,
-            vlan_id=str(second_vlan_id))
-
-        self.assertEqual(expected_second_net_id, second_net_id)
-
-    def test_e_update_network_True(self):
-        """
-        Test to update a disabled network state to True
-        resulting in enabling a vlan corresponding to
-        that network to the configured nexus interfaces
-        """
-        tenant_id = self.tenant_id
-        net_name = self.net_name
-        net_id = self.net_id
-        vlan_name = self.vlan_name
-        vlan_id = self.vlan_id
-        second_vlan_id = self.second_vlan_id
-
-        self.test_c_update_network_False()
-
-        vlan_ids = str(vlan_id) + "," + str(second_vlan_id)
-        expected_net_id = self._cisco_nexus_plugin.update_network(
-            tenant_id, net_id, net_admin_state=True,
-            vlan_id=vlan_id, vlan_ids=str(vlan_ids))
-
-        self.assertEqual(expected_net_id, net_id)
+        expected_instance_id = self._cisco_nexus_plugin.delete_port(
+            INSTANCE, self.second_vlan_id
+        )
 
-        self.test_d_nexus_clean_vlan_update()
+        self.assertEqual(expected_instance_id, INSTANCE)
 
     def tearDown(self):
         """Clear the test environment"""
+        pass
         # Remove database contents
-        db.clear_db(network_models_v2.model_base.BASEV2)
+        #db.clear_db(network_models_v2.model_base.BASEV2)
index 331b5beb9b060bbe2c2f7189a21c4cd35eff83ec..c9c40cbc0ddb12230c0a11abd7fc2a4e37bdc5db 100644 (file)
@@ -17,3 +17,7 @@ sqlalchemy==0.7.9
 webob>=1.0.8
 python-keystoneclient>=0.2.0
 alembic>=0.4.1
+
+# Cisco plugin dependencies
+python-novaclient
+# End Cisco dependencies