]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Fixes to support multinic.
authorSumit Naiksatam <snaiksat@cisco.com>
Mon, 29 Aug 2011 09:06:48 +0000 (02:06 -0700)
committerSumit Naiksatam <snaiksat@cisco.com>
Mon, 29 Aug 2011 09:06:48 +0000 (02:06 -0700)
quantum/plugins/cisco/client/cli [new file with mode: 0755]
quantum/plugins/cisco/common/cisco_constants.py
quantum/plugins/cisco/l2network_plugin.py
quantum/plugins/cisco/models/l2network_multi_blade.py
quantum/plugins/cisco/models/l2network_single_blade.py
quantum/plugins/cisco/ucs/cisco_ucs_inventory.py
quantum/plugins/cisco/ucs/cisco_ucs_plugin.py

diff --git a/quantum/plugins/cisco/client/cli b/quantum/plugins/cisco/client/cli
new file mode 100755 (executable)
index 0000000..b20ada1
--- /dev/null
@@ -0,0 +1,194 @@
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# Basic structure and framework of this CLI has been borrowed from Quantum,
+# written by the following authors
+# @author: Somik Behera, Nicira Networks, Inc.
+# @author: Brad Hall, Nicira Networks, Inc.
+# @author: Salvatore Orlando, Citrix
+#
+# Cisco adaptation for extensions
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+"""
+
+import gettext
+import logging
+import logging.handlers
+import os
+import sys
+
+from optparse import OptionParser
+
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+                                   os.pardir,
+                                   os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')):
+    sys.path.insert(0, possible_topdir)
+
+gettext.install('quantum', unicode=1)
+
+from quantum import cli_lib
+from quantum.client import Client
+
+from quantum.plugins.cisco.common import cisco_constants as const
+
+LOG = logging.getLogger('quantum')
+FORMAT = 'json'
+ACTION_PREFIX_EXT = '/v1.0'
+ACTION_PREFIX_CSCO = ACTION_PREFIX_EXT + \
+        '/extensions/csco/tenants/{tenant_id}'
+TENANT_ID = 'nova'
+CSCO_EXT_NAME = 'Cisco Nova Tenant'
+
+def help():
+    print "\nCommands:"
+    for k in commands.keys():
+        print "    %s %s" % (k,
+          " ".join(["<%s>" % y for y in commands[k]["args"]]))
+
+
+def build_args(cmd, cmdargs, arglist):
+    args = []
+    orig_arglist = arglist[:]
+    try:
+        for x in cmdargs:
+            args.append(arglist[0])
+            del arglist[0]
+    except:
+        LOG.error("Not enough arguments for \"%s\" (expected: %d, got: %d)" % (
+          cmd, len(cmdargs), len(orig_arglist)))
+        print "Usage:\n    %s %s" % (cmd,
+          " ".join(["<%s>" % y for y in commands[cmd]["args"]]))
+        return None
+    if len(arglist) > 0:
+        LOG.error("Too many arguments for \"%s\" (expected: %d, got: %d)" % (
+          cmd, len(cmdargs), len(orig_arglist)))
+        print "Usage:\n    %s %s" % (cmd,
+          " ".join(["<%s>" % y for y in commands[cmd]["args"]]))
+        return None
+    return args
+
+def list_extensions(*args):
+    request_url = "/extensions"
+    client = Client(HOST, PORT, USE_SSL, format='json',
+                    action_prefix=ACTION_PREFIX_EXT, tenant="dummy")
+    data = client.do_request('GET', request_url)
+    print("Obtained supported extensions from Quantum: %s" % data)
+
+def schedule_host(tenant_id, instance_id, user_id=None):
+    """Gets the host name from the Quantum service"""
+    project_id = tenant_id
+
+    instance_data_dict = \
+            {'novatenant': \
+             {'instance_id': instance_id,
+              'instance_desc': \
+              {'user_id': user_id,
+               'project_id': project_id}}}
+
+    request_url = "/novatenants/" + project_id + "/schedule_host"
+    client = Client(HOST, PORT, USE_SSL, format='json', tenant=TENANT_ID,
+                    action_prefix=ACTION_PREFIX_CSCO)
+    data = client.do_request('PUT', request_url, body=instance_data_dict)
+
+    hostname = data["host_list"]["host_1"]
+    if not hostname:
+        print("Scheduler was unable to locate a host" + \
+             " for this request. Is the appropriate" + \
+              " service running?")
+
+    print("Quantum service returned host: %s" % hostname)
+
+def create_ports(tenant_id, net_id_list, *args):
+    """Creates ports on a single host"""
+    net_list = net_id_list.split(",") 
+    ports_info = {'novatenant': \
+                  {'status': 'ACTIVE',
+                   'net_id': "dummy",
+                   'ports_num': len(net_list),
+                   'ports_desc' : \
+                   {const.NETID_LIST: net_list}}}
+
+    request_url = "/novatenants/" + tenant_id + "/create_ports"
+    client = Client(HOST, PORT, USE_SSL, format='json', tenant=tenant_id,
+                    action_prefix=ACTION_PREFIX_CSCO)
+    data = client.do_request('POST', request_url, body=ports_info)
+
+    print("Created ports: %s" % data)
+
+commands = {
+  "create_ports": {
+    "func": create_ports,
+    "args": ["tenant-id", "net-id-list"]},
+  "list_extensions": {
+    "func": list_extensions,
+    "args": []},
+  "schedule_host": {
+    "func": schedule_host,
+    "args": ["tenant-id", "instance-id"]}, }
+
+
+if __name__ == "__main__":
+    usagestr = "Usage: %prog [OPTIONS] <command> [args]"
+    parser = OptionParser(usage=usagestr)
+    parser.add_option("-H", "--host", dest="host",
+      type="string", default="127.0.0.1", help="ip address of api host")
+    parser.add_option("-p", "--port", dest="port",
+      type="int", default=9696, help="api poort")
+    parser.add_option("-s", "--ssl", dest="ssl",
+      action="store_true", default=False, help="use ssl")
+    parser.add_option("-v", "--verbose", dest="verbose",
+      action="store_true", default=False, help="turn on verbose logging")
+    parser.add_option("-f", "--logfile", dest="logfile",
+      type="string", default="syslog", help="log file path")
+    options, args = parser.parse_args()
+
+    if options.verbose:
+        LOG.setLevel(logging.DEBUG)
+    else:
+        LOG.setLevel(logging.WARN)
+
+    if options.logfile == "syslog":
+        LOG.addHandler(logging.handlers.SysLogHandler(address='/dev/log'))
+    else:
+        LOG.addHandler(logging.handlers.WatchedFileHandler(options.logfile))
+        os.chmod(options.logfile, 0644)
+
+    if len(args) < 1:
+        parser.print_help()
+        help()
+        sys.exit(1)
+
+    cmd = args[0]
+    if cmd not in commands.keys():
+        LOG.error("Unknown command: %s" % cmd)
+        help()
+        sys.exit(1)
+
+    args = build_args(cmd, commands[cmd]["args"], args[1:])
+
+    LOG.info("Executing command \"%s\" with args: %s" % (cmd, args))
+
+    HOST = options.host
+    PORT = options.port
+    USE_SSL = options.ssl
+    commands[cmd]["func"](*args)
+
+    LOG.info("Command execution completed")
+    sys.exit(0)
index fbcc9b94cc18327442b8afa8fe7c81c3279a50a2..2780e72bc18de4eca4243f303b463412dcd61978 100644 (file)
@@ -149,3 +149,7 @@ LEAST_RSVD_BLADE_DICT = 'least_rsvd_blade_dict'
 UCSM_IP = 'ucsm_ip_address'
 
 NETWORK_ADMIN = 'network_admin'
+
+NETID_LIST = 'net_id_list'
+
+DELIMITERS = "[,;:\b\s]"
index 8eb908657d6b6b8fe02052ae1e102cbebd16c01d..4b4fdfa0cdeb58b6ec96261e2799a8a39c50f015 100644 (file)
@@ -21,7 +21,7 @@
 
 import inspect
 import logging as LOG
-import platform
+import re
 
 from quantum.common import exceptions as exc
 from quantum.common import utils
@@ -178,6 +178,10 @@ class L2Network(QuantumPluginBase):
         Creates a port on the specified Virtual Network.
         """
         LOG.debug("create_port() called\n")
