merge sumit's branch for lp837752

This commit is contained in:
Dan Wendlandt 2011-09-09 09:01:30 -07:00
commit 9b6efba5e9
20 changed files with 849 additions and 206 deletions

116
extensions/multiport.py Normal file
View File

@ -0,0 +1,116 @@
"""
# 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: Ying Liu, Cisco Systems, Inc.
#
"""
import logging
from webob import exc
from quantum.api import api_common as common
from quantum.api.views import ports as port_view
from quantum.common import extensions
from quantum.manager import QuantumManager
from quantum.plugins.cisco.common import cisco_exceptions as exception
from quantum.plugins.cisco.common import cisco_faults as faults
LOG = logging.getLogger('quantum.api.multiports')
class Multiport(object):
"""extension class multiport"""
def __init__(self):
pass
@classmethod
def get_name(cls):
""" Returns Ext Resource Name """
return "Cisco Multiport"
@classmethod
def get_alias(cls):
""" Returns Ext Resource Alias """
return "Cisco Multiport"
@classmethod
def get_description(cls):
""" Returns Ext Resource Description """
return "handle multiple ports in one call"
@classmethod
def get_namespace(cls):
""" Returns Ext Resource Namespace """
return "http://docs.ciscocloud.com/api/ext/multiport/v1.0"
@classmethod
def get_updated(cls):
""" Returns Ext Resource Update Time """
return "2011-08-25T13:25:27-06:00"
@classmethod
def get_resources(cls):
""" Returns Ext Resources """
parent_resource = dict(member_name="tenant",
collection_name="extensions/csco/tenants")
controller = MultiportController(QuantumManager.get_plugin())
return [extensions.ResourceExtension('multiport', controller,
parent=parent_resource)]
class MultiportController(common.QuantumController):
""" multiport API controller
based on QuantumController """
_multiport_ops_param_list = [{
'param-name': 'net_id_list',
'required': True}, {
'param-name': 'status',
'required': True}, {
'param-name': 'ports_desc',
'required': True}]
_serialization_metadata = {
"application/xml": {
"attributes": {
"multiport": ["id", "name"],
},
},
}
def __init__(self, plugin):
self._resource_name = 'multiport'
self._plugin = plugin
# pylint: disable-msg=E1101,W0613
def create(self, request, tenant_id):
""" Creates a new multiport for a given tenant """
try:
req_params = \
self._parse_request_params(request,
self._multiport_ops_param_list)
except exc.HTTPError as exp:
return faults.Fault(exp)
multiports = self._plugin.\
create_multiport(tenant_id,
req_params['net_id_list'],
req_params['status'],
req_params['ports_desc'])
builder = port_view.get_view_builder(request)
result = [builder.build(port)['port']
for port in multiports]
return dict(ports=result)

View File

