Merge "Remove the Cisco Nexus monolithic plugin"
This commit is contained in:
commit
01ce3988d0
@ -1,16 +1,3 @@
|
||||
[cisco_plugins]
|
||||
|
||||
# (StrOpt) Period-separated module path to the plugin class to use for
|
||||
# the Cisco Nexus switches.
|
||||
#
|
||||
# nexus_plugin = neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin
|
||||
|
||||
# (StrOpt) Period-separated module path to the plugin class to use for
|
||||
# the virtual switches on compute nodes.
|
||||
#
|
||||
# vswitch_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2
|
||||
|
||||
|
||||
[cisco]
|
||||
|
||||
# (StrOpt) A short prefix to prepend to the VLAN number when creating a
|
||||
@ -48,14 +35,6 @@
|
||||
#
|
||||
# model_class = neutron.plugins.cisco.models.virt_phy_sw_v2.VirtualPhysicalSwitchModelV2
|
||||
|
||||
# (StrOpt) Period-separated module path to the driver class to use for
|
||||
# the Cisco Nexus switches.
|
||||
#
|
||||
# If no value is configured, a fake driver will be used.
|
||||
# nexus_driver = neutron.plugins.cisco.test.nexus.fake_nexus_driver.CiscoNEXUSFakeDriver
|
||||
# With real hardware, use the CiscoNEXUSDriver class:
|
||||
# nexus_driver = neutron.plugins.cisco.nexus.cisco_nexus_network_driver_v2.CiscoNEXUSDriver
|
||||
|
||||
# (BoolOpt) A flag to enable Layer 3 support on the Nexus switches.
|
||||
# Note: This feature is not supported on all models/versions of Cisco
|
||||
# Nexus switches. To use this feature, all of the Nexus switches in the
|
||||
@ -67,29 +46,6 @@
|
||||
|
||||
# Cisco Nexus Switch configurations.
|
||||
# Each switch to be managed by Openstack Neutron must be configured here.
|
||||
#
|
||||
# Cisco Nexus Switch Format.
|
||||
# [NEXUS_SWITCH:<IP address of switch>]
|
||||
# <hostname>=<port> (1)
|
||||
# ssh_port=<ssh port> (2)
|
||||
# username=<credential username> (3)
|
||||
# password=<credential password> (4)
|
||||
#
|
||||
# (1) For each host connected to a port on the switch, specify the hostname
|
||||
# and the Nexus physical port (interface) it is connected to.
|
||||
# (2) The TCP port for connecting via SSH to manage the switch. This is
|
||||
# port number 22 unless the switch has been configured otherwise.
|
||||
# (3) The username for logging into the switch to manage it.
|
||||
# (4) The password for logging into the switch to manage it.
|
||||
#
|
||||
# Example:
|
||||
# [NEXUS_SWITCH:1.1.1.1]
|
||||
# compute1=1/1
|
||||
# compute2=1/2
|
||||
# ssh_port=22
|
||||
# username=admin
|
||||
# password=mySecretPassword
|
||||
|
||||
#
|
||||
# N1KV Format.
|
||||
# [N1KV:<IP address of VSM>]
|
||||
|
@ -0,0 +1,53 @@
|
||||
# Copyright 2014 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.
|
||||
#
|
||||
|
||||
"""Remove Cisco Nexus Monolithic Plugin
|
||||
|
||||
Revision ID: 1680e1f0c4dc
|
||||
Revises: 3c346828361e
|
||||
Create Date: 2014-08-31 08:58:37.123992
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1680e1f0c4dc'
|
||||
down_revision = '3c346828361e'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.execute('INSERT INTO cisco_ml2_nexusport_bindings (port_id, '
|
||||
'vlan_id, switch_ip, instance_id) SELECT '
|
||||
'port_id, vlan_id, switch_ip, instance_id FROM '
|
||||
'cisco_nexusport_bindings')
|
||||
op.drop_table('cisco_nexusport_bindings')
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
op.create_table(
|
||||
'cisco_nexusport_bindings',
|
||||
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column('port_id', sa.String(255)),
|
||||
sa.Column('vlan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('switch_ip', sa.String(255), nullable=False),
|
||||
sa.Column('instance_id', sa.String(255), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.execute('INSERT INTO cisco_nexusport_bindings (port_id, '
|
||||
'vlan_id, switch_ip, instance_id) SELECT '
|
||||
'port_id, vlan_id, switch_ip, instance_id FROM '
|
||||
'cisco_ml2_nexusport_bindings')
|
@ -1 +1 @@
|
||||
3c346828361e
|
||||
1680e1f0c4dc
|
||||
|
@ -53,7 +53,6 @@ from neutron.plugins.brocade.db import models as brocade_models # noqa
|
||||
from neutron.plugins.cisco.db.l3 import l3_models # noqa
|
||||
from neutron.plugins.cisco.db import n1kv_models_v2 # noqa
|
||||
from neutron.plugins.cisco.db import network_models_v2 # noqa
|
||||
from neutron.plugins.cisco.db import nexus_models_v2 # noqa
|
||||
from neutron.plugins.hyperv import model # noqa
|
||||
from neutron.plugins.linuxbridge.db import l2network_models_v2 # noqa
|
||||
from neutron.plugins.metaplugin import meta_models_v2 # noqa
|
||||
|
@ -40,7 +40,6 @@ PASSWORD = 'password'
|
||||
|
||||
LOGGER_COMPONENT_NAME = "cisco_plugin"
|
||||
|
||||
NEXUS_PLUGIN = 'nexus_plugin'
|
||||
VSWITCH_PLUGIN = 'vswitch_plugin'
|
||||
|
||||
DEVICE_IP = 'device_ip'
|
||||
@ -101,11 +100,11 @@ ENCAPSULATION_PROFILE_SUFFIX = '_profile'
|
||||
|
||||
UUID_LENGTH = 36
|
||||
|
||||
# Nexus vlan and vxlan segment range
|
||||
NEXUS_VLAN_RESERVED_MIN = 3968
|
||||
NEXUS_VLAN_RESERVED_MAX = 4047
|
||||
NEXUS_VXLAN_MIN = 4096
|
||||
NEXUS_VXLAN_MAX = 16000000
|
||||
# N1KV vlan and vxlan segment range
|
||||
N1KV_VLAN_RESERVED_MIN = 3968
|
||||
N1KV_VLAN_RESERVED_MAX = 4047
|
||||
N1KV_VXLAN_MIN = 4096
|
||||
N1KV_VXLAN_MAX = 16000000
|
||||
|
||||
# Type and topic for Cisco cfg agent
|
||||
# ==================================
|
||||
|
@ -17,17 +17,6 @@ from oslo.config import cfg
|
||||
from neutron.agent.common import config
|
||||
|
||||
|
||||
cisco_plugins_opts = [
|
||||
cfg.StrOpt('vswitch_plugin',
|
||||
default='neutron.plugins.openvswitch.ovs_neutron_plugin.'
|
||||
'OVSNeutronPluginV2',
|
||||
help=_("Virtual Switch to use")),
|
||||
cfg.StrOpt('nexus_plugin',
|
||||
default='neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.'
|
||||
'NexusPlugin',
|
||||
help=_("Nexus Switch to use")),
|
||||
]
|
||||
|
||||
cisco_opts = [
|
||||
cfg.StrOpt('vlan_name_prefix', default='q-',
|
||||
help=_("VLAN Name prefix")),
|
||||
@ -47,10 +36,6 @@ cisco_opts = [
|
||||
default='neutron.plugins.cisco.models.virt_phy_sw_v2.'
|
||||
'VirtualPhysicalSwitchModelV2',
|
||||
help=_("Model Class")),
|
||||
cfg.StrOpt('nexus_driver',
|
||||
default='neutron.plugins.cisco.test.nexus.'
|
||||
'fake_nexus_driver.CiscoNEXUSFakeDriver',
|
||||
help=_("Nexus Driver Name")),
|
||||
]
|
||||
|
||||
cisco_n1k_opts = [
|
||||
@ -89,14 +74,12 @@ cisco_n1k_opts = [
|
||||
|
||||
cfg.CONF.register_opts(cisco_opts, "CISCO")
|
||||
cfg.CONF.register_opts(cisco_n1k_opts, "CISCO_N1K")
|
||||
cfg.CONF.register_opts(cisco_plugins_opts, "CISCO_PLUGINS")
|
||||
config.register_root_helper(cfg.CONF)
|
||||
|
||||
# shortcuts
|
||||
CONF = cfg.CONF
|
||||
CISCO = cfg.CONF.CISCO
|
||||
CISCO_N1K = cfg.CONF.CISCO_N1K
|
||||
CISCO_PLUGINS = cfg.CONF.CISCO_PLUGINS
|
||||
|
||||
#
|
||||
# device_dictionary - Contains all external device configuration.
|
||||
@ -143,7 +126,7 @@ class CiscoConfigOptions():
|
||||
for parsed_file in multi_parser.parsed:
|
||||
for parsed_item in parsed_file.keys():
|
||||
dev_id, sep, dev_ip = parsed_item.partition(':')
|
||||
if dev_id.lower() in ['nexus_switch', 'n1kv']:
|
||||
if dev_id.lower() == 'n1kv':
|
||||
for dev_key, value in parsed_file[parsed_item].items():
|
||||
if dev_ip and not first_device_ip:
|
||||
first_device_ip = dev_ip
|
||||
|
@ -1362,18 +1362,18 @@ class NetworkProfile_db_mixin(object):
|
||||
if segment_type == c_const.NETWORK_TYPE_VLAN:
|
||||
if not ((seg_min <= seg_max) and
|
||||
((seg_min in range(constants.MIN_VLAN_TAG,
|
||||
c_const.NEXUS_VLAN_RESERVED_MIN) and
|
||||
c_const.N1KV_VLAN_RESERVED_MIN) and
|
||||
seg_max in range(constants.MIN_VLAN_TAG,
|
||||
c_const.NEXUS_VLAN_RESERVED_MIN)) or
|
||||
(seg_min in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1,
|
||||
c_const.N1KV_VLAN_RESERVED_MIN)) or
|
||||
(seg_min in range(c_const.N1KV_VLAN_RESERVED_MAX + 1,
|
||||
constants.MAX_VLAN_TAG) and
|
||||
seg_max in range(c_const.NEXUS_VLAN_RESERVED_MAX + 1,
|
||||
seg_max in range(c_const.N1KV_VLAN_RESERVED_MAX + 1,
|
||||
constants.MAX_VLAN_TAG)))):
|
||||
msg = (_("Segment range is invalid, select from "
|
||||
"%(min)s-%(nmin)s, %(nmax)s-%(max)s") %
|
||||
{"min": constants.MIN_VLAN_TAG,
|
||||
"nmin": c_const.NEXUS_VLAN_RESERVED_MIN - 1,
|
||||
"nmax": c_const.NEXUS_VLAN_RESERVED_MAX + 1,
|
||||
"nmin": c_const.N1KV_VLAN_RESERVED_MIN - 1,
|
||||
"nmax": c_const.N1KV_VLAN_RESERVED_MAX + 1,
|
||||
"max": constants.MAX_VLAN_TAG - 1})
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
@ -1385,12 +1385,12 @@ class NetworkProfile_db_mixin(object):
|
||||
c_const.NETWORK_TYPE_MULTI_SEGMENT,
|
||||
c_const.NETWORK_TYPE_TRUNK]:
|
||||
if (seg_min > seg_max or
|
||||
seg_min < c_const.NEXUS_VXLAN_MIN or
|
||||
seg_max > c_const.NEXUS_VXLAN_MAX):
|
||||
seg_min < c_const.N1KV_VXLAN_MIN or
|
||||
seg_max > c_const.N1KV_VXLAN_MAX):
|
||||
msg = (_("segment range is invalid. Valid range is : "
|
||||
"%(min)s-%(max)s") %
|
||||
{"min": c_const.NEXUS_VXLAN_MIN,
|
||||
"max": c_const.NEXUS_VXLAN_MAX})
|
||||
{"min": c_const.N1KV_VXLAN_MIN,
|
||||
"max": c_const.N1KV_VXLAN_MAX})
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
profiles = _get_network_profiles(db_session=context.session)
|
||||
|
@ -22,7 +22,6 @@ from neutron.openstack.common import uuidutils
|
||||
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.db import network_models_v2
|
||||
from neutron.plugins.openvswitch import ovs_models_v2
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -236,13 +235,6 @@ def is_provider_vlan(vlan_id):
|
||||
return True
|
||||
|
||||
|
||||
def get_ovs_vlans():
|
||||
session = db.get_session()
|
||||
bindings = (session.query(ovs_models_v2.VlanAllocation.vlan_id).
|
||||
filter_by(allocated=True))
|
||||
return [binding.vlan_id for binding in bindings]
|
||||
|
||||
|
||||
class Credential_db_mixin(object):
|
||||
|
||||
"""Mixin class for Cisco Credentials as a resource."""
|
||||
|
@ -1,152 +0,0 @@
|
||||
# Copyright 2012, Cisco Systems, Inc.
|
||||
#
|
||||
# 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: Rohit Agarwalla, Cisco Systems, Inc.
|
||||
# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com)
|
||||
#
|
||||
|
||||
import sqlalchemy.orm.exc as sa_exc
|
||||
|
||||
import neutron.db.api as db
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as c_exc
|
||||
from neutron.plugins.cisco.db import nexus_models_v2
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
|
||||
"""Lists a nexusport binding."""
|
||||
LOG.debug(_("get_nexusport_binding() called"))
|
||||
return _lookup_all_nexus_bindings(port_id=port_id,
|
||||
vlan_id=vlan_id,
|
||||
switch_ip=switch_ip,
|
||||
instance_id=instance_id)
|
||||
|
||||
|
||||
def get_nexusvlan_binding(vlan_id, switch_ip):
|
||||
"""Lists a vlan and switch binding."""
|
||||
LOG.debug(_("get_nexusvlan_binding() called"))
|
||||
return _lookup_all_nexus_bindings(vlan_id=vlan_id, switch_ip=switch_ip)
|
||||
|
||||
|
||||
def add_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
|
||||
"""Adds a nexusport binding."""
|
||||
LOG.debug(_("add_nexusport_binding() called"))
|
||||
session = db.get_session()
|
||||
binding = nexus_models_v2.NexusPortBinding(port_id=port_id,
|
||||
vlan_id=vlan_id,
|
||||
switch_ip=switch_ip,
|
||||
instance_id=instance_id)
|
||||
session.add(binding)
|
||||
session.flush()
|
||||
return binding
|
||||
|
||||
|
||||
def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
|
||||
"""Removes a nexusport binding."""
|
||||
LOG.debug(_("remove_nexusport_binding() called"))
|
||||
session = db.get_session()
|
||||
binding = _lookup_all_nexus_bindings(session=session,
|
||||
vlan_id=vlan_id,
|
||||
switch_ip=switch_ip,
|
||||
port_id=port_id,
|
||||
instance_id=instance_id)
|
||||
for bind in binding:
|
||||
session.delete(bind)
|
||||
session.flush()
|
||||
return binding
|
||||
|
||||
|
||||
def update_nexusport_binding(port_id, new_vlan_id):
|
||||
"""Updates nexusport binding."""
|
||||
if not new_vlan_id:
|
||||
LOG.warning(_("update_nexusport_binding called with no vlan"))
|
||||
return
|
||||
LOG.debug(_("update_nexusport_binding called"))
|
||||
session = db.get_session()
|
||||
binding = _lookup_one_nexus_binding(session=session, port_id=port_id)
|
||||
binding.vlan_id = new_vlan_id
|
||||
session.merge(binding)
|
||||
session.flush()
|
||||
return binding
|
||||
|
||||
|
||||
def get_nexusvm_bindings(vlan_id, instance_id):
|
||||
"""Lists nexusvm bindings."""
|
||||
LOG.debug(_("get_nexusvm_binding() called"))
|
||||
|
||||
return _lookup_all_nexus_bindings(vlan_id=vlan_id,
|
||||
instance_id=instance_id)
|
||||
|
||||
|
||||
def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
|
||||
"""Lists nexusvm bindings."""
|
||||
LOG.debug(_("get_port_vlan_switch_binding() called"))
|
||||
return _lookup_all_nexus_bindings(port_id=port_id,
|
||||
switch_ip=switch_ip,
|
||||
vlan_id=vlan_id)
|
||||
|
||||
|
||||
def get_port_switch_bindings(port_id, switch_ip):
|
||||
"""List all vm/vlan bindings on a Nexus switch port."""
|
||||
LOG.debug(_("get_port_switch_bindings() called, "
|
||||
"port:'%(port_id)s', switch:'%(switch_ip)s'"),
|
||||
{'port_id': port_id, 'switch_ip': switch_ip})
|
||||
try:
|
||||
return _lookup_all_nexus_bindings(port_id=port_id,
|
||||
switch_ip=switch_ip)
|
||||
except c_exc.NexusPortBindingNotFound:
|
||||
pass
|
||||
|
||||
|
||||
def get_nexussvi_bindings():
|
||||
"""Lists nexus svi bindings."""
|
||||
LOG.debug(_("get_nexussvi_bindings() called"))
|
||||
return _lookup_all_nexus_bindings(port_id='router')
|
||||
|
||||
|
||||
def _lookup_nexus_bindings(query_type, session=None, **bfilter):
|
||||
"""Look up 'query_type' Nexus bindings matching the filter.
|
||||
|
||||
:param query_type: 'all', 'one' or 'first'
|
||||
:param session: db session
|
||||
:param bfilter: filter for bindings query
|
||||
:return: bindings if query gave a result, else
|
||||
raise NexusPortBindingNotFound.
|
||||
"""
|
||||
if session is None:
|
||||
session = db.get_session()
|
||||
query_method = getattr(session.query(
|
||||
nexus_models_v2.NexusPortBinding).filter_by(**bfilter), query_type)
|
||||
try:
|
||||
bindings = query_method()
|
||||
if bindings:
|
||||
return bindings
|
||||
except sa_exc.NoResultFound:
|
||||
pass
|
||||
raise c_exc.NexusPortBindingNotFound(**bfilter)
|
||||
|
||||
|
||||
def _lookup_all_nexus_bindings(session=None, **bfilter):
|
||||
return _lookup_nexus_bindings('all', session, **bfilter)
|
||||
|
||||
|
||||
def _lookup_one_nexus_binding(session=None, **bfilter):
|
||||
return _lookup_nexus_bindings('one', session, **bfilter)
|
||||
|
||||
|
||||
def _lookup_first_nexus_binding(session=None, **bfilter):
|
||||
return _lookup_nexus_bindings('first', session, **bfilter)
|
@ -1,44 +0,0 @@
|
||||
# Copyright 2012, Cisco Systems, Inc.
|
||||
#
|
||||
# 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: Rohit Agarwalla, Cisco Systems, Inc.
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import model_base
|
||||
|
||||
|
||||
class NexusPortBinding(model_base.BASEV2):
|
||||
"""Represents a binding of VM's to nexus ports."""
|
||||
|
||||
__tablename__ = "cisco_nexusport_bindings"
|
||||
|
||||
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
port_id = sa.Column(sa.String(255))
|
||||
vlan_id = sa.Column(sa.Integer, nullable=False)
|
||||
switch_ip = sa.Column(sa.String(255), nullable=False)
|
||||
instance_id = sa.Column(sa.String(255), nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
"""Just the binding, without the id key."""
|
||||
return ("<NexusPortBinding(%s,%s,%s,%s)>" %
|
||||
(self.port_id, self.vlan_id, self.switch_ip, self.instance_id))
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare only the binding, without the id key."""
|
||||
return (
|
||||
self.port_id == other.port_id and
|
||||
self.vlan_id == other.vlan_id and
|
||||
self.switch_ip == other.switch_ip and
|
||||
self.instance_id == other.instance_id
|
||||
)
|
@ -18,20 +18,19 @@
|
||||
#
|
||||
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron import neutron_plugin_base_v2
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common.gettextutils import _LE
|
||||
from neutron.openstack.common import importutils
|
||||
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_credentials_v2 as cred
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as cexc
|
||||
from neutron.plugins.cisco.common import config as conf
|
||||
from neutron.plugins.cisco.db import network_db_v2 as cdb
|
||||
from neutron.plugins.openvswitch import ovs_db_v2 as odb
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -40,9 +39,9 @@ LOG = logging.getLogger(__name__)
|
||||
class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""Virtual Physical Switch Model.
|
||||
|
||||
This implementation works with OVS and Nexus plugin for the
|
||||
This implementation works with n1kv sub-plugin for the
|
||||
following topology:
|
||||
One or more servers to a nexus switch.
|
||||
One or more servers to a n1kv switch.
|
||||
"""
|
||||
__native_bulk_support = True
|
||||
supported_extension_aliases = ["provider", "binding"]
|
||||
@ -64,12 +63,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
conf.CiscoConfigOptions()
|
||||
|
||||
self._plugins = {}
|
||||
for key in conf.CISCO_PLUGINS.keys():
|
||||
plugin_obj = conf.CISCO_PLUGINS[key]
|
||||
if plugin_obj is not None:
|
||||
self._plugins[key] = importutils.import_object(plugin_obj)
|
||||
LOG.debug(_("Loaded device plugin %s"),
|
||||
conf.CISCO_PLUGINS[key])
|
||||
self._plugins['vswitch_plugin'] = importutils.import_object(
|
||||
'neutron.plugins.cisco.n1kv.n1kv_neutron_plugin.'
|
||||
'N1kvNeutronPluginV2')
|
||||
|
||||
if ((const.VSWITCH_PLUGIN in self._plugins) and
|
||||
hasattr(self._plugins[const.VSWITCH_PLUGIN],
|
||||
@ -84,20 +80,14 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
{'module': __name__,
|
||||
'name': self.__class__.__name__})
|
||||
|
||||
# Check whether we have a valid Nexus driver loaded
|
||||
self.is_nexus_plugin = False
|
||||
nexus_driver = conf.CISCO.nexus_driver
|
||||
if nexus_driver.endswith('CiscoNEXUSDriver'):
|
||||
self.is_nexus_plugin = True
|
||||
|
||||
def __getattribute__(self, name):
|
||||
"""Delegate calls to OVS sub-plugin.
|
||||
"""Delegate calls to sub-plugin.
|
||||
|
||||
This delegates the calls to the methods implemented only by the OVS
|
||||
This delegates the calls to the methods implemented by the
|
||||
sub-plugin. Note: Currently, bulking is handled by the caller
|
||||
(PluginV2), and this model class expects to receive only non-bulking
|
||||
calls. If, however, a bulking call is made, this will method will
|
||||
delegate the call to the OVS plugin.
|
||||
delegate the call to the sub-plugin.
|
||||
"""
|
||||
super_getattribute = super(VirtualPhysicalSwitchModelV2,
|
||||
self).__getattribute__
|
||||
@ -137,12 +127,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
func = getattr(self._plugins[plugin_key], function_name)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
def _get_segmentation_id(self, network_id):
|
||||
binding_seg_id = odb.get_network_binding(None, network_id)
|
||||
if not binding_seg_id:
|
||||
raise cexc.NetworkSegmentIDNotFound(net_id=network_id)
|
||||
return binding_seg_id.segmentation_id
|
||||
|
||||
def _get_provider_vlan_id(self, network):
|
||||
if (all(attributes.is_attr_set(network.get(attr))
|
||||
for attr in (provider.NETWORK_TYPE,
|
||||
@ -161,34 +145,26 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
LOG.debug(_("create_network() called"))
|
||||
provider_vlan_id = self._get_provider_vlan_id(network[const.NETWORK])
|
||||
args = [context, network]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
switch_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
# The vswitch plugin did all the verification. If it's a provider
|
||||
# vlan network, save it for the nexus plugin to use later.
|
||||
# vlan network, save it for the sub-plugin to use later.
|
||||
if provider_vlan_id:
|
||||
network_id = ovs_output[const.NET_ID]
|
||||
network_id = switch_output[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
|
||||
return switch_output
|
||||
|
||||
def update_network(self, context, id, network):
|
||||
"""Update network.
|
||||
|
||||
Perform this operation in the context of the configured device
|
||||
plugins.
|
||||
|
||||
Note that the Nexus sub-plugin does not need to be notified
|
||||
(and the Nexus switch does not need to be [re]configured)
|
||||
for an update network operation because the Nexus sub-plugin
|
||||
is agnostic of all network-level attributes except the
|
||||
segmentation ID. Furthermore, updating of the segmentation ID
|
||||
is not supported by the OVS plugin since it is considered a
|
||||
provider attribute, so it is not supported by this method.
|
||||
"""
|
||||
LOG.debug(_("update_network() called"))
|
||||
|
||||
@ -210,12 +186,12 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
plugins.
|
||||
"""
|
||||
args = [context, id]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
switch_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
if cdb.remove_provider_network(id):
|
||||
LOG.debug(_("Provider network removed from DB: %s"), id)
|
||||
return ovs_output
|
||||
return switch_output
|
||||
|
||||
def get_network(self, context, id, fields=None):
|
||||
"""Get network. This method is delegated to the vswitch plugin.
|
||||
@ -232,30 +208,10 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""
|
||||
pass # pragma no cover
|
||||
|
||||
def _invoke_nexus_for_net_create(self, context, tenant_id, net_id,
|
||||
instance_id, host_id):
|
||||
if not self.is_nexus_plugin:
|
||||
return False
|
||||
|
||||
network = self.get_network(context, net_id)
|
||||
vlan_id = self._get_segmentation_id(net_id)
|
||||
vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id)
|
||||
network[const.NET_VLAN_ID] = vlan_id
|
||||
network[const.NET_VLAN_NAME] = vlan_name
|
||||
attachment = {
|
||||
const.TENANT_ID: tenant_id,
|
||||
const.INSTANCE_ID: instance_id,
|
||||
const.HOST_NAME: host_id,
|
||||
}
|
||||
self._invoke_plugin_per_device(
|
||||
const.NEXUS_PLUGIN,
|
||||
'create_network',
|
||||
[network, attachment])
|
||||
|
||||
def _check_valid_port_device_owner(self, port):
|
||||
"""Check the port for valid device_owner.
|
||||
|
||||
Don't call the nexus plugin for router and dhcp
|
||||
Don't call the sub-plugin for router and dhcp
|
||||
port owners.
|
||||
"""
|
||||
return port['device_owner'].startswith('compute')
|
||||
@ -278,36 +234,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""
|
||||
LOG.debug(_("create_port() called"))
|
||||
args = [context, port]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
instance_id = port['port']['device_id']
|
||||
|
||||
# Only call nexus plugin if there's a valid instance_id, host_id
|
||||
# and device_owner
|
||||
try:
|
||||
host_id = self._get_port_host_id_from_bindings(port['port'])
|
||||
if (instance_id and host_id and
|
||||
self._check_valid_port_device_owner(port['port'])):
|
||||
net_id = port['port']['network_id']
|
||||
tenant_id = port['port']['tenant_id']
|
||||
self._invoke_nexus_for_net_create(
|
||||
context, tenant_id, net_id, instance_id, host_id)
|
||||
except Exception:
|
||||
# Create network on the Nexus plugin has failed, so we need
|
||||
# to rollback the port creation on the VSwitch plugin.
|
||||
exc_info = sys.exc_info()
|
||||
try:
|
||||
id = ovs_output['id']
|
||||
args = [context, id]
|
||||
ovs_output = self._invoke_plugin_per_device(
|
||||
const.VSWITCH_PLUGIN,
|
||||
'delete_port',
|
||||
args)
|
||||
finally:
|
||||
# Re-raise the original exception
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
return ovs_output
|
||||
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
|
||||
def get_port(self, context, id, fields=None):
|
||||
"""Get port. This method is delegated to the vswitch plugin.
|
||||
@ -323,47 +252,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""
|
||||
pass # pragma no cover
|
||||
|
||||
def _check_nexus_net_create_needed(self, new_port, old_port):
|
||||
"""Check if nexus plugin should be invoked for net_create.
|
||||
|
||||
In the following cases, the plugin should be invoked:
|
||||
-- a port is attached to a VM instance. The old host id is None
|
||||
-- VM migration. The old host id has a valid value
|
||||
|
||||
When the plugin needs to be invoked, return the old_host_id,
|
||||
and a list of calling arguments.
|
||||
Otherwise, return '' for old host id and an empty list
|
||||
"""
|
||||
old_device_id = old_port['device_id']
|
||||
new_device_id = new_port.get('device_id')
|
||||
new_host_id = self._get_port_host_id_from_bindings(new_port)
|
||||
tenant_id = old_port['tenant_id']
|
||||
net_id = old_port['network_id']
|
||||
old_host_id = self._get_port_host_id_from_bindings(old_port)
|
||||
|
||||
LOG.debug(_("tenant_id: %(tid)s, net_id: %(nid)s, "
|
||||
"old_device_id: %(odi)s, new_device_id: %(ndi)s, "
|
||||
"old_host_id: %(ohi)s, new_host_id: %(nhi)s, "
|
||||
"old_device_owner: %(odo)s, new_device_owner: %(ndo)s"),
|
||||
{'tid': tenant_id, 'nid': net_id,
|
||||
'odi': old_device_id, 'ndi': new_device_id,
|
||||
'ohi': old_host_id, 'nhi': new_host_id,
|
||||
'odo': old_port.get('device_owner'),
|
||||
'ndo': new_port.get('device_owner')})
|
||||
|
||||
# A port is attached to an instance
|
||||
if (new_device_id and not old_device_id and new_host_id and
|
||||
self._check_valid_port_device_owner(new_port)):
|
||||
return '', [tenant_id, net_id, new_device_id, new_host_id]
|
||||
|
||||
# An instance is being migrated
|
||||
if (old_device_id and old_host_id and new_host_id != old_host_id and
|
||||
self._check_valid_port_device_owner(old_port)):
|
||||
return old_host_id, [tenant_id, net_id, old_device_id, new_host_id]
|
||||
|
||||
# no need to invoke the plugin
|
||||
return '', []
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
"""Update port.
|
||||
|
||||
@ -371,44 +259,10 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
plugins.
|
||||
"""
|
||||
LOG.debug(_("update_port() called"))
|
||||
old_port = self.get_port(context, id)
|
||||
args = [context, id, port]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
try:
|
||||
# Check if the nexus plugin needs to be invoked
|
||||
old_host_id, create_args = self._check_nexus_net_create_needed(
|
||||
port['port'], old_port)
|
||||
|
||||
# In the case of migration, invoke it to remove
|
||||
# the previous port binding
|
||||
if old_host_id:
|
||||
vlan_id = self._get_segmentation_id(old_port['network_id'])
|
||||
delete_args = [old_port['device_id'], vlan_id]
|
||||
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
"delete_port",
|
||||
delete_args)
|
||||
|
||||
# Invoke the Nexus plugin to create a net and/or new port binding
|
||||
if create_args:
|
||||
self._invoke_nexus_for_net_create(context, *create_args)
|
||||
|
||||
return ovs_output
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
LOG.error(_("Unable to update port '%s' on Nexus switch"),
|
||||
old_port['name'], exc_info=exc_info)
|
||||
try:
|
||||
# Roll back vSwitch plugin to original port attributes.
|
||||
args = [context, id, {'port': old_port}]
|
||||
self._invoke_plugin_per_device(
|
||||
const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
finally:
|
||||
# Re-raise the original exception
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
|
||||
def delete_port(self, context, id, l3_port_check=True):
|
||||
"""Delete port.
|
||||
@ -419,94 +273,18 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
LOG.debug(_("delete_port() called"))
|
||||
port = self.get_port(context, id)
|
||||
|
||||
host_id = self._get_port_host_id_from_bindings(port)
|
||||
|
||||
if (self.is_nexus_plugin and host_id and
|
||||
self._check_valid_port_device_owner(port)):
|
||||
vlan_id = self._get_segmentation_id(port['network_id'])
|
||||
n_args = [port['device_id'], vlan_id]
|
||||
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
try:
|
||||
args = [context, id]
|
||||
ovs_output = self._invoke_plugin_per_device(
|
||||
switch_output = self._invoke_plugin_per_device(
|
||||
const.VSWITCH_PLUGIN, self._func_name(),
|
||||
args, l3_port_check=l3_port_check)
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
# Roll back the delete port on the Nexus plugin
|
||||
try:
|
||||
tenant_id = port['tenant_id']
|
||||
net_id = port['network_id']
|
||||
instance_id = port['device_id']
|
||||
host_id = port[portbindings.HOST_ID]
|
||||
self._invoke_nexus_for_net_create(context, tenant_id, net_id,
|
||||
instance_id, host_id)
|
||||
finally:
|
||||
# Raise the original exception.
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Unable to delete port '%(pname)s' on switch. "
|
||||
"Exception: %(exp)s"), {'pname': port['name'],
|
||||
'exp': e})
|
||||
|
||||
return ovs_output
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
"""Add a router interface on a subnet.
|
||||
|
||||
Only invoke the Nexus plugin to create SVI if L3 support on
|
||||
the Nexus switches is enabled and a Nexus plugin is loaded,
|
||||
otherwise send it to the vswitch plugin
|
||||
"""
|
||||
if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin):
|
||||
LOG.debug(_("L3 enabled on Nexus plugin, create SVI on switch"))
|
||||
if 'subnet_id' not in interface_info:
|
||||
raise cexc.SubnetNotSpecified()
|
||||
if 'port_id' in interface_info:
|
||||
raise cexc.PortIdForNexusSvi()
|
||||
subnet = self.get_subnet(context, interface_info['subnet_id'])
|
||||
gateway_ip = subnet['gateway_ip']
|
||||
# Get gateway IP address and netmask
|
||||
cidr = subnet['cidr']
|
||||
netmask = cidr.split('/', 1)[1]
|
||||
gateway_ip = gateway_ip + '/' + netmask
|
||||
network_id = subnet['network_id']
|
||||
vlan_id = self._get_segmentation_id(network_id)
|
||||
vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id)
|
||||
|
||||
n_args = [vlan_name, vlan_id, subnet['id'], gateway_ip, router_id]
|
||||
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
else:
|
||||
LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch"))
|
||||
n_args = [context, router_id, interface_info]
|
||||
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
"""Remove a router interface.
|
||||
|
||||
Only invoke the Nexus plugin to delete SVI if L3 support on
|
||||
the Nexus switches is enabled and a Nexus plugin is loaded,
|
||||
otherwise send it to the vswitch plugin
|
||||
"""
|
||||
if (conf.CISCO.nexus_l3_enable and self.is_nexus_plugin):
|
||||
LOG.debug(_("L3 enabled on Nexus plugin, delete SVI from switch"))
|
||||
|
||||
subnet = self.get_subnet(context, interface_info['subnet_id'])
|
||||
network_id = subnet['network_id']
|
||||
vlan_id = self._get_segmentation_id(network_id)
|
||||
n_args = [vlan_id, router_id]
|
||||
|
||||
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
else:
|
||||
LOG.debug(_("L3 disabled or not Nexus plugin, send to vswitch"))
|
||||
n_args = [context, router_id, interface_info]
|
||||
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
return switch_output
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
"""Create subnet. This method is delegated to the vswitch plugin.
|
||||
|
@ -1,19 +0,0 @@
|
||||
# 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.
|
||||
# @author: Edgar Magana, Cisco Systems, Inc.
|
||||
"""
|
||||
Init module for Nexus Driver
|
||||
"""
|
@ -1,194 +0,0 @@
|
||||
# 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: Debojyoti Dutta, Cisco Systems, Inc.
|
||||
# @author: Edgar Magana, Cisco Systems Inc.
|
||||
#
|
||||
"""
|
||||
Implements a Nexus-OS NETCONF over SSHv2 API Client
|
||||
"""
|
||||
|
||||
|
||||
from ncclient import manager
|
||||
|
||||
from neutron.openstack.common import excutils
|
||||
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_credentials_v2 as cred
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as cexc
|
||||
from neutron.plugins.cisco.common import config as conf
|
||||
from neutron.plugins.cisco.db import nexus_db_v2
|
||||
from neutron.plugins.cisco.nexus import cisco_nexus_snippets as snipp
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CiscoNEXUSDriver():
|
||||
"""Nexus Driver Main Class."""
|
||||
def __init__(self):
|
||||
cisco_switches = conf.get_device_dictionary()
|
||||
self.nexus_switches = dict(((key[1], key[2]), val)
|
||||
for key, val in cisco_switches.items()
|
||||
if key[0] == 'NEXUS_SWITCH')
|
||||
self.credentials = {}
|
||||
self.connections = {}
|
||||
|
||||
def _edit_config(self, nexus_host, target='running', config='',
|
||||
allowed_exc_strs=None):
|
||||
"""Modify switch config for a target config type.
|
||||
|
||||
:param nexus_host: IP address of switch to configure
|
||||
:param target: Target config type
|
||||
:param config: Configuration string in XML format
|
||||
:param allowed_exc_strs: Exceptions which have any of these strings
|
||||
as a subset of their exception message
|
||||
(str(exception)) can be ignored
|
||||
|
||||
:raises: NexusConfigFailed
|
||||
|
||||
"""
|
||||
if not allowed_exc_strs:
|
||||
allowed_exc_strs = []
|
||||
mgr = self.nxos_connect(nexus_host)
|
||||
try:
|
||||
mgr.edit_config(target, config=config)
|
||||
except Exception as e:
|
||||
for exc_str in allowed_exc_strs:
|
||||
if exc_str in str(e):
|
||||
break
|
||||
else:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original ncclient exception. No need to preserve T/B
|
||||
raise cexc.NexusConfigFailed(config=config, exc=e)
|
||||
|
||||
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] = {
|
||||
const.USERNAME: nexus_username,
|
||||
const.PASSWORD: nexus_password
|
||||
}
|
||||
return self.credentials[nexus_ip]
|
||||
|
||||
def nxos_connect(self, nexus_host):
|
||||
"""Make SSH connection to the Nexus Switch."""
|
||||
if getattr(self.connections.get(nexus_host), 'connected', None):
|
||||
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:
|
||||
man = manager.connect(host=nexus_host,
|
||||
port=nexus_ssh_port,
|
||||
username=nexus_user,
|
||||
password=nexus_password)
|
||||
self.connections[nexus_host] = man
|
||||
except Exception as e:
|
||||
# Raise a Neutron exception. Include a description of
|
||||
# the original ncclient exception. No need to preserve T/B.
|
||||
raise cexc.NexusConnectFailed(nexus_host=nexus_host, exc=e)
|
||||
|
||||
return self.connections[nexus_host]
|
||||
|
||||
def create_xml_snippet(self, cutomized_config):
|
||||
"""Create XML snippet.
|
||||
|
||||
Creates the Proper XML structure for the Nexus Switch Configuration.
|
||||
"""
|
||||
conf_xml_snippet = snipp.EXEC_CONF_SNIPPET % (cutomized_config)
|
||||
return conf_xml_snippet
|
||||
|
||||
def create_vlan(self, nexus_host, vlanid, vlanname):
|
||||
"""Create a VLAN on Nexus Switch given the VLAN ID and Name."""
|
||||
confstr = self.create_xml_snippet(
|
||||
snipp.CMD_VLAN_CONF_SNIPPET % (vlanid, vlanname))
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
# Enable VLAN active and no-shutdown states. Some versions of
|
||||
# Nexus switch do not allow state changes for the extended VLAN
|
||||
# range (1006-4094), but these errors can be ignored (default
|
||||
# values are appropriate).
|
||||
state_config = [snipp.CMD_VLAN_ACTIVE_SNIPPET,
|
||||
snipp.CMD_VLAN_NO_SHUTDOWN_SNIPPET]
|
||||
for snippet in state_config:
|
||||
try:
|
||||
confstr = self.create_xml_snippet(snippet % vlanid)
|
||||
self._edit_config(
|
||||
nexus_host,
|
||||
target='running',
|
||||
config=confstr,
|
||||
allowed_exc_strs=["Can't modify state for extended",
|
||||
"Command is only allowed on VLAN"])
|
||||
except cexc.NexusConfigFailed:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.delete_vlan(nexus_host, vlanid)
|
||||
|
||||
def delete_vlan(self, nexus_host, vlanid):
|
||||
"""Delete a VLAN on Nexus Switch given the VLAN ID."""
|
||||
confstr = snipp.CMD_NO_VLAN_CONF_SNIPPET % vlanid
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def enable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface):
|
||||
"""Enable a VLAN on a trunk interface."""
|
||||
# If one or more VLANs are already configured on this interface,
|
||||
# include the 'add' keyword.
|
||||
if nexus_db_v2.get_port_switch_bindings('%s:%s' % (etype, interface),
|
||||
nexus_host):
|
||||
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
|
||||
else:
|
||||
snippet = snipp.CMD_INT_VLAN_SNIPPET
|
||||
confstr = snippet % (etype, interface, vlanid, etype)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def disable_vlan_on_trunk_int(self, nexus_host, vlanid, etype, interface):
|
||||
"""Disable a VLAN on a trunk interface."""
|
||||
confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (etype, interface,
|
||||
vlanid, etype)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name,
|
||||
etype, nexus_port):
|
||||
"""Create VLAN and trunk it on the specified ports."""
|
||||
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,
|
||||
etype, nexus_port)
|
||||
|
||||
def delete_and_untrunk_vlan(self, nexus_host, vlan_id, etype, nexus_port):
|
||||
"""Delete VLAN and untrunk it from the specified ports."""
|
||||
self.delete_vlan(nexus_host, vlan_id)
|
||||
if nexus_port:
|
||||
self.disable_vlan_on_trunk_int(nexus_host, vlan_id,
|
||||
etype, nexus_port)
|
||||
|
||||
def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
|
||||
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def delete_vlan_svi(self, nexus_host, vlan_id):
|
||||
confstr = snipp.CMD_NO_VLAN_SVI_SNIPPET % vlan_id
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
@ -1,345 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
# @author: Edgar Magana, Cisco Systems, Inc.
|
||||
# @author: Arvind Somya, Cisco Systems, Inc. (asomya@cisco.com)
|
||||
#
|
||||
|
||||
"""
|
||||
PlugIn for Nexus OS driver
|
||||
"""
|
||||
|
||||
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import importutils
|
||||
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 cisco_exc
|
||||
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 import l2device_plugin_base
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NexusPlugin(l2device_plugin_base.L2DevicePluginBase):
|
||||
"""Nexus PlugIn Main Class."""
|
||||
_networks = {}
|
||||
|
||||
def __init__(self):
|
||||
"""Extract configuration parameters from the configuration file."""
|
||||
self._client = importutils.import_object(conf.CISCO.nexus_driver)
|
||||
LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver)
|
||||
self._nexus_switches = conf.get_device_dictionary()
|
||||
|
||||
def create_network(self, network, attachment):
|
||||
"""Create or update a network when an attachment is changed.
|
||||
|
||||
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
|
||||
appropriate interfaces for this VLAN.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:create_network() called"))
|
||||
# Grab the switch IPs and ports for this host
|
||||
host_connections = []
|
||||
host = attachment['host_name']
|
||||
for switch_type, switch_ip, attr in self._nexus_switches:
|
||||
if str(attr) == str(host):
|
||||
port = self._nexus_switches[switch_type, switch_ip, attr]
|
||||
# Get ether type for port, assume an ethernet type
|
||||
# if none specified.
|
||||
if ':' in port:
|
||||
etype, port_id = port.split(':')
|
||||
else:
|
||||
etype, port_id = 'ethernet', port
|
||||
host_connections.append((switch_ip, etype, port_id))
|
||||
if not host_connections:
|
||||
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
|
||||
for switch_ip, etype, port_id in host_connections:
|
||||
vlan_created = False
|
||||
vlan_trunked = False
|
||||
eport_id = '%s:%s' % (etype, port_id)
|
||||
# Check for switch vlan bindings
|
||||
try:
|
||||
# This vlan has already been created on this switch
|
||||
# via another operation, like SVI bindings.
|
||||
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
vlan_created = True
|
||||
auto_create = False
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
# No changes, proceed as normal
|
||||
pass
|
||||
|
||||
try:
|
||||
nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id,
|
||||
switch_ip)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
if auto_create and auto_trunk:
|
||||
# Create vlan and trunk vlan on the port
|
||||
LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
|
||||
self._client.create_and_trunk_vlan(
|
||||
switch_ip, vlan_id, vlan_name, etype, port_id)
|
||||
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
|
||||
elif auto_trunk:
|
||||
# Only trunk vlan on the port
|
||||
LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
|
||||
self._client.enable_vlan_on_trunk_int(
|
||||
switch_ip, vlan_id, etype, port_id)
|
||||
vlan_trunked = True
|
||||
|
||||
try:
|
||||
instance = attachment[const.INSTANCE_ID]
|
||||
nxos_db.add_nexusport_binding(eport_id, str(vlan_id),
|
||||
switch_ip, instance)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
# Add binding failed, roll back any vlan creation/enabling
|
||||
if vlan_created and vlan_trunked:
|
||||
LOG.debug(_("Nexus: delete & untrunk vlan %s"),
|
||||
vlan_name)
|
||||
self._client.delete_and_untrunk_vlan(switch_ip,
|
||||
vlan_id,
|
||||
etype, port_id)
|
||||
elif vlan_created:
|
||||
LOG.debug(_("Nexus: delete vlan %s"), vlan_name)
|
||||
self._client.delete_vlan(switch_ip, vlan_id)
|
||||
elif vlan_trunked:
|
||||
LOG.debug(_("Nexus: untrunk vlan %s"), vlan_name)
|
||||
self._client.disable_vlan_on_trunk_int(switch_ip,
|
||||
vlan_id,
|
||||
etype,
|
||||
port_id)
|
||||
|
||||
net_id = network[const.NET_ID]
|
||||
new_net_dict = {const.NET_ID: net_id,
|
||||
const.NET_NAME: network[const.NET_NAME],
|
||||
const.NET_PORTS: {},
|
||||
const.NET_VLAN_NAME: vlan_name,
|
||||
const.NET_VLAN_ID: vlan_id}
|
||||
self._networks[net_id] = new_net_dict
|
||||
return new_net_dict
|
||||
|
||||
def add_router_interface(self, vlan_name, vlan_id, subnet_id,
|
||||
gateway_ip, router_id):
|
||||
"""Create VLAN SVI on the Nexus switch."""
|
||||
# Find a switch to create the SVI on
|
||||
switch_ip = self._find_switch_for_svi()
|
||||
if not switch_ip:
|
||||
raise cisco_exc.NoNexusSviSwitch()
|
||||
|
||||
# Check if this vlan exists on the switch already
|
||||
try:
|
||||
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
# Create vlan and trunk vlan on the port
|
||||
self._client.create_and_trunk_vlan(
|
||||
switch_ip, vlan_id, vlan_name, etype=None, nexus_port=None)
|
||||
# Check if a router interface has already been created
|
||||
try:
|
||||
nxos_db.get_nexusvm_bindings(vlan_id, router_id)
|
||||
raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id,
|
||||
router_id=router_id)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
self._client.create_vlan_svi(switch_ip, vlan_id, gateway_ip)
|
||||
nxos_db.add_nexusport_binding('router', str(vlan_id),
|
||||
switch_ip, router_id)
|
||||
|
||||
return True
|
||||
|
||||
def remove_router_interface(self, vlan_id, router_id):
|
||||
"""Remove VLAN SVI from the Nexus Switch."""
|
||||
# Grab switch_ip from database
|
||||
switch_ip = nxos_db.get_nexusvm_bindings(vlan_id,
|
||||
router_id)[0].switch_ip
|
||||
|
||||
# Delete the SVI interface from the switch
|
||||
self._client.delete_vlan_svi(switch_ip, vlan_id)
|
||||
|
||||
# Invoke delete_port to delete this row
|
||||
# And delete vlan if required
|
||||
return self.delete_port(router_id, vlan_id)
|
||||
|
||||
def _find_switch_for_svi(self):
|
||||
"""Get a switch to create the SVI on."""
|
||||
LOG.debug(_("Grabbing a switch to create SVI"))
|
||||
nexus_switches = self._client.nexus_switches
|
||||
if conf.CISCO.svi_round_robin:
|
||||
LOG.debug(_("Using round robin to create SVI"))
|
||||
switch_dict = dict(
|
||||
(switch_ip, 0) for switch_ip, _ in nexus_switches)
|
||||
try:
|
||||
bindings = nxos_db.get_nexussvi_bindings()
|
||||
# Build a switch dictionary with weights
|
||||
for binding in bindings:
|
||||
switch_ip = binding.switch_ip
|
||||
if switch_ip not in switch_dict:
|
||||
switch_dict[switch_ip] = 1
|
||||
else:
|
||||
switch_dict[switch_ip] += 1
|
||||
# Search for the lowest value in the dict
|
||||
if switch_dict:
|
||||
switch_ip = min(switch_dict, key=switch_dict.get)
|
||||
return switch_ip
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
pass
|
||||
|
||||
LOG.debug(_("No round robin or zero weights, using first switch"))
|
||||
# Return the first switch in the config
|
||||
return conf.first_device_ip
|
||||
|
||||
def delete_network(self, tenant_id, net_id, **kwargs):
|
||||
"""Delete network.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:delete_network() called")) # pragma no cover
|
||||
|
||||
def update_network(self, tenant_id, net_id, **kwargs):
|
||||
"""Update the properties of a particular Virtual Network.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:update_network() called")) # pragma no cover
|
||||
|
||||
def create_port(self, tenant_id, net_id, port_state, port_id, **kwargs):
|
||||
"""Create port.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:create_port() called")) # pragma no cover
|
||||
|
||||
def delete_port(self, device_id, vlan_id):
|
||||
"""Delete port.
|
||||
|
||||
Delete port bindings from the database and scan whether the network
|
||||
is still required on the interfaces trunked.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:delete_port() called"))
|
||||
# Delete DB row(s) for this port
|
||||
try:
|
||||
rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
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)
|
||||
|
||||
instance_id = False
|
||||
for row in rows:
|
||||
instance_id = row['instance_id']
|
||||
switch_ip = row.switch_ip
|
||||
etype, nexus_port = '', ''
|
||||
if row['port_id'] == 'router':
|
||||
etype, nexus_port = 'vlan', row['port_id']
|
||||
auto_untrunk = False
|
||||
else:
|
||||
etype, nexus_port = row['port_id'].split(':')
|
||||
|
||||
nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
|
||||
row.switch_ip,
|
||||
row.instance_id)
|
||||
# Check whether there are any remaining instances using this
|
||||
# vlan on this Nexus port.
|
||||
try:
|
||||
nxos_db.get_port_vlan_switch_binding(row.port_id,
|
||||
row.vlan_id,
|
||||
row.switch_ip)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
try:
|
||||
if nexus_port and auto_untrunk:
|
||||
# Untrunk the vlan from this Nexus interface
|
||||
self._client.disable_vlan_on_trunk_int(
|
||||
switch_ip, row.vlan_id, etype, nexus_port)
|
||||
|
||||
# Check whether there are any remaining instances
|
||||
# using this vlan on the Nexus switch.
|
||||
if auto_delete:
|
||||
try:
|
||||
nxos_db.get_nexusvlan_binding(row.vlan_id,
|
||||
row.switch_ip)
|
||||
except cisco_exc.NexusPortBindingNotFound:
|
||||
# Delete this vlan from this switch
|
||||
self._client.delete_vlan(switch_ip, row.vlan_id)
|
||||
except Exception:
|
||||
# The delete vlan operation on the Nexus failed,
|
||||
# so this delete_port request has failed. For
|
||||
# consistency, roll back the Nexus database to what
|
||||
# it was before this request.
|
||||
with excutils.save_and_reraise_exception():
|
||||
nxos_db.add_nexusport_binding(row.port_id,
|
||||
row.vlan_id,
|
||||
row.switch_ip,
|
||||
row.instance_id)
|
||||
|
||||
return instance_id
|
||||
|
||||
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
|
||||
"""Update port.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:update_port() called")) # pragma no cover
|
||||
|
||||
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id,
|
||||
**kwargs):
|
||||
"""Plug interfaces.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:plug_interface() called")) # pragma no cover
|
||||
|
||||
def unplug_interface(self, tenant_id, net_id, port_id, **kwargs):
|
||||
"""Unplug interface.
|
||||
|
||||
Not applicable to Nexus plugin. Defined here to satisfy abstract
|
||||
method requirements.
|
||||
"""
|
||||
LOG.debug(_("NexusPlugin:unplug_interface() called")
|
||||
) # pragma no cover
|
@ -1,173 +0,0 @@
|
||||
# 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: Edgar Magana, Cisco Systems, Inc.
|
||||
# @author: Arvind Somya (asomya@cisco.com) Cisco Systems, Inc.
|
||||
|
||||
"""
|
||||
Nexus-OS XML-based configuration snippets
|
||||
"""
|
||||
|
||||
|
||||
# The following are standard strings, messages used to communicate with Nexus,
|
||||
EXEC_CONF_SNIPPET = """
|
||||
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
|
||||
<configure>
|
||||
<__XML__MODE__exec_configure>%s
|
||||
</__XML__MODE__exec_configure>
|
||||
</configure>
|
||||
</config>
|
||||
"""
|
||||
|
||||
CMD_VLAN_CONF_SNIPPET = """
|
||||
<vlan>
|
||||
<vlan-id-create-delete>
|
||||
<__XML__PARAM_value>%s</__XML__PARAM_value>
|
||||
<__XML__MODE_vlan>
|
||||
<name>
|
||||
<vlan-name>%s</vlan-name>
|
||||
</name>
|
||||
</__XML__MODE_vlan>
|
||||
</vlan-id-create-delete>
|
||||
</vlan>
|
||||
"""
|
||||
|
||||
CMD_VLAN_ACTIVE_SNIPPET = """
|
||||
<vlan>
|
||||
<vlan-id-create-delete>
|
||||
<__XML__PARAM_value>%s</__XML__PARAM_value>
|
||||
<__XML__MODE_vlan>
|
||||
<state>
|
||||
<vstate>active</vstate>
|
||||
</state>
|
||||
</__XML__MODE_vlan>
|
||||
</vlan-id-create-delete>
|
||||
</vlan>
|
||||
"""
|
||||
|
||||
CMD_VLAN_NO_SHUTDOWN_SNIPPET = """
|
||||
<vlan>
|
||||
<vlan-id-create-delete>
|
||||
<__XML__PARAM_value>%s</__XML__PARAM_value>
|
||||
<__XML__MODE_vlan>
|
||||
<no>
|
||||
<shutdown/>
|
||||
</no>
|
||||
</__XML__MODE_vlan>
|
||||
</vlan-id-create-delete>
|
||||
</vlan>
|
||||
"""
|
||||
|
||||
CMD_NO_VLAN_CONF_SNIPPET = """
|
||||
<no>
|
||||
<vlan>
|
||||
<vlan-id-create-delete>
|
||||
<__XML__PARAM_value>%s</__XML__PARAM_value>
|
||||
</vlan-id-create-delete>
|
||||
</vlan>
|
||||
</no>
|
||||
"""
|
||||
|
||||
CMD_INT_VLAN_HEADER = """
|
||||
<interface>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<switchport>
|
||||
<trunk>
|
||||
<allowed>
|
||||
<vlan>"""
|
||||
|
||||
CMD_VLAN_ID = """
|
||||
<vlan_id>%s</vlan_id>"""
|
||||
|
||||
CMD_VLAN_ADD_ID = """
|
||||
<add>%s
|
||||
</add>""" % CMD_VLAN_ID
|
||||
|
||||
CMD_INT_VLAN_TRAILER = """
|
||||
</vlan>
|
||||
</allowed>
|
||||
</trunk>
|
||||
</switchport>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
CMD_INT_VLAN_SNIPPET = (CMD_INT_VLAN_HEADER +
|
||||
CMD_VLAN_ID +
|
||||
CMD_INT_VLAN_TRAILER)
|
||||
|
||||
CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
|
||||
CMD_VLAN_ADD_ID +
|
||||
CMD_INT_VLAN_TRAILER)
|
||||
|
||||
CMD_NO_VLAN_INT_SNIPPET = """
|
||||
<interface>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<switchport></switchport>
|
||||
<switchport>
|
||||
<trunk>
|
||||
<allowed>
|
||||
<vlan>
|
||||
<remove>
|
||||
<vlan>%s</vlan>
|
||||
</remove>
|
||||
</vlan>
|
||||
</allowed>
|
||||
</trunk>
|
||||
</switchport>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
FILTER_SHOW_VLAN_BRIEF_SNIPPET = """
|
||||
<show xmlns="http://www.cisco.com/nxos:1.0:vlan_mgr_cli">
|
||||
<vlan>
|
||||
<brief/>
|
||||
</vlan>
|
||||
</show>
|
||||
"""
|
||||
|
||||
CMD_VLAN_SVI_SNIPPET = """
|
||||
<interface>
|
||||
<vlan>
|
||||
<vlan>%s</vlan>
|
||||
<__XML__MODE_vlan>
|
||||
<no>
|
||||
<shutdown/>
|
||||
</no>
|
||||
<ip>
|
||||
<address>
|
||||
<address>%s</address>
|
||||
</address>
|
||||
</ip>
|
||||
</__XML__MODE_vlan>
|
||||
</vlan>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
CMD_NO_VLAN_SVI_SNIPPET = """
|
||||
<no>
|
||||
<interface>
|
||||
<vlan>
|
||||
<vlan>%s</vlan>
|
||||
</vlan>
|
||||
</interface>
|
||||
</no>
|
||||
"""
|
@ -1,17 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import __builtin__
|
||||
setattr(__builtin__, '_', lambda x: x)
|
@ -1,99 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
# @author: Rohit Agarwalla, Cisco Systems, Inc.
|
||||
|
||||
|
||||
class CiscoNEXUSFakeDriver():
|
||||
"""Nexus Driver Fake Class."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def nxos_connect(self, nexus_host, nexus_ssh_port, nexus_user,
|
||||
nexus_password):
|
||||
"""Make the fake connection to the Nexus Switch."""
|
||||
pass
|
||||
|
||||
def create_xml_snippet(self, cutomized_config):
|
||||
"""Create XML snippet.
|
||||
|
||||
Creates the Proper XML structure for the Nexus Switch
|
||||
Configuration.
|
||||
"""
|
||||
pass
|
||||
|
||||
def enable_vlan(self, mgr, vlanid, vlanname):
|
||||
"""Create a VLAN on Nexus Switch given the VLAN ID and Name."""
|
||||
pass
|
||||
|
||||
def disable_vlan(self, mgr, vlanid):
|
||||
"""Delete a VLAN on Nexus Switch given the VLAN ID."""
|
||||
pass
|
||||
|
||||
def disable_switch_port(self, mgr, interface):
|
||||
"""Disable trunk mode an interface on Nexus Switch."""
|
||||
pass
|
||||
|
||||
def enable_vlan_on_trunk_int(self, mgr, etype, interface, vlanid):
|
||||
"""Enable vlan on trunk interface.
|
||||
|
||||
Enable trunk mode vlan access an interface on Nexus Switch given
|
||||
VLANID.
|
||||
"""
|
||||
pass
|
||||
|
||||
def disable_vlan_on_trunk_int(self, mgr, interface, vlanid):
|
||||
"""Disables vlan in trunk interface.
|
||||
|
||||
Enables trunk mode vlan access an interface on Nexus Switch given
|
||||
VLANID.
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
|
||||
nexus_password, nexus_ports, nexus_ssh_port, vlan_ids):
|
||||
"""Create VLAN and enable it on interface.
|
||||
|
||||
Creates a VLAN and Enable on trunk mode an interface on Nexus Switch
|
||||
given the VLAN ID and Name and Interface Number.
|
||||
"""
|
||||
pass
|
||||
|
||||
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.
|
||||
"""
|
||||
pass
|
||||
|
||||
def build_vlans_cmd(self):
|
||||
"""Build a string with all the VLANs on the same Switch."""
|
||||
pass
|
||||
|
||||
def add_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
|
||||
nexus_ports, nexus_ssh_port, vlan_ids=None):
|
||||
"""Add a vlan from interfaces on the Nexus switch given the VLAN ID."""
|
||||
pass
|
||||
|
||||
def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
|
||||
nexus_ports, nexus_ssh_port):
|
||||
"""Remove vlan from interfaces.
|
||||
|
||||
Removes a vlan from interfaces on the Nexus switch given the VLAN ID.
|
||||
"""
|
||||
pass
|
@ -1,72 +0,0 @@
|
||||
# Copyright (c) 2013 Cisco Systems Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.plugins.cisco.common import config as cisco_config
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
class TestCiscoNexusPluginConfig(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Point neutron config file to: neutron/tests/etc/neutron.conf.test
|
||||
self.config_parse()
|
||||
|
||||
super(TestCiscoNexusPluginConfig, self).setUp()
|
||||
|
||||
def test_config_parse_error(self):
|
||||
"""Check that config error is raised upon config parser failure."""
|
||||
with mock.patch.object(cfg, 'MultiConfigParser') as parser:
|
||||
parser.return_value.read.return_value = []
|
||||
self.assertRaises(cfg.Error, cisco_config.CiscoConfigOptions)
|
||||
|
||||
def test_create_device_dictionary(self):
|
||||
"""Test creation of the device dictionary based on nexus config."""
|
||||
test_config = {
|
||||
'NEXUS_SWITCH:1.1.1.1': {
|
||||
'username': ['admin'],
|
||||
'password': ['mySecretPassword'],
|
||||
'ssh_port': [22],
|
||||
'compute1': ['1/1'],
|
||||
'compute2': ['1/2'],
|
||||
},
|
||||
'NEXUS_SWITCH:2.2.2.2': {
|
||||
'username': ['admin'],
|
||||
'password': ['mySecretPassword'],
|
||||
'ssh_port': [22],
|
||||
'compute3': ['1/1'],
|
||||
'compute4': ['1/2'],
|
||||
},
|
||||
}
|
||||
expected_dev_dict = {
|
||||
('NEXUS_SWITCH', '1.1.1.1', 'username'): 'admin',
|
||||
('NEXUS_SWITCH', '1.1.1.1', 'password'): 'mySecretPassword',
|
||||
('NEXUS_SWITCH', '1.1.1.1', 'ssh_port'): 22,
|
||||
('NEXUS_SWITCH', '1.1.1.1', 'compute1'): '1/1',
|
||||
('NEXUS_SWITCH', '1.1.1.1', 'compute2'): '1/2',
|
||||
('NEXUS_SWITCH', '2.2.2.2', 'username'): 'admin',
|
||||
('NEXUS_SWITCH', '2.2.2.2', 'password'): 'mySecretPassword',
|
||||
('NEXUS_SWITCH', '2.2.2.2', 'ssh_port'): 22,
|
||||
('NEXUS_SWITCH', '2.2.2.2', 'compute3'): '1/1',
|
||||
('NEXUS_SWITCH', '2.2.2.2', 'compute4'): '1/2',
|
||||
}
|
||||
with mock.patch.object(cfg, 'MultiConfigParser') as parser:
|
||||
parser.return_value.read.return_value = cfg.CONF.config_file
|
||||
parser.return_value.parsed = [test_config]
|
||||
cisco_config.CiscoConfigOptions()
|
||||
self.assertEqual(cisco_config.device_dictionary,
|
||||
expected_dev_dict)
|
File diff suppressed because it is too large
Load Diff
@ -1,237 +0,0 @@
|
||||
# Copyright (c) 2013 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from neutron.db import api as db
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as c_exc
|
||||
from neutron.plugins.cisco.common import config
|
||||
from neutron.plugins.cisco.db import nexus_db_v2 as nxdb
|
||||
from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class CiscoNexusDbTest(testlib_api.SqlTestCase):
|
||||
|
||||
"""Unit tests for cisco.db.nexus_models_v2.NexusPortBinding model."""
|
||||
|
||||
NpbObj = collections.namedtuple('NpbObj', 'port vlan switch instance')
|
||||
|
||||
def setUp(self):
|
||||
super(CiscoNexusDbTest, self).setUp()
|
||||
self.session = db.get_session()
|
||||
|
||||
def _npb_test_obj(self, pnum, vnum, switch=None, instance=None):
|
||||
"""Create a Nexus port binding test object from a pair of numbers."""
|
||||
if pnum is 'router':
|
||||
port = pnum
|
||||
else:
|
||||
port = '1/%s' % str(pnum)
|
||||
vlan = str(vnum)
|
||||
if switch is None:
|
||||
switch = '10.9.8.7'
|
||||
if instance is None:
|
||||
instance = 'instance_%s_%s' % (str(pnum), str(vnum))
|
||||
return self.NpbObj(port, vlan, switch, instance)
|
||||
|
||||
def _assert_equal(self, npb, npb_obj):
|
||||
self.assertEqual(npb.port_id, npb_obj.port)
|
||||
self.assertEqual(int(npb.vlan_id), int(npb_obj.vlan))
|
||||
self.assertEqual(npb.switch_ip, npb_obj.switch)
|
||||
self.assertEqual(npb.instance_id, npb_obj.instance)
|
||||
|
||||
def _add_to_db(self, npbs):
|
||||
for npb in npbs:
|
||||
nxdb.add_nexusport_binding(
|
||||
npb.port, npb.vlan, npb.switch, npb.instance)
|
||||
|
||||
def test_nexusportbinding_add_remove(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb = nxdb.add_nexusport_binding(
|
||||
npb11.port, npb11.vlan, npb11.switch, npb11.instance)
|
||||
self._assert_equal(npb, npb11)
|
||||
npb = nxdb.remove_nexusport_binding(
|
||||
npb11.port, npb11.vlan, npb11.switch, npb11.instance)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb11)
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.remove_nexusport_binding(
|
||||
npb11.port, npb11.vlan, npb11.switch, npb11.instance)
|
||||
|
||||
def test_nexusportbinding_get(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb21 = self._npb_test_obj(20, 100)
|
||||
npb22 = self._npb_test_obj(20, 200)
|
||||
self._add_to_db([npb11, npb21, npb22])
|
||||
|
||||
npb = nxdb.get_nexusport_binding(
|
||||
npb11.port, npb11.vlan, npb11.switch, npb11.instance)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb11)
|
||||
npb = nxdb.get_nexusport_binding(
|
||||
npb21.port, npb21.vlan, npb21.switch, npb21.instance)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb21)
|
||||
npb = nxdb.get_nexusport_binding(
|
||||
npb22.port, npb22.vlan, npb22.switch, npb22.instance)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb22)
|
||||
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.get_nexusport_binding(
|
||||
npb21.port, npb21.vlan, npb21.switch, "dummyInstance")
|
||||
|
||||
def test_nexusvlanbinding_get(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb21 = self._npb_test_obj(20, 100)
|
||||
npb22 = self._npb_test_obj(20, 200)
|
||||
self._add_to_db([npb11, npb21, npb22])
|
||||
|
||||
npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, npb11.switch)
|
||||
self.assertEqual(len(npb_all_v100), 2)
|
||||
npb_v200 = nxdb.get_nexusvlan_binding(npb22.vlan, npb22.switch)
|
||||
self.assertEqual(len(npb_v200), 1)
|
||||
self._assert_equal(npb_v200[0], npb22)
|
||||
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.get_nexusvlan_binding(npb21.vlan, "dummySwitch")
|
||||
|
||||
def test_nexusvmbinding_get(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb21 = self._npb_test_obj(20, 100)
|
||||
npb22 = self._npb_test_obj(20, 200)
|
||||
self._add_to_db([npb11, npb21, npb22])
|
||||
|
||||
npb = nxdb.get_nexusvm_bindings(npb21.vlan, npb21.instance)[0]
|
||||
self._assert_equal(npb, npb21)
|
||||
npb = nxdb.get_nexusvm_bindings(npb22.vlan, npb22.instance)[0]
|
||||
self._assert_equal(npb, npb22)
|
||||
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.get_nexusvm_bindings(npb21.vlan, "dummyInstance")
|
||||
|
||||
def test_nexusportvlanswitchbinding_get(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb21 = self._npb_test_obj(20, 100)
|
||||
self._add_to_db([npb11, npb21])
|
||||
|
||||
npb = nxdb.get_port_vlan_switch_binding(
|
||||
npb11.port, npb11.vlan, npb11.switch)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb11)
|
||||
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.get_port_vlan_switch_binding(
|
||||
npb21.port, npb21.vlan, "dummySwitch")
|
||||
|
||||
def test_nexusportswitchbinding_get(self):
|
||||
npb11 = self._npb_test_obj(10, 100)
|
||||
npb21 = self._npb_test_obj(20, 100, switch='2.2.2.2')
|
||||
npb22 = self._npb_test_obj(20, 200, switch='2.2.2.2')
|
||||
self._add_to_db([npb11, npb21, npb22])
|
||||
|
||||
npb = nxdb.get_port_switch_bindings(npb11.port, npb11.switch)
|
||||
self.assertEqual(len(npb), 1)
|
||||
self._assert_equal(npb[0], npb11)
|
||||
npb_all_p20 = nxdb.get_port_switch_bindings(npb21.port, npb21.switch)
|
||||
self.assertEqual(len(npb_all_p20), 2)
|
||||
|
||||
npb = nxdb.get_port_switch_bindings(npb21.port, "dummySwitch")
|
||||
self.assertIsNone(npb)
|
||||
|
||||
def test_nexussvibinding_get(self):
|
||||
npbr1 = self._npb_test_obj('router', 100)
|
||||
npb21 = self._npb_test_obj(20, 100)
|
||||
self._add_to_db([npbr1, npb21])
|
||||
|
||||
npb_svi = nxdb.get_nexussvi_bindings()
|
||||
self.assertEqual(len(npb_svi), 1)
|
||||
self._assert_equal(npb_svi[0], npbr1)
|
||||
|
||||
npbr2 = self._npb_test_obj('router', 200)
|
||||
self._add_to_db([npbr2])
|
||||
npb_svi = nxdb.get_nexussvi_bindings()
|
||||
self.assertEqual(len(npb_svi), 2)
|
||||
|
||||
def test_nexussviswitch_find(self):
|
||||
"""Test Nexus switch selection for SVI placement."""
|
||||
# Configure 2 Nexus switches
|
||||
nexus_switches = {
|
||||
('1.1.1.1', 'username'): 'admin',
|
||||
('1.1.1.1', 'password'): 'password1',
|
||||
('1.1.1.1', 'host1'): '1/1',
|
||||
('2.2.2.2', 'username'): 'admin',
|
||||
('2.2.2.2', 'password'): 'password2',
|
||||
('2.2.2.2', 'host2'): '1/1',
|
||||
}
|
||||
nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin()
|
||||
nexus_plugin._client = mock.Mock()
|
||||
nexus_plugin._client.nexus_switches = nexus_switches
|
||||
|
||||
# Set the Cisco config module's first configured device IP address
|
||||
# according to the preceding switch config
|
||||
with mock.patch.object(config, 'first_device_ip', new='1.1.1.1'):
|
||||
|
||||
# Enable round-robin mode with no SVIs configured on any of the
|
||||
# Nexus switches (i.e. no entries in the SVI database). The
|
||||
# plugin should select the first switch in the configuration.
|
||||
config.CONF.set_override('svi_round_robin', True, 'CISCO')
|
||||
switch_ip = nexus_plugin._find_switch_for_svi()
|
||||
self.assertEqual(switch_ip, '1.1.1.1')
|
||||
|
||||
# Keep round-robin mode enabled, and add entries to the SVI
|
||||
# database. The plugin should select the switch with the least
|
||||
# number of entries in the SVI database.
|
||||
vlan = 100
|
||||
npbr11 = self._npb_test_obj('router', vlan, switch='1.1.1.1',
|
||||
instance='instance11')
|
||||
npbr12 = self._npb_test_obj('router', vlan, switch='1.1.1.1',
|
||||
instance='instance12')
|
||||
npbr21 = self._npb_test_obj('router', vlan, switch='2.2.2.2',
|
||||
instance='instance21')
|
||||
self._add_to_db([npbr11, npbr12, npbr21])
|
||||
switch_ip = nexus_plugin._find_switch_for_svi()
|
||||
self.assertEqual(switch_ip, '2.2.2.2')
|
||||
|
||||
# Disable round-robin mode. The plugin should select the
|
||||
# first switch in the configuration.
|
||||
config.CONF.clear_override('svi_round_robin', 'CISCO')
|
||||
switch_ip = nexus_plugin._find_switch_for_svi()
|
||||
self.assertEqual(switch_ip, '1.1.1.1')
|
||||
|
||||
def test_nexusbinding_update(self):
|
||||
npb11 = self._npb_test_obj(10, 100, switch='1.1.1.1', instance='test')
|
||||
npb21 = self._npb_test_obj(20, 100, switch='1.1.1.1', instance='test')
|
||||
self._add_to_db([npb11, npb21])
|
||||
|
||||
npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, '1.1.1.1')
|
||||
self.assertEqual(len(npb_all_v100), 2)
|
||||
|
||||
npb22 = self._npb_test_obj(20, 200, switch='1.1.1.1', instance='test')
|
||||
npb = nxdb.update_nexusport_binding(npb21.port, 200)
|
||||
self._assert_equal(npb, npb22)
|
||||
|
||||
npb_all_v100 = nxdb.get_nexusvlan_binding(npb11.vlan, '1.1.1.1')
|
||||
self.assertEqual(len(npb_all_v100), 1)
|
||||
self._assert_equal(npb_all_v100[0], npb11)
|
||||
|
||||
npb = nxdb.update_nexusport_binding(npb21.port, 0)
|
||||
self.assertIsNone(npb)
|
||||
|
||||
npb33 = self._npb_test_obj(30, 300, switch='1.1.1.1', instance='test')
|
||||
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
|
||||
nxdb.update_nexusport_binding(npb33.port, 200)
|
@ -1,297 +0,0 @@
|
||||
# Copyright (c) 2012 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.
|
||||
|
||||
import contextlib
|
||||
import mock
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron.openstack.common import importutils
|
||||
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 config as cisco_config
|
||||
from neutron.plugins.cisco.db import network_db_v2 as cdb
|
||||
from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
NEXUS_IP_ADDRESS = '1.1.1.1'
|
||||
HOSTNAME1 = 'testhost1'
|
||||
HOSTNAME2 = 'testhost2'
|
||||
HOSTNAME3 = 'testhost3'
|
||||
INSTANCE1 = 'testvm1'
|
||||
INSTANCE2 = 'testvm2'
|
||||
INSTANCE3 = 'testvm3'
|
||||
NEXUS_PORT1 = '1/10'
|
||||
NEXUS_PORT2 = '1/20'
|
||||
NEXUS_PC_IP_ADDRESS = '2.2.2.2'
|
||||
NEXUS_PORTCHANNELS = 'portchannel:2'
|
||||
PC_HOSTNAME = 'testpchost'
|
||||
NEXUS_SSH_PORT = '22'
|
||||
NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.'
|
||||
'cisco_nexus_network_driver_v2.CiscoNEXUSDriver')
|
||||
NET_ATTRS = [const.NET_ID,
|
||||
const.NET_NAME,
|
||||
const.NET_VLAN_NAME,
|
||||
const.NET_VLAN_ID]
|
||||
|
||||
|
||||
class TestCiscoNexusPlugin(testlib_api.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Set up function."""
|
||||
super(TestCiscoNexusPlugin, self).setUp()
|
||||
self.tenant_id = "test_tenant_cisco1"
|
||||
self.net_name = "test_network_cisco1"
|
||||
self.net_id = 7
|
||||
self.vlan_name = "q-" + str(self.net_id) + "vlan"
|
||||
self.vlan_id = 267
|
||||
self.second_tenant_id = "test_tenant_2"
|
||||
self.second_net_name = "test_network_cisco2"
|
||||
self.second_net_id = 5
|
||||
self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
|
||||
self.second_vlan_id = 265
|
||||
self._pchostname = PC_HOSTNAME
|
||||
|
||||
self.attachment1 = {
|
||||
const.TENANT_ID: self.tenant_id,
|
||||
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.attachment3 = {
|
||||
const.TENANT_ID: self.second_tenant_id,
|
||||
const.INSTANCE_ID: INSTANCE3,
|
||||
const.HOST_NAME: HOSTNAME3,
|
||||
}
|
||||
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.network3 = {
|
||||
const.NET_ID: 8,
|
||||
const.NET_NAME: 'vpc_net',
|
||||
const.NET_VLAN_NAME: 'q-268',
|
||||
const.NET_VLAN_ID: '268',
|
||||
}
|
||||
self.delete_port_args_1 = [
|
||||
self.attachment1[const.INSTANCE_ID],
|
||||
self.network1[const.NET_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,
|
||||
}
|
||||
|
||||
def new_nexus_init(self):
|
||||
self._client = importutils.import_object(NEXUS_DRIVER)
|
||||
self._client.nexus_switches = {
|
||||
(NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1,
|
||||
(NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
|
||||
(NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
|
||||
(NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
|
||||
(NEXUS_PC_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
|
||||
}
|
||||
self._nexus_switches = {
|
||||
('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1,
|
||||
('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
|
||||
('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, HOSTNAME3):
|
||||
NEXUS_PORTCHANNELS,
|
||||
('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, 'ssh_port'):
|
||||
NEXUS_SSH_PORT,
|
||||
('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME3):
|
||||
NEXUS_PORTCHANNELS,
|
||||
('NEXUS_SWITCH', NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
|
||||
}
|
||||
self._client.credentials = {
|
||||
NEXUS_IP_ADDRESS: {
|
||||
'username': 'admin',
|
||||
'password': 'pass1234'
|
||||
},
|
||||
NEXUS_PC_IP_ADDRESS: {
|
||||
'username': 'admin',
|
||||
'password': 'password'
|
||||
},
|
||||
}
|
||||
|
||||
# Use a mock netconf client
|
||||
self.mock_ncclient = mock.Mock()
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.dict('sys.modules', {'ncclient': self.mock_ncclient}),
|
||||
mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin,
|
||||
'__init__', new=new_nexus_init)
|
||||
):
|
||||
self._cisco_nexus_plugin = cisco_nexus_plugin_v2.NexusPlugin()
|
||||
|
||||
# Set the Cisco config module's first configured device IP address
|
||||
# according to the preceding switch config.
|
||||
mock.patch.object(cisco_config, 'first_device_ip',
|
||||
new=NEXUS_IP_ADDRESS).start()
|
||||
|
||||
def test_create_delete_networks(self):
|
||||
"""Tests creation of two new Virtual Networks."""
|
||||
new_net_dict = self._cisco_nexus_plugin.create_network(
|
||||
self.network1, self.attachment1)
|
||||
for attr in NET_ATTRS:
|
||||
self.assertEqual(new_net_dict[attr], self.network1[attr])
|
||||
|
||||
expected_instance_id = self._cisco_nexus_plugin.delete_port(
|
||||
INSTANCE1, self.vlan_id)
|
||||
|
||||
self.assertEqual(expected_instance_id, INSTANCE1)
|
||||
|
||||
new_net_dict = self._cisco_nexus_plugin.create_network(
|
||||
self.network2, self.attachment1)
|
||||
for attr in NET_ATTRS:
|
||||
self.assertEqual(new_net_dict[attr], self.network2[attr])
|
||||
|
||||
expected_instance_id = self._cisco_nexus_plugin.delete_port(
|
||||
INSTANCE1, self.second_vlan_id)
|
||||
|
||||
self.assertEqual(expected_instance_id, INSTANCE1)
|
||||
|
||||
def _create_delete_providernet(self, auto_create, auto_trunk):
|
||||
cfg.CONF.set_override(
|
||||
'provider_vlan_auto_create', auto_create, 'CISCO')
|
||||
cfg.CONF.set_override(
|
||||
'provider_vlan_auto_trunk', auto_trunk, 'CISCO')
|
||||
with mock.patch.object(cdb, 'is_provider_vlan',
|
||||
return_value=True) as mock_db:
|
||||
# Create a provider network
|
||||
new_net_dict = self._cisco_nexus_plugin.create_network(
|
||||
self.providernet, self.attachment1)
|
||||
self.assertEqual(mock_db.call_count, 1)
|
||||
for attr in NET_ATTRS:
|
||||
self.assertEqual(new_net_dict[attr], self.providernet[attr])
|
||||
# Delete the provider network
|
||||
instance_id = self._cisco_nexus_plugin.delete_port(
|
||||
self.attachment1[const.INSTANCE_ID],
|
||||
self.providernet[const.NET_VLAN_ID])
|
||||
self.assertEqual(instance_id,
|
||||
self.attachment1[const.INSTANCE_ID])
|
||||
|
||||
def test_create_delete_providernet(self):
|
||||
self._create_delete_providernet(auto_create=True, auto_trunk=True)
|
||||
|
||||
def test_create_delete_provider_vlan_network_cfg_auto_man(self):
|
||||
self._create_delete_providernet(auto_create=True, auto_trunk=False)
|
||||
|
||||
def test_create_delete_provider_vlan_network_cfg_man_auto(self):
|
||||
self._create_delete_providernet(auto_create=False, auto_trunk=True)
|
||||
|
||||
def test_create_delete_provider_vlan_network_cfg_man_man(self):
|
||||
self._create_delete_providernet(auto_create=False, auto_trunk=False)
|
||||
|
||||
def test_create_delete_network_portchannel(self):
|
||||
"""Tests creation of a network over a portchannel."""
|
||||
new_net_dict = self._cisco_nexus_plugin.create_network(
|
||||
self.network3, self.attachment3)
|
||||
self.assertEqual(new_net_dict[const.NET_ID],
|
||||
self.network3[const.NET_ID])
|
||||
self.assertEqual(new_net_dict[const.NET_NAME],
|
||||
self.network3[const.NET_NAME])
|
||||
self.assertEqual(new_net_dict[const.NET_VLAN_NAME],
|
||||
self.network3[const.NET_VLAN_NAME])
|
||||
self.assertEqual(new_net_dict[const.NET_VLAN_ID],
|
||||
self.network3[const.NET_VLAN_ID])
|
||||
|
||||
self._cisco_nexus_plugin.delete_port(
|
||||
INSTANCE3, self.network3[const.NET_VLAN_ID]
|
||||
)
|
||||
|
||||
def _add_router_interface(self):
|
||||
"""Add a router interface using fixed (canned) parameters."""
|
||||
vlan_name = self.vlan_name
|
||||
vlan_id = self.vlan_id
|
||||
gateway_ip = '10.0.0.1/24'
|
||||
router_id = '00000R1'
|
||||
subnet_id = '00001'
|
||||
return self._cisco_nexus_plugin.add_router_interface(
|
||||
vlan_name, vlan_id, subnet_id, gateway_ip, router_id)
|
||||
|
||||
def _remove_router_interface(self):
|
||||
"""Remove a router interface created with _add_router_interface."""
|
||||
vlan_id = self.vlan_id
|
||||
router_id = '00000R1'
|
||||
return self._cisco_nexus_plugin.remove_router_interface(vlan_id,
|
||||
router_id)
|
||||
|
||||
def test_nexus_add_remove_router_interface(self):
|
||||
"""Tests addition of a router interface."""
|
||||
self.assertTrue(self._add_router_interface())
|
||||
self.assertEqual(self._remove_router_interface(), '00000R1')
|
||||
|
||||
def test_nexus_dup_add_router_interface(self):
|
||||
"""Tests a duplicate add of a router interface."""
|
||||
self._add_router_interface()
|
||||
try:
|
||||
self.assertRaises(
|
||||
cisco_exc.SubnetInterfacePresent,
|
||||
self._add_router_interface)
|
||||
finally:
|
||||
self._remove_router_interface()
|
||||
|
||||
def test_nexus_no_svi_switch_exception(self):
|
||||
"""Tests failure to find a Nexus switch for SVI placement."""
|
||||
# Clear the Nexus switches dictionary.
|
||||
with mock.patch.dict(self._cisco_nexus_plugin._client.nexus_switches,
|
||||
{}, clear=True):
|
||||
# Clear the first Nexus IP address discovered in config
|
||||
with mock.patch.object(cisco_config, 'first_device_ip',
|
||||
new=None):
|
||||
self.assertRaises(cisco_exc.NoNexusSviSwitch,
|
||||
self._add_router_interface)
|
||||
|
||||
def test_nexus_add_port_after_router_interface(self):
|
||||
"""Tests creating a port after a router interface.
|
||||
|
||||
Test creating a port after an SVI router interface has
|
||||
been created. Only a trunk call should be invoked and the
|
||||
plugin should not attempt to recreate the vlan.
|
||||
"""
|
||||
self._add_router_interface()
|
||||
# Create a network on the switch
|
||||
self._cisco_nexus_plugin.create_network(
|
||||
self.network1, self.attachment1)
|
||||
|
||||
# Grab a list of all mock calls from ncclient
|
||||
last_cfgs = (self.mock_ncclient.manager.connect.return_value.
|
||||
edit_config.mock_calls)
|
||||
|
||||
# The last ncclient call should be for trunking and the second
|
||||
# to last call should be creating the SVI interface
|
||||
last_cfg = last_cfgs[-1][2]['config']
|
||||
self.assertIn('allowed', last_cfg)
|
||||
|
||||
slast_cfg = last_cfgs[-2][2]['config']
|
||||
self.assertIn('10.0.0.1/24', slast_cfg)
|
@ -1,63 +0,0 @@
|
||||
# Copyright 2014 Cisco Systems, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
|
||||
import mock
|
||||
|
||||
from neutron import context
|
||||
from neutron.plugins.cisco.common import cisco_constants as const
|
||||
from neutron.plugins.cisco.common import config as cisco_config
|
||||
from neutron.plugins.cisco.models import virt_phy_sw_v2
|
||||
from neutron.plugins.cisco.nexus import cisco_nexus_plugin_v2
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class TestCiscoPluginModel(testlib_api.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Point config file to: neutron/tests/etc/neutron.conf.test
|
||||
self.config_parse()
|
||||
|
||||
super(TestCiscoPluginModel, self).setUp()
|
||||
|
||||
def test_non_nexus_device_driver(self):
|
||||
"""Tests handling of an non-Nexus device driver being configured."""
|
||||
with mock.patch.dict(sys.modules, {'mock_driver': mock.Mock()}):
|
||||
cisco_config.CONF.set_override('nexus_driver',
|
||||
'mock_driver.Non_Nexus_Driver',
|
||||
'CISCO')
|
||||
# Plugin model instance should have is_nexus_plugin set to False
|
||||
model = virt_phy_sw_v2.VirtualPhysicalSwitchModelV2()
|
||||
self.assertFalse(model.is_nexus_plugin)
|
||||
|
||||
# Model's _invoke_nexus_for_net_create should just return False
|
||||
user_id = 'user_id'
|
||||
tenant_id = 'tenant_id'
|
||||
ctx = context.Context(user_id, tenant_id)
|
||||
self.assertFalse(model._invoke_nexus_for_net_create(
|
||||
ctx, tenant_id, net_id='net_id',
|
||||
instance_id='instance_id', host_id='host_id'))
|
||||
|
||||
def test_nexus_plugin_calls_ignored_if_plugin_not_loaded(self):
|
||||
"""Verifies Nexus plugin calls are ignored if plugin is not loaded."""
|
||||
cisco_config.CONF.set_override(const.NEXUS_PLUGIN,
|
||||
None, 'CISCO_PLUGINS')
|
||||
with mock.patch.object(cisco_nexus_plugin_v2.NexusPlugin,
|
||||
'create_network') as mock_create_network:
|
||||
model = virt_phy_sw_v2.VirtualPhysicalSwitchModelV2()
|
||||
model._invoke_plugin_per_device(model, const.NEXUS_PLUGIN,
|
||||
'create_network')
|
||||
self.assertFalse(mock_create_network.called)
|
Loading…
Reference in New Issue
Block a user