9bab0960b6
This change condenses the directory structure to something more similar to what we had before while producing similar packages. It also introduces version.py which allows us to get the version from git tags (or a fallback version if not available). Fixes lp bug 889336 Fixes lp bug 888795 Change-Id: I86136bd9dbabb5eb1f8366ed665ed9b54f695124
326 lines
14 KiB
Python
326 lines
14 KiB
Python
"""
|
|
# 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.
|
|
#
|
|
"""
|
|
|
|
"""
|
|
Implements a UCSM XML API Client
|
|
"""
|
|
|
|
import httplib
|
|
import logging
|
|
from xml.etree import ElementTree as et
|
|
|
|
from quantum.plugins.cisco.common import cisco_exceptions as cexc
|
|
from quantum.plugins.cisco.common import cisco_constants as const
|
|
from quantum.plugins.cisco.ucs import cisco_getvif as gvif
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
COOKIE_VALUE = "cookie_placeholder"
|
|
PROFILE_NAME = "profilename_placeholder"
|
|
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,
|
|
#only place holder values change for each message
|
|
HEADERS = {"Content-Type": "text/xml"}
|
|
METHOD = "POST"
|
|
URL = "/nuova"
|
|
|
|
CREATE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"true\"> <inConfigs>" \
|
|
"<pair key=\"fabric/lan/net-" + VLAN_NAME + \
|
|
"\"> <fabricVlan defaultNet=\"no\" " \
|
|
"dn=\"fabric/lan/net-" + VLAN_NAME + \
|
|
"\" id=\"" + VLAN_ID + "\" name=\"" + \
|
|
VLAN_NAME + "\" status=\"created\">" \
|
|
"</fabricVlan> </pair> </inConfigs> </configConfMos>"
|
|
|
|
CREATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"true\"> <inConfigs>" \
|
|
"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
|
|
"\"> <vnicProfile descr=\"Profile created by " \
|
|
"Cisco OpenStack Quantum Plugin\" " \
|
|
"dn=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
|
|
"\" maxPorts=\"64\" name=\"" + PROFILE_NAME + \
|
|
"\" nwCtrlPolicyName=\"\" pinToGroupName=\"\" " \
|
|
"qosPolicyName=\"\" status=\"created\"> " \
|
|
"<vnicEtherIf defaultNet=\"yes\" name=\"" + VLAN_NAME + \
|
|
"\" rn=\"if-" + VLAN_NAME + "\" > </vnicEtherIf> " \
|
|
"</vnicProfile> </pair> </inConfigs> </configConfMos>"
|
|
|
|
ASSOCIATE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"true\"> <inConfigs> <pair " \
|
|
"key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
|
|
"/cl-" + PROFILE_CLIENT + "\"> <vmVnicProfCl dcName=\".*\" " \
|
|
"descr=\"\" dn=\"fabric/lan/profiles/vnic-" + \
|
|
PROFILE_NAME + "/cl-" + PROFILE_CLIENT + \
|
|
"\"name=\"" + PROFILE_CLIENT + "\" orgPath=\".*\" " \
|
|
"status=\"created\" swName=\"default$\"> </vmVnicProfCl>" \
|
|
"</pair> </inConfigs> </configConfMos>"
|
|
|
|
CHANGE_VLAN_IN_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"true\"> <inConfigs>" \
|
|
"<pair key=\"fabric/lan/profiles/vnic-" + \
|
|
PROFILE_NAME + "\"> <vnicProfile descr=\"Profile " \
|
|
"created by Cisco OpenStack Quantum Plugin\" " \
|
|
"dn=\"fabric/lan/profiles/vnic-" + \
|
|
PROFILE_NAME + "\" maxPorts=\"64\" name=\"" + \
|
|
PROFILE_NAME + "\" nwCtrlPolicyName=\"\" " \
|
|
"pinToGroupName=\"\" qosPolicyName=\"\" " \
|
|
"status=\"created,modified\">" \
|
|
"<vnicEtherIf rn=\"if-" + OLD_VLAN_NAME + \
|
|
"\" status=\"deleted\"> </vnicEtherIf> <vnicEtherIf " \
|
|
"defaultNet=\"yes\" name=\"" + \
|
|
VLAN_NAME + "\" rn=\"if-" + VLAN_NAME + \
|
|
"\" > </vnicEtherIf> </vnicProfile> </pair>" \
|
|
"</inConfigs> </configConfMos>"
|
|
|
|
DELETE_VLAN = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"true\"> <inConfigs>" \
|
|
"<pair key=\"fabric/lan/net-" + VLAN_NAME + \
|
|
"\"> <fabricVlan dn=\"fabric/lan/net-" + VLAN_NAME + \
|
|
"\" status=\"deleted\"> </fabricVlan> </pair> </inConfigs>" \
|
|
"</configConfMos>"
|
|
|
|
DELETE_PROFILE = "<configConfMos cookie=\"" + COOKIE_VALUE + \
|
|
"\" inHierarchical=\"false\"> <inConfigs>" \
|
|
"<pair key=\"fabric/lan/profiles/vnic-" + PROFILE_NAME + \
|
|
"\"> <vnicProfile dn=\"fabric/lan/profiles/vnic-" + \
|
|
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"""
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def _post_data(self, ucsm_ip, ucsm_username, ucsm_password, data):
|
|
"""Send command to UCSM in http request"""
|
|
conn = httplib.HTTPSConnection(ucsm_ip)
|
|
login_data = "<aaaLogin inName=\"" + ucsm_username + \
|
|
"\" inPassword=\"" + ucsm_password + "\" />"
|
|
conn.request(METHOD, URL, login_data, HEADERS)
|
|
response = conn.getresponse()
|
|
response_data = response.read()
|
|
#LOG.debug(response.status)
|
|
#LOG.debug(response.reason)
|
|
#LOG.debug(response_data)
|
|
# TODO (Sumit): If login is not successful, throw exception
|
|
xml_tree = et.XML(response_data)
|
|
cookie = xml_tree.attrib["outCookie"]
|
|
|
|
data = data.replace(COOKIE_VALUE, cookie)
|
|
#LOG.debug("POST: %s" % data)
|
|
conn.request(METHOD, URL, data, HEADERS)
|
|
response = conn.getresponse()
|
|
response_data = response.read()
|
|
#LOG.debug(response.status)
|
|
#LOG.debug(response.reason)
|
|
#LOG.debug("UCSM Response: %s" % response_data)
|
|
post_data_response = response_data
|
|
|
|
logout_data = "<aaaLogout inCookie=\"" + cookie + "\" />"
|
|
conn.request(METHOD, URL, logout_data, HEADERS)
|
|
response = conn.getresponse()
|
|
response_data = response.read()
|
|
#LOG.debug(response.status)
|
|
#LOG.debug(response.reason)
|
|
#LOG.debug(response_data)
|
|
return post_data_response
|
|
|
|
def _create_vlan_post_data(self, vlan_name, vlan_id):
|
|
"""Create command"""
|
|
data = CREATE_VLAN.replace(VLAN_NAME, vlan_name)
|
|
data = data.replace(VLAN_ID, vlan_id)
|
|
return data
|
|
|
|
def _create_profile_post_data(self, profile_name, vlan_name):
|
|
"""Create command"""
|
|
data = CREATE_PROFILE.replace(PROFILE_NAME, profile_name)
|
|
data = data.replace(VLAN_NAME, vlan_name)
|
|
return data
|
|
|
|
def _create_pclient_post_data(self, profile_name,
|
|
profile_client_name):
|
|
"""Create command"""
|
|
data = ASSOCIATE_PROFILE.replace(PROFILE_NAME, profile_name)
|
|
data = data.replace(PROFILE_CLIENT, profile_client_name)
|
|
return data
|
|
|
|
def _change_vlaninprof_post_data(self, profile_name, old_vlan_name,
|
|
new_vlan_name):
|
|
"""Create command"""
|
|
data = CHANGE_VLAN_IN_PROFILE.replace(PROFILE_NAME, profile_name)
|
|
data = data.replace(OLD_VLAN_NAME, old_vlan_name)
|
|
data = data.replace(VLAN_NAME, new_vlan_name)
|
|
return data
|
|
|
|
def _delete_vlan_post_data(self, vlan_name):
|
|
"""Create command"""
|
|
data = DELETE_VLAN.replace(VLAN_NAME, vlan_name)
|
|
return data
|
|
|
|
def _delete_profile_post_data(self, profile_name):
|
|
"""Create command"""
|
|
data = DELETE_PROFILE.replace(PROFILE_NAME, profile_name)
|
|
return data
|
|
|
|
def _get_blade_interfaces_post_data(self, chassis_number, blade_number):
|
|
"""Create command"""
|
|
data = GET_BLADE_INTERFACES.replace(CHASSIS_VALUE, chassis_number)
|
|
data = data.replace(BLADE_VALUE, blade_number)
|
|
return data
|
|
|
|
def _get_blade_intf_st_post_data(self, blade_dn):
|
|
"""Create command"""
|
|
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):
|
|
"""Create command"""
|
|
data = self._get_blade_interfaces_post_data(chassis_number,
|
|
blade_number)
|
|
response = self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
elements = \
|
|
et.XML(response).find("outConfigs").findall("adaptorHostEthIf")
|
|
blade_interfaces = {}
|
|
for element in elements:
|
|
dist_name = element.get("dn", default=None)
|
|
if dist_name:
|
|
order = element.get("order", default=None)
|
|
blade_interface = {const.BLADE_INTF_DN: dist_name,
|
|
const.BLADE_INTF_ORDER: order,
|
|
const.BLADE_INTF_LINK_STATE: None,
|
|
const.BLADE_INTF_OPER_STATE: None,
|
|
const.BLADE_INTF_INST_TYPE: None,
|
|
const.BLADE_INTF_RHEL_DEVICE_NAME:
|
|
self._get_rhel_device_name(order)}
|
|
blade_interfaces[dist_name] = blade_interface
|
|
|
|
return blade_interfaces
|
|
|
|
def _get_blade_interface_state(self, blade_intf, ucsm_ip,
|
|
ucsm_username, ucsm_password):
|
|
"""Create command"""
|
|
data = \
|
|
self._get_blade_intf_st_post_data(blade_intf[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:
|
|
blade_intf[const.BLADE_INTF_LINK_STATE] = element.get("linkState",
|
|
default=None)
|
|
blade_intf[const.BLADE_INTF_OPER_STATE] = element.get("operState",
|
|
default=None)
|
|
blade_intf[const.BLADE_INTF_INST_TYPE] = element.get("instType",
|
|
default=None)
|
|
|
|
def _get_rhel_device_name(self, order):
|
|
"""Get the device name as on the RHEL host"""
|
|
device_name = const.RHEL_DEVICE_NAME_REPFIX + str(int(order) - 1)
|
|
return device_name
|
|
|
|
def create_vlan(self, vlan_name, vlan_id, ucsm_ip, ucsm_username,
|
|
ucsm_password):
|
|
"""Create request for UCSM"""
|
|
data = self._create_vlan_post_data(vlan_name, vlan_id)
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
|
|
def create_profile(self, profile_name, vlan_name, ucsm_ip, ucsm_username,
|
|
ucsm_password):
|
|
"""Create request for UCSM"""
|
|
data = self._create_profile_post_data(profile_name, vlan_name)
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
data = self._create_pclient_post_data(profile_name,
|
|
profile_name[-16:])
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
|
|
def change_vlan_in_profile(self, profile_name, old_vlan_name,
|
|
new_vlan_name, ucsm_ip, ucsm_username,
|
|
ucsm_password):
|
|
"""Create request for UCSM"""
|
|
data = self._change_vlaninprof_post_data(profile_name,
|
|
old_vlan_name,
|
|
new_vlan_name)
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
|
|
def get_blade_data(self, chassis_number, blade_number,
|
|
ucsm_ip, ucsm_username,
|
|
ucsm_password):
|
|
"""
|
|
Returns only the dynamic interfaces on the blade
|
|
"""
|
|
blade_interfaces = self._get_blade_interfaces(chassis_number,
|
|
blade_number,
|
|
ucsm_ip,
|
|
ucsm_username,
|
|
ucsm_password)
|
|
for blade_intf in blade_interfaces.keys():
|
|
self._get_blade_interface_state(blade_interfaces[blade_intf],
|
|
ucsm_ip, ucsm_username,
|
|
ucsm_password)
|
|
if blade_interfaces[blade_intf][const.BLADE_INTF_INST_TYPE] != \
|
|
const.BLADE_INTF_DYNAMIC:
|
|
blade_interfaces.pop(blade_intf)
|
|
|
|
return blade_interfaces
|
|
|
|
def delete_vlan(self, vlan_name, ucsm_ip, ucsm_username, ucsm_password):
|
|
"""Create request for UCSM"""
|
|
data = self._delete_vlan_post_data(vlan_name)
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|
|
|
|
def delete_profile(self, profile_name, ucsm_ip, ucsm_username,
|
|
ucsm_password):
|
|
"""Create request for UCSM"""
|
|
data = self._delete_profile_post_data(profile_name)
|
|
self._post_data(ucsm_ip, ucsm_username, ucsm_password, data)
|