Merge trunk

Now re-submitting admin credentials if token expires
This commit is contained in:
Salvatore Orlando 2011-08-31 11:52:41 +01:00
commit 42ddbf938a
46 changed files with 3028 additions and 648 deletions

View File

@ -17,13 +17,13 @@ api_extensions_path = extensions
[composite:quantum]
use = egg:Paste#urlmap
/: quantumversions
/v0.1: quantumapi
/v1.0: quantumapi
[pipeline:quantumapi]
# To enable keystone integration uncomment the following line and
# comment the next one
#pipeline = authN authZ extensions quantumapiapp
pipeline = extensions quantumapiapp
pipeline = authN authZ extensions quantumapiapp
#pipeline = extensions quantumapiapp
[filter:authN]
@ -46,4 +46,4 @@ paste.filter_factory = quantum.common.extensions:plugin_aware_extension_middlewa
paste.app_factory = quantum.api.versions:Versions.factory
[app:quantumapiapp]
paste.app_factory = quantum.api:APIRouterV01.factory
paste.app_factory = quantum.api:APIRouterV1.factory

View File

@ -63,8 +63,8 @@ class Novatenant(object):
""" Returns Ext Resource """
parent_resource = dict(member_name="tenant",
collection_name="extensions/csco/tenants")
member_actions = {'get_host': "PUT",
'get_instance_port': "PUT"}
member_actions = {'schedule_host': "PUT",
'associate_port': "PUT"}
controller = NovatenantsController(QuantumManager.get_plugin())
return [extensions.ResourceExtension('novatenants', controller,
parent=parent_resource,
@ -79,7 +79,7 @@ class NovatenantsController(common.QuantumController):
'param-name': 'novatenant_name',
'required': True}]
_get_host_ops_param_list = [{
_schedule_host_ops_param_list = [{
'param-name': 'instance_id',
'required': True}, {
'param-name': 'instance_desc',
@ -99,40 +99,56 @@ class NovatenantsController(common.QuantumController):
#added for cisco's extension
# pylint: disable-msg=E1101,W0613
def get_host(self, request, tenant_id, id):
def show(self, request, tenant_id, id):
""" Returns novatenant details for the given novatenant id """
return "novatenant is a dummy resource"
def create(self, request, tenant_id):
""" Creates a new novatenant for a given tenant """
return "novatenant is a dummy resource"
def update(self, request, tenant_id, id):
""" Updates the name for the novatenant with the given id """
return "novatenant is a dummy resource"
def delete(self, request, tenant_id, id):
""" Destroys the Novatenant with the given id """
return "novatenant is a dummy resource"
#added for cisco's extension
def schedule_host(self, request, tenant_id, id):
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
self._parse_request_params(request,
self._get_host_ops_param_list)
self._schedule_host_ops_param_list)
except exc.HTTPError as exp:
return faults.Fault(exp)
instance_id = req_params['instance_id']
instance_desc = req_params['instance_desc']
try:
host = self._plugin.get_host(tenant_id, instance_id, instance_desc)
host = self._plugin.\
schedule_host(tenant_id, instance_id, instance_desc)
builder = novatenant_view.get_view_builder(request)
result = builder.build_host(host)
return result
except qexception.PortNotFound as exp:
return faults.Fault(faults.PortNotFound(exp))
def get_instance_port(self, request, tenant_id, id):
def associate_port(self, request, tenant_id, id):
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
self._parse_request_params(request,
self._get_host_ops_param_list)
self._schedule_host_ops_param_list)
except exc.HTTPError as exp:
return faults.Fault(exp)
instance_id = req_params['instance_id']
instance_desc = req_params['instance_desc']
try:
vif = self._plugin. \
get_instance_port(tenant_id, instance_id, instance_desc)
associate_port(tenant_id, instance_id, instance_desc)
builder = novatenant_view.get_view_builder(request)
result = builder.build_vif(vif)
return result

View File

@ -175,7 +175,6 @@ class PortprofilesController(common.QuantumController):
def associate_portprofile(self, request, tenant_id, id):
""" associate a portprofile to the port """
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
@ -198,7 +197,6 @@ class PortprofilesController(common.QuantumController):
def disassociate_portprofile(self, request, tenant_id, id):
""" Disassociate a portprofile from a port """
content_type = request.best_match_content_type()
print "Content type:%s" % content_type
try:
req_params = \
self._parse_request_params(request,

View File

@ -37,7 +37,7 @@ LOG = logging.getLogger('quantum.api')
FLAGS = flags.FLAGS
class APIRouterV01(wsgi.Router):
class APIRouterV1(wsgi.Router):
"""
Routes requests on the Quantum API to the appropriate controller
"""
@ -45,7 +45,7 @@ class APIRouterV01(wsgi.Router):
def __init__(self, options=None):
mapper = routes.Mapper()
self._setup_routes(mapper, options)
super(APIRouterV01, self).__init__(mapper)
super(APIRouterV1, self).__init__(mapper)
def _setup_routes(self, mapper, options):
# Loads the quantum plugin
@ -64,9 +64,7 @@ class APIRouterV01(wsgi.Router):
parent_resource=dict(member_name='network',
collection_name=uri_prefix +\
'networks'))
attachments_ctrl = attachments.Controller(plugin)
mapper.connect("get_resource",
uri_prefix + 'networks/{network_id}/' \
'ports/{id}/attachment{.format}',

View File

@ -229,7 +229,7 @@ class AuthProtocol(object):
"""Client sent bad claims"""
return HTTPUnauthorized()(self.env, self.start_response)
def _validate_claims(self, claims):
def _validate_claims(self, claims, retry=False):
"""Validate claims, and provide identity information if applicable """
# Step 1: We need to auth with the keystone service, so get an
@ -257,16 +257,27 @@ class AuthProtocol(object):
conn = http_connect(self.auth_host, self.auth_port, 'GET',
self._build_token_uri(claims), headers=headers)
resp = conn.getresponse()
# data = resp.read()
conn.close()
if not str(resp.status).startswith('20'):
# Keystone rejected claim
# In case a 404 error it might just be that the token has expired
# Therefore try and get a new token
# of course assuming admin credentials have been specified
# Note(salvatore-orlando): the 404 here is not really
# what should be returned
if self.admin_user and self.admin_password and \
not retry and str(resp.status) == '404':
LOG.warn("Unable to validate token." +
"Admin token possibly expired.")
self.admin_token = None
return self._validate_claims(claims, True)
return False
else:
#TODO(Ziad): there is an optimization we can do here. We have just
#received data from Keystone that we can use instead of making
#another call in _expound_claims
LOG.info("Claims successfully validated")
return True
def _expound_claims(self):

View File

@ -338,7 +338,7 @@ def plugin_aware_extension_middleware_factory(global_config, **local_config):
def _factory(app):
extensions_path = global_config.get('api_extensions_path', '')
ext_mgr = PluginAwareExtensionManager(extensions_path,
QuantumManager().get_plugin())
QuantumManager.get_plugin())
return ExtensionMiddleware(app, global_config, ext_mgr=ext_mgr)
return _factory

View File

@ -26,7 +26,7 @@ The caller should make sure that QuantumManager is a singleton.
import gettext
import logging
import os
import logging
gettext.install('quantum', unicode=1)
from common import utils

View File

@ -2,7 +2,8 @@
README: A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
=========================================================================================
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri, Rohit Agarwalla, Ying Liu, Debo Dutta
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri,
Rohit Agarwalla, Ying Liu, Debo Dutta
:Contact: netstack@lists.launchpad.net
:Web site: https://launchpad.net/~cisco-openstack
:Copyright: 2011 Cisco Systems, Inc.
@ -86,7 +87,11 @@ Module Structure:
/common - Modules common to the entire plugin
/conf - All configuration files
/db - Persistence framework
/models - Class(es) which tie the logical abstractions
to the physical topology
/nexus - Nexus-specific modules
/segmentation - Implementation of segmentation manager,
e.g. VLAN Manager
/tests - Tests specific to this plugin
/ucs - UCS-specific modules
@ -100,9 +105,16 @@ Plugin Installation Instructions
provider = quantum.plugins.cisco.l2network_plugin.L2Network
3. If you are not running Quantum on the same host as the OpenStack Cloud
Controller, you will need to change the db_ip_address configuration
in nova.ini.
3. Configure your OpenStack installation to use the 802.1qbh VIF driver and
Quantum-aware scheduler by editing the /etc/nova/nova.conf file with the
following entries:
--scheduler_driver=quantum.plugins.cisco.nova.quantum_aware_scheduler.QuantumScheduler
--quantum_host=127.0.0.1
--quantum_port=9696
--libvirt_vif_driver=quantum.plugins.cisco.nova.vifdirect.Libvirt802dot1QbhDriver
--libvirt_vif_type=802.1Qbh
4. If you want to turn on support for Cisco Nexus switches:
4a. Uncomment the nexus_plugin property in
@ -173,7 +185,39 @@ password=mySecretPasswordForNova
username=admin
password=mySecretPasswordForNexus
7. Start the Quantum service. If something doesn't work, verify that
7. Configure the UCS systems' information in your deployment by editing the
quantum/plugins/cisco/conf/ucs_inventory.ini file. You can configure multiple
UCSMs per deployment, multiple chasses per UCSM, and multiple blades per
chassis. Chassis ID and blade ID can be obtained from the UCSM (they will
typically numbers like 1, 2, 3, etc.
[ucsm-1]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-3]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[ucsm-2]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
8. Start the Quantum service. If something doesn't work, verify that
your configuration of each of the above files hasn't gone a little kaka.
Once you've put right what once went wrong, leap on.
@ -241,54 +285,9 @@ result the quantum/plugins/cisco/run_tests.py script.
python run_tests.py quantum.plugins.cisco.tests.unit.test_cisco_extension
Additional installation required on Nova Compute
------------------------------------------------
1. Create a table in the "nova" database for ports. This can be
accomplished with the following SQL statement:
CREATE TABLE ports (
port_id VARCHAR(255) PRIMARY KEY,
profile_name VARCHAR(255),
dynamic_vnic VARCHAR(255),
host VARCHAR(255),
instance_name VARCHAR(255),
instance_nic_name VARCHAR(255),
used TINYINT(1)
);
Assuming you're using MySQL, you can run the following command from a
shell prompt on the Cloud Controller node to create the table:
mysql -uroot -p nova -e 'create table ports (port_id VARCHAR(255) primary key, profile_name VARCHAR(255), dynamic_vnic VARCHAR(255), host VARCHAR(255), instance_name VARCHAR(255), instance_nic_name VARCHAR(255), used tinyint(1));'
You'll be prompted for a password.
2. A patch is available for the Cactus release in this branch:
https://code.launchpad.net/~snaiksat/quantum/cactus-ucs-support
replace the following file in your installation:
/usr/lib/python2.6/site-packages/nova/virt/libvirt_conn.py
with the file from the branch:
nova/virt/libvirt_conn.py
3. Add the following file from the Cisco Nova branch:
nova/virt/cisco_ucs.py
to:
/usr/lib/python2.6/site-packages/nova/virt/cisco_ucs.py
4. Add the 802.1Qbh specific libvirt template file, from:
nova/virt/libvirt-qbh.xml.template
to:
/usr/share/nova/libvirt-qbh.xml.template
5. Edit /etc/nova.conf to set the libvirt XML template to the above template:
--libvirt_xml_template=/usr/share/nova/libvirt-qbh.xml.template
6. Restart the nova-compute service.
(Note that the requirement for the above patch is temporary and will go away
with the integration with OpenStack Diablo. A 802.1Qbh-specific VIF driver
will be made available as per the specification here:
http://wiki.openstack.org/network-refactoring#VIF_driver)
To run specific tests
python run_tests.py
quantum.plugins.cisco.tests.unit.test_cisco_extension:<ClassName>.<funcName>
Bingo bango bongo! That's it! Thanks for taking the leap into Quantum.

View File

@ -20,6 +20,7 @@
"""
PLUGINS = 'PLUGINS'
INVENTORY = 'INVENTORY'
PORT_STATE = 'port-state'
PORT_UP = "ACTIVE"
@ -39,6 +40,9 @@ PPQOS = 'qos'
PPID = 'portprofile_id'
PPDEFAULT = 'default'
VLANID = 'vlan_id'
VLANNAME = 'vlan_name'
PORTPROFILENAME = 'portprofile_name'
QOS = 'qos'
ATTACHMENT = 'attachment'
PORT_ID = 'port-id'
@ -113,11 +117,13 @@ RHEL_DEVICE_NAME_REPFIX = "eth"
UCS_PLUGIN = 'ucs_plugin'
NEXUS_PLUGIN = 'nexus_plugin'
UCS_INVENTORY = 'ucs_inventory'
NEXUS_INVENTORY = 'nexus_inventory'
PLUGIN_OBJ_REF = 'plugin-obj-ref'
PARAM_LIST = 'param-list'
DEVICE_IP = 'device-ip'
DEVICE_IP = 'device_ip'
NO_VLAN_ID = 0
@ -128,5 +134,18 @@ VIF_DESC = 'vif_desc'
DEVICENAME = 'device'
UCSPROFILE = 'portprofile'
MAX_CREDENTIALS = 65568
MAX_QOS_LEVELS = 1024
IP_ADDRESS = 'ip_address'
CHASSIS_ID = 'chassis_id'
BLADE_ID = 'blade_id'
HOST_NAME = 'host_name'
INSTANCE_ID = 'instance_id'
VIF_ID = 'vif_id'
PROJECT_ID = 'project_id'
UCS_INVENTORY = 'ucs_inventory'
LEAST_RSVD_BLADE_DICT = 'least_rsvd_blade_dict'
UCSM_IP = 'ucsm_ip_address'
NETWORK_ADMIN = 'network_admin'

View File

@ -24,10 +24,14 @@ import os
from quantum.plugins.cisco.common import cisco_configparser as confp
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.db import l2network_db as cdb
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
TENANT = const.NETWORK_ADMIN
CREDENTIALS_FILE = "../conf/credentials.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
@ -39,32 +43,43 @@ class Store(object):
"""Credential Store"""
@staticmethod
def putCredential(id, username, password):
def initialize():
for id in _creds_dictionary.keys():
try:
cdb.add_credential(TENANT, id,
_creds_dictionary[id][const.USERNAME],
_creds_dictionary[id][const.PASSWORD])
except cexc.CredentialAlreadyExists:
# We are quietly ignoring this, since it only happens
# if this class module is loaded more than once, in which
# case, the credentials are already populated
pass
@staticmethod
def putCredential(cred_name, username, password):
"""Set the username and password"""
_creds_dictionary[id] = {const.USERNAME: username,
const.PASSWORD: password}
credential = cdb.add_credential(TENANT, cred_name, username, password)
@staticmethod
def getUsername(id):
def getUsername(cred_name):
"""Get the username"""
return _creds_dictionary[id][const.USERNAME]
credential = cdb.get_credential_name(TENANT, cred_name)
return credential[const.CREDENTIAL_USERNAME]
@staticmethod
def getPassword(id):
def getPassword(cred_name):
"""Get the password"""
return _creds_dictionary[id][const.PASSWORD]
credential = cdb.get_credential_name(TENANT, cred_name)
return credential[const.CREDENTIAL_PASSWORD]
@staticmethod
def getCredential(id):
def getCredential(cred_name):
"""Get the username and password"""
return _creds_dictionary[id]
credential = cdb.get_credential_name(TENANT, cred_name)
return {const.USERNAME: const.CREDENTIAL_USERNAME,
const.PASSWORD: const.CREDENTIAL_PASSWORD}
@staticmethod
def getCredentials():
"""Get all usernames and passwords"""
return _creds_dictionary
@staticmethod
def deleteCredential(id):
def deleteCredential(cred_name):
"""Delete a credential"""
return _creds_dictionary.pop(id)
cdb.remove_credential(TENANT, cred_name)

View File

@ -101,17 +101,75 @@ class QoSLevelInvalidDelete(exceptions.QuantumException):
"for tenant %(tenant_id)s since association exists")
class QosNameAlreadyExists(exceptions.QuantumException):
"""QoS Name already exists"""
message = _("QoS level with name %(qos_name)s already exists " \
"for tenant %(tenant_id)s")
class CredentialNotFound(exceptions.QuantumException):
"""Credential with this ID cannot be found"""
message = _("Credential %(credential_id)s could not be found " \
"for tenant %(tenant_id)s")
class CredentialNameNotFound(exceptions.QuantumException):
"""Credential Name could not be found"""
message = _("Credential %(credential_name)s could not be found " \
"for tenant %(tenant_id)s")
class CredentialAlreadyExists(exceptions.QuantumException):
"""Credential ID already exists"""
message = _("Credential %(credential_id)s already exists " \
"for tenant %(tenant_id)s")
class NexusPortBindingNotFound(exceptions.QuantumException):
"""NexusPort Binding is not present"""
message = _("Nexus Port Binding %(port_id) is not present")
class UcsmBindingNotFound(exceptions.QuantumException):
"""Ucsm Binding is not present"""
message = _("Ucsm Binding with ip %(ucsm_ip) is not present")
class UcsmBindingAlreadyExists(exceptions.QuantumException):
"""Ucsm Binding already exists"""
message = _("Ucsm Binding with ip %(ucsm_ip) already exists")
class DynamicVnicNotFound(exceptions.QuantumException):
"""Ucsm Binding is not present"""
message = _("Dyanmic Vnic %(vnic_id) is not present")
class DynamicVnicAlreadyExists(exceptions.QuantumException):
"""Ucsm Binding already exists"""
message = _("Dynamic Vnic with name %(device_name) already exists")
class BladeNotFound(exceptions.QuantumException):
"""Blade is not present"""
message = _("Blade %(blade_id) is not present")
class BladeAlreadyExists(exceptions.QuantumException):
"""Blade already exists"""
message = _("Blade with mgmt_ip %(mgmt_ip) already exists")
class PortVnicBindingAlreadyExists(exceptions.QuantumException):
"""PortVnic Binding already exists"""
message = _("PortVnic Binding %(port_id) already exists")
class PortVnicNotFound(exceptions.QuantumException):
"""PortVnic Binding is not present"""
message = _("PortVnic Binding %(port_id) is not present")
try:
_("test")
except NameError:

View File

@ -26,6 +26,8 @@ import traceback
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_nova_configuration as conf
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.db import l2network_db as cdb
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
@ -39,6 +41,44 @@ def get16ByteUUID(uuid):
return hashlib.md5(uuid).hexdigest()[:16]
def make_net_dict(net_id, net_name, ports):
"""Helper funciton"""
res = {const.NET_ID: net_id, const.NET_NAME: net_name}
res[const.NET_PORTS] = ports
return res
def make_port_dict(port_id, port_state, net_id, attachment):
"""Helper funciton"""
res = {const.PORT_ID: port_id, const.PORT_STATE: port_state}
res[const.NET_ID] = net_id
res[const.ATTACHMENT] = attachment
return res
def make_portprofile_dict(tenant_id, profile_id, profile_name,
qos):
"""Helper funciton"""
profile_associations = make_portprofile_assc_list(tenant_id,
profile_id)
res = {const.PROFILE_ID: str(profile_id),
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: profile_associations,
const.PROFILE_VLAN_ID: None,
const.PROFILE_QOS: qos}
return res
def make_portprofile_assc_list(tenant_id, profile_id):
"""Helper function to create port profile association list"""
plist = cdb.get_pp_binding(tenant_id, profile_id)
assc_list = []
for port in plist:
assc_list.append(port[const.PORTID])
return assc_list
class DBUtils(object):
"""Utilities to use connect to MySQL DB and execute queries"""

View File

@ -1,6 +1,6 @@
[VLANS]
vlan_start=<put_vlan_id_range_start_here>
vlan_end=<put_vlan_id_range_end_here>
vlan_start=100
vlan_end=3000
vlan_name_prefix=q-
[PORTS]
@ -13,4 +13,7 @@ max_port_profiles=65568
max_networks=65568
[MODEL]
model_class=quantum.plugins.cisco.l2network_model.L2NetworkModel
model_class=quantum.plugins.cisco.models.l2network_multi_blade.L2NetworkMultiBlade
[SEGMENTATION]
manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr.L2NetworkVLANMgr

View File

@ -1,3 +1,7 @@
[PLUGINS]
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin
[INVENTORY]
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory.UCSInventory
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_inventory.NexusInventory

View File

@ -0,0 +1,24 @@
[ucsm-1]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-3]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[ucsm-2]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>

View File

@ -252,3 +252,46 @@ def port_destroy(net_id, port_id):
return port
except exc.NoResultFound:
raise q_exc.PortNotFound(port_id=port_id)
#methods using just port_id
def port_get_by_id(port_id):
session = get_session()
try:
return session.query(models.Port).\
filter_by(uuid=port_id).one()
except exc.NoResultFound:
raise q_exc.PortNotFound(port_id=port_id)
def port_set_attachment_by_id(port_id, new_interface_id):
session = get_session()
port = port_get_by_id(port_id)
if new_interface_id != "":
if port['interface_id']:
raise q_exc.PortInUse(port_id=port_id,
att_id=port['interface_id'])
try:
port = session.query(models.Port).\
filter_by(interface_id=new_interface_id).\
one()
raise q_exc.AlreadyAttached(port_id=port_id,
att_id=new_interface_id,
att_port_id=port['uuid'])
except exc.NoResultFound:
pass
port.interface_id = new_interface_id
session.merge(port)
session.flush()
return port
def port_unset_attachment_by_id(port_id):
session = get_session()
port = port_get_by_id(port_id)
port.interface_id = None
session.merge(port)
session.flush()
return port

View File

@ -24,6 +24,8 @@ from quantum.plugins.cisco.db import l2network_models
import logging as LOG
import quantum.plugins.cisco.db.api as db
import quantum.plugins.cisco.db.nexus_db as ndb
import quantum.plugins.cisco.db.ucs_db as udb
def initialize():
@ -129,6 +131,19 @@ def reserve_vlanid():
raise c_exc.VlanIDNotAvailable()
def get_all_vlanids_used():
"""Gets all the vlanids used"""
LOG.debug("get_all_vlanids() called")
session = db.get_session()
try:
vlanids = session.query(l2network_models.VlanID).\
filter_by(vlan_used=True).\
all()
return vlanids
except exc.NoResultFound:
return []
def get_all_vlan_bindings():
"""Lists all the vlan to network associations"""
LOG.debug("get_all_vlan_bindings() called")
@ -366,3 +381,176 @@ def update_pp_binding(tenantid, ppid, newtenantid=None, newportid=None,
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)
def get_all_qoss(tenant_id):
"""Lists all the qos to tenant associations"""
LOG.debug("get_all_qoss() called")
session = db.get_session()
try:
qoss = session.query(l2network_models.QoS).\
filter_by(tenant_id=tenant_id).\
all()
return qoss
except exc.NoResultFound:
return []
def get_qos(tenant_id, qos_id):
"""Lists the qos given a tenant_id and qos_id"""
LOG.debug("get_qos() called")
session = db.get_session()
try:
qos = session.query(l2network_models.QoS).\
filter_by(tenant_id=tenant_id).\
filter_by(qos_id=qos_id).\
one()
return qos
except exc.NoResultFound:
raise c_exc.QosNotFound(qos_id=qos_id,
tenant_id=tenant_id)
def add_qos(tenant_id, qos_name, qos_desc):
"""Adds a qos to tenant association"""
LOG.debug("add_qos() called")
session = db.get_session()
try:
qos = session.query(l2network_models.QoS).\
filter_by(tenant_id=tenant_id).\
filter_by(qos_name=qos_name).\
one()
raise c_exc.QosNameAlreadyExists(qos_name=qos_name,
tenant_id=tenant_id)
except exc.NoResultFound:
qos = l2network_models.QoS(tenant_id, qos_name, qos_desc)
session.add(qos)
session.flush()
return qos
def remove_qos(tenant_id, qos_id):
"""Removes a qos to tenant association"""
session = db.get_session()
try:
qos = session.query(l2network_models.QoS).\
filter_by(tenant_id=tenant_id).\
filter_by(qos_id=qos_id).\
one()
session.delete(qos)
session.flush()
return qos
except exc.NoResultFound:
pass
def update_qos(tenant_id, qos_id, new_qos_name=None):
"""Updates a qos to tenant association"""
session = db.get_session()
try:
qos = session.query(l2network_models.QoS).\
filter_by(tenant_id=tenant_id).\
filter_by(qos_id=qos_id).\
one()
if new_qos_name:
qos["qos_name"] = new_qos_name
session.merge(qos)
session.flush()
return qos
except exc.NoResultFound:
raise c_exc.QosNotFound(qos_id=qos_id,
tenant_id=tenant_id)
def get_all_credentials(tenant_id):
"""Lists all the creds for a tenant"""
session = db.get_session()
try:
creds = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
all()
return creds
except exc.NoResultFound:
return []
def get_credential(tenant_id, credential_id):
"""Lists the creds for given a cred_id and tenant_id"""
session = db.get_session()
try:
cred = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
filter_by(credential_id=credential_id).\
one()
return cred
except exc.NoResultFound:
raise c_exc.CredentialNotFound(credential_id=credential_id,
tenant_id=tenant_id)
def get_credential_name(tenant_id, credential_name):
"""Lists the creds for given a cred_name and tenant_id"""
session = db.get_session()
try:
cred = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
filter_by(credential_name=credential_name).\
one()
return cred
except exc.NoResultFound:
raise c_exc.CredentialNameNotFound(credential_name=credential_name,
tenant_id=tenant_id)
def add_credential(tenant_id, credential_name, user_name, password):
"""Adds a qos to tenant association"""
session = db.get_session()
try:
cred = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
filter_by(credential_name=credential_name).\
one()
raise c_exc.CredentialAlreadyExists(credential_name=credential_name,
tenant_id=tenant_id)
except exc.NoResultFound:
cred = l2network_models.Credential(tenant_id,
credential_name, user_name, password)
session.add(cred)
session.flush()
return cred
def remove_credential(tenant_id, credential_id):
"""Removes a credential from a tenant"""
session = db.get_session()
try:
cred = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
filter_by(credential_id=credential_id).\
one()
session.delete(cred)
session.flush()
return cred
except exc.NoResultFound:
pass
def update_credential(tenant_id, credential_id,
new_user_name=None, new_password=None):
"""Updates a credential for a tenant"""
session = db.get_session()
try:
cred = session.query(l2network_models.Credential).\
filter_by(tenant_id=tenant_id).\
filter_by(credential_id=credential_id).\
one()
if new_user_name:
cred["user_name"] = new_user_name
if new_password:
cred["password"] = new_password
session.merge(cred)
session.flush()
return cred
except exc.NoResultFound:
raise c_exc.CredentialNotFound(credential_id=credential_id,
tenant_id=tenant_id)

View File

@ -77,7 +77,7 @@ class VlanID(BASE, L2NetworkBase):
self.vlan_used = False
def __repr__(self):
return "<VlanBinding(%d,%s)>" % \
return "<VlanID(%d,%s)>" % \
(self.vlan_id, self.vlan_used)
@ -87,7 +87,7 @@ class VlanBinding(BASE, L2NetworkBase):
vlan_id = Column(Integer, primary_key=True)
vlan_name = Column(String(255))
network_id = Column(String(255), ForeignKey("networks.uuid"), \
network_id = Column(String(255), ForeignKey("networks.uuid"),
nullable=False)
network = relation(models.Network, uselist=False)
@ -128,9 +128,9 @@ class PortProfileBinding(BASE, L2NetworkBase):
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(255))
port_id = Column(String(255), ForeignKey("ports.uuid"), \
port_id = Column(String(255), ForeignKey("ports.uuid"),
nullable=False)
portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"), \
portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"),
nullable=False)
default = Column(Boolean)
ports = relation(models.Port)
@ -145,3 +145,46 @@ class PortProfileBinding(BASE, L2NetworkBase):
def __repr__(self):
return "<PortProfile Binding(%s,%s,%s,%s)>" % \
(self.tenant_id, self.port_id, self.portprofile_id, self.default)
class QoS(BASE, L2NetworkBase):
"""Represents QoS for a tenant"""
__tablename__ = 'qoss'
qos_id = Column(String(255))
tenant_id = Column(String(255), primary_key=True)
qos_name = Column(String(255), primary_key=True)
qos_desc = Column(String(255))
def __init__(self, tenant_id, qos_name, qos_desc):
self.qos_id = str(uuid.uuid4())
self.tenant_id = tenant_id
self.qos_name = qos_name
self.qos_desc = qos_desc
def __repr__(self):
return "<QoS(%s,%s,%s,%s)>" % \
(self.qos_id, self.tenant_id, self.qos_name, self.qos_desc)
class Credential(BASE, L2NetworkBase):
"""Represents credentials for a tenant"""
__tablename__ = 'credentials'
credential_id = Column(String(255))
tenant_id = Column(String(255), primary_key=True)
credential_name = Column(String(255), primary_key=True)
user_name = Column(String(255))
password = Column(String(255))
def __init__(self, tenant_id, credential_name, user_name, password):
self.credential_id = str(uuid.uuid4())
self.tenant_id = tenant_id
self.credential_name = credential_name
self.user_name = user_name
self.password = password
def __repr__(self):
return "<Credentials(%s,%s,%s,%s,%s)>" % \
(self.credential_id, self.tenant_id, self.credential_name,
self.user_name, self.password)

View File

@ -0,0 +1,130 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Cisco Systems, Inc.
#
# 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: Rohit Agarwalla, Cisco Systems, Inc.
import logging as LOG
from sqlalchemy.orm import exc
import quantum.plugins.cisco.db.api as db
from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import ucs_models
def get_all_portbindings():
"""Lists all the port bindings"""
LOG.debug("db get_all_portbindings() called")
session = db.get_session()
try:
port_bindings = session.query(ucs_models.PortBinding).\
all()
return port_bindings
except exc.NoResultFound:
return []
def get_portbinding(port_id):
"""Lists a port binding"""
LOG.debug("get_portbinding() called")
session = db.get_session()
try:
port_binding = session.query(ucs_models.PortBinding).\
filter_by(port_id=port_id).\
one()
return port_binding
except exc.NoResultFound:
raise c_exc.PortVnicNotFound(port_id=port_id)
def add_portbinding(port_id, blade_intf_dn, portprofile_name,
vlan_name, vlan_id, qos):
"""Adds a port binding"""
LOG.debug("add_portbinding() called")
session = db.get_session()
try:
port_binding = session.query(ucs_models.PortBinding).\
filter_by(port_id=port_id).\
one()
raise c_exc.PortVnicBindingAlreadyExists(port_id=port_id)
except exc.NoResultFound:
port_binding = ucs_models.PortBinding(port_id, blade_intf_dn, \
portprofile_name, vlan_name, vlan_id, qos)
session.add(port_binding)
session.flush()
return port_binding
def remove_portbinding(port_id):
"""Removes a port binding"""
LOG.debug("db remove_portbinding() called")
session = db.get_session()
try:
port_binding = session.query(ucs_models.PortBinding).\
filter_by(port_id=port_id).\
one()
session.delete(port_binding)
session.flush()
return port_binding
except exc.NoResultFound:
pass
def update_portbinding(port_id, blade_intf_dn=None, portprofile_name=None,
vlan_name=None, vlan_id=None, qos=None,
tenant_id=None, instance_id=None,
vif_id=None):
"""Updates port binding"""
LOG.debug("db update_portbinding() called")
session = db.get_session()
try:
port_binding = session.query(ucs_models.PortBinding).\
filter_by(port_id=port_id).\
one()
if blade_intf_dn:
port_binding.blade_intf_dn = blade_intf_dn
if portprofile_name:
port_binding.portprofile_name = portprofile_name
if vlan_name:
port_binding.vlan_name = vlan_name
if vlan_name:
port_binding.vlan_id = vlan_id
if qos:
port_binding.qos = qos
if tenant_id:
port_binding.tenant_id = tenant_id
if instance_id:
port_binding.instance_id = instance_id
if vif_id:
port_binding.vif_id = vif_id
session.merge(port_binding)
session.flush()
return port_binding
except exc.NoResultFound:
raise c_exc.PortVnicNotFound(port_id=port_id)
def get_portbinding_dn(blade_intf_dn):
"""Lists a port binding"""
LOG.debug("get_portbinding_dn() called")
session = db.get_session()
try:
port_binding = session.query(ucs_models.PortBinding).\
filter_by(blade_intf_dn=blade_intf_dn).\
one()
return port_binding
except exc.NoResultFound:
return []

View File

@ -0,0 +1,55 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Cisco Systems, Inc.
#
# 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: Rohit Agarwalla, Cisco Systems, Inc.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relation
from quantum.plugins.cisco.db import models
from quantum.plugins.cisco.db.l2network_models import L2NetworkBase
from quantum.plugins.cisco.db.models import BASE
class PortBinding(BASE, L2NetworkBase):
"""Represents Port binding to device interface"""
__tablename__ = 'port_bindings'
id = Column(Integer, primary_key=True, autoincrement=True)
port_id = Column(String(255), ForeignKey("ports.uuid"),
nullable=False)
blade_intf_dn = Column(String(255), nullable=False)
portprofile_name = Column(String(255))
vlan_name = Column(String(255))
vlan_id = Column(Integer)
qos = Column(String(255))
tenant_id = Column(String(255))
instance_id = Column(String(255))
vif_id = Column(String(255))
ports = relation(models.Port, uselist=False)
def __init__(self, port_id, blade_intf_dn, portprofile_name,
vlan_name, vlan_id, qos):
self.port_id = port_id
self.blade_intf_dn = blade_intf_dn
self.portprofile_name = portprofile_name
self.vlan_name = vlan_name
self.vlan_id = vlan_id
self.qos = qos
def __repr__(self):
return "<PortProfile Binding(%s,%s,%s,%s,%s,%s)>" % \
(self.port_id, self.blade_intf_dn, self.portprofile_name,
self.vlan_name, self.vlan_id, self.qos)

View File

@ -0,0 +1,343 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
from abc import ABCMeta, abstractmethod
class L2NetworkDeviceInventoryBase(object):
"""
Base class for L2 Network Device Inventory
This is used by the L2Nework Model to get information about
the actual devices of a particular type in a given deployment.
For instance, an implementation in the context of UCS will
know what UCSMs, chasses, blades, and dynamic vnics are
present in a particular deployment.
Similarly, an implementation in the context of Nexus switches
will know which switches are present in the system, and how they
are interconnected to other switches/devices.
"""
__metaclass__ = ABCMeta
@abstractmethod
def get_all_networks(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def create_network(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def delete_network(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def get_network_details(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def rename_network(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def get_all_ports(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def create_port(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def delete_port(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def update_port(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def get_port_details(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def plug_interface(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@abstractmethod
def unplug_interface(self, args):
"""
Returns a dictionary containing the first element as a device
IP address list. The model then invokes the device-specific plugin
for each device IP in that list. This is followed by zero or more
key-value pairs (specific to each operation, device type, and
deployment.
The model implementation may or may not process the returned
values, but needs to pass them to the device-specific plugin.
Since the device-specific plugin and this inventory implementation
are assumed to be implemented by the same entity, the
device-sepcific knows how to process this dictionary.
:returns: a dictionary with the following signature:
{'device_ip': []
'key-1': "value 1",
...
'key-n': "value n"
}
:raises:
"""
pass
@classmethod
def __subclasshook__(cls, klass):
"""
The __subclasshook__ method is a class method
that will be called everytime a class is tested
using issubclass(klass, Plugin).
In that case, it will check that every method
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is L2NetworkDeviceInventoryBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:
if method in base.__dict__:
fn_obj = base.__dict__[method]
if inspect.isfunction(fn_obj):
abstract_fn_obj = cls.__dict__[method]
arg_count = fn_obj.func_code.co_argcount
expected_arg_count = \
abstract_fn_obj.func_code.co_argcount
method_ok = arg_count == expected_arg_count
if method_ok:
continue
return NotImplemented
return True
return NotImplemented

View File

@ -1,123 +0,0 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
import logging as LOG
from quantum.common import utils
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2NetworkModel(L2NetworkModelBase):
"""
Implements the L2NetworkModelBase
This implementation works with UCS and Nexus plugin,
with one UCS blade, and one Nexus switch.
"""
_plugins = {}
def __init__(self):
for key in conf.PLUGINS[const.PLUGINS].keys():
self._plugins[key] = utils.import_object(
conf.PLUGINS[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s" % \
conf.PLUGINS[const.PLUGINS][key])
def _func_name(self, offset=0):
"""Get the name of the calling function"""
return inspect.stack()[1 + offset][3]
def _invoke_all_device_plugins(self, function_name, args, kwargs):
"""Invoke all device plugins for this model implementation"""
for plugin_obj_ref in self._plugins.values():
getattr(plugin_obj_ref, function_name)(*args, **kwargs)
def _invoke_ucs_plugin(self, function_name, args, kwargs):
"""Invoke only the UCS plugin"""
if const.UCS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.UCS_PLUGIN],
function_name)(*args, **kwargs)
def _invoke_nexus_plugin(self, function_name, args, kwargs):
"""Invoke only the Nexus plugin"""
if const.NEXUS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.NEXUS_PLUGIN],
function_name)(*args, **kwargs)
def get_all_networks(self, args):
"""Not implemented for this model"""
pass
def create_network(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def delete_network(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def get_network_details(self, args):
"""Not implemented for this model"""
pass
def rename_network(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def get_all_ports(self, args):
"""Not implemented for this model"""
pass
def create_port(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def delete_port(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def update_port(self, args):
"""Not implemented for this model"""
pass
def get_port_details(self, args):
"""Not implemented for this model"""
pass
def plug_interface(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def unplug_interface(self, args):
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)

View File

@ -26,10 +26,12 @@ import platform
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.quantum_plugin_base import QuantumPluginBase
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
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 api as db
from quantum.plugins.cisco.db import l2network_db as cdb
@ -41,17 +43,13 @@ class L2Network(QuantumPluginBase):
""" L2 Network Framework Plugin """
supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
"Cisco qos", "Cisco Nova Tenant"]
_qos_levels = {}
_credentials = {}
def __init__(self):
self._vlan_counter = int(conf.VLAN_START) - 1
self._model = utils.import_object(conf.MODEL_CLASS)
cdb.initialize()
# TODO (Sumit): The following should move to the segmentation module
cdb.create_vlanids()
self._qoslevels_counter = 0
self._credentials_counter = 0
cred.Store.initialize()
self._model = utils.import_object(conf.MODEL_CLASS)
self._vlan_mgr = utils.import_object(conf.MANAGER_CLASS)
LOG.debug("L2Network plugin initialization done successfully\n")
"""
Core API implementation
@ -67,7 +65,7 @@ class L2Network(QuantumPluginBase):
networks_list = db.network_list(tenant_id)
new_networks_list = []
for network in networks_list:
new_network_dict = self._make_net_dict(network[const.UUID],
new_network_dict = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
new_networks_list.append(new_network_dict)
@ -107,10 +105,10 @@ class L2Network(QuantumPluginBase):
if port[const.INTERFACEID]:
raise exc.NetworkInUse(net_id=net_id)
for port in ports_on_net:
self.delete_port(tenant_id, net_id, port[const.PORTID])
self.delete_port(tenant_id, net_id, port[const.UUID])
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
net_dict = self._make_net_dict(net[const.UUID],
net_dict = cutil.make_net_dict(net[const.UUID],
net[const.NETWORKNAME],
[])
self._release_vlan_for_tenant(tenant_id, net_id)
@ -125,18 +123,18 @@ class L2Network(QuantumPluginBase):
Gets the details of a particular network
"""
LOG.debug("get_network_details() called\n")
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = self._make_port_dict(port[const.UUID],
new_port = cutil.make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
ports_on_net.append(new_port)
new_network = self._make_net_dict(network[const.UUID],
new_network = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
ports_on_net)
@ -148,10 +146,10 @@ class L2Network(QuantumPluginBase):
Virtual Network.
"""
LOG.debug("rename_network() called\n")
network = db.network_rename(tenant_id, net_id, new_name)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
new_name])
network = db.network_rename(tenant_id, net_id, new_name)
net_dict = self._make_net_dict(network[const.UUID],
net_dict = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
return net_dict
@ -162,12 +160,12 @@ class L2Network(QuantumPluginBase):
specified Virtual Network.
"""
LOG.debug("get_all_ports() called\n")
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = self._make_port_dict(port[const.UUID],
new_port = cutil.make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
@ -185,7 +183,7 @@ class L2Network(QuantumPluginBase):
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_state,
unique_port_id_string])
new_port_dict = self._make_port_dict(port[const.UUID],
new_port_dict = cutil.make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
@ -199,22 +197,31 @@ class L2Network(QuantumPluginBase):
then the port can be deleted.
"""
LOG.debug("delete_port() called\n")
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
db.port_destroy(net_id, port_id)
new_port_dict = self._make_port_dict(port_id, None, None, None)
return new_port_dict
network = db.network_get(net_id)
port = db.port_get(net_id, port_id)
attachment_id = port[const.INTERFACEID]
if not attachment_id:
self._invoke_device_plugins(self._func_name(), [tenant_id,
net_id,
port_id])
db.port_destroy(net_id, port_id)
new_port_dict = cutil.make_port_dict(port_id, None, None, None)
return new_port_dict
else:
raise exc.PortInUse(port_id=port_id, net_id=net_id,
att_id=attachment_id)
def update_port(self, tenant_id, net_id, port_id, port_state):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("update_port() called\n")
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id, port_state])
self._validate_port_state(port_state)
db.port_set_state(net_id, port_id, port_state)
new_port_dict = self._make_port_dict(port_id, port_state, net_id,
new_port_dict = cutil.make_port_dict(port_id, port_state, net_id,
None)
return new_port_dict
@ -224,10 +231,11 @@ class L2Network(QuantumPluginBase):
that is attached to this particular port.
"""
LOG.debug("get_port_details() called\n")
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
port = db.port_get(net_id, port_id)
new_port_dict = self._make_port_dict(port[const.UUID],
new_port_dict = cutil.make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
@ -240,6 +248,7 @@ class L2Network(QuantumPluginBase):
specified Virtual Network.
"""
LOG.debug("plug_interface() called\n")
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id,
remote_interface_id])
@ -251,6 +260,7 @@ class L2Network(QuantumPluginBase):
specified Virtual Network.
"""
LOG.debug("unplug_interface() called\n")
network = db.network_get(net_id)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
db.port_unset_attachment(net_id, port_id)
@ -264,7 +274,7 @@ class L2Network(QuantumPluginBase):
pplist = cdb.get_all_portprofiles()
new_pplist = []
for portprofile in pplist:
new_pp = self._make_portprofile_dict(tenant_id,
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
@ -281,7 +291,7 @@ class L2Network(QuantumPluginBase):
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
new_pp = self._make_portprofile_dict(tenant_id,
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
@ -292,7 +302,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("create_portprofile() called\n")
portprofile = cdb.add_portprofile(tenant_id, profile_name,
const.NO_VLAN_ID, qos)
new_pp = self._make_portprofile_dict(tenant_id,
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
@ -323,7 +333,7 @@ class L2Network(QuantumPluginBase):
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
new_pp = self._make_portprofile_dict(tenant_id,
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
@ -356,66 +366,57 @@ class L2Network(QuantumPluginBase):
def get_all_qoss(self, tenant_id):
"""Get all QoS levels"""
LOG.debug("get_all_qoss() called\n")
return self._qos_levels.values()
qoslist = cdb.get_all_qoss(tenant_id)
return qoslist
def get_qos_details(self, tenant_id, qos_id):
"""Get QoS Details"""
LOG.debug("get_qos_details() called\n")
try:
qos_level = self._get_qos_level(tenant_id, qos_id)
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
qos_id=qos_id)
return qos_level
def create_qos(self, tenant_id, qos_name, qos_desc):
"""Create a QoS level"""
LOG.debug("create_qos() called\n")
qos_id = self._get_unique_qos_id(tenant_id)
new_qos_level_dict = {const.QOS_LEVEL_ID: qos_id,
const.QOS_LEVEL_NAME: qos_name,
const.QOS_LEVEL_ASSOCIATIONS: [],
const.QOS_LEVEL_DESCRIPTION: qos_desc}
self._qos_levels[qos_id] = new_qos_level_dict
return new_qos_level_dict
qos = cdb.add_qos(tenant_id, qos_name, str(qos_desc))
return qos
def delete_qos(self, tenant_id, qos_id):
"""Delete a QoS level"""
LOG.debug("delete_qos() called\n")
try:
qos_level = self._get_qos_level(tenant_id, qos_id)
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
associations = qos_level[const.QOS_LEVEL_ASSOCIATIONS]
if len(associations) > 0:
raise cexc.QoSLevelInvalidDelete(tenant_id=tenant_id,
qos_id=qos_id)
else:
self._qos_levels.pop(qos_id)
qos_id=qos_id)
return cdb.remove_qos(tenant_id, qos_id)
def rename_qos(self, tenant_id, qos_id, new_name):
"""Rename QoS level"""
LOG.debug("rename_qos() called\n")
qos_level = self._get_qos_level(tenant_id, qos_id)
try:
qos_level = self._get_qos_level(tenant_id, qos_id)
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
qos_level[const.QOS_LEVEL_NAME] = new_name
return qos_level
qos_id=qos_id)
qos = cdb.update_qos(tenant_id, qos_id, new_name)
return qos
def get_all_credentials(self, tenant_id):
"""Get all credentials"""
LOG.debug("get_all_credentials() called\n")
return self._credentials.values()
credential_list = cdb.get_all_credentials(tenant_id)
return credential_list
def get_credential_details(self, tenant_id, credential_id):
"""Get a particular credential"""
LOG.debug("get_credential_details() called\n")
try:
credential = self._get_credential(tenant_id, credential_id)
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
@ -425,77 +426,65 @@ class L2Network(QuantumPluginBase):
password):
"""Create a new credential"""
LOG.debug("create_credential() called\n")
credential_id = self._get_unique_credential_id(tenant_id)
masked_password = const.MASKED_PASSWORD
new_credential_dict = {const.CREDENTIAL_ID: credential_id,
const.CREDENTIAL_NAME: credential_name,
const.CREDENTIAL_USERNAME: user_name,
const.CREDENTIAL_PASSWORD: masked_password}
self._credentials[credential_id] = new_credential_dict
cred.Store.putCredential(credential_id, user_name, password)
return new_credential_dict
credential = cdb.add_credential(tenant_id, credential_name,
user_name, password)
return credential
def delete_credential(self, tenant_id, credential_id):
"""Delete a credential"""
LOG.debug("delete_credential() called\n")
try:
credential = self._get_credential(tenant_id, credential_id)
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
self._credentials.pop(credential_id)
cred.Store.deleteCredential(credential_id)
credential = cdb.remove_credential(tenant_id, credential_id)
return credential
def rename_credential(self, tenant_id, credential_id, new_name):
"""Rename the particular credential resource"""
LOG.debug("rename_credential() called\n")
try:
credential = self._get_credential(tenant_id, credential_id)
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
credential[const.CREDENTIAL_NAME] = new_name
credential = cdb.update_credential(tenant_id, credential_id, new_name)
return credential
def get_host(self, tenant_id, instance_id, instance_desc):
def schedule_host(self, tenant_id, instance_id, instance_desc):
"""Provides the hostname on which a dynamic vnic is reserved"""
LOG.debug("get_host() called\n")
host_list = {const.HOST_LIST: {const.HOST_1: platform.node()}}
LOG.debug("schedule_host() called\n")
host_list = self._invoke_device_plugins(self._func_name(), [tenant_id,
instance_id,
instance_desc])
return host_list
def get_instance_port(self, tenant_id, instance_id, instance_desc):
def associate_port(self, tenant_id, instance_id, instance_desc):
"""
Get the portprofile name and the device namei for the dynamic vnic
"""
LOG.debug("get_instance_port() called\n")
vif_desc = {const.VIF_DESC:
{const.DEVICENAME: "eth2", const.UCSPROFILE: "default"}}
return vif_desc
LOG.debug("associate_port() called\n")
return self._invoke_device_plugins(self._func_name(), [tenant_id,
instance_id,
instance_desc])
"""
Private functions
"""
def _invoke_device_plugins(self, function_name, args):
"""
All device-specific calls are delegate to the model
All device-specific calls are delegated to the model
"""
getattr(self._model, function_name)(args)
return getattr(self._model, function_name)(args)
def _get_vlan_for_tenant(self, tenant_id, net_name):
"""Get vlan ID"""
# TODO (Sumit):
# The VLAN ID for a tenant might need to be obtained from
# somewhere (from Donabe/Melange?)
# Also need to make sure that the VLAN ID is not being used already
# Currently, just a wrap-around counter ranging from VLAN_START to
# VLAN_END
return cdb.reserve_vlanid()
return self._vlan_mgr.reserve_segmentation_id(tenant_id, net_name)
def _release_vlan_for_tenant(self, tenant_id, net_id):
"""Relase VLAN"""
vlan_binding = cdb.get_vlan_binding(net_id)
return cdb.release_vlanid(vlan_binding[const.VLANID])
return self._vlan_mgr.release_segmentation_id(tenant_id, net_id)
def _get_vlan_name(self, net_id, vlan):
"""Getting the vlan name from the tenant and vlan"""
@ -511,69 +500,3 @@ class L2Network(QuantumPluginBase):
def _func_name(self, offset=0):
"""Getting the name of the calling funciton"""
return inspect.stack()[1 + offset][3]
def _make_net_dict(self, net_id, net_name, ports):
"""Helper funciton to create network resource dictionary"""
res = {const.NET_ID: net_id, const.NET_NAME: net_name}
res[const.NET_PORTS] = ports
return res
def _make_port_dict(self, port_id, port_state, net_id, attachment):
"""Helper function to create port resource dictionary"""
res = {const.PORT_ID: port_id, const.PORT_STATE: port_state}
res[const.NET_ID] = net_id
res[const.ATTACHMENT] = attachment
return res
def _make_portprofile_dict(self, tenant_id, profile_id, profile_name,
qos):
"""Helper funciton to create port-profile resource dictionary"""
profile_associations = self._make_portprofile_assc_list(tenant_id,
profile_id)
res = {const.PROFILE_ID: str(profile_id),
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: profile_associations,
const.PROFILE_VLAN_ID: None,
const.PROFILE_QOS: qos}
return res
def _make_portprofile_assc_list(self, tenant_id, profile_id):
"""Helper function to create port profile association list"""
plist = cdb.get_pp_binding(tenant_id, profile_id)
assc_list = []
for port in plist:
assc_list.append(port[const.PORTID])
return assc_list
def _get_qos_level(self, tenant_id, qos_id):
"""Return a QoS level based on the ID"""
qos_level = self._qos_levels.get(qos_id)
if not qos_level:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
return qos_level
def _get_credential(self, tenant_id, credential_id):
"""Return a credential based on the ID"""
credential = self._credentials.get(credential_id)
if not credential:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credetial_id=credential_id)
return credential
def _get_unique_qos_id(self, tenant_id):
"""Get a unique QoS ID"""
self._qoslevels_counter += 1
self._qoslevels_counter %= int(const.MAX_QOS_LEVELS)
qos_id = tenant_id[16:] + "-qos-" + str(self._qoslevels_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
return qos_id
def _get_unique_credential_id(self, tenant_id):
"""Get a unique credential ID"""
self._credentials_counter += 1
self._credentials_counter %= int(const.MAX_CREDENTIALS)
cred_id = tenant_id[16:] + "-crd-" + str(self._credentials_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
return cred_id

View File

@ -49,6 +49,9 @@ MAX_NETWORKS = SECTION_CONF['max_networks']
SECTION_CONF = CONF_PARSER_OBJ['MODEL']
MODEL_CLASS = SECTION_CONF['model_class']
SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION']
MANAGER_CLASS = SECTION_CONF['manager_class']
CONF_FILE = "conf/plugins.ini"
CONF_PARSER_OBJ = confp.\

View File

@ -0,0 +1,75 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
from abc import ABCMeta, abstractmethod
class L2NetworkSegmentationMgrBase(object):
"""
Base class for L2 Network Segmentation Manager
"""
__metaclass__ = ABCMeta
@abstractmethod
def reserve_segmentation_id(self, tenant_id, net_name, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def release_segmentation_id(self, tenant_id, net_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@classmethod
def __subclasshook__(cls, klass):
"""
The __subclasshook__ method is a class method
that will be called everytime a class is tested
using issubclass(klass, Plugin).
In that case, it will check that every method
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is L2NetworkSegmentationMgrBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:
if method in base.__dict__:
fn_obj = base.__dict__[method]
if inspect.isfunction(fn_obj):
abstract_fn_obj = cls.__dict__[method]
arg_count = fn_obj.func_code.co_argcount
expected_arg_count = \
abstract_fn_obj.func_code.co_argcount
method_ok = arg_count == expected_arg_count
if method_ok:
continue
return NotImplemented
return True
return NotImplemented

View File

@ -0,0 +1,20 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""

View File

@ -0,0 +1,173 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
from copy import deepcopy
import inspect
import logging as LOG
import platform
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(__name__)
class L2NetworkMultiBlade(L2NetworkModelBase):
"""
Implements the L2NetworkModelBase
This implementation works with UCS and Nexus plugin for the
following topology:
One or more UCSM (each with one or more chasses connected)
All UCSM connected to a single Nexus Switch
"""
_plugins = {}
_inventory = {}
def __init__(self):
for key in conf.PLUGINS[const.PLUGINS].keys():
self._plugins[key] = utils.import_object(
conf.PLUGINS[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s\n" % \
conf.PLUGINS[const.PLUGINS][key])
if key in conf.PLUGINS[const.INVENTORY].keys():
self._inventory[key] = utils.import_object(
conf.PLUGINS[const.INVENTORY][key])
LOG.debug("Loaded device inventory %s\n" % \
conf.PLUGINS[const.INVENTORY][key])
def _func_name(self, offset=0):
"""Get the name of the calling function"""
return inspect.stack()[1 + offset][3]
def _invoke_plugin_per_device(self, plugin_key, function_name, args):
"""Invoke only device plugin for all the devices in the system"""
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:
self._invoke_plugin(plugin_key, function_name, args,
device_params)
else:
for device_ip in device_ips:
new_device_params = deepcopy(device_params)
new_device_params[const.DEVICE_IP] = device_ip
self._invoke_plugin(plugin_key, function_name, args,
new_device_params)
def _invoke_inventory(self, plugin_key, function_name, args):
"""Invoke only the inventory implementation"""
if not plugin_key in self._inventory.keys():
LOG.warn("No %s inventory loaded" % plugin_key)
LOG.warn("%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):
"""Invoke only the device plugin"""
return getattr(self._plugins[plugin_key], function_name)(*args,
**kwargs)
def get_all_networks(self, args):
"""Not implemented for this model"""
pass
def create_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(), args)
def delete_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(), args)
def get_network_details(self, args):
"""Not implemented for this model"""
pass
def rename_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(), args)
def get_all_ports(self, args):
"""Not implemented for this model"""
pass
def create_port(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def delete_port(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def update_port(self, args):
"""Not implemented for this model"""
pass
def get_port_details(self, args):
"""Not implemented for this model"""
pass
def plug_interface(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def unplug_interface(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def schedule_host(self, args):
"""Provides the hostname on which a dynamic vnic is reserved"""
LOG.debug("schedule_host() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
def associate_port(self, args):
"""
Get the portprofile name and the device namei for the dynamic vnic
"""
LOG.debug("associate_port() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)

View File

@ -0,0 +1,164 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
from copy import deepcopy
import inspect
import logging as LOG
import platform
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(__name__)
class L2NetworkSingleBlade(L2NetworkModelBase):
"""
Implements the L2NetworkModelBase
This implementation works with a single UCS blade
"""
_plugins = {}
_inventory = {}
def __init__(self):
for key in conf.PLUGINS[const.PLUGINS].keys():
self._plugins[key] = utils.import_object(
conf.PLUGINS[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s\n" % \
conf.PLUGINS[const.PLUGINS][key])
if key in conf.PLUGINS[const.INVENTORY].keys():
self._inventory[key] = utils.import_object(
conf.PLUGINS[const.INVENTORY][key])
LOG.debug("Loaded device inventory %s\n" % \
conf.PLUGINS[const.INVENTORY][key])
def _func_name(self, offset=0):
"""Get the name of the calling function"""
return inspect.stack()[1 + offset][3]
def _invoke_plugin_per_device(self, plugin_key, function_name, args):
"""Invoke only device plugin for all the devices in the system"""
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:
self._invoke_plugin(plugin_key, function_name, args,
device_params)
else:
for device_ip in device_ips:
new_device_params = deepcopy(device_params)
new_device_params[const.DEVICE_IP] = device_ip
self._invoke_plugin(plugin_key, function_name, args,
new_device_params)
def _invoke_inventory(self, plugin_key, function_name, args):
"""Invoke only the inventory implementation"""
if not plugin_key in self._inventory.keys():
LOG.warn("No %s inventory loaded" % plugin_key)
LOG.warn("%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):
"""Invoke only the device plugin"""
return getattr(self._plugins[plugin_key], function_name)(*args,
**kwargs)
def get_all_networks(self, args):
"""Not implemented for this model"""
pass
def create_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def delete_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def get_network_details(self, args):
"""Not implemented for this model"""
pass
def rename_network(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def get_all_ports(self, args):
"""Not implemented for this model"""
pass
def create_port(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def delete_port(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def update_port(self, args):
"""Not implemented for this model"""
pass
def get_port_details(self, args):
"""Not implemented for this model"""
pass
def plug_interface(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def unplug_interface(self, args):
"""Support for the Quantum core API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
def schedule_host(self, args):
"""Provides the hostname on which a dynamic vnic is reserved"""
LOG.debug("schedule_host() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
def associate_port(self, args):
"""
Get the portprofile name and the device namei for the dynamic vnic
"""
LOG.debug("associate_port() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)

View File

@ -24,6 +24,7 @@ Implements a Nexus-OS NETCONF over SSHv2 API Client
import logging as LOG
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.db import l2network_db as cdb
from quantum.plugins.cisco.nexus import cisco_nexus_snippets as snipp
from ncclient import manager
@ -119,8 +120,12 @@ class CiscoNEXUSDriver():
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
nexus_password) as man:
self.enable_vlan(man, vlan_id, vlan_name)
self.enable_vlan_on_trunk_int(man, nexus_first_interface, vlan_id)
self.enable_vlan_on_trunk_int(man, nexus_second_interface, vlan_id)
vlan_ids = self.build_vlans_cmd()
LOG.debug("NexusDriver VLAN IDs: %s" % vlan_ids)
self.enable_vlan_on_trunk_int(man, nexus_first_interface,
vlan_ids)
self.enable_vlan_on_trunk_int(man, nexus_second_interface,
vlan_ids)
def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_first_interface, nexus_second_interface,
@ -132,5 +137,19 @@ class CiscoNEXUSDriver():
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
nexus_password) as man:
self.disable_vlan(man, vlan_id)
self.disable_switch_port(man, nexus_first_interface)
self.disable_switch_port(man, nexus_second_interface)
self.disable_vlan_on_trunk_int(man, nexus_first_interface,
vlan_id)
self.disable_vlan_on_trunk_int(man, nexus_second_interface,
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(',')

View File

@ -26,9 +26,9 @@ from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
from quantum.plugins.cisco.db import nexus_db as nxos_db
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.nexus import cisco_nexus_configuration as conf
from quantum.plugins.cisco.db import nexus_db as nxos_db
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)

View File

@ -35,11 +35,12 @@ flags.DEFINE_integer('quantum_port', 9696,
HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_EXT = '/v1.0'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'
CSCO_EXT_NAME = 'Cisco Nova Tenant'
ACTION = '/schedule_host'
class QuantumScheduler(driver.Scheduler):
@ -83,7 +84,7 @@ class QuantumScheduler(driver.Scheduler):
client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
action_prefix=ACTION_PREFIX_CSCO)
request_url = "/novatenants/" + project_id + "/get_host"
request_url = "/novatenants/" + project_id + ACTION
data = client.do_request('PUT', request_url, body=instance_data_dict)
hostname = data["host_list"]["host_1"]

View File

@ -40,11 +40,12 @@ HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
TENANT_ID = 'nova'
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_EXT = '/v1.0'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'
CSCO_EXT_NAME = 'Cisco Nova Tenant'
ACTION = '/associate_port'
class Libvirt802dot1QbhDriver(VIFDriver):
@ -87,7 +88,7 @@ class Libvirt802dot1QbhDriver(VIFDriver):
client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
action_prefix=ACTION_PREFIX_CSCO)
request_url = "/novatenants/" + project_id + "/get_instance_port"
request_url = "/novatenants/" + project_id + ACTION
data = client.do_request('PUT', request_url, body=instance_data_dict)
device = data['vif_desc']['device']

View File

@ -0,0 +1,20 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""

View File

@ -0,0 +1,47 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import logging as LOG
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.db import l2network_db as cdb
from quantum.plugins.cisco.l2network_segmentation_base \
import L2NetworkSegmentationMgrBase
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2NetworkVLANMgr(L2NetworkSegmentationMgrBase):
"""
VLAN Manager which gets VLAN ID from DB
"""
def __init__(self):
cdb.create_vlanids()
def reserve_segmentation_id(self, tenant_id, net_name, **kwargs):
"""Get an available VLAN ID"""
return cdb.reserve_vlanid()
def release_segmentation_id(self, tenant_id, net_id, **kwargs):
"""Release the ID"""
vlan_binding = cdb.get_vlan_binding(net_id)
return cdb.release_vlanid(vlan_binding[const.VLANID])

View File

@ -80,14 +80,14 @@ class PortprofileExtensionTest(unittest.TestCase):
self.profile_path = '/extensions/csco/tenants/tt/portprofiles'
self.portprofile_path = '/extensions/csco/tenants/tt/portprofiles/'
self.test_port_profile = {'portprofile':
{'portprofile_name': 'cisco_test_portprofile',
'qos_name': 'test-qos1'}}
{'portprofile_name': 'cisco_test_portprofile',
'qos_name': 'test-qos1'}}
self.tenant_id = "test_tenant"
self.network_name = "test_network"
options = {}
options['plugin_provider'] = 'quantum.plugins.cisco.l2network_plugin'\
'.L2Network'
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self._l2network_plugin = l2network_plugin.L2Network()
def test_list_portprofile(self):
@ -107,9 +107,10 @@ class PortprofileExtensionTest(unittest.TestCase):
content_type=self.contenttype)
index_response = self.test_app.get(self.profile_path)
index_resp_body = wsgi.Serializer().deserialize(index_response.body,
self.contenttype)
self.assertEqual(200, index_response.status_int)
# Clean Up - Delete the Port Profiles
resp_body1 = wsgi.Serializer().deserialize(create_response1.body,
self.contenttype)
portprofile_path1_temp = self.portprofile_path +\
@ -117,9 +118,17 @@ class PortprofileExtensionTest(unittest.TestCase):
portprofile_path1 = str(portprofile_path1_temp)
resp_body2 = wsgi.Serializer().deserialize(create_response2.body,
self.contenttype)
list_all_portprofiles = [resp_body1['portprofiles']['portprofile'],
resp_body2['portprofiles']['portprofile']]
self.assertTrue(index_resp_body['portprofiles'][0] in
list_all_portprofiles)
self.assertTrue(index_resp_body['portprofiles'][1] in
list_all_portprofiles)
portprofile_path2_temp = self.portprofile_path +\
resp_body2['portprofiles']['portprofile']['id']
portprofile_path2 = str(portprofile_path2_temp)
# Clean Up - Delete the Port Profiles
self.tear_down_profile(portprofile_path1)
self.tear_down_profile(portprofile_path2)
LOG.debug("test_list_portprofile - END")
@ -168,6 +177,14 @@ class PortprofileExtensionTest(unittest.TestCase):
resp_body['portprofiles']['portprofile']['id']
show_port_path = str(show_path_temp)
show_response = self.test_app.get(show_port_path)
show_resp_dict = wsgi.Serializer().deserialize(show_response.body,
self.contenttype)
self.assertEqual(
show_resp_dict['portprofiles']['portprofile']['qos_name'],
self.test_port_profile['portprofile']['qos_name'])
self.assertEqual(
show_resp_dict['portprofiles']['portprofile']['name'],
self.test_port_profile['portprofile']['portprofile_name'])
self.assertEqual(200, show_response.status_int)
# Clean Up - Delete the Port Profile
@ -204,6 +221,14 @@ class PortprofileExtensionTest(unittest.TestCase):
resp_body['portprofiles']['portprofile']['id']
rename_path = str(rename_path_temp)
rename_response = self.test_app.put(rename_path, rename_req_body)
rename_resp_dict = wsgi.Serializer().deserialize(rename_response.body,
self.contenttype)
self.assertEqual(
rename_resp_dict['portprofiles']['portprofile']['qos_name'],
self.test_port_profile['portprofile']['qos_name'])
self.assertEqual(
rename_resp_dict['portprofiles']['portprofile']['name'],
rename_port_profile['portprofile']['portprofile_name'])
self.assertEqual(200, rename_response.status_int)
# Clean Up - Delete the Port Profile
@ -288,8 +313,8 @@ class PortprofileExtensionTest(unittest.TestCase):
req.headers = {}
req.headers['Accept'] = content_type
req.body = body
return req
LOG.debug("test_create_request - END")
return req
def _create_network(self, name=None):
@ -301,15 +326,15 @@ class PortprofileExtensionTest(unittest.TestCase):
else:
net_name = self.network_name
net_path = "/tenants/tt/networks"
net_data = {'network': {'net-name': '%s' % net_name}}
net_data = {'network': {'name': '%s' % net_name}}
req_body = wsgi.Serializer().serialize(net_data, self.contenttype)
network_req = self.create_request(net_path, req_body,
self.contenttype, 'POST')
network_res = network_req.get_response(self.api)
network_data = wsgi.Serializer().deserialize(network_res.body,
self.contenttype)
return network_data['networks']['network']['id']
LOG.debug("Creating network - END")
return network_data['network']['id']
def _create_port(self, network_id, port_state):
@ -317,7 +342,7 @@ class PortprofileExtensionTest(unittest.TestCase):
LOG.debug("Creating port for network %s - START", network_id)
port_path = "/tenants/tt/networks/%s/ports" % network_id
port_req_data = {'port': {'port-state': '%s' % port_state}}
port_req_data = {'port': {'state': '%s' % port_state}}
req_body = wsgi.Serializer().serialize(port_req_data,
self.contenttype)
port_req = self.create_request(port_path, req_body,
@ -325,8 +350,27 @@ class PortprofileExtensionTest(unittest.TestCase):
port_res = port_req.get_response(self.api)
port_data = wsgi.Serializer().deserialize(port_res.body,
self.contenttype)
return port_data['ports']['port']['id']
LOG.debug("Creating port for network - END")
return port_data['port']['id']
def _delete_port(self, network_id, port_id):
""" Delete port """
LOG.debug("Deleting port for network %s - START", network_id)
port_path = "/tenants/tt/networks/%(network_id)s/ports/"\
"%(port_id)s" % locals()
port_req = self.create_request(port_path, None,
self.contenttype, 'DELETE')
port_req.get_response(self.api)
LOG.debug("Deleting port for network - END")
def _delete_network(self, network_id):
""" Delete network """
LOG.debug("Deleting network %s - START", network_id)
network_path = "/tenants/tt/networks/%s" % network_id
network_req = self.create_request(network_path, None,
self.contenttype, 'DELETE')
network_req.get_response(self.api)
LOG.debug("Deleting network - END")
def test_associate_portprofile(self):
@ -363,6 +407,7 @@ class PortprofileExtensionTest(unittest.TestCase):
delete_path = str(delete_path_temp)
self.tear_down_associate_profile(delete_path, disassociate_path,
req_assign_body)
self.tear_down_port_network(net_id, port_id)
LOG.debug("test_associate_portprofile - END")
def test_associate_portprofileDNE(self, portprofile_id='100'):
@ -420,8 +465,15 @@ class PortprofileExtensionTest(unittest.TestCase):
resp_body['portprofiles']['portprofile']['id']
delete_path = str(delete_path_temp)
self.tear_down_profile(delete_path)
self.tear_down_port_network(net_id, port_id)
LOG.debug("test_disassociate_portprofile - END")
def tear_down_port_network(self, net_id, port_id):
""" Tear down port and network """
self._delete_port(net_id, port_id)
self._delete_network(net_id)
def tear_down_profile(self, delete_profile_path):
""" Tear down profile"""
@ -452,8 +504,8 @@ class NovatenantExtensionTest(unittest.TestCase):
parent_resource = dict(member_name="tenant",
collection_name="extensions/csco/tenants")
member_actions = {'get_host': "PUT",
'get_instance_port': "PUT"}
member_actions = {'schedule_host': "PUT",
'associate_port': "PUT"}
controller = novatenant.NovatenantsController(
QuantumManager.get_plugin())
res_ext = extensions.ResourceExtension('novatenants', controller,
@ -463,47 +515,50 @@ class NovatenantExtensionTest(unittest.TestCase):
SimpleExtensionManager(res_ext))
self.contenttype = 'application/json'
self.novatenants_path = '/extensions/csco/tenants/tt/novatenants/'
self.test_instance_data = {'novatenant': {'instance_id': 1,
'instance_desc': {'key1': '1',
'key2': '2'}}}
def test_get_host(self):
self.test_associate_port_data = {'novatenant': {'instance_id': 1,
'instance_desc': {'project_id': 'demo',
'user_id': 'root', 'vif_id': '23432423'}}}
self.test_associate_data = {'novatenant': {'instance_id': 1,
'instance_desc': {'project_id': 'demo',
'user_id': 'root'}}}
self._l2network_plugin = l2network_plugin.L2Network()
def test_schedule_host(self):
""" Test get host"""
LOG.debug("test_get_host - START")
req_body = json.dumps(self.test_instance_data)
host_path = self.novatenants_path + "001/get_host"
LOG.debug("test_schedule_host - START")
req_body = json.dumps(self.test_associate_data)
host_path = self.novatenants_path + "001/schedule_host"
host_response = self.test_app.put(
host_path, req_body,
content_type=self.contenttype)
host_path, req_body,
content_type=self.contenttype)
self.assertEqual(200, host_response.status_int)
LOG.debug("test_get_host - END")
def test_get_hostBADRequest(self):
LOG.debug("test_schedule_host - END")
def test_schedule_hostBADRequest(self):
""" Test get host bad request"""
LOG.debug("test_get_hostBADRequest - START")
host_path = self.novatenants_path + "001/get_host"
LOG.debug("test_schedule_hostBADRequest - START")
host_path = self.novatenants_path + "001/schedule_host"
host_response = self.test_app.put(
host_path, 'BAD_REQUEST',
content_type=self.contenttype, status='*')
self.assertEqual(400, host_response.status_int)
LOG.debug("test_get_hostBADRequest - END")
LOG.debug("test_schedule_hostBADRequest - END")
def test_instance_port(self):
""" Test get instance port """
LOG.debug("test_instance_port - START")
req_body = json.dumps(self.test_instance_data)
instance_port_path = self.novatenants_path + "001/get_instance_port"
instance_port_response = self.test_app.put(
instance_port_path, req_body,
def test_associate_port(self):
""" Test get associate port """
LOG.debug("test_associate_port - START")
req_body = json.dumps(self.test_associate_port_data)
associate_port_path = self.novatenants_path + "001/associate_port"
associate_port_response = self.test_app.put(
associate_port_path, req_body,
content_type=self.contenttype)
self.assertEqual(200, instance_port_response.status_int)
LOG.debug("test_instance_port - END")
self.assertEqual(200, associate_port_response.status_int)
LOG.debug("test_associate_port - END")
def tearDown(self):
""" Tear down """
db.clear_db()
class QosExtensionTest(unittest.TestCase):
@ -525,6 +580,7 @@ class QosExtensionTest(unittest.TestCase):
self.qos_second_path = '/extensions/csco/tenants/tt/qos/'
self.test_qos_data = {'qos': {'qos_name': 'cisco_test_qos',
'qos_desc': {'PPS': 50, 'TTL': 5}}}
self._l2network_plugin = l2network_plugin.L2Network()
def test_create_qos(self):
@ -571,6 +627,8 @@ class QosExtensionTest(unittest.TestCase):
create_resp2 = self.test_app.post(self.qos_path, req_body2,
content_type=self.contenttype)
index_response = self.test_app.get(self.qos_path)
index_resp_body = wsgi.Serializer().deserialize(index_response.body,
self.contenttype)
self.assertEqual(200, index_response.status_int)
# Clean Up - Delete the qos's
@ -581,6 +639,9 @@ class QosExtensionTest(unittest.TestCase):
qos_path1 = str(qos_path1_temp)
resp_body2 = wsgi.Serializer().deserialize(create_resp2.body,
self.contenttype)
list_all_qos = [resp_body1['qoss']['qos'], resp_body2['qoss']['qos']]
self.assertTrue(index_resp_body['qoss'][0] in list_all_qos)
self.assertTrue(index_resp_body['qoss'][1] in list_all_qos)
qos_path2_temp = self.qos_second_path +\
resp_body2['qoss']['qos']['id']
qos_path2 = str(qos_path2_temp)
@ -602,6 +663,12 @@ class QosExtensionTest(unittest.TestCase):
resp_body['qoss']['qos']['id']
show_qos_path = str(show_path_temp)
show_response = self.test_app.get(show_qos_path)
show_resp_dict = wsgi.Serializer().deserialize(show_response.body,
self.contenttype)
self.assertEqual(
show_resp_dict['qoss']['qos']['name'],
self.test_qos_data['qos']['qos_name'])
self.assertEqual(200, show_response.status_int)
# Clean Up - Delete the qos
@ -636,6 +703,11 @@ class QosExtensionTest(unittest.TestCase):
rename_path = str(rename_path_temp)
rename_response = self.test_app.put(rename_path, rename_req_body)
self.assertEqual(200, rename_response.status_int)
rename_resp_dict = wsgi.Serializer().deserialize(rename_response.body,
self.contenttype)
self.assertEqual(
rename_resp_dict['qoss']['qos']['name'],
'cisco_rename_qos')
self.tearDownQos(rename_path)
LOG.debug("test_update_qos - END")
@ -709,6 +781,9 @@ class QosExtensionTest(unittest.TestCase):
self.test_app.delete(delete_profile_path)
def tearDown(self):
db.clear_db()
class CredentialExtensionTest(unittest.TestCase):
@ -731,6 +806,7 @@ class CredentialExtensionTest(unittest.TestCase):
{'credential_name': 'cred8',
'user_name': 'newUser2',
'password': 'newPasswd1'}}
self._l2network_plugin = l2network_plugin.L2Network()
def test_list_credentials(self):
@ -751,6 +827,8 @@ class CredentialExtensionTest(unittest.TestCase):
content_type=self.contenttype)
index_response = self.test_app.get(
self.credential_path)
index_resp_body = wsgi.Serializer().deserialize(index_response.body,
self.contenttype)
self.assertEqual(200, index_response.status_int)
#CLean Up - Deletion of the Credentials
resp_body1 = wsgi.Serializer().deserialize(
@ -760,6 +838,12 @@ class CredentialExtensionTest(unittest.TestCase):
delete_path1 = str(delete_path1_temp)
resp_body2 = wsgi.Serializer().deserialize(
create_response2.body, self.contenttype)
list_all_credential = [resp_body1['credentials']['credential'],
resp_body2['credentials']['credential']]
self.assertTrue(index_resp_body['credentials'][0] in
list_all_credential)
self.assertTrue(index_resp_body['credentials'][1] in
list_all_credential)
delete_path2_temp = self.cred_second_path +\
resp_body2['credentials']['credential']['id']
delete_path2 = str(delete_path2_temp)
@ -812,6 +896,14 @@ class CredentialExtensionTest(unittest.TestCase):
resp_body['credentials']['credential']['id']
show_cred_path = str(show_path_temp)
show_response = self.test_app.get(show_cred_path)
show_resp_dict = wsgi.Serializer().deserialize(show_response.body,
self.contenttype)
self.assertEqual(
show_resp_dict['credentials']['credential']['name'],
self.test_credential_data['credential']['user_name'])
self.assertEqual(
show_resp_dict['credentials']['credential']['password'],
self.test_credential_data['credential']['password'])
self.assertEqual(200, show_response.status_int)
LOG.debug("test_show_credential - END")
@ -846,6 +938,14 @@ class CredentialExtensionTest(unittest.TestCase):
resp_body['credentials']['credential']['id']
rename_path = str(rename_path_temp)
rename_response = self.test_app.put(rename_path, rename_req_body)
rename_resp_dict = wsgi.Serializer().deserialize(rename_response.body,
self.contenttype)
self.assertEqual(
rename_resp_dict['credentials']['credential']['name'],
'cred3')
self.assertEqual(
rename_resp_dict['credentials']['credential']['password'],
self.test_credential_data['credential']['password'])
self.assertEqual(200, rename_response.status_int)
# Clean Up - Delete the Credentials
self.tearDownCredential(rename_path)
@ -918,6 +1018,9 @@ class CredentialExtensionTest(unittest.TestCase):
def tearDownCredential(self, delete_path):
self.test_app.delete(delete_path)
def tearDown(self):
db.clear_db()
def app_factory(global_conf, **local_conf):
conf = global_conf.copy()

View File

@ -27,11 +27,99 @@ from quantum.plugins.cisco.common import cisco_constants as const
import quantum.plugins.cisco.db.api as db
import quantum.plugins.cisco.db.l2network_db as l2network_db
import quantum.plugins.cisco.db.nexus_db as nexus_db
import quantum.plugins.cisco.db.ucs_db as ucs_db
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class UcsDB(object):
"""Class consisting of methods to call ucs db methods"""
def get_all_port_bindings(self):
"""get all port binding"""
port_bindings = []
try:
for bind in ucs_db.get_all_portbindings():
LOG.debug("Getting port binding for port: %s" % bind.port_id)
port_bind_dict = {}
port_bind_dict["port-id"] = bind.port_id
port_bind_dict["blade-intf-dn"] = str(bind.blade_intf_dn)
port_bind_dict["portprofile-name"] = bind.portprofile_name
port_bind_dict["vlan-name"] = bind.vlan_name
port_bind_dict["vlan-id"] = str(bind.vlan_id)
port_bind_dict["qos"] = bind.qos
port_bindings.append(port_bind_dict)
except Exception, exc:
LOG.error("Failed to get all port bindings: %s" % str(exc))
return port_bindings
def get_port_binding(self, port_id):
"""get port binding"""
port_binding = []
try:
for bind in ucs_db.get_portbinding(port_id):
LOG.debug("Getting port binding for port: %s" % bind.port_id)
port_bind_dict = {}
port_bind_dict["port-id"] = bind.port_id
port_bind_dict["blade-intf-dn"] = str(bind.blade_intf_dn)
port_bind_dict["portprofile-name"] = bind.portprofile_name
port_bind_dict["vlan-name"] = bind.vlan_name
port_bind_dict["vlan-id"] = str(bind.vlan_id)
port_bind_dict["qos"] = bind.qos
port_binding.append(port_bind_dict)
except Exception, exc:
LOG.error("Failed to get port binding: %s" % str(exc))
return port_binding
def create_port_binding(self, port_id, blade_intf_dn, portprofile_name, \
vlan_name, vlan_id, qos):
"""create port binding"""
port_bind_dict = {}
try:
res = ucs_db.add_portbinding(port_id, blade_intf_dn, \
portprofile_name, vlan_name, vlan_id, qos)
LOG.debug("Created port binding: %s" % res.port_id)
port_bind_dict["port-id"] = res.port_id
port_bind_dict["blade-intf-dn"] = str(res.blade_intf_dn)
port_bind_dict["portprofile-name"] = res.portprofile_name
port_bind_dict["vlan-name"] = res.vlan_name
port_bind_dict["vlan-id"] = str(res.vlan_id)
port_bind_dict["qos"] = res.qos
return port_bind_dict
except Exception, exc:
LOG.error("Failed to create port binding: %s" % str(exc))
def delete_port_binding(self, port_id):
"""delete port binding"""
try:
res = ucs_db.remove_portbinding(port_id)
LOG.debug("Deleted port binding : %s" % res.port_id)
port_bind_dict = {}
port_bind_dict["port-id"] = res.port_id
return port_bind_dict
except Exception, exc:
raise Exception("Failed to delete port profile: %s" % str(exc))
def update_port_binding(self, port_id, blade_intf_dn, \
portprofile_name, vlan_name, vlan_id, qos):
"""update port binding"""
try:
res = ucs_db.update_portbinding(port_id, blade_intf_dn, \
portprofile_name, vlan_name, vlan_id, qos)
LOG.debug("Updating port binding: %s" % res.port_id)
port_bind_dict = {}
port_bind_dict["port-id"] = res.port_id
port_bind_dict["dynamic-vnic-id"] = str(res.blade_intf_dn)
port_bind_dict["portprofile-name"] = res.portprofile_name
port_bind_dict["vlan-name"] = res.vlan_name
port_bind_dict["vlan-id"] = str(res.vlan_id)
port_bind_dict["qos"] = res.qos
return port_bind_dict
except Exception, exc:
raise Exception("Failed to update portprofile binding:%s"
% str(exc))
class NexusDB(object):
"""Class consisting of methods to call nexus db methods"""
def get_all_nexusportbindings(self):
@ -110,7 +198,7 @@ class L2networkDB(object):
vlans = []
try:
for vlan_bind in l2network_db.get_all_vlan_bindings():
LOG.debug("Getting vlan bindings for vlan: %s" % \
LOG.debug("Getting vlan bindings for vlan: %s" %
vlan_bind.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
@ -126,7 +214,7 @@ class L2networkDB(object):
vlan = []
try:
for vlan_bind in l2network_db.get_vlan_binding(network_id):
LOG.debug("Getting vlan binding for vlan: %s" \
LOG.debug("Getting vlan binding for vlan: %s"
% vlan_bind.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
@ -164,7 +252,7 @@ class L2networkDB(object):
def update_vlan_binding(self, network_id, vlan_id, vlan_name):
"""Update a vlan binding"""
try:
res = l2network_db.update_vlan_binding(network_id, vlan_id, \
res = l2network_db.update_vlan_binding(network_id, vlan_id,
vlan_name)
LOG.debug("Updating vlan binding for vlan: %s" % res.vlan_id)
vlan_dict = {}
@ -252,7 +340,7 @@ class L2networkDB(object):
pp_bindings = []
try:
for pp_bind in l2network_db.get_all_pp_bindings():
LOG.debug("Getting port profile binding: %s" % \
LOG.debug("Getting port profile binding: %s" %
pp_bind.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(pp_bind.portprofile_id)
@ -269,7 +357,7 @@ class L2networkDB(object):
pp_binding = []
try:
for pp_bind in l2network_db.get_pp_binding(tenant_id, pp_id):
LOG.debug("Getting port profile binding: %s" % \
LOG.debug("Getting port profile binding: %s" %
pp_bind.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(pp_bind.portprofile_id)
@ -285,7 +373,7 @@ class L2networkDB(object):
"""Add a portprofile binding"""
ppbinding_dict = {}
try:
res = l2network_db.add_pp_binding(tenant_id, port_id, pp_id, \
res = l2network_db.add_pp_binding(tenant_id, port_id, pp_id,
default)
LOG.debug("Created port profile binding: %s" % res.portprofile_id)
ppbinding_dict["portprofile-id"] = str(res.portprofile_id)
@ -307,7 +395,7 @@ class L2networkDB(object):
except Exception, exc:
raise Exception("Failed to delete port profile: %s" % str(exc))
def update_pp_binding(self, tenant_id, pp_id, newtenant_id, \
def update_pp_binding(self, tenant_id, pp_id, newtenant_id,
port_id, default):
"""Update portprofile binding"""
try:
@ -321,7 +409,7 @@ class L2networkDB(object):
ppbinding_dict["default"] = res.default
return ppbinding_dict
except Exception, exc:
raise Exception("Failed to update portprofile binding:%s" \
raise Exception("Failed to update portprofile binding:%s"
% str(exc))
@ -494,6 +582,101 @@ class QuantumDB(object):
raise Exception("Failed to unplug interface: %s" % str(exc))
class UcsDBTest(unittest.TestCase):
"""Class conisting of ucs DB unit tests"""
def setUp(self):
"""Setup for ucs db tests"""
l2network_db.initialize()
self.quantum = QuantumDB()
self.dbtest = UcsDB()
LOG.debug("Setup")
def tearDown(self):
"""Tear Down"""
db.clear_db()
def testm_create_portbinding(self):
"""create port binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
port_bind1 = self.dbtest.create_port_binding(port1["port-id"],
"vnic1", "pp1", "vlan1", 10, "qos1")
self.assertTrue(port_bind1["port-id"] == port1["port-id"])
self.teardown_portbinding()
self.teardown_network_port()
def testn_getall_portbindings(self):
"""get all port binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
port2 = self.quantum.create_port(net1["net-id"])
port_bind1 = self.dbtest.create_port_binding(port1["port-id"],
"vnic1", "pp1", "vlan1", 10, "qos1")
port_bind2 = self.dbtest.create_port_binding(port2["port-id"],
"vnic2", "pp2", "vlan2", 20, "qos2")
port_bindings = self.dbtest.get_all_port_bindings()
count = 0
for pbind in port_bindings:
if "vlan" in pbind["vlan-name"]:
count += 1
self.assertTrue(count == 2)
self.teardown_portbinding()
self.teardown_network_port()
def testo_delete_portbinding(self):
"""delete port binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
port_bind1 = self.dbtest.create_port_binding(port1["port-id"],
"vnic1", "pp1", "vlan1", 10, "qos1")
self.dbtest.delete_port_binding(port1["port-id"])
port_bindings = self.dbtest.get_all_port_bindings()
count = 0
for pbind in port_bindings:
if "vlan " in pbind["vlan-name"]:
count += 1
self.assertTrue(count == 0)
self.teardown_portbinding()
self.teardown_network_port()
def testp_update_portbinding(self):
"""update port binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
port_bind1 = self.dbtest.create_port_binding(port1["port-id"],
"vnic1", "pp1", "vlan1", 10, "qos1")
port_bind1 = self.dbtest.update_port_binding(port1["port-id"],
"vnic1", "newpp1", "newvlan1", 11, "newqos1")
port_bindings = self.dbtest.get_all_port_bindings()
count = 0
for pbind in port_bindings:
if "new" in pbind["vlan-name"]:
count += 1
self.assertTrue(count == 1)
self.teardown_portbinding()
self.teardown_network_port()
def teardown_portbinding(self):
"""tear down port binding"""
LOG.debug("Tearing Down Port Binding")
port_bindings = self.dbtest.get_all_port_bindings()
for port_binding in port_bindings:
portid = port_binding["port-id"]
self.dbtest.delete_port_binding(portid)
def teardown_network_port(self):
"""tearDown for Network and Port table"""
networks = self.quantum.get_all_networks("t1")
for net in networks:
netid = net["net-id"]
name = net["net-name"]
if "net" in name:
ports = self.quantum.get_all_ports(netid)
for por in ports:
self.quantum.delete_port(netid, por["port-id"])
self.quantum.delete_network(netid)
class NexusDBTest(unittest.TestCase):
"""Class conisting of nexus DB unit tests"""
def setUp(self):
@ -763,7 +946,7 @@ class L2networkDBTest(unittest.TestCase):
self.assertTrue(used == True)
used = l2network_db.release_vlanid(vlanid)
self.assertTrue(used == False)
self.teardown_vlanid()
#counting on default teardown here to clear db
def teardown_network(self):
"""tearDown Network table"""
@ -809,14 +992,6 @@ class L2networkDBTest(unittest.TestCase):
portid = pp_binding["port-id"]
self.dbtest.delete_pp_binding("t1", portid, ppid)
def teardown_vlanid(self):
"""tearDown VlanID table"""
LOG.debug("Tearing Down Vlan IDs")
vlanids = l2network_db.get_all_vlanids()
for vlanid in vlanids:
vlan_id = vlanid["vlan_id"]
l2network_db.delete_vlanid(vlan_id)
class QuantumDBTest(unittest.TestCase):
"""Class conisting of Quantum DB unit tests"""

View File

@ -14,12 +14,13 @@
#
# @author: Shweta Padubidri, Peter Strunk, Cisco Systems, Inc.
#
import unittest
import logging
import unittest
from quantum.common import exceptions as exc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.nexus import cisco_nexus_plugin
from quantum.plugins.cisco.db import l2network_db as cdb
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.nexus import cisco_nexus_plugin
LOG = logging.getLogger('quantum.tests.test_nexus')
@ -37,8 +38,8 @@ class TestNexusPlugin(unittest.TestCase):
self.vlan_name = "q-" + str(self.net_id) + "vlan"
self.vlan_id = 267
self.port_id = "9"
self._cisco_nexus_plugin = cisco_nexus_plugin.NexusPlugin()
cdb.initialize()
self._cisco_nexus_plugin = cisco_nexus_plugin.NexusPlugin()
def test_create_network(self, net_tenant_id=None, network_name=None,
network_id=None, net_vlan_name=None,
@ -69,6 +70,8 @@ class TestNexusPlugin(unittest.TestCase):
else:
vlan_id = self.vlan_id
network_created = self.create_network(tenant_id, net_id)
cdb.add_vlan_binding(vlan_id, vlan_name, network_created["net-id"])
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, net_name, net_id, vlan_name, vlan_id)
self.assertEqual(new_net_dict[const.NET_ID], self.net_id)
@ -263,6 +266,19 @@ class TestNexusPlugin(unittest.TestCase):
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_get_vlan_id_for_network - END")
def create_network(self, tenant_id, net_name):
"""Create a network"""
net_dict = {}
try:
res = db.network_create(tenant_id, net_name)
LOG.debug("Created network: %s" % res.uuid)
net_dict["tenant-id"] = res.tenant_id
net_dict["net-id"] = str(res.uuid)
net_dict["net-name"] = res.name
return net_dict
except Exception, exc:
LOG.error("Failed to create network: %s" % str(exc))
def tearDownNetwork(self, tenant_id, network_dict_id):
"""
Clean up functions after the tests

View File

@ -155,13 +155,3 @@ class TestUCSDriver(unittest.TestCase):
self.profile_name, self.profile_client_name)
self.assertEqual(profile_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_get_next_dynamic_nic(self):
"""
Tests get next dynamic nic
"""
LOG.debug("test_get_next_dynamic_nic - START")
dynamic_nic_id = self.ucsm_driver._get_next_dynamic_nic()
self.assertTrue(len(dynamic_nic_id) > 0)
LOG.debug("test_get_next_dynamic_nic - END")

View File

@ -37,3 +37,10 @@ PROFILE_NAME_PREFIX = SECTION['profile_name_prefix']
SECTION = CP['DRIVER']
UCSM_DRIVER = SECTION['name']
CONF_FILE = "../conf/ucs_inventory.ini"
CP = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
INVENTORY = CP.walk(CP.dummy)

View File

@ -0,0 +1,682 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
from copy import deepcopy
import logging as LOG
from quantum.common import exceptions as exc
from quantum.plugins.cisco.l2device_inventory_base \
import L2NetworkDeviceInventoryBase
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
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 api as db
from quantum.plugins.cisco.db import ucs_db as udb
from quantum.plugins.cisco.ucs \
import cisco_ucs_inventory_configuration as conf
from quantum.plugins.cisco.ucs import cisco_ucs_network_driver
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(__name__)
"""
The _inventory data strcuture contains a nested disctioary:
{"UCSM_IP: {"Chassis-ID": [Balde-ID, Blade-ID],
"Chassis-ID": [Blade-ID, Blade-ID, Blade-ID]]},
"UCSM_IP: {"Chassis-ID": [Balde-ID]}
}
"""
"""
_inventory_state data structure is organized as below:
{ucsm_ip:
{chassis_id:
{blade_id:
{'blade-data':
{blade-dn-1: {blade-intf-data},
blade-dn-2: {blade-intf-data}
}
}
}
}
}
'blade-data': Blade Data dictionary has the following keys:
===========================================================
const.BLADE_INTF_DATA: This is a dictionary, with the key as the
dn of the interface, and the value as the
Blade Interface Dictionary described next
const.BLADE_UNRESERVED_INTF_COUNT: Number of unreserved interfaces
on this blade
'blade-intf-data': Blade Interface dictionary has the following keys:
=====================================================================
const.BLADE_INTF_DN
const.BLADE_INTF_ORDER
const.BLADE_INTF_LINK_STATE
const.BLADE_INTF_OPER_STATE
const.BLADE_INTF_INST_TYPE
const.BLADE_INTF_RHEL_DEVICE_NAME
const.BLADE_INTF_RESERVATION
const.TENANTID
const.PORTID
const.PROFILE_ID
const.INSTANCE_ID
const.VIF_ID
"""
class UCSInventory(L2NetworkDeviceInventoryBase):
"""
Manages the state of all the UCS chasses, and blades in
the system
"""
_inventory = {}
_host_names = {}
_inventory_state = {}
def __init__(self):
self._client = cisco_ucs_network_driver.CiscoUCSMDriver()
self._load_inventory()
def _load_inventory(self):
"""Load the inventory from a config file"""
inventory = deepcopy(conf.INVENTORY)
LOG.info("Loaded UCS inventory: %s\n" % inventory)
LOG.info("Building UCS inventory state (this may take a while)...")
for ucsm in inventory.keys():
ucsm_ip = inventory[ucsm][const.IP_ADDRESS]
inventory[ucsm].pop(const.IP_ADDRESS)
chassis_dict = {}
for chassis in inventory[ucsm].keys():
chassis_id = inventory[ucsm][chassis][const.CHASSIS_ID]
inventory[ucsm][chassis].pop(const.CHASSIS_ID)
blade_list = []
for blade in inventory[ucsm][chassis].keys():
blade_id = \
inventory[ucsm][chassis][blade][const.BLADE_ID]
host_name = \
inventory[ucsm][chassis][blade][const.HOST_NAME]
host_key = ucsm_ip + "-" + chassis_id + "-" + blade_id
self._host_names[host_key] = host_name
blade_list.append(blade_id)
chassis_dict[chassis_id] = blade_list
self._inventory[ucsm_ip] = chassis_dict
self.build_inventory_state()
def _get_host_name(self, ucsm_ip, chassis_id, blade_id):
"""Get the hostname based on the blade info"""
host_key = ucsm_ip + "-" + chassis_id + "-" + blade_id
return self._host_names[host_key]
def _get_initial_blade_state(self, chassis_id, blade_id, ucsm_ip,
ucsm_username, ucsm_password):
"""Get the initial blade state"""
blade_intf_data = self._client.get_blade_data(chassis_id, blade_id,
ucsm_ip, ucsm_username,
ucsm_password)
unreserved_counter = 0
for blade_intf in blade_intf_data.keys():
dist_name = blade_intf_data[blade_intf][const.BLADE_INTF_DN]
# We first make a pass through the state in UCSM
# If a particular interface is showing as being allocated in
# UCSM then it is definitely being used and so should be
# marked as reserved, else we temporarily mark it as unreserved
# based on the UCSM state, but may later change it if a port
# association is found in the DB
if (blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] == \
const.BLADE_INTF_STATE_UNALLOCATED or \
blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] == \
const.BLADE_INTF_STATE_UNKNOWN) and \
blade_intf_data[blade_intf][const.BLADE_INTF_OPER_STATE] == \
const.BLADE_INTF_STATE_UNKNOWN:
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_UNRESERVED
unreserved_counter += 1
blade_intf_data[blade_intf][const.TENANTID] = None
blade_intf_data[blade_intf][const.PORTID] = None
blade_intf_data[blade_intf][const.PROFILE_ID] = None
blade_intf_data[blade_intf][const.INSTANCE_ID] = None
blade_intf_data[blade_intf][const.VIF_ID] = None
else:
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_RESERVED
port_binding = udb.get_portbinding_dn(dist_name)
if port_binding:
# We have found a port binding for this interface in the DB,
# so we have earlier marked this interface as unreserved, we
# need to change it, and also load the state from the DB for
# other associations
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_UNRESERVED:
unreserved_counter -= 1
blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_RESERVED
blade_intf_data[blade_intf][const.TENANTID] = \
port_binding[const.TENANTID]
blade_intf_data[blade_intf][const.PORTID] = \
port_binding[const.PORTID]
blade_intf_data[blade_intf][const.PROFILE_ID] = \
port_binding[const.PORTPROFILENAME]
blade_intf_data[blade_intf][const.INSTANCE_ID] = \
port_binding[const.INSTANCE_ID]
blade_intf_data[blade_intf][const.VIF_ID] = \
port_binding[const.VIF_ID]
blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter}
return blade_data
def _get_blade_state(self, chassis_id, blade_id, ucsm_ip,
ucsm_username, ucsm_password):
"""Get the blade state"""
blade_intf_data = self._client.get_blade_data(chassis_id, blade_id,
ucsm_ip, ucsm_username,
ucsm_password)
unreserved_counter = 0
for blade_intf in blade_intf_data.keys():
if (blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] == \
const.BLADE_INTF_STATE_UNALLOCATED or \
blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] == \
const.BLADE_INTF_STATE_UNKNOWN) and \
blade_intf_data[blade_intf][const.BLADE_INTF_OPER_STATE] == \
const.BLADE_INTF_STATE_UNKNOWN:
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_UNRESERVED
unreserved_counter += 1
else:
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_RESERVED
blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter}
return blade_data
def _get_all_ucsms(self):
"""Return a list of the IPs of all the UCSMs in the system"""
return {const.DEVICE_IP: self._inventory.keys()}
def _get_blade_for_port(self, args):
"""
Return the a dict with IP address of the blade
on which a dynamic vnic was reserved for this port
"""
tenant_id = args[0]
net_id = args[1]
port_id = args[2]
rsvd_info = self.get_rsvd_blade_intf_by_port(tenant_id, port_id)
if not rsvd_info:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
device_params = {const.DEVICE_IP: [rsvd_info[const.UCSM_IP]]}
return device_params
def _get_host_name_for_rsvd_intf(self, tenant_id, instance_id):
"""
Return the hostname of the blade with a reserved instance
for this tenant
"""
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
for blade_intf in blade_intf_data.keys():
tmp = deepcopy(blade_intf_data[blade_intf])
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] == None:
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] = instance_id
host_name = self._get_host_name(ucsm_ip,
chassis_id,
blade_id)
port_binding = udb.get_portbinding_dn(blade_intf)
port_id = port_binding[const.PORTID]
udb.update_portbinding(port_id,
instance_id=instance_id)
return host_name
LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
tenant_id)
return None
def _get_instance_port(self, tenant_id, instance_id, vif_id):
"""
Return the device name for a reserved interface
"""
found_blade_intf_data = None
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] == instance_id:
found_blade_intf_data = blade_intf_data
LOG.debug("Found blade %s associated with this" \
" instance: %s" % \
(blade_id,
instance_id))
break
if found_blade_intf_data:
blade_intf_data = found_blade_intf_data
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
(not blade_intf_data[blade_intf][const.VIF_ID]):
blade_intf_data[blade_intf][const.VIF_ID] = \
vif_id
blade_intf_data[blade_intf]\
[const.INSTANCE_ID] = instance_id
port_binding = udb.get_portbinding_dn(blade_intf)
port_id = port_binding[const.PORTID]
udb.update_portbinding(port_id, instance_id=instance_id,
vif_id=vif_id)
db.port_set_attachment_by_id(port_id, vif_id)
device_name = blade_intf_data[blade_intf]\
[const.BLADE_INTF_RHEL_DEVICE_NAME]
profile_name = port_binding[const.PORTPROFILENAME]
dynamicnic_details = \
{const.DEVICENAME: device_name,
const.UCSPROFILE: profile_name}
LOG.debug("Found reserved dynamic nic: %s" \
"associated with port %s" %
(blade_intf_data[blade_intf], port_id))
LOG.debug("Returning dynamic nic details: %s" %
dynamicnic_details)
return dynamicnic_details
LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
tenant_id)
return None
def _disassociate_vifid_from_port(self, tenant_id, port_id):
"""
Return the device name for a reserved interface
"""
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf][const.PORTID] == \
port_id:
vif_id = blade_intf_data[blade_intf][const.VIF_ID]
blade_intf_data[blade_intf][const.VIF_ID] = \
None
blade_intf_data[blade_intf][const.INSTANCE_ID] = \
None
udb.update_portbinding(port_id, instance_id=None,
vif_id=None)
LOG.debug("Disassociated VIF-ID: %s " \
"from port: %s" \
"in UCS inventory state for blade: %s" %
(vif_id, port_id,
blade_intf_data[blade_intf]))
return
LOG.warn("Disassociating VIF-ID %s in UCS inventory failed. " \
"Could not find a reserved dynamic nic for tenant: %s" %
(vif_id, tenant_id))
return None
def reload_inventory(self):
"""Reload the inventory from a conf file"""
self._load_inventory()
def build_inventory_state(self):
"""Populate the state of all the blades"""
for ucsm_ip in self._inventory.keys():
self._inventory_state[ucsm_ip] = {ucsm_ip: {}}
ucsm_username = cred.Store.getUsername(ucsm_ip)
ucsm_password = cred.Store.getPassword(ucsm_ip)
chasses_state = {}
self._inventory_state[ucsm_ip] = chasses_state
ucsm = self._inventory[ucsm_ip]
for chassis_id in ucsm.keys():
blades_dict = {}
chasses_state[chassis_id] = blades_dict
for blade_id in ucsm[chassis_id]:
blade_data = self._get_initial_blade_state(chassis_id,
blade_id,
ucsm_ip,
ucsm_username,
ucsm_password)
blades_dict[blade_id] = blade_data
LOG.debug("UCS Inventory state is: %s\n" % self._inventory_state)
return True
def get_least_reserved_blade(self):
"""Return the blade with least number of dynamic nics reserved"""
unreserved_interface_count = 0
least_reserved_blade_ucsm = None
least_reserved_blade_chassis = None
least_reserved_blade_id = None
least_reserved_blade_data = None
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
if blade_data[const.BLADE_UNRESERVED_INTF_COUNT] > \
unreserved_interface_count:
unreserved_interface_count = \
blade_data[const.BLADE_UNRESERVED_INTF_COUNT]
least_reserved_blade_ucsm = ucsm_ip
least_reserved_blade_chassis = chassis_id
least_reserved_blade_id = blade_id
least_reserved_blade_data = blade_data
if unreserved_interface_count == 0:
LOG.warn("No more dynamic nics available for reservation")
return False
least_reserved_blade_dict = \
{const.LEAST_RSVD_BLADE_UCSM: least_reserved_blade_ucsm,
const.LEAST_RSVD_BLADE_CHASSIS: least_reserved_blade_chassis,
const.LEAST_RSVD_BLADE_ID: least_reserved_blade_id,
const.LEAST_RSVD_BLADE_DATA: least_reserved_blade_data}
LOG.debug("Found dynamic nic %s available for reservation",
least_reserved_blade_dict)
return least_reserved_blade_dict
def reserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
blade_data_dict, tenant_id, port_id,
portprofile_name):
"""Reserve an interface on a blade"""
ucsm_username = cred.Store.getUsername(ucsm_ip)
ucsm_password = cred.Store.getPassword(ucsm_ip)
"""
We are first getting the updated blade interface state
"""
blade_data = self._get_blade_state(chassis_id, blade_id, ucsm_ip,
ucsm_username, ucsm_password)
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
old_blade_intf_data = blade_data_dict[const.BLADE_INTF_DATA]
"""
We will now copy the older blade interface state
"""
for blade_intf in blade_intf_data.keys():
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
old_blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION]
blade_intf_data[blade_intf][const.TENANTID] = \
old_blade_intf_data[blade_intf][const.TENANTID]
blade_intf_data[blade_intf][const.PORTID] = \
old_blade_intf_data[blade_intf][const.PORTID]
blade_intf_data[blade_intf][const.PROFILE_ID] = \
old_blade_intf_data[blade_intf][const.PROFILE_ID]
blade_intf_data[blade_intf][const.INSTANCE_ID] = \
old_blade_intf_data[blade_intf][const.INSTANCE_ID]
blade_intf_data[blade_intf][const.VIF_ID] = \
old_blade_intf_data[blade_intf][const.VIF_ID]
blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = \
blade_data_dict[const.BLADE_UNRESERVED_INTF_COUNT]
"""
Now we will reserve an interface if its available
"""
for blade_intf in blade_intf_data.keys():
if blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_UNRESERVED:
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
const.BLADE_INTF_RESERVED
blade_intf_data[blade_intf][const.TENANTID] = tenant_id
blade_intf_data[blade_intf][const.PORTID] = port_id
#blade_intf_data[blade_intf][const.PROFILE_ID] = \
# portprofile_name
blade_intf_data[blade_intf][const.INSTANCE_ID] = None
dev_eth_name = blade_intf_data[blade_intf] \
[const.BLADE_INTF_RHEL_DEVICE_NAME]
"""
We are replacing the older blade interface state with new
"""
self._inventory_state[ucsm_ip][chassis_id][blade_id] \
[const.BLADE_INTF_DATA] = blade_intf_data
self._inventory_state[ucsm_ip][chassis_id][blade_id] \
[const.BLADE_UNRESERVED_INTF_COUNT] -= 1
host_name = self._get_host_name(ucsm_ip, chassis_id,
blade_id)
reserved_nic_dict = {const.RESERVED_NIC_HOSTNAME: host_name,
const.RESERVED_NIC_NAME: dev_eth_name,
const.BLADE_INTF_DN: blade_intf}
port_binding = udb.add_portbinding(port_id, blade_intf, None,
None, None, None)
udb.update_portbinding(port_id,
tenant_id=blade_intf_data[blade_intf]\
[const.TENANTID])
LOG.debug("Reserved blade interface: %s\n" % reserved_nic_dict)
return reserved_nic_dict
LOG.warn("Dynamic nic %s could not be reserved for port-id: %s" %
(blade_data_dict, port_id))
return False
def unreserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
interface_dn):
"""Unreserve a previously reserved interface on a blade"""
ucsm_username = cred.Store.getUsername(ucsm_ip)
ucsm_password = cred.Store.getPassword(ucsm_ip)
self._inventory_state[ucsm_ip][chassis_id][blade_id] \
[const.BLADE_UNRESERVED_INTF_COUNT] += 1
blade_intf = self._inventory_state[ucsm_ip][chassis_id]\
[blade_id][const.BLADE_INTF_DATA][interface_dn]
blade_intf[const.BLADE_INTF_RESERVATION] = const.BLADE_INTF_UNRESERVED
blade_intf[const.TENANTID] = None
blade_intf[const.PORTID] = None
blade_intf[const.PROFILE_ID] = None
blade_intf[const.INSTANCE_ID] = None
blade_intf[const.VIF_ID] = None
LOG.debug("Unreserved blade interface %s\n" % interface_dn)
def get_rsvd_blade_intf_by_port(self, tenant_id, port_id):
"""
Lookup a reserved blade interface based on tenant_id and port_id
and return the blade interface info
"""
for ucsm_ip in self._inventory_state.keys():
ucsm = self._inventory_state[ucsm_ip]
for chassis_id in ucsm.keys():
for blade_id in ucsm[chassis_id]:
blade_data = ucsm[chassis_id][blade_id]
blade_intf_data = blade_data[const.BLADE_INTF_DATA]
for blade_intf in blade_intf_data.keys():
if not blade_intf_data[blade_intf][const.PORTID] or \
not blade_intf_data[blade_intf][const.TENANTID]:
continue
if blade_intf_data[blade_intf]\
[const.BLADE_INTF_RESERVATION] == \
const.BLADE_INTF_RESERVED and \
blade_intf_data[blade_intf]\
[const.TENANTID] == tenant_id and \
blade_intf_data[blade_intf]\
[const.PORTID] == port_id:
interface_dn = blade_intf_data[blade_intf]\
[const.BLADE_INTF_DN]
blade_intf_info = {const.UCSM_IP: ucsm_ip,
const.CHASSIS_ID: chassis_id,
const.BLADE_ID: blade_id,
const.BLADE_INTF_DN:
interface_dn}
return blade_intf_info
LOG.warn("Could not find a reserved nic for tenant: %s port: %s" %
(tenant_id, port_id))
return None
def add_blade(self, ucsm_ip, chassis_id, blade_id):
"""Add a blade to the inventory"""
# TODO (Sumit)
pass
def get_all_networks(self, args):
"""Return all UCSM IPs"""
LOG.debug("get_all_networks() called\n")
return self._get_all_ucsms()
def create_network(self, args):
"""Return all UCSM IPs"""
LOG.debug("create_network() called\n")
return self._get_all_ucsms()
def delete_network(self, args):
"""Return all UCSM IPs"""
LOG.debug("delete_network() called\n")
return self._get_all_ucsms()
def get_network_details(self, args):
"""Return all UCSM IPs"""
LOG.debug("get_network_details() called\n")
return self._get_all_ucsms()
def rename_network(self, args):
"""Return all UCSM IPs"""
LOG.debug("rename_network() called\n")
return self._get_all_ucsms()
def get_all_ports(self, args):
"""Return all UCSM IPs"""
LOG.debug("get_all_ports() called\n")
return self._get_all_ucsms()
def create_port(self, args):
"""
Return the a dict with information of the blade
on which a dynamic vnic is available
"""
LOG.debug("create_port() called\n")
least_reserved_blade_dict = self.get_least_reserved_blade()
if not least_reserved_blade_dict:
raise cexc.NoMoreNics()
ucsm_ip = least_reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM]
device_params = {const.DEVICE_IP: [ucsm_ip],
const.UCS_INVENTORY: self,
const.LEAST_RSVD_BLADE_DICT:\
least_reserved_blade_dict}
return device_params
def delete_port(self, args):
"""
Return the a dict with information of the blade
on which a dynamic vnic was reserved for this port
"""
LOG.debug("delete_port() called\n")
tenant_id = args[0]
net_id = args[1]
port_id = args[2]
rsvd_info = self.get_rsvd_blade_intf_by_port(tenant_id, port_id)
if not rsvd_info:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
device_params = \
{const.DEVICE_IP: [rsvd_info[const.UCSM_IP]],
const.UCS_INVENTORY: self,
const.CHASSIS_ID: rsvd_info[const.CHASSIS_ID],
const.BLADE_ID: rsvd_info[const.BLADE_ID],
const.BLADE_INTF_DN: rsvd_info[const.BLADE_INTF_DN]}
return device_params
def update_port(self, args):
"""
Return the a dict with IP address of the blade
on which a dynamic vnic was reserved for this port
"""
LOG.debug("update_port() called\n")
return self._get_blade_for_port(args)
def get_port_details(self, args):
"""
Return the a dict with IP address of the blade
on which a dynamic vnic was reserved for this port
"""
LOG.debug("get_port_details() called\n")
return self._get_blade_for_port(args)
def plug_interface(self, args):
"""
Return the a dict with IP address of the blade
on which a dynamic vnic was reserved for this port
"""
LOG.debug("plug_interface() called\n")
return self._get_blade_for_port(args)
def unplug_interface(self, args):
"""
Return the a dict with IP address of the blade
on which a dynamic vnic was reserved for this port
"""
LOG.debug("unplug_interface() called\n")
tenant_id = args[0]
port_id = args[2]
self._disassociate_vifid_from_port(tenant_id, port_id)
return self._get_blade_for_port(args)
def schedule_host(self, args):
"""Provides the hostname on which a dynamic vnic is reserved"""
LOG.debug("schedule_host() called\n")
instance_id = args[1]
tenant_id = args[2][const.PROJECT_ID]
host_name = self._get_host_name_for_rsvd_intf(tenant_id, instance_id)
host_list = {const.HOST_LIST: {const.HOST_1: host_name}}
LOG.debug("host_list is: %s" % host_list)
return host_list
def associate_port(self, args):
"""
Get the portprofile name and the device name for the dynamic vnic
"""
LOG.debug("associate_port() called\n")
instance_id = args[1]
tenant_id = args[2][const.PROJECT_ID]
vif_id = args[2][const.VIF_ID]
vif_info = self._get_instance_port(tenant_id, instance_id, vif_id)
vif_desc = {const.VIF_DESC: vif_info}
LOG.debug("vif_desc is: %s" % vif_desc)
return vif_desc

View File

@ -0,0 +1,31 @@
"""
# 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: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "../conf/ucs_inventory.ini"
CP = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
INVENTORY = CP.walk(CP.dummy)

View File

@ -41,6 +41,9 @@ PROFILE_CLIENT = "profileclient_placeholder"
VLAN_NAME = "vlanname_placeholder"
VLAN_ID = "vlanid_placeholder"
OLD_VLAN_NAME = "old_vlanname_placeholder"
BLADE_VALUE = "blade_number_placeholder"
BLADE_DN_VALUE = "blade_dn_placeholder"
CHASSIS_VALUE = "chassis_number_placeholder"
DYNAMIC_NIC_PREFIX = "eth"
# The following are standard strings, messages used to communicate with UCSM,
@ -112,6 +115,27 @@ DELETE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
PROFILE_NAME + "\" status=\"deleted\"> </vnicProfile>" \
"</pair> </inConfigs> </configConfMos>"
GET_BLADE_INTERFACE_STATE = "<configScope cookie=\"" + COOKIE_VALUE + \
"\" dn=\"" + BLADE_DN_VALUE + "\" inClass=\"dcxVIf\" " + \
"inHierarchical=\"false\" inRecursive=\"false\"> " + \
"<inFilter> </inFilter> </configScope>"
GET_BLADE_INTERFACE = "<configResolveClass cookie=\"" + COOKIE_VALUE + \
"\" classId=\"vnicEther\"" + \
" inHierarchical=\"false\">" + \
" <inFilter> <eq class=\"vnicEther\" property=\"equipmentDn\"" + \
" value=\"sys/chassis-" + CHASSIS_VALUE + "/blade-" + \
BLADE_VALUE + "/adaptor-1/host-eth-?\"/> " + \
"</inFilter> </configResolveClass>"
# TODO (Sumit): Assumes "adaptor-1", check if this has to be discovered too
GET_BLADE_INTERFACES = "<configResolveChildren cookie=\"" + \
COOKIE_VALUE + "\" inDn=\"sys/chassis-" + \
CHASSIS_VALUE + "/blade-" + BLADE_VALUE + \
"/adaptor-1\"" + \
" inHierarchical=\"false\"> <inFilter> </inFilter>" + \
" </configResolveChildren>"
class CiscoUCSMDriver():
"""UCSM Driver"""
@ -127,29 +151,31 @@ class CiscoUCSMDriver():
conn.request(METHOD, URL, login_data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug(response_data)
#LOG.debug(response.status)
#LOG.debug(response.reason)
#LOG.debug(response_data)
# TODO (Sumit): If login is not successful, throw exception
xml_tree = et.XML(response_data)
cookie = xml_tree.attrib["outCookie"]
data = data.replace(COOKIE_VALUE, cookie)
LOG.debug("POST: %s" % data)
#LOG.debug("POST: %s" % data)
conn.request(METHOD, URL, data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug("UCSM Response: %s" % response_data)
#LOG.debug(response.status)
#LOG.debug(response.reason)
#LOG.debug("UCSM Response: %s" % response_data)
post_data_response = response_data
logout_data = "<aaaLogout inCookie=\"" + cookie + "\" />"
conn.request(METHOD, URL, logout_data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug(response_data)
#LOG.debug(response.status)
#LOG.debug(response.reason)
#LOG.debug(response_data)
return post_data_response
def _create_vlan_post_data(self, vlan_name, vlan_id):
"""Create command"""
@ -188,13 +214,61 @@ class CiscoUCSMDriver():
data = DELETE_PROFILE.replace(PROFILE_NAME, profile_name)
return data
def _get_next_dynamic_nic(self):
"""Get an avaialble dynamic nic on the host"""
dynamic_nic_id = gvif.get_next_dynic()
if len(dynamic_nic_id) > 0:
return dynamic_nic_id
else:
raise cexc.NoMoreNics()
def _get_blade_interfaces_post_data(self, chassis_number, blade_number):
"""Create command"""
data = GET_BLADE_INTERFACES.replace(CHASSIS_VALUE, chassis_number)
data = data.replace(BLADE_VALUE, blade_number)
return data
def _get_blade_intf_st_post_data(self, blade_dn):
"""Create command"""
data = GET_BLADE_INTERFACE_STATE.replace(BLADE_DN_VALUE, blade_dn)
return data
def _get_blade_interfaces(self, chassis_number, blade_number, ucsm_ip,
ucsm_username, ucsm_password):
"""Create command"""
data = self._get_blade_interfaces_post_data(chassis_number,
blade_number)
response = self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
elements = \
et.XML(response).find("outConfigs").findall("adaptorHostEthIf")
blade_interfaces = {}
for element in elements:
dist_name = element.get("dn", default=None)
if dist_name:
order = element.get("order", default=None)
blade_interface = {const.BLADE_INTF_DN: dist_name,
const.BLADE_INTF_ORDER: order,
const.BLADE_INTF_LINK_STATE: None,
const.BLADE_INTF_OPER_STATE: None,
const.BLADE_INTF_INST_TYPE: None,
const.BLADE_INTF_RHEL_DEVICE_NAME:
self._get_rhel_device_name(order)}
blade_interfaces[dist_name] = blade_interface
return blade_interfaces
def _get_blade_interface_state(self, blade_intf, ucsm_ip,
ucsm_username, ucsm_password):
"""Create command"""
data = \
self._get_blade_intf_st_post_data(blade_intf[const.BLADE_INTF_DN])
response = self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
elements = \
et.XML(response).find("outConfigs").findall("dcxVIf")
for element in elements:
blade_intf[const.BLADE_INTF_LINK_STATE] = element.get("linkState",
default=None)
blade_intf[const.BLADE_INTF_OPER_STATE] = element.get("operState",
default=None)
blade_intf[const.BLADE_INTF_INST_TYPE] = element.get("instType",
default=None)
def _get_rhel_device_name(self, order):
"""Get the device name as on the RHEL host"""
device_name = const.RHEL_DEVICE_NAME_REPFIX + str(int(order) - 1)
return device_name
def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
ucsm_password):
@ -220,19 +294,26 @@ class CiscoUCSMDriver():
new_vlan_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def get_dynamic_nic(self, host):
"""Get an avaialble dynamic nic on the host"""
# TODO (Sumit): Check availability per host
# TODO (Sumit): If not available raise exception
# TODO (Sumit): This simple logic assumes that create-port and
# spawn-VM happens in lock-step
# But we should support multiple create-port calls,
# followed by spawn-VM calls
# That would require managing a pool of available
# dynamic vnics per host
dynamic_nic_name = self._get_next_dynamic_nic()
LOG.debug("Reserving dynamic nic %s" % dynamic_nic_name)
return dynamic_nic_name
def get_blade_data(self, chassis_number, blade_number,
ucsm_ip, ucsm_username,
ucsm_password):
"""
Returns only the dynamic interfaces on the blade
"""
blade_interfaces = self._get_blade_interfaces(chassis_number,
blade_number,
ucsm_ip,
ucsm_username,
ucsm_password)
for blade_intf in blade_interfaces.keys():
self._get_blade_interface_state(blade_interfaces[blade_intf],
ucsm_ip, ucsm_username,
ucsm_password)
if blade_interfaces[blade_intf][const.BLADE_INTF_INST_TYPE] != \
const.BLADE_INTF_DYNAMIC:
blade_interfaces.pop(blade_intf)
return blade_interfaces
def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password):
"""Create request for UCSM"""
@ -244,8 +325,3 @@ class CiscoUCSMDriver():
"""Create request for UCSM"""
data = self._delete_profile_post_data(profile_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def release_dynamic_nic(self, host):
"""Release a reserved dynamic nic on the host"""
# TODO (Sumit): Release on a specific host
pass

View File

@ -27,6 +27,9 @@ from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
from quantum.plugins.cisco.common import cisco_utils as cutil
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.db import l2network_db as cdb
from quantum.plugins.cisco.db import ucs_db as udb
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf
@ -36,16 +39,10 @@ LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class UCSVICPlugin(L2DevicePluginBase):
"""UCS Device Plugin"""
_networks = {}
def __init__(self):
self._client = utils.import_object(conf.UCSM_DRIVER)
self._driver = utils.import_object(conf.UCSM_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER)
self._utils = cutil.DBUtils()
# TODO (Sumit) This is for now, when using only one chassis
self._ucsm_ip = conf.UCSM_IP_ADDRESS
self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS)
self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS)
# TODO (Sumit) Make the counter per UCSM
self._port_profile_counter = 0
@ -56,7 +53,16 @@ class UCSVICPlugin(L2DevicePluginBase):
the specified tenant.
"""
LOG.debug("UCSVICPlugin:get_all_networks() called\n")
return self._networks.values()
self._set_ucsm(kwargs[const.DEVICE_IP])
networks_list = db.network_list(tenant_id)
new_networks_list = []
for network in networks_list:
new_network_dict = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
new_networks_list.append(new_network_dict)
return new_networks_list
def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
**kwargs):
@ -65,15 +71,15 @@ class UCSVICPlugin(L2DevicePluginBase):
a symbolic name.
"""
LOG.debug("UCSVICPlugin:create_network() called\n")
self._client.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip,
self._set_ucsm(kwargs[const.DEVICE_IP])
self._driver.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
new_net_dict = {const.NET_ID: net_id,
const.NET_NAME: net_name,
const.NET_PORTS: {},
const.NET_VLAN_NAME: vlan_name,
const.NET_VLAN_ID: vlan_id}
self._networks[net_id] = new_net_dict
return new_net_dict
network = db.network_get(net_id)
ports_on_net = []
new_network_dict = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
ports_on_net)
return new_network_dict
def delete_network(self, tenant_id, net_id, **kwargs):
"""
@ -81,17 +87,16 @@ class UCSVICPlugin(L2DevicePluginBase):
belonging to the specified tenant.
"""
LOG.debug("UCSVICPlugin:delete_network() called\n")
net = self._networks.get(net_id)
# TODO (Sumit) : Verify that no attachments are plugged into the
# network
if net:
# TODO (Sumit) : Before deleting the network, make sure all the
# ports associated with this network are also deleted
self._client.delete_vlan(net[const.NET_VLAN_NAME], self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._networks.pop(net_id)
return net
raise exc.NetworkNotFound(net_id=net_id)
self._set_ucsm(kwargs[const.DEVICE_IP])
net = db.network_get(net_id)
vlan_binding = cdb.get_vlan_binding(net[const.UUID])
vlan_name = vlan_binding[const.VLANNAME]
self._driver.delete_vlan(vlan_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
net_dict = cutil.make_net_dict(net[const.UUID],
net[const.NETWORKNAME],
[])
return net_dict
def get_network_details(self, tenant_id, net_id, **kwargs):
"""
@ -99,8 +104,22 @@ class UCSVICPlugin(L2DevicePluginBase):
spec
"""
LOG.debug("UCSVICPlugin:get_network_details() called\n")
network = self._get_network(tenant_id, net_id)
return network
self._set_ucsm(kwargs[const.DEVICE_IP])
network = db.network_get(net_id)
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = cutil.make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
ports_on_net.append(new_port)
new_network = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
ports_on_net)
return new_network
def rename_network(self, tenant_id, net_id, new_name, **kwargs):
"""
@ -108,9 +127,12 @@ class UCSVICPlugin(L2DevicePluginBase):
Virtual Network.
"""
LOG.debug("UCSVICPlugin:rename_network() called\n")
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_name
return network
self._set_ucsm(kwargs[const.DEVICE_IP])
network = db.network_get(net_id)
net_dict = cutil.make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
return net_dict
def get_all_ports(self, tenant_id, net_id, **kwargs):
"""
@ -118,8 +140,14 @@ class UCSVICPlugin(L2DevicePluginBase):
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:get_all_ports() called\n")
network = self._get_network(tenant_id, net_id)
ports_on_net = network[const.NET_PORTS].values()
self._set_ucsm(kwargs[const.DEVICE_IP])
network = db.network_get(net_id)
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
port_binding = udb.get_portbinding(port[const.UUID])
ports_on_net.append(port_binding)
return ports_on_net
def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
@ -127,28 +155,29 @@ class UCSVICPlugin(L2DevicePluginBase):
Creates a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:create_port() called\n")
net = self._get_network(tenant_id, net_id)
ports = net[const.NET_PORTS]
# TODO (Sumit): This works on a single host deployment,
# in multi-host environment, dummy needs to be replaced with the
# hostname
dynamic_nic_name = self._client.get_dynamic_nic("dummy")
self._set_ucsm(kwargs[const.DEVICE_IP])
qos = None
ucs_inventory = kwargs[const.UCS_INVENTORY]
least_rsvd_blade_dict = kwargs[const.LEAST_RSVD_BLADE_DICT]
chassis_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_CHASSIS]
blade_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_ID]
blade_data_dict = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_DATA]
new_port_profile = self._create_port_profile(tenant_id, net_id,
port_id,
conf.DEFAULT_VLAN_NAME,
conf.DEFAULT_VLAN_ID)
profile_name = new_port_profile[const.PROFILE_NAME]
sql_query = "INSERT INTO ports (port_id, profile_name, dynamic_vnic," \
"host, instance_name, instance_nic_name, used) VALUES" \
"('%s', '%s', '%s', 'dummy', NULL, NULL, 0)" % \
(port_id, profile_name, dynamic_nic_name)
self._utils.execute_db_query(sql_query)
new_port_dict = {const.PORT_ID: port_id,
const.PORT_STATE: const.PORT_UP,
const.ATTACHMENT: None,
const.PORT_PROFILE: new_port_profile}
ports[port_id] = new_port_dict
return new_port_dict
rsvd_nic_dict = ucs_inventory.\
reserve_blade_interface(self._ucsm_ip, chassis_id,
blade_id, blade_data_dict,
tenant_id, port_id,
profile_name)
port_binding = udb.update_portbinding(port_id,
portprofile_name=profile_name,
vlan_name=conf.DEFAULT_VLAN_NAME,
vlan_id=conf.DEFAULT_VLAN_ID,
qos=qos)
return port_binding
def delete_port(self, tenant_id, net_id, port_id, **kwargs):
"""
@ -158,34 +187,25 @@ class UCSVICPlugin(L2DevicePluginBase):
then the port can be deleted.
"""
LOG.debug("UCSVICPlugin:delete_port() called\n")
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
try:
#TODO (Sumit): Before deleting port profile make sure that there
# is no VM using this port profile
self._client.release_dynamic_nic("dummy")
port_profile = port[const.PORT_PROFILE]
self._delete_port_profile(port_id,
port_profile[const.PROFILE_NAME])
sql_query = "delete from ports where port_id = \"%s\"" % \
(port[const.PORT_ID])
self._utils.execute_db_query(sql_query)
net = self._get_network(tenant_id, net_id)
net[const.NET_PORTS].pop(port_id)
except KeyError:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
self._set_ucsm(kwargs[const.DEVICE_IP])
ucs_inventory = kwargs[const.UCS_INVENTORY]
chassis_id = kwargs[const.CHASSIS_ID]
blade_id = kwargs[const.BLADE_ID]
interface_dn = kwargs[const.BLADE_INTF_DN]
port_binding = udb.get_portbinding(port_id)
profile_name = port_binding[const.PORTPROFILENAME]
self._delete_port_profile(port_id, profile_name)
ucs_inventory.unreserve_blade_interface(self._ucsm_ip, chassis_id,
blade_id, interface_dn)
return udb.remove_portbinding(port_id)
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:update_port() called\n")
port = self._get_port(tenant_id, net_id, port_id)
self._validate_port_state(port_state)
port[const.PORT_STATE] = port_state
return port
self._set_ucsm(kwargs[const.DEVICE_IP])
pass
def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
"""
@ -193,7 +213,9 @@ class UCSVICPlugin(L2DevicePluginBase):
that is attached to this particular port.
"""
LOG.debug("UCSVICPlugin:get_port_details() called\n")
return self._get_port(tenant_id, net_id, port_id)
self._set_ucsm(kwargs[const.DEVICE_IP])
port_binding = udb.get_portbinding(port_id)
return port_binding
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
**kwargs):
@ -202,24 +224,18 @@ class UCSVICPlugin(L2DevicePluginBase):
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:plug_interface() called\n")
self._validate_attachment(tenant_id, net_id, port_id,
remote_interface_id)
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
port[const.ATTACHMENT] = remote_interface_id
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
old_vlan_name = port_profile[const.PROFILE_VLAN_NAME]
self._set_ucsm(kwargs[const.DEVICE_IP])
port_binding = udb.get_portbinding(port_id)
profile_name = port_binding[const.PORTPROFILENAME]
old_vlan_name = port_binding[const.VLANNAME]
new_vlan_name = self._get_vlan_name_for_network(tenant_id, net_id)
new_vlan_id = self._get_vlan_id_for_network(tenant_id, net_id)
self._client.change_vlan_in_profile(profile_name, old_vlan_name,
self._driver.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
port_profile[const.PROFILE_VLAN_NAME] = new_vlan_name
port_profile[const.PROFILE_VLAN_ID] = new_vlan_id
return udb.update_portbinding(port_id, vlan_name=new_vlan_name,
vlan_id=new_vlan_id)
def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
"""
@ -227,18 +243,17 @@ class UCSVICPlugin(L2DevicePluginBase):
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:unplug_interface() called\n")
port = self._get_port(tenant_id, net_id, port_id)
port[const.ATTACHMENT] = None
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
old_vlan_name = port_profile[const.PROFILE_VLAN_NAME]
self._set_ucsm(kwargs[const.DEVICE_IP])
port_binding = udb.get_portbinding(port_id)
profile_name = port_binding[const.PORTPROFILENAME]
old_vlan_name = port_binding[const.VLANNAME]
new_vlan_name = conf.DEFAULT_VLAN_NAME
self._client.change_vlan_in_profile(profile_name, old_vlan_name,
self._driver.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
port_profile[const.PROFILE_VLAN_NAME] = conf.DEFAULT_VLAN_NAME
port_profile[const.PROFILE_VLAN_ID] = conf.DEFAULT_VLAN_ID
return udb.update_portbinding(port_id, vlan_name=new_vlan_name,
vlan_id=conf.DEFAULT_VLAN_ID)
def _get_profile_name(self, port_id):
"""Returns the port profile name based on the port UUID"""
@ -246,48 +261,15 @@ class UCSVICPlugin(L2DevicePluginBase):
+ cutil.get16ByteUUID(port_id)
return profile_name
def _validate_port_state(self, port_state):
"""Check the port state"""
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
"""Check if the VIF can be attached"""
network = self._get_network(tenant_id, network_id)
for port in network[const.NET_PORTS].values():
if port[const.ATTACHMENT] == remote_interface_id:
raise exc.PortInUse(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT])
def _get_network(self, tenant_id, network_id):
"""Get the network object ref"""
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network
def _get_vlan_name_for_network(self, tenant_id, network_id):
"""Return the VLAN name as set by the L2 network plugin"""
net = self._get_network(tenant_id, network_id)
vlan_name = net[const.NET_VLAN_NAME]
return vlan_name
vlan_binding = cdb.get_vlan_binding(network_id)
return vlan_binding[const.VLANNAME]
def _get_vlan_id_for_network(self, tenant_id, network_id):
"""Return the VLAN id as set by the L2 network plugin"""
net = self._get_network(tenant_id, network_id)
vlan_id = net[const.NET_VLAN_ID]
return vlan_id
def _get_port(self, tenant_id, network_id, port_id):
"""Get the port object ref"""
net = self._get_network(tenant_id, network_id)
port = net[const.NET_PORTS].get(port_id)
if not port:
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
return port
vlan_binding = cdb.get_vlan_binding(network_id)
return vlan_binding[const.VLANID]
def _create_port_profile(self, tenant_id, net_id, port_id, vlan_name,
vlan_id):
@ -295,7 +277,7 @@ class UCSVICPlugin(L2DevicePluginBase):
if self._port_profile_counter >= int(conf.MAX_UCSM_PORT_PROFILES):
raise cexc.UCSMPortProfileLimit(net_id=net_id, port_id=port_id)
profile_name = self._get_profile_name(port_id)
self._client.create_profile(profile_name, vlan_name, self._ucsm_ip,
self._driver.create_profile(profile_name, vlan_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._port_profile_counter += 1
new_port_profile = {const.PROFILE_NAME: profile_name,
@ -305,6 +287,12 @@ class UCSVICPlugin(L2DevicePluginBase):
def _delete_port_profile(self, port_id, profile_name):
"""Delete port profile in UCSM"""
self._client.delete_profile(profile_name, self._ucsm_ip,
self._driver.delete_profile(profile_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._port_profile_counter -= 1
def _set_ucsm(self, ucsm_ip):
"""Set the UCSM IP, username, and password"""
self._ucsm_ip = ucsm_ip
self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS)
self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS)

View File

@ -42,7 +42,7 @@ class FakeHTTPConnection:
self._req = None
options = \
dict(plugin_provider='quantum.plugins.SamplePlugin.FakePlugin')
self._api = server.APIRouterV01(options)
self._api = server.APIRouterV1(options)
def request(self, method, action, body, headers):
# TODO: remove version prefix from action!

View File

@ -218,8 +218,8 @@ class APITest(unittest.TestCase):
LOG.debug("_test_delete_network - format:%s - START", format)
content_type = "application/%s" % format
network_id = self._create_network(format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
@ -240,8 +240,8 @@ class APITest(unittest.TestCase):
port_state = "ACTIVE"
attachment_id = "test_attachment"
network_id = self._create_network(format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
port_id = self._create_port(network_id, port_state, format)
#plug an attachment into the port
LOG.debug("Putting attachment into port %s", port_id)
@ -252,8 +252,8 @@ class APITest(unittest.TestCase):
attachment_res = attachment_req.get_response(self.api)
self.assertEquals(attachment_res.status_int, 204)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
@ -267,12 +267,12 @@ class APITest(unittest.TestCase):
content_type = "application/%s" % format
port_state = "ACTIVE"
network_id = self._create_network(format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
port_id = self._create_port(network_id, port_state, format)
LOG.debug("Deleting network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting network %s"\
" of tenant %s" % (network_id, self.tenant_id))
delete_network_req = testlib.network_delete_request(self.tenant_id,
network_id,
format)
@ -453,8 +453,9 @@ class APITest(unittest.TestCase):
port_state = "ACTIVE"
network_id = self._create_network(format)
port_id = self._create_port(network_id, port_state, format)
LOG.debug("Deleting port %(port_id)s for network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting port %s for network %s"\
" of tenant %s" % (port_id, network_id,
self.tenant_id))
delete_port_req = testlib.port_delete_request(self.tenant_id,
network_id, port_id,
format)
@ -484,8 +485,9 @@ class APITest(unittest.TestCase):
attachment_id)
attachment_res = attachment_req.get_response(self.api)
self.assertEquals(attachment_res.status_int, 204)
LOG.debug("Deleting port %(port_id)s for network %(network_id)s"\
" of tenant %(tenant_id)s", locals())
LOG.debug("Deleting port %s for network %s"\
" of tenant %s" % (port_id, network_id,
self.tenant_id))
delete_port_req = testlib.port_delete_request(self.tenant_id,
network_id, port_id,
format)
@ -783,7 +785,7 @@ class APITest(unittest.TestCase):
def setUp(self):
options = {}
options['plugin_provider'] = test_config['plugin_name']
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self.tenant_id = "test_tenant"
self.network_name = "test_network"
self._net_serializer = \

View File

@ -42,7 +42,7 @@ class CLITest(unittest.TestCase):
"""Prepare the test environment"""
options = {}
options['plugin_provider'] = 'quantum.plugins.SamplePlugin.FakePlugin'
self.api = server.APIRouterV01(options)
self.api = server.APIRouterV1(options)
self.tenant_id = "test_tenant"
self.network_name_1 = "test_network_1"