Merging Cisco's contribution to Quantum. Thanks to various folks at Cisco Systems, Quantum will have plugins

to integrate with Cisco UCS blade servers using 802.1Qbh, Cisco Nexus family of switches and the ability for
Quantum plugin to have multiple switches/devices within a single Quantum plugin.

 

added:
  quantum/plugins/cisco/
  quantum/plugins/cisco/README
  quantum/plugins/cisco/__init__.py
  quantum/plugins/cisco/common/
  quantum/plugins/cisco/common/__init__.py
  quantum/plugins/cisco/common/cisco_configparser.py
  quantum/plugins/cisco/common/cisco_constants.py
  quantum/plugins/cisco/common/cisco_credentials.py
  quantum/plugins/cisco/common/cisco_exceptions.py
  quantum/plugins/cisco/common/cisco_nova_configuration.py
  quantum/plugins/cisco/common/cisco_utils.py
  quantum/plugins/cisco/conf/
  quantum/plugins/cisco/conf/credentials.ini
  quantum/plugins/cisco/conf/l2network_plugin.ini
  quantum/plugins/cisco/conf/nexus.ini
  quantum/plugins/cisco/conf/nova.ini
  quantum/plugins/cisco/conf/plugins.ini
  quantum/plugins/cisco/conf/ucs.ini
  quantum/plugins/cisco/db/
  quantum/plugins/cisco/db/__init__.py
  quantum/plugins/cisco/l2device_plugin_base.py
  quantum/plugins/cisco/l2network_model.py
  quantug/plugins/cisco/l2network_model_base.py
  quantum/plugins/cisco/l2network_plugin.py
  quantum/plugins/cisco/l2network_plugin_configuration.py
  quantum/plugins/cisco/nexus/
  quantum/plugins/cisco/nexus/__init__.py
  quantum/plugins/cisco/nexus/cisco_nexus_configuration.py
  quantum/plugins/cisco/nexus/cisco_nexus_network_driver.py
  quantum/plugins/cisco/nexus/cisco_nexus_plugin.py
  quantum/plugins/cisco/run_tests.py
  quantum/plugins/cisco/tests/
  quantum/plugins/cisco/tests/__init__.py
  quantum/plugins/cisco/tests/unit/
  quantum/plugins/cisco/tests/unit/__init__.py
  quantum/plugins/cisco/tests/unit/test_l2networkApi.py
  quantum/plugins/cisco/tests/unit/test_nexus_plugin.py
  quantum/plugins/cisco/tests/unit/test_ucs_driver.py
  quantum/plugins/cisco/tests/unit/test_ucs_plugin.py
  quantum/plugins/cisco/ucs/
  quantum/plugins/cisco/ucs/__init__.py
  quantum/plugins/cisco/ucs/cisco_getvif.py
  quantum/plugins/cisco/ucs/cisco_ucs_configuration.py
  quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py
  quantum/plugins/cisco/ucs/cisco_ucs_plugin.py
