Initial commit with lots of changes.

This commit is contained in:
Sumit Naiksatam 2011-08-19 20:07:28 -07:00
parent 629b9bae28
commit d047001964
14 changed files with 928 additions and 36 deletions

View File

@ -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

View File

@ -1,6 +1,6 @@
[VLANS]
vlan_start=<put_vlan_id_range_start_here>
vlan_end=<put_vlan_id_range_end_here>
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

View File

@ -0,0 +1,24 @@
[ucsm-1]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-3]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[ucsm-2]
ip_address = <put_ucsm_ip_address_here>
[[chassis-1]]
chassis_id = <put_the_chassis_id_here>
[[[blade-1]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>
[[[blade-2]]]
blade_id = <put_blade_id_here>
host_name = <put_hostname_here>

View File

@ -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"""

View File

@ -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

View File

@ -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.
#
"""

View File

@ -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

View File

@ -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

View File

@ -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.
#
"""

View File

@ -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])

View File

@ -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)

View File

@ -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()

View File

@ -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 = "<configConfMos cookie=\"" + COOKIE_VALUE + \
PROFILE_NAME + "\" status=\"deleted\"> </vnicProfile>" \
"</pair> </inConfigs> </configConfMos>"
GET_BLADE_INTERFACE_STATE = "<configScope cookie=\"" + COOKIE_VALUE + \
"\" dn=\"" + BLADE_DN_VALUE + "\" inClass=\"dcxVIf\" " + \
"inHierarchical=\"false\" inRecursive=\"false\"> " + \
"<inFilter> </inFilter> </configScope>"
GET_BLADE_INTERFACE = "<configResolveClass cookie=\"" + COOKIE_VALUE + \
"\" classId=\"vnicEther\"" + \
" inHierarchical=\"false\">" + \
" <inFilter> <eq class=\"vnicEther\" property=\"equipmentDn\"" + \
" value=\"sys/chassis-" + CHASSIS_VALUE +"/blade-" + \
BLADE_VALUE + "/adaptor-1/host-eth-?\"/> " + \
"</inFilter> </configResolveClass>"
# TODO (Sumit): Assumes "adaptor-1", check if this has to be discovered too
GET_BLADE_INTERFACES = "<configResolveChildren cookie=\"" + \
COOKIE_VALUE + "\" inDn=\"sys/chassis-" + \
CHASSIS_VALUE + "/blade-" + BLADE_VALUE + \
"/adaptor-1\"" + \
" inHierarchical=\"false\"> <inFilter> </inFilter>" + \
" </configResolveChildren>"
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 = "<aaaLogout inCookie=\"" + cookie + "\" />"
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)

View File

@ -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)