Implement Rest API in ML2 Nexus Driver

Supercedes https://review.openstack.org/429897

This change set includes the ability to configure Nexus 9K using
REST API drivers instead of ncclient ssh Drivers.  By default,
ssh drivers are used.  To utilize REST API drivers, the user must
configure 'use_restapi_driver' to True in the ml2 config file.

The benefit of using REST API drivers is there are no limits
on the number of session limits on the Nexus side.  Also there
is significant performance improvements.  However, to utilize
this driver, the Nexus 9K must support the ability to
configure VLANS and VXLANS by way of REST. The N9K image
which meets all required REST API configuration is
version 7.0(3)I5(2).

Removed methods 'create_vlan_svi' and 'get_version' from the file
nexus_network_driver.py since it is not being called by any code.
I want the capability in this driver to match the
new rest api driver. There was no sense in creating unused
methods in the new rest api driver.

Change-Id: I841fc46af5fc7cd668935c1c734f74d7a4cd888a
Closes-bug:  #1655097
This commit is contained in:
Carol Bouchard 2017-02-10 09:36:22 -05:00
parent d88831a06e
commit 6de0f6026b
18 changed files with 3647 additions and 783 deletions

View File

@ -16,6 +16,15 @@
# Nexus switches. Defaults to False (No hostkey checks)
# host_key_checks = False
# (ListOp) A choice of drivers to configure Nexus devices. The
# default choice is 'ncclient' which was the original driver
# until rest api driver was developed. To choose the
# rest api driver, set nexus_driver to 'restapi' which
# has better performance and no Nexus session limits.
# This option should only be selected if you have a
# Nexus 9K image version 7.0(3)I5(2) or greater.
# nexus_driver = ncclient
#
# (StrOpt) The name of the physical_network managed via the Cisco Nexus Switch.
# This string value must be present in the ml2_conf.ini network_vlan_ranges

View File

@ -45,6 +45,11 @@ ml2_cisco_opts = [
cfg.BoolOpt('host_key_checks', default=False,
help=_("Enable strict host key checks when "
"connecting to Nexus switches")),
cfg.StrOpt('nexus_driver',
default='ncclient',
help=_("Choice of Nexus Config Driver to be loaded from "
"the networking_cisco.ml2.nexus_driver namespace.")),
]

View File

@ -44,7 +44,7 @@ class NexusConnectFailed(exceptions.NeutronException):
class NexusConfigFailed(exceptions.NeutronException):
"""Failed to configure Nexus switch."""
message = _("Failed to configure Nexus switch: %(nexus_host)s "
"XML: %(config)s. Reason: %(exc)s.")
"Config: %(config)s. Reason: %(exc)s.")
class NexusPortBindingNotFound(exceptions.NeutronException):

View File

@ -30,6 +30,7 @@ from oslo_utils import excutils
from networking_cisco import backwards_compatibility as bc
from neutron.common import utils as neutron_utils
from neutron.extensions import portbindings
from neutron.plugins.common import constants as p_const
@ -49,8 +50,6 @@ from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
exceptions as excep)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_db_v2 as nxos_db)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_network_driver)
LOG = logging.getLogger(__name__)
@ -157,6 +156,16 @@ class CiscoNexusCfgMonitor(object):
nve_bindings = nxos_db.get_nve_switch_bindings(switch_ip)
# If configured to set global VXLAN values and
# there exists VXLAN data base entries, then configure
# the "interface nve" entry on the switch.
if (len(nve_bindings) > 0 and
cfg.CONF.ml2_cisco.vxlan_global_config):
LOG.debug("Nexus: Replay NVE Interface")
loopback = self._mdriver.get_nve_loopback(switch_ip)
self._driver.enable_vxlan_feature(switch_ip,
const.NVE_INT_NUM, loopback)
for x in nve_bindings:
try:
self._driver.create_nve_member(switch_ip,
@ -271,6 +280,21 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
"""Cisco Nexus ML2 Mechanism Driver."""
def _load_nexus_cfg_driver(self):
"""Load Nexus Config driver.
:raises SystemExit of 1 if driver cannot be loaded
"""
try:
loaded_class = neutron_utils.load_class_by_alias_or_classname(
'networking_cisco.ml2.nexus_driver',
conf.cfg.CONF.ml2_cisco.nexus_driver)
return loaded_class()
except ImportError:
LOG.error(_LE("Error loading Nexus Config driver '%s'"),
conf.cfg.CONF.ml2_cisco.nexus_driver)
raise SystemExit(1)
def initialize(self):
# Create ML2 device dictionary from ml2_conf.ini entries.
conf.ML2MechCiscoConfig()
@ -278,10 +302,11 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
# Extract configuration parameters from the configuration file.
self._nexus_switches = conf.ML2MechCiscoConfig.nexus_dict
LOG.debug("nexus_switches found = %s", self._nexus_switches)
# Save dynamic switch information
self._switch_state = {}
self.driver = nexus_network_driver.CiscoNexusDriver()
self.driver = self._load_nexus_cfg_driver()
# This method is only called once regardless of number of
# api/rpc workers defined.
@ -1059,6 +1084,10 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
nxos_db.add_nexusnve_binding(vni, switch_ip, device_id,
mcast_group)
def get_nve_loopback(self, switch_ip):
return self._nexus_switches.get(
(switch_ip, 'nve_src_intf'), '0')
def _configure_nve_member(self, vni, device_id, mcast_group, host_id):
"""Add "member vni" configuration to the NVE interface.
@ -1075,8 +1104,7 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
nve_bindings = nxos_db.get_nve_switch_bindings(switch_ip)
if len(nve_bindings) == 1:
LOG.debug("Nexus: create NVE interface")
loopback = self._nexus_switches.get(
(switch_ip, 'nve_src_intf'), '0')
loopback = self.get_nve_loopback(switch_ip)
self.driver.enable_vxlan_feature(switch_ip,
const.NVE_INT_NUM, loopback)
@ -1177,7 +1205,7 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
nexus_port, vni, is_native)
elif auto_create:
LOG.debug("Nexus: create vlan %s", vlan_name)
self.driver.create_vlan_segment(switch_ip, vlan_id,
self.driver.create_vlan(switch_ip, vlan_id,
vlan_name, vni)
elif auto_trunk:
LOG.debug("Nexus: trunk vlan %s", vlan_name)
@ -1234,11 +1262,21 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
def _restore_port_binding(self,
switch_ip, pvlan_ids,
port, is_native):
port, native_vlan):
"""Restores a set of vlans for a given port."""
intf_type, nexus_port = self.split_interface_name(port)
# If native_vlan is configured, this is isolated since
# two configs (native + trunk) must be sent for this vlan only.
if native_vlan != 0:
self.driver.send_enable_vlan_on_trunk_int(
switch_ip, native_vlan,
intf_type, nexus_port, True)
# If this is the only vlan
if len(pvlan_ids) == 1:
return
concat_vlans = ''
compressed_vlans = self._get_compressed_vlan_list(pvlan_ids)
for pvlan in compressed_vlans:
@ -1252,14 +1290,14 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
if len(concat_vlans) >= const.CREATE_PORT_VLAN_LENGTH:
self.driver.send_enable_vlan_on_trunk_int(
switch_ip, concat_vlans,
intf_type, nexus_port, is_native)
intf_type, nexus_port, False)
concat_vlans = ''
# Send remaining vlans if any
if len(concat_vlans):
self.driver.send_enable_vlan_on_trunk_int(
switch_ip, concat_vlans,
intf_type, nexus_port, is_native)
intf_type, nexus_port, False)
def _restore_vxlan_entries(self, switch_ip, vlans):
"""Restore vxlan entries on a Nexus switch."""
@ -1267,24 +1305,30 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
count = 1
conf_str = ''
vnsegment_sent = 0
path_str, conf_str = self.driver.start_create_vlan()
# At this time, this will only configure vni information when needed
while vnsegment_sent < const.CREATE_VLAN_BATCH and vlans:
vlan_id, vni, vlan_name = vlans.pop(0)
# Add it to the batch
conf_str += self.driver.get_create_vlan(
switch_ip, vlan_id, vni)
conf_str = self.driver.get_create_vlan(
switch_ip, vlan_id, vni, conf_str)
# batch size has been met
if (count == const.CREATE_VLAN_SEND_SIZE):
self.driver.send_edit_string(switch_ip, conf_str)
conf_str = self.driver.end_create_vlan(conf_str)
self.driver.send_edit_string(switch_ip, path_str, conf_str)
vnsegment_sent += count
conf_str = ''
count = 1
else:
count += 1
# batch size was not met
if conf_str:
vnsegment_sent += count
self.driver.send_edit_string(switch_ip, conf_str)
conf_str = self.driver.end_create_vlan(conf_str)
self.driver.send_edit_string(switch_ip, path_str, conf_str)
conf_str = ''
LOG.debug("Switch %s VLAN vn-segment replay summary: %d",
switch_ip, vnsegment_sent)
@ -1391,7 +1435,7 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
prev_vlan = -1
prev_vni = -1
prev_port = None
prev_is_native = False
prev_native_vlan = 0
starttime = time.time()
port_bindings.sort(key=lambda x: (x.port_id, x.vlan_id, x.vni))
@ -1422,6 +1466,8 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
vlans.add((port.vlan_id, port.vni, vlan_name))
if auto_trunk:
pvlans.add(port.vlan_id)
if port.is_native:
prev_native_vlan = port.vlan_id
else:
# Different port - write out interface trunk on previous port
if prev_port:
@ -1433,22 +1479,24 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
vlan_count = 0
if pvlans:
self._restore_port_binding(
switch_ip, pvlans, prev_port, prev_is_native)
switch_ip, pvlans, prev_port, prev_native_vlan)
pvlans.clear()
prev_native_vlan = 0
# Start tracking new port
if auto_create:
vlans.add((port.vlan_id, port.vni, vlan_name))
if auto_trunk:
pvlans.add(port.vlan_id)
prev_port = port.port_id
prev_is_native = port.is_native
if port.is_native:
prev_native_vlan = port.vlan_id
if pvlans:
LOG.debug("Switch %s port %s replay summary: unique vlan "
"count %d, duplicate port entries %d",
switch_ip, port.port_id, vlan_count, duplicate_port)
self._restore_port_binding(
switch_ip, pvlans, prev_port, prev_is_native)
switch_ip, pvlans, prev_port, prev_native_vlan)
LOG.debug("Replayed total %d ports for Switch %s",
interface_count + 1, switch_ip)

View File

@ -0,0 +1,184 @@
# Copyright (c) 2013-2016 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.
"""
Base class driver for SSH and RESTAPI Client. Some APIs are
called by upper layer code but only apply to either RESTAPIs drivers
or SSH drivers. Having base class which simply performs 'pass'
is necessary so these two drivers get required actions without
breaking the other driver. Drivers defined in this base are
mostly those called externally.
"""
import os
import threading
import time
from oslo_log import log as logging
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
config as conf)
LOG = logging.getLogger(__name__)
class CiscoNexusBaseDriver(object):
"""Nexus Driver Base Class."""
def __init__(self):
self.nexus_switches = conf.ML2MechCiscoConfig.nexus_dict
self.time_stats = {}
def keep_ssh_caching(self):
pass
def init_ssh_caching(self):
pass
def capture_and_print_timeshot(self, start_time, which,
other=99, switch="0.0.0.0"):
"""Determine delta, keep track, and print results."""
curr_timeout = time.time() - start_time
if which in self.time_stats:
self.time_stats[which]["total_time"] += curr_timeout
self.time_stats[which]["total_count"] += 1
if (curr_timeout < self.time_stats[which]["min"]):
self.time_stats[which]["min"] = curr_timeout
if (curr_timeout > self.time_stats[which]["max"]):
self.time_stats[which]["max"] = curr_timeout
else:
self.time_stats[which] = {
"total_time": curr_timeout,
"total_count": 1,
"min": curr_timeout,
"max": curr_timeout}
LOG.debug("NEXUS_TIME_STATS %(switch)s, pid %(pid)d, tid %(tid)d: "
"%(which)s_timeout %(curr)f count %(count)d "
"average %(ave)f other %(other)d min %(min)f max %(max)f",
{'switch': switch,
'pid': os.getpid(),
'tid': threading.current_thread().ident,
'which': which,
'curr': curr_timeout,
'count': self.time_stats[which]["total_count"],
'ave': (self.time_stats[which]["total_time"] /
self.time_stats[which]["total_count"]),
'other': other,
'min': self.time_stats[which]["min"],
'max': self.time_stats[which]["max"]})
def close_session(self, nexus_host):
pass
def get_interface_switch(self, nexus_host,
intf_type, interface):
"""Get the interface data from host.
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns response:
"""
return None
def initialize_all_switch_interfaces(self, interfaces):
"""Configure Nexus interface and get port channel number.
Receive a list of interfaces containing:
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns interface: Appends port channel to each entry
channel number is 0 if none
"""
pass
def get_nexus_type(self, nexus_host):
"""Given the nexus host, get the type of Nexus switch.
:param nexus_host: IP address of Nexus switch
:returns Nexus type
"""
return None
def set_all_vlan_states(self, nexus_host, vlanid_range):
"""Set the VLAN states to active."""
pass
def start_create_vlan(self):
"""Returns an XML snippet for start of create VLAN."""
return None, ''
def get_create_vlan(self, nexus_host, vlanid, vni, conf_str):
"""Returns an XML snippet for create VLAN."""
return conf_str
def end_create_vlan(self, conf_str):
"""Returns an XML snippet for terminate of create VLAN."""
return conf_str
def create_vlan(self, nexus_host,
vlanid, vlanname, vni):
"""Create a VLAN on a Nexus Switch.
Creates a VLAN given the VLAN ID, name and possible VxLAN ID.
"""
pass
def delete_vlan(self, nexus_host, vlanid):
"""Delete a VLAN on Nexus Switch given the VLAN ID."""
pass
def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native):
"""Disable a VLAN on a trunk interface."""
pass
def send_edit_string(self, nexus_host, path, confstr,
check_to_close_session=True):
"""Sends any XML snippet to Nexus switch."""
pass
def send_enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native):
"""Gathers and sends an interface trunk XML snippet."""
pass
def create_and_trunk_vlan(self, nexus_host, vlan_id,
vlan_name, intf_type,
nexus_port, vni,
is_native):
"""Create VLAN and trunk it on the specified ports."""
pass
def enable_vxlan_feature(self, nexus_host, nve_int_num, src_intf):
"""Enable VXLAN on the switch."""
pass
def disable_vxlan_feature(self, nexus_host):
"""Disable VXLAN on the switch."""
pass
def create_nve_member(self, nexus_host, nve_int_num, vni, mcast_group):
"""Add a member configuration to the NVE interface."""
pass
def delete_nve_member(self, nexus_host, nve_int_num, vni):
"""Delete a member configuration on the NVE interface."""
pass

View File

@ -17,10 +17,8 @@
Implements a Nexus-OS NETCONF over SSHv2 API Client
"""
import os
import re
import six
import threading
import time
from oslo_concurrency import lockutils
@ -36,20 +34,23 @@ from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
constants as const)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
exceptions as cexc)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_base_network_driver as basedrvr)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_snippets as snipp)
LOG = logging.getLogger(__name__)
class CiscoNexusDriver(object):
class CiscoNexusSshDriver(basedrvr.CiscoNexusBaseDriver):
"""Nexus Driver Main Class."""
def __init__(self):
self.ncclient = None
self.nexus_switches = conf.ML2MechCiscoConfig.nexus_dict
self.connections = {}
self.time_stats = {}
self.init_ssh_caching()
super(CiscoNexusSshDriver, self).__init__()
LOG.debug("ML2 Nexus SSH Drivers initialized.")
# Driver lock introduced to prevent replay thread and
# transaction thread from closing each others
@ -68,7 +69,7 @@ class CiscoNexusDriver(object):
(cfg.CONF.rpc_workers + cfg.CONF.api_workers) >=
const.MAX_NEXUS_SSH_SESSIONS) else False
def _import_ncclient(self):
def _import_client(self):
"""Import the NETCONF client (ncclient) module.
The ncclient module is not installed as part of the normal Neutron
@ -79,39 +80,6 @@ class CiscoNexusDriver(object):
"""
return importutils.import_module('ncclient.manager')
def capture_and_print_timeshot(self, start_time, which,
other=99, switch="0.0.0.0"):
"""Determine delta, keep track, and print results."""
curr_timeout = time.time() - start_time
if which in self.time_stats:
self.time_stats[which]["total_time"] += curr_timeout
self.time_stats[which]["total_count"] += 1
if (curr_timeout < self.time_stats[which]["min"]):
self.time_stats[which]["min"] = curr_timeout
if (curr_timeout > self.time_stats[which]["max"]):
self.time_stats[which]["max"] = curr_timeout
else:
self.time_stats[which] = {
"total_time": curr_timeout,
"total_count": 1,
"min": curr_timeout,
"max": curr_timeout}
LOG.debug("NEXUS_TIME_STATS %(switch)s, pid %(pid)d, tid %(tid)d: "
"%(which)s_timeout %(curr)f count %(count)d "
"average %(ave)f other %(other)d min %(min)f max %(max)f",
{'switch': switch,
'pid': os.getpid(),
'tid': threading.current_thread().ident,
'which': which,
'curr': curr_timeout,
'count': self.time_stats[which]["total_count"],
'ave': (self.time_stats[which]["total_time"] /
self.time_stats[which]["total_count"]),
'other': other,
'min': self.time_stats[which]["min"],
'max': self.time_stats[which]["max"]})
def _get_close_ssh_session(self):
return self._close_ssh_session
@ -134,7 +102,7 @@ class CiscoNexusDriver(object):
# session before complete.
@lockutils.synchronized('cisco-nexus-drvrlock')
def close_session(self, nexus_host):
mgr = self.nxos_connect(nexus_host)
mgr = self._nxos_connect(nexus_host)
self._close_session(mgr, nexus_host)
# Driver lock introduced to prevent replay thread and
@ -157,7 +125,7 @@ class CiscoNexusDriver(object):
# try again
# then quit
for retry_count in (1, 2):
mgr = self.nxos_connect(nexus_host)
mgr = self._nxos_connect(nexus_host)
starttime = time.time()
try:
data_xml = mgr.get(filter=('subtree', filter)).data_xml
@ -224,7 +192,7 @@ class CiscoNexusDriver(object):
# try again
# then quit
for retry_count in (1, 2):
mgr = self.nxos_connect(nexus_host)
mgr = self._nxos_connect(nexus_host)
LOG.debug("NexusDriver edit config for host %s: %s",
nexus_host, config)
starttime = time.time()
@ -259,7 +227,7 @@ class CiscoNexusDriver(object):
if check_to_close_session and self._get_close_ssh_session():
self._close_session(mgr, nexus_host)
def nxos_connect(self, nexus_host):
def _nxos_connect(self, nexus_host):
"""Make SSH connection to the Nexus Switch."""
starttime = time.time()
@ -270,7 +238,7 @@ class CiscoNexusDriver(object):
return self.connections[nexus_host]
if not self.ncclient:
self.ncclient = self._import_ncclient()
self.ncclient = self._import_client()
nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port'])
nexus_user = self.nexus_switches[nexus_host, const.USERNAME]
nexus_password = self.nexus_switches[nexus_host, const.PASSWORD]
@ -374,7 +342,7 @@ class CiscoNexusDriver(object):
"switchport trunk allowed vlan" in response):
pass
else:
ifs.append(self.build_intf_confstr(
ifs.append(self._build_intf_confstr(
snipp.CMD_INT_VLAN_SNIPPET,
intf_type, nexus_port, 'None'))
if ifs:
@ -385,22 +353,6 @@ class CiscoNexusDriver(object):
starttime, "get_allif",
switch=nexus_host)
def get_version(self, nexus_host):
"""Given the nexus host, get the version data.
:param nexus_host: IP address of Nexus switch
:returns version number
"""
confstr = snipp.EXEC_GET_VERSION_SNIPPET
response = self._get_config(nexus_host, confstr)
LOG.debug("GET call returned version")
version = None
if response:
version = re.findall(
"\<sys_ver_str\>([\x20-\x7e]+)\<\/sys_ver_str\>", response)
return version
def get_nexus_type(self, nexus_host):
"""Given the nexus host, get the type of Nexus switch.
@ -443,9 +395,9 @@ class CiscoNexusDriver(object):
self.capture_and_print_timeshot(
starttime, "set_all_vlan_states",
switch=nexus_host)
self.send_edit_string(nexus_host, snippet)
self.send_edit_string(nexus_host, None, snippet)
def get_create_vlan(self, nexus_host, vlanid, vni):
def get_create_vlan(self, nexus_host, vlanid, vni, conf_str):
"""Returns an XML snippet for create VLAN on a Nexus Switch."""
LOG.debug("NexusDriver: ")
@ -460,7 +412,8 @@ class CiscoNexusDriver(object):
starttime, "get_create_vlan",
switch=nexus_host)
return snippet
conf_str += snippet
return conf_str
def create_vlan(self, nexus_host,
vlanid, vlanname, vni):
@ -472,9 +425,10 @@ class CiscoNexusDriver(object):
LOG.debug("NexusDriver: ")
starttime = time.time()
confstr = self.get_create_vlan(nexus_host, vlanid, vni)
confstr = ''
confstr = self.get_create_vlan(nexus_host, vlanid, vni, confstr)
self.send_edit_string(nexus_host, confstr,
self.send_edit_string(nexus_host, None, confstr,
check_to_close_session=False)
self.capture_and_print_timeshot(
@ -492,12 +446,12 @@ class CiscoNexusDriver(object):
starttime, "del_vlan",
switch=nexus_host)
def build_intf_confstr(self, snippet, intf_type, interface, vlanid):
def _build_intf_confstr(self, snippet, intf_type, interface, vlanid):
"""Build the VLAN config string xml snippet to be used."""
confstr = snippet % (intf_type, interface, vlanid, intf_type)
return confstr
def get_enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
def _get_enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native, confstr=''):
"""Prepares an XML snippet for VLAN on a trunk interface.
@ -519,7 +473,7 @@ class CiscoNexusDriver(object):
snippets.append(snipp.CMD_INT_VLAN_ADD_SNIPPET)
for snip in snippets:
confstr += self.build_intf_confstr(
confstr += self._build_intf_confstr(
snippet=snip,
intf_type=intf_type,
interface=interface,
@ -551,7 +505,7 @@ class CiscoNexusDriver(object):
starttime, "delif",
switch=nexus_host)
def send_edit_string(self, nexus_host, confstr,
def send_edit_string(self, nexus_host, path, confstr,
check_to_close_session=True):
"""Sends any XML snippet to Nexus switch."""
@ -571,10 +525,10 @@ class CiscoNexusDriver(object):
def send_enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native):
"""Gathers and sends an interface trunk XML snippet."""
confstr = self.get_enable_vlan_on_trunk_int(
confstr = self._get_enable_vlan_on_trunk_int(
nexus_host, vlanid,
intf_type, interface, is_native)
self.send_edit_string(nexus_host, confstr)
self.send_edit_string(nexus_host, None, confstr)
def create_and_trunk_vlan(self, nexus_host, vlan_id,
vlan_name, intf_type,
@ -593,23 +547,11 @@ class CiscoNexusDriver(object):
starttime, "create_all",
switch=nexus_host)
def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
"""Create VLAN vn_segment."""
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, target='running', config=confstr)
def delete_vlan_svi(self, nexus_host, vlan_id):
"""Delete VLAN vn_segment."""
confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id
confstr = self.create_xml_snippet(confstr)
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, target='running', config=confstr)
def enable_vxlan_feature(self, nexus_host, nve_int_num, src_intf):
"""Enable VXLAN on the switch."""
starttime = time.time()
# Configure the "feature" commands and NVE interface
# (without "member" subcommand configuration).
# The Nexus 9K will not allow the "interface nve" configuration
@ -624,25 +566,47 @@ class CiscoNexusDriver(object):
(nve_int_num, src_intf)))
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, config=confstr)
self.capture_and_print_timeshot(
starttime, "enable_vxlan",
switch=nexus_host)
def disable_vxlan_feature(self, nexus_host):
"""Disable VXLAN on the switch."""
starttime = time.time()
# Removing the "feature" commands also removes the NVE interface.
confstr = self.create_xml_snippet(snipp.CMD_NO_FEATURE_VXLAN_SNIPPET)
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, config=confstr)
self.capture_and_print_timeshot(
starttime, "disable_vxlan",
switch=nexus_host)
def create_nve_member(self, nexus_host, nve_int_num, vni, mcast_group):
"""Add a member configuration to the NVE interface."""
starttime = time.time()
confstr = self.create_xml_snippet((snipp.CMD_INT_NVE_MEMBER_SNIPPET %
(nve_int_num, vni, mcast_group)))
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, config=confstr)
self.capture_and_print_timeshot(
starttime, "create_nve",
switch=nexus_host)
def delete_nve_member(self, nexus_host, nve_int_num, vni):
"""Delete a member configuration on the NVE interface."""
starttime = time.time()
confstr = self.create_xml_snippet((snipp.CMD_INT_NVE_NO_MEMBER_SNIPPET
% (nve_int_num, vni)))
LOG.debug("NexusDriver: ")
self._edit_config(nexus_host, config=confstr)
self.capture_and_print_timeshot(
starttime, "delete_nve",
switch=nexus_host)

