[PLUGINS]
-#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin
-#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin
+#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
+#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
[INVENTORY]
-#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory.UCSInventory
+#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
+#ucs_plugin=quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake.UCSInventory
#nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_inventory.NexusInventory
password=<put_password_here>
#Provide the Nexus credentials, if you are using Nexus
-[<put_nexus_ip_address_here>]
-username=<put_user_name_here>
-password=<put_password_here>
+[1.1.1.1]
+username=abc
+password=def
max_networks=65568
[MODEL]
-model_class=quantum.plugins.cisco.models.l2network_multi_blade.L2NetworkMultiBlade
+#model_class=quantum.plugins.cisco.models.l2network_multi_blade.L2NetworkMultiBlade
+model_class=quantum.plugins.cisco.models.network_multi_blade_v2.NetworkMultiBladeV2
[SEGMENTATION]
-manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr.L2NetworkVLANMgr
+manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr_v2.L2NetworkVLANMgr
nexus_ssh_port=22
[DRIVER]
-name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
+#name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
+name=quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver
profile_name_prefix=q-
[DRIVER]
-name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
+#name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
+name=quantum.plugins.cisco.tests.unit.v2.ucs.fake_ucs_driver.CiscoUCSMFakeDriver
=========================================================================================
-README: A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
+README for Quantum v2.0:
+A Plugin Framework for Supporting Quantum Networks Spannning Multiple Switches
+=========================================================================================
+
+Introduction
+------------
+
+This plugin implementation provides the following capabilities:
+
+* A reference implementation for a Quantum Plugin Framework
+(For details see: http://wiki.openstack.org/quantum-multi-switch-plugin)
+* Supports multiple switches in the network
+* Supports multiple models of switches concurrently
+* Supports use of multiple L2 technologies
+* Supports the Cisco Nexus family of switches.
+* Supports Cisco UCS blade servers with M81KR Virtual Interface Cards
+ (aka "Palo adapters") via 802.1Qbh.
+
+Pre-requisites
+--------------
+(The following are necessary only when using the UCS and/or Nexus devices in your system.
+If you plan to just leverage the plugin framework, you do not need these.)
+
+If you are using a Nexus switch in your topology, you'll need the following
+NX-OS version and packages to enable Nexus support:
+* NX-OS 5.2.1 (Delhi) Build 69 or above.
+* paramiko library - SSHv2 protocol library for python
+* ncclient v0.3.1 - Python library for NETCONF clients
+ ** You need a version of ncclient modifed by Cisco Systems.
+ To get it, from your shell prompt do:
+
+ git clone git@github.com:CiscoSystems/ncclient.git
+ sudo python ./setup.py install
+
+ ** For more information of ncclient, see:
+ http://schmizz.net/ncclient/
+
+* One or more UCS B200 series blade servers with M81KR VIC (aka
+ Palo adapters) installed.
+* UCSM 2.0 (Capitola) Build 230 or above.
+* OS supported:
+ ** RHEL 6.1 or above
+ ** Ubuntu 11.10 or above
+ ** Package: python-configobj-4.6.0-3.el6.noarch (or newer)
+ ** Package: python-routes-1.12.3-2.el6.noarch (or newer)
+ ** Package: pip install mysql-python
+
+
+Module Structure:
+-----------------
+* quantum/plugins/cisco/ - Contains the Network Plugin Framework
+ /client - CLI module for core and extensions API
+ /common - Modules common to the entire plugin
+ /conf - All configuration files
+ /db - Persistence framework
+ /models - Class(es) which tie the logical abstractions
+ to the physical topology
+ /nova - Scheduler and VIF-driver to be used by Nova
+ /nexus - Nexus-specific modules
+ /segmentation - Implementation of segmentation manager,
+ e.g. VLAN Manager
+ /services - Set of orchestration libraries to insert
+ In-path Networking Services
+ /tests - Tests specific to this plugin
+ /ucs - UCS-specific modules
+
+
+Plugin Installation Instructions
+----------------------------------
+1. Make a backup copy of quantum/etc/quantum.conf
+
+2. Edit quantum/etc/quantum.conf and edit the "core_plugin" for v2 API
+
+core_plugin = quantum.plugins.cisco.network_plugin.PluginV2
+
+3. MySQL database setup:
+ 3a. Create quantum_l2network database in mysql with the following command -
+
+mysql -u<mysqlusername> -p<mysqlpassword> -e "create database quantum_l2network"
+
+ 3b. Enter the quantum_l2network database configuration info in the
+ quantum/plugins/cisco/conf/db_conn.ini file.
+
+4. If you want to turn on support for Cisco Nexus switches:
+ 4a. Uncomment the nexus_plugin property in
+ etc/quantum/plugins/cisco/cisco_plugins.ini to read:
+
+[PLUGINS]
+nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
+
+ 4b. Enter the relevant configuration in the
+ etc/quantum/plugins/cisco/nexus.ini file. Example:
+
+[SWITCH]
+# Change the following to reflect the IP address of the Nexus switch.
+# This will be the address at which Quantum sends and receives configuration
+# information via SSHv2.
+nexus_ip_address=10.0.0.1
+# Port numbers on the Nexus switch to each one of the UCSM 6120s is connected
+# Use shortened interface syntax, e.g. "1/10" not "Ethernet1/10".
+nexus_first_port=1/10
+nexus_second_port=1/11
+#Port number where SSH will be running on the Nexus switch. Typically this is 22
+#unless you've configured your switch otherwise.
+nexus_ssh_port=22
+
+[DRIVER]
+name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
+
+ 4c. Make sure that SSH host key of the Nexus switch is known to the
+ host on which you are running the Quantum service. You can do
+ this simply by logging in to your Quantum host as the user that
+ Quantum runs as and SSHing to the switch at least once. If the
+ host key changes (e.g. due to replacement of the supervisor or
+ clearing of the SSH config on the switch), you may need to repeat
+ this step and remove the old hostkey from ~/.ssh/known_hosts.
+
+5. If your are using UCS blade servers with M81KR Virtual Interface Cards and
+ want to leverage the VM-FEX features,
+
+ 5a. Uncomment the ucs_plugin propertes in
+ etc/quantum/plugins/cisco/cisco_plugins.ini to read:
+
+[PLUGINS]
+ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
+[INVENTORY]
+ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
+
+ 5b. Enter the relevant configuration in the
+ etc/quantum/plugins/cisco/ucs.ini file. Example:
+
+[UCSM]
+#change the following to the appropriate UCSM IP address
+#if you have more than one UCSM, enter info from any one
+ip_address=<put_ucsm_ip_address_here>
+default_vlan_name=default
+default_vlan_id=1
+max_ucsm_port_profiles=1024
+profile_name_prefix=q-
+
+[DRIVER]
+name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
+
+ 5c. Configure the UCS systems' information in your deployment by editing the
+ quantum/plugins/cisco/conf/ucs_inventory.ini file. You can configure multiple
+ UCSMs per deployment, multiple chassis per UCSM, and multiple blades per
+ chassis. Chassis ID and blade ID can be obtained from the UCSM (they will
+ typically be numbers like 1, 2, 3, etc.). Also make sure that you put the exact
+ hostname as nova sees it (the host column in the services table of the nova
+ DB will give you that information).
+
+[ucsm-1]
+ip_address = <put_ucsm_ip_address_here>
+[[chassis-1]]
+chassis_id = <put_the_chassis_id_here>
+[[[blade-1]]]
+blade_id = <put_blade_id_here>
+host_name = <put_hostname_here>
+[[[blade-2]]]
+blade_id = <put_blade_id_here>
+host_name = <put_hostname_here>
+[[[blade-3]]]
+blade_id = <put_blade_id_here>
+host_name = <put_hostname_here>
+
+[ucsm-2]
+ip_address = <put_ucsm_ip_address_here>
+[[chassis-1]]
+chassis_id = <put_the_chassis_id_here>
+[[[blade-1]]]
+blade_id = <put_blade_id_here>
+host_name = <put_hostname_here>
+[[[blade-2]]]
+blade_id = <put_blade_id_here>
+host_name = <put_hostname_here>
+
+ 5d. Configure your OpenStack installation to use the 802.1qbh VIF driver and
+ Quantum-aware scheduler by editing the /etc/nova/nova.conf file with the
+ following entries:
+
+scheduler_driver=quantum.plugins.cisco.nova.quantum_port_aware_scheduler.QuantumPortAwareScheduler
+quantum_host=127.0.0.1
+quantum_port=9696
+libvirt_vif_driver=quantum.plugins.cisco.nova.vifdirect.Libvirt802dot1QbhDriver
+libvirt_vif_type=802.1Qbh
+
+ Note: To be able to bring up a VM on a UCS blade, you should first create a
+ port for that VM using the Quantum create port API. VM creation will
+ fail if an unused port is not available. If you have configured your
+ Nova project with more than one network, Nova will attempt to instantiate
+ the VM with one network interface (VIF) per configured network. To provide
+ plugin points for each of these VIFs, you will need to create multiple
+ Quantum ports, one for each of the networks, prior to starting the VM.
+ However, in this case you will need to use the Cisco multiport extension
+ API instead of the Quantum create port API. More details on using the
+ multiport extension follow in the section on multi NIC support.
+
+ To support the above configuration, you will need some Quantum modules. It's easiest
+ to copy the entire quantum directory from your quantum installation into:
+
+ /usr/lib/python2.7/site-packages/
+
+ This needs to be done on each nova compute node.
+
+7. Verify that you have the correct credentials for each IP address listed
+ in quantum/plugins/cisco/conf/credentials.ini. Example:
+
+# Provide the UCSM credentials, create a separte entry for each UCSM used in your system
+# UCSM IP address, username and password.
+[10.0.0.2]
+username=admin
+password=mySecretPasswordForUCSM
+
+# Provide the Nexus credentials, if you are using Nexus switches.
+# If not this will be ignored.
+[10.0.0.1]
+username=admin
+password=mySecretPasswordForNexus
+
+ In general, make sure that every UCSM and Nexus switch used in your system,
+ has a credential entry in the above file. This is required for the system to
+ be able to communicate with those switches.
+
+
+9. Start the Quantum service. If something doesn't work, verify the
+ your configuration of each of the above files.
+
+
+Multi NIC support for VMs
+-------------------------
+As indicated earlier, if your Nova setup has a project with more than one network,
+Nova will try to create a virtual network interface (VIF) on the VM for each of those
+networks. Before each VM is instantiated, you should create Quantum ports on each of
+those networks. These ports need to be created using the following rest call:
+
+POST /1.0/extensions/csco/tenants/{tenant_id}/multiport/
+
+with request body:
+
+{'multiport':
+ {'status': 'ACTIVE',
+ 'net_id_list': net_id_list,
+ 'ports_desc': {'key': 'value'}}}
+
+where,
+
+net_id_list is a list of network IDs: [netid1, netid2, ...]. The "ports_desc" dictionary
+is reserved for later use. For now, the same structure in terms of the dictionary name, key
+and value should be used.
+
+The corresponding CLI for this operation is as follows:
+
+PYTHONPATH=. python quantum/plugins/cisco/client/cli.py create_multiport <tenant_id> <net_id1,net_id2,...>
+
+ (Note that you should not be using the create port core API in the above case.)
+
+
+How to test the installation
+----------------------------
+The unit tests are located at quantum/plugins/cisco/tests/unit/v2. They can be
+executed from the top level Quantum directory using the run_tests.sh script.
+
+1. Testing the core API (without UCS/Nexus/RHEL device sub-plugins configured):
+ By default all the device sub-plugins are disabled (commented out) in
+ etc/quantum/plugins/cisco/cisco_plugins.ini
+
+ ./run_tests.sh quantum.plugins.cisco.tests.unit.v2.test_api_v2
+ ./run_tests.sh quantum.plugins.cisco.tests.unit.v2.test_network_plugin
+
+2. For testing the Nexus device sub-plugin perform the following configuration:
+
+ Edit etc/quantum/plugins/cisco/cisco_plugins.ini to add:
+ In the [PLUGINS] section add:
+nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
+
+ Edit the etc/quantum/plugins/cisco/nexus.ini file.
+ When not using Nexus hardware use the following dummy configuration verbatim:
+[SWITCH]
+nexus_ip_address=1.1.1.1
+nexus_first_port=1/10
+nexus_second_port=1/11
+nexus_ssh_port=22
+[DRIVER]
+name=quantum.plugins.cisco.tests.unit.v2.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver
+ Or when using Nexus hardware (put the values relevant to your setup):
+[SWITCH]
+nexus_ip_address=1.1.1.1
+nexus_first_port=1/10
+nexus_second_port=1/11
+nexus_ssh_port=22
+[DRIVER]
+name=quantum.plugins.cisco.nexus.cisco_nexus_network_driver.CiscoNEXUSDriver
+
+ (Note: Make sure that quantum/plugins/cisco/conf/credentials.ini has an entry for
+ the nexus_ip_address being used in the above cases)
+
+3. For testing the UCS device sub-plugin perform the following configuration:
+
+ Edit etc/quantum/plugins/cisco/cisco_plugins.ini to add:
+ In the [PLUGINS] section add:
+ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin_v2.UCSVICPlugin
+
+ In the [INVENTORY] section add:
+ When not using UCS hardware:
+ucs_plugin=quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake.UCSInventory
+ Or when using UCS hardware:
+ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_inventory_v2.UCSInventory
+
+ Edit the etc/quantum/plugins/cisco/ucs.ini file.
+ When not using UCS hardware:
+[DRIVER]
+name=quantum.plugins.cisco.tests.unit.v2.ucs.fake_ucs_driver.CiscoUCSMFakeDriver
+ Or when using UCS hardware:
+[DRIVER]
+name=quantum.plugins.cisco.ucs.cisco_ucs_network_driver.CiscoUCSMDriver
+
+
+:Web site: http://wiki.openstack.org/cisco-quantum
+:Copyright: 2012 Cisco Systems, Inc.
+:Contact: netstack@lists.launchpad.net
+
+=========================================================================================
+README for Quantum v1 and v1.1:
+A Quantum Plugin Framework for Supporting L2 Networks Spannning Multiple Switches
=========================================================================================
:Author: Sumit Naiksatam, Ram Durairaj, Mark Voelker, Edgar Magana, Shweta Padubidri,
Plugin Installation Instructions
----------------------------------
-1. Make a backup copy of quantum/etc/plugins.ini.
+1. Make a backup copy of quantum/etc/quantum.conf
+
+2. Edit quantum/etc/quantum.conf and edit the "core_plugin" for v2 API
+
+core_plugin = quantum.plugins.cisco.network_plugin.PluginV2
-2. Edit quantum/etc/plugins.ini and edit the "provider" entry to point
- to the L2Network-plugin:
+ OR for v1.1 API
-provider = quantum.plugins.cisco.l2network_plugin.L2Network
+core_plugin = quantum.plugins.cisco.l2network_plugin.L2Network
3. Configure your OpenStack installation to use the 802.1qbh VIF driver and
Quantum-aware scheduler by editing the /etc/nova/nova.conf file with the
Device-specific sub-plugins can be disabled by commenting out all the entries in:
etc/quantum/plugins/cisco/cisco_plugins.ini
- Execute the l2networkApi tests only using:
+ Execute the v2 API tests only using:
+ ./run_tests.sh quantum.plugins.cisco.tests.unit.test_api_v2
+ Execute the v1.1 API tests only using:
./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
If just the ucs or both ucs and the nexus plugins are configured then all the tests could be executed by
Nexus plugins.
Device-specific plugins can be disabled by commenting out the entries in:
etc/quantum/plugins/cisco/cisco_plugins.ini
- Execute the test script as follows:
- ./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
+ Execute the v2 API tests only using:
+ ./run_tests.sh quantum.plugins.cisco.tests.unit.test_api_v2
+ or
+ python run_tests.py quantum.plugins.cisco.tests.unit.test_api_v2
- or
-
- python run_tests.py quantum.plugins.cisco.tests.unit.test_l2networkApi
+ Execute the v1.1 API tests only using:
+ ./run_tests.sh quantum.plugins.cisco.tests.unit.test_l2networkApi
+ or
+ python run_tests.py quantum.plugins.cisco.tests.unit.test_l2networkApi
3. Specific Plugin unit test (needs environment setup as indicated in the
pre-requisites):
ATTACHED = 'attached'
DETACHED = 'detached'
+
+NETWORK = 'network'
+PORT = 'port'
+BASE_PLUGIN_REF = 'base_plugin_ref'
+CONTEXT = 'context'
+SUBNET = 'subnet'
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+
+import logging as LOG
+
+from quantum.common.utils import find_config_file
+from quantum.plugins.cisco.common import cisco_configparser as confp
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_exceptions as cexc
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+
+
+LOG.basicConfig(level=LOG.WARN)
+LOG.getLogger(const.LOGGER_COMPONENT_NAME)
+
+CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'},
+ "credentials.ini")
+TENANT = const.NETWORK_ADMIN
+
+cp = confp.CiscoConfigParser(CREDENTIALS_FILE)
+_creds_dictionary = cp.walk(cp.dummy)
+
+
+class Store(object):
+ """Credential Store"""
+
+ @staticmethod
+ def initialize():
+ for id in _creds_dictionary.keys():
+ try:
+ cdb.add_credential(TENANT, id,
+ _creds_dictionary[id][const.USERNAME],
+ _creds_dictionary[id][const.PASSWORD])
+ except cexc.CredentialAlreadyExists:
+ # We are quietly ignoring this, since it only happens
+ # if this class module is loaded more than once, in which
+ # case, the credentials are already populated
+ pass
+
+ @staticmethod
+ def put_credential(cred_name, username, password):
+ """Set the username and password"""
+ credential = cdb.add_credential(TENANT, cred_name, username, password)
+
+ @staticmethod
+ def get_username(cred_name):
+ """Get the username"""
+ credential = cdb.get_credential_name(TENANT, cred_name)
+ return credential[const.CREDENTIAL_USERNAME]
+
+ @staticmethod
+ def get_password(cred_name):
+ """Get the password"""
+ credential = cdb.get_credential_name(TENANT, cred_name)
+ return credential[const.CREDENTIAL_PASSWORD]
+
+ @staticmethod
+ def get_credential(cred_name):
+ """Get the username and password"""
+ credential = cdb.get_credential_name(TENANT, cred_name)
+ return {const.USERNAME: const.CREDENTIAL_USERNAME,
+ const.PASSWORD: const.CREDENTIAL_PASSWORD}
+
+ @staticmethod
+ def delete_credential(cred_name):
+ """Delete a credential"""
+ cdb.remove_credential(TENANT, cred_name)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+import logging as LOG
+from sqlalchemy.orm import exc
+
+from quantum.common import exceptions as q_exc
+from quantum.db import api as db
+from quantum.db import models_v2
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_exceptions as c_exc
+from quantum.plugins.cisco.db import network_models_v2
+from quantum.plugins.cisco.db import nexus_models_v2
+from quantum.plugins.cisco.db import ucs_models_v2
+from quantum.plugins.cisco import l2network_plugin_configuration as conf
+
+
+def initialize():
+ 'Establish database connection and load models'
+ sql_connection = "mysql://%s:%s@%s/%s" % (conf.DB_USER, conf.DB_PASS,
+ conf.DB_HOST, conf.DB_NAME)
+ db.configure_db({'sql_connection': sql_connection,
+ 'base': network_models_v2.model_base.BASEV2})
+
+
+def create_vlanids():
+ """Prepopulates the vlan_bindings table"""
+ LOG.debug("create_vlanids() called")
+ session = db.get_session()
+ try:
+ vlanid = session.query(network_models_v2.VlanID).one()
+ except exc.MultipleResultsFound:
+ pass
+ except exc.NoResultFound:
+ start = int(conf.VLAN_START)
+ end = int(conf.VLAN_END)
+ while start <= end:
+ vlanid = network_models_v2.VlanID(start)
+ session.add(vlanid)
+ start += 1
+ session.flush()
+ return
+
+
+def get_all_vlanids():
+ """Gets all the vlanids"""
+ LOG.debug("get_all_vlanids() called")
+ session = db.get_session()
+ try:
+ vlanids = session.query(network_models_v2.VlanID).all()
+ return vlanids
+ except exc.NoResultFound:
+ return []
+
+
+def is_vlanid_used(vlan_id):
+ """Checks if a vlanid is in use"""
+ LOG.debug("is_vlanid_used() called")
+ session = db.get_session()
+ try:
+ vlanid = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_id=vlan_id).one())
+ return vlanid["vlan_used"]
+ except exc.NoResultFound:
+ raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
+
+
+def release_vlanid(vlan_id):
+ """Sets the vlanid state to be unused"""
+ LOG.debug("release_vlanid() called")
+ session = db.get_session()
+ try:
+ vlanid = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_id=vlan_id).one())
+ vlanid["vlan_used"] = False
+ session.merge(vlanid)
+ session.flush()
+ return vlanid["vlan_used"]
+ except exc.NoResultFound:
+ raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
+ return
+
+
+def delete_vlanid(vlan_id):
+ """Deletes a vlanid entry from db"""
+ LOG.debug("delete_vlanid() called")
+ session = db.get_session()
+ try:
+ vlanid = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_id=vlan_id).one())
+ session.delete(vlanid)
+ session.flush()
+ return vlanid
+ except exc.NoResultFound:
+ pass
+
+
+def reserve_vlanid():
+ """Reserves the first unused vlanid"""
+ LOG.debug("reserve_vlanid() called")
+ session = db.get_session()
+ try:
+ rvlan = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_used=False).first())
+ if not rvlan:
+ raise exc.NoResultFound
+ rvlanid = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_id=rvlan["vlan_id"]).one())
+ rvlanid["vlan_used"] = True
+ session.merge(rvlanid)
+ session.flush()
+ return rvlan["vlan_id"]
+ except exc.NoResultFound:
+ raise c_exc.VlanIDNotAvailable()
+
+
+def get_all_vlanids_used():
+ """Gets all the vlanids used"""
+ LOG.debug("get_all_vlanids() called")
+ session = db.get_session()
+ try:
+ vlanids = (session.query(network_models_v2.VlanID).
+ filter_by(vlan_used=True).all())
+ return vlanids
+ except exc.NoResultFound:
+ return []
+
+
+def get_all_vlan_bindings():
+ """Lists all the vlan to network associations"""
+ LOG.debug("get_all_vlan_bindings() called")
+ session = db.get_session()
+ try:
+ bindings = session.query(network_models_v2.VlanBinding).all()
+ return bindings
+ except exc.NoResultFound:
+ return []
+
+
+def get_vlan_binding(netid):
+ """Lists the vlan given a network_id"""
+ LOG.debug("get_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.VlanBinding).
+ filter_by(network_id=netid).one())
+ return binding
+ except exc.NoResultFound:
+ raise q_exc.NetworkNotFound(net_id=netid)
+
+
+def add_vlan_binding(vlanid, vlanname, netid):
+ """Adds a vlan to network association"""
+ LOG.debug("add_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.VlanBinding).
+ filter_by(vlan_id=vlanid).one())
+ raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
+ network_id=netid)
+ except exc.NoResultFound:
+ binding = network_models_v2.VlanBinding(vlanid, vlanname, netid)
+ session.add(binding)
+ session.flush()
+ return binding
+
+
+def remove_vlan_binding(netid):
+ """Removes a vlan to network association"""
+ LOG.debug("remove_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.VlanBinding).
+ filter_by(network_id=netid).one())
+ session.delete(binding)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ pass
+
+
+def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
+ """Updates a vlan to network association"""
+ LOG.debug("update_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.VlanBinding).
+ filter_by(network_id=netid).one())
+ if newvlanid:
+ binding["vlan_id"] = newvlanid
+ if newvlanname:
+ binding["vlan_name"] = newvlanname
+ session.merge(binding)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ raise q_exc.NetworkNotFound(net_id=netid)
+
+
+def get_all_portprofiles():
+ """Lists all the port profiles"""
+ LOG.debug("get_all_portprofiles() called")
+ session = db.get_session()
+ try:
+ pps = session.query(network_models_v2.PortProfile).all()
+ return pps
+ except exc.NoResultFound:
+ return []
+
+
+def get_portprofile(tenantid, ppid):
+ """Lists a port profile"""
+ LOG.debug("get_portprofile() called")
+ session = db.get_session()
+ try:
+ pp = (session.query(network_models_v2.PortProfile).
+ filter_by(uuid=ppid).one())
+ return pp
+ except exc.NoResultFound:
+ raise c_exc.PortProfileNotFound(tenant_id=tenantid,
+ portprofile_id=ppid)
+
+
+def add_portprofile(tenantid, ppname, vlanid, qos):
+ """Adds a port profile"""
+ LOG.debug("add_portprofile() called")
+ session = db.get_session()
+ try:
+ pp = (session.query(network_models_v2.PortProfile).
+ filter_by(name=ppname).one())
+ raise c_exc.PortProfileAlreadyExists(tenant_id=tenantid,
+ pp_name=ppname)
+ except exc.NoResultFound:
+ pp = network_models_v2.PortProfile(ppname, vlanid, qos)
+ session.add(pp)
+ session.flush()
+ return pp
+
+
+def remove_portprofile(tenantid, ppid):
+ """Removes a port profile"""
+ LOG.debug("remove_portprofile() called")
+ session = db.get_session()
+ try:
+ pp = (session.query(network_models_v2.PortProfile).
+ filter_by(uuid=ppid).one())
+ session.delete(pp)
+ session.flush()
+ return pp
+ except exc.NoResultFound:
+ pass
+
+
+def update_portprofile(tenantid, ppid, newppname=None, newvlanid=None,
+ newqos=None):
+ """Updates port profile"""
+ LOG.debug("update_portprofile() called")
+ session = db.get_session()
+ try:
+ pp = (session.query(network_models_v2.PortProfile).
+ filter_by(uuid=ppid).one())
+ if newppname:
+ pp["name"] = newppname
+ if newvlanid:
+ pp["vlan_id"] = newvlanid
+ if newqos:
+ pp["qos"] = newqos
+ session.merge(pp)
+ session.flush()
+ return pp
+ except exc.NoResultFound:
+ raise c_exc.PortProfileNotFound(tenant_id=tenantid,
+ portprofile_id=ppid)
+
+
+def get_all_pp_bindings():
+ """Lists all the port profiles"""
+ LOG.debug("get_all_pp_bindings() called")
+ session = db.get_session()
+ try:
+ bindings = session.query(network_models_v2.PortProfileBinding).all()
+ return bindings
+ except exc.NoResultFound:
+ return []
+
+
+def get_pp_binding(tenantid, ppid):
+ """Lists a port profile binding"""
+ LOG.debug("get_pp_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.PortProfileBinding).
+ filter_by(portprofile_id=ppid).one())
+ return binding
+ except exc.NoResultFound:
+ return []
+
+
+def add_pp_binding(tenantid, portid, ppid, default):
+ """Adds a port profile binding"""
+ LOG.debug("add_pp_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.PortProfileBinding).
+ filter_by(portprofile_id=ppid).one())
+ raise c_exc.PortProfileBindingAlreadyExists(pp_id=ppid,
+ port_id=portid)
+ except exc.NoResultFound:
+ binding = network_models_v2.PortProfileBinding(tenantid, portid,
+ ppid, default)
+ session.add(binding)
+ session.flush()
+ return binding
+
+
+def remove_pp_binding(tenantid, portid, ppid):
+ """Removes a port profile binding"""
+ LOG.debug("remove_pp_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.PortProfileBinding).
+ filter_by(portprofile_id=ppid).filter_by(port_id=portid).
+ one())
+ session.delete(binding)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ pass
+
+
+def update_pp_binding(tenantid, ppid, newtenantid=None,
+ newportid=None, newdefault=None):
+ """Updates port profile binding"""
+ LOG.debug("update_pp_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(network_models_v2.PortProfileBinding).
+ filter_by(portprofile_id=ppid).one())
+ if newtenantid:
+ binding["tenant_id"] = newtenantid
+ if newportid:
+ binding["port_id"] = newportid
+ if newdefault:
+ binding["default"] = newdefault
+ session.merge(binding)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ raise c_exc.PortProfileNotFound(tenant_id=tenantid,
+ portprofile_id=ppid)
+
+
+def get_all_qoss(tenant_id):
+ """Lists all the qos to tenant associations"""
+ LOG.debug("get_all_qoss() called")
+ session = db.get_session()
+ try:
+ qoss = (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).all())
+ return qoss
+ except exc.NoResultFound:
+ return []
+
+
+def get_qos(tenant_id, qos_id):
+ """Lists the qos given a tenant_id and qos_id"""
+ LOG.debug("get_qos() called")
+ session = db.get_session()
+ try:
+ qos = (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).
+ filter_by(qos_id=qos_id).one())
+ return qos
+ except exc.NoResultFound:
+ raise c_exc.QosNotFound(qos_id=qos_id,
+ tenant_id=tenant_id)
+
+
+def add_qos(tenant_id, qos_name, qos_desc):
+ """Adds a qos to tenant association"""
+ LOG.debug("add_qos() called")
+ session = db.get_session()
+ try:
+ qos = (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).
+ filter_by(qos_name=qos_name).one())
+ raise c_exc.QosNameAlreadyExists(qos_name=qos_name,
+ tenant_id=tenant_id)
+ except exc.NoResultFound:
+ qos = network_models_v2.QoS(tenant_id, qos_name, qos_desc)
+ session.add(qos)
+ session.flush()
+ return qos
+
+
+def remove_qos(tenant_id, qos_id):
+ """Removes a qos to tenant association"""
+ session = db.get_session()
+ try:
+ qos = (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).
+ filter_by(qos_id=qos_id).one())
+ session.delete(qos)
+ session.flush()
+ return qos
+ except exc.NoResultFound:
+ pass
+
+
+def update_qos(tenant_id, qos_id, new_qos_name=None):
+ """Updates a qos to tenant association"""
+ session = db.get_session()
+ try:
+ qos = (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).
+ filter_by(qos_id=qos_id).one())
+ if new_qos_name:
+ qos["qos_name"] = new_qos_name
+ session.merge(qos)
+ session.flush()
+ return qos
+ except exc.NoResultFound:
+ raise c_exc.QosNotFound(qos_id=qos_id,
+ tenant_id=tenant_id)
+
+
+def get_all_credentials(tenant_id):
+ """Lists all the creds for a tenant"""
+ session = db.get_session()
+ try:
+ creds = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).all())
+ return creds
+ except exc.NoResultFound:
+ return []
+
+
+def get_credential(tenant_id, credential_id):
+ """Lists the creds for given a cred_id and tenant_id"""
+ session = db.get_session()
+ try:
+ cred = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).
+ filter_by(credential_id=credential_id).one())
+ return cred
+ except exc.NoResultFound:
+ raise c_exc.CredentialNotFound(credential_id=credential_id,
+ tenant_id=tenant_id)
+
+
+def get_credential_name(tenant_id, credential_name):
+ """Lists the creds for given a cred_name and tenant_id"""
+ session = db.get_session()
+ try:
+ cred = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).
+ filter_by(credential_name=credential_name).one())
+ return cred
+ except exc.NoResultFound:
+ raise c_exc.CredentialNameNotFound(credential_name=credential_name,
+ tenant_id=tenant_id)
+
+
+def add_credential(tenant_id, credential_name, user_name, password):
+ """Adds a qos to tenant association"""
+ session = db.get_session()
+ try:
+ cred = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).
+ filter_by(credential_name=credential_name).one())
+ raise c_exc.CredentialAlreadyExists(credential_name=credential_name,
+ tenant_id=tenant_id)
+ except exc.NoResultFound:
+ cred = network_models_v2.Credential(tenant_id, credential_name,
+ user_name, password)
+ session.add(cred)
+ session.flush()
+ return cred
+
+
+def remove_credential(tenant_id, credential_id):
+ """Removes a credential from a tenant"""
+ session = db.get_session()
+ try:
+ cred = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).
+ filter_by(credential_id=credential_id).one())
+ session.delete(cred)
+ session.flush()
+ return cred
+ except exc.NoResultFound:
+ pass
+
+
+def update_credential(tenant_id, credential_id,
+ new_user_name=None, new_password=None):
+ """Updates a credential for a tenant"""
+ session = db.get_session()
+ try:
+ cred = (session.query(network_models_v2.Credential).
+ filter_by(tenant_id=tenant_id).
+ filter_by(credential_id=credential_id).one())
+ if new_user_name:
+ cred["user_name"] = new_user_name
+ if new_password:
+ cred["password"] = new_password
+ session.merge(cred)
+ session.flush()
+ return cred
+ except exc.NoResultFound:
+ raise c_exc.CredentialNotFound(credential_id=credential_id,
+ tenant_id=tenant_id)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+import uuid
+
+from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
+from sqlalchemy.orm import relation, object_mapper
+
+from quantum.db import model_base
+from quantum.db import models_v2 as models
+
+
+class L2NetworkBase(object):
+ """Base class for L2Network Models."""
+ #__table_args__ = {'mysql_engine': 'InnoDB'}
+
+ def __setitem__(self, key, value):
+ """Internal Dict set method"""
+ setattr(self, key, value)
+
+ def __getitem__(self, key):
+ """Internal Dict get method"""
+ return getattr(self, key)
+
+ def get(self, key, default=None):
+ """Dict get method"""
+ return getattr(self, key, default)
+
+ def __iter__(self):
+ """Iterate over table columns"""
+ self._i = iter(object_mapper(self).columns)
+ return self
+
+ def next(self):
+ """Next method for the iterator"""
+ n = self._i.next().name
+ return n, getattr(self, n)
+
+ def update(self, values):
+ """Make the model object behave like a dict"""
+ for k, v in values.iteritems():
+ setattr(self, k, v)
+
+ def iteritems(self):
+ """Make the model object behave like a dict"
+ Includes attributes from joins."""
+ local = dict(self)
+ joined = dict([(k, v) for k, v in self.__dict__.iteritems()
+ if not k[0] == '_'])
+ local.update(joined)
+ return local.iteritems()
+
+
+class VlanID(model_base.BASEV2, L2NetworkBase):
+ """Represents a vlan_id usage"""
+ __tablename__ = 'vlan_ids'
+
+ vlan_id = Column(Integer, primary_key=True)
+ vlan_used = Column(Boolean)
+
+ def __init__(self, vlan_id):
+ self.vlan_id = vlan_id
+ self.vlan_used = False
+
+ def __repr__(self):
+ return "<VlanID(%d,%s)>" % (self.vlan_id, self.vlan_used)
+
+
+class VlanBinding(model_base.BASEV2, L2NetworkBase):
+ """Represents a binding of vlan_id to network_id"""
+ __tablename__ = 'vlan_bindings'
+
+ vlan_id = Column(Integer, primary_key=True)
+ vlan_name = Column(String(255))
+ network_id = Column(String(255),
+ nullable=False)
+
+ def __init__(self, vlan_id, vlan_name, network_id):
+ self.vlan_id = vlan_id
+ self.vlan_name = vlan_name
+ self.network_id = network_id
+
+ def __repr__(self):
+ return "<VlanBinding(%d,%s,%s)>" % (self.vlan_id,
+ self.vlan_name,
+ self.network_id)
+
+
+class PortProfile(model_base.BASEV2, L2NetworkBase):
+ """Represents L2 network plugin level PortProfile for a network"""
+ __tablename__ = 'portprofiles'
+
+ uuid = Column(String(255), primary_key=True)
+ name = Column(String(255))
+ vlan_id = Column(Integer)
+ qos = Column(String(255))
+
+ def __init__(self, name, vlan_id, qos=None):
+ self.uuid = uuid.uuid4()
+ self.name = name
+ self.vlan_id = vlan_id
+ self.qos = qos
+
+ def __repr__(self):
+ return "<PortProfile(%s,%s,%d,%s)>" % (self.uuid,
+ self.name,
+ self.vlan_id,
+ self.qos)
+
+
+class PortProfileBinding(model_base.BASEV2, L2NetworkBase):
+ """Represents PortProfile binding to tenant and network"""
+ __tablename__ = 'portprofile_bindings'
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ tenant_id = Column(String(255))
+
+ port_id = Column(String(255), ForeignKey("ports.id"), nullable=False)
+ portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"),
+ nullable=False)
+ default = Column(Boolean)
+ ports = relation(models.Port)
+ portprofile = relation(PortProfile, uselist=False)
+
+ def __init__(self, tenant_id, port_id, portprofile_id, default):
+ self.tenant_id = tenant_id
+ self.port_id = port_id
+ self.portprofile_id = portprofile_id
+ self.default = default
+
+ def __repr__(self):
+ return "<PortProfile Binding(%s,%s,%s,%s)>" % (self.tenant_id,
+ self.port_id,
+ self.portprofile_id,
+ self.default)
+
+
+class QoS(model_base.BASEV2, L2NetworkBase):
+ """Represents QoS for a tenant"""
+ __tablename__ = 'qoss'
+
+ qos_id = Column(String(255))
+ tenant_id = Column(String(255), primary_key=True)
+ qos_name = Column(String(255), primary_key=True)
+ qos_desc = Column(String(255))
+
+ def __init__(self, tenant_id, qos_name, qos_desc):
+ self.qos_id = str(uuid.uuid4())
+ self.tenant_id = tenant_id
+ self.qos_name = qos_name
+ self.qos_desc = qos_desc
+
+ def __repr__(self):
+ return "<QoS(%s,%s,%s,%s)>" % (self.qos_id, self.tenant_id,
+ self.qos_name, self.qos_desc)
+
+
+class Credential(model_base.BASEV2, L2NetworkBase):
+ """Represents credentials for a tenant"""
+ __tablename__ = 'credentials'
+
+ credential_id = Column(String(255))
+ tenant_id = Column(String(255), primary_key=True)
+ credential_name = Column(String(255), primary_key=True)
+ user_name = Column(String(255))
+ password = Column(String(255))
+
+ def __init__(self, tenant_id, credential_name, user_name, password):
+ self.credential_id = str(uuid.uuid4())
+ self.tenant_id = tenant_id
+ self.credential_name = credential_name
+ self.user_name = user_name
+ self.password = password
+
+ def __repr__(self):
+ return "<Credentials(%s,%s,%s,%s,%s)>" % (self.credential_id,
+ self.tenant_id,
+ self.credential_name,
+ self.user_name,
+ self.password)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+import logging as LOG
+
+from sqlalchemy.orm import exc
+
+import quantum.db.api as db
+
+from quantum.plugins.cisco.common import cisco_exceptions as c_exc
+from quantum.plugins.cisco.db import nexus_models_v2
+
+
+def get_all_nexusport_bindings():
+ """Lists all the nexusport bindings"""
+ LOG.debug("get_all_nexusport_bindings() called")
+ session = db.get_session()
+ try:
+ bindings = session.query(nexus_models_v2.NexusPortBinding).all()
+ return bindings
+ except exc.NoResultFound:
+ return []
+
+
+def get_nexusport_binding(vlan_id):
+ """Lists a nexusport binding"""
+ LOG.debug("get_nexusport_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(nexus_models_v2.NexusPortBinding).
+ filter_by(vlan_id=vlan_id).all())
+ return binding
+ except exc.NoResultFound:
+ raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
+
+
+def add_nexusport_binding(port_id, vlan_id):
+ """Adds a nexusport binding"""
+ LOG.debug("add_nexusport_binding() called")
+ session = db.get_session()
+ binding = nexus_models_v2.NexusPortBinding(port_id, vlan_id)
+ session.add(binding)
+ session.flush()
+ return binding
+
+
+def remove_nexusport_binding(vlan_id):
+ """Removes a nexusport binding"""
+ LOG.debug("remove_nexusport_binding() called")
+ session = db.get_session()
+ try:
+ binding = (session.query(nexus_models_v2.NexusPortBinding).
+ filter_by(vlan_id=vlan_id).all())
+ for bind in binding:
+ session.delete(bind)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ pass
+
+
+def update_nexusport_binding(port_id, new_vlan_id):
+ """Updates nexusport binding"""
+ LOG.debug("update_nexusport_binding called")
+ session = db.get_session()
+ try:
+ binding = (session.query(nexus_models_v2.NexusPortBinding).
+ filter_by(port_id=port_id).one())
+ if new_vlan_id:
+ binding["vlan_id"] = new_vlan_id
+ session.merge(binding)
+ session.flush()
+ return binding
+ except exc.NoResultFound:
+ raise c_exc.NexusPortBindingNotFound()
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+from sqlalchemy import Column, Integer, String
+
+from quantum.db import model_base
+from quantum.plugins.cisco.db.l2network_models import L2NetworkBase
+
+
+class NexusPortBinding(model_base.BASEV2, L2NetworkBase):
+ """Represents a binding of nexus port to vlan_id"""
+ __tablename__ = 'nexusport_bindings'
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ port_id = Column(String(255))
+ vlan_id = Column(Integer, nullable=False)
+
+ def __init__(self, port_id, vlan_id):
+ self.port_id = port_id
+ self.vlan_id = vlan_id
+
+ def __repr__(self):
+ return "<NexusPortBinding (%s,%d)>" % (self.port_id, self.vlan_id)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+import logging as LOG
+
+from sqlalchemy.orm import exc
+
+from quantum.db import api as db
+
+from quantum.plugins.cisco.common import cisco_exceptions as c_exc
+from quantum.plugins.cisco.db import ucs_models_v2 as ucs_models
+
+
+def get_all_portbindings():
+ """Lists all the port bindings"""
+ LOG.debug("db get_all_portbindings() called")
+ session = db.get_session()
+ try:
+ port_bindings = session.query(ucs_models.PortBinding).all()
+ return port_bindings
+ except exc.NoResultFound:
+ return []
+
+
+def get_portbinding(port_id):
+ """Lists a port binding"""
+ LOG.debug("get_portbinding() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ return port_binding
+ except exc.NoResultFound:
+ raise c_exc.PortVnicNotFound(port_id=port_id)
+
+
+def add_portbinding(port_id, blade_intf_dn, portprofile_name,
+ vlan_name, vlan_id, qos):
+ """Adds a port binding"""
+ LOG.debug("add_portbinding() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ raise c_exc.PortVnicBindingAlreadyExists(port_id=port_id)
+ except exc.NoResultFound:
+ port_binding = ucs_models.PortBinding(port_id, blade_intf_dn,
+ portprofile_name, vlan_name,
+ vlan_id, qos)
+ session.add(port_binding)
+ session.flush()
+ return port_binding
+
+
+def remove_portbinding(port_id):
+ """Removes a port binding"""
+ LOG.debug("db remove_portbinding() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ session.delete(port_binding)
+ session.flush()
+ return port_binding
+ except exc.NoResultFound:
+ pass
+
+
+def update_portbinding(port_id, blade_intf_dn=None, portprofile_name=None,
+ vlan_name=None, vlan_id=None, qos=None,
+ tenant_id=None, instance_id=None,
+ vif_id=None):
+ """Updates port binding"""
+ LOG.debug("db update_portbinding() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ if blade_intf_dn:
+ port_binding.blade_intf_dn = blade_intf_dn
+ if portprofile_name:
+ port_binding.portprofile_name = portprofile_name
+ if vlan_name:
+ port_binding.vlan_name = vlan_name
+ if vlan_name:
+ port_binding.vlan_id = vlan_id
+ if qos:
+ port_binding.qos = qos
+ if tenant_id:
+ port_binding.tenant_id = tenant_id
+ if instance_id:
+ port_binding.instance_id = instance_id
+ if vif_id:
+ port_binding.vif_id = vif_id
+ session.merge(port_binding)
+ session.flush()
+ return port_binding
+ except exc.NoResultFound:
+ raise c_exc.PortVnicNotFound(port_id=port_id)
+
+
+def update_portbinding_instance_id(port_id, instance_id):
+ """Updates port binding for the instance ID"""
+ LOG.debug("db update_portbinding_instance_id() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ port_binding.instance_id = instance_id
+ session.merge(port_binding)
+ session.flush()
+ return port_binding
+ except exc.NoResultFound:
+ raise c_exc.PortVnicNotFound(port_id=port_id)
+
+
+def update_portbinding_vif_id(port_id, vif_id):
+ """Updates port binding for the VIF ID"""
+ LOG.debug("db update_portbinding_vif_id() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(port_id=port_id).one())
+ port_binding.vif_id = vif_id
+ session.merge(port_binding)
+ session.flush()
+ return port_binding
+ except exc.NoResultFound:
+ raise c_exc.PortVnicNotFound(port_id=port_id)
+
+
+def get_portbinding_dn(blade_intf_dn):
+ """Lists a port binding"""
+ LOG.debug("get_portbinding_dn() called")
+ session = db.get_session()
+ try:
+ port_binding = (session.query(ucs_models.PortBinding).
+ filter_by(blade_intf_dn=blade_intf_dn).one())
+ return port_binding
+ except exc.NoResultFound:
+ return []
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+from sqlalchemy import Column, Integer, String
+from sqlalchemy.orm import relation
+
+from quantum.db.model_base import BASEV2 as BASE
+from quantum.db import models_v2 as models
+from quantum.plugins.cisco.db.network_models_v2 import L2NetworkBase
+
+
+class PortBinding(BASE, L2NetworkBase):
+ """Represents Port binding to device interface"""
+ __tablename__ = 'port_bindings'
+
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ port_id = Column(String(255), nullable=False)
+ blade_intf_dn = Column(String(255), nullable=False)
+ portprofile_name = Column(String(255))
+ vlan_name = Column(String(255))
+ vlan_id = Column(Integer)
+ qos = Column(String(255))
+ tenant_id = Column(String(255))
+ instance_id = Column(String(255))
+ vif_id = Column(String(255))
+
+ def __init__(self, port_id, blade_intf_dn, portprofile_name,
+ vlan_name, vlan_id, qos):
+ self.port_id = port_id
+ self.blade_intf_dn = blade_intf_dn
+ self.portprofile_name = portprofile_name
+ self.vlan_name = vlan_name
+ self.vlan_id = vlan_id
+ self.qos = qos
+
+ def __repr__(self):
+ return "<PortProfile Binding(%s,%s,%s,%s,%s,%s)>" % (
+ self.port_id, self.blade_intf_dn, self.portprofile_name,
+ self.vlan_name, self.vlan_id, self.qos)
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
-# Copyright 2011 Cisco Systems, Inc. All rights reserved.
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
"""
pass
+ def create_subnet(self, tenant_id, net_id, ip_version,
+ subnet_cidr, **kwargs):
+ """
+ :returns:
+ :raises:
+ """
+ pass
+
+ def get_subnets(self, tenant_id, net_id, **kwargs):
+ """
+ :returns:
+ :raises:
+ """
+ pass
+
+ def get_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
+ """
+ :returns:
+ :raises:
+ """
+ pass
+
+ def update_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
+ """
+ :returns:
+ :raises:
+ """
+ pass
+
+ def delete_subnet(self, tenant_id, net_id, subnet_id, **kwargs):
+ """
+ :returns:
+ :raises:
+ """
+ pass
+
@classmethod
def __subclasshook__(cls, klass):
"""
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+
+from copy import deepcopy
+import inspect
+import logging
+
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco import l2network_plugin_configuration as conf
+from quantum import quantum_plugin_base_v2
+
+
+LOG = logging.getLogger(__name__)
+
+
+class NetworkMultiBladeV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
+ """
+ This implementation works with UCS and Nexus plugin for the
+ following topology:
+ One or more UCSM (each with one or more chasses connected),
+ All FICs connected to a single Nexus Switch.
+ """
+ _plugins = {}
+ _inventory = {}
+
+ def __init__(self):
+ """
+ Initialize the segmentation manager, check which device plugins are
+ configured, and load the inventories those device plugins for which the
+ inventory is configured
+ """
+ self._vlan_mgr = importutils.import_object(conf.MANAGER_CLASS)
+ for key in conf.PLUGINS[const.PLUGINS].keys():
+ plugin_obj = conf.PLUGINS[const.PLUGINS][key]
+ self._plugins[key] = importutils.import_object(plugin_obj)
+ LOG.debug("Loaded device plugin %s\n" %
+ conf.PLUGINS[const.PLUGINS][key])
+ if key in conf.PLUGINS[const.INVENTORY].keys():
+ inventory_obj = conf.PLUGINS[const.INVENTORY][key]
+ self._inventory[key] = importutils.import_object(inventory_obj)
+ LOG.debug("Loaded device inventory %s\n" %
+ conf.PLUGINS[const.INVENTORY][key])
+
+ LOG.debug("%s.%s init done" % (__name__, self.__class__.__name__))
+
+ def _func_name(self, offset=0):
+ """Get the name of the calling function"""
+ return inspect.stack()[1 + offset][3]
+
+ def _invoke_plugin_per_device(self, plugin_key, function_name, args):
+ """
+ Invokes a device plugin's relevant functions (on the it's
+ inventory and plugin implementation) for completing this operation.
+ """
+ if not plugin_key in self._plugins.keys():
+ LOG.info("No %s Plugin loaded" % plugin_key)
+ LOG.info("%s: %s with args %s ignored" %
+ (plugin_key, function_name, args))
+ return
+ device_params = self._invoke_inventory(plugin_key, function_name,
+ args)
+ device_ips = device_params[const.DEVICE_IP]
+ if not device_ips:
+ return [self._invoke_plugin(plugin_key, function_name, args,
+ device_params)]
+ else:
+ output = []
+ for device_ip in device_ips:
+ new_device_params = deepcopy(device_params)
+ new_device_params[const.DEVICE_IP] = device_ip
+ output.append(self._invoke_plugin(plugin_key, function_name,
+ args, new_device_params))
+ return output
+
+ def _invoke_inventory(self, plugin_key, function_name, args):
+ """
+ Invokes the relevant function on a device plugin's
+ inventory for completing this operation.
+ """
+ if not plugin_key in self._inventory.keys():
+ LOG.info("No %s inventory loaded" % plugin_key)
+ LOG.info("%s: %s with args %s ignored" %
+ (plugin_key, function_name, args))
+ return {const.DEVICE_IP: []}
+ else:
+ return getattr(self._inventory[plugin_key], function_name)(args)
+
+ def _invoke_plugin(self, plugin_key, function_name, args, kwargs):
+ """
+ Invokes the relevant function on a device plugin's
+ implementation for completing this operation.
+ """
+ func = getattr(self._plugins[plugin_key], function_name)
+ func_args_len = int(inspect.getargspec(func).args.__len__()) - 1
+ if args.__len__() > func_args_len:
+ func_args = args[:func_args_len]
+ extra_args = args[func_args_len:]
+ for dict_arg in extra_args:
+ for k, v in dict_arg.iteritems():
+ kwargs[k] = v
+ return func(*func_args, **kwargs)
+ else:
+ return func(*args, **kwargs)
+
+ def create_network(self, context, network):
+ """
+ Perform this operation in the context of the configured device
+ plugins.
+ """
+ n = network
+ try:
+ vlan_id = self._vlan_mgr.reserve_segmentation_id(n['tenant_id'],
+ n['name'])
+ vlan_name = self._vlan_mgr.get_vlan_name(n['id'], str(vlan_id))
+ args = [n['tenant_id'], n['name'], n['id'], vlan_name, vlan_id]
+ output = []
+ ucs_output = self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ self._func_name(),
+ args)
+ nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+ self._func_name(),
+ args)
+ output.extend(ucs_output or [])
+ output.extend(nexus_output or [])
+ cdb.add_vlan_binding(vlan_id, vlan_name, n['id'])
+ return output
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def get_network(self, context, id, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def get_networks(self, context, filters=None, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def update_network(self, context, id, network):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def delete_network(self, context, id, kwargs):
+ """
+ Perform this operation in the context of the configured device
+ plugins.
+ """
+ try:
+ base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
+ n = kwargs[const.NETWORK]
+ tenant_id = n['tenant_id']
+ args = [tenant_id, id, {const.CONTEXT:context},
+ {const.BASE_PLUGIN_REF:base_plugin_ref}]
+ # TODO (Sumit): Might first need to check here if there are active
+ # ports
+ output = []
+ ucs_output = self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ self._func_name(),
+ args)
+ nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+ self._func_name(),
+ args)
+ output.extend(ucs_output or [])
+ output.extend(nexus_output or [])
+ self._vlan_mgr.release_segmentation_id(tenant_id, id)
+ cdb.remove_vlan_binding(id)
+ return output
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def create_port(self, context, port):
+ """
+ Perform this operation in the context of the configured device
+ plugins.
+ """
+ try:
+ tenant_id = port['tenant_id']
+ net_id = port['network_id']
+ port_state = port['admin_state_up']
+ port_id_string = port['id']
+ args = [tenant_id, net_id, port_state, port_id_string]
+ ret_val = self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ self._func_name(), args)
+ new_args = [tenant_id, net_id, port['id'], port['id']]
+ self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ "plug_interface", new_args)
+ return ret_val
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def get_port(self, context, id, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def get_ports(self, context, filters=None, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def update_port(self, context, id, port):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def delete_port(self, context, id, kwargs):
+ """
+ Perform this operation in the context of the configured device
+ plugins.
+ """
+ try:
+ p = kwargs['port']
+ args = [p['tenant_id'], p['network_id'], p['id']]
+ return self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ self._func_name(), args)
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def create_subnet(self, context, subnet):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def update_subnet(self, context, id, subnet):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def get_subnet(self, context, id, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def delete_subnet(self, context, id, kwargs):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ def get_subnets(self, context, filters=None, fields=None, verbose=None):
+ """Currently there is no processing required for the device plugins"""
+ pass
+
+ """
+ Extensions' implementation in device plugins
+ """
+ def schedule_host(self, args):
+ """Provides the hostname on which a dynamic vnic is reserved"""
+ try:
+ return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
+ args)
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def associate_port(self, args):
+ """Get the portprofile name and the device name for the dynamic vnic"""
+ try:
+ return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
+ args)
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def detach_port(self, args):
+ """Remove the association of the VIF with the dynamic vnic """
+ try:
+ return self._invoke_plugin_per_device(const.UCS_PLUGIN,
+ self._func_name(), args)
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
+
+ def create_multiport(self, args):
+ """
+ Makes a call to the UCS device plugin to create ports on the same
+ host.
+ """
+ try:
+ self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
+ args)
+ except:
+ # TODO (Sumit): Check if we need to perform any rollback here
+ raise
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+
+import inspect
+import logging
+
+from quantum.common import exceptions as exc
+from quantum.db import db_base_plugin_v2
+from quantum.db import models_v2
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
+from quantum.plugins.cisco.common import cisco_exceptions as cexc
+from quantum.plugins.cisco.common import cisco_utils as cutil
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco import l2network_plugin_configuration as conf
+from quantum.quantum_plugin_base import QuantumPluginBase
+
+LOG = logging.getLogger(__name__)
+
+
+class PluginV2(db_base_plugin_v2.QuantumDbPluginV2):
+ """
+ Plugin with v2 API support for multiple sub-plugins
+ """
+ supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile",
+ "Cisco qos", "Cisco Nova Tenant",
+ "Cisco Multiport"]
+
+ """
+ Core API implementation
+ """
+ def __init__(self):
+ """
+ Initializes the DB, and credential store.
+ """
+ cdb.initialize()
+ cred.Store.initialize()
+ self._model = importutils.import_object(conf.MODEL_CLASS)
+ super(PluginV2, self).__init__()
+ LOG.debug("Plugin initialization complete")
+
+ def create_network(self, context, network):
+ """
+ Creates a new Virtual Network, and assigns it
+ a symbolic name.
+ """
+ LOG.debug("create_network() called\n")
+ new_network = super(PluginV2, self).create_network(context, network)
+ try:
+ self._invoke_device_plugins(self._func_name(), [context,
+ new_network])
+ return new_network
+ except:
+ super(PluginV2, self).delete_network(context, new_network['id'])
+ raise
+
+ def update_network(self, context, id, network):
+ """
+ Updates the symbolic name belonging to a particular
+ Virtual Network.
+ """
+ LOG.debug("update_network() called\n")
+ try:
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ network])
+ return super(PluginV2, self).update_network(context, id, network)
+ except:
+ raise
+
+ def delete_network(self, context, id):
+ """
+ Deletes the network with the specified network identifier
+ belonging to the specified tenant.
+ """
+ LOG.debug("delete_network() called\n")
+ #We first need to check if there are any ports on this network
+ with context.session.begin():
+ network = self._get_network(context, id)
+
+ filter = {'network_id': [id]}
+ ports = self.get_ports(context, filters=filter)
+ if ports:
+ raise exc.NetworkInUse(net_id=id)
+ context.session.close()
+ #Network does not have any ports, we can proceed to delete
+ try:
+ network = self._get_network(context, id)
+ kwargs = {const.NETWORK: network,
+ const.BASE_PLUGIN_REF: self}
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ kwargs])
+ return super(PluginV2, self).delete_network(context, id)
+ except:
+ raise
+
+ def create_port(self, context, port):
+ """
+ Creates a port on the specified Virtual Network.
+ """
+ LOG.debug("create_port() called\n")
+ new_port = super(PluginV2, self).create_port(context, port)
+ try:
+ self._invoke_device_plugins(self._func_name(), [context, new_port])
+ return new_port
+ except:
+ super(PluginV2, self).delete_port(context, new_port['id'])
+ raise
+
+ def delete_port(self, context, id):
+ """
+ Deletes a port
+ """
+ LOG.debug("delete_port() called\n")
+ port = self._get_port(context, id)
+ """
+ TODO (Sumit): Disabling this check for now, check later
+ #Allow deleting a port only if the administrative state is down,
+ #and its operation status is also down
+ if port['admin_state_up'] or port['status'] == 'ACTIVE':
+ raise exc.PortInUse(port_id=id, net_id=port['network_id'],
+ att_id=port['device_id'])
+ """
+ try:
+ kwargs = {const.PORT: port}
+ # TODO (Sumit): Might first need to check here if port is active
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ kwargs])
+ return super(PluginV2, self).delete_port(context, id)
+ except:
+ raise
+
+ def update_port(self, context, id, port):
+ """
+ Updates the state of a port and returns the updated port
+ """
+ LOG.debug("update_port() called\n")
+ try:
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ port])
+ return super(PluginV2, self).update_port(context, id, port)
+ except:
+ raise
+
+ def create_subnet(self, context, subnet):
+ """
+ Create a subnet, which represents a range of IP addresses
+ that can be allocated to devices.
+ """
+ LOG.debug("create_subnet() called\n")
+ new_subnet = super(PluginV2, self).create_subnet(context, subnet)
+ try:
+ self._invoke_device_plugins(self._func_name(), [context,
+ new_subnet])
+ return new_subnet
+ except:
+ super(PluginV2, self).delete_subnet(context, new_subnet['id'])
+ raise
+
+ def update_subnet(self, context, id, subnet):
+ """
+ Updates the state of a subnet and returns the updated subnet
+ """
+ LOG.debug("update_subnet() called\n")
+ try:
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ subnet])
+ return super(PluginV2, self).update_subnet(context, id, subnet)
+ except:
+ raise
+
+ def delete_subnet(self, context, id):
+ """
+ Deletes a subnet
+ """
+ LOG.debug("delete_subnet() called\n")
+ with context.session.begin():
+ subnet = self._get_subnet(context, id)
+ # Check if ports are using this subnet
+ allocated_qry = context.session.query(models_v2.IPAllocation)
+ allocated = allocated_qry.filter_by(subnet_id=id).all()
+ if allocated:
+ raise exc.SubnetInUse(subnet_id=id)
+ context.session.close()
+ try:
+ kwargs = {const.SUBNET: subnet}
+ self._invoke_device_plugins(self._func_name(), [context, id,
+ kwargs])
+ return super(PluginV2, self).delete_subnet(context, id)
+ except:
+ raise
+
+ """
+ Extension API implementation
+ """
+ def get_all_portprofiles(self, tenant_id):
+ """Get all port profiles"""
+ LOG.debug("get_all_portprofiles() called\n")
+ pplist = cdb.get_all_portprofiles()
+ new_pplist = []
+ for portprofile in pplist:
+ new_pp = cutil.make_portprofile_dict(tenant_id,
+ portprofile[const.UUID],
+ portprofile[const.PPNAME],
+ portprofile[const.PPQOS])
+ new_pplist.append(new_pp)
+
+ return new_pplist
+
+ def get_portprofile_details(self, tenant_id, profile_id):
+ """Get port profile details"""
+ LOG.debug("get_portprofile_details() called\n")
+ try:
+ portprofile = cdb.get_portprofile(tenant_id, profile_id)
+ except Exception:
+ raise cexc.PortProfileNotFound(tenant_id=tenant_id,
+ portprofile_id=profile_id)
+
+ new_pp = cutil.make_portprofile_dict(tenant_id,
+ portprofile[const.UUID],
+ portprofile[const.PPNAME],
+ portprofile[const.PPQOS])
+ return new_pp
+
+ def create_portprofile(self, tenant_id, profile_name, qos):
+ """Create port profile"""
+ LOG.debug("create_portprofile() called\n")
+ portprofile = cdb.add_portprofile(tenant_id, profile_name,
+ const.NO_VLAN_ID, qos)
+ new_pp = cutil.make_portprofile_dict(tenant_id,
+ portprofile[const.UUID],
+ portprofile[const.PPNAME],
+ portprofile[const.PPQOS])
+ return new_pp
+
+ def delete_portprofile(self, tenant_id, profile_id):
+ """Delete portprofile"""
+ LOG.debug("delete_portprofile() called\n")
+ try:
+ portprofile = cdb.get_portprofile(tenant_id, profile_id)
+ except Exception:
+ raise cexc.PortProfileNotFound(tenant_id=tenant_id,
+ portprofile_id=profile_id)
+
+ plist = cdb.get_pp_binding(tenant_id, profile_id)
+ if plist:
+ raise cexc.PortProfileInvalidDelete(tenant_id=tenant_id,
+ profile_id=profile_id)
+ else:
+ cdb.remove_portprofile(tenant_id, profile_id)
+
+ def rename_portprofile(self, tenant_id, profile_id, new_name):
+ """Rename port profile"""
+ LOG.debug("rename_portprofile() called\n")
+ try:
+ portprofile = cdb.get_portprofile(tenant_id, profile_id)
+ except Exception:
+ raise cexc.PortProfileNotFound(tenant_id=tenant_id,
+ portprofile_id=profile_id)
+ portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
+ new_pp = cutil.make_portprofile_dict(tenant_id,
+ portprofile[const.UUID],
+ portprofile[const.PPNAME],
+ portprofile[const.PPQOS])
+ return new_pp
+
+ def associate_portprofile(self, tenant_id, net_id,
+ port_id, portprofile_id):
+ """Associate port profile"""
+ LOG.debug("associate_portprofile() called\n")
+ try:
+ portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
+ except Exception:
+ raise cexc.PortProfileNotFound(tenant_id=tenant_id,
+ portprofile_id=portprofile_id)
+
+ cdb.add_pp_binding(tenant_id, port_id, portprofile_id, False)
+
+ def disassociate_portprofile(self, tenant_id, net_id,
+ port_id, portprofile_id):
+ """Disassociate port profile"""
+ LOG.debug("disassociate_portprofile() called\n")
+ try:
+ portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
+ except Exception:
+ raise cexc.PortProfileNotFound(tenant_id=tenant_id,
+ portprofile_id=portprofile_id)
+
+ cdb.remove_pp_binding(tenant_id, port_id, portprofile_id)
+
+ def get_all_qoss(self, tenant_id):
+ """Get all QoS levels"""
+ LOG.debug("get_all_qoss() called\n")
+ qoslist = cdb.get_all_qoss(tenant_id)
+ return qoslist
+
+ def get_qos_details(self, tenant_id, qos_id):
+ """Get QoS Details"""
+ LOG.debug("get_qos_details() called\n")
+ try:
+ qos_level = cdb.get_qos(tenant_id, qos_id)
+ except Exception:
+ raise cexc.QosNotFound(tenant_id=tenant_id,
+ qos_id=qos_id)
+ return qos_level
+
+ def create_qos(self, tenant_id, qos_name, qos_desc):
+ """Create a QoS level"""
+ LOG.debug("create_qos() called\n")
+ qos = cdb.add_qos(tenant_id, qos_name, str(qos_desc))
+ return qos
+
+ def delete_qos(self, tenant_id, qos_id):
+ """Delete a QoS level"""
+ LOG.debug("delete_qos() called\n")
+ try:
+ qos_level = cdb.get_qos(tenant_id, qos_id)
+ except Exception:
+ raise cexc.QosNotFound(tenant_id=tenant_id,
+ qos_id=qos_id)
+ return cdb.remove_qos(tenant_id, qos_id)
+
+ def rename_qos(self, tenant_id, qos_id, new_name):
+ """Rename QoS level"""
+ LOG.debug("rename_qos() called\n")
+ try:
+ qos_level = cdb.get_qos(tenant_id, qos_id)
+ except Exception:
+ raise cexc.QosNotFound(tenant_id=tenant_id,
+ qos_id=qos_id)
+ qos = cdb.update_qos(tenant_id, qos_id, new_name)
+ return qos
+
+ def get_all_credentials(self, tenant_id):
+ """Get all credentials"""
+ LOG.debug("get_all_credentials() called\n")
+ credential_list = cdb.get_all_credentials(tenant_id)
+ return credential_list
+
+ def get_credential_details(self, tenant_id, credential_id):
+ """Get a particular credential"""
+ LOG.debug("get_credential_details() called\n")
+ try:
+ credential = cdb.get_credential(tenant_id, credential_id)
+ except Exception:
+ raise cexc.CredentialNotFound(tenant_id=tenant_id,
+ credential_id=credential_id)
+ return credential
+
+ def create_credential(self, tenant_id, credential_name, user_name,
+ password):
+ """Create a new credential"""
+ LOG.debug("create_credential() called\n")
+ credential = cdb.add_credential(tenant_id, credential_name,
+ user_name, password)
+ return credential
+
+ def delete_credential(self, tenant_id, credential_id):
+ """Delete a credential"""
+ LOG.debug("delete_credential() called\n")
+ try:
+ credential = cdb.get_credential(tenant_id, credential_id)
+ except Exception:
+ raise cexc.CredentialNotFound(tenant_id=tenant_id,
+ credential_id=credential_id)
+ credential = cdb.remove_credential(tenant_id, credential_id)
+ return credential
+
+ def rename_credential(self, tenant_id, credential_id, new_name):
+ """Rename the particular credential resource"""
+ LOG.debug("rename_credential() called\n")
+ try:
+ credential = cdb.get_credential(tenant_id, credential_id)
+ except Exception:
+ raise cexc.CredentialNotFound(tenant_id=tenant_id,
+ credential_id=credential_id)
+ credential = cdb.update_credential(tenant_id, credential_id, new_name)
+ return credential
+
+ def schedule_host(self, tenant_id, instance_id, instance_desc):
+ """Provides the hostname on which a dynamic vnic is reserved"""
+ LOG.debug("schedule_host() called\n")
+ host_list = self._invoke_device_plugins(self._func_name(),
+ [tenant_id,
+ instance_id,
+ instance_desc])
+ return host_list
+
+ def associate_port(self, tenant_id, instance_id, instance_desc):
+ """
+ Get the portprofile name and the device name for the dynamic vnic
+ """
+ LOG.debug("associate_port() called\n")
+ return self._invoke_device_plugins(self._func_name(), [tenant_id,
+ instance_id,
+ instance_desc])
+
+ def detach_port(self, tenant_id, instance_id, instance_desc):
+ """
+ Remove the association of the VIF with the dynamic vnic
+ """
+ LOG.debug("detach_port() called\n")
+ return self._invoke_device_plugins(self._func_name(), [tenant_id,
+ instance_id,
+ instance_desc])
+
+ def create_multiport(self, tenant_id, net_id_list, port_state, ports_desc):
+ """
+ Creates multiple ports on the specified Virtual Network.
+ """
+ LOG.debug("create_ports() called\n")
+ ports_num = len(net_id_list)
+ ports_id_list = []
+ ports_dict_list = []
+
+ for net_id in net_id_list:
+ db.validate_network_ownership(tenant_id, net_id)
+ port = db.port_create(net_id, port_state)
+ ports_id_list.append(port[const.UUID])
+ port_dict = {const.PORT_ID: port[const.UUID]}
+ ports_dict_list.append(port_dict)
+
+ self._invoke_device_plugins(self._func_name(), [tenant_id,
+ net_id_list,
+ ports_num,
+ ports_id_list])
+ return ports_dict_list
+
+ """
+ Private functions
+ """
+ def _invoke_device_plugins(self, function_name, args):
+ """
+ All device-specific calls are delegated to the model
+ """
+ return getattr(self._model, function_name)(*args)
+
+ def _func_name(self, offset=0):
+ """Getting the name of the calling funciton"""
+ return inspect.stack()[1 + offset][3]
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Edgar Magana, Cisco Systems, Inc.
+#
+"""
+PlugIn for Nexus OS driver
+"""
+import logging
+
+from quantum.common import exceptions as exc
+from quantum.db import api as db
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials as cred
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco.db import nexus_db_v2 as nxos_db
+from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
+from quantum.plugins.cisco.nexus import cisco_nexus_configuration as conf
+
+
+LOG = logging.getLogger(__name__)
+
+
+class NexusPlugin(L2DevicePluginBase):
+ """
+ Nexus PLugIn Main Class
+ """
+ _networks = {}
+
+ def __init__(self):
+ """
+ Extracts the configuration parameters from the configuration file
+ """
+ self._client = importutils.import_object(conf.NEXUS_DRIVER)
+ LOG.debug("Loaded driver %s\n" % conf.NEXUS_DRIVER)
+ self._nexus_ip = conf.NEXUS_IP_ADDRESS
+ self._nexus_username = cred.Store.get_username(conf.NEXUS_IP_ADDRESS)
+ self._nexus_password = cred.Store.get_password(conf.NEXUS_IP_ADDRESS)
+ self._nexus_first_port = conf.NEXUS_FIRST_PORT
+ self._nexus_second_port = conf.NEXUS_SECOND_PORT
+ self._nexus_ssh_port = conf.NEXUS_SSH_PORT
+
+ def get_all_networks(self, tenant_id):
+ """
+ Returns a dictionary containing all
+ <network_uuid, network_name> for
+ the specified tenant.
+ """
+ LOG.debug("NexusPlugin:get_all_networks() called\n")
+ return self._networks.values()
+
+ def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
+ **kwargs):
+ """
+ Create a VLAN in the switch, and configure the appropriate interfaces
+ for this VLAN
+ """
+ LOG.debug("NexusPlugin:create_network() called\n")
+ self._client.create_vlan(
+ vlan_name, str(vlan_id), self._nexus_ip,
+ self._nexus_username, self._nexus_password,
+ self._nexus_first_port, self._nexus_second_port,
+ self._nexus_ssh_port)
+ nxos_db.add_nexusport_binding(self._nexus_first_port, str(vlan_id))
+ nxos_db.add_nexusport_binding(self._nexus_second_port, str(vlan_id))
+
+ new_net_dict = {const.NET_ID: net_id,
+ const.NET_NAME: net_name,
+ const.NET_PORTS: {},
+ const.NET_VLAN_NAME: vlan_name,
+ const.NET_VLAN_ID: vlan_id}
+ self._networks[net_id] = new_net_dict
+ return new_net_dict
+
+ def delete_network(self, tenant_id, net_id, **kwargs):
+ """
+ Deletes a VLAN in the switch, and removes the VLAN configuration
+ from the relevant interfaces
+ """
+ LOG.debug("NexusPlugin:delete_network() called\n")
+ context = kwargs[const.CONTEXT]
+ base_plugin_ref = kwargs[const.BASE_PLUGIN_REF]
+ vlan_id = self._get_vlan_id_for_network(tenant_id, net_id,
+ context, base_plugin_ref)
+ ports_id = nxos_db.get_nexusport_binding(vlan_id)
+ LOG.debug("NexusPlugin: Interfaces to be disassociated: %s" % ports_id)
+ nxos_db.remove_nexusport_binding(vlan_id)
+ net = self._get_network(tenant_id, net_id, context, base_plugin_ref)
+ if net:
+ self._client.delete_vlan(
+ str(vlan_id), self._nexus_ip,
+ self._nexus_username, self._nexus_password,
+ self._nexus_first_port, self._nexus_second_port,
+ self._nexus_ssh_port)
+ return net
+ # Network not found
+ raise exc.NetworkNotFound(net_id=net_id)
+
+ def get_network_details(self, tenant_id, net_id, **kwargs):
+ """
+ Returns the details of a particular network
+ """
+ LOG.debug("NexusPlugin:get_network_details() called\n")
+ network = self._get_network(tenant_id, net_id)
+ return network
+
+ def update_network(self, tenant_id, net_id, **kwargs):
+ """
+ Updates the properties of a particular
+ Virtual Network.
+ """
+ LOG.debug("NexusPlugin:update_network() called\n")
+ network = self._get_network(tenant_id, net_id)
+ network[const.NET_NAME] = kwargs["name"]
+ return network
+
+ def get_all_ports(self, tenant_id, net_id, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:get_all_ports() called\n")
+
+ def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:create_port() called\n")
+
+ def delete_port(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:delete_port() called\n")
+
+ def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:update_port() called\n")
+
+ def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:get_port_details() called\n")
+
+ def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
+ **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:plug_interface() called\n")
+
+ def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ This is probably not applicable to the Nexus plugin.
+ Delete if not required.
+ """
+ LOG.debug("NexusPlugin:unplug_interface() called\n")
+
+ def _get_vlan_id_for_network(self, tenant_id, network_id, context,
+ base_plugin_ref):
+ """
+ Obtain the VLAN ID given the Network ID
+ """
+ net = self._get_network(tenant_id, network_id, context,
+ base_plugin_ref)
+ vlan_id = net[const.NET_VLAN_ID]
+ return vlan_id
+
+ def _get_network(self, tenant_id, network_id, context, base_plugin_ref):
+ """
+ Gets the NETWORK ID
+ """
+ network = base_plugin_ref._get_network(context, network_id)
+ if not network:
+ raise exc.NetworkNotFound(net_id=network_id)
+ vlan = cdb.get_vlan_binding(network_id)
+ return {const.NET_ID: network_id, const.NET_NAME: network.name,
+ const.NET_PORTS: network.ports,
+ const.NET_VLAN_NAME: vlan.vlan_name,
+ const.NET_VLAN_ID: vlan.vlan_id}
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+
+import logging
+
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco import l2network_plugin_configuration as conf
+from quantum.plugins.cisco.l2network_segmentation_base import (
+ L2NetworkSegmentationMgrBase,
+)
+
+
+LOG = logging.getLogger(__name__)
+
+
+class L2NetworkVLANMgr(L2NetworkSegmentationMgrBase):
+ """
+ VLAN Manager which gets VLAN ID from DB
+ """
+ def __init__(self):
+ cdb.create_vlanids()
+
+ def reserve_segmentation_id(self, tenant_id, net_name, **kwargs):
+ """Get an available VLAN ID"""
+ return cdb.reserve_vlanid()
+
+ def release_segmentation_id(self, tenant_id, net_id, **kwargs):
+ """Release the ID"""
+ vlan_binding = cdb.get_vlan_binding(net_id)
+ return cdb.release_vlanid(vlan_binding[const.VLANID])
+
+ def get_vlan_name(self, net_id, vlan):
+ """Getting the vlan name from the tenant and vlan"""
+ vlan_name = conf.VLAN_NAME_PREFIX + vlan
+ return vlan_name
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import __builtin__
+import unittest
+
+
+setattr(__builtin__, '_', lambda x: x)
+
+
+class BaseTest(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+
+def setUp():
+ pass
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import __builtin__
+import unittest
+
+
+setattr(__builtin__, '_', lambda x: x)
+
+
+class BaseTest(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+
+def setUp():
+ pass
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+
+class CiscoNEXUSFakeDriver():
+ """
+ Nexus Driver Fake Class
+ """
+ def __init__(self):
+ pass
+
+ def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user,
+ nexus_password):
+ """
+ Makes the fake connection to the Nexus Switch
+ """
+ pass
+
+ def create_xml_snippet(self, cutomized_config):
+ """
+ Creates the Proper XML structure for the Nexus Switch Configuration
+ """
+ pass
+
+ def enable_vlan(self, mgr, vlanid, vlanname):
+ """
+ Creates a VLAN on Nexus Switch given the VLAN ID and Name
+ """
+ pass
+
+ def disable_vlan(self, mgr, vlanid):
+ """
+ Delete a VLAN on Nexus Switch given the VLAN ID
+ """
+ pass
+
+ def enable_port_trunk(self, mgr, interface):
+ """
+ Enables trunk mode an interface on Nexus Switch
+ """
+ pass
+
+ def disable_switch_port(self, mgr, interface):
+ """
+ Disables trunk mode an interface on Nexus Switch
+ """
+ pass
+
+ def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+ """
+ Enables trunk mode vlan access an interface on Nexus Switch given
+ VLANID
+ """
+ pass
+
+ def disable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+ """
+ Enables trunk mode vlan access an interface on Nexus Switch given
+ VLANID
+ """
+ pass
+
+ def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
+ nexus_password, nexus_first_interface,
+ nexus_second_interface, nexus_ssh_port):
+ """
+ Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
+ given the VLAN ID and Name and Interface Number
+ """
+ pass
+
+ def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
+ nexus_first_interface, nexus_second_interface,
+ nexus_ssh_port):
+ """
+ Delete a VLAN and Disables trunk mode an interface on Nexus Switch
+ given the VLAN ID and Interface Number
+ """
+ pass
+
+ def build_vlans_cmd(self):
+ """
+ Builds a string with all the VLANs on the same Switch
+ """
+ pass
--- /dev/null
+[DEFAULT]
+# Show more verbose log output (sets INFO log level output)
+verbose = True
+
+# Show debugging output in logs (sets DEBUG log level output)
+debug = False
+
+# Address to bind the API server
+bind_host = 0.0.0.0
+
+# Port the bind the API server to
+bind_port = 9696
+
+# Path to the extensions
+api_extensions_path = ../../../../extensions
+
+# Paste configuration file
+api_paste_config = api-paste.ini.cisco.test
+
+core_plugin = quantum.plugins.cisco.network_plugin.PluginV2
--- /dev/null
+# Copyright 2012 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the spec
+
+import inspect
+import logging
+import mock
+import os
+import webtest
+
+from quantum.api.v2 import router
+from quantum.common import config
+from quantum.openstack.common import cfg
+from quantum.tests.unit import test_api_v2
+
+
+LOG = logging.getLogger(__name__)
+
+
+def curdir(*p):
+ return os.path.join(os.path.dirname(__file__), *p)
+
+
+class APIv2TestCase(test_api_v2.APIv2TestCase):
+
+ def setUp(self):
+ plugin = 'quantum.plugins.cisco.network_plugin.PluginV2'
+ # Create the default configurations
+ args = ['--config-file', curdir('quantumv2.conf.cisco.test')]
+ config.parse(args=args)
+ # Update the plugin
+ cfg.CONF.set_override('core_plugin', plugin)
+
+ self._plugin_patcher = mock.patch(plugin, autospec=True)
+ self.plugin = self._plugin_patcher.start()
+
+ api = router.APIRouter()
+ self.api = webtest.TestApp(api)
+ LOG.debug("%s.%s.%s done" % (__name__, self.__class__.__name__,
+ inspect.stack()[0][3]))
+
+
+class JSONV2TestCase(APIv2TestCase, test_api_v2.JSONV2TestCase):
+
+ pass
--- /dev/null
+# Copyright (c) 2012 OpenStack, LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import inspect
+import logging
+import mock
+import os
+
+from quantum.api.v2.router import APIRouter
+from quantum.common import config
+from quantum.db import api as db
+from quantum.plugins.cisco.db import network_models_v2
+from quantum.openstack.common import cfg
+from quantum.tests.unit import test_db_plugin
+from quantum.wsgi import JSONDeserializer
+
+LOG = logging.getLogger(__name__)
+
+
+def curdir(*p):
+ return os.path.join(os.path.dirname(__file__), *p)
+
+
+class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase):
+
+ def setUp(self):
+ db._ENGINE = None
+ db._MAKER = None
+
+ self._tenant_id = 'test-tenant'
+
+ json_deserializer = JSONDeserializer()
+ self._deserializers = {
+ 'application/json': json_deserializer,
+ }
+
+ plugin = 'quantum.plugins.cisco.network_plugin.PluginV2'
+ # Create the default configurations
+ args = ['--config-file', curdir('quantumv2.conf.cisco.test')]
+ config.parse(args=args)
+ # Update the plugin
+ cfg.CONF.set_override('core_plugin', plugin)
+ cfg.CONF.set_override('base_mac', "12:34:56:78:90:ab")
+ self.api = APIRouter()
+ LOG.debug("%s.%s.%s done" % (__name__, self.__class__.__name__,
+ inspect.stack()[0][3]))
+
+ def tearDown(self):
+ db.clear_db(network_models_v2.model_base.BASEV2)
+ db._ENGINE = None
+ db._MAKER = None
+
+ cfg.CONF.reset()
+
+
+class TestV2HTTPResponse(NetworkPluginV2TestCase,
+ test_db_plugin.TestV2HTTPResponse):
+
+ pass
+
+
+class TestPortsV2(NetworkPluginV2TestCase, test_db_plugin.TestPortsV2):
+
+ pass
+
+
+class TestNetworksV2(NetworkPluginV2TestCase, test_db_plugin.TestNetworksV2):
+
+ pass
+
+
+class TestSubnetsV2(NetworkPluginV2TestCase, test_db_plugin.TestSubnetsV2):
+
+ pass
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import __builtin__
+import unittest
+
+
+setattr(__builtin__, '_', lambda x: x)
+
+
+class BaseTest(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+
+def setUp():
+ pass
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+
+
+import logging
+import os
+
+from quantum.common.utils import find_config_file
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_configparser as confp
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
+from quantum.plugins.cisco.ucs import (
+ cisco_ucs_inventory_configuration as conf,
+)
+from quantum.plugins.cisco.ucs import cisco_ucs_inventory_v2
+
+
+LOG = logging.getLogger(__name__)
+
+
+def curdir(*p):
+ return os.path.join(os.path.dirname(__file__), *p)
+
+
+class UCSInventory(cisco_ucs_inventory_v2.UCSInventory):
+ """
+ Inventory implementation for testing
+ """
+
+ def __init__(self):
+ fake_ucs_driver = "quantum.plugins.cisco.tests.unit.v2.ucs." + \
+ "fake_ucs_driver.CiscoUCSMFakeDriver"
+ self._client = importutils.import_object(fake_ucs_driver)
+ conf_parser = confp.CiscoConfigParser(curdir("fake_ucs_inventory.ini"))
+
+ conf.INVENTORY = conf_parser.walk(conf_parser.dummy)
+ for ucsm in conf.INVENTORY.keys():
+ ucsm_ip = conf.INVENTORY[ucsm][const.IP_ADDRESS]
+ try:
+ cred.Store.put_credential(ucsm_ip, "username", "password")
+ except:
+ pass
+ self._load_inventory()
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+from quantum.plugins.cisco.common import cisco_constants as const
+
+
+class CiscoUCSMFakeDriver():
+ """UCSM Fake Driver"""
+
+ def __init__(self):
+ pass
+
+ def _get_blade_interfaces(self, chassis_number, blade_number, ucsm_ip,
+ ucsm_username, ucsm_password):
+ blade_interfaces = {}
+ for element in range(20):
+ dist_name = "dn" + str(element)
+ if dist_name:
+ order = str(element)
+ rhel_name = "eth" + str(element)
+ blade_interface = {
+ const.BLADE_INTF_DN: dist_name,
+ const.BLADE_INTF_ORDER: order,
+ const.BLADE_INTF_LINK_STATE: None,
+ const.BLADE_INTF_OPER_STATE: None,
+ const.BLADE_INTF_INST_TYPE: const.BLADE_INTF_DYNAMIC,
+ const.BLADE_INTF_RHEL_DEVICE_NAME: rhel_name,
+ }
+ blade_interfaces[dist_name] = blade_interface
+
+ return blade_interfaces
+
+ def _get_blade_interface_state(self, blade_intf, ucsm_ip,
+ ucsm_username, ucsm_password):
+ blade_intf[const.BLADE_INTF_LINK_STATE] = \
+ const.BLADE_INTF_STATE_UNKNOWN
+ blade_intf[const.BLADE_INTF_OPER_STATE] = \
+ const.BLADE_INTF_STATE_UNKNOWN
+ blade_intf[const.BLADE_INTF_INST_TYPE] = \
+ const.BLADE_INTF_DYNAMIC
+
+ def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
+ ucsm_password):
+ pass
+
+ def create_profile(self, profile_name, vlan_name, ucsm_ip, ucsm_username,
+ ucsm_password):
+ pass
+
+ def change_vlan_in_profile(self, profile_name, old_vlan_name,
+ new_vlan_name, ucsm_ip, ucsm_username,
+ ucsm_password):
+ pass
+
+ def get_blade_data(self, chassis_number, blade_number, ucsm_ip,
+ ucsm_username, ucsm_password):
+ """
+ Returns only the dynamic interfaces on the blade
+ """
+ blade_interfaces = self._get_blade_interfaces(chassis_number,
+ blade_number,
+ ucsm_ip,
+ ucsm_username,
+ ucsm_password)
+ for blade_intf in blade_interfaces.keys():
+ self._get_blade_interface_state(blade_interfaces[blade_intf],
+ ucsm_ip, ucsm_username,
+ ucsm_password)
+ if ((blade_interfaces[blade_intf][const.BLADE_INTF_INST_TYPE] !=
+ const.BLADE_INTF_DYNAMIC)):
+ blade_interfaces.pop(blade_intf)
+
+ return blade_interfaces
+
+ def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password):
+ pass
+
+ def delete_profile(self, profile_name, ucsm_ip, ucsm_username,
+ ucsm_password):
+ pass
--- /dev/null
+[ucsm-1]
+ip_address = 192.168.100.2
+[[chassis-1]]
+chassis_id = 1
+[[[blade-1]]]
+blade_id = 1
+host_name = blade1
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Shubhangi Satras, Cisco Systems, Inc.
+# @author: Tyler Smith, Cisco Systems, Inc.
+
+import logging
+import unittest
+import uuid
+
+from quantum.common import exceptions as exc
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials_v2 as creds
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco.tests.unit.v2.ucs.cisco_ucs_inventory_fake import (
+ UCSInventory,
+)
+
+
+LOG = logging.getLogger(__name__)
+
+# Set some data to use in tests
+tenant = 'shubh'
+net_name = 'TestNetwork1'
+port_state = const.PORT_UP
+interface_id = 'vif-01'
+
+
+class TestUCSInventory(unittest.TestCase):
+ """
+ Tests for the UCS Inventory. Each high-level operation should return
+ some information about which devices to perform the action on.
+ """
+
+ def setUp(self):
+ """Setup our tests"""
+ cdb.initialize()
+ creds.Store.initialize()
+
+ # Create the ucs inventory object
+ self._ucs_inventory = UCSInventory()
+ self.inventory = self._ucs_inventory._inventory
+
+ def assertValidUCM(self, ip_address):
+ """Asserts that the given ip is in the UCS inventory"""
+ if ip_address in self.inventory.keys():
+ assert(1)
+ return
+ assert(0)
+
+ def _test_get_all_ucms(self, cmd):
+ """Runs tests for commands that expect a list of all UCMS"""
+ LOG.debug("test_%s - START", cmd)
+ results = getattr(self._ucs_inventory, cmd)([])
+ self.assertEqual(results[const.DEVICE_IP], self.inventory.keys())
+ LOG.debug("test_%s - END", cmd)
+
+ def _test_with_port_creation(self, cmd, params=None):
+ """Tests commands that requires a port to exist"""
+ LOG.debug("test_%s - START", cmd)
+ net_uuid = str(uuid.uuid4())
+ device_params = self._ucs_inventory.create_port(tenant, net_uuid,
+ port_state,
+ state=port_state)
+
+ args = [tenant, net_uuid, port[const.PORT_ID]]
+ if params is not None:
+ args.extend(params)
+
+ ip_address = getattr(self._ucs_inventory, cmd)(args)
+ ip_address = ip_address[const.DEVICE_IP][0]
+ self.assertValidUCM(ip_address)
+ cdb.clear_db()
+
+ LOG.debug("test_%s - END", cmd)
+
+ def test_create_port(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ LOG.debug("test_create_port - START")
+ results = self._ucs_inventory.create_port([])
+ results = results[const.LEAST_RSVD_BLADE_DICT]
+
+ ip_address = results[const.LEAST_RSVD_BLADE_UCSM]
+ chassis = results[const.LEAST_RSVD_BLADE_CHASSIS]
+ blade = results[const.LEAST_RSVD_BLADE_ID]
+
+ if blade not in self.inventory[ip_address][chassis]:
+ self.assertEqual(0, 1)
+ self.assertEqual(1, 1)
+ LOG.debug("test_create_port - END")
+
+ def test_get_all_networks(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('get_all_networks')
+
+ def test_create_network(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('create_network')
+
+ def test_delete_network(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('delete_network')
+
+ def test_get_network_details(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('get_network_details')
+
+ def test_update_network(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('update_network')
+
+ def test_get_all_ports(self):
+ """Test that the UCS Inventory returns the correct devices to use"""
+ self._test_get_all_ucms('get_all_ports')
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+
+"""
+The _inventory data strcuture contains a nested disctioary:
+ {"UCSM_IP: {"Chassis-ID": [Balde-ID, Blade-ID],
+ "Chassis-ID": [Blade-ID, Blade-ID, Blade-ID]]},
+ "UCSM_IP: {"Chassis-ID": [Balde-ID]}
+ }
+"""
+"""
+_inventory_state data structure is organized as below:
+{ucsm_ip:
+ {chassis_id:
+ {blade_id:
+ {'blade-data':
+ {blade-dn-1: {blade-intf-data},
+ blade-dn-2: {blade-intf-data}
+ }
+ }
+ }
+ }
+}
+'blade-data': Blade Data dictionary has the following keys:
+===========================================================
+const.BLADE_INTF_DATA: This is a dictionary, with the key as the
+ dn of the interface, and the value as the
+ Blade Interface Dictionary described next
+const.BLADE_UNRESERVED_INTF_COUNT: Number of unreserved interfaces
+ on this blade
+
+'blade-intf-data': Blade Interface dictionary has the following keys:
+=====================================================================
+const.BLADE_INTF_DN
+const.BLADE_INTF_ORDER
+const.BLADE_INTF_LINK_STATE
+const.BLADE_INTF_OPER_STATE
+const.BLADE_INTF_INST_TYPE
+const.BLADE_INTF_RHEL_DEVICE_NAME
+const.BLADE_INTF_RESERVATION
+const.TENANTID
+const.PORTID
+const.PROFILE_ID
+const.INSTANCE_ID
+const.VIF_ID
+"""
+
+from copy import deepcopy
+import logging
+
+from quantum.common import exceptions as exc
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials_v2 as cred
+from quantum.plugins.cisco.common import cisco_exceptions as cexc
+from quantum.plugins.cisco.db import ucs_db_v2 as udb
+from quantum.plugins.cisco.l2device_inventory_base import (
+ L2NetworkDeviceInventoryBase,
+)
+from quantum.plugins.cisco.ucs import (
+ cisco_ucs_inventory_configuration as conf,
+)
+
+
+LOG = logging.getLogger(__name__)
+
+
+class UCSInventory(L2NetworkDeviceInventoryBase):
+ """
+ Manages the state of all the UCS chasses, and blades in
+ the system
+ """
+
+ _inventory = {}
+ _host_names = {}
+ _inventory_state = {}
+
+ def __init__(self):
+ self._client = importutils.import_object(conf.UCSM_DRIVER)
+ self._load_inventory()
+
+ def _load_inventory(self):
+ """Load the inventory from a config file"""
+ inventory = deepcopy(conf.INVENTORY)
+ LOG.info("Loaded UCS inventory: %s\n" % inventory)
+ LOG.info("Building UCS inventory state (this may take a while)...")
+
+ for ucsm in inventory.keys():
+ ucsm_ip = inventory[ucsm][const.IP_ADDRESS]
+ inventory[ucsm].pop(const.IP_ADDRESS)
+ chassis_dict = {}
+ for chassis in inventory[ucsm].keys():
+ chassis_id = inventory[ucsm][chassis][const.CHASSIS_ID]
+ inventory[ucsm][chassis].pop(const.CHASSIS_ID)
+ blade_list = []
+ for blade in inventory[ucsm][chassis].keys():
+ blade_id = (
+ inventory[ucsm][chassis][blade][const.BLADE_ID])
+ host_name = (
+ inventory[ucsm][chassis][blade][const.HOST_NAME])
+ host_key = ucsm_ip + "-" + chassis_id + "-" + blade_id
+ self._host_names[host_key] = host_name
+ blade_list.append(blade_id)
+ chassis_dict[chassis_id] = blade_list
+ self._inventory[ucsm_ip] = chassis_dict
+
+ self._build_inventory_state()
+
+ def _build_inventory_state(self):
+ """Populate the state of all the blades"""
+ for ucsm_ip in self._inventory.keys():
+ self._inventory_state[ucsm_ip] = {ucsm_ip: {}}
+ ucsm_username = cred.Store.get_username(ucsm_ip)
+ ucsm_password = cred.Store.get_password(ucsm_ip)
+ chasses_state = {}
+ self._inventory_state[ucsm_ip] = chasses_state
+ ucsm = self._inventory[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ blades_dict = {}
+ chasses_state[chassis_id] = blades_dict
+ for blade_id in ucsm[chassis_id]:
+ blade_data = self._get_initial_blade_state(chassis_id,
+ blade_id,
+ ucsm_ip,
+ ucsm_username,
+ ucsm_password)
+ blades_dict[blade_id] = blade_data
+
+ LOG.debug("UCS Inventory state is: %s\n" % self._inventory_state)
+ return True
+
+ def _get_host_name(self, ucsm_ip, chassis_id, blade_id):
+ """Get the hostname based on the blade info"""
+ host_key = ucsm_ip + "-" + chassis_id + "-" + blade_id
+ return self._host_names[host_key]
+
+ def _get_initial_blade_state(self, chassis_id, blade_id, ucsm_ip,
+ ucsm_username, ucsm_password):
+ """Get the initial blade state"""
+ blade_intf_data = self._client.get_blade_data(chassis_id, blade_id,
+ ucsm_ip, ucsm_username,
+ ucsm_password)
+
+ unreserved_counter = 0
+
+ for blade_intf in blade_intf_data.keys():
+ dist_name = blade_intf_data[blade_intf][const.BLADE_INTF_DN]
+ # We first make a pass through the state in UCSM
+ # If a particular interface is showing as being allocated in
+ # UCSM then it is definitely being used and so should be
+ # marked as reserved, else we temporarily mark it as unreserved
+ # based on the UCSM state, but may later change it if a port
+ # association is found in the DB
+ if not const.TENANTID in blade_intf_data[blade_intf].keys():
+ blade_intf_data[blade_intf][const.TENANTID] = None
+ if not const.PORTID in blade_intf_data[blade_intf].keys():
+ blade_intf_data[blade_intf][const.PORTID] = None
+ if not const.PROFILE_ID in blade_intf_data[blade_intf].keys():
+ blade_intf_data[blade_intf][const.PROFILE_ID] = None
+ if not const.INSTANCE_ID in blade_intf_data[blade_intf].keys():
+ blade_intf_data[blade_intf][const.INSTANCE_ID] = None
+ if not const.VIF_ID in blade_intf_data[blade_intf].keys():
+ blade_intf_data[blade_intf][const.VIF_ID] = None
+
+ if (blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] ==
+ const.BLADE_INTF_STATE_UNALLOCATED or
+ blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] ==
+ const.BLADE_INTF_STATE_UNKNOWN) and (
+ blade_intf_data[blade_intf][const.BLADE_INTF_OPER_STATE] ==
+ const.BLADE_INTF_STATE_UNKNOWN):
+ blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_UNRESERVED)
+ unreserved_counter += 1
+ else:
+ blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_RESERVED)
+
+ port_binding = udb.get_portbinding_dn(dist_name)
+ if port_binding:
+ # We have found a port binding for this interface in the DB,
+ # so we have earlier marked this interface as unreserved, we
+ # need to change it, and also load the state from the DB for
+ # other associations
+ intf_data = blade_intf_data[blade_intf]
+ if ((intf_data[const.BLADE_INTF_RESERVATION] == const.
+ BLADE_INTF_UNRESERVED)):
+ unreserved_counter -= 1
+ intf_data[const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_RESERVED)
+ intf_data[const.TENANTID] = port_binding[const.TENANTID]
+ intf_data[const.PORTID] = port_binding[const.PORTID]
+ intf_data[const.PROFILE_ID] = (
+ port_binding[const.PORTPROFILENAME])
+ intf_data[const.INSTANCE_ID] = port_binding[const.INSTANCE_ID]
+ intf_data[const.VIF_ID] = port_binding[const.VIF_ID]
+ host_name = self._get_host_name(ucsm_ip, chassis_id, blade_id)
+ blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
+ const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter,
+ const.HOST_NAME: host_name}
+ return blade_data
+
+ def _get_blade_state(self, chassis_id, blade_id, ucsm_ip,
+ ucsm_username, ucsm_password):
+ """Get the blade state"""
+ blade_intf_data = self._client.get_blade_data(chassis_id, blade_id,
+ ucsm_ip, ucsm_username,
+ ucsm_password)
+ unreserved_counter = 0
+
+ for blade_intf in blade_intf_data.keys():
+ if (blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] ==
+ const.BLADE_INTF_STATE_UNALLOCATED or
+ blade_intf_data[blade_intf][const.BLADE_INTF_LINK_STATE] ==
+ const.BLADE_INTF_STATE_UNKNOWN) and (
+ blade_intf_data[blade_intf][const.BLADE_INTF_OPER_STATE] ==
+ const.BLADE_INTF_STATE_UNKNOWN):
+ blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_UNRESERVED)
+ unreserved_counter += 1
+ else:
+ blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_RESERVED)
+
+ blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
+ const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter}
+ return blade_data
+
+ def _get_all_ucsms(self):
+ """Return a list of the IPs of all the UCSMs in the system"""
+ return {const.DEVICE_IP: self._inventory.keys()}
+
+ def _get_blade_for_port(self, args):
+ """
+ Return the a dict with IP address of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ tenant_id = args[0]
+ net_id = args[1]
+ port_id = args[2]
+ rsvd_info = self._get_rsvd_blade_intf_by_port(tenant_id, port_id)
+ if not rsvd_info:
+ raise exc.PortNotFound(net_id=net_id, port_id=port_id)
+ device_params = {const.DEVICE_IP: [rsvd_info[const.UCSM_IP]]}
+ return device_params
+
+ def _get_host_name_for_rsvd_intf(self, tenant_id, instance_id):
+ """
+ Return the hostname of the blade with a reserved instance
+ for this tenant
+ """
+ for ucsm_ip in self._inventory_state.keys():
+ ucsm = self._inventory_state[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ for blade_id in ucsm[chassis_id]:
+ blade_data = ucsm[chassis_id][blade_id]
+ blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+ for blade_intf in blade_intf_data.keys():
+ tmp = deepcopy(blade_intf_data[blade_intf])
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_RESERVED and
+ intf_data[const.TENANTID] == tenant_id and
+ intf_data[const.INSTANCE_ID] is None):
+ intf_data[const.INSTANCE_ID] = instance_id
+ host_name = self._get_host_name(ucsm_ip,
+ chassis_id,
+ blade_id)
+ port_binding = udb.get_portbinding_dn(blade_intf)
+ port_id = port_binding[const.PORTID]
+ udb.update_portbinding(port_id,
+ instance_id=instance_id)
+ return host_name
+ LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
+ tenant_id)
+ return None
+
+ def _get_instance_port(self, tenant_id, instance_id, vif_id):
+ """
+ Return the device name for a reserved interface
+ """
+ found_blade_intf_data = None
+ for ucsm_ip in self._inventory_state.keys():
+ ucsm = self._inventory_state[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ for blade_id in ucsm[chassis_id]:
+ blade_data = ucsm[chassis_id][blade_id]
+ blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+ for blade_intf in blade_intf_data.keys():
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_RESERVED and
+ intf_data[const.TENANTID] == tenant_id and
+ intf_data[const.INSTANCE_ID] == instance_id):
+ found_blade_intf_data = blade_intf_data
+ LOG.debug(("Found blade %s associated with this"
+ " instance: %s") % (blade_id,
+ instance_id))
+ break
+
+ if found_blade_intf_data:
+ blade_intf_data = found_blade_intf_data
+ for blade_intf in blade_intf_data.keys():
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_RESERVED and
+ intf_data[const.TENANTID] == tenant_id and
+ (not intf_data[const.VIF_ID])):
+ intf_data[const.VIF_ID] = vif_id
+ intf_data[const.INSTANCE_ID] = instance_id
+ port_binding = udb.get_portbinding_dn(blade_intf)
+ port_id = port_binding[const.PORTID]
+ udb.update_portbinding(port_id, instance_id=instance_id,
+ vif_id=vif_id)
+ device_name = intf_data[const.BLADE_INTF_RHEL_DEVICE_NAME]
+ profile_name = port_binding[const.PORTPROFILENAME]
+ dynamicnic_details = {
+ const.DEVICENAME: device_name,
+ const.UCSPROFILE: profile_name,
+ }
+ LOG.debug(("Found reserved dynamic nic: %s"
+ "associated with port %s") %
+ (intf_data, port_id))
+ LOG.debug("Returning dynamic nic details: %s" %
+ dynamicnic_details)
+ return dynamicnic_details
+
+ LOG.warn("Could not find a reserved dynamic nic for tenant: %s" %
+ tenant_id)
+ return None
+
+ def _disassociate_vifid_from_port(self, tenant_id, instance_id, vif_id):
+ """
+ Disassociate a VIF-ID from a port, this happens when a
+ VM is destroyed
+ """
+ for ucsm_ip in self._inventory_state.keys():
+ ucsm = self._inventory_state[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ for blade_id in ucsm[chassis_id]:
+ blade_data = ucsm[chassis_id][blade_id]
+ blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+ for blade_intf in blade_intf_data.keys():
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_RESERVED and
+ intf_data[const.TENANTID] == tenant_id and
+ blade_intf_data[blade_intf][const.INSTANCE_ID]
+ == instance_id and
+ intf_data[const.VIF_ID][:const.UUID_LENGTH] ==
+ vif_id):
+ intf_data[const.VIF_ID] = None
+ intf_data[const.INSTANCE_ID] = None
+ port_binding = udb.get_portbinding_dn(blade_intf)
+ port_id = port_binding[const.PORTID]
+ udb.update_portbinding(port_id, instance_id=None,
+ vif_id=None)
+ LOG.debug(
+ ("Disassociated VIF-ID: %s "
+ "from port: %s"
+ "in UCS inventory state for blade: %s") %
+ (vif_id, port_id, intf_data))
+ device_params = {const.DEVICE_IP: [ucsm_ip],
+ const.PORTID: port_id}
+ return device_params
+ LOG.warn(("Disassociating VIF-ID in UCS inventory failed. "
+ "Could not find a reserved dynamic nic for tenant: %s") %
+ tenant_id)
+ return None
+
+ def _get_rsvd_blade_intf_by_port(self, tenant_id, port_id):
+ """
+ Lookup a reserved blade interface based on tenant_id and port_id
+ and return the blade interface info
+ """
+ for ucsm_ip in self._inventory_state.keys():
+ ucsm = self._inventory_state[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ for blade_id in ucsm[chassis_id]:
+ blade_data = ucsm[chassis_id][blade_id]
+ blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+ for blade_intf in blade_intf_data.keys():
+ if ((not blade_intf_data[blade_intf][const.PORTID] or
+ not blade_intf_data[blade_intf][const.TENANTID])):
+ continue
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_RESERVED and
+ intf_data[const.TENANTID] == tenant_id and
+ intf_data[const.PORTID] == port_id):
+ interface_dn = intf_data[const.BLADE_INTF_DN]
+ blade_intf_info = {const.UCSM_IP: ucsm_ip,
+ const.CHASSIS_ID: chassis_id,
+ const.BLADE_ID: blade_id,
+ const.BLADE_INTF_DN:
+ interface_dn}
+ return blade_intf_info
+ LOG.warn("Could not find a reserved nic for tenant: %s port: %s" %
+ (tenant_id, port_id))
+ return None
+
+ def _get_least_reserved_blade(self, intf_count=1):
+ """Return the blade with least number of dynamic nics reserved"""
+ unreserved_interface_count = 0
+ least_reserved_blade_ucsm = None
+ least_reserved_blade_chassis = None
+ least_reserved_blade_id = None
+ least_reserved_blade_data = None
+
+ for ucsm_ip in self._inventory_state.keys():
+ ucsm = self._inventory_state[ucsm_ip]
+ for chassis_id in ucsm.keys():
+ for blade_id in ucsm[chassis_id]:
+ blade_data = ucsm[chassis_id][blade_id]
+ if ((blade_data[const.BLADE_UNRESERVED_INTF_COUNT] >
+ unreserved_interface_count)):
+ unreserved_interface_count = (
+ blade_data[const.BLADE_UNRESERVED_INTF_COUNT])
+ least_reserved_blade_ucsm = ucsm_ip
+ least_reserved_blade_chassis = chassis_id
+ least_reserved_blade_id = blade_id
+ least_reserved_blade_data = blade_data
+
+ if unreserved_interface_count < intf_count:
+ LOG.warn(("Not enough dynamic nics available on a single host."
+ " Requested: %s, Maximum available: %s") %
+ (intf_count, unreserved_interface_count))
+ return False
+
+ least_reserved_blade_dict = {
+ const.LEAST_RSVD_BLADE_UCSM: least_reserved_blade_ucsm,
+ const.LEAST_RSVD_BLADE_CHASSIS: least_reserved_blade_chassis,
+ const.LEAST_RSVD_BLADE_ID: least_reserved_blade_id,
+ const.LEAST_RSVD_BLADE_DATA: least_reserved_blade_data,
+ }
+ LOG.debug("Found dynamic nic %s available for reservation",
+ least_reserved_blade_dict)
+ return least_reserved_blade_dict
+
+ def reload_inventory(self):
+ """Reload the inventory from a conf file"""
+ self._load_inventory()
+
+ def reserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
+ blade_data_dict, tenant_id, port_id,
+ portprofile_name):
+ """Reserve an interface on a blade"""
+ ucsm_username = cred.Store.get_username(ucsm_ip)
+ ucsm_password = cred.Store.get_password(ucsm_ip)
+ """
+ We are first getting the updated UCSM-specific blade
+ interface state
+ """
+ blade_data = self._get_blade_state(chassis_id, blade_id, ucsm_ip,
+ ucsm_username, ucsm_password)
+ blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+ chassis_data = self._inventory_state[ucsm_ip][chassis_id]
+ old_blade_intf_data = chassis_data[blade_id][const.BLADE_INTF_DATA]
+
+ """
+ We will now copy the older non-UCSM-specific blade
+ interface state
+ """
+ for blade_intf in blade_intf_data.keys():
+ old_intf_data = old_blade_intf_data[blade_intf]
+ blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = (
+ old_intf_data[const.BLADE_INTF_RESERVATION])
+ blade_intf_data[blade_intf][const.TENANTID] = (
+ old_intf_data[const.TENANTID])
+ blade_intf_data[blade_intf][const.PORTID] = (
+ old_intf_data[const.PORTID])
+ blade_intf_data[blade_intf][const.PROFILE_ID] = (
+ old_intf_data[const.PROFILE_ID])
+ blade_intf_data[blade_intf][const.INSTANCE_ID] = (
+ old_intf_data[const.INSTANCE_ID])
+ blade_intf_data[blade_intf][const.VIF_ID] = (
+ old_intf_data[const.VIF_ID])
+
+ blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = (
+ chassis_data[blade_id][const.BLADE_UNRESERVED_INTF_COUNT])
+ """
+ Now we will reserve an interface if its available
+ """
+ for blade_intf in blade_intf_data.keys():
+ intf_data = blade_intf_data[blade_intf]
+ if (intf_data[const.BLADE_INTF_RESERVATION] ==
+ const.BLADE_INTF_UNRESERVED):
+ intf_data[const.BLADE_INTF_RESERVATION] = (
+ const.BLADE_INTF_RESERVED)
+ intf_data[const.TENANTID] = tenant_id
+ intf_data[const.PORTID] = port_id
+ intf_data[const.INSTANCE_ID] = None
+ dev_eth_name = intf_data[const.BLADE_INTF_RHEL_DEVICE_NAME]
+ """
+ We are replacing the older blade interface state with new
+ """
+ chassis_data[blade_id][const.BLADE_INTF_DATA] = blade_intf_data
+ chassis_data[blade_id][const.BLADE_UNRESERVED_INTF_COUNT] -= 1
+ host_name = self._get_host_name(ucsm_ip, chassis_id, blade_id)
+ reserved_nic_dict = {
+ const.RESERVED_NIC_HOSTNAME: host_name,
+ const.RESERVED_NIC_NAME: dev_eth_name,
+ const.BLADE_INTF_DN: blade_intf,
+ }
+ port_binding = udb.add_portbinding(port_id, blade_intf, None,
+ None, None, None)
+ udb.update_portbinding(port_id,
+ tenant_id=intf_data[const.TENANTID])
+ LOG.debug("Reserved blade interface: %s\n" % reserved_nic_dict)
+ return reserved_nic_dict
+
+ LOG.warn("Dynamic nic %s could not be reserved for port-id: %s" %
+ (blade_data, port_id))
+ return False
+
+ def unreserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
+ interface_dn):
+ """Unreserve a previously reserved interface on a blade"""
+ ucsm_username = cred.Store.get_username(ucsm_ip)
+ ucsm_password = cred.Store.get_password(ucsm_ip)
+ blade_data = self._inventory_state[ucsm_ip][chassis_id][blade_id]
+
+ blade_data[const.BLADE_UNRESERVED_INTF_COUNT] += 1
+ blade_intf = blade_data[const.BLADE_INTF_DATA][interface_dn]
+ blade_intf[const.BLADE_INTF_RESERVATION] = const.BLADE_INTF_UNRESERVED
+ blade_intf[const.TENANTID] = None
+ blade_intf[const.PORTID] = None
+ blade_intf[const.PROFILE_ID] = None
+ blade_intf[const.INSTANCE_ID] = None
+ blade_intf[const.VIF_ID] = None
+ LOG.debug("Unreserved blade interface %s\n" % interface_dn)
+
+ def add_blade(self, ucsm_ip, chassis_id, blade_id):
+ """Add a blade to the inventory"""
+ # TODO (Sumit)
+ pass
+
+ def get_all_networks(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("get_all_networks() called\n")
+ return self._get_all_ucsms()
+
+ def create_network(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("create_network() called\n")
+ return self._get_all_ucsms()
+
+ def delete_network(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("delete_network() called\n")
+ return self._get_all_ucsms()
+
+ def get_network_details(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("get_network_details() called\n")
+ return self._get_all_ucsms()
+
+ def update_network(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("update_network() called\n")
+ return self._get_all_ucsms()
+
+ def get_all_ports(self, args):
+ """Return all UCSM IPs"""
+ LOG.debug("get_all_ports() called\n")
+ return self._get_all_ucsms()
+
+ def create_port(self, args):
+ """
+ Return the a dict with information of the blade
+ on which a dynamic vnic is available
+ """
+ LOG.debug("create_port() called\n")
+ least_reserved_blade_dict = self._get_least_reserved_blade()
+ if not least_reserved_blade_dict:
+ raise cexc.NoMoreNics()
+ ucsm_ip = least_reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM]
+ device_params = {
+ const.DEVICE_IP: [ucsm_ip],
+ const.UCS_INVENTORY: self,
+ const.LEAST_RSVD_BLADE_DICT: least_reserved_blade_dict,
+ }
+ return device_params
+
+ def delete_port(self, args):
+ """
+ Return the a dict with information of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ LOG.debug("delete_port() called\n")
+ tenant_id = args[0]
+ net_id = args[1]
+ port_id = args[2]
+ rsvd_info = self._get_rsvd_blade_intf_by_port(tenant_id, port_id)
+ if not rsvd_info:
+ LOG.warn("UCSInventory: Port not found: net_id: %s, port_id: %s" %
+ (net_id, port_id))
+ return {const.DEVICE_IP: []}
+ device_params = {
+ const.DEVICE_IP: [rsvd_info[const.UCSM_IP]],
+ const.UCS_INVENTORY: self,
+ const.CHASSIS_ID: rsvd_info[const.CHASSIS_ID],
+ const.BLADE_ID: rsvd_info[const.BLADE_ID],
+ const.BLADE_INTF_DN: rsvd_info[const.BLADE_INTF_DN],
+ }
+ return device_params
+
+ def update_port(self, args):
+ """
+ Return the a dict with IP address of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ LOG.debug("update_port() called\n")
+ return self._get_blade_for_port(args)
+
+ def get_port_details(self, args):
+ """
+ Return the a dict with IP address of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ LOG.debug("get_port_details() called\n")
+ return self._get_blade_for_port(args)
+
+ def plug_interface(self, args):
+ """
+ Return the a dict with IP address of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ LOG.debug("plug_interface() called\n")
+ return self._get_blade_for_port(args)
+
+ def unplug_interface(self, args):
+ """
+ Return the a dict with IP address of the blade
+ on which a dynamic vnic was reserved for this port
+ """
+ LOG.debug("unplug_interface() called\n")
+ return self._get_blade_for_port(args)
+
+ def schedule_host(self, args):
+ """Provides the hostname on which a dynamic vnic is reserved"""
+ LOG.debug("schedule_host() called\n")
+ instance_id = args[1]
+ tenant_id = args[2][const.PROJECT_ID]
+ host_name = self._get_host_name_for_rsvd_intf(tenant_id, instance_id)
+ host_list = {const.HOST_LIST: {const.HOST_1: host_name}}
+ LOG.debug("host_list is: %s" % host_list)
+ return host_list
+
+ def associate_port(self, args):
+ """
+ Get the portprofile name and the device name for the dynamic vnic
+ """
+ LOG.debug("associate_port() called\n")
+ instance_id = args[1]
+ tenant_id = args[2][const.PROJECT_ID]
+ vif_id = args[2][const.VIF_ID]
+ vif_info = self._get_instance_port(tenant_id, instance_id, vif_id)
+ vif_desc = {const.VIF_DESC: vif_info}
+
+ LOG.debug("vif_desc is: %s" % vif_desc)
+ return vif_desc
+
+ def detach_port(self, args):
+ """
+ Remove the VIF-ID and instance name association
+ with the port
+ """
+ LOG.debug("detach_port() called\n")
+ instance_id = args[1]
+ tenant_id = args[2][const.PROJECT_ID]
+ vif_id = args[2][const.VIF_ID]
+ device_params = self._disassociate_vifid_from_port(tenant_id,
+ instance_id,
+ vif_id)
+ return device_params
+
+ def create_multiport(self, args):
+ """
+ Create multiple ports for a VM
+ """
+ LOG.debug("create_ports() called\n")
+ tenant_id = args[0]
+ ports_num = args[2]
+ least_reserved_blade_dict = self._get_least_reserved_blade(ports_num)
+ if not least_reserved_blade_dict:
+ raise cexc.NoMoreNics()
+ ucsm_ip = least_reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM]
+ device_params = {
+ const.DEVICE_IP: [ucsm_ip],
+ const.UCS_INVENTORY: self,
+ const.LEAST_RSVD_BLADE_DICT:
+ least_reserved_blade_dict,
+ }
+ return device_params
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+
+import logging
+
+from quantum.db import api as db
+
+from quantum.openstack.common import importutils
+from quantum.plugins.cisco.common import cisco_constants as const
+from quantum.plugins.cisco.common import cisco_credentials as cred
+from quantum.plugins.cisco.common import cisco_exceptions as cexc
+from quantum.plugins.cisco.common import cisco_utils as cutil
+from quantum.plugins.cisco.db import network_db_v2 as cdb
+from quantum.plugins.cisco.db import ucs_db_v2 as udb
+from quantum.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
+from quantum.plugins.cisco.ucs import cisco_ucs_configuration as conf
+
+
+LOG = logging.getLogger(__name__)
+
+
+class UCSVICPlugin(L2DevicePluginBase):
+ """UCS Device Plugin"""
+
+ def __init__(self):
+ self._driver = importutils.import_object(conf.UCSM_DRIVER)
+ LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER)
+ # TODO (Sumit) Make the counter per UCSM
+ self._port_profile_counter = 0
+
+ def get_all_networks(self, tenant_id, **kwargs):
+ """
+ Returns a dictionary containing all
+ <network_uuid, network_name> for
+ the specified tenant.
+ """
+ LOG.debug("UCSVICPlugin:get_all_networks() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ networks_list = db.network_list(tenant_id)
+ new_networks_list = []
+ for network in networks_list:
+ new_network_dict = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ [])
+ new_networks_list.append(new_network_dict)
+
+ return new_networks_list
+
+ def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id,
+ **kwargs):
+ """
+ Creates a new Virtual Network, and assigns it
+ a symbolic name.
+ """
+ LOG.debug("UCSVICPlugin:create_network() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ self._driver.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip,
+ self._ucsm_username, self._ucsm_password)
+ ports_on_net = []
+ new_network_dict = cutil.make_net_dict(net_id,
+ net_name,
+ ports_on_net)
+ return new_network_dict
+
+ def delete_network(self, tenant_id, net_id, **kwargs):
+ """
+ Deletes the network with the specified network identifier
+ belonging to the specified tenant.
+ """
+ LOG.debug("UCSVICPlugin:delete_network() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ vlan_binding = cdb.get_vlan_binding(net_id)
+ vlan_name = vlan_binding[const.VLANNAME]
+ self._driver.delete_vlan(vlan_name, self._ucsm_ip,
+ self._ucsm_username, self._ucsm_password)
+ #Rohit:passing empty network name, might not need fixing
+ net_dict = cutil.make_net_dict(net_id,
+ "",
+ [])
+ return net_dict
+
+ def get_network_details(self, tenant_id, net_id, **kwargs):
+ """
+ Deletes the Virtual Network belonging to a the
+ spec
+ """
+ LOG.debug("UCSVICPlugin:get_network_details() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ network = db.network_get(net_id)
+ ports_list = network[const.NETWORKPORTS]
+ ports_on_net = []
+ for port in ports_list:
+ new_port = cutil.make_port_dict(port[const.UUID],
+ port[const.PORTSTATE],
+ port[const.NETWORKID],
+ port[const.INTERFACEID])
+ ports_on_net.append(new_port)
+
+ new_network = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ ports_on_net)
+
+ return new_network
+
+ def update_network(self, tenant_id, net_id, **kwargs):
+ """
+ Updates the symbolic name belonging to a particular
+ Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:update_network() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ network = db.network_get(net_id)
+ net_dict = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ [])
+ return net_dict
+
+ def get_all_ports(self, tenant_id, net_id, **kwargs):
+ """
+ Retrieves all port identifiers belonging to the
+ specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:get_all_ports() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ network = db.network_get(net_id)
+ ports_list = network[const.NETWORKPORTS]
+ ports_on_net = []
+ for port in ports_list:
+ port_binding = udb.get_portbinding(port[const.UUID])
+ ports_on_net.append(port_binding)
+
+ return ports_on_net
+
+ def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
+ """
+ Creates a port on the specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:create_port() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ qos = None
+ ucs_inventory = kwargs[const.UCS_INVENTORY]
+ least_rsvd_blade_dict = kwargs[const.LEAST_RSVD_BLADE_DICT]
+ chassis_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_CHASSIS]
+ blade_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_ID]
+ blade_data_dict = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_DATA]
+ new_port_profile = self._create_port_profile(tenant_id, net_id,
+ port_id,
+ conf.DEFAULT_VLAN_NAME,
+ conf.DEFAULT_VLAN_ID)
+ profile_name = new_port_profile[const.PROFILE_NAME]
+ rsvd_nic_dict = ucs_inventory.reserve_blade_interface(
+ self._ucsm_ip, chassis_id,
+ blade_id, blade_data_dict,
+ tenant_id, port_id,
+ profile_name)
+ port_binding = udb.update_portbinding(port_id,
+ portprofile_name=profile_name,
+ vlan_name=conf.DEFAULT_VLAN_NAME,
+ vlan_id=conf.DEFAULT_VLAN_ID,
+ qos=qos)
+ return port_binding
+
+ def delete_port(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ Deletes a port on a specified Virtual Network,
+ if the port contains a remote interface attachment,
+ the remote interface should first be un-plugged and
+ then the port can be deleted.
+ """
+ LOG.debug("UCSVICPlugin:delete_port() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ ucs_inventory = kwargs[const.UCS_INVENTORY]
+ chassis_id = kwargs[const.CHASSIS_ID]
+ blade_id = kwargs[const.BLADE_ID]
+ interface_dn = kwargs[const.BLADE_INTF_DN]
+ port_binding = udb.get_portbinding(port_id)
+ profile_name = port_binding[const.PORTPROFILENAME]
+ self._delete_port_profile(port_id, profile_name)
+ ucs_inventory.unreserve_blade_interface(self._ucsm_ip, chassis_id,
+ blade_id, interface_dn)
+ return udb.remove_portbinding(port_id)
+
+ def update_port(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ Updates the state of a port on the specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:update_port() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ pass
+
+ def get_port_details(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ This method allows the user to retrieve a remote interface
+ that is attached to this particular port.
+ """
+ LOG.debug("UCSVICPlugin:get_port_details() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ port_binding = udb.get_portbinding(port_id)
+ return port_binding
+
+ def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
+ **kwargs):
+ """
+ Attaches a remote interface to the specified port on the
+ specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:plug_interface() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ port_binding = udb.get_portbinding(port_id)
+ profile_name = port_binding[const.PORTPROFILENAME]
+ old_vlan_name = port_binding[const.VLANNAME]
+ new_vlan_name = self._get_vlan_name_for_network(tenant_id, net_id)
+ new_vlan_id = self._get_vlan_id_for_network(tenant_id, net_id)
+ self._driver.change_vlan_in_profile(profile_name, old_vlan_name,
+ new_vlan_name, self._ucsm_ip,
+ self._ucsm_username,
+ self._ucsm_password)
+ return udb.update_portbinding(port_id, vlan_name=new_vlan_name,
+ vlan_id=new_vlan_id)
+
+ def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ Detaches a remote interface from the specified port on the
+ specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:unplug_interface() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ port_binding = udb.get_portbinding(port_id)
+ profile_name = port_binding[const.PORTPROFILENAME]
+ old_vlan_name = port_binding[const.VLANNAME]
+ new_vlan_name = conf.DEFAULT_VLAN_NAME
+ self._driver.change_vlan_in_profile(profile_name, old_vlan_name,
+ new_vlan_name, self._ucsm_ip,
+ self._ucsm_username,
+ self._ucsm_password)
+ return udb.update_portbinding(port_id, vlan_name=new_vlan_name,
+ vlan_id=conf.DEFAULT_VLAN_ID)
+
+ def create_multiport(self, tenant_id, net_id_list, ports_num,
+ port_id_list, **kwargs):
+ """
+ Creates a port on the specified Virtual Network.
+ """
+ LOG.debug("UCSVICPlugin:create_multiport() called\n")
+ self._set_ucsm(kwargs[const.DEVICE_IP])
+ qos = None
+ ucs_inventory = kwargs[const.UCS_INVENTORY]
+ least_rsvd_blade_dict = kwargs[const.LEAST_RSVD_BLADE_DICT]
+ chassis_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_CHASSIS]
+ blade_id = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_ID]
+ blade_data_dict = least_rsvd_blade_dict[const.LEAST_RSVD_BLADE_DATA]
+ port_binding_list = []
+ for port_id, net_id in zip(port_id_list, net_id_list):
+ new_port_profile = self._create_port_profile(
+ tenant_id, net_id, port_id,
+ conf.DEFAULT_VLAN_NAME,
+ conf.DEFAULT_VLAN_ID)
+ profile_name = new_port_profile[const.PROFILE_NAME]
+ rsvd_nic_dict = ucs_inventory.reserve_blade_interface(
+ self._ucsm_ip, chassis_id,
+ blade_id, blade_data_dict,
+ tenant_id, port_id,
+ profile_name)
+ port_binding = udb.update_portbinding(
+ port_id,
+ portprofile_name=profile_name,
+ vlan_name=conf.DEFAULT_VLAN_NAME,
+ vlan_id=conf.DEFAULT_VLAN_ID,
+ qos=qos)
+ port_binding_list.append(port_binding)
+ return port_binding_list
+
+ def detach_port(self, tenant_id, instance_id, instance_desc, **kwargs):
+ """
+ Remove the association of the VIF with the dynamic vnic
+ """
+ LOG.debug("detach_port() called\n")
+ port_id = kwargs[const.PORTID]
+ kwargs.pop(const.PORTID)
+ return self.unplug_interface(tenant_id, None, port_id, **kwargs)
+
+ def _get_profile_name(self, port_id):
+ """Returns the port profile name based on the port UUID"""
+ profile_name = conf.PROFILE_NAME_PREFIX + cutil.get16ByteUUID(port_id)
+ return profile_name
+
+ def _get_vlan_name_for_network(self, tenant_id, network_id):
+ """Return the VLAN name as set by the L2 network plugin"""
+ vlan_binding = cdb.get_vlan_binding(network_id)
+ return vlan_binding[const.VLANNAME]
+
+ def _get_vlan_id_for_network(self, tenant_id, network_id):
+ """Return the VLAN id as set by the L2 network plugin"""
+ vlan_binding = cdb.get_vlan_binding(network_id)
+ return vlan_binding[const.VLANID]
+
+ def _create_port_profile(self, tenant_id, net_id, port_id, vlan_name,
+ vlan_id):
+ """Create port profile in UCSM"""
+ if self._port_profile_counter >= int(conf.MAX_UCSM_PORT_PROFILES):
+ raise cexc.UCSMPortProfileLimit(net_id=net_id, port_id=port_id)
+ profile_name = self._get_profile_name(port_id)
+ self._driver.create_profile(profile_name, vlan_name, self._ucsm_ip,
+ self._ucsm_username, self._ucsm_password)
+ self._port_profile_counter += 1
+ new_port_profile = {const.PROFILE_NAME: profile_name,
+ const.PROFILE_VLAN_NAME: vlan_name,
+ const.PROFILE_VLAN_ID: vlan_id}
+ return new_port_profile
+
+ def _delete_port_profile(self, port_id, profile_name):
+ """Delete port profile in UCSM"""
+ self._driver.delete_profile(profile_name, self._ucsm_ip,
+ self._ucsm_username, self._ucsm_password)
+ self._port_profile_counter -= 1
+
+ def _set_ucsm(self, ucsm_ip):
+ """Set the UCSM IP, username, and password"""
+ self._ucsm_ip = ucsm_ip
+ self._ucsm_username = cred.Store.get_username(conf.UCSM_IP_ADDRESS)
+ self._ucsm_password = cred.Store.get_password(conf.UCSM_IP_ADDRESS)