Merge trunk

This commit is contained in:
Salvatore Orlando 2011-08-24 12:54:53 +01:00
commit c67ae6e12f
41 changed files with 2982 additions and 898 deletions

View File

@ -30,8 +30,7 @@ EXCEPTIONS = {
430: exceptions.PortNotFound,
431: exceptions.StateInvalid,
432: exceptions.PortInUse,
440: exceptions.AlreadyAttached
}
440: exceptions.AlreadyAttached}
class ApiCall(object):
@ -61,8 +60,6 @@ class Client(object):
"""A base client class - derived from Glance.BaseClient"""
action_prefix = '/v0.1/tenants/{tenant_id}'
# Action query strings
networks_path = "/networks"
network_path = "/networks/%s"
@ -72,7 +69,7 @@ class Client(object):
def __init__(self, host="127.0.0.1", port=9696, use_ssl=False, tenant=None,
format="xml", testingStub=None, key_file=None, cert_file=None,
logger=None):
logger=None, action_prefix="/v1.0/tenants/{tenant_id}"):
"""
Creates a new client to some service.
@ -95,6 +92,7 @@ class Client(object):
self.key_file = key_file
self.cert_file = cert_file
self.logger = logger
self.action_prefix = action_prefix
def get_connection_type(self):
"""
@ -128,7 +126,7 @@ class Client(object):
# Add format and tenant_id
action += ".%s" % self.format
action = Client.action_prefix + action
action = self.action_prefix + action
action = action.replace('{tenant_id}', self.tenant)
if type(params) is dict:

View File

@ -1,8 +1,8 @@
=====================================================================
README: A Framework for a Quantum Plugin Supporting Multiple Switches
=====================================================================
=========================================================================================
README: A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
=========================================================================================
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri, Rohit Agarwalla, Ying Liu
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri, Rohit Agarwalla, Ying Liu, Debo Dutta
:Contact: netstack@lists.launchpad.net
:Web site: https://launchpad.net/~cisco-openstack
:Copyright: 2011 Cisco Systems, Inc.
@ -15,9 +15,11 @@ 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
* A reference implementation for a Quantum Plugin Framework
(For details see: http://wiki.openstack.org/quantum-multi-switch-plugin)
* Supports multiple switches in the network
* Supports multiple models of switches concurrently
* Supports use of multiple L2 technologies
* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards
(aka "Palo adapters") via 802.1Qbh.
* Supports the Cisco Nexus family of switches.
@ -119,6 +121,8 @@ 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
#Port number where the SSH will be running at Nexus Switch, e.g.: 22 (Default)
nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
@ -130,8 +134,23 @@ name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
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. Plugin Persistence framework setup:
5a. Create quantum_l2network database in mysql with the following command -
mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
5. Verify that you have the correct credentials for each IP address listed
5b. Enter the quantum_l2network database configuration info in the
quantum/plugins/cisco/conf/db_conn.ini file.
5c. If there is a change in the plugin configuration, service would need
to be restarted after dropping and re-creating the database using
the following commands -
mysql -u<mysqlusername> -p<mysqlpassword> -e "drop database quantum_l2network"
mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
6. Verify that you have the correct credentials for each IP address listed
in quantum/plugins/cisco/conf/credentials.ini. Example:
# Provide the UCSM credentials
@ -152,7 +171,7 @@ password=mySecretPasswordForNova
username=admin
password=mySecretPasswordForNexus
6. Start the Quantum service. If something doesn't work, verify that
7. 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.
@ -160,7 +179,8 @@ password=mySecretPasswordForNexus
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.
executed from the main folder using the run_tests.sh or to get a more detailed
result the quantum/plugins/cisco/run_tests.py script.
1. Testing the core API (without UCS/Nexus/RHEL hardware, and can be run on
Ubuntu):
@ -168,18 +188,41 @@ executed from quantum/plugins/cisco/ using the run_tests.py script.
quantum/plugins/cisco/conf/plugins.ini
Then run the test script:
python run_tests.py unit.test_l2networkApi
Set the environment variable PLUGIN_DIR to the location of the plugin
directory. This is manadatory if the run_tests.sh script is used.
export PLUGIN_DIR=quantum/plugins/cisco
./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
or
python quantum/plugins/cisco/run_tests.py
quantum.plugins.cisco.tests.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>
export PLUGIN_DIR=quantum/plugins/cisco
./run_tests.sh quantum.plugins.cisco.tests.unit.<name_of_the file>
or
python <path to the plugin directory>/run_tests.py
quantum.plugins.cisco.tests.unit.<name_of_the file>
E.g.:
python run_tests.py unit.test_ucs_plugin.py
python quantum/plugins/cisco/run_tests.py
quantum.plugins.cisco.tests.unit.test_ucs_plugin.py
3. All unit tests (needs environment setup as indicated in the pre-requisites):
python run_tests.py unit
export PLUGIN_DIR=quantum/plugins/cisco
./run_tests.sh quantum.plugins.cisco.tests.unit
or
python quantum/plugins/cisco/run_tests.py quantum.plugins.cisco.tests.unit
Additional installation required on Nova Compute

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,3 +17,4 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,3 +17,4 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,25 +17,23 @@
#
# @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):
"""Config Parser based on the ConfigObj module"""
def __init__(self, filename):
super(CiscoConfigParser, self).__init__(filename, raise_errors=True,
file_error=True)
def dummy(self, section, key):
"""Dummy function to return the same key, used in walk"""
return section[key]

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,13 +17,29 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
PLUGINS = 'PLUGINS'
PORT_STATE = 'port-state'
PORT_UP = "UP"
PORT_UP = "ACTIVE"
PORT_DOWN = "DOWN"
UUID = 'uuid'
TENANTID = 'tenant_id'
NETWORKID = 'network_id'
NETWORKNAME = 'name'
NETWORKPORTS = 'ports'
INTERFACEID = 'interface_id'
PORTSTATE = 'state'
PORTID = 'port_id'
PPNAME = 'name'
PPVLANID = 'vlan_id'
PPQOS = 'qos'
PPID = 'portprofile_id'
PPDEFAULT = 'default'
VLANID = 'vlan_id'
ATTACHMENT = 'attachment'
PORT_ID = 'port-id'
@ -101,3 +118,5 @@ PLUGIN_OBJ_REF = 'plugin-obj-ref'
PARAM_LIST = 'param-list'
DEVICE_IP = 'device-ip'
NO_VLAN_ID = 0

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import logging as LOG
import os
@ -34,27 +36,35 @@ _creds_dictionary = cp.walk(cp.dummy)
class Store(object):
"""Credential Store"""
@staticmethod
def putCredential(id, username, password):
"""Set the username and password"""
_creds_dictionary[id] = {const.USERNAME: username,
const.PASSWORD: password}
const.PASSWORD: password}
@staticmethod
def getUsername(id):
"""Get the username"""
return _creds_dictionary[id][const.USERNAME]
@staticmethod
def getPassword(id):
"""Get the password"""
return _creds_dictionary[id][const.PASSWORD]
@staticmethod
def getCredential(id):
"""Get the username and password"""
return _creds_dictionary[id]
@staticmethod
def getCredentials():
"""Get all usernames and passwords"""
return _creds_dictionary
@staticmethod
def deleteCredential(id):
"""Delete a credential"""
return _creds_dictionary.pop(id)

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -15,43 +16,89 @@
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
# @author: Rohit Agarwalla, 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.")
"""No more dynamic nics are available in the system"""
message = _("Unable to complete operation. No more dynamic nics are " \
"available in the system.")
class PortProfileLimit(exceptions.QuantumException):
"""Port profile limit has been hit"""
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):
"""UCSM Port profile limit has been hit"""
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):
"""Total number of network objects limit has been hit"""
message = _("Unable to create new network. Number of networks" \
"for the system has exceeded the limit")
class PortProfileNotFound(exceptions.QuantumException):
"""Port profile cannot be found"""
message = _("Port profile %(portprofile_id)s could not be found " \
"for tenant %(tenant_id)s")
class PortProfileInvalidDelete(exceptions.QuantumException):
"""Port profile cannot be deleted since its being used"""
message = _("Port profile %(profile_id)s could not be deleted " \
"for tenant %(tenant_id)s since port associations exist")
class NetworkVlanBindingAlreadyExists(exceptions.QuantumException):
"""Binding cannot be created, since it already exists"""
message = _("NetworkVlanBinding for %(vlan_id)s and network " \
"%(network_id)s already exists")
class PortProfileAlreadyExists(exceptions.QuantumException):
"""Port profile cannot be created since it already exisits"""
message = _("PortProfile %(pp_name) for %(tenant_id)s " \
"already exists")
class PortProfileBindingAlreadyExists(exceptions.QuantumException):
"""Binding cannot be created, since it already exists"""
message = _("PortProfileBinding for port profile %(pp_id)s to " \
"port %(port_id) already exists")
class VlanIDNotFound(exceptions.QuantumException):
"""VLAN ID cannot be found"""
message = _("Vlan ID %(vlan_id)s not found")
class VlanIDNotAvailable(exceptions.QuantumException):
"""VLAN ID is reserved"""
message = _("No available Vlan ID found")
try:
_("test")
except NameError:
def _(a_string):
"""
Default implementation of the gettext string
translation function: no translation
"""
return a_string
except TypeError:
# during doctesting, _ might mean something else
pass

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import os
@ -23,13 +25,13 @@ 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__)) \
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']
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

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,27 +17,36 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import MySQLdb
import hashlib
import logging as LOG
import sys
import MySQLdb
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)
def get16ByteUUID(uuid):
"""
Return a 16 byte has of the UUID, used when smaller unique
ID is required.
"""
return hashlib.md5(uuid).hexdigest()[:16]
class DBUtils(object):
"""Utilities to use connect to MySQL DB and execute queries"""
def __init__(self):
pass
def _get_db_connection(self):
"""Get a connection to the DB"""
db_ip = conf.DB_SERVER_IP
db_username = conf.DB_USERNAME
db_password = conf.DB_PASSWORD
@ -45,6 +55,7 @@ class DBUtils(object):
return self.db
def execute_db_query(self, sql_query):
"""Execute a DB query"""
db = self._get_db_connection()
cursor = db.cursor()
try:
@ -52,6 +63,7 @@ class DBUtils(object):
results = cursor.fetchall()
db.commit()
LOG.debug("DB query execution succeeded: %s" % sql_query)
db.close()
except:
db.rollback()
LOG.debug("DB query execution failed: %s" % sql_query)

View File

@ -0,0 +1,5 @@
[DATABASE]
name = quantum_l2network
user = <put_db_user_name_here>
pass = <put_db_password_here>
host = <put_quantum_mysql_host_here>

View File

@ -3,6 +3,8 @@
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>
#Port number where the SSH will be running at the Nexus Switch, e.g.: 22 (Default)
nexus_ssh_port=22
[DRIVER]
name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver

View File

@ -0,0 +1,254 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Nicira Networks, 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: Somik Behera, Nicira Networks, Inc.
# @author: Brad Hall, Nicira Networks, Inc.
# @author: Dan Wendlandt, Nicira Networks, Inc.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, exc, joinedload
from quantum.common import exceptions as q_exc
from quantum.plugins.cisco.db import models
_ENGINE = None
_MAKER = None
BASE = models.BASE
def configure_db(options):
"""
Establish the database, create an engine if needed, and
register the models.
:param options: Mapping of configuration options
"""
global _ENGINE
if not _ENGINE:
_ENGINE = create_engine(options['sql_connection'],
echo=False,
echo_pool=True,
pool_recycle=3600)
register_models()
def clear_db():
global _ENGINE
assert _ENGINE
for table in reversed(BASE.metadata.sorted_tables):
_ENGINE.execute(table.delete())
def get_session(autocommit=True, expire_on_commit=False):
"""Helper method to grab session"""
global _MAKER, _ENGINE
if not _MAKER:
assert _ENGINE
_MAKER = sessionmaker(bind=_ENGINE,
autocommit=autocommit,
expire_on_commit=expire_on_commit)
return _MAKER()
def register_models():
"""Register Models and create properties"""
global _ENGINE
assert _ENGINE
BASE.metadata.create_all(_ENGINE)
def unregister_models():
"""Unregister Models, useful clearing out data before testing"""
global _ENGINE
assert _ENGINE
BASE.metadata.drop_all(_ENGINE)
def _check_duplicate_net_name(tenant_id, net_name):
session = get_session()
try:
net = session.query(models.Network).\
filter_by(tenant_id=tenant_id, name=net_name).\
one()
raise q_exc.NetworkNameExists(tenant_id=tenant_id,
net_name=net_name, net_id=net.uuid)
except exc.NoResultFound:
# this is the "normal" path, as API spec specifies
# that net-names are unique within a tenant
pass
def network_create(tenant_id, name):
session = get_session()
_check_duplicate_net_name(tenant_id, name)
with session.begin():
net = models.Network(tenant_id, name)
session.add(net)
session.flush()
return net
def network_list(tenant_id):
session = get_session()
return session.query(models.Network).\
options(joinedload(models.Network.ports)). \
filter_by(tenant_id=tenant_id).\
all()
def network_get(net_id):
session = get_session()
try:
return session.query(models.Network).\
options(joinedload(models.Network.ports)). \
filter_by(uuid=net_id).\
one()
except exc.NoResultFound, e:
raise q_exc.NetworkNotFound(net_id=net_id)
def network_rename(tenant_id, net_id, new_name):
session = get_session()
net = network_get(net_id)
_check_duplicate_net_name(tenant_id, new_name)
net.name = new_name
session.merge(net)
session.flush()
return net
def network_destroy(net_id):
session = get_session()
try:
net = session.query(models.Network).\
filter_by(uuid=net_id).\
one()
session.delete(net)
session.flush()
return net
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=net_id)
def port_create(net_id, state=None):
# confirm network exists
network_get(net_id)
session = get_session()
with session.begin():
port = models.Port(net_id)
port['state'] = state or 'DOWN'
session.add(port)
session.flush()
return port
def port_list(net_id):
session = get_session()
return session.query(models.Port).\
options(joinedload(models.Port.network)). \
filter_by(network_id=net_id).\
all()
def port_get(net_id, port_id):
# confirm network exists
network_get(net_id)
session = get_session()
try:
return session.query(models.Port).\
filter_by(uuid=port_id).\
filter_by(network_id=net_id).\
one()
except exc.NoResultFound:
raise q_exc.PortNotFound(net_id=net_id, port_id=port_id)
def port_set_state(net_id, port_id, new_state):
if new_state not in ('ACTIVE', 'DOWN'):
raise q_exc.StateInvalid(port_state=new_state)
# confirm network exists
network_get(net_id)
port = port_get(net_id, port_id)
session = get_session()
port.state = new_state
session.merge(port)
session.flush()
return port
def port_set_attachment(net_id, port_id, new_interface_id):
# confirm network exists
network_get(net_id)
session = get_session()
port = port_get(net_id, port_id)
if new_interface_id != "":
# We are setting, not clearing, the attachment-id
if port['interface_id']:
raise q_exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port['interface_id'])
try:
port = session.query(models.Port).\
filter_by(interface_id=new_interface_id).\
one()
raise q_exc.AlreadyAttached(net_id=net_id,
port_id=port_id,
att_id=new_interface_id,
att_port_id=port['uuid'])
except exc.NoResultFound:
# this is what should happen
pass
port.interface_id = new_interface_id
session.merge(port)
session.flush()
return port
def port_unset_attachment(net_id, port_id):
# confirm network exists
network_get(net_id)
session = get_session()
port = port_get(net_id, port_id)
port.interface_id = None
session.merge(port)
session.flush()
return port
def port_destroy(net_id, port_id):
# confirm network exists
network_get(net_id)
session = get_session()
try:
port = session.query(models.Port).\
filter_by(uuid=port_id).\
filter_by(network_id=net_id).\
one()
if port['interface_id']:
raise q_exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port['interface_id'])
session.delete(port)
session.flush()
return port
except exc.NoResultFound:
raise q_exc.PortNotFound(port_id=port_id)

