]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add support for the Nexus 1000V into the Cisco Plugin.
authorAbhishek Raut <abhraut@cisco.com>
Tue, 9 Jul 2013 19:29:54 +0000 (12:29 -0700)
committerAbhishek Raut <abhraut@cisco.com>
Fri, 9 Aug 2013 23:56:54 +0000 (16:56 -0700)
This will enable the Cisco Nexus 1000V to integrate with the Cisco plugin
and be used to drive the realization of Neutron constructs.
Network profile and Policy profile are introduced as extended neutron
resources, while n1kv:profile_id is introduced as an extended attribute
for network and port objects. Necessary changes to the Cisco plugin are
made to accomodate Nexus 1000V as a configurable vswitch plugin.

Implements: blueprint cisco-plugin-n1k-support
Change-Id: I951e10c57d74c935fca8754c0e21e1ac9df35704

27 files changed:
etc/neutron/plugins/cisco/cisco_plugins.ini
etc/policy.json
neutron/db/migration/alembic_migrations/versions/263772d65691_cisco_db_cleanup_2.py
neutron/db/migration/alembic_migrations/versions/c88b6b5fea3_cisco_n1kv_tables.py [new file with mode: 0644]
neutron/plugins/cisco/common/cisco_constants.py
neutron/plugins/cisco/common/cisco_credentials_v2.py
neutron/plugins/cisco/common/cisco_exceptions.py
neutron/plugins/cisco/common/config.py
neutron/plugins/cisco/db/n1kv_db_v2.py [new file with mode: 0644]
neutron/plugins/cisco/db/n1kv_models_v2.py [new file with mode: 0644]
neutron/plugins/cisco/db/network_db_v2.py
neutron/plugins/cisco/db/network_models_v2.py
neutron/plugins/cisco/extensions/credential.py
neutron/plugins/cisco/extensions/n1kv_profile.py [new file with mode: 0644]
neutron/plugins/cisco/extensions/network_profile.py [new file with mode: 0644]
neutron/plugins/cisco/extensions/policy_profile.py [new file with mode: 0644]
neutron/plugins/cisco/models/virt_phy_sw_v2.py
neutron/plugins/cisco/n1kv/__init__.py [new file with mode: 0644]
neutron/plugins/cisco/n1kv/n1kv_client.py [new file with mode: 0644]
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py [new file with mode: 0644]
neutron/plugins/cisco/network_plugin.py
neutron/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
neutron/tests/unit/cisco/n1kv/__init__.py [new file with mode: 0644]
neutron/tests/unit/cisco/n1kv/test_n1kv_db.py [new file with mode: 0644]
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py [new file with mode: 0644]
neutron/tests/unit/cisco/test_network_db.py
neutron/tests/unit/cisco/test_network_plugin.py

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