View File

@ -0,0 +1,178 @@
# Copyright (c) 2017-2017 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.
"""
Implements REST API Client For Nexus
"""
import netaddr
import requests
from networking_cisco._i18n import _LE
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
exceptions as cexc)
from oslo_log import log as logging
from oslo_serialization import jsonutils
DEFAULT_HEADER = {"Content-type": "application/json", "Accept": "text/plain"}
COOKIE_HEADER = """
{"Cookie": %s, "Content-type": "application/json", "Accept": "text/plain"}"""
DEFAULT_SCHEME = "http"
ACCEPTED_CODES = [200, 201, 204]
LOG = logging.getLogger(__name__)
class CiscoNexusRestapiClient(object):
def __init__(self, credentials,
accepted_codes=ACCEPTED_CODES,
scheme=DEFAULT_SCHEME,
timeout=30,
max_retries=2):
"""Initialize the rest api client for Nexus."""
self.format = 'json'
self.accepted_codes = accepted_codes
self.action_prefix = 'http://%s/'
self.scheme = scheme
self.status = requests.codes.OK
self.time_stats = {}
self.timeout = timeout
self.max_retries = max_retries
self.session = requests.Session()
self.credentials = credentials
def _get_cookie(self, mgmt_ip, config):
"""Performs authentication and retries cookie."""
if mgmt_ip not in self.credentials:
return None
security_data = self.credentials[mgmt_ip]
payload = {"aaaUser": {"attributes": {"name": security_data[0],
"pwd": security_data[1]}}}
headers = {"Content-type": "application/json", "Accept": "text/plain"}
url = "http://{0}/api/aaaLogin.json".format(mgmt_ip)
try:
response = self.session.request('POST',
url,
data=jsonutils.dumps(payload),
headers=headers,
timeout=self.timeout * 2)
except Exception as e:
raise cexc.NexusConnectFailed(nexus_host=mgmt_ip,
exc=e)
self.status = response.status_code
if response.status_code == requests.codes.OK:
return response.headers.get('Set-Cookie')
else:
e = "REST API connect returned Error code: "
e += str(self.status)
raise cexc.NexusConnectFailed(nexus_host=mgmt_ip,
exc=e)
def send_request(self, method, action, body=None,
headers=None, ipaddr=None):
"""Perform the HTTP request.
The response is in either JSON format or plain text. A GET method will
invoke a JSON response while a PUT/POST/DELETE returns message from the
the server in plain text format.
Exception is raised when server replies with an INTERNAL SERVER ERROR
status code (500) i.e. an error has occurred on the server or SERVICE
UNAVAILABLE (404) i.e. server is not reachable.
:param method: type of the HTTP request. POST, GET, PUT or DELETE
:param action: path to which the client makes request
:param body: dict of arguments which are sent as part of the request
:param headers: header for the HTTP request
:param server_ip: server_ip for the HTTP request.
:returns: JSON or plain text in HTTP response
"""
action = ''.join([self.scheme, '://%s/', action])
if netaddr.valid_ipv6(ipaddr):
# Enclose IPv6 address in [] in the URL
action = action % ("[%s]" % ipaddr)
else:
# IPv4 address
action = action % ipaddr
config = action + " : " + body if body else action
cookie = self._get_cookie(ipaddr, config)
if not cookie or self.status != requests.codes.OK:
return {}
headers = {"Content-type": "application/json",
"Accept": "text/plain", "Cookie": cookie}
for attempt in range(self.max_retries + 1):
try:
LOG.debug("[Nexus %(ipaddr)s attempt %(id)s]: Connecting.." %
{"ipaddr": ipaddr, "id": attempt})
response = self.session.request(
method,
action,
data=body,
headers=headers,
timeout=self.timeout)
except Exception as e:
LOG.error(_LE(
"Exception raised %(err)s for Rest API %(cfg)s"),
{'err': str(e), 'cfg': config})
raise cexc.NexusConfigFailed(nexus_host=ipaddr,
config=config,
exc=e)
else:
break
status_string = requests.status_codes._codes[response.status_code][0]
if response.status_code in self.accepted_codes:
LOG.debug(
"Good status %(status)s(%(code)d) returned for %(url)s",
{'status': status_string,
'code': response.status_code,
'url': action})
if 'application/json' in response.headers['content-type']:
try:
return response.json()
except ValueError:
return {}
else:
LOG.error(_LE(
"Bad status %(status)s(%(code)d) returned for %(url)s"),
{'status': status_string,
'code': response.status_code,
'url': action})
LOG.error(_LE("Response text: %(txt)s"),
{'txt': response.text})
raise cexc.NexusConfigFailed(nexus_host=ipaddr,
config=action,
exc=e)
def rest_delete(self, action, ipaddr=None, body=None, headers=None):
return self.send_request("DELETE", action, body=body,
headers=headers, ipaddr=ipaddr)
def rest_get(self, action, ipaddr, body=None, headers=None):
return self.send_request("GET", action, body=body,
headers=headers, ipaddr=ipaddr)
def rest_post(self, action, ipaddr=None, body=None, headers=None):
return self.send_request("POST", action, body=body,
headers=headers, ipaddr=ipaddr)

View File

