]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Enhancements to Cisco v2 meta-plugin
authorRohit Agarwalla <roagarwa@cisco.com>
Tue, 7 Aug 2012 10:28:12 +0000 (03:28 -0700)
committerRohit Agarwalla <roagarwa@cisco.com>
Wed, 15 Aug 2012 00:58:12 +0000 (17:58 -0700)
Implements blueprint cisco-v2-meta-plugin

This patch allows a stand alone plugin to be configured as a device sub-plugin.
(changes are contained within the cisco plugin only)

Change-Id: I4de53afc3a7e8c79ab8637fe04a90da1d1b05342

etc/quantum/plugins/cisco/nexus.ini
quantum/plugins/cisco/README
quantum/plugins/cisco/common/cisco_constants.py
quantum/plugins/cisco/db/network_db_v2.py
quantum/plugins/cisco/db/network_models_v2.py
quantum/plugins/cisco/models/virt_phy_sw_v2.py [new file with mode: 0644]
quantum/plugins/cisco/network_plugin.py
quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py [new file with mode: 0644]
quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py
quantum/plugins/cisco/nexus/cisco_nexus_snippets.py
quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py

index b5015db831abdd09b99f687198843d4bbd0edc0b..54bb967e012d2c8b1db874e49b37835b1a88a0c8 100644 (file)
@@ -7,5 +7,5 @@ ports=<put_interfaces_names_here_separated_by_commas>
 nexus_ssh_port=22
 
 [DRIVER]
-#name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
+#name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver
 name=quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver
index 0e3302c80147dd9e7c13faee9fe195866dfdc44f..a205fe03b496dcc0358a315d15e8788948518888 100755 (executable)
@@ -254,6 +254,38 @@ PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_multiport <tenant
 
     (Note that you should not be using the create port core API in the above case.)
 
+Using an independent plugin as a device sub-plugin
+-------------------------------------------------
+
+If you would like to use an independent virtual switch plugin as one of the sub-plugins
+(for eg: the OpenVSwitch plugin) with the nexus device sub-plugin perform the following steps:
+
+(The following instructions are with respect to the OpenVSwitch plugin)
+1. Update etc/quantum/plugins/cisco/l2network_plugin.ini
+   In the [MODEL] section of the configuration file put the following configuration
+   (note that this should be the only configuration in this section, all other configuration
+   should be either removed or commented)
+
+    model_class=quantum.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2
+
+2. Update etc/quantum/plugins/cisco/cisco_plugins.ini
+   In the [PLUGINS] section of the configuration file put the following configuration:
+
+   vswitch_plugin=quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2
+
+3. Set the DB name, the same name has to be configured in three places:
+   In etc/quantum/plugins/cisco/conf/db_conn.ini set the "name" value
+   In /etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini set the "sql_connection"
+   In /etc/quantum/dhcp_agent.ini set the "db_connection"
+
+4. The range of VLAN IDs has to be set in the OpenVSwitch configuration file:
+   In /etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
+   Set:
+   vlan_min = <lower_id>
+   vlan_max = <higher_id>
+   enable_tunneling = False
+
+5. For Nexus device sub-plugin configuration refer to the above sections
 
 How to test the installation
 ----------------------------
index 7019fe883ae663cbddc64359b4c1ae7302e7ae62..2e2564841986240b0f78433aabbf9e5ecbf38b1e 100644 (file)
@@ -117,6 +117,7 @@ UCS_PLUGIN = 'ucs_plugin'
 NEXUS_PLUGIN = 'nexus_plugin'
 UCS_INVENTORY = 'ucs_inventory'
 NEXUS_INVENTORY = 'nexus_inventory'
+VSWITCH_PLUGIN = 'vswitch_plugin'
 
 PLUGIN_OBJ_REF = 'plugin-obj-ref'
 PARAM_LIST = 'param-list'
index b617290d68f7c1fd4074f09470c8c0a0cae8f051..ab0c67df558e4740b2601c0a4412c4b437c2ad93 100644 (file)
@@ -146,7 +146,7 @@ def get_all_vlan_bindings():
     LOG.debug("get_all_vlan_bindings() called")
     session = db.get_session()
     try:
-        bindings = session.query(network_models_v2.VlanBinding).all()
+        bindings = session.query(network_models_v2.Vlan_Binding).all()
         return bindings
     except exc.NoResultFound:
         return []