pending merges:
  Sumit Naiksatam 2011-08-15 Changes in the README file to incorporate Somik's comments.
    Edgar Magana 2011-08-15 Removing extra testing function on Nexus Driver
    Sumit Naiksatam 2011-08-13 Removed main from modules as per review comments.
    Sumit Naiksatam 2011-08-09 Tiny change to the README file, instructions on how to get ncclient.
    Sumit Naiksatam 2011-08-08 Changed to default plugin class name.
    Sumit Naiksatam 2011-08-08 [merge] Added tests directory to list of modules in the README file.
    Edgar Magana 2011-08-08 Adding the required build for Nexus support
    Sumit Naiksatam 2011-08-08 [merge] Merge latest from lp:quantum
    Sumit Naiksatam 2011-08-08 Added "tests" directory to the list modules in the README file.
    Sumit Naiksatam 2011-08-08 Fixed typo in README
    Sumit Naiksatam 2011-08-08 README file updates (pointer to Nova Cactus branch), and numerous other edits based on Mark's template.
    Sumit Naiksatam 2011-08-08 [merge] L2 Network Plugin Framework merge.
    Sumit Naiksatam 2011-08-08 Incorporated changes in response to review comments from Ram.
    Sumit Naiksatam 2011-08-08 Making a check for the presence of UCS/Nexus plugin (earlier it was not in certain cases). With this change, if th...
    Sumit Naiksatam 2011-08-08 [merge] Merging test cases from Shwetas' branch, and further modified README file.
    Sumit Naiksatam 2011-08-07 RHEL limitation updated.
    Sumit Naiksatam 2011-08-07 Changes to enhance L2 network plugin framework.
    Sumit Naiksatam 2011-08-06 Added QuantunPluginBase as the base class for the l2network_plugin.
    Sumit Naiksatam 2011-08-06 Generalized and put placeholders.
    Sumit Naiksatam 2011-08-05 Added info about ssh conf required for nexus switch.
    Sumit Naiksatam 2011-08-05 Nexus plugin classpath was incorrect, fixed it.
    Sumit Naiksatam 2011-08-05 [merge] Merge latest from lp:quantum (via lp:~cisco-openstack/quantum/l2network-plugin).
    Sumit Naiksatam 2011-08-05 Edits to reflect conf changes, made it easier to follow.
    Sumit Naiksatam 2011-08-05 Fixed issue with creating new port profiles (one configuration parameter got left out during the migration to the ...
    Sumit Naiksatam 2011-08-05 Fixes the broken call to second level of plugins. Renaming will work now.
    Sumit Naiksatam 2011-08-05 Loading of device-specific plugins and drivers is done dynamically by setting configuration.
    Sumit Naiksatam 2011-08-08 [merge] Merging the test framework from Shweta's branch.
    Shweta P 2011-08-08 Adding Unit Test Cases Now
    Shweta P 2011-08-08 Adding Cisco Unit Tests
    Sumit Naiksatam 2011-08-05 [merge] Merge from lp:quantum
    Edgar Magana 2011-08-04 [merge] Removing extra file in Nexus Driver
    Edgar Magana 2011-08-04 Removing extra file in Nexus Driver
    Sumit Naiksatam 2011-08-03 Removed quantum/plugins/cisco/db/ and quantum/cisco_extensions since these will be merged separately.
    Sumit Naiksatam 2011-08-03 Adding conf directory for configuration files.
    Sumit Naiksatam 2011-08-03 Fixed pep8 error.
    Sumit Naiksatam 2011-08-03 [merge] Merging changes.
    Edgar Magana 2011-08-03 Fixed an issue selecting the right port interface and also properly switching off the Nexus Interface
    Sumit Naiksatam 2011-08-03 [merge] Merging changes from lp:quantum
    Sumit Naiksatam 2011-08-02 [merge] Merging the port profile client name fix.
    Sumit Naiksatam 2011-08-02 Earlier fix resulted in a different issue (profile client name, was also being used as profile name, hence breaking).
    Sumit Naiksatam 2011-08-02 Truncated the port profile client name length to 16 characters (ucsm excepts max 17 chars).
    Edgar Magana 2011-08-01 Including a flag to activate the NX-OS driver
    Edgar Magana 2011-08-01 Adding the Nexus OS driver based on the new PlugIn structure
    rohitagarwalla 2011-07-31 [merge] persistence of l2network & ucs plugins using mysql
    rohitagarwalla 2011-07-31 [merge] merged the latest changes from plugin-framework branch - revision 39
    rohitagarwalla 2011-07-29 persistence of l2network & ucs plugins using mysql
    Sumit Naiksatam 2011-07-31 Adding a tests directory, this can be used for plugin-specific test cases.
    Sumit Naiksatam 2011-07-31 Including copyright info.
    Sumit Naiksatam 2011-07-31 For the modules to get added, missed in the earlier checkin.
    Sumit Naiksatam 2011-07-31 Changed the directory structure to a more organized one.
    Sumit Naiksatam 2011-07-31 [merge] Merging the latest changes from lp:quantum.
    Sumit Naiksatam 2011-07-28 Changed the param name "network-name" to "net-name" since the Quantum service expects the later.
    Sumit Naiksatam 2011-07-24 [merge] Merge changes from lp:quantum
    Ying Liu 2011-07-15 add extension code in.(last push does not include this directory.)
    Ying Liu 2011-07-15 add api extensions (including portprofiles resources and associate/disassociate actions.)
    Sumit Naiksatam 2011-07-15 [merge] Merge latest from lp:quantum (picking up API framework changes like quantum/manager.py and resource contro...
    Sumit Naiksatam 2011-07-14 Changes to support port-profile extension.
    Debo 2011-07-14 Very initial version of the nxos driver .... lets call it ver 0.0.1!
    rohitagarwalla 2011-07-13 Porting shell script get-vif.sh to python module get-vif.py for cisco ucsm module
    Sumit Naiksatam 2011-07-12 [merge] Merge pep8 changes
    Rick Clark 2011-07-08 minor pep8 fix.
    Sumit Naiksatam 2011-07-12 Required for recognizing the "cisco" package. Missed in the initial checkin.
    Sumit Naiksatam 2011-07-08 Changed some credentials (does not affect functionality).
    Sumit Naiksatam 2011-07-08 This file is not required.
    Sumit Naiksatam 2011-07-08 Initial checkin for the L2-Network Plugin with all the associated modules and artifacts.
This commit is contained in:
Somik Behera 2011-08-16 11:12:37 -07:00
commit 8e63fd5762
37 changed files with 4806 additions and 0 deletions

236
quantum/plugins/cisco/README Executable file
View File

@ -0,0 +1,236 @@
=====================================================================
README: A Framework for a Quantum Plugin Supporting Multiple Switches
=====================================================================
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri, Rohit Agarwalla, Ying Liu
:Contact: netstack@lists.launchpad.net
:Web site: https://launchpad.net/~cisco-openstack
:Copyright: 2011 Cisco Systems, Inc.
.. contents::
Introduction
------------
This plugin implementation provides the following capabilities
to help you take your Layer 2 network for a Quantum leap:
* A reference implementation a framework for a Quantum Plugin
to use multiple devices/switches in a L2 network
* Supports multiple models of switches concurrently
* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards
(aka "Palo adapters") via 802.1Qbh.
* Supports the Cisco Nexus family of switches.
It does not provide:
* A hologram of Al that only you can see.
* A map to help you find your way through time.
* A cure for amnesia or your swiss-cheesed brain.
Let's leap in!
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.)
* One or more UCS B200 series blade servers with M81KR VIC (aka
Palo adapters) installed.
* UCSM 2.0 (Capitola) Build 230 or above.
* OpenStack Cactus release installation (additional patch is required,
details follow in this document)
* RHEL 6.1 (as of this writing, UCS only officially supports RHEL, but
it should be noted that Ubuntu support is planned in coming releases as well)
** Package: python-configobj-4.6.0-3.el6.noarch (or newer)
** Package: python-routes-1.12.3-2.el6.noarch (or newer)
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
** To install on RHEL 6.1, run: yum install python-paramiko
* ncclient v0.3.1 - Python library for NETCONF clients
** RedHat does not provide a package for ncclient in RHEL 6.1. Here is how
to get it, from your shell prompt do:
git clone git@github.com:ddutta/ncclient.git
sudo python ./setup.py install
** For more information of ncclient, see:
http://schmizz.net/ncclient/
To verify the version of any package you have installed on your system,
run "rpm -qav | grep <package name>", where <package name> is the
package you want to query (for example: python-routes).
Note that you can get access to recent versions of the packages above
and other OpenStack software packages by adding a new repository to
your yum configuration. To do so, edit or create
/etc/yum.repos.d/openstack.repo and add the following:
[openstack-deps]
name=OpenStack Nova Compute Dependencies
baseurl=http://yum.griddynamics.net/yum/cactus/deps
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-OPENSTACK
Then run "yum install python-routes".
Module Structure:
-----------------
* quantum/plugins/cisco/ - Contains the L2-Network Plugin Framework
/common - Modules common to the entire plugin
/conf - All configuration files
/db - Persistence framework
/nexus - Nexus-specific modules
/tests - Tests specific to this plugin
/ucs - UCS-specific modules
Plugin Installation Instructions
----------------------------------
1. Make a backup copy of quantum/quantum/plugins.ini.
2. Edit quantum/quantum/plugins.ini and edit the "provider" entry to point
to the L2Network-plugin:
provider = quantum.plugins.cisco.l2network_plugin.L2Network
3. If you are not running Quantum on the same host as the OpenStack Cloud
Controller, you will need to change the db_ip_address configuration
in nova.ini.
4. If you want to turn on support for Cisco Nexus switches:
4a. Uncomment the nexus_plugin property in
quantum/plugins/cisco/conf/plugins.ini to read:
nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin
4b. Enter the relevant configuration in the
quantum/plugins/cisco/conf/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 number on the Nexus switch to which the UCSM 6120 is connected
# Use shortened interface syntax, e.g. "3/23" not "Ethernet3/23".
nexus_port=3/23
[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. Verify that you have the correct credentials for each IP address listed
in quantum/plugins/cisco/conf/credentials.ini. Example:
# Provide the UCSM credentials
# UCSM IP address, username and password.
[10.0.0.2]
username=admin
password=mySecretPasswordForUCSM
# Provide the Nova DB credentials.
# The IP address should be the same as in nova.ini.
[10.0.0.3]
username=nova
password=mySecretPasswordForNova
# Provide the Nexus credentials, if you are using Nexus switches.
# If not this will be ignored.
[10.0.0.1]
username=admin
password=mySecretPasswordForNexus
6. Start the Quantum service. If something doesn't work, verify that
your configuration of each of the above files hasn't gone a little kaka.
Once you've put right what once went wrong, leap on.
How to test the installation
----------------------------
The unit tests are located at quantum/plugins/cisco/tests/unit. They can be
executed from quantum/plugins/cisco/ using the run_tests.py script.
1. Testing the core API (without UCS/Nexus/RHEL hardware, and can be run on
Ubuntu):
First disable all device-specific plugins by commenting out the entries in:
quantum/plugins/cisco/conf/plugins.ini
Then run the test script:
python run_tests.py unit.test_l2networkApi
2. Specific Plugin unit test (needs environment setup as indicated in the
pre-requisites):
python run_tests.py unit.<name_of_the file>
E.g.:
python run_tests.py unit.test_ucs_plugin.py
3. All unit tests (needs environment setup as indicated in the pre-requisites):
python run_tests.py unit
Additional installation required on Nova Compute
------------------------------------------------
1. Create a table in the "nova" database for ports. This can be
accomplished with the following SQL statement:
CREATE TABLE ports (
port_id VARCHAR(255) PRIMARY KEY,
profile_name VARCHAR(255),
dynamic_vnic VARCHAR(255),
host VARCHAR(255),
instance_name VARCHAR(255),
instance_nic_name VARCHAR(255),
used TINYINT(1)
);
Assuming you're using MySQL, you can run the following command from a
shell prompt on the Cloud Controller node to create the table:
mysql -uroot -p nova -e 'create table ports (port_id VARCHAR(255) primary key, profile_name VARCHAR(255), dynamic_vnic VARCHAR(255), host VARCHAR(255), instance_name VARCHAR(255), instance_nic_name VARCHAR(255), used tinyint(1));'
You'll be prompted for a password.
2. A patch is available for the Cactus release in this branch:
https://code.launchpad.net/~snaiksat/quantum/cactus-ucs-support
replace the following file in your installation:
/usr/lib/python2.6/site-packages/nova/virt/libvirt_conn.py
with the file from the branch:
nova/virt/libvirt_conn.py
3. Add the following file from the Cisco Nova branch:
nova/virt/cisco_ucs.py
to:
/usr/lib/python2.6/site-packages/nova/virt/cisco_ucs.py
4. Add the 802.1Qbh specific libvirt template file, from:
nova/virt/libvirt-qbh.xml.template
to:
/usr/share/nova/libvirt-qbh.xml.template
5. Edit /etc/nova.conf to set the libvirt XML template to the above template:
--libvirt_xml_template=/usr/share/nova/libvirt-qbh.xml.template
6. Restart the nova-compute service.
(Note that the requirement for the above patch is temporary and will go away
with the integration with OpenStack Diablo. A 802.1Qbh-specific VIF driver
will be made available as per the specification here:
http://wiki.openstack.org/network-refactoring#VIF_driver)
Bingo bango bongo! That's it! Thanks for taking the leap into Quantum.
...Oh, boy!

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,40 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import logging as LOG
import os
from configobj import ConfigObj
from validate import Validator
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class CiscoConfigParser(ConfigObj):
def __init__(self, filename):
super(CiscoConfigParser, self).__init__(filename, raise_errors=True,
file_error=True)
def dummy(self, section, key):
return section[key]

View File

@ -0,0 +1,103 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
PLUGINS = 'PLUGINS'
PORT_STATE = 'port-state'
PORT_UP = "UP"
PORT_DOWN = "DOWN"
ATTACHMENT = 'attachment'
PORT_ID = 'port-id'
NET_ID = 'net-id'
NET_NAME = 'net-name'
NET_PORTS = 'net-ports'
NET_VLAN_NAME = 'net-vlan-name'
NET_VLAN_ID = 'net-vlan-id'
NET_TENANTS = 'net-tenants'
TENANT_ID = 'tenant-id'
TENANT_NETWORKS = 'tenant-networks'
TENANT_NAME = 'tenant-name'
TENANT_PORTPROFILES = 'tenant-portprofiles'
TENANT_QOS_LEVELS = 'tenant-qos-levels'
TENANT_CREDENTIALS = 'tenant-credentials'
PORT_PROFILE = 'port-profile'
PROFILE_ID = 'profile-id'
PROFILE_NAME = 'profile-name'
PROFILE_VLAN_NAME = 'profile-vlan-name'
PROFILE_VLAN_ID = 'vlan-id'
PROFILE_QOS = 'profile-qos'
PROFILE_ASSOCIATIONS = 'assignment'
QOS_LEVEL_ID = 'qos-id'
QOS_LEVEL_NAME = 'qos-name'
QOS_LEVEL_ASSOCIATIONS = 'qos-level-associations'
QOS_LEVEL_DESCRIPTION = 'qos-desc'
CREDENTIAL_ID = 'credential-id'
CREDENTIAL_NAME = 'credential-name'
CREDENTIAL_USERNAME = 'credential-username'
CREDENTIAL_PASSWORD = 'credential-password'
MASKED_PASSWORD = '********'
USERNAME = 'username'
PASSWORD = 'password'
LOGGER_COMPONENT_NAME = "cisco_plugin"
BLADE_INTF_DN = "blade-intf-distinguished-name"
BLADE_INTF_ORDER = "blade-intf-order"
BLADE_INTF_LINK_STATE = "blade-intf-link-state"
BLADE_INTF_OPER_STATE = "blade-intf-operational-state"
BLADE_INTF_INST_TYPE = "blade-intf-inst-type"
BLADE_INTF_RHEL_DEVICE_NAME = "blade-intf-rhel-device-name"
BLADE_INTF_DYNAMIC = "dynamic"
BLADE_INTF_STATE_UNKNOWN = "unknown"
BLADE_INTF_STATE_UNALLOCATED = "unallocated"
BLADE_INTF_RESERVED = "blade-intf-reserved"
BLADE_INTF_UNRESERVED = "blade-intf-unreserved"
BLADE_INTF_RESERVATION = "blade-intf-reservation-status"
BLADE_UNRESERVED_INTF_COUNT = "blade-unreserved-interfaces-count"
BLADE_INTF_DATA = "blade-intf-data"
LEAST_RSVD_BLADE_UCSM = "least-reserved-blade-ucsm"
LEAST_RSVD_BLADE_CHASSIS = "least-reserved-blade-chassis"
LEAST_RSVD_BLADE_ID = "least-reserved-blade-id"
LEAST_RSVD_BLADE_DATA = "least-reserved-blade-data"
RESERVED_NIC_HOSTNAME = "reserved-dynamic-nic-hostname"
RESERVED_NIC_NAME = "reserved-dynamic-nic-device-name"
RESERVED_INTERFACE_UCSM = "reserved-interface-ucsm-ip"
RESERVED_INTERFACE_CHASSIS = "reserved-interface-chassis"
RESERVED_INTERFACE_BLADE = "reserved-interface-blade"
RESERVED_INTERFACE_DN = "reserved-interface-dn"
RHEL_DEVICE_NAME_REPFIX = "eth"
UCS_PLUGIN = 'ucs_plugin'
NEXUS_PLUGIN = 'nexus_plugin'
PLUGIN_OBJ_REF = 'plugin-obj-ref'
PARAM_LIST = 'param-list'
DEVICE_IP = 'device-ip'

View File

@ -0,0 +1,60 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import logging as LOG
import os
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_configparser as confp
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
CREDENTIALS_FILE = "../conf/credentials.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CREDENTIALS_FILE)
_creds_dictionary = cp.walk(cp.dummy)
class Store(object):
@staticmethod
def putCredential(id, username, password):
_creds_dictionary[id] = {const.USERNAME: username,
const.PASSWORD: password}
@staticmethod
def getUsername(id):
return _creds_dictionary[id][const.USERNAME]
@staticmethod
def getPassword(id):
return _creds_dictionary[id][const.PASSWORD]
@staticmethod
def getCredential(id):
return _creds_dictionary[id]
@staticmethod
def getCredentials():
return _creds_dictionary
@staticmethod
def deleteCredential(id):
return _creds_dictionary.pop(id)

View File

@ -0,0 +1,57 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
Exceptions used by the Cisco plugin
"""
from quantum.common import exceptions
class NoMoreNics(exceptions.QuantumException):
message = _("Unable to complete operation on port %(port_id)s " \
"for network %(net_id)s. No more dynamic nics are available" \
"in the system.")
class PortProfileLimit(exceptions.QuantumException):
message = _("Unable to complete operation on port %(port_id)s " \
"for network %(net_id)s. The system has reached the maximum" \
"limit of allowed port profiles.")
class UCSMPortProfileLimit(exceptions.QuantumException):
message = _("Unable to complete operation on port %(port_id)s " \
"for network %(net_id)s. The system has reached the maximum" \
"limit of allowed UCSM port profiles.")
class NetworksLimit(exceptions.QuantumException):
message = _("Unable to create new network. Number of networks" \
"for the system has exceeded the limit")
class PortProfileNotFound(exceptions.QuantumException):
message = _("Port profile %(portprofile_id)s could not be found " \
"for tenant %(tenant_id)s")
class PortProfileInvalidDelete(exceptions.QuantumException):
message = _("Port profile %(profile_id)s could not be deleted " \
"for tenant %(tenant_id)s since port associations exist")

View File

@ -0,0 +1,35 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "../conf/nova.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
section = cp['NOVA']
DB_SERVER_IP = section['db_server_ip']
DB_NAME = section['db_name']
DB_USERNAME = section['db_username']
DB_PASSWORD = section['db_password']
NOVA_HOST_NAME = section['nova_host_name']
NOVA_PROJ_NAME = section['nova_proj_name']

View File

@ -0,0 +1,59 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import MySQLdb
import logging as LOG
import sys
import traceback
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 as cred
from quantum.plugins.cisco.common import cisco_nova_configuration as conf
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class DBUtils(object):
def __init__(self):
pass
def _get_db_connection(self):
db_ip = conf.DB_SERVER_IP
db_username = conf.DB_USERNAME
db_password = conf.DB_PASSWORD
self.db = MySQLdb.connect(db_ip, db_username, db_password,
conf.DB_NAME)
return self.db
def execute_db_query(self, sql_query):
db = self._get_db_connection()
cursor = db.cursor()
try:
cursor.execute(sql_query)
results = cursor.fetchall()
db.commit()
LOG.debug("DB query execution succeeded: %s" % sql_query)
except:
db.rollback()
LOG.debug("DB query execution failed: %s" % sql_query)
traceback.print_exc()
db.close()

View File

@ -0,0 +1,15 @@
#Provide the UCSM credentials
[<put_ucsm_ip_address_here>]
username=<put_user_name_here>
password=<put_password_here>
#Provide the Nova DB credentials, the IP address should be the same as in nova.ini
[<put_nova_db_ip_here>]
username=<put_user_name_here>
password=<put_password_here>
#Provide the Nexus credentials, if you are using Nexus
[<put_nexus_ip_address_here>]
username=<put_user_name_here>
password=<put_password_here>

View File

@ -0,0 +1,16 @@
[VLANS]
vlan_start=<put_vlan_id_range_start_here>
vlan_end=<put_vlan_id_range_end_here>
vlan_name_prefix=q-
[PORTS]
max_ports=100
[PORTPROFILES]
max_port_profiles=65568
[NETWORKS]
max_networks=65568
[MODEL]
model_class=quantum.plugins.cisco.l2network_model.L2NetworkModel

View File

@ -0,0 +1,8 @@
[SWITCH]
# Change the following to reflect the Nexus switch details
nexus_ip_address=<put_nexus_switch_ip_address_here>
#Port number of the Interface connected from the Nexus 7K Switch to UCSM 6120, e.g.: 3/23
nexus_port=<put_interface_name_here>
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver

View File

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

View File

@ -0,0 +1,3 @@
[PLUGINS]
ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin

View File

@ -0,0 +1,10 @@
[UCSM]
#change the following to the appropriate UCSM IP address
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

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,152 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import inspect
from abc import ABCMeta, abstractmethod
class L2DevicePluginBase(object):
__metaclass__ = ABCMeta
@abstractmethod
def get_all_networks(self, tenant_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
**kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def delete_network(self, tenant_id, net_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_network_details(self, tenant_id, net_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def rename_network(self, tenant_id, net_id, new_name, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_all_ports(self, tenant_id, net_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def delete_port(self, tenant_id, net_id, port_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
**kwargs):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
"""
:returns:
:raises:
"""
pass
@classmethod
def __subclasshook__(cls, klass):
"""
The __subclasshook__ method is a class method
that will be called everytime a class is tested
using issubclass(klass, Plugin).
In that case, it will check that every method
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is QuantumPluginBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:
if method in base.__dict__:
fn_obj = base.__dict__[method]
if inspect.isfunction(fn_obj):
abstract_fn_obj = cls.__dict__[method]
arg_count = fn_obj.func_code.co_argcount
expected_arg_count = \
abstract_fn_obj.func_code.co_argcount
method_ok = arg_count == expected_arg_count
if method_ok:
continue
return NotImplemented
return True
return NotImplemented

View File

@ -0,0 +1,100 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import inspect
import logging as LOG
from quantum.common import utils
from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_constants as const
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2NetworkModel(L2NetworkModelBase):
_plugins = {}
def __init__(self):
for key in conf.plugins[const.PLUGINS].keys():
self._plugins[key] = utils.import_object(
conf.plugins[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s\n" % \
conf.plugins[const.PLUGINS][key])
def _funcName(self, offset=0):
return inspect.stack()[1 + offset][3]
def _invokeAllDevicePlugins(self, function_name, args, kwargs):
for pluginObjRef in self._plugins.values():
getattr(pluginObjRef, function_name)(*args, **kwargs)
def _invokeUCSPlugin(self, function_name, args, kwargs):
if const.UCS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.UCS_PLUGIN],
function_name)(*args, **kwargs)
def _invokeNexusPlugin(self, function_name, args, kwargs):
if const.NEXUS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.NEXUS_PLUGIN],
function_name)(*args, **kwargs)
def get_all_networks(self, args):
pass
def create_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
def delete_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
def get_network_details(self, args):
pass
def rename_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
def get_all_ports(self, args):
pass
def create_port(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
def delete_port(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
def update_port(self, args):
pass
def get_port_details(self, args):
pass
def plug_interface(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
def unplug_interface(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)

View File

@ -0,0 +1,150 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import inspect
from abc import ABCMeta, abstractmethod
class L2NetworkModelBase(object):
__metaclass__ = ABCMeta
@abstractmethod
def get_all_networks(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def create_network(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def delete_network(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_network_details(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def rename_network(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_all_ports(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def create_port(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def delete_port(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def update_port(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def get_port_details(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def plug_interface(self, args):
"""
:returns:
:raises:
"""
pass
@abstractmethod
def unplug_interface(self, args):
"""
:returns:
:raises:
"""
pass
@classmethod
def __subclasshook__(cls, klass):
"""
The __subclasshook__ method is a class method
that will be called everytime a class is tested
using issubclass(klass, Plugin).
In that case, it will check that every method
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is QuantumPluginBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:
if method in base.__dict__:
fn_obj = base.__dict__[method]
if inspect.isfunction(fn_obj):
abstract_fn_obj = cls.__dict__[method]
arg_count = fn_obj.func_code.co_argcount
expected_arg_count = \
abstract_fn_obj.func_code.co_argcount
method_ok = arg_count == expected_arg_count
if method_ok:
continue
return NotImplemented
return True
return NotImplemented

View File

@ -0,0 +1,394 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import inspect
import logging as LOG
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.quantum_plugin_base import QuantumPluginBase
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2Network(QuantumPluginBase):
_networks = {}
_tenants = {}
_portprofiles = {}
def __init__(self):
self._net_counter = 0
self._portprofile_counter = 0
self._port_counter = 0
self._vlan_counter = int(conf.VLAN_START) - 1
self._model = utils.import_object(conf.MODEL_CLASS)
"""
Core API implementation
"""
def get_all_networks(self, tenant_id):
"""
Returns a dictionary containing all
<network_uuid, network_name> for
the specified tenant.
"""
LOG.debug("get_all_networks() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id])
return self._networks.values()
def create_network(self, tenant_id, net_name):
"""
Creates a new Virtual Network, and assigns it
a symbolic name.
"""
LOG.debug("create_network() called\n")
new_net_id = self._get_unique_net_id(tenant_id)
vlan_id = self._get_vlan_for_tenant(tenant_id, net_name)
vlan_name = self._get_vlan_name(new_net_id, str(vlan_id))
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_name,
new_net_id, vlan_name,
vlan_id])
new_net_dict = {const.NET_ID: new_net_id,
const.NET_NAME: net_name,
const.NET_PORTS: {},
const.NET_VLAN_NAME: vlan_name,
const.NET_VLAN_ID: vlan_id,
const.NET_TENANTS: [tenant_id]}
self._networks[new_net_id] = new_net_dict
tenant = self._get_tenant(tenant_id)
tenant_networks = tenant[const.TENANT_NETWORKS]
tenant_networks[new_net_id] = new_net_dict
return new_net_dict
def delete_network(self, tenant_id, net_id):
"""
Deletes the network with the specified network identifier
belonging to the specified tenant.
"""
LOG.debug("delete_network() called\n")
net = self._networks.get(net_id)
if net:
if len(net[const.NET_PORTS].values()) > 0:
ports_on_net = net[const.NET_PORTS].values()
for port in ports_on_net:
if port[const.ATTACHMENT]:
raise exc.NetworkInUse(net_id=net_id)
for port in ports_on_net:
self.delete_port(tenant_id, net_id, port[const.PORT_ID])
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id])
self._networks.pop(net_id)
tenant = self._get_tenant(tenant_id)
tenant_networks = tenant[const.TENANT_NETWORKS]
tenant_networks.pop(net_id)
return net
# Network not found
raise exc.NetworkNotFound(net_id=net_id)
def get_network_details(self, tenant_id, net_id):
"""
Gets the details of a particular network
"""
LOG.debug("get_network_details() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id])
network = self._get_network(tenant_id, net_id)
ports_on_net = network[const.NET_PORTS].values()
return {const.NET_ID: network[const.NET_ID],
const.NET_NAME: network[const.NET_NAME],
const.NET_PORTS: ports_on_net}
def rename_network(self, tenant_id, net_id, new_name):
"""
Updates the symbolic name belonging to a particular
Virtual Network.
"""
LOG.debug("rename_network() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
new_name])
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_name
return network
def get_all_ports(self, tenant_id, net_id):
"""
Retrieves all port identifiers belonging to the
specified Virtual Network.
"""
LOG.debug("get_all_ports() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id])
network = self._get_network(tenant_id, net_id)
ports_on_net = network[const.NET_PORTS].values()
return ports_on_net
def create_port(self, tenant_id, net_id, port_state=None):
"""
Creates a port on the specified Virtual Network.
"""
LOG.debug("create_port() called\n")
net = self._get_network(tenant_id, net_id)
ports = net[const.NET_PORTS]
unique_port_id_string = self._get_unique_port_id(tenant_id, net_id)
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_state,
unique_port_id_string])
new_port_dict = {const.PORT_ID: unique_port_id_string,
const.PORT_STATE: const.PORT_UP,
const.ATTACHMENT: None}
ports[unique_port_id_string] = new_port_dict
return new_port_dict
def delete_port(self, tenant_id, net_id, port_id):
"""
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("delete_port() called\n")
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
try:
#TODO (Sumit): Before deleting port profile make sure that there
# is no VM using this port profile
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_id])
net = self._get_network(tenant_id, net_id)
net[const.NET_PORTS].pop(port_id)
except KeyError:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
def update_port(self, tenant_id, net_id, port_id, port_state):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("update_port() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_id, port_state])
port = self._get_port(tenant_id, net_id, port_id)
self._validate_port_state(port_state)
port[const.PORT_STATE] = port_state
return port
def get_port_details(self, tenant_id, net_id, port_id):
"""
This method allows the user to retrieve a remote interface
that is attached to this particular port.
"""
LOG.debug("get_port_details() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_id])
return self._get_port(tenant_id, net_id, port_id)
def plug_interface(self, tenant_id, net_id, port_id,
remote_interface_id):
"""
Attaches a remote interface to the specified port on the
specified Virtual Network.
"""
LOG.debug("plug_interface() called\n")
self._validate_attachment(tenant_id, net_id, port_id,
remote_interface_id)
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_id,
remote_interface_id])
port[const.ATTACHMENT] = remote_interface_id
def unplug_interface(self, tenant_id, net_id, port_id):
"""
Detaches a remote interface from the specified port on the
specified Virtual Network.
"""
LOG.debug("unplug_interface() called\n")
port = self._get_port(tenant_id, net_id, port_id)
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
port_id])
port[const.ATTACHMENT] = None
"""
Extension API implementation
"""
def get_all_portprofiles(self, tenant_id):
return self._portprofiles.values()
def get_portprofile_details(self, tenant_id, profile_id):
return self._get_portprofile(tenant_id, profile_id)
def create_portprofile(self, tenant_id, profile_name, vlan_id):
profile_id = self._get_unique_profile_id(tenant_id)
new_port_profile_dict = {const.PROFILE_ID: profile_id,
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: [],
const.PROFILE_VLAN_ID: vlan_id,
const.PROFILE_QOS: None}
self._portprofiles[profile_id] = new_port_profile_dict
tenant = self._get_tenant(tenant_id)
portprofiles = tenant[const.TENANT_PORTPROFILES]
portprofiles[profile_id] = new_port_profile_dict
return new_port_profile_dict
def delete_portprofile(self, tenant_id, profile_id):
portprofile = self._get_portprofile(tenant_id, profile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
if len(associations) > 0:
raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id,
profile_id=profile_id)
else:
self._portprofiles.pop(profile_id)
tenant = self._get_tenant(tenant_id)
tenant[const.TENANT_PORTPROFILES].pop(profile_id)
def rename_portprofile(self, tenant_id, profile_id, new_name):
portprofile = self._get_portprofile(tenant_id, profile_id)
portprofile[const.PROFILE_NAME] = new_name
return portprofile
def associate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
portprofile = self._get_portprofile(tenant_id, portprofile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
associations.append(port_id)
def disassociate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
portprofile = self._get_portprofile(tenant_id, portprofile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
associations.remove(port_id)
def create_defaultPProfile(self, tenant_id, network_id, profile_name,
vlan_id):
pass
"""
Private functions
"""
def _invokeDevicePlugins(self, function_name, args):
"""
All device-specific calls are delegate to the model
"""
getattr(self._model, function_name)(args)
def _get_vlan_for_tenant(self, tenant_id, net_name):
# TODO (Sumit):
# The VLAN ID for a tenant might need to be obtained from
# somewhere (from Donabe/Melange?)
# Also need to make sure that the VLAN ID is not being used already
# Currently, just a wrap-around counter ranging from VLAN_START to
# VLAN_END
self._vlan_counter += 1
self._vlan_counter %= int(conf.VLAN_END)
if self._vlan_counter < int(conf.VLAN_START):
self._vlan_counter = int(conf.VLAN_START)
return self._vlan_counter
def _get_vlan_name(self, net_id, vlan):
vlan_name = conf.VLAN_NAME_PREFIX + net_id + "-" + vlan
return vlan_name
def _validate_port_state(self, port_state):
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
network = self._get_network(tenant_id, network_id)
for port in network[const.NET_PORTS].values():
if port[const.ATTACHMENT] == remote_interface_id:
raise exc.AlreadyAttached(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT],
att_port_id=port[const.PORT_ID])
def _get_network(self, tenant_id, network_id):
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network
def _get_tenant(self, tenant_id):
tenant = self._tenants.get(tenant_id)
if not tenant:
LOG.debug("Creating new tenant record with tenant id %s\n" %
tenant_id)
tenant = {const.TENANT_ID: tenant_id,
const.TENANT_NAME: tenant_id,
const.TENANT_NETWORKS: {},
const.TENANT_PORTPROFILES: {}}
self._tenants[tenant_id] = tenant
return tenant
def _get_port(self, tenant_id, network_id, port_id):
net = self._get_network(tenant_id, network_id)
port = net[const.NET_PORTS].get(port_id)
if not port:
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
return port
def _get_portprofile(self, tenant_id, portprofile_id):
portprofile = self._portprofiles.get(portprofile_id)
if not portprofile:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
return portprofile
def _get_unique_net_id(self, tenant_id):
self._net_counter += 1
self._net_counter %= int(conf.MAX_NETWORKS)
id = tenant_id[:3] + \
"-n-" + ("0" * (6 - len(str(self._net_counter)))) + \
str(self._net_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
def _get_unique_port_id(self, tenant_id, net_id):
self._port_counter += 1
self._port_counter %= int(conf.MAX_PORTS)
id = net_id + "-p-" + str(self._port_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
def _get_unique_profile_id(self, tenant_id):
self._portprofile_counter += 1
self._portprofile_counter %= int(conf.MAX_PORT_PROFILES)
id = tenant_id[:3] + "-pp-" + \
("0" * (6 - len(str(self._net_counter)))) \
+ str(self._portprofile_counter)
# TODO (Sumit): Need to check if the ID has already been allocated
# ID will be generated by DB
return id
def _funcName(self, offset=0):
return inspect.stack()[1 + offset][3]
"""
TODO (Sumit):
(1) Persistent storage
"""

View File

@ -0,0 +1,50 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "conf/l2network_plugin.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
section = cp['VLANS']
VLAN_NAME_PREFIX = section['vlan_name_prefix']
VLAN_START = section['vlan_start']
VLAN_END = section['vlan_end']
section = cp['PORTS']
MAX_PORTS = section['max_ports']
section = cp['PORTPROFILES']
MAX_PORT_PROFILES = section['max_port_profiles']
section = cp['NETWORKS']
MAX_NETWORKS = section['max_networks']
section = cp['MODEL']
MODEL_CLASS = section['model_class']
CONF_FILE = "conf/plugins.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
plugins = cp.walk(cp.dummy)

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,35 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc.
#
import os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "../conf/nexus.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
section = cp['SWITCH']
NEXUS_IP_ADDRESS = section['nexus_ip_address']
NEXUS_PORT = section['nexus_port']
section = cp['DRIVER']
NEXUS_DRIVER = section['name']

View File

@ -0,0 +1,226 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Debojyoti Dutta, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems Inc.
#
"""
Implements a Nexus-OS NETCONF over SSHv2 API Client
"""
import logging as LOG
import string
import subprocess
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from ncclient import manager
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
# The following are standard strings, messages used to communicate with Nexus,
#only place holder values change for each message
exec_conf_prefix = """
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<configure xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli">
<__XML__MODE__exec_configure>
"""
exec_conf_postfix = """
</__XML__MODE__exec_configure>
</configure>
</config>
"""
cmd_vlan_conf_snippet = """
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
<__XML__MODE_vlan>
<name>
<vlan-name>%s</vlan-name>
</name>
<state>
<vstate>active</vstate>
</state>
<no>
<shutdown/>
</no>
</__XML__MODE_vlan>
</vlan-id-create-delete>
</vlan>
"""
cmd_no_vlan_conf_snippet = """
<no>
<vlan>
<vlan-id-create-delete>
<__XML__PARAM_value>%s</__XML__PARAM_value>
</vlan-id-create-delete>
</vlan>
</no>
"""
cmd_vlan_int_snippet = """
<interface>
<ethernet>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport></switchport>
<switchport>
<trunk>
<allowed>
<vlan>
<__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans>
<allow-vlans>%s</allow-vlans>
</__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans>
</vlan>
</allowed>
</trunk>
</switchport>
</__XML__MODE_if-ethernet-switch>
</ethernet>
</interface>
"""
cmd_port_trunk = """
<interface>
<ethernet>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport></switchport>
<switchport>
<mode>
<trunk>
</trunk>
</mode>
</switchport>
</__XML__MODE_if-ethernet-switch>
</ethernet>
</interface>
"""
cmd_no_switchport = """
<interface>
<ethernet>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<no>
<switchport>
</switchport>
</no>
</__XML__MODE_if-ethernet-switch>
</ethernet>
</interface>
"""
cmd_no_vlan_int_snippet = """
<interface>
<ethernet>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport></switchport>
<no>
<switchport>
<trunk>
<allowed>
<vlan>
<__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans>
<allow-vlans>%s</allow-vlans>
</__XML__BLK_Cmd_switchport_trunk_allowed_allow-vlans>
</vlan>
</allowed>
</trunk>
</switchport>
</no>
</__XML__MODE_if-ethernet-switch>
</ethernet>
</interface>
"""
filter_show_vlan_brief_snippet = """
<show xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli">
<vlan>
<brief/>
</vlan>
</show> """
class CiscoNEXUSDriver():
def __init__(self):
pass
def nxos_connect(self, nexus_host, port, nexus_user, nexus_password):
m = manager.connect(host=nexus_host, port=22, username=nexus_user,
password=nexus_password)
return m
def enable_vlan(self, mgr, vlanid, vlanname):
confstr = cmd_vlan_conf_snippet % (vlanid, vlanname)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
mgr.edit_config(target='running', config=confstr)
def disable_vlan(self, mgr, vlanid):
confstr = cmd_no_vlan_conf_snippet % vlanid
confstr = exec_conf_prefix + confstr + exec_conf_postfix
mgr.edit_config(target='running', config=confstr)
def enable_port_trunk(self, mgr, interface):
confstr = cmd_port_trunk % (interface)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
print confstr
mgr.edit_config(target='running', config=confstr)
def disable_switch_port(self, mgr, interface):
confstr = cmd_no_switchport % (interface)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
print confstr
mgr.edit_config(target='running', config=confstr)
def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
confstr = cmd_vlan_int_snippet % (interface, vlanid)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
print confstr
mgr.edit_config(target='running', config=confstr)
def disable_vlan_on_trunk_int(self, mgr, interface, vlanid):
confstr = cmd_no_vlan_int_snippet % (interface, vlanid)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
print confstr
mgr.edit_config(target='running', config=confstr)
def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
nexus_password, nexus_interface):
#TODO (Edgar) Move the SSH port to the configuration file
with self.nxos_connect(nexus_host, 22, nexus_user,
nexus_password) as m:
self.enable_vlan(m, vlan_id, vlan_name)
self.enable_vlan_on_trunk_int(m, nexus_interface, vlan_id)
def delete_vlan(self, vlan_id, nexus_host, nexus_user,
nexus_password, nexus_interface):
with self.nxos_connect(nexus_host, 22, nexus_user,
nexus_password) as m:
self.disable_vlan(m, vlan_id)
self.disable_switch_port(m, nexus_interface)

View File

@ -0,0 +1,168 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc.
#
import logging as LOG
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_utils as cutil
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.nexus import cisco_nexus_configuration as conf
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class NexusPlugin(L2DevicePluginBase):
_networks = {}
def __init__(self):
self._client = utils.import_object(conf.NEXUS_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER)
#TODO (Edgar) Using just one Nexus 7K Switch and Port
self._nexus_ip = conf.NEXUS_IP_ADDRESS
self._nexus_username = cred.Store.getUsername(conf.NEXUS_IP_ADDRESS)
self._nexus_password = cred.Store.getPassword(conf.NEXUS_IP_ADDRESS)
self._nexus_port = conf.NEXUS_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_port)
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")
net = self._networks.get(net_id)
vlan_id = self._get_vlan_id_for_network(tenant_id, net_id)
if net:
self._client.delete_vlan(str(vlan_id), self._nexus_ip,
self._nexus_username, self._nexus_password, self._nexus_port)
self._networks.pop(net_id)
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 rename_network(self, tenant_id, net_id, new_name, **kwargs):
"""
Updates the symbolic name belonging to a particular
Virtual Network.
"""
#TODO (Edgar) We need to add an update method in the Nexus Driver
LOG.debug("NexusPlugin:rename_network() called\n")
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_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):
net = self._get_network(tenant_id, network_id)
vlan_id = net[const.NET_VLAN_ID]
return vlan_id
def _get_network(self, tenant_id, network_id):
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network

View File

@ -0,0 +1,301 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 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.
# Colorizer Code is borrowed from Twisted:
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""Unittest runner for quantum
To run all test::
python run_tests.py
To run all unit tests::
python run_tests.py unit
To run all functional tests::
python run_tests.py functional
To run a single unit test::
python run_tests.py unit.test_stores:TestSwiftBackend.test_get
To run a single functional test::
python run_tests.py functional.test_service:TestController.test_create
To run a single unit test module::
python run_tests.py unit.test_stores
To run a single functional test module::
python run_tests.py functional.test_stores
"""
import gettext
import logging
import os
import unittest
import sys
from nose import config
from nose import result
from nose import core
class _AnsiColorizer(object):
"""
A colorizer is an object that loosely wraps around a stream, allowing
callers to write text to the stream in a particular color.
Colorizer classes must implement C{supported()} and C{write(text, color)}.
"""
_colors = dict(black=30, red=31, green=32, yellow=33,
blue=34, magenta=35, cyan=36, white=37)
def __init__(self, stream):
self.stream = stream
def supported(cls, stream=sys.stdout):
"""
A class method that returns True if the current platform supports
coloring terminal output using this method. Returns False otherwise.
"""
if not stream.isatty():
return False # auto color only on TTYs
try:
import curses
except ImportError:
return False
else:
try:
try:
return curses.tigetnum("colors") > 2
except curses.error:
curses.setupterm()
return curses.tigetnum("colors") > 2
except:
raise
# guess false in case of error
return False
supported = classmethod(supported)
def write(self, text, color):
"""
Write the given text to the stream in the given color.
@param text: Text to be written to the stream.
@param color: A string label for a color. e.g. 'red', 'white'.
"""
color = self._colors[color]
self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
class _Win32Colorizer(object):
"""
See _AnsiColorizer docstring.
"""
def __init__(self, stream):
from win32console import GetStdHandle, STD_OUT_HANDLE, \
FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
FOREGROUND_INTENSITY
red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
FOREGROUND_BLUE, FOREGROUND_INTENSITY)
self.stream = stream
self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
self._colors = {
'normal': red | green | blue,
'red': red | bold,
'green': green | bold,
'blue': blue | bold,
'yellow': red | green | bold,
'magenta': red | blue | bold,
'cyan': green | blue | bold,
'white': red | green | blue | bold}
def supported(cls, stream=sys.stdout):
try:
import win32console
screenBuffer = win32console.GetStdHandle(
win32console.STD_OUT_HANDLE)
except ImportError:
return False
import pywintypes
try:
screenBuffer.SetConsoleTextAttribute(
win32console.FOREGROUND_RED |
win32console.FOREGROUND_GREEN |
win32console.FOREGROUND_BLUE)
except pywintypes.error:
return False
else:
return True
supported = classmethod(supported)
def write(self, text, color):
color = self._colors[color]
self.screenBuffer.SetConsoleTextAttribute(color)
self.stream.write(text)
self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
class _NullColorizer(object):
"""
See _AnsiColorizer docstring.
"""
def __init__(self, stream):
self.stream = stream
def supported(cls, stream=sys.stdout):
return True
supported = classmethod(supported)
def write(self, text, color):
self.stream.write(text)
class QuantumTestResult(result.TextTestResult):
def __init__(self, *args, **kw):
result.TextTestResult.__init__(self, *args, **kw)
self._last_case = None
self.colorizer = None
# NOTE(vish, tfukushima): reset stdout for the terminal check
stdout = sys.__stdout__
for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
if colorizer.supported():
self.colorizer = colorizer(self.stream)
break
sys.stdout = stdout
def getDescription(self, test):
return str(test)
# NOTE(vish, tfukushima): copied from unittest with edit to add color
def addSuccess(self, test):
unittest.TestResult.addSuccess(self, test)
if self.showAll:
self.colorizer.write("OK", 'green')
self.stream.writeln()
elif self.dots:
self.stream.write('.')
self.stream.flush()
# NOTE(vish, tfukushima): copied from unittest with edit to add color
def addFailure(self, test, err):
unittest.TestResult.addFailure(self, test, err)
if self.showAll:
self.colorizer.write("FAIL", 'red')
self.stream.writeln()
elif self.dots:
self.stream.write('F')
self.stream.flush()
# NOTE(vish, tfukushima): copied from unittest with edit to add color
def addError(self, test, err):
"""Overrides normal addError to add support for errorClasses.
If the exception is a registered class, the error will be added
to the list for that class, not errors.
"""
stream = getattr(self, 'stream', None)
ec, ev, tb = err
try:
exc_info = self._exc_info_to_string(err, test)
except TypeError:
# This is for compatibility with Python 2.3.
exc_info = self._exc_info_to_string(err)
for cls, (storage, label, isfail) in self.errorClasses.items():
if result.isclass(ec) and issubclass(ec, cls):
if isfail:
test.passwd = False
storage.append((test, exc_info))
# Might get patched into a streamless result
if stream is not None:
if self.showAll:
message = [label]
detail = result._exception_details(err[1])
if detail:
message.append(detail)
stream.writeln(": ".join(message))
elif self.dots:
stream.write(label[:1])
return
self.errors.append((test, exc_info))
test.passed = False
if stream is not None:
if self.showAll:
self.colorizer.write("ERROR", 'red')
self.stream.writeln()
elif self.dots:
stream.write('E')
def startTest(self, test):
unittest.TestResult.startTest(self, test)
current_case = test.test.__class__.__name__
if self.showAll:
if current_case != self._last_case:
self.stream.writeln(current_case)
self._last_case = current_case
self.stream.write(
' %s' % str(test.test._testMethodName).ljust(60))
self.stream.flush()
class QuantumTestRunner(core.TextTestRunner):
def _makeResult(self):
return QuantumTestResult(self.stream,
self.descriptions,
self.verbosity,
self.config)
if __name__ == '__main__':
# Set up test logger.
logger = logging.getLogger()
hdlr = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.DEBUG)
working_dir = os.path.abspath("tests")
c = config.Config(stream=sys.stdout,
env=os.environ,
verbosity=3,
workingDir=working_dir)
runner = QuantumTestRunner(stream=c.stream,
verbosity=c.verbosity,
config=c)
sys.exit(not core.run(config=c, testRunner=runner))

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 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.
# See http://code.google.com/p/python-nose/issues/detail?id=373
# The code below enables nosetests to work with i18n _() blocks
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,892 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Shweta Padubidri, Cisco Systems, Inc.
#
import logging
import unittest
from quantum.common import exceptions as exc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco import l2network_plugin
from quantum.plugins.cisco import l2network_plugin_configuration as conf
LOG = logging.getLogger('quantum.tests.test_core_api_func')
class CoreAPITestFunc(unittest.TestCase):
def test_create_network(self, net_tenant_id=None, net_name=None):
"""
Tests creation of new Virtual Network.
"""
LOG.debug("test_create_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if net_name:
network_name = net_name
else:
network_name = self.network_name
new_net_dict = self._l2network_plugin.create_network(
tenant_id, network_name)
self.assertEqual(new_net_dict[const.NET_NAME], network_name)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_create_network - END")
def test_delete_network(self, net_tenant_id=None):
"""
Tests deletion of a Virtual Network.
"""
LOG.debug("test_delete_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
delete_net_dict = self._l2network_plugin.delete_network(
tenant_id, new_net_dict[const.NET_ID])
self.assertEqual(
new_net_dict[const.NET_ID], delete_net_dict[const.NET_ID])
LOG.debug("test_delete_network - END")
def test_delete_networkDNE(self, net_tenant_id=None, net_id='0005'):
"""
Tests deletion of a Virtual Network when Network does not exist.
"""
LOG.debug("test_delete_network_not_found - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(
exc.NetworkNotFound, self._l2network_plugin.delete_network,
tenant_id, net_id)
LOG.debug("test_delete_network_not_found - END")
def test_delete_networkInUse(self, tenant_id='test_network'):
"""
Tests deletion of a Virtual Network when Network is in Use.
"""
LOG.debug("test_delete_networkInUse - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], self.remote_interface)
self.assertRaises(exc.NetworkInUse,
self._l2network_plugin.delete_network, tenant_id,
new_net_dict[const.NET_ID])
self.tearDownNetworkPortInterface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_delete_networkInUse - END")
def test_show_network(self, net_tenant_id=None):
"""
Tests display of details of a Virtual Network .
"""
LOG.debug("test_show_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
result_net_dict = self._l2network_plugin.get_network_details(
tenant_id, new_net_dict[const.NET_ID])
self.assertEqual(
new_net_dict[const.NET_ID], result_net_dict[const.NET_ID])
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_show_network - END")
def test_show_networkDNE(self, net_tenant_id=None, net_id='0005'):
"""
Tests display of a Virtual Network when Network does not exist.
"""
LOG.debug("test_show_network_not_found - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.get_network_details,
tenant_id, net_id)
LOG.debug("test_show_network_not_found - END")
def test_rename_network(self, net_tenant_id=None,
new_name='new_test_network'):
"""
Tests rename of a Virtual Network .
"""
LOG.debug("test_rename_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
rename_net_dict = self._l2network_plugin.rename_network(
tenant_id, new_net_dict[const.NET_ID], new_name)
self.assertEqual(new_name, rename_net_dict[const.NET_NAME])
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_rename_network - END")
def test_rename_networkDNE(self, net_tenant_id=None,
net_id='0005', new_name='new_test_network'):
"""
Tests rename of a Virtual Network when Network does not exist.
"""
LOG.debug("test_rename_network_not_found - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.rename_network,
tenant_id, net_id, new_name)
LOG.debug("test_rename_network_not_found - END")
def test_list_networks(self, tenant_id='test_network'):
"""
Tests listing of all the Virtual Networks .
"""
LOG.debug("test_list_networks - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
new_net_dict2 = self._l2network_plugin.create_network(
tenant_id, 'test_net2')
net_list = self._l2network_plugin.get_all_networks(tenant_id)
net_temp_list = [new_net_dict, new_net_dict2]
self.assertEqual(len(net_list), 2)
self.assertTrue(net_list[0] in net_temp_list)
self.assertTrue(net_list[1] in net_temp_list)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
self.tearDownNetwork(tenant_id, new_net_dict2[const.NET_ID])
LOG.debug("test_list_networks - END")
def test_list_ports(self, tenant_id='test_network'):
"""
Tests listing of all the Ports.
"""
LOG.debug("test_list_ports - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
port_dict2 = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
port_list = self._l2network_plugin.get_all_ports(
tenant_id, new_net_dict[const.NET_ID])
port_temp_list = [port_dict, port_dict2]
self.assertEqual(len(port_list), 2)
self.assertTrue(port_list[0] in port_temp_list)
self.assertTrue(port_list[1] in port_temp_list)
self.tearDownPortOnly(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict2[const.PORT_ID])
LOG.debug("test_list_ports - END")
def test_create_port(self, tenant_id='test_network',
port_state=const.PORT_UP):
"""
Tests creation of Ports.
"""
LOG.debug("test_create_port - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], port_state)
self.assertEqual(port_dict[const.PORT_STATE], port_state)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_create_port - END")
def test_create_port_network_DNE(
self, net_tenant_id=None, net_id='0005', port_state=const.PORT_UP):
"""
Tests creation of Ports when network does not exist.
"""
LOG.debug("test_create_port_network_DNE - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.create_port,
tenant_id, net_id, port_state)
LOG.debug("test_create_port_network_DNE - END:")
def test_delete_port(self, tenant_id='test_tenant',
port_state=const.PORT_UP):
"""
Tests deletion of Ports
"""
LOG.debug("test_delete_port - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], port_state)
delete_port_dict = self._l2network_plugin.delete_port(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
self.assertEqual(delete_port_dict, None)
LOG.debug("test_delete_port - END")
def test_delete_port_networkDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p0005'):
"""
Tests deletion of Ports when network does not exist.
"""
LOG.debug("test_delete_port_networkDNE - START")
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.delete_port, tenant_id,
net_id, port_id)
LOG.debug("test_delete_port_networkDNE - END")
def test_delete_portDNE(self, tenant_id='test_tenant', port_id='p0005'):
"""
Tests deletion of Ports when port does not exist.
"""
LOG.debug("test_delete_portDNE - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
self.assertRaises(exc.PortNotFound, self._l2network_plugin.delete_port,
tenant_id, new_net_dict[const.NET_ID], port_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_delete_portDNE - END")
def test_delete_portInUse(self, tenant_id='test_tenant'):
"""
Tests deletion of Ports when port is in Use.
"""
LOG.debug("test_delete_portInUse - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID],
self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], self.remote_interface)
self.assertRaises(exc.PortInUse,
self._l2network_plugin.delete_port, tenant_id,
new_net_dict[const.NET_ID], port_dict[const.PORT_ID])
self.tearDownNetworkPortInterface(
tenant_id, new_net_dict[const.NET_ID], port_dict[const.PORT_ID])
LOG.debug("test_delete_portInUse - END")
def test_update_port(self, tenant_id='test_tenant',
port_state=const.PORT_DOWN):
"""
Tests updation of Ports.
"""
LOG.debug("test_update_port - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
update_port_dict = self._l2network_plugin.update_port(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_state)
self.assertEqual(update_port_dict[const.PORT_STATE], port_state)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_update_port - END")
def test_update_port_networkDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p0005'):
"""
Tests updation of Ports when network does not exist.
"""
LOG.debug("test_update_port_networkDNE - START")
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.update_port, tenant_id,
net_id, port_id, self.port_state)
LOG.debug("test_update_port_networkDNE - END")
def test_update_portDNE(self, tenant_id='test_tenant', port_id='p0005'):
"""
Tests updation of Ports when port does not exist.
"""
LOG.debug("test_update_portDNE - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
self.assertRaises(
exc.PortNotFound, self._l2network_plugin.update_port, tenant_id,
new_net_dict[const.NET_ID], port_id, self.port_state)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_update_portDNE - END")
def test_show_port(self, tenant_id='test_tenant'):
"""
Tests display of Ports
"""
LOG.debug("test_show_port - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
get_port_dict = self._l2network_plugin.get_port_details(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(get_port_dict[const.PORT_STATE], self.port_state)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_show_port - END")
def test_show_port_networkDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p0005'):
"""
Tests display of Ports when network does not exist
"""
LOG.debug("test_show_port_networkDNE - START")
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.get_port_details,
tenant_id, net_id, port_id)
LOG.debug("test_show_port_networkDNE - END")
def test_show_portDNE(self, tenant_id='test_tenant', port_id='p0005'):
"""
Tests display of Ports when port does not exist
"""
LOG.debug("test_show_portDNE - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
self.assertRaises(exc.PortNotFound,
self._l2network_plugin.get_port_details, tenant_id,
new_net_dict[const.NET_ID], port_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_show_portDNE - END")
def test_plug_interface(self, tenant_id='test_tenant',
remote_interface='new_interface'):
"""
Tests attachment of interface to the port
"""
LOG.debug("test_plug_interface - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], remote_interface)
self.assertEqual(
self._l2network_plugin._networks[new_net_dict[const.NET_ID]]
[const.NET_PORTS][port_dict[const.PORT_ID]]
[const.ATTACHMENT], remote_interface)
self.tearDownNetworkPortInterface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_plug_interface - END")
def test_plug_interface_networkDNE(
self, tenant_id='test_tenant', net_id='0005',
port_id='p0005', remote_interface='new_interface'):
"""
Tests attachment of interface network does not exist
"""
LOG.debug("test_plug_interface_networkDNE - START")
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.plug_interface, tenant_id,
net_id, port_id, remote_interface)
LOG.debug("test_plug_interface_networkDNE - END")
def test_plug_interface_portDNE(self, tenant_id='test_tenant',
port_id='p0005',
remote_interface='new_interface'):
"""
Tests attachment of interface port does not exist
"""
LOG.debug("test_plug_interface_portDNE - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
self.assertRaises(
exc.PortNotFound, self._l2network_plugin.plug_interface, tenant_id,
new_net_dict[const.NET_ID], port_id, remote_interface)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_plug_interface_portDNE - END")
def test_plug_interface_portInUse(self, tenant_id='test_tenant',
remote_interface='new_interface'):
"""
Tests attachment of new interface to the port when there is an
existing attachment
"""
LOG.debug("test_plug_interface_portInUse - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], remote_interface)
self.assertRaises(exc.AlreadyAttached,
self._l2network_plugin.plug_interface, tenant_id,
new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], remote_interface)
self.tearDownNetworkPortInterface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_plug_interface_portInUse - END")
def test_unplug_interface(self, tenant_id='test_tenant'):
"""
Tests detaachment of an interface to a port
"""
LOG.debug("test_unplug_interface - START")
new_net_dict = self._l2network_plugin.create_network(
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID],
self.port_state)
self._l2network_plugin.plug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], self.remote_interface)
self._l2network_plugin.unplug_interface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(self._l2network_plugin._networks
[new_net_dict[const.NET_ID]][const.NET_PORTS]
[port_dict[const.PORT_ID]][const.ATTACHMENT], None)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_unplug_interface - END")
def test_unplug_interface_networkDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p0005'):
"""
Tests detaachment of an interface to a port, when the network does
not exist
"""
LOG.debug("test_unplug_interface_networkDNE - START")
self.assertRaises(exc.NetworkNotFound,
self._l2network_plugin.unplug_interface,
tenant_id, net_id, port_id)
LOG.debug("test_unplug_interface_networkDNE - END")
def test_unplug_interface_portDNE(self, tenant_id='test_tenant',
port_id='p0005'):
"""
Tests detaachment of an interface to a port, when the port does
not exist
"""
LOG.debug("test_unplug_interface_portDNE - START")
new_net_dict = self._l2network_plugin.create_network(tenant_id,
self.network_name)
self.assertRaises(exc.PortNotFound,
self._l2network_plugin.unplug_interface, tenant_id,
new_net_dict[const.NET_ID], port_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_unplug_interface_portDNE - END")
def test_create_portprofile(self, net_tenant_id=None,
net_profile_name=None, net_vlan_id=None):
"""
Tests creation of a port-profile
"""
LOG.debug("test_create_portprofile - tenant id: %s - START",
net_tenant_id)
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if net_profile_name:
profile_name = net_profile_name
else:
profile_name = self.profile_name
if net_vlan_id:
vlan_id = net_vlan_id
else:
vlan_id = self.vlan_id
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, profile_name, vlan_id)
port_profile_id = port_profile_dict['profile-id']
self.assertEqual(
self._l2network_plugin._portprofiles[port_profile_id]['vlan-id'],
vlan_id)
self.assertEqual(
self._l2network_plugin._portprofiles[port_profile_id]
['profile-name'], profile_name)
self.tearDownPortProfile(tenant_id, port_profile_id)
LOG.debug("test_create_portprofile - tenant id: %s - END",
net_tenant_id)
def test_delete_portprofile(self, net_tenant_id=None):
"""
Tests deletion of a port-profile
"""
LOG.debug("test_delete_portprofile - tenant id: %s - START",
net_tenant_id)
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id)
self.assertEqual(self._l2network_plugin._portprofiles, {})
LOG.debug("test_delete_portprofile - tenant id: %s - END",
net_tenant_id)
def test_delete_portprofileDNE(self, tenant_id='test_tenant',
profile_id='pr0005'):
"""
Tests deletion of a port-profile when netowrk does not exist
"""
LOG.debug("test_delete_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self._l2network_plugin.delete_portprofile,
tenant_id, profile_id)
LOG.debug("test_delete_portprofileDNE - END")
def test_delete_portprofileAssociated(self, tenant_id='test_tenant'):
"""
Tests deletion of an associatedport-profile
"""
LOG.debug("test_delete_portprofileAssociated - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
self._l2network_plugin.associate_portprofile(
tenant_id, self.net_id, self.port_id, port_profile_id)
self.assertRaises(cexc.PortProfileInvalidDelete,
self._l2network_plugin.delete_portprofile,
tenant_id, port_profile_id)
self.tearDownAssociatePortProfile(tenant_id, self.net_id,
self.port_id, port_profile_id)
LOG.debug("test_delete_portprofileAssociated - END")
def test_list_portprofile(self, tenant_id='test_tenant'):
"""
Tests listing of port-profiles
"""
LOG.debug("test_list_portprofile - tenant id: %s - START", tenant_id)
profile_name2 = tenant_id + '_port_profile2'
vlan_id2 = tenant_id + '201'
port_profile_dict1 = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_dict2 = self._l2network_plugin.create_portprofile(
tenant_id, profile_name2, vlan_id2)
port_profile_id1 = port_profile_dict1['profile-id']
port_profile_id2 = port_profile_dict2['profile-id']
list_all_portprofiles = self._l2network_plugin.get_all_portprofiles(
tenant_id)
self.assertEqual(self._l2network_plugin._portprofiles
[port_profile_id1]['vlan-id'], self.vlan_id)
self.assertEqual(self._l2network_plugin._portprofiles
[port_profile_id1]['profile-name'], self.profile_name)
self.assertEqual(self._l2network_plugin._portprofiles
[port_profile_id2]['vlan-id'], vlan_id2)
self.assertEqual(self._l2network_plugin._portprofiles
[port_profile_id2]['profile-name'], profile_name2)
LOG.debug("test_create_portprofile - tenant id: %s - END", tenant_id)
def test_show_portprofile(self, net_tenant_id=None):
"""
Tests display of a port-profile
"""
LOG.debug("test_show_portprofile - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
result_port_profile = self._l2network_plugin.get_portprofile_details(
tenant_id, port_profile_id)
self.assertEqual(result_port_profile[const.PROFILE_VLAN_ID],
self.vlan_id)
self.assertEqual(result_port_profile[const.PROFILE_NAME],
self.profile_name)
self.tearDownPortProfile(tenant_id, port_profile_id)
LOG.debug("test_show_portprofile - tenant id: %s - END", net_tenant_id)
def test_show_portprofileDNE(self, tenant_id='test_tenant',
profile_id='pr0005'):
"""
Tests display of a port-profile when network does not exist
"""
LOG.debug("test_show_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self._l2network_plugin.get_portprofile_details,
tenant_id, profile_id)
LOG.debug("test_show_portprofileDNE - END")
def test_rename_portprofile(self, tenant_id='test_tenant',
new_profile_name='new_profile_name'):
"""
Tests rename of a port-profile
"""
LOG.debug("test_rename_portprofile - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
result_port_profile_dict = self._l2network_plugin.rename_portprofile(
tenant_id, port_profile_id, new_profile_name)
self.assertEqual(result_port_profile_dict[const.PROFILE_NAME],
new_profile_name)
self.tearDownPortProfile(tenant_id, port_profile_id)
LOG.debug("test_show_portprofile - tenant id: %s - END")
def test_rename_portprofileDNE(self, tenant_id='test_tenant',
profile_id='pr0005',
new_profile_name='new_profile_name'):
"""
Tests rename of a port-profile when network does not exist
"""
LOG.debug("test_rename_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self._l2network_plugin.rename_portprofile,
tenant_id, profile_id, new_profile_name)
LOG.debug("test_rename_portprofileDNE - END")
def test_associate_portprofile(self, tenant_id='test_tenant',
net_id='0005', port_id='p00005'):
"""
Tests association of a port-profile
"""
LOG.debug("test_associate_portprofile - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
self._l2network_plugin.associate_portprofile(
tenant_id, net_id, port_id, port_profile_id)
self.assertEqual(
self._l2network_plugin._portprofiles[port_profile_id]
[const.PROFILE_ASSOCIATIONS][0], port_id)
self.tearDownAssociatePortProfile(tenant_id, net_id,
port_id, port_profile_id)
LOG.debug("test_associate_portprofile - END")
def test_associate_portprofileDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p00005',
profile_id='pr0005'):
"""
Tests association of a port-profile when a network does not exist
"""
LOG.debug("test_associate_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self._l2network_plugin.associate_portprofile,
tenant_id, net_id, port_id, profile_id)
LOG.debug("test_associate_portprofileDNE - END")
def test_disassociate_portprofile(self, tenant_id='test_tenant',
net_id='0005', port_id='p00005'):
"""
Tests disassociation of a port-profile
"""
LOG.debug("test_disassociate_portprofile - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
port_profile_id = port_profile_dict['profile-id']
self._l2network_plugin.associate_portprofile(tenant_id, net_id,
port_id, port_profile_id)
self._l2network_plugin.disassociate_portprofile(
tenant_id, net_id, port_id, port_profile_id)
self.assertEqual(self._l2network_plugin._portprofiles
[port_profile_id][const.PROFILE_ASSOCIATIONS], [])
self.tearDownPortProfile(tenant_id, port_profile_id)
LOG.debug("test_disassociate_portprofile - END")
def test_disassociate_portprofileDNE(self, tenant_id='test_tenant',
net_id='0005', port_id='p00005', profile_id='pr0005'):
"""
Tests disassociation of a port-profile when network does not exist
"""
LOG.debug("test_disassociate_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self._l2network_plugin.disassociate_portprofile,
tenant_id, net_id, port_id, profile_id)
LOG.debug("test_disassociate_portprofileDNE - END")
# def test_disassociate_portprofile_Unassociated
def test_get_tenant(self, net_tenant_id=None):
"""
Tests get tenant
"""
LOG.debug("test_get_tenant - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
tenant_dict = self._l2network_plugin._get_tenant(tenant_id)
self.assertEqual(tenant_dict[const.TENANT_ID], tenant_id)
self.assertEqual(tenant_dict[const.TENANT_NAME], tenant_id)
LOG.debug("test_get_tenant - END")
def test_get_vlan_name(self, net_tenant_id=None, vlan_name="NewVlan",
vlan_prefix=conf.VLAN_NAME_PREFIX):
"""
Tests get vlan name
"""
LOG.debug("test_get_vlan_name - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
result_vlan_name = self._l2network_plugin._get_vlan_name(tenant_id,
vlan_name)
expected_output = vlan_prefix + tenant_id + "-" + vlan_name
self.assertEqual(result_vlan_name, expected_output)
LOG.debug("test_get_vlan_name - END")
def test_validate_port_state(self, port_state=const.PORT_UP):
"""
Tests validate port state
"""
LOG.debug("test_validate_port_state - START")
result = self._l2network_plugin._validate_port_state(port_state)
self.assertEqual(result, True)
LOG.debug("test_validate_port_state - END")
def test_invalid_port_state(self, port_state="BADSTATE"):
"""
Tests invalidate port state
"""
LOG.debug("test_validate_port_state - START")
self.assertRaises(exc.StateInvalid,
self._l2network_plugin._validate_port_state,
port_state)
LOG.debug("test_validate_port_state - END")
def test_validate_attachment(self, net_tenant_id=None,
remote_interface_id="new_interface"):
"""
Tests validate attachment
"""
LOG.debug("test_validate_attachment - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
net_name = self.network_name
new_network_dict = self._l2network_plugin.create_network(tenant_id,
net_name)
network_id = new_network_dict[const.NET_ID]
new_port_dict = self._l2network_plugin.create_port(tenant_id,
network_id)
port_id = new_port_dict[const.PORT_ID]
self._l2network_plugin.plug_interface(
tenant_id, new_network_dict[const.NET_ID], port_id,
remote_interface_id)
self.assertRaises(exc.AlreadyAttached,
self._l2network_plugin._validate_attachment,
tenant_id, network_id, port_id, remote_interface_id)
self.tearDownNetworkPortInterface(
tenant_id, new_network_dict[const.NET_ID], port_id)
LOG.debug("test_validate_attachment - END")
def setUp(self):
self.tenant_id = "test_tenant"
self.network_name = "test_network"
self.profile_name = "test_tenant_port_profile"
self.vlan_id = "test_tenant_vlanid300"
self.port_state = const.PORT_UP
self.net_id = '00005'
self.port_id = 'p0005'
self.remote_interface = 'new_interface'
self._l2network_plugin = l2network_plugin.L2Network()
"""
Clean up functions after the tests
"""
def tearDownNetwork(self, tenant_id, network_dict_id):
self._l2network_plugin.delete_network(tenant_id, network_dict_id)
def tearDownPortOnly(self, tenant_id, network_dict_id, port_id):
self._l2network_plugin.delete_port(tenant_id, network_dict_id, port_id)
def tearDownNetworkPort(self, tenant_id, network_dict_id, port_id):
self._l2network_plugin.delete_port(tenant_id, network_dict_id, port_id)
self.tearDownNetwork(tenant_id, network_dict_id)
def tearDownNetworkPortInterface(self, tenant_id, network_dict_id,
port_id):
self._l2network_plugin.unplug_interface(tenant_id,
network_dict_id, port_id)
self.tearDownNetworkPort(tenant_id, network_dict_id, port_id)
def tearDownPortProfile(self, tenant_id, port_profile_id):
self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id)
def tearDownAssociatePortProfile(self, tenant_id, net_id, port_id,
port_profile_id):
self._l2network_plugin.disassociate_portprofile(
tenant_id, net_id, port_id, port_profile_id)
self.tearDownPortProfile(tenant_id, port_profile_id)

View File

@ -0,0 +1,282 @@
# copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Shweta Padubidri, Peter Strunk, Cisco Systems, Inc.
#
import unittest
import logging
from quantum.common import exceptions as exc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.nexus import cisco_nexus_plugin
LOG = logging.getLogger('quantum.tests.test_nexus')
class TestNexusPlugin(unittest.TestCase):
def setUp(self):
self.tenant_id = "test_tenant_cisco1"
self.net_name = "test_network_cisco1"
self.net_id = 000007
self.vlan_name = "q-" + str(self.net_id) + "vlan"
self.vlan_id = 267
self.port_id = "9"
self._cisco_nexus_plugin = cisco_nexus_plugin.NexusPlugin()
def test_create_network(self, net_tenant_id=None, network_name=None,
network_id=None, net_vlan_name=None,
net_vlan_id=None):
"""
Tests creation of new Virtual Network.
"""
LOG.debug("test_create_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_name:
net_name = network_name
else:
net_name = self.net_name
if network_id:
net_id = network_id
else:
net_id = self.net_id
if net_vlan_name:
vlan_name = net_vlan_name
else:
vlan_name = self.vlan_name
if net_vlan_id:
vlan_id = net_vlan_id
else:
vlan_id = self.vlan_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, net_name, net_id, vlan_name, vlan_id)
self.assertEqual(new_net_dict[const.NET_ID], self.net_id)
self.assertEqual(new_net_dict[const.NET_NAME], self.net_name)
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_create_network - END")
def test_delete_network(self, net_tenant_id=None, network_id=None):
"""
Tests deletion of a Virtual Network.
"""
LOG.debug("test_delete_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_id:
net_id = network_id
else:
net_id = self.net_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, self.net_name, net_id, self.vlan_name, self.vlan_id)
deleted_net_dict = self._cisco_nexus_plugin.delete_network(
tenant_id, new_net_dict[const.NET_ID])
self.assertEqual(deleted_net_dict[const.NET_ID], net_id)
LOG.debug("test_delete_network - END")
def test_delete_network_DNE(self, net_tenant_id=None, net_id='0005'):
"""
Tests deletion of a Virtual Network when Network does not exist.
"""
LOG.debug("test_delete_network_DNE - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(exc.NetworkNotFound,
self._cisco_nexus_plugin.delete_network,
tenant_id, net_id)
LOG.debug("test_delete_network_DNE - END")
def test_get_network_details(self, net_tenant_id=None, network_id=None):
"""
Tests displays details of a Virtual Network .
"""
LOG.debug("test_get_network_details - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_id:
net_id = network_id
else:
net_id = self.net_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, self.net_name, net_id, self.vlan_name, self.vlan_id)
check_net_dict = self._cisco_nexus_plugin.get_network_details(
tenant_id, net_id)
self.assertEqual(check_net_dict[const.NET_ID], net_id)
self.assertEqual(check_net_dict[const.NET_NAME], self.net_name)
self.assertEqual(check_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(check_net_dict[const.NET_VLAN_ID], self.vlan_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_get_network_details - END")
def test_get_networkDNE(self, net_tenant_id=None, net_id='0005'):
"""
Tests display of a Virtual Network when Network does not exist.
"""
LOG.debug("test_get_network_details_network_does_not_exist - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
self.assertRaises(exc.NetworkNotFound,
self._cisco_nexus_plugin.get_network_details,
tenant_id, net_id)
LOG.debug("test_get_network_details_network_does_not_exist - END")
def test_rename_network(self, new_name="new_network_name",
net_tenant_id=None, network_id=None):
"""
Tests rename of a Virtual Network .
"""
LOG.debug("test_rename_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_id:
net_id = network_id
else:
net_id = self.net_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, self.net_name, net_id, self.vlan_name,
self.vlan_id)
rename_net_dict = self._cisco_nexus_plugin.rename_network(
tenant_id, new_net_dict[const.NET_ID], new_name)
self.assertEqual(rename_net_dict[const.NET_NAME], new_name)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_rename_network - END")
def test_rename_network_DNE(self, new_name="new_network_name",
net_tenant_id=None, network_id='0005'):
"""
Tests rename of a Virtual Network when Network does not exist.
"""
LOG.debug("test_rename_network_DNE - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_id:
net_id = network_id
else:
net_id = self.net_id
self.assertRaises(exc.NetworkNotFound,
self._cisco_nexus_plugin.rename_network,
new_name, tenant_id, net_id)
LOG.debug("test_rename_network_DNE - END")
def test_list_all_networks(self, net_tenant_id=None):
"""
Tests listing of all the Virtual Networks .
"""
LOG.debug("test_list_all_networks - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
new_net_dict1 = self._cisco_nexus_plugin.create_network(
tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
new_net_dict2 = self._cisco_nexus_plugin.create_network(
tenant_id, "New_Network2", "0011",
"second_vlan", "2003")
list_net_dict = self._cisco_nexus_plugin.get_all_networks(tenant_id)
net_temp_list = [new_net_dict1, new_net_dict2]
self.assertEqual(len(list_net_dict), 2)
self.assertTrue(list_net_dict[0] in net_temp_list)
self.assertTrue(list_net_dict[1] in net_temp_list)
self.tearDownNetwork(tenant_id, new_net_dict1[const.NET_ID])
self.tearDownNetwork(tenant_id, new_net_dict2[const.NET_ID])
LOG.debug("test_list_all_networks - END")
def test_get_vlan_id_for_network(self, net_tenant_id=None,
network_id=None):
"""
Tests retrieval of vlan id for a Virtual Networks .
"""
LOG.debug("test_get_vlan_id_for_network - START")
if net_tenant_id:
tenant_id = net_tenant_id
else:
tenant_id = self.tenant_id
if network_id:
net_id = network_id
else:
net_id = self.net_id
new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, self.net_name, net_id, self.vlan_name,
self.vlan_id)
result_vlan_id = self._cisco_nexus_plugin._get_vlan_id_for_network(
tenant_id, net_id)
self.assertEqual(result_vlan_id, self.vlan_id)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_get_vlan_id_for_network - END")
"""
Clean up functions after the tests
"""
def tearDownNetwork(self, tenant_id, network_dict_id):
self._cisco_nexus_plugin.delete_network(tenant_id, network_dict_id)
# def test_create_network(self):
# _test_create_network(self._cisco_nexus_plugin)
# def test_delete_network(self):
# _test_delete_network(self._cisco_nexus_plugin)
# def test_rename_network(self):
# _test_rename_network(self._cisco_nexus_plugin)
# def test_show_network(self):
# _test_get_network_details(self._cisco_nexus_plugin)
# def test_list_networks(self):
# _test_list_all_networks(self._cisco_nexus_plugin)

View File

@ -0,0 +1,165 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Shweta Padubidri, Cisco Systems, Inc.
#
import logging
import unittest
from quantum.plugins.cisco.ucs import cisco_ucs_network_driver
LOG = logging.getLogger('quantum.tests.test_ucs_driver')
create_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/net-New Vlan\"> "\
"<fabricVlan defaultNet=\"no\" dn=\"fabric/lan/net-New Vlan\" id=\"200\" "\
"name=\"New Vlan\" status=\"created\"></fabricVlan> </pair> </inConfigs> "\
"</configConfMos>"
create_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/profiles/vnic-"\
"New Profile\"> <vnicProfile descr=\"Profile created by Cisco OpenStack "\
"Quantum Plugin\" dn=\"fabric/lan/profiles/vnic-New Profile\" maxPorts="\
"\"64\" name=\"New Profile\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" "\
"qosPolicyName=\"\" status=\"created\"> <vnicEtherIf defaultNet=\"yes\" "\
"name=\"New Vlan\" rn=\"if-New Vlan\" > </vnicEtherIf> </vnicProfile> "\
"</pair> </inConfigs> </configConfMos>"
change_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"true\"> <inConfigs><pair key=\""\
"fabric/lan/profiles/vnic-New Profile\"> <vnicProfile descr=\"Profile "\
"created by Cisco OpenStack Quantum Plugin\" "\
"dn=\"fabric/lan/profiles/vnic-New Profile\" maxPorts=\"64\" "\
"name=\"New Profile\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" "\
"qosPolicyName=\"\" status=\"created,modified\"><vnicEtherIf "\
"rn=\"if-Old Vlan\" status=\"deleted\"> </vnicEtherIf> "\
"<vnicEtherIf defaultNet=\"yes\" name=\"New Vlan\" rn=\"if-New Vlan\" > "\
"</vnicEtherIf> </vnicProfile> </pair></inConfigs> </configConfMos>"
delete_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"true\"> <inConfigs><pair key=\"fabric/lan/net-New Vlan\"> "\
"<fabricVlan dn=\"fabric/lan/net-New Vlan\" status=\"deleted\"> "\
"</fabricVlan> </pair> </inConfigs></configConfMos>"
delete_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"false\"> <inConfigs><pair key=\""\
"fabric/lan/profiles/vnic-New Profile\"> <vnicProfile "\
"dn=\"fabric/lan/profiles/vnic-New Profile\" status=\"deleted\"> "\
"</vnicProfile></pair> </inConfigs> </configConfMos>"
associate_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"inHierarchical=\"true\"> <inConfigs> <pair key="\
"\"fabric/lan/profiles/vnic-New Profile/cl-New Profile Client\">"\
" <vmVnicProfCl dcName=\".*\" descr=\"\" dn=\"fabric/lan/profiles/vnic-"\
"New Profile/cl-New Profile Client\"name=\"New Profile Client\" "\
"orgPath=\".*\" status=\"created\" swName=\"default$\"> </vmVnicProfCl>" \
"</pair> </inConfigs> </configConfMos>"
class TestUCSDriver(unittest.TestCase):
def setUp(self):
self._ucsmDriver = cisco_ucs_network_driver.CiscoUCSMDriver()
self.vlan_name = 'New Vlan'
self.vlan_id = '200'
self.profile_name = 'New Profile'
self.old_vlan_name = 'Old Vlan'
self.profile_client_name = 'New Profile Client'
def test_create_vlan_post_data(self, expected_output=create_vlan_output):
"""
Tests creation of vlan post Data
"""
LOG.debug("test_create_vlan")
vlan_details = self._ucsmDriver._create_vlan_post_data(
self.vlan_name, self.vlan_id)
self.assertEqual(vlan_details, expected_output)
LOG.debug("test_create_vlan - END")
def test_create_profile_post_data(
self, expected_output=create_profile_output):
"""
Tests creation of profile post Data
"""
LOG.debug("test_create_profile_post_data - START")
profile_details = self._ucsmDriver._create_profile_post_data(
self.profile_name, self.vlan_name)
self.assertEqual(profile_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_change_vlan_in_profile_post_data(
self, expected_output=change_vlan_output):
"""
Tests creation of change vlan in profile post Data
"""
LOG.debug("test_create_profile_post_data - START")
profile_details = self._ucsmDriver._change_vlan_in_profile_post_data(
self.profile_name, self.old_vlan_name, self.vlan_name)
self.assertEqual(profile_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_delete_vlan_post_data(self, expected_output=delete_vlan_output):
LOG.debug("test_create_profile_post_data - START")
"""
Tests deletion of vlan post Data
"""
vlan_details = self._ucsmDriver._create_vlan_post_data(
self.vlan_name, self.vlan_id)
vlan_delete_details = self._ucsmDriver._delete_vlan_post_data(
self.vlan_name)
self.assertEqual(vlan_delete_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_delete_profile_post_data(
self, expected_output=delete_profile_output):
"""
Tests deletion of profile post Data
"""
LOG.debug("test_create_profile_post_data - START")
profile_details = self._ucsmDriver._create_profile_post_data(
self.profile_name, self.vlan_name)
profile_delete_details = self._ucsmDriver._delete_profile_post_data(
self.profile_name)
self.assertEqual(profile_delete_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_create_profile_client_post_data(
self, expected_output=associate_profile_output):
"""
Tests creation of profile client post Data
"""
LOG.debug("test_create_profile_client_post_data - START")
profile_details = self._ucsmDriver._create_profile_client_post_data(
self.profile_name, self.profile_client_name)
self.assertEqual(profile_details, expected_output)
LOG.debug("test_create_profile_post - END")
def test_get_next_dynamic_nic(self):
"""
Tests get next dynamic nic
"""
LOG.debug("test_get_next_dynamic_nic - START")
dynamic_nic_id = self._ucsmDriver._get_next_dynamic_nic()
self.assertTrue(len(dynamic_nic_id) > 0)
LOG.debug("test_get_next_dynamic_nic - END")

View File

@ -0,0 +1,480 @@
#vim: tabstop=4 shiftwidth=4 softtabstop=4
#copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0(the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Shubhangi Satras, Cisco Systems, Inc.
#
import unittest
import logging as LOG
from quantum.common import exceptions as exc
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.ucs import cisco_ucs_plugin
from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger("cisco_plugin")
class UCSVICTestPlugin(unittest.TestCase):
def setUp(self):
self.tenant_id = "test_tenant_cisco12"
self.net_name = "test_network_cisco12"
self.net_id = 000007
self.vlan_name = "q-" + str(self.net_id) + "vlan"
self.vlan_id = 266
self.port_id = "4"
self._cisco_ucs_plugin = cisco_ucs_plugin.UCSVICPlugin()
def test_create_network(self):
"""
Tests creation of new Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_create_network() called\n")
new_net_dict = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
self.assertEqual(new_net_dict[const.NET_ID], self.net_id)
self.assertEqual(new_net_dict[const.NET_NAME], self.net_name)
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_delete_network(self):
"""
Tests deletion of the network with the specified network identifier
belonging to the specified tenant.
"""
LOG.debug("UCSVICTestPlugin:test_delete_network() called\n")
self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
new_net_dict = self._cisco_ucs_plugin.delete_network(
self.tenant_id, self.net_id)
self.assertEqual(new_net_dict[const.NET_ID], self.net_id)
def test_get_network_details(self):
"""
Tests the deletion the Virtual Network belonging to a the
spec
"""
LOG.debug("UCSVICTestPlugin:test_get_network_details() called\n")
self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
new_net_dict = self._cisco_ucs_plugin.get_network_details(
self.tenant_id, self.net_id)
self.assertEqual(new_net_dict[const.NET_ID], self.net_id)
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_get_all_networks(self):
"""
Tests whether dictionary is returned containing all
<network_uuid, network_name> for
the specified tenant.
"""
LOG.debug("UCSVICTestPlugin:test_get_all_networks() called\n")
new_net_dict1 = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
new_net_dict2 = self._cisco_ucs_plugin.create_network(
self.tenant_id, "test_network2",
000006, "q-000006vlan", "6")
net_list = self._cisco_ucs_plugin.get_all_networks(self.tenant_id)
net_id_list = [new_net_dict1, new_net_dict2]
self.assertTrue(net_list[0] in net_id_list)
self.assertTrue(net_list[1] in net_id_list)
self.tearDownNetwork(self.tenant_id, new_net_dict1[const.NET_ID])
self.tearDownNetwork(self.tenant_id, new_net_dict2[const.NET_ID])
def test_get_all_ports(self):
"""
Retrieves all port identifiers belonging to the
specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:get_all_ports() called\n")
new_net_dict = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
port_dict1 = self._cisco_ucs_plugin.create_port(
self.tenant_id, self.net_id, const.PORT_UP,
self.port_id)
port_dict2 = self._cisco_ucs_plugin.create_port(
self.tenant_id, self.net_id,
const.PORT_UP, "10")
ports_on_net = self._cisco_ucs_plugin.get_all_ports(
self.tenant_id, self.net_id)
port_list = [port_dict1, port_dict2]
self.assertTrue(port_list[0] in ports_on_net)
self.assertTrue(port_list[1] in ports_on_net)
self._cisco_ucs_plugin.delete_port(self.tenant_id, self.net_id,
self.port_id)
self.tearDownNetworkPort(self.tenant_id, new_net_dict[const.NET_ID],
port_dict2[const.PORT_ID])
def _test_rename_network(self, new_name):
"""
Tests whether symbolic name is updated for the particular
Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_rename_network() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
new_net_dict = self._cisco_ucs_plugin.rename_network(
self.tenant_id, self.net_id, new_name)
self.assertEqual(new_net_dict[const.NET_NAME], new_name)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_rename_network(self):
self._test_rename_network("new_test_network1")
def _test_create_port(self, port_state):
"""
Tests creation of a port on the specified Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_create_port() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
new_port_dict = self._cisco_ucs_plugin.create_port(
self.tenant_id, self.net_id, port_state, self.port_id)
self.assertEqual(new_port_dict[const.PORT_ID], self.port_id)
self.assertEqual(new_port_dict[const.PORT_STATE], port_state)
self.assertEqual(new_port_dict[const.ATTACHMENT], None)
profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id)
new_port_profile = new_port_dict[const.PORT_PROFILE]
self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME],
conf.DEFAULT_VLAN_NAME)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID],
conf.DEFAULT_VLAN_ID)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def test_create_port(self):
self._test_create_port(const.PORT_UP)
def _test_delete_port(self, port_state):
"""
Tests Deletion of 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("UCSVICTestPlugin:_test_delete_port() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
port_state, self.port_id)
self._cisco_ucs_plugin.delete_port(self.tenant_id, self.net_id,
self.port_id)
net = self._cisco_ucs_plugin._get_network(self.tenant_id, self.net_id)
self.assertEqual(net[const.NET_PORTS], {})
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_delete_port(self):
self._test_delete_port(const.PORT_UP)
def _test_update_port(self, port_state):
"""
Tests Updation of the state of a port on the specified Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_update_port() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
port_state, self.port_id)
port = self._cisco_ucs_plugin.update_port(
self.tenant_id, self.net_id,
self.port_id, port_state)
self.assertEqual(port[const.PORT_STATE], port_state)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def test_update_port_state_up(self):
self._test_update_port(const.PORT_UP)
def test_update_port_state_down(self):
self._test_update_port(const.PORT_DOWN)
def _test_get_port_details_state_up(self, port_state):
"""
Tests whether user is able to retrieve a remote interface
that is attached to this particular port when port state is Up.
"""
LOG.debug("UCSVICTestPlugin:_test_get_port_details_state_up()" +
"called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
port_state, self.port_id)
port = self._cisco_ucs_plugin.get_port_details(
self.tenant_id, self.net_id, self.port_id)
self.assertEqual(port[const.PORT_ID], self.port_id)
self.assertEqual(port[const.PORT_STATE], port_state)
self.assertEqual(port[const.ATTACHMENT], None)
new_port_profile = port[const.PORT_PROFILE]
profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME],
conf.DEFAULT_VLAN_NAME)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID],
conf.DEFAULT_VLAN_ID)
self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def _test_get_port_details_state_down(self, port_state):
"""
Tests whether user is able to retrieve a remote interface
that is attached to this particular port when port state is down.
"""
LOG.debug("UCSVICTestPlugin:_test_get_port_details_state_down()" +
"called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
port_state, self.port_id)
port = self._cisco_ucs_plugin.get_port_details(self.tenant_id,
self.net_id,
self.port_id)
self.assertEqual(port[const.PORT_ID], self.port_id)
self.assertNotEqual(port[const.PORT_STATE], port_state)
self.assertEqual(port[const.ATTACHMENT], None)
new_port_profile = port[const.PORT_PROFILE]
profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME],
conf.DEFAULT_VLAN_NAME)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID],
conf.DEFAULT_VLAN_ID)
self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def test_get_port_details_state_up(self):
self._test_get_port_details_state_up(const.PORT_UP)
def test_get_port_details_state_down(self):
self._test_get_port_details_state_down(const.PORT_DOWN)
def test_create_port_profile(self):
LOG.debug("UCSVICTestPlugin:test_create_port_profile() called\n")
new_port_profile = self._cisco_ucs_plugin._create_port_profile(
self.tenant_id, self.net_id, self.port_id,
self.vlan_name, self.vlan_id)
profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id)
self.assertEqual(new_port_profile[const.PROFILE_NAME], profile_name)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_NAME],
self.vlan_name)
self.assertEqual(new_port_profile[const.PROFILE_VLAN_ID], self.vlan_id)
self._cisco_ucs_plugin._delete_port_profile(self.port_id, profile_name)
def test_delete_port_profile(self):
LOG.debug("UCSVICTestPlugin:test_delete_port_profile() called\n")
self._cisco_ucs_plugin._create_port_profile(
self.tenant_id, self.net_id, self.port_id, self.vlan_name,
self.vlan_id)
profile_name = self._cisco_ucs_plugin._get_profile_name(self.port_id)
counter1 = self._cisco_ucs_plugin._port_profile_counter
self._cisco_ucs_plugin._delete_port_profile(self.port_id,
profile_name)
counter2 = self._cisco_ucs_plugin._port_profile_counter
self.assertNotEqual(counter1, counter2)
def _test_plug_interface(self, remote_interface_id):
"""
Attaches a remote interface to the specified port on the
specified Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_plug_interface() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id,
self.port_id,
remote_interface_id)
port = self._cisco_ucs_plugin._get_port(
self.tenant_id, self.net_id, self.port_id)
self.assertEqual(port[const.ATTACHMENT], remote_interface_id)
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
new_vlan_name = self._cisco_ucs_plugin._get_vlan_name_for_network(
self.tenant_id, self.net_id)
new_vlan_id = self._cisco_ucs_plugin._get_vlan_id_for_network(
self.tenant_id, self.net_id)
self.assertEqual(port_profile[const.PROFILE_VLAN_NAME], new_vlan_name)
self.assertEqual(port_profile[const.PROFILE_VLAN_ID], new_vlan_id)
self.tearDownNetworkPortInterface(self.tenant_id, self.net_id,
self.port_id)
def test_plug_interface(self):
self._test_plug_interface("4")
def _test_unplug_interface(self, remote_interface_id):
"""
Tests whether remote interface detaches from the specified port on the
specified Virtual Network.
"""
LOG.debug("UCSVICTestPlugin:_test_unplug_interface() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id,
self.port_id,
remote_interface_id)
self._cisco_ucs_plugin.unplug_interface(self.tenant_id, self.net_id,
self.port_id)
port = self._cisco_ucs_plugin._get_port(
self.tenant_id, self.net_id, self.port_id)
self.assertEqual(port[const.ATTACHMENT], None)
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
self.assertEqual(port_profile[const.PROFILE_VLAN_NAME],
conf.DEFAULT_VLAN_NAME)
self.assertEqual(port_profile[const.PROFILE_VLAN_ID],
conf.DEFAULT_VLAN_ID)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def test_unplug_interface(self):
self._test_unplug_interface("4")
def test_get_vlan_name_for_network(self):
LOG.debug("UCSVICTestPlugin:test_get_vlan_name_for_network() called\n")
net = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id,
self.vlan_name, self.vlan_id)
self.assertEqual(net[const.NET_VLAN_NAME], self.vlan_name)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_get_vlan_id_for_network(self):
LOG.debug("UCSVICTestPlugin:test_get_vlan_id_for_network() called\n")
net = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id, self.vlan_name,
self.vlan_id)
self.assertEqual(net[const.NET_VLAN_ID], self.vlan_id)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_get_network(self):
LOG.debug("UCSVICTestPlugin:test_get_network() called\n")
net = self._cisco_ucs_plugin.create_network(
self.tenant_id, self.net_name, self.net_id, self.vlan_name,
self.vlan_id)
self.assertEqual(net[const.NET_ID], self.net_id)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_get_port(self):
LOG.debug("UCSVICTestPlugin:test_get_port() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
new_port_dict = self._cisco_ucs_plugin.create_port(
self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self.assertEqual(new_port_dict[const.PORT_ID], self.port_id)
self.tearDownNetworkPort(self.tenant_id, self.net_id, self.port_id)
def test_get_network_NetworkNotFound(self):
self.assertRaises(exc.NetworkNotFound,
self._cisco_ucs_plugin._get_network,
*(self.tenant_id, self.net_id))
def test_delete_network_NetworkNotFound(self):
self.assertRaises(exc.NetworkNotFound,
self._cisco_ucs_plugin.delete_network,
*(self.tenant_id, self.net_id))
def test_delete_port_PortInUse(self):
self._test_delete_port_PortInUse("4")
def _test_delete_port_PortInUse(self, remote_interface_id):
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id,
self.port_id,
remote_interface_id)
self.assertRaises(exc.PortInUse, self._cisco_ucs_plugin.delete_port,
*(self.tenant_id, self.net_id, self.port_id))
self.tearDownNetworkPortInterface(self.tenant_id, self.net_id,
self.port_id)
def test_delete_port_PortNotFound(self):
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self.assertRaises(exc.PortNotFound, self._cisco_ucs_plugin.delete_port,
*(self.tenant_id, self.net_id, self.port_id))
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_plug_interface_PortInUse(self):
self._test_plug_interface_PortInUse("6", "5")
def _test_plug_interface_PortInUse(self, remote_interface_id1,
remote_interface_id2):
LOG.debug("UCSVICTestPlugin:_test_plug_interface_PortInUse() called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id,
self.port_id,
remote_interface_id1)
self.assertRaises(exc.PortInUse, self._cisco_ucs_plugin.plug_interface,
*(self.tenant_id, self.net_id, self.port_id,
remote_interface_id2))
self.tearDownNetworkPortInterface(self.tenant_id, self.net_id,
self.port_id)
def test_validate_attachment_AlreadyAttached(self):
LOG.debug("UCSVICTestPlugin:testValidateAttachmentAlreadyAttached")
self._test_validate_attachment_AlreadyAttached("4")
def _test_validate_attachment_AlreadyAttached(self, remote_interface_id):
LOG.debug("UCSVICTestPlugin:_test_validate_attachmentAlreadyAttached")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
self.vlan_id)
self._cisco_ucs_plugin.create_port(self.tenant_id, self.net_id,
const.PORT_UP, self.port_id)
self._cisco_ucs_plugin.plug_interface(self.tenant_id, self.net_id,
self.port_id,
remote_interface_id)
self.assertRaises(
exc.AlreadyAttached, self._cisco_ucs_plugin._validate_attachment,
*(self.tenant_id, self.net_id, self.port_id, remote_interface_id))
self.tearDownNetworkPortInterface(self.tenant_id, self.net_id,
self.port_id)
def tearDownNetwork(self, tenant_id, net_id):
self._cisco_ucs_plugin.delete_network(tenant_id, net_id)
def tearDownNetworkPort(self, tenant_id, net_id, port_id):
self._cisco_ucs_plugin.delete_port(tenant_id, net_id,
port_id)
self.tearDownNetwork(tenant_id, net_id)
def tearDownNetworkPortInterface(self, tenant_id, net_id, port_id):
self._cisco_ucs_plugin.unplug_interface(tenant_id, net_id,
port_id)
self.tearDownNetworkPort(tenant_id, net_id, port_id)

View File

@ -0,0 +1,18 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#

View File

@ -0,0 +1,51 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Rohit Agarwalla, Cisco Systems Inc.
#
import sys
import subprocess
def get_next_dynic(argv=[]):
cmd = ["ifconfig", "-a"]
f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\
communicate()[0]
eths = [lines.split(' ')[0] for lines in f_cmd_output.splitlines() \
if "eth" in lines]
#print eths
for eth in eths:
cmd = ["ethtool", "-i", eth]
f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\
communicate()[0]
bdf = [lines.split(' ')[1] for lines in f_cmd_output.splitlines() \
if "bus-info" in lines]
#print bdf
cmd = ["lspci", "-n", "-s", bdf[0]]
f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\
communicate()[0]
deviceid = [(lines.split(':')[3]).split(' ')[0] \
for lines in f_cmd_output.splitlines()]
#print deviceid
if deviceid[0] == "0044":
cmd = ["/sbin/ip", "link", "show", eth]
f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\
communicate()[0]
used = [lines for lines in f_cmd_output.splitlines() \
if "UP" in lines]
if not used:
break
return eth

View File

@ -0,0 +1,37 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import os
from quantum.plugins.cisco.common import cisco_configparser as confp
CONF_FILE = "../conf/ucs.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
section = cp['UCSM']
UCSM_IP_ADDRESS = section['ip_address']
DEFAULT_VLAN_NAME = section['default_vlan_name']
DEFAULT_VLAN_ID = section['default_vlan_id']
MAX_UCSM_PORT_PROFILES = section['max_ucsm_port_profiles']
PROFILE_NAME_PREFIX = section['profile_name_prefix']
section = cp['DRIVER']
UCSM_DRIVER = section['name']

View File

@ -0,0 +1,235 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems Inc.
#
"""
Implements a UCSM XML API Client
"""
import httplib
import logging as LOG
import string
import subprocess
from xml.etree import ElementTree as et
import urllib
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.ucs import cisco_getvif as gvif
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
COOKIE_VALUE = "cookie_placeholder"
PROFILE_NAME = "profilename_placeholder"
PROFILE_CLIENT = "profileclient_placeholder"
VLAN_NAME = "vlanname_placeholder"
VLAN_ID = "vlanid_placeholder"
OLD_VLAN_NAME = "old_vlanname_placeholder"
DYNAMIC_NIC_PREFIX = "eth"
# The following are standard strings, messages used to communicate with UCSM,
#only place holder values change for each message
HEADERS = {"Content-Type": "text/xml"}
METHOD = "POST"
URL = "/nuova"
CREATE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"true\"> <inConfigs>" \
"<pair key=\"fabric/lan/net-" + VLAN_NAME + \
"\"> <fabricVlan defaultNet=\"no\" " \
"dn=\"fabric/lan/net-" + VLAN_NAME + \
"\" id=\"" + VLAN_ID + "\" name=\"" + \
VLAN_NAME + "\" status=\"created\">" \
"</fabricVlan> </pair> </inConfigs> </configConfMos>"
CREATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"true\"> <inConfigs>" \
"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
"\"> <vnicProfile descr=\"Profile created by " \
"Cisco OpenStack Quantum Plugin\" " \
"dn=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
"\" maxPorts=\"64\" name=\"" + PROFILE_NAME + \
"\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" " \
"qosPolicyName=\"\" status=\"created\"> " \
"<vnicEtherIf defaultNet=\"yes\" name=\"" + VLAN_NAME + \
"\" rn=\"if-" + VLAN_NAME + "\" > </vnicEtherIf> " \
"</vnicProfile> </pair> </inConfigs> </configConfMos>"
ASSOCIATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"true\"> <inConfigs> <pair " \
"key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
"/cl-" + PROFILE_CLIENT + "\"> <vmVnicProfCl dcName=\".*\" " \
"descr=\"\" dn=\"fabric/lan/profiles/vnic-" + \
PROFILE_NAME + "/cl-" + PROFILE_CLIENT + \
"\"name=\"" + PROFILE_CLIENT + "\" orgPath=\".*\" " \
"status=\"created\" swName=\"default$\"> </vmVnicProfCl>" \
"</pair> </inConfigs> </configConfMos>"
CHANGE_VLAN_IN_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"true\"> <inConfigs>" \
"<pair key=\"fabric/lan/profiles/vnic-" + \
PROFILE_NAME + "\"> <vnicProfile descr=\"Profile " \
"created by Cisco OpenStack Quantum Plugin\" " \
"dn=\"fabric/lan/profiles/vnic-" + \
PROFILE_NAME + "\" maxPorts=\"64\" name=\"" + \
PROFILE_NAME + "\" nwCtrlPolicyName=\"\" " \
"pinToGroupName=\"\" qosPolicyName=\"\" " \
"status=\"created,modified\">" \
"<vnicEtherIf rn=\"if-" + OLD_VLAN_NAME + \
"\" status=\"deleted\"> </vnicEtherIf> <vnicEtherIf " \
"defaultNet=\"yes\" name=\"" + \
VLAN_NAME + "\" rn=\"if-" + VLAN_NAME + \
"\" > </vnicEtherIf> </vnicProfile> </pair>" \
"</inConfigs> </configConfMos>"
DELETE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"true\"> <inConfigs>" \
"<pair key=\"fabric/lan/net-" + VLAN_NAME + \
"\"> <fabricVlan dn=\"fabric/lan/net-" + VLAN_NAME + \
"\" status=\"deleted\"> </fabricVlan> </pair> </inConfigs>" \
"</configConfMos>"
DELETE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
"\" inHierarchical=\"false\"> <inConfigs>" \
"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
"\"> <vnicProfile dn=\"fabric/lan/profiles/vnic-" + \
PROFILE_NAME + "\" status=\"deleted\"> </vnicProfile>" \
"</pair> </inConfigs> </configConfMos>"
class CiscoUCSMDriver():
def __init__(self):
pass
def _post_data(self, ucsm_ip, ucsm_username, ucsm_password, data):
conn = httplib.HTTPConnection(ucsm_ip)
login_data = "<aaaLogin inName=\"" + ucsm_username + \
"\" inPassword=\"" + ucsm_password + "\" />"
conn.request(METHOD, URL, login_data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug(response_data)
# TODO (Sumit): If login is not successful, throw exception
xmlTree = et.XML(response_data)
cookie = xmlTree.attrib["outCookie"]
data = data.replace(COOKIE_VALUE, cookie)
LOG.debug("POST: %s" % data)
conn.request(METHOD, URL, data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug("UCSM Response: %s" % response_data)
logout_data = "<aaaLogout inCookie=\"" + cookie + "\" />"
conn.request(METHOD, URL, logout_data, HEADERS)
response = conn.getresponse()
response_data = response.read()
LOG.debug(response.status)
LOG.debug(response.reason)
LOG.debug(response_data)
def _create_vlan_post_data(self, vlan_name, vlan_id):
data = CREATE_VLAN.replace(VLAN_NAME, vlan_name)
data = data.replace(VLAN_ID, vlan_id)
return data
def _create_profile_post_data(self, profile_name, vlan_name):
data = CREATE_PROFILE.replace(PROFILE_NAME, profile_name)
data = data.replace(VLAN_NAME, vlan_name)
return data
def _create_profile_client_post_data(self, profile_name,
profile_client_name):
data = ASSOCIATE_PROFILE.replace(PROFILE_NAME, profile_name)
data = data.replace(PROFILE_CLIENT, profile_client_name)
return data
def _change_vlan_in_profile_post_data(self, profile_name, old_vlan_name,
new_vlan_name):
data = CHANGE_VLAN_IN_PROFILE.replace(PROFILE_NAME, profile_name)
data = data.replace(OLD_VLAN_NAME, old_vlan_name)
data = data.replace(VLAN_NAME, new_vlan_name)
return data
def _delete_vlan_post_data(self, vlan_name):
data = DELETE_VLAN.replace(VLAN_NAME, vlan_name)
return data
def _delete_profile_post_data(self, profile_name):
data = DELETE_PROFILE.replace(PROFILE_NAME, profile_name)
return data
def _get_next_dynamic_nic(self):
dynamic_nic_id = gvif.get_next_dynic()
if len(dynamic_nic_id) > 0:
return dynamic_nic_id
else:
raise cisco_exceptions.NoMoreNics(net_id=net_id, port_id=port_id)
def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
ucsm_password):
data = self._create_vlan_post_data(vlan_name, vlan_id)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def create_profile(self, profile_name, vlan_name, ucsm_ip, ucsm_username,
ucsm_password):
data = self._create_profile_post_data(profile_name, vlan_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
data = self._create_profile_client_post_data(profile_name,
profile_name[-16:])
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def change_vlan_in_profile(self, profile_name, old_vlan_name,
new_vlan_name, ucsm_ip, ucsm_username,
ucsm_password):
data = self._change_vlan_in_profile_post_data(profile_name,
old_vlan_name,
new_vlan_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def get_dynamic_nic(self, host):
# TODO (Sumit): Check availability per host
# TODO (Sumit): If not available raise exception
# TODO (Sumit): This simple logic assumes that create-port and
# spawn-VM happens in lock-step
# But we should support multiple create-port calls,
# followed by spawn-VM calls
# That would require managing a pool of available
# dynamic vnics per host
dynamic_nic_name = self._get_next_dynamic_nic()
LOG.debug("Reserving dynamic nic %s" % dynamic_nic_name)
return dynamic_nic_name
def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password):
data = self._delete_vlan_post_data(vlan_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def delete_profile(self, profile_name, ucsm_ip, ucsm_username,
ucsm_password):
data = self._delete_profile_post_data(profile_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def release_dynamic_nic(self, host):
# TODO (Sumit): Release on a specific host
pass

View File

@ -0,0 +1,298 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
import logging as LOG
from quantum.common import exceptions as exc
from quantum.common import utils
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials as cred
from quantum.plugins.cisco.common import cisco_exceptions as cexc
from quantum.plugins.cisco.common import cisco_utils as cutil
from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class UCSVICPlugin(L2DevicePluginBase):
_networks = {}
def __init__(self):
self._client = utils.import_object(conf.UCSM_DRIVER)
LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER)
self._utils = cutil.DBUtils()
# TODO (Sumit) This is for now, when using only one chassis
self._ucsm_ip = conf.UCSM_IP_ADDRESS
self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS)
self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS)
# TODO (Sumit) Make the counter per UCSM
self._port_profile_counter = 0
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")
return self._networks.values()
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._client.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
new_net_dict = {const.NET_ID: net_id,
const.NET_NAME: net_name,
const.NET_PORTS: {},
const.NET_VLAN_NAME: vlan_name,
const.NET_VLAN_ID: vlan_id}
self._networks[net_id] = new_net_dict
return new_net_dict
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")
net = self._networks.get(net_id)
# TODO (Sumit) : Verify that no attachments are plugged into the
# network
if net:
# TODO (Sumit) : Before deleting the network, make sure all the
# ports associated with this network are also deleted
self._client.delete_vlan(net[const.NET_VLAN_NAME], self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._networks.pop(net_id)
return net
raise exc.NetworkNotFound(net_id=net_id)
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")
network = self._get_network(tenant_id, net_id)
return network
def rename_network(self, tenant_id, net_id, new_name, **kwargs):
"""
Updates the symbolic name belonging to a particular
Virtual Network.
"""
LOG.debug("UCSVICPlugin:rename_network() called\n")
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_name
return network
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")
network = self._get_network(tenant_id, net_id)
ports_on_net = network[const.NET_PORTS].values()
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")
net = self._get_network(tenant_id, net_id)
ports = net[const.NET_PORTS]
# TODO (Sumit): This works on a single host deployment,
# in multi-host environment, dummy needs to be replaced with the
# hostname
dynamic_nic_name = self._client.get_dynamic_nic("dummy")
new_port_profile = self._create_port_profile(tenant_id, net_id,
port_id,
conf.DEFAULT_VLAN_NAME,
conf.DEFAULT_VLAN_ID)
profile_name = new_port_profile[const.PROFILE_NAME]
sql_query = "INSERT INTO ports (port_id, profile_name, dynamic_vnic," \
"host, instance_name, instance_nic_name, used) VALUES" \
"('%s', '%s', '%s', 'dummy', NULL, NULL, 0)" % \
(port_id, profile_name, dynamic_nic_name)
self._utils.execute_db_query(sql_query)
new_port_dict = {const.PORT_ID: port_id,
const.PORT_STATE: const.PORT_UP,
const.ATTACHMENT: None,
const.PORT_PROFILE: new_port_profile}
ports[port_id] = new_port_dict
return new_port_dict
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")
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
try:
#TODO (Sumit): Before deleting port profile make sure that there
# is no VM using this port profile
self._client.release_dynamic_nic("dummy")
port_profile = port[const.PORT_PROFILE]
self._delete_port_profile(port_id,
port_profile[const.PROFILE_NAME])
sql_query = "delete from ports where port_id = \"%s\"" % \
(port[const.PORT_ID])
self._utils.execute_db_query(sql_query)
net = self._get_network(tenant_id, net_id)
net[const.NET_PORTS].pop(port_id)
except KeyError:
raise exc.PortNotFound(net_id=net_id, port_id=port_id)
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("UCSVICPlugin:update_port() called\n")
port = self._get_port(tenant_id, net_id, port_id)
self._validate_port_state(port_state)
port[const.PORT_STATE] = port_state
return port
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")
return self._get_port(tenant_id, net_id, port_id)
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._validate_attachment(tenant_id, net_id, port_id,
remote_interface_id)
port = self._get_port(tenant_id, net_id, port_id)
if port[const.ATTACHMENT]:
raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port[const.ATTACHMENT])
port[const.ATTACHMENT] = remote_interface_id
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
old_vlan_name = port_profile[const.PROFILE_VLAN_NAME]
new_vlan_name = self._get_vlan_name_for_network(tenant_id, net_id)
new_vlan_id = self._get_vlan_id_for_network(tenant_id, net_id)
self._client.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
port_profile[const.PROFILE_VLAN_NAME] = new_vlan_name
port_profile[const.PROFILE_VLAN_ID] = new_vlan_id
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")
port = self._get_port(tenant_id, net_id, port_id)
port[const.ATTACHMENT] = None
port_profile = port[const.PORT_PROFILE]
profile_name = port_profile[const.PROFILE_NAME]
old_vlan_name = port_profile[const.PROFILE_VLAN_NAME]
new_vlan_name = conf.DEFAULT_VLAN_NAME
self._client.change_vlan_in_profile(profile_name, old_vlan_name,
new_vlan_name, self._ucsm_ip,
self._ucsm_username,
self._ucsm_password)
port_profile[const.PROFILE_VLAN_NAME] = conf.DEFAULT_VLAN_NAME
port_profile[const.PROFILE_VLAN_ID] = conf.DEFAULT_VLAN_ID
def _get_profile_name(self, port_id):
profile_name = conf.PROFILE_NAME_PREFIX + port_id
return profile_name
def _validate_port_state(self, port_state):
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
network = self._get_network(tenant_id, network_id)
for port in network[const.NET_PORTS].values():
if port[const.ATTACHMENT] == remote_interface_id:
raise exc.AlreadyAttached(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT],
att_port_id=port[const.PORT_ID])
def _get_network(self, tenant_id, network_id):
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network
def _get_vlan_name_for_network(self, tenant_id, network_id):
net = self._get_network(tenant_id, network_id)
vlan_name = net[const.NET_VLAN_NAME]
return vlan_name
def _get_vlan_id_for_network(self, tenant_id, network_id):
net = self._get_network(tenant_id, network_id)
vlan_id = net[const.NET_VLAN_ID]
return vlan_id
def _get_port(self, tenant_id, network_id, port_id):
net = self._get_network(tenant_id, network_id)
port = net[const.NET_PORTS].get(port_id)
if not port:
raise exc.PortNotFound(net_id=network_id, port_id=port_id)
return port
def _create_port_profile(self, tenant_id, net_id, port_id, vlan_name,
vlan_id):
if self._port_profile_counter >= int(conf.MAX_UCSM_PORT_PROFILES):
raise cexc.UCSMPortProfileLimit(net_id=net_id, port_id=port_id)
profile_name = self._get_profile_name(port_id)
self._client.create_profile(profile_name, vlan_name, self._ucsm_ip,
self._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):
self._client.delete_profile(profile_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._port_profile_counter -= 1