@ -0,0 +1,533 @@
# Copyright (c) 2017-2017 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.
"""
Implements a Nexus-OS NETCONF over SSHv2 API Client
"""
import re
import time
from oslo_log import log as logging
from networking_cisco._i18n import _LW
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
config as conf)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
constants as const)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_base_network_driver as basedrvr)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_client as client)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_snippets as snipp)
LOG = logging.getLogger(__name__)
class CiscoNexusRestapiDriver(basedrvr.CiscoNexusBaseDriver):
"""Nexus Driver Restapi Class."""
def __init__(self):
conf.ML2MechCiscoConfig()
credentials = self._build_credentials(
conf.ML2MechCiscoConfig.nexus_dict)
self.client = self._import_client(credentials)
super(CiscoNexusRestapiDriver, self).__init__()
LOG.debug("ML2 Nexus RESTAPI Drivers initialized.")
def _import_client(self, credentials):
"""Import the local RESTAPI client module.
This method was created to mirror original ssh driver so
test code was in sync.
"""
return client.CiscoNexusRestapiClient(credentials)
def _build_credentials(self, nexus_switches):
"""Build credential table for Rest API Client.
:param nexus_switches: switch config
:returns credentials: switch credentials list
"""
credential_attr = [const.USERNAME, const.PASSWORD]
credentials = {}
for switch_ip, option in nexus_switches:
if option in credential_attr:
value = nexus_switches[switch_ip, option]
if switch_ip in credentials:
credential_tuple = credentials[switch_ip]
else:
credential_tuple = (None, None)
if option == const.USERNAME:
credential_tuple = (value,
credential_tuple[1])
else:
credential_tuple = (
credential_tuple[0],
value)
credentials[switch_ip] = credential_tuple
return credentials
def get_interface_switch(self, nexus_host,
intf_type, interface):
"""Get the interface data from host.
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns response: Returns interface data
"""
if intf_type == "ethernet":
path_interface = "phys-[eth" + interface + "]"
else:
path_interface = "aggr-[po" + interface + "]"
action = snipp.PATH_IF % path_interface
starttime = time.time()
response = self.client.rest_get(action, nexus_host)
self.capture_and_print_timeshot(starttime, "getif",
switch=nexus_host)
LOG.debug("GET call returned interface %(if_type)s %(interface)s "
"config", {'if_type': intf_type, 'interface': interface})
return response
def _get_interface_switch_trunk_present(
self, nexus_host, intf_type, interface):
"""Check if 'switchport trunk allowed vlan' present.
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns found: True if config already present
"""
result = self.get_interface_switch(nexus_host, intf_type, interface)
try:
if_type = 'l1PhysIf' if intf_type == "ethernet" else 'pcAggrIf'
vlan_list = result['imdata'][0][if_type]
vlan_list = vlan_list['attributes']['trunkVlans']
if vlan_list != "1-4094":
found = True
else:
found = False
except Exception:
found = False
return found
def _get_port_channel_group(
self, nexus_host, intf_type, interface):
"""Look for 'channel-group x' config and return x.
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns pc_group: Returns port channel group if
present else 0
"""
ch_grp = 0
# channel-group only applied to ethernet,
# otherwise, return 0
if intf_type != 'ethernet':
return ch_grp
match_key = "eth" + interface
action = snipp.PATH_GET_PC_MEMBERS
starttime = time.time()
result = self.client.rest_get(action, nexus_host)
self.capture_and_print_timeshot(starttime, "getpc",
switch=nexus_host)
try:
for pcmbr in result['imdata']:
mbr_data = pcmbr['pcRsMbrIfs']['attributes']
if mbr_data['tSKey'] == match_key:
_, nbr = mbr_data['parentSKey'].split("po")
ch_grp = int(nbr)
break
except Exception:
pass
LOG.debug("GET interface %(key)s port channel is %(pc)",
{'key': match_key, 'pc': ch_grp})
return ch_grp
def initialize_all_switch_interfaces(self, interfaces):
"""Configure Nexus interface and get port channel number.
Receive a list of interfaces containing:
:param nexus_host: IP address of Nexus switch
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:returns interface: Appends port channel to each entry
channel number is 0 if none
"""
if not interfaces:
return
starttime = time.time()
for i in range(len(interfaces)):
nexus_host, intf_type, nexus_port, is_native = interfaces[i]
ch_grp = self._get_port_channel_group(
nexus_host, intf_type, nexus_port)
if ch_grp is not 0:
# if channel-group returned, init port-channel
# instead of the provided ethernet interface
intf_type = 'port-channel'
nexus_port = str(ch_grp)
interfaces[i] += (ch_grp,)
present = self._get_interface_switch_trunk_present(
nexus_host, intf_type, nexus_port)
if not present:
self.send_enable_vlan_on_trunk_int(
nexus_host, "", intf_type, nexus_port, False)
self.capture_and_print_timeshot(
starttime, "get_allif",
switch=nexus_host)
def get_nexus_type(self, nexus_host):
"""Given the nexus host, get the type of Nexus switch.
:param nexus_host: IP address of Nexus switch
:returns Nexus type
"""
starttime = time.time()
response = self.client.rest_get(
snipp.PATH_GET_NEXUS_TYPE, nexus_host)
self.capture_and_print_timeshot(
starttime, "gettype",
switch=nexus_host)
if response:
try:
result = response['imdata'][0]["eqptCh"]['attributes']['descr']
except Exception:
result = ''
nexus_type = re.findall(
"Nexus\s*(\d)\d+\s*[0-9A-Z]+\s*"
"[cC]hassis",
result)
if len(nexus_type) > 0:
LOG.debug("GET call returned Nexus type %d",
int(nexus_type[0]))
return int(nexus_type[0])
LOG.warning(_LW("GET call failed to return Nexus type"))
return -1
def start_create_vlan(self):
"""Returns REST API path and config start."""
return snipp.PATH_VLAN_ALL, snipp.BODY_VLAN_ALL_BEG
def end_create_vlan(self, conf_str):
"""Returns current config + end of config."""
conf_str += snipp.BODY_VLAN_ALL_END
return conf_str
def get_create_vlan(self, nexus_host, vlanid, vni, conf_str):
"""Returns an XML snippet for create VLAN on a Nexus Switch."""
starttime = time.time()
if vni:
body_snip = snipp.BODY_VXLAN_ALL_INCR % (vlanid, vni)
else:
body_snip = snipp.BODY_VLAN_ALL_INCR % vlanid
conf_str += body_snip + snipp.BODY_VLAN_ALL_CONT
self.capture_and_print_timeshot(
starttime, "get_create_vlan",
switch=nexus_host)
return conf_str
def set_all_vlan_states(self, nexus_host, vlanid_range):
"""Set the VLAN states to active."""
starttime = time.time()
if not vlanid_range:
LOG.warning("Exiting set_all_vlan_states: No vlans to configure")
return
# Eliminate possible whitespace and separate vlans by commas
vlan_id_list = re.sub(r'\s', '', vlanid_range).split(',')
if not vlan_id_list or not vlan_id_list[0]:
LOG.warning("Exiting set_all_vlan_states: No vlans to configure")
return
path_str, body_vlan_all = self.start_create_vlan()
while vlan_id_list:
rangev = vlan_id_list.pop(0)
if '-' in rangev:
fr, to = rangev.split('-')
max = int(to) + 1
for vlan_id in range(int(fr), max):
body_vlan_all = self.get_create_vlan(
nexus_host, vlan_id, 0, body_vlan_all)
else:
body_vlan_all = self.get_create_vlan(
nexus_host, rangev, 0, body_vlan_all)
body_vlan_all = self.end_create_vlan(body_vlan_all)
self.send_edit_string(
nexus_host, path_str, body_vlan_all)
self.capture_and_print_timeshot(
starttime, "set_all_vlan_states",
switch=nexus_host)
def create_vlan(self, nexus_host,
vlanid, vlanname, vni):
"""Given switch, vlanid, vni, Create a VLAN on Switch."""
starttime = time.time()
path_snip, body_snip = self.start_create_vlan()
body_snip = self.get_create_vlan(nexus_host, vlanid, vni, body_snip)
body_snip = self.end_create_vlan(body_snip)
self.send_edit_string(nexus_host, path_snip, body_snip)
self.capture_and_print_timeshot(
starttime, "create_vlan_seg",
switch=nexus_host)
def delete_vlan(self, nexus_host, vlanid):
"""Delete a VLAN on Nexus Switch given the VLAN ID."""
starttime = time.time()
path_snip = snipp.PATH_VLAN % vlanid
self.client.rest_delete(path_snip, nexus_host)
self.capture_and_print_timeshot(
starttime, "del_vlan",
switch=nexus_host)
def _get_vlan_body_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native, is_delete):
"""Prepares an XML snippet for VLAN on a trunk interface.
:param nexus_host: IP address of Nexus switch
:param vlanid: Vlanid(s) to add to interface
:param intf_type: String which specifies interface type.
example: ethernet
:param interface: String indicating which interface.
example: 1/19
:param is_native: Is native vlan config desired?
:param is_delete: Is this a delete operation?
:returns path_snippet, body_snippet
"""
starttime = time.time()
LOG.debug("NexusDriver get if body config for host %s: "
"if_type %s port %s",
nexus_host, intf_type, interface)
if intf_type == "ethernet":
body_if_type = "l1PhysIf"
path_interface = "phys-[eth" + interface + "]"
else:
body_if_type = "pcAggrIf"
path_interface = "aggr-[po" + interface + "]"
path_snip = (snipp.PATH_IF % (path_interface))
if is_delete:
increment_it = "-"
debug_desc = "delif"
native_vlan = ""
else:
native_vlan = 'vlan-' + str(vlanid)
debug_desc = "createif"
if vlanid is "":
increment_it = ""
else:
increment_it = "+"
if is_native:
body_snip = (snipp.BODY_NATIVE_TRUNKVLAN %
(body_if_type, increment_it + str(vlanid),
str(native_vlan)))
else:
body_snip = (snipp.BODY_TRUNKVLAN %
(body_if_type, increment_it + str(vlanid)))
self.capture_and_print_timeshot(
starttime, debug_desc,
switch=nexus_host)
return path_snip, body_snip
def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native):
"""Disable a VLAN on a trunk interface."""
starttime = time.time()
path_snip, body_snip = self._get_vlan_body_on_trunk_int(
nexus_host, vlanid, intf_type, interface,
is_native, True)
self.send_edit_string(nexus_host, path_snip, body_snip)
self.capture_and_print_timeshot(
starttime, "delif",
switch=nexus_host)
def send_edit_string(self, nexus_host, path_snip, body_snip,
check_to_close_session=True):
"""Sends rest Post request to Nexus switch."""
starttime = time.time()
LOG.debug("NexusDriver edit config for host %s: path: %s body: %s",
nexus_host, path_snip, body_snip)
self.client.rest_post(path_snip, nexus_host, body_snip)
self.capture_and_print_timeshot(
starttime, "send_edit",
switch=nexus_host)
def send_enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
interface, is_native):
"""Gathers and sends an interface trunk XML snippet."""
path_snip, body_snip = self._get_vlan_body_on_trunk_int(
nexus_host, vlanid, intf_type, interface,
is_native, False)
self.send_edit_string(nexus_host, path_snip, body_snip)
def create_and_trunk_vlan(self, nexus_host, vlan_id,
vlan_name, intf_type,
nexus_port, vni,
is_native):
"""Create VLAN and trunk it on the specified ports."""
starttime = time.time()
self.create_vlan(nexus_host, vlan_id, vlan_name, vni)
LOG.debug("NexusDriver created VLAN: %s", vlan_id)
if nexus_port:
self.send_enable_vlan_on_trunk_int(
nexus_host, vlan_id,
intf_type, nexus_port,
is_native)
self.capture_and_print_timeshot(
starttime, "create_all",
switch=nexus_host)
def enable_vxlan_feature(self, nexus_host, nve_int_num, src_intf):
"""Enable VXLAN on the switch."""
# Configure the "feature" commands and NVE interface
# (without "member" subcommand configuration).
# The Nexus 9K will not allow the "interface nve" configuration
# until the "feature nv overlay" command is issued and installed.
# To get around the N9K failing on the "interface nve" command
# send the two XML snippets down separately.
starttime = time.time()
## Do CLI 'feature nv overlay'
self.send_edit_string(nexus_host, snipp.PATH_VXLAN_STATE,
(snipp.BODY_VXLAN_STATE % "enabled"))
# Do CLI 'feature vn-segment-vlan-based'
self.send_edit_string(nexus_host, snipp.PATH_VNSEG_STATE,
(snipp.BODY_VNSEG_STATE % "enabled"))
# Do CLI 'int nve1' to Create nve1
self.send_edit_string(
nexus_host,
(snipp.PATH_NVE_CREATE % nve_int_num),
(snipp.BODY_NVE_CREATE % nve_int_num))
# Do CLI 'no shut
# source-interface loopback %s'
# beneath int nve1
self.send_edit_string(
nexus_host,
(snipp.PATH_NVE_CREATE % nve_int_num),
(snipp.BODY_NVE_ADD_LOOPBACK % ("enabled", src_intf)))
self.capture_and_print_timeshot(
starttime, "enable_vxlan",
switch=nexus_host)
def disable_vxlan_feature(self, nexus_host):
"""Disable VXLAN on the switch."""
# Removing the "feature nv overlay" configuration also
# removes the "interface nve" configuration.
starttime = time.time()
# Do CLI 'no feature nv overlay'
self.send_edit_string(nexus_host, snipp.PATH_VXLAN_STATE,
(snipp.BODY_VXLAN_STATE % "disabled"))
# Do CLI 'no feature vn-segment-vlan-based'
self.send_edit_string(nexus_host, snipp.PATH_VNSEG_STATE,
(snipp.BODY_VNSEG_STATE % "disabled"))
self.capture_and_print_timeshot(
starttime, "disable_vxlan",
switch=nexus_host)
def create_nve_member(self, nexus_host, nve_int_num, vni, mcast_group):
"""Add a member configuration to the NVE interface."""
# Do CLI [no] member vni %s mcast-group %s
# beneath int nve1
starttime = time.time()
path = snipp.PATH_VNI_UPDATE % (nve_int_num, vni)
body = snipp.BODY_VNI_UPDATE % (vni, vni, vni, mcast_group)
self.send_edit_string(nexus_host, path, body)
self.capture_and_print_timeshot(
starttime, "create_nve",
switch=nexus_host)
def delete_nve_member(self, nexus_host, nve_int_num, vni):
"""Delete a member configuration on the NVE interface."""
starttime = time.time()
path_snip = snipp.PATH_VNI_UPDATE % (nve_int_num, vni)
self.client.rest_delete(path_snip, nexus_host)
self.capture_and_print_timeshot(
starttime, "delete_nve",
switch=nexus_host)

View File