@ -40,8 +40,7 @@ If you plan to just leverage the plugin framework, you do not need these.)
* One or more UCS B200 series blade servers with M81KR VIC (aka
Palo adapters) installed.
* UCSM 2.0 (Capitola) Build 230 or above.
* OpenStack Cactus release installation (additional patch is required,
details follow in this document)
* OpenStack Diablo D3 or later (should have VIF-driver support)
* RHEL 6.1 (as of this writing, UCS only officially supports RHEL, but
it should be noted that Ubuntu support is planned in coming releases as well)
** Package: python-configobj-4.6.0-3.el6.noarch (or newer)
@ -84,11 +83,13 @@ Then run "yum install python-routes".
Module Structure:
-----------------
* quantum/plugins/cisco/ - Contains the L2-Network Plugin Framework
/client - CLI module for core and extensions API
/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
/nova - Scheduler and VIF-driver to be used by Nova
/nexus - Nexus-specific modules
/segmentation - Implementation of segmentation manager,
e.g. VLAN Manager
@ -109,20 +110,37 @@ provider = quantum.plugins.cisco.l2network_plugin.L2Network
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
--scheduler_driver=quantum.plugins.cisco.nova.quantum_port_aware_scheduler.QuantumPortAwareScheduler
--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
Note: To be able to bring up a VM on a UCS blade, you should first create a
port for that VM using the Quantum create port API. VM creation will
fail if an unused port is not available. If you have configured your
Nova project with more than one network, Nova will attempt to instantiate
the VM with one network interface (VIF) per configured network. To provide
plugin points for each of these VIFs, you will need to create multiple
Quantum ports, one for each of the networks, prior to starting the VM.
However, in this case you will need to use the Cisco multiport extension
API instead of the Quantum create port API. More details on using the
multiport extension follow in the section on multi NIC support.
4. To support the above configuration, you will need some Quantum modules. It's easiest
to copy the entire quantum directory from your quantum installation into:
/usr/lib/python2.6/site-packages/
This needs to be done for each nova compute node.
5. If you want to turn on support for Cisco Nexus switches:
5a. Uncomment the nexus_plugin property in
quantum/plugins/cisco/conf/plugins.ini to read:
nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin
4b. Enter the relevant configuration in the
5b. Enter the relevant configuration in the
quantum/plugins/cisco/conf/nexus.ini file. Example:
[SWITCH]
@ -141,7 +159,7 @@ nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
4c. Make sure that SSH host key of the Nexus switch is known to the
5c. Make sure that SSH host key of the Nexus switch is known to the
host on which you are running the Quantum service. You can do
this simply by logging in to your Quantum host as the user that
Quantum runs as and SSHing to the switch at least once. If the
@ -149,47 +167,45 @@ name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
clearing of the SSH config on the switch), you may need to repeat
this step and remove the old hostkey from ~/.ssh/known_hosts.
5. Plugin Persistence framework setup:
5a. Create quantum_l2network database in mysql with the following command -
6. Plugin Persistence framework setup:
6a. Create quantum_l2network database in mysql with the following command -
mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
5b. Enter the quantum_l2network database configuration info in the
6b. Enter the quantum_l2network database configuration info in the
quantum/plugins/cisco/conf/db_conn.ini file.
5c. If there is a change in the plugin configuration, service would need
6c. If there is a change in the plugin configuration, service would need
to be restarted after dropping and re-creating the database using
the following commands -
mysql -u<mysqlusername> -p<mysqlpassword> -e "drop database quantum_l2network"
mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
6. Verify that you have the correct credentials for each IP address listed
7. Verify that you have the correct credentials for each IP address listed
in quantum/plugins/cisco/conf/credentials.ini. Example:
# Provide the UCSM credentials
# Provide the UCSM credentials, create a separte entry for each UCSM used in your system
# UCSM IP address, username and password.
[10.0.0.2]
username=admin
password=mySecretPasswordForUCSM
# Provide the Nova DB credentials.
# The IP address should be the same as in nova.ini.
[10.0.0.3]
username=nova
password=mySecretPasswordForNova
# Provide the Nexus credentials, if you are using Nexus switches.
# If not this will be ignored.
[10.0.0.1]
username=admin
password=mySecretPasswordForNexus
7. Configure the UCS systems' information in your deployment by editing the
In general, make sure that every UCSM and Nexus switch used in your system,
has a credential entry in the above file. This is required for the system to
be able to communicate with those switches.
8. 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
UCSMs per deployment, multiple chassis 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.
typically be numbers like 1, 2, 3, etc.)
[ucsm-1]
ip_address = <put_ucsm_ip_address_here>
@ -217,11 +233,132 @@ blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
8. Start the Quantum service. If something doesn't work, verify that
9. 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.
Multi NIC support for VMs
-------------------------
As indicated earlier, if your Nova setup has a project with more than one network,
Nova will try to create a vritual network interface (VIF) on the VM for each of those
networks. That implies that,
(1) You should create the same number of networks in Quantum as in your Nova
project.
(2) Before each VM is instantiated, you should create Quantum ports on each of those
networks. These ports need to be created using the following rest call:
POST /v1.0/extensions/csco/tenants/{tenant_id}/multiport/
with request body:
{'multiport':
{'status': 'ACTIVE',
'net_id_list': net_id_list,
'ports_desc': {'key': 'value'}}}
where,
net_id_list is a list of network IDs: [netid1, netid2, ...]. The "ports_desc" dictionary
is reserved for later use. For now, the same structure in terms of the dictionary name, key
and value should be used.
The corresponding CLI for this operation is as follows:
PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_multiport <tenant_id> <net_id1,net_id2,...>
(Note that you should not be using the create port core API in the above case.)
Using the Command Line Client to work with this Plugin
------------------------------------------------------
A command line client is packaged with this plugin. This module can be used
to invoke the core API as well as the extensions API, so that you don't have
to switch between different CLI modules (it internally invokes the Quantum
CLI module for the core APIs to ensure consistency when using either). This
command line client can be invoked as follows:
PYTHONPATH=. python quantum/plugins/cisco/client/cli.py
1. Creating the network
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_net -H 10.10.2.6 demo net1
Created a new Virtual Network with ID: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant demo
2. Listing the networks
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py list_nets -H 10.10.2.6 demo
Virtual Networks for Tenant demo
Network ID: 0e85e924-6ef6-40c1-9f7a-3520ac6888b3
Network ID: c4a2bea7-a528-4caf-b16e-80397cd1663a
3. Creating one port on each of the networks
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_multiport -H 10.10.2.6 demo c4a2bea7-a528-4caf-b16e-80397cd1663a,0e85e924-6ef6-40c1-9f7a-3520ac6888b3
Created ports: {u'ports': [{u'id': u'118ac473-294d-480e-8f6d-425acbbe81ae'}, {u'id': u'996e84b8-2ed3-40cf-be75-de17ff1214c4'}]}
4. List all the ports on a network
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py list_ports -H 10.10.2.6 demo c4a2bea7-a528-4caf-b16e-80397cd1663a
Ports on Virtual Network: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant: demo
Logical Port: 118ac473-294d-480e-8f6d-425acbbe81ae
5. Show the details of a port
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py show_port -H 10.10.2.6 demo c4a2bea7-a528-4caf-b16e-80397cd1663a 118ac473-294d-480e-8f6d-425acbbe81ae
Logical Port ID: 118ac473-294d-480e-8f6d-425acbbe81ae
administrative State: ACTIVE
interface: <none>
on Virtual Network: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant: demo
6. Start the VM instance using Nova
Note that when using UCS and the 802.1Qbh features, the association of the
VIF-ID (also referred to as interface ID) on the VM's NIC with a port will
happen automatically when the VM is instantiated. At this point, doing a
show_port will reveal the VIF-ID associated with the port.
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py show_port demo c4a2bea7-a528-4caf-b16e-80397cd1663a 118ac473-294d-480e-8f6d-425acbbe81ae
Logical Port ID: 118ac473-294d-480e-8f6d-425acbbe81ae
administrative State: ACTIVE
interface: b73e3585-d074-4379-8dde-931c0fc4db0e
on Virtual Network: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant: demo
7. Plug interface and port into the network
Use the interface information obtained in step 6 to plug the interface into
the network.
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py plug_iface demo c4a2bea7-a528-4caf-b16e-80397cd1663a 118ac473-294d-480e-8f6d-425acbbe81ae b73e3585-d074-4379-8dde-931c0fc4db0e
Plugged interface b73e3585-d074-4379-8dde-931c0fc4db0e
into Logical Port: 118ac473-294d-480e-8f6d-425acbbe81ae
on Virtual Network: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant: demo
8. Unplug an interface and port from the network
Note: Before unplugging, make a note of the interface ID (you can use the
show_port CLI as before). While the VM, which has a VIF with this interface
ID still exists, you can only plug that same interface back into this port.
So the subsequent plug interface operation on this port will have to make
use of the same interface ID.
# PYTHONPATH=. python quantum/plugins/cisco/client/cli.py unplug_iface demo c4a2bea7-a528-4caf-b16e-80397cd1663a 118ac473-294d-480e-8f6d-425acbbe81ae
Unplugged interface from Logical Port: 118ac473-294d-480e-8f6d-425acbbe81ae
on Virtual Network: c4a2bea7-a528-4caf-b16e-80397cd1663a
for Tenant: demo
How to test the installation
----------------------------
The unit tests are located at quantum/plugins/cisco/tests/unit. They can be