@@ -157,7 +157,7 @@ def get_vlan_binding(netid):
     LOG.debug("get_vlan_binding() called")
     session = db.get_session()
     try:
-        binding = (session.query(network_models_v2.VlanBinding).
+        binding = (session.query(network_models_v2.Vlan_Binding).
                    filter_by(network_id=netid).one())
         return binding
     except exc.NoResultFound:
@@ -169,12 +169,12 @@ def add_vlan_binding(vlanid, vlanname, netid):
     LOG.debug("add_vlan_binding() called")
     session = db.get_session()
     try:
-        binding = (session.query(network_models_v2.VlanBinding).
+        binding = (session.query(network_models_v2.Vlan_Binding).
                    filter_by(vlan_id=vlanid).one())
         raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
                                                     network_id=netid)
     except exc.NoResultFound:
-        binding = network_models_v2.VlanBinding(vlanid, vlanname, netid)
+        binding = network_models_v2.Vlan_Binding(vlanid, vlanname, netid)
         session.add(binding)
         session.flush()
         return binding
@@ -185,7 +185,7 @@ def remove_vlan_binding(netid):
     LOG.debug("remove_vlan_binding() called")
     session = db.get_session()
     try:
-        binding = (session.query(network_models_v2.VlanBinding).
+        binding = (session.query(network_models_v2.Vlan_Binding).
                    filter_by(network_id=netid).one())
         session.delete(binding)
         session.flush()
@@ -199,7 +199,7 @@ def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
     LOG.debug("update_vlan_binding() called")
     session = db.get_session()
     try:
-        binding = (session.query(network_models_v2.VlanBinding).
+        binding = (session.query(network_models_v2.Vlan_Binding).
                    filter_by(network_id=netid).one())
         if newvlanid:
             binding["vlan_id"] = newvlanid
index 3d70ae275d577f956ef4d088a51ef4bfa77d9ef8..84cd2081fba3f131d2cdcc65d727c5e68b9e727d 100644 (file)
@@ -68,7 +68,7 @@ class L2NetworkBase(object):
 
 class VlanID(model_base.BASEV2, L2NetworkBase):
     """Represents a vlan_id usage"""
-    __tablename__ = 'vlan_ids'
+    __tablename__ = 'cisco_vlan_ids'
 
     vlan_id = Column(Integer, primary_key=True)
     vlan_used = Column(Boolean)
@@ -81,9 +81,9 @@ class VlanID(model_base.BASEV2, L2NetworkBase):
         return "<VlanID(%d,%s)>" % (self.vlan_id, self.vlan_used)
 
 
-class VlanBinding(model_base.BASEV2, L2NetworkBase):
+class Vlan_Binding(model_base.BASEV2, L2NetworkBase):
     """Represents a binding of vlan_id to network_id"""
-    __tablename__ = 'vlan_bindings'
+    __tablename__ = 'cisco_vlan_bindings'
 
     vlan_id = Column(Integer, primary_key=True)
     vlan_name = Column(String(255))
diff --git a/quantum/plugins/cisco/models/virt_phy_sw_v2.py b/quantum/plugins/cisco/models/virt_phy_sw_v2.py
new file mode 100644 (file)
index 0000000..653d39b
--- /dev/null
@@ -0,0 +1,251 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+from copy import deepcopy
+import inspect
+import logging
+
+from quantum.manager import QuantumManager
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+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__)
+
+
+class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
+    """
+    This implementation works with OVS and Nexus plugin for the
+    following topology:
+    One or more servers to a nexus switch.
+    """
+    MANAGE_STATE = True
+    supported_extension_aliases = []
+    _plugins = {}
+    _inventory = {}
+    _methods_to_delegate = ['update_network', 'get_network', 'get_networks',
+                            'create_port', 'delete_port', 'update_port',
+                            'get_port', 'get_ports',
+                            'create_subnet', 'delete_subnet', 'update_subnet',
+                            'get_subnet', 'get_subnets']
+
+    def __init__(self):
+        """
+        Initialize the segmentation manager, check which device plugins are
+        configured, and load the inventories those device plugins for which the
+        inventory is configured
+        """
+        cdb.initialize()
+        cred.Store.initialize()
+        for key in conf.PLUGINS[const.PLUGINS].keys():
+            plugin_obj = conf.PLUGINS[const.PLUGINS][key]
+            self._plugins[key] = importutils.import_object(plugin_obj)
+            LOG.debug("Loaded device plugin %s\n" %
+                      conf.PLUGINS[const.PLUGINS][key])
+            if key in conf.PLUGINS[const.INVENTORY].keys():
+                inventory_obj = conf.PLUGINS[const.INVENTORY][key]
+                self._inventory[key] = importutils.import_object(inventory_obj)
+                LOG.debug("Loaded device inventory %s\n" %
+                          conf.PLUGINS[const.INVENTORY][key])
+
+        LOG.debug("%s.%s init done" % (__name__, self.__class__.__name__))
+
+    def __getattribute__(self, name):
+        methods = object.__getattribute__(self, "_methods_to_delegate")
+        if name in methods:
+            return getattr(object.__getattribute__(self, "_plugins")
+                           [const.VSWITCH_PLUGIN], name)
+        else:
+            return object.__getattribute__(self, name)
+
+    def _func_name(self, offset=0):
+        """Get the name of the calling function"""
+        frame_record = inspect.stack()[1 + offset]
+        func_name = frame_record[3]
+        return func_name
+
+    def _invoke_plugin_per_device(self, plugin_key, function_name, args):
+        """
+        Invokes a device plugin's relevant functions (on the it's
+        inventory and plugin implementation) for completing this operation.
+        """
+        if not plugin_key in self._plugins.keys():
+            LOG.info("No %s Plugin loaded" % plugin_key)
+            LOG.info("%s: %s with args %s ignored" %
+                     (plugin_key, function_name, args))
+            return
+        device_params = self._invoke_inventory(plugin_key, function_name,
+                                               args)
+        device_ips = device_params[const.DEVICE_IP]
+        if not device_ips:
+            return [self._invoke_plugin(plugin_key, function_name, args,
+                                        device_params)]
+        else:
+            output = []
+            for device_ip in device_ips:
+                new_device_params = deepcopy(device_params)
+                new_device_params[const.DEVICE_IP] = device_ip
+                output.append(self._invoke_plugin(plugin_key, function_name,
+                                                  args, new_device_params))
+            return output
+
+    def _invoke_inventory(self, plugin_key, function_name, args):
+        """
+        Invokes the relevant function on a device plugin's
+        inventory for completing this operation.
+        """
+        if not plugin_key in self._inventory.keys():
+            LOG.info("No %s inventory loaded" % plugin_key)
+            LOG.info("%s: %s with args %s ignored" %
+                     (plugin_key, function_name, args))
+            return {const.DEVICE_IP: []}
+        else:
+            return getattr(self._inventory[plugin_key], function_name)(args)
+
+    def _invoke_plugin(self, plugin_key, function_name, args, kwargs):
+        """
+        Invokes the relevant function on a device plugin's
+        implementation for completing this operation.
+        """
+        func = getattr(self._plugins[plugin_key], function_name)
+        func_args_len = int(inspect.getargspec(func).args.__len__()) - 1
+        fargs, varargs, varkw, defaults = inspect.getargspec(func)
+        if args.__len__() > func_args_len:
+            func_args = args[:func_args_len]
+            extra_args = args[func_args_len:]
+            for dict_arg in extra_args:
+                for k, v in dict_arg.iteritems():
+                    kwargs[k] = v
+            return func(*func_args, **kwargs)
+        else:
+            if (varkw == 'kwargs'):
+                return func(*args, **kwargs)
+            else:
+                return func(*args)
+
+    def create_network(self, context, network):
+        """
+        Perform this operation in the context of the configured device
+        plugins.
+        """
+        LOG.debug("create_network() called\n")
+        try:
+            args = [context, network]
+            ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+                                                        self._func_name(),
+                                                        args)
+            vlan_id = odb.get_vlan(ovs_output[0]['id'])
+            vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id)
+            vlan_ids = odb.get_vlans()
+            vlanids = ''
+            for v_id in vlan_ids:
+                vlanids = str(v_id[0]) + ',' + vlanids
+            vlanids = vlanids.strip(',')
+            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
+            raise
+
+    def update_network(self, context, id, network):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def delete_network(self, context, id):
+        """
+        Perform this operation in the context of the configured device
+        plugins.
+        """
+        try:
+            base_plugin_ref = QuantumManager.get_plugin()
+            n = base_plugin_ref.get_network(context, id)
+            tenant_id = n['tenant_id']
+            vlan_id = odb.get_vlan(id)
+            output = []
+            args = [tenant_id, id, {const.VLANID:vlan_id},
+                    {const.CONTEXT:context},
+                    {const.BASE_PLUGIN_REF:base_plugin_ref}]
+            nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+                                                          self._func_name(),
+                                                          args)
+            args = [context, id]
+            ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+                                                        self._func_name(),
+                                                        args)
+            return ovs_output[0]
+        except:
+            raise
+
+    def get_network(self, context, id, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def get_networks(self, context, filters=None, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def create_port(self, context, port):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def get_port(self, context, id, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def get_ports(self, context, filters=None, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def update_port(self, context, id, port):
+        """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 create_subnet(self, context, subnet):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def update_subnet(self, context, id, subnet):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def get_subnet(self, context, id, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def delete_subnet(self, context, id, kwargs):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
+
+    def get_subnets(self, context, filters=None, fields=None, verbose=None):
+        """For this model this method will be delegated to vswitch plugin"""
+        pass
index 5c7fbf8aa7906b32a8d6c5496d6599c62b4e24cd..9339d2246343330b9e8e124133dae7844af983b9 100644 (file)
@@ -29,45 +29,83 @@ from quantum.plugins.cisco.common import cisco_exceptions as cexc
 from quantum.plugins.cisco.common import cisco_utils as cutil
 from quantum.plugins.cisco.db import network_db_v2 as cdb
 from quantum.plugins.cisco import l2network_plugin_configuration as conf
-from quantum.quantum_plugin_base_v2 import QuantumPluginBaseV2
 
 LOG = logging.getLogger(__name__)
 
 
 class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
     """
-    Plugin with v2 API support for multiple sub-plugins
+    Meta-Plugin with v2 API support for multiple sub-plugins.
     """
     supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
                                    "Cisco qos", "Cisco Nova Tenant",
                                    "Cisco Multiport"]
+    _methods_to_delegate = ['create_network', 'delete_network',
+                            'update_network', 'get_network', 'get_networks',
+                            'create_port', 'delete_port', 'update_port',
+                            'get_port', 'get_ports',
+                            'create_subnet', 'delete_subnet', 'update_subnet',
+                            'get_subnet', 'get_subnets']
+    _master = True
 
-    """
-    Core API implementation
-    """
     def __init__(self):
         """
-        Initializes the DB, and credential store.
+        Loads the model class, initializes the DB, and credential store.
         """
-        cdb.initialize()
-        cred.Store.initialize()
         self._model = importutils.import_object(conf.MODEL_CLASS)
+        if hasattr(self._model, "MANAGE_STATE") and self._model.MANAGE_STATE:
+            self._master = False
+            LOG.debug("Model %s manages state" % conf.MODEL_CLASS)
+        else:
+            cdb.initialize()
+            cred.Store.initialize()
+
+        if hasattr(self._model, "supported_extension_aliases"):
+            self.supported_extension_aliases.extend(
+                self._model.supported_extension_aliases)
+
         super(PluginV2, self).__init__()
         LOG.debug("Plugin initialization complete")
 
+    def __getattribute__(self, name):
+        """
+        When the configured model class offers to manage the state of the
+        logical resources, we delegate the core API calls directly to it.
+        """
+        master = object.__getattribute__(self, "_master")
+        methods = object.__getattribute__(self, "_methods_to_delegate")
+        if not master and name in methods:
+            return getattr(object.__getattribute__(self, "_model"),
+                           name)
+        else:
+            return object.__getattribute__(self, name)
+
+    def __getattr__(self, name):
+        """
+        This delegates the calls to the extensions explicitly implemented by
+        the model.
+        """
+        if hasattr(self._model, name):
+            return getattr(self._model, name)
+
+    """
+    Core API implementation
+    """
     def create_network(self, context, network):
         """
         Creates a new Virtual Network, and assigns it
         a symbolic name.
         """
         LOG.debug("create_network() called\n")
-        new_network = super(PluginV2, self).create_network(context, network)
+        new_network = super(PluginV2, self).create_network(context,
+                                                           network)
         try:
             self._invoke_device_plugins(self._func_name(), [context,
                                                             new_network])
             return new_network
         except:
-            super(PluginV2, self).delete_network(context, new_network['id'])
+            super(PluginV2, self).delete_network(context,
+                                                 new_network['id'])
             raise
 
     def update_network(self, context, id, network):
@@ -79,7 +117,8 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
         try:
             self._invoke_device_plugins(self._func_name(), [context, id,
                                                             network])
-            return super(PluginV2, self).update_network(context, id, network)
+            return super(PluginV2, self).update_network(context, id,
+                                                        network)
         except:
             raise
 
@@ -92,7 +131,6 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
         #We first need to check if there are any ports on this network
         with context.session.begin():
             network = self._get_network(context, id)
-
             filter = {'network_id': [id]}
             ports = self.get_ports(context, filters=filter)
             if ports:
@@ -109,6 +147,22 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
         except:
             raise
 
+    def get_network(self, context, id, fields=None, verbose=None):
+        """
+        Gets a particular network
+        """
+        LOG.debug("get_network() called\n")
+        return super(PluginV2, self).get_network(context, id,
+                                                 fields, verbose)
+
+    def get_networks(self, context, filters=None, fields=None, verbose=None):
+        """
+        Gets all networks
+        """
+        LOG.debug("get_networks() called\n")
+        return super(PluginV2, self).get_networks(context, filters,
+                                                  fields, verbose)
+
     def create_port(self, context, port):
         """
         Creates a port on the specified Virtual Network.
@@ -446,9 +500,11 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
     """
     def _invoke_device_plugins(self, function_name, args):
         """
-        All device-specific calls are delegated to the model
+        Device-specific calls including core API and extensions are
+        delegated to the model.
         """
-        return getattr(self._model, function_name)(*args)
+        if hasattr(self._model, function_name):
+            return getattr(self._model, function_name)(*args)
 
     def _func_name(self, offset=0):
         """Getting the name of the calling funciton"""
diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py b/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
new file mode 100644 (file)
index 0000000..c3e6f6d
--- /dev/null
@@ -0,0 +1,150 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Debojyoti Dutta, Cisco Systems, Inc.
+# @author: Edgar Magana, Cisco Systems Inc.
+#
+"""
+Implements a Nexus-OS NETCONF over SSHv2 API Client
+"""
+
+import logging
+
+from ncclient import manager
+
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco.nexus import cisco_nexus_snippets as snipp
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CiscoNEXUSDriver():
+    """
+    Nexus Driver Main Class
+    """
+    def __init__(self):
+        pass
+
+    def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user,
+                     nexus_password):
+        """
+        Makes the SSH connection to the Nexus Switch
+        """
+        man = manager.connect(host=nexus_host, port=nexus_ssh_port,
+                              username=nexus_user, password=nexus_password)
+        return man
+
+    def create_xml_snippet(self, cutomized_config):
+        """
+        Creates the Proper XML structure for the Nexus Switch Configuration
+        """
+        conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config)
+        return conf_xml_snippet
+
+    def enable_vlan(self, mgr, vlanid, vlanname):
+        """
+        Creates a VLAN on Nexus Switch given the VLAN ID and Name
+        """
+        confstr = snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname)
+        confstr = self.create_xml_snippet(confstr)
+        mgr.edit_config(target='running', config=confstr)
+
+    def disable_vlan(self, mgr, 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)
+        mgr.edit_config(target='running', config=confstr)
+
+    def enable_port_trunk(self, mgr, interface):
+        """
+        Enables trunk mode an interface on Nexus Switch
+        """
+        confstr = snipp.CMD_PORT_TRUNK % (interface)
+        confstr = self.create_xml_snippet(confstr)
+        LOG.debug("NexusDriver: %s" % confstr)
+        mgr.edit_config(target='running', config=confstr)
+
+    def disable_switch_port(self, mgr, interface):
+        """
+        Disables trunk mode an interface on Nexus Switch
+        """
+        confstr = snipp.CMD_NO_SWITCHPORT % (interface)
+        confstr = self.create_xml_snippet(confstr)
+        LOG.debug("NexusDriver: %s" % confstr)
+        mgr.edit_config(target='running', config=confstr)
+
+    def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+        """
+        Enables trunk mode vlan access an interface on Nexus Switch given
+        VLANID
+        """
+        confstr = snipp.CMD_VLAN_INT_SNIPPET % (interface, vlanid)
+        confstr = self.create_xml_snippet(confstr)
+        LOG.debug("NexusDriver: %s" % confstr)
+        mgr.edit_config(target='running', config=confstr)
+
+    def disable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+        """
+        Enables trunk mode vlan access an interface on Nexus Switch given
+        VLANID
+        """
+        confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
+        confstr = self.create_xml_snippet(confstr)
+        LOG.debug("NexusDriver: %s" % confstr)
+        mgr.edit_config(target='running', config=confstr)
+
+    def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
+                    nexus_password, nexus_ports,
+                    nexus_ssh_port, vlan_ids=None):
+        """
+        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)
+
+    def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
+                    nexus_ports, nexus_ssh_port):
+        """
+        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)
+
+    def build_vlans_cmd(self):
+        """
+        Builds a string with all the VLANs on the same Switch
+        """
+        assigned_vlan = cdb.get_all_vlanids_used()
+        vlans = ''
+        for vlanid in assigned_vlan:
+            vlans = str(vlanid["vlan_id"]) + ',' + vlans
+        if vlans == '':
+            vlans = 'none'
+        return vlans.strip(',')
index e893a996e341169c4aaa54c35153f8cceea253ca..a99785f262bd5ec1a1e1daf3612eca8c35b17373 100644 (file)
@@ -71,10 +71,14 @@ class NexusPlugin(L2DevicePluginBase):
         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)
+            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))
@@ -95,10 +99,15 @@ class NexusPlugin(L2DevicePluginBase):
         from the relevant interfaces
         """
         LOG.debug("NexusPlugin:delete_network() called\n")
+        vlan_id = None
         context = kwargs[const.CONTEXT]
         base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
-        vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
-                                                context, base_plugin_ref)
+        for key in kwargs:
+            if 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)
@@ -185,10 +194,8 @@ class NexusPlugin(L2DevicePluginBase):
         """
         Obtain the VLAN ID given the Network ID
         """
