Merge "Enhancements to Cisco v2 meta-plugin"
This commit is contained in:
commit
d51ad9a9cb
@ -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
|
||||
|
@ -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
|
||||
----------------------------
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
251
quantum/plugins/cisco/models/virt_phy_sw_v2.py
Normal file
251
quantum/plugins/cisco/models/virt_phy_sw_v2.py
Normal file
@ -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
|
@ -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,8 +500,10 @@ 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.
|
||||
"""
|
||||
if hasattr(self._model, function_name):
|
||||
return getattr(self._model, function_name)(*args)
|
||||
|
||||
def _func_name(self, offset=0):
|
||||
|
150
quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
Normal file
150
quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
Normal file
@ -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(',')
|
@ -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,8 +99,13 @@ 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]
|
||||
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)
|
||||
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user