View File

@ -0,0 +1,346 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# @author: Rohit Agarwalla, Cisco Systems, Inc.
from sqlalchemy.orm import exc
from quantum.common import exceptions as q_exc
from quantum.plugins.cisco import l2network_plugin_configuration as conf
from quantum.plugins.cisco.common import cisco_exceptions as c_exc
from quantum.plugins.cisco.db import l2network_models
import quantum.plugins.cisco.db.api as db
def initialize():
'Establish database connection and load models'
options = {"sql_connection": "mysql://%s:%s@%s/%s" % (conf.DB_USER,
conf.DB_PASS, conf.DB_HOST, conf.DB_NAME)}
db.configure_db(options)
def create_vlanids():
"""Prepopulates the vlan_bindings table"""
session = db.get_session()
try:
vlanid = session.query(l2network_models.VlanID).\
one()
except exc.MultipleResultsFound:
pass
except exc.NoResultFound:
start = int(conf.VLAN_START)
end = int(conf.VLAN_END)
while start <= end:
vlanid = l2network_models.VlanID(start)
session.add(vlanid)
start += 1
session.flush()
return
def get_all_vlanids():
"""Gets all the vlanids"""
session = db.get_session()
try:
vlanids = session.query(l2network_models.VlanID).\
all()
return vlanids
except exc.NoResultFound:
return []
def is_vlanid_used(vlan_id):
"""Checks if a vlanid is in use"""
session = db.get_session()
try:
vlanid = session.query(l2network_models.VlanID).\
filter_by(vlan_id=vlan_id).\
one()
return vlanid["vlan_used"]
except exc.NoResultFound:
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
def release_vlanid(vlan_id):
"""Sets the vlanid state to be unused"""
session = db.get_session()
try:
vlanid = session.query(l2network_models.VlanID).\
filter_by(vlan_id=vlan_id).\
one()
vlanid["vlan_used"] = False
session.merge(vlanid)
session.flush()
return vlanid["vlan_used"]
except exc.NoResultFound:
raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
return
def delete_vlanid(vlan_id):
"""Deletes a vlanid entry from db"""
session = db.get_session()
try:
vlanid = session.query(l2network_models.VlanID).\
filter_by(vlan_id=vlan_id).\
one()
session.delete(vlanid)
session.flush()
return vlanid
except exc.NoResultFound:
pass
def reserve_vlanid():
"""Reserves the first unused vlanid"""
session = db.get_session()
try:
rvlan = session.query(l2network_models.VlanID).\
filter_by(vlan_used=False).\
first()
rvlanid = session.query(l2network_models.VlanID).\
filter_by(vlan_id=rvlan["vlan_id"]).\
one()
rvlanid["vlan_used"] = True
session.merge(rvlanid)
session.flush()
return rvlan["vlan_id"]
except exc.NoResultFound:
raise c_exc.VlanIDNotAvailable()
def get_all_vlan_bindings():
"""Lists all the vlan to network associations"""
session = db.get_session()
try:
bindings = session.query(l2network_models.VlanBinding).\
all()
return bindings
except exc.NoResultFound:
return []
def get_vlan_binding(netid):
"""Lists the vlan given a network_id"""
session = db.get_session()
try:
binding = session.query(l2network_models.VlanBinding).\
filter_by(network_id=netid).\
one()
return binding
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=netid)
def add_vlan_binding(vlanid, vlanname, netid):
"""Adds a vlan to network association"""
session = db.get_session()
try:
binding = session.query(l2network_models.VlanBinding).\
filter_by(vlan_id=vlanid).\
one()
raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
network_id=netid)
except exc.NoResultFound:
binding = l2network_models.VlanBinding(vlanid, vlanname, netid)
session.add(binding)
session.flush()
return binding
def remove_vlan_binding(netid):
"""Removes a vlan to network association"""
session = db.get_session()
try:
binding = session.query(l2network_models.VlanBinding).\
filter_by(network_id=netid).\
one()
session.delete(binding)
session.flush()
return binding
except exc.NoResultFound:
pass
def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
"""Updates a vlan to network association"""
session = db.get_session()
try:
binding = session.query(l2network_models.VlanBinding).\
filter_by(network_id=netid).\
one()
if newvlanid:
binding["vlan_id"] = newvlanid
if newvlanname:
binding["vlan_name"] = newvlanname
session.merge(binding)
session.flush()
return binding
except exc.NoResultFound:
raise q_exc.NetworkNotFound(net_id=netid)
def get_all_portprofiles():
"""Lists all the port profiles"""
session = db.get_session()
try:
pps = session.query(l2network_models.PortProfile).\
all()
return pps
except exc.NoResultFound:
return []
def get_portprofile(tenantid, ppid):
"""Lists a port profile"""
session = db.get_session()
try:
pp = session.query(l2network_models.PortProfile).\
filter_by(uuid=ppid).\
one()
return pp
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)
def add_portprofile(tenantid, ppname, vlanid, qos):
"""Adds a port profile"""
session = db.get_session()
try:
pp = session.query(l2network_models.PortProfile).\
filter_by(name=ppname).\
one()
raise c_exc.PortProfileAlreadyExists(tenant_id=tenantid,
pp_name=ppname)
except exc.NoResultFound:
pp = l2network_models.PortProfile(ppname, vlanid, qos)
session.add(pp)
session.flush()
return pp
def remove_portprofile(tenantid, ppid):
"""Removes a port profile"""
session = db.get_session()
try:
pp = session.query(l2network_models.PortProfile).\
filter_by(uuid=ppid).\
one()
session.delete(pp)
session.flush()
return pp
except exc.NoResultFound:
pass
def update_portprofile(tenantid, ppid, newppname=None, newvlanid=None,
newqos=None):
"""Updates port profile"""
session = db.get_session()
try:
pp = session.query(l2network_models.PortProfile).\
filter_by(uuid=ppid).\
one()
if newppname:
pp["name"] = newppname
if newvlanid:
pp["vlan_id"] = newvlanid
if newqos:
pp["qos"] = newqos
session.merge(pp)
session.flush()
return pp
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)
def get_all_pp_bindings():
"""Lists all the port profiles"""
session = db.get_session()
try:
bindings = session.query(l2network_models.PortProfileBinding).\
all()
return bindings
except exc.NoResultFound:
return []
def get_pp_binding(tenantid, ppid):
"""Lists a port profile binding"""
session = db.get_session()
try:
binding = session.query(l2network_models.PortProfileBinding).\
filter_by(portprofile_id=ppid).\
one()
return binding
except exc.NoResultFound:
return []
def add_pp_binding(tenantid, portid, ppid, default):
"""Adds a port profile binding"""
session = db.get_session()
try:
binding = session.query(l2network_models.PortProfileBinding).\
filter_by(portprofile_id=ppid).\
one()
raise c_exc.PortProfileBindingAlreadyExists(pp_id=ppid,
port_id=portid)
except exc.NoResultFound:
binding = l2network_models.PortProfileBinding(tenantid, portid, \
ppid, default)
session.add(binding)
session.flush()
return binding
def remove_pp_binding(tenantid, portid, ppid):
"""Removes a port profile binding"""
session = db.get_session()
try:
binding = session.query(l2network_models.PortProfileBinding).\
filter_by(portprofile_id=ppid).\
filter_by(port_id=portid).\
one()
session.delete(binding)
session.flush()
return binding
except exc.NoResultFound:
pass
def update_pp_binding(tenantid, ppid, newtenantid=None, newportid=None,
newdefault=None):
"""Updates port profile binding"""
session = db.get_session()
try:
binding = session.query(l2network_models.PortProfileBinding).\
filter_by(portprofile_id=ppid).\
one()
if newtenantid:
binding["tenant_id"] = newtenantid
if newportid:
binding["port_id"] = newportid
if newdefault:
binding["default"] = newdefault
session.merge(binding)
session.flush()
return binding
except exc.NoResultFound:
raise c_exc.PortProfileNotFound(tenant_id=tenantid,
portprofile_id=ppid)

View File

@ -0,0 +1,147 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# @author: Rohit Agarwalla, Cisco Systems, Inc.
import uuid
from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
from sqlalchemy.orm import relation, object_mapper
from quantum.plugins.cisco.db.models import BASE
from quantum.plugins.cisco.db import models
class L2NetworkBase(object):
"""Base class for L2Network Models."""
__table_args__ = {'mysql_engine': 'InnoDB'}
def __setitem__(self, key, value):
"""Internal Dict set method"""
setattr(self, key, value)
def __getitem__(self, key):
"""Internal Dict get method"""
return getattr(self, key)
def get(self, key, default=None):
"""Dict get method"""
return getattr(self, key, default)
def __iter__(self):
"""Iterate over table columns"""
self._i = iter(object_mapper(self).columns)
return self
def next(self):
"""Next method for the iterator"""
n = self._i.next().name
return n, getattr(self, n)
def update(self, values):
"""Make the model object behave like a dict"""
for k, v in values.iteritems():
setattr(self, k, v)
def iteritems(self):
"""Make the model object behave like a dict"
Includes attributes from joins."""
local = dict(self)
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
if not k[0] == '_'])
local.update(joined)
return local.iteritems()
class VlanID(BASE, L2NetworkBase):
"""Represents a vlan_id usage"""
__tablename__ = 'vlan_ids'
vlan_id = Column(Integer, primary_key=True)
vlan_used = Column(Boolean)
def __init__(self, vlan_id):
self.vlan_id = vlan_id
self.vlan_used = False
def __repr__(self):
return "<VlanBinding(%d,%s)>" % \
(self.vlan_id, self.vlan_used)
class VlanBinding(BASE, L2NetworkBase):
"""Represents a binding of vlan_id to network_id"""
__tablename__ = 'vlan_bindings'
vlan_id = Column(Integer, primary_key=True)
vlan_name = Column(String(255))
network_id = Column(String(255), ForeignKey("networks.uuid"), \
nullable=False)
network = relation(models.Network, uselist=False)
def __init__(self, vlan_id, vlan_name, network_id):
self.vlan_id = vlan_id
self.vlan_name = vlan_name
self.network_id = network_id
def __repr__(self):
return "<VlanBinding(%d,%s,%s)>" % \
(self.vlan_id, self.vlan_name, self.network_id)
class PortProfile(BASE, L2NetworkBase):
"""Represents L2 network plugin level PortProfile for a network"""
__tablename__ = 'portprofiles'
uuid = Column(String(255), primary_key=True)
name = Column(String(255))
vlan_id = Column(Integer)
qos = Column(String(255))
def __init__(self, name, vlan_id, qos=None):
self.uuid = uuid.uuid4()
self.name = name
self.vlan_id = vlan_id
self.qos = qos
def __repr__(self):
return "<PortProfile(%s,%s,%d,%s)>" % \
(self.uuid, self.name, self.vlan_id, self.qos)
class PortProfileBinding(BASE, L2NetworkBase):
"""Represents PortProfile binding to tenant and network"""
__tablename__ = 'portprofile_bindings'
id = Column(Integer, primary_key=True, autoincrement=True)
tenant_id = Column(String(255))
port_id = Column(String(255), ForeignKey("ports.uuid"), \
nullable=False)
portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"), \
nullable=False)
default = Column(Boolean)
ports = relation(models.Port)
portprofile = relation(PortProfile, uselist=False)
def __init__(self, tenant_id, port_id, portprofile_id, default):
self.tenant_id = tenant_id
self.port_id = port_id
self.portprofile_id = portprofile_id
self.default = default
def __repr__(self):
return "<PortProfile Binding(%s,%s,%s,%s)>" % \
(self.tenant_id, self.port_id, self.portprofile_id, self.default)

View File