-        net = self._get_network(tenant_id, network_id, context,
-                                base_plugin_ref)
-        vlan_id = net[const.NET_VLAN_ID]
-        return vlan_id
+        vlan = cdb.get_vlan_binding(network_id)
+        return vlan.vlan_id
 
     def _get_network(self, tenant_id, network_id, context, base_plugin_ref):
         """
@@ -197,8 +204,5 @@ class NexusPlugin(L2DevicePluginBase):
         network = base_plugin_ref._get_network(context, network_id)
         if not network:
             raise exc.NetworkNotFound(net_id=network_id)
-        vlan = cdb.get_vlan_binding(network_id)
         return {const.NET_ID: network_id, const.NET_NAME: network.name,
-                const.NET_PORTS: network.ports,
-                const.NET_VLAN_NAME: vlan.vlan_name,
-                const.NET_VLAN_ID: vlan.vlan_id}
+                const.NET_PORTS: network.ports}
index 0ff0bebedb76d12c896298cec7f24ad7ea74822c..7983cb9f7ecfe3065ffe430b74ef04d16edcbb22 100644 (file)
@@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
 # The following are standard strings, messages used to communicate with Nexus,
 EXEC_CONF_SNIPPET = """
       <config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
-        <configure xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli">
+        <configure>
           <__XML__MODE__exec_configure>%s
           </__XML__MODE__exec_configure>
         </configure>
index b799207f4701c14ad24b0c4d8dd54640f106d870..89a4fa8018455a6f7cfa305c9d5dac9979fd0841 100644 (file)
@@ -21,6 +21,7 @@ import os
 from quantum.api.v2.router import APIRouter
 from quantum.common import config
 from quantum.db import api as db
+from quantum.manager import QuantumManager
 from quantum.plugins.cisco.db import network_models_v2
 from quantum.openstack.common import cfg
 from quantum.tests.unit import test_db_plugin
@@ -38,7 +39,7 @@ class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase):
     def setUp(self):
         db._ENGINE = None
         db._MAKER = None
-
+        QuantumManager._instance = None
         self._tenant_id = 'test-tenant'
 
         json_deserializer = JSONDeserializer()