From d047001964401cec3e64396bfd73f73901cdc7c5 Mon Sep 17 00:00:00 2001 From: Sumit Naiksatam Date: Fri, 19 Aug 2011 20:07:28 -0700 Subject: [PATCH] Initial commit with lots of changes. --- .../plugins/cisco/common/cisco_constants.py | 12 + .../plugins/cisco/conf/l2network_plugin.ini | 9 +- quantum/plugins/cisco/conf/ucs_inventory.ini | 24 ++ quantum/plugins/cisco/l2network_plugin.py | 25 +- .../cisco/l2network_segmentation_base.py | 75 ++++ quantum/plugins/cisco/models/__init__.py | 20 + .../cisco/models/l2network_multi_blade.py | 170 +++++++++ .../l2network_single_blade.py} | 18 +- .../plugins/cisco/segmentation/__init__.py | 20 + .../cisco/segmentation/l2network_vlan_mgr.py | 47 +++ .../cisco/ucs/cisco_ucs_configuration.py | 7 + .../plugins/cisco/ucs/cisco_ucs_inventory.py | 361 ++++++++++++++++++ .../cisco/ucs/cisco_ucs_network_driver.py | 127 +++++- quantum/plugins/cisco/ucs/cisco_ucs_plugin.py | 49 ++- 14 files changed, 928 insertions(+), 36 deletions(-) create mode 100644 quantum/plugins/cisco/conf/ucs_inventory.ini create mode 100644 quantum/plugins/cisco/l2network_segmentation_base.py create mode 100644 quantum/plugins/cisco/models/__init__.py create mode 100644 quantum/plugins/cisco/models/l2network_multi_blade.py rename quantum/plugins/cisco/{l2network_model.py => models/l2network_single_blade.py} (87%) create mode 100644 quantum/plugins/cisco/segmentation/__init__.py create mode 100644 quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py create mode 100644 quantum/plugins/cisco/ucs/cisco_ucs_inventory.py diff --git a/quantum/plugins/cisco/common/cisco_constants.py b/quantum/plugins/cisco/common/cisco_constants.py index 67ba05d5811..02eb2d43110 100644 --- a/quantum/plugins/cisco/common/cisco_constants.py +++ b/quantum/plugins/cisco/common/cisco_constants.py @@ -128,5 +128,17 @@ VIF_DESC = 'vif_desc' DEVICENAME = 'device' UCSPROFILE = 'portprofile' +IP_ADDRESS = 'ip_address' +CHASSIS_ID = 'chassis_id' +BLADE_ID = 'blade_id' +HOST_NAME = 'host_name' + +INSTANCE_ID = 'instance_id' + +UCS_INVENTORY = 'ucs_inventory' +LEAST_RSVD_BLADE_DICT = 'least_rsvd_blade_dict' + +UCSM_IP = 'ucsm_ip_address' + MAX_CREDENTIALS = 65568 MAX_QOS_LEVELS = 1024 diff --git a/quantum/plugins/cisco/conf/l2network_plugin.ini b/quantum/plugins/cisco/conf/l2network_plugin.ini index 3a740a9713c..494279e7903 100644 --- a/quantum/plugins/cisco/conf/l2network_plugin.ini +++ b/quantum/plugins/cisco/conf/l2network_plugin.ini @@ -1,6 +1,6 @@ [VLANS] -vlan_start= -vlan_end= +vlan_start=100 +vlan_end=3000 vlan_name_prefix=q- [PORTS] @@ -13,4 +13,7 @@ max_port_profiles=65568 max_networks=65568 [MODEL] -model_class=quantum.plugins.cisco.l2network_model.L2NetworkModel +model_class=quantum.plugins.cisco.models.l2network_single_blade.L2NetworkSinlgeBlade + +[SEGMENTATION] +manager_class=quantum.plugins.cisco.segmentation.l2network_vlan_mgr.L2NetworkVLANMgr diff --git a/quantum/plugins/cisco/conf/ucs_inventory.ini b/quantum/plugins/cisco/conf/ucs_inventory.ini new file mode 100644 index 00000000000..889609adc81 --- /dev/null +++ b/quantum/plugins/cisco/conf/ucs_inventory.ini @@ -0,0 +1,24 @@ +[ucsm-1] +ip_address = +[[chassis-1]] +chassis_id = +[[[blade-1]]] +blade_id = +host_name = +[[[blade-2]]] +blade_id = +host_name = +[[[blade-3]]] +blade_id = +host_name = + +[ucsm-2] +ip_address = +[[chassis-1]] +chassis_id = +[[[blade-1]]] +blade_id = +host_name = +[[[blade-2]]] +blade_id = +host_name = diff --git a/quantum/plugins/cisco/l2network_plugin.py b/quantum/plugins/cisco/l2network_plugin.py index 29fc3542429..a4b30953e79 100644 --- a/quantum/plugins/cisco/l2network_plugin.py +++ b/quantum/plugins/cisco/l2network_plugin.py @@ -47,6 +47,7 @@ class L2Network(QuantumPluginBase): def __init__(self): self._vlan_counter = int(conf.VLAN_START) - 1 self._model = utils.import_object(conf.MODEL_CLASS) + self._vlan_mgr = utils.import_object(conf.MANAGER_CLASS) cdb.initialize() # TODO (Sumit): The following should move to the segmentation module cdb.create_vlanids() @@ -442,41 +443,35 @@ class L2Network(QuantumPluginBase): def get_host(self, tenant_id, instance_id, instance_desc): """Provides the hostname on which a dynamic vnic is reserved""" LOG.debug("get_host() called\n") - host_list = {const.HOST_LIST: {const.HOST_1: platform.node()}} - return host_list + return self._invoke_device_plugins(self._func_name(), [tenant_id, + instance_id, + instance_desc]) def get_instance_port(self, tenant_id, instance_id, instance_desc): """ Get the portprofile name and the device namei for the dynamic vnic """ LOG.debug("get_instance_port() called\n") - vif_desc = {const.VIF_DESC: - {const.DEVICENAME: "eth2", const.UCSPROFILE: "default"}} - return vif_desc + return self._invoke_device_plugins(self._func_name(), [tenant_id, + instance_id, + instance_desc]) """ Private functions """ def _invoke_device_plugins(self, function_name, args): """ - All device-specific calls are delegate to the model + All device-specific calls are delegated to the model """ getattr(self._model, function_name)(args) def _get_vlan_for_tenant(self, tenant_id, net_name): """Get vlan ID""" - # TODO (Sumit): - # The VLAN ID for a tenant might need to be obtained from - # somewhere (from Donabe/Melange?) - # Also need to make sure that the VLAN ID is not being used already - # Currently, just a wrap-around counter ranging from VLAN_START to - # VLAN_END - return cdb.reserve_vlanid() + return self._vlan_mgr.reserve_segmentation_id(tenant_id, net_name) def _release_vlan_for_tenant(self, tenant_id, net_id): """Relase VLAN""" - vlan_binding = cdb.get_vlan_binding(net_id) - return cdb.release_vlanid(vlan_binding[const.VLANID]) + return self._vlan_mgr.release_segmentation_id(tenant_id, net_id) def _get_vlan_name(self, net_id, vlan): """Getting the vlan name from the tenant and vlan""" diff --git a/quantum/plugins/cisco/l2network_segmentation_base.py b/quantum/plugins/cisco/l2network_segmentation_base.py new file mode 100644 index 00000000000..d7b4378b8ba --- /dev/null +++ b/quantum/plugins/cisco/l2network_segmentation_base.py @@ -0,0 +1,75 @@ +""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# +""" + +import inspect +from abc import ABCMeta, abstractmethod + + +class L2NetworkSegmentationMgrBase(object): + """ + Base class for L2 Network Segmentation Manager + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def reserve_segmentation_id(self, tenant_id, net_name, **kwargs): + """ + :returns: + :raises: + """ + pass + + @abstractmethod + def release_segmentation_id(self, tenant_id, net_id, **kwargs): + """ + :returns: + :raises: + """ + pass + + @classmethod + def __subclasshook__(cls, klass): + """ + The __subclasshook__ method is a class method + that will be called everytime a class is tested + using issubclass(klass, Plugin). + In that case, it will check that every method + marked with the abstractmethod decorator is + provided by the plugin class. + """ + if cls is L2NetworkSegementationMgrBase: + for method in cls.__abstractmethods__: + method_ok = False + for base in klass.__mro__: + if method in base.__dict__: + fn_obj = base.__dict__[method] + if inspect.isfunction(fn_obj): + abstract_fn_obj = cls.__dict__[method] + arg_count = fn_obj.func_code.co_argcount + expected_arg_count = \ + abstract_fn_obj.func_code.co_argcount + method_ok = arg_count == expected_arg_count + if method_ok: + continue + return NotImplemented + return True + return NotImplemented diff --git a/quantum/plugins/cisco/models/__init__.py b/quantum/plugins/cisco/models/__init__.py new file mode 100644 index 00000000000..09b3fab8968 --- /dev/null +++ b/quantum/plugins/cisco/models/__init__.py @@ -0,0 +1,20 @@ +""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# +""" diff --git a/quantum/plugins/cisco/models/l2network_multi_blade.py b/quantum/plugins/cisco/models/l2network_multi_blade.py new file mode 100644 index 00000000000..c5ba157d296 --- /dev/null +++ b/quantum/plugins/cisco/models/l2network_multi_blade.py @@ -0,0 +1,170 @@ +""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# +""" + +import inspect +import logging as LOG + +from quantum.common import utils +from quantum.plugins.cisco.l2network_model_base import L2NetworkModelBase +from quantum.plugins.cisco import l2network_plugin_configuration as conf +from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.ucs import cisco_ucs_inventory as ucsinv + +LOG.basicConfig(level=LOG.WARN) +LOG.getLogger(const.LOGGER_COMPONENT_NAME) + + +class L2NetworkMultiBlade(L2NetworkModelBase): + """ + Implements the L2NetworkModelBase + This implementation works with UCS and Nexus plugin for the + following topology: + One or more UCSM (each with one or more chasses connected) + All UCSM connected to a single Nexus Switch + """ + _plugins = {} + + def __init__(self): + for key in conf.PLUGINS[const.PLUGINS].keys(): + self._plugins[key] = utils.import_object( + conf.PLUGINS[const.PLUGINS][key]) + LOG.debug("Loaded device plugin %s\n" % \ + conf.PLUGINS[const.PLUGINS][key]) + + self._ucs_inventory = ucsinv.UCSInventory() + + def _func_name(self, offset=0): + """Get the name of the calling function""" + return inspect.stack()[1 + offset][3] + + def _invoke_all_device_plugins(self, function_name, args, kwargs): + """Invoke all device plugins for this model implementation""" + for plugin_obj_ref in self._plugins.values(): + getattr(plugin_obj_ref, function_name)(*args, **kwargs) + + def _invoke_ucs_plugin(self, function_name, args, kwargs): + """Invoke only the UCS plugin""" + if const.UCS_PLUGIN in self._plugins.keys(): + getattr(self._plugins[const.UCS_PLUGIN], + function_name)(*args, **kwargs) + + def _invoke_ucs_plugin_per_device(self, function_name, args): + """Invoke only UCS plugin for all the UCSMs in the system""" + ucsm_ips = self._ucs_inventory.get_all_ucsms() + for ucsm_ip in ucsm_ips: + device_params = {const.DEVICE_IP: ucsm_ip} + self._invoke_ucs_plugin(function_name, args, device_params) + + def _invoke_nexus_plugin(self, function_name, args, kwargs): + """Invoke only the Nexus plugin""" + if const.NEXUS_PLUGIN in self._plugins.keys(): + getattr(self._plugins[const.NEXUS_PLUGIN], + function_name)(*args, **kwargs) + + def _invoke_nexus_plugin_per_device(self, function_name, args): + """Invoke only the nexus plugin for all the switches in the system""" + device_params = {const.DEVICE_IP: ""} + self._invoke_nexus_plugin(self._func_name(), args, device_params) + + def get_all_networks(self, args): + """Not implemented for this model""" + pass + + def create_network(self, args): + """Support for the Quantum core API call""" + self._invoke_ucs_plugin_per_device(self._func_name(), args) + self._invoke_nexus_plugin_per_device(self._func_name(), args) + + def delete_network(self, args): + """Support for the Quantum core API call""" + self._invoke_ucs_plugin_per_device(self._func_name(), args) + self._invoke_nexus_plugin_per_device(self._func_name(), args) + + def get_network_details(self, args): + """Not implemented for this model""" + pass + + def rename_network(self, args): + """Support for the Quantum core API call""" + self._invoke_ucs_plugin_per_device(self._func_name(), args) + self._invoke_nexus_plugin_per_device(self._func_name(), args) + + def get_all_ports(self, args): + """Not implemented for this model""" + pass + + def create_port(self, args): + """Support for the Quantum core API call""" + least_reserved_blade_dict = \ + self._ucs_inventory.get_least_reserved_blade() + ucsm_ip = least_reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM] + device_params = {const.DEVICE_IP: ucsm_ip, + const.UCS_INVENTORY: self._ucs_inventory, + const.LEAST_RSVD_BLADE_DICT:\ + least_reserved_blade_dict} + self._invoke_ucs_plugin(self._func_name(), args, device_params) + + def delete_port(self, args): + """Support for the Quantum core API call""" + rsvd_info = \ + self._ucs_inventory.get_rsvd_blade_intf_by_port(args[0], + args[2]) + device_params = \ + {const.DEVICE_IP: rsvd_info[const.UCSM_IP], + const.UCS_INVENTORY: self._ucs_inventory, + const.CHASSIS_ID: rsvd_info[const.const.CHASSIS_ID], + const.BLADE_ID: rsvd_info[const.const.BLADE_ID], + const.BLADE_INTF_DN: rsvd_info[const.BLADE_INTF_DN]} + self._invoke_ucs_plugin(self._func_name(), args, device_params) + + def update_port(self, args): + """Not implemented for this model""" + pass + + def get_port_details(self, args): + """Not implemented for this model""" + pass + + def plug_interface(self, args): + """Support for the Quantum core API call""" + device_params = {const.DEVICE_IP: ""} + self._invoke_ucs_plugin(self._func_name(), args, device_params) + + def unplug_interface(self, args): + """Support for the Quantum core API call""" + device_params = {const.DEVICE_IP: ""} + self._invoke_ucs_plugin(self._func_name(), args, device_params) + + def get_host(self, args): + """Provides the hostname on which a dynamic vnic is reserved""" + LOG.debug("get_host() called\n") + host_list = {const.HOST_LIST: {const.HOST_1: platform.node()}} + return host_list + + def get_instance_port(self, args): + """ + Get the portprofile name and the device namei for the dynamic vnic + """ + LOG.debug("get_instance_port() called\n") + vif_desc = {const.VIF_DESC: + {const.DEVICENAME: "eth2", const.UCSPROFILE: "default"}} + return vif_desc + diff --git a/quantum/plugins/cisco/l2network_model.py b/quantum/plugins/cisco/models/l2network_single_blade.py similarity index 87% rename from quantum/plugins/cisco/l2network_model.py rename to quantum/plugins/cisco/models/l2network_single_blade.py index cdffa594e41..286f5c7ddef 100644 --- a/quantum/plugins/cisco/l2network_model.py +++ b/quantum/plugins/cisco/models/l2network_single_blade.py @@ -31,7 +31,7 @@ LOG.basicConfig(level=LOG.WARN) LOG.getLogger(const.LOGGER_COMPONENT_NAME) -class L2NetworkModel(L2NetworkModelBase): +class L2NetworkSinlgeBlade(L2NetworkModelBase): """ Implements the L2NetworkModelBase This implementation works with UCS and Nexus plugin, @@ -121,3 +121,19 @@ class L2NetworkModel(L2NetworkModelBase): """Support for the Quantum core API call""" device_params = {const.DEVICE_IP: ""} self._invoke_ucs_plugin(self._func_name(), args, device_params) + + def get_host(self, args): + """Provides the hostname on which a dynamic vnic is reserved""" + LOG.debug("get_host() called\n") + host_list = {const.HOST_LIST: {const.HOST_1: platform.node()}} + return host_list + + def get_instance_port(self, args): + """ + Get the portprofile name and the device namei for the dynamic vnic + """ + LOG.debug("get_instance_port() called\n") + vif_desc = {const.VIF_DESC: + {const.DEVICENAME: "eth2", const.UCSPROFILE: "default"}} + return vif_desc + diff --git a/quantum/plugins/cisco/segmentation/__init__.py b/quantum/plugins/cisco/segmentation/__init__.py new file mode 100644 index 00000000000..09b3fab8968 --- /dev/null +++ b/quantum/plugins/cisco/segmentation/__init__.py @@ -0,0 +1,20 @@ +""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# +""" diff --git a/quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py b/quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py new file mode 100644 index 00000000000..427ab690f69 --- /dev/null +++ b/quantum/plugins/cisco/segmentation/l2network_vlan_mgr.py @@ -0,0 +1,47 @@ +""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# +""" + +import logging as LOG + +from quantum.common import utils +from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.db import l2network_db as cdb +from quantum.plugins.cisco.l2network_segmentation_base \ + import L2NetworkSegmentationMgrBase + +LOG.basicConfig(level=LOG.WARN) +LOG.getLogger(const.LOGGER_COMPONENT_NAME) + +class L2NetworkVLANMgr(L2NetworkSegmentationMgrBase): + """ + VLAN Manager which gets VLAN ID from DB + """ + + 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]) + diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_configuration.py b/quantum/plugins/cisco/ucs/cisco_ucs_configuration.py index 8c334815425..a4fbec42b1b 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_configuration.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_configuration.py @@ -37,3 +37,10 @@ PROFILE_NAME_PREFIX = SECTION['profile_name_prefix'] SECTION = CP['DRIVER'] UCSM_DRIVER = SECTION['name'] + +CONF_FILE = "../conf/ucs_inventory.ini" + +CP = confp.CiscoConfigParser(os.path.dirname(os.path.realpath(__file__)) \ + + "/" + CONF_FILE) + +INVENTORY = CP.walk(CP.dummy) diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py b/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py new file mode 100644 index 00000000000..a272c0ae00e --- /dev/null +++ b/quantum/plugins/cisco/ucs/cisco_ucs_inventory.py @@ -0,0 +1,361 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2011 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# @author: Sumit Naiksatam, Cisco Systems, Inc. +# + +import logging as LOG + +from quantum.common import exceptions as exc +from quantum.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.ucs import cisco_ucs_configuration as conf +from quantum.plugins.cisco.ucs import cisco_ucs_network_driver + +LOG.basicConfig(level=LOG.WARN) +LOG.getLogger(const.LOGGER_COMPONENT_NAME) + +""" +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]} + } +""" + +class UCSInventory(object): + + _inventory = {} + _host_names = {} + _inventory_state = {} + + def __init__(self): + self._client = cisco_ucs_network_driver.CiscoUCSMDriver() + self._load_inventory() + + def _load_inventory(self): + """Load the inventory from a config file""" + inventory = conf.INVENTORY + 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 + + 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_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 self._inventory.keys() + + def reload_inventory(self): + """Reload the inventory from a conf file""" + self._load_inventory() + pass + + 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_blade_state(chassis_id, blade_id, + ucsm_ip, ucsm_username, + ucsm_password) + blades_dict[blade_id] = blade_data + + return True + + def get_least_reserved_blade(self): + """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 == 0: + 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} + return least_reserved_blade_dict + + 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.getUsername(ucsm_ip) + ucsm_password = cred.Store.getPassword(ucsm_ip) + """ + We are first getting the updated 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] + + """ + We will now copy the older blade interface reservation state + """ + for blade_intf in blade_intf_data.keys(): + blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \ + old_blade_intf_data[blade_intf]\ + [const.BLADE_INTF_RESERVATION] + + blade_data[const.BLADE_UNRESERVED_INTF_COUNT] = \ + blade_data_dict[const.BLADE_UNRESERVED_INTF_COUNT] + """ + Now we will reserve an interface if its available + """ + for blade_intf in blade_intf_data.keys(): + if blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] == \ + const.BLADE_INTF_UNRESERVED: + blade_intf_data[blade_intf][const.BLADE_INTF_RESERVATION] = \ + const.BLADE_INTF_RESERVED + blade_intf_data[blade_intf][const.TENANTID] = tenant_id + blade_intf_data[blade_intf][const.PORTID] = port_id + blade_intf_data[blade_intf][const.PROFILEID] = portprofile_name + blade_intf_data[blade_intf][const.INSTANCE_ID] = None + dev_eth_name = blade_intf_data[blade_intf] \ + [const.BLADE_INTF_RHEL_DEVICE_NAME] + """ + We are replacing the older blade interface state with new + """ + self._inventory_state[ucsm_ip][chassis_id][blade_id] \ + [const.BLADE_INTF_DATA] = blade_intf_data + self._inventory_state[ucsm_ip][chassis_id][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} + LOG.debug("Reserved blade interface: %s\n" % reserved_nic_dict) + return reserved_nic_dict + + 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.getUsername(ucsm_ip) + ucsm_password = cred.Store.getPassword(ucsm_ip) + self._inventory_state[ucsm_ip][chassis_id][blade_id]\ + [const.BLADE_INTF_DATA] \ + [interface_dn][const.BLADE_INTF_RESERVATION] = \ + const.BLADE_INTF_UNRESERVED + self._inventory_state[ucsm_ip][chassis_id][blade_id] \ + [const.BLADE_UNRESERVED_INTF_COUNT] += 1 + 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 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 + return None + + def get_host_name(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(): + 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.INSTANCE_ID] == None: + blade_intf_data[blade_intf]\ + [const.INSTANCE_ID] = instance_id + host_name = self._get_host_name(ucsm_ip, + chassis_id, + blade_id) + return host_name + return None + + def get_instance_port(self, tenant_id, instance_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(): + 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 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.INSTANCE_ID] == instance_id: + return blade_intf_data[blade_intf]\ + [const.BLADE_INTF_RHEL_DEVICE_NAME] + return None + + def add_blade(self, ucsm_ip, chassis_id, blade_id): + """Add a blade to the inventory""" + pass + + +def main(): + #client = UCSInventory() + #client.build_state() + ucsinv = UCSInventory() + reserved_nics = [] + ucsinv.build_inventory_state() + while True: + reserved_blade_dict = ucsinv.get_least_reserved_blade() + if not reserved_blade_dict: + print "No more unreserved blades\n" + break + + least_reserved_blade_ucsm = reserved_blade_dict[const.LEAST_RSVD_BLADE_UCSM] + least_reserved_blade_chassis = \ + reserved_blade_dict[const.LEAST_RSVD_BLADE_CHASSIS] + least_reserved_blade_id = \ + reserved_blade_dict[const.LEAST_RSVD_BLADE_ID] + least_reserved_blade_data = \ + reserved_blade_dict[const.LEAST_RSVD_BLADE_DATA] + reserved_nic_dict = \ + ucsinv.reserve_blade_interface(least_reserved_blade_ucsm, + least_reserved_blade_chassis, + least_reserved_blade_id, + least_reserved_blade_data, + "demo") + if reserved_nic_dict: + reserved_intf_nic_info = {const.RESERVED_INTERFACE_UCSM: + least_reserved_blade_ucsm, + const.RESERVED_INTERFACE_CHASSIS: + least_reserved_blade_chassis, + const.RESERVED_INTERFACE_BLADE: + least_reserved_blade_id, + const.RESERVED_INTERFACE_DN: + reserved_nic_dict[const.BLADE_INTF_DN]} + reserved_nics.append(reserved_intf_nic_info) + #break + + for rnic in reserved_nics: + ucsinv.unreserve_blade_interface( + rnic[const.RESERVED_INTERFACE_UCSM], + rnic[const.RESERVED_INTERFACE_CHASSIS], + rnic[const.RESERVED_INTERFACE_BLADE], + rnic[const.RESERVED_INTERFACE_DN]) + + +if __name__ == '__main__': + main() diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py b/quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py index faba86fcbda..dfb7839f2e8 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_network_driver.py @@ -32,7 +32,8 @@ from quantum.plugins.cisco.common import cisco_exceptions as cexc from quantum.plugins.cisco.ucs import cisco_getvif as gvif -LOG.basicConfig(level=LOG.WARN) +LOG.basicConfig(level=LOG.DEBUG) +#LOG.basicConfig(level=LOG.WARN) LOG.getLogger(const.LOGGER_COMPONENT_NAME) COOKIE_VALUE = "cookie_placeholder" @@ -41,6 +42,9 @@ PROFILE_CLIENT = "profileclient_placeholder" VLAN_NAME = "vlanname_placeholder" VLAN_ID = "vlanid_placeholder" OLD_VLAN_NAME = "old_vlanname_placeholder" +BLADE_VALUE = "blade_number_placeholder" +BLADE_DN_VALUE = "blade_dn_placeholder" +CHASSIS_VALUE = "chassis_number_placeholder" DYNAMIC_NIC_PREFIX = "eth" # The following are standard strings, messages used to communicate with UCSM, @@ -112,6 +116,27 @@ DELETE_PROFILE = " " \ " " +GET_BLADE_INTERFACE_STATE = " " + \ + " " + +GET_BLADE_INTERFACE = "" + \ + " " + \ + " " + +# TODO (Sumit): Assumes "adaptor-1", check if this has to be discovered too +GET_BLADE_INTERFACES = " " + \ + " " + class CiscoUCSMDriver(): """UCSM Driver""" @@ -130,6 +155,7 @@ class CiscoUCSMDriver(): LOG.debug(response.status) LOG.debug(response.reason) LOG.debug(response_data) + #print("***Sumit: response %s") % response_data # TODO (Sumit): If login is not successful, throw exception xml_tree = et.XML(response_data) cookie = xml_tree.attrib["outCookie"] @@ -142,6 +168,8 @@ class CiscoUCSMDriver(): LOG.debug(response.status) LOG.debug(response.reason) LOG.debug("UCSM Response: %s" % response_data) + #print("***Sumit: response %s") % response_data + post_data_response = response_data logout_data = "" conn.request(METHOD, URL, logout_data, HEADERS) @@ -150,6 +178,8 @@ class CiscoUCSMDriver(): LOG.debug(response.status) LOG.debug(response.reason) LOG.debug(response_data) + #print("***Sumit: response %s") % response_data + return post_data_response def _create_vlan_post_data(self, vlan_name, vlan_id): """Create command""" @@ -196,6 +226,81 @@ class CiscoUCSMDriver(): else: raise cexc.NoMoreNics() + def _get_blade_interfaces_post_data(self, chassis_number, blade_number): + data = GET_BLADE_INTERFACES.replace(CHASSIS_VALUE, chassis_number) + data = data.replace(BLADE_VALUE, blade_number) + return data + + def _get_blade_interface_state_post_data(self, blade_dn): + data = GET_BLADE_INTERFACE_STATE.replace(BLADE_DN_VALUE, blade_dn) + return data + + def _get_blade_interfaces(self, chassis_number, blade_number, ucsm_ip, + ucsm_username, ucsm_password): + data = self._get_blade_interfaces_post_data(chassis_number, + blade_number) + response = self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) + """ + print("***Sumit: ucsmp_ip %s ucsm_username %s ucsm_password %s data %s \ + response %s") % (ucsm_ip, ucsm_username, ucsm_password, data, + response) + """ + elements = \ + et.XML(response).find("outConfigs").findall("adaptorHostEthIf") + bladeInterfaces = {} + for element in elements: + dn = element.get("dn", default=None) + if dn: + order = element.get("order", default=None) + bladeInterface = {const.BLADE_INTF_DN: dn, + const.BLADE_INTF_ORDER: order, + const.BLADE_INTF_LINK_STATE: None, + const.BLADE_INTF_OPER_STATE: None, + const.BLADE_INTF_INST_TYPE: None, + const.BLADE_INTF_RHEL_DEVICE_NAME: + self._get_rhel_device_name(order)} + bladeInterfaces[dn] = bladeInterface + + return bladeInterfaces + + def _get_blade_interface_state(self, bladeIntf, ucsm_ip, + ucsm_username, ucsm_password): + data = \ + self._get_blade_interface_state_post_data(bladeIntf[const.BLADE_INTF_DN]) + response = self._post_data(ucsm_ip, ucsm_username, ucsm_password, data) + elements = \ + et.XML(response).find("outConfigs").findall("dcxVIf") + for element in elements: + bladeIntf[const.BLADE_INTF_LINK_STATE] = element.get("linkState", + default=None) + bladeIntf[const.BLADE_INTF_OPER_STATE] = element.get("operState", + default=None) + bladeIntf[const.BLADE_INTF_INST_TYPE] = element.get("instType", + default=None) + + def _get_rhel_device_name(self, order): + deviceName = const.RHEL_DEVICE_NAME_REPFIX + str(int(order) - 1) + return deviceName + + def _get_dynamic_interface(self, chassis_number, blade_number, + ucsm_ip,ucsm_username, + ucsm_password): + bladeInterfaces = client._get_blade_interfaces(chassis_number, + blade_number, + ucsm_ip, + ucsm_username, + ucsm_password) + for bladeIntf in bladeInterfaces.values(): + client._get_blade_interface_state(bladeIntf, ucsm_ip, + ucsm_username, ucsm_password) + if bladeIntf[const.BLADE_INTF_INST_TYPE] == \ + const.BLADE_INTF_DYNAMIC and \ + bladeIntf[const.BLADE_INTF_LINK_STATE] == \ + const.BLADE_INTF_STATE_UNKNOWN and \ + bladeIntf[const.BLADE_INTF_OPER_STATE] == \ + const.BLADE_INTF_STATE_UNKNOWN: + return bladeIntf + def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username, ucsm_password): """Create request for UCSM""" @@ -234,6 +339,26 @@ class CiscoUCSMDriver(): LOG.debug("Reserving dynamic nic %s" % dynamic_nic_name) return dynamic_nic_name + def get_blade_data(self, chassis_number, blade_number, + ucsm_ip,ucsm_username, + ucsm_password): + """ + Returns only the dynamic interfaces on the blade + """ + bladeInterfaces = self._get_blade_interfaces(chassis_number, + blade_number, + ucsm_ip, + ucsm_username, + ucsm_password) + for bladeIntf in bladeInterfaces.keys(): + self._get_blade_interface_state(bladeInterfaces[bladeIntf], ucsm_ip, + ucsm_username, ucsm_password) + if bladeInterfaces[bladeIntf][const.BLADE_INTF_INST_TYPE] != \ + const.BLADE_INTF_DYNAMIC: + bladeInterfaces.pop(bladeIntf) + + return bladeInterfaces + def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password): """Create request for UCSM""" data = self._delete_vlan_post_data(vlan_name) diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py b/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py index f6b7daf5ffb..d2f85fef740 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_plugin.py @@ -42,10 +42,6 @@ class UCSVICPlugin(L2DevicePluginBase): self._client = utils.import_object(conf.UCSM_DRIVER) LOG.debug("Loaded driver %s\n" % conf.UCSM_DRIVER) self._utils = cutil.DBUtils() - # TODO (Sumit) This is for now, when using only one chassis - self._ucsm_ip = conf.UCSM_IP_ADDRESS - self._ucsm_username = cred.Store.getUsername(conf.UCSM_IP_ADDRESS) - self._ucsm_password = cred.Store.getPassword(conf.UCSM_IP_ADDRESS) # TODO (Sumit) Make the counter per UCSM self._port_profile_counter = 0 @@ -56,6 +52,7 @@ class UCSVICPlugin(L2DevicePluginBase): the specified tenant. """ LOG.debug("UCSVICPlugin:get_all_networks() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) return self._networks.values() def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, @@ -65,6 +62,7 @@ class UCSVICPlugin(L2DevicePluginBase): a symbolic name. """ LOG.debug("UCSVICPlugin:create_network() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) self._client.create_vlan(vlan_name, str(vlan_id), self._ucsm_ip, self._ucsm_username, self._ucsm_password) new_net_dict = {const.NET_ID: net_id, @@ -81,6 +79,7 @@ class UCSVICPlugin(L2DevicePluginBase): belonging to the specified tenant. """ LOG.debug("UCSVICPlugin:delete_network() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) net = self._networks.get(net_id) # TODO (Sumit) : Verify that no attachments are plugged into the # network @@ -99,6 +98,7 @@ class UCSVICPlugin(L2DevicePluginBase): spec """ LOG.debug("UCSVICPlugin:get_network_details() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) network = self._get_network(tenant_id, net_id) return network @@ -108,6 +108,7 @@ class UCSVICPlugin(L2DevicePluginBase): Virtual Network. """ LOG.debug("UCSVICPlugin:rename_network() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) network = self._get_network(tenant_id, net_id) network[const.NET_NAME] = new_name return network @@ -118,6 +119,7 @@ class UCSVICPlugin(L2DevicePluginBase): specified Virtual Network. """ LOG.debug("UCSVICPlugin:get_all_ports() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) network = self._get_network(tenant_id, net_id) ports_on_net = network[const.NET_PORTS].values() return ports_on_net @@ -127,27 +129,28 @@ class UCSVICPlugin(L2DevicePluginBase): Creates a port on the specified Virtual Network. """ LOG.debug("UCSVICPlugin:create_port() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) + 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] net = self._get_network(tenant_id, net_id) ports = net[const.NET_PORTS] - # TODO (Sumit): This works on a single host deployment, - # in multi-host environment, dummy needs to be replaced with the - # hostname - dynamic_nic_name = self._client.get_dynamic_nic("dummy") new_port_profile = self._create_port_profile(tenant_id, net_id, port_id, conf.DEFAULT_VLAN_NAME, conf.DEFAULT_VLAN_ID) profile_name = new_port_profile[const.PROFILE_NAME] - sql_query = "INSERT INTO ports (port_id, profile_name, dynamic_vnic," \ - "host, instance_name, instance_nic_name, used) VALUES" \ - "('%s', '%s', '%s', 'dummy', NULL, NULL, 0)" % \ - (port_id, profile_name, dynamic_nic_name) - self._utils.execute_db_query(sql_query) new_port_dict = {const.PORT_ID: port_id, const.PORT_STATE: const.PORT_UP, const.ATTACHMENT: None, const.PORT_PROFILE: new_port_profile} ports[port_id] = new_port_dict + ucs_inventory.reserve_blade_interface(self._ucsm_ip, chassis_id, + blade_id, blade_data_dict, + tenant_id, port_id, + portprofile_name) return new_port_dict def delete_port(self, tenant_id, net_id, port_id, **kwargs): @@ -158,6 +161,11 @@ class UCSVICPlugin(L2DevicePluginBase): 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.const.CHASSIS_ID] + blade_id = kwargs[const.const.BLADE_ID] + interface_dn = kwargs[const.BLADE_INTF_DN] port = self._get_port(tenant_id, net_id, port_id) if port[const.ATTACHMENT]: raise exc.PortInUse(net_id=net_id, port_id=port_id, @@ -169,11 +177,10 @@ class UCSVICPlugin(L2DevicePluginBase): port_profile = port[const.PORT_PROFILE] self._delete_port_profile(port_id, port_profile[const.PROFILE_NAME]) - sql_query = "delete from ports where port_id = \"%s\"" % \ - (port[const.PORT_ID]) - self._utils.execute_db_query(sql_query) net = self._get_network(tenant_id, net_id) net[const.NET_PORTS].pop(port_id) + ucs_inventory.unreserve_blade_interface(ucsm_ip, chassis_id, + blade_id, interface_dn) except KeyError: raise exc.PortNotFound(net_id=net_id, port_id=port_id) @@ -182,6 +189,7 @@ class UCSVICPlugin(L2DevicePluginBase): 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]) port = self._get_port(tenant_id, net_id, port_id) self._validate_port_state(port_state) port[const.PORT_STATE] = port_state @@ -193,6 +201,7 @@ class UCSVICPlugin(L2DevicePluginBase): that is attached to this particular port. """ LOG.debug("UCSVICPlugin:get_port_details() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) return self._get_port(tenant_id, net_id, port_id) def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id, @@ -202,6 +211,7 @@ class UCSVICPlugin(L2DevicePluginBase): specified Virtual Network. """ LOG.debug("UCSVICPlugin:plug_interface() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) self._validate_attachment(tenant_id, net_id, port_id, remote_interface_id) port = self._get_port(tenant_id, net_id, port_id) @@ -227,6 +237,7 @@ class UCSVICPlugin(L2DevicePluginBase): specified Virtual Network. """ LOG.debug("UCSVICPlugin:unplug_interface() called\n") + self._set_ucsm(kwargs[const.DEVICE_IP]) port = self._get_port(tenant_id, net_id, port_id) port[const.ATTACHMENT] = None port_profile = port[const.PORT_PROFILE] @@ -308,3 +319,9 @@ class UCSVICPlugin(L2DevicePluginBase): self._client.delete_profile(profile_name, self._ucsm_ip, self._ucsm_username, self._ucsm_password) self._port_profile_counter -= 1 + + def _set_ucsm(self, ucsm_ip): + 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) +