View File

@ -0,0 +1,225 @@
"""
# 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.
#
# Initial structure and framework of this CLI has been borrowed from Quantum,
# written by the following authors
# @author: Somik Behera, Nicira Networks, Inc.
# @author: Brad Hall, Nicira Networks, Inc.
# @author: Salvatore Orlando, Citrix
#
# Cisco adaptation for extensions
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Ying Liu, Cisco Systems, Inc.
#
"""
import gettext
import logging
import logging.handlers
import os
import sys
import subprocess
from optparse import OptionParser
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'quantum', '__init__.py')):
sys.path.insert(0, POSSIBLE_TOPDIR)
gettext.install('quantum', unicode=1)
from quantum.client import Client
from quantum.plugins.cisco.common import cisco_constants as const
LOG = logging.getLogger('quantum')
FORMAT = 'json'
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'
def help():
"""Help for CLI"""
print "\nCisco Extension Commands:"
for key in COMMANDS.keys():
print " %s %s" % (key,
" ".join(["<%s>" % y for y in COMMANDS[key]["args"]]))
def build_args(cmd, cmdargs, arglist):
"""Building the list of args for a particular CLI"""
args = []
orig_arglist = arglist[:]
try:
for cmdarg in cmdargs:
args.append(arglist[0])
del arglist[0]
except:
LOG.error("Not enough arguments for \"%s\" (expected: %d, got: %d)" % (
cmd, len(cmdargs), len(orig_arglist)))
print "Usage:\n %s %s" % (cmd,
" ".join(["<%s>" % y for y in COMMANDS[cmd]["args"]]))
sys.exit()
if len(arglist) > 0:
LOG.error("Too many arguments for \"%s\" (expected: %d, got: %d)" % (
cmd, len(cmdargs), len(orig_arglist)))
print "Usage:\n %s %s" % (cmd,
" ".join(["<%s>" % y for y in COMMANDS[cmd]["args"]]))
sys.exit()
return args
def list_extensions(*args):
"""Invoking the action to get the supported extensions"""
request_url = "/extensions"
client = Client(HOST, PORT, USE_SSL, format='json',
action_prefix=ACTION_PREFIX_EXT, tenant="dummy")
data = client.do_request('GET', request_url)
print("Obtained supported extensions from Quantum: %s" % data)
def schedule_host(tenant_id, instance_id, user_id=None):
"""Gets the host name from the Quantum service"""
project_id = tenant_id
instance_data_dict = \
{'novatenant': \
{'instance_id': instance_id,
'instance_desc': \
{'user_id': user_id,
'project_id': project_id}}}
request_url = "/novatenants/" + project_id + "/schedule_host"
client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
action_prefix=ACTION_PREFIX_CSCO)
data = client.do_request('PUT', request_url, body=instance_data_dict)
hostname = data["host_list"]["host_1"]
if not hostname:
print("Scheduler was unable to locate a host" + \
" for this request. Is the appropriate" + \
" service running?")
print("Quantum service returned host: %s" % hostname)
def create_multiport(tenant_id, net_id_list, *args):
"""Creates ports on a single host"""
net_list = net_id_list.split(",")
ports_info = {'multiport': \
{'status': 'ACTIVE',
'net_id_list': net_list,
'ports_desc': {'key': 'value'}}}
request_url = "/multiport"
client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id,
action_prefix=ACTION_PREFIX_CSCO)
data = client.do_request('POST', request_url, body=ports_info)
print("Created ports: %s" % data)
COMMANDS = {
"create_multiport": {
"func": create_multiport,
"args": ["tenant-id",
"net-id-list (comma separated list of netword IDs)"]},
"list_extensions": {
"func": list_extensions,
"args": []},
"schedule_host": {
"func": schedule_host,
"args": ["tenant-id", "instance-id"]}, }
class _DynamicModule(object):
"""Loading a string as python module"""
def load(self, code):
execdict = {}
exec code in execdict
for key in execdict:
if not key.startswith('_'):
setattr(self, key, execdict[key])
import sys as _sys
_ref, _sys.modules[__name__] = _sys.modules[__name__], _DynamicModule()
if __name__ == "__main__":
import cli
FILE_NAME = os.path.join("bin/", "cli")
MODULE_CODE = open(FILE_NAME).read()
cli.load(MODULE_CODE)
usagestr = "Usage: %prog [OPTIONS] <command> [args]"
PARSER = OptionParser(usage=usagestr)
PARSER.add_option("-H", "--host", dest="host",
type="string", default="127.0.0.1", help="ip address of api host")
PARSER.add_option("-p", "--port", dest="port",
type="int", default=9696, help="api poort")
PARSER.add_option("-s", "--ssl", dest="ssl",
action="store_true", default=False, help="use ssl")
PARSER.add_option("-v", "--verbose", dest="verbose",
action="store_true", default=False, help="turn on verbose logging")
PARSER.add_option("-f", "--logfile", dest="logfile",
type="string", default="syslog", help="log file path")
options, args = PARSER.parse_args()
if options.verbose:
LOG.setLevel(logging.DEBUG)
else:
LOG.setLevel(logging.WARN)
if options.logfile == "syslog":
LOG.addHandler(logging.handlers.SysLogHandler(address='/dev/log'))
else:
LOG.addHandler(logging.handlers.WatchedFileHandler(options.logfile))
os.chmod(options.logfile, 0644)
if len(args) < 1:
PARSER.print_help()
cli.help()
help()
sys.exit(1)
CMD = args[0]
if CMD in cli.commands.keys():
args.insert(0, FILE_NAME)
subprocess.call(args)
sys.exit(1)
if CMD not in COMMANDS.keys():
LOG.error("Unknown command: %s" % CMD)
cli.help()
help()
sys.exit(1)
args = build_args(CMD, COMMANDS[CMD]["args"], args[1:])
LOG.info("Executing command \"%s\" with args: %s" % (CMD, args))
HOST = options.host
PORT = options.port
USE_SSL = options.ssl
COMMANDS[CMD]["func"](*args)
LOG.info("Command execution completed")
sys.exit(0)

