Cisco UCS Manager vendor code for ML2 mechanism driver.
Change-Id: I23e9db27adc6efda415ef3f3d3046a988fbf7e53
This commit is contained in:
@@ -116,3 +116,22 @@
|
||||
# encap=vlan-100
|
||||
# cidr_exposed=10.10.40.2/16
|
||||
# gateway_ip=10.10.40.1
|
||||
|
||||
[ml2_cisco_ucsm]
|
||||
|
||||
# Cisco UCS Manager IP address
|
||||
# ucsm_ip=1.1.1.1
|
||||
|
||||
# Username to connect to UCS Manager
|
||||
# ucsm_username=user
|
||||
|
||||
# Password to connect to UCS Manager
|
||||
# ucsm_password=password
|
||||
|
||||
# SR-IOV and VM-FEX vendors supported by this plugin
|
||||
# xxxx:yyyy represents vendor_id:product_id
|
||||
# supported_pci_devs=['2222:3333', '4444:5555']
|
||||
|
||||
# Hostname to Service profile mapping for UCS Manager
|
||||
# controlled compute hosts
|
||||
# ucsm_host_list=Hostname1:Serviceprofile1, Hostname2:Serviceprofile2
|
||||
|
||||
76
networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py
Normal file
76
networking_cisco/plugins/ml2/drivers/cisco/ucsm/config.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import constants as const
|
||||
|
||||
""" Cisco UCS Manager ML2 Mechanism driver specific configuration.
|
||||
|
||||
Following are user configurable options for UCS Manager ML2 Mechanism
|
||||
driver. The ucsm_username, ucsm_password, and ucsm_ip are
|
||||
required options. Additional configuration knobs are provided to pre-
|
||||
create UCS Manager port profiles.
|
||||
"""
|
||||
|
||||
ml2_cisco_ucsm_opts = [
|
||||
cfg.StrOpt('ucsm_ip',
|
||||
help=_('Cisco UCS Manager IP address. This is a required field '
|
||||
'to communicate with a Cisco UCS Manager.')),
|
||||
cfg.StrOpt('ucsm_username',
|
||||
help=_('Username for UCS Manager. This is a required field '
|
||||
'to communicate with a Cisco UCS Manager.')),
|
||||
cfg.StrOpt('ucsm_password',
|
||||
secret=True, # do not expose value in the logs
|
||||
help=_('Password for UCS Manager. This is a required field '
|
||||
'to communicate with a Cisco UCS Manager.')),
|
||||
cfg.ListOpt('supported_pci_devs',
|
||||
default=[const.PCI_INFO_CISCO_VIC_1240,
|
||||
const.PCI_INFO_INTEL_82599],
|
||||
help=_('List of comma separated vendor_id:product_id of '
|
||||
'SR_IOV capable devices supported by this MD. This MD '
|
||||
'supports both VM-FEX and SR-IOV devices.')),
|
||||
cfg.ListOpt('ucsm_host_list',
|
||||
help=_('List of comma separated Host:Service Profile tuples '
|
||||
'providing the Service Profile associated with each '
|
||||
'Host to be supported by this MD.')),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(ml2_cisco_ucsm_opts, "ml2_cisco_ucsm")
|
||||
|
||||
|
||||
def parse_pci_vendor_config():
|
||||
vendor_list = []
|
||||
vendor_config_list = cfg.CONF.ml2_cisco_ucsm.supported_pci_devs
|
||||
for vendor in vendor_config_list:
|
||||
vendor_product = vendor.split(':')
|
||||
if len(vendor_product) != 2:
|
||||
raise cfg.Error(_("UCS Mech Driver: Invalid PCI device "
|
||||
"config: %s") % vendor)
|
||||
vendor_list.append(vendor)
|
||||
return vendor_list
|
||||
|
||||
|
||||
def parse_ucsm_host_config():
|
||||
host_dict = {}
|
||||
host_config_list = cfg.CONF.ml2_cisco_ucsm.ucsm_host_list
|
||||
for host in host_config_list:
|
||||
host_sp = host.split(':')
|
||||
if len(host_sp) != 2:
|
||||
raise cfg.Error(_("UCS Mech Driver: Invalid Host Service "
|
||||
"Profile config: %s") % host)
|
||||
key = host_sp[0]
|
||||
host_dict[key] = host_sp[1]
|
||||
return host_dict
|
||||
41
networking_cisco/plugins/ml2/drivers/cisco/ucsm/constants.py
Normal file
41
networking_cisco/plugins/ml2/drivers/cisco/ucsm/constants.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
# VIF_DETAILS attribute for port profile name used by the Nova VIF driver.
|
||||
VIF_DETAILS_PROFILEID = 'profileid'
|
||||
|
||||
# Supported network interface cards
|
||||
PCI_INFO_CISCO_VIC_1240 = "1137:0071"
|
||||
PCI_INFO_INTEL_82599 = "8086:10c9"
|
||||
|
||||
VLAN_PATH = "fabric/lan"
|
||||
VLAN_COMPRESSION_TYPE = "included"
|
||||
DESCR = "Created by Openstack UCSM Mech Driver"
|
||||
PORT_PROFILESETDN = "fabric/lan/profiles"
|
||||
HIGH_PERF = "high-perf-reqd"
|
||||
NONE = "none"
|
||||
|
||||
VNIC_PATH_PREFIX = "/vnic-"
|
||||
VLAN_PATH_PREFIX = "/if-"
|
||||
VLAN_PROFILE_PATH_PREFIX = "/net-"
|
||||
VLAN_PROFILE_NAME_PREFIX = "OS-"
|
||||
PORT_PROFILE_NAME_PREFIX = "OS-PP-"
|
||||
CLIENT_PROFILE_NAME_PREFIX = "OS-CL-"
|
||||
CLIENT_PROFILE_PATH_PREFIX = "/cl-"
|
||||
|
||||
SERVICE_PROFILE_PATH_PREFIX = "org-root/ls-"
|
||||
ETH0 = "/ether-eth0"
|
||||
ETH1 = "/ether-eth1"
|
||||
DUPLICATE_EXCEPTION = "object already exists"
|
||||
@@ -0,0 +1,33 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
"""Exceptions used by Cisco UCSM ML2 mechanism driver."""
|
||||
|
||||
from neutron.common import exceptions
|
||||
|
||||
|
||||
class UcsmConnectFailed(exceptions.NeutronException):
|
||||
message = _("Unable to connect to UCS Manager %(ucsm_ip)s. "
|
||||
"Reason: %(exc)s.")
|
||||
|
||||
|
||||
class UcsmConfigReadFailed(exceptions.NeutronException):
|
||||
message = _("Unable to read config from UCS Manager %(ucsm_ip)s. "
|
||||
"Reason: %(exc)s.")
|
||||
|
||||
|
||||
class UcsmConfigFailed(exceptions.NeutronException):
|
||||
message = _("Failed to configure %(config)s on UCS Manager %(ucsm_ip)s. "
|
||||
"Reason: %(exc)s.")
|
||||
70
networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_db.py
Normal file
70
networking_cisco/plugins/ml2/drivers/cisco/ucsm/ucsm_db.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from sqlalchemy import orm
|
||||
|
||||
from neutron.db import api as db_api
|
||||
from neutron.plugins.ml2.drivers.cisco.ucsm import ucsm_model
|
||||
|
||||
|
||||
class UcsmDbModel(object):
|
||||
def __init__(self):
|
||||
self.session = db_api.get_session()
|
||||
|
||||
def is_port_profile_created(self, vlan_id):
|
||||
"""Indicates if port profile has been created on UCS Manager."""
|
||||
entry = self.session.query(ucsm_model.PortProfile).filter_by(
|
||||
vlan_id=vlan_id).first()
|
||||
return entry and entry.created_on_ucs
|
||||
|
||||
def get_port_profile_for_vlan(self, vlan_id):
|
||||
"""Returns Vlan id associated with the port profile."""
|
||||
entry = self.session.query(ucsm_model.PortProfile).filter_by(
|
||||
vlan_id=vlan_id).first()
|
||||
return entry.profile_id if entry else None
|
||||
|
||||
def add_port_profile(self, profile_name, vlan_id):
|
||||
"""Adds a port profile and its vlan_id to the table."""
|
||||
if not self.get_port_profile_for_vlan(vlan_id):
|
||||
port_profile = ucsm_model.PortProfile(profile_id=profile_name,
|
||||
vlan_id=vlan_id,
|
||||
created_on_ucs=False)
|
||||
with self.session.begin(subtransactions=True):
|
||||
self.session.add(port_profile)
|
||||
return port_profile
|
||||
|
||||
def set_port_profile_created(self, vlan_id, profile_name):
|
||||
"""Sets created_on_ucs flag to True."""
|
||||
with self.session.begin(subtransactions=True):
|
||||
port_profile = self.session.query(
|
||||
ucsm_model.PortProfile).filter_by(
|
||||
vlan_id=vlan_id, profile_id=profile_name).first()
|
||||
if port_profile:
|
||||
port_profile.created_on_ucs = True
|
||||
self.session.merge(port_profile)
|
||||
else:
|
||||
new_profile = ucsm_model.PortProfile(profile_id=profile_name,
|
||||
vlan_id=vlan_id,
|
||||
created_on_ucs=True)
|
||||
self.session.add(new_profile)
|
||||
|
||||
def delete_vlan_entry(self, vlan_id):
|
||||
"""Deletes entry for a vlan_id if it exists."""
|
||||
with self.session.begin(subtransactions=True):
|
||||
try:
|
||||
self.session.query(ucsm_model.PortProfile).filter_by(
|
||||
vlan_id=vlan_id).delete()
|
||||
except orm.exc.NoResultFound:
|
||||
return
|
||||
@@ -0,0 +1,548 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
from neutron.i18n import _LE, _LI
|
||||
from neutron.extensions import portbindings
|
||||
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import config as config
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import constants as const
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import exceptions as cexc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CiscoUcsmDriver(object):
|
||||
|
||||
"""UCS Manager Driver Main Class."""
|
||||
|
||||
def __init__(self):
|
||||
LOG.debug("UCS Manager Network driver found")
|
||||
self.ucsmsdk = None
|
||||
self.ucsm_ip = cfg.CONF.ml2_cisco_ucsm.ucsm_ip
|
||||
self.username = cfg.CONF.ml2_cisco_ucsm.ucsm_username
|
||||
self.password = cfg.CONF.ml2_cisco_ucsm.ucsm_password
|
||||
LOG.debug("UCS Manager Network driver Ip: %s", self.ucsm_ip)
|
||||
self.handles = {}
|
||||
|
||||
self.supported_sriov_vnic_types = [portbindings.VNIC_DIRECT,
|
||||
portbindings.VNIC_MACVTAP]
|
||||
self.supported_pci_devs = config.parse_pci_vendor_config()
|
||||
self.ucsm_host_dict = config.parse_ucsm_host_config()
|
||||
|
||||
def _validate_config(self):
|
||||
if not cfg.CONF.ml2_cisco_ucsm.get('ucsm_ip'):
|
||||
msg = _('UCS Manager IP address is not provided')
|
||||
LOG.error(msg)
|
||||
if not cfg.CONF.ml2_cisco_ucsm.get('ucsm_username'):
|
||||
msg = _('UCS Manager username is not provided')
|
||||
LOG.error(msg)
|
||||
if not cfg.CONF.ml2_cisco_ucsm.get('ucsm_password'):
|
||||
msg = _('UCS Manager password is not provided')
|
||||
LOG.error(msg)
|
||||
|
||||
def check_vnic_type_and_vendor_info(self, vnic_type, profile):
|
||||
"""Checks if this vnic_type and vendor device info are supported.
|
||||
|
||||
Returns True if:
|
||||
1. the port vnic_type is direct or macvtap and
|
||||
2. the vendor_id and product_id of the port is supported by
|
||||
this MD
|
||||
Useful in determining if this MD should bind the current
|
||||
port.
|
||||
"""
|
||||
# Check for vnic_type
|
||||
if vnic_type not in self.supported_sriov_vnic_types:
|
||||
LOG.info(_LI('Non SR-IOV vnic_type: %s.'), vnic_type)
|
||||
return False
|
||||
|
||||
if not profile:
|
||||
LOG.debug("Port binding missing profile info")
|
||||
return False
|
||||
|
||||
# Check for vendor_info
|
||||
return self._check_for_supported_vendor(profile)
|
||||
|
||||
def _check_for_supported_vendor(self, profile):
|
||||
"""Checks if the port belongs to a supported vendor.
|
||||
|
||||
Returns True for supported_pci_devs.
|
||||
"""
|
||||
vendor_info = profile.get('pci_vendor_info')
|
||||
if not vendor_info:
|
||||
LOG.debug("Port binding missing pci vendor info")
|
||||
return False
|
||||
if vendor_info not in self.supported_pci_devs:
|
||||
LOG.debug("Unsupported vendor and product type %s",
|
||||
str(vendor_info))
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_vmfex_port(self, profile):
|
||||
"""Checks if the port is a VMFEX port.
|
||||
|
||||
Returns True only for port that support VM-FEX.
|
||||
It is important to distinguish between the two since Port Profiles
|
||||
on the UCS Manager are created only for the VM-FEX ports.
|
||||
"""
|
||||
vendor_info = profile.get('pci_vendor_info')
|
||||
|
||||
return vendor_info == const.PCI_INFO_CISCO_VIC_1240
|
||||
|
||||
def _import_ucsmsdk(self):
|
||||
"""Imports the Ucsm SDK module.
|
||||
|
||||
This module is not installed as part of the normal Neutron
|
||||
distributions. It is imported dynamically in this module so that
|
||||
the import can be mocked, allowing unit testing without requiring
|
||||
the installation of UcsSdk.
|
||||
|
||||
"""
|
||||
return importutils.import_module('UcsSdk')
|
||||
|
||||
def ucs_manager_connect(self):
|
||||
"""Connects to a UCS Manager."""
|
||||
self._validate_config()
|
||||
|
||||
if not self.ucsmsdk:
|
||||
self.ucsmsdk = self._import_ucsmsdk()
|
||||
|
||||
handle = self.ucsmsdk.UcsHandle()
|
||||
try:
|
||||
handle.Login(self.ucsm_ip, self.username, self.password)
|
||||
self.handles[self.ucsm_ip] = handle
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConnectFailed(ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
return self.handles[self.ucsm_ip]
|
||||
|
||||
def _get_all_portprofiles(self, handle):
|
||||
"""Gets all port profiles from a specific UCS Manager."""
|
||||
|
||||
# Get Managed Object VnicProfile
|
||||
try:
|
||||
port_profiles = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.VnicProfile.ClassId())
|
||||
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigReadFailed(ucsm_ip=self.ucsm_ip, exc=e)
|
||||
return port_profiles
|
||||
|
||||
def _create_vlanprofile(self, handle, vlan_id):
|
||||
"""Creates VLAN profile to be assosiated with the Port Profile."""
|
||||
vlan_name = self.make_vlan_name(vlan_id)
|
||||
vlan_profile_dest = (const.VLAN_PATH + const.VLAN_PROFILE_PATH_PREFIX +
|
||||
vlan_name)
|
||||
LOG.debug("Creating Vlan Profile: %s", vlan_name)
|
||||
|
||||
try:
|
||||
vp1 = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.FabricLanCloud.ClassId(),
|
||||
{self.ucsmsdk.FabricLanCloud.DN: const.VLAN_PATH})
|
||||
if not vp1:
|
||||
LOG.debug("UCS Manager network driver Vlan Profile "
|
||||
"path at %s missing", const.VLAN_PATH)
|
||||
return False
|
||||
|
||||
#Create a vlan profile with the given vlan_id
|
||||
vp2 = handle.AddManagedObject(
|
||||
vp1,
|
||||
self.ucsmsdk.FabricVlan.ClassId(),
|
||||
{self.ucsmsdk.FabricVlan.COMPRESSION_TYPE:
|
||||
const.VLAN_COMPRESSION_TYPE,
|
||||
self.ucsmsdk.FabricVlan.DN: vlan_profile_dest,
|
||||
self.ucsmsdk.FabricVlan.SHARING: const.NONE,
|
||||
self.ucsmsdk.FabricVlan.PUB_NW_NAME: "",
|
||||
self.ucsmsdk.FabricVlan.ID: str(vlan_id),
|
||||
self.ucsmsdk.FabricVlan.MCAST_POLICY_NAME: "",
|
||||
self.ucsmsdk.FabricVlan.NAME: vlan_name,
|
||||
self.ucsmsdk.FabricVlan.DEFAULT_NET: "no"})
|
||||
if not vp2:
|
||||
LOG.debug("UCS Manager network driver could not create Vlan "
|
||||
"Profile %s", vlan_name)
|
||||
return False
|
||||
|
||||
LOG.debug("UCS Manager network driver created Vlan Profile %s "
|
||||
"at %s", vlan_name, vlan_profile_dest)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigFailed(config=vlan_name,
|
||||
ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
def _create_port_profile(self, handle, profile_name, vlan_id, vnic_type):
|
||||
"""Creates a Port Profile on the UCS Manager.
|
||||
|
||||
Significant parameters set in the port profile are:
|
||||
1. Port profile name - Should match what was set in vif_details
|
||||
2. High performance mode - For VM-FEX to be enabled/configured on
|
||||
the port using this port profile, this mode should be enabled.
|
||||
3. Vlan id - Vlan id used by traffic to and from the port.
|
||||
"""
|
||||
port_profile_dest = (const.PORT_PROFILESETDN + const.VNIC_PATH_PREFIX +
|
||||
profile_name)
|
||||
# Max ports that this port profile can be applied to
|
||||
max_ports = 64
|
||||
|
||||
vlan_name = self.make_vlan_name(vlan_id)
|
||||
vlan_associate_path = (const.PORT_PROFILESETDN +
|
||||
const.VNIC_PATH_PREFIX + profile_name +
|
||||
const.VLAN_PATH_PREFIX + vlan_name)
|
||||
cl_profile_name = const.CLIENT_PROFILE_NAME_PREFIX + str(vlan_id)
|
||||
cl_profile_dest = (const.PORT_PROFILESETDN + const.VNIC_PATH_PREFIX +
|
||||
profile_name + const.CLIENT_PROFILE_PATH_PREFIX +
|
||||
cl_profile_name)
|
||||
|
||||
# Check if direct or macvtap mode
|
||||
if vnic_type == portbindings.VNIC_DIRECT:
|
||||
port_mode = const.HIGH_PERF
|
||||
else:
|
||||
port_mode = const.NONE
|
||||
|
||||
try:
|
||||
port_profile = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.VnicProfileSet.ClassId(),
|
||||
{self.ucsmsdk.VnicProfileSet.DN: const.PORT_PROFILESETDN})
|
||||
|
||||
if not port_profile:
|
||||
LOG.debug("UCS Manager network driver Port Profile path at "
|
||||
"%s missing", const.PORT_PROFILESETDN)
|
||||
return False
|
||||
|
||||
# Create a port profile on the UCS Manager
|
||||
p_profile = handle.AddManagedObject(
|
||||
port_profile,
|
||||
self.ucsmsdk.VnicProfile.ClassId(),
|
||||
{self.ucsmsdk.VnicProfile.NAME: profile_name,
|
||||
self.ucsmsdk.VnicProfile.POLICY_OWNER: "local",
|
||||
self.ucsmsdk.VnicProfile.NW_CTRL_POLICY_NAME: "",
|
||||
self.ucsmsdk.VnicProfile.PIN_TO_GROUP_NAME: "",
|
||||
self.ucsmsdk.VnicProfile.DN: port_profile_dest,
|
||||
self.ucsmsdk.VnicProfile.DESCR: const.DESCR,
|
||||
self.ucsmsdk.VnicProfile.QOS_POLICY_NAME: "",
|
||||
self.ucsmsdk.VnicProfile.HOST_NW_IOPERF: port_mode,
|
||||
self.ucsmsdk.VnicProfile.MAX_PORTS: max_ports})
|
||||
if not p_profile:
|
||||
LOG.debug("UCS Manager network driver could not create Port "
|
||||
"Profile %s at %s", profile_name, port_profile_dest)
|
||||
return False
|
||||
|
||||
LOG.debug("UCS Manager network driver associating Vlan Profile "
|
||||
"with Port Profile at %s", vlan_associate_path)
|
||||
# Associate port profile with vlan profile
|
||||
mo = handle.AddManagedObject(
|
||||
p_profile,
|
||||
self.ucsmsdk.VnicEtherIf.ClassId(),
|
||||
{self.ucsmsdk.VnicEtherIf.DN: vlan_associate_path,
|
||||
self.ucsmsdk.VnicEtherIf.NAME: vlan_name,
|
||||
self.ucsmsdk.VnicEtherIf.DEFAULT_NET: "yes"}, True)
|
||||
if not mo:
|
||||
LOG.debug("UCS Manager network driver cannot associate Vlan "
|
||||
"Profile %s to Port Profile %s", vlan_name,
|
||||
profile_name)
|
||||
return False
|
||||
|
||||
LOG.debug("UCS Manager network driver created Port Profile %s "
|
||||
"at %s", profile_name, port_profile_dest)
|
||||
|
||||
cl_profile = handle.AddManagedObject(
|
||||
p_profile,
|
||||
self.ucsmsdk.VmVnicProfCl.ClassId(),
|
||||
{self.ucsmsdk.VmVnicProfCl.ORG_PATH: ".*",
|
||||
self.ucsmsdk.VmVnicProfCl.DN: cl_profile_dest,
|
||||
self.ucsmsdk.VmVnicProfCl.NAME: cl_profile_name,
|
||||
self.ucsmsdk.VmVnicProfCl.POLICY_OWNER: "local",
|
||||
self.ucsmsdk.VmVnicProfCl.SW_NAME: ".*",
|
||||
self.ucsmsdk.VmVnicProfCl.DC_NAME: ".*",
|
||||
self.ucsmsdk.VmVnicProfCl.DESCR: const.DESCR})
|
||||
if not cl_profile:
|
||||
LOG.debug("UCS Manager network driver could not create Client "
|
||||
"Profile %s at %s", cl_profile_name, cl_profile_dest)
|
||||
return False
|
||||
|
||||
LOG.debug("UCS Manager network driver created Client Profile %s "
|
||||
"at %s", cl_profile_name, cl_profile_dest)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigFailed(config=profile_name,
|
||||
ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
def create_portprofile(self, profile_name, vlan_id, vnic_type):
|
||||
"""Top level method to create Port Profiles on the UCS Manager.
|
||||
|
||||
Calls all the methods responsible for the individual tasks that
|
||||
ultimately result in the creation of the Port Profile on the UCS
|
||||
Manager.
|
||||
"""
|
||||
# Connect to UCS Manager
|
||||
handle = self.ucs_manager_connect()
|
||||
if not handle:
|
||||
LOG.error(_LE('UCS Manager network driver failed to connect '
|
||||
'to UCS Manager.'))
|
||||
return False
|
||||
|
||||
try:
|
||||
handle.StartTransaction()
|
||||
# Create Vlan Profile
|
||||
if not self._create_vlanprofile(handle, vlan_id):
|
||||
LOG.error(_LE('UCS Manager network driver failed to create '
|
||||
'Vlan Profile for vlan %s'), str(vlan_id))
|
||||
return False
|
||||
|
||||
# Create Port Profile
|
||||
if not self._create_port_profile(handle, profile_name, vlan_id,
|
||||
vnic_type):
|
||||
LOG.error(_LE('UCS Manager network driver failed to create '
|
||||
'Port Profile %s'), profile_name)
|
||||
return False
|
||||
handle.CompleteTransaction()
|
||||
|
||||
except Exception as e:
|
||||
if const.DUPLICATE_EXCEPTION in str(e):
|
||||
LOG.debug("UCS Manager network driver found an entry that "
|
||||
"already exists %s", profile_name)
|
||||
else:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigFailed(config=profile_name,
|
||||
ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
finally:
|
||||
# Disconnect from UCS Manager
|
||||
self.ucs_manager_disconnect()
|
||||
return True
|
||||
|
||||
def _update_service_profile(self, handle, service_profile, vlan_id):
|
||||
service_profile_path = (const.SERVICE_PROFILE_PATH_PREFIX +
|
||||
str(service_profile))
|
||||
eth0 = service_profile_path + const.ETH0
|
||||
eth1 = service_profile_path + const.ETH1
|
||||
eth_port_paths = [eth0, eth1]
|
||||
|
||||
obj = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.LsServer.ClassId(),
|
||||
{self.ucsmsdk.LsServer.DN: service_profile_path})
|
||||
|
||||
if not obj:
|
||||
LOG.debug("UCS Manager network driver could not find Service "
|
||||
"Profile %s", service_profile_path)
|
||||
return False
|
||||
|
||||
for eth_port_path in eth_port_paths:
|
||||
eth = handle.GetManagedObject(
|
||||
obj, self.ucsmsdk.VnicEther.ClassId(),
|
||||
{self.ucsmsdk.VnicEther.DN: eth_port_path}, True)
|
||||
|
||||
if eth:
|
||||
vlan_name = self.make_vlan_name(vlan_id)
|
||||
vlan_path = eth_port_path + const.VLAN_PATH_PREFIX + vlan_name
|
||||
|
||||
eth_if = handle.AddManagedObject(eth,
|
||||
self.ucsmsdk.VnicEtherIf.ClassId(),
|
||||
{self.ucsmsdk.VnicEtherIf.DN: vlan_path,
|
||||
self.ucsmsdk.VnicEtherIf.NAME: vlan_name,
|
||||
self.ucsmsdk.VnicEtherIf.DEFAULT_NET: "no"}, True)
|
||||
|
||||
if not eth_if:
|
||||
LOG.debug("UCS Manager network driver could not update "
|
||||
"Service Profile %s with %s", service_profile,
|
||||
str(vlan_id))
|
||||
return False
|
||||
else:
|
||||
LOG.debug("UCS Manager network driver did not find ethernet "
|
||||
"port at %s", eth_port_path)
|
||||
return True
|
||||
|
||||
def update_serviceprofile(self, host_id, vlan_id):
|
||||
service_profile = self.ucsm_host_dict[host_id]
|
||||
if service_profile:
|
||||
LOG.debug("UCS Manager network driver Service Profile : %s",
|
||||
service_profile)
|
||||
else:
|
||||
LOG.debug("UCS Manager network driver does not support Host_id %s",
|
||||
str(host_id))
|
||||
return
|
||||
|
||||
# Connect to UCS Manager
|
||||
handle = self.ucs_manager_connect()
|
||||
if not handle:
|
||||
LOG.error(_LE('UCS Manager network driver failed to connect '
|
||||
'to UCS Manager.'))
|
||||
return False
|
||||
|
||||
try:
|
||||
handle.StartTransaction()
|
||||
# Create Vlan Profile
|
||||
if not self._create_vlanprofile(handle, vlan_id):
|
||||
LOG.error(_LE('UCS Manager network driver failed to create '
|
||||
'Vlan Profile for vlan %s'), str(vlan_id))
|
||||
return False
|
||||
|
||||
# Update Service Profile
|
||||
if not self._update_service_profile(
|
||||
handle,
|
||||
service_profile,
|
||||
vlan_id):
|
||||
LOG.error(_LE('UCS Manager network driver failed to update '
|
||||
'Service Profile %s'), service_profile)
|
||||
return False
|
||||
# Complete Transaction
|
||||
handle.CompleteTransaction()
|
||||
|
||||
except Exception as e:
|
||||
if const.DUPLICATE_EXCEPTION in str(e):
|
||||
LOG.debug("UCS Manager network driver found an entry that "
|
||||
"already exists %s", service_profile)
|
||||
else:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigFailed(config=service_profile,
|
||||
ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
finally:
|
||||
# Disconnect from UCS Manager
|
||||
self.ucs_manager_disconnect()
|
||||
|
||||
def _delete_vlan_profile(self, handle, vlan_id):
|
||||
vlan_name = self.make_vlan_name(vlan_id)
|
||||
vlan_profile_dest = (const.VLAN_PATH + const.VLAN_PROFILE_PATH_PREFIX +
|
||||
vlan_name)
|
||||
|
||||
obj = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.FabricVlan.ClassId(),
|
||||
{self.ucsmsdk.FabricVlan.DN: vlan_profile_dest})
|
||||
|
||||
if obj:
|
||||
handle.RemoveManagedObject(obj)
|
||||
|
||||
return True
|
||||
|
||||
def _delete_port_profile(self, handle, port_profile):
|
||||
port_profile_dest = (const.PORT_PROFILESETDN + const.VNIC_PATH_PREFIX +
|
||||
port_profile)
|
||||
|
||||
# Find port profile on the UCS Manager
|
||||
p_profile = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.VnicProfile.ClassId(),
|
||||
{self.ucsmsdk.VnicProfile.NAME: port_profile,
|
||||
self.ucsmsdk.VnicProfile.DN: port_profile_dest})
|
||||
|
||||
if not p_profile:
|
||||
LOG.debug("UCS Manager network driver did not find port profile "
|
||||
"%s to delete", port_profile_dest)
|
||||
return
|
||||
|
||||
handle.RemoveManagedObject(p_profile)
|
||||
return True
|
||||
|
||||
def _remove_vlan_from_all_service_profiles(self, handle, vlan_id):
|
||||
service_profile_list = []
|
||||
for host_id, value in self.ucsm_host_dict.iteritems():
|
||||
if value:
|
||||
service_profile_list.append(value)
|
||||
|
||||
if not service_profile_list:
|
||||
# Nothing to do
|
||||
return
|
||||
for service_profile in service_profile_list:
|
||||
service_profile_path = (const.SERVICE_PROFILE_PATH_PREFIX +
|
||||
service_profile)
|
||||
eth0 = service_profile_path + const.ETH0
|
||||
eth1 = service_profile_path + const.ETH1
|
||||
eth_port_paths = [eth0, eth1]
|
||||
|
||||
# 1. From the Service Profile config, access the configuration for
|
||||
# its ports.
|
||||
# 2. Check if that Vlan has been configured on each of the ports
|
||||
# 3. If Vlan conifg found, remove it.
|
||||
obj = handle.GetManagedObject(
|
||||
None,
|
||||
self.ucsmsdk.LsServer.ClassId(),
|
||||
{self.ucsmsdk.LsServer.DN: service_profile_path})
|
||||
|
||||
if obj:
|
||||
# Check if this vlan_id has been configured on the ports
|
||||
# in this Service profile
|
||||
for eth_port_path in eth_port_paths:
|
||||
eth = handle.GetManagedObject(
|
||||
obj, self.ucsmsdk.VnicEther.ClassId(),
|
||||
{self.ucsmsdk.VnicEther.DN: eth_port_path}, True)
|
||||
if eth:
|
||||
vlan_name = self.make_vlan_name(vlan_id)
|
||||
vlan_path = eth_port_path + "/if-" + vlan_name
|
||||
vlan = handle.GetManagedObject(eth,
|
||||
self.ucsmsdk.VnicEtherIf.ClassId(),
|
||||
{self.ucsmsdk.VnicEtherIf.DN: vlan_path})
|
||||
if vlan:
|
||||
# Found vlan config. Now remove it.
|
||||
handle.RemoveManagedObject(vlan)
|
||||
return True
|
||||
|
||||
def delete_all_config_for_vlan(self, vlan_id, port_profile):
|
||||
# Connect to UCS Manager
|
||||
handle = self.ucs_manager_connect()
|
||||
if not handle:
|
||||
LOG.error(_LE('UCS Manager network driver failed to connect '
|
||||
'to UCS Manager.'))
|
||||
return False
|
||||
|
||||
try:
|
||||
handle.StartTransaction()
|
||||
self._delete_port_profile(handle, port_profile)
|
||||
self._remove_vlan_from_all_service_profiles(handle, vlan_id)
|
||||
self._delete_vlan_profile(handle, vlan_id)
|
||||
|
||||
# Complete Transaction
|
||||
handle.CompleteTransaction()
|
||||
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original exception.
|
||||
raise cexc.UcsmConfigFailed(config=vlan_id,
|
||||
ucsm_ip=self.ucsm_ip, exc=e)
|
||||
|
||||
finally:
|
||||
# Disconnect from UCS Manager
|
||||
self.ucs_manager_disconnect()
|
||||
|
||||
def ucs_manager_disconnect(self):
|
||||
"""Disconnects from the UCS Manager.
|
||||
|
||||
After the disconnect, the handle associated with this connection
|
||||
is no longer valid.
|
||||
"""
|
||||
handle = self.handles[self.ucsm_ip]
|
||||
handle.Logout()
|
||||
|
||||
@staticmethod
|
||||
def make_vlan_name(vlan_id):
|
||||
return const.VLAN_PROFILE_NAME_PREFIX + str(vlan_id)
|
||||
@@ -154,12 +154,6 @@ class PolicyProfileTests(testlib_api.SqlTestCase):
|
||||
self.assertEqual(profile.id, got_profile.id)
|
||||
self.assertEqual(profile.name, got_profile.name)
|
||||
|
||||
def test_get_policy_profiles(self):
|
||||
profile = _create_test_policy_profile_if_not_there(self.session)
|
||||
got_profile = pprofile_mixin._get_policy_profiles().one()
|
||||
self.assertEqual(profile.id, got_profile.id)
|
||||
self.assertEqual(profile.name, got_profile.name)
|
||||
|
||||
|
||||
class NetworkBindingsTest(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from neutron.common import config as neutron_config
|
||||
from neutron.plugins.ml2 import config as ml2_config
|
||||
from neutron.tests import base
|
||||
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import config
|
||||
|
||||
UCSM_IP_ADDRESS = '1.1.1.1'
|
||||
UCSM_USERNAME = 'username'
|
||||
UCSM_PASSWORD = 'password'
|
||||
UCSM_PHY_NETS = ['test_physnet']
|
||||
HOST_CONFIG1 = ['Hostname1:Service_profile1']
|
||||
|
||||
|
||||
class ConfigMixin(object):
|
||||
|
||||
"""Mock config for UCSM driver."""
|
||||
|
||||
mocked_parser = None
|
||||
|
||||
def set_up_mocks(self):
|
||||
# Mock the configuration file
|
||||
|
||||
args = ['--config-file', base.etcdir('neutron.conf.test')]
|
||||
neutron_config.init(args=args)
|
||||
|
||||
# Configure the ML2 mechanism drivers and network types
|
||||
ml2_opts = {
|
||||
'mechanism_drivers': ['cisco_ucsm'],
|
||||
'tenant_network_types': ['vlan'],
|
||||
}
|
||||
for opt, val in ml2_opts.items():
|
||||
ml2_config.cfg.CONF.set_override(opt, val, 'ml2')
|
||||
|
||||
# Configure the Cisco UCS Manager mechanism driver
|
||||
ucsm_test_config = {
|
||||
'ucsm_ip': UCSM_IP_ADDRESS,
|
||||
'ucsm_username': UCSM_USERNAME,
|
||||
'ucsm_password': UCSM_PASSWORD,
|
||||
'ucsm_host_list': HOST_CONFIG1,
|
||||
}
|
||||
|
||||
for opt, val in ucsm_test_config.items():
|
||||
config.cfg.CONF.set_override(opt, val, 'ml2_cisco_ucsm')
|
||||
@@ -0,0 +1,484 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.db import api as db_api
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers.cisco.ucsm import mech_cisco_ucsm as md
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import constants as const
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import ucsm_db
|
||||
from networking_cisco.plugins.ml2.drivers.cisco.ucsm import ucsm_network_driver
|
||||
from networking_cisco.tests.unit.ml2.drivers.cisco.ucsm import (
|
||||
test_cisco_ucsm_common as mocked)
|
||||
|
||||
|
||||
UCSM_IP_ADDRESS = '1.1.1.1'
|
||||
UCSM_USERNAME = 'username'
|
||||
UCSM_PASSWORD = 'password'
|
||||
|
||||
VNIC_NORMAL = 'normal'
|
||||
VNIC_DIRECT = 'direct'
|
||||
VNIC_MACVTAP = 'macvtap'
|
||||
VNIC_TYPES = [VNIC_NORMAL, VNIC_DIRECT, VNIC_MACVTAP]
|
||||
SRIOV_VNIC_TYPES = [VNIC_DIRECT, VNIC_MACVTAP]
|
||||
|
||||
SUPPORTED_PCI_DEVS = ["1137:0071", "8086:10c9"]
|
||||
|
||||
NETWORK_ID_1 = 1001
|
||||
VLAN_ID_1 = 100
|
||||
VLAN_ID_2 = 101
|
||||
PORT_STATE_ACTIVE = n_const.PORT_STATUS_ACTIVE
|
||||
NETWORK_TYPE = 'vlan'
|
||||
NETWORK_ID = 'test-network'
|
||||
PORT_NAME = 'port1'
|
||||
PORT_NAME2 = 'port2'
|
||||
PORT_ID = '100001'
|
||||
PORT_ID2 = '100002'
|
||||
HOST1 = "Hostname1"
|
||||
|
||||
PCI_INFO_BAD_NIC = '1111:2222'
|
||||
PCI_INFO_INVALID = '1111'
|
||||
|
||||
UCSM_DRIVER = ('neutron.plugins.ml2.drivers.cisco.ucsm.'
|
||||
'ucsm_network_driver.CiscoUcsmDriver')
|
||||
|
||||
VLAN_SEGMENTS = {api.ID: 'vlan_segment_id',
|
||||
api.NETWORK_TYPE: 'vlan',
|
||||
api.PHYSICAL_NETWORK: 'test_physnet',
|
||||
api.SEGMENTATION_ID: VLAN_ID_1}
|
||||
|
||||
VXLAN_SEGMENTS = {api.ID: 'vlan_segment_id',
|
||||
api.NETWORK_TYPE: 'vxlan',
|
||||
api.PHYSICAL_NETWORK: 'test_physnet',
|
||||
api.SEGMENTATION_ID: VLAN_ID_1}
|
||||
|
||||
VLAN_SEGMENTS_BAD = {api.ID: 'vlan_segment_id',
|
||||
api.NETWORK_TYPE: 'vlan',
|
||||
api.PHYSICAL_NETWORK: 'fake_physnet',
|
||||
api.SEGMENTATION_ID: VLAN_ID_2}
|
||||
|
||||
VLAN_SEGMENTS_GOOD = [{api.ID: 'vlan_segment_id',
|
||||
api.NETWORK_TYPE: 'vlan',
|
||||
api.PHYSICAL_NETWORK: 'test_physnet',
|
||||
api.SEGMENTATION_ID: VLAN_ID_2}]
|
||||
|
||||
|
||||
class FakeNetworkContext(api.NetworkContext):
|
||||
|
||||
"""Network context for testing purposes only."""
|
||||
|
||||
def __init__(self, segments):
|
||||
|
||||
self._network_segments = segments
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return {'id': NETWORK_ID}
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def network_segments(self):
|
||||
return self._network_segments
|
||||
|
||||
|
||||
class FakePortContext(object):
|
||||
|
||||
"""Port context for testing purposes only."""
|
||||
|
||||
def __init__(self, name, port_id, vnic_type, profile,
|
||||
network_context):
|
||||
self._port = {
|
||||
'status': None,
|
||||
'id': port_id,
|
||||
'name': name,
|
||||
'host_id': HOST1,
|
||||
portbindings.VNIC_TYPE: vnic_type,
|
||||
portbindings.PROFILE: profile
|
||||
}
|
||||
self._network = network_context
|
||||
self._segment = network_context.network_segments
|
||||
self.session = db_api.get_session()
|
||||
|
||||
@property
|
||||
def current(self):
|
||||
return self._port
|
||||
|
||||
@property
|
||||
def original(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def network(self):
|
||||
return self._network
|
||||
|
||||
@property
|
||||
def segment(self):
|
||||
return self._segment
|
||||
|
||||
@property
|
||||
def bottom_bound_segment(self):
|
||||
return self._segment
|
||||
|
||||
def set_binding(self, segment_id, vif_type, vif_details,
|
||||
status=None):
|
||||
self._bound_segment_id = segment_id
|
||||
self._bound_vif_type = vif_type
|
||||
self._bound_vif_details = vif_details
|
||||
self._new_port_status = status
|
||||
|
||||
|
||||
class TestCiscoUcsmMechDriver(testlib_api.SqlTestCase,
|
||||
mocked.ConfigMixin):
|
||||
|
||||
"""Unit tests for Cisco ML2 UCS Manager MD."""
|
||||
|
||||
def setUp(self):
|
||||
"""Sets up mock Ucs Sdk."""
|
||||
super(TestCiscoUcsmMechDriver, self).setUp()
|
||||
self.set_up_mocks()
|
||||
|
||||
def new_ucsm_driver_init(mech_instance):
|
||||
|
||||
mech_instance.ucsm_ip = UCSM_IP_ADDRESS
|
||||
mech_instance.username = UCSM_USERNAME
|
||||
mech_instance.password = UCSM_PASSWORD
|
||||
mech_instance.ucsmsdk = None
|
||||
mech_instance.handles = {}
|
||||
mech_instance.supported_sriov_vnic_types = SRIOV_VNIC_TYPES
|
||||
mech_instance.supported_pci_devs = SUPPORTED_PCI_DEVS
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'__init__',
|
||||
new=new_ucsm_driver_init).start()
|
||||
|
||||
self.mech_driver = md.CiscoUcsmMechanismDriver()
|
||||
self.mech_driver.initialize()
|
||||
self.vif_type = portbindings.VIF_TYPE_802_QBH
|
||||
self.db = ucsm_db.UcsmDbModel()
|
||||
self.ucsm_driver = ucsm_network_driver.CiscoUcsmDriver()
|
||||
|
||||
def _create_network_context(self):
|
||||
segment = {api.SEGMENTATION_ID: "",
|
||||
api.NETWORK_TYPE: "",
|
||||
}
|
||||
segment[api.SEGMENTATION_ID] = VLAN_ID_1
|
||||
segment[api.NETWORK_TYPE] = 'vlan'
|
||||
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS)
|
||||
return network_context
|
||||
|
||||
def _create_port_context_vmfex(self):
|
||||
"""Creates port context with valid VM-FEX vendor info."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS)
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
return port_context
|
||||
|
||||
def _create_port_context_bad(self):
|
||||
"""Creates port context with badly formed vendor info."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': PCI_INFO_BAD_NIC}
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS)
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
return port_context
|
||||
|
||||
def _create_port_context_sriov(self):
|
||||
"""Creates port context with valid SR-IOV vendor info."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_MACVTAP
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_INTEL_82599}
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS)
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
return port_context
|
||||
|
||||
def _create_port_context_normal(self):
|
||||
"""Creates port context with Normal vnic type."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_NORMAL
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS)
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
return port_context
|
||||
|
||||
def test_parse_pci_vendor_config(self):
|
||||
"""Verifies parsing of both good and bad pci vendor config."""
|
||||
vendor1 = PCI_INFO_INVALID
|
||||
vendor2 = const.PCI_INFO_INTEL_82599
|
||||
self.assertNotIn(vendor1, self.ucsm_driver.supported_pci_devs)
|
||||
self.assertIn(vendor2, self.ucsm_driver.supported_pci_devs)
|
||||
|
||||
def test_vmfex_vnic_type_and_vendor_info(self):
|
||||
"""Verifies VM-FEX port is recognized as a supported vendor."""
|
||||
port_context = self._create_port_context_vmfex()
|
||||
vnic_type = port_context.current.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
supported = self.ucsm_driver.check_vnic_type_and_vendor_info(
|
||||
vnic_type, profile)
|
||||
self.assertTrue(supported)
|
||||
|
||||
def test_unsupported_vnic_type_and_vendor_info(self):
|
||||
"""Verifies unsupported pci vendor is rejected."""
|
||||
port_context = self._create_port_context_bad()
|
||||
vnic_type = port_context.current.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
supported = self.ucsm_driver.check_vnic_type_and_vendor_info(
|
||||
vnic_type, profile)
|
||||
self.assertFalse(supported)
|
||||
|
||||
def test_sriov_vnic_type_and_vendor_info(self):
|
||||
"""Verifies SR-IOV port and MACVTAP vnic_type are supported."""
|
||||
port_context = self._create_port_context_sriov()
|
||||
vnic_type = port_context.current.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
supported = self.ucsm_driver.check_vnic_type_and_vendor_info(
|
||||
vnic_type, profile)
|
||||
self.assertTrue(supported)
|
||||
|
||||
def test_normal_vnic_type(self):
|
||||
"""Verifies NORMAL vnic type is not supported."""
|
||||
port_context = self._create_port_context_normal()
|
||||
vnic_type = port_context.current.get(portbindings.VNIC_TYPE,
|
||||
portbindings.VNIC_NORMAL)
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
supported = self.ucsm_driver.check_vnic_type_and_vendor_info(
|
||||
vnic_type, profile)
|
||||
self.assertFalse(supported)
|
||||
|
||||
def test_validate_vm_fex_port_cisco(self):
|
||||
"""Verifies port's pci vendor info makes it VM-FEX capable."""
|
||||
port_context = self._create_port_context_vmfex()
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
valid = self.ucsm_driver.is_vmfex_port(profile)
|
||||
self.assertTrue(valid)
|
||||
|
||||
def test_validate_vm_fex_port_bad(self):
|
||||
"""Verifies unsupported pci vendor is not VM-FEX capable."""
|
||||
port_context = self._create_port_context_bad()
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
valid = self.ucsm_driver.is_vmfex_port(profile)
|
||||
self.assertFalse(valid)
|
||||
|
||||
def test_validate_vm_fex_port_sriov(self):
|
||||
"""Verifies valid SR-IOV port is not VM-FEX capable."""
|
||||
port_context = self._create_port_context_sriov()
|
||||
profile = port_context.current.get(portbindings.PROFILE, {})
|
||||
valid = self.ucsm_driver.is_vmfex_port(profile)
|
||||
# For ex: Intel PCI is supported but is not vm-fex.
|
||||
# so, should return False
|
||||
self.assertFalse(valid)
|
||||
|
||||
def test_check_segment_vlan(self):
|
||||
"""Verifies VLAN network segments are supported."""
|
||||
self.assertTrue(self.mech_driver.check_segment(VLAN_SEGMENTS))
|
||||
|
||||
def test_check_segment_vxlan(self):
|
||||
"""Verifies VXLAN network segments are not supported."""
|
||||
self.assertFalse(self.mech_driver.check_segment(VXLAN_SEGMENTS))
|
||||
|
||||
def test_vmfex_update_port_precommit(self):
|
||||
"""Verifies MD saves relevant info for VM-FEX ports into DB."""
|
||||
name = PORT_NAME2
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
profile_name = "OS-PP-100"
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
# Port Profile name and Vlan id are written to DB.
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
# Look for presence of above entry in the DB.
|
||||
p_profile = self.db.get_port_profile_for_vlan(VLAN_ID_1)
|
||||
self.assertEqual(profile_name, p_profile)
|
||||
# Look to see if flag is set for update_port_postcommit to
|
||||
# create Port Profile on UCS Manager.
|
||||
self.assertFalse(self.db.is_port_profile_created(VLAN_ID_1))
|
||||
|
||||
def test_sriov_update_port_precommit(self):
|
||||
"""Verifies MD does not create Port Profiles for SR-IOV ports."""
|
||||
port_context = self._create_port_context_sriov()
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
p_profile = self.db.get_port_profile_for_vlan(VLAN_ID_1)
|
||||
self.assertIsNone(p_profile)
|
||||
|
||||
def test_update_port_postcommit_success(self):
|
||||
"""Verifies duplicate Port Profiles are not being created."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
# Port Profile is added to DB and created on UCS Manager.
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
self.assertFalse(self.db.is_port_profile_created(VLAN_ID_1))
|
||||
|
||||
# Call to UCS Manager driver top level method to create Port Profile
|
||||
# is mocked to a new method here. This method verifies input params
|
||||
# are correct.
|
||||
def new_create_portprofile(mech_context, profile_name, vlan_id,
|
||||
vnic_type):
|
||||
return True
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'create_portprofile',
|
||||
new=new_create_portprofile).start()
|
||||
|
||||
self.mech_driver.update_port_postcommit(port_context)
|
||||
self.assertTrue(self.db.is_port_profile_created(VLAN_ID_1))
|
||||
|
||||
def test_update_port_postcommit_failure(self):
|
||||
"""Verifies duplicate Port Profiles are not being created."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
# Port Profile is added to DB and created on UCS Manager.
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
self.assertFalse(self.db.is_port_profile_created(VLAN_ID_1))
|
||||
|
||||
# Call to UCS Manager driver top level method to create Port Profile
|
||||
# is mocked to a new method here. This method verifies input params
|
||||
# are correct.
|
||||
def new_create_portprofile(mech_context, profile_name, vlan_id,
|
||||
vnic_type):
|
||||
return False
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'create_portprofile',
|
||||
new=new_create_portprofile).start()
|
||||
|
||||
self.mech_driver.update_port_postcommit(port_context)
|
||||
self.assertFalse(self.db.is_port_profile_created(VLAN_ID_1))
|
||||
|
||||
def test_update_port_postcommit_direct(self):
|
||||
"""Verifies UCS Manager driver is called with correct parameters."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_direct = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_direct,
|
||||
profile, network_context)
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
|
||||
# Call to UCS Manager driver top level method to create Port Profile
|
||||
# is mocked to a new method here. This method verifies input params
|
||||
# are correct.
|
||||
def new_create_portprofile(mech_context, profile_name, vlan_id,
|
||||
vnic_type):
|
||||
self.assertEqual("OS-PP-100", profile_name)
|
||||
self.assertEqual(100, vlan_id)
|
||||
self.assertEqual(VNIC_DIRECT, vnic_type)
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'create_portprofile',
|
||||
new=new_create_portprofile).start()
|
||||
|
||||
self.mech_driver.update_port_postcommit(port_context)
|
||||
|
||||
def test_update_port_postcommit_macvtap(self):
|
||||
"""Verifies UCS Manager driver is called with correct parameters."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_macvtap = VNIC_MACVTAP
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_macvtap,
|
||||
profile, network_context)
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
|
||||
# Call to UCS Manager driver top level method to create Port Profile
|
||||
# is mocked to a new method here. This method verifies input params
|
||||
# are correct.
|
||||
def new_create_portprofile(mech_context, profile_name, vlan_id,
|
||||
vnic_type):
|
||||
self.assertEqual("OS-PP-100", profile_name)
|
||||
self.assertEqual(100, vlan_id)
|
||||
self.assertEqual(VNIC_MACVTAP, vnic_type)
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'create_portprofile',
|
||||
new=new_create_portprofile).start()
|
||||
|
||||
self.mech_driver.update_port_postcommit(port_context)
|
||||
|
||||
def test_update_port_postcommit_normal(self):
|
||||
"""Verifies UCS Manager driver is called with correct parameters."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_NORMAL
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = self._create_network_context()
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
self.mech_driver.update_port_precommit(port_context)
|
||||
|
||||
# Call to UCS Manager driver top level method to create Port Profile
|
||||
# is mocked to a new method here. This method verifies input params
|
||||
# are correct.
|
||||
def new_update_serviceprofile(mech_context, host_id, vlan_id):
|
||||
return True
|
||||
|
||||
mock.patch.object(ucsm_network_driver.CiscoUcsmDriver,
|
||||
'update_serviceprofile',
|
||||
new=new_update_serviceprofile).start()
|
||||
|
||||
self.mech_driver.update_port_postcommit(port_context)
|
||||
|
||||
def test_bind_port_active(self):
|
||||
"""Verifies bind_port sets the port status as active."""
|
||||
name = PORT_NAME
|
||||
port_id = PORT_ID
|
||||
vnic_type = VNIC_DIRECT
|
||||
profile = {'pci_vendor_info': const.PCI_INFO_CISCO_VIC_1240}
|
||||
|
||||
network_context = FakeNetworkContext(VLAN_SEGMENTS_GOOD)
|
||||
port_context = FakePortContext(name, port_id, vnic_type,
|
||||
profile, network_context)
|
||||
self.mech_driver.bind_port(port_context)
|
||||
self.assertEqual(PORT_STATE_ACTIVE, port_context._new_port_status)
|
||||
@@ -51,6 +51,7 @@ neutron.service_plugins =
|
||||
cisco_n1kv_profile = networking_cisco.plugins.ml2.drivers.cisco.n1kv.policy_profile_service:PolicyProfilePlugin
|
||||
neutron.ml2.extension_drivers =
|
||||
cisco_n1kv_ext = neutron.plugins.ml2.drivers.cisco.n1kv.n1kv_ext_driver:CiscoN1kvExtensionDriver
|
||||
cisco_ucsm = neutron.plugins.ml2.drivers.cisco.ucsm.mech_cisco_ucsm:CiscoUcsmMechanismDriver
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
|
||||
@@ -1 +1 @@
|
||||
refs/changes/43/157243/15
|
||||
refs/changes/36/155436/21
|
||||
|
||||
Reference in New Issue
Block a user