# 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>
"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": ""
}
if not migration.should_run(active_plugins, migration_for_plugins):
return
- if 'credentials' in sa.MetaData().tables:
- op.rename_table('credentials', 'cisco_credentials')
- if 'nexusport_bindings' in sa.MetaData().tables:
- op.rename_table('nexusport_bindings', 'cisco_nexusport_bindings')
- if 'qoss' in sa.MetaData().tables:
- op.rename_table('qoss', 'cisco_qos_policies')
+ op.rename_table('credentials', 'cisco_credentials')
+ op.rename_table('nexusport_bindings', 'cisco_nexusport_bindings')
+ op.rename_table('qoss', 'cisco_qos_policies')
op.drop_table('cisco_vlan_ids')
sa.PrimaryKeyConstraint('vlan_id'),
)
- if 'cisco_credentials' in sa.MetaData().tables:
- op.rename_table('cisco_credentials', 'credentials')
- if 'cisco_nexusport_bindings' in sa.MetaData().tables:
- op.rename_table('cisco_nexusport_bindings', 'nexusport_bindings')
- if 'cisco_qos_policies' in sa.MetaData().tables:
- op.rename_table('cisco_qos_policies', 'qoss')
+ op.rename_table('cisco_credentials', 'credentials')
+ op.rename_table('cisco_nexusport_bindings', 'nexusport_bindings')
+ op.rename_table('cisco_qos_policies', 'qoss')
--- /dev/null
+# 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)
+ )
CREDENTIAL_NAME = 'credential_name'
CREDENTIAL_USERNAME = 'user_name'
CREDENTIAL_PASSWORD = 'password'
+CREDENTIAL_TYPE = 'type'
MASKED_PASSWORD = '********'
USERNAME = 'username'
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'
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)
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):
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())
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.")
help=_("Nexus Switch to use")),
]
-
cisco_opts = [
cfg.StrOpt('vlan_name_prefix', default='q-',
help=_("VLAN Name prefix")),
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()
-
- def _create_nexus_dictionary(self):
- """Create the Nexus dictionary.
+ self._create_device_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()
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
--- /dev/null
+# 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: Aruna Kushwaha, Cisco Systems Inc.
+# @author: Abhishek Raut, Cisco Systems Inc.
+# @author: Rudrajit Tapadar, Cisco Systems Inc.
+# @author: Sergey Sudakovich, Cisco Systems Inc.
+
+import netaddr
+import re
+from sqlalchemy.orm import exc
+from sqlalchemy.sql import and_
+
+from neutron.common import exceptions as q_exc
+import neutron.db.api as db
+from neutron.db import models_v2
+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_exceptions as c_exc
+from neutron.plugins.cisco.db import n1kv_models_v2
+
+LOG = logging.getLogger(__name__)
+
+
+def initialize():
+ """Initialize the database."""
+ db.configure_db()
+
+
+def get_network_binding(db_session, network_id):
+ """
+ Retrieve network binding.
+
+ :param db_session: database session
+ :param network_id: UUID representing the network whose binding is
+ to fetch
+ :returns: binding object
+ """
+ try:
+ return (db_session.query(n1kv_models_v2.N1kvNetworkBinding).
+ filter_by(network_id=network_id).
+ one())
+ except exc.NoResultFound:
+ raise c_exc.NetworkBindingNotFound(network_id=network_id)
+
+
+def add_network_binding(db_session, network_id, network_type,
+ physical_network, segmentation_id,
+ multicast_ip, network_profile_id):
+ """
+ Create network binding.
+
+ :param db_session: database session
+ :param network_id: UUID representing the network
+ :param network_type: string representing type of network (VLAN or VXLAN)
+ :param physical_network: Only applicable for VLAN networks. It
+ represents a L2 Domain
+ :param segmentation_id: integer representing VLAN or VXLAN ID
+ :param multicast_ip: VXLAN technology needs a multicast IP to be associated
+ with every VXLAN ID to deal with broadcast packets. A
+ single multicast IP can be shared by multiple VXLAN
+ IDs.
+ :param network_profile_id: network profile ID based on which this network
+ is created
+ """
+ with db_session.begin(subtransactions=True):
+ binding = n1kv_models_v2.N1kvNetworkBinding(
+ network_id=network_id,
+ network_type=network_type,
+ physical_network=physical_network,
+ segmentation_id=segmentation_id,
+ multicast_ip=multicast_ip,
+ profile_id=network_profile_id)
+ db_session.add(binding)
+
+
+def get_segment_range(network_profile):
+ """
+ Get the segment range min and max for a network profile.
+
+ :params network_profile: object of type network profile
+ :returns: integer values representing minimum and maximum segment
+ range value
+ """
+ # Sort the range to ensure min, max is in order
+ seg_min, seg_max = sorted(
+ int(i) for i in network_profile.segment_range.split('-'))
+ LOG.debug(_("seg_min %(seg_min)s, seg_max %(seg_max)s"),
+ {'seg_min': seg_min, 'seg_max': seg_max})
+ return seg_min, seg_max
+
+
+def get_multicast_ip(network_profile):
+ """
+ Retreive a multicast ip from the defined pool.
+
+ :params network_profile: object of type network profile
+ :returns: string representing multicast IP
+ """
+ # Round robin multicast ip allocation
+ min_ip, max_ip = _get_multicast_ip_range(network_profile)
+ addr_list = list((netaddr.iter_iprange(min_ip, max_ip)))
+ mul_ip_str = str(addr_list[network_profile.multicast_ip_index])
+
+ network_profile.multicast_ip_index += 1
+ if network_profile.multicast_ip_index == len(addr_list):
+ network_profile.multicast_ip_index = 0
+ return mul_ip_str
+
+
+def _get_multicast_ip_range(network_profile):
+ """
+ Helper method to retrieve minimum and maximum multicast ip.
+
+ :params network_profile: object of type network profile
+ :returns: two strings representing minimum multicast ip and
+ maximum multicast ip
+ """
+ # Assumption: ip range belongs to the same subnet
+ # Assumption: ip range is already sorted
+ return network_profile.multicast_ip_range.split('-')
+
+
+def get_port_binding(db_session, port_id):
+ """
+ Retrieve port binding.
+
+ :param db_session: database session
+ :param port_id: UUID representing the port whose binding is to fetch
+ :returns: port binding object
+ """
+ try:
+ return (db_session.query(n1kv_models_v2.N1kvPortBinding).
+ filter_by(port_id=port_id).
+ one())
+ except exc.NoResultFound:
+ raise c_exc.PortBindingNotFound(port_id=port_id)
+
+
+def add_port_binding(db_session, port_id, policy_profile_id):
+ """
+ Create port binding.
+
+ Bind the port with policy profile.
+ :param db_session: database session
+ :param port_id: UUID of the port
+ :param policy_profile_id: UUID of the policy profile
+ """
+ with db_session.begin(subtransactions=True):
+ binding = n1kv_models_v2.N1kvPortBinding(port_id=port_id,
+ profile_id=policy_profile_id)
+ db_session.add(binding)
+
+
+def _get_sorted_vlan_ids(vlan_ranges):
+ """Return sorted allocatable VLAN IDs."""
+ vlan_ids = set()
+ for vlan_range in vlan_ranges:
+ vlan_ids |= set(xrange(vlan_range[0], vlan_range[1] + 1))
+ return sorted(vlan_ids)
+
+
+def sync_vlan_allocations(db_session, network_vlan_ranges):
+ """
+ Synchronize vlan_allocations table with configured VLAN ranges.
+
+ Sync the network profile range with the vlan_allocations table for each
+ physical network.
+ :param db_session: database session
+ :param network_vlan_ranges: dictionary of network vlan ranges with the
+ physical network name as key.
+ """
+
+ with db_session.begin():
+ # process vlan ranges for each physical network separately
+ for physical_network, vlan_ranges in network_vlan_ranges.items():
+
+ # determine current configured allocatable vlans for this
+ # physical network
+ vlan_ids = _get_sorted_vlan_ids(vlan_ranges)
+
+ # add missing allocatable vlans to table
+ for vlan_id in vlan_ids:
+ try:
+ alloc = get_vlan_allocation(db_session,
+ physical_network,
+ vlan_id)
+ except c_exc.VlanIDNotFound:
+ alloc = n1kv_models_v2.N1kvVlanAllocation(
+ physical_network=physical_network, vlan_id=vlan_id)
+ db_session.add(alloc)
+
+
+def delete_vlan_allocations(db_session, network_vlan_ranges):
+ """
+ Delete vlan_allocations for deleted network profile range.
+
+ :param db_session: database session
+ :param network_vlan_ranges: dictionary of network vlan ranges with the
+ physical network name as key.
+ """
+
+ with db_session.begin():
+ # process vlan ranges for each physical network separately
+ for physical_network, vlan_ranges in network_vlan_ranges.items():
+ # Determine the set of vlan ids which need to be deleted.
+ vlan_ids = _get_sorted_vlan_ids(vlan_ranges)
+ allocs = (db_session.query(n1kv_models_v2.N1kvVlanAllocation).
+ filter_by(physical_network=physical_network).
+ filter_by(allocated=False))
+ for alloc in allocs:
+ if alloc.vlan_id in vlan_ids:
+ LOG.debug(_("Removing vlan %(vlan)s on physical "
+ "network %(network)s from pool"),
+ {"vlan": alloc.vlan_id,
+ "network": physical_network})
+ db_session.delete(alloc)
+
+
+def get_vlan_allocation(db_session, physical_network, vlan_id):
+ """
+ Retrieve vlan allocation.
+
+ :param db_session: database session
+ :param physical network: string name for the physical network
+ :param vlan_id: integer representing the VLAN ID.
+ :returns: allocation object for given physical network and VLAN ID
+ """
+ try:
+ return (db_session.query(n1kv_models_v2.N1kvVlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).one())
+ except exc.NoResultFound:
+ raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
+
+
+def reserve_vlan(db_session, network_profile):
+ """
+ Reserve a VLAN ID within the range of the network profile.
+
+ :param db_session: database session
+ :param network_profile: network profile object
+ """
+ seg_min, seg_max = get_segment_range(network_profile)
+ segment_type = c_const.NETWORK_TYPE_VLAN
+
+ with db_session.begin(subtransactions=True):
+ alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation).
+ filter(and_(
+ n1kv_models_v2.N1kvVlanAllocation.vlan_id >= seg_min,
+ n1kv_models_v2.N1kvVlanAllocation.vlan_id <= seg_max,
+ n1kv_models_v2.N1kvVlanAllocation.allocated == False)
+ )).first()
+ if alloc:
+ segment_id = alloc.vlan_id
+ physical_network = alloc.physical_network
+ alloc.allocated = True
+ return (physical_network, segment_type, segment_id, "0.0.0.0")
+ raise q_exc.NoNetworkAvailable()
+
+
+def reserve_vxlan(db_session, network_profile):
+ """
+ Reserve a VXLAN ID within the range of the network profile.
+
+ :param db_session: database session
+ :param network_profile: network profile object
+ """
+ seg_min, seg_max = get_segment_range(network_profile)
+ segment_type = c_const.NETWORK_TYPE_VXLAN
+ physical_network = ""
+
+ with db_session.begin(subtransactions=True):
+ alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation).
+ filter(and_(
+ n1kv_models_v2.N1kvVxlanAllocation.vxlan_id >=
+ seg_min,
+ n1kv_models_v2.N1kvVxlanAllocation.vxlan_id <=
+ seg_max,
+ n1kv_models_v2.N1kvVxlanAllocation.allocated == False)
+ ).first())
+ if alloc:
+ segment_id = alloc.vxlan_id
+ alloc.allocated = True
+ return (physical_network, segment_type,
+ segment_id, get_multicast_ip(network_profile))
+ raise q_exc.NoNetworkAvailable()
+
+
+def alloc_network(db_session, network_profile_id):
+ """
+ Allocate network using first available free segment ID in segment range.
+
+ :param db_session: database session
+ :param network_profile_id: UUID representing the network profile
+ """
+ with db_session.begin(subtransactions=True):
+ network_profile = get_network_profile(db_session,
+ network_profile_id)
+ try:
+ if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN:
+ return reserve_vlan(db_session, network_profile)
+ else:
+ return reserve_vxlan(db_session, network_profile)
+ except q_exc.NoNetworkAvailable:
+ raise c_exc.NoMoreNetworkSegments(
+ network_profile_name=network_profile.name)
+
+
+def reserve_specific_vlan(db_session, physical_network, vlan_id):
+ """
+ Reserve a specific VLAN ID for the network.
+
+ :param db_session: database session
+ :param physical_network: string representing the name of physical network
+ :param vlan_id: integer value of the segmentation ID to be reserved
+ """
+ with db_session.begin(subtransactions=True):
+ try:
+ alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ if alloc.allocated:
+ if vlan_id == c_const.FLAT_VLAN_ID:
+ raise q_exc.FlatNetworkInUse(
+ physical_network=physical_network)
+ else:
+ raise q_exc.VlanIdInUse(vlan_id=vlan_id,
+ physical_network=physical_network)
+ LOG.debug(_("Reserving specific vlan %(vlan)s on physical "
+ "network %(network)s from pool"),
+ {"vlan": vlan_id, "network": physical_network})
+ except exc.NoResultFound:
+ LOG.debug(_("Reserving specific vlan %(vlan)s on physical "
+ "network %(network)s outside pool"),
+ {"vlan": vlan_id, "network": physical_network})
+ alloc = n1kv_models_v2.N1kvVlanAllocation(
+ physical_network=physical_network, vlan_id=vlan_id)
+ db_session.add(alloc)
+ alloc.allocated = True
+
+
+def release_vlan(db_session, physical_network, vlan_id, network_vlan_ranges):
+ """
+ Release a given VLAN ID.
+
+ :param db_session: database session
+ :param physical_network: string representing the name of physical network
+ :param vlan_id: integer value of the segmentation ID to be released
+ :param network_vlan_ranges: dictionary of network vlan ranges with the
+ physical network name as key.
+ """
+ with db_session.begin(subtransactions=True):
+ try:
+ alloc = (db_session.query(n1kv_models_v2.N1kvVlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ alloc.allocated = False
+ for vlan_range in network_vlan_ranges.get(physical_network, []):
+ if vlan_range[0] <= vlan_id <= vlan_range[1]:
+ msg = _("Releasing vlan %(vlan)s on physical "
+ "network %(network)s to pool")
+ break
+ else:
+ db_session.delete(alloc)
+ msg = _("Releasing vlan %(vlan)s on physical "
+ "network %(network)s outside pool")
+ LOG.debug(msg, {"vlan": vlan_id, "network": physical_network})
+ except exc.NoResultFound:
+ LOG.warning(_("vlan_id %(vlan)s on physical network %(network)s "
+ "not found"),
+ {"vlan": vlan_id, "network": physical_network})
+
+
+def _get_sorted_vxlan_ids(vxlan_id_ranges):
+ """Return sorted VXLAN IDs."""
+ vxlan_ids = set()
+ for vxlan_min, vxlan_max in vxlan_id_ranges:
+ if vxlan_max + 1 - vxlan_min > c_const.MAX_VXLAN_RANGE:
+ LOG.error(_("Skipping unreasonable vxlan ID range %(vxlan_min)s - "
+ "%(vxlan_max)s"),
+ {"vxlan_min": vxlan_min, "vxlan_max": vxlan_max})
+ else:
+ vxlan_ids |= set(xrange(vxlan_min, vxlan_max + 1))
+ return sorted(vxlan_ids)
+
+
+def sync_vxlan_allocations(db_session, vxlan_id_ranges):
+ """
+ Synchronize vxlan_allocations table with configured vxlan ranges.
+
+ :param db_session: database session
+ :param vxlan_id_ranges: list of segment range tuples
+ """
+
+ vxlan_ids = _get_sorted_vxlan_ids(vxlan_id_ranges)
+ with db_session.begin():
+ for vxlan_id in vxlan_ids:
+ alloc = get_vxlan_allocation(db_session, vxlan_id)
+ if not alloc:
+ alloc = n1kv_models_v2.N1kvVxlanAllocation(vxlan_id=vxlan_id)
+ db_session.add(alloc)
+
+
+def delete_vxlan_allocations(db_session, vxlan_id_ranges):
+ """
+ Delete vxlan_allocations for deleted network profile range.
+
+ :param db_session: database session
+ :param vxlan_id_ranges: list of segment range tuples
+ """
+ vxlan_ids = _get_sorted_vxlan_ids(vxlan_id_ranges)
+ with db_session.begin():
+ allocs = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation).
+ filter_by(allocated=False))
+ for alloc in allocs:
+ if alloc.vxlan_id in vxlan_ids:
+ LOG.debug(_("Removing vxlan %s from pool") %
+ alloc.vxlan_id)
+ db_session.delete(alloc)
+
+
+def get_vxlan_allocation(db_session, vxlan_id):
+ """
+ Retrieve VXLAN allocation for the given VXLAN ID.
+
+ :param db_session: database session
+ :param vxlan_id: integer value representing the segmentation ID
+ :returns: allocation object
+ """
+ return (db_session.query(n1kv_models_v2.N1kvVxlanAllocation).
+ filter_by(vxlan_id=vxlan_id).first())
+
+
+def reserve_specific_vxlan(db_session, vxlan_id):
+ """
+ Reserve a specific VXLAN ID.
+
+ :param db_session: database session
+ :param vxlan_id: integer value representing the segmentation ID
+ """
+ with db_session.begin(subtransactions=True):
+ try:
+ alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation).
+ filter_by(vxlan_id=vxlan_id).
+ one())
+ if alloc.allocated:
+ raise c_exc.VxlanIdInUse(vxlan_id=vxlan_id)
+ LOG.debug(_("Reserving specific vxlan %s from pool") % vxlan_id)
+ except exc.NoResultFound:
+ LOG.debug(_("Reserving specific vxlan %s outside pool") % vxlan_id)
+ alloc = n1kv_models_v2.N1kvVxlanAllocation(vxlan_id=vxlan_id)
+ db_session.add(alloc)
+ alloc.allocated = True
+
+
+def release_vxlan(db_session, vxlan_id, vxlan_id_ranges):
+ """
+ Release a given VXLAN ID.
+
+ :param db_session: database session
+ :param vxlan_id: integer value representing the segmentation ID
+ :param vxlan_id_ranges: list of the segment range tuples.
+ """
+ with db_session.begin(subtransactions=True):
+ try:
+ alloc = (db_session.query(n1kv_models_v2.N1kvVxlanAllocation).
+ filter_by(vxlan_id=vxlan_id).
+ one())
+ alloc.allocated = False
+ for vxlan_id_range in vxlan_id_ranges:
+ if vxlan_id_range[0] <= vxlan_id <= vxlan_id_range[1]:
+ msg = _("Releasing vxlan %s to pool")
+ break
+ else:
+ db_session.delete(alloc)
+ msg = _("Releasing vxlan %s outside pool")
+ LOG.debug(msg, vxlan_id)
+ except exc.NoResultFound:
+ LOG.warning(_("vxlan_id %s not found"), vxlan_id)
+
+
+def set_port_status(port_id, status):
+ """
+ Set the status of the port.
+
+ :param port_id: UUID representing the port
+ :param status: string representing the new status
+ """
+ db_session = db.get_session()
+ try:
+ port = db_session.query(models_v2.Port).filter_by(id=port_id).one()
+ port.status = status
+ except exc.NoResultFound:
+ raise q_exc.PortNotFound(port_id=port_id)
+
+
+def get_vm_network(db_session, policy_profile_id, network_id):
+ """
+ Retrieve a vm_network based on policy profile and network id.
+
+ :param db_session: database session
+ :param policy_profile_id: UUID representing policy profile
+ :param network_id: UUID representing network
+ :returns: VM network object
+ """
+ try:
+ return (db_session.query(n1kv_models_v2.N1kVmNetwork).
+ filter_by(profile_id=policy_profile_id,
+ network_id=network_id).one())
+ except exc.NoResultFound:
+ name = (c_const.VM_NETWORK_NAME_PREFIX + policy_profile_id
+ + "_" + network_id)
+ raise c_exc.VMNetworkNotFound(name=name)
+
+
+def add_vm_network(db_session,
+ name,
+ policy_profile_id,
+ network_id,
+ port_count):
+ """
+ Create a VM network.
+
+ Add a VM network for a unique combination of network and
+ policy profile. All ports having the same policy profile
+ on one network will be associated with one VM network.
+ :param db_session: database session
+ :param name: string representing the name of the VM network
+ :param policy_profile_id: UUID representing policy profile
+ :param network_id: UUID representing a network
+ :param port_count: integer representing the number of ports on vm network
+ """
+ with db_session.begin(subtransactions=True):
+ vm_network = n1kv_models_v2.N1kVmNetwork(
+ name=name,
+ profile_id=policy_profile_id,
+ network_id=network_id,
+ port_count=port_count)
+ db_session.add(vm_network)
+
+
+def update_vm_network_port_count(db_session, name, port_count):
+ """
+ Update a VM network with new port count.
+
+ :param db_session: database session
+ :param name: string representing the name of the VM network
+ :param port_count: integer representing the number of ports on VM network
+ """
+ try:
+ with db_session.begin(subtransactions=True):
+ vm_network = (db_session.query(n1kv_models_v2.N1kVmNetwork).
+ filter_by(name=name).one())
+ if port_count is not None:
+ vm_network.port_count = port_count
+ return vm_network
+ except exc.NoResultFound:
+ raise c_exc.VMNetworkNotFound(name=name)
+
+
+def delete_vm_network(db_session, policy_profile_id, network_id):
+ """
+ Delete a VM network.
+
+ :param db_session: database session
+ :param policy_profile_id: UUID representing a policy profile
+ :param network_id: UUID representing a network
+ :returns: deleted VM network object
+ """
+ with db_session.begin(subtransactions=True):
+ try:
+ vm_network = get_vm_network(db_session,
+ policy_profile_id,
+ network_id)
+ db_session.delete(vm_network)
+ db_session.query(n1kv_models_v2.N1kVmNetwork).filter_by(
+ name=vm_network["name"]).delete()
+ return vm_network
+ except exc.NoResultFound:
+ name = (c_const.VM_NETWORK_NAME_PREFIX + policy_profile_id +
+ "_" + network_id)
+ raise c_exc.VMNetworkNotFound(name=name)
+
+
+def create_network_profile(db_session, network_profile):
+ """Create a network profile."""
+ LOG.debug(_("create_network_profile()"))
+ with db_session.begin(subtransactions=True):
+ kwargs = {"name": network_profile["name"],
+ "segment_type": network_profile["segment_type"],
+ "segment_range": network_profile["segment_range"]}
+ if network_profile["segment_type"] == c_const.NETWORK_TYPE_VLAN:
+ kwargs["physical_network"] = network_profile["physical_network"]
+ elif network_profile["segment_type"] == c_const.NETWORK_TYPE_VXLAN:
+ kwargs["multicast_ip_index"] = 0
+ kwargs["multicast_ip_range"] = network_profile[
+ "multicast_ip_range"]
+ net_profile = n1kv_models_v2.NetworkProfile(**kwargs)
+ db_session.add(net_profile)
+ return net_profile
+
+
+def delete_network_profile(db_session, id):
+ """Delete Network Profile."""
+ LOG.debug(_("delete_network_profile()"))
+ with db_session.begin(subtransactions=True):
+ try:
+ network_profile = get_network_profile(db_session, id)
+ db_session.delete(network_profile)
+ (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter_by(profile_id=id).delete())
+ return network_profile
+ except exc.NoResultFound:
+ raise c_exc.ProfileTenantBindingNotFound(profile_id=id)
+
+
+def update_network_profile(db_session, id, network_profile):
+ """Update Network Profile."""
+ LOG.debug(_("update_network_profile()"))
+ with db_session.begin(subtransactions=True):
+ profile = get_network_profile(db_session, id)
+ profile.update(network_profile)
+ return profile
+
+
+def get_network_profile(db_session, id):
+ """Get Network Profile."""
+ LOG.debug(_("get_network_profile()"))
+ try:
+ return db_session.query(
+ n1kv_models_v2.NetworkProfile).filter_by(id=id).one()
+ except exc.NoResultFound:
+ raise c_exc.NetworkProfileIdNotFound(profile_id=id)
+
+
+def _get_network_profiles(**kwargs):
+ """
+ Retrieve all network profiles.
+
+ Get Network Profiles on a particular physical network, if physical
+ network is specified. If no physical network is specified, return
+ all network profiles.
+ """
+ db_session = db.get_session()
+ if "physical_network" in kwargs:
+ return (db_session.query(n1kv_models_v2.NetworkProfile).
+ filter_by(physical_network=kwargs["physical_network"]))
+ else:
+ return db_session.query(n1kv_models_v2.NetworkProfile)
+
+
+def create_policy_profile(policy_profile):
+ """Create Policy Profile."""
+ LOG.debug(_("create_policy_profile()"))
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ p_profile = n1kv_models_v2.PolicyProfile(id=policy_profile["id"],
+ name=policy_profile["name"])
+ db_session.add(p_profile)
+ return p_profile
+
+
+def delete_policy_profile(id):
+ """Delete Policy Profile."""
+ LOG.debug(_("delete_policy_profile()"))
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ policy_profile = get_policy_profile(db_session, id)
+ db_session.delete(policy_profile)
+
+
+def update_policy_profile(db_session, id, policy_profile):
+ """Update a policy profile."""
+ LOG.debug(_("update_policy_profile()"))
+ with db_session.begin(subtransactions=True):
+ _profile = get_policy_profile(db_session, id)
+ _profile.update(policy_profile)
+ return _profile
+
+
+def get_policy_profile(db_session, id):
+ """Get Policy Profile."""
+ LOG.debug(_("get_policy_profile()"))
+ try:
+ return db_session.query(
+ n1kv_models_v2.PolicyProfile).filter_by(id=id).one()
+ except exc.NoResultFound:
+ raise c_exc.PolicyProfileIdNotFound(profile_id=id)
+
+
+def create_profile_binding(tenant_id, profile_id, profile_type):
+ """Create Network/Policy Profile association with a tenant."""
+ if profile_type not in ["network", "policy"]:
+ raise q_exc.NeutronException("Invalid profile type")
+
+ if _profile_binding_exists(tenant_id, profile_id, profile_type):
+ return get_profile_binding(tenant_id, profile_id)
+
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ binding = n1kv_models_v2.ProfileBinding(profile_type=profile_type,
+ profile_id=profile_id,
+ tenant_id=tenant_id)
+ db_session.add(binding)
+ return binding
+
+
+def _profile_binding_exists(tenant_id, profile_id, profile_type):
+ db_session = db.get_session()
+ LOG.debug(_("_profile_binding_exists()"))
+ return (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter_by(tenant_id=tenant_id, profile_id=profile_id,
+ profile_type=profile_type).first())
+
+
+def _get_profile_binding(tenant_id, profile_id):
+ LOG.debug(_("_get_profile_binding"))
+ db_session = db.get_session()
+ binding = db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
+ tenant_id=tenant_id, profile_id=profile_id).one()
+ return binding
+
+
+def get_profile_binding(tenant_id, profile_id):
+ """Get Network/Policy Profile - Tenant binding."""
+ LOG.debug(_("get_profile_binding()"))
+ try:
+ return _get_profile_binding(tenant_id, profile_id)
+ except exc.NoResultFound:
+ c_exc.ProfileTenantBindingNotFound(profile_id=profile_id)
+
+
+def delete_profile_binding(tenant_id, profile_id):
+ """Delete Policy Binding."""
+ LOG.debug(_("delete_profile_binding()"))
+ db_session = db.get_session()
+ try:
+ binding = get_profile_binding(tenant_id, profile_id)
+ with db_session.begin(subtransactions=True):
+ db_session.delete(binding)
+ except c_exc.ProfileTenantBindingNotFound:
+ LOG.debug(_("Profile-Tenant binding missing for profile ID "
+ "%(profile_id)s and tenant ID %(tenant_id)") %
+ {"profile_id": profile_id, "tenant_id": tenant_id})
+ return
+
+
+def _get_profile_bindings(profile_type=None):
+ """
+ Retrieve a list of profile bindings.
+
+ Get all profile-tenant bindings based on profile type.
+ If profile type is None, return profile-tenant binding for all
+ profile types.
+ """
+ LOG.debug(_("_get_profile_bindings()"))
+ db_session = db.get_session()
+ if profile_type:
+ profile_bindings = (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter_by(profile_type=profile_type))
+ return profile_bindings
+ else:
+ return db_session.query(n1kv_models_v2.ProfileBinding)
+
+
+class NetworkProfile_db_mixin(object):
+
+ """Network Profile Mixin."""
+
+ def _replace_fake_tenant_id_with_real(self, context):
+ """
+ Replace default tenant-id with admin tenant-ids.
+
+ Default tenant-ids are populated in profile bindings when plugin is
+ initialized. Replace these tenant-ids with admin's tenant-id.
+ :param context: neutron api request context
+ """
+ if context.is_admin and context.tenant_id:
+ tenant_id = context.tenant_id
+ db_session = context.session
+ with db_session.begin(subtransactions=True):
+ (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter_by(tenant_id=c_const.TENANT_ID_NOT_SET).
+ update({'tenant_id': tenant_id}))
+
+ def _get_network_collection_for_tenant(self, db_session, model, tenant_id):
+ net_profile_ids = (db_session.query(n1kv_models_v2.ProfileBinding.
+ profile_id).
+ filter_by(tenant_id=tenant_id).
+ filter_by(profile_type=c_const.NETWORK))
+ network_profiles = (db_session.query(model).filter(model.id.in_(
+ pid[0] for pid in net_profile_ids)))
+ return [self._make_network_profile_dict(p) for p in network_profiles]
+
+ def _make_profile_bindings_dict(self, profile_binding, fields=None):
+ res = {"profile_id": profile_binding["profile_id"],
+ "tenant_id": profile_binding["tenant_id"]}
+ return self._fields(res, fields)
+
+ def _make_network_profile_dict(self, network_profile, fields=None):
+ res = {"id": network_profile["id"],
+ "name": network_profile["name"],
+ "segment_type": network_profile["segment_type"],
+ "segment_range": network_profile["segment_range"],
+ "multicast_ip_index": network_profile["multicast_ip_index"],
+ "multicast_ip_range": network_profile["multicast_ip_range"],
+ "physical_network": network_profile["physical_network"]}
+ return self._fields(res, fields)
+
+ def get_network_profile_bindings(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of profile bindings for network profiles.
+
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ profile bindings object. Values in this dictiontary are
+ an iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a profile
+ bindings dictionary. Only these fields will be returned
+ :returns: list of profile bindings
+ """
+ if context.is_admin:
+ profile_bindings = _get_profile_bindings(
+ profile_type=c_const.NETWORK)
+ return [self._make_profile_bindings_dict(pb)
+ for pb in profile_bindings]
+
+ def create_network_profile(self, context, network_profile):
+ """
+ Create a network profile.
+
+ :param context: neutron api request context
+ :param network_profile: network profile dictionary
+ :returns: network profile dictionary
+ """
+ self._replace_fake_tenant_id_with_real(context)
+ p = network_profile["network_profile"]
+ self._validate_network_profile_args(context, p)
+ net_profile = create_network_profile(context.session, p)
+ create_profile_binding(context.tenant_id,
+ net_profile.id,
+ c_const.NETWORK)
+ if p.get("add_tenant"):
+ self.add_network_profile_tenant(net_profile.id, p["add_tenant"])
+ return self._make_network_profile_dict(net_profile)
+
+ def delete_network_profile(self, context, id):
+ """
+ Delete a network profile.
+
+ :param context: neutron api request context
+ :param id: UUID representing network profile to delete
+ :returns: deleted network profile dictionary
+ """
+ _profile = delete_network_profile(context.session, id)
+ return self._make_network_profile_dict(_profile)
+
+ def update_network_profile(self, context, id, network_profile):
+ """
+ Update a network profile.
+
+ Add/remove network profile to tenant-id binding for the corresponding
+ options and if user is admin.
+ :param context: neutron api request context
+ :param id: UUID representing network profile to update
+ :param network_profile: network profile dictionary
+ :returns: updated network profile dictionary
+ """
+ p = network_profile["network_profile"]
+ if context.is_admin and "add_tenant" in p:
+ self.add_network_profile_tenant(id, p["add_tenant"])
+ return self._make_network_profile_dict(get_network_profile(
+ context.session, id))
+ elif context.is_admin and "remove_tenant" in p:
+ delete_profile_binding(p["remove_tenant"], id)
+ return self._make_network_profile_dict(get_network_profile(
+ context.session, id))
+ else:
+ return self._make_network_profile_dict(
+ update_network_profile(context.session, id, p))
+
+ def get_network_profile(self, context, id, fields=None):
+ """
+ Retrieve a network profile.
+
+ :param context: neutron api request context
+ :param id: UUID representing the network profile to retrieve
+ :params fields: a list of strings that are valid keys in a network
+ profile dictionary. Only these fields will be returned
+ :returns: network profile dictionary
+ """
+ try:
+ profile = get_network_profile(context.session, id)
+ except exc.NoResultFound:
+ raise c_exc.NetworkProfileIdNotFound(profile_id=id)
+ return self._make_network_profile_dict(profile, fields)
+
+ def get_network_profiles(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of all network profiles.
+
+ Retrieve all network profiles if tenant is admin. For a non-admin
+ tenant, retrieve all network profiles belonging to this tenant only.
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ network profile object. Values in this dictiontary are
+ an iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a network
+ profile dictionary. Only these fields will be returned
+ :returns: list of all network profiles
+ """
+ if context.is_admin:
+ return self._get_collection(context, n1kv_models_v2.NetworkProfile,
+ self._make_network_profile_dict,
+ filters=filters, fields=fields)
+ else:
+ return self._get_network_collection_for_tenant(context.session,
+ n1kv_models_v2.
+ NetworkProfile,
+ context.tenant_id)
+
+ def add_network_profile_tenant(self, network_profile_id, tenant_id):
+ """
+ Add a tenant to a network profile.
+
+ :param network_profile_id: UUID representing network profile
+ :param tenant_id: UUID representing the tenant
+ :returns: profile binding object
+ """
+ return create_profile_binding(tenant_id,
+ network_profile_id,
+ c_const.NETWORK)
+
+ def network_profile_exists(self, context, id):
+ """
+ Verify whether a network profile for given id exists.
+
+ :param context: neutron api request context
+ :param id: UUID representing network profile
+ :returns: true if network profile exist else False
+ """
+ try:
+ get_network_profile(context.session, id)
+ return True
+ except c_exc.NetworkProfileIdNotFound(profile_id=id):
+ return False
+
+ def _get_segment_range(self, data):
+ # Sort the range to ensure min, max is in order
+ return sorted(int(seg) for seg in data.split("-")[:2])
+
+ def _validate_network_profile_args(self, context, p):
+ """
+ Validate completeness of Nexus1000V network profile arguments.
+
+ :param context: neutron api request context
+ :param p: network profile object
+ """
+ self._validate_network_profile(p)
+ self._validate_segment_range_uniqueness(context, p)
+
+ def _validate_segment_range(self, network_profile):
+ """
+ Validate segment range values.
+
+ :param network_profile: network profile object
+ """
+ if not re.match(r"(\d+)\-(\d+)", network_profile["segment_range"]):
+ msg = _("invalid segment range. example range: 500-550")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ def _validate_network_profile(self, net_p):
+ """
+ Validate completeness of a network profile arguments.
+
+ :param net_p: network profile object
+ """
+ if any(net_p[arg] == "" for arg in ("segment_type", "segment_range")):
+ msg = _("arguments segment_type and segment_range missing"
+ " for network profile")
+ LOG.exception(msg)
+ raise q_exc.InvalidInput(error_message=msg)
+ _segment_type = net_p["segment_type"].lower()
+ if _segment_type not in [c_const.NETWORK_TYPE_VLAN,
+ c_const.NETWORK_TYPE_VXLAN]:
+ msg = _("segment_type should either be vlan or vxlan")
+ LOG.exception(msg)
+ raise q_exc.InvalidInput(error_message=msg)
+ self._validate_segment_range(net_p)
+ if _segment_type == c_const.NETWORK_TYPE_VLAN:
+ net_p["multicast_ip_range"] = "0.0.0.0"
+
+ def _validate_segment_range_uniqueness(self, context, net_p):
+ """
+ Validate that segment range doesn't overlap.
+
+ :param context: neutron api request context
+ :param net_p: network profile dictionary
+ """
+ segment_type = net_p["segment_type"].lower()
+ if segment_type == c_const.NETWORK_TYPE_VLAN:
+ profiles = _get_network_profiles(
+ physical_network=net_p["physical_network"])
+ elif segment_type == c_const.NETWORK_TYPE_VXLAN:
+ profiles = _get_network_profiles()
+ else:
+ # TODO(Abhishek): Handle this when we support other segment types
+ return
+ if profiles:
+ for prfl in profiles:
+ name = prfl.name
+ segment_range = prfl.segment_range
+ if net_p["name"] == name:
+ msg = (_("NetworkProfile name %s already exists"),
+ net_p["name"])
+ LOG.exception(msg)
+ raise q_exc.InvalidInput(error_message=msg)
+ seg_min, seg_max = self._get_segment_range(
+ net_p["segment_range"])
+ prfl_seg_min, prfl_seg_max = self._get_segment_range(
+ segment_range)
+ if ((prfl_seg_min <= seg_min <= prfl_seg_max) or
+ (prfl_seg_min <= seg_max <= prfl_seg_max) or
+ ((seg_min <= prfl_seg_min) and
+ (seg_max >= prfl_seg_max))):
+ msg = _("segment range overlaps with another profile")
+ LOG.exception(msg)
+ raise q_exc.InvalidInput(error_message=msg)
+
+
+class PolicyProfile_db_mixin(object):
+
+ """Policy Profile Mixin."""
+
+ def _get_policy_collection_for_tenant(self, db_session, model, tenant_id):
+ profile_ids = (db_session.query(n1kv_models_v2.
+ ProfileBinding.profile_id)
+ .filter_by(tenant_id=tenant_id).
+ filter_by(profile_type=c_const.POLICY).all())
+ profiles = db_session.query(model).filter(model.id.in_(
+ pid[0] for pid in profile_ids))
+ return [self._make_policy_profile_dict(p) for p in profiles]
+
+ def _make_policy_profile_dict(self, policy_profile, fields=None):
+ res = {"id": policy_profile["id"], "name": policy_profile["name"]}
+ return self._fields(res, fields)
+
+ def _make_profile_bindings_dict(self, profile_binding, fields=None):
+ res = {"profile_id": profile_binding["profile_id"],
+ "tenant_id": profile_binding["tenant_id"]}
+ return self._fields(res, fields)
+
+ def _policy_profile_exists(self, id):
+ db_session = db.get_session()
+ return (db_session.query(n1kv_models_v2.PolicyProfile).
+ filter_by(id=id).first())
+
+ def get_policy_profile(self, context, id, fields=None):
+ """
+ Retrieve a policy profile for the given UUID.
+
+ :param context: neutron api request context
+ :param id: UUID representing policy profile to fetch
+ :params fields: a list of strings that are valid keys in a policy
+ profile dictionary. Only these fields will be returned
+ :returns: policy profile dictionary
+ """
+ try:
+ profile = get_policy_profile(context.session, id)
+ except exc.NoResultFound:
+ raise c_exc.PolicyProfileIdNotFound(profile_id=id)
+ return self._make_policy_profile_dict(profile, fields)
+
+ def get_policy_profiles(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of policy profiles.
+
+ Retrieve all policy profiles if tenant is admin. For a non-admin
+ tenant, retrieve all policy profiles belonging to this tenant only.
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ policy profile object. Values in this dictiontary are
+ an iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a policy
+ profile dictionary. Only these fields will be returned
+ :returns: list of all policy profiles
+ """
+ if context.is_admin:
+ return self._get_collection(context, n1kv_models_v2.PolicyProfile,
+ self._make_policy_profile_dict,
+ filters=filters, fields=fields)
+ else:
+ return self._get_policy_collection_for_tenant(context.session,
+ n1kv_models_v2.
+ PolicyProfile,
+ context.tenant_id)
+
+ def get_policy_profile_bindings(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of profile bindings for policy profiles.
+
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ profile bindings object. Values in this dictiontary are
+ an iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a profile
+ bindings dictionary. Only these fields will be returned
+ :returns: list of profile bindings
+ """
+ if context.is_admin:
+ profile_bindings = _get_profile_bindings(
+ profile_type=c_const.POLICY)
+ return [self._make_profile_bindings_dict(pb)
+ for pb in profile_bindings]
+
+ def update_policy_profile(self, context, id, policy_profile):
+ """
+ Update a policy profile.
+
+ Add/remove policy profile to tenant-id binding for the corresponding
+ option and if user is admin.
+ :param context: neutron api request context
+ :param id: UUID representing policy profile to update
+ :param policy_profile: policy profile dictionary
+ :returns: updated policy profile dictionary
+ """
+ p = policy_profile["policy_profile"]
+ if context.is_admin and "add_tenant" in p:
+ self.add_policy_profile_tenant(id, p["add_tenant"])
+ return self._make_policy_profile_dict(get_policy_profile(
+ context.session, id))
+ elif context.is_admin and "remove_tenant" in p:
+ delete_profile_binding(p["remove_tenant"], id)
+ return self._make_policy_profile_dict(get_policy_profile(
+ context.session, id))
+ else:
+ return self._make_policy_profile_dict(
+ update_policy_profile(context.session, id, p))
+
+ def add_policy_profile_tenant(self, policy_profile_id, tenant_id):
+ """
+ Add a tenant to a policy profile binding.
+
+ :param policy_profile_id: UUID representing policy profile
+ :param tenant_id: UUID representing the tenant
+ :returns: profile binding object
+ """
+ return create_profile_binding(tenant_id,
+ policy_profile_id,
+ c_const.POLICY)
+
+ def remove_policy_profile_tenant(self, policy_profile_id, tenant_id):
+ """
+ Remove a tenant to a policy profile binding.
+
+ :param policy_profile_id: UUID representing policy profile
+ :param tenant_id: UUID representing the tenant
+ """
+ delete_profile_binding(tenant_id, policy_profile_id)
+
+ def _delete_policy_profile(self, policy_profile_id):
+ """Delete policy profile and associated binding."""
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ (db_session.query(n1kv_models_v2.PolicyProfile).
+ filter_by(id=policy_profile_id).delete())
+
+ def _get_policy_profile_by_name(self, name):
+ """
+ Retrieve policy profile based on name.
+
+ :param name: string representing the name for the policy profile
+ :returns: policy profile object
+ """
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ return (db_session.query(n1kv_models_v2.PolicyProfile).
+ filter_by(name=name).first())
+
+ def _remove_all_fake_policy_profiles(self):
+ """
+ Remove all policy profiles associated with fake tenant id.
+
+ This will find all Profile ID where tenant is not set yet - set A
+ and profiles where tenant was already set - set B
+ and remove what is in both and no tenant id set
+ """
+ db_session = db.get_session()
+ with db_session.begin(subtransactions=True):
+ a_set_q = (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter_by(tenant_id=c_const.TENANT_ID_NOT_SET,
+ profile_type=c_const.POLICY))
+ a_set = set(i.profile_id for i in a_set_q)
+ b_set_q = (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter(and_(n1kv_models_v2.ProfileBinding.
+ tenant_id != c_const.TENANT_ID_NOT_SET,
+ n1kv_models_v2.ProfileBinding.
+ profile_type == c_const.POLICY)))
+ b_set = set(i.profile_id for i in b_set_q)
+ (db_session.query(n1kv_models_v2.ProfileBinding).
+ filter(and_(n1kv_models_v2.ProfileBinding.profile_id.
+ in_(a_set & b_set), n1kv_models_v2.ProfileBinding.
+ tenant_id == c_const.TENANT_ID_NOT_SET)).
+ delete(synchronize_session="fetch"))
+
+ def _add_policy_profile(self,
+ policy_profile_name,
+ policy_profile_id,
+ tenant_id=None):
+ """
+ Add Policy profile and tenant binding.
+
+ :param policy_profile_name: string representing the name for the
+ policy profile
+ :param policy_profile_id: UUID representing the policy profile
+ :param tenant_id: UUID representing the tenant
+ """
+ policy_profile = {"id": policy_profile_id, "name": policy_profile_name}
+ tenant_id = tenant_id or c_const.TENANT_ID_NOT_SET
+ if not self._policy_profile_exists(policy_profile_id):
+ create_policy_profile(policy_profile)
+ create_profile_binding(tenant_id, policy_profile["id"], c_const.POLICY)
--- /dev/null
+# 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)
LOG.debug(_("get_qos() called"))
session = db.get_session()
try:
- qos = (session.query(network_models_v2.QoS).
- filter_by(tenant_id=tenant_id).
- filter_by(qos_id=qos_id).one())
- return qos
+ return (session.query(network_models_v2.QoS).
+ filter_by(tenant_id=tenant_id).
+ filter_by(qos_id=qos_id).one())
except exc.NoResultFound:
raise c_exc.QosNotFound(qos_id=qos_id,
tenant_id=tenant_id)
tenant_id=tenant_id)
-def get_all_credentials(tenant_id):
+def get_all_credentials():
"""Lists all the creds for a tenant."""
session = db.get_session()
- return (session.query(network_models_v2.Credential).
- filter_by(tenant_id=tenant_id).all())
+ return (session.query(network_models_v2.Credential).all())
-def get_credential(tenant_id, credential_id):
- """Lists the creds for given a cred_id and tenant_id."""
+def get_credential(credential_id):
+ """Lists the creds for given a cred_id."""
session = db.get_session()
try:
- cred = (session.query(network_models_v2.Credential).
- filter_by(tenant_id=tenant_id).
+ return (session.query(network_models_v2.Credential).
filter_by(credential_id=credential_id).one())
- return cred
except exc.NoResultFound:
- raise c_exc.CredentialNotFound(credential_id=credential_id,
- tenant_id=tenant_id)
+ raise c_exc.CredentialNotFound(credential_id=credential_id)
-def get_credential_name(tenant_id, credential_name):
- """Lists the creds for given a cred_name and tenant_id."""
+def get_credential_name(credential_name):
+ """Lists the creds for given a cred_name."""
session = db.get_session()
try:
- cred = (session.query(network_models_v2.Credential).
- filter_by(tenant_id=tenant_id).
+ return (session.query(network_models_v2.Credential).
filter_by(credential_name=credential_name).one())
- return cred
except exc.NoResultFound:
- raise c_exc.CredentialNameNotFound(credential_name=credential_name,
- tenant_id=tenant_id)
+ raise c_exc.CredentialNameNotFound(credential_name=credential_name)
-def add_credential(tenant_id, credential_name, user_name, password):
- """Adds a qos to tenant association."""
+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())
- 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()
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
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):
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)
__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):
# 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
# under the License.
#
# @author: Ying Liu, Cisco Systems, Inc.
-#
-
-from webob import exc
+# @author: Abhishek Raut, Cisco Systems, Inc
-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)]
--- /dev/null
+# 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 {}
--- /dev/null
+# 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
--- /dev/null
+# 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
'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.
for key in conf.CISCO_PLUGINS.keys():
plugin_obj = conf.CISCO_PLUGINS[key]
- self._plugins[key] = importutils.import_object(plugin_obj)
- LOG.debug(_("Loaded device plugin %s\n"),
- conf.CISCO_PLUGINS[key])
+ if plugin_obj is not None:
+ self._plugins[key] = importutils.import_object(plugin_obj)
+ LOG.debug(_("Loaded device plugin %s\n"),
+ conf.CISCO_PLUGINS[key])
if ((const.VSWITCH_PLUGIN in self._plugins) and
hasattr(self._plugins[const.VSWITCH_PLUGIN],
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
vlan_name = conf.CISCO.vlan_name_prefix + str(vlan_id)
n_args = [vlan_name, vlan_id, subnet['id'], gateway_ip, router_id]
- nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
- self._func_name(),
- n_args)
- return nexus_output
+ return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+ self._func_name(),
+ n_args)
else:
LOG.debug(_("No Nexus plugin, sending to vswitch"))
n_args = [context, router_id, interface_info]
- ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
- self._func_name(),
- n_args)
- return ovs_output
+ return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+ self._func_name(),
+ n_args)
def remove_router_interface(self, context, router_id, interface_info):
"""Remove a router interface.
vlan_id = self._get_segmentation_id(network_id)
n_args = [vlan_id, router_id]
- nexus_output = self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
- self._func_name(),
- n_args)
- return nexus_output
+ return self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
+ self._func_name(),
+ n_args)
else:
LOG.debug(_("No Nexus plugin, sending to vswitch"))
n_args = [context, router_id, interface_info]
- ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
- self._func_name(),
- n_args)
- return ovs_output
+ return self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
+ self._func_name(),
+ n_args)
def create_subnet(self, context, subnet):
"""For this model this method will be delegated to vswitch plugin."""
--- /dev/null
+# 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.
+#
--- /dev/null
+# 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
--- /dev/null
+# 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: Aruna Kushwaha, Cisco Systems, Inc.
+# @author: Rudrajit Tapadar, Cisco Systems, Inc.
+# @author: Abhishek Raut, Cisco Systems, Inc.
+# @author: Sergey Sudakovich, Cisco Systems, Inc.
+
+import eventlet
+
+from oslo.config import cfg as q_conf
+
+from neutron.agent import securitygroups_rpc as sg_rpc
+from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
+from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
+from neutron.api.v2 import attributes
+from neutron.common import exceptions as q_exc
+from neutron.common import rpc as q_rpc
+from neutron.common import topics
+from neutron.db import agents_db
+from neutron.db import agentschedulers_db
+from neutron.db import db_base_plugin_v2
+from neutron.db import dhcp_rpc_base
+from neutron.db import l3_db
+from neutron.db import l3_rpc_base
+from neutron.db import securitygroups_rpc_base as sg_db_rpc
+from neutron.extensions import providernet
+from neutron.openstack.common import log as logging
+from neutron.openstack.common import rpc
+from neutron.openstack.common.rpc import proxy
+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
+from neutron.plugins.cisco.common import config as c_conf
+from neutron.plugins.cisco.db import n1kv_db_v2
+from neutron.plugins.cisco.db import network_db_v2
+from neutron.plugins.cisco.extensions import n1kv_profile
+from neutron.plugins.cisco.n1kv import n1kv_client
+from neutron import policy
+
+
+LOG = logging.getLogger(__name__)
+
+
+class N1kvRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
+ l3_rpc_base.L3RpcCallbackMixin,
+ sg_db_rpc.SecurityGroupServerRpcCallbackMixin):
+
+ """Class to handle agent RPC calls."""
+
+ # Set RPC API version to 1.1 by default.
+ RPC_API_VERSION = '1.1'
+
+ def __init__(self, notifier):
+ self.notifier = notifier
+
+ def create_rpc_dispatcher(self):
+ """Get the rpc dispatcher for this rpc manager.
+
+ If a manager would like to set an rpc API version, or support more than
+ one class as the target of rpc messages, override this method.
+ """
+ return q_rpc.PluginRpcDispatcher([self,
+ agents_db.AgentExtRpcCallback()])
+
+
+class AgentNotifierApi(proxy.RpcProxy,
+ sg_rpc.SecurityGroupAgentRpcApiMixin):
+
+ """Agent side of the N1kv rpc API.
+
+ API version history:
+ 1.0 - Initial version.
+ """
+
+ BASE_RPC_API_VERSION = '1.0'
+
+ def __init__(self, topic):
+ super(AgentNotifierApi, self).__init__(
+ topic=topic, default_version=self.BASE_RPC_API_VERSION)
+ self.topic_network_delete = topics.get_topic_name(topic,
+ topics.NETWORK,
+ topics.DELETE)
+ self.topic_port_update = topics.get_topic_name(topic,
+ topics.PORT,
+ topics.UPDATE)
+ self.topic_vxlan_update = topics.get_topic_name(topic,
+ c_const.TUNNEL,
+ topics.UPDATE)
+
+ def network_delete(self, context, network_id):
+ self.fanout_cast(context,
+ self.make_msg('network_delete',
+ network_id=network_id),
+ topic=self.topic_network_delete)
+
+ def port_update(self, context, port, network_type, segmentation_id,
+ physical_network):
+ self.fanout_cast(context,
+ self.make_msg('port_update',
+ port=port,
+ network_type=network_type,
+ segmentation_id=segmentation_id,
+ physical_network=physical_network),
+ topic=self.topic_port_update)
+
+ def vxlan_update(self, context, vxlan_ip, vxlan_id):
+ self.fanout_cast(context,
+ self.make_msg('vxlan_update',
+ vxlan_ip=vxlan_ip,
+ vxlan_id=vxlan_id),
+ topic=self.topic_vxlan_update)
+
+
+class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
+ l3_db.L3_NAT_db_mixin,
+ n1kv_db_v2.NetworkProfile_db_mixin,
+ n1kv_db_v2.PolicyProfile_db_mixin,
+ network_db_v2.Credential_db_mixin,
+ agentschedulers_db.AgentSchedulerDbMixin):
+
+ """
+ Implement the Neutron abstractions using Cisco Nexus1000V.
+
+ Refer README file for the architecture, new features, and
+ workflow
+
+ """
+
+ # This attribute specifies whether the plugin supports or not
+ # bulk operations.
+ __native_bulk_support = False
+ supported_extension_aliases = ["provider", "agent",
+ "policy_profile_binding",
+ "network_profile_binding",
+ "n1kv_profile", "network_profile",
+ "policy_profile", "router", "credential"]
+
+ def __init__(self, configfile=None):
+ """
+ Initialize Nexus1000V Neutron plugin.
+
+ 1. Initialize Nexus1000v and Credential DB
+ 2. Establish communication with Cisco Nexus1000V
+ """
+ n1kv_db_v2.initialize()
+ c_cred.Store.initialize()
+ self._initialize_network_vlan_ranges()
+ # If no api_extensions_path is provided set the following
+ if not q_conf.CONF.api_extensions_path:
+ q_conf.CONF.set_override(
+ 'api_extensions_path',
+ 'extensions:neutron/plugins/cisco/extensions')
+ self._setup_vsm()
+ self._setup_rpc()
+
+ def _setup_rpc(self):
+ # RPC support
+ self.topic = topics.PLUGIN
+ self.conn = rpc.create_connection(new=True)
+ self.notifier = AgentNotifierApi(topics.AGENT)
+ self.callbacks = N1kvRpcCallbacks(self.notifier)
+ self.dispatcher = self.callbacks.create_rpc_dispatcher()
+ self.conn.create_consumer(self.topic, self.dispatcher,
+ fanout=False)
+ # Consume from all consumers in a thread
+ self.dhcp_agent_notifier = dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
+ self.l3_agent_notifier = l3_rpc_agent_api.L3AgentNotify
+ self.conn.consume_in_thread()
+
+ def _setup_vsm(self):
+ """
+ Setup Cisco Nexus 1000V related parameters and pull policy profiles.
+
+ Retreive all the policy profiles from the VSM when the plugin is
+ is instantiated for the first time and then continue to poll for
+ policy profile updates.
+ """
+ LOG.debug(_('_setup_vsm'))
+ self.agent_vsm = True
+ # Retrieve all the policy profiles from VSM.
+ self._populate_policy_profiles()
+ # Continue to poll VSM for any create/delete of policy profiles.
+ eventlet.spawn(self._poll_policy_profiles)
+
+ def _poll_policy_profiles(self):
+ """Start a green thread to pull policy profiles from VSM."""
+ while True:
+ self._poll_policies(event_type='port_profile')
+ eventlet.sleep(int(c_conf.CISCO_N1K.poll_duration))
+
+ def _populate_policy_profiles(self):
+ """
+ Populate all the policy profiles from VSM.
+
+ The tenant id is not available when the policy profiles are polled
+ from the VSM. Hence we associate the policy profiles with fake
+ tenant-ids.
+ """
+ LOG.debug(_('_populate_policy_profiles'))
+ n1kvclient = n1kv_client.Client()
+ policy_profiles = n1kvclient.list_port_profiles()
+ LOG.debug(_('_populate_policy_profiles %s'), policy_profiles)
+ if policy_profiles:
+ for profile in policy_profiles['body'][c_const.SET]:
+ if c_const.ID and c_const.NAME in profile:
+ profile_id = profile[c_const.PROPERTIES][c_const.ID]
+ profile_name = profile[c_const.PROPERTIES][c_const.NAME]
+ self._add_policy_profile(profile_name, profile_id)
+ else:
+ LOG.warning(_('No policy profile populated from VSM'))
+ self._remove_all_fake_policy_profiles()
+
+ def _poll_policies(self, event_type=None, epoch=None, tenant_id=None):
+ """
+ Poll for Policy Profiles from Cisco Nexus1000V for any update/delete.
+ """
+ LOG.debug(_('_poll_policies'))
+ n1kvclient = n1kv_client.Client()
+ policy_profiles = n1kvclient.list_events(event_type, epoch)
+ if policy_profiles:
+ for profile in policy_profiles['body'][c_const.SET]:
+ if c_const.NAME in profile:
+ # Extract commands from the events XML.
+ cmd = profile[c_const.PROPERTIES]['cmd']
+ cmds = cmd.split(';')
+ cmdwords = cmds[1].split()
+ profile_name = profile[c_const.PROPERTIES][c_const.NAME]
+ # Delete the policy profile from db if it's deleted on VSM
+ if 'no' in cmdwords[0]:
+ p = self._get_policy_profile_by_name(profile_name)
+ if p:
+ self._delete_policy_profile(p['id'])
+ # Add policy profile to neutron DB idempotently
+ elif c_const.ID in profile[c_const.PROPERTIES]:
+ profile_id = profile[c_const.PROPERTIES][c_const.ID]
+ self._add_policy_profile(
+ profile_name, profile_id, tenant_id)
+ # Replace tenant-id for profile bindings with admin's tenant-id
+ self._remove_all_fake_policy_profiles()
+
+ def _initialize_network_vlan_ranges(self):
+ self.network_vlan_ranges = {}
+ network_profiles = n1kv_db_v2._get_network_profiles()
+ for network_profile in network_profiles:
+ if network_profile['segment_type'] == c_const.NETWORK_TYPE_VLAN:
+ seg_min, seg_max = self._get_segment_range(
+ network_profile['segment_range'])
+ self._add_network_vlan_range(network_profile[
+ 'physical_network'], int(seg_min), int(seg_max))
+
+ def _add_network_vlan_range(self, physical_network, vlan_min, vlan_max):
+ self._add_network(physical_network)
+ self.network_vlan_ranges[physical_network].append((vlan_min, vlan_max))
+
+ def _add_network(self, physical_network):
+ if physical_network not in self.network_vlan_ranges:
+ self.network_vlan_ranges[physical_network] = []
+
+ def _check_provider_view_auth(self, context, network):
+ return policy.check(context,
+ "extension:provider_network:view",
+ network)
+
+ def _enforce_provider_set_auth(self, context, network):
+ return policy.enforce(context,
+ "extension:provider_network:set",
+ network)
+
+ def _extend_network_dict_provider(self, context, network):
+ """Add extended network parameters."""
+ binding = n1kv_db_v2.get_network_binding(context.session,
+ network['id'])
+ network[providernet.NETWORK_TYPE] = binding.network_type
+ if binding.network_type == c_const.NETWORK_TYPE_VXLAN:
+ network[providernet.PHYSICAL_NETWORK] = None
+ network[providernet.SEGMENTATION_ID] = binding.segmentation_id
+ network[n1kv_profile.MULTICAST_IP] = binding.multicast_ip
+ elif binding.network_type == c_const.NETWORK_TYPE_VLAN:
+ network[providernet.PHYSICAL_NETWORK] = binding.physical_network
+ network[providernet.SEGMENTATION_ID] = binding.segmentation_id
+
+ def _process_provider_create(self, context, attrs):
+ network_type = attrs.get(providernet.NETWORK_TYPE)
+ physical_network = attrs.get(providernet.PHYSICAL_NETWORK)
+ segmentation_id = attrs.get(providernet.SEGMENTATION_ID)
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ segmentation_id_set = attributes.is_attr_set(segmentation_id)
+
+ if not (network_type_set or physical_network_set or
+ segmentation_id_set):
+ return (None, None, None)
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ if not network_type_set:
+ msg = _("provider:network_type required")
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_type == c_const.NETWORK_TYPE_VLAN:
+ if not segmentation_id_set:
+ msg = _("provider:segmentation_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ if segmentation_id < 1 or segmentation_id > 4094:
+ msg = _("provider:segmentation_id out of range "
+ "(1 through 4094)")
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_type == c_const.NETWORK_TYPE_VXLAN:
+ if physical_network_set:
+ msg = _("provider:physical_network specified for VXLAN "
+ "network")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ physical_network = None
+ if not segmentation_id_set:
+ msg = _("provider:segmentation_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ if segmentation_id < 5000:
+ msg = _("provider:segmentation_id out of range "
+ "(5000+)")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ msg = _("provider:network_type %s not supported"), network_type
+ raise q_exc.InvalidInput(error_message=msg)
+
+ if network_type == c_const.NETWORK_TYPE_VLAN:
+ if physical_network_set:
+ if physical_network not in self.network_vlan_ranges:
+ msg = (_("unknown provider:physical_network %s"),
+ physical_network)
+ raise q_exc.InvalidInput(error_message=msg)
+ elif 'default' in self.network_vlan_ranges:
+ physical_network = 'default'
+ else:
+ msg = _("provider:physical_network required")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ return (network_type, physical_network, segmentation_id)
+
+ def _check_provider_update(self, context, attrs):
+ """Handle Provider network updates."""
+ network_type = attrs.get(providernet.NETWORK_TYPE)
+ physical_network = attrs.get(providernet.PHYSICAL_NETWORK)
+ segmentation_id = attrs.get(providernet.SEGMENTATION_ID)
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ segmentation_id_set = attributes.is_attr_set(segmentation_id)
+
+ if not (network_type_set or physical_network_set or
+ segmentation_id_set):
+ return
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ # TBD : Need to handle provider network updates
+ msg = _("plugin does not support updating provider attributes")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ def _extend_network_dict_profile(self, context, network):
+ """Add the extended parameter network profile to the network."""
+ binding = n1kv_db_v2.get_network_binding(context.session,
+ network['id'])
+ network[n1kv_profile.PROFILE_ID] = binding.profile_id
+
+ def _extend_port_dict_profile(self, context, port):
+ """Add the extended parameter port profile to the port."""
+ binding = n1kv_db_v2.get_port_binding(context.session,
+ port['id'])
+ port[n1kv_profile.PROFILE_ID] = binding.profile_id
+
+ def _process_network_profile(self, context, attrs):
+ """Validate network profile exists."""
+ profile_id = attrs.get(n1kv_profile.PROFILE_ID)
+ profile_id_set = attributes.is_attr_set(profile_id)
+ if not profile_id_set:
+ raise cisco_exceptions.NetworkProfileIdNotFound(
+ profile_id=profile_id)
+ if not self.network_profile_exists(context, profile_id):
+ raise cisco_exceptions.NetworkProfileIdNotFound(
+ profile_id=profile_id)
+ return profile_id
+
+ def _process_policy_profile(self, context, attrs):
+ """Validates whether policy profile exists."""
+ profile_id = attrs.get(n1kv_profile.PROFILE_ID)
+ profile_id_set = attributes.is_attr_set(profile_id)
+ if not profile_id_set:
+ msg = _("n1kv:profile_id does not exist")
+ raise q_exc.InvalidInput(error_message=msg)
+ if not self._policy_profile_exists(profile_id):
+ msg = _("n1kv:profile_id does not exist")
+ raise q_exc.InvalidInput(error_message=msg)
+
+ return profile_id
+
+ def _send_create_logical_network_request(self, network_profile):
+ """
+ Send create logical network request to VSM.
+
+ :param network_profile: network profile dictionary
+ """
+ LOG.debug(_('_send_create_logical_network'))
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.create_logical_network(network_profile)
+
+ def _send_delete_logical_network_request(self, network_profile):
+ """
+ Send delete logical network request to VSM.
+
+ :param network_profile: network profile dictionary
+ """
+ LOG.debug('_send_delete_logical_network')
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.delete_logical_network(network_profile)
+
+ def _send_create_network_profile_request(self, context, profile):
+ """
+ Send create network profile request to VSM.
+
+ :param context: neutron api request context
+ :param profile: network profile dictionary
+ """
+ LOG.debug(_('_send_create_network_profile_request: %s'), profile['id'])
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.create_network_segment_pool(profile)
+
+ def _send_delete_network_profile_request(self, profile):
+ """
+ Send delete network profile request to VSM.
+
+ :param profile: network profile dictionary
+ """
+ LOG.debug(_('_send_delete_network_profile_request: %s'),
+ profile['name'])
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.delete_network_segment_pool(profile['name'])
+
+ def _send_create_network_request(self, context, network):
+ """
+ Send create network request to VSM.
+
+ Create a bridge domain for network of type VXLAN.
+ :param context: neutron api request context
+ :param network: network dictionary
+ """
+ LOG.debug(_('_send_create_network_request: %s'), network['id'])
+ profile = self.get_network_profile(context,
+ network[n1kv_profile.PROFILE_ID])
+ n1kvclient = n1kv_client.Client()
+ if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VXLAN:
+ n1kvclient.create_bridge_domain(network)
+ n1kvclient.create_network_segment(network, profile)
+
+ def _send_update_network_request(self, db_session, network):
+ """
+ Send update network request to VSM.
+
+ :param network: network dictionary
+ """
+ LOG.debug(_('_send_update_network_request: %s'), network['id'])
+ profile = n1kv_db_v2.get_network_profile(
+ db_session, network[n1kv_profile.PROFILE_ID])
+ body = {'name': network['name'],
+ 'id': network['id'],
+ 'networkDefinition': profile['name'],
+ 'vlan': network[providernet.SEGMENTATION_ID]}
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.update_network_segment(network['name'], body)
+
+ def _send_delete_network_request(self, network):
+ """
+ Send delete network request to VSM.
+
+ Delete bridge domain if network is of type VXLAN.
+ :param network: network dictionary
+ """
+ LOG.debug(_('_send_delete_network_request: %s'), network['id'])
+ n1kvclient = n1kv_client.Client()
+ if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VXLAN:
+ name = network['name'] + '_bd'
+ n1kvclient.delete_bridge_domain(name)
+ n1kvclient.delete_network_segment(network['name'])
+
+ def _send_create_subnet_request(self, context, subnet):
+ """
+ Send create subnet request to VSM.
+
+ :param context: neutron api request context
+ :param subnet: subnet dictionary
+ """
+ LOG.debug(_('_send_create_subnet_request: %s'), subnet['id'])
+ network = self.get_network(context, subnet['network_id'])
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.create_ip_pool(subnet)
+ body = {'ipPoolName': subnet['name']}
+ n1kvclient.update_network_segment(network['name'], body=body)
+
+ def _send_delete_subnet_request(self, context, subnet):
+ """
+ Send delete subnet request to VSM.
+
+ :param context: neutron api request context
+ :param subnet: subnet dictionary
+ """
+ LOG.debug(_('_send_delete_subnet_request: %s'), subnet['name'])
+ network = self.get_network(context, subnet['network_id'])
+ body = {'ipPoolName': subnet['name'], 'deleteSubnet': True}
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.update_network_segment(network['name'], body=body)
+ n1kvclient.delete_ip_pool(subnet['name'])
+
+ def _send_create_port_request(self, context, port):
+ """
+ Send create port request to VSM.
+
+ Create a VM network for a network and policy profile combination.
+ If the VM network already exists, bind this port to the existing
+ VM network and increment its port count.
+ :param context: neutron api request context
+ :param port: port dictionary
+ """
+ LOG.debug(_('_send_create_port_request: %s'), port)
+ try:
+ vm_network = n1kv_db_v2.get_vm_network(
+ context.session,
+ port[n1kv_profile.PROFILE_ID],
+ port['network_id'])
+ except cisco_exceptions.VMNetworkNotFound:
+ policy_profile = n1kv_db_v2.get_policy_profile(
+ context.session, port[n1kv_profile.PROFILE_ID])
+ network = self.get_network(context, port['network_id'])
+ vm_network_name = (c_const.VM_NETWORK_NAME_PREFIX +
+ str(port[n1kv_profile.PROFILE_ID]) +
+ "_" + str(port['network_id']))
+ port_count = 1
+ n1kv_db_v2.add_vm_network(context.session,
+ vm_network_name,
+ port[n1kv_profile.PROFILE_ID],
+ port['network_id'],
+ port_count)
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.create_vm_network(port,
+ vm_network_name,
+ policy_profile,
+ network['name'])
+ n1kvclient.create_n1kv_port(port, vm_network_name)
+ else:
+ vm_network_name = vm_network['name']
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.create_n1kv_port(port, vm_network_name)
+ vm_network['port_count'] += 1
+ n1kv_db_v2.update_vm_network_port_count(
+ context.session, vm_network_name, vm_network['port_count'])
+
+ def _send_update_port_request(self, port_id, mac_address, vm_network_name):
+ """
+ Send update port request to VSM.
+
+ :param port_id: UUID representing port to update
+ :param mac_address: string representing the mac address
+ :param vm_network_name: VM network name to which the port is bound
+ """
+ LOG.debug(_('_send_update_port_request: %s'), port_id)
+ body = {'portId': port_id,
+ 'macAddress': mac_address}
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.update_n1kv_port(vm_network_name, port_id, body)
+
+ def _send_delete_port_request(self, context, id):
+ """
+ Send delete port request to VSM.
+
+ Decrement the port count of the VM network after deleting the port.
+ If the port count reaches zero, delete the VM network.
+ :param context: neutron api request context
+ :param id: UUID of the port to be deleted
+ """
+ LOG.debug(_('_send_delete_port_request: %s'), id)
+ port = self.get_port(context, id)
+ vm_network = n1kv_db_v2.get_vm_network(context.session,
+ port[n1kv_profile.PROFILE_ID],
+ port['network_id'])
+ vm_network['port_count'] -= 1
+ n1kv_db_v2.update_vm_network_port_count(
+ context.session, vm_network['name'], vm_network['port_count'])
+ n1kvclient = n1kv_client.Client()
+ n1kvclient.delete_n1kv_port(vm_network['name'], id)
+ if vm_network['port_count'] == 0:
+ n1kv_db_v2.delete_vm_network(context.session,
+ port[n1kv_profile.PROFILE_ID],
+ port['network_id'])
+ n1kvclient.delete_vm_network(vm_network['name'])
+
+ def _get_segmentation_id(self, context, id):
+ """
+ Retreive segmentation ID for a given network.
+
+ :param context: neutron api request context
+ :param id: UUID of the network
+ :returns: segmentation ID for the network
+ """
+ session = context.session
+ binding = n1kv_db_v2.get_network_binding(session, id)
+ return binding.segmentation_id
+
+ def create_network(self, context, network):
+ """
+ Create network based on network profile.
+
+ :param context: neutron api request context
+ :param network: network dictionary
+ :returns: network object
+ """
+ (network_type, physical_network,
+ segmentation_id) = self._process_provider_create(context,
+ network['network'])
+ self._add_dummy_profile_only_if_testing(network)
+ profile_id = self._process_network_profile(context, network['network'])
+ LOG.debug(_('create network: profile_id=%s'), profile_id)
+ session = context.session
+ with session.begin(subtransactions=True):
+ if not network_type:
+ # tenant network
+ (physical_network, network_type, segmentation_id,
+ multicast_ip) = n1kv_db_v2.alloc_network(session,
+ profile_id)
+ LOG.debug(_('Physical_network %(phy_net)s, '
+ 'seg_type %(net_type)s, '
+ 'seg_id %(seg_id)s, '
+ 'multicast_ip %(multicast_ip)s'),
+ {'phy_net': physical_network,
+ 'net_type': network_type,
+ 'seg_id': segmentation_id,
+ 'multicast_ip': multicast_ip})
+ if not segmentation_id:
+ raise q_exc.TenantNetworksDisabled()
+ else:
+ # provider network
+ if network_type == c_const.NETWORK_TYPE_VLAN:
+ network_profile = self.get_network_profile(context,
+ profile_id)
+ seg_min, seg_max = self._get_segment_range(
+ network_profile['segment_range'])
+ if not seg_min <= segmentation_id <= seg_max:
+ raise cisco_exceptions.VlanIDOutsidePool
+ n1kv_db_v2.reserve_specific_vlan(session,
+ physical_network,
+ segmentation_id)
+ multicast_ip = "0.0.0.0"
+ net = super(N1kvNeutronPluginV2, self).create_network(context,
+ network)
+ n1kv_db_v2.add_network_binding(session,
+ net['id'],
+ network_type,
+ physical_network,
+ segmentation_id,
+ multicast_ip,
+ profile_id)
+ self._process_l3_create(context, net, network['network'])
+ self._extend_network_dict_provider(context, net)
+ self._extend_network_dict_profile(context, net)
+
+ try:
+ self._send_create_network_request(context, net)
+ except(cisco_exceptions.VSMError,
+ cisco_exceptions.VSMConnectionFailed):
+ super(N1kvNeutronPluginV2, self).delete_network(context, net['id'])
+ else:
+ # note - exception will rollback entire transaction
+ LOG.debug(_("Created network: %s"), net['id'])
+ return net
+
+ def update_network(self, context, id, network):
+ """
+ Update network parameters.
+
+ :param context: neutron api request context
+ :param id: UUID representing the network to update
+ :returns: updated network object
+ """
+ self._check_provider_update(context, network['network'])
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = super(N1kvNeutronPluginV2, self).update_network(context, id,
+ network)
+ self._process_l3_update(context, net, network['network'])
+ self._extend_network_dict_provider(context, net)
+ self._extend_network_dict_profile(context, net)
+ self._send_update_network_request(context.session, net)
+ LOG.debug(_("Updated network: %s"), net['id'])
+ return net
+
+ def delete_network(self, context, id):
+ """
+ Delete a network.
+
+ :param context: neutron api request context
+ :param id: UUID representing the network to delete
+ """
+ session = context.session
+ with session.begin(subtransactions=True):
+ binding = n1kv_db_v2.get_network_binding(session, id)
+ network = self.get_network(context, id)
+ super(N1kvNeutronPluginV2, self).delete_network(context, id)
+ if binding.network_type == c_const.NETWORK_TYPE_VXLAN:
+ n1kv_db_v2.release_vxlan(session, binding.segmentation_id,
+ self.vxlan_id_ranges)
+ elif binding.network_type == c_const.NETWORK_TYPE_VLAN:
+ n1kv_db_v2.release_vlan(session, binding.physical_network,
+ binding.segmentation_id,
+ self.network_vlan_ranges)
+ # the network_binding record is deleted via cascade from
+ # the network record, so explicit removal is not necessary
+ if self.agent_vsm:
+ self._send_delete_network_request(network)
+ LOG.debug(_("Deleted network: %s"), id)
+
+ def get_network(self, context, id, fields=None):
+ """
+ Retreive a Network.
+
+ :param context: neutron api request context
+ :param id: UUID representing the network to fetch
+ :returns: requested network dictionary
+ """
+ LOG.debug(_("Get network: %s"), id)
+ net = super(N1kvNeutronPluginV2, self).get_network(context, id, None)
+ self._extend_network_dict_provider(context, net)
+ self._extend_network_dict_profile(context, net)
+ return self._fields(net, fields)
+
+ def get_networks(self, context, filters=None, fields=None):
+ """
+ Retreive a list of networks.
+
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ network object. Values in this dictiontary are an
+ iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a network
+ dictionary. Only these fields will be returned.
+ :returns: list of network dictionaries.
+ """
+ LOG.debug(_("Get networks"))
+ nets = super(N1kvNeutronPluginV2, self).get_networks(context, filters,
+ None)
+ for net in nets:
+ self._extend_network_dict_provider(context, net)
+ self._extend_network_dict_profile(context, net)
+
+ return [self._fields(net, fields) for net in nets]
+
+ def create_port(self, context, port):
+ """
+ Create neutron port.
+
+ Create a port. Use a default policy profile for ports created for dhcp
+ and router interface. Default policy profile name is configured in the
+ /etc/neutron/cisco_plugins.ini file.
+
+ :param context: neutron api request context
+ :param port: port dictionary
+ :returns: port object
+ """
+ self._add_dummy_profile_only_if_testing(port)
+
+ if ('device_id' in port['port'] and port['port']['device_owner'] in
+ ['network:dhcp', 'network:router_interface']):
+ p_profile_name = c_conf.CISCO_N1K.default_policy_profile
+ p_profile = self._get_policy_profile_by_name(p_profile_name)
+ if p_profile:
+ port['port']['n1kv:profile_id'] = p_profile['id']
+
+ profile_id_set = False
+ if n1kv_profile.PROFILE_ID in port['port']:
+ profile_id = port['port'].get(n1kv_profile.PROFILE_ID)
+ profile_id_set = attributes.is_attr_set(profile_id)
+
+ if profile_id_set:
+ profile_id = self._process_policy_profile(context,
+ port['port'])
+ LOG.debug(_('create port: profile_id=%s'), profile_id)
+ session = context.session
+ with session.begin(subtransactions=True):
+ pt = super(N1kvNeutronPluginV2, self).create_port(context,
+ port)
+ n1kv_db_v2.add_port_binding(session, pt['id'], profile_id)
+ self._extend_port_dict_profile(context, pt)
+ try:
+ self._send_create_port_request(context, pt)
+ except(cisco_exceptions.VSMError,
+ cisco_exceptions.VSMConnectionFailed):
+ super(N1kvNeutronPluginV2, self).delete_port(context, pt['id'])
+ else:
+ LOG.debug(_("Created port: %s"), pt)
+ return pt
+
+ def _add_dummy_profile_only_if_testing(self, obj):
+ """
+ Method to be patched by the test_n1kv_plugin module to
+ inject n1kv:profile_id into the network/port object, since the plugin
+ tests for its existence. This method does not affect
+ the plugin code in any way.
+ """
+ pass
+
+ def update_port(self, context, id, port):
+ """
+ Update port parameters.
+
+ :param context: neutron api request context
+ :param id: UUID representing the port to update
+ :returns: updated port object
+ """
+ LOG.debug(_("Update port: %s"), id)
+ if self.agent_vsm:
+ super(N1kvNeutronPluginV2, self).get_port(context, id)
+ port = super(N1kvNeutronPluginV2, self).update_port(context, id, port)
+ self._extend_port_dict_profile(context, port)
+ return port
+
+ def delete_port(self, context, id):
+ """
+ Delete a port.
+
+ :param context: neutron api request context
+ :param id: UUID representing the port to delete
+ :returns: deleted port object
+ """
+ self._send_delete_port_request(context, id)
+ return super(N1kvNeutronPluginV2, self).delete_port(context, id)
+
+ def get_port(self, context, id, fields=None):
+ """
+ Retrieve a port.
+ :param context: neutron api request context
+ :param id: UUID representing the port to retrieve
+ :param fields: a list of strings that are valid keys in a port
+ dictionary. Only these fields will be returned.
+ :returns: port dictionary
+ """
+ LOG.debug(_("Get port: %s"), id)
+ port = super(N1kvNeutronPluginV2, self).get_port(context, id, fields)
+ self._extend_port_dict_profile(context, port)
+ return self._fields(port, fields)
+
+ def get_ports(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of ports.
+
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ port object. Values in this dictiontary are an
+ iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a port
+ dictionary. Only these fields will be returned.
+ :returns: list of port dictionaries
+ """
+ LOG.debug(_("Get ports"))
+ ports = super(N1kvNeutronPluginV2, self).get_ports(context, filters,
+ fields)
+ for port in ports:
+ self._extend_port_dict_profile(context, port)
+
+ return [self._fields(port, fields) for port in ports]
+
+ def create_subnet(self, context, subnet):
+ """
+ Create subnet for a given network.
+
+ :param context: neutron api request context
+ :param subnet: subnet dictionary
+ :returns: subnet object
+ """
+ LOG.debug(_('Create subnet'))
+ sub = super(N1kvNeutronPluginV2, self).create_subnet(context, subnet)
+ try:
+ self._send_create_subnet_request(context, sub)
+ except(cisco_exceptions.VSMError,
+ cisco_exceptions.VSMConnectionFailed):
+ super(N1kvNeutronPluginV2, self).delete_subnet(context, sub['id'])
+ else:
+ LOG.debug(_("Created subnet: %s"), sub['id'])
+ return sub
+
+ def update_subnet(self, context, id, subnet):
+ """
+ Update a subnet.
+
+ :param context: neutron api request context
+ :param id: UUID representing subnet to update
+ :returns: updated subnet object
+ """
+ LOG.debug(_('Update subnet'))
+ sub = super(N1kvNeutronPluginV2, self).update_subnet(context,
+ id,
+ subnet)
+ return sub
+
+ def delete_subnet(self, context, id):
+ """
+ Delete a subnet.
+
+ :param context: neutron api request context
+ :param id: UUID representing subnet to delete
+ :returns: deleted subnet object
+ """
+ LOG.debug(_('Delete subnet: %s'), id)
+ subnet = self.get_subnet(context, id)
+ self._send_delete_subnet_request(context, subnet)
+ return super(N1kvNeutronPluginV2, self).delete_subnet(context, id)
+
+ def get_subnet(self, context, id, fields=None):
+ """
+ Retrieve a subnet.
+
+ :param context: neutron api request context
+ :param id: UUID representing subnet to retrieve
+ :params fields: a list of strings that are valid keys in a subnet
+ dictionary. Only these fields will be returned.
+ :returns: subnet object
+ """
+ LOG.debug(_("Get subnet: %s"), id)
+ subnet = super(N1kvNeutronPluginV2, self).get_subnet(context, id,
+ fields)
+ return self._fields(subnet, fields)
+
+ def get_subnets(self, context, filters=None, fields=None):
+ """
+ Retrieve a list of subnets.
+
+ :param context: neutron api request context
+ :param filters: a dictionary with keys that are valid keys for a
+ subnet object. Values in this dictiontary are an
+ iterable containing values that will be used for an
+ exact match comparison for that value. Each result
+ returned by this function will have matched one of the
+ values for each key in filters
+ :params fields: a list of strings that are valid keys in a subnet
+ dictionary. Only these fields will be returned.
+ :returns: list of dictionaries of subnets
+ """
+ LOG.debug(_("Get subnets"))
+ subnets = super(N1kvNeutronPluginV2, self).get_subnets(context,
+ filters,
+ fields)
+ return [self._fields(subnet, fields) for subnet in subnets]
+
+ def create_network_profile(self, context, network_profile):
+ """
+ Create a network profile.
+
+ Create a network profile, which represents a pool of networks
+ belonging to one type (VLAN or VXLAN). On creation of network
+ profile, we retrieve the admin tenant-id which we use to replace
+ the previously stored fake tenant-id in tenant-profile bindings.
+ :param context: neutron api request context
+ :param network_profile: network profile dictionary
+ :returns: network profile object
+ """
+ self._replace_fake_tenant_id_with_real(context)
+ _network_profile = super(
+ N1kvNeutronPluginV2, self).create_network_profile(context,
+ network_profile)
+ seg_min, seg_max = self._get_segment_range(
+ _network_profile['segment_range'])
+ if _network_profile['segment_type'] == c_const.NETWORK_TYPE_VLAN:
+ self._add_network_vlan_range(_network_profile['physical_network'],
+ int(seg_min),
+ int(seg_max))
+ n1kv_db_v2.sync_vlan_allocations(context.session,
+ self.network_vlan_ranges)
+ elif _network_profile['segment_type'] == c_const.NETWORK_TYPE_VXLAN:
+ self.vxlan_id_ranges = []
+ self.vxlan_id_ranges.append((int(seg_min), int(seg_max)))
+ n1kv_db_v2.sync_vxlan_allocations(context.session,
+ self.vxlan_id_ranges)
+ try:
+ self._send_create_logical_network_request(_network_profile)
+ except(cisco_exceptions.VSMError,
+ cisco_exceptions.VSMConnectionFailed):
+ super(N1kvNeutronPluginV2, self).delete_network_profile(
+ context, _network_profile['id'])
+ try:
+ self._send_create_network_profile_request(context,
+ _network_profile)
+ except(cisco_exceptions.VSMError,
+ cisco_exceptions.VSMConnectionFailed):
+ self._send_delete_logical_network_request(_network_profile)
+ super(N1kvNeutronPluginV2, self).delete_network_profile(
+ context, _network_profile['id'])
+ else:
+ return _network_profile
+
+ def delete_network_profile(self, context, id):
+ """
+ Delete a network profile.
+
+ :param context: neutron api request context
+ :param id: UUID of the network profile to delete
+ :returns: deleted network profile object
+ """
+ _network_profile = super(
+ N1kvNeutronPluginV2, self).delete_network_profile(context, id)
+ seg_min, seg_max = self._get_segment_range(
+ _network_profile['segment_range'])
+ if _network_profile['segment_type'] == c_const.NETWORK_TYPE_VLAN:
+ self._add_network_vlan_range(_network_profile['physical_network'],
+ int(seg_min),
+ int(seg_max))
+ n1kv_db_v2.delete_vlan_allocations(context.session,
+ self.network_vlan_ranges)
+ elif _network_profile['segment_type'] == c_const.NETWORK_TYPE_VXLAN:
+ self.delete_vxlan_ranges = []
+ self.delete_vxlan_ranges.append((int(seg_min), int(seg_max)))
+ n1kv_db_v2.delete_vxlan_allocations(context.session,
+ self.delete_vxlan_ranges)
+ self._send_delete_network_profile_request(_network_profile)
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',
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):
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 = {}
--- /dev/null
+# 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.
+#
--- /dev/null
+# 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'])
--- /dev/null
+# 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()
"""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()
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)
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)
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):
{'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)
},
cisco_config: {
'CISCO': {'nexus_driver': nexus_driver},
+ 'CISCO_PLUGINS': {'nexus_plugin': NEXUS_PLUGIN},
}
}
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,