View File

@ -149,3 +149,7 @@ LEAST_RSVD_BLADE_DICT = 'least_rsvd_blade_dict'
UCSM_IP = 'ucsm_ip_address'
NETWORK_ADMIN = 'network_admin'
NETID_LIST = 'net_id_list'
DELIMITERS = "[,;:\b\s]"

View File

@ -55,6 +55,12 @@ class PortProfileNotFound(exceptions.QuantumException):
"for tenant %(tenant_id)s")
class MultiportNotFound(exceptions.QuantumException):
"""Multiport cannot be found"""
message = _("Multiports %(port_id)s could not be found " \
"for tenant %(tenant_id)s")
class PortProfileInvalidDelete(exceptions.QuantumException):
"""Port profile cannot be deleted since its being used"""
message = _("Port profile %(profile_id)s could not be deleted " \

View File

@ -34,6 +34,7 @@ class Fault(webob.exc.HTTPException):
451: "CredentialNotFound",
452: "QoSNotFound",
453: "NovatenantNotFound",
454: "MultiportNotFound",
470: "serviceUnavailable",
471: "pluginFault"}
@ -96,7 +97,7 @@ class CredentialNotFound(webob.exc.HTTPClientError):
This indicates that the server did not find the Credential specified
in the HTTP request
code: 460, title: Credential not Found
code: 451, title: Credential not Found
"""
code = 451
title = 'Credential Not Found'
@ -111,7 +112,7 @@ class QosNotFound(webob.exc.HTTPClientError):
This indicates that the server did not find the QoS specified
in the HTTP request
code: 480, title: QoS not Found
code: 452, title: QoS not Found
"""
code = 452
title = 'QoS Not Found'
@ -126,7 +127,7 @@ class NovatenantNotFound(webob.exc.HTTPClientError):
This indicates that the server did not find the Novatenant specified
in the HTTP request
code: 480, title: Nova tenant not Found
code: 453, title: Nova tenant not Found
"""
code = 453
title = 'Nova tenant Not Found'
@ -134,6 +135,21 @@ class NovatenantNotFound(webob.exc.HTTPClientError):
+ ' the specified identifier.')
class MultiportNotFound(webob.exc.HTTPClientError):
"""
subclass of :class:`~HTTPClientError`
This indicates that the server did not find the Multiport specified
in the HTTP request
code: 454, title: Multiport not Found
"""
code = 454
title = 'Multiport Not Found'
explanation = ('Unable to find Multiport with'
+ ' the specified identifier.')
class RequestedStateInvalid(webob.exc.HTTPClientError):
"""
subclass of :class:`~HTTPClientError`

