From: Sumit Naiksatam Date: Mon, 29 Aug 2011 09:06:48 +0000 (-0700) Subject: Fixes to support multinic. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=4d3c1165bd5007dc7e4955de50080b2ad8864912;p=openstack-build%2Fneutron-build.git Fixes to support multinic. --- diff --git a/quantum/plugins/cisco/client/cli b/quantum/plugins/cisco/client/cli new file mode 100755 index 000000000..b20ada180 --- /dev/null +++ b/quantum/plugins/cisco/client/cli @@ -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] [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) diff --git a/quantum/plugins/cisco/common/cisco_constants.py b/quantum/plugins/cisco/common/cisco_constants.py index fbcc9b94c..2780e72bc 100644 --- a/quantum/plugins/cisco/common/cisco_constants.py +++ b/quantum/plugins/cisco/common/cisco_constants.py @@ -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]" diff --git a/quantum/plugins/cisco/l2network_plugin.py b/quantum/plugins/cisco/l2network_plugin.py index 8eb908657..4b4fdfa0c 100644 --- a/quantum/plugins/cisco/l2network_plugin.py +++ b/quantum/plugins/cisco/l2network_plugin.py @@ -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 """ diff --git a/quantum/plugins/cisco/models/l2network_multi_blade.py b/quantum/plugins/cisco/models/l2network_multi_blade.py index acd24d5ee..598c588c2 100644 --- a/quantum/plugins/cisco/models/l2network_multi_blade.py +++ b/quantum/plugins/cisco/models/l2network_multi_blade.py @@ -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) diff --git a/quantum/plugins/cisco/models/l2network_single_blade.py b/quantum/plugins/cisco/models/l2network_single_blade.py index 617d1a55d..3b0f62e31 100644 --- a/quantum/plugins/cisco/models/l2network_single_blade.py +++ b/quantum/plugins/cisco/models/l2network_single_blade.py @@ -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) diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py b/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py index 2a90824bb..cf441af1e 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py @@ -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 diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py b/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py index 6fe99f39b..7ce896549 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py @@ -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