@ -0,0 +1,102 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Nicira Networks, 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: Somik Behera, Nicira Networks, Inc.
# @author: Brad Hall, Nicira Networks, Inc.
# @author: Dan Wendlandt, Nicira Networks, Inc.
import uuid
from sqlalchemy import Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, object_mapper
BASE = declarative_base()
class QuantumBase(object):
"""Base class for Quantum Models."""
__table_args__ = {'mysql_engine': 'InnoDB'}
def __setitem__(self, key, value):
setattr(self, key, value)
def __getitem__(self, key):
return getattr(self, key)
def get(self, key, default=None):
return getattr(self, key, default)
def __iter__(self):
self._i = iter(object_mapper(self).columns)
return self
def next(self):
n = self._i.next().name
return n, getattr(self, n)
def update(self, values):
"""Make the model object behave like a dict"""
for k, v in values.iteritems():
setattr(self, k, v)
def iteritems(self):
"""Make the model object behave like a dict.
Includes attributes from joins."""
local = dict(self)
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
if not k[0] == '_'])
local.update(joined)
return local.iteritems()
class Port(BASE, QuantumBase):
"""Represents a port on a quantum network"""
__tablename__ = 'ports'
uuid = Column(String(255), primary_key=True)
network_id = Column(String(255), ForeignKey("networks.uuid"),
nullable=False)
interface_id = Column(String(255))
# Port state - Hardcoding string value at the moment
state = Column(String(8))
def __init__(self, network_id):
self.uuid = str(uuid.uuid4())
self.network_id = network_id
self.state = "DOWN"
def __repr__(self):
return "<Port(%s,%s,%s,%s)>" % (self.uuid, self.network_id,
self.state, self.interface_id)
class Network(BASE, QuantumBase):
"""Represents a quantum network"""
__tablename__ = 'networks'
uuid = Column(String(255), primary_key=True)
tenant_id = Column(String(255), nullable=False)
name = Column(String(255))
ports = relation(Port, order_by=Port.uuid, backref="network")
def __init__(self, tenant_id, name):
self.uuid = str(uuid.uuid4())
self.tenant_id = tenant_id
self.name = name
def __repr__(self):
return "<Network(%s,%s,%s)>" % \
(self.uuid, self.name, self.tenant_id)

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,12 +17,19 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
from abc import ABCMeta, abstractmethod
class L2DevicePluginBase(object):
"""
Base class for a device-specific plugin.
An example of a device-specific plugin is a Nexus switch plugin.
The network model relies on device-category-specific plugins to perform
the configuration on each device.
"""
__metaclass__ = ABCMeta
@ -133,7 +141,7 @@ class L2DevicePluginBase(object):
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is QuantumPluginBase:
if cls is L2DevicePluginBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
import logging as LOG
@ -30,71 +32,92 @@ LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2NetworkModel(L2NetworkModelBase):
"""
Implements the L2NetworkModelBase
This implementation works with UCS and Nexus plugin,
with one UCS blade, and one Nexus switch.
"""
_plugins = {}
def __init__(self):
for key in conf.plugins[const.PLUGINS].keys():
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])
conf.PLUGINS[const.PLUGINS][key])
LOG.debug("Loaded device plugin %s" % \
conf.PLUGINS[const.PLUGINS][key])
def _funcName(self, offset=0):
def _func_name(self, offset=0):
"""Get the name of the calling function"""
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 _invoke_all_device_plugins(self, function_name, args, kwargs):
"""Invoke all device plugins for this model implementation"""
for plugin_obj_ref in self._plugins.values():
getattr(plugin_obj_ref, function_name)(*args, **kwargs)
def _invokeUCSPlugin(self, function_name, args, kwargs):
def _invoke_ucs_plugin(self, function_name, args, kwargs):
"""Invoke only the UCS plugin"""
if const.UCS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.UCS_PLUGIN],
function_name)(*args, **kwargs)
def _invokeNexusPlugin(self, function_name, args, kwargs):
def _invoke_nexus_plugin(self, function_name, args, kwargs):
"""Invoke only the Nexus plugin"""
if const.NEXUS_PLUGIN in self._plugins.keys():
getattr(self._plugins[const.NEXUS_PLUGIN],
function_name)(*args, **kwargs)
def get_all_networks(self, args):
"""Not implemented for this model"""
pass
def create_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def delete_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def get_network_details(self, args):
"""Not implemented for this model"""
pass
def rename_network(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeAllDevicePlugins(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_all_device_plugins(self._func_name(), args, device_params)
def get_all_ports(self, args):
"""Not implemented for this model"""
pass
def create_port(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def delete_port(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def update_port(self, args):
"""Not implemented for this model"""
pass
def get_port_details(self, args):
"""Not implemented for this model"""
pass
def plug_interface(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)
def unplug_interface(self, args):
deviceParams = {const.DEVICE_IP: ""}
self._invokeUCSPlugin(self._funcName(), args, deviceParams)
"""Support for the Quantum core API call"""
device_params = {const.DEVICE_IP: ""}
self._invoke_ucs_plugin(self._func_name(), args, device_params)

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,12 +17,20 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
from abc import ABCMeta, abstractmethod
class L2NetworkModelBase(object):
"""
Base class for L2 Network Model
It relies on a pluggable network configuration module to gather
knowledge of the system, but knows which device-specific plugins
to invoke for a corresponding core API call, and what parameters to pass
to that plugin.
"""
__metaclass__ = ABCMeta
@ -131,7 +140,7 @@ class L2NetworkModelBase(object):
marked with the abstractmethod decorator is
provided by the plugin class.
"""
if cls is QuantumPluginBase:
if cls is L2NetworkModelBase:
for method in cls.__abstractmethods__:
method_ok = False
for base in klass.__mro__:

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import inspect
import logging as LOG
@ -26,22 +28,22 @@ 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
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.db import l2network_db as cdb
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2Network(QuantumPluginBase):
_networks = {}
_tenants = {}
_portprofiles = {}
""" L2 Network Framework Plugin """
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)
cdb.initialize()
# TODO (Sumit): The following should move to the segmentation module
cdb.create_vlanids()
"""
Core API implementation
@ -53,8 +55,16 @@ class L2Network(QuantumPluginBase):
the specified tenant.
"""
LOG.debug("get_all_networks() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id])
return self._networks.values()
self._invoke_device_plugins(self._func_name(), [tenant_id])
networks_list = db.network_list(tenant_id)
new_networks_list = []
for network in networks_list:
new_network_dict = self._make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
new_networks_list.append(new_network_dict)
return new_networks_list
def create_network(self, tenant_id, net_name):
"""
@ -62,22 +72,17 @@ class L2Network(QuantumPluginBase):
a symbolic name.
"""
LOG.debug("create_network() called\n")
new_net_id = self._get_unique_net_id(tenant_id)
new_network = db.network_create(tenant_id, net_name)
new_net_id = new_network[const.UUID]
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,
self._invoke_device_plugins(self._func_name(), [tenant_id, net_name,
new_net_id, vlan_name,
vlan_id])
cdb.add_vlan_binding(vlan_id, vlan_name, new_net_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
const.NET_PORTS: []}
return new_net_dict
def delete_network(self, tenant_id, net_id):
@ -86,22 +91,24 @@ class L2Network(QuantumPluginBase):
belonging to the specified tenant.
"""
LOG.debug("delete_network() called\n")
net = self._networks.get(net_id)
net = db.network_get(net_id)
if net:
if len(net[const.NET_PORTS].values()) > 0:
ports_on_net = net[const.NET_PORTS].values()
if len(net[const.NETWORKPORTS]) > 0:
ports_on_net = db.port_list(net_id)
for port in ports_on_net:
if port[const.ATTACHMENT]:
if port[const.INTERFACEID]:
raise exc.NetworkInUse(net_id=net_id)
for port in ports_on_net:
self.delete_port(tenant_id, net_id, port[const.PORT_ID])
self.delete_port(tenant_id, net_id, port[const.PORTID])
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
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
net_dict = self._make_net_dict(net[const.UUID],
net[const.NETWORKNAME],
[])
self._release_vlan_for_tenant(tenant_id, net_id)
cdb.remove_vlan_binding(net_id)
db.network_destroy(net_id)
return net_dict
# Network not found
raise exc.NetworkNotFound(net_id=net_id)
@ -110,12 +117,22 @@ class L2Network(QuantumPluginBase):
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}
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
network = db.network_get(net_id)
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = self._make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
ports_on_net.append(new_port)
new_network = self._make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
ports_on_net)
return new_network
def rename_network(self, tenant_id, net_id, new_name):
"""
@ -123,11 +140,13 @@ class L2Network(QuantumPluginBase):
Virtual Network.
"""
LOG.debug("rename_network() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
new_name])
network = self._get_network(tenant_id, net_id)
network[const.NET_NAME] = new_name
return network
network = db.network_rename(tenant_id, net_id, new_name)
net_dict = self._make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
return net_dict
def get_all_ports(self, tenant_id, net_id):
"""
@ -135,9 +154,17 @@ class L2Network(QuantumPluginBase):
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()
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id])
network = db.network_get(net_id)
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = self._make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
ports_on_net.append(new_port)
return ports_on_net
def create_port(self, tenant_id, net_id, port_state=None):
@ -145,16 +172,15 @@ class L2Network(QuantumPluginBase):
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 = db.port_create(net_id, port_state)
unique_port_id_string = port[const.UUID]
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
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
new_port_dict = self._make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
return new_port_dict
def delete_port(self, tenant_id, net_id, port_id):
@ -165,31 +191,24 @@ class L2Network(QuantumPluginBase):
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)
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
db.port_destroy(net_id, port_id)
new_port_dict = self._make_port_dict(port_id, None, None, None)
return new_port_dict
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,
self._invoke_device_plugins(self._func_name(), [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
db.port_set_state(net_id, port_id, port_state)
new_port_dict = self._make_port_dict(port_id, port_state, net_id,
None)
return new_port_dict
def get_port_details(self, tenant_id, net_id, port_id):
"""
@ -197,9 +216,14 @@ class L2Network(QuantumPluginBase):
that is attached to this particular port.
"""
LOG.debug("get_port_details() called\n")
self._invokeDevicePlugins(self._funcName(), [tenant_id, net_id,
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
return self._get_port(tenant_id, net_id, port_id)
port = db.port_get(net_id, port_id)
new_port_dict = self._make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
return new_port_dict
def plug_interface(self, tenant_id, net_id, port_id,
remote_interface_id):
@ -208,16 +232,10 @@ class L2Network(QuantumPluginBase):
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,
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id,
remote_interface_id])
port[const.ATTACHMENT] = remote_interface_id
db.port_set_attachment(net_id, port_id, remote_interface_id)
def unplug_interface(self, tenant_id, net_id, port_id):
"""
@ -225,170 +243,165 @@ class L2Network(QuantumPluginBase):
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,
self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
port_id])
port[const.ATTACHMENT] = None
db.port_unset_attachment(net_id, port_id)
"""
Extension API implementation
"""
def get_all_portprofiles(self, tenant_id):
return self._portprofiles.values()
"""Get all port profiles"""
pplist = cdb.get_all_portprofiles()
new_pplist = []
for portprofile in pplist:
new_pp = self._make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
new_pplist.append(new_pp)
return new_pplist
def get_portprofile_details(self, tenant_id, profile_id):
return self._get_portprofile(tenant_id, profile_id)
"""Get port profile details"""
portprofile = cdb.get_portprofile(tenant_id, profile_id)
new_pp = self._make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
def create_portprofile(self, tenant_id, profile_name, 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 create_portprofile(self, tenant_id, profile_name, qos):
"""Create port profile"""
portprofile = cdb.add_portprofile(tenant_id, profile_name,
const.NO_VLAN_ID, qos)
new_pp = self._make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
def delete_portprofile(self, tenant_id, profile_id):
portprofile = self._get_portprofile(tenant_id, profile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
if len(associations) > 0:
"""Delete portprofile"""
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception, exc:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
plist = cdb.get_pp_binding(tenant_id, profile_id)
if plist:
raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id,
profile_id=profile_id)
profile_id=profile_id)
else:
self._portprofiles.pop(profile_id)
tenant = self._get_tenant(tenant_id)
tenant[const.TENANT_PORTPROFILES].pop(profile_id)
cdb.remove_portprofile(tenant_id, 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
"""Rename port profile"""
try:
portprofile = cdb.get_portprofile(tenant_id, profile_id)
except Exception, exc:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=profile_id)
portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
new_pp = self._make_portprofile_dict(tenant_id,
portprofile[const.UUID],
portprofile[const.PPNAME],
portprofile[const.PPQOS])
return new_pp
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)
"""Associate port profile"""
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception, exc:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
cdb.add_pp_binding(tenant_id, port_id, portprofile_id, False)
def disassociate_portprofile(self, tenant_id, net_id,
port_id, portprofile_id):
portprofile = self._get_portprofile(tenant_id, portprofile_id)
associations = portprofile[const.PROFILE_ASSOCIATIONS]
associations.remove(port_id)
"""Disassociate port profile"""
try:
portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
except Exception, exc:
raise cexc.PortProfileNotFound(tenant_id=tenant_id,
portprofile_id=portprofile_id)
def create_defaultPProfile(self, tenant_id, network_id, profile_name,
vlan_id):
pass
cdb.remove_pp_binding(tenant_id, port_id, portprofile_id)
"""
Private functions
"""
def _invokeDevicePlugins(self, function_name, args):
def _invoke_device_plugins(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):
"""Get vlan ID"""
# TODO (Sumit):
# The VLAN ID for a tenant might need to be obtained from
# somewhere (from Donabe/Melange?)
# Also need to make sure that the VLAN ID is not being used already
# Currently, just a wrap-around counter ranging from VLAN_START to
# VLAN_END
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
return cdb.reserve_vlanid()
def _release_vlan_for_tenant(self, tenant_id, net_id):
"""Relase VLAN"""
vlan_binding = cdb.get_vlan_binding(net_id)
return cdb.release_vlanid(vlan_binding[const.VLANID])
def _get_vlan_name(self, net_id, vlan):
vlan_name = conf.VLAN_NAME_PREFIX + net_id + "-" + vlan
"""Getting the vlan name from the tenant and vlan"""
vlan_name = conf.VLAN_NAME_PREFIX + vlan
return vlan_name
def _validate_port_state(self, port_state):
"""Checking the port state"""
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
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):
def _func_name(self, offset=0):
"""Getting the name of the calling funciton"""
return inspect.stack()[1 + offset][3]
"""
TODO (Sumit):
(1) Persistent storage
"""
def _make_net_dict(self, net_id, net_name, ports):
"""Helper funciton"""
res = {const.NET_ID: net_id, const.NET_NAME: net_name}
res[const.NET_PORTS] = ports
return res
def _make_port_dict(self, port_id, port_state, net_id, attachment):
"""Helper funciton"""
res = {const.PORT_ID: port_id, const.PORT_STATE: port_state}
res[const.NET_ID] = net_id
res[const.ATTACHMENT] = attachment
return res
def _make_portprofile_dict(self, tenant_id, profile_id, profile_name,
qos):
"""Helper funciton"""
profile_associations = self._make_portprofile_assc_list(tenant_id,
profile_id)
res = {const.PROFILE_ID: str(profile_id),
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: profile_associations,
const.PROFILE_VLAN_ID: None,
const.PROFILE_QOS: qos}
return res
def _make_portprofile_assc_list(self, tenant_id, profile_id):
"""Helper function to create port profile association list"""
plist = cdb.get_pp_binding(tenant_id, profile_id)
assc_list = []
for port in plist:
assc_list.append(port[const.PORTID])
return assc_list

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -15,7 +16,8 @@
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
# @author: Rohit Agarwalla, Cisco Systems, Inc.
"""
import os
@ -23,28 +25,43 @@ 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)
CONF_PARSER_OBJ = 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_CONF = CONF_PARSER_OBJ['VLANS']
VLAN_NAME_PREFIX = SECTION_CONF['vlan_name_prefix']
VLAN_START = SECTION_CONF['vlan_start']
VLAN_END = SECTION_CONF['vlan_end']
section = cp['PORTS']
MAX_PORTS = section['max_ports']
SECTION_CONF = CONF_PARSER_OBJ['PORTS']
MAX_PORTS = SECTION_CONF['max_ports']
section = cp['PORTPROFILES']
MAX_PORT_PROFILES = section['max_port_profiles']
SECTION_CONF = CONF_PARSER_OBJ['PORTPROFILES']
MAX_PORT_PROFILES = SECTION_CONF['max_port_profiles']
section = cp['NETWORKS']
MAX_NETWORKS = section['max_networks']
SECTION_CONF = CONF_PARSER_OBJ['NETWORKS']
MAX_NETWORKS = SECTION_CONF['max_networks']
section = cp['MODEL']
MODEL_CLASS = section['model_class']
SECTION_CONF = CONF_PARSER_OBJ['MODEL']
MODEL_CLASS = SECTION_CONF['model_class']
CONF_FILE = "conf/plugins.ini"
cp = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \
+ "/" + CONF_FILE)
plugins = cp.walk(cp.dummy)
CONF_PARSER_OBJ = confp.\
CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) + \
"/" + CONF_FILE)
PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy)
CONF_FILE = "conf/db_conn.ini"
CONF_PARSER_OBJ = confp.\
CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) + \
"/" + CONF_FILE)
SECTION_CONF = CONF_PARSER_OBJ['DATABASE']
DB_NAME = SECTION_CONF['name']
DB_USER = SECTION_CONF['user']
DB_PASS = SECTION_CONF['pass']
DB_HOST = SECTION_CONF['host']

View File

@ -15,4 +15,7 @@
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
# @author: Edgar Magana, Cisco Systems, Inc.
"""
Init module for Nexus Driver
"""

View File

@ -17,19 +17,24 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc.
#
"""
Configuration consolidation for the Nexus Driver
This module will export the configuration parameters
from the nexus.ini file
"""
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__)) \
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['SWITCH']
NEXUS_IP_ADDRESS = SECTION['nexus_ip_address']
NEXUS_PORT = SECTION['nexus_port']
NEXUS_SSH_PORT = SECTION['nexus_ssh_port']
section = cp['DRIVER']
NEXUS_DRIVER = section['name']
SECTION = CP['DRIVER']
NEXUS_DRIVER = SECTION['name']

View File

@ -22,11 +22,9 @@ 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 quantum.plugins.cisco.nexus import cisco_nexus_snippets as snipp
from ncclient import manager
@ -34,193 +32,101 @@ 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():
"""
Nexus Driver Main Class
"""
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 nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user,
nexus_password):
"""
Makes the SSH connection to the Nexus Switch
"""
man = manager.connect(host=nexus_host, port=nexus_ssh_port,
username=nexus_user, password=nexus_password)
return man
def create_xml_snippet(self, cutomized_config):
"""
Creates the Proper XML structure for the Nexus Switch Configuration
"""
conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config)
return conf_xml_snippet
def enable_vlan(self, mgr, vlanid, vlanname):
confstr = cmd_vlan_conf_snippet % (vlanid, vlanname)
confstr = exec_conf_prefix + confstr + exec_conf_postfix
"""
Creates a VLAN on Nexus Switch given the VLAN ID and Name
"""
confstr = snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname)
confstr = self.create_xml_snippet(confstr)
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
"""
Delete a VLAN on Nexus Switch given the VLAN ID
"""
confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid
confstr = self.create_xml_snippet(confstr)
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
"""
Enables trunk mode an interface on Nexus Switch
"""
confstr = snipp.CMD_PORT_TRUNK % (interface)
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: %s" % 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
"""
Disables trunk mode an interface on Nexus Switch
"""
confstr = snipp.CMD_NO_SWITCHPORT % (interface)
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: %s" % 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
"""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID
"""
confstr = snipp.CMD_VLAN_INT_SNIPPET % (interface, vlanid)
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: %s" % 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
"""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID
"""
confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: %s" % 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)
nexus_password, nexus_interface, nexus_ssh_port):
"""
Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
given the VLAN ID and Name and Interface Number
"""
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
nexus_password) as man:
self.enable_vlan(man, vlan_id, vlan_name)
self.enable_vlan_on_trunk_int(man, nexus_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)
nexus_password, nexus_interface, nexus_ssh_port):
"""
Delete a VLAN and Disables trunk mode an interface on Nexus Switch
given the VLAN ID and Interface Number
"""
with self.nxos_connect(nexus_host, int(nexus_ssh_port), nexus_user,
nexus_password) as man:
self.disable_vlan(man, vlan_id)
self.disable_switch_port(man, nexus_interface)

View File

@ -17,14 +17,15 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Edgar Magana, Cisco Systems, Inc.
#
"""
PlugIn for Nexus OS driver
"""
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
@ -33,16 +34,22 @@ LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class NexusPlugin(L2DevicePluginBase):
"""
Nexus PLugIn Main Class
"""
_networks = {}
def __init__(self):
"""
Extracts the configuration parameters from the configuration file
"""
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
self._nexus_ssh_port = conf.NEXUS_SSH_PORT
def get_all_networks(self, tenant_id):
"""
@ -61,7 +68,8 @@ class NexusPlugin(L2DevicePluginBase):
"""
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)
self._nexus_username, self._nexus_password, self._nexus_port,
self._nexus_ssh_port)
new_net_dict = {const.NET_ID: net_id,
const.NET_NAME: net_name,
@ -81,7 +89,8 @@ class NexusPlugin(L2DevicePluginBase):
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._nexus_username, self._nexus_password, self._nexus_port,
self._nexus_ssh_port)
self._networks.pop(net_id)
return net
# Network not found
@ -100,7 +109,6 @@ class NexusPlugin(L2DevicePluginBase):
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
@ -157,11 +165,17 @@ class NexusPlugin(L2DevicePluginBase):
LOG.debug("NexusPlugin:unplug_interface() called\n")
def _get_vlan_id_for_network(self, tenant_id, network_id):
"""
Obtain the VLAN ID given the 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):
"""
Gets the NETWORK ID
"""
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)

