Providernet extension support for the Cisco Nexus plugin

Implements blueprint provider-network-extensions-cisco

Change-Id: Ia22c21a7a66d22040811a9b43e7749892405e5e7
This commit is contained in:
HenryGessau 2013-07-09 10:49:40 -04:00
parent b5d1c993de
commit 731401e59c
15 changed files with 495 additions and 403 deletions

View File

@ -20,6 +20,29 @@
# vlan_name_prefix = q- # vlan_name_prefix = q-
# Example: vlan_name_prefix = vnet- # Example: vlan_name_prefix = vnet-
# (StrOpt) A short prefix to prepend to the VLAN number when creating a
# provider VLAN interface. For example, if an interface is being created
# for provider VLAN 3003 it will be named 'p-3003' using the default prefix.
#
# provider_vlan_name_prefix = p-
# Example: provider_vlan_name_prefix = PV-
# (BoolOpt) A flag indicating whether Openstack networking should manage the
# creation and removal of VLAN interfaces for provider networks on the Nexus
# switches. If the flag is set to False then Openstack will not create or
# remove VLAN interfaces for provider networks, and the administrator needs
# to manage these interfaces manually or by external orchestration.
#
# provider_vlan_auto_create = True
# (BoolOpt) A flag indicating whether Openstack networking should manage
# the adding and removing of provider VLANs from trunk ports on the Nexus
# switches. If the flag is set to False then Openstack will not add or
# remove provider VLANs from trunk ports, and the administrator needs to
# manage these operations manually or by external orchestration.
#
# provider_vlan_auto_trunk = True
# (StrOpt) Period-separated module path to the model class to use for # (StrOpt) Period-separated module path to the model class to use for
# the Cisco neutron plugin. # the Cisco neutron plugin.
# #

View File

@ -0,0 +1,60 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 OpenStack Foundation
#
# 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.
#
"""Add cisco_provider_networks table
Revision ID: e6b16a30d97
Revises: 2032abe8edac
Create Date: 2013-07-18 21:46:12.792504
"""
# revision identifiers, used by Alembic.
revision = 'e6b16a30d97'
down_revision = '2032abe8edac'
# Change to ['*'] if this migration applies to all plugins
migration_for_plugins = [
'neutron.plugins.cisco.network_plugin.PluginV2'
]
from alembic import op
import sqlalchemy as sa
from neutron.db import migration
def upgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return
op.create_table(
'cisco_provider_networks',
sa.Column('network_id', sa.String(length=36), nullable=False),
sa.Column('network_type', sa.String(length=255), nullable=False),
sa.Column('segmentation_id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('network_id')
)
def downgrade(active_plugin=None, options=None):
if not migration.should_run(active_plugin, migration_for_plugins):
return
op.drop_table('cisco_provider_networks')

View File

@ -17,48 +17,24 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc. # @author: Sumit Naiksatam, Cisco Systems, Inc.
PLUGINS = 'PLUGINS' # Attachment attributes
INVENTORY = 'INVENTORY' INSTANCE_ID = 'instance_id'
TENANT_ID = 'tenant_id'
TENANT_NAME = 'tenant_name'
HOST_NAME = 'host_name'
PORT_STATE = 'port-state' # Network attributes
PORT_UP = "ACTIVE" NET_ID = 'id'
PORT_DOWN = "DOWN" NET_NAME = 'name'
NET_VLAN_ID = 'vlan_id'
NET_VLAN_NAME = 'vlan_name'
NET_PORTS = 'ports'
UUID = 'uuid' # Network types
TENANTID = 'tenant_id' NETWORK_TYPE_FLAT = 'flat'
NETWORKID = 'network_id' NETWORK_TYPE_LOCAL = 'local'
NETWORKNAME = 'name' NETWORK_TYPE_VLAN = 'vlan'
NETWORKPORTS = 'ports' NETWORK_TYPE_NONE = 'none'
INTERFACEID = 'interface_id'
PORTSTATE = 'state'
PORTID = 'port_id'
PPNAME = 'name'
PPVLANID = 'vlan_id'
PPQOS = 'qos'
VLANID = 'vlan_id'
VLANNAME = 'vlan_name'
QOS = 'qos'
ATTACHMENT = 'attachment'
PORT_ID = 'port-id'
NET_ID = 'net-id'
NET_NAME = 'net-name'
NET_PORTS = 'net-ports'
NET_VLAN_NAME = 'net-vlan-name'
NET_VLAN_ID = 'net-vlan-id'
NET_TENANTS = 'net-tenants'
TENANT_ID = 'tenant-id'
TENANT_NETWORKS = 'tenant-networks'
TENANT_NAME = 'tenant-name'
TENANT_QOS_LEVELS = 'tenant-qos-levels'
TENANT_CREDENTIALS = 'tenant-credentials'
QOS_LEVEL_ID = 'qos_id'
QOS_LEVEL_NAME = 'qos_name'
QOS_LEVEL_ASSOCIATIONS = 'qos-level-associations'
QOS_LEVEL_DESCRIPTION = 'qos_desc'
CREDENTIAL_ID = 'credential_id' CREDENTIAL_ID = 'credential_id'
CREDENTIAL_NAME = 'credential_name' CREDENTIAL_NAME = 'credential_name'
@ -71,52 +47,13 @@ PASSWORD = 'password'
LOGGER_COMPONENT_NAME = "cisco_plugin" LOGGER_COMPONENT_NAME = "cisco_plugin"
RESERVED_NIC_HOSTNAME = "reserved-dynamic-nic-hostname"
RESERVED_NIC_NAME = "reserved-dynamic-nic-device-name"
RHEL_DEVICE_NAME_REPFIX = "eth"
NEXUS_PLUGIN = 'nexus_plugin' NEXUS_PLUGIN = 'nexus_plugin'
VSWITCH_PLUGIN = 'vswitch_plugin' VSWITCH_PLUGIN = 'vswitch_plugin'
PLUGIN_OBJ_REF = 'plugin-obj-ref'
PARAM_LIST = 'param-list'
DEVICE_IP = 'device_ip' DEVICE_IP = 'device_ip'
NO_VLAN_ID = 0
HOST_LIST = 'host_list'
HOST_1 = 'host_1'
VIF_DESC = 'vif_desc'
DEVICENAME = 'device'
IP_ADDRESS = 'ip_address'
CHASSIS_ID = 'chassis_id'
BLADE_ID = 'blade_id'
HOST_NAME = 'host_name'
INSTANCE_ID = 'instance_id'
VIF_ID = 'vif_id'
PROJECT_ID = 'project_id'
NETWORK_ADMIN = 'network_admin' NETWORK_ADMIN = 'network_admin'
NETID_LIST = 'net_id_list'
DELIMITERS = "[,;:\b\s]"
UUID_LENGTH = 36
UNPLUGGED = '(detached)'
ASSOCIATION_STATUS = 'association_status'
ATTACHED = 'attached'
DETACHED = 'detached'
NETWORK = 'network' NETWORK = 'network'
PORT = 'port' PORT = 'port'
BASE_PLUGIN_REF = 'base_plugin_ref' BASE_PLUGIN_REF = 'base_plugin_ref'

View File

@ -79,6 +79,11 @@ class CredentialAlreadyExists(exceptions.NeutronException):
"for tenant %(tenant_id)s") "for tenant %(tenant_id)s")
class ProviderNetworkExists(exceptions.NeutronException):
"""Provider network already exists."""
message = _("Provider network %s already exists")
class NexusComputeHostNotConfigured(exceptions.NeutronException): class NexusComputeHostNotConfigured(exceptions.NeutronException):
"""Connection to compute host is not configured.""" """Connection to compute host is not configured."""
message = _("Connection to %(host)s is not configured.") message = _("Connection to %(host)s is not configured.")

