Add v2 API support for the Cisco plugin

Blueprint cisco-plugin-v2-api-support

New meta-plugin which makes use of the Quantum db_plugin and supports
the new v2 API
Changes to the Cisco DB model, now reusing quantum DB for core attributes
Changes to the device sub plugins to access the quantum DB for core
resources' state versus Cisco DB
Addition of fake/dummy drivers to support testing of the device sub
plugins even without actual hardware
New v2 unit tests which exercise the meta-plugin and the device sub
-plugins as well
In general creating new v2 modules such that v1.x code can be deprecated
easily by deleting the older modules. The following files are v2
versions of older modules, only the imports have changed, most of the
other code is the same as from the older modules (already reviewed):
quantum/plugins/cisco/common/cisco_credentials_v2.py
quantum/plugins/cisco/db/network_db_v2.py
quantum/plugins/cisco/db/network_models_v2.py
quantum/plugins/cisco/db/nexus_db_v2.py
quantum/plugins/cisco/db/nexus_models_v2.py
quantum/plugins/cisco/db/ucs_db_v2.py
quantum/plugins/cisco/db/ucs_models_v2.py
quantum/plugins/cisco/nexus/cisco_nexus_plugin_v2.py
quantum/plugins/cisco/ucs/cisco_ucs_inventory_v2.py
quantum/plugins/cisco/ucs/cisco_ucs_plugin_v2.py
quantum/plugins/cisco/segmentation/l2network_vlan_mgr_v2.py

All changes are contained with the Cisco plugin.

(Sumit & Rohit)

Change-Id: Ib82a9f843548c286c84ba63caf5406a773ac85b1
This commit is contained in:
Sumit Naiksatam 2012-07-18 13:30:21 -07:00
parent 73ec08b7fc
commit 9cc6decb7a
32 changed files with 4236 additions and 22 deletions

View File

@ -1,7 +1,8 @@
[PLUGINS]
#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin
#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
[INVENTORY]
#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory.UCSInventory
#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
#ucs_plugin=quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake.UCSInventory
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_inventory.NexusInventory

View File

@ -4,7 +4,7 @@ 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>
password=<put_password_here>
[1.1.1.1]
username=abc
password=def

View File

@ -13,7 +13,8 @@ max_port_profiles=65568
max_networks=65568
[MODEL]
model_class=quantum.plugins.cisco.models.l2network_multi_blade.L2NetworkMultiBlade
#model_class=quantum.plugins.cisco.models.l2network_multi_blade.L2NetworkMultiBlade
model_class=quantum.plugins.cisco.models.network_multi_blade_v2.NetworkMultiBladeV2
[SEGMENTATION]
manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr.L2NetworkVLANMgr
manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr

View File

@ -8,4 +8,5 @@ nexus_second_port=<put_interface_name_here>
nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
#name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
name=quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver

View File

@ -8,4 +8,5 @@ max_ucsm_port_profiles=1024
profile_name_prefix=q-
[DRIVER]
name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
#name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
name=quantum.plugins.cisco.tests.unit.v2.ucs.fake_ucs_driver.CiscoUCSMFakeDriver

View File