@ -0,0 +1,110 @@
# Copyright (c) 2017-2017 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.
# This section is organized by N9K CLI commands
# show inventory
PATH_GET_NEXUS_TYPE = 'api/mo/sys/ch.json'
# conf t
# vlan <a,n-y>
# state active
PATH_VLAN_ALL = 'api/mo.json'
BODY_VLAN_ALL_BEG = '{"topSystem": { "children": [ {"bdEntity":'
BODY_VLAN_ALL_BEG += ' { children": ['
BODY_VLAN_ALL_INCR = ' {"l2BD": {"attributes": {"fabEncap": "vlan-%s",'
BODY_VLAN_ALL_INCR += ' "pcTag": "1", "adminSt": "active"}}}'
BODY_VXLAN_ALL_INCR = ' {"l2BD": {"attributes": {"fabEncap": "vlan-%s",'
BODY_VXLAN_ALL_INCR += ' "pcTag": "1", "adminSt": "active",'
BODY_VXLAN_ALL_INCR += ' "accEncap": "vxlan-%s"}}}'
BODY_VLAN_ALL_CONT = ','
BODY_VLAN_ALL_END = ' ]}}]}}'
# The following was added to make simple Test case results more readible.
BODY_VLAN_ADD_START = (BODY_VLAN_ALL_BEG + BODY_VLAN_ALL_INCR +
BODY_VLAN_ALL_CONT)
BODY_VLAN_ADD_NEXT = BODY_VLAN_ALL_INCR + BODY_VLAN_ALL_CONT
BODY_VLAN_ADD = (BODY_VLAN_ALL_BEG + BODY_VLAN_ALL_INCR +
BODY_VLAN_ALL_CONT + BODY_VLAN_ALL_END)
BODY_VXLAN_ADD = (BODY_VLAN_ALL_BEG + BODY_VXLAN_ALL_INCR +
BODY_VLAN_ALL_CONT + BODY_VLAN_ALL_END)
# conf t
# vlan <n>
# state active
PATH_VLAN = 'api/mo/sys/bd/bd-[vlan-%s].json'
BODY_VLAN_ACTIVE = '{"l2BD": {"attributes": {"adminSt": "active"}}}'
# conf t
# vlan <n>
# state active
# vn-segment <vni>
BODY_VXLAN_ACTIVE = '{"l2BD": {"attributes": {"adminSt": "active",'
BODY_VXLAN_ACTIVE += ' "accEncap": "vxlan-%s"}}}'
# conf t
# int ethernet x/x OR int port-channel n
# where %s is "phys-[eth1/19]" OR "aggr-[po50]"
PATH_IF = 'api/mo/sys/intf/%s.json'
# THEN
# switchport trunk native vlan <vlan>
# switchport trunk allowed vlan none | add <vlan> | remove <vlan>
# first %s is "l1PhysIf" | "pcAggrIf", 2nd trunkvlan string, 3rd one
# native vlan
BODY_TRUNKVLAN = '{"%s": {"attributes": {"trunkVlans": "%s"}}}'
BODY_NATIVE_TRUNKVLAN = '{"%s": {"attributes": {"trunkVlans": "%s",'
BODY_NATIVE_TRUNKVLAN += ' "nativeVlan": "%s"}}}'
# conf t
# feature nv overlay
PATH_VXLAN_STATE = 'api/mo/sys/fm/nvo.json'
# where %s is "enable" | "disable"
BODY_VXLAN_STATE = '{"fmNvo": {"attributes": {"adminSt": "%s"}}}'
# conf t
# feature vn-segment-vlan-based
PATH_VNSEG_STATE = 'api/mo/sys/fm/vnsegment.json'
BODY_VNSEG_STATE = '{"fmVnSegment": {"attributes": {"adminSt": "%s"}}}'
# conf t
# int nve%s
# no shut
# source-interface loopback %s
PATH_NVE_CREATE = 'api/mo/sys/epId-%s.json'
BODY_NVE_CREATE = '{"nvoEp": {"attributes": {"epId": "%s"}}}'
BODY_NVE_ADD_LOOPBACK = '{"nvoEp": {"attributes": {"adminSt": "%s",'
BODY_NVE_ADD_LOOPBACK += ' "sourceInterface": "lo%s"}}}'
# conf t
# int nve%s
# no shut
# source-interface loopback %s
# conf t
# int nve%s
# [no] member vni %s mcast-group %s
PATH_VNI_UPDATE = 'api/mo/sys/epId-%s/nws/vni-%s.json'
BODY_VNI_UPDATE = '{"nvoNw": {"attributes": {"vni": "%s", "vniRangeMin": "%s",'
BODY_VNI_UPDATE += ' "vniRangeMax": "%s", "mcastGroup": "%s", "isMcastRange":'
BODY_VNI_UPDATE += ' "yes", "suppressARP": "no", "associateVrfFlag": "no"}}}'
# channel-group x mode active is not immediately available beneath the
# ethernet interface data. Instead one needs to gather pc channel members
# and search for ethernet interface.
PATH_GET_PC_MEMBERS = 'api/mo/sys/intf.json?query-target=subtree&'
PATH_GET_PC_MEMBERS += 'target-subtree-class=pcRsMbrIfs'

View File

