From e609209299dec757c4c4cd954a7050969033fd98 Mon Sep 17 00:00:00 2001 From: Rohit Agarwalla Date: Tue, 7 Aug 2012 03:28:12 -0700 Subject: [PATCH] Enhancements to Cisco v2 meta-plugin 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 | 2 +- quantum/plugins/cisco/README | 32 +++ .../plugins/cisco/common/cisco_constants.py | 1 + quantum/plugins/cisco/db/network_db_v2.py | 12 +- quantum/plugins/cisco/db/network_models_v2.py | 6 +- .../plugins/cisco/models/virt_phy_sw_v2.py | 251 ++++++++++++++++++ quantum/plugins/cisco/network_plugin.py | 92 +++++-- .../nexus/cisco_nexus_network_driver_v2.py | 150 +++++++++++ .../cisco/nexus/cisco_nexus_plugin_v2.py | 26 +- .../cisco/nexus/cisco_nexus_snippets.py | 2 +- .../tests/unit/v2/test_network_plugin.py | 3 +- 11 files changed, 536 insertions(+), 41 deletions(-) create mode 100644 quantum/plugins/cisco/models/virt_phy_sw_v2.py create mode 100644 quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py diff --git a/etc/quantum/plugins/cisco/nexus.ini b/etc/quantum/plugins/cisco/nexus.ini index b5015db831..54bb967e01 100644 --- a/etc/quantum/plugins/cisco/nexus.ini +++ b/etc/quantum/plugins/cisco/nexus.ini @@ -7,5 +7,5 @@ ports= 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 diff --git a/quantum/plugins/cisco/README b/quantum/plugins/cisco/README index 0e3302c801..a205fe03b4 100755 --- a/quantum/plugins/cisco/README +++ b/quantum/plugins/cisco/README @@ -254,6 +254,38 @@ PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_multiport + vlan_max = + enable_tunneling = False + +5. For Nexus device sub-plugin configuration refer to the above sections How to test the installation ---------------------------- diff --git a/quantum/plugins/cisco/common/cisco_constants.py b/quantum/plugins/cisco/common/cisco_constants.py index 7019fe883a..2e25648419 100644 --- a/quantum/plugins/cisco/common/cisco_constants.py +++ b/quantum/plugins/cisco/common/cisco_constants.py @@ -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' diff --git a/quantum/plugins/cisco/db/network_db_v2.py b/quantum/plugins/cisco/db/network_db_v2.py index b617290d68..ab0c67df55 100644 --- a/quantum/plugins/cisco/db/network_db_v2.py +++ b/quantum/plugins/cisco/db/network_db_v2.py @@ -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 diff --git a/quantum/plugins/cisco/db/network_models_v2.py b/quantum/plugins/cisco/db/network_models_v2.py index 3d70ae275d..84cd2081fb 100644 --- a/quantum/plugins/cisco/db/network_models_v2.py +++ b/quantum/plugins/cisco/db/network_models_v2.py @@ -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 "" % (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 index 0000000000..653d39bdea --- /dev/null +++ b/quantum/plugins/cisco/models/virt_phy_sw_v2.py @@ -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 diff --git a/quantum/plugins/cisco/network_plugin.py b/quantum/plugins/cisco/network_plugin.py index 5c7fbf8aa7..9339d22463 100644 --- a/quantum/plugins/cisco/network_plugin.py +++ b/quantum/plugins/cisco/network_plugin.py @@ -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 + + def __init__(self): + """ + Loads the model class, initializes the DB, and credential store. + """ + 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 __init__(self): - """ - Initializes the DB, and credential store. - """ - cdb.initialize() - cred.Store.initialize() - self._model = importutils.import_object(conf.MODEL_CLASS) - super(PluginV2, self).__init__() - LOG.debug("Plugin initialization complete") - 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 index 0000000000..c3e6f6dacf --- /dev/null +++ b/quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py @@ -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(',') diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py b/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py index e893a996e3..a99785f262 100644 --- a/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py +++ b/quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py @@ -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} diff --git a/quantum/plugins/cisco/nexus/cisco_nexus_snippets.py b/quantum/plugins/cisco/nexus/cisco_nexus_snippets.py index 0ff0bebedb..7983cb9f7e 100644 --- a/quantum/plugins/cisco/nexus/cisco_nexus_snippets.py +++ b/quantum/plugins/cisco/nexus/cisco_nexus_snippets.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) # The following are standard strings, messages used to communicate with Nexus, EXEC_CONF_SNIPPET = """ - + <__XML__MODE__exec_configure>%s diff --git a/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py b/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py index b799207f47..89a4fa8018 100644 --- a/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py +++ b/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py @@ -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()