@ -1,5 +1,328 @@
=========================================================================================
README: A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
README for Quantum v2.0:
A Plugin Framework for Supporting Quantum Networks Spannning Multiple Switches
=========================================================================================
Introduction
------------
This plugin implementation provides the following capabilities:
* A reference implementation for a Quantum Plugin Framework
(For details see: http://wiki.openstack.org/quantum-multi-switch-plugin)
* Supports multiple switches in the network
* Supports multiple models of switches concurrently
* Supports use of multiple L2 technologies
* Supports the Cisco Nexus family of switches.
* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards
(aka "Palo adapters") via 802.1Qbh.
Pre-requisites
--------------
(The following are necessary only when using the UCS and/or Nexus devices in your system.
If you plan to just leverage the plugin framework, you do not need these.)
If you are using a Nexus switch in your topology, you'll need the following
NX-OS version and packages to enable Nexus support:
* NX-OS 5.2.1 (Delhi) Build 69 or above.
* paramiko library - SSHv2 protocol library for python
* ncclient v0.3.1 - Python library for NETCONF clients
** You need a version of ncclient modifed by Cisco Systems.
To get it, from your shell prompt do:
git clone git@github.com:CiscoSystems/ncclient.git
sudo python ./setup.py install
** For more information of ncclient, see:
http://schmizz.net/ncclient/
* One or more UCS B200 series blade servers with M81KR VIC (aka
Palo adapters) installed.
* UCSM 2.0 (Capitola) Build 230 or above.
* OS supported:
** RHEL 6.1 or above
** Ubuntu 11.10 or above
** Package: python-configobj-4.6.0-3.el6.noarch (or newer)
** Package: python-routes-1.12.3-2.el6.noarch (or newer)
** Package: pip install mysql-python
Module Structure:
-----------------
* quantum/plugins/cisco/ - Contains the 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
/services - Set of orchestration libraries to insert
In-path Networking Services
/tests - Tests specific to this plugin
/ucs - UCS-specific modules
Plugin Installation Instructions
----------------------------------
1. Make a backup copy of quantum/etc/quantum.conf
2. Edit quantum/etc/quantum.conf and edit the "core_plugin" for v2 API
core_plugin = quantum.plugins.cisco.network_plugin.PluginV2
3. MySQL database setup:
3a. Create quantum_l2network database in mysql with the following command -
mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
3b. Enter the quantum_l2network database configuration info in the
quantum/plugins/cisco/conf/db_conn.ini file.
4. If you want to turn on support for Cisco Nexus switches:
4a. Uncomment the nexus_plugin property in
etc/quantum/plugins/cisco/cisco_plugins.ini to read:
[PLUGINS]
nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
4b. Enter the relevant configuration in the
etc/quantum/plugins/cisco/nexus.ini file. Example:
[SWITCH]
# Change the following to reflect the IP address of the Nexus switch.
# This will be the address at which Quantum sends and receives configuration
# information via SSHv2.
nexus_ip_address=10.0.0.1
# Port numbers on the Nexus switch to each one of the UCSM 6120s is connected
# Use shortened interface syntax, e.g. "1/10" not "Ethernet1/10".
nexus_first_port=1/10
nexus_second_port=1/11
#Port number where SSH will be running on the Nexus switch. Typically this is 22
#unless you've configured your switch otherwise.
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
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
host key changes (e.g. due to replacement of the supervisor or
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. If your are using UCS blade servers with M81KR Virtual Interface Cards and
want to leverage the VM-FEX features,
5a. Uncomment the ucs_plugin propertes in
etc/quantum/plugins/cisco/cisco_plugins.ini to read:
[PLUGINS]
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
[INVENTORY]
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
5b. Enter the relevant configuration in the
etc/quantum/plugins/cisco/ucs.ini file. Example:
[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
max_ucsm_port_profiles=1024
profile_name_prefix=q-
[DRIVER]
name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
5c. 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 chassis per UCSM, and multiple blades per
chassis. Chassis ID and blade ID can be obtained from the UCSM (they will
typically be numbers like 1, 2, 3, etc.). Also make sure that you put the exact
hostname as nova sees it (the host column in the services table of the nova
DB will give you that information).
[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>
5d. 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_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
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.
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.7/site-packages/
This needs to be done on each nova compute node.
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, 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 Nexus credentials, if you are using Nexus switches.
# If not this will be ignored.
[10.0.0.1]
username=admin
password=mySecretPasswordForNexus
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.
9. Start the Quantum service. If something doesn't work, verify the
your configuration of each of the above files.
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 virtual network interface (VIF) on the VM for each of those
networks. 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 /1.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.)
How to test the installation
----------------------------
The unit tests are located at quantum/plugins/cisco/tests/unit/v2. They can be
executed from the top level Quantum directory using the run_tests.sh script.
1. Testing the core API (without UCS/Nexus/RHEL device sub-plugins configured):
By default all the device sub-plugins are disabled (commented out) in
etc/quantum/plugins/cisco/cisco_plugins.ini
./run_tests.sh quantum.plugins.cisco.tests.unit.v2.test_api_v2
./run_tests.sh quantum.plugins.cisco.tests.unit.v2.test_network_plugin
2. For testing the Nexus device sub-plugin perform the following configuration:
Edit etc/quantum/plugins/cisco/cisco_plugins.ini to add:
In the [PLUGINS] section add:
nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
Edit the etc/quantum/plugins/cisco/nexus.ini file.
When not using Nexus hardware use the following dummy configuration verbatim:
[SWITCH]
nexus_ip_address=1.1.1.1
nexus_first_port=1/10
nexus_second_port=1/11
nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver
Or when using Nexus hardware (put the values relevant to your setup):
[SWITCH]
nexus_ip_address=1.1.1.1
nexus_first_port=1/10
nexus_second_port=1/11
nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
(Note: Make sure that quantum/plugins/cisco/conf/credentials.ini has an entry for
the nexus_ip_address being used in the above cases)
3. For testing the UCS device sub-plugin perform the following configuration:
Edit etc/quantum/plugins/cisco/cisco_plugins.ini to add:
In the [PLUGINS] section add:
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
In the [INVENTORY] section add:
When not using UCS hardware:
ucs_plugin=quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake.UCSInventory
Or when using UCS hardware:
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
Edit the etc/quantum/plugins/cisco/ucs.ini file.
When not using UCS hardware:
[DRIVER]
name=quantum.plugins.cisco.tests.unit.v2.ucs.fake_ucs_driver.CiscoUCSMFakeDriver
Or when using UCS hardware:
[DRIVER]
name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
:Web site: http://wiki.openstack.org/cisco-quantum
:Copyright: 2012 Cisco Systems, Inc.
:Contact: netstack@lists.launchpad.net
=========================================================================================
README for Quantum v1 and v1.1:
A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
=========================================================================================
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri,
@ -83,12 +406,15 @@ Module Structure:
Plugin Installation Instructions
----------------------------------
1. Make a backup copy of quantum/etc/plugins.ini.
1. Make a backup copy of quantum/etc/quantum.conf
2. Edit quantum/etc/plugins.ini and edit the "provider" entry to point
to the L2Network-plugin:
2. Edit quantum/etc/quantum.conf and edit the "core_plugin" for v2 API
provider = quantum.plugins.cisco.l2network_plugin.L2Network
core_plugin = quantum.plugins.cisco.network_plugin.PluginV2
OR for v1.1 API
core_plugin = quantum.plugins.cisco.l2network_plugin.L2Network
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
@ -374,7 +700,9 @@ result the run_tests.py script.
Device-specific sub-plugins can be disabled by commenting out all the entries in:
etc/quantum/plugins/cisco/cisco_plugins.ini
Execute the l2networkApi tests only using:
Execute the v2 API tests only using:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_api_v2
Execute the v1.1 API tests only using:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
If just the ucs or both ucs and the nexus plugins are configured then all the tests could be executed by
@ -389,12 +717,15 @@ result the run_tests.py script.
Nexus plugins.
Device-specific plugins can be disabled by commenting out the entries in:
etc/quantum/plugins/cisco/cisco_plugins.ini
Execute the test script as follows:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
Execute the v2 API tests only using:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_api_v2
or
python run_tests.py quantum.plugins.cisco.tests.unit.test_api_v2
Execute the v1.1 API tests only using:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
or
python run_tests.py quantum.plugins.cisco.tests.unit.test_l2networkApi
3. Specific Plugin unit test (needs environment setup as indicated in the

View File

@ -161,3 +161,9 @@ ASSOCIATION_STATUS = 'association_status'
ATTACHED = 'attached'
DETACHED = 'detached'
NETWORK = 'network'
PORT = 'port'
BASE_PLUGIN_REF = 'base_plugin_ref'
CONTEXT = 'context'
SUBNET = 'subnet'

View File

@ -0,0 +1,82 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
import logging as LOG
from quantum.common.utils import find_config_file
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 network_db_v2 as cdb
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'},
"credentials.ini")
TENANT = const.NETWORK_ADMIN
cp = confp.CiscoConfigParser(CREDENTIALS_FILE)
_creds_dictionary = cp.walk(cp.dummy)
class Store(object):
"""Credential Store"""
@staticmethod
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 put_credential(cred_name, username, password):
"""Set the username and password"""
credential = cdb.add_credential(TENANT, cred_name, username, password)
@staticmethod
def get_username(cred_name):
"""Get the username"""
credential = cdb.get_credential_name(TENANT, cred_name)
return credential[const.CREDENTIAL_USERNAME]
@staticmethod
def get_password(cred_name):
"""Get the password"""
credential = cdb.get_credential_name(TENANT, cred_name)
return credential[const.CREDENTIAL_PASSWORD]
@staticmethod
def get_credential(cred_name):
"""Get the username and password"""
credential = cdb.get_credential_name(TENANT, cred_name)
return {const.USERNAME: const.CREDENTIAL_USERNAME,
const.PASSWORD: const.CREDENTIAL_PASSWORD}
@staticmethod
def delete_credential(cred_name):
"""Delete a credential"""
cdb.remove_credential(TENANT, cred_name)

View File

@ -0,0 +1,527 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012, 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
from quantum.common import exceptions as q_exc
from quantum.db import api as db
from quantum.db import models_v2
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import network_models_v2
from quantum.plugins.cisco.db import nexus_models_v2
from quantum.plugins.cisco.db import ucs_models_v2
from quantum.plugins.cisco import l2network_plugin_configuration as conf
def initialize():
'Establish database connection and load models'
sql_connection = "mysql://%s:%s@%s/%s" % (conf.DB_USER, conf.DB_PASS,
conf.DB_HOST, conf.DB_NAME)
db.configure_db({'sql_connection': sql_connection,
'base': network_models_v2.model_base.BASEV2})
def create_vlanids():
"""Prepopulates the vlan_bindings table"""
LOG.debug("create_vlanids() called")
session = db.get_session()
try:
vlanid = session.query(network_models_v2.VlanID).one()
except exc.MultipleResultsFound:
pass
except exc.NoResultFound:
start = int(conf.VLAN_START)
end = int(conf.VLAN_END)
while start <= end:
vlanid = network_models_v2.VlanID(start)
session.add(vlanid)
start += 1
session.flush()
return
def get_all_vlanids():
"""Gets all the vlanids"""
LOG.debug("get_all_vlanids() called")
session = db.get_session()
try:
vlanids = session.query(network_models_v2.VlanID).all()
return vlanids
except exc.NoResultFound:
return []
def is_vlanid_used(vlan_id):
"""Checks if a vlanid is in use"""
LOG.debug("is_vlanid_used() called")
session = db.get_session()
try:
vlanid = (session.query(network_models_v2.VlanID).
filter_by(vlan_id=vlan_id).one())
return vlanid["vlan_used"]
except exc.NoResultFound:
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
def release_vlanid(vlan_id):
"""Sets the vlanid state to be unused"""
LOG.debug("release_vlanid() called")
session = db.get_session()
try:
vlanid = (session.query(network_models_v2.VlanID).
filter_by(vlan_id=vlan_id).one())
vlanid["vlan_used"] = False
session.merge(vlanid)
session.flush()
return vlanid["vlan_used"]
except exc.NoResultFound:
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
return
def delete_vlanid(vlan_id):
"""Deletes a vlanid entry from db"""
LOG.debug("delete_vlanid() called")
session = db.get_session()
try:
vlanid = (session.query(network_models_v2.VlanID).
filter_by(vlan_id=vlan_id).one())
session.delete(vlanid)
session.flush()
return vlanid
except exc.NoResultFound:
pass
def reserve_vlanid():
"""Reserves the first unused vlanid"""
LOG.debug("reserve_vlanid() called")
session = db.get_session()
try:
rvlan = (session.query(network_models_v2.VlanID).
filter_by(vlan_used=False).first())
if not rvlan:
raise exc.NoResultFound
rvlanid = (session.query(network_models_v2.VlanID).
filter_by(vlan_id=rvlan["vlan_id"]).one())
rvlanid["vlan_used"] = True
session.merge(rvlanid)
session.flush()
return rvlan["vlan_id"]
except exc.NoResultFound:
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(network_models_v2.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")
session = db.get_session()
try:
bindings = session.query(network_models_v2.VlanBinding).all()
return bindings
except exc.NoResultFound:
return []
def get_vlan_binding(netid):
"""Lists the vlan given a network_id"""
LOG.debug("get_vlan_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.VlanBinding).
filter_by(network_id=netid).one())
return binding
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=netid)
def add_vlan_binding(vlanid, vlanname, netid):
"""Adds a vlan to network association"""
LOG.debug("add_vlan_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.VlanBinding).
filter_by(vlan_id=vlanid).one())
raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
network_id=netid)
except exc.NoResultFound:
binding = network_models_v2.VlanBinding(vlanid, vlanname, netid)
session.add(binding)
session.flush()
return binding
def remove_vlan_binding(netid):
"""Removes a vlan to network association"""
LOG.debug("remove_vlan_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.VlanBinding).
filter_by(network_id=netid).one())
session.delete(binding)
session.flush()
return binding
except exc.NoResultFound:
pass
def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
"""Updates a vlan to network association"""
LOG.debug("update_vlan_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.VlanBinding).
filter_by(network_id=netid).one())
if newvlanid:
binding["vlan_id"] = newvlanid
if newvlanname:
binding["vlan_name"] = newvlanname
session.merge(binding)
session.flush()
return binding
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=netid)
def get_all_portprofiles():
"""Lists all the port profiles"""
LOG.debug("get_all_portprofiles() called")
session = db.get_session()
try:
pps = session.query(network_models_v2.PortProfile).all()
return pps
except exc.NoResultFound:
return []
def get_portprofile(tenantid, ppid):
"""Lists a port profile"""
LOG.debug("get_portprofile() called")
session = db.get_session()
try:
pp = (session.query(network_models_v2.PortProfile).
filter_by(uuid=ppid).one())
return pp
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)
def add_portprofile(tenantid, ppname, vlanid, qos):
"""Adds a port profile"""
LOG.debug("add_portprofile() called")
session = db.get_session()
try:
pp = (session.query(network_models_v2.PortProfile).
filter_by(name=ppname).one())
raise c_exc.PortProfileAlreadyExists(tenant_id=tenantid,
pp_name=ppname)
except exc.NoResultFound:
pp = network_models_v2.PortProfile(ppname, vlanid, qos)
session.add(pp)
session.flush()
return pp
def remove_portprofile(tenantid, ppid):
"""Removes a port profile"""
LOG.debug("remove_portprofile() called")
session = db.get_session()
try:
pp = (session.query(network_models_v2.PortProfile).
filter_by(uuid=ppid).one())
session.delete(pp)
session.flush()
return pp
except exc.NoResultFound:
pass
def update_portprofile(tenantid, ppid, newppname=None, newvlanid=None,
newqos=None):
"""Updates port profile"""
LOG.debug("update_portprofile() called")
session = db.get_session()
try:
pp = (session.query(network_models_v2.PortProfile).
filter_by(uuid=ppid).one())
if newppname:
pp["name"] = newppname
if newvlanid:
pp["vlan_id"] = newvlanid
if newqos:
pp["qos"] = newqos
session.merge(pp)
session.flush()
return pp
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)
def get_all_pp_bindings():
"""Lists all the port profiles"""
LOG.debug("get_all_pp_bindings() called")
session = db.get_session()
try:
bindings = session.query(network_models_v2.PortProfileBinding).all()
return bindings
except exc.NoResultFound:
return []
def get_pp_binding(tenantid, ppid):
"""Lists a port profile binding"""
LOG.debug("get_pp_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.PortProfileBinding).
filter_by(portprofile_id=ppid).one())
return binding
except exc.NoResultFound:
return []
def add_pp_binding(tenantid, portid, ppid, default):
"""Adds a port profile binding"""
LOG.debug("add_pp_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.PortProfileBinding).
filter_by(portprofile_id=ppid).one())
raise c_exc.PortProfileBindingAlreadyExists(pp_id=ppid,
port_id=portid)
except exc.NoResultFound:
binding = network_models_v2.PortProfileBinding(tenantid, portid,
ppid, default)
session.add(binding)
session.flush()
return binding
def remove_pp_binding(tenantid, portid, ppid):
"""Removes a port profile binding"""
LOG.debug("remove_pp_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.PortProfileBinding).
filter_by(portprofile_id=ppid).filter_by(port_id=portid).
one())
session.delete(binding)
session.flush()
return binding
except exc.NoResultFound:
pass
def update_pp_binding(tenantid, ppid, newtenantid=None,
newportid=None, newdefault=None):
"""Updates port profile binding"""
LOG.debug("update_pp_binding() called")
session = db.get_session()
try:
binding = (session.query(network_models_v2.PortProfileBinding).
filter_by(portprofile_id=ppid).one())
if newtenantid:
binding["tenant_id"] = newtenantid
if newportid:
binding["port_id"] = newportid
if newdefault:
binding["default"] = newdefault
session.merge(binding)
session.flush()
return binding
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(network_models_v2.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(network_models_v2.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(network_models_v2.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 = network_models_v2.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(network_models_v2.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(network_models_v2.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(network_models_v2.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(network_models_v2.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(network_models_v2.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(network_models_v2.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 = network_models_v2.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(network_models_v2.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(network_models_v2.Credential).
filter_by(tenant_id=tenant_id).
filter_by(credential_id=credential_id).one())
if new_user_name:
cred["user_name"] = new_user_name
if new_password:
cred["password"] = new_password
session.merge(cred)
session.flush()
return cred
except exc.NoResultFound:
raise c_exc.CredentialNotFound(credential_id=credential_id,
tenant_id=tenant_id)

View File

@ -0,0 +1,195 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012, 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 uuid
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.orm import relation, object_mapper
from quantum.db import model_base
from quantum.db import models_v2 as models
class L2NetworkBase(object):
"""Base class for L2Network Models."""
#__table_args__ = {'mysql_engine': 'InnoDB'}
def __setitem__(self, key, value):
"""Internal Dict set method"""
setattr(self, key, value)
def __getitem__(self, key):
"""Internal Dict get method"""
return getattr(self, key)
def get(self, key, default=None):
"""Dict get method"""
return getattr(self, key, default)
def __iter__(self):
"""Iterate over table columns"""
self._i = iter(object_mapper(self).columns)
return self
def next(self):
"""Next method for the iterator"""
n = self._i.next().name
return n, getattr(self, n)
def update(self, values):
"""Make the model object behave like a dict"""
for k, v in values.iteritems():
setattr(self, k, v)
def iteritems(self):
"""Make the model object behave like a dict"
Includes attributes from joins."""
local = dict(self)
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
if not k[0] == '_'])
local.update(joined)
return local.iteritems()
class VlanID(model_base.BASEV2, L2NetworkBase):
"""Represents a vlan_id usage"""
__tablename__ = 'vlan_ids'
vlan_id = Column(Integer, primary_key=True)
vlan_used = Column(Boolean)
def __init__(self, vlan_id):
self.vlan_id = vlan_id
self.vlan_used = False
def __repr__(self):
return "<VlanID(%d,%s)>" % (self.vlan_id, self.vlan_used)
class VlanBinding(model_base.BASEV2, L2NetworkBase):
"""Represents a binding of vlan_id to network_id"""
__tablename__ = 'vlan_bindings'
vlan_id = Column(Integer, primary_key=True)
vlan_name = Column(String(255))
network_id = Column(String(255),
nullable=False)
def __init__(self, vlan_id, vlan_name, network_id):
self.vlan_id = vlan_id
self.vlan_name = vlan_name
self.network_id = network_id
def __repr__(self):
return "<VlanBinding(%d,%s,%s)>" % (self.vlan_id,
self.vlan_name,
self.network_id)
class PortProfile(model_base.BASEV2, L2NetworkBase):
"""Represents L2 network plugin level PortProfile for a network"""
__tablename__ = 'portprofiles'
uuid = Column(String(255), primary_key=True)
name = Column(String(255))
vlan_id = Column(Integer)
qos = Column(String(255))
def __init__(self, name, vlan_id, qos=None):
self.uuid = uuid.uuid4()
self.name = name
self.vlan_id = vlan_id
self.qos = qos
def __repr__(self):
return "<PortProfile(%s,%s,%d,%s)>" % (self.uuid,
self.name,
self.vlan_id,
self.qos)
class PortProfileBinding(model_base.BASEV2, L2NetworkBase):
"""Represents PortProfile binding to tenant and network"""
__tablename__ = 'portprofile_bindings'
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(255))
port_id = Column(String(255), ForeignKey("ports.id"), nullable=False)
portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"),
nullable=False)
default = Column(Boolean)
ports = relation(models.Port)
portprofile = relation(PortProfile, uselist=False)
def __init__(self, tenant_id, port_id, portprofile_id, default):
self.tenant_id = tenant_id
self.port_id = port_id
self.portprofile_id = portprofile_id
self.default = default
def __repr__(self):
return "<PortProfile Binding(%s,%s,%s,%s)>" % (self.tenant_id,
self.port_id,
self.portprofile_id,
self.default)
class QoS(model_base.BASEV2, 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(model_base.BASEV2, L2NetworkBase):
"""Represents credentials for a tenant"""
__tablename__ = 'credentials'
credential_id = Column(String(255))
tenant_id = Column(String(255), primary_key=True)
credential_name = Column(String(255), primary_key=True)
user_name = Column(String(255))
password = Column(String(255))
def __init__(self, tenant_id, credential_name, user_name, password):
self.credential_id = str(uuid.uuid4())
self.tenant_id = tenant_id
self.credential_name = credential_name
self.user_name = user_name
self.password = password
def __repr__(self):
return "<Credentials(%s,%s,%s,%s,%s)>" % (self.credential_id,
self.tenant_id,
self.credential_name,
self.user_name,
self.password)

View File

@ -0,0 +1,89 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, 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.db.api as db
from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import nexus_models_v2
def get_all_nexusport_bindings():
"""Lists all the nexusport bindings"""
LOG.debug("get_all_nexusport_bindings() called")
session = db.get_session()
try:
bindings = session.query(nexus_models_v2.NexusPortBinding).all()
return bindings
except exc.NoResultFound:
return []
def get_nexusport_binding(vlan_id):
"""Lists a nexusport binding"""
LOG.debug("get_nexusport_binding() called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
def add_nexusport_binding(port_id, vlan_id):
"""Adds a nexusport binding"""
LOG.debug("add_nexusport_binding() called")
session = db.get_session()
binding = nexus_models_v2.NexusPortBinding(port_id, vlan_id)
session.add(binding)
session.flush()
return binding
def remove_nexusport_binding(vlan_id):
"""Removes a nexusport binding"""
LOG.debug("remove_nexusport_binding() called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).all())
for bind in binding:
session.delete(bind)
session.flush()
return binding
except exc.NoResultFound:
pass
def update_nexusport_binding(port_id, new_vlan_id):
"""Updates nexusport binding"""
LOG.debug("update_nexusport_binding called")
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(port_id=port_id).one())
if new_vlan_id:
binding["vlan_id"] = new_vlan_id
session.merge(binding)
session.flush()
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound()

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, 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
from quantum.db import model_base
from quantum.plugins.cisco.db.l2network_models import L2NetworkBase
class NexusPortBinding(model_base.BASEV2, L2NetworkBase):
"""Represents a binding of nexus port to vlan_id"""
__tablename__ = 'nexusport_bindings'
id = Column(Integer, primary_key=True, autoincrement=True)
port_id = Column(String(255))
vlan_id = Column(Integer, nullable=False)
def __init__(self, port_id, vlan_id):
self.port_id = port_id
self.vlan_id = vlan_id
def __repr__(self):
return "<NexusPortBinding (%s,%d)>" % (self.port_id, self.vlan_id)

View File

@ -0,0 +1,155 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, 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
from quantum.db import api as db
from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import ucs_models_v2 as 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 update_portbinding_instance_id(port_id, instance_id):
"""Updates port binding for the instance ID"""
LOG.debug("db update_portbinding_instance_id() called")
session = db.get_session()
try:
port_binding = (session.query(ucs_models.PortBinding).
filter_by(port_id=port_id).one())
port_binding.instance_id = instance_id
session.merge(port_binding)
session.flush()
return port_binding
except exc.NoResultFound:
raise c_exc.PortVnicNotFound(port_id=port_id)
def update_portbinding_vif_id(port_id, vif_id):
"""Updates port binding for the VIF ID"""
LOG.debug("db update_portbinding_vif_id() called")
session = db.get_session()
try:
port_binding = (session.query(ucs_models.PortBinding).
filter_by(port_id=port_id).one())
port_binding.vif_id = vif_id
session.merge(port_binding)
session.flush()
return port_binding
except exc.NoResultFound:
raise c_exc.PortVnicNotFound(port_id=port_id)
def get_portbinding_dn(blade_intf_dn):
"""Lists a port binding"""
LOG.debug("get_portbinding_dn() called")
session = db.get_session()
try:
port_binding = (session.query(ucs_models.PortBinding).
filter_by(blade_intf_dn=blade_intf_dn).one())
return port_binding
except exc.NoResultFound:
return []

View File

@ -0,0 +1,53 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012, 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
from sqlalchemy.orm import relation
from quantum.db.model_base import BASEV2 as BASE
from quantum.db import models_v2 as models
from quantum.plugins.cisco.db.network_models_v2 import L2NetworkBase
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), 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))
def __init__(self, port_id, blade_intf_dn, portprofile_name,
vlan_name, vlan_id, qos):
self.port_id = port_id
self.blade_intf_dn = blade_intf_dn
self.portprofile_name = portprofile_name
self.vlan_name = vlan_name
self.vlan_id = vlan_id
self.qos = qos
def __repr__(self):
return "<PortProfile Binding(%s,%s,%s,%s,%s,%s)>" % (
self.port_id, self.blade_intf_dn, self.portprofile_name,
self.vlan_name, self.vlan_id, self.qos)

View File

@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -128,6 +128,42 @@ class L2DevicePluginBase(object):
"""
pass
def create_subnet(self, tenant_id, net_id, ip_version,
subnet_cidr, **kwargs):
"""
:returns:
:raises:
"""
pass
def get_subnets(self, tenant_id, net_id, **kwargs):
"""
:returns:
:raises:
"""
pass
def get_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
"""
:returns:
:raises:
"""
pass
def update_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
"""
:returns:
:raises:
"""
pass
def delete_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@classmethod
def __subclasshook__(cls, klass):
"""

View File

@ -0,0 +1,296 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
from copy import deepcopy
import inspect
import logging
from quantum.openstack.common import importutils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.db import network_db_v2 as cdb
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum import quantum_plugin_base_v2
LOG = logging.getLogger(__name__)
class NetworkMultiBladeV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
"""
This implementation works with UCS and Nexus plugin for the
following topology:
One or more UCSM (each with one or more chasses connected),
All FICs connected to a single Nexus Switch.
"""
_plugins = {}
_inventory = {}
def __init__(self):
"""
Initialize the segmentation manager, check which device plugins are
configured, and load the inventories those device plugins for which the
inventory is configured
"""
self._vlan_mgr = importutils.import_object(conf.MANAGER_CLASS)
for key in conf.PLUGINS[const.PLUGINS].keys():
plugin_obj = conf.PLUGINS[const.PLUGINS][key]
self._plugins[key] = importutils.import_object(plugin_obj)
LOG.debug("Loaded device plugin %s\n" %
conf.PLUGINS[const.PLUGINS][key])
if key in conf.PLUGINS[const.INVENTORY].keys():
inventory_obj = conf.PLUGINS[const.INVENTORY][key]
self._inventory[key] = importutils.import_object(inventory_obj)
LOG.debug("Loaded device inventory %s\n" %
conf.PLUGINS[const.INVENTORY][key])
LOG.debug("%s.%s init done" % (__name__, self.__class__.__name__))
def _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):
"""
Invokes a device plugin's relevant functions (on the it's
inventory and plugin implementation) for completing this operation.
"""
if not plugin_key in self._plugins.keys():
LOG.info("No %s Plugin loaded" % plugin_key)
LOG.info("%s: %s with args %s ignored" %
(plugin_key, function_name, args))
return
device_params = self._invoke_inventory(plugin_key, function_name,
args)
device_ips = device_params[const.DEVICE_IP]
if not device_ips:
return [self._invoke_plugin(plugin_key, function_name, args,
device_params)]
else:
output = []
for device_ip in device_ips:
new_device_params = deepcopy(device_params)
new_device_params[const.DEVICE_IP] = device_ip
output.append(self._invoke_plugin(plugin_key, function_name,
args, new_device_params))
return output
def _invoke_inventory(self, plugin_key, function_name, args):
"""
Invokes the relevant function on a device plugin's
inventory for completing this operation.
"""
if not plugin_key in self._inventory.keys():
LOG.info("No %s inventory loaded" % plugin_key)
LOG.info("%s: %s with args %s ignored" %
(plugin_key, function_name, args))
return {const.DEVICE_IP: []}
else:
return getattr(self._inventory[plugin_key], function_name)(args)
def _invoke_plugin(self, plugin_key, function_name, args, kwargs):
"""
Invokes the relevant function on a device plugin's
implementation for completing this operation.
"""
func = getattr(self._plugins[plugin_key], function_name)
func_args_len = int(inspect.getargspec(func).args.__len__()) - 1
if args.__len__() > func_args_len:
func_args = args[:func_args_len]
extra_args = args[func_args_len:]
for dict_arg in extra_args:
for k, v in dict_arg.iteritems():
kwargs[k] = v
return func(*func_args, **kwargs)
else:
return func(*args, **kwargs)
def create_network(self, context, network):
"""
Perform this operation in the context of the configured device
plugins.
"""
n = network
try:
vlan_id = self._vlan_mgr.reserve_segmentation_id(n['tenant_id'],
n['name'])
vlan_name = self._vlan_mgr.get_vlan_name(n['id'], str(vlan_id))
args = [n['tenant_id'], n['name'], n['id'], vlan_name, vlan_id]
output = []
ucs_output = self._invoke_plugin_per_device(const.UCS_PLUGIN,
self._func_name(),
args)
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
args)
output.extend(ucs_output or [])
output.extend(nexus_output or [])
cdb.add_vlan_binding(vlan_id, vlan_name, n['id'])
return output
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def get_network(self, context, id, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
def get_networks(self, context, filters=None, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
def update_network(self, context, id, network):
"""Currently there is no processing required for the device plugins"""
pass
def delete_network(self, context, id, kwargs):
"""
Perform this operation in the context of the configured device
plugins.
"""
try:
base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
n = kwargs[const.NETWORK]
tenant_id = n['tenant_id']
args = [tenant_id, id, {const.CONTEXT:context},
{const.BASE_PLUGIN_REF:base_plugin_ref}]
# TODO (Sumit): Might first need to check here if there are active
# ports
output = []
ucs_output = self._invoke_plugin_per_device(const.UCS_PLUGIN,
self._func_name(),
args)
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
args)
output.extend(ucs_output or [])
output.extend(nexus_output or [])
self._vlan_mgr.release_segmentation_id(tenant_id, id)
cdb.remove_vlan_binding(id)
return output
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def create_port(self, context, port):
"""
Perform this operation in the context of the configured device
plugins.
"""
try:
tenant_id = port['tenant_id']
net_id = port['network_id']
port_state = port['admin_state_up']
port_id_string = port['id']
args = [tenant_id, net_id, port_state, port_id_string]
ret_val = self._invoke_plugin_per_device(const.UCS_PLUGIN,
self._func_name(), args)
new_args = [tenant_id, net_id, port['id'], port['id']]
self._invoke_plugin_per_device(const.UCS_PLUGIN,
"plug_interface", new_args)
return ret_val
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def get_port(self, context, id, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
def get_ports(self, context, filters=None, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
def update_port(self, context, id, port):
"""Currently there is no processing required for the device plugins"""
pass
def delete_port(self, context, id, kwargs):
"""
Perform this operation in the context of the configured device
plugins.
"""
try:
p = kwargs['port']
args = [p['tenant_id'], p['network_id'], p['id']]
return self._invoke_plugin_per_device(const.UCS_PLUGIN,
self._func_name(), args)
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def create_subnet(self, context, subnet):
"""Currently there is no processing required for the device plugins"""
pass
def update_subnet(self, context, id, subnet):
"""Currently there is no processing required for the device plugins"""
pass
def get_subnet(self, context, id, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
def delete_subnet(self, context, id, kwargs):
"""Currently there is no processing required for the device plugins"""
pass
def get_subnets(self, context, filters=None, fields=None, verbose=None):
"""Currently there is no processing required for the device plugins"""
pass
"""
Extensions' implementation in device plugins
"""
def schedule_host(self, args):
"""Provides the hostname on which a dynamic vnic is reserved"""
try:
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def associate_port(self, args):
"""Get the portprofile name and the device name for the dynamic vnic"""
try:
return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
args)
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def detach_port(self, args):
"""Remove the association of the VIF with the dynamic vnic """
try:
return self._invoke_plugin_per_device(const.UCS_PLUGIN,
self._func_name(), args)
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise
def create_multiport(self, args):
"""
Makes a call to the UCS device plugin to create ports on the same
host.
"""
try:
self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
args)
except:
# TODO (Sumit): Check if we need to perform any rollback here
raise

View File

@ -0,0 +1,455 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
import inspect
import logging
from quantum.common import exceptions as exc
from quantum.db import db_base_plugin_v2
from quantum.db import models_v2
from quantum.openstack.common import importutils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_utils as cutil
from quantum.plugins.cisco.db import network_db_v2 as cdb
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.quantum_plugin_base import QuantumPluginBase
LOG = logging.getLogger(__name__)
class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
"""
Plugin with v2 API support for multiple sub-plugins
"""
supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
"Cisco qos", "Cisco Nova Tenant",
"Cisco Multiport"]
"""
Core API implementation
"""
def __init__(self):
"""
Initializes the DB, and credential store.
"""
cdb.initialize()
cred.Store.initialize()
self._model = importutils.import_object(conf.MODEL_CLASS)
super(PluginV2, self).__init__()
LOG.debug("Plugin initialization complete")
def create_network(self, context, network):
"""
Creates a new Virtual Network, and assigns it
a symbolic name.
"""
LOG.debug("create_network() called\n")
new_network = super(PluginV2, self).create_network(context, network)
try:
self._invoke_device_plugins(self._func_name(), [context,
new_network])
return new_network
except:
super(PluginV2, self).delete_network(context, new_network['id'])
raise
def update_network(self, context, id, network):
"""
Updates the symbolic name belonging to a particular
Virtual Network.
"""
LOG.debug("update_network() called\n")
try:
self._invoke_device_plugins(self._func_name(), [context, id,
network])
return super(PluginV2, self).update_network(context, id, network)
except:
raise
def delete_network(self, context, id):
"""
Deletes the network with the specified network identifier
belonging to the specified tenant.
"""
LOG.debug("delete_network() called\n")
#We first need to check if there are any ports on this network
with context.session.begin():
network = self._get_network(context, id)
filter = {'network_id': [id]}
ports = self.get_ports(context, filters=filter)
if ports:
raise exc.NetworkInUse(net_id=id)
context.session.close()
#Network does not have any ports, we can proceed to delete
try:
network = self._get_network(context, id)
kwargs = {const.NETWORK: network,
const.BASE_PLUGIN_REF: self}
self._invoke_device_plugins(self._func_name(), [context, id,
kwargs])
return super(PluginV2, self).delete_network(context, id)
except:
raise
def create_port(self, context, port):
"""
Creates a port on the specified Virtual Network.
"""
LOG.debug("create_port() called\n")
new_port = super(PluginV2, self).create_port(context, port)
try:
self._invoke_device_plugins(self._func_name(), [context, new_port])
return new_port
except:
super(PluginV2, self).delete_port(context, new_port['id'])
raise
def delete_port(self, context, id):
"""
Deletes a port
"""
LOG.debug("delete_port() called\n")
port = self._get_port(context, id)
"""
TODO (Sumit): Disabling this check for now, check later
#Allow deleting a port only if the administrative state is down,
#and its operation status is also down
if port['admin_state_up'] or port['status'] == 'ACTIVE':
raise exc.PortInUse(port_id=id, net_id=port['network_id'],
att_id=port['device_id'])
"""
try:
kwargs = {const.PORT: port}
# TODO (Sumit): Might first need to check here if port is active
self._invoke_device_plugins(self._func_name(), [context, id,
kwargs])
return super(PluginV2, self).delete_port(context, id)
except:
raise
def update_port(self, context, id, port):
"""
Updates the state of a port and returns the updated port
"""
LOG.debug("update_port() called\n")
try:
self._invoke_device_plugins(self._func_name(), [context, id,
port])
return super(PluginV2, self).update_port(context, id, port)
except:
raise
def create_subnet(self, context, subnet):
"""
Create a subnet, which represents a range of IP addresses
that can be allocated to devices.
"""
LOG.debug("create_subnet() called\n")
new_subnet = super(PluginV2, self).create_subnet(context, subnet)
try:
self._invoke_device_plugins(self._func_name(), [context,
new_subnet])
return new_subnet
except:
super(PluginV2, self).delete_subnet(context, new_subnet['id'])
raise
def update_subnet(self, context, id, subnet):
"""
Updates the state of a subnet and returns the updated subnet
"""
LOG.debug("update_subnet() called\n")
try:
self._invoke_device_plugins(self._func_name(), [context, id,
subnet])
return super(PluginV2, self).update_subnet(context, id, subnet)
except:
raise
def delete_subnet(self, context, id):
"""
Deletes a subnet
"""
LOG.debug("delete_subnet() called\n")
with context.session.begin():
subnet = self._get_subnet(context, id)
# Check if ports are using this subnet
allocated_qry = context.session.query(models_v2.IPAllocation)
allocated = allocated_qry.filter_by(subnet_id=id).all()
if allocated:
raise exc.SubnetInUse(subnet_id=id)
context.session.close()
try:
kwargs = {const.SUBNET: subnet}
self._invoke_device_plugins(self._func_name(), [context, id,
kwargs])
return super(PluginV2, self).delete_subnet(context, id)
except:
raise
"""
Extension API implementation
"""
def get_all_portprofiles(self, tenant_id):
"""Get all port profiles"""
LOG.debug("get_all_portprofiles() called\n")
pplist = cdb.get_all_portprofiles()
new_pplist = []
for portprofile in pplist:
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
new_pplist.append(new_pp)
return new_pplist
def get_portprofile_details(self, tenant_id, profile_id):
"""Get port profile details"""
LOG.debug("get_portprofile_details() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
def create_portprofile(self, tenant_id, profile_name, qos):
"""Create port profile"""
LOG.debug("create_portprofile() called\n")
portprofile = cdb.add_portprofile(tenant_id, profile_name,
const.NO_VLAN_ID, qos)
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
def delete_portprofile(self, tenant_id, profile_id):
"""Delete portprofile"""
LOG.debug("delete_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
plist = cdb.get_pp_binding(tenant_id, profile_id)
if plist:
raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id,
profile_id=profile_id)
else:
cdb.remove_portprofile(tenant_id, profile_id)
def rename_portprofile(self, tenant_id, profile_id, new_name):
"""Rename port profile"""
LOG.debug("rename_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
new_pp = cutil.make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
def associate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
"""Associate port profile"""
LOG.debug("associate_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
cdb.add_pp_binding(tenant_id, port_id, portprofile_id, False)
def disassociate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
"""Disassociate port profile"""
LOG.debug("disassociate_portprofile() called\n")
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
cdb.remove_pp_binding(tenant_id, port_id, portprofile_id)
def get_all_qoss(self, tenant_id):
"""Get all QoS levels"""
LOG.debug("get_all_qoss() called\n")
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 = cdb.get_qos(tenant_id, qos_id)
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_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 = 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 = cdb.get_qos(tenant_id, qos_id)
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_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")
try:
qos_level = cdb.get_qos(tenant_id, qos_id)
except Exception:
raise cexc.QosNotFound(tenant_id=tenant_id,
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")
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 = cdb.get_credential(tenant_id, credential_id)
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
return credential
def create_credential(self, tenant_id, credential_name, user_name,
password):
"""Create a new credential"""
LOG.debug("create_credential() called\n")
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 = cdb.get_credential(tenant_id, credential_id)
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=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 = cdb.get_credential(tenant_id, credential_id)
except Exception:
raise cexc.CredentialNotFound(tenant_id=tenant_id,
credential_id=credential_id)
credential = cdb.update_credential(tenant_id, credential_id, new_name)
return credential
def schedule_host(self, tenant_id, instance_id, instance_desc):
"""Provides the hostname on which a dynamic vnic is reserved"""
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 associate_port(self, tenant_id, instance_id, instance_desc):
"""
Get the portprofile name and the device name for the dynamic vnic
"""
LOG.debug("associate_port() called\n")
return self._invoke_device_plugins(self._func_name(), [tenant_id,
instance_id,
instance_desc])
def detach_port(self, tenant_id, instance_id, instance_desc):
"""
Remove the association of the VIF with the dynamic vnic
"""
LOG.debug("detach_port() called\n")
return self._invoke_device_plugins(self._func_name(), [tenant_id,
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:
db.validate_network_ownership(tenant_id, net_id)
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
"""
def _invoke_device_plugins(self, function_name, args):
"""
All device-specific calls are delegated to the model
"""
return getattr(self._model, function_name)(*args)
def _func_name(self, offset=0):
"""Getting the name of the calling funciton"""
return inspect.stack()[1 + offset][3]

View File

@ -0,0 +1,203 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc.
#
"""
PlugIn for Nexus OS driver
"""
import logging
from quantum.common import exceptions as exc
from quantum.db import api as db
from quantum.openstack.common import importutils
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 network_db_v2 as cdb
from quantum.plugins.cisco.db import nexus_db_v2 as nxos_db
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.nexus import cisco_nexus_configuration as conf
LOG = logging.getLogger(__name__)
class NexusPlugin(L2DevicePluginBase):
"""
Nexus PLugIn Main Class
"""
_networks = {}
def __init__(self):
"""
Extracts the configuration parameters from the configuration file
"""
self._client = importutils.import_object(conf.NEXUS_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER)
self._nexus_ip = conf.NEXUS_IP_ADDRESS
self._nexus_username = cred.Store.get_username(conf.NEXUS_IP_ADDRESS)
self._nexus_password = cred.Store.get_password(conf.NEXUS_IP_ADDRESS)
self._nexus_first_port = conf.NEXUS_FIRST_PORT
self._nexus_second_port = conf.NEXUS_SECOND_PORT
self._nexus_ssh_port = conf.NEXUS_SSH_PORT
def get_all_networks(self, tenant_id):
"""
Returns a dictionary containing all
<network_uuid, network_name> for
the specified tenant.
"""
LOG.debug("NexusPlugin:get_all_networks() called\n")
return self._networks.values()
def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
**kwargs):
"""
Create a VLAN in the switch, and configure the appropriate interfaces
for this VLAN
"""
LOG.debug("NexusPlugin:create_network() called\n")
self._client.create_vlan(
vlan_name, str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password,
self._nexus_first_port, self._nexus_second_port,
self._nexus_ssh_port)
nxos_db.add_nexusport_binding(self._nexus_first_port, str(vlan_id))
nxos_db.add_nexusport_binding(self._nexus_second_port, str(vlan_id))
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
def delete_network(self, tenant_id, net_id, **kwargs):
"""
Deletes a VLAN in the switch, and removes the VLAN configuration
from the relevant interfaces
"""
LOG.debug("NexusPlugin:delete_network() called\n")
context = kwargs[const.CONTEXT]
base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
context, base_plugin_ref)
ports_id = nxos_db.get_nexusport_binding(vlan_id)
LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
nxos_db.remove_nexusport_binding(vlan_id)
net = self._get_network(tenant_id, net_id, context, base_plugin_ref)
if net:
self._client.delete_vlan(
str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password,
self._nexus_first_port, self._nexus_second_port,
self._nexus_ssh_port)
return net
# Network not found
raise exc.NetworkNotFound(net_id=net_id)
def get_network_details(self, tenant_id, net_id, **kwargs):
"""
Returns the details of a particular network
"""
LOG.debug("NexusPlugin:get_network_details() called\n")
network = self._get_network(tenant_id, net_id)
return network
def update_network(self, tenant_id, net_id, **kwargs):
"""
Updates the properties of a particular
Virtual Network.
"""
LOG.debug("NexusPlugin:update_network() called\n")
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = kwargs["name"]
return network
def get_all_ports(self, tenant_id, net_id, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:get_all_ports() called\n")
def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:create_port() called\n")
def delete_port(self, tenant_id, net_id, port_id, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:delete_port() called\n")
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:update_port() called\n")
def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:get_port_details() called\n")
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
**kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:plug_interface() called\n")
def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
"""
This is probably not applicable to the Nexus plugin.
Delete if not required.
"""
LOG.debug("NexusPlugin:unplug_interface() called\n")
def _get_vlan_id_for_network(self, tenant_id, network_id, context,
base_plugin_ref):
"""
Obtain the VLAN ID given the Network ID
"""
net = self._get_network(tenant_id, network_id, context,
base_plugin_ref)
vlan_id = net[const.NET_VLAN_ID]
return vlan_id
def _get_network(self, tenant_id, network_id, context, base_plugin_ref):
"""
Gets the NETWORK ID
"""
network = base_plugin_ref._get_network(context, network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
vlan = cdb.get_vlan_binding(network_id)
return {const.NET_ID: network_id, const.NET_NAME: network.name,
const.NET_PORTS: network.ports,
const.NET_VLAN_NAME: vlan.vlan_name,
const.NET_VLAN_ID: vlan.vlan_id}

View File

@ -0,0 +1,52 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import logging
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.db import network_db_v2 as cdb
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.l2network_segmentation_base import (
L2NetworkSegmentationMgrBase,
)
LOG = logging.getLogger(__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])
def get_vlan_name(self, net_id, vlan):
"""Getting the vlan name from the tenant and vlan"""
vlan_name = conf.VLAN_NAME_PREFIX + vlan
return vlan_name

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC.
# 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.
import __builtin__
import unittest
setattr(__builtin__, '_', lambda x: x)
class BaseTest(unittest.TestCase):
def setUp(self):
pass
def setUp():
pass

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC.
# 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.
import __builtin__
import unittest
setattr(__builtin__, '_', lambda x: x)
class BaseTest(unittest.TestCase):
def setUp(self):
pass
def setUp():
pass

View File

@ -0,0 +1,101 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Rohit Agarwalla, Cisco Systems, Inc.
class CiscoNEXUSFakeDriver():
"""
Nexus Driver Fake Class
"""
def __init__(self):
pass
def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user,
nexus_password):
"""
Makes the fake connection to the Nexus Switch
"""
pass
def create_xml_snippet(self, cutomized_config):
"""
Creates the Proper XML structure for the Nexus Switch Configuration
"""
pass
def enable_vlan(self, mgr, vlanid, vlanname):
"""
Creates a VLAN on Nexus Switch given the VLAN ID and Name
"""
pass
def disable_vlan(self, mgr, vlanid):
"""
Delete a VLAN on Nexus Switch given the VLAN ID
"""
pass
def enable_port_trunk(self, mgr, interface):
"""
Enables trunk mode an interface on Nexus Switch
"""
pass
def disable_switch_port(self, mgr, interface):
"""
Disables trunk mode an interface on Nexus Switch
"""
pass
def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
"""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID
"""
pass
def disable_vlan_on_trunk_int(self, mgr, interface, vlanid):
"""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID
"""
pass
def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
nexus_password, nexus_first_interface,
nexus_second_interface, nexus_ssh_port):
"""
Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
given the VLAN ID and Name and Interface Number
"""
pass
def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_first_interface, nexus_second_interface,
nexus_ssh_port):
"""
Delete a VLAN and Disables trunk mode an interface on Nexus Switch
given the VLAN ID and Interface Number
"""
pass
def build_vlans_cmd(self):
"""
Builds a string with all the VLANs on the same Switch
"""
pass

View File

@ -0,0 +1,20 @@
[DEFAULT]
# Show more verbose log output (sets INFO log level output)
verbose = True
# Show debugging output in logs (sets DEBUG log level output)
debug = False
# Address to bind the API server
bind_host = 0.0.0.0
# Port the bind the API server to
bind_port = 9696
# Path to the extensions
api_extensions_path = ../../../../extensions
# Paste configuration file
api_paste_config = api-paste.ini.cisco.test
core_plugin = quantum.plugins.cisco.network_plugin.PluginV2

View File

@ -0,0 +1,55 @@
# Copyright 2012 OpenStack LLC.
# 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 spec
import inspect
import logging
import mock
import os
import webtest
from quantum.api.v2 import router
from quantum.common import config
from quantum.openstack.common import cfg
from quantum.tests.unit import test_api_v2
LOG = logging.getLogger(__name__)
def curdir(*p):
return os.path.join(os.path.dirname(__file__), *p)
class APIv2TestCase(test_api_v2.APIv2TestCase):
def setUp(self):
plugin = 'quantum.plugins.cisco.network_plugin.PluginV2'
# Create the default configurations
args = ['--config-file', curdir('quantumv2.conf.cisco.test')]
config.parse(args=args)
# Update the plugin
cfg.CONF.set_override('core_plugin', plugin)
self._plugin_patcher = mock.patch(plugin, autospec=True)
self.plugin = self._plugin_patcher.start()
api = router.APIRouter()
self.api = webtest.TestApp(api)
LOG.debug("%s.%s.%s done" % (__name__, self.__class__.__name__,
inspect.stack()[0][3]))
class JSONV2TestCase(APIv2TestCase, test_api_v2.JSONV2TestCase):
pass

View File

@ -0,0 +1,86 @@
# Copyright (c) 2012 OpenStack, LLC.
#
# 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.
import inspect
import logging
import mock
import os
from quantum.api.v2.router import APIRouter
from quantum.common import config
from quantum.db import api as db
from quantum.plugins.cisco.db import network_models_v2
from quantum.openstack.common import cfg
from quantum.tests.unit import test_db_plugin
from quantum.wsgi import JSONDeserializer
LOG = logging.getLogger(__name__)
def curdir(*p):
return os.path.join(os.path.dirname(__file__), *p)
class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase):
def setUp(self):
db._ENGINE = None
db._MAKER = None
self._tenant_id = 'test-tenant'
json_deserializer = JSONDeserializer()
self._deserializers = {
'application/json': json_deserializer,
}
plugin = 'quantum.plugins.cisco.network_plugin.PluginV2'
# Create the default configurations
args = ['--config-file', curdir('quantumv2.conf.cisco.test')]
config.parse(args=args)
# Update the plugin
cfg.CONF.set_override('core_plugin', plugin)
cfg.CONF.set_override('base_mac', "12:34:56:78:90:ab")
self.api = APIRouter()
LOG.debug("%s.%s.%s done" % (__name__, self.__class__.__name__,
inspect.stack()[0][3]))
def tearDown(self):
db.clear_db(network_models_v2.model_base.BASEV2)
db._ENGINE = None
db._MAKER = None
cfg.CONF.reset()
class TestV2HTTPResponse(NetworkPluginV2TestCase,
test_db_plugin.TestV2HTTPResponse):
pass
class TestPortsV2(NetworkPluginV2TestCase, test_db_plugin.TestPortsV2):
pass
class TestNetworksV2(NetworkPluginV2TestCase, test_db_plugin.TestNetworksV2):
pass
class TestSubnetsV2(NetworkPluginV2TestCase, test_db_plugin.TestSubnetsV2):
pass

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 OpenStack LLC.
# 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.
import __builtin__
import unittest
setattr(__builtin__, '_', lambda x: x)
class BaseTest(unittest.TestCase):
def setUp(self):
pass
def setUp():
pass

View File

@ -0,0 +1,59 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
import logging
import os
from quantum.common.utils import find_config_file
from quantum.openstack.common import importutils
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_credentials_v2 as cred
from quantum.plugins.cisco.ucs import (
cisco_ucs_inventory_configuration as conf,
)
from quantum.plugins.cisco.ucs import cisco_ucs_inventory_v2
LOG = logging.getLogger(__name__)
def curdir(*p):
return os.path.join(os.path.dirname(__file__), *p)
class UCSInventory(cisco_ucs_inventory_v2.UCSInventory):
"""
Inventory implementation for testing
"""
def __init__(self):
fake_ucs_driver = "quantum.plugins.cisco.tests.unit.v2.ucs." + \
"fake_ucs_driver.CiscoUCSMFakeDriver"
self._client = importutils.import_object(fake_ucs_driver)
conf_parser = confp.CiscoConfigParser(curdir("fake_ucs_inventory.ini"))
conf.INVENTORY = conf_parser.walk(conf_parser.dummy)
for ucsm in conf.INVENTORY.keys():
ucsm_ip = conf.INVENTORY[ucsm][const.IP_ADDRESS]
try:
cred.Store.put_credential(ucsm_ip, "username", "password")
except:
pass
self._load_inventory()

View File

@ -0,0 +1,96 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Rohit Agarwalla, Cisco Systems, Inc.
from quantum.plugins.cisco.common import cisco_constants as const
class CiscoUCSMFakeDriver():
"""UCSM Fake Driver"""
def __init__(self):
pass
def _get_blade_interfaces(self, chassis_number, blade_number, ucsm_ip,
ucsm_username, ucsm_password):
blade_interfaces = {}
for element in range(20):
dist_name = "dn" + str(element)
if dist_name:
order = str(element)
rhel_name = "eth" + str(element)
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: const.BLADE_INTF_DYNAMIC,
const.BLADE_INTF_RHEL_DEVICE_NAME: rhel_name,
}
blade_interfaces[dist_name] = blade_interface
return blade_interfaces
def _get_blade_interface_state(self, blade_intf, ucsm_ip,
ucsm_username, ucsm_password):
blade_intf[const.BLADE_INTF_LINK_STATE] = \
const.BLADE_INTF_STATE_UNKNOWN
blade_intf[const.BLADE_INTF_OPER_STATE] = \
const.BLADE_INTF_STATE_UNKNOWN
blade_intf[const.BLADE_INTF_INST_TYPE] = \
const.BLADE_INTF_DYNAMIC
def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
ucsm_password):
pass
def create_profile(self, profile_name, vlan_name, ucsm_ip, ucsm_username,
ucsm_password):
pass
def change_vlan_in_profile(self, profile_name, old_vlan_name,
new_vlan_name, ucsm_ip, ucsm_username,
ucsm_password):
pass
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):
pass
def delete_profile(self, profile_name, ucsm_ip, ucsm_username,
ucsm_password):
pass

View File

@ -0,0 +1,7 @@
[ucsm-1]
ip_address = 192.168.100.2
[[chassis-1]]
chassis_id = 1
[[[blade-1]]]
blade_id = 1
host_name = blade1

View File

@ -0,0 +1,127 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Shubhangi Satras, Cisco Systems, Inc.
# @author: Tyler Smith, Cisco Systems, Inc.
import logging
import unittest
import uuid
from quantum.common import exceptions as exc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials_v2 as creds
from quantum.plugins.cisco.db import network_db_v2 as cdb
from quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake import (
UCSInventory,
)
LOG = logging.getLogger(__name__)
# Set some data to use in tests
tenant = 'shubh'
net_name = 'TestNetwork1'
port_state = const.PORT_UP
interface_id = 'vif-01'
class TestUCSInventory(unittest.TestCase):
"""
Tests for the UCS Inventory. Each high-level operation should return
some information about which devices to perform the action on.
"""
def setUp(self):
"""Setup our tests"""
cdb.initialize()
creds.Store.initialize()
# Create the ucs inventory object
self._ucs_inventory = UCSInventory()
self.inventory = self._ucs_inventory._inventory
def assertValidUCM(self, ip_address):
"""Asserts that the given ip is in the UCS inventory"""
if ip_address in self.inventory.keys():
assert(1)
return
assert(0)
def _test_get_all_ucms(self, cmd):
"""Runs tests for commands that expect a list of all UCMS"""
LOG.debug("test_%s - START", cmd)
results = getattr(self._ucs_inventory, cmd)([])
self.assertEqual(results[const.DEVICE_IP], self.inventory.keys())
LOG.debug("test_%s - END", cmd)
def _test_with_port_creation(self, cmd, params=None):
"""Tests commands that requires a port to exist"""
LOG.debug("test_%s - START", cmd)
net_uuid = str(uuid.uuid4())
device_params = self._ucs_inventory.create_port(tenant, net_uuid,
port_state,
state=port_state)
args = [tenant, net_uuid, port[const.PORT_ID]]
if params is not None:
args.extend(params)
ip_address = getattr(self._ucs_inventory, cmd)(args)
ip_address = ip_address[const.DEVICE_IP][0]
self.assertValidUCM(ip_address)
cdb.clear_db()
LOG.debug("test_%s - END", cmd)
def test_create_port(self):
"""Test that the UCS Inventory returns the correct devices to use"""
LOG.debug("test_create_port - START")
results = self._ucs_inventory.create_port([])
results = results[const.LEAST_RSVD_BLADE_DICT]
ip_address = results[const.LEAST_RSVD_BLADE_UCSM]
chassis = results[const.LEAST_RSVD_BLADE_CHASSIS]
blade = results[const.LEAST_RSVD_BLADE_ID]
if blade not in self.inventory[ip_address][chassis]:
self.assertEqual(0, 1)
self.assertEqual(1, 1)
LOG.debug("test_create_port - END")
def test_get_all_networks(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('get_all_networks')
def test_create_network(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('create_network')
def test_delete_network(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('delete_network')
def test_get_network_details(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('get_network_details')
def test_update_network(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('update_network')
def test_get_all_ports(self):
"""Test that the UCS Inventory returns the correct devices to use"""
self._test_get_all_ucms('get_all_ports')

View File

@ -0,0 +1,709 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
"""
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
"""
from copy import deepcopy
import logging
from quantum.common import exceptions as exc
from quantum.openstack.common import importutils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.db import ucs_db_v2 as udb
from quantum.plugins.cisco.l2device_inventory_base import (
L2NetworkDeviceInventoryBase,
)
from quantum.plugins.cisco.ucs import (
cisco_ucs_inventory_configuration as conf,
)
LOG = logging.getLogger(__name__)
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 = importutils.import_object(conf.UCSM_DRIVER)
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 _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.get_username(ucsm_ip)
ucsm_password = cred.Store.get_password(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"""
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 not const.TENANTID in blade_intf_data[blade_intf].keys():
blade_intf_data[blade_intf][const.TENANTID] = None
if not const.PORTID in blade_intf_data[blade_intf].keys():
blade_intf_data[blade_intf][const.PORTID] = None
if not const.PROFILE_ID in blade_intf_data[blade_intf].keys():
blade_intf_data[blade_intf][const.PROFILE_ID] = None
if not const.INSTANCE_ID in blade_intf_data[blade_intf].keys():
blade_intf_data[blade_intf][const.INSTANCE_ID] = None
if not const.VIF_ID in blade_intf_data[blade_intf].keys():
blade_intf_data[blade_intf][const.VIF_ID] = None
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)
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
intf_data = blade_intf_data[blade_intf]
if ((intf_data[const.BLADE_INTF_RESERVATION] == const.
BLADE_INTF_UNRESERVED)):
unreserved_counter -= 1
intf_data[const.BLADE_INTF_RESERVATION] = (
const.BLADE_INTF_RESERVED)
intf_data[const.TENANTID] = port_binding[const.TENANTID]
intf_data[const.PORTID] = port_binding[const.PORTID]
intf_data[const.PROFILE_ID] = (
port_binding[const.PORTPROFILENAME])
intf_data[const.INSTANCE_ID] = port_binding[const.INSTANCE_ID]
intf_data[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.HOST_NAME: host_name}
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])
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_RESERVED and
intf_data[const.TENANTID] == tenant_id and
intf_data[const.INSTANCE_ID] is None):
intf_data[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():
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_RESERVED and
intf_data[const.TENANTID] == tenant_id and
intf_data[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():
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_RESERVED and
intf_data[const.TENANTID] == tenant_id and
(not intf_data[const.VIF_ID])):
intf_data[const.VIF_ID] = vif_id
intf_data[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)
device_name = intf_data[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") %
(intf_data, 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, instance_id, vif_id):
"""
Disassociate a VIF-ID from a port, this happens when a
VM is destroyed
"""
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():
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_RESERVED and
intf_data[const.TENANTID] == tenant_id and
blade_intf_data[blade_intf][const.INSTANCE_ID]
== instance_id and
intf_data[const.VIF_ID][:const.UUID_LENGTH] ==
vif_id):
intf_data[const.VIF_ID] = None
intf_data[const.INSTANCE_ID] = None
port_binding = udb.get_portbinding_dn(blade_intf)
port_id = port_binding[const.PORTID]
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, intf_data))
device_params = {const.DEVICE_IP: [ucsm_ip],
const.PORTID: port_id}
return device_params
LOG.warn(("Disassociating VIF-ID in UCS inventory failed. "
"Could not find a reserved dynamic nic for tenant: %s") %
tenant_id)
return None
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
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_RESERVED and
intf_data[const.TENANTID] == tenant_id and
intf_data[const.PORTID] == port_id):
interface_dn = intf_data[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 _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
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 < 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 = {
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 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):
"""Reserve an interface on a blade"""
ucsm_username = cred.Store.get_username(ucsm_ip)
ucsm_password = cred.Store.get_password(ucsm_ip)
"""
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]
chassis_data = self._inventory_state[ucsm_ip][chassis_id]
old_blade_intf_data = chassis_data[blade_id][const.BLADE_INTF_DATA]
"""
We will now copy the older non-UCSM-specific blade
interface state
"""
for blade_intf in blade_intf_data.keys():
old_intf_data = old_blade_intf_data[blade_intf]
blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
old_intf_data[const.BLADE_INTF_RESERVATION])
blade_intf_data[blade_intf][const.TENANTID] = (
old_intf_data[const.TENANTID])
blade_intf_data[blade_intf][const.PORTID] = (
old_intf_data[const.PORTID])
blade_intf_data[blade_intf][const.PROFILE_ID] = (
old_intf_data[const.PROFILE_ID])
blade_intf_data[blade_intf][const.INSTANCE_ID] = (
old_intf_data[const.INSTANCE_ID])
blade_intf_data[blade_intf][const.VIF_ID] = (
old_intf_data[const.VIF_ID])
blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = (
chassis_data[blade_id][const.BLADE_UNRESERVED_INTF_COUNT])
"""
Now we will reserve an interface if its available
"""
for blade_intf in blade_intf_data.keys():
intf_data = blade_intf_data[blade_intf]
if (intf_data[const.BLADE_INTF_RESERVATION] ==
const.BLADE_INTF_UNRESERVED):
intf_data[const.BLADE_INTF_RESERVATION] = (
const.BLADE_INTF_RESERVED)
intf_data[const.TENANTID] = tenant_id
intf_data[const.PORTID] = port_id
intf_data[const.INSTANCE_ID] = None
dev_eth_name = intf_data[const.BLADE_INTF_RHEL_DEVICE_NAME]
"""
We are replacing the older blade interface state with new
"""
chassis_data[blade_id][const.BLADE_INTF_DATA] = blade_intf_data
chassis_data[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=intf_data[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, 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.get_username(ucsm_ip)
ucsm_password = cred.Store.get_password(ucsm_ip)
blade_data = self._inventory_state[ucsm_ip][chassis_id][blade_id]
blade_data[const.BLADE_UNRESERVED_INTF_COUNT] += 1
blade_intf = blade_data[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 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 update_network(self, args):
"""Return all UCSM IPs"""
LOG.debug("update_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:
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,
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")
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
def detach_port(self, args):
"""
Remove the VIF-ID and instance name association
with the port
"""
LOG.debug("detach_port() called\n")
instance_id = args[1]
tenant_id = args[2][const.PROJECT_ID]
vif_id = args[2][const.VIF_ID]
device_params = self._disassociate_vifid_from_port(tenant_id,
instance_id,
vif_id)
return device_params
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

@ -0,0 +1,337 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import logging
from quantum.db import api as db
from quantum.openstack.common import importutils
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 network_db_v2 as cdb
from quantum.plugins.cisco.db import ucs_db_v2 as udb
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf
LOG = logging.getLogger(__name__)
class UCSVICPlugin(L2DevicePluginBase):
"""UCS Device Plugin"""
def __init__(self):
self._driver = importutils.import_object(conf.UCSM_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER)
# TODO (Sumit) Make the counter per UCSM
self._port_profile_counter = 0
def get_all_networks(self, tenant_id, **kwargs):
"""
Returns a dictionary containing all
<network_uuid, network_name> for
the specified tenant.
"""
LOG.debug("UCSVICPlugin:get_all_networks() called\n")
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):
"""
Creates a new Virtual Network, and assigns it
a symbolic name.
"""
LOG.debug("UCSVICPlugin:create_network() called\n")
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)
ports_on_net = []
new_network_dict = cutil.make_net_dict(net_id,
net_name,
ports_on_net)
return new_network_dict
def delete_network(self, tenant_id, net_id, **kwargs):
"""
Deletes the network with the specified network identifier
belonging to the specified tenant.
"""
LOG.debug("UCSVICPlugin:delete_network() called\n")
self._set_ucsm(kwargs[const.DEVICE_IP])
vlan_binding = cdb.get_vlan_binding(net_id)
vlan_name = vlan_binding[const.VLANNAME]
self._driver.delete_vlan(vlan_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
#Rohit:passing empty network name, might not need fixing
net_dict = cutil.make_net_dict(net_id,
"",
[])
return net_dict
def get_network_details(self, tenant_id, net_id, **kwargs):
"""
Deletes the Virtual Network belonging to a the
spec
"""
LOG.debug("UCSVICPlugin:get_network_details() called\n")
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 update_network(self, tenant_id, net_id, **kwargs):
"""
Updates the symbolic name belonging to a particular
Virtual Network.
"""
LOG.debug("UCSVICPlugin:update_network() called\n")
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):
"""
Retrieves all port identifiers belonging to the
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:get_all_ports() called\n")
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):
"""
Creates a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:create_port() 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]
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)
return port_binding
def delete_port(self, tenant_id, net_id, port_id, **kwargs):
"""
Deletes a port on a specified Virtual Network,
if the port contains a remote interface attachment,
the remote interface should first be un-plugged and
then the port can be deleted.
"""
LOG.debug("UCSVICPlugin:delete_port() called\n")
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, **kwargs):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:update_port() called\n")
self._set_ucsm(kwargs[const.DEVICE_IP])
pass
def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
"""
This method allows the user to retrieve a remote interface
that is attached to this particular port.
"""
LOG.debug("UCSVICPlugin:get_port_details() called\n")
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):
"""
Attaches a remote interface to the specified port on the
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:plug_interface() called\n")
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._driver.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
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):
"""
Detaches a remote interface from the specified port on the
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:unplug_interface() called\n")
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._driver.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
return udb.update_portbinding(port_id, vlan_name=new_vlan_name,
vlan_id=conf.DEFAULT_VLAN_ID)
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
def detach_port(self, tenant_id, instance_id, instance_desc, **kwargs):
"""
Remove the association of the VIF with the dynamic vnic
"""
LOG.debug("detach_port() called\n")
port_id = kwargs[const.PORTID]
kwargs.pop(const.PORTID)
return self.unplug_interface(tenant_id, None, port_id, **kwargs)
def _get_profile_name(self, port_id):
"""Returns the port profile name based on the port UUID"""
profile_name = conf.PROFILE_NAME_PREFIX + cutil.get16ByteUUID(port_id)
return profile_name
def _get_vlan_name_for_network(self, tenant_id, network_id):
"""Return the VLAN name as set by the L2 network plugin"""
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"""
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):
"""Create port profile in UCSM"""
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._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,
const.PROFILE_VLAN_NAME: vlan_name,
const.PROFILE_VLAN_ID: vlan_id}
return new_port_profile
def _delete_port_profile(self, port_id, profile_name):
"""Delete port profile in UCSM"""
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.get_username(conf.UCSM_IP_ADDRESS)
self._ucsm_password = cred.Store.get_password(conf.UCSM_IP_ADDRESS)