@ -1,4 +1,4 @@
# Copyright (c) 2013-2016 Cisco Systems, Inc.
# Copyright (c) 2013-2017 Cisco Systems, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -33,10 +33,6 @@ EXEC_GET_INTF_SNIPPET = """
<cmd>show running-config interface %s %s</cmd>
"""
EXEC_GET_VERSION_SNIPPET = """
<cmd>show version</cmd>
"""
EXEC_GET_INVENTORY_SNIPPET = """
<cmd>show inventory</cmd>
"""

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015-2016 Cisco Systems, Inc.
# Copyright (c) 2015-2017 Cisco Systems, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -15,13 +15,20 @@
"""
Basic test Class and elements for testing Cisco Nexus platforms.
Most classes in this file do not contain test cases but instead
provide common methods for other classes to utilize. This class
provides the basic methods needed to drive a create or delete
port request thru to the ssh or restapi driver. It verifies the
final content of the data base and verifies what data the
Drivers sent out. There also exists another 'base' class
specifically for Replay testing.
"""
import collections
import mock
import os
from oslo_config import cfg
from oslo_utils import importutils
import re
import six
import testtools
@ -31,6 +38,10 @@ from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
constants as const)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_network_driver)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_network_driver)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_snippets as rest_snipp)
from networking_cisco.plugins.ml2.drivers.cisco.nexus import constants
from networking_cisco.plugins.ml2.drivers.cisco.nexus import exceptions
from networking_cisco.plugins.ml2.drivers.cisco.nexus import mech_cisco_nexus
@ -106,6 +117,55 @@ BAREMETAL_VNIC = u'baremetal'
CONNECT_ERROR = 'Unable to connect to Nexus'
GET_NEXUS_TYPE_RESPONSE = {
"totalCount": "1",
"imdata": [
{
"eqptCh": {
"attributes": {
"descr": "Nexus9000 C9396PX Chassis",
}
}
}
]
}
GET_INTERFACE_RESPONSE = {
"totalCount": "1",
"imdata": [
{
"l1PhysIf": {
"attributes": {
"trunkVlans": ""
}
}
}
]
}
GET_INTERFACE_PCHAN_RESPONSE = {
"totalCount": "1",
"imdata": [
{
"pcAggrIf": {
"attributes": {
"trunkVlans": ""
}
}
}
]
}
GET_NO_PORT_CH_RESPONSE = {
"totalCount": "3",
"imdata": [
]
}
POST = 0
DELETE = 1
## Test snippets used to verify nexus command output
RESULT_ADD_VLAN = """configure\>\s+\<vlan\>\s+\
<vlan-id-create-delete\>\s+\<__XML__PARAM_value\>{0}"""
@ -147,9 +207,6 @@ RESULT_DEL_NATIVE_INTERFACE = """\
RESULT_DEL_NVE_INTERFACE = """\<interface\>\s+\
\<nve\>nve{0}\<\/nve\>\s+[\x20-\x7e]+\s+\<member\>no member vni {1}"""
NEXUS_DRIVER = ('networking_cisco.plugins.ml2.drivers.cisco.nexus.'
'nexus_network_driver.CiscoNexusDriver')
class FakeNetworkContext(object):
"""Network context for testing purposes only."""
@ -291,6 +348,19 @@ class FakeUnbindPortContext(FakePortContext):
return self._bottom_segment
class TestCiscoNexusBaseResults(object):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {}
def get_test_results(self, name):
if name in self.test_results:
return self.test_results[name]
else:
return None
class TestCiscoNexusBase(testlib_api.SqlTestCase):
"""Feature Base Test Class for Cisco ML2 Nexus driver."""
@ -299,19 +369,45 @@ class TestCiscoNexusBase(testlib_api.SqlTestCase):
'nexus_ip_addr host_name nexus_port instance_id vlan_id vxlan_id '
'mcast_group device_owner profile vnic_type')
def _mock_init(self):
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
def mock_init(self):
# This initializes interface responses to prevent
# unnecessary noise to the results.
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none'}
self.mock_ncclient.configure_mock(**data_xml)
def restapi_mock_init(self):
# This initializes RESTAPI responses to prevent
# unnecessary noise to the results.
data_json = {'rest_get.side_effect':
self.get_side_effect}
self.mock_ncclient.configure_mock(**data_json)
def get_side_effect(self, action, ipaddr=None, body=None, headers=None):
eth_path = 'api/mo/sys/intf/phys-'
port_chan_path = 'api/mo/sys/intf/aggr-'
if action == rest_snipp.PATH_GET_NEXUS_TYPE:
return GET_NEXUS_TYPE_RESPONSE
elif action in rest_snipp.PATH_GET_PC_MEMBERS:
return GET_NO_PORT_CH_RESPONSE
elif eth_path in action:
return GET_INTERFACE_RESPONSE
elif port_chan_path in action:
return GET_INTERFACE_PCHAN_RESPONSE
return {}
def _clear_port_dbs(self):
nexus_db_v2.remove_all_nexusport_bindings()
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
"""Sets up mock client, switch, and credentials dictionaries."""
super(TestCiscoNexusBase, self).setUp()
@ -320,20 +416,32 @@ class TestCiscoNexusBase(testlib_api.SqlTestCase):
cfg.CONF.import_opt('rpc_workers', 'neutron.service')
cfg.CONF.set_default('rpc_workers', 0)
# Use a mock netconf client
# Use a mock netconf or REST API client
self.mock_ncclient = mock.Mock()
mock.patch.object(nexus_network_driver.CiscoNexusDriver,
'_import_ncclient',
return_value=self.mock_ncclient).start()
if cfg.CONF.ml2_cisco.nexus_driver == 'restapi':
mock.patch.object(
nexus_restapi_network_driver.CiscoNexusRestapiDriver,
'_import_client',
return_value=self.mock_ncclient).start()
self._verify_results = self._verify_restapi_results
else:
mock.patch.object(
nexus_network_driver.CiscoNexusSshDriver,
'_import_client',
return_value=self.mock_ncclient).start()
self._verify_results = self._verify_ssh_results
self.mock_continue_binding = mock.patch.object(
FakePortContext,
'continue_binding').start()
self._mock_init()
if cfg.CONF.ml2_cisco.nexus_driver == 'restapi':
self.restapi_mock_init()
else:
self.mock_init()
def new_nexus_init(mech_instance):
mech_instance.driver = importutils.import_object(NEXUS_DRIVER)
mech_instance.driver = mech_instance._load_nexus_cfg_driver()
mech_instance.monitor_timeout = (
cfg.CONF.ml2_cisco.switch_heartbeat_time)
mech_instance._ppid = os.getpid()
@ -471,6 +579,57 @@ class TestCiscoNexusBase(testlib_api.SqlTestCase):
port_config.instance_id)
self.assertEqual(1, len(bindings))
def _verify_restapi_results(self, driver_result):
"""Verifies correct entries sent to Nexus."""
posts = 0
deletes = 0
for idx in range(0, len(driver_result)):
if driver_result[idx][3] == POST:
posts += 1
else:
deletes += 1
self.assertEqual(
posts,
len(self.mock_ncclient.rest_post.mock_calls),
"Unexpected driver post calls")
self.assertEqual(
deletes,
len(self.mock_ncclient.rest_delete.mock_calls),
"Unexpected driver delete calls")
post_calls = self.mock_ncclient.rest_post.mock_calls
del_calls = self.mock_ncclient.rest_delete.mock_calls
posts = 0
deletes = 0
for idx in range(0, len(driver_result)):
# assigned None to skip testing this one.
if not driver_result[idx]:
continue
if driver_result[idx][3] == POST:
test_it = post_calls[posts][1]
else:
test_it = del_calls[deletes][1]
self.assertTrue(
(driver_result[idx][0] ==
test_it[0]),
"Expected Rest URI does not match")
if driver_result[idx][1] is not None:
self.assertTrue(
(driver_result[idx][1] ==
test_it[1]),
"Expected Nexus Switch ip does not match")
if driver_result[idx][3] == POST:
self.assertTrue(
(driver_result[idx][2] ==
test_it[2]),
"Expected Rest Body does not match")
posts += 1
else:
deletes += 1
def _delete_port(self, port_config):
"""Tests deletion of a virtual port."""
port_context = self._generate_port_context(port_config)
@ -498,7 +657,7 @@ class TestCiscoNexusBase(testlib_api.SqlTestCase):
port_config.nexus_ip_addr,
port_config.instance_id)
def _verify_results(self, driver_result):
def _verify_ssh_results(self, driver_result):
"""Verifies correct entries sent to Nexus."""
self.assertEqual(

View File

@ -34,7 +34,7 @@ CONNECT_ERROR = 'Unable to connect to Nexus'
class TestCiscoNexusDeviceConfig(object):
"""Unit tests for Cisco ML2 Nexus device driver."""
"""Unit tests Config for Cisco ML2 Nexus device driver."""
test_configs = {
'test_config1':
@ -185,20 +185,105 @@ class TestCiscoNexusDeviceConfig(object):
test_configs = collections.OrderedDict(sorted(test_configs.items()))
# The following contains desired Nexus output for some basic config above.
duplicate_add_port_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)])
duplicate_delete_port_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
class TestCiscoNexusDeviceResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'duplicate_add_port_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'duplicate_del_port_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'add_port2_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 265)]),
'delete_port2_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'add_port2_driver_result2': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)]),
'delete_port2_driver_result2': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)]),
'add_port2_driver_result3': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268)]),
'delete_port2_driver_result3': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268)]),
'add_port_channel_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268)]),
'delete_port_channel_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268)]),
'dual_add_port_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 269)]),
'dual_delete_port_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 269)]),
'migrate_add_host2_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)]),
'add_port_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'del_port_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
}
class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
TestCiscoNexusDeviceConfig):
TestCiscoNexusDeviceConfig,
TestCiscoNexusDeviceResults):
"""Unit tests for Cisco ML2 Nexus device driver."""
@ -208,13 +293,15 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusDevice, self).setUp()
self.mock_ncclient.reset_mock()
self.results = TestCiscoNexusDeviceResults()
def test_create_delete_duplicate_ports(self):
"""Tests creation and deletion of two new virtual Ports."""
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result')
)
self._create_port(
self.test_configs['test_config3'])
@ -245,14 +332,15 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_config3',
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_del_port_driver_result'))
def test_create_delete_duplicate_port_transaction(self):
"""Tests creation and deletion same port transaction."""
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result')
)
self.assertEqual(
1, len(nexus_db_v2.get_nexusport_switch_bindings(
@ -260,7 +348,9 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._create_port(
self.test_configs['test_config1'])
self._verify_results(self.duplicate_add_port_driver_result)
self._verify_results(
self.results.get_test_results('duplicate_add_port_driver_result')
)
self.assertEqual(
1, len(nexus_db_v2.get_nexusport_switch_bindings(
@ -272,32 +362,24 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_config1',
self.duplicate_delete_port_driver_result,
self.results.get_test_results('duplicate_del_port_driver_result'),
nbr_of_bindings=0)
self._basic_delete_verify_port_vlan(
'test_config1',
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_del_port_driver_result'))
def test_create_delete_same_switch_diff_hosts_diff_vlan(self):
"""Test create/delete two Ports, same switch/diff host & vlan."""
add_port2_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 265)])
delete_port2_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result'))
self._create_port(
self.test_configs['test_config2'])
self._verify_results(add_port2_driver_result)
self._verify_results(
self.results.get_test_results('add_port2_driver_result'))
# Verify there are 2 port configs
bindings = nexus_db_v2.get_nexusport_switch_bindings(
@ -310,31 +392,24 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_config2',
delete_port2_driver_result,
self.results.get_test_results('delete_port2_driver_result'),
nbr_of_bindings=1)
self._basic_delete_verify_port_vlan(
'test_config1',
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_del_port_driver_result'))
def test_create_delete_same_switch_diff_hosts_same_vlan(self):
"""Test create/delete two Ports, same switch & vlan/diff host."""
add_port2_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)])
delete_port2_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)])
self._basic_create_verify_port_vlan(
'test_config4',
self.duplicate_add_port_driver_result)
self.results.get_test_results('add_port_driver_result'))
self._create_port(
self.test_configs['test_config5'])
self._verify_results(add_port2_driver_result)
self._verify_results(
self.results.get_test_results('add_port2_driver_result2'))
# Verify there are 2 port configs
bindings = nexus_db_v2.get_nexusport_switch_bindings(
@ -347,34 +422,19 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_config5',
delete_port2_driver_result,
self.results.get_test_results('delete_port2_driver_result2'),
nbr_of_bindings=1)
self._basic_delete_verify_port_vlan(
'test_config4',
self.duplicate_delete_port_driver_result)
self.results.get_test_results('del_port_driver_result'))
def test_create_delete_diff_switch_same_host(self):
"""Test create/delete of two Ports, diff switch/same host."""
add_port2_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268)])
delete_port2_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268)])
self._basic_create_verify_port_vlan(
'test_config_portchannel2',
add_port2_driver_result)
self.results.get_test_results('add_port2_driver_result3'))
# Verify there are 2 port configs. One per switch.
bindings = nexus_db_v2.get_nexusport_switch_bindings(
@ -392,60 +452,35 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# first port removal.
self._basic_delete_verify_port_vlan(
'test_config_portchannel2',
delete_port2_driver_result)
self.results.get_test_results('delete_port2_driver_result3'))
def test_create_delete_portchannel(self):
"""Tests creation of a port over a portchannel."""
duplicate_add_port_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(268),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 268)])
duplicate_delete_port_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 268),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(268)])
self._create_delete_port(
'test_config_portchannel',
duplicate_add_port_driver_result,
duplicate_delete_port_driver_result)
self.results.get_test_results('add_port_channel_driver_result'),
self.results.get_test_results('delete_port_channel_driver_result'))
def test_create_delete_dual(self):
"""Tests creation and deletion of dual ports for single server"""
duplicate_add_port_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('portchannel', '2', 269)])
duplicate_delete_port_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('portchannel', '2', 269)])
self._basic_create_verify_port_vlan(
'test_config_dual',
duplicate_add_port_driver_result,
self.results.get_test_results('dual_add_port_driver_result'),
nbr_of_bindings=2)
self._basic_delete_verify_port_vlan(
'test_config_dual',
duplicate_delete_port_driver_result)
self.results.get_test_results('dual_delete_port_driver_result'))
def test_create_delete_dhcp(self):
"""Tests creation and deletion of ports with device_owner of dhcp."""
self._create_delete_port(
'test_config_dhcp',
self.duplicate_add_port_driver_result,
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result'),
self.results.get_test_results('duplicate_del_port_driver_result'))
def test_create_delete_router_ha_intf(self):
"""Tests creation and deletion of ports with device_owner
@ -454,8 +489,95 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._create_delete_port(
'test_config_router_ha_intf',
self.duplicate_add_port_driver_result,
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result'),
self.results.get_test_results('duplicate_del_port_driver_result'))
def test_nexus_vm_migration(self):
"""Verify VM (live) migration.
Simulate the following:
Nova informs neutron of live-migration with port-update(new host).
This should trigger two update_port_pre/postcommit() calls.
The first one should only change the current host_id and remove the
binding resulting in the mechanism drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_top_bound_segment: previous value
PortContext.current['binding:host_id']: current (new) value
PortContext.top_bound_segment: None
The second one binds the new host resulting in the mechanism
drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_top_bound_segment: None
PortContext.current['binding:host_id']: previous value
PortContext.top_bound_segment: new value
"""
self._basic_create_verify_port_vlan(
'test_config1',
self.results.get_test_results('duplicate_add_port_driver_result'))
binding = nexus_db_v2.get_nexusvm_bindings(
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)[0]
self.assertEqual(
test_cisco_nexus_base.NEXUS_PORT_1,
binding.port_id)
port_context = self._generate_port_context(
self.test_configs['test_config_migrate'],
unbind_port=True)
port_cfg = self.test_configs['test_config1']
port_context.set_orig_port(
port_cfg.instance_id,
port_cfg.host_name,
port_cfg.device_owner,
port_cfg.profile,
port_cfg.vnic_type,
test_cisco_nexus_base.NETID)
self._cisco_mech_driver.create_port_postcommit(port_context)
self._cisco_mech_driver.update_port_precommit(port_context)
self._cisco_mech_driver.update_port_postcommit(port_context)
# Verify that port entry has been deleted.
self.assertRaises(
exceptions.NexusPortBindingNotFound,
nexus_db_v2.get_nexusvm_bindings,
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)
# Clean all the ncclient mock_calls to clear exception
# and other mock_call history.
self.mock_ncclient.reset_mock()
self._basic_create_verify_port_vlan(
'test_config_migrate',
self.results.get_test_results('migrate_add_host2_driver_result'))
# Verify that port entry has been added using new host name.
# Use port_id to verify that 2nd host name was used.
binding = nexus_db_v2.get_nexusvm_bindings(
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)[0]
self.assertEqual(
test_cisco_nexus_base.NEXUS_PORT_2,
binding.port_id)
class TestCiscoNexusDeviceFailure(test_cisco_nexus_base.TestCiscoNexusBase,
TestCiscoNexusDeviceConfig,
TestCiscoNexusDeviceResults):
"""Negative Unit tests for Cisco ML2 Nexus device driver."""
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusDeviceFailure, self).setUp()
self.mock_ncclient.reset_mock()
self.results = TestCiscoNexusDeviceResults()
def test_connect_failure(self):
"""Verifies exception handling during ncclient connect. """
@ -743,7 +865,7 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# Mock to keep track of number of close_session calls.
ncclient_close = mock.patch.object(
nexus_network_driver.CiscoNexusDriver,
nexus_network_driver.CiscoNexusSshDriver,
'_close_session').start()
# Clear out call_count changes during initialization activity
@ -752,15 +874,15 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# Verify that ncclient is not closed by default.
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
self.results.get_test_results('duplicate_add_port_driver_result'))
assert not ncclient_close.called
self._basic_delete_verify_port_vlan(
'test_config1',
self.duplicate_delete_port_driver_result)
self.results.get_test_results('duplicate_del_port_driver_result'))
# Patch to close ncclient session.
mock.patch.object(nexus_network_driver.CiscoNexusDriver,
mock.patch.object(nexus_network_driver.CiscoNexusSshDriver,
'_get_close_ssh_session',
return_value=True).start()
@ -770,7 +892,8 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# after trunk interface calls.
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
self.results.get_test_results(
'duplicate_add_port_driver_result'))
self.assertEqual(2, ncclient_close.call_count)
def test_nexus_extended_vlan_range_failure(self):
@ -801,88 +924,53 @@ class TestCiscoNexusDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# is called in _create_port_valid_exception and caching enabled
self.assertEqual(0, self.mock_ncclient.connect.call_count)
def test_nexus_vm_migration(self):
"""Verify VM (live) migration.
Simulate the following:
Nova informs neutron of live-migration with port-update(new host).
This should trigger two update_port_pre/postcommit() calls.
class TestCiscoNexusInitResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
The first one should only change the current host_id and remove the
binding resulting in the mechanism drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_top_bound_segment: previous value
PortContext.current['binding:host_id']: current (new) value
PortContext.top_bound_segment: None
"""Unit tests driver results for Cisco ML2 Nexus."""
The second one binds the new host resulting in the mechanism
drivers receiving:
PortContext.original['binding:host_id']: previous value
PortContext.original_top_bound_segment: None
PortContext.current['binding:host_id']: previous value
PortContext.top_bound_segment: new value
"""
migrate_add_host2_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)])
test_results = {
# set 1 - switch 1.1.1.1 sets eth 1/10 & 1/20 to None
# set 2 - switch 8.8.8.8 sets eth 1/10 & 1/20 to None
# set 3 - switch 4.4.4.4 sets eth 1/3 & portchannel 2 to None
# set 4 - switch 2.2.2.2 sets portchannel 2 to None
# set 5 - switch 6.6.6.6 sets portchannel 2 to None
# set 6 - switch 7.7.7.7 sets portchannel 2 to None
'duplicate_init_port_driver_result1': (
[test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/10', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/10', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/3', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None')]),
self._basic_create_verify_port_vlan(
'test_config1',
self.duplicate_add_port_driver_result)
binding = nexus_db_v2.get_nexusvm_bindings(
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)[0]
self.assertEqual(
test_cisco_nexus_base.NEXUS_PORT_1,
binding.port_id)
port_context = self._generate_port_context(
self.test_configs['test_config_migrate'],
unbind_port=True)
port_cfg = self.test_configs['test_config1']
port_context.set_orig_port(
port_cfg.instance_id,
port_cfg.host_name,
port_cfg.device_owner,
port_cfg.profile,
port_cfg.vnic_type,
test_cisco_nexus_base.NETID)
self._cisco_mech_driver.create_port_postcommit(port_context)
self._cisco_mech_driver.update_port_precommit(port_context)
self._cisco_mech_driver.update_port_postcommit(port_context)
# Verify that port entry has been deleted.
self.assertRaises(
exceptions.NexusPortBindingNotFound,
nexus_db_v2.get_nexusvm_bindings,
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)
# Clean all the ncclient mock_calls to clear exception
# and other mock_call history.
self.mock_ncclient.reset_mock()
self._basic_create_verify_port_vlan(
'test_config_migrate',
migrate_add_host2_driver_result)
# Verify that port entry has been added using new host name.
# Use port_id to verify that 2nd host name was used.
binding = nexus_db_v2.get_nexusvm_bindings(
test_cisco_nexus_base.VLAN_ID_1,
test_cisco_nexus_base.INSTANCE_1)[0]
self.assertEqual(
test_cisco_nexus_base.NEXUS_PORT_2,
binding.port_id)
# Only one entry to match for last 3 so make None
# so count matches in _verify_results
'duplicate_init_port_driver_result2': (
[test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/20', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/20', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
None,
None,
None])
}
class TestCiscoNexusDeviceInit(test_cisco_nexus_base.TestCiscoNexusBase,
TestCiscoNexusDeviceConfig):
"""Verifies interface vlan allowed none is set when missing."""
def _mock_init(self):
def mock_init(self):
# Prevent default which returns
# 'switchport trunk allowed vlan none'
# in get interface calls so Nexus driver
@ -896,44 +984,60 @@ class TestCiscoNexusDeviceInit(test_cisco_nexus_base.TestCiscoNexusBase,
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusDeviceInit, self).setUp()
self.results = TestCiscoNexusInitResults()
def test_verify_initialization(self):
# set 1 - switch 1.1.1.1 sets eth 1/10 & 1/20 to None
# set 2 - switch 8.8.8.8 sets eth 1/10 & 1/20 to None
# set 3 - switch 4.4.4.4 sets eth 1/3 & portchannel 2 to None
# set 4 - switch 2.2.2.2 sets portchannel 2 to None
# set 5 - switch 6.6.6.6 sets portchannel 2 to None
# set 6 - switch 7.7.7.7 sets portchannel 2 to None
duplicate_init_port_driver_result1 = (
[test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/10', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/10', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/3', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None')])
self._verify_results(
self.results.get_test_results(
'duplicate_init_port_driver_result1'))
self._verify_results(
self.results.get_test_results(
'duplicate_init_port_driver_result2'))
# Only one entry to match for last 3 so make None
# so count matches in _verify_results
duplicate_init_port_driver_result2 = (
[test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/20', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('ethernet', '1\/20', 'None'),
test_cisco_nexus_base.RESULT_INTERFACE.
format('portchannel', '2', 'None'),
None,
None,
None])
self._verify_results(duplicate_init_port_driver_result1)
self._verify_results(duplicate_init_port_driver_result2)
class TestCiscoNexusBaremetalResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'add_port_ethernet_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'delete_port_ethernet_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'add_port_channel_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 267)]),
'delete_port_channel_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'add_port_ethernet_native_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265))]),
'delete_port_ethernet_native_driver_result': (
[(test_cisco_nexus_base.RESULT_DEL_NATIVE_INTERFACE.
format('ethernet', '1\/10') +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265)),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
}
class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
@ -995,69 +1099,14 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
test_cisco_nexus_base.BAREMETAL_VNIC),
}
simple_add_port_ethernet_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)])
simple_delete_port_ethernet_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
simple_add_port_channel_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 267)])
simple_delete_port_channel_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
simple_add_port_ethernet_native_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265))])
simple_delete_port_ethernet_native_driver_result = (
[(test_cisco_nexus_base.RESULT_DEL_NATIVE_INTERFACE.
format('ethernet', '1\/10') +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265)),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusBaremetalDevice, self).setUp()
self.results = TestCiscoNexusBaremetalResults()
def test_create_delete_basic_ethernet_port(self):
"""Basic creation and deletion test of 1 ethernet port."""
# nbr_of_bindings includes reserved port binding
self._basic_create_verify_port_vlan(
'test_config1',
self.simple_add_port_ethernet_driver_result, 2)
# Clean all the ncclient mock_calls so we can evaluate
# results of delete operations.
self.mock_ncclient.reset_mock()
# nbr_of_bindings includes reserved port binding
self._basic_delete_verify_port_vlan(
'test_config1',
self.simple_delete_port_ethernet_driver_result,
nbr_of_bindings=1)
def test_create_delete_basic_port_channel(self):
"""Basic creation and deletion test of 1 port-channel."""
def _init_port_channel(self):
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
data_xml = {'connect.return_value.get.return_value.data_xml':
@ -1065,10 +1114,15 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
'channel-group 469 mode active'}
self.mock_ncclient.configure_mock(**data_xml)
def test_create_delete_basic_ethernet_port(self):
"""Basic creation and deletion test of 1 ethernet port."""
# nbr_of_bindings includes reserved port binding
self._basic_create_verify_port_vlan(
'test_config1',
self.simple_add_port_channel_driver_result, 2)
self.results.get_test_results(
'add_port_ethernet_driver_result'),
2)
# Clean all the ncclient mock_calls so we can evaluate
# results of delete operations.
@ -1077,7 +1131,30 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
# nbr_of_bindings includes reserved port binding
self._basic_delete_verify_port_vlan(
'test_config1',
self.simple_delete_port_channel_driver_result,
self.results.get_test_results(
'delete_port_ethernet_driver_result'),
nbr_of_bindings=1)
def test_create_delete_basic_port_channel(self):
"""Basic creation and deletion test of 1 port-channel."""
self._init_port_channel()
# nbr_of_bindings includes reserved port binding
self._basic_create_verify_port_vlan(
'test_config1',
self.results.get_test_results(
'add_port_channel_driver_result'),
2)
# Clean all the ncclient mock_calls so we can evaluate
# results of delete operations.
self.mock_ncclient.reset_mock()
# nbr_of_bindings includes reserved port binding
self._basic_delete_verify_port_vlan(
'test_config1',
self.results.get_test_results(
'delete_port_channel_driver_result'),
nbr_of_bindings=1)
def test_create_delete_basic_eth_port_is_native(self):
@ -1086,7 +1163,9 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
# nbr_of_bindings includes reserved port binding
self._basic_create_verify_port_vlan(
'test_config_native',
self.simple_add_port_ethernet_native_driver_result, 2)
self.results.get_test_results(
'add_port_ethernet_native_driver_result'),
2)
# Clean all the ncclient mock_calls so we can evaluate
# results of delete operations.
@ -1095,7 +1174,8 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
# nbr_of_bindings includes reserved port binding
self._basic_delete_verify_port_vlan(
'test_config_native',
self.simple_delete_port_ethernet_native_driver_result,
self.results.get_test_results(
'delete_port_ethernet_native_driver_result'),
nbr_of_bindings=1)
def test_create_delete_switch_ip_not_defined(self):
@ -1140,7 +1220,8 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
# nbr_of_bindings includes reserved port binding
self._basic_create_verify_port_vlan(
'',
self.simple_add_port_ethernet_driver_result, 2,
self.results.get_test_results(
'add_port_ethernet_driver_result'), 2,
other_test=local_test_configs['test_config1'])
# Clean all the ncclient mock_calls so we can evaluate
@ -1150,7 +1231,8 @@ class TestCiscoNexusBaremetalDevice(test_cisco_nexus_base.TestCiscoNexusBase):
# nbr_of_bindings includes reserved port binding
self._basic_delete_verify_port_vlan(
'',
self.simple_delete_port_ethernet_driver_result,
self.results.get_test_results(
'delete_port_ethernet_driver_result'),
nbr_of_bindings=1,
other_test=local_test_configs['test_config1'])

View File

@ -115,21 +115,89 @@ class TestCiscoNexusVxlanDeviceConfig(object):
test_configs = collections.OrderedDict(sorted(test_configs.items()))
# The following contains desired Nexus output for some basic config above.
add_port_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)])
delete_port_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
class TestCiscoNexusVxlanResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
# The following contains desired Nexus output for
# some basic config above.
'add_port_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'delete_port_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'add_port2_driver_result': (
[test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)]),
'delete_port2_driver_result': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)]),
'add_port_driver_result3': (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/2', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 267)]),
'delete_port_driver_result3': (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/2', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 267)]),
'add_port_driver_result2': (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70001, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(265, 70001),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 265)]),
'delete_port_driver_result2': (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70001, 265),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
}
class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
@ -144,6 +212,7 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
super(TestCiscoNexusVxlanDevice, self).setUp()
self.mock_ncclient.reset_mock()
self.addCleanup(self._clear_nve_db)
self.results = TestCiscoNexusVxlanResults()
def _clear_nve_db(self):
nexus_db_v2.remove_all_nexusnve_bindings()
@ -193,23 +262,16 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
def test_nexus_vxlan_one_network_two_hosts(self):
"""Tests creation and deletion of two new virtual Ports."""
add_port2_driver_result = (
[test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)])
delete_port2_driver_result = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)])
self._basic_create_verify_port_vlan(
'test_vxlan_config1',
self.add_port_driver_result)
self.results.get_test_results(
'add_port_driver_result'))
self._create_port(
self.test_configs['test_vxlan_config2'])
self._verify_results(add_port2_driver_result)
self._verify_results(
self.results.get_test_results(
'add_port2_driver_result'))
bindings = nexus_db_v2.get_nexusvlan_binding(
test_cisco_nexus_base.VLAN_ID_1,
@ -222,11 +284,14 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_vxlan_config2',
delete_port2_driver_result, nbr_of_bindings=1)
self.results.get_test_results(
'delete_port2_driver_result'),
nbr_of_bindings=1)
self._basic_delete_verify_port_vlan(
'test_vxlan_config1',
self.delete_port_driver_result)
self.results.get_test_results(
'delete_port_driver_result'))
def test_nexus_missing_vxlan_fields(self):
"""Test handling of a VXLAN NexusMissingRequiredFields exception.
@ -324,44 +389,13 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
def test_nexus_vxlan_one_network(self):
"""Test processing for creating one VXLAN segment."""
add_port_driver_result3 = (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70000, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/2', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(267, 70000),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 267)])
delete_port_driver_result3 = (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70000, 267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/2', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 267)])
# Since test_vxlan_config3 & test_vxlan_config4 share
# the same host name they both get processed in the
# next call.
self._basic_create_verify_port_vlan(
'test_vxlan_config3',
add_port_driver_result3)
self.results.get_test_results(
'add_port_driver_result3'))
for switch_ip, nbr_bind in [
(test_cisco_nexus_base.NEXUS_IP_ADDRESS_1, 1),
@ -378,7 +412,8 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
# next call.
self._basic_delete_verify_port_vlan(
'test_vxlan_config3',
delete_port_driver_result3)
self.results.get_test_results(
'delete_port_driver_result3'))
for switch_ip in [
test_cisco_nexus_base.NEXUS_IP_ADDRESS_1,
@ -399,29 +434,17 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
def test_nexus_vxlan_two_network(self):
"""Test processing for creating one VXLAN segment."""
add_port_driver_result2 = (
[test_cisco_nexus_base.RESULT_ADD_NVE_INTERFACE.
format(1, 70001, '255.1.1.1'),
test_cisco_nexus_base.RESULT_ADD_VLAN_VNI.
format(265, 70001),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 265)])
delete_port_driver_result2 = (
[test_cisco_nexus_base.RESULT_DEL_NVE_INTERFACE.
format(1, 70001, 265),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
self._basic_create_verify_port_vlan(
'test_vxlan_config5',
self.add_port_driver_result)
self.results.get_test_results(
'add_port_driver_result'))
self._create_port(
self.test_configs['test_vxlan_config6'],
override_netid=888)
self._verify_results(add_port_driver_result2)
self._verify_results(
self.results.get_test_results(
'add_port_driver_result2'))
binding = nexus_db_v2.get_nve_switch_bindings(
test_cisco_nexus_base.NEXUS_IP_ADDRESS_1)
@ -433,11 +456,14 @@ class TestCiscoNexusVxlanDevice(test_cisco_nexus_base.TestCiscoNexusBase,
self._basic_delete_verify_port_vlan(
'test_vxlan_config6',
delete_port_driver_result2, nbr_of_bindings=1)
self.results.get_test_results(
'delete_port_driver_result2'),
nbr_of_bindings=1)
self._basic_delete_verify_port_vlan(
'test_vxlan_config5',
self.delete_port_driver_result)
self.results.get_test_results(
'delete_port_driver_result'))
try:
binding = nexus_db_v2.get_nve_switch_bindings(

View File

@ -31,6 +31,106 @@ RP_HOST_NAME_DUAL = 'testdualhost'
MAX_REPLAY_COUNT = 4
class TestCiscoNexusReplayResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'driver_result_unique_init': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 'None')]),
'driver_result_unique_add1': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'driver_result_unique_add2': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265)]),
'driver_result_unique_del1': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'driver_result_unique_del2': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'driver_result_unique_2vlan_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')]),
'dupl_vlan_result1_add': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)]),
'dupl_vlan_result2_add': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)]),
'dupl_vlan_result2_del': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)]),
'dupl_vlan_result_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267)]),
'dupl_port_result_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267)]),
'switch_up_result_add': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269)]),
'switch_up_result_del': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269)]),
'switch_restore_result_add': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269)]),
'switch_restore_result_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/2', 269),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(269)]),
'switch_restore_result_del': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/2', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269)]),
}
class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
"""Unit tests for Replay of Cisco ML2 Nexus data."""
test_configs = {
@ -143,30 +243,6 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
{},
test_cisco_nexus_base.NORMAL_VNIC),
}
driver_result_unique_init = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 'None')])
driver_result_unique_add1 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)])
driver_result_unique_add2 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265)])
driver_result_unique_del1 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
driver_result_unique_del2 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
test_configs = collections.OrderedDict(sorted(test_configs.items()))
def setUp(self):
@ -174,117 +250,103 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusReplay, self).setUp()
self.results = TestCiscoNexusReplayResults()
def test_replay_unique_ports(self):
"""Provides replay data and result data for unique ports. """
first_add = {'driver_results': self.
driver_result_unique_add1,
'nbr_db_entries': 1}
second_add = {'driver_results': self.
driver_result_unique_add2,
'nbr_db_entries': 2}
first_del = {'driver_results': self.
driver_result_unique_del1,
'nbr_db_entries': 1}
second_del = {'driver_results': self.
driver_result_unique_del2,
'nbr_db_entries': 0}
driver_result_unique_2vlan_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_add1'),
'nbr_db_entries': 1}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_add2'),
'nbr_db_entries': 2}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_del1'),
'nbr_db_entries': 1}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_del2'),
'nbr_db_entries': 0}
self._process_replay(
'test_replay_unique1',
'test_replay_unique2',
self.driver_result_unique_init,
self.results.get_test_results(
'driver_result_unique_init'),
first_add,
second_add,
driver_result_unique_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_2vlan_replay'),
first_del,
second_del)
def test_replay_duplicate_vlan(self):
"""Provides replay data and result data for duplicate vlans. """
result1_add = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)])
first_add = {'driver_results': result1_add,
'nbr_db_entries': 2}
first_add = {
'driver_results': self.results.get_test_results(
'dupl_vlan_result1_add'),
'nbr_db_entries': 2}
result2_add = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267)])
# TODO(caboucha)
# 'driver_result': [], until the correct fix for
# the following issue is resolved.
# https://review.openstack.org/#/c/241216/
second_add = {'driver_results': result2_add,
'nbr_db_entries': 4}
second_add = {
'driver_results': self.results.get_test_results(
'dupl_vlan_result2_add'),
'nbr_db_entries': 4}
first_del = {'driver_results': [],
'nbr_db_entries': 2}
result2_del = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/20', 267)])
second_del = {'driver_results': result2_del,
'nbr_db_entries': 0}
result_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/20', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267)])
second_del = {
'driver_results': self.results.get_test_results(
'dupl_vlan_result2_del'),
'nbr_db_entries': 0}
self._process_replay('test_replay_duplvlan1',
'test_replay_duplvlan2',
[],
first_add, second_add,
result_replay,
self.results.get_test_results(
'dupl_vlan_result_replay'),
first_del, second_del)
def test_replay_duplicate_ports(self):
"""Provides replay data and result data for duplicate ports. """
first_add = {'driver_results': self.driver_result_unique_add1,
'nbr_db_entries': 1}
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_add1'),
'nbr_db_entries': 1}
# TODO(caboucha)
# 'driver_result': [], until the correct fix for
# the following issue is resolved.
# https://review.openstack.org/#/c/241216/
second_add = {'driver_results': self.driver_result_unique_add1,
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_add1'),
'nbr_db_entries': 2}
first_del = {'driver_results': [],
'nbr_db_entries': 1}
second_del = {'driver_results': self.driver_result_unique_del2,
'nbr_db_entries': 0}
result_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(267)])
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_del2'),
'nbr_db_entries': 0}
self._process_replay('test_replay_duplport1',
'test_replay_duplport2',
[],
first_add, second_add,
result_replay,
self.results.get_test_results(
'dupl_port_result_replay'),
first_del, second_del)
def test_replay_enable_vxlan_feature_failure(self):
@ -378,12 +440,9 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
port_cfg2.nexus_ip_addr, const.SWITCH_INACTIVE)
# Set-up successful creation of port vlan config
result_add = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269)])
self._basic_create_verify_port_vlan('test_replay_dual',
result_add,
self.results.get_test_results(
'switch_up_result_add'),
nbr_of_bindings=1)
# Even though 2nd entry is inactive, there should be
@ -394,12 +453,9 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
port_cfg2.nexus_ip_addr)))
# Clean-up the port entry
result_del = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269)])
self._basic_delete_verify_port_vlan('test_replay_dual',
result_del,
self.results.get_test_results(
'switch_up_result_del'),
nbr_of_bindings=0)
def test_replay_port_success_if_one_switch_restored(self):
@ -417,12 +473,9 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
port_cfg2.nexus_ip_addr, const.SWITCH_INACTIVE)
# Set-up successful creation of port vlan config
result_add = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(269),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/3', 269)])
self._basic_create_verify_port_vlan('test_replay_dual',
result_add,
self.results.get_test_results(
'switch_restore_result_add'),
nbr_of_bindings=1)
# Even though 2nd entry is inactive, there should be
@ -434,25 +487,17 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
# Restore port data for that switch
self._cfg_monitor.check_connections()
result_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/2', 269),
test_cisco_nexus_base.RESULT_ADD_VLAN.format(269)])
self._verify_results(result_replay)
self._verify_results(
self.results.get_test_results(
'switch_restore_result_replay'))
# Clear mock_call history.
self.mock_ncclient.reset_mock()
# Clean-up the port entries
result_del = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/3', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269),
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/2', 269),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(269)])
self._basic_delete_verify_port_vlan('test_replay_dual',
result_del,
self.results.get_test_results(
'switch_restore_result_del'),
nbr_of_bindings=0)
def test_replay_create_fails_if_single_switch_down(self):
@ -495,7 +540,8 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
port_cfg.nexus_ip_addr, const.SWITCH_ACTIVE)
self._basic_create_verify_port_vlan('test_replay_unique1',
self.driver_result_unique_add1)
self.results.get_test_results(
'driver_result_unique_add1'))
# Make switch inactive before delete
self._cisco_mech_driver.set_switch_ip_and_active_state(
@ -544,7 +590,8 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
# Set-up successful creation of port vlan config
self._basic_create_verify_port_vlan('test_replay_unique1',
self.driver_result_unique_add1)
self.results.get_test_results(
'driver_result_unique_add1'))
# Set-up so get_nexus_type driver fails
config = {'connect.return_value.get.side_effect':
@ -581,7 +628,8 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
# Set-up successful creation of port vlan config
self._basic_create_verify_port_vlan('test_replay_unique1',
self.driver_result_unique_add1)
self.results.get_test_results(
'driver_result_unique_add1'))
# Set-up exception during create_vlan
config = {'connect.return_value.edit_config.side_effect':
@ -608,6 +656,7 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
result_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)] + driver_result1)
self._verify_results(result_replay)
# Clear the edit driver exception for next test.
@ -630,7 +679,8 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
# Clean-up the port entry
self._basic_delete_verify_port_vlan('test_replay_unique1',
self.driver_result_unique_del2)
self.results.get_test_results(
'driver_result_unique_del2'))
def test_replay_vlan_batch_failure_during_replay(self):
"""Verifies handling of batch vlan during replay."""
@ -737,8 +787,10 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
port_cfg.nexus_ip_addr, const.SWITCH_ACTIVE)
# Set-up successful creation of port vlan config
self._basic_create_verify_port_vlan('test_replay_unique1',
self.driver_result_unique_add1)
self._basic_create_verify_port_vlan(
'test_replay_unique1',
self.results.get_test_results(
'driver_result_unique_add1'))
# Test 1:
# Set the edit create vlan driver exception
@ -855,9 +907,123 @@ class TestCiscoNexusReplay(test_cisco_nexus_base.TestCiscoNexusReplayBase):
test_cisco_nexus_base.NEXUS_IP_ADDRESS_1))
class TestCiscoNexusBaremetalReplayResults(
test_cisco_nexus_base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'driver_result_unique_eth_init': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 'None')]),
'driver_result_unique_eth_add1': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)]),
'driver_result_unique_eth_add2': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265)]),
'driver_result_unique_eth_del1': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'driver_result_unique_eth_del2': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'driver_result_unique_vPC_init': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 'None')]),
'driver_result_unique_vPC_add1': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 267)]),
'driver_result_unique_vPC_add2': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 265)]),
'driver_result_unique_vPC_del1': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'driver_result_unique_vPC_del2': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'driver_result_unique_native_port_ethernet_add': (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265))]),
'driver_result_unique_native_port_ethernet_del': (
[(test_cisco_nexus_base.RESULT_DEL_NATIVE_INTERFACE.
format('ethernet', '1\/10') +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265)),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'driver_result_unique_2vlan_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')]),
'driver_result_unique_2vlan_vpc_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')]),
'driver_result_unique_vPC470_del1': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '470', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)]),
'driver_result_unique_vPC470_del2': (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '470', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)]),
'driver_result_unique_vPC470_2vlan_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '470', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')]),
'driver_result_unique_2vlan_vpc_enchg_replay': (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')]),
'driver_result_unique_native_2vlan_replay': (
[(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265')),
(test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267')),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
}
class TestCiscoNexusBaremetalReplay(
test_cisco_nexus_base.TestCiscoNexusReplayBase):
"""Unit tests for Replay of Cisco ML2 Nexus data."""
baremetal_profile = {
"local_link_information": [
{
@ -921,70 +1087,6 @@ class TestCiscoNexusBaremetalReplay(
test_cisco_nexus_base.BAREMETAL_VNIC),
}
driver_result_unique_eth_init = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 'None')])
driver_result_unique_eth_add1 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 267)])
driver_result_unique_eth_add2 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265)])
driver_result_unique_eth_del1 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
driver_result_unique_eth_del2 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
driver_result_unique_vPC_init = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 'None')])
driver_result_unique_vPC_add1 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(267),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 267)])
driver_result_unique_vPC_add2 = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', 265)])
driver_result_unique_vPC_del1 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
driver_result_unique_vPC_del2 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '469', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
driver_result_unique_native_port_ethernet_add = (
[test_cisco_nexus_base.RESULT_ADD_VLAN.format(265),
(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', 265))])
driver_result_unique_native_port_ethernet_del = (
[(test_cisco_nexus_base.RESULT_DEL_NATIVE_INTERFACE.
format('ethernet', '1\/10') +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('ethernet', '1\/10', 265)),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
test_configs = collections.OrderedDict(sorted(test_configs.items()))
def setUp(self):
@ -992,71 +1094,81 @@ class TestCiscoNexusBaremetalReplay(
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusBaremetalReplay, self).setUp()
self.results = TestCiscoNexusBaremetalReplayResults()
def _init_port_channel(self, ch_grp):
# with Baremetal config when enet interface associated to port-channel,
# the port-channel interface is configured instead. This config
# causes this to happen.
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'
'channel-group ' + str(ch_grp) + ' mode active'}
self.mock_ncclient.configure_mock(**data_xml)
def test_replay_unique_ethernet_ports(self):
"""Provides replay data and result data for unique ports. """
first_add = {'driver_results': self.
driver_result_unique_eth_add1,
'nbr_db_entries': 2}
second_add = {'driver_results': self.
driver_result_unique_eth_add2,
'nbr_db_entries': 3}
first_del = {'driver_results': self.
driver_result_unique_eth_del1,
'nbr_db_entries': 2}
second_del = {'driver_results': self.
driver_result_unique_eth_del2,
'nbr_db_entries': 1}
driver_result_unique_2vlan_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_add1'),
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_add2'),
'nbr_db_entries': 3}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_del1'),
'nbr_db_entries': 2}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_del2'),
'nbr_db_entries': 1}
self._process_replay(
'test_replay_unique1',
'test_replay_unique2',
self.driver_result_unique_eth_init,
self.results.get_test_results(
'driver_result_unique_eth_init'),
first_add,
second_add,
driver_result_unique_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_2vlan_replay'),
first_del,
second_del)
def test_replay_unique_vPC_ports(self):
"""Provides replay data and result data for unique ports. """
first_add = {'driver_results': self.
driver_result_unique_vPC_add1,
'nbr_db_entries': 2}
second_add = {'driver_results': self.
driver_result_unique_vPC_add2,
'nbr_db_entries': 3}
first_del = {'driver_results': self.
driver_result_unique_vPC_del1,
'nbr_db_entries': 2}
second_del = {'driver_results': self.
driver_result_unique_vPC_del2,
'nbr_db_entries': 1}
driver_result_unique_2vlan_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '469', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add1'),
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add2'),
'nbr_db_entries': 3}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_del1'),
'nbr_db_entries': 2}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_del2'),
'nbr_db_entries': 1}
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'
'channel-group 469 mode active'}
self.mock_ncclient.configure_mock(**data_xml)
self._init_port_channel(469)
self._process_replay(
'test_replay_unique1',
'test_replay_unique2',
self.driver_result_unique_vPC_init,
self.results.get_test_results(
'driver_result_unique_vPC_init'),
first_add,
second_add,
driver_result_unique_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_2vlan_vpc_replay'),
first_del,
second_del)
@ -1064,53 +1176,39 @@ class TestCiscoNexusBaremetalReplay(
"""Provides replay data and result data for unique ports. """
def replay_init():
# This is to cause port-channel to get configured to 470
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'
'channel-group 470 mode active'}
self.mock_ncclient.configure_mock(**data_xml)
# This causes port-channel 470 to get configured instead.
self._init_port_channel(470)
driver_result_unique_vPC470_del1 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '470', 265),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(265)])
driver_result_unique_vPC470_del2 = (
[test_cisco_nexus_base.RESULT_DEL_INTERFACE.
format('port-channel', '470', 267),
test_cisco_nexus_base.RESULT_DEL_VLAN.format(267)])
first_add = {'driver_results': self.
driver_result_unique_vPC_add1,
'nbr_db_entries': 2}
second_add = {'driver_results': self.
driver_result_unique_vPC_add2,
'nbr_db_entries': 3}
first_del = {'driver_results':
driver_result_unique_vPC470_del1,
'nbr_db_entries': 2}
second_del = {'driver_results':
driver_result_unique_vPC470_del2,
'nbr_db_entries': 1}
driver_result_unique_vPC470_2vlan_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('port-channel', '470', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add1'),
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add2'),
'nbr_db_entries': 3}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC470_del1'),
'nbr_db_entries': 2}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC470_del2'),
'nbr_db_entries': 1}
# This is to cause port-channel 469 to get configured
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'
'channel-group 469 mode active'}
self.mock_ncclient.configure_mock(**data_xml)
self._init_port_channel(469)
# Providing replay_init to change channel-group
self._process_replay(
'test_replay_unique1',
'test_replay_unique2',
self.driver_result_unique_vPC_init,
self.results.get_test_results(
'driver_result_unique_vPC_init'),
first_add,
second_add,
driver_result_unique_vPC470_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_vPC470_2vlan_replay'),
first_del,
second_del,
replay_init)
@ -1119,43 +1217,42 @@ class TestCiscoNexusBaremetalReplay(
"""Provides replay data and result data for unique ports. """
def replay_init():
# This is to cause port-channel to get replaced with enet
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'}
self.mock_ncclient.configure_mock(**data_xml)
# This causes port-channel to get replaced with enet
# by eliminating channel-group config from enet config.
if cfg.CONF.ml2_cisco.nexus_driver == 'restapi':
self.restapi_mock_init()
else:
self.mock_init()
first_add = {'driver_results': self.
driver_result_unique_vPC_add1,
'nbr_db_entries': 2}
second_add = {'driver_results': self.
driver_result_unique_vPC_add2,
'nbr_db_entries': 3}
first_del = {'driver_results': self.
driver_result_unique_eth_del1,
'nbr_db_entries': 2}
second_del = {'driver_results': self.
driver_result_unique_eth_del2,
'nbr_db_entries': 1}
driver_result_unique_2vlan_replay = (
[test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267'),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add1'),
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_vPC_add2'),
'nbr_db_entries': 3}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_del1'),
'nbr_db_entries': 2}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_del2'),
'nbr_db_entries': 1}
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
data_xml = {'connect.return_value.get.return_value.data_xml':
'switchport trunk allowed vlan none\n'
'channel-group 469 mode active'}
self.mock_ncclient.configure_mock(**data_xml)
self._init_port_channel(469)
# Providing replay_init to remove port-channel
self._process_replay(
'test_replay_unique1',
'test_replay_unique2',
self.driver_result_unique_vPC_init,
self.results.get_test_results(
'driver_result_unique_vPC_init'),
first_add,
second_add,
driver_result_unique_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_2vlan_vpc_enchg_replay'),
first_del,
second_del,
replay_init)
@ -1163,33 +1260,32 @@ class TestCiscoNexusBaremetalReplay(
def test_replay_unique_native_nonnative_ethernet_ports(self):
"""Test replay with native and nonnative ethernet ports. """
first_add = {'driver_results': self.
driver_result_unique_native_port_ethernet_add,
'nbr_db_entries': 2}
second_add = {'driver_results': self.
driver_result_unique_eth_add1,
'nbr_db_entries': 3}
first_del = {'driver_results': self.
driver_result_unique_eth_del2,
'nbr_db_entries': 2}
second_del = {'driver_results': self.
driver_result_unique_native_port_ethernet_del,
'nbr_db_entries': 1}
driver_result_unique_native_2vlan_replay = (
[(test_cisco_nexus_base.RESULT_ADD_NATIVE_INTERFACE.
format('ethernet', '1\/10', 265) +
'[\x00-\x7f]+' +
test_cisco_nexus_base.RESULT_ADD_INTERFACE.
format('ethernet', '1\/10', '265,267')),
test_cisco_nexus_base.RESULT_ADD_VLAN.format('265,267')])
first_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_native_port_ethernet_add'),
'nbr_db_entries': 2}
second_add = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_add1'),
'nbr_db_entries': 3}
first_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_eth_del2'),
'nbr_db_entries': 2}
second_del = {
'driver_results': self.results.get_test_results(
'driver_result_unique_native_port_ethernet_del'),
'nbr_db_entries': 1}
self._process_replay(
'test_replay_unique_native1',
'test_replay_unique1',
self.driver_result_unique_eth_init,
self.results.get_test_results(
'driver_result_unique_eth_init'),
first_add,
second_add,
driver_result_unique_native_2vlan_replay,
self.results.get_test_results(
'driver_result_unique_native_2vlan_replay'),
first_del,
second_del)