View File

@ -1,48 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2011 Cisco Systems, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Sumit Naiksatam, Cisco Systems, Inc.
import hashlib
import logging
from neutron.plugins.cisco.common import cisco_constants as const
LOG = logging.getLogger(__name__)
def get16ByteUUID(uuid):
"""Return first 16 bytes of UUID.
Used when smaller unique ID is required.
"""
return hashlib.md5(uuid).hexdigest()[:16]
def make_net_dict(net_id, net_name, ports):
"""Helper funciton."""
res = {const.NET_ID: net_id, const.NET_NAME: net_name}
res[const.NET_PORTS] = ports
return res
def make_port_dict(port_id, port_state, net_id, attachment):
"""Helper funciton."""
res = {const.PORT_ID: port_id, const.PORT_STATE: port_state}
res[const.NET_ID] = net_id
res[const.ATTACHMENT] = attachment
return res

View File

@ -34,6 +34,14 @@ cisco_plugins_opts = [
cisco_opts = [ cisco_opts = [
cfg.StrOpt('vlan_name_prefix', default='q-', cfg.StrOpt('vlan_name_prefix', default='q-',
help=_("VLAN Name prefix")), help=_("VLAN Name prefix")),
cfg.StrOpt('provider_vlan_name_prefix', default='p-',
help=_("VLAN Name prefix for provider vlans")),
cfg.BoolOpt('provider_vlan_auto_create', default=True,
help='Provider VLANs are automatically created as needed '
'on the Nexus switch'),
cfg.BoolOpt('provider_vlan_auto_trunk', default=True,
help='Provider VLANs are automatically trunked as needed '
'on the ports of the Nexus switch'),
cfg.BoolOpt('svi_round_robin', default=False, cfg.BoolOpt('svi_round_robin', default=False,
help=_("Distribute SVI interfaces over all switches")), help=_("Distribute SVI interfaces over all switches")),
cfg.StrOpt('model_class', cfg.StrOpt('model_class',

View File

@ -20,6 +20,7 @@ from sqlalchemy.orm import exc
from neutron.db import api as db from neutron.db import api as db
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.common import cisco_exceptions as c_exc
from neutron.plugins.cisco.db import network_models_v2 from neutron.plugins.cisco.db import network_models_v2
from neutron.plugins.openvswitch import ovs_models_v2 from neutron.plugins.openvswitch import ovs_models_v2
@ -257,6 +258,55 @@ def update_credential(tenant_id, credential_id,
tenant_id=tenant_id) tenant_id=tenant_id)
def add_provider_network(network_id, network_type, segmentation_id):
"""Add a network to the provider network table."""
session = db.get_session()
if session.query(network_models_v2.ProviderNetwork).filter_by(
network_id=network_id).first():
raise c_exc.ProviderNetworkExists(network_id)
pnet = network_models_v2.ProviderNetwork(network_id=network_id,
network_type=network_type,
segmentation_id=segmentation_id)
session.add(pnet)
session.flush()
def remove_provider_network(network_id):
"""Remove network_id from the provider network table.
:param network_id: Any network id. If it is not in the table, do nothing.
:return: network_id if it was in the table and successfully removed.
"""
session = db.get_session()
pnet = (session.query(network_models_v2.ProviderNetwork).
filter_by(network_id=network_id).first())
if pnet:
session.delete(pnet)
session.flush()
return network_id
def is_provider_network(network_id):
"""Return True if network_id is in the provider network table."""
session = db.get_session()
if session.query(network_models_v2.ProviderNetwork).filter_by(
network_id=network_id).first():
return True
def is_provider_vlan(vlan_id):
"""Check for a for a vlan provider network with the specified vland_id.
Returns True if the provider network table contains a vlan network
with the specified vlan_id.
"""
session = db.get_session()
if (session.query(network_models_v2.ProviderNetwork).
filter_by(network_type=const.NETWORK_TYPE_VLAN,
segmentation_id=vlan_id).first()):
return True
def get_ovs_vlans(): def get_ovs_vlans():
session = db.get_session() session = db.get_session()
bindings = (session.query(ovs_models_v2.VlanAllocation.vlan_id). bindings = (session.query(ovs_models_v2.VlanAllocation.vlan_id).

View File

@ -16,7 +16,7 @@
# #
# @author: Rohit Agarwalla, Cisco Systems, Inc. # @author: Rohit Agarwalla, Cisco Systems, Inc.
from sqlalchemy import Column, Integer, String, Boolean from sqlalchemy import Column, ForeignKey, Integer, String, Boolean
from neutron.db import model_base from neutron.db import model_base
from neutron.openstack.common import uuidutils from neutron.openstack.common import uuidutils
@ -82,3 +82,15 @@ class Credential(model_base.BASEV2):
self.credential_name, self.credential_name,
self.user_name, self.user_name,
self.password) self.password)
class ProviderNetwork(model_base.BASEV2):
"""Represents networks that were created as provider networks."""
__tablename__ = 'cisco_provider_networks'
network_id = Column(String(36),
ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True)
network_type = Column(String(255), nullable=False)
segmentation_id = Column(Integer, nullable=False)

View File

@ -58,15 +58,6 @@ class L2DevicePluginBase(object):
""" """
pass pass
@abstractmethod
def get_network_details(self, tenant_id, net_id, **kwargs):
"""Get network details.
:returns:
:raises:
"""
pass
@abstractmethod @abstractmethod
def update_network(self, tenant_id, net_id, name, **kwargs): def update_network(self, tenant_id, net_id, name, **kwargs):
"""Update network. """Update network.

View File

@ -26,7 +26,9 @@ import sys
from novaclient.v1_1 import client as nova_client from novaclient.v1_1 import client as nova_client
from oslo.config import cfg from oslo.config import cfg
from neutron.api.v2 import attributes
from neutron.db import api as db_api from neutron.db import api as db_api
from neutron.extensions import providernet as provider
from neutron import neutron_plugin_base_v2 from neutron import neutron_plugin_base_v2
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_constants as const
@ -49,7 +51,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
""" """
MANAGE_STATE = True MANAGE_STATE = True
__native_bulk_support = True __native_bulk_support = True
supported_extension_aliases = [] supported_extension_aliases = ["provider"]
_plugins = {} _plugins = {}
_methods_to_delegate = ['create_network_bulk', _methods_to_delegate = ['create_network_bulk',
'get_network', 'get_networks', 'get_network', 'get_networks',
@ -202,6 +204,15 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
return host return host
def _get_provider_vlan_id(self, network):
if (all(attributes.is_attr_set(network.get(attr))
for attr in (provider.NETWORK_TYPE,
provider.PHYSICAL_NETWORK,
provider.SEGMENTATION_ID))
and
network[provider.NETWORK_TYPE] == const.NETWORK_TYPE_VLAN):
return network[provider.SEGMENTATION_ID]
def create_network(self, context, network): def create_network(self, context, network):
"""Create network. """Create network.
@ -209,10 +220,21 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
plugins. plugins.
""" """
LOG.debug(_("create_network() called")) LOG.debug(_("create_network() called"))
provider_vlan_id = self._get_provider_vlan_id(network[const.NETWORK])
args = [context, network] args = [context, network]
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(), self._func_name(),
args) args)
# The vswitch plugin did all the verification. If it's a provider
# vlan network, save it for the nexus plugin to use later.
if provider_vlan_id:
network_id = ovs_output[0][const.NET_ID]
cdb.add_provider_network(network_id,
const.NETWORK_TYPE_VLAN,
provider_vlan_id)
LOG.debug(_("provider network added to DB: %(network_id)s, "
"%(vlan_id)s"), {'network_id': network_id,
'vlan_id': provider_vlan_id})
return ovs_output[0] return ovs_output[0]
def update_network(self, context, id, network): def update_network(self, context, id, network):
@ -230,6 +252,13 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
provider attribute, so it is not supported by this method. provider attribute, so it is not supported by this method.
""" """
LOG.debug(_("update_network() called")) LOG.debug(_("update_network() called"))
# We can only support updating of provider attributes if all the
# configured sub-plugins support it. Currently we have no method
# in place for checking whether a sub-plugin supports it,
# so assume not.
provider._raise_if_updates_provider_attributes(network['network'])
args = [context, id, network] args = [context, id, network]
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(), self._func_name(),
@ -246,6 +275,8 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN, ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(), self._func_name(),
args) args)
if cdb.remove_provider_network(id):
LOG.debug(_("provider network removed from DB: %s"), id)
return ovs_output[0] return ovs_output[0]
def get_network(self, context, id, fields=None): def get_network(self, context, id, fields=None):
@ -261,22 +292,20 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
if not self.config_nexus: if not self.config_nexus:
return False return False
net_dict = self.get_network(context, net_id) network = self.get_network(context, net_id)
net_name = net_dict['name']
vlan_id = self._get_segmentation_id(net_id) vlan_id = self._get_segmentation_id(net_id)
host = self._get_instance_host(tenant_id, instance_id)
# Trunk segmentation id for only this host
vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id) vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id)
n_args = [tenant_id, net_name, net_id, network[const.NET_VLAN_ID] = vlan_id
vlan_name, vlan_id, host, instance_id] network[const.NET_VLAN_NAME] = vlan_name
nexus_output = self._invoke_plugin_per_device( attachment = {
const.TENANT_ID: tenant_id,
const.INSTANCE_ID: instance_id,
const.HOST_NAME: self._get_instance_host(tenant_id, instance_id),
}
self._invoke_plugin_per_device(
const.NEXUS_PLUGIN, const.NEXUS_PLUGIN,
'create_network', 'create_network',
n_args) [network, attachment])
return nexus_output
@staticmethod @staticmethod
def _should_call_create_net(device_owner, instance_id): def _should_call_create_net(device_owner, instance_id):