View File

@ -0,0 +1,156 @@
# 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: Edgar Magana, Cisco Systems, Inc.
"""
Nexus-OS XML-based configuration snippets
"""
import logging as LOG
from quantum.plugins.cisco.common import cisco_constants as const
LOG.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
# The following are standard strings, messages used to communicate with Nexus,
EXEC_CONF_SNIPPET = """
<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>%s
</__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>import logging as LOG
<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>C: 1: Missing docstring
</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>C: 1: Missing docstring
<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>
"""

View File

View File

@ -0,0 +1,96 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
from nova import exception as excp
from nova import flags
from nova import log as logging
from nova.scheduler import driver
from quantum.client import Client
from quantum.common.wsgi import Serializer
LOG = logging.getLogger('quantum.plugins.cisco.nova.quantum_aware_scheduler')
FLAGS = flags.FLAGS
flags.DEFINE_string('quantum_host', "127.0.0.1",
'IP address of the quantum network service.')
flags.DEFINE_integer('quantum_port', 9696,
'Listening port for Quantum network service')
HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'
CSCO_EXT_NAME = 'Cisco Nova Tenant'
class QuantumScheduler(driver.Scheduler):
"""
Quantum network service dependent scheduler
Obtains the hostname from Quantum using an extension API
"""
def __init__(self):
# We have to send a dummy tenant name here since the client
# needs some tenant name, but the tenant name will not be used
# since the extensions URL does not require it
client = Client(HOST, PORT, USE_SSL, format='json',
action_prefix=ACTION_PREFIX_EXT, tenant="dummy")
request_url = "/extensions"
data = client.do_request('GET', request_url)
LOG.debug("Obtained supported extensions from Quantum: %s" % data)
for ext in data['extensions']:
name = ext['name']
if name == CSCO_EXT_NAME:
LOG.debug("Quantum plugin supports required \"%s\" extension"
"for the scheduler." % name)
return
LOG.error("Quantum plugin does not support required \"%s\" extension"
" for the scheduler. Scheduler will quit." % CSCO_EXT_NAME)
raise excp.ServiceUnavailable()
def schedule(self, context, topic, *args, **kwargs):
"""Gets the host name from the Quantum service"""
instance_id = kwargs['instance_id']
user_id = \
kwargs['request_spec']['instance_properties']['user_id']
project_id = \
kwargs['request_spec']['instance_properties']['project_id']
instance_data_dict = \
{'novatenant': \
{'instance_id': instance_id,
'instance_desc': \
{'user_id': user_id,
'project_id': project_id}}}
client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
action_prefix=ACTION_PREFIX_CSCO)
request_url = "/novatenants/" + project_id + "/get_host"
data = client.do_request('PUT', request_url, body=instance_data_dict)
hostname = data["host_list"]["host_1"]
if not hostname:
raise driver.NoValidHost(_("Scheduler was unable to locate a host"
" for this request. Is the appropriate"
" service running?"))
LOG.debug(_("Quantum service returned host: %s") % hostname)
return hostname

View File

@ -0,0 +1,112 @@
# 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.
#
"""VIF drivers for interface type direct."""
from nova import exception as excp
from nova import flags
from nova import log as logging
from nova.network import linux_net
from nova.virt.libvirt import netutils
from nova import utils
from nova.virt.vif import VIFDriver
from quantum.client import Client
from quantum.common.wsgi import Serializer
LOG = logging.getLogger('quantum.plugins.cisco.nova.vifdirect')
FLAGS = flags.FLAGS
flags.DEFINE_string('quantum_host', "127.0.0.1",
'IP address of the quantum network service.')
flags.DEFINE_integer('quantum_port', 9696,
'Listening port for Quantum network service')
HOST = FLAGS.quantum_host
PORT = FLAGS.quantum_port
USE_SSL = False
TENANT_ID = 'nova'
ACTION_PREFIX_EXT = '/v0.1'
ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
'/extensions/csco/tenants/{tenant_id}'
TENANT_ID = 'nova'
CSCO_EXT_NAME = 'Cisco Nova Tenant'
class Libvirt802dot1QbhDriver(VIFDriver):
"""VIF driver for Linux bridge."""
def __init__(self):
# We have to send a dummy tenant name here since the client
# needs some tenant name, but the tenant name will not be used
# since the extensions URL does not require it
client = Client(HOST, PORT, USE_SSL, format='json',
action_prefix=ACTION_PREFIX_EXT, tenant="dummy")
request_url = "/extensions"
data = client.do_request('GET', request_url)
LOG.debug("Obtained supported extensions from Quantum: %s" % data)
for ext in data['extensions']:
name = ext['name']
if name == CSCO_EXT_NAME:
LOG.debug("Quantum plugin supports required \"%s\" extension"
"for the VIF driver." % name)
return
LOG.error("Quantum plugin does not support required \"%s\" extension"
" for the VIF driver. nova-compute will quit." \
% CSCO_EXT_NAME)
raise excp.ServiceUnavailable()
def _get_configurations(self, instance, network, mapping):
"""Gets the device name and the profile name from Quantum"""
instance_id = instance['id']
user_id = instance['user_id']
project_id = instance['project_id']
vif_id = mapping['vif_uuid']
instance_data_dict = \
{'novatenant': \
{'instance_id': instance_id,
'instance_desc': \
{'user_id': user_id,
'project_id': project_id,
'vif_id': vif_id}}}
client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
action_prefix=ACTION_PREFIX_CSCO)
request_url = "/novatenants/" + project_id + "/get_instance_port"
data = client.do_request('PUT', request_url, body=instance_data_dict)
device = data['vif_desc']['device']
portprofile = data['vif_desc']['portprofile']
LOG.debug(_("Quantum provided the device: %s") % device)
LOG.debug(_("Quantum provided the portprofile: %s") % portprofile)
mac_id = mapping['mac'].replace(':', '')
result = {
'id': mac_id,
'mac_address': mapping['mac'],
'device_name': device,
'profile_name': portprofile,
}
return result
def plug(self, instance, network, mapping):
return self._get_configurations(instance, network, mapping)
def unplug(self, instance, network, mapping):
pass

View File

