Merge "Enhancements to Cisco v2 meta-plugin"
This commit is contained in:
commit
8a09da6495
@ -7,5 +7,5 @@ ports=<put_interfaces_names_here_separated_by_commas>
|
|||||||
nexus_ssh_port=22
|
nexus_ssh_port=22
|
||||||
|
|
||||||
[DRIVER]
|
[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
|
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.)
|
(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
|
How to test the installation
|
||||||
----------------------------
|
----------------------------
|
||||||
|
@ -117,6 +117,7 @@ UCS_PLUGIN = 'ucs_plugin'
|
|||||||
NEXUS_PLUGIN = 'nexus_plugin'
|
NEXUS_PLUGIN = 'nexus_plugin'
|
||||||
UCS_INVENTORY = 'ucs_inventory'
|
UCS_INVENTORY = 'ucs_inventory'
|
||||||
NEXUS_INVENTORY = 'nexus_inventory'
|
NEXUS_INVENTORY = 'nexus_inventory'
|
||||||
|
VSWITCH_PLUGIN = 'vswitch_plugin'
|
||||||
|
|
||||||
PLUGIN_OBJ_REF = 'plugin-obj-ref'
|
PLUGIN_OBJ_REF = 'plugin-obj-ref'
|
||||||
PARAM_LIST = 'param-list'
|
PARAM_LIST = 'param-list'
|
||||||
|
@ -146,7 +146,7 @@ def get_all_vlan_bindings():
|
|||||||
LOG.debug("get_all_vlan_bindings() called")
|
LOG.debug("get_all_vlan_bindings() called")
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
bindings = session.query(network_models_v2.VlanBinding).all()
|
bindings = session.query(network_models_v2.Vlan_Binding).all()
|
||||||
return bindings
|
return bindings
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
return []
|
return []
|
||||||
@ -157,7 +157,7 @@ def get_vlan_binding(netid):
|
|||||||
LOG.debug("get_vlan_binding() called")
|
LOG.debug("get_vlan_binding() called")
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
binding = (session.query(network_models_v2.VlanBinding).
|
binding = (session.query(network_models_v2.Vlan_Binding).
|
||||||
filter_by(network_id=netid).one())
|
filter_by(network_id=netid).one())
|
||||||
return binding
|
return binding
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
@ -169,12 +169,12 @@ def add_vlan_binding(vlanid, vlanname, netid):
|
|||||||
LOG.debug("add_vlan_binding() called")
|
LOG.debug("add_vlan_binding() called")
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
binding = (session.query(network_models_v2.VlanBinding).
|
binding = (session.query(network_models_v2.Vlan_Binding).
|
||||||
filter_by(vlan_id=vlanid).one())
|
filter_by(vlan_id=vlanid).one())
|
||||||
raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
|
raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
|
||||||
network_id=netid)
|
network_id=netid)
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
binding = network_models_v2.VlanBinding(vlanid, vlanname, netid)
|
binding = network_models_v2.Vlan_Binding(vlanid, vlanname, netid)
|
||||||
session.add(binding)
|
session.add(binding)
|
||||||
session.flush()
|
session.flush()
|
||||||
return binding
|
return binding
|
||||||
@ -185,7 +185,7 @@ def remove_vlan_binding(netid):
|
|||||||
LOG.debug("remove_vlan_binding() called")
|
LOG.debug("remove_vlan_binding() called")
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
binding = (session.query(network_models_v2.VlanBinding).
|
binding = (session.query(network_models_v2.Vlan_Binding).
|
||||||
filter_by(network_id=netid).one())
|
filter_by(network_id=netid).one())
|
||||||
session.delete(binding)
|
session.delete(binding)
|
||||||
session.flush()
|
session.flush()
|
||||||
@ -199,7 +199,7 @@ def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
|
|||||||
LOG.debug("update_vlan_binding() called")
|
LOG.debug("update_vlan_binding() called")
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
try:
|
try:
|
||||||
binding = (session.query(network_models_v2.VlanBinding).
|
binding = (session.query(network_models_v2.Vlan_Binding).
|
||||||
filter_by(network_id=netid).one())
|
filter_by(network_id=netid).one())
|
||||||
if newvlanid:
|
if newvlanid:
|
||||||
binding["vlan_id"] = newvlanid
|
binding["vlan_id"] = newvlanid
|
||||||
|
@ -68,7 +68,7 @@ class L2NetworkBase(object):
|
|||||||
|
|
||||||
class VlanID(model_base.BASEV2, L2NetworkBase):
|
class VlanID(model_base.BASEV2, L2NetworkBase):
|
||||||
"""Represents a vlan_id usage"""
|
"""Represents a vlan_id usage"""
|
||||||
__tablename__ = 'vlan_ids'
|
__tablename__ = 'cisco_vlan_ids'
|
||||||
|
|
||||||
vlan_id = Column(Integer, primary_key=True)
|
vlan_id = Column(Integer, primary_key=True)
|
||||||
vlan_used = Column(Boolean)
|
vlan_used = Column(Boolean)
|
||||||
@ -81,9 +81,9 @@ class VlanID(model_base.BASEV2, L2NetworkBase):
|
|||||||
return "<VlanID(%d,%s)>" % (self.vlan_id, self.vlan_used)
|
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"""
|
"""Represents a binding of vlan_id to network_id"""
|
||||||
__tablename__ = 'vlan_bindings'
|
__tablename__ = 'cisco_vlan_bindings'
|
||||||
|
|
||||||
vlan_id = Column(Integer, primary_key=True)
|
vlan_id = Column(Integer, primary_key=True)
|
||||||
vlan_name = Column(String(255))
|
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.common import cisco_utils as cutil
|
||||||
from quantum.plugins.cisco.db import network_db_v2 as cdb
|
from quantum.plugins.cisco.db import network_db_v2 as cdb
|
||||||
from quantum.plugins.cisco import l2network_plugin_configuration as conf
|
from quantum.plugins.cisco import l2network_plugin_configuration as conf
|
||||||
from quantum.quantum_plugin_base_v2 import QuantumPluginBaseV2
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
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",
|
supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
|
||||||
"Cisco qos", "Cisco Nova Tenant",
|
"Cisco qos", "Cisco Nova Tenant",
|
||||||
"Cisco Multiport"]
|
"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
|
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):
|
def create_network(self, context, network):
|
||||||
"""
|
"""
|
||||||
Creates a new Virtual Network, and assigns it
|
Creates a new Virtual Network, and assigns it
|
||||||
a symbolic name.
|
a symbolic name.
|
||||||
"""
|
"""
|
||||||
LOG.debug("create_network() called\n")
|
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:
|
try:
|
||||||
self._invoke_device_plugins(self._func_name(), [context,
|
self._invoke_device_plugins(self._func_name(), [context,
|
||||||
new_network])
|
new_network])
|
||||||
return new_network
|
return new_network
|
||||||
except:
|
except:
|
||||||
super(PluginV2, self).delete_network(context, new_network['id'])
|
super(PluginV2, self).delete_network(context,
|
||||||
|
new_network['id'])
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
def update_network(self, context, id, network):
|
||||||
@ -79,7 +117,8 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
try:
|
try:
|
||||||
self._invoke_device_plugins(self._func_name(), [context, id,
|
self._invoke_device_plugins(self._func_name(), [context, id,
|
||||||
network])
|
network])
|
||||||
return super(PluginV2, self).update_network(context, id, network)
|
return super(PluginV2, self).update_network(context, id,
|
||||||
|
network)
|
||||||
except:
|
except:
|
||||||
raise
|
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
|
#We first need to check if there are any ports on this network
|
||||||
with context.session.begin():
|
with context.session.begin():
|
||||||
network = self._get_network(context, id)
|
network = self._get_network(context, id)
|
||||||
|
|
||||||
filter = {'network_id': [id]}
|
filter = {'network_id': [id]}
|
||||||
ports = self.get_ports(context, filters=filter)
|
ports = self.get_ports(context, filters=filter)
|
||||||
if ports:
|
if ports:
|
||||||
@ -109,6 +147,22 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
except:
|
except:
|
||||||
raise
|
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):
|
def create_port(self, context, port):
|
||||||
"""
|
"""
|
||||||
Creates a port on the specified Virtual Network.
|
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):
|
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):
|
def _func_name(self, offset=0):
|
||||||
"""Getting the name of the calling funciton"""
|
"""Getting the name of the calling funciton"""
|
||||||
|
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
|
for this VLAN
|
||||||
"""
|
"""
|
||||||
LOG.debug("NexusPlugin:create_network() called\n")
|
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(
|
self._client.create_vlan(
|
||||||
vlan_name, str(vlan_id), self._nexus_ip,
|
vlan_name, str(vlan_id), self._nexus_ip,
|
||||||
self._nexus_username, self._nexus_password,
|
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:
|
for ports in self._nexus_ports:
|
||||||
try:
|
try:
|
||||||
nxos_db.add_nexusport_binding(ports, str(vlan_id))
|
nxos_db.add_nexusport_binding(ports, str(vlan_id))
|
||||||
@ -95,10 +99,15 @@ class NexusPlugin(L2DevicePluginBase):
|
|||||||
from the relevant interfaces
|
from the relevant interfaces
|
||||||
"""
|
"""
|
||||||
LOG.debug("NexusPlugin:delete_network() called\n")
|
LOG.debug("NexusPlugin:delete_network() called\n")
|
||||||
|
vlan_id = None
|
||||||
context = kwargs[const.CONTEXT]
|
context = kwargs[const.CONTEXT]
|
||||||
base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
|
base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
|
||||||
vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
|
for key in kwargs:
|
||||||
context, base_plugin_ref)
|
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)
|
ports_id = nxos_db.get_nexusport_binding(vlan_id)
|
||||||
LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
|
LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
|
||||||
nxos_db.remove_nexusport_binding(vlan_id)
|
nxos_db.remove_nexusport_binding(vlan_id)
|
||||||
@ -185,10 +194,8 @@ class NexusPlugin(L2DevicePluginBase):
|
|||||||
"""
|
"""
|
||||||
Obtain the VLAN ID given the Network ID
|
Obtain the VLAN ID given the Network ID
|
||||||
"""
|
"""
|
||||||
net = self._get_network(tenant_id, network_id, context,
|
vlan = cdb.get_vlan_binding(network_id)
|
||||||
base_plugin_ref)
|
return vlan.vlan_id
|
||||||
vlan_id = net[const.NET_VLAN_ID]
|
|
||||||
return vlan_id
|
|
||||||
|
|
||||||
def _get_network(self, tenant_id, network_id, context, base_plugin_ref):
|
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)
|
network = base_plugin_ref._get_network(context, network_id)
|
||||||
if not network:
|
if not network:
|
||||||
raise exc.NetworkNotFound(net_id=network_id)
|
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,
|
return {const.NET_ID: network_id, const.NET_NAME: network.name,
|
||||||
const.NET_PORTS: network.ports,
|
const.NET_PORTS: network.ports}
|
||||||
const.NET_VLAN_NAME: vlan.vlan_name,
|
|
||||||
const.NET_VLAN_ID: vlan.vlan_id}
|
|
||||||
|
@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
# The following are standard strings, messages used to communicate with Nexus,
|
# The following are standard strings, messages used to communicate with Nexus,
|
||||||
EXEC_CONF_SNIPPET = """
|
EXEC_CONF_SNIPPET = """
|
||||||
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
|
<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>%s
|
||||||
</__XML__MODE__exec_configure>
|
</__XML__MODE__exec_configure>
|
||||||
</configure>
|
</configure>
|
||||||
|
@ -21,6 +21,7 @@ import os
|
|||||||
from quantum.api.v2.router import APIRouter
|
from quantum.api.v2.router import APIRouter
|
||||||
from quantum.common import config
|
from quantum.common import config
|
||||||
from quantum.db import api as db
|
from quantum.db import api as db
|
||||||
|
from quantum.manager import QuantumManager
|
||||||
from quantum.plugins.cisco.db import network_models_v2
|
from quantum.plugins.cisco.db import network_models_v2
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.tests.unit import test_db_plugin
|
from quantum.tests.unit import test_db_plugin
|
||||||
@ -38,7 +39,7 @@ class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
db._ENGINE = None
|
db._ENGINE = None
|
||||||
db._MAKER = None
|
db._MAKER = None
|
||||||
|
QuantumManager._instance = None
|
||||||
self._tenant_id = 'test-tenant'
|
self._tenant_id = 'test-tenant'
|
||||||
|
|
||||||
json_deserializer = JSONDeserializer()
|
json_deserializer = JSONDeserializer()
|
||||||
|
Loading…
Reference in New Issue
Block a user