Merge trunk
Now re-submitting admin credentials if token expires
This commit is contained in:
commit
42ddbf938a
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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}',
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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"""
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
24
quantum/plugins/cisco/conf/ucs_inventory.ini
Normal file
24
quantum/plugins/cisco/conf/ucs_inventory.ini
Normal 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>
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
130
quantum/plugins/cisco/db/ucs_db.py
Normal file
130
quantum/plugins/cisco/db/ucs_db.py
Normal 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 []
|
55
quantum/plugins/cisco/db/ucs_models.py
Normal file
55
quantum/plugins/cisco/db/ucs_models.py
Normal 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)
|
343
quantum/plugins/cisco/l2device_inventory_base.py
Normal file
343
quantum/plugins/cisco/l2device_inventory_base.py
Normal 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
|
@ -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)
|
@ -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
|
||||
|
@ -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.\
|
||||
|
75
quantum/plugins/cisco/l2network_segmentation_base.py
Normal file
75
quantum/plugins/cisco/l2network_segmentation_base.py
Normal 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
|
20
quantum/plugins/cisco/models/__init__.py
Normal file
20
quantum/plugins/cisco/models/__init__.py
Normal 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.
|
||||
#
|
||||
"""
|
173
quantum/plugins/cisco/models/l2network_multi_blade.py
Normal file
173
quantum/plugins/cisco/models/l2network_multi_blade.py
Normal 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)
|
164
quantum/plugins/cisco/models/l2network_single_blade.py
Normal file
164
quantum/plugins/cisco/models/l2network_single_blade.py
Normal 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)
|
@ -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(',')
|
||||
|
@ -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)
|
||||
|
@ -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"]
|
||||
|
@ -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']
|
||||
|
20
quantum/plugins/cisco/segmentation/__init__.py
Normal file
20
quantum/plugins/cisco/segmentation/__init__.py
Normal 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.
|
||||
#
|
||||
"""
|
47
quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py
Normal file
47
quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py
Normal 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])
|
@ -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()
|
||||
|
@ -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"""
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
682
quantum/plugins/cisco/ucs/cisco_ucs_inventory.py
Normal file
682
quantum/plugins/cisco/ucs/cisco_ucs_inventory.py
Normal 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
|
@ -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)
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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!
|
||||
|
@ -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 = \
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user