View File

@ -0,0 +1,571 @@
# Copyright (c) 2017 Cisco Systems, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Basic Test Classes using RESTAPI Driver to test Cisco Nexus platforms.
These Classes are based on the original ssh event driver so same
tests occur with same configuration. What's different between
the tests is the resulting driver output which is what
the tests in this class presents to its parent class.
You will notice in this file there are test methods which
are skipped by using 'pass'. This is because these tests
apply to ssh only OR because rerunning the test would be
redundant.
"""
from oslo_config import cfg
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_snippets as snipp)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_base as base)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_events)
class TestCiscoNexusRestDeviceResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'duplicate_add_port_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'duplicate_del_port_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'add_port2_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265')),
base.POST]
],
'delete_port2_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'add_port2_driver_result2': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'delete_port2_driver_result2': [
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST]
],
'add_port2_driver_result3': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_6,
(snipp.BODY_VLAN_ADD % 268),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_6,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+268')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_7,
(snipp.BODY_VLAN_ADD % 268),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_7,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+268')),
base.POST]
],
'delete_port2_driver_result3': [
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_6,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-268')),
base.POST],
[(snipp.PATH_VLAN % '268'),
base.NEXUS_IP_ADDRESS_6,
'',
base.DELETE],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_7,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-268')),
base.POST],
[(snipp.PATH_VLAN % '268'),
base.NEXUS_IP_ADDRESS_7,
'',
base.DELETE]
],
'add_port_channel_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VLAN_ADD % 268),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+268')),
base.POST]
],
'delete_port_channel_driver_result': [
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-268')),
base.POST],
[(snipp.PATH_VLAN % '268'),
base.NEXUS_IP_ADDRESS_2,
'',
base.DELETE]
],
'dual_add_port_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_VLAN_ADD % 269),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+269')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_VLAN_ADD % 269),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+269')),
base.POST]
],
'dual_delete_port_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-269')),
base.POST],
[(snipp.PATH_VLAN % '269'),
base.NEXUS_IP_ADDRESS_DUAL,
'',
base.DELETE],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-269')),
base.POST],
],
'add_port_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'del_port_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_8,
'',
base.DELETE]
],
'migrate_add_host2_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
}
class TestCiscoNexusRestDevice(test_cisco_nexus_events.TestCiscoNexusDevice):
"""Unit tests for Cisco ML2 Nexus restapi device driver"""
def setUp(self):
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
super(TestCiscoNexusRestDevice, self).setUp()
self.results = TestCiscoNexusRestDeviceResults()
def test_create_delete_duplicate_ports(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_duplicate_ports())
def test_create_delete_duplicate_port_transaction(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_duplicate_port_transaction())
def test_create_delete_same_switch_diff_hosts_diff_vlan(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_same_switch_diff_hosts_diff_vlan())
def test_create_delete_same_switch_diff_hosts_same_vlan(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_same_switch_diff_hosts_same_vlan())
def test_create_delete_diff_switch_same_host(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_diff_switch_same_host())
def test_create_delete_portchannel(self):
super(TestCiscoNexusRestDevice, self).test_create_delete_portchannel()
def test_create_delete_dual(self):
super(TestCiscoNexusRestDevice, self).test_create_delete_dual()
def test_create_delete_dhcp(self):
super(TestCiscoNexusRestDevice, self).test_create_delete_dhcp()
def test_create_delete_router_ha_intf(self):
(super(TestCiscoNexusRestDevice, self).
test_create_delete_router_ha_intf())
def test_nexus_vm_migration(self):
super(TestCiscoNexusRestDevice, self).test_nexus_vm_migration()
class TestCiscoNexusRestInitResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
# set 1 - switch 1.1.1.1 sets eth 1/10 & 1/20 to None
# set 2 - switch 8.8.8.8 sets eth 1/10 & 1/20 to None
# set 3 - switch 4.4.4.4 sets eth 1/3 & portchannel 2 to None
# set 4 - switch 2.2.2.2 sets portchannel 2 to None
# set 5 - switch 6.6.6.6 sets portchannel 2 to None
# set 6 - switch 7.7.7.7 sets portchannel 2 to None
'duplicate_init_port_driver_result1': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_8,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '')),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '')),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_6,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '')),
base.POST],
[(snipp.PATH_IF % 'aggr-[po2]'),
base.NEXUS_IP_ADDRESS_7,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '')),
base.POST],
],
}
GET_INTERFACE_NO_TRUNK_RESPONSE = {
"totalCount": "1",
"imdata": [
{
"l1PhysIf": {
"attributes": {
"trunkVlans": "1-4094"
}
}
}
]
}
GET_INTERFACE_PCHAN_NO_TRUNK_RESPONSE = {
"totalCount": "1",
"imdata": [
{
"pcAggrIf": {
"attributes": {
"trunkVlans": "1-4094"
}
}
}
]
}
# Skipped inheriting event class TestCiscoNexusDeviceFailure
# since some tests are generic and need not be executed twice
# and some apply only to SSH driver.
class TestCiscoNexusRestDeviceInit(
test_cisco_nexus_events.TestCiscoNexusDeviceInit):
"""Verifies interface vlan allowed none is set when missing."""
def get_init_side_effect(
self, action, ipaddr=None, body=None, headers=None):
eth_path = 'api/mo/sys/intf/phys-'
port_chan_path = 'api/mo/sys/intf/aggr-'
if action == snipp.PATH_GET_NEXUS_TYPE:
return base.GET_NEXUS_TYPE_RESPONSE
elif action in snipp.PATH_GET_PC_MEMBERS:
return base.GET_NO_PORT_CH_RESPONSE
elif eth_path in action:
return GET_INTERFACE_NO_TRUNK_RESPONSE
elif port_chan_path in action:
return GET_INTERFACE_PCHAN_NO_TRUNK_RESPONSE
return {}
def restapi_mock_init(self):
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
data_json = {'rest_get.side_effect':
self.get_init_side_effect}
self.mock_ncclient.configure_mock(**data_json)
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusRestDeviceInit, self).setUp()
self.results = TestCiscoNexusRestInitResults()
def test_verify_initialization(self):
self._verify_results(
self.results.get_test_results(
'duplicate_init_port_driver_result1'))
class TestCiscoNexusRestBaremetalResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'add_port_ethernet_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'delete_port_ethernet_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'add_port_channel_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+267')),
base.POST]
],
'delete_port_channel_driver_result': [
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'add_port_ethernet_native_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_NATIVE_TRUNKVLAN % ('l1PhysIf', '+265', 'vlan-265')),
base.POST]
],
'delete_port_ethernet_native_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_NATIVE_TRUNKVLAN % ('l1PhysIf', '-265', '')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
}
GET_PORT_CH_RESPONSE = {
"totalCount": "3",
"imdata": [
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po1",
"tSKey": "eth1/11",
}
}
},
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po469",
"tSKey": "eth1/10",
}
}
},
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po2",
"tSKey": "eth1/12",
}
}
}
]
}
class TestCiscoNexusRestBaremetalDevice(
test_cisco_nexus_events.TestCiscoNexusBaremetalDevice):
"""Tests for Cisco ML2 Nexus baremetal RESTAPI device driver."""
def get_init_side_effect(
self, action, ipaddr=None, body=None, headers=None):
eth_path = 'api/mo/sys/intf/phys-'
port_chan_path = 'api/mo/sys/intf/aggr-'
if action == snipp.PATH_GET_NEXUS_TYPE:
return base.GET_NEXUS_TYPE_RESPONSE
elif action in snipp.PATH_GET_PC_MEMBERS:
return GET_PORT_CH_RESPONSE
elif eth_path in action:
return base.GET_INTERFACE_RESPONSE
elif port_chan_path in action:
return base.GET_INTERFACE_PCHAN_RESPONSE
return {}
def _init_port_channel(self):
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
data_json = {'rest_get.side_effect':
self.get_init_side_effect}
self.mock_ncclient.configure_mock(**data_json)
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusRestBaremetalDevice, self).setUp()
self.results = TestCiscoNexusRestBaremetalResults()
def test_create_delete_basic_ethernet_port(self):
(super(TestCiscoNexusRestBaremetalDevice, self).
test_create_delete_basic_ethernet_port())
def test_create_delete_basic_port_channel(self):
(super(TestCiscoNexusRestBaremetalDevice, self).
test_create_delete_basic_port_channel())
def test_create_delete_basic_eth_port_is_native(self):
(super(TestCiscoNexusRestBaremetalDevice, self).
test_create_delete_basic_eth_port_is_native())
def test_create_delete_switch_ip_not_defined(self):
(super(TestCiscoNexusRestBaremetalDevice, self).
test_create_delete_switch_ip_not_defined())
# Skipped inheriting event class TestCiscoNexusNonCacheSshDevice
# since it does not apply to REST API

View File

@ -0,0 +1,255 @@
#Copyright (c) 2017 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.
"""
VXLAN Test Class using RESTAPI Driver to test Cisco Nexus platforms.
These Classes are based on the original ssh VXLAN event driver
so same tests occur with same configuration. What's different
between the tests is the resulting driver output which is what
the tests in this class presents to its parent class.
You will notice in this file there are test methods which
are skipped by using 'pass'. This is because these tests
apply to ssh only OR because rerunning the test would be
redundant.
"""
from oslo_config import cfg
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_snippets as snipp)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_base as base)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_events_vxlan)
class TestCiscoNexusRestVxlanResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
# The following contains desired Nexus output for
# some basic vxlan config.
'add_port_driver_result': [
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VNI_UPDATE % (
'70000', '70000', '70000',
base.MCAST_GROUP)),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VXLAN_ADD % (267, 70000)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'delete_port_driver_result': [
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'add_port2_driver_result': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VXLAN_ADD % (267, 70000)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'delete_port2_driver_result': [
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
],
'add_port_driver_result3': [
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VNI_UPDATE % (
'70000', '70000', '70000',
base.MCAST_GROUP)),
base.POST],
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VNI_UPDATE % (
'70000', '70000', '70000',
base.MCAST_GROUP)),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VXLAN_ADD % (267, 70000)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VXLAN_ADD % (267, 70000)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/2]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VXLAN_ADD % (267, 70000)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'delete_port_driver_result3': [
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE],
[(snipp.PATH_VNI_UPDATE % ('1', '70000')),
base.NEXUS_IP_ADDRESS_2,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/2]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_2,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST]
],
'add_port_driver_result2': [
[(snipp.PATH_VNI_UPDATE % ('1', '70001')),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VNI_UPDATE % (
'70001', '70001', '70001',
base.MCAST_GROUP)),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VXLAN_ADD % (265, 70001)),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265')),
base.POST]
],
'delete_port_driver_result2': [
[(snipp.PATH_VNI_UPDATE % ('1', '70001')),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
}
class TestCiscoNexusRestVxlanDevice(
test_cisco_nexus_events_vxlan.TestCiscoNexusVxlanDevice):
"""Unit tests for Cisco ML2 VXLAN Nexus device driver."""
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusRestVxlanDevice, self).setUp()
self.mock_ncclient.reset_mock()
self.addCleanup(self._clear_nve_db)
self.results = TestCiscoNexusRestVxlanResults()
def test_enable_vxlan_feature_failure(self):
pass
def test_disable_vxlan_feature_failure(self):
pass
def test_create_nve_member_failure(self):
pass
def test_delete_nve_member_failure(self):
pass
def test_nexus_vxlan_one_network_two_hosts(self):
(super(TestCiscoNexusRestVxlanDevice, self).
test_nexus_vxlan_one_network_two_hosts())
def test_nexus_missing_vxlan_fields(self):
pass
def test_nexus_vxlan_bind_port(self):
pass
def test_nexus_vxlan_bind_port_no_physnet(self):
pass
def test_nexus_vxlan_bind_port_no_dynamic_segment(self):
pass
def test_nexus_vxlan_one_network(self):
(super(TestCiscoNexusRestVxlanDevice, self).
test_nexus_vxlan_one_network())
def test_nexus_vxlan_two_network(self):
(super(TestCiscoNexusRestVxlanDevice, self).
test_nexus_vxlan_two_network())

View File

@ -0,0 +1,644 @@
# Copyright (c) 2017 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.
"""
Replay Test Classes using RESTAPI Driver to test Cisco Nexus platforms.
These Classes are based on the original ssh event driver so same
tests occur with same configuration. What's different between
the tests is the resulting driver output which is what
the tests in this class presents to its parent class.
You will notice in this file there are test methods which
are skipped by using 'pass'. This is because these tests
apply to ssh only OR because rerunning the test would be
redundant.
"""
from oslo_config import cfg
from networking_cisco.plugins.ml2.drivers.cisco.nexus import (
nexus_restapi_snippets as snipp)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_base as base)
from networking_cisco.tests.unit.ml2.drivers.cisco.nexus import (
test_cisco_nexus_replay)
class TestCiscoNexusRestReplayResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'driver_result_unique_init': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
],
'driver_result_unique_add1': [
[snipp.PATH_VLAN_ALL,
None,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
None,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'driver_result_unique_add2': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265')),
base.POST]
],
'driver_result_unique_del1': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_del2': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
None,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
None,
'',
base.DELETE]
],
'driver_result_unique_2vlan_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
],
'dupl_vlan_result1_add': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'dupl_vlan_result2_add': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'dupl_vlan_result2_del': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_2,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST]
],
'dupl_vlan_result_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/20]'),
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_2,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
],
'dupl_port_result_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_3,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_3,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
],
'switch_up_result_add': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_VLAN_ADD % 269),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+269')),
base.POST]
],
'switch_up_result_del': [
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-269')),
base.POST],
[(snipp.PATH_VLAN % '269'),
base.NEXUS_IP_ADDRESS_DUAL,
'',
base.DELETE]
],
'switch_restore_result_add': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_VLAN_ADD % 269),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+269')),
base.POST]
],
'switch_restore_result_replay': [
[(snipp.PATH_IF % 'phys-[eth1/2]'),
base.NEXUS_IP_ADDRESS_DUAL2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+269')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_DUAL2,
(snipp.BODY_VLAN_ADD % 269),
base.POST]
],
'switch_restore_result_del': [
[(snipp.PATH_IF % 'phys-[eth1/3]'),
base.NEXUS_IP_ADDRESS_DUAL,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-269')),
base.POST],
[(snipp.PATH_VLAN % '269'),
base.NEXUS_IP_ADDRESS_DUAL,
'',
base.DELETE],
[(snipp.PATH_IF % 'phys-[eth1/2]'),
base.NEXUS_IP_ADDRESS_DUAL2,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-269')),
base.POST],
[(snipp.PATH_VLAN % '269'),
base.NEXUS_IP_ADDRESS_DUAL2,
'',
base.DELETE]
],
}
class TestCiscoNexusRestReplay(test_cisco_nexus_replay.TestCiscoNexusReplay):
"""Unit tests for Replay of Cisco ML2 Nexus data."""
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusRestReplay, self).setUp()
self.results = TestCiscoNexusRestReplayResults()
def test_replay_unique_ports(self):
super(TestCiscoNexusRestReplay, self).test_replay_unique_ports()
def test_replay_duplicate_vlan(self):
super(TestCiscoNexusRestReplay, self).test_replay_duplicate_vlan()
def test_replay_duplicate_ports(self):
super(TestCiscoNexusRestReplay, self).test_replay_duplicate_ports()
def test_replay_enable_vxlan_feature_failure(self):
pass
def test_replay_disable_vxlan_feature_failure(self):
pass
def test_replay_create_nve_member_failure(self):
pass
def test_replay_delete_nve_member_failure(self):
pass
def test_replay_create_vlan_failure(self):
pass
def test_replay_delete_vlan_failure(self):
pass
def test_replay_create_trunk_failure(self):
pass
def test_replay_delete_trunk_failure(self):
pass
def test_replay_new_port_success_if_one_switch_up(self):
(super(TestCiscoNexusRestReplay, self).
test_replay_new_port_success_if_one_switch_up())
def test_replay_port_success_if_one_switch_restored(self):
(super(TestCiscoNexusRestReplay, self).
test_replay_port_success_if_one_switch_restored())
def test_replay_create_fails_if_single_switch_down(self):
(super(TestCiscoNexusRestReplay, self).
test_replay_create_fails_if_single_switch_down())
def test_replay_update_fails_if_single_switch_down(self):
(super(TestCiscoNexusRestReplay, self).
test_replay_update_fails_if_single_switch_down())
def test_replay_delete_success_if_switch_down(self):
(super(TestCiscoNexusRestReplay, self).
test_replay_delete_success_if_switch_down())
def test_replay_get_nexus_type_failure_two_switches(self):
pass
def test_replay_get_nexus_type_failure(self):
pass
def test_replay_create_vlan_failure_during_replay(self):
pass
def test_replay_vlan_batch_failure_during_replay(self):
pass
def test_replay_no_retry_failure_handling(self):
pass
class TestCiscoNexusRestBaremetalReplayResults(base.TestCiscoNexusBaseResults):
"""Unit tests driver results for Cisco ML2 Nexus."""
test_results = {
'driver_result_unique_init': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '')),
base.POST],
],
'driver_result_unique_eth_add1': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+267')),
base.POST]
],
'driver_result_unique_eth_add2': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265')),
base.POST]
],
'driver_result_unique_eth_del1': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_eth_del2': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_vPC_init': [
[(snipp.PATH_IF % 'aggr--[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '')),
base.POST],
],
'driver_result_unique_vPC_add1': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 267),
base.POST],
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+267')),
base.POST]
],
'driver_result_unique_vPC_add2': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+265')),
base.POST]
],
'driver_result_unique_vPC_del1': [
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_vPC_del2': [
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_native_port_ethernet_add': [
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD % 265),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_NATIVE_TRUNKVLAN % ('l1PhysIf', '+265', 'vlan-265')),
base.POST]
],
'driver_result_unique_native_port_ethernet_del': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_NATIVE_TRUNKVLAN % ('l1PhysIf', '-265', '')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_2vlan_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
],
'driver_result_unique_2vlan_vpc_replay': [
[(snipp.PATH_IF % 'aggr-[po469]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
],
'driver_result_unique_vPC470_del1': [
[(snipp.PATH_IF % 'aggr-[po470]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-265')),
base.POST],
[(snipp.PATH_VLAN % '265'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_vPC470_del2': [
[(snipp.PATH_IF % 'aggr-[po470]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '-267')),
base.POST],
[(snipp.PATH_VLAN % '267'),
base.NEXUS_IP_ADDRESS_1,
'',
base.DELETE]
],
'driver_result_unique_vPC470_2vlan_replay': [
[(snipp.PATH_IF % 'aggr-[po470]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('pcAggrIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
],
'driver_result_unique_2vlan_vpc_enchg_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
],
'driver_result_unique_native_2vlan_replay': [
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_NATIVE_TRUNKVLAN % ('l1PhysIf', '+265', 'vlan-265')),
base.POST],
[(snipp.PATH_IF % 'phys-[eth1/10]'),
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_TRUNKVLAN % ('l1PhysIf', '+265,267')),
base.POST],
[snipp.PATH_VLAN_ALL,
base.NEXUS_IP_ADDRESS_1,
(snipp.BODY_VLAN_ADD_START % 265) + (
snipp.BODY_VLAN_ADD_NEXT % 267) + snipp.BODY_VLAN_ALL_END,
base.POST]
]
}
GET_PORT_CH_RESPONSE = {
"totalCount": "3",
"imdata": [
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po1",
"tSKey": "eth1/11",
}
}
},
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po469",
"tSKey": "eth1/10",
}
}
},
{
"pcRsMbrIfs": {
"attributes": {
"parentSKey": "po2",
"tSKey": "eth1/12",
}
}
}
]
}
class TestCiscoNexusRestBaremetalReplay(
test_cisco_nexus_replay.TestCiscoNexusBaremetalReplay):
def get_init_side_effect(
self, action, ipaddr=None, body=None, headers=None):
eth_path = 'api/mo/sys/intf/phys-'
port_chan_path = 'api/mo/sys/intf/aggr-'
if action == snipp.PATH_GET_NEXUS_TYPE:
return base.GET_NEXUS_TYPE_RESPONSE
elif action in snipp.PATH_GET_PC_MEMBERS:
return GET_PORT_CH_RESPONSE
elif eth_path in action:
return base.GET_INTERFACE_RESPONSE
elif port_chan_path in action:
return base.GET_INTERFACE_PCHAN_RESPONSE
return {}
def _init_port_channel(self, ch_grp):
# this is to prevent interface initialization from occurring
# which adds unnecessary noise to the results.
GET_PORT_CH_RESPONSE['imdata'][1]['pcRsMbrIfs'][
'attributes']['parentSKey'] = ('po' + str(ch_grp))
data_json = {'rest_get.side_effect':
self.get_init_side_effect}
self.mock_ncclient.configure_mock(**data_json)
def setUp(self):
"""Sets up mock ncclient, and switch and credentials dictionaries."""
cfg.CONF.set_override('nexus_driver', 'restapi', 'ml2_cisco')
cfg.CONF.set_override('never_cache_ssh_connection', False, 'ml2_cisco')
super(TestCiscoNexusRestBaremetalReplay, self).setUp()
self.results = TestCiscoNexusRestBaremetalReplayResults()
def test_replay_unique_ethernet_ports(self):
(super(TestCiscoNexusRestBaremetalReplay, self).
test_replay_unique_ethernet_ports())
def test_replay_unique_vPC_ports(self):
(super(TestCiscoNexusRestBaremetalReplay, self).
test_replay_unique_vPC_ports())
def test_replay_unique_vPC_ports_chg_vPC_nbr(self):
(super(TestCiscoNexusRestBaremetalReplay, self).
test_replay_unique_vPC_ports_chg_vPC_nbr())
def test_replay_unique_vPC_ports_chg_to_enet(self):
(super(TestCiscoNexusRestBaremetalReplay, self).
test_replay_unique_vPC_ports_chg_to_enet())
def test_replay_unique_native_nonnative_ethernet_ports(self):
(super(TestCiscoNexusRestBaremetalReplay, self).
test_replay_unique_native_nonnative_ethernet_ports())
#The tests in class below is reproduced this is does not apply to restapis.
#class TestCiscoNexusNonCachedSshReplay(
# test_cisco_nexus_base.TestCiscoNexusReplayBase):

View File

@ -101,6 +101,10 @@ neutronclient.extension =
policy_profile = networking_cisco.neutronclient.policyprofile
network_profile = networking_cisco.neutronclient.networkprofile
networking_cisco.ml2.nexus_driver =
ncclient = networking_cisco.plugins.ml2.drivers.cisco.nexus.nexus_network_driver:CiscoNexusSshDriver
restapi = networking_cisco.plugins.ml2.drivers.cisco.nexus.nexus_restapi_network_driver:CiscoNexusRestapiDriver
# Extension Firewall drivers for SAF
services.firewall.native.drivers =
native = networking_cisco.apps.saf.server.services.firewall.native.drivers.native:NativeFirewall