View File

@ -1,37 +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 os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "../conf/nova.ini"
CP = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
SECTION = CP['NOVA']
DB_SERVER_IP = SECTION['db_server_ip']
DB_NAME = SECTION['db_name']
DB_USERNAME = SECTION['db_username']
DB_PASSWORD = SECTION['db_password']
NOVA_HOST_NAME = SECTION['nova_host_name']
NOVA_PROJ_NAME = SECTION['nova_proj_name']

View File

@ -25,7 +25,6 @@ import MySQLdb
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
@ -77,35 +76,3 @@ def make_portprofile_assc_list(tenant_id, profile_id):
assc_list.append(port[const.PORTID])
return assc_list
class DBUtils(object):
"""Utilities to use connect to MySQL DB and execute queries"""
def __init__(self):
pass
def _get_db_connection(self):
"""Get a connection to the DB"""
db_ip = conf.DB_SERVER_IP
db_username = conf.DB_USERNAME
db_password = conf.DB_PASSWORD
self.db = MySQLdb.connect(db_ip, db_username, db_password,
conf.DB_NAME)
return self.db
def execute_db_query(self, sql_query):
"""Execute a DB query"""
db = self._get_db_connection()
cursor = db.cursor()
try:
cursor.execute(sql_query)
results = cursor.fetchall()
db.commit()
LOG.debug("DB query execution succeeded: %s" % sql_query)
db.close()
except:
db.rollback()
LOG.debug("DB query execution failed: %s" % sql_query)
traceback.print_exc()
db.close()

View File

@ -1,13 +1,8 @@
#Provide the UCSM credentials
#Provide the UCSM credentials, make sure you have a separate entry for every UCSM in your deployment
[<put_ucsm_ip_address_here>]
username=<put_user_name_here>
password=<put_password_here>
#Provide the Nova DB credentials, the IP address should be the same as in nova.ini
[<put_nova_db_ip_here>]
username=<put_user_name_here>
password=<put_password_here>
#Provide the Nexus credentials, if you are using Nexus
[<put_nexus_ip_address_here>]
username=<put_user_name_here>

View File