@ -16,50 +16,34 @@
# 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
"""Unittest runner for quantum Cisco plugin
This file should be run from the top dir in the quantum directory
To run all test::
python run_tests.py
python quantum/plugins/cisco/run_tests.py
To run all unit tests::
python run_tests.py unit
python quantum/plugins/cisco/run_tests.py quantum.plugins.cisco.tests.unit
To run all functional tests::
python run_tests.py functional
python quantum/plugins/cisco/run_tests.py functional
To run a single unit test::
python run_tests.py unit.test_stores:TestSwiftBackend.test_get
python quantum/plugins/cisco/run_tests.py \
quantum.plugins.cisco.tests.unit.test_stores:TestSwiftBackend.test_get
To run a single functional test::
python run_tests.py functional.test_service:TestController.test_create
python quantum/plugins/cisco/run_tests.py \
quantum.plugins.cisco.tests.functional.test_service \
:TestController.test_create
To run a single unit test module::
python run_tests.py unit.test_stores
python quantum/plugins/cisco/run_tests.py unit.test_stores
To run a single functional test module::
python run_tests.py functional.test_stores
python quantum/plugins/cisco/run_tests.py functional.test_stores
"""
import gettext
@ -69,233 +53,37 @@ import unittest
import sys
from nose import config
from nose import result
from nose import core
sys.path.append(os.getcwd())
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)
from quantum.common.test_lib import run_tests, test_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)
exit_status = False
# if a single test case was specified,
# we should only invoked the tests once
invoke_once = len(sys.argv) > 1
cwd = os.getcwd()
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))
exit_status = run_tests(c)
if invoke_once:
sys.exit(0)
os.chdir(cwd)
working_dir = os.path.abspath("quantum/plugins/cisco/tests")
c = config.Config(stream=sys.stdout,
env=os.environ,
verbosity=3,
workingDir=working_dir)
exit_status = exit_status or run_tests(c)
sys.exit(exit_status)

View File