View File

@ -29,7 +29,7 @@ from neutron.db import models_v2
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_exceptions as cexc from neutron.plugins.cisco.common import cisco_exceptions as cexc
from neutron.plugins.cisco.common import config # noqa from neutron.plugins.cisco.common import config
from neutron.plugins.cisco.db import network_db_v2 as cdb from neutron.plugins.cisco.db import network_db_v2 as cdb
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)

View File

@ -26,8 +26,10 @@ import logging
from ncclient import manager from ncclient import manager
from neutron.openstack.common import excutils from neutron.openstack.common import excutils
from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_credentials_v2 as cred
from neutron.plugins.cisco.common import cisco_exceptions as cexc from neutron.plugins.cisco.common import cisco_exceptions as cexc
from neutron.plugins.cisco.db import network_db_v2 as cdb from neutron.plugins.cisco.common import config as conf
from neutron.plugins.cisco.db import nexus_db_v2 from neutron.plugins.cisco.db import nexus_db_v2
from neutron.plugins.cisco.nexus import cisco_nexus_snippets as snipp from neutron.plugins.cisco.nexus import cisco_nexus_snippets as snipp
@ -37,13 +39,15 @@ LOG = logging.getLogger(__name__)
class CiscoNEXUSDriver(): class CiscoNEXUSDriver():
"""Nexus Driver Main Class.""" """Nexus Driver Main Class."""
def __init__(self): def __init__(self):
self.nexus_switches = conf.get_nexus_dictionary()
self.credentials = {}
self.connections = {} self.connections = {}
def _edit_config(self, mgr, target='running', config='', def _edit_config(self, nexus_host, target='running', config='',
allowed_exc_strs=None): allowed_exc_strs=None):
"""Modify switch config for a target config type. """Modify switch config for a target config type.
:param mgr: NetConf client manager :param nexus_host: IP address of switch to configure
:param target: Target config type :param target: Target config type
:param config: Configuration string in XML format :param config: Configuration string in XML format
:param allowed_exc_strs: Exceptions which have any of these strings :param allowed_exc_strs: Exceptions which have any of these strings
@ -55,6 +59,7 @@ class CiscoNEXUSDriver():
""" """
if not allowed_exc_strs: if not allowed_exc_strs:
allowed_exc_strs = [] allowed_exc_strs = []
mgr = self.nxos_connect(nexus_host)
try: try:
mgr.edit_config(target, config=config) mgr.edit_config(target, config=config)
except Exception as e: except Exception as e:
@ -66,12 +71,31 @@ class CiscoNEXUSDriver():
# the original ncclient exception. # the original ncclient exception.
raise cexc.NexusConfigFailed(config=config, exc=e) raise cexc.NexusConfigFailed(config=config, exc=e)
def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user, def get_credential(self, nexus_ip):
nexus_password): if nexus_ip not in self.credentials:
nexus_username = cred.Store.get_username(nexus_ip)
nexus_password = cred.Store.get_password(nexus_ip)
self.credentials[nexus_ip] = {
const.USERNAME: nexus_username,
const.PASSWORD: nexus_password
}
return self.credentials[nexus_ip]
def get_switch_and_port_id(self, host_name):
for switch_ip, attr in self.nexus_switches:
if str(attr) == host_name:
return switch_ip, self.nexus_switches[switch_ip, attr]
return None, None
def nxos_connect(self, nexus_host):
"""Make SSH connection to the Nexus Switch.""" """Make SSH connection to the Nexus Switch."""
if getattr(self.connections.get(nexus_host), 'connected', None): if getattr(self.connections.get(nexus_host), 'connected', None):
return self.connections[nexus_host] return self.connections[nexus_host]
nexus_ssh_port = int(self.nexus_switches[nexus_host, 'ssh_port'])
nexus_creds = self.get_credential(nexus_host)
nexus_user = nexus_creds[const.USERNAME]
nexus_password = nexus_creds[const.PASSWORD]
try: try:
man = manager.connect(host=nexus_host, man = manager.connect(host=nexus_host,
port=nexus_ssh_port, port=nexus_ssh_port,
@ -93,11 +117,11 @@ class CiscoNEXUSDriver():
conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config) conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config)
return conf_xml_snippet return conf_xml_snippet
def enable_vlan(self, mgr, vlanid, vlanname): def create_vlan(self, nexus_host, vlanid, vlanname):
"""Create a VLAN on Nexus Switch given the VLAN ID and Name.""" """Create a VLAN on Nexus Switch given the VLAN ID and Name."""
confstr = self.create_xml_snippet( confstr = self.create_xml_snippet(
snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname)) snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname))
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
# Enable VLAN active and no-shutdown states. Some versions of # Enable VLAN active and no-shutdown states. Some versions of
# Nexus switch do not allow state changes for the extended VLAN # Nexus switch do not allow state changes for the extended VLAN
@ -109,143 +133,77 @@ class CiscoNEXUSDriver():
try: try:
confstr = self.create_xml_snippet(snippet % vlanid) confstr = self.create_xml_snippet(snippet % vlanid)
self._edit_config( self._edit_config(
mgr, nexus_host,
target='running', target='running',
config=confstr, config=confstr,
allowed_exc_strs=["Can't modify state for extended", allowed_exc_strs=["Can't modify state for extended",
"Command is only allowed on VLAN"]) "Command is only allowed on VLAN"])
except cexc.NexusConfigFailed: except cexc.NexusConfigFailed:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.disable_vlan(mgr, vlanid) self.delete_vlan(nexus_host, vlanid)
def disable_vlan(self, mgr, vlanid): def delete_vlan(self, nexus_host, vlanid):
"""Delete a VLAN on Nexus Switch given the VLAN ID.""" """Delete a VLAN on Nexus Switch given the VLAN ID."""
confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def enable_port_trunk(self, mgr, interface): def enable_port_trunk(self, nexus_host, interface):
"""Enable trunk mode an interface on Nexus Switch.""" """Enable trunk mode an interface on Nexus Switch."""
confstr = snipp.CMD_PORT_TRUNK % (interface) confstr = snipp.CMD_PORT_TRUNK % (interface)
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def disable_switch_port(self, mgr, interface): def disable_switch_port(self, nexus_host, interface):
"""Disable trunk mode an interface on Nexus Switch.""" """Disable trunk mode an interface on Nexus Switch."""
confstr = snipp.CMD_NO_SWITCHPORT % (interface) confstr = snipp.CMD_NO_SWITCHPORT % (interface)
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def enable_vlan_on_trunk_int(self, mgr, nexus_switch, interface, vlanid): def enable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
"""Enable vlan in trunk interface. """Enable a VLAN on a trunk interface."""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID.
"""
# If one or more VLANs are already configured on this interface, # If one or more VLANs are already configured on this interface,
# include the 'add' keyword. # include the 'add' keyword.
if nexus_db_v2.get_port_switch_bindings(interface, nexus_switch): if nexus_db_v2.get_port_switch_bindings(interface, nexus_host):
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
else: else:
snippet = snipp.CMD_INT_VLAN_SNIPPET snippet = snipp.CMD_INT_VLAN_SNIPPET
confstr = snippet % (interface, vlanid) confstr = snippet % (interface, vlanid)
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def disable_vlan_on_trunk_int(self, mgr, interface, vlanid): def disable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
"""Disable VLAN. """Disable a VLAN on a trunk interface."""
Disables trunk mode vlan access an interface on Nexus Switch given
VLANID.
"""
confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid) confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(mgr, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user, def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name,
nexus_password, nexus_ports, nexus_port):
nexus_ssh_port, vlan_ids=None): """Create VLAN and trunk it on the specified ports."""
"""Create VLAN and enablt in on the interface. self.create_vlan(nexus_host, vlan_id, vlan_name)
LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id)
if nexus_port:
self.enable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
Creates a VLAN and Enable on trunk mode an interface on Nexus Switch def delete_and_untrunk_vlan(self, nexus_host, vlan_id, nexus_port):
given the VLAN ID and Name and Interface Number. """Delete VLAN and untrunk it from the specified ports."""
""" self.delete_vlan(nexus_host, vlan_id)
man = self.nxos_connect(nexus_host, int(nexus_ssh_port), if nexus_port:
nexus_user, nexus_password) self.disable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
self.enable_vlan(man, vlan_id, vlan_name)
if vlan_ids is '':
vlan_ids = self.build_vlans_cmd()
LOG.debug(_("NexusDriver VLAN IDs: %s"), vlan_ids)
for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)
def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port):
"""Delete vlan.
Delete a VLAN and Disables trunk mode an interface on Nexus Switch
given the VLAN ID and Interface Number.
"""
man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_user, nexus_password)
self.disable_vlan(man, vlan_id)
for ports in nexus_ports:
self.disable_vlan_on_trunk_int(man, ports, vlan_id)
def build_vlans_cmd(self):
"""Builds a string with all the VLANs on the same Switch."""
assigned_vlan = cdb.get_all_vlanids_used()
vlans = ''
for vlanid in assigned_vlan:
vlans = str(vlanid["vlan_id"]) + ',' + vlans
if vlans == '':
vlans = 'none'
return vlans.strip(',')
def add_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port, vlan_ids=None):
"""Add vlan.
Adds a vlan from interfaces on the Nexus switch given the VLAN ID.
"""
man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_user, nexus_password)
if not vlan_ids:
vlan_ids = self.build_vlans_cmd()
for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)
def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port):
"""Remove vlan.
Removes a vlan from interfaces on the Nexus switch given the VLAN ID.
"""
man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_user, nexus_password)
for ports in nexus_ports:
self.disable_vlan_on_trunk_int(man, ports, vlan_id)
def create_vlan_svi(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ssh_port, gateway_ip):
man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_user, nexus_password)
def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip) confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
man.edit_config(target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def delete_vlan_svi(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ssh_port):
man = self.nxos_connect(nexus_host, int(nexus_ssh_port),
nexus_user, nexus_password)
def delete_vlan_svi(self, nexus_host, vlan_id):
confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
man.edit_config(target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)

View File

@ -30,9 +30,9 @@ from neutron.common import exceptions as exc
from neutron.openstack.common import excutils from neutron.openstack.common import excutils
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_credentials_v2 as cred
from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc
from neutron.plugins.cisco.common import config as conf from neutron.plugins.cisco.common import config as conf
from neutron.plugins.cisco.db import network_db_v2 as cdb
from neutron.plugins.cisco.db import nexus_db_v2 as nxos_db from neutron.plugins.cisco.db import nexus_db_v2 as nxos_db
from neutron.plugins.cisco.l2device_plugin_base import L2DevicePluginBase from neutron.plugins.cisco.l2device_plugin_base import L2DevicePluginBase
@ -48,18 +48,6 @@ class NexusPlugin(L2DevicePluginBase):
"""Extract configuration parameters from the configuration file.""" """Extract configuration parameters from the configuration file."""
self._client = importutils.import_object(conf.CISCO.nexus_driver) self._client = importutils.import_object(conf.CISCO.nexus_driver)
LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver) LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver)
self._nexus_switches = conf.get_nexus_dictionary()
self.credentials = {}
def get_credential(self, nexus_ip):
if nexus_ip not in self.credentials:
_nexus_username = cred.Store.get_username(nexus_ip)
_nexus_password = cred.Store.get_password(nexus_ip)
self.credentials[nexus_ip] = {
'username': _nexus_username,
'password': _nexus_password
}
return self.credentials[nexus_ip]
def get_all_networks(self, tenant_id): def get_all_networks(self, tenant_id):
"""Get all networks. """Get all networks.
@ -70,76 +58,88 @@ class NexusPlugin(L2DevicePluginBase):
LOG.debug(_("NexusPlugin:get_all_networks() called")) LOG.debug(_("NexusPlugin:get_all_networks() called"))
return self._networks.values() return self._networks.values()
def create_network(self, tenant_id, net_name, net_id, vlan_name, vlan_id, def create_network(self, network, attachment):
host, instance): """Create or update a network when an attachment is changed.
"""Create network.
This method is not invoked at the usual plugin create_network() time.
Instead, it is invoked on create/update port.
:param network: Network on which the port operation is happening
:param attachment: Details about the owner of the port
Create a VLAN in the appropriate switch/port, and configure the Create a VLAN in the appropriate switch/port, and configure the
appropriate interfaces for this VLAN. appropriate interfaces for this VLAN.
""" """
LOG.debug(_("NexusPlugin:create_network() called")) LOG.debug(_("NexusPlugin:create_network() called"))
# Grab the switch IP and port for this host # Grab the switch IP and port for this host
for switch_ip, attr in self._nexus_switches: host = str(attachment[const.HOST_NAME])
if str(attr) == str(host): switch_ip, port_id = self._client.get_switch_and_port_id(host)
port_id = self._nexus_switches[switch_ip, attr] if not switch_ip and not port_id:
break
else:
raise cisco_exc.NexusComputeHostNotConfigured(host=host) raise cisco_exc.NexusComputeHostNotConfigured(host=host)
vlan_id = network[const.NET_VLAN_ID]
vlan_name = network[const.NET_VLAN_NAME]
auto_create = True
auto_trunk = True
if cdb.is_provider_vlan(vlan_id):
vlan_name = ''.join([conf.CISCO.provider_vlan_name_prefix,
str(vlan_id)])
auto_create = conf.CISCO.provider_vlan_auto_create
auto_trunk = conf.CISCO.provider_vlan_auto_trunk
# Check if this network is already in the DB # Check if this network is already in the DB
vlan_created = False vlan_created = False
vlan_enabled = False vlan_trunked = False
try: try:
nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip) nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip)
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
_nexus_ip = switch_ip
_nexus_ports = (port_id,)
_nexus_ssh_port = \
self._nexus_switches[switch_ip, 'ssh_port']
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
# Check for vlan/switch binding # Check for vlan/switch binding
try: try:
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
# Create vlan and trunk vlan on the port if auto_create and auto_trunk:
self._client.create_vlan( # Create vlan and trunk vlan on the port
vlan_name, str(vlan_id), _nexus_ip, LOG.debug("Nexus: create & trunk vlan %s" % vlan_name)
_nexus_username, _nexus_password, self._client.create_and_trunk_vlan(
_nexus_ports, _nexus_ssh_port, vlan_id) switch_ip, vlan_id, vlan_name, port_id)
vlan_created = True vlan_created = True
vlan_trunked = True
elif auto_create:
# Create vlan but do not trunk it on the port
LOG.debug("Nexus: create vlan %s" % vlan_name)
self._client.create_vlan(switch_ip, vlan_id, vlan_name)
vlan_created = True
else: else:
# Only trunk vlan on the port if auto_trunk:
man = self._client.nxos_connect(_nexus_ip, # Only trunk vlan on the port
int(_nexus_ssh_port), LOG.debug("Nexus: trunk vlan %s" % vlan_name)
_nexus_username, self._client.enable_vlan_on_trunk_int(
_nexus_password) switch_ip, vlan_id, port_id)
self._client.enable_vlan_on_trunk_int(man, vlan_trunked = True
_nexus_ip,
port_id,
vlan_id)
vlan_enabled = True
try: try:
instance = attachment[const.INSTANCE_ID]
nxos_db.add_nexusport_binding(port_id, str(vlan_id), nxos_db.add_nexusport_binding(port_id, str(vlan_id),
switch_ip, instance) switch_ip, instance)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
# Add binding failed, roll back any vlan creation/enabling # Add binding failed, roll back any vlan creation/enabling
if vlan_created: if vlan_created and vlan_trunked:
self._client.delete_vlan( LOG.debug("Nexus: delete & untrunk vlan %s" % vlan_name)
str(vlan_id), _nexus_ip, self._client.delete_and_untrunk_vlan(switch_ip, vlan_id,
_nexus_username, _nexus_password, port_id)
_nexus_ports, _nexus_ssh_port) elif vlan_created:
if vlan_enabled: LOG.debug("Nexus: delete vlan %s" % vlan_name)
self._client.disable_vlan_on_trunk_int(man, self._client.delete_vlan(switch_ip, vlan_id)
port_id, elif vlan_trunked:
vlan_id) LOG.debug("Nexus: untrunk vlan %s" % vlan_name)
self._client.disable_vlan_on_trunk_int(switch_ip, vlan_id,
port_id)
net_id = network[const.NET_ID]
new_net_dict = {const.NET_ID: net_id, new_net_dict = {const.NET_ID: net_id,
const.NET_NAME: net_name, const.NET_NAME: network[const.NET_NAME],
const.NET_PORTS: {}, const.NET_PORTS: {},
const.NET_VLAN_NAME: vlan_name, const.NET_VLAN_NAME: vlan_name,
const.NET_VLAN_ID: vlan_id} const.NET_VLAN_ID: vlan_id}
@ -152,33 +152,22 @@ class NexusPlugin(L2DevicePluginBase):
# Find a switch to create the SVI on # Find a switch to create the SVI on
switch_ip = self._find_switch_for_svi() switch_ip = self._find_switch_for_svi()
if not switch_ip: if not switch_ip:
raise cisco_exc.NoNexusSwitch() raise cisco_exc.NoNexusSviSwitch()
_nexus_ip = switch_ip
_nexus_ssh_port = self._nexus_switches[switch_ip, 'ssh_port']
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
# Check if this vlan exists on the switch already # Check if this vlan exists on the switch already
try: try:
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
# Create vlan and trunk vlan on the port # Create vlan and trunk vlan on the port
self._client.create_vlan( self._client.create_and_trunk_vlan(
vlan_name, str(vlan_id), _nexus_ip, switch_ip, vlan_id, vlan_name, nexus_port=None)
_nexus_username, _nexus_password,
[], _nexus_ssh_port, vlan_id)
# Check if a router interface has already been created # Check if a router interface has already been created
try: try:
nxos_db.get_nexusvm_binding(vlan_id, router_id) nxos_db.get_nexusvm_binding(vlan_id, router_id)
raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id, raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id,
router_id=router_id) router_id=router_id)
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
self._client.create_vlan_svi(vlan_id, _nexus_ip, _nexus_username, self._client.create_vlan_svi(switch_ip, vlan_id, gateway_ip)
_nexus_password, _nexus_ssh_port,
gateway_ip)
nxos_db.add_nexusport_binding('router', str(vlan_id), nxos_db.add_nexusport_binding('router', str(vlan_id),
switch_ip, router_id) switch_ip, router_id)
@ -187,17 +176,11 @@ class NexusPlugin(L2DevicePluginBase):
def remove_router_interface(self, vlan_id, router_id): def remove_router_interface(self, vlan_id, router_id):
"""Remove VLAN SVI from the Nexus Switch.""" """Remove VLAN SVI from the Nexus Switch."""
# Grab switch_ip from database # Grab switch_ip from database
row = nxos_db.get_nexusvm_binding(vlan_id, router_id) switch_ip = nxos_db.get_nexusvm_binding(vlan_id,
router_id)['switch_ip']
# Delete the SVI interface from the switch # Delete the SVI interface from the switch
_nexus_ip = row['switch_ip'] self._client.delete_vlan_svi(switch_ip, vlan_id)
_nexus_ssh_port = self._nexus_switches[_nexus_ip, 'ssh_port']
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
self._client.delete_vlan_svi(vlan_id, _nexus_ip, _nexus_username,
_nexus_password, _nexus_ssh_port)
# Invoke delete_port to delete this row # Invoke delete_port to delete this row
# And delete vlan if required # And delete vlan if required
@ -206,10 +189,11 @@ class NexusPlugin(L2DevicePluginBase):
def _find_switch_for_svi(self): def _find_switch_for_svi(self):
"""Get a switch to create the SVI on.""" """Get a switch to create the SVI on."""
LOG.debug(_("Grabbing a switch to create SVI")) LOG.debug(_("Grabbing a switch to create SVI"))
nexus_switches = self._client.nexus_switches
if conf.CISCO.svi_round_robin: if conf.CISCO.svi_round_robin:
LOG.debug(_("Using round robin to create SVI")) LOG.debug(_("Using round robin to create SVI"))
switch_dict = dict( switch_dict = dict(
(switch_ip, 0) for switch_ip, _ in self._nexus_switches) (switch_ip, 0) for switch_ip, _ in nexus_switches)
try: try:
bindings = nxos_db.get_nexussvi_bindings() bindings = nxos_db.get_nexussvi_bindings()
# Build a switch dictionary with weights # Build a switch dictionary with weights
@ -228,7 +212,7 @@ class NexusPlugin(L2DevicePluginBase):
LOG.debug(_("No round robin or zero weights, using first switch")) LOG.debug(_("No round robin or zero weights, using first switch"))
# Return the first switch in the config # Return the first switch in the config
for switch_ip, attr in self._nexus_switches: for switch_ip, attr in nexus_switches:
return switch_ip return switch_ip
def delete_network(self, tenant_id, net_id, **kwargs): def delete_network(self, tenant_id, net_id, **kwargs):
@ -239,12 +223,6 @@ class NexusPlugin(L2DevicePluginBase):
""" """
LOG.debug(_("NexusPlugin:delete_network() called")) LOG.debug(_("NexusPlugin:delete_network() called"))
def get_network_details(self, tenant_id, net_id, **kwargs):
"""Return the details of a particular network."""
LOG.debug(_("NexusPlugin:get_network_details() called"))
network = self._get_network(tenant_id, net_id)
return network
def update_network(self, tenant_id, net_id, **kwargs): def update_network(self, tenant_id, net_id, **kwargs):
"""Update the properties of a particular Virtual Network.""" """Update the properties of a particular Virtual Network."""
LOG.debug(_("NexusPlugin:update_network() called")) LOG.debug(_("NexusPlugin:update_network() called"))
@ -278,6 +256,18 @@ class NexusPlugin(L2DevicePluginBase):
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
return return
auto_delete = True
auto_untrunk = True
if cdb.is_provider_vlan(vlan_id):
auto_delete = conf.CISCO.provider_vlan_auto_create
auto_untrunk = conf.CISCO.provider_vlan_auto_trunk
LOG.debug("delete_network(): provider vlan %s" % vlan_id)
switch_ip = row['switch_ip']
nexus_port = None
if row['port_id'] != 'router':
nexus_port = row['port_id']
nxos_db.remove_nexusport_binding(row['port_id'], row['vlan_id'], nxos_db.remove_nexusport_binding(row['port_id'], row['vlan_id'],
row['switch_ip'], row['switch_ip'],
row['instance_id']) row['instance_id'])
@ -287,19 +277,11 @@ class NexusPlugin(L2DevicePluginBase):
except cisco_exc.NexusPortBindingNotFound: except cisco_exc.NexusPortBindingNotFound:
try: try:
# Delete this vlan from this switch # Delete this vlan from this switch
_nexus_ip = row['switch_ip'] if nexus_port and auto_untrunk:
_nexus_ports = () self._client.disable_vlan_on_trunk_int(
if row['port_id'] != 'router': switch_ip, row['vlan_id'], nexus_port)
_nexus_ports = (row['port_id'],) if auto_delete:
_nexus_ssh_port = (self._nexus_switches[_nexus_ip, self._client.delete_vlan(switch_ip, row['vlan_id'])
'ssh_port'])
_nexus_creds = self.get_credential(_nexus_ip)
_nexus_username = _nexus_creds['username']
_nexus_password = _nexus_creds['password']
self._client.delete_vlan(
str(row['vlan_id']), _nexus_ip,
_nexus_username, _nexus_password,
_nexus_ports, _nexus_ssh_port)
except Exception: except Exception:
# The delete vlan operation on the Nexus failed, # The delete vlan operation on the Nexus failed,
# so this delete_port request has failed. For # so this delete_port request has failed. For