+
+        if re.search(const.DELIMITERS, net_id):
+            return self.create_ports(tenant_id, net_id, port_state)
+
         port = db.port_create(net_id, port_state)
         unique_port_id_string = port[const.UUID]
         self._invoke_device_plugins(self._func_name(), [tenant_id, net_id,
@@ -287,7 +291,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("get_portprofile_details() called\n")
         try:
             portprofile = cdb.get_portprofile(tenant_id, profile_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.PortProfileNotFound(tenant_id=tenant_id,
                                            portprofile_id=profile_id)
 
@@ -313,7 +317,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("delete_portprofile() called\n")
         try:
             portprofile = cdb.get_portprofile(tenant_id, profile_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.PortProfileNotFound(tenant_id=tenant_id,
                                            portprofile_id=profile_id)
 
@@ -329,7 +333,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("rename_portprofile() called\n")
         try:
             portprofile = cdb.get_portprofile(tenant_id, profile_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.PortProfileNotFound(tenant_id=tenant_id,
                                            portprofile_id=profile_id)
         portprofile = cdb.update_portprofile(tenant_id, profile_id, new_name)
@@ -345,7 +349,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("associate_portprofile() called\n")
         try:
             portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.PortProfileNotFound(tenant_id=tenant_id,
                                            portprofile_id=portprofile_id)
 
@@ -357,7 +361,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("disassociate_portprofile() called\n")
         try:
             portprofile = cdb.get_portprofile(tenant_id, portprofile_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.PortProfileNotFound(tenant_id=tenant_id,
                                       portprofile_id=portprofile_id)
 
@@ -374,7 +378,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("get_qos_details() called\n")
         try:
             qos_level = cdb.get_qos(tenant_id, qos_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.QosNotFound(tenant_id=tenant_id,
                                    qos_id=qos_id)
         return qos_level
@@ -390,7 +394,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("delete_qos() called\n")
         try:
             qos_level = cdb.get_qos(tenant_id, qos_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.QosNotFound(tenant_id=tenant_id,
                                    qos_id=qos_id)
         return cdb.remove_qos(tenant_id, qos_id)
@@ -400,7 +404,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("rename_qos() called\n")
         try:
             qos_level = cdb.get_qos(tenant_id, qos_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.QosNotFound(tenant_id=tenant_id,
                                    qos_id=qos_id)
         qos = cdb.update_qos(tenant_id, qos_id, new_name)
@@ -417,7 +421,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("get_credential_details() called\n")
         try:
             credential = cdb.get_credential(tenant_id, credential_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.CredentialNotFound(tenant_id=tenant_id,
                                           credential_id=credential_id)
         return credential
@@ -435,7 +439,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("delete_credential() called\n")
         try:
             credential = cdb.get_credential(tenant_id, credential_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.CredentialNotFound(tenant_id=tenant_id,
                                           credential_id=credential_id)
         credential = cdb.remove_credential(tenant_id, credential_id)
@@ -446,7 +450,7 @@ class L2Network(QuantumPluginBase):
         LOG.debug("rename_credential() called\n")
         try:
             credential = cdb.get_credential(tenant_id, credential_id)
-        except Exception, excp:
+        except Exception:
             raise cexc.CredentialNotFound(tenant_id=tenant_id,
                                           credential_id=credential_id)
         credential = cdb.update_credential(tenant_id, credential_id, new_name)
@@ -469,6 +473,33 @@ class L2Network(QuantumPluginBase):
                                                                instance_id,
                                                                instance_desc])
 
+    def create_ports(self, tenant_id, net_id, port_state=None):
+        """
+        Creates multiple ports on the specified Virtual Network.
+        """
+        LOG.debug("create_ports() called\n")
+        net_id_list = re.split(const.DELIMITERS, net_id)
+        ports_num = len(net_id_list)
+        ports_id_list = []
+        ports_list_str = ""
+
+        for net_id in net_id_list:
+            port = db.port_create(net_id, port_state)
+            ports_id_list.append(port[const.UUID])
+            if ports_list_str != "":
+                ports_list_str = ports_list_str + "," + \
+                        str(port[const.UUID])
+            else:
+                ports_list_str = str(port[const.UUID])
+
+        self._invoke_device_plugins(self._func_name(), [tenant_id,
+                                                        net_id_list,
+                                                        ports_num,
+                                                        ports_id_list])
+        new_ports_dict = cutil.make_port_dict(ports_list_str, port_state,
+                                              net_id, None)
+        return new_ports_dict
+
     """
     Private functions
     """
index acd24d5ee674e8b040d904edd7a6a052f680ea22..598c588c2f1d86d3173c5b43a3d1747f16c064e2 100644 (file)
@@ -171,3 +171,8 @@ class L2NetworkMultiBlade(L2NetworkModelBase):
         LOG.debug("associate_port() called\n")
         return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
                                       args)
+
+    def create_ports(self, args):
+        """Support for extension  API call"""
+        self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
+                                       args)
index 617d1a55d6b611f61ec57e959c62c205bac41005..3b0f62e31459f283b1d6a0e058c4c741375d79c4 100644 (file)
@@ -162,3 +162,8 @@ class L2NetworkSingleBlade(L2NetworkModelBase):
         LOG.debug("associate_port() called\n")
         return self._invoke_inventory(const.UCS_PLUGIN, self._func_name(),
                                       args)
+
+    def create_ports(self, args):
+        """Support for extension  API call"""
+        self._invoke_plugin_per_device(const.UCS_PLUGIN, self._func_name(),
+                                       args)
index 2a90824bb8eb115c9d3936beb6d5361f3e4dcecb..cf441af1ee705b58bc66876c6f7e6c33053a45e7 100644 (file)
@@ -28,6 +28,7 @@ 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 api as db
 from quantum.plugins.cisco.db import ucs_db as udb
 from quantum.plugins.cisco.ucs \
         import cisco_ucs_inventory_configuration as conf
@@ -120,7 +121,30 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                 chassis_dict[chassis_id] = blade_list
             self._inventory[ucsm_ip] = chassis_dict
 
-        self.build_inventory_state()
+        self._build_inventory_state()
+
+    def _build_inventory_state(self):
+        """Populate the state of all the blades"""
+        for ucsm_ip in self._inventory.keys():
+            self._inventory_state[ucsm_ip] = {ucsm_ip: {}}
+            ucsm_username = cred.Store.getUsername(ucsm_ip)
+            ucsm_password = cred.Store.getPassword(ucsm_ip)
+            chasses_state = {}
+            self._inventory_state[ucsm_ip] = chasses_state
+            ucsm = self._inventory[ucsm_ip]
+            for chassis_id in ucsm.keys():
+                blades_dict = {}
+                chasses_state[chassis_id] = blades_dict
+                for blade_id in ucsm[chassis_id]:
+                    blade_data = self._get_initial_blade_state(chassis_id,
+                                                               blade_id,
+                                                               ucsm_ip,
+                                                               ucsm_username,
+                                                               ucsm_password)
+                    blades_dict[blade_id] = blade_data
+
+        LOG.debug("UCS Inventory state is: %s\n" % self._inventory_state)
+        return True
 
     def _get_host_name(self, ucsm_ip, chassis_id, blade_id):
         """Get the hostname based on the blade info"""
@@ -186,8 +210,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                 blade_intf_data[blade_intf][const.VIF_ID] = \
                         port_binding[const.VIF_ID]
 
+        host_name = self._get_host_name(ucsm_ip, chassis_id, blade_id)
         blade_data = {const.BLADE_INTF_DATA: blade_intf_data,
-                     const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter}
+                      const.BLADE_UNRESERVED_INTF_COUNT: unreserved_counter,
+                      const.HOST_NAME: host_name}
         return blade_data
 
     def _get_blade_state(self, chassis_id, blade_id, ucsm_ip,
@@ -228,7 +254,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         tenant_id = args[0]
         net_id = args[1]
         port_id = args[2]
-        rsvd_info = self.get_rsvd_blade_intf_by_port(tenant_id, port_id)
+        rsvd_info = self._get_rsvd_blade_intf_by_port(tenant_id, port_id)
         if not rsvd_info:
             raise exc.PortNotFound(net_id=net_id, port_id=port_id)
         device_params = {const.DEVICE_IP: [rsvd_info[const.UCSM_IP]]}
@@ -268,10 +294,11 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                  tenant_id)
         return None
 
-    def _get_instance_port(self, tenant_id, instance_id, vif_id=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():
@@ -286,56 +313,119 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                            [const.TENANTID] == tenant_id and \
                            blade_intf_data[blade_intf]\
                            [const.INSTANCE_ID] == instance_id:
-                            blade_intf_data[blade_intf][const.VIF_ID] = \
-                                    vif_id
-                            port_binding = udb.get_portbinding_dn(blade_intf)
-                            port_id = port_binding[const.PORTID]
-                            udb.update_portbinding(port_id,
-                                                   vif_id=vif_id)
-                            device_name = blade_intf_data[blade_intf]\
-                                    [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" %
-                                      (blade_intf_data[blade_intf], port_id))
-                            LOG.debug("Returning dynamic nic details: %s" %
-                                      dynamicnic_details)
-                            return dynamicnic_details
+                            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():
+                if blade_intf_data[blade_intf]\
+                   [const.BLADE_INTF_RESERVATION] == \
+                   const.BLADE_INTF_RESERVED and \
+                   blade_intf_data[blade_intf]\
+                   [const.TENANTID] == tenant_id and \
+                   (not blade_intf_data[blade_intf][const.VIF_ID]):
+                    blade_intf_data[blade_intf][const.VIF_ID] = \
+                            vif_id
+                    blade_intf_data[blade_intf]\
+                            [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)
+                    db.port_set_attachment_by_id(port_id, vif_id)
+                    device_name = blade_intf_data[blade_intf]\
+                            [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" %
+                              (blade_intf_data[blade_intf], 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 reload_inventory(self):
-        """Reload the inventory from a conf file"""
-        self._load_inventory()
-
-    def build_inventory_state(self):
-        """Populate the state of all the blades"""
-        for ucsm_ip in self._inventory.keys():
-            self._inventory_state[ucsm_ip] = {ucsm_ip: {}}
-            ucsm_username = cred.Store.getUsername(ucsm_ip)
-            ucsm_password = cred.Store.getPassword(ucsm_ip)
-            chasses_state = {}
-            self._inventory_state[ucsm_ip] = chasses_state
-            ucsm = self._inventory[ucsm_ip]
+    def _disassociate_vifid_from_port(self, tenant_id, port_id):
+        """
+        Return the device name for a reserved interface
+        """
+        for ucsm_ip in self._inventory_state.keys():
+            ucsm = self._inventory_state[ucsm_ip]
             for chassis_id in ucsm.keys():
-                blades_dict = {}
-                chasses_state[chassis_id] = blades_dict
                 for blade_id in ucsm[chassis_id]:
-                    blade_data = self._get_initial_blade_state(chassis_id,
-                                                               blade_id,
-                                                               ucsm_ip,
-                                                               ucsm_username,
-                                                               ucsm_password)
-                    blades_dict[blade_id] = blade_data
+                    blade_data = ucsm[chassis_id][blade_id]
+                    blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+                    for blade_intf in blade_intf_data.keys():
+                        if blade_intf_data[blade_intf]\
+                           [const.BLADE_INTF_RESERVATION] == \
+                           const.BLADE_INTF_RESERVED and \
+                           blade_intf_data[blade_intf]\
+                           [const.TENANTID] == tenant_id and \
+                           blade_intf_data[blade_intf][const.PORTID] == \
+                           port_id:
+                            vif_id = blade_intf_data[blade_intf][const.VIF_ID]
+                            blade_intf_data[blade_intf][const.VIF_ID] = \
+                                    None
+                            blade_intf_data[blade_intf][const.INSTANCE_ID] = \
+                                    None
+                            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,
+                                       blade_intf_data[blade_intf]))
+                            return
+        LOG.warn("Disassociating VIF-ID in UCS inventory failed. " \
+                 "Could not find a reserved dynamic nic for tenant: %s" %
+                 tenant_id)
+        return None
 
-        LOG.debug("UCS Inventory state is: %s\n" % self._inventory_state)
-        return True
+    def _get_rsvd_blade_intf_by_port(self, tenant_id, port_id):
+        """
+        Lookup a reserved blade interface based on tenant_id and port_id
+        and return the blade interface info
+        """
+        for ucsm_ip in self._inventory_state.keys():
+            ucsm = self._inventory_state[ucsm_ip]
+            for chassis_id in ucsm.keys():
+                for blade_id in ucsm[chassis_id]:
+                    blade_data = ucsm[chassis_id][blade_id]
+                    blade_intf_data = blade_data[const.BLADE_INTF_DATA]
+                    for blade_intf in blade_intf_data.keys():
+                        if not blade_intf_data[blade_intf][const.PORTID] or \
+                           not blade_intf_data[blade_intf][const.TENANTID]:
+                            continue
+                        if blade_intf_data[blade_intf]\
+                           [const.BLADE_INTF_RESERVATION] == \
+                           const.BLADE_INTF_RESERVED and \
+                           blade_intf_data[blade_intf]\
+                           [const.TENANTID] == tenant_id and \
+                           blade_intf_data[blade_intf]\
+                           [const.PORTID] == port_id:
+                            interface_dn = blade_intf_data[blade_intf]\
+                                    [const.BLADE_INTF_DN]
+                            blade_intf_info = {const.UCSM_IP: ucsm_ip,
+                                               const.CHASSIS_ID: chassis_id,
+                                               const.BLADE_ID: blade_id,
+                                               const.BLADE_INTF_DN:
+                                               interface_dn}
+                            return blade_intf_info
+        LOG.warn("Could not find a reserved nic for tenant: %s port: %s" %
+                 (tenant_id, port_id))
+        return None
 
-    def get_least_reserved_blade(self):
+    def _get_least_reserved_blade(self, intf_count=1):
         """Return the blade with least number of dynamic nics reserved"""
         unreserved_interface_count = 0
         least_reserved_blade_ucsm = None
@@ -357,8 +447,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                         least_reserved_blade_id = blade_id
                         least_reserved_blade_data = blade_data
 
-        if unreserved_interface_count == 0:
-            LOG.warn("No more dynamic nics available for reservation")
+        if unreserved_interface_count < intf_count:
+            LOG.warn("Not enough dynamic nics available on a single host." \
+                     " Requested: %s, Maximum available: %s" %
+                     (intf_count, unreserved_interface_count))
             return False
 
         least_reserved_blade_dict = \
@@ -370,6 +462,10 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                   least_reserved_blade_dict)
         return least_reserved_blade_dict
 
+    def reload_inventory(self):
+        """Reload the inventory from a conf file"""
+        self._load_inventory()
+
     def reserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
                                 blade_data_dict, tenant_id, port_id,
                                 portprofile_name):
@@ -377,15 +473,19 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         ucsm_username = cred.Store.getUsername(ucsm_ip)
         ucsm_password = cred.Store.getPassword(ucsm_ip)
         """
-        We are first getting the updated blade interface state
+        We are first getting the updated UCSM-specific blade
+        interface state
         """
         blade_data = self._get_blade_state(chassis_id, blade_id, ucsm_ip,
                                            ucsm_username, ucsm_password)
         blade_intf_data = blade_data[const.BLADE_INTF_DATA]
-        old_blade_intf_data = blade_data_dict[const.BLADE_INTF_DATA]
+        old_blade_intf_data = \
+                self._inventory_state[ucsm_ip][chassis_id]\
+                [blade_id][const.BLADE_INTF_DATA]
 
         """
-        We will now copy the older blade interface state
+        We will now copy the older non-UCSM-specific blade
+        interface state
         """
         for blade_intf in blade_intf_data.keys():
             blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \
@@ -403,7 +503,8 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                     old_blade_intf_data[blade_intf][const.VIF_ID]
 
         blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = \
-                blade_data_dict[const.BLADE_UNRESERVED_INTF_COUNT]
+                self._inventory_state[ucsm_ip][chassis_id]\
+                [blade_id][const.BLADE_UNRESERVED_INTF_COUNT]
         """
         Now we will reserve an interface if its available
         """
@@ -440,7 +541,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
                 return reserved_nic_dict
 
         LOG.warn("Dynamic nic %s could not be reserved for port-id: %s" %
-                 (blade_data_dict, port_id))
+                 (blade_data, port_id))
         return False
 
     def unreserve_blade_interface(self, ucsm_ip, chassis_id, blade_id,
@@ -460,40 +561,6 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         blade_intf[const.VIF_ID] = None
         LOG.debug("Unreserved blade interface %s\n" % interface_dn)
 
-    def get_rsvd_blade_intf_by_port(self, tenant_id, port_id):
-        """
-        Lookup a reserved blade interface based on tenant_id and port_id
-        and return the blade interface info
-        """
-        for ucsm_ip in self._inventory_state.keys():
-            ucsm = self._inventory_state[ucsm_ip]
-            for chassis_id in ucsm.keys():
-                for blade_id in ucsm[chassis_id]:
-                    blade_data = ucsm[chassis_id][blade_id]
-                    blade_intf_data = blade_data[const.BLADE_INTF_DATA]
-                    for blade_intf in blade_intf_data.keys():
-                        if not blade_intf_data[blade_intf][const.PORTID] or \
-                           not blade_intf_data[blade_intf][const.TENANTID]:
-                            continue
-                        if blade_intf_data[blade_intf]\
-                           [const.BLADE_INTF_RESERVATION] == \
-                           const.BLADE_INTF_RESERVED and \
-                           blade_intf_data[blade_intf]\
-                           [const.TENANTID] == tenant_id and \
-                           blade_intf_data[blade_intf]\
-                           [const.PORTID] == port_id:
-                            interface_dn = blade_intf_data[blade_intf]\
-                                    [const.BLADE_INTF_DN]
-                            blade_intf_info = {const.UCSM_IP: ucsm_ip,
-                                               const.CHASSIS_ID: chassis_id,
-                                               const.BLADE_ID: blade_id,
-                                               const.BLADE_INTF_DN:
-                                               interface_dn}
-                            return blade_intf_info
-        LOG.warn("Could not find a reserved nic for tenant: %s port: %s" %
-                 (tenant_id, port_id))
-        return None
-
     def add_blade(self, ucsm_ip, chassis_id, blade_id):
         """Add a blade to the inventory"""
         # TODO (Sumit)
@@ -535,7 +602,7 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         on which a dynamic vnic is available
         """
         LOG.debug("create_port() called\n")
-        least_reserved_blade_dict = self.get_least_reserved_blade()
+        least_reserved_blade_dict = self._get_least_reserved_blade()
         if not least_reserved_blade_dict:
             raise cexc.NoMoreNics()
         ucsm_ip = least_reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM]
@@ -554,9 +621,12 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         tenant_id = args[0]
         net_id = args[1]
         port_id = args[2]
-        rsvd_info = self.get_rsvd_blade_intf_by_port(tenant_id, port_id)
+        rsvd_info = self._get_rsvd_blade_intf_by_port(tenant_id, port_id)
         if not rsvd_info:
             raise exc.PortNotFound(net_id=net_id, port_id=port_id)
+            LOG.warn("UCSInventory: Port not found: net_id: %s, port_id: %s" %
+                     (net_id, port_id))
+            return {const.DEVICE_IP: []}
         device_params = \
                 {const.DEVICE_IP: [rsvd_info[const.UCSM_IP]],
                  const.UCS_INVENTORY: self,
@@ -595,6 +665,9 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         on which a dynamic vnic was reserved for this port
         """
         LOG.debug("unplug_interface() called\n")
+        tenant_id = args[0]
+        port_id = args[2]
+        self._disassociate_vifid_from_port(tenant_id, port_id)
         return self._get_blade_for_port(args)
 
     def schedule_host(self, args):
@@ -619,3 +692,20 @@ class UCSInventory(L2NetworkDeviceInventoryBase):
         vif_desc = {const.VIF_DESC: vif_info}
         LOG.debug("vif_desc is: %s" % vif_desc)
         return vif_desc
+
+    def create_ports(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
index 6fe99f39beaa2280eeb3419571697b1a590e7c95..7ce896549f21ea2102d654f3182a1ebdd9ad5dbc 100644 (file)
@@ -296,3 +296,36 @@ class UCSVICPlugin(L2DevicePluginBase):
         self._ucsm_ip = ucsm_ip
         self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS)
         self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS)
+
+    def create_ports(self, tenant_id, net_id_list, ports_num, port_id_list,
+                     **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]
+        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