@ -0,0 +1,840 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011, Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# @author: Rohit Agarwalla, Cisco Systems, Inc.
"""
test_database.py is an independent test suite
that tests the database api method calls
"""
import logging as LOG
import unittest
from quantum.plugins.cisco.common import cisco_constants as const
import quantum.plugins.cisco.db.api as db
import quantum.plugins.cisco.db.l2network_db as l2network_db
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class L2networkDB(object):
"""Class conisting of methods to call L2network db methods"""
def get_all_vlan_bindings(self):
"""Get all vlan binding into a list of dict"""
vlans = []
try:
for vlan_bind in l2network_db.get_all_vlan_bindings():
LOG.debug("Getting vlan bindings for vlan: %s" % \
vlan_bind.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
vlan_dict["vlan-name"] = vlan_bind.vlan_name
vlan_dict["net-id"] = str(vlan_bind.network_id)
vlans.append(vlan_dict)
except Exception, exc:
LOG.error("Failed to get all vlan bindings: %s" % str(exc))
return vlans
def get_vlan_binding(self, network_id):
"""Get a vlan binding"""
vlan = []
try:
for vlan_bind in l2network_db.get_vlan_binding(network_id):
LOG.debug("Getting vlan binding for vlan: %s" \
% vlan_bind.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
vlan_dict["vlan-name"] = vlan_bind.vlan_name
vlan_dict["net-id"] = str(vlan_bind.network_id)
vlan.append(vlan_dict)
except Exception, exc:
LOG.error("Failed to get vlan binding: %s" % str(exc))
return vlan
def create_vlan_binding(self, vlan_id, vlan_name, network_id):
"""Create a vlan binding"""
vlan_dict = {}
try:
res = l2network_db.add_vlan_binding(vlan_id, vlan_name, network_id)
LOG.debug("Created vlan binding for vlan: %s" % res.vlan_id)
vlan_dict["vlan-id"] = str(res.vlan_id)
vlan_dict["vlan-name"] = res.vlan_name
vlan_dict["net-id"] = str(res.network_id)
return vlan_dict
except Exception, exc:
LOG.error("Failed to create vlan binding: %s" % str(exc))
def delete_vlan_binding(self, network_id):
"""Delete a vlan binding"""
try:
res = l2network_db.remove_vlan_binding(network_id)
LOG.debug("Deleted vlan binding for vlan: %s" % res.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(res.vlan_id)
return vlan_dict
except Exception, exc:
raise Exception("Failed to delete vlan binding: %s" % str(exc))
def update_vlan_binding(self, network_id, vlan_id, vlan_name):
"""Update a vlan binding"""
try:
res = l2network_db.update_vlan_binding(network_id, vlan_id, \
vlan_name)
LOG.debug("Updating vlan binding for vlan: %s" % res.vlan_id)
vlan_dict = {}
vlan_dict["vlan-id"] = str(res.vlan_id)
vlan_dict["vlan-name"] = res.vlan_name
vlan_dict["net-id"] = str(res.network_id)
return vlan_dict
except Exception, exc:
raise Exception("Failed to update vlan binding: %s" % str(exc))
def get_all_portprofiles(self):
"""Get all portprofiles"""
pps = []
try:
for portprof in l2network_db.get_all_portprofiles():
LOG.debug("Getting port profile : %s" % portprof.uuid)
pp_dict = {}
pp_dict["portprofile-id"] = str(portprof.uuid)
pp_dict["portprofile-name"] = portprof.name
pp_dict["vlan-id"] = str(portprof.vlan_id)
pp_dict["qos"] = portprof.qos
pps.append(pp_dict)
except Exception, exc:
LOG.error("Failed to get all port profiles: %s" % str(exc))
return pps
def get_portprofile(self, tenant_id, pp_id):
"""Get a portprofile"""
pp_list = []
try:
for portprof in l2network_db.get_portprofile(tenant_id, pp_id):
LOG.debug("Getting port profile : %s" % portprof.uuid)
pp_dict = {}
pp_dict["portprofile-id"] = str(portprof.uuid)
pp_dict["portprofile-name"] = portprof.name
pp_dict["vlan-id"] = str(portprof.vlan_id)
pp_dict["qos"] = portprof.qos
pp_list.append(pp_dict)
except Exception, exc:
LOG.error("Failed to get port profile: %s" % str(exc))
return pp
def create_portprofile(self, tenant_id, name, vlan_id, qos):
"""Create a portprofile"""
pp_dict = {}
try:
res = l2network_db.add_portprofile(tenant_id, name, vlan_id, qos)
LOG.debug("Created port profile: %s" % res.uuid)
pp_dict["portprofile-id"] = str(res.uuid)
pp_dict["portprofile-name"] = res.name
pp_dict["vlan-id"] = str(res.vlan_id)
pp_dict["qos"] = res.qos
return pp_dict
except Exception, exc:
LOG.error("Failed to create port profile: %s" % str(exc))
def delete_portprofile(self, tenant_id, pp_id):
"""Delete a portprofile"""
try:
res = l2network_db.remove_portprofile(tenant_id, pp_id)
LOG.debug("Deleted port profile : %s" % res.uuid)
pp_dict = {}
pp_dict["pp-id"] = str(res.uuid)
return pp_dict
except Exception, exc:
raise Exception("Failed to delete port profile: %s" % str(exc))
def update_portprofile(self, tenant_id, pp_id, name, vlan_id, qos):
"""Update a portprofile"""
try:
res = l2network_db.update_portprofile(tenant_id, pp_id, name,
vlan_id, qos)
LOG.debug("Updating port profile : %s" % res.uuid)
pp_dict = {}
pp_dict["portprofile-id"] = str(res.uuid)
pp_dict["portprofile-name"] = res.name
pp_dict["vlan-id"] = str(res.vlan_id)
pp_dict["qos"] = res.qos
return pp_dict
except Exception, exc:
raise Exception("Failed to update port profile: %s" % str(exc))
def get_all_pp_bindings(self):
"""Get all portprofile bindings"""
pp_bindings = []
try:
for pp_bind in l2network_db.get_all_pp_bindings():
LOG.debug("Getting port profile binding: %s" % \
pp_bind.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(pp_bind.portprofile_id)
ppbinding_dict["port-id"] = str(pp_bind.port_id)
ppbinding_dict["tenant-id"] = pp_bind.tenant_id
ppbinding_dict["default"] = pp_bind.default
pp_bindings.append(ppbinding_dict)
except Exception, exc:
LOG.error("Failed to get all port profiles: %s" % str(exc))
return pp_bindings
def get_pp_binding(self, tenant_id, pp_id):
"""Get a portprofile binding"""
pp_binding = []
try:
for pp_bind in l2network_db.get_pp_binding(tenant_id, pp_id):
LOG.debug("Getting port profile binding: %s" % \
pp_bind.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(pp_bind.portprofile_id)
ppbinding_dict["port-id"] = str(pp_bind.port_id)
ppbinding_dict["tenant-id"] = pp_bind.tenant_id
ppbinding_dict["default"] = pp_bind.default
pp_binding.append(ppbinding_dict)
except Exception, exc:
LOG.error("Failed to get port profile binding: %s" % str(exc))
return pp_binding
def create_pp_binding(self, tenant_id, port_id, pp_id, default):
"""Add a portprofile binding"""
ppbinding_dict = {}
try:
res = l2network_db.add_pp_binding(tenant_id, port_id, pp_id, \
default)
LOG.debug("Created port profile binding: %s" % res.portprofile_id)
ppbinding_dict["portprofile-id"] = str(res.portprofile_id)
ppbinding_dict["port-id"] = str(res.port_id)
ppbinding_dict["tenant-id"] = res.tenant_id
ppbinding_dict["default"] = res.default
return ppbinding_dict
except Exception, exc:
LOG.error("Failed to create port profile binding: %s" % str(exc))
def delete_pp_binding(self, tenant_id, port_id, pp_id):
"""Delete a portprofile binding"""
try:
res = l2network_db.remove_pp_binding(tenant_id, port_id, pp_id)
LOG.debug("Deleted port profile binding : %s" % res.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(res.portprofile_id)
return ppbinding_dict
except Exception, exc:
raise Exception("Failed to delete port profile: %s" % str(exc))
def update_pp_binding(self, tenant_id, pp_id, newtenant_id, \
port_id, default):
"""Update portprofile binding"""
try:
res = l2network_db.update_pp_binding(tenant_id, pp_id,
newtenant_id, port_id, default)
LOG.debug("Updating port profile binding: %s" % res.portprofile_id)
ppbinding_dict = {}
ppbinding_dict["portprofile-id"] = str(res.portprofile_id)
ppbinding_dict["port-id"] = str(res.port_id)
ppbinding_dict["tenant-id"] = res.tenant_id
ppbinding_dict["default"] = res.default
return ppbinding_dict
except Exception, exc:
raise Exception("Failed to update portprofile binding:%s" \
% str(exc))
class QuantumDB(object):
"""Class conisting of methods to call Quantum db methods"""
def get_all_networks(self, tenant_id):
"""Get all networks"""
nets = []
try:
for net in db.network_list(tenant_id):
LOG.debug("Getting network: %s" % net.uuid)
net_dict = {}
net_dict["tenant-id"] = net.tenant_id
net_dict["net-id"] = str(net.uuid)
net_dict["net-name"] = net.name
nets.append(net_dict)
except Exception, exc:
LOG.error("Failed to get all networks: %s" % str(exc))
return nets
def get_network(self, network_id):
"""Get a network"""
net = []
try:
for net in db.network_get(network_id):
LOG.debug("Getting network: %s" % net.uuid)
net_dict = {}
net_dict["tenant-id"] = net.tenant_id
net_dict["net-id"] = str(net.uuid)
net_dict["net-name"] = net.name
net.append(net_dict)
except Exception, exc:
LOG.error("Failed to get network: %s" % str(exc))
return net
def create_network(self, tenant_id, net_name):
"""Create a network"""
net_dict = {}
try:
res = db.network_create(tenant_id, net_name)
LOG.debug("Created network: %s" % res.uuid)
net_dict["tenant-id"] = res.tenant_id
net_dict["net-id"] = str(res.uuid)
net_dict["net-name"] = res.name
return net_dict
except Exception, exc:
LOG.error("Failed to create network: %s" % str(exc))
def delete_network(self, net_id):
"""Delete a network"""
try:
net = db.network_destroy(net_id)
LOG.debug("Deleted network: %s" % net.uuid)
net_dict = {}
net_dict["net-id"] = str(net.uuid)
return net_dict
except Exception, exc:
raise Exception("Failed to delete port: %s" % str(exc))
def rename_network(self, tenant_id, net_id, new_name):
"""Rename a network"""
try:
net = db.network_rename(tenant_id, net_id, new_name)
LOG.debug("Renamed network: %s" % net.uuid)
net_dict = {}
net_dict["net-id"] = str(net.uuid)
net_dict["net-name"] = net.name
return net_dict
except Exception, exc:
raise Exception("Failed to rename network: %s" % str(exc))
def get_all_ports(self, net_id):
"""Get all ports"""
ports = []
try:
for port in db.port_list(net_id):
LOG.debug("Getting port: %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
port_dict["net"] = port.network
ports.append(port_dict)
return ports
except Exception, exc:
LOG.error("Failed to get all ports: %s" % str(exc))
def get_port(self, net_id, port_id):
"""Get a port"""
port_list = []
port = db.port_get(net_id, port_id)
try:
LOG.debug("Getting port: %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
port_list.append(port_dict)
return port_list
except Exception, exc:
LOG.error("Failed to get port: %s" % str(exc))
def create_port(self, net_id):
"""Add a port"""
port_dict = {}
try:
port = db.port_create(net_id)
LOG.debug("Creating port %s" % port.uuid)
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
return port_dict
except Exception, exc:
LOG.error("Failed to create port: %s" % str(exc))
def delete_port(self, net_id, port_id):
"""Delete a port"""
try:
port = db.port_destroy(net_id, port_id)
LOG.debug("Deleted port %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
return port_dict
except Exception, exc:
raise Exception("Failed to delete port: %s" % str(exc))
def update_port(self, net_id, port_id, port_state):
"""Update a port"""
try:
port = db.port_set_state(net_id, port_id, port_state)
LOG.debug("Updated port %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
return port_dict
except Exception, exc:
raise Exception("Failed to update port state: %s" % str(exc))
def plug_interface(self, net_id, port_id, int_id):
"""Plug interface to a port"""
try:
port = db.port_set_attachment(net_id, port_id, int_id)
LOG.debug("Attached interface to port %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
return port_dict
except Exception, exc:
raise Exception("Failed to plug interface: %s" % str(exc))
def unplug_interface(self, net_id, port_id):
"""Unplug interface to a port"""
try:
port = db.port_unset_attachment(net_id, port_id)
LOG.debug("Detached interface from port %s" % port.uuid)
port_dict = {}
port_dict["port-id"] = str(port.uuid)
port_dict["net-id"] = str(port.network_id)
port_dict["int-id"] = port.interface_id
port_dict["state"] = port.state
return port_dict
except Exception, exc:
raise Exception("Failed to unplug interface: %s" % str(exc))
class L2networkDBTest(unittest.TestCase):
"""Class conisting of L2network DB unit tests"""
def setUp(self):
"""Setup for tests"""
l2network_db.initialize()
self.dbtest = L2networkDB()
self.quantum = QuantumDB()
LOG.debug("Setup")
def tearDown(self):
"""Tear Down"""
db.clear_db()
def testa_create_vlanbinding(self):
"""test add vlan binding"""
net1 = self.quantum.create_network("t1", "netid1")
vlan1 = self.dbtest.create_vlan_binding(10, "vlan1", net1["net-id"])
self.assertTrue(vlan1["vlan-id"] == "10")
self.teardown_vlanbinding()
self.teardown_network()
def testb_getall_vlanbindings(self):
"""test get all vlan binding"""
net1 = self.quantum.create_network("t1", "netid1")
net2 = self.quantum.create_network("t1", "netid2")
vlan1 = self.dbtest.create_vlan_binding(10, "vlan1", net1["net-id"])
self.assertTrue(vlan1["vlan-id"] == "10")
vlan2 = self.dbtest.create_vlan_binding(20, "vlan2", net2["net-id"])
self.assertTrue(vlan2["vlan-id"] == "20")
vlans = self.dbtest.get_all_vlan_bindings()
count = 0
for vlan in vlans:
if "vlan" in vlan["vlan-name"]:
count += 1
self.assertTrue(count == 2)
self.teardown_vlanbinding()
self.teardown_network()
def testc_delete_vlanbinding(self):
"""test delete vlan binding"""
net1 = self.quantum.create_network("t1", "netid1")
vlan1 = self.dbtest.create_vlan_binding(10, "vlan1", net1["net-id"])
self.assertTrue(vlan1["vlan-id"] == "10")
self.dbtest.delete_vlan_binding(net1["net-id"])
vlans = self.dbtest.get_all_vlan_bindings()
count = 0
for vlan in vlans:
if "vlan " in vlan["vlan-name"]:
count += 1
self.assertTrue(count == 0)
self.teardown_vlanbinding()
self.teardown_network()
def testd_update_vlanbinding(self):
"""test update vlan binding"""
net1 = self.quantum.create_network("t1", "netid1")
vlan1 = self.dbtest.create_vlan_binding(10, "vlan1", net1["net-id"])
self.assertTrue(vlan1["vlan-id"] == "10")
vlan1 = self.dbtest.update_vlan_binding(net1["net-id"], 11, "newvlan1")
vlans = self.dbtest.get_all_vlan_bindings()
count = 0
for vlan in vlans:
if "new" in vlan["vlan-name"]:
count += 1
self.assertTrue(count == 1)
self.teardown_vlanbinding()
self.teardown_network()
def teste_create_portprofile(self):
"""test add port profile"""
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
self.assertTrue(pp1["portprofile-name"] == "portprofile1")
self.teardown_portprofile()
self.teardown_network()
def testf_getall_portprofile(self):
"""test get all portprofiles"""
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
self.assertTrue(pp1["portprofile-name"] == "portprofile1")
pp2 = self.dbtest.create_portprofile("t1", "portprofile2", 20, "qos2")
self.assertTrue(pp2["portprofile-name"] == "portprofile2")
pps = self.dbtest.get_all_portprofiles()
count = 0
for pprofile in pps:
if "portprofile" in pprofile["portprofile-name"]:
count += 1
self.assertTrue(count == 2)
self.teardown_portprofile()
def testg_delete_portprofile(self):
"""test delete portprofile"""
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
self.assertTrue(pp1["portprofile-name"] == "portprofile1")
self.dbtest.delete_portprofile("t1", pp1["portprofile-id"])
pps = self.dbtest.get_all_portprofiles()
count = 0
for pprofile in pps:
if "portprofile " in pprofile["portprofile-name"]:
count += 1
self.assertTrue(count == 0)
self.teardown_portprofile()
def testh_update_portprofile(self):
"""test update portprofile"""
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
self.assertTrue(pp1["portprofile-name"] == "portprofile1")
pp1 = self.dbtest.update_portprofile("t1", pp1["portprofile-id"], \
"newportprofile1", 20, "qos2")
pps = self.dbtest.get_all_portprofiles()
count = 0
for pprofile in pps:
if "new" in pprofile["portprofile-name"]:
count += 1
self.assertTrue(count == 1)
self.teardown_portprofile()
def testi_create_portprofilebinding(self):
"""test create portprofile binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
pp_binding1 = self.dbtest.create_pp_binding("t1", port1["port-id"], \
pp1["portprofile-id"], "0")
self.assertTrue(pp_binding1["tenant-id"] == "t1")
self.teardown_portprofilebinding()
self.teardown_port()
self.teardown_network()
self.teardown_portprofile()
def testj_getall_portprofilebinding(self):
"""test get all portprofile binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
port2 = self.quantum.create_port(net1["net-id"])
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
pp2 = self.dbtest.create_portprofile("t1", "portprofile2", 20, "qos2")
pp_binding1 = self.dbtest.create_pp_binding("t1", port1["port-id"], \
pp1["portprofile-id"], "0")
self.assertTrue(pp_binding1["tenant-id"] == "t1")
pp_binding2 = self.dbtest.create_pp_binding("t1", port2["port-id"], \
pp2["portprofile-id"], "0")
self.assertTrue(pp_binding2["tenant-id"] == "t1")
pp_bindings = self.dbtest.get_all_pp_bindings()
count = 0
for pp_bind in pp_bindings:
if "t1" in pp_bind["tenant-id"]:
count += 1
self.assertTrue(count == 2)
self.teardown_portprofilebinding()
self.teardown_port()
self.teardown_network()
self.teardown_portprofile()
def testk_delete_portprofilebinding(self):
"""test delete portprofile binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
pp_binding1 = self.dbtest.create_pp_binding("t1", port1["port-id"], \
pp1["portprofile-id"], "0")
self.assertTrue(pp_binding1["tenant-id"] == "t1")
self.dbtest.delete_pp_binding("t1", port1["port-id"], \
pp_binding1["portprofile-id"])
pp_bindings = self.dbtest.get_all_pp_bindings()
count = 0
for pp_bind in pp_bindings:
if "t1 " in pp_bind["tenant-id"]:
count += 1
self.assertTrue(count == 0)
self.teardown_portprofilebinding()
self.teardown_port()
self.teardown_network()
self.teardown_portprofile()
def testl_update_portprofilebinding(self):
"""test update portprofile binding"""
net1 = self.quantum.create_network("t1", "netid1")
port1 = self.quantum.create_port(net1["net-id"])
pp1 = self.dbtest.create_portprofile("t1", "portprofile1", 10, "qos1")
pp_binding1 = self.dbtest.create_pp_binding("t1", port1["port-id"], \
pp1["portprofile-id"], "0")
self.assertTrue(pp_binding1["tenant-id"] == "t1")
pp_binding1 = self.dbtest.update_pp_binding("t1", \
pp1["portprofile-id"], "newt1", port1["port-id"], "1")
pp_bindings = self.dbtest.get_all_pp_bindings()
count = 0
for pp_bind in pp_bindings:
if "new" in pp_bind["tenant-id"]:
count += 1
self.assertTrue(count == 1)
self.teardown_portprofilebinding()
self.teardown_port()
self.teardown_network()
self.teardown_portprofile()
def testm_test_vlanids(self):
"""test vlanid methods"""
l2network_db.create_vlanids()
vlanids = l2network_db.get_all_vlanids()
self.assertTrue(len(vlanids) > 0)
vlanid = l2network_db.reserve_vlanid()
used = l2network_db.is_vlanid_used(vlanid)
self.assertTrue(used == True)
used = l2network_db.release_vlanid(vlanid)
self.assertTrue(used == False)
self.teardown_vlanid()
def teardown_network(self):
"""tearDown Network table"""
LOG.debug("Tearing Down Network")
nets = self.quantum.get_all_networks("t1")
for net in nets:
netid = net["net-id"]
self.quantum.delete_network(netid)
def teardown_port(self):
"""tearDown Port table"""
LOG.debug("Tearing Down Port")
nets = self.quantum.get_all_networks("t1")
for net in nets:
netid = net["net-id"]
ports = self.quantum.get_all_ports(netid)
for port in ports:
portid = port["port-id"]
self.quantum.delete_port(netid, portid)
def teardown_vlanbinding(self):
"""tearDown VlanBinding table"""
LOG.debug("Tearing Down Vlan Binding")
vlans = self.dbtest.get_all_vlan_bindings()
for vlan in vlans:
netid = vlan["net-id"]
self.dbtest.delete_vlan_binding(netid)
def teardown_portprofile(self):
"""tearDown PortProfile table"""
LOG.debug("Tearing Down Port Profile")
pps = self.dbtest.get_all_portprofiles()
for pprofile in pps:
ppid = pprofile["portprofile-id"]
self.dbtest.delete_portprofile("t1", ppid)
def teardown_portprofilebinding(self):
"""tearDown PortProfileBinding table"""
LOG.debug("Tearing Down Port Profile Binding")
pp_bindings = self.dbtest.get_all_pp_bindings()
for pp_binding in pp_bindings:
ppid = pp_binding["portprofile-id"]
portid = pp_binding["port-id"]
self.dbtest.delete_pp_binding("t1", portid, ppid)
def teardown_vlanid(self):
"""tearDown VlanID table"""
LOG.debug("Tearing Down Vlan IDs")
vlanids = l2network_db.get_all_vlanids()
for vlanid in vlanids:
vlan_id = vlanid["vlan_id"]
l2network_db.delete_vlanid(vlan_id)
class QuantumDBTest(unittest.TestCase):
"""Class conisting of Quantum DB unit tests"""
def setUp(self):
"""Setup for tests"""
l2network_db.initialize()
self.dbtest = QuantumDB()
self.tenant_id = "t1"
LOG.debug("Setup")
def tearDown(self):
"""Tear Down"""
db.clear_db()
def testa_create_network(self):
"""test to create network"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
self.assertTrue(net1["net-name"] == "plugin_test1")
self.teardown_network_port()
def testb_get_networks(self):
"""test to get all networks"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
self.assertTrue(net1["net-name"] == "plugin_test1")
net2 = self.dbtest.create_network(self.tenant_id, "plugin_test2")
self.assertTrue(net2["net-name"] == "plugin_test2")
nets = self.dbtest.get_all_networks(self.tenant_id)
count = 0
for net in nets:
if "plugin_test" in net["net-name"]:
count += 1
self.assertTrue(count == 2)
self.teardown_network_port()
def testc_delete_network(self):
"""test to delete network"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
self.assertTrue(net1["net-name"] == "plugin_test1")
self.dbtest.delete_network(net1["net-id"])
nets = self.dbtest.get_all_networks(self.tenant_id)
count = 0
for net in nets:
if "plugin_test1" in net["net-name"]:
count += 1
self.assertTrue(count == 0)
self.teardown_network_port()
def testd_rename_network(self):
"""test to rename network"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
self.assertTrue(net1["net-name"] == "plugin_test1")
net = self.dbtest.rename_network(self.tenant_id, net1["net-id"],
"plugin_test1_renamed")
self.assertTrue(net["net-name"] == "plugin_test1_renamed")
self.teardown_network_port()
def teste_create_port(self):
"""test to create port"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
port = self.dbtest.create_port(net1["net-id"])
self.assertTrue(port["net-id"] == net1["net-id"])
ports = self.dbtest.get_all_ports(net1["net-id"])
count = 0
for por in ports:
count += 1
self.assertTrue(count == 1)
self.teardown_network_port()
def testf_delete_port(self):
"""test to delete port"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
port = self.dbtest.create_port(net1["net-id"])
self.assertTrue(port["net-id"] == net1["net-id"])
ports = self.dbtest.get_all_ports(net1["net-id"])
count = 0
for por in ports:
count += 1
self.assertTrue(count == 1)
for por in ports:
self.dbtest.delete_port(net1["net-id"], por["port-id"])
ports = self.dbtest.get_all_ports(net1["net-id"])
count = 0
for por in ports:
count += 1
self.assertTrue(count == 0)
self.teardown_network_port()
def testg_plug_unplug_interface(self):
"""test to plug/unplug interface"""
net1 = self.dbtest.create_network(self.tenant_id, "plugin_test1")
port1 = self.dbtest.create_port(net1["net-id"])
self.dbtest.plug_interface(net1["net-id"], port1["port-id"], "vif1.1")
port = self.dbtest.get_port(net1["net-id"], port1["port-id"])
self.assertTrue(port[0]["int-id"] == "vif1.1")
self.dbtest.unplug_interface(net1["net-id"], port1["port-id"])
port = self.dbtest.get_port(net1["net-id"], port1["port-id"])
self.assertTrue(port[0]["int-id"] == None)
self.teardown_network_port()
def testh_joined_test(self):
"""test to get network and port"""
net1 = self.dbtest.create_network("t1", "net1")
port1 = self.dbtest.create_port(net1["net-id"])
self.assertTrue(port1["net-id"] == net1["net-id"])
port2 = self.dbtest.create_port(net1["net-id"])
self.assertTrue(port2["net-id"] == net1["net-id"])
ports = self.dbtest.get_all_ports(net1["net-id"])
for port in ports:
net = port["net"]
LOG.debug("Port id %s Net id %s" % (port["port-id"], net.uuid))
self.teardown_joined_test()
def teardown_network_port(self):
"""tearDown for Network and Port table"""
networks = self.dbtest.get_all_networks(self.tenant_id)
for net in networks:
netid = net["net-id"]
name = net["net-name"]
if "plugin_test" in name:
ports = self.dbtest.get_all_ports(netid)
for por in ports:
self.dbtest.delete_port(netid, por["port-id"])
self.dbtest.delete_network(netid)
def teardown_joined_test(self):
"""tearDown for joined Network and Port test"""
LOG.debug("Tearing Down Network and Ports")
nets = self.dbtest.get_all_networks("t1")
for net in nets:
netid = net["net-id"]
ports = self.dbtest.get_all_ports(netid)
for port in ports:
self.dbtest.delete_port(port["net-id"], port["port-id"])
self.dbtest.delete_network(netid)
"""
if __name__ == "__main__":
usagestr = "Usage: %prog [OPTIONS] <command> [args]"
parser = OptionParser(usage=usagestr)
parser.add_option("-v", "--verbose", dest="verbose",
action="store_true", default=False, help="turn on verbose logging")
options, args = parser.parse_args()
if options.verbose:
LOG.basicConfig(level=LOG.DEBUG)
else:
LOG.basicConfig(level=LOG.WARN)
l2network_db.initialize()
# Run the tests
suite = unittest.TestLoader().loadTestsFromTestCase(QuantumDBTest)
unittest.TextTestRunner(verbosity=2).run(suite)
suite = unittest.TestLoader().loadTestsFromTestCase(L2networkDBTest)
unittest.TextTestRunner(verbosity=2).run(suite)
"""

View File

@ -24,6 +24,8 @@ 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
from quantum.plugins.cisco.db import api as db
from quantum.plugins.cisco.db import l2network_db as cdb
LOG = logging.getLogger('quantum.tests.test_core_api_func')
@ -47,6 +49,8 @@ class CoreAPITestFunc(unittest.TestCase):
network_name = self.network_name
new_net_dict = self._l2network_plugin.create_network(
tenant_id, network_name)
net = db.network_get(new_net_dict[const.NET_ID])
self.assertEqual(net[const.NETWORKNAME], 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")
@ -64,6 +68,8 @@ class CoreAPITestFunc(unittest.TestCase):
tenant_id, self.network_name)
delete_net_dict = self._l2network_plugin.delete_network(
tenant_id, new_net_dict[const.NET_ID])
self.assertRaises(exc.NetworkNotFound, db.network_get,
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")
@ -117,6 +123,8 @@ class CoreAPITestFunc(unittest.TestCase):
tenant_id, self.network_name)
result_net_dict = self._l2network_plugin.get_network_details(
tenant_id, new_net_dict[const.NET_ID])
net = db.network_get(new_net_dict[const.NET_ID])
self.assertEqual(net[const.UUID], 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])
@ -152,6 +160,8 @@ class CoreAPITestFunc(unittest.TestCase):
tenant_id, self.network_name)
rename_net_dict = self._l2network_plugin.rename_network(
tenant_id, new_net_dict[const.NET_ID], new_name)
net = db.network_get(new_net_dict[const.NET_ID])
self.assertEqual(net[const.NETWORKNAME], 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")
@ -184,9 +194,18 @@ class CoreAPITestFunc(unittest.TestCase):
tenant_id, 'test_net2')
net_list = self._l2network_plugin.get_all_networks(tenant_id)
net_temp_list = [new_net_dict, new_net_dict2]
networks_list = db.network_list(tenant_id)
new_networks_list = []
for network in networks_list:
new_network_dict = self._make_net_dict(network[const.UUID],
network[const.NETWORKNAME],
[])
new_networks_list.append(new_network_dict)
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.assertTrue(new_networks_list[0] in net_temp_list)
self.assertTrue(new_networks_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")
@ -206,9 +225,20 @@ class CoreAPITestFunc(unittest.TestCase):
port_list = self._l2network_plugin.get_all_ports(
tenant_id, new_net_dict[const.NET_ID])
port_temp_list = [port_dict, port_dict2]
network = db.network_get(new_net_dict[const.NET_ID])
ports_list = network[const.NETWORKPORTS]
ports_on_net = []
for port in ports_list:
new_port = self._make_port_dict(port[const.UUID],
port[const.PORTSTATE],
port[const.NETWORKID],
port[const.INTERFACEID])
ports_on_net.append(new_port)
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.assertTrue(ports_on_net[0] in port_temp_list)
self.assertTrue(ports_on_net[1] in port_temp_list)
self.tearDownPortOnly(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
@ -227,7 +257,12 @@ class CoreAPITestFunc(unittest.TestCase):
tenant_id, self.network_name)
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], port_state)
port = db.port_get(new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(port_dict[const.PORT_STATE], port_state)
self.assertEqual(port_dict[const.NET_ID], new_net_dict[const.NET_ID])
self.assertEqual(port[const.PORTSTATE], port_state)
self.assertEqual(port[const.NETWORKID], new_net_dict[const.NET_ID])
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_create_port - END")
@ -263,8 +298,11 @@ class CoreAPITestFunc(unittest.TestCase):
delete_port_dict = self._l2network_plugin.delete_port(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertRaises(exc.PortNotFound, db.port_get,
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)
self.assertEqual(delete_port_dict[const.PORT_ID],
port_dict[const.PORT_ID])
LOG.debug("test_delete_port - END")
def test_delete_port_networkDNE(self, tenant_id='test_tenant',
@ -327,6 +365,9 @@ class CoreAPITestFunc(unittest.TestCase):
update_port_dict = self._l2network_plugin.update_port(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_state)
new_port = db.port_get(new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(new_port[const.PORTSTATE], 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])
@ -341,7 +382,7 @@ class CoreAPITestFunc(unittest.TestCase):
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)
net_id, port_id, const.PORT_UP)
LOG.debug("test_update_port_networkDNE - END")
def test_update_portDNE(self, tenant_id='test_tenant', port_id='p0005'):
@ -354,7 +395,7 @@ class CoreAPITestFunc(unittest.TestCase):
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)
new_net_dict[const.NET_ID], port_id, const.PORT_UP)
self.tearDownNetwork(tenant_id, new_net_dict[const.NET_ID])
LOG.debug("test_update_portDNE - END")
@ -371,6 +412,9 @@ class CoreAPITestFunc(unittest.TestCase):
get_port_dict = self._l2network_plugin.get_port_details(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
port = db.port_get(new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(port[const.PORTSTATE], self.port_state)
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])
@ -416,10 +460,9 @@ class CoreAPITestFunc(unittest.TestCase):
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)
port = db.port_get(new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(port[const.INTERFACEID], remote_interface)
self.tearDownNetworkPortInterface(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
@ -470,7 +513,7 @@ class CoreAPITestFunc(unittest.TestCase):
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.assertRaises(exc.PortInUse,
self._l2network_plugin.plug_interface, tenant_id,
new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], remote_interface)
@ -496,9 +539,9 @@ class CoreAPITestFunc(unittest.TestCase):
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)
port = db.port_get(new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
self.assertEqual(port[const.INTERFACEID], None)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_unplug_interface - END")
@ -533,7 +576,7 @@ class CoreAPITestFunc(unittest.TestCase):
LOG.debug("test_unplug_interface_portDNE - END")
def test_create_portprofile(self, net_tenant_id=None,
net_profile_name=None, net_vlan_id=None):
net_profile_name=None, net_qos=None):
"""
Tests creation of a port-profile
"""
@ -548,19 +591,16 @@ class CoreAPITestFunc(unittest.TestCase):
profile_name = net_profile_name
else:
profile_name = self.profile_name
if net_vlan_id:
vlan_id = net_vlan_id
if net_qos:
qos = net_qos
else:
vlan_id = self.vlan_id
qos = self.qos
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, profile_name, vlan_id)
tenant_id, profile_name, qos)
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)
port_profile = cdb.get_portprofile(tenant_id, port_profile_id)
self.assertEqual(port_profile[const.PPNAME], profile_name)
self.assertEqual(port_profile[const.PPQOS], qos)
self.tearDownPortProfile(tenant_id, port_profile_id)
LOG.debug("test_create_portprofile - tenant id: %s - END",
net_tenant_id)
@ -577,10 +617,10 @@ class CoreAPITestFunc(unittest.TestCase):
else:
tenant_id = self.tenant_id
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
port_profile_id = port_profile_dict['profile-id']
self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id)
self.assertEqual(self._l2network_plugin._portprofiles, {})
self.assertRaises(Exception, cdb.get_portprofile, port_profile_id)
LOG.debug("test_delete_portprofile - tenant id: %s - END",
net_tenant_id)
@ -604,15 +644,23 @@ class CoreAPITestFunc(unittest.TestCase):
LOG.debug("test_delete_portprofileAssociated - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
port_profile_id = port_profile_dict['profile-id']
new_net_dict = self._l2network_plugin.create_network(
tenant_id, 'test_network')
port_dict = self._l2network_plugin.create_port(
tenant_id, new_net_dict[const.NET_ID], 'const.PORT_UP')
self._l2network_plugin.associate_portprofile(
tenant_id, self.net_id, self.port_id, port_profile_id)
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.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)
self.tearDownAssociatePortProfile(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_profile_id)
self.tearDownNetworkPort(tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_delete_portprofileAssociated - END")
def test_list_portprofile(self, tenant_id='test_tenant'):
@ -622,24 +670,30 @@ class CoreAPITestFunc(unittest.TestCase):
LOG.debug("test_list_portprofile - tenant id: %s - START", tenant_id)
profile_name2 = tenant_id + '_port_profile2'
vlan_id2 = tenant_id + '201'
qos2 = tenant_id + 'qos2'
port_profile_dict1 = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
port_profile_dict2 = self._l2network_plugin.create_portprofile(
tenant_id, profile_name2, vlan_id2)
tenant_id, profile_name2, qos2)
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)
port_profile_list = [port_profile_dict1, port_profile_dict2]
pplist = cdb.get_all_portprofiles()
new_pplist = []
for pp in pplist:
new_pp = self._make_portprofile_dict(tenant_id,
pp[const.UUID],
pp[const.PPNAME],
pp[const.PPQOS])
new_pplist.append(new_pp)
self.assertTrue(new_pplist[0] in port_profile_list)
self.assertTrue(new_pplist[1] in port_profile_list)
self.tearDownPortProfile(tenant_id, port_profile_id1)
self.tearDownPortProfile(tenant_id, port_profile_id2)
LOG.debug("test_list_portprofile - tenant id: %s - END", tenant_id)
def test_show_portprofile(self, net_tenant_id=None):
"""
@ -652,12 +706,15 @@ class CoreAPITestFunc(unittest.TestCase):
else:
tenant_id = self.tenant_id
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
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)
port_profile = cdb.get_portprofile(tenant_id, port_profile_id)
self.assertEqual(port_profile[const.PPQOS], self.qos)
self.assertEqual(port_profile[const.PPNAME], self.profile_name)
self.assertEqual(result_port_profile[const.PROFILE_QOS],
self.qos)
self.assertEqual(result_port_profile[const.PROFILE_NAME],
self.profile_name)
self.tearDownPortProfile(tenant_id, port_profile_id)
@ -670,7 +727,7 @@ class CoreAPITestFunc(unittest.TestCase):
"""
LOG.debug("test_show_portprofileDNE - START")
self.assertRaises(cexc.PortProfileNotFound,
self.assertRaises(Exception,
self._l2network_plugin.get_portprofile_details,
tenant_id, profile_id)
LOG.debug("test_show_portprofileDNE - END")
@ -683,10 +740,12 @@ class CoreAPITestFunc(unittest.TestCase):
LOG.debug("test_rename_portprofile - START")
port_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
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)
port_profile = cdb.get_portprofile(tenant_id, port_profile_id)
self.assertEqual(port_profile[const.PPNAME], new_profile_name)
self.assertEqual(result_port_profile_dict[const.PROFILE_NAME],
new_profile_name)
self.tearDownPortProfile(tenant_id, port_profile_id)
@ -705,23 +764,32 @@ class CoreAPITestFunc(unittest.TestCase):
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'):
def test_associate_portprofile(self, tenant_id='test_tenant'):
"""
Tests association of a port-profile
"""
LOG.debug("test_associate_portprofile - 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_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
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)
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_profile_id)
port_profile_associate = cdb.get_pp_binding(tenant_id, port_profile_id)
self.assertEqual(port_profile_associate[const.PORTID],
port_dict[const.PORT_ID])
self.tearDownAssociatePortProfile(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_profile_id)
self.tearDownNetworkPort(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_associate_portprofile - END")
def test_associate_portprofileDNE(self, tenant_id='test_tenant',
@ -738,22 +806,32 @@ class CoreAPITestFunc(unittest.TestCase):
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")
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_profile_dict = self._l2network_plugin.create_portprofile(
tenant_id, self.profile_name, self.vlan_id)
tenant_id, self.profile_name, self.qos)
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.associate_portprofile(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.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], [])
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID], port_profile_id)
port_profile_associate = cdb.get_pp_binding(tenant_id, port_profile_id)
self.assertEqual(port_profile_associate, [])
self.tearDownPortProfile(tenant_id, port_profile_id)
self.tearDownNetworkPort(
tenant_id, new_net_dict[const.NET_ID],
port_dict[const.PORT_ID])
LOG.debug("test_disassociate_portprofile - END")
def test_disassociate_portprofileDNE(self, tenant_id='test_tenant',
@ -768,24 +846,7 @@ class CoreAPITestFunc(unittest.TestCase):
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",
def test_get_vlan_name(self, net_tenant_id=None, vlan_id="NewVlan",
vlan_prefix=conf.VLAN_NAME_PREFIX):
"""
Tests get vlan name
@ -797,8 +858,8 @@ class CoreAPITestFunc(unittest.TestCase):
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
vlan_id)
expected_output = vlan_prefix + vlan_id
self.assertEqual(result_vlan_name, expected_output)
LOG.debug("test_get_vlan_name - END")
@ -823,39 +884,11 @@ class CoreAPITestFunc(unittest.TestCase):
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.qos = "test_qos"
self.port_state = const.PORT_UP
self.net_id = '00005'
self.port_id = 'p0005'
@ -865,6 +898,10 @@ class CoreAPITestFunc(unittest.TestCase):
"""
Clean up functions after the tests
"""
def tearDown(self):
"""Clear the test environment"""
# Remove database contents
db.clear_db()
def tearDownNetwork(self, tenant_id, network_dict_id):
self._l2network_plugin.delete_network(tenant_id, network_dict_id)
@ -885,8 +922,41 @@ class CoreAPITestFunc(unittest.TestCase):
def tearDownPortProfile(self, tenant_id, port_profile_id):
self._l2network_plugin.delete_portprofile(tenant_id, port_profile_id)
def tearDownPortProfileBinding(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)
def _make_net_dict(self, net_id, net_name, ports):
res = {const.NET_ID: net_id, const.NET_NAME: net_name}
res[const.NET_PORTS] = ports
return res
def _make_port_dict(self, port_id, port_state, net_id, attachment):
res = {const.PORT_ID: port_id, const.PORT_STATE: port_state}
res[const.NET_ID] = net_id
res[const.ATTACHMENT] = attachment
return res
def _make_portprofile_dict(self, tenant_id, profile_id, profile_name,
qos):
profile_associations = self._make_portprofile_assc_list(
tenant_id, profile_id)
res = {const.PROFILE_ID: str(profile_id),
const.PROFILE_NAME: profile_name,
const.PROFILE_ASSOCIATIONS: profile_associations,
const.PROFILE_VLAN_ID: None,
const.PROFILE_QOS: qos}
return res
def _make_portprofile_assc_list(self, tenant_id, profile_id):
plist = cdb.get_pp_binding(tenant_id, profile_id)
assc_list = []
for port in plist:
assc_list.append(port[const.PORTID])
return assc_list

View File

@ -259,11 +259,10 @@ class TestNexusPlugin(unittest.TestCase):
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):
"""
Clean up functions after the tests
"""
self._cisco_nexus_plugin.delete_network(tenant_id, network_dict_id)
# def test_create_network(self):

View File

@ -24,13 +24,13 @@ 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\" "\
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\" "\
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="\
@ -39,7 +39,7 @@ create_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"name=\"New Vlan\" rn=\"if-New Vlan\" > </vnicEtherIf> </vnicProfile> "\
"</pair> </inConfigs> </configConfMos>"
change_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
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\" "\
@ -50,18 +50,18 @@ change_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
"<vnicEtherIf defaultNet=\"yes\" name=\"New Vlan\" rn=\"if-New Vlan\" > "\
"</vnicEtherIf> </vnicProfile> </pair></inConfigs> </configConfMos>"
delete_vlan_output = "<configConfMos cookie=\"cookie_placeholder\" "\
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\" "\
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\" "\
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-"\
@ -73,83 +73,86 @@ associate_profile_output = "<configConfMos cookie=\"cookie_placeholder\" "\
class TestUCSDriver(unittest.TestCase):
def setUp(self):
self._ucsmDriver = cisco_ucs_network_driver.CiscoUCSMDriver()
self.ucsm_driver = 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):
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(
vlan_details = self.ucsm_driver._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):
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(
profile_details = self.ucsm_driver._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):
def test_change_vlan_profile_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(
profile_details = self.ucsm_driver._change_vlaninprof_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")
def test_delete_vlan_post_data(self, expected_output=DELETE_VLAN_OUTPUT):
"""
Tests deletion of vlan post Data
"""
vlan_details = self._ucsmDriver._create_vlan_post_data(
LOG.debug("test_create_profile_post_data - START")
self.ucsm_driver._create_vlan_post_data(
self.vlan_name, self.vlan_id)
vlan_delete_details = self._ucsmDriver._delete_vlan_post_data(
vlan_delete_details = self.ucsm_driver._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):
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(
#profile_details = self.ucsm_driver._create_profile_post_data(
# self.profile_name, self.vlan_name)
self.ucsm_driver._create_profile_post_data(
self.profile_name, self.vlan_name)
profile_delete_details = self._ucsmDriver._delete_profile_post_data(
profile_delete_details = self.ucsm_driver._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):
def test_create_profile_client_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(
LOG.debug("test_create_profile_client_data - START")
profile_details = self.ucsm_driver._create_pclient_post_data(
self.profile_name, self.profile_client_name)
self.assertEqual(profile_details, expected_output)
LOG.debug("test_create_profile_post - END")
@ -160,6 +163,6 @@ class TestUCSDriver(unittest.TestCase):
"""
LOG.debug("test_get_next_dynamic_nic - START")
dynamic_nic_id = self._ucsmDriver._get_next_dynamic_nic()
dynamic_nic_id = self.ucsm_driver._get_next_dynamic_nic()
self.assertTrue(len(dynamic_nic_id) > 0)
LOG.debug("test_get_next_dynamic_nic - END")

View File

@ -32,7 +32,7 @@ class UCSVICTestPlugin(unittest.TestCase):
self.tenant_id = "test_tenant_cisco12"
self.net_name = "test_network_cisco12"
self.net_id = 000007
self.net_id = 000011
self.vlan_name = "q-" + str(self.net_id) + "vlan"
self.vlan_id = 266
self.port_id = "4"
@ -238,12 +238,12 @@ class UCSVICTestPlugin(unittest.TestCase):
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):
def _test_show_port_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()" +
LOG.debug("UCSVICTestPlugin:_test_show_port_state_down()" +
"called\n")
self._cisco_ucs_plugin.create_network(self.tenant_id, self.net_name,
self.net_id, self.vlan_name,
@ -268,8 +268,8 @@ class UCSVICTestPlugin(unittest.TestCase):
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_show_port_state_down(self):
self._test_show_port_state_down(const.PORT_DOWN)
def test_create_port_profile(self):
LOG.debug("UCSVICTestPlugin:test_create_port_profile() called\n")
@ -313,7 +313,6 @@ class UCSVICTestPlugin(unittest.TestCase):
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(
@ -346,7 +345,6 @@ class UCSVICTestPlugin(unittest.TestCase):
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],
@ -394,12 +392,12 @@ class UCSVICTestPlugin(unittest.TestCase):
def test_get_network_NetworkNotFound(self):
self.assertRaises(exc.NetworkNotFound,
self._cisco_ucs_plugin._get_network,
*(self.tenant_id, self.net_id))
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))
self.tenant_id, self.net_id)
def test_delete_port_PortInUse(self):
self._test_delete_port_PortInUse("4")
@ -414,7 +412,7 @@ class UCSVICTestPlugin(unittest.TestCase):
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.tenant_id, self.net_id, self.port_id)
self.tearDownNetworkPortInterface(self.tenant_id, self.net_id,
self.port_id)
@ -423,7 +421,7 @@ class UCSVICTestPlugin(unittest.TestCase):
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.tenant_id, self.net_id, self.port_id)
self.tearDownNetwork(self.tenant_id, self.net_id)
def test_plug_interface_PortInUse(self):
@ -441,16 +439,16 @@ class UCSVICTestPlugin(unittest.TestCase):
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.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):
def test_attachment_exists(self):
LOG.debug("UCSVICTestPlugin:testValidateAttachmentAlreadyAttached")
self._test_validate_attachment_AlreadyAttached("4")
self._test_attachment_exists("4")
def _test_validate_attachment_AlreadyAttached(self, remote_interface_id):
def _test_attachment_exists(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,
@ -461,8 +459,8 @@ class UCSVICTestPlugin(unittest.TestCase):
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))
exc.PortInUse, 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)

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,3 +17,4 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,11 +17,13 @@
#
# @author: Rohit Agarwalla, Cisco Systems Inc.
#
import sys
"""
import subprocess
def get_next_dynic(argv=[]):
"""Get the next available dynamic nic on this host"""
cmd = ["ifconfig", "-a"]
f_cmd_output = subprocess.Popen(cmd, stdout=subprocess.PIPE).\
communicate()[0]

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import os
@ -23,15 +25,15 @@ 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__)) \
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['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']
SECTION = CP['DRIVER']
UCSM_DRIVER = SECTION['name']

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,16 +17,15 @@
#
# @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
@ -114,11 +114,13 @@ PROFILE_NAME + "\" status=\"deleted\"> </vnicProfile>" \
class CiscoUCSMDriver():
"""UCSM Driver"""
def __init__(self):
pass
def _post_data(self, ucsm_ip, ucsm_username, ucsm_password, data):
"""Send command to UCSM in http request"""
conn = httplib.HTTPConnection(ucsm_ip)
login_data = "<aaaLogin inName=\"" + ucsm_username + \
"\" inPassword=\"" + ucsm_password + "\" />"
@ -129,8 +131,8 @@ class CiscoUCSMDriver():
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"]
xml_tree = et.XML(response_data)
cookie = xml_tree.attrib["outCookie"]
data = data.replace(COOKIE_VALUE, cookie)
LOG.debug("POST: %s" % data)
@ -150,65 +152,76 @@ class CiscoUCSMDriver():
LOG.debug(response_data)
def _create_vlan_post_data(self, vlan_name, vlan_id):
"""Create command"""
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):
"""Create command"""
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,
def _create_pclient_post_data(self, profile_name,
profile_client_name):
"""Create command"""
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,
def _change_vlaninprof_post_data(self, profile_name, old_vlan_name,
new_vlan_name):
"""Create command"""
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):
"""Create command"""
data = DELETE_VLAN.replace(VLAN_NAME, vlan_name)
return data
def _delete_profile_post_data(self, profile_name):
"""Create command"""
data = DELETE_PROFILE.replace(PROFILE_NAME, profile_name)
return data
def _get_next_dynamic_nic(self):
"""Get an avaialble dynamic nic on the host"""
dynamic_nic_id = gvif.get_next_dynic()
if len(dynamic_nic_id) > 0:
return dynamic_nic_id
else:
raise cisco_exceptions.NoMoreNics(net_id=net_id, port_id=port_id)
raise cexc.NoMoreNics()
def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
ucsm_password):
"""Create request for UCSM"""
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):
"""Create request for UCSM"""
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,
data = self._create_pclient_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,
"""Create request for UCSM"""
data = self._change_vlaninprof_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):
"""Get an avaialble dynamic nic on the host"""
# TODO (Sumit): Check availability per host
# TODO (Sumit): If not available raise exception
# TODO (Sumit): This simple logic assumes that create-port and
@ -222,14 +235,17 @@ class CiscoUCSMDriver():
return dynamic_nic_name
def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password):
"""Create request for UCSM"""
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):
"""Create request for UCSM"""
data = self._delete_profile_post_data(profile_name)
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
def release_dynamic_nic(self, host):
"""Release a reserved dynamic nic on the host"""
# TODO (Sumit): Release on a specific host
pass

View File

@ -1,3 +1,4 @@
"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
@ -16,6 +17,7 @@
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
#
"""
import logging as LOG
@ -33,6 +35,7 @@ LOG.getLogger(const.LOGGER_COMPONENT_NAME)
class UCSVICPlugin(L2DevicePluginBase):
"""UCS Device Plugin"""
_networks = {}
def __init__(self):
@ -238,41 +241,48 @@ class UCSVICPlugin(L2DevicePluginBase):
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
"""Returns the port profile name based on the port UUID"""
profile_name = conf.PROFILE_NAME_PREFIX \
+ cutil.get16ByteUUID(port_id)
return profile_name
def _validate_port_state(self, port_state):
"""Check the port state"""
if port_state.upper() not in (const.PORT_UP, const.PORT_DOWN):
raise exc.StateInvalid(port_state=port_state)
return True
def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id):
"""Check if the VIF can be attached"""
network = self._get_network(tenant_id, network_id)
for port in network[const.NET_PORTS].values():
if port[const.ATTACHMENT] == remote_interface_id:
raise exc.AlreadyAttached(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT],
att_port_id=port[const.PORT_ID])
raise exc.PortInUse(net_id=network_id,
port_id=port_id,
att_id=port[const.ATTACHMENT])
def _get_network(self, tenant_id, network_id):
"""Get the network object ref"""
network = self._networks.get(network_id)
if not network:
raise exc.NetworkNotFound(net_id=network_id)
return network
def _get_vlan_name_for_network(self, tenant_id, network_id):
"""Return the VLAN name as set by the L2 network plugin"""
net = self._get_network(tenant_id, network_id)
vlan_name = net[const.NET_VLAN_NAME]
return vlan_name
def _get_vlan_id_for_network(self, tenant_id, network_id):
"""Return the VLAN id as set by the L2 network plugin"""
net = self._get_network(tenant_id, network_id)
vlan_id = net[const.NET_VLAN_ID]
return vlan_id
def _get_port(self, tenant_id, network_id, port_id):
"""Get the port object ref"""
net = self._get_network(tenant_id, network_id)
port = net[const.NET_PORTS].get(port_id)
if not port:
@ -281,6 +291,7 @@ class UCSVICPlugin(L2DevicePluginBase):
def _create_port_profile(self, tenant_id, net_id, port_id, vlan_name,
vlan_id):
"""Create port profile in UCSM"""
if self._port_profile_counter >= int(conf.MAX_UCSM_PORT_PROFILES):
raise cexc.UCSMPortProfileLimit(net_id=net_id, port_id=port_id)
profile_name = self._get_profile_name(port_id)
@ -293,6 +304,7 @@ class UCSVICPlugin(L2DevicePluginBase):
return new_port_profile
def _delete_port_profile(self, port_id, profile_name):
"""Delete port profile in UCSM"""
self._client.delete_profile(profile_name, self._ucsm_ip,
self._ucsm_username, self._ucsm_password)
self._port_profile_counter -= 1

View File

@ -20,8 +20,8 @@
import logging
import unittest
import tests.unit.testlib_api as testlib
from quantum import api as server
from quantum.db import api as db
from quantum.common.test_lib import test_config
@ -523,7 +523,6 @@ class APITest(unittest.TestCase):
show_port_res.body, content_type)
self.assertEqual({'id': port_id, 'state': new_port_state},
port_data['port'])
# now set it back to the original value
update_port_req = testlib.update_port_request(self.tenant_id,
network_id, port_id,