Merge "Add support for the Nexus 1000V into the Cisco Plugin."
This commit is contained in:
commit
cba5f28267
@ -85,3 +85,22 @@
|
||||
# username=admin
|
||||
# password=mySecretPassword
|
||||
|
||||
#
|
||||
# N1KV Format.
|
||||
# [N1KV:<IP address of VSM>]
|
||||
# username=<credential username>
|
||||
# password=<credential password>
|
||||
#
|
||||
# Example:
|
||||
# [N1KV:2.2.2.2]
|
||||
# username=admin
|
||||
# password=mySecretPassword
|
||||
|
||||
[cisco_n1k]
|
||||
# integration_bridge=br-int
|
||||
# enable_tunneling=True
|
||||
# tunnel_bridge=br-tun
|
||||
# local_ip=10.0.0.3
|
||||
# tenant_network_type=local
|
||||
# default_policy_profile=<my default dhcp/router policy profile name>
|
||||
# poll_duration=<Time in seconds>
|
||||
|
@ -105,5 +105,14 @@
|
||||
"create_floatingip": "rule:regular_user",
|
||||
"update_floatingip": "rule:admin_or_owner",
|
||||
"delete_floatingip": "rule:admin_or_owner",
|
||||
"get_floatingip": "rule:admin_or_owner"
|
||||
"get_floatingip": "rule:admin_or_owner",
|
||||
|
||||
"create_network_profile": "rule:admin_only",
|
||||
"update_network_profile": "rule:admin_only",
|
||||
"delete_network_profile": "rule:admin_only",
|
||||
"get_network_profiles": "",
|
||||
"get_network_profile": "",
|
||||
"update_policy_profiles": "rule:admin_only",
|
||||
"get_policy_profiles": "",
|
||||
"get_policy_profile": ""
|
||||
}
|
||||
|
@ -43,12 +43,9 @@ def upgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
if 'credentials' in sa.MetaData().tables:
|
||||
op.rename_table('credentials', 'cisco_credentials')
|
||||
if 'nexusport_bindings' in sa.MetaData().tables:
|
||||
op.rename_table('nexusport_bindings', 'cisco_nexusport_bindings')
|
||||
if 'qoss' in sa.MetaData().tables:
|
||||
op.rename_table('qoss', 'cisco_qos_policies')
|
||||
op.rename_table('credentials', 'cisco_credentials')
|
||||
op.rename_table('nexusport_bindings', 'cisco_nexusport_bindings')
|
||||
op.rename_table('qoss', 'cisco_qos_policies')
|
||||
|
||||
op.drop_table('cisco_vlan_ids')
|
||||
|
||||
@ -64,9 +61,6 @@ def downgrade(active_plugins=None, options=None):
|
||||
sa.PrimaryKeyConstraint('vlan_id'),
|
||||
)
|
||||
|
||||
if 'cisco_credentials' in sa.MetaData().tables:
|
||||
op.rename_table('cisco_credentials', 'credentials')
|
||||
if 'cisco_nexusport_bindings' in sa.MetaData().tables:
|
||||
op.rename_table('cisco_nexusport_bindings', 'nexusport_bindings')
|
||||
if 'cisco_qos_policies' in sa.MetaData().tables:
|
||||
op.rename_table('cisco_qos_policies', 'qoss')
|
||||
op.rename_table('cisco_credentials', 'credentials')
|
||||
op.rename_table('cisco_nexusport_bindings', 'nexusport_bindings')
|
||||
op.rename_table('cisco_qos_policies', 'qoss')
|
||||
|
@ -0,0 +1,144 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Cisco N1KV tables
|
||||
|
||||
Revision ID: c88b6b5fea3
|
||||
Revises: 263772d65691
|
||||
Create Date: 2013-08-06 15:08:32.651975
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c88b6b5fea3'
|
||||
down_revision = '263772d65691'
|
||||
|
||||
migration_for_plugins = [
|
||||
'neutron.plugins.cisco.network_plugin.PluginV2'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.drop_column('cisco_credentials', 'tenant_id')
|
||||
op.add_column(
|
||||
'cisco_credentials',
|
||||
sa.Column('type', sa.String(length=255), nullable=True)
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_policy_profiles',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_vmnetworks',
|
||||
sa.Column('name', sa.String(length=80), nullable=False),
|
||||
sa.Column('profile_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('network_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('port_count', sa.Integer(), autoincrement=False,
|
||||
nullable=True),
|
||||
sa.ForeignKeyConstraint(['profile_id'], ['cisco_policy_profiles.id']),
|
||||
sa.PrimaryKeyConstraint('name')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_vxlan_allocations',
|
||||
sa.Column('vxlan_id', sa.Integer(), autoincrement=False,
|
||||
nullable=False),
|
||||
sa.Column('allocated', sa.Boolean(), autoincrement=False,
|
||||
nullable=False),
|
||||
sa.PrimaryKeyConstraint('vxlan_id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_network_profiles',
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('segment_type', sa.Enum('vlan', 'vxlan'), nullable=False),
|
||||
sa.Column('segment_range', sa.String(length=255), nullable=True),
|
||||
sa.Column('multicast_ip_index', sa.Integer(), autoincrement=False,
|
||||
nullable=True),
|
||||
sa.Column('multicast_ip_range', sa.String(length=255), nullable=True),
|
||||
sa.Column('physical_network', sa.String(length=255), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_profile_bindings',
|
||||
sa.Column('profile_type', sa.Enum('network', 'policy'), nullable=True),
|
||||
sa.Column('tenant_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('profile_id', sa.String(length=36), nullable=False),
|
||||
sa.PrimaryKeyConstraint('tenant_id', 'profile_id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_port_bindings',
|
||||
sa.Column('port_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('profile_id', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['port_id'], ['ports.id']),
|
||||
sa.ForeignKeyConstraint(['profile_id'], ['cisco_policy_profiles.id']),
|
||||
sa.PrimaryKeyConstraint('port_id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_vlan_allocations',
|
||||
sa.Column('physical_network', sa.String(length=64), nullable=False),
|
||||
sa.Column('vlan_id',
|
||||
sa.Integer(),
|
||||
autoincrement=False,
|
||||
nullable=False),
|
||||
sa.Column('allocated',
|
||||
sa.Boolean(),
|
||||
autoincrement=False,
|
||||
nullable=False),
|
||||
sa.PrimaryKeyConstraint('physical_network', 'vlan_id')
|
||||
)
|
||||
op.create_table(
|
||||
'cisco_n1kv_network_bindings',
|
||||
sa.Column('network_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('network_type', sa.String(length=32), nullable=False),
|
||||
sa.Column('physical_network', sa.String(length=64), nullable=True),
|
||||
sa.Column('segmentation_id', sa.Integer(), autoincrement=False,
|
||||
nullable=True),
|
||||
sa.Column('multicast_ip', sa.String(length=32), nullable=True),
|
||||
sa.Column('profile_id', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['network_id'], ['networks.id']),
|
||||
sa.ForeignKeyConstraint(['profile_id'], ['cisco_network_profiles.id']),
|
||||
sa.PrimaryKeyConstraint('network_id')
|
||||
)
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
if not migration.should_run(active_plugins, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.drop_table('cisco_n1kv_network_bindings')
|
||||
op.drop_table('cisco_n1kv_vlan_allocations')
|
||||
op.drop_table('cisco_n1kv_port_bindings')
|
||||
op.drop_table('cisco_n1kv_profile_bindings')
|
||||
op.drop_table('cisco_network_profiles')
|
||||
op.drop_table('cisco_n1kv_vxlan_allocations')
|
||||
op.drop_table('cisco_n1kv_vmnetworks')
|
||||
op.drop_table('cisco_policy_profiles')
|
||||
op.drop_column('cisco_credentials', 'type')
|
||||
op.add_column(
|
||||
'cisco_credentials',
|
||||
sa.Column('tenant_id', sa.String(length=255), nullable=False)
|
||||
)
|
@ -40,6 +40,7 @@ CREDENTIAL_ID = 'credential_id'
|
||||
CREDENTIAL_NAME = 'credential_name'
|
||||
CREDENTIAL_USERNAME = 'user_name'
|
||||
CREDENTIAL_PASSWORD = 'password'
|
||||
CREDENTIAL_TYPE = 'type'
|
||||
MASKED_PASSWORD = '********'
|
||||
|
||||
USERNAME = 'username'
|
||||
@ -59,3 +60,31 @@ PORT = 'port'
|
||||
BASE_PLUGIN_REF = 'base_plugin_ref'
|
||||
CONTEXT = 'context'
|
||||
SUBNET = 'subnet'
|
||||
|
||||
#### N1Kv CONSTANTS
|
||||
# Special vlan_id value in n1kv_vlan_allocations table indicating flat network
|
||||
FLAT_VLAN_ID = -1
|
||||
|
||||
# Topic for tunnel notifications between the plugin and agent
|
||||
TUNNEL = 'tunnel'
|
||||
|
||||
# Maximum VXLAN range configurable for one network profile.
|
||||
MAX_VXLAN_RANGE = 1000000
|
||||
|
||||
# Values for network_type
|
||||
NETWORK_TYPE_FLAT = 'flat'
|
||||
NETWORK_TYPE_VLAN = 'vlan'
|
||||
NETWORK_TYPE_VXLAN = 'vxlan'
|
||||
NETWORK_TYPE_LOCAL = 'local'
|
||||
NETWORK_TYPE_NONE = 'none'
|
||||
|
||||
# Prefix for VM Network name
|
||||
VM_NETWORK_NAME_PREFIX = 'vmn_'
|
||||
|
||||
SET = 'set'
|
||||
INSTANCE = 'instance'
|
||||
PROPERTIES = 'properties'
|
||||
NAME = 'name'
|
||||
ID = 'id'
|
||||
POLICY = 'policy'
|
||||
TENANT_ID_NOT_SET = 'TENANT_ID_NOT_SET'
|
||||
|
@ -23,57 +23,56 @@ from neutron.plugins.cisco.common import cisco_exceptions as cexc
|
||||
from neutron.plugins.cisco.common import config
|
||||
from neutron.plugins.cisco.db import network_db_v2 as cdb
|
||||
|
||||
|
||||
LOG.basicConfig(level=LOG.WARN)
|
||||
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
|
||||
|
||||
TENANT = const.NETWORK_ADMIN
|
||||
|
||||
_nexus_dict = config.get_nexus_dictionary()
|
||||
|
||||
|
||||
class Store(object):
|
||||
"""Credential Store."""
|
||||
|
||||
@staticmethod
|
||||
def initialize():
|
||||
for keys in _nexus_dict.keys():
|
||||
if keys[1] == const.USERNAME:
|
||||
dev_dict = config.get_device_dictionary()
|
||||
for key in dev_dict:
|
||||
dev_id, dev_ip, dev_key = key
|
||||
if dev_key == const.USERNAME:
|
||||
try:
|
||||
cdb.add_credential(TENANT, keys[0],
|
||||
_nexus_dict[keys[0], const.USERNAME],
|
||||
_nexus_dict[keys[0], const.PASSWORD])
|
||||
cdb.add_credential(
|
||||
dev_ip,
|
||||
dev_dict[dev_id, dev_ip, const.USERNAME],
|
||||
dev_dict[dev_id, dev_ip, const.PASSWORD],
|
||||
dev_id)
|
||||
except cexc.CredentialAlreadyExists:
|
||||
# We are quietly ignoring this, since it only happens
|
||||
# if this class module is loaded more than once, in which
|
||||
# case, the credentials are already populated
|
||||
# if this class module is loaded more than once, in
|
||||
# which case, the credentials are already populated
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def put_credential(cred_name, username, password):
|
||||
"""Set the username and password."""
|
||||
cdb.add_credential(TENANT, cred_name, username, password)
|
||||
cdb.add_credential(cred_name, username, password)
|
||||
|
||||
@staticmethod
|
||||
def get_username(cred_name):
|
||||
"""Get the username."""
|
||||
credential = cdb.get_credential_name(TENANT, cred_name)
|
||||
credential = cdb.get_credential_name(cred_name)
|
||||
return credential[const.CREDENTIAL_USERNAME]
|
||||
|
||||
@staticmethod
|
||||
def get_password(cred_name):
|
||||
"""Get the password."""
|
||||
credential = cdb.get_credential_name(TENANT, cred_name)
|
||||
credential = cdb.get_credential_name(cred_name)
|
||||
return credential[const.CREDENTIAL_PASSWORD]
|
||||
|
||||
@staticmethod
|
||||
def get_credential(cred_name):
|
||||
"""Get the username and password."""
|
||||
cdb.get_credential_name(TENANT, cred_name)
|
||||
cdb.get_credential_name(cred_name)
|
||||
return {const.USERNAME: const.CREDENTIAL_USERNAME,
|
||||
const.PASSWORD: const.CREDENTIAL_PASSWORD}
|
||||
|
||||
@staticmethod
|
||||
def delete_credential(cred_name):
|
||||
"""Delete a credential."""
|
||||
cdb.remove_credential(TENANT, cred_name)
|
||||
cdb.remove_credential(cred_name)
|
||||
|
@ -28,55 +28,58 @@ class NetworkSegmentIDNotFound(exceptions.NeutronException):
|
||||
|
||||
|
||||
class NoMoreNics(exceptions.NeutronException):
|
||||
"""No more dynamic nics are available in the system."""
|
||||
message = _("Unable to complete operation. No more dynamic nics are "
|
||||
"""No more dynamic NICs are available in the system."""
|
||||
message = _("Unable to complete operation. No more dynamic NICs are "
|
||||
"available in the system.")
|
||||
|
||||
|
||||
class NetworkVlanBindingAlreadyExists(exceptions.NeutronException):
|
||||
"""Binding cannot be created, since it already exists."""
|
||||
message = _("NetworkVlanBinding for %(vlan_id)s and network "
|
||||
"%(network_id)s already exists")
|
||||
"%(network_id)s already exists.")
|
||||
|
||||
|
||||
class VlanIDNotFound(exceptions.NeutronException):
|
||||
"""VLAN ID cannot be found."""
|
||||
message = _("Vlan ID %(vlan_id)s not found")
|
||||
message = _("Vlan ID %(vlan_id)s not found.")
|
||||
|
||||
|
||||
class VlanIDOutsidePool(exceptions.NeutronException):
|
||||
"""VLAN ID cannot be allocated, since it is outside the configured pool."""
|
||||
message = _("Unable to complete operation. VLAN ID exists outside of the "
|
||||
"configured network segment range.")
|
||||
|
||||
|
||||
class VlanIDNotAvailable(exceptions.NeutronException):
|
||||
"""No VLAN ID available."""
|
||||
message = _("No Vlan ID available")
|
||||
message = _("No Vlan ID available.")
|
||||
|
||||
|
||||
class QosNotFound(exceptions.NeutronException):
|
||||
"""QoS level with this ID cannot be found."""
|
||||
message = _("QoS level %(qos_id)s could not be found "
|
||||
"for tenant %(tenant_id)s")
|
||||
"for tenant %(tenant_id)s.")
|
||||
|
||||
|
||||
class QosNameAlreadyExists(exceptions.NeutronException):
|
||||
"""QoS Name already exists."""
|
||||
message = _("QoS level with name %(qos_name)s already exists "
|
||||
"for tenant %(tenant_id)s")
|
||||
"for tenant %(tenant_id)s.")
|
||||
|
||||
|
||||
class CredentialNotFound(exceptions.NeutronException):
|
||||
"""Credential with this ID cannot be found."""
|
||||
message = _("Credential %(credential_id)s could not be found "
|
||||
"for tenant %(tenant_id)s")
|
||||
message = _("Credential %(credential_id)s could not be found.")
|
||||
|
||||
|
||||
class CredentialNameNotFound(exceptions.NeutronException):
|
||||
"""Credential Name could not be found."""
|
||||
message = _("Credential %(credential_name)s could not be found "
|
||||
"for tenant %(tenant_id)s")
|
||||
message = _("Credential %(credential_name)s could not be found.")
|
||||
|
||||
|
||||
class CredentialAlreadyExists(exceptions.NeutronException):
|
||||
"""Credential already exists."""
|
||||
message = _("Credential %(credential_name)s already exists "
|
||||
"for tenant %(tenant_id)s")
|
||||
message = _("Credential %(credential_name)s already exists.")
|
||||
|
||||
|
||||
class ProviderNetworkExists(exceptions.NeutronException):
|
||||
@ -101,7 +104,7 @@ class NexusConfigFailed(exceptions.NeutronException):
|
||||
|
||||
class NexusPortBindingNotFound(exceptions.NeutronException):
|
||||
"""NexusPort Binding is not present."""
|
||||
message = _("Nexus Port Binding (%(filters)s) is not present")
|
||||
message = _("Nexus Port Binding (%(filters)s) is not present.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
filters = ','.join('%s=%s' % i for i in kwargs.items())
|
||||
@ -110,29 +113,102 @@ class NexusPortBindingNotFound(exceptions.NeutronException):
|
||||
|
||||
class NoNexusSviSwitch(exceptions.NeutronException):
|
||||
"""No usable nexus switch found."""
|
||||
message = _("No usable Nexus switch found to create SVI interface")
|
||||
message = _("No usable Nexus switch found to create SVI interface.")
|
||||
|
||||
|
||||
class PortVnicBindingAlreadyExists(exceptions.NeutronException):
|
||||
"""PortVnic Binding already exists."""
|
||||
message = _("PortVnic Binding %(port_id)s already exists")
|
||||
message = _("PortVnic Binding %(port_id)s already exists.")
|
||||
|
||||
|
||||
class PortVnicNotFound(exceptions.NeutronException):
|
||||
"""PortVnic Binding is not present."""
|
||||
message = _("PortVnic Binding %(port_id)s is not present")
|
||||
message = _("PortVnic Binding %(port_id)s is not present.")
|
||||
|
||||
|
||||
class SubnetNotSpecified(exceptions.NeutronException):
|
||||
"""Subnet id not specified."""
|
||||
message = _("No subnet_id specified for router gateway")
|
||||
message = _("No subnet_id specified for router gateway.")
|
||||
|
||||
|
||||
class SubnetInterfacePresent(exceptions.NeutronException):
|
||||
"""Subnet SVI interface already exists."""
|
||||
message = _("Subnet %(subnet_id)s has an interface on %(router_id)s")
|
||||
message = _("Subnet %(subnet_id)s has an interface on %(router_id)s.")
|
||||
|
||||
|
||||
class PortIdForNexusSvi(exceptions.NeutronException):
|
||||
"""Port Id specified for Nexus SVI."""
|
||||
message = _('Nexus hardware router gateway only uses Subnet Ids')
|
||||
message = _('Nexus hardware router gateway only uses Subnet Ids.')
|
||||
|
||||
|
||||
class InvalidDetach(exceptions.NeutronException):
|
||||
message = _("Unable to unplug the attachment %(att_id)s from port "
|
||||
"%(port_id)s for network %(net_id)s. The attachment "
|
||||
"%(att_id)s does not exist.")
|
||||
|
||||
|
||||
class PolicyProfileAlreadyExists(exceptions.NeutronException):
|
||||
"""Policy Profile cannot be created since it already exists."""
|
||||
message = _("Policy Profile %(profile_id)s "
|
||||
"already exists.")
|
||||
|
||||
|
||||
class PolicyProfileIdNotFound(exceptions.NotFound):
|
||||
"""Policy Profile with the given UUID cannot be found."""
|
||||
message = _("Policy Profile %(profile_id)s could not be found.")
|
||||
|
||||
|
||||
class NetworkProfileAlreadyExists(exceptions.NeutronException):
|
||||
"""Network Profile cannot be created since it already exists."""
|
||||
message = _("Network Profile %(profile_id)s "
|
||||
"already exists.")
|
||||
|
||||
|
||||
class NetworkProfileIdNotFound(exceptions.NotFound):
|
||||
"""Network Profile with the given UUID cannot be found."""
|
||||
message = _("Network Profile %(profile_id)s could not be found.")
|
||||
|
||||
|
||||
class NoMoreNetworkSegments(exceptions.NoNetworkAvailable):
|
||||
"""Network segments exhausted for the given network profile."""
|
||||
message = _("No more segments available in network segment pool "
|
||||
"%(network_profile_name)s.")
|
||||
|
||||
|
||||
class VMNetworkNotFound(exceptions.NotFound):
|
||||
"""VM Network with the given name cannot be found."""
|
||||
message = _("VM Network %(name)s could not be found.")
|
||||
|
||||
|
||||
class VxlanIdInUse(exceptions.InUse):
|
||||
"""VXLAN ID is in use."""
|
||||
message = _("Unable to create the network. "
|
||||
"The VXLAN ID %(vxlan_id)s is in use.")
|
||||
|
||||
|
||||
class VSMConnectionFailed(exceptions.ServiceUnavailable):
|
||||
"""Connection to VSM failed."""
|
||||
message = _("Connection to VSM failed: %(reason)s.")
|
||||
|
||||
|
||||
class VSMError(exceptions.NeutronException):
|
||||
"""Error has occured on the VSM."""
|
||||
message = _("Internal VSM Error: %(reason)s.")
|
||||
|
||||
|
||||
class NetworkBindingNotFound(exceptions.NotFound):
|
||||
"""Network Binding for network cannot be found."""
|
||||
message = _("Network Binding for network %(network_id)s could "
|
||||
"not be found.")
|
||||
|
||||
|
||||
class PortBindingNotFound(exceptions.NotFound):
|
||||
"""Port Binding for port cannot be found."""
|
||||
message = _("Port Binding for port %(port_id)s could "
|
||||
"not be found.")
|
||||
|
||||
|
||||
class ProfileTenantBindingNotFound(exceptions.NotFound):
|
||||
"""Profile to Tenant binding for given profile ID cannot be found."""
|
||||
message = _("Profile-Tenant binding for profile %(profile_id)s could "
|
||||
"not be found.")
|
||||
|
@ -30,7 +30,6 @@ cisco_plugins_opts = [
|
||||
help=_("Nexus Switch to use")),
|
||||
]
|
||||
|
||||
|
||||
cisco_opts = [
|
||||
cfg.StrOpt('vlan_name_prefix', default='q-',
|
||||
help=_("VLAN Name prefix")),
|
||||
@ -54,38 +53,64 @@ cisco_opts = [
|
||||
help=_("Nexus Driver Name")),
|
||||
]
|
||||
|
||||
cisco_n1k_opts = [
|
||||
cfg.StrOpt('integration_bridge', default='br-int',
|
||||
help=_("N1K Integration Bridge")),
|
||||
cfg.BoolOpt('enable_tunneling', default=True,
|
||||
help=_("N1K Enable Tunneling")),
|
||||
cfg.StrOpt('tunnel_bridge', default='br-tun',
|
||||
help=_("N1K Tunnel Bridge")),
|
||||
cfg.StrOpt('local_ip', default='10.0.0.3',
|
||||
help=_("N1K Local IP")),
|
||||
cfg.StrOpt('tenant_network_type', default='local',
|
||||
help=_("N1K Tenant Network Type")),
|
||||
cfg.StrOpt('bridge_mappings', default='',
|
||||
help=_("N1K Bridge Mappings")),
|
||||
cfg.StrOpt('vxlan_id_ranges', default='5000:10000',
|
||||
help=_("N1K VXLAN ID Ranges")),
|
||||
cfg.StrOpt('network_vlan_ranges', default='vlan:1:4095',
|
||||
help=_("N1K Network VLAN Ranges")),
|
||||
cfg.StrOpt('default_policy_profile', default='service_profile',
|
||||
help=_("N1K default policy profile")),
|
||||
cfg.StrOpt('poll_duration', default='10',
|
||||
help=_("N1K Policy profile polling duration in seconds")),
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
#
|
||||
# When populated the nexus_dictionary format is:
|
||||
# {('<nexus ipaddr>', '<key>'): '<value>', ...}
|
||||
# device_dictionary - Contains all external device configuration.
|
||||
#
|
||||
# When populated the device dictionary format is:
|
||||
# {('<device ID>', '<device ipaddr>', '<keyword>'): '<value>', ...}
|
||||
#
|
||||
# Example:
|
||||
# {('1.1.1.1', 'username'): 'admin',
|
||||
# ('1.1.1.1', 'password'): 'mySecretPassword',
|
||||
# ('1.1.1.1', 'ssh_port'): 22,
|
||||
# ('1.1.1.1', 'compute1'): '1/1', ...}
|
||||
# {('NEXUS_SWITCH', '1.1.1.1', 'username'): 'admin',
|
||||
# ('NEXUS_SWITCH', '1.1.1.1', 'password'): 'mySecretPassword',
|
||||
# ('NEXUS_SWITCH', '1.1.1.1', 'compute1'): '1/1', ...}
|
||||
#
|
||||
nexus_dictionary = {}
|
||||
device_dictionary = {}
|
||||
|
||||
|
||||
class CiscoConfigOptions():
|
||||
"""Cisco Configuration Options Class."""
|
||||
|
||||
def __init__(self):
|
||||
self._create_nexus_dictionary()
|
||||
self._create_device_dictionary()
|
||||
|
||||
def _create_nexus_dictionary(self):
|
||||
"""Create the Nexus dictionary.
|
||||
|
||||
Reads data from cisco_plugins.ini NEXUS_SWITCH section(s).
|
||||
def _create_device_dictionary(self):
|
||||
"""
|
||||
Create the device dictionary from the cisco_plugins.ini
|
||||
device supported sections. Ex. NEXUS_SWITCH, N1KV.
|
||||
"""
|
||||
|
||||
multi_parser = cfg.MultiConfigParser()
|
||||
@ -96,11 +121,11 @@ class CiscoConfigOptions():
|
||||
|
||||
for parsed_file in multi_parser.parsed:
|
||||
for parsed_item in parsed_file.keys():
|
||||
nexus_name, sep, nexus_ip = parsed_item.partition(':')
|
||||
if nexus_name.lower() == "nexus_switch":
|
||||
for nexus_key, value in parsed_file[parsed_item].items():
|
||||
nexus_dictionary[nexus_ip, nexus_key] = value[0]
|
||||
dev_id, sep, dev_ip = parsed_item.partition(':')
|
||||
if dev_id.lower() in ['nexus_switch', 'n1kv']:
|
||||
for dev_key, value in parsed_file[parsed_item].items():
|
||||
device_dictionary[dev_id, dev_ip, dev_key] = value[0]
|
||||
|
||||
|
||||
def get_nexus_dictionary():
|
||||
return nexus_dictionary
|
||||
def get_device_dictionary():
|
||||
return device_dictionary
|
||||
|
1250
neutron/plugins/cisco/db/n1kv_db_v2.py
Normal file
1250
neutron/plugins/cisco/db/n1kv_db_v2.py
Normal file
File diff suppressed because it is too large
Load Diff
144
neutron/plugins/cisco/db/n1kv_models_v2.py
Normal file
144
neutron/plugins/cisco/db/n1kv_models_v2.py
Normal file
@ -0,0 +1,144 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems Inc.
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import model_base
|
||||
from neutron.db import models_v2
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.cisco.common import cisco_constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class N1kvVlanAllocation(model_base.BASEV2):
|
||||
|
||||
"""Represents allocation state of vlan_id on physical network."""
|
||||
__tablename__ = 'cisco_n1kv_vlan_allocations'
|
||||
|
||||
physical_network = sa.Column(sa.String(64),
|
||||
nullable=False,
|
||||
primary_key=True)
|
||||
vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True,
|
||||
autoincrement=False)
|
||||
allocated = sa.Column(sa.Boolean, nullable=False, default=False)
|
||||
|
||||
|
||||
class N1kvVxlanAllocation(model_base.BASEV2):
|
||||
|
||||
"""Represents allocation state of vxlan_id."""
|
||||
__tablename__ = 'cisco_n1kv_vxlan_allocations'
|
||||
|
||||
vxlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True,
|
||||
autoincrement=False)
|
||||
allocated = sa.Column(sa.Boolean, nullable=False, default=False)
|
||||
|
||||
|
||||
class N1kvPortBinding(model_base.BASEV2):
|
||||
|
||||
"""Represents binding of ports to policy profile."""
|
||||
__tablename__ = 'cisco_n1kv_port_bindings'
|
||||
|
||||
port_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('ports.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
profile_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('cisco_policy_profiles.id'))
|
||||
|
||||
|
||||
class N1kvNetworkBinding(model_base.BASEV2):
|
||||
|
||||
"""Represents binding of virtual network to physical realization."""
|
||||
__tablename__ = 'cisco_n1kv_network_bindings'
|
||||
|
||||
network_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('networks.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
network_type = sa.Column(sa.String(32), nullable=False)
|
||||
physical_network = sa.Column(sa.String(64))
|
||||
segmentation_id = sa.Column(sa.Integer)
|
||||
multicast_ip = sa.Column(sa.String(32))
|
||||
profile_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('cisco_network_profiles.id'))
|
||||
|
||||
|
||||
class N1kVmNetwork(model_base.BASEV2):
|
||||
|
||||
"""Represents VM Network information."""
|
||||
__tablename__ = 'cisco_n1kv_vmnetworks'
|
||||
|
||||
name = sa.Column(sa.String(80), primary_key=True)
|
||||
profile_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('cisco_policy_profiles.id'))
|
||||
network_id = sa.Column(sa.String(36))
|
||||
port_count = sa.Column(sa.Integer)
|
||||
|
||||
|
||||
class NetworkProfile(model_base.BASEV2, models_v2.HasId):
|
||||
|
||||
"""
|
||||
Nexus1000V Network Profiles
|
||||
|
||||
segment_type - VLAN, VXLAN
|
||||
segment_range - '<integer>-<integer>'
|
||||
multicast_ip_index - <integer>
|
||||
multicast_ip_range - '<ip>-<ip>'
|
||||
physical_network - Name for the physical network
|
||||
"""
|
||||
__tablename__ = 'cisco_network_profiles'
|
||||
|
||||
name = sa.Column(sa.String(255))
|
||||
segment_type = sa.Column(sa.Enum(cisco_constants.NETWORK_TYPE_VLAN,
|
||||
cisco_constants.NETWORK_TYPE_VXLAN,
|
||||
name='segment_type'),
|
||||
nullable=False)
|
||||
segment_range = sa.Column(sa.String(255))
|
||||
multicast_ip_index = sa.Column(sa.Integer, default=0)
|
||||
multicast_ip_range = sa.Column(sa.String(255))
|
||||
physical_network = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class PolicyProfile(model_base.BASEV2):
|
||||
|
||||
"""
|
||||
Nexus1000V Network Profiles
|
||||
|
||||
Both 'id' and 'name' are coming from Nexus1000V switch
|
||||
"""
|
||||
__tablename__ = 'cisco_policy_profiles'
|
||||
|
||||
id = sa.Column(sa.String(36), primary_key=True)
|
||||
name = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class ProfileBinding(model_base.BASEV2):
|
||||
|
||||
"""
|
||||
Represents a binding of Network Profile
|
||||
or Policy Profile to tenant_id
|
||||
"""
|
||||
__tablename__ = 'cisco_n1kv_profile_bindings'
|
||||
|
||||
profile_type = sa.Column(sa.Enum(cisco_constants.NETWORK,
|
||||
cisco_constants.POLICY,
|
||||
name='profile_type'))
|
||||
tenant_id = sa.Column(sa.String(36),
|
||||
primary_key=True,
|
||||
default=cisco_constants.TENANT_ID_NOT_SET)
|
||||
profile_id = sa.Column(sa.String(36), primary_key=True)
|
@ -46,10 +46,9 @@ def get_qos(tenant_id, qos_id):
|
||||
LOG.debug(_("get_qos() called"))
|
||||
session = db.get_session()
|
||||
try:
|
||||
qos = (session.query(network_models_v2.QoS).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(qos_id=qos_id).one())
|
||||
return qos
|
||||
return (session.query(network_models_v2.QoS).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(qos_id=qos_id).one())
|
||||
except exc.NoResultFound:
|
||||
raise c_exc.QosNotFound(qos_id=qos_id,
|
||||
tenant_id=tenant_id)
|
||||
@ -106,66 +105,56 @@ def update_qos(tenant_id, qos_id, new_qos_name=None):
|
||||
tenant_id=tenant_id)
|
||||
|
||||
|
||||
def get_all_credentials(tenant_id):
|
||||
def get_all_credentials():
|
||||
"""Lists all the creds for a tenant."""
|
||||
session = db.get_session()
|
||||
return (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).all())
|
||||
return (session.query(network_models_v2.Credential).all())
|
||||
|
||||
|
||||
def get_credential(tenant_id, credential_id):
|
||||
"""Lists the creds for given a cred_id and tenant_id."""
|
||||
def get_credential(credential_id):
|
||||
"""Lists the creds for given a cred_id."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
cred = (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
return (session.query(network_models_v2.Credential).
|
||||
filter_by(credential_id=credential_id).one())
|
||||
return cred
|
||||
except exc.NoResultFound:
|
||||
raise c_exc.CredentialNotFound(credential_id=credential_id,
|
||||
tenant_id=tenant_id)
|
||||
raise c_exc.CredentialNotFound(credential_id=credential_id)
|
||||
|
||||
|
||||
def get_credential_name(tenant_id, credential_name):
|
||||
"""Lists the creds for given a cred_name and tenant_id."""
|
||||
def get_credential_name(credential_name):
|
||||
"""Lists the creds for given a cred_name."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
return (session.query(network_models_v2.Credential).
|
||||
filter_by(credential_name=credential_name).one())
|
||||
except exc.NoResultFound:
|
||||
raise c_exc.CredentialNameNotFound(credential_name=credential_name)
|
||||
|
||||
|
||||
def add_credential(credential_name, user_name, password, type):
|
||||
"""Create a credential."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
cred = (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(credential_name=credential_name).one())
|
||||
return cred
|
||||
except exc.NoResultFound:
|
||||
raise c_exc.CredentialNameNotFound(credential_name=credential_name,
|
||||
tenant_id=tenant_id)
|
||||
|
||||
|
||||
def add_credential(tenant_id, credential_name, user_name, password):
|
||||
"""Adds a qos to tenant association."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
cred = (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(credential_name=credential_name).one())
|
||||
raise c_exc.CredentialAlreadyExists(credential_name=credential_name,
|
||||
tenant_id=tenant_id)
|
||||
raise c_exc.CredentialAlreadyExists(credential_name=credential_name)
|
||||
except exc.NoResultFound:
|
||||
cred = network_models_v2.Credential(
|
||||
credential_id=uuidutils.generate_uuid(),
|
||||
tenant_id=tenant_id,
|
||||
credential_name=credential_name,
|
||||
user_name=user_name,
|
||||
password=password)
|
||||
password=password,
|
||||
type=type)
|
||||
session.add(cred)
|
||||
session.flush()
|
||||
return cred
|
||||
|
||||
|
||||
def remove_credential(tenant_id, credential_id):
|
||||
"""Removes a credential from a tenant."""
|
||||
def remove_credential(credential_id):
|
||||
"""Removes a credential."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
cred = (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(credential_id=credential_id).one())
|
||||
session.delete(cred)
|
||||
session.flush()
|
||||
@ -174,13 +163,12 @@ def remove_credential(tenant_id, credential_id):
|
||||
pass
|
||||
|
||||
|
||||
def update_credential(tenant_id, credential_id,
|
||||
def update_credential(credential_id,
|
||||
new_user_name=None, new_password=None):
|
||||
"""Updates a credential for a tenant."""
|
||||
session = db.get_session()
|
||||
try:
|
||||
cred = (session.query(network_models_v2.Credential).
|
||||
filter_by(tenant_id=tenant_id).
|
||||
filter_by(credential_id=credential_id).one())
|
||||
if new_user_name:
|
||||
cred["user_name"] = new_user_name
|
||||
@ -190,8 +178,13 @@ def update_credential(tenant_id, credential_id,
|
||||
session.flush()
|
||||
return cred
|
||||
except exc.NoResultFound:
|
||||
raise c_exc.CredentialNotFound(credential_id=credential_id,
|
||||
tenant_id=tenant_id)
|
||||
raise c_exc.CredentialNotFound(credential_id=credential_id)
|
||||
|
||||
|
||||
def get_all_n1kv_credentials():
|
||||
session = db.get_session()
|
||||
return (session.query(network_models_v2.Credential).
|
||||
filter_by(type='n1kv'))
|
||||
|
||||
|
||||
def add_provider_network(network_id, network_type, segmentation_id):
|
||||
@ -248,3 +241,50 @@ def get_ovs_vlans():
|
||||
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."""
|
||||
|
||||
def _make_credential_dict(self, credential, fields=None):
|
||||
res = {'credential_id': credential['credential_id'],
|
||||
'credential_name': credential['credential_name'],
|
||||
'user_name': credential['user_name'],
|
||||
'password': credential['password'],
|
||||
'type': credential['type']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def create_credential(self, context, credential):
|
||||
"""Create a credential."""
|
||||
c = credential['credential']
|
||||
cred = add_credential(c['credential_name'],
|
||||
c['user_name'],
|
||||
c['password'],
|
||||
c['type'])
|
||||
return self._make_credential_dict(cred)
|
||||
|
||||
def get_credentials(self, context, filters=None, fields=None):
|
||||
"""Retrieve a list of credentials."""
|
||||
return self._get_collection(context,
|
||||
network_models_v2.Credential,
|
||||
self._make_credential_dict,
|
||||
filters=filters,
|
||||
fields=fields)
|
||||
|
||||
def get_credential(self, context, id, fields=None):
|
||||
"""Retireve the requested credential based on its id."""
|
||||
credential = get_credential(id)
|
||||
return self._make_credential_dict(credential, fields)
|
||||
|
||||
def update_credential(self, context, id, credential):
|
||||
"""Update a credential based on its id."""
|
||||
c = credential['credential']
|
||||
cred = update_credential(id,
|
||||
c['user_name'],
|
||||
c['password'])
|
||||
return self._make_credential_dict(cred)
|
||||
|
||||
def delete_credential(self, context, id):
|
||||
"""Delete a credential based on its id."""
|
||||
return remove_credential(id)
|
||||
|
@ -38,10 +38,10 @@ class Credential(model_base.BASEV2):
|
||||
__tablename__ = 'cisco_credentials'
|
||||
|
||||
credential_id = sa.Column(sa.String(255))
|
||||
tenant_id = sa.Column(sa.String(255), primary_key=True)
|
||||
credential_name = sa.Column(sa.String(255), primary_key=True)
|
||||
user_name = sa.Column(sa.String(255))
|
||||
password = sa.Column(sa.String(255))
|
||||
type = sa.Column(sa.String(255))
|
||||
|
||||
|
||||
class ProviderNetwork(model_base.BASEV2):
|
||||
|
@ -1,7 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Cisco Systems, Inc.
|
||||
# All rights reserved.
|
||||
# Copyright 2013 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
|
||||
@ -16,145 +15,68 @@
|
||||
# under the License.
|
||||
#
|
||||
# @author: Ying Liu, Cisco Systems, Inc.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc
|
||||
|
||||
from webob import exc
|
||||
|
||||
from neutron.api import api_common as common
|
||||
from neutron.api import extensions
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as exception
|
||||
from neutron.plugins.cisco.common import cisco_faults as faults
|
||||
from neutron.plugins.cisco.extensions import (_credential_view as
|
||||
credential_view)
|
||||
from neutron import wsgi
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.api.v2 import base
|
||||
from neutron import manager
|
||||
|
||||
|
||||
# Attribute Map
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'credentials': {
|
||||
'credential_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'is_visible': True},
|
||||
'credential_name': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'type': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'user_name': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'password': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Credential(extensions.ExtensionDescriptor):
|
||||
"""Extension class Credential."""
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
"""Returns Ext Resource Name."""
|
||||
"""Returns Extended Resource Name."""
|
||||
return "Cisco Credential"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
"""Returns Ext Resource Alias."""
|
||||
return "Cisco Credential"
|
||||
"""Returns Extended Resource Alias."""
|
||||
return "credential"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
"""Returns Ext Resource Description."""
|
||||
"""Returns Extended Resource Description."""
|
||||
return "Credential include username and password"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
"""Returns Ext Resource Namespace."""
|
||||
return "http://docs.ciscocloud.com/api/ext/credential/v1.0"
|
||||
"""Returns Extended Resource Namespace."""
|
||||
return "http://docs.ciscocloud.com/api/ext/credential/v2.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
"""Returns Ext Resource Update Time."""
|
||||
"""Returns Extended Resource Update Time."""
|
||||
return "2011-07-25T13:25:27-06:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
"""Returns Ext Resources."""
|
||||
parent_resource = dict(member_name="tenant",
|
||||
collection_name="extensions/csco/tenants")
|
||||
controller = CredentialController(NeutronManager.get_plugin())
|
||||
return [extensions.ResourceExtension('credentials', controller,
|
||||
parent=parent_resource)]
|
||||
|
||||
|
||||
class CredentialController(common.NeutronController, wsgi.Controller):
|
||||
"""Credential API controller based on NeutronController."""
|
||||
|
||||
_credential_ops_param_list = [
|
||||
{'param-name': 'credential_name', 'required': True},
|
||||
{'param-name': 'user_name', 'required': True},
|
||||
{'param-name': 'password', 'required': True},
|
||||
]
|
||||
|
||||
_serialization_metadata = {
|
||||
"application/xml": {
|
||||
"attributes": {
|
||||
"credential": ["id", "name"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, plugin):
|
||||
self._resource_name = 'credential'
|
||||
self._plugin = plugin
|
||||
|
||||
def index(self, request, tenant_id):
|
||||
"""Returns a list of credential ids."""
|
||||
return self._items(request, tenant_id, is_detail=False)
|
||||
|
||||
def _items(self, request, tenant_id, is_detail):
|
||||
"""Returns a list of credentials."""
|
||||
credentials = self._plugin.get_all_credentials(tenant_id)
|
||||
builder = credential_view.get_view_builder(request)
|
||||
result = [builder.build(credential, is_detail)['credential']
|
||||
for credential in credentials]
|
||||
return dict(credentials=result)
|
||||
|
||||
# pylint: disable-msg=E1101,W0613
|
||||
def show(self, request, tenant_id, id):
|
||||
"""Returns credential details for the given credential id."""
|
||||
try:
|
||||
credential = self._plugin.get_credential_details(tenant_id, id)
|
||||
builder = credential_view.get_view_builder(request)
|
||||
#build response with details
|
||||
result = builder.build(credential, True)
|
||||
return dict(credentials=result)
|
||||
except exception.CredentialNotFound as exp:
|
||||
return faults.Fault(faults.CredentialNotFound(exp))
|
||||
|
||||
def create(self, request, tenant_id):
|
||||
"""Creates a new credential for a given tenant."""
|
||||
try:
|
||||
body = self._deserialize(request.body, request.get_content_type())
|
||||
req_body = self._prepare_request_body(
|
||||
body, self._credential_ops_param_list)
|
||||
req_params = req_body[self._resource_name]
|
||||
|
||||
except exc.HTTPError as exp:
|
||||
return faults.Fault(exp)
|
||||
credential = self._plugin.create_credential(
|
||||
tenant_id,
|
||||
req_params['credential_name'],
|
||||
req_params['user_name'],
|
||||
req_params['password'])
|
||||
builder = credential_view.get_view_builder(request)
|
||||
result = builder.build(credential)
|
||||
return dict(credentials=result)
|
||||
|
||||
def update(self, request, tenant_id, id):
|
||||
"""Updates the name for the credential with the given id."""
|
||||
try:
|
||||
body = self._deserialize(request.body, request.get_content_type())
|
||||
req_body = self._prepare_request_body(
|
||||
body, self._credential_ops_param_list)
|
||||
req_params = req_body[self._resource_name]
|
||||
except exc.HTTPError as exp:
|
||||
return faults.Fault(exp)
|
||||
try:
|
||||
credential = self._plugin.rename_credential(
|
||||
tenant_id, id, req_params['credential_name'])
|
||||
|
||||
builder = credential_view.get_view_builder(request)
|
||||
result = builder.build(credential, True)
|
||||
return dict(credentials=result)
|
||||
except exception.CredentialNotFound as exp:
|
||||
return faults.Fault(faults.CredentialNotFound(exp))
|
||||
|
||||
def delete(self, request, tenant_id, id):
|
||||
"""Destroys the credential with the given id."""
|
||||
try:
|
||||
self._plugin.delete_credential(tenant_id, id)
|
||||
return exc.HTTPOk()
|
||||
except exception.CredentialNotFound as exp:
|
||||
return faults.Fault(faults.CredentialNotFound(exp))
|
||||
"""Returns Extended Resources."""
|
||||
resource_name = "credential"
|
||||
collection_name = resource_name + "s"
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
params = RESOURCE_ATTRIBUTE_MAP.get(collection_name, dict())
|
||||
controller = base.create_resource(collection_name,
|
||||
resource_name,
|
||||
plugin, params)
|
||||
return [extensions.ResourceExtension(collection_name,
|
||||
controller)]
|
||||
|
93
neutron/plugins/cisco/extensions/n1kv_profile.py
Normal file
93
neutron/plugins/cisco/extensions/n1kv_profile.py
Normal file
@ -0,0 +1,93 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
# @author: Rudrajit Tapadar, Cisco Systems, Inc.
|
||||
# @author: Aruna Kushwaha, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
|
||||
|
||||
PROFILE_ID = 'n1kv:profile_id'
|
||||
MULTICAST_IP = 'n1kv:multicast_ip'
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'networks': {
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True},
|
||||
MULTICAST_IP: {'allow_post': True, 'allow_put': True,
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True},
|
||||
},
|
||||
'ports': {
|
||||
PROFILE_ID: {'allow_post': True, 'allow_put': True,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class N1kv_profile(object):
|
||||
|
||||
"""Extension class supporting N1kv profiles.
|
||||
|
||||
This class is used by neutron's extension framework to make
|
||||
metadata about the n1kv profile extension available to
|
||||
clients. No new resources are defined by this extension. Instead,
|
||||
the existing network resource's request and response messages are
|
||||
extended with attributes in the n1kv profile namespace.
|
||||
|
||||
To create a network based on n1kv profile using the CLI with admin rights:
|
||||
|
||||
(shell) net-create --tenant_id <tenant-id> <net-name> \
|
||||
--n1kv:profile_id <id>
|
||||
(shell) port-create --tenant_id <tenant-id> <net-name> \
|
||||
--n1kv:profile_id <id>
|
||||
|
||||
|
||||
With admin rights, network dictionaries returned from CLI commands
|
||||
will also include n1kv profile attributes.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "n1kv_profile"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "n1kv_profile"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Expose network profile"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://docs.openstack.org/ext/n1kv_profile/api/v2.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2012-11-15T10:00:00-00:00"
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
98
neutron/plugins/cisco/extensions/network_profile.py
Normal file
98
neutron/plugins/cisco/extensions/network_profile.py
Normal file
@ -0,0 +1,98 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.api.v2 import base
|
||||
from neutron import manager
|
||||
|
||||
|
||||
# Attribute Map
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'network_profiles': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'segment_type': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'segment_range': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'multicast_ip_range': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': '0.0.0.0'},
|
||||
'multicast_ip_index': {'allow_post': False, 'allow_put': False,
|
||||
'is_visible': False, 'default': '0'},
|
||||
'physical_network': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': ''},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': False, 'default': ''},
|
||||
'add_tenant': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None},
|
||||
'remove_tenant': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None},
|
||||
},
|
||||
'network_profile_bindings': {
|
||||
'profile_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'is_visible': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Network_profile(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Cisco N1kv Network Profiles"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return 'network_profile'
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return ("Profile includes the type of profile for N1kv")
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://docs.openstack.org/ext/n1kv/network-profile/api/v2.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2012-07-20T10:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
"""Returns Ext Resources."""
|
||||
exts = []
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
for resource_name in ['network_profile', 'network_profile_binding']:
|
||||
collection_name = resource_name + "s"
|
||||
controller = base.create_resource(
|
||||
collection_name,
|
||||
resource_name,
|
||||
plugin,
|
||||
RESOURCE_ATTRIBUTE_MAP.get(collection_name))
|
||||
ex = extensions.ResourceExtension(collection_name,
|
||||
controller)
|
||||
exts.append(ex)
|
||||
return exts
|
85
neutron/plugins/cisco/extensions/policy_profile.py
Normal file
85
neutron/plugins/cisco/extensions/policy_profile.py
Normal file
@ -0,0 +1,85 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.api.v2 import base
|
||||
from neutron import manager
|
||||
|
||||
# Attribute Map
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
'policy_profiles': {
|
||||
'id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'is_visible': True},
|
||||
'name': {'allow_post': False, 'allow_put': False,
|
||||
'is_visible': True, 'default': ''},
|
||||
'add_tenant': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None},
|
||||
'remove_tenant': {'allow_post': True, 'allow_put': True,
|
||||
'is_visible': True, 'default': None},
|
||||
},
|
||||
'policy_profile_bindings': {
|
||||
'profile_id': {'allow_post': False, 'allow_put': False,
|
||||
'validate': {'type:regex': attributes.UUID_PATTERN},
|
||||
'is_visible': True},
|
||||
'tenant_id': {'allow_post': True, 'allow_put': False,
|
||||
'is_visible': True},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Policy_profile(extensions.ExtensionDescriptor):
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Cisco Nexus1000V Policy Profiles"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return 'policy_profile'
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Profile includes the type of profile for N1kv"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return "http://docs.openstack.org/ext/n1kv/policy-profile/api/v2.0"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2012-07-20T10:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
"""Returns Ext Resources."""
|
||||
exts = []
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
for resource_name in ['policy_profile', 'policy_profile_binding']:
|
||||
collection_name = resource_name + "s"
|
||||
controller = base.create_resource(
|
||||
collection_name,
|
||||
resource_name,
|
||||
plugin,
|
||||
RESOURCE_ATTRIBUTE_MAP.get(collection_name))
|
||||
ex = extensions.ResourceExtension(collection_name,
|
||||
controller)
|
||||
exts.append(ex)
|
||||
return exts
|
@ -59,7 +59,8 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
'get_port', 'get_ports',
|
||||
'create_subnet', 'create_subnet_bulk',
|
||||
'delete_subnet', 'update_subnet',
|
||||
'get_subnet', 'get_subnets', ]
|
||||
'get_subnet', 'get_subnets',
|
||||
'create_or_update_agent', 'report_state']
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the segmentation manager.
|
||||
@ -71,9 +72,10 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
|
||||
for key in conf.CISCO_PLUGINS.keys():
|
||||
plugin_obj = conf.CISCO_PLUGINS[key]
|
||||
self._plugins[key] = importutils.import_object(plugin_obj)
|
||||
LOG.debug(_("Loaded device plugin %s\n"),
|
||||
conf.CISCO_PLUGINS[key])
|
||||
if plugin_obj is not None:
|
||||
self._plugins[key] = importutils.import_object(plugin_obj)
|
||||
LOG.debug(_("Loaded device plugin %s\n"),
|
||||
conf.CISCO_PLUGINS[key])
|
||||
|
||||
if ((const.VSWITCH_PLUGIN in self._plugins) and
|
||||
hasattr(self._plugins[const.VSWITCH_PLUGIN],
|
||||
@ -81,7 +83,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
self.supported_extension_aliases.extend(
|
||||
self._plugins[const.VSWITCH_PLUGIN].
|
||||
supported_extension_aliases)
|
||||
|
||||
# At this point, all the database models should have been loaded. It's
|
||||
# possible that configure_db() may have been called by one of the
|
||||
# plugins loaded in above. Otherwise, this call is to make sure that
|
||||
@ -455,17 +456,15 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id)
|
||||
|
||||
n_args = [vlan_name, vlan_id, subnet['id'], gateway_ip, router_id]
|
||||
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
return nexus_output
|
||||
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
else:
|
||||
LOG.debug(_("No Nexus plugin, sending to vswitch"))
|
||||
n_args = [context, router_id, interface_info]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
return ovs_output
|
||||
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.
|
||||
@ -482,17 +481,15 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
vlan_id = self._get_segmentation_id(network_id)
|
||||
n_args = [vlan_id, router_id]
|
||||
|
||||
nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
return nexus_output
|
||||
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
else:
|
||||
LOG.debug(_("No Nexus plugin, sending to vswitch"))
|
||||
n_args = [context, router_id, interface_info]
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
return ovs_output
|
||||
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
n_args)
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
"""For this model this method will be delegated to vswitch plugin."""
|
||||
|
18
neutron/plugins/cisco/n1kv/__init__.py
Normal file
18
neutron/plugins/cisco/n1kv/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
#
|
500
neutron/plugins/cisco/n1kv/n1kv_client.py
Normal file
500
neutron/plugins/cisco/n1kv/n1kv_client.py
Normal file
@ -0,0 +1,500 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
|
||||
import base64
|
||||
import httplib2
|
||||
import netaddr
|
||||
|
||||
from neutron.common import exceptions as q_exc
|
||||
from neutron.extensions import providernet
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.cisco.common import cisco_constants as c_const
|
||||
from neutron.plugins.cisco.common import cisco_credentials_v2 as c_cred
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as c_exc
|
||||
from neutron.plugins.cisco.db import network_db_v2
|
||||
from neutron.plugins.cisco.extensions import n1kv_profile
|
||||
from neutron import wsgi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
"""
|
||||
Client for the Cisco Nexus1000V Neutron Plugin.
|
||||
|
||||
This client implements functions to communicate with
|
||||
Cisco Nexus1000V VSM.
|
||||
|
||||
For every Neutron objects, Cisco Nexus1000V Neutron Plugin
|
||||
creates a corresponding object in the controller (Cisco
|
||||
Nexus1000V VSM).
|
||||
|
||||
CONCEPTS:
|
||||
|
||||
Following are few concepts used in Nexus1000V VSM:
|
||||
|
||||
port-profiles:
|
||||
Policy profiles correspond to port profiles on Nexus1000V VSM.
|
||||
Port profiles are the primary mechanism by which network policy is
|
||||
defined and applied to switch interfaces in a Nexus 1000V system.
|
||||
|
||||
network-segment:
|
||||
Each network-segment represents a broadcast domain.
|
||||
|
||||
network-segment-pool:
|
||||
A network-segment-pool contains one or more network-segments.
|
||||
|
||||
logical-network:
|
||||
A logical-network contains one or more network-segment-pools.
|
||||
|
||||
bridge-domain:
|
||||
A bridge-domain is created when the network-segment is of type VXLAN.
|
||||
Each VXLAN <--> VLAN combination can be thought of as a bridge domain.
|
||||
|
||||
ip-pool:
|
||||
Each ip-pool represents a subnet on the Nexus1000V VSM.
|
||||
|
||||
vm-network:
|
||||
vm-network refers to a network-segment and policy-profile.
|
||||
It maintains a list of ports that uses the network-segment and
|
||||
policy-profile this vm-network refers to.
|
||||
|
||||
events:
|
||||
Events correspond to commands that are logged on Nexus1000V VSM.
|
||||
Events are used to poll for a certain resource on Nexus1000V VSM.
|
||||
Event type of port_profile: Return all updates/create/deletes
|
||||
of port profiles from the VSM.
|
||||
Event type of port_profile_update: Return only updates regarding
|
||||
policy-profiles.
|
||||
Event type of port_profile_delete: Return only deleted policy profiles.
|
||||
|
||||
|
||||
WORK FLOW:
|
||||
|
||||
For every network profile a corresponding logical-network and
|
||||
a network-segment-pool, under this logical-network, will be created.
|
||||
|
||||
For every network created from a given network profile, a
|
||||
network-segment will be added to the network-segment-pool corresponding
|
||||
to that network profile.
|
||||
|
||||
A port is created on a network and associated with a policy-profile.
|
||||
Hence for every unique combination of a network and a policy-profile, a
|
||||
unique vm-network will be created and a reference to the port will be
|
||||
added. If the same combination of network and policy-profile is used by
|
||||
another port, the refernce to that port will be added to the same
|
||||
vm-network.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Metadata for deserializing xml
|
||||
_serialization_metadata = {
|
||||
"application/xml": {
|
||||
"attributes": {
|
||||
"network": ["id", "name"],
|
||||
"port": ["id", "mac_address"],
|
||||
"subnet": ["id", "prefix"]
|
||||
},
|
||||
},
|
||||
"plurals": {
|
||||
"networks": "network",
|
||||
"ports": "port",
|
||||
"set": "instance",
|
||||
"subnets": "subnet"
|
||||
}
|
||||
}
|
||||
|
||||
# Define paths for the URI where the client connects for HTTP requests.
|
||||
port_profiles_path = "/virtual-port-profile"
|
||||
network_segments_path = "/network-segment"
|
||||
network_segment_path = "/network-segment/%s"
|
||||
network_segment_pools_path = "/network-segment-pool"
|
||||
network_segment_pool_path = "/network-segment-pool/%s"
|
||||
ip_pools_path = "/ip-pool-template"
|
||||
ip_pool_path = "/ip-pool-template/%s"
|
||||
ports_path = "/kvm/vm-network/%s/ports"
|
||||
port_path = "/kvm/vm-network/%s/ports/%s"
|
||||
vm_networks_path = "/kvm/vm-network"
|
||||
vm_network_path = "/kvm/vm-network/%s"
|
||||
bridge_domains_path = "/kvm/bridge-domain"
|
||||
bridge_domain_path = "/kvm/bridge-domain/%s"
|
||||
logical_networks_path = "/logical-network"
|
||||
logical_network_path = "/logical-network/%s"
|
||||
events_path = "/kvm/events"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize a new client for the plugin."""
|
||||
self.format = 'json'
|
||||
self.hosts = self._get_vsm_hosts()
|
||||
self.action_prefix = 'http://%s/api/n1k' % self.hosts[0]
|
||||
|
||||
def list_port_profiles(self):
|
||||
"""
|
||||
Fetch all policy profiles from the VSM.
|
||||
|
||||
:returns: XML string
|
||||
"""
|
||||
return self._get(self.port_profiles_path)
|
||||
|
||||
def list_events(self, event_type=None, epoch=None):
|
||||
"""
|
||||
Fetch all events of event_type from the VSM.
|
||||
|
||||
:param event_type: type of event to be listed.
|
||||
:param epoch: timestamp after which the events occurred to be listed.
|
||||
:returns: XML string
|
||||
"""
|
||||
if event_type:
|
||||
self.events_path = self.events_path + '?type=' + event_type
|
||||
return self._get(self.events_path)
|
||||
|
||||
def create_bridge_domain(self, network):
|
||||
"""
|
||||
Create a bridge domain on VSM.
|
||||
|
||||
:param network: network dict
|
||||
"""
|
||||
body = {'name': network['name'] + '_bd',
|
||||
'segmentId': network[providernet.SEGMENTATION_ID],
|
||||
'groupIp': network[n1kv_profile.MULTICAST_IP], }
|
||||
return self._post(self.bridge_domains_path,
|
||||
body=body)
|
||||
|
||||
def delete_bridge_domain(self, name):
|
||||
"""
|
||||
Delete a bridge domain on VSM.
|
||||
|
||||
:param name: name of the bridge domain to be deleted
|
||||
"""
|
||||
return self._delete(self.bridge_domain_path % (name))
|
||||
|
||||
def create_network_segment(self, network, network_profile):
|
||||
"""
|
||||
Create a network segment on the VSM.
|
||||
|
||||
:param network: network dict
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
LOG.debug(_("seg id %s\n"), network_profile['name'])
|
||||
body = {'name': network['name'],
|
||||
'id': network['id'],
|
||||
'networkSegmentPool': network_profile['name'], }
|
||||
if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VLAN:
|
||||
body['vlan'] = network[providernet.SEGMENTATION_ID]
|
||||
elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VXLAN:
|
||||
body['bridgeDomain'] = network['name'] + '_bd'
|
||||
return self._post(self.network_segments_path,
|
||||
body=body)
|
||||
|
||||
def update_network_segment(self, network_segment_name, body):
|
||||
"""
|
||||
Update a network segment on the VSM.
|
||||
|
||||
Network segment on VSM can be updated to associate it with an ip-pool
|
||||
or update its description and segment id.
|
||||
|
||||
:param network_segment_name: name of the network segment
|
||||
:param body: dict of arguments to be updated
|
||||
"""
|
||||
return self._post(self.network_segment_path % (network_segment_name),
|
||||
body=body)
|
||||
|
||||
def delete_network_segment(self, network_segment_name):
|
||||
"""
|
||||
Delete a network segment on the VSM.
|
||||
|
||||
:param network_segment_name: name of the network segment
|
||||
"""
|
||||
return self._delete(self.network_segment_path % (network_segment_name))
|
||||
|
||||
def create_logical_network(self, network_profile):
|
||||
"""
|
||||
Create a logical network on the VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
LOG.debug(_("logical network"))
|
||||
body = {'name': network_profile['name']}
|
||||
return self._post(self.logical_networks_path,
|
||||
body=body)
|
||||
|
||||
def delete_logical_network(self, network_profile):
|
||||
"""
|
||||
Delete a logical network on VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
return self._delete(
|
||||
self.logical_network_path % (network_profile['name']))
|
||||
|
||||
def create_network_segment_pool(self, network_profile):
|
||||
"""
|
||||
Create a network segment pool on the VSM.
|
||||
|
||||
:param network_profile: network profile dict
|
||||
"""
|
||||
LOG.debug(_("network_segment_pool"))
|
||||
body = {'name': network_profile['name'],
|
||||
'id': network_profile['id'],
|
||||
'logicalNetwork': network_profile['name']}
|
||||
return self._post(self.network_segment_pools_path,
|
||||
body=body)
|
||||
|
||||
def update_network_segment_pool(self, network_segment_pool, body):
|
||||
"""
|
||||
Update a network segment pool on the VSM.
|
||||
|
||||
:param network_segment_pool: string representing the name of network
|
||||
segment pool to be updated
|
||||
:param body: dictionary representing key values of network segment
|
||||
pool which need to be updated
|
||||
"""
|
||||
return self._post(self.network_segment_pool_path %
|
||||
(network_segment_pool), body=body)
|
||||
|
||||
def delete_network_segment_pool(self, network_segment_pool_name):
|
||||
"""
|
||||
Delete a network segment pool on the VSM.
|
||||
|
||||
:param network_segment_pool_name: name of the network segment pool
|
||||
"""
|
||||
return self._delete(self.network_segment_pool_path %
|
||||
(network_segment_pool_name))
|
||||
|
||||
def create_ip_pool(self, subnet):
|
||||
"""
|
||||
Create an ip-pool on the VSM.
|
||||
|
||||
:param subnet: subnet dict
|
||||
"""
|
||||
if subnet['cidr']:
|
||||
try:
|
||||
ip = netaddr.IPNetwork(subnet['cidr'])
|
||||
netmask = str(ip.netmask)
|
||||
network_address = str(ip.network)
|
||||
except netaddr.AddrFormatError:
|
||||
msg = _("Invalid input for CIDR")
|
||||
raise q_exc.InvalidInput(error_message=msg)
|
||||
else:
|
||||
netmask = network_address = ""
|
||||
|
||||
if subnet['allocation_pools']:
|
||||
address_range_start = subnet['allocation_pools'][0]['start']
|
||||
address_range_end = subnet['allocation_pools'][0]['end']
|
||||
else:
|
||||
address_range_start = None
|
||||
address_range_end = None
|
||||
|
||||
body = {'addressRangeStart': address_range_start,
|
||||
'addressRangeEnd': address_range_end,
|
||||
'ipAddressSubnet': netmask,
|
||||
'name': subnet['name'],
|
||||
'gateway': subnet['gateway_ip'],
|
||||
'networkAddress': network_address}
|
||||
return self._post(self.ip_pools_path,
|
||||
body=body)
|
||||
|
||||
def delete_ip_pool(self, subnet_name):
|
||||
"""
|
||||
Delete an ip-pool on the VSM.
|
||||
|
||||
:param subnet_name: name of the subnet
|
||||
"""
|
||||
return self._delete(self.ip_pool_path % (subnet_name))
|
||||
|
||||
def create_vm_network(self,
|
||||
port,
|
||||
vm_network_name,
|
||||
policy_profile,
|
||||
network_name):
|
||||
"""
|
||||
Create a VM network on the VSM.
|
||||
|
||||
:param port: port dict
|
||||
:param vm_network_name: name of the VM network
|
||||
:param policy_profile: policy profile dict
|
||||
:param network_name: string representing the name of the network
|
||||
"""
|
||||
body = {'name': vm_network_name,
|
||||
'networkSegmentId': port['network_id'],
|
||||
'networkSegment': network_name,
|
||||
'portProfile': policy_profile['name'],
|
||||
'portProfileId': policy_profile['id'],
|
||||
}
|
||||
return self._post(self.vm_networks_path,
|
||||
body=body)
|
||||
|
||||
def delete_vm_network(self, vm_network_name):
|
||||
"""
|
||||
Delete a VM network on the VSM.
|
||||
|
||||
:param vm_network_name: name of the VM network
|
||||
"""
|
||||
return self._delete(self.vm_network_path % (vm_network_name))
|
||||
|
||||
def create_n1kv_port(self, port, vm_network_name):
|
||||
"""
|
||||
Create a port on the VSM.
|
||||
|
||||
:param port: port dict
|
||||
:param vm_network_name: name of the VM network which imports this port
|
||||
"""
|
||||
body = {'id': port['id'],
|
||||
'macAddress': port['mac_address']}
|
||||
return self._post(self.ports_path % (vm_network_name),
|
||||
body=body)
|
||||
|
||||
def update_n1kv_port(self, vm_network_name, port_id, body):
|
||||
"""
|
||||
Update a port on the VSM.
|
||||
|
||||
Update the mac address associated with the port
|
||||
|
||||
:param vm_network_name: name of the VM network which imports this port
|
||||
:param port_id: UUID of the port
|
||||
:param body: dict of the arguments to be updated
|
||||
"""
|
||||
return self._post(self.port_path % ((vm_network_name), (port_id)),
|
||||
body=body)
|
||||
|
||||
def delete_n1kv_port(self, vm_network_name, port_id):
|
||||
"""
|
||||
Delete a port on the VSM.
|
||||
|
||||
:param vm_network_name: name of the VM network which imports this port
|
||||
:param port_id: UUID of the port
|
||||
"""
|
||||
return self._delete(self.port_path % ((vm_network_name), (port_id)))
|
||||
|
||||
def _do_request(self, method, action, body=None,
|
||||
headers=None):
|
||||
"""
|
||||
Perform the HTTP request.
|
||||
|
||||
The response is in either XML format or plain text. A GET method will
|
||||
invoke a XML response while a PUT/POST/DELETE returns message from the
|
||||
VSM in plain text format.
|
||||
Exception is raised when VSM replies with an INTERNAL SERVER ERROR HTTP
|
||||
status code (500) i.e. an error has occurred on the VSM or SERVICE
|
||||
UNAVAILABLE (503) i.e. VSM is not reachable.
|
||||
|
||||
:param method: type of the HTTP request. POST, GET, PUT or DELETE
|
||||
:param action: path to which the client makes request
|
||||
:param body: dict for arguments which are sent as part of the request
|
||||
:param headers: header for the HTTP request
|
||||
:returns: XML or plain text in HTTP response
|
||||
"""
|
||||
action = self.action_prefix + action
|
||||
if not headers and self.hosts:
|
||||
headers = self._get_auth_header(self.hosts[0])
|
||||
headers['Content-Type'] = self._set_content_type('json')
|
||||
if body:
|
||||
body = self._serialize(body)
|
||||
LOG.debug(_("req: %s"), body)
|
||||
resp, replybody = httplib2.Http().request(action,
|
||||
method,
|
||||
body=body,
|
||||
headers=headers)
|
||||
LOG.debug(_("status_code %s"), resp.status)
|
||||
if resp.status == 200:
|
||||
if 'application/xml' in resp['content-type']:
|
||||
return self._deserialize(replybody, resp.status)
|
||||
elif 'text/plain' in resp['content-type']:
|
||||
LOG.debug(_("VSM: %s"), replybody)
|
||||
elif resp.status == 500:
|
||||
raise c_exc.VSMError(reason=replybody)
|
||||
elif resp.status == 503:
|
||||
raise c_exc.VSMConnectionFailed
|
||||
|
||||
def _serialize(self, data):
|
||||
"""
|
||||
Serialize a dictionary with a single key into either xml or json.
|
||||
|
||||
:param data: data in the form of dict
|
||||
"""
|
||||
if data is None:
|
||||
return None
|
||||
elif type(data) is dict:
|
||||
return wsgi.Serializer().serialize(data, self._set_content_type())
|
||||
else:
|
||||
raise Exception("unable to serialize object of type = '%s'" %
|
||||
type(data))
|
||||
|
||||
def _deserialize(self, data, status_code):
|
||||
"""
|
||||
Deserialize an XML string into a dictionary.
|
||||
|
||||
:param data: XML string from the HTTP response
|
||||
:param status_code: integer status code from the HTTP response
|
||||
:return: data in the form of dict
|
||||
"""
|
||||
if status_code == 204:
|
||||
return data
|
||||
return wsgi.Serializer(self._serialization_metadata).deserialize(
|
||||
data, self._set_content_type('xml'))
|
||||
|
||||
def _set_content_type(self, format=None):
|
||||
"""
|
||||
Set the mime-type to either 'xml' or 'json'.
|
||||
|
||||
:param format: format to be set.
|
||||
:return: mime-type string
|
||||
"""
|
||||
if not format:
|
||||
format = self.format
|
||||
return "application/%s" % (format)
|
||||
|
||||
def _delete(self, action, body=None, headers=None):
|
||||
return self._do_request("DELETE", action, body=body,
|
||||
headers=headers)
|
||||
|
||||
def _get(self, action, body=None, headers=None):
|
||||
return self._do_request("GET", action, body=body,
|
||||
headers=headers)
|
||||
|
||||
def _post(self, action, body=None, headers=None):
|
||||
return self._do_request("POST", action, body=body,
|
||||
headers=headers)
|
||||
|
||||
def _put(self, action, body=None, headers=None):
|
||||
return self._do_request("PUT", action, body=body,
|
||||
headers=headers)
|
||||
|
||||
def _get_vsm_hosts(self):
|
||||
"""
|
||||
Retrieve a list of VSM ip addresses.
|
||||
|
||||
:return: list of host ip addresses
|
||||
"""
|
||||
return [cr[c_const.CREDENTIAL_NAME] for cr in
|
||||
network_db_v2.get_all_n1kv_credentials()]
|
||||
|
||||
def _get_auth_header(self, host_ip):
|
||||
"""
|
||||
Retrieve header with auth info for the VSM.
|
||||
|
||||
:param host_ip: IP address of the VSM
|
||||
:return: authorization header dict
|
||||
"""
|
||||
username = c_cred.Store.get_username(host_ip)
|
||||
password = c_cred.Store.get_password(host_ip)
|
||||
auth = base64.encodestring("%s:%s" % (username, password))
|
||||
header = {"Authorization": "Basic %s" % auth}
|
||||
return header
|
1042
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
Normal file
1042
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,6 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
class PluginV2(db_base_plugin_v2.NeutronDbPluginV2):
|
||||
"""Meta-Plugin with v2 API support for multiple sub-plugins."""
|
||||
|
||||
supported_extension_aliases = ["Cisco Credential", "Cisco qos"]
|
||||
_methods_to_delegate = ['create_network',
|
||||
'delete_network', 'update_network', 'get_network',
|
||||
@ -316,50 +315,29 @@ class PluginV2(db_base_plugin_v2.NeutronDbPluginV2):
|
||||
qos = cdb.update_qos(tenant_id, qos_id, new_name)
|
||||
return qos
|
||||
|
||||
def get_all_credentials(self, tenant_id):
|
||||
def get_all_credentials(self):
|
||||
"""Get all credentials."""
|
||||
LOG.debug(_("get_all_credentials() called"))
|
||||
credential_list = cdb.get_all_credentials(tenant_id)
|
||||
credential_list = cdb.get_all_credentials()
|
||||
return credential_list
|
||||
|
||||
def get_credential_details(self, tenant_id, credential_id):
|
||||
def get_credential_details(self, credential_id):
|
||||
"""Get a particular credential."""
|
||||
LOG.debug(_("get_credential_details() called"))
|
||||
try:
|
||||
credential = cdb.get_credential(tenant_id, credential_id)
|
||||
except Exception:
|
||||
raise cexc.CredentialNotFound(tenant_id=tenant_id,
|
||||
credential_id=credential_id)
|
||||
credential = cdb.get_credential(credential_id)
|
||||
except exc.NotFound:
|
||||
raise cexc.CredentialNotFound(credential_id=credential_id)
|
||||
return credential
|
||||
|
||||
def create_credential(self, tenant_id, credential_name, user_name,
|
||||
password):
|
||||
"""Create a new credential."""
|
||||
LOG.debug(_("create_credential() called"))
|
||||
credential = cdb.add_credential(tenant_id, credential_name,
|
||||
user_name, password)
|
||||
return credential
|
||||
|
||||
def delete_credential(self, tenant_id, credential_id):
|
||||
"""Delete a credential."""
|
||||
LOG.debug(_("delete_credential() called"))
|
||||
try:
|
||||
credential = cdb.get_credential(tenant_id, credential_id)
|
||||
except Exception:
|
||||
raise cexc.CredentialNotFound(tenant_id=tenant_id,
|
||||
credential_id=credential_id)
|
||||
credential = cdb.remove_credential(tenant_id, credential_id)
|
||||
return credential
|
||||
|
||||
def rename_credential(self, tenant_id, credential_id, new_name):
|
||||
def rename_credential(self, credential_id, new_name):
|
||||
"""Rename the particular credential resource."""
|
||||
LOG.debug(_("rename_credential() called"))
|
||||
try:
|
||||
credential = cdb.get_credential(tenant_id, credential_id)
|
||||
except Exception:
|
||||
raise cexc.CredentialNotFound(tenant_id=tenant_id,
|
||||
credential_id=credential_id)
|
||||
credential = cdb.update_credential(tenant_id, credential_id, new_name)
|
||||
credential = cdb.get_credential(credential_id)
|
||||
except exc.NotFound:
|
||||
raise cexc.CredentialNotFound(credential_id=credential_id)
|
||||
credential = cdb.update_credential(credential_id, new_name)
|
||||
return credential
|
||||
|
||||
def schedule_host(self, tenant_id, instance_id, instance_desc):
|
||||
|
@ -39,7 +39,10 @@ LOG = logging.getLogger(__name__)
|
||||
class CiscoNEXUSDriver():
|
||||
"""Nexus Driver Main Class."""
|
||||
def __init__(self):
|
||||
self.nexus_switches = conf.get_nexus_dictionary()
|
||||
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 = {}
|
||||
|
||||
|
18
neutron/tests/unit/cisco/n1kv/__init__.py
Normal file
18
neutron/tests/unit/cisco/n1kv/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Abhishek Raut, Cisco Systems, Inc.
|
||||
#
|
672
neutron/tests/unit/cisco/n1kv/test_n1kv_db.py
Normal file
672
neutron/tests/unit/cisco/n1kv/test_n1kv_db.py
Normal file
@ -0,0 +1,672 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Juergen Brendel, Cisco Systems Inc.
|
||||
# @author: Abhishek Raut, Cisco Systems Inc.
|
||||
|
||||
from sqlalchemy.orm import exc as s_exc
|
||||
from testtools import matchers
|
||||
|
||||
from neutron.common import exceptions as q_exc
|
||||
from neutron import context
|
||||
from neutron.db import api as db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.plugins.cisco.common import cisco_constants
|
||||
from neutron.plugins.cisco.common import cisco_exceptions as c_exc
|
||||
from neutron.plugins.cisco.db import n1kv_db_v2
|
||||
from neutron.plugins.cisco.db import n1kv_models_v2
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
|
||||
PHYS_NET = 'physnet1'
|
||||
PHYS_NET_2 = 'physnet2'
|
||||
VLAN_MIN = 10
|
||||
VLAN_MAX = 19
|
||||
VLAN_RANGES = {PHYS_NET: [(VLAN_MIN, VLAN_MAX)]}
|
||||
UPDATED_VLAN_RANGES = {PHYS_NET: [(VLAN_MIN + 20, VLAN_MAX + 20)],
|
||||
PHYS_NET_2: [(VLAN_MIN + 40, VLAN_MAX + 40)]}
|
||||
VXLAN_MIN = 5000
|
||||
VXLAN_MAX = 5009
|
||||
VXLAN_RANGES = [(VXLAN_MIN, VXLAN_MAX)]
|
||||
UPDATED_VXLAN_RANGES = [(VXLAN_MIN + 20, VXLAN_MAX + 20)]
|
||||
SEGMENT_RANGE = '200-220'
|
||||
SEGMENT_RANGE_MIN_OVERLAP = '210-230'
|
||||
SEGMENT_RANGE_MAX_OVERLAP = '190-209'
|
||||
SEGMENT_RANGE_OVERLAP = '190-230'
|
||||
TEST_NETWORK_ID = 'abcdefghijklmnopqrstuvwxyz'
|
||||
TEST_NETWORK_PROFILE = {'name': 'test_profile',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'physnet1',
|
||||
'segment_range': '10-19'}
|
||||
TEST_NETWORK_PROFILE_2 = {'name': 'test_profile_2',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'physnet1',
|
||||
'segment_range': SEGMENT_RANGE}
|
||||
TEST_NETWORK_PROFILE_VXLAN = {'name': 'test_profile',
|
||||
'segment_type': 'vxlan',
|
||||
'segment_range': '5000-5009',
|
||||
'multicast_ip_range': '239.0.0.70-239.0.0.80'}
|
||||
TEST_POLICY_PROFILE = {'id': '4a417990-76fb-11e2-bcfd-0800200c9a66',
|
||||
'name': 'test_policy_profile'}
|
||||
|
||||
|
||||
def _create_test_network_profile_if_not_there(session,
|
||||
profile=TEST_NETWORK_PROFILE):
|
||||
try:
|
||||
_profile = session.query(n1kv_models_v2.NetworkProfile).filter_by(
|
||||
name=profile['name']).one()
|
||||
except s_exc.NoResultFound:
|
||||
_profile = n1kv_db_v2.create_network_profile(session, profile)
|
||||
return _profile
|
||||
|
||||
|
||||
def _create_test_policy_profile_if_not_there(session,
|
||||
profile=TEST_POLICY_PROFILE):
|
||||
try:
|
||||
_profile = session.query(n1kv_models_v2.PolicyProfile).filter_by(
|
||||
name=profile['name']).one()
|
||||
except s_exc.NoResultFound:
|
||||
_profile = n1kv_db_v2.create_policy_profile(profile)
|
||||
return _profile
|
||||
|
||||
|
||||
class VlanAllocationsTest(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VlanAllocationsTest, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
n1kv_db_v2.sync_vlan_allocations(self.session, VLAN_RANGES)
|
||||
|
||||
def tearDown(self):
|
||||
super(VlanAllocationsTest, self).tearDown()
|
||||
|
||||
def test_sync_vlan_allocations_outside_segment_range(self):
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN - 1)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MAX + 1)
|
||||
n1kv_db_v2.sync_vlan_allocations(self.session, UPDATED_VLAN_RANGES)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN + 20 - 1)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MAX + 20 + 1)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MIN + 40 - 1)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MAX + 40 + 1)
|
||||
n1kv_db_v2.sync_vlan_allocations(self.session, VLAN_RANGES)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MIN + 20)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MIN + 20)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MAX + 20)
|
||||
|
||||
def test_sync_vlan_allocations_unallocated_vlans(self):
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN).allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN + 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MAX - 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MAX).allocated)
|
||||
n1kv_db_v2.sync_vlan_allocations(self.session, UPDATED_VLAN_RANGES)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN + 20).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MIN + 20 + 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
VLAN_MAX + 20 - 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session, PHYS_NET,
|
||||
VLAN_MAX + 20).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MIN + 40).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MIN + 40 + 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MAX + 40 - 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET_2,
|
||||
VLAN_MAX + 40).
|
||||
allocated)
|
||||
|
||||
def test_vlan_pool(self):
|
||||
vlan_ids = set()
|
||||
p = _create_test_network_profile_if_not_there(self.session)
|
||||
for x in xrange(VLAN_MIN, VLAN_MAX + 1):
|
||||
(physical_network, seg_type,
|
||||
vlan_id, m_ip) = n1kv_db_v2.reserve_vlan(self.session, p)
|
||||
self.assertEqual(physical_network, PHYS_NET)
|
||||
self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
|
||||
self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
|
||||
vlan_ids.add(vlan_id)
|
||||
|
||||
self.assertRaises(q_exc.NoNetworkAvailable,
|
||||
n1kv_db_v2.reserve_vlan,
|
||||
self.session,
|
||||
p)
|
||||
|
||||
n1kv_db_v2.release_vlan(self.session, PHYS_NET, vlan_ids.pop(),
|
||||
VLAN_RANGES)
|
||||
physical_network, seg_type, vlan_id, m_ip = (n1kv_db_v2.reserve_vlan(
|
||||
self.session, p))
|
||||
self.assertEqual(physical_network, PHYS_NET)
|
||||
self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
|
||||
self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
|
||||
vlan_ids.add(vlan_id)
|
||||
|
||||
for vlan_id in vlan_ids:
|
||||
n1kv_db_v2.release_vlan(self.session, PHYS_NET, vlan_id,
|
||||
VLAN_RANGES)
|
||||
|
||||
def test_specific_vlan_inside_pool(self):
|
||||
vlan_id = VLAN_MIN + 5
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
vlan_id).allocated)
|
||||
n1kv_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
|
||||
self.assertTrue(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
vlan_id).allocated)
|
||||
|
||||
self.assertRaises(q_exc.VlanIdInUse,
|
||||
n1kv_db_v2.reserve_specific_vlan,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
vlan_id)
|
||||
|
||||
n1kv_db_v2.release_vlan(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
|
||||
self.assertFalse(n1kv_db_v2.get_vlan_allocation(self.session,
|
||||
PHYS_NET,
|
||||
vlan_id).allocated)
|
||||
|
||||
def test_specific_vlan_outside_pool(self):
|
||||
vlan_id = VLAN_MAX + 5
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
vlan_id)
|
||||
n1kv_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
|
||||
self.assertTrue(n1kv_db_v2.get_vlan_allocation(self.session, PHYS_NET,
|
||||
vlan_id).allocated)
|
||||
|
||||
self.assertRaises(q_exc.VlanIdInUse,
|
||||
n1kv_db_v2.reserve_specific_vlan,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
vlan_id)
|
||||
|
||||
n1kv_db_v2.release_vlan(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
|
||||
self.assertRaises(c_exc.VlanIDNotFound,
|
||||
n1kv_db_v2.get_vlan_allocation,
|
||||
self.session,
|
||||
PHYS_NET,
|
||||
vlan_id)
|
||||
|
||||
|
||||
class VxlanAllocationsTest(base.BaseTestCase,
|
||||
n1kv_db_v2.NetworkProfile_db_mixin):
|
||||
|
||||
def setUp(self):
|
||||
super(VxlanAllocationsTest, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
n1kv_db_v2.sync_vxlan_allocations(self.session, VXLAN_RANGES)
|
||||
|
||||
def tearDown(self):
|
||||
super(VxlanAllocationsTest, self).tearDown()
|
||||
|
||||
def test_sync_vxlan_allocations_outside_segment_range(self):
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN - 1))
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX + 1))
|
||||
n1kv_db_v2.sync_vxlan_allocations(self.session, UPDATED_VXLAN_RANGES)
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN + 20 - 1))
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX + 20 + 1))
|
||||
|
||||
def test_sync_vxlan_allocations_unallocated_vxlans(self):
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN).allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN + 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX - 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX).allocated)
|
||||
n1kv_db_v2.sync_vxlan_allocations(self.session, UPDATED_VXLAN_RANGES)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN + 20).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MIN + 20 + 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX + 20 - 1).
|
||||
allocated)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
VXLAN_MAX + 20).
|
||||
allocated)
|
||||
|
||||
def test_vxlan_pool(self):
|
||||
vxlan_ids = set()
|
||||
profile = n1kv_db_v2.create_network_profile(self.session,
|
||||
TEST_NETWORK_PROFILE_VXLAN)
|
||||
for x in xrange(VXLAN_MIN, VXLAN_MAX + 1):
|
||||
vxlan = n1kv_db_v2.reserve_vxlan(self.session, profile)
|
||||
vxlan_id = vxlan[2]
|
||||
self.assertThat(vxlan_id, matchers.GreaterThan(VXLAN_MIN - 1))
|
||||
self.assertThat(vxlan_id, matchers.LessThan(VXLAN_MAX + 1))
|
||||
vxlan_ids.add(vxlan_id)
|
||||
|
||||
self.assertRaises(q_exc.NoNetworkAvailable,
|
||||
n1kv_db_v2.reserve_vxlan,
|
||||
self.session,
|
||||
profile)
|
||||
n1kv_db_v2.release_vxlan(self.session, vxlan_ids.pop(), VXLAN_RANGES)
|
||||
vxlan = n1kv_db_v2.reserve_vxlan(self.session, profile)
|
||||
vxlan_id = vxlan[2]
|
||||
self.assertThat(vxlan_id, matchers.GreaterThan(VXLAN_MIN - 1))
|
||||
self.assertThat(vxlan_id, matchers.LessThan(VXLAN_MAX + 1))
|
||||
vxlan_ids.add(vxlan_id)
|
||||
|
||||
for vxlan_id in vxlan_ids:
|
||||
n1kv_db_v2.release_vxlan(self.session, vxlan_id, VXLAN_RANGES)
|
||||
n1kv_db_v2.delete_network_profile(self.session, profile.id)
|
||||
|
||||
def test_specific_vxlan_inside_pool(self):
|
||||
vxlan_id = VXLAN_MIN + 5
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id).allocated)
|
||||
n1kv_db_v2.reserve_specific_vxlan(self.session, vxlan_id)
|
||||
self.assertTrue(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id).allocated)
|
||||
|
||||
self.assertRaises(c_exc.VxlanIdInUse,
|
||||
n1kv_db_v2.reserve_specific_vxlan,
|
||||
self.session,
|
||||
vxlan_id)
|
||||
|
||||
n1kv_db_v2.release_vxlan(self.session, vxlan_id, VXLAN_RANGES)
|
||||
self.assertFalse(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id).allocated)
|
||||
|
||||
def test_specific_vxlan_outside_pool(self):
|
||||
vxlan_id = VXLAN_MAX + 5
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id))
|
||||
n1kv_db_v2.reserve_specific_vxlan(self.session, vxlan_id)
|
||||
self.assertTrue(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id).allocated)
|
||||
|
||||
self.assertRaises(c_exc.VxlanIdInUse,
|
||||
n1kv_db_v2.reserve_specific_vxlan,
|
||||
self.session,
|
||||
vxlan_id)
|
||||
|
||||
n1kv_db_v2.release_vxlan(self.session, vxlan_id, VXLAN_RANGES)
|
||||
self.assertIsNone(n1kv_db_v2.get_vxlan_allocation(self.session,
|
||||
vxlan_id))
|
||||
|
||||
|
||||
class NetworkBindingsTest(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NetworkBindingsTest, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
|
||||
def tearDown(self):
|
||||
super(NetworkBindingsTest, self).tearDown()
|
||||
|
||||
def test_add_network_binding(self):
|
||||
with self.network() as network:
|
||||
TEST_NETWORK_ID = network['network']['id']
|
||||
|
||||
self.assertRaises(c_exc.NetworkBindingNotFound,
|
||||
n1kv_db_v2.get_network_binding,
|
||||
self.session,
|
||||
TEST_NETWORK_ID)
|
||||
|
||||
p = _create_test_network_profile_if_not_there(self.session)
|
||||
n1kv_db_v2.add_network_binding(
|
||||
self.session, TEST_NETWORK_ID, 'vlan',
|
||||
PHYS_NET, 1234, '0.0.0.0', p.id)
|
||||
binding = n1kv_db_v2.get_network_binding(
|
||||
self.session, TEST_NETWORK_ID)
|
||||
self.assertIsNotNone(binding)
|
||||
self.assertEqual(binding.network_id, TEST_NETWORK_ID)
|
||||
self.assertEqual(binding.network_type, 'vlan')
|
||||
self.assertEqual(binding.physical_network, PHYS_NET)
|
||||
self.assertEqual(binding.segmentation_id, 1234)
|
||||
|
||||
|
||||
class NetworkProfileTests(base.BaseTestCase,
|
||||
n1kv_db_v2.NetworkProfile_db_mixin):
|
||||
|
||||
def setUp(self):
|
||||
super(NetworkProfileTests, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
|
||||
def tearDown(self):
|
||||
super(NetworkProfileTests, self).tearDown()
|
||||
|
||||
def test_create_network_profile(self):
|
||||
_db_profile = n1kv_db_v2.create_network_profile(self.session,
|
||||
TEST_NETWORK_PROFILE)
|
||||
self.assertIsNotNone(_db_profile)
|
||||
db_profile = (self.session.query(n1kv_models_v2.NetworkProfile).
|
||||
filter_by(name=TEST_NETWORK_PROFILE['name']).one())
|
||||
self.assertIsNotNone(db_profile)
|
||||
self.assertEqual(_db_profile.id, db_profile.id)
|
||||
self.assertEqual(_db_profile.name, db_profile.name)
|
||||
self.assertEqual(_db_profile.segment_type, db_profile.segment_type)
|
||||
self.assertEqual(_db_profile.segment_range, db_profile.segment_range)
|
||||
self.assertEqual(_db_profile.multicast_ip_index,
|
||||
db_profile.multicast_ip_index)
|
||||
self.assertEqual(_db_profile.multicast_ip_range,
|
||||
db_profile.multicast_ip_range)
|
||||
n1kv_db_v2.delete_network_profile(self.session, _db_profile.id)
|
||||
|
||||
def test_create_network_profile_overlap(self):
|
||||
_db_profile = n1kv_db_v2.create_network_profile(self.session,
|
||||
TEST_NETWORK_PROFILE_2)
|
||||
ctx = context.get_admin_context()
|
||||
TEST_NETWORK_PROFILE_2['name'] = 'net-profile-min-overlap'
|
||||
TEST_NETWORK_PROFILE_2['segment_range'] = SEGMENT_RANGE_MIN_OVERLAP
|
||||
test_net_profile = {'network_profile': TEST_NETWORK_PROFILE_2}
|
||||
self.assertRaises(q_exc.InvalidInput,
|
||||
self.create_network_profile,
|
||||
ctx,
|
||||
test_net_profile)
|
||||
|
||||
TEST_NETWORK_PROFILE_2['name'] = 'net-profile-max-overlap'
|
||||
TEST_NETWORK_PROFILE_2['segment_range'] = SEGMENT_RANGE_MAX_OVERLAP
|
||||
test_net_profile = {'network_profile': TEST_NETWORK_PROFILE_2}
|
||||
self.assertRaises(q_exc.InvalidInput,
|
||||
self.create_network_profile,
|
||||
ctx,
|
||||
test_net_profile)
|
||||
|
||||
TEST_NETWORK_PROFILE_2['name'] = 'net-profile-overlap'
|
||||
TEST_NETWORK_PROFILE_2['segment_range'] = SEGMENT_RANGE_OVERLAP
|
||||
test_net_profile = {'network_profile': TEST_NETWORK_PROFILE_2}
|
||||
self.assertRaises(q_exc.InvalidInput,
|
||||
self.create_network_profile,
|
||||
ctx,
|
||||
test_net_profile)
|
||||
n1kv_db_v2.delete_network_profile(self.session, _db_profile.id)
|
||||
|
||||
def test_delete_network_profile(self):
|
||||
try:
|
||||
profile = (self.session.query(n1kv_models_v2.NetworkProfile).
|
||||
filter_by(name=TEST_NETWORK_PROFILE['name']).one())
|
||||
except s_exc.NoResultFound:
|
||||
profile = n1kv_db_v2.create_network_profile(self.session,
|
||||
TEST_NETWORK_PROFILE)
|
||||
|
||||
n1kv_db_v2.delete_network_profile(self.session, profile.id)
|
||||
try:
|
||||
self.session.query(n1kv_models_v2.NetworkProfile).filter_by(
|
||||
name=TEST_NETWORK_PROFILE['name']).one()
|
||||
except s_exc.NoResultFound:
|
||||
pass
|
||||
else:
|
||||
self.fail("Network Profile (%s) was not deleted" %
|
||||
TEST_NETWORK_PROFILE['name'])
|
||||
|
||||
def test_update_network_profile(self):
|
||||
TEST_PROFILE_1 = {'name': 'test_profile_1'}
|
||||
profile = _create_test_network_profile_if_not_there(self.session)
|
||||
updated_profile = n1kv_db_v2.update_network_profile(self.session,
|
||||
profile.id,
|
||||
TEST_PROFILE_1)
|
||||
self.assertEqual(updated_profile.name, TEST_PROFILE_1['name'])
|
||||
n1kv_db_v2.delete_network_profile(self.session, profile.id)
|
||||
|
||||
def test_get_network_profile(self):
|
||||
profile = n1kv_db_v2.create_network_profile(self.session,
|
||||
TEST_NETWORK_PROFILE)
|
||||
got_profile = n1kv_db_v2.get_network_profile(self.session, profile.id)
|
||||
self.assertEqual(profile.id, got_profile.id)
|
||||
self.assertEqual(profile.name, got_profile.name)
|
||||
n1kv_db_v2.delete_network_profile(self.session, profile.id)
|
||||
|
||||
def test_get_network_profiles(self):
|
||||
test_profiles = [{'name': 'test_profile1',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '200-210'},
|
||||
{'name': 'test_profile2',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '211-220'},
|
||||
{'name': 'test_profile3',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '221-230'},
|
||||
{'name': 'test_profile4',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '231-240'},
|
||||
{'name': 'test_profile5',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '241-250'},
|
||||
{'name': 'test_profile6',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '251-260'},
|
||||
{'name': 'test_profile7',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '261-270'}]
|
||||
[n1kv_db_v2.create_network_profile(self.session, p)
|
||||
for p in test_profiles]
|
||||
# TODO(abhraut): Fix this test to work with real tenant_td
|
||||
profiles = n1kv_db_v2._get_network_profiles()
|
||||
self.assertEqual(len(test_profiles), len(list(profiles)))
|
||||
|
||||
|
||||
class PolicyProfileTests(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(PolicyProfileTests, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
|
||||
def tearDown(self):
|
||||
super(PolicyProfileTests, self).tearDown()
|
||||
|
||||
def test_create_policy_profile(self):
|
||||
_db_profile = n1kv_db_v2.create_policy_profile(TEST_POLICY_PROFILE)
|
||||
self.assertIsNotNone(_db_profile)
|
||||
db_profile = (self.session.query(n1kv_models_v2.PolicyProfile).
|
||||
filter_by(name=TEST_POLICY_PROFILE['name']).one)()
|
||||
self.assertIsNotNone(db_profile)
|
||||
self.assertTrue(_db_profile.id == db_profile.id)
|
||||
self.assertTrue(_db_profile.name == db_profile.name)
|
||||
|
||||
def test_delete_policy_profile(self):
|
||||
profile = _create_test_policy_profile_if_not_there(self.session)
|
||||
n1kv_db_v2.delete_policy_profile(profile.id)
|
||||
try:
|
||||
self.session.query(n1kv_models_v2.PolicyProfile).filter_by(
|
||||
name=TEST_POLICY_PROFILE['name']).one()
|
||||
except s_exc.NoResultFound:
|
||||
pass
|
||||
else:
|
||||
self.fail("Policy Profile (%s) was not deleted" %
|
||||
TEST_POLICY_PROFILE['name'])
|
||||
|
||||
def test_update_policy_profile(self):
|
||||
TEST_PROFILE_1 = {'name': 'test_profile_1'}
|
||||
profile = _create_test_policy_profile_if_not_there(self.session)
|
||||
updated_profile = n1kv_db_v2.update_policy_profile(self.session,
|
||||
profile.id,
|
||||
TEST_PROFILE_1)
|
||||
self.assertEqual(updated_profile.name, TEST_PROFILE_1['name'])
|
||||
|
||||
def test_get_policy_profile(self):
|
||||
profile = _create_test_policy_profile_if_not_there(self.session)
|
||||
got_profile = n1kv_db_v2.get_policy_profile(self.session, profile.id)
|
||||
self.assertEqual(profile.id, got_profile.id)
|
||||
self.assertEqual(profile.name, got_profile.name)
|
||||
|
||||
|
||||
class ProfileBindingTests(base.BaseTestCase,
|
||||
n1kv_db_v2.NetworkProfile_db_mixin,
|
||||
db_base_plugin_v2.CommonDbMixin):
|
||||
|
||||
def setUp(self):
|
||||
super(ProfileBindingTests, self).setUp()
|
||||
n1kv_db_v2.initialize()
|
||||
self.session = db.get_session()
|
||||
|
||||
def tearDown(self):
|
||||
super(ProfileBindingTests, self).tearDown()
|
||||
|
||||
def _create_test_binding_if_not_there(self, tenant_id, profile_id,
|
||||
profile_type):
|
||||
try:
|
||||
_binding = (self.session.query(n1kv_models_v2.ProfileBinding).
|
||||
filter_by(profile_type=profile_type,
|
||||
tenant_id=tenant_id,
|
||||
profile_id=profile_id).one())
|
||||
except s_exc.NoResultFound:
|
||||
_binding = n1kv_db_v2.create_profile_binding(tenant_id,
|
||||
profile_id,
|
||||
profile_type)
|
||||
return _binding
|
||||
|
||||
def test_create_profile_binding(self):
|
||||
test_tenant_id = "d434dd90-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_id = "dd7b9741-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_type = "network"
|
||||
n1kv_db_v2.create_profile_binding(test_tenant_id, test_profile_id,
|
||||
test_profile_type)
|
||||
try:
|
||||
self.session.query(n1kv_models_v2.ProfileBinding).filter_by(
|
||||
profile_type=test_profile_type,
|
||||
tenant_id=test_tenant_id,
|
||||
profile_id=test_profile_id).one()
|
||||
except s_exc.MultipleResultsFound:
|
||||
self.fail("Bindings must be unique")
|
||||
except s_exc.NoResultFound:
|
||||
self.fail("Could not create Profile Binding")
|
||||
|
||||
def test_get_profile_binding(self):
|
||||
test_tenant_id = "d434dd90-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_id = "dd7b9741-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_type = "network"
|
||||
self._create_test_binding_if_not_there(test_tenant_id,
|
||||
test_profile_id,
|
||||
test_profile_type)
|
||||
binding = n1kv_db_v2.get_profile_binding(test_tenant_id,
|
||||
test_profile_id)
|
||||
self.assertEqual(binding.tenant_id, test_tenant_id)
|
||||
self.assertEqual(binding.profile_id, test_profile_id)
|
||||
self.assertEqual(binding.profile_type, test_profile_type)
|
||||
|
||||
def test_delete_profile_binding(self):
|
||||
test_tenant_id = "d434dd90-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_id = "dd7b9741-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_type = "network"
|
||||
self._create_test_binding_if_not_there(test_tenant_id,
|
||||
test_profile_id,
|
||||
test_profile_type)
|
||||
n1kv_db_v2.delete_profile_binding(test_tenant_id, test_profile_id)
|
||||
q = (self.session.query(n1kv_models_v2.ProfileBinding).filter_by(
|
||||
profile_type=test_profile_type,
|
||||
tenant_id=test_tenant_id,
|
||||
profile_id=test_profile_id))
|
||||
self.assertFalse(q.count())
|
||||
|
||||
def test_default_tenant_replace(self):
|
||||
ctx = context.get_admin_context()
|
||||
ctx.tenant_id = "d434dd90-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_id = "AAAAAAAA-76ec-11e2-bcfd-0800200c9a66"
|
||||
test_profile_type = "policy"
|
||||
n1kv_db_v2.create_profile_binding(cisco_constants.TENANT_ID_NOT_SET,
|
||||
test_profile_id,
|
||||
test_profile_type)
|
||||
network_profile = {"network_profile": TEST_NETWORK_PROFILE}
|
||||
test_network_profile = self.create_network_profile(ctx,
|
||||
network_profile)
|
||||
binding = n1kv_db_v2.get_profile_binding(ctx.tenant_id,
|
||||
test_profile_id)
|
||||
self.assertIsNone(n1kv_db_v2.get_profile_binding(
|
||||
cisco_constants.TENANT_ID_NOT_SET,
|
||||
test_profile_id))
|
||||
self.assertNotEqual(binding.tenant_id,
|
||||
cisco_constants.TENANT_ID_NOT_SET)
|
||||
n1kv_db_v2.delete_network_profile(self.session,
|
||||
test_network_profile['id'])
|
318
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py
Normal file
318
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py
Normal file
@ -0,0 +1,318 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 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.
|
||||
#
|
||||
# @author: Juergen Brendel, Cisco Systems Inc.
|
||||
# @author: Abhishek Raut, Cisco Systems Inc.
|
||||
|
||||
from mock import patch
|
||||
|
||||
from neutron import context
|
||||
import neutron.db.api as db
|
||||
from neutron.plugins.cisco.db import n1kv_db_v2
|
||||
from neutron.plugins.cisco.db import n1kv_models_v2
|
||||
from neutron.plugins.cisco.db import network_db_v2 as cdb
|
||||
from neutron.plugins.cisco.extensions import n1kv_profile
|
||||
from neutron.plugins.cisco.n1kv import n1kv_client
|
||||
from neutron.plugins.cisco.n1kv import n1kv_neutron_plugin
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit import test_db_plugin as test_plugin
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
|
||||
"""
|
||||
This object is returned by mocked httplib instead of a normal response.
|
||||
|
||||
Initialize it with the status code, content type and buffer contents
|
||||
you wish to return.
|
||||
|
||||
"""
|
||||
def __init__(self, status, response_text, content_type):
|
||||
self.buffer = response_text
|
||||
self.status = status
|
||||
|
||||
def __getitem__(cls, val):
|
||||
return "application/xml"
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
return self.buffer
|
||||
|
||||
|
||||
def _fake_add_dummy_profile_for_test(self, obj):
|
||||
"""
|
||||
Replacement for a function in the N1KV neutron plugin module.
|
||||
|
||||
Since VSM is not available at the time of tests, we have no
|
||||
policy profiles. Hence we inject a dummy policy/network profile into the
|
||||
port/network object.
|
||||
"""
|
||||
dummy_profile_name = "dummy_profile"
|
||||
dummy_tenant_id = "test-tenant"
|
||||
db_session = db.get_session()
|
||||
if 'port' in obj:
|
||||
dummy_profile_id = "00000000-1111-1111-1111-000000000000"
|
||||
self._add_policy_profile(dummy_profile_name,
|
||||
dummy_profile_id,
|
||||
dummy_tenant_id)
|
||||
obj['port'][n1kv_profile.PROFILE_ID] = dummy_profile_id
|
||||
elif 'network' in obj:
|
||||
profile = {'name': 'dummy_profile',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phsy1',
|
||||
'segment_range': '3968-4047'}
|
||||
self.network_vlan_ranges = {profile[
|
||||
'physical_network']: [(3968, 4047)]}
|
||||
n1kv_db_v2.sync_vlan_allocations(db_session, self.network_vlan_ranges)
|
||||
np = n1kv_db_v2.create_network_profile(db_session, profile)
|
||||
obj['network'][n1kv_profile.PROFILE_ID] = np.id
|
||||
|
||||
|
||||
def _fake_setup_vsm(self):
|
||||
"""Fake establish Communication with Cisco Nexus1000V VSM."""
|
||||
self.agent_vsm = True
|
||||
self._poll_policies(event_type="port_profile")
|
||||
|
||||
|
||||
class N1kvPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
|
||||
_plugin_name = ('neutron.plugins.cisco.n1kv.'
|
||||
'n1kv_neutron_plugin.N1kvNeutronPluginV2')
|
||||
|
||||
tenant_id = "some_tenant"
|
||||
|
||||
DEFAULT_RESP_BODY = ""
|
||||
DEFAULT_RESP_CODE = 200
|
||||
DEFAULT_CONTENT_TYPE = ""
|
||||
|
||||
def _make_test_policy_profile(self, id):
|
||||
"""Create a policy profile record for testing purpose."""
|
||||
profile = {'id': id,
|
||||
'name': 'TestGrizzlyPP'}
|
||||
profile_obj = n1kv_db_v2.create_policy_profile(profile)
|
||||
return profile_obj
|
||||
|
||||
def _make_test_profile(self):
|
||||
"""Create a profile record for testing purposes."""
|
||||
alloc_obj = n1kv_models_v2.N1kvVlanAllocation(physical_network='foo',
|
||||
vlan_id=123)
|
||||
alloc_obj.allocated = False
|
||||
segment_range = "100-900"
|
||||
segment_type = 'vlan'
|
||||
physical_network = 'phys1'
|
||||
profile_obj = n1kv_models_v2.NetworkProfile(
|
||||
name="test_np",
|
||||
segment_type=segment_type,
|
||||
segment_range=segment_range,
|
||||
physical_network=physical_network)
|
||||
session = db.get_session()
|
||||
session.add(profile_obj)
|
||||
session.flush()
|
||||
return profile_obj
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup method for n1kv plugin tests.
|
||||
|
||||
First step is to define an acceptable response from the VSM to
|
||||
our requests. This needs to be done BEFORE the setUp() function
|
||||
of the super-class is called.
|
||||
|
||||
This default here works for many cases. If you need something
|
||||
extra, please define your own setUp() function in your test class,
|
||||
and set your DEFAULT_RESPONSE value also BEFORE calling the
|
||||
setUp() of the super-function (this one here). If you have set
|
||||
a value already, it will not be overwritten by this code.
|
||||
|
||||
"""
|
||||
if not self.DEFAULT_RESP_BODY:
|
||||
self.DEFAULT_RESP_BODY = (
|
||||
"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<set name="events_set">
|
||||
<instance name="1" url="/api/hyper-v/events/1">
|
||||
<properties>
|
||||
<cmd>configure terminal ; port-profile type vethernet grizzlyPP
|
||||
(SUCCESS)
|
||||
</cmd>
|
||||
<id>42227269-e348-72ed-bdb7-7ce91cd1423c</id>
|
||||
<time>1369223611</time>
|
||||
<name>grizzlyPP</name>
|
||||
</properties>
|
||||
</instance>
|
||||
<instance name="2" url="/api/hyper-v/events/2">
|
||||
<properties>
|
||||
<cmd>configure terminal ; port-profile type vethernet havanaPP
|
||||
(SUCCESS)
|
||||
</cmd>
|
||||
<id>3fc83608-ae36-70e7-9d22-dec745623d06</id>
|
||||
<time>1369223661</time>
|
||||
<name>havanaPP</name>
|
||||
</properties>
|
||||
</instance>
|
||||
</set>
|
||||
""")
|
||||
# Creating a mock HTTP connection object for httplib. The N1KV client
|
||||
# interacts with the VSM via HTTP. Since we don't have a VSM running
|
||||
# in the unit tests, we need to 'fake' it by patching the HTTP library
|
||||
# itself. We install a patch for a fake HTTP connection class.
|
||||
# Using __name__ to avoid having to enter the full module path.
|
||||
http_patcher = patch(n1kv_client.httplib2.__name__ + ".Http")
|
||||
FakeHttpConnection = http_patcher.start()
|
||||
self.addCleanup(http_patcher.stop)
|
||||
# Now define the return values for a few functions that may be called
|
||||
# on any instance of the fake HTTP connection class.
|
||||
instance = FakeHttpConnection.return_value
|
||||
instance.getresponse.return_value = (FakeResponse(
|
||||
self.DEFAULT_RESP_CODE,
|
||||
self.DEFAULT_RESP_BODY,
|
||||
'application/xml'))
|
||||
instance.request.return_value = (instance.getresponse.return_value,
|
||||
self.DEFAULT_RESP_BODY)
|
||||
|
||||
# Patch some internal functions in a few other parts of the system.
|
||||
# These help us move along, without having to mock up even more systems
|
||||
# in the background.
|
||||
|
||||
# Return a dummy VSM IP address
|
||||
get_vsm_hosts_patcher = patch(n1kv_client.__name__ +
|
||||
".Client._get_vsm_hosts")
|
||||
fake_get_vsm_hosts = get_vsm_hosts_patcher.start()
|
||||
self.addCleanup(get_vsm_hosts_patcher.stop)
|
||||
fake_get_vsm_hosts.return_value = ["127.0.0.1"]
|
||||
|
||||
# Return dummy user profiles
|
||||
get_cred_name_patcher = patch(cdb.__name__ + ".get_credential_name")
|
||||
fake_get_cred_name = get_cred_name_patcher.start()
|
||||
self.addCleanup(get_cred_name_patcher.stop)
|
||||
fake_get_cred_name.return_value = {"user_name": "admin",
|
||||
"password": "admin_password"}
|
||||
|
||||
# Patch a dummy profile creation into the N1K plugin code. The original
|
||||
# function in the plugin is a noop for production, but during test, we
|
||||
# need it to return a dummy network profile.
|
||||
(n1kv_neutron_plugin.N1kvNeutronPluginV2.
|
||||
_add_dummy_profile_only_if_testing) = _fake_add_dummy_profile_for_test
|
||||
|
||||
n1kv_neutron_plugin.N1kvNeutronPluginV2._setup_vsm = _fake_setup_vsm
|
||||
|
||||
super(N1kvPluginTestCase, self).setUp(self._plugin_name)
|
||||
# Create some of the database entries that we require.
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = (self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB8'))
|
||||
# Additional args for create_network(), create_port(), etc.
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
|
||||
def test_plugin(self):
|
||||
self._make_network('json',
|
||||
'some_net',
|
||||
True,
|
||||
tenant_id=self.tenant_id,
|
||||
set_context=True)
|
||||
|
||||
req = self.new_list_request('networks', params="fields=tenant_id")
|
||||
req.environ['neutron.context'] = context.Context('', self.tenant_id)
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 200)
|
||||
body = self.deserialize('json', res)
|
||||
self.assertIn('tenant_id', body['networks'][0])
|
||||
|
||||
|
||||
class TestN1kvBasicGet(test_plugin.TestBasicGet,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TestN1kvHTTPResponse(test_plugin.TestV2HTTPResponse,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TestN1kvPorts(test_plugin.TestPortsV2,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
def _make_other_tenant_profile(self):
|
||||
"""Underlying test uses other tenant Id for tests."""
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB9')
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
|
||||
def test_create_port_public_network(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_create_port_public_network()
|
||||
|
||||
def test_create_port_public_network_with_ip(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_create_port_public_network_with_ip()
|
||||
|
||||
def test_create_ports_bulk_emulated(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts,
|
||||
self).test_create_ports_bulk_emulated()
|
||||
|
||||
def test_create_ports_bulk_emulated_plugin_failure(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts,
|
||||
self).test_create_ports_bulk_emulated_plugin_failure()
|
||||
|
||||
def test_delete_port_public_network(self):
|
||||
self._make_other_tenant_profile()
|
||||
super(TestN1kvPorts, self).test_delete_port_public_network()
|
||||
|
||||
|
||||
class TestN1kvNetworks(test_plugin.TestNetworksV2,
|
||||
N1kvPluginTestCase):
|
||||
|
||||
_default_tenant = "somebody_else" # Tenant-id determined by underlying
|
||||
# DB-plugin test cases. Need to use this
|
||||
# one for profile creation
|
||||
|
||||
def test_update_network_set_not_shared_single_tenant(self):
|
||||
# The underlying test function needs a profile for a different tenant.
|
||||
profile_obj = self._make_test_profile()
|
||||
policy_profile_obj = self._make_test_policy_profile(
|
||||
'41548d21-7f89-4da0-9131-3d4fd4e8BBB9')
|
||||
self.more_args = {
|
||||
"network": {"n1kv:profile_id": profile_obj.id},
|
||||
"port": {"n1kv:profile_id": policy_profile_obj.id}
|
||||
}
|
||||
super(TestN1kvNetworks,
|
||||
self).test_update_network_set_not_shared_single_tenant()
|
||||
|
||||
|
||||
class TestN1kvNonDbTest(base.BaseTestCase):
|
||||
|
||||
"""
|
||||
This test class here can be used to test the plugin directly,
|
||||
without going through the DB plugin test cases.
|
||||
|
||||
None of the set-up done in N1kvPluginTestCase applies here.
|
||||
|
||||
"""
|
||||
def test_db(self):
|
||||
n1kv_db_v2.initialize()
|
@ -116,7 +116,7 @@ class CiscoNetworkCredentialDbTest(base.BaseTestCase):
|
||||
|
||||
"""Unit tests for cisco.db.network_models_v2.Credential model."""
|
||||
|
||||
CredObj = collections.namedtuple('CredObj', 'tenant cname usr pwd')
|
||||
CredObj = collections.namedtuple('CredObj', 'cname usr pwd ctype')
|
||||
|
||||
def setUp(self):
|
||||
super(CiscoNetworkCredentialDbTest, self).setUp()
|
||||
@ -126,14 +126,14 @@ class CiscoNetworkCredentialDbTest(base.BaseTestCase):
|
||||
|
||||
def _cred_test_obj(self, tnum, cnum):
|
||||
"""Create a Credential test object from a pair of numbers."""
|
||||
tenant = 'tenant_%s' % str(tnum)
|
||||
cname = 'credential_%s' % str(cnum)
|
||||
cname = 'credential_%s_%s' % (str(tnum), str(cnum))
|
||||
usr = 'User_%s_%s' % (str(tnum), str(cnum))
|
||||
pwd = 'Password_%s_%s' % (str(tnum), str(cnum))
|
||||
return self.CredObj(tenant, cname, usr, pwd)
|
||||
ctype = 'ctype_%s' % str(tnum)
|
||||
return self.CredObj(cname, usr, pwd, ctype)
|
||||
|
||||
def _assert_equal(self, credential, cred_obj):
|
||||
self.assertEqual(credential.tenant_id, cred_obj.tenant)
|
||||
self.assertEqual(credential.type, cred_obj.ctype)
|
||||
self.assertEqual(credential.credential_name, cred_obj.cname)
|
||||
self.assertEqual(credential.user_name, cred_obj.usr)
|
||||
self.assertEqual(credential.password, cred_obj.pwd)
|
||||
@ -141,100 +141,90 @@ class CiscoNetworkCredentialDbTest(base.BaseTestCase):
|
||||
def test_credential_add_remove(self):
|
||||
cred11 = self._cred_test_obj(1, 1)
|
||||
cred = cdb.add_credential(
|
||||
cred11.tenant, cred11.cname, cred11.usr, cred11.pwd)
|
||||
cred11.cname, cred11.usr, cred11.pwd, cred11.ctype)
|
||||
self._assert_equal(cred, cred11)
|
||||
cred_id = cred.credential_id
|
||||
cred = cdb.remove_credential(cred11.tenant, cred_id)
|
||||
cred = cdb.remove_credential(cred_id)
|
||||
self._assert_equal(cred, cred11)
|
||||
cred = cdb.remove_credential(cred11.tenant, cred_id)
|
||||
cred = cdb.remove_credential(cred_id)
|
||||
self.assertIsNone(cred)
|
||||
|
||||
def test_credential_add_dup(self):
|
||||
cred22 = self._cred_test_obj(2, 2)
|
||||
cred = cdb.add_credential(
|
||||
cred22.tenant, cred22.cname, cred22.usr, cred22.pwd)
|
||||
cred22.cname, cred22.usr, cred22.pwd, cred22.ctype)
|
||||
self._assert_equal(cred, cred22)
|
||||
cred_id = cred.credential_id
|
||||
with testtools.ExpectedException(c_exc.CredentialAlreadyExists):
|
||||
cdb.add_credential(
|
||||
cred22.tenant, cred22.cname, cred22.usr, cred22.pwd)
|
||||
cred = cdb.remove_credential(cred22.tenant, cred_id)
|
||||
cred22.cname, cred22.usr, cred22.pwd, cred22.ctype)
|
||||
cred = cdb.remove_credential(cred_id)
|
||||
self._assert_equal(cred, cred22)
|
||||
cred = cdb.remove_credential(cred22.tenant, cred_id)
|
||||
cred = cdb.remove_credential(cred_id)
|
||||
self.assertIsNone(cred)
|
||||
|
||||
def test_credential_get_id(self):
|
||||
cred11 = self._cred_test_obj(1, 1)
|
||||
cred11_id = cdb.add_credential(
|
||||
cred11.tenant, cred11.cname, cred11.usr, cred11.pwd).credential_id
|
||||
cred11.cname, cred11.usr, cred11.pwd, cred11.ctype).credential_id
|
||||
cred21 = self._cred_test_obj(2, 1)
|
||||
cred21_id = cdb.add_credential(
|
||||
cred21.tenant, cred21.cname, cred21.usr, cred21.pwd).credential_id
|
||||
cred21.cname, cred21.usr, cred21.pwd, cred21.ctype).credential_id
|
||||
cred22 = self._cred_test_obj(2, 2)
|
||||
cred22_id = cdb.add_credential(
|
||||
cred22.tenant, cred22.cname, cred22.usr, cred22.pwd).credential_id
|
||||
cred22.cname, cred22.usr, cred22.pwd, cred22.ctype).credential_id
|
||||
|
||||
cred = cdb.get_credential(cred11.tenant, cred11_id)
|
||||
cred = cdb.get_credential(cred11_id)
|
||||
self._assert_equal(cred, cred11)
|
||||
cred = cdb.get_credential(cred21.tenant, cred21_id)
|
||||
cred = cdb.get_credential(cred21_id)
|
||||
self._assert_equal(cred, cred21)
|
||||
cred = cdb.get_credential(cred21.tenant, cred22_id)
|
||||
cred = cdb.get_credential(cred22_id)
|
||||
self._assert_equal(cred, cred22)
|
||||
|
||||
with testtools.ExpectedException(c_exc.CredentialNotFound):
|
||||
cdb.get_credential(cred11.tenant, "dummyCredentialId")
|
||||
with testtools.ExpectedException(c_exc.CredentialNotFound):
|
||||
cdb.get_credential(cred11.tenant, cred21_id)
|
||||
with testtools.ExpectedException(c_exc.CredentialNotFound):
|
||||
cdb.get_credential(cred21.tenant, cred11_id)
|
||||
cdb.get_credential("dummyCredentialId")
|
||||
|
||||
cred_all_t1 = cdb.get_all_credentials(cred11.tenant)
|
||||
self.assertEqual(len(cred_all_t1), 1)
|
||||
cred_all_t2 = cdb.get_all_credentials(cred21.tenant)
|
||||
self.assertEqual(len(cred_all_t2), 2)
|
||||
cred_all_t3 = cdb.get_all_credentials("dummyTenant")
|
||||
self.assertEqual(len(cred_all_t3), 0)
|
||||
cred_all_t1 = cdb.get_all_credentials()
|
||||
self.assertEqual(len(cred_all_t1), 3)
|
||||
|
||||
def test_credential_get_name(self):
|
||||
cred11 = self._cred_test_obj(1, 1)
|
||||
cred11_id = cdb.add_credential(
|
||||
cred11.tenant, cred11.cname, cred11.usr, cred11.pwd).credential_id
|
||||
cred11.cname, cred11.usr, cred11.pwd, cred11.ctype).credential_id
|
||||
cred21 = self._cred_test_obj(2, 1)
|
||||
cred21_id = cdb.add_credential(
|
||||
cred21.tenant, cred21.cname, cred21.usr, cred21.pwd).credential_id
|
||||
cred21.cname, cred21.usr, cred21.pwd, cred21.ctype).credential_id
|
||||
cred22 = self._cred_test_obj(2, 2)
|
||||
cred22_id = cdb.add_credential(
|
||||
cred22.tenant, cred22.cname, cred22.usr, cred22.pwd).credential_id
|
||||
cred22.cname, cred22.usr, cred22.pwd, cred22.ctype).credential_id
|
||||
self.assertNotEqual(cred11_id, cred21_id)
|
||||
self.assertNotEqual(cred11_id, cred22_id)
|
||||
self.assertNotEqual(cred21_id, cred22_id)
|
||||
|
||||
cred = cdb.get_credential_name(cred11.tenant, cred11.cname)
|
||||
cred = cdb.get_credential_name(cred11.cname)
|
||||
self._assert_equal(cred, cred11)
|
||||
cred = cdb.get_credential_name(cred21.tenant, cred21.cname)
|
||||
cred = cdb.get_credential_name(cred21.cname)
|
||||
self._assert_equal(cred, cred21)
|
||||
cred = cdb.get_credential_name(cred22.tenant, cred22.cname)
|
||||
cred = cdb.get_credential_name(cred22.cname)
|
||||
self._assert_equal(cred, cred22)
|
||||
|
||||
with testtools.ExpectedException(c_exc.CredentialNameNotFound):
|
||||
cdb.get_credential_name(cred11.tenant, "dummyCredentialName")
|
||||
with testtools.ExpectedException(c_exc.CredentialNameNotFound):
|
||||
cdb.get_credential_name(cred11.tenant, cred22.cname)
|
||||
cdb.get_credential_name("dummyCredentialName")
|
||||
|
||||
def test_credential_update(self):
|
||||
cred11 = self._cred_test_obj(1, 1)
|
||||
cred11_id = cdb.add_credential(
|
||||
cred11.tenant, cred11.cname, cred11.usr, cred11.pwd).credential_id
|
||||
cdb.update_credential(cred11.tenant, cred11_id)
|
||||
cred11.cname, cred11.usr, cred11.pwd, cred11.ctype).credential_id
|
||||
cdb.update_credential(cred11_id)
|
||||
new_usr = "new user name"
|
||||
new_pwd = "new password"
|
||||
new_credential = cdb.update_credential(
|
||||
cred11.tenant, cred11_id, new_usr, new_pwd)
|
||||
cred11_id, new_usr, new_pwd)
|
||||
expected_cred = self.CredObj(
|
||||
cred11.tenant, cred11.cname, new_usr, new_pwd)
|
||||
cred11.cname, new_usr, new_pwd, cred11.ctype)
|
||||
self._assert_equal(new_credential, expected_cred)
|
||||
new_credential = cdb.get_credential(cred11.tenant, cred11_id)
|
||||
new_credential = cdb.get_credential(cred11_id)
|
||||
self._assert_equal(new_credential, expected_cred)
|
||||
with testtools.ExpectedException(c_exc.CredentialNotFound):
|
||||
cdb.update_credential(
|
||||
cred11.tenant, "dummyCredentialId", new_usr, new_pwd)
|
||||
"dummyCredentialId", new_usr, new_pwd)
|
||||
|
@ -38,6 +38,7 @@ from neutron.plugins.openvswitch import ovs_db_v2
|
||||
from neutron.tests.unit import test_db_plugin
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NEXUS_PLUGIN = 'neutron.plugins.cisco.nexus.cisco_nexus_plugin_v2.NexusPlugin'
|
||||
|
||||
|
||||
class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||
@ -51,6 +52,10 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
||||
{'ncclient': self.mock_ncclient})
|
||||
self.patch_obj.start()
|
||||
|
||||
cisco_config.cfg.CONF.set_override('nexus_plugin', NEXUS_PLUGIN,
|
||||
'CISCO_PLUGINS')
|
||||
self.addCleanup(cisco_config.cfg.CONF.reset)
|
||||
|
||||
super(CiscoNetworkPluginV2TestCase, self).setUp(self._plugin_name)
|
||||
self.port_create_status = 'DOWN'
|
||||
self.addCleanup(self.patch_obj.stop)
|
||||
@ -107,6 +112,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
},
|
||||
cisco_config: {
|
||||
'CISCO': {'nexus_driver': nexus_driver},
|
||||
'CISCO_PLUGINS': {'nexus_plugin': NEXUS_PLUGIN},
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,12 +124,16 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
group)
|
||||
self.addCleanup(module.cfg.CONF.reset)
|
||||
|
||||
# TODO(Henry): add tests for other devices
|
||||
self.dev_id = 'NEXUS_SWITCH'
|
||||
self.switch_ip = '1.1.1.1'
|
||||
nexus_config = {(self.switch_ip, 'username'): 'admin',
|
||||
(self.switch_ip, 'password'): 'mySecretPassword',
|
||||
(self.switch_ip, 'ssh_port'): 22,
|
||||
(self.switch_ip, 'testhost'): '1/1'}
|
||||
mock.patch.dict(cisco_config.nexus_dictionary, nexus_config).start()
|
||||
nexus_config = {
|
||||
(self.dev_id, self.switch_ip, 'username'): 'admin',
|
||||
(self.dev_id, self.switch_ip, 'password'): 'mySecretPassword',
|
||||
(self.dev_id, self.switch_ip, 'ssh_port'): 22,
|
||||
(self.dev_id, self.switch_ip, 'testhost'): '1/1',
|
||||
}
|
||||
mock.patch.dict(cisco_config.device_dictionary, nexus_config).start()
|
||||
|
||||
patches = {
|
||||
'_should_call_create_net': True,
|
||||
|
Loading…
Reference in New Issue
Block a user