Merge "Add support for the Nexus 1000V into the Cisco Plugin."

This commit is contained in:
Jenkins 2013-08-12 21:47:15 +00:00 committed by Gerrit Code Review
commit f3c016d76a
27 changed files with 4806 additions and 333 deletions

View File

@ -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>

View File

@ -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": ""
}

View File

@ -43,11 +43,8 @@ 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.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')

View File

@ -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)
)

View File

@ -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'

View File

@ -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)

View File

@ -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.")

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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)

View File

@ -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).
return (session.query(network_models_v2.QoS).
filter_by(tenant_id=tenant_id).
filter_by(qos_id=qos_id).one())
return qos
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).all())
def get_credential(credential_id):
"""Lists the creds for given a cred_id."""
session = db.get_session()
try:
return (session.query(network_models_v2.Credential).
filter_by(tenant_id=tenant_id).all())
def get_credential(tenant_id, credential_id):
"""Lists the creds for given a cred_id and tenant_id."""
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())
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)

View File

@ -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):

View File

@ -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)]

View 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 {}

View 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

View 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

View File

@ -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,6 +72,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
for key in conf.CISCO_PLUGINS.keys():
plugin_obj = conf.CISCO_PLUGINS[key]
if plugin_obj is not None:
self._plugins[key] = importutils.import_object(plugin_obj)
LOG.debug(_("Loaded device plugin %s\n"),
conf.CISCO_PLUGINS[key])
@ -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,
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
n_args)
return nexus_output
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,
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(),
n_args)
return ovs_output
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,
return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
self._func_name(),
n_args)
return nexus_output
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,
return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
self._func_name(),
n_args)
return ovs_output
def create_subnet(self, context, subnet):
"""For this model this method will be delegated to vswitch plugin."""

View 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.
#

View 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

File diff suppressed because it is too large Load Diff

View File

@ -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):

View File

@ -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 = {}

View 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.
#

View 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'])

View 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()

View File

@ -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)

View File

@ -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,