View File

@ -18,6 +18,7 @@ import inspect
import logging import logging
import mock import mock
from oslo.config import cfg
import webob.exc as wexc import webob.exc as wexc
from neutron.api.v2 import base from neutron.api.v2 import base
@ -25,6 +26,7 @@ from neutron.common import exceptions as q_exc
from neutron import context from neutron import context
from neutron.db import db_base_plugin_v2 as base_plugin from neutron.db import db_base_plugin_v2 as base_plugin
from neutron.db import l3_db from neutron.db import l3_db
from neutron.extensions import providernet as provider
from neutron.manager import NeutronManager from neutron.manager import NeutronManager
from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.common import cisco_exceptions as c_exc
@ -519,6 +521,16 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase, class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase,
test_db_plugin.TestNetworksV2): test_db_plugin.TestNetworksV2):
def setUp(self):
self.physnet = 'testphys1'
self.vlan_range = '100:199'
phys_vrange = ':'.join([self.physnet, self.vlan_range])
cfg.CONF.set_override('tenant_network_type', 'vlan', 'OVS')
cfg.CONF.set_override('network_vlan_ranges', [phys_vrange], 'OVS')
self.addCleanup(cfg.CONF.reset)
super(TestCiscoNetworksV2, self).setUp()
def test_create_networks_bulk_emulated_plugin_failure(self): def test_create_networks_bulk_emulated_plugin_failure(self):
real_has_attr = hasattr real_has_attr = hasattr
@ -566,6 +578,24 @@ class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase,
'networks', 'networks',
wexc.HTTPInternalServerError.code) wexc.HTTPInternalServerError.code)
def test_create_provider_vlan_network(self):
provider_attrs = {provider.NETWORK_TYPE: 'vlan',
provider.PHYSICAL_NETWORK: self.physnet,
provider.SEGMENTATION_ID: '1234'}
arg_list = tuple(provider_attrs.keys())
res = self._create_network(self.fmt, 'pvnet1', True,
arg_list=arg_list, **provider_attrs)
net = self.deserialize(self.fmt, res)
expected = [('name', 'pvnet1'),
('admin_state_up', True),
('status', 'ACTIVE'),
('shared', False),
(provider.NETWORK_TYPE, 'vlan'),
(provider.PHYSICAL_NETWORK, self.physnet),
(provider.SEGMENTATION_ID, 1234)]
for k, v in expected:
self.assertEqual(net['network'][k], v)
class TestCiscoSubnetsV2(CiscoNetworkPluginV2TestCase, class TestCiscoSubnetsV2(CiscoNetworkPluginV2TestCase,
test_db_plugin.TestSubnetsV2): test_db_plugin.TestSubnetsV2):