@ -1,8 +0,0 @@
[NOVA]
#Change the following details to reflect your OpenStack Nova configuration. If you are running this service on the same machine as the Nova DB, you do not have to change the IP address.
db_server_ip=127.0.0.1
db_name=nova
db_username=<put_db_user_name_here>
db_password=<put_db_password_here>
nova_host_name=<put_openstack_cloud_controller_hostname_here>
nova_proj_name=<put_openstack_project_name_here>

View File

@ -1,5 +1,6 @@
[UCSM]
#change the following to the appropriate UCSM IP address
#if you have more than one UCSM, enter info from any one
ip_address=<put_ucsm_ip_address_here>
default_vlan_name=default
default_vlan_id=1

View File

@ -21,7 +21,7 @@
import inspect
import logging as LOG
import platform
import re
from quantum.common import exceptions as exc
from quantum.common import utils
@ -41,8 +41,9 @@ LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2Network(QuantumPluginBase):
""" L2 Network Framework Plugin """
supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
"Cisco qos", "Cisco Nova Tenant"]
supported_extension_aliases = ["Cisco Multiport", "Cisco Credential",
"Cisco Port Profile", "Cisco qos",
"Cisco Nova Tenant"]
def __init__(self):
cdb.initialize()
@ -178,6 +179,7 @@ class L2Network(QuantumPluginBase):
Creates a port on the specified Virtual Network.
"""
LOG.debug("create_port() called\n")
port = db.port_create(net_id, port_state)
unique_port_id_string = port[const.UUID]
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
@ -249,10 +251,19 @@ class L2Network(QuantumPluginBase):
"""
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])
db.port_set_attachment(net_id, port_id, remote_interface_id)
port = db.port_get(net_id, port_id)
attachment_id = port[const.INTERFACEID]
if attachment_id and remote_interface_id != attachment_id:
raise exc.PortInUse(port_id=port_id, net_id=net_id,
att_id=attachment_id)
self._invoke_device_plugins(self._func_name(), [tenant_id,
net_id, port_id,
remote_interface_id])
if attachment_id == None:
db.port_set_attachment(net_id, port_id, remote_interface_id)
#Note: The remote_interface_id gets associated with the port
# when the VM is instantiated. The plug interface call results
# in putting the port on the VLAN associated with this network
def unplug_interface(self, tenant_id, net_id, port_id):
"""
@ -287,7 +298,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("get_portprofile_details() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception, excp:
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
@ -313,7 +324,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("delete_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception, excp:
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
@ -329,7 +340,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("rename_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception, excp:
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
@ -345,7 +356,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("associate_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception, excp:
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
@ -357,7 +368,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("disassociate_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception, excp:
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
@ -374,7 +385,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("get_qos_details() called\n")
try:
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
return qos_level
@ -390,7 +401,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("delete_qos() called\n")
try:
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
return cdb.remove_qos(tenant_id, qos_id)
@ -400,7 +411,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("rename_qos() called\n")
try:
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception, excp:
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_id,
qos_id=qos_id)
qos = cdb.update_qos(tenant_id, qos_id, new_name)
@ -417,7 +428,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("get_credential_details() called\n")
try:
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
return credential
@ -435,7 +446,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("delete_credential() called\n")
try:
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
credential = cdb.remove_credential(tenant_id, credential_id)
@ -446,7 +457,7 @@ class L2Network(QuantumPluginBase):
LOG.debug("rename_credential() called\n")
try:
credential = cdb.get_credential(tenant_id, credential_id)
except Exception, excp:
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
credential = cdb.update_credential(tenant_id, credential_id, new_name)
@ -469,6 +480,27 @@ class L2Network(QuantumPluginBase):
instance_id,
instance_desc])
def create_multiport(self, tenant_id, net_id_list, port_state, ports_desc):
"""
Creates multiple ports on the specified Virtual Network.
"""
LOG.debug("create_ports() called\n")
ports_num = len(net_id_list)
ports_id_list = []
ports_dict_list = []
for net_id in net_id_list:
port = db.port_create(net_id, port_state)
ports_id_list.append(port[const.UUID])
port_dict = {const.PORT_ID: port[const.UUID]}
ports_dict_list.append(port_dict)
self._invoke_device_plugins(self._func_name(), [tenant_id,
net_id_list,
ports_num,
ports_id_list])
return ports_dict_list
"""
Private functions
"""

View File

@ -171,3 +171,8 @@ class L2NetworkMultiBlade(L2NetworkModelBase):
LOG.debug("associate_port() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
def create_multiport(self, args):
"""Support for extension API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)

View File

@ -162,3 +162,8 @@ class L2NetworkSingleBlade(L2NetworkModelBase):
LOG.debug("associate_port() called\n")
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
def create_multiport(self, args):
"""Support for extension API call"""
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)

View File

@ -43,7 +43,7 @@ CSCO_EXT_NAME = 'Cisco Nova Tenant'
ACTION = '/schedule_host'
class QuantumScheduler(driver.Scheduler):
class QuantumPortAwareScheduler(driver.Scheduler):
"""
Quantum network service dependent scheduler
Obtains the hostname from Quantum using an extension API

View File

@ -28,6 +28,7 @@ from extensions import credential
from extensions import portprofile
from extensions import novatenant
from extensions import qos
from extensions import multiport
from quantum.plugins.cisco.db import api as db
from quantum.common import wsgi
from quantum.common import config
@ -1022,6 +1023,122 @@ class CredentialExtensionTest(unittest.TestCase):
db.clear_db()
class MultiPortExtensionTest(unittest.TestCase):
def setUp(self):
""" Set up function """
parent_resource = dict(member_name="tenant",
collection_name="extensions/csco/tenants")
controller = multiport.MultiportController(
QuantumManager.get_plugin())
res_ext = extensions.ResourceExtension('multiport', controller,
parent=parent_resource)
self.test_app = setup_extensions_test_app(
SimpleExtensionManager(res_ext))
self.contenttype = 'application/json'
self.multiport_path = '/extensions/csco/tenants/tt/multiport'
self.multiport_path2 = '/extensions/csco/tenants/tt/multiport/'
self.test_multi_port = {'multiport':
{'net_id_list': '1',
'status': 'test-qos1',
'ports_desc': 'Port Descr'}}
self.tenant_id = "test_tenant"
self.network_name = "test_network"
options = {}
options['plugin_provider'] = 'quantum.plugins.cisco.l2network_plugin'\
'.L2Network'
self.api = server.APIRouterV1(options)
self._l2network_plugin = l2network_plugin.L2Network()
def create_request(self, path, body, content_type, method='GET'):
""" Test create request"""
LOG.debug("test_create_request - START")
req = webob.Request.blank(path)
req.method = method
req.headers = {}
req.headers['Accept'] = content_type
req.body = body
LOG.debug("test_create_request - END")
return req
def _create_network(self, name=None):
""" Test create network"""
LOG.debug("Creating network - START")
if name:
net_name = name
else:
net_name = self.network_name
net_path = "/tenants/tt/networks"
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)
LOG.debug("Creating network - END")
return network_data['network']['id']
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_create_multiport(self):
""" Test create MultiPort"""
LOG.debug("test_create_multiport - START")
net_id = self._create_network('net1')
net_id2 = self._create_network('net2')
test_multi_port = {'multiport':
{'net_id_list': [net_id, net_id2],
'status': 'ACTIVE',
'ports_desc': {'key': 'value'}}}
req_body = json.dumps(test_multi_port)
index_response = self.test_app.post(self.multiport_path, req_body,
content_type=self.contenttype)
resp_body = wsgi.Serializer().deserialize(index_response.body,
self.contenttype)
self.assertEqual(200, index_response.status_int)
self.assertEqual(len(test_multi_port['multiport']['net_id_list']),
len(resp_body['ports']))
# Clean Up - Delete the Port Profile
self._delete_network(net_id)
self._delete_network(net_id2)
LOG.debug("test_create_multiport - END")
def test_create_multiportBADRequest(self):
""" Test create MultiPort Bad Request"""
LOG.debug("test_create_multiportBADRequest - START")
net_id = self._create_network('net1')
net_id2 = self._create_network('net2')
index_response = self.test_app.post(self.multiport_path, 'BAD_REQUEST',
content_type=self.contenttype,
status='*')
self.assertEqual(400, index_response.status_int)
# Clean Up - Delete the Port Profile
self._delete_network(net_id)
self._delete_network(net_id2)
LOG.debug("test_create_multiportBADRequest - END")
def tearDown(self):
db.clear_db()
def app_factory(global_conf, **local_conf):
conf = global_conf.copy()
conf.update(local_conf)

View File

@ -506,13 +506,14 @@ class CoreAPITestFunc(unittest.TestCase):
"""
LOG.debug("test_plug_interface_portInUse - START")
current_interface = "current_interface"
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], remote_interface)
port_dict[const.PORT_ID], current_interface)
self.assertRaises(exc.PortInUse,
self._l2network_plugin.plug_interface, tenant_id,
new_net_dict[const.NET_ID],

View File

@ -121,7 +121,30 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
chassis_dict[chassis_id] = blade_list
self._inventory[ucsm_ip] = chassis_dict
self.build_inventory_state()
self._build_inventory_state()
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_host_name(self, ucsm_ip, chassis_id, blade_id):
"""Get the hostname based on the blade info"""
@ -187,8 +210,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
blade_intf_data[blade_intf][const.VIF_ID] = \
port_binding[const.VIF_ID]
host_name = self._get_host_name(ucsm_ip, chassis_id, blade_id)
blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter}
const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter,
const.HOST_NAME: host_name}
return blade_data
def _get_blade_state(self, chassis_id, blade_id, ucsm_ip,
@ -229,7 +254,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
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)
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]]}
@ -361,39 +386,46 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
(vif_id, port_id,
blade_intf_data[blade_intf]))
return
LOG.warn("Disassociating VIF-ID %s in UCS inventory failed. " \
LOG.warn("Disassociating VIF-ID in UCS inventory failed. " \
"Could not find a reserved dynamic nic for tenant: %s" %
(vif_id, tenant_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]
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():
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
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
LOG.debug("UCS Inventory state is: %s\n" % self._inventory_state)
return True
def get_least_reserved_blade(self):
def _get_least_reserved_blade(self, intf_count=1):
"""Return the blade with least number of dynamic nics reserved"""
unreserved_interface_count = 0
least_reserved_blade_ucsm = None
@ -415,8 +447,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
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")
if unreserved_interface_count < intf_count:
LOG.warn("Not enough dynamic nics available on a single host." \
" Requested: %s, Maximum available: %s" %
(intf_count, unreserved_interface_count))
return False
least_reserved_blade_dict = \
@ -428,6 +462,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
least_reserved_blade_dict)
return least_reserved_blade_dict
def reload_inventory(self):
"""Reload the inventory from a conf file"""
self._load_inventory()
def reserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
blade_data_dict, tenant_id, port_id,
portprofile_name):
@ -435,15 +473,19 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
ucsm_username = cred.Store.getUsername(ucsm_ip)
ucsm_password = cred.Store.getPassword(ucsm_ip)
"""
We are first getting the updated blade interface state
We are first getting the updated UCSM-specific 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]
old_blade_intf_data = \
self._inventory_state[ucsm_ip][chassis_id]\
[blade_id][const.BLADE_INTF_DATA]
"""
We will now copy the older blade interface state
We will now copy the older non-UCSM-specific blade
interface state
"""
for blade_intf in blade_intf_data.keys():
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
@ -461,7 +503,8 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
old_blade_intf_data[blade_intf][const.VIF_ID]
blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = \
blade_data_dict[const.BLADE_UNRESERVED_INTF_COUNT]
self._inventory_state[ucsm_ip][chassis_id]\
[blade_id][const.BLADE_UNRESERVED_INTF_COUNT]
"""
Now we will reserve an interface if its available
"""
@ -498,7 +541,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
return reserved_nic_dict
LOG.warn("Dynamic nic %s could not be reserved for port-id: %s" %
(blade_data_dict, port_id))
(blade_data, port_id))
return False
def unreserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
@ -518,40 +561,6 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
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)
@ -593,7 +602,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
on which a dynamic vnic is available
"""
LOG.debug("create_port() called\n")
least_reserved_blade_dict = self.get_least_reserved_blade()
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]
@ -612,9 +621,11 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
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)
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)
LOG.warn("UCSInventory: Port not found: net_id: %s, port_id: %s" %
(net_id, port_id))
return {const.DEVICE_IP: []}
device_params = \
{const.DEVICE_IP: [rsvd_info[const.UCSM_IP]],
const.UCS_INVENTORY: self,
@ -680,3 +691,20 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
vif_desc = {const.VIF_DESC: vif_info}
LOG.debug("vif_desc is: %s" % vif_desc)
return vif_desc
def create_multiport(self, args):
"""
Create multiple ports for a VM
"""
LOG.debug("create_ports() called\n")
tenant_id = args[0]
ports_num = args[2]
least_reserved_blade_dict = self._get_least_reserved_blade(ports_num)
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

View File

@ -145,7 +145,7 @@ class CiscoUCSMDriver():
def _post_data(self, ucsm_ip, ucsm_username, ucsm_password, data):
"""Send command to UCSM in http request"""
conn = httplib.HTTPConnection(ucsm_ip)
conn = httplib.HTTPSConnection(ucsm_ip)
login_data = "<aaaLogin inName=\"" + ucsm_username + \
"\" inPassword=\"" + ucsm_password + "\" />"
conn.request(METHOD, URL, login_data, HEADERS)

View File

@ -296,3 +296,36 @@ class UCSVICPlugin(L2DevicePluginBase):
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)
def create_multiport(self, tenant_id, net_id_list, ports_num, port_id_list,
**kwargs):
"""
Creates a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:create_multiport() called\n")
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]
port_binding_list = []
for port_id, net_id in zip(port_id_list, net_id_list):
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]
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)
port_binding_list.append(port_binding)
return port_binding_list