View File

@ -15,23 +15,32 @@
import mock import mock
from oslo.config import cfg
from neutron.db import api as db from neutron.db import api as db
from neutron.extensions import providernet as provider
from neutron.openstack.common import importutils from neutron.openstack.common import importutils
from neutron.plugins.cisco.common import cisco_constants as const from neutron.plugins.cisco.common import cisco_constants as const
from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc from neutron.plugins.cisco.common import cisco_exceptions as cisco_exc
from neutron.plugins.cisco.db import network_db_v2 as cdb
from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2 from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2
from neutron.tests import base from neutron.tests import base
NEXUS_IP_ADDRESS = '1.1.1.1' NEXUS_IP_ADDRESS = '1.1.1.1'
NEXUS_USERNAME = 'username' HOSTNAME1 = 'testhost1'
NEXUS_PASSWORD = 'password' HOSTNAME2 = 'testhost2'
HOSTNAME = 'testhost' INSTANCE1 = 'testvm1'
INSTANCE = 'testvm' INSTANCE2 = 'testvm2'
NEXUS_PORTS = '1/10' NEXUS_PORT1 = '1/10'
NEXUS_PORT2 = '1/20'
NEXUS_SSH_PORT = '22' NEXUS_SSH_PORT = '22'
NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.' NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.'
'cisco_nexus_network_driver_v2.CiscoNEXUSDriver') 'cisco_nexus_network_driver_v2.CiscoNEXUSDriver')
NET_ATTRS = [const.NET_ID,
const.NET_NAME,
const.NET_VLAN_NAME,
const.NET_VLAN_ID]
class TestCiscoNexusPlugin(base.BaseTestCase): class TestCiscoNexusPlugin(base.BaseTestCase):
@ -44,28 +53,56 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
self.net_id = 7 self.net_id = 7
self.vlan_name = "q-" + str(self.net_id) + "vlan" self.vlan_name = "q-" + str(self.net_id) + "vlan"
self.vlan_id = 267 self.vlan_id = 267
self.second_tenant_id = "test_tenant_2"
self.second_net_name = "test_network_cisco2" self.second_net_name = "test_network_cisco2"
self.second_net_id = 5 self.second_net_id = 5
self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan" self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
self.second_vlan_id = 265 self.second_vlan_id = 265
self._nexus_switches = { self.attachment1 = {
(NEXUS_IP_ADDRESS, HOSTNAME): NEXUS_PORTS, const.TENANT_ID: self.tenant_id,
(NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT, const.INSTANCE_ID: INSTANCE1,
const.HOST_NAME: HOSTNAME1,
}
self.attachment2 = {
const.TENANT_ID: self.second_tenant_id,
const.INSTANCE_ID: INSTANCE2,
const.HOST_NAME: HOSTNAME2,
}
self.network1 = {
const.NET_ID: self.net_id,
const.NET_NAME: self.net_name,
const.NET_VLAN_NAME: self.vlan_name,
const.NET_VLAN_ID: self.vlan_id,
}
self.network2 = {
const.NET_ID: self.second_net_id,
const.NET_NAME: self.second_net_name,
const.NET_VLAN_NAME: self.second_vlan_name,
const.NET_VLAN_ID: self.second_vlan_id,
}
self.providernet = {
const.NET_ID: 9,
const.NET_NAME: 'pnet1',
const.NET_VLAN_NAME: 'p-300',
const.NET_VLAN_ID: 300,
provider.NETWORK_TYPE: 'vlan',
provider.PHYSICAL_NETWORK: self.net_name + '200:299',
provider.SEGMENTATION_ID: 300,
} }
self._hostname = HOSTNAME
def new_nexus_init(self): def new_nexus_init(self):
self._client = importutils.import_object(NEXUS_DRIVER) self._client = importutils.import_object(NEXUS_DRIVER)
self._nexus_ip = NEXUS_IP_ADDRESS self._client.nexus_switches = {
self._nexus_username = NEXUS_USERNAME (NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1,
self._nexus_password = NEXUS_PASSWORD (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
self._nexus_ports = NEXUS_PORTS (NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
self._nexus_ssh_port = NEXUS_SSH_PORT (NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
self.credentials = { }
self._nexus_ip: { self._client.credentials = {
'username': self._nexus_username, NEXUS_IP_ADDRESS: {
'password': self._nexus_password 'username': 'admin',
} 'password': 'pass1234'
},
} }
db.configure_db() db.configure_db()
@ -78,51 +115,69 @@ class TestCiscoNexusPlugin(base.BaseTestCase):
with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin, with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin,
'__init__', new=new_nexus_init): '__init__', new=new_nexus_init):
self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin() self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin()
self._cisco_nexus_plugin._nexus_switches = self._nexus_switches
self.addCleanup(self.patch_obj.stop) self.addCleanup(self.patch_obj.stop)
def test_create_networks(self): def test_create_networks(self):
"""Tests creation of two new Virtual Networks.""" """Tests creation of two new Virtual Networks."""
tenant_id = self.tenant_id new_net_dict = self._cisco_nexus_plugin.create_network(
net_name = self.net_name self.network1, self.attachment1)
net_id = self.net_id for attr in NET_ATTRS:
vlan_name = self.vlan_name self.assertEqual(new_net_dict[attr], self.network1[attr])
vlan_id = self.vlan_id
second_net_name = self.second_net_name
second_net_id = self.second_net_id
second_vlan_name = self.second_vlan_name
second_vlan_id = self.second_vlan_id
new_net_dict = self._cisco_nexus_plugin.create_network( new_net_dict = self._cisco_nexus_plugin.create_network(
tenant_id, net_name, net_id, self.network2, self.attachment1)
vlan_name, vlan_id, self._hostname, INSTANCE) for attr in NET_ATTRS:
self.assertEqual(new_net_dict[const.NET_ID], net_id) self.assertEqual(new_net_dict[attr], self.network2[attr])
self.assertEqual(new_net_dict[const.NET_NAME], self.net_name)
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], self.vlan_name)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.vlan_id)
new_net_dict = self._cisco_nexus_plugin.create_network( def test_create_providernet(self):
tenant_id, second_net_name, second_net_id, with mock.patch.object(cdb, 'is_provider_vlan',
second_vlan_name, second_vlan_id, self._hostname, return_value=True) as mock_db:
INSTANCE) new_net_dict = self._cisco_nexus_plugin.create_network(
self.providernet, self.attachment1)
mock_db.assert_called_once()
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.providernet[attr])
self.assertEqual(new_net_dict[const.NET_ID], second_net_id) def test_create_provider_vlan_network_cfg_auto_man(self):
self.assertEqual(new_net_dict[const.NET_NAME], self.second_net_name) cfg.CONF.set_override('provider_vlan_auto_create', True, 'CISCO')
self.assertEqual(new_net_dict[const.NET_VLAN_NAME], cfg.CONF.set_override('provider_vlan_auto_trunk', False, 'CISCO')
self.second_vlan_name) self.addCleanup(cfg.CONF.reset)
self.assertEqual(new_net_dict[const.NET_VLAN_ID], self.second_vlan_id) with mock.patch.object(cdb, 'is_provider_vlan', return_value=True):
new_net_dict = self._cisco_nexus_plugin.create_network(
self.providernet, self.attachment1)
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.providernet[attr])
def test_create_provider_vlan_network_cfg_man_auto(self):
cfg.CONF.set_override('provider_vlan_auto_create', False, 'CISCO')
cfg.CONF.set_override('provider_vlan_auto_trunk', True, 'CISCO')
self.addCleanup(cfg.CONF.reset)
with mock.patch.object(cdb, 'is_provider_vlan', return_value=True):
new_net_dict = self._cisco_nexus_plugin.create_network(
self.providernet, self.attachment1)
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.providernet[attr])
def test_create_provider_vlan_network_cfg_man_man(self):
cfg.CONF.set_override('provider_vlan_auto_create', False, 'CISCO')
cfg.CONF.set_override('provider_vlan_auto_trunk', False, 'CISCO')
self.addCleanup(cfg.CONF.reset)
with mock.patch.object(cdb, 'is_provider_vlan', return_value=True):
new_net_dict = self._cisco_nexus_plugin.create_network(
self.providernet, self.attachment1)
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.providernet[attr])
def test_nexus_delete_port(self): def test_nexus_delete_port(self):
"""Test deletion of a vlan.""" """Test deletion of a vlan."""
self._cisco_nexus_plugin.create_network( self._cisco_nexus_plugin.create_network(
self.tenant_id, self.net_name, self.net_id, self.vlan_name, self.network1, self.attachment1)
self.vlan_id, self._hostname, INSTANCE)
expected_instance_id = self._cisco_nexus_plugin.delete_port( expected_instance_id = self._cisco_nexus_plugin.delete_port(
INSTANCE, self.vlan_id) INSTANCE1, self.vlan_id)
self.assertEqual(expected_instance_id, INSTANCE) self.assertEqual(expected_instance_id, INSTANCE1)
def test_nexus_add_remove_router_interface(self): def test_nexus_add_remove_router_interface(self):
"""Tests addition of a router interface.""" """Tests addition of a router interface."""