]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Multi-segment and trunk support for the Cisco N1Kv Plugin
authorRudrajit Tapadar <rtapadar@cisco.com>
Sat, 10 Aug 2013 06:42:45 +0000 (23:42 -0700)
committerRudrajit Tapadar <rtapadar@cisco.com>
Tue, 27 Aug 2013 01:43:49 +0000 (18:43 -0700)
This patch adds vlan and vxlan trunk support in the
Cisco N1Kv plugin. It also adds support for multi-segment
networks for bridging vlan networks with vxlan networks.

Change-Id: Ibecbbfbce1eb4d18ef6a1bc5250622b9ae87d39c
Implements: blueprint multi-segment-and-trunk-support-cisco-nexus1000v

neutron/db/migration/alembic_migrations/versions/46a0efbd8f0_cisco_n1kv_multisegm.py [new file with mode: 0644]
neutron/plugins/cisco/common/cisco_constants.py
neutron/plugins/cisco/common/cisco_exceptions.py
neutron/plugins/cisco/db/n1kv_db_v2.py
neutron/plugins/cisco/db/n1kv_models_v2.py
neutron/plugins/cisco/extensions/n1kv_profile.py
neutron/plugins/cisco/extensions/network_profile.py
neutron/plugins/cisco/extensions/policy_profile.py
neutron/plugins/cisco/n1kv/n1kv_client.py
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
neutron/tests/unit/cisco/n1kv/test_n1kv_db.py

diff --git a/neutron/db/migration/alembic_migrations/versions/46a0efbd8f0_cisco_n1kv_multisegm.py b/neutron/db/migration/alembic_migrations/versions/46a0efbd8f0_cisco_n1kv_multisegm.py
new file mode 100644 (file)
index 0000000..317dca3
--- /dev/null
@@ -0,0 +1,80 @@
+# 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_multisegment_trunk
+
+Revision ID: 46a0efbd8f0
+Revises: 53bbd27ec841
+Create Date: 2013-08-20 20:44:08.711110
+
+"""
+
+revision = '46a0efbd8f0'
+down_revision = '53bbd27ec841'
+
+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.create_table(
+        'cisco_n1kv_trunk_segments',
+        sa.Column('trunk_segment_id', sa.String(length=36), nullable=False),
+        sa.Column('segment_id', sa.String(length=36), nullable=False),
+        sa.Column('dot1qtag', sa.String(length=36), nullable=False),
+        sa.ForeignKeyConstraint(['trunk_segment_id'], ['networks.id'],
+                                ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('trunk_segment_id', 'segment_id', 'dot1qtag')
+    )
+    op.create_table(
+        'cisco_n1kv_multi_segments',
+        sa.Column('multi_segment_id', sa.String(length=36), nullable=False),
+        sa.Column('segment1_id', sa.String(length=36), nullable=False),
+        sa.Column('segment2_id', sa.String(length=36), nullable=False),
+        sa.Column('encap_profile_name', sa.String(length=36), nullable=True),
+        sa.ForeignKeyConstraint(['multi_segment_id'], ['networks.id'],
+                                ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('multi_segment_id', 'segment1_id',
+                                'segment2_id')
+    )
+    op.alter_column('cisco_network_profiles', 'segment_type',
+                    existing_type=sa.Enum('vlan', 'vxlan', 'trunk',
+                                          'multi-segment'),
+                    existing_nullable=False)
+    op.add_column('cisco_network_profiles',
+                  sa.Column('sub_type', sa.String(length=255), nullable=True))
+
+
+def downgrade(active_plugins=None, options=None):
+    if not migration.should_run(active_plugins, migration_for_plugins):
+        return
+
+    op.drop_table('cisco_n1kv_trunk_segments')
+    op.drop_table('cisco_n1kv_multi_segments')
+    op.alter_column('cisco_network_profiles', 'segment_type',
+                    existing_type=sa.Enum('vlan', 'vxlan'),
+                    existing_nullable=False)
+    op.drop_column('cisco_network_profiles', 'sub_type')
index 9ce2503d4ae9dc30a722fee783ad5fdf26d7fc3d..b64c0591b5c74f9e5012273a39204c048b9aa5b6 100644 (file)
@@ -77,6 +77,12 @@ NETWORK_TYPE_VLAN = 'vlan'
 NETWORK_TYPE_VXLAN = 'vxlan'
 NETWORK_TYPE_LOCAL = 'local'
 NETWORK_TYPE_NONE = 'none'
+NETWORK_TYPE_TRUNK = 'trunk'
+NETWORK_TYPE_MULTI_SEGMENT = 'multi-segment'
+
+# Values for network sub_type
+NETWORK_SUBTYPE_TRUNK_VLAN = NETWORK_TYPE_VLAN
+NETWORK_SUBTYPE_TRUNK_VXLAN = NETWORK_TYPE_VXLAN
 
 # Prefix for VM Network name
 VM_NETWORK_NAME_PREFIX = 'vmn_'
@@ -88,3 +94,14 @@ NAME = 'name'
 ID = 'id'
 POLICY = 'policy'
 TENANT_ID_NOT_SET = 'TENANT_ID_NOT_SET'
+ENCAPSULATIONS = 'encapsulations'
+STATE = 'state'
+ONLINE = 'online'
+MAPPINGS = 'mappings'
+MAPPING = 'mapping'
+SEGMENTS = 'segments'
+SEGMENT = 'segment'
+BRIDGE_DOMAIN_SUFFIX = '_bd'
+ENCAPSULATION_PROFILE_SUFFIX = '_profile'
+
+UUID_LENGTH = 36
index 12ef93e7a14a049399ed78edcf9726cd21725be6..a37dd8b382e60d4040d2d7d6b499234d5b195e96 100644 (file)
@@ -212,3 +212,8 @@ 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.")
+
+
+class NoClusterFound(exceptions.NotFound):
+    """No service cluster found to perform multi-segment bridging."""
+    message = _("No service cluster found to perform multi-segment bridging.")
index 51b7c6a77cbf5149f4e0f68a4858b1f799807e7a..6db40fff80fe723ac780ef10ccb0d47c9ce179cd 100644 (file)
@@ -40,6 +40,230 @@ def initialize():
     db.configure_db()
 
 
+def del_trunk_segment_binding(db_session, trunk_segment_id, segment_pairs):
+    """
+    Delete a trunk network binding.
+
+    :param db_session: database session
+    :param trunk_segment_id: UUID representing the trunk network
+    :param segment_pairs: List of segment UUIDs in pair
+                          representing the segments that are trunked
+    """
+    with db_session.begin(subtransactions=True):
+        for (segment_id, dot1qtag) in segment_pairs:
+            (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding).
+             filter_by(trunk_segment_id=trunk_segment_id,
+                       segment_id=segment_id,
+                       dot1qtag=dot1qtag).delete())
+        alloc = (db_session.query(n1kv_models_v2.
+                 N1kvTrunkSegmentBinding).
+                 filter_by(trunk_segment_id=trunk_segment_id).first())
+        if not alloc:
+            binding = get_network_binding(db_session, trunk_segment_id)
+            binding.physical_network = None
+
+
+def del_multi_segment_binding(db_session, multi_segment_id, segment_pairs):
+    """
+    Delete a multi-segment network binding.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the multi-segment network
+    :param segment_pairs: List of segment UUIDs in pair
+                          representing the segments that are bridged
+    """
+    with db_session.begin(subtransactions=True):
+        for (segment1_id, segment2_id) in segment_pairs:
+            (db_session.query(n1kv_models_v2.
+             N1kvMultiSegmentNetworkBinding).filter_by(
+             multi_segment_id=multi_segment_id,
+             segment1_id=segment1_id,
+             segment2_id=segment2_id).delete())
+
+
+def add_trunk_segment_binding(db_session, trunk_segment_id, segment_pairs):
+    """
+    Create a trunk network binding.
+
+    :param db_session: database session
+    :param trunk_segment_id: UUID representing the multi-segment network
+    :param segment_pairs: List of segment UUIDs in pair
+                          representing the segments to be trunked
+    """
+    with db_session.begin(subtransactions=True):
+        binding = get_network_binding(db_session, trunk_segment_id)
+        for (segment_id, tag) in segment_pairs:
+            if not binding.physical_network:
+                member_seg_binding = get_network_binding(db_session,
+                                                         segment_id)
+                binding.physical_network = member_seg_binding.physical_network
+            trunk_segment_binding = (
+                n1kv_models_v2.N1kvTrunkSegmentBinding(
+                    trunk_segment_id=trunk_segment_id,
+                    segment_id=segment_id, dot1qtag=tag))
+            db_session.add(trunk_segment_binding)
+
+
+def add_multi_segment_binding(db_session, multi_segment_id, segment_pairs):
+    """
+    Create a multi-segment network binding.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the multi-segment network
+    :param segment_pairs: List of segment UUIDs in pair
+                          representing the segments to be bridged
+    """
+    with db_session.begin(subtransactions=True):
+        for (segment1_id, segment2_id) in segment_pairs:
+            multi_segment_binding = (
+                n1kv_models_v2.N1kvMultiSegmentNetworkBinding(
+                    multi_segment_id=multi_segment_id,
+                    segment1_id=segment1_id,
+                    segment2_id=segment2_id))
+            db_session.add(multi_segment_binding)
+
+
+def add_multi_segment_encap_profile_name(db_session, multi_segment_id,
+                                         segment_pair, profile_name):
+    """
+    Add the encapsulation profile name to the multi-segment network binding.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the multi-segment network
+    :param segment_pair: set containing the segment UUIDs that are bridged
+    """
+    with db_session.begin(subtransactions=True):
+        binding = get_multi_segment_network_binding(db_session,
+                                                    multi_segment_id,
+                                                    segment_pair)
+        binding.encap_profile_name = profile_name
+
+
+def get_multi_segment_network_binding(db_session,
+                                      multi_segment_id, segment_pair):
+    """
+    Retrieve multi-segment network binding.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the trunk network whose binding
+                             is to fetch
+    :param segment_pair: set containing the segment UUIDs that are bridged
+    :returns: binding object
+    """
+    try:
+        (segment1_id, segment2_id) = segment_pair
+        return (db_session.query(
+                n1kv_models_v2.N1kvMultiSegmentNetworkBinding).
+                filter_by(multi_segment_id=multi_segment_id,
+                          segment1_id=segment1_id,
+                          segment2_id=segment2_id)).one()
+    except exc.NoResultFound:
+        raise c_exc.NetworkBindingNotFound(network_id=multi_segment_id)
+
+
+def get_multi_segment_members(db_session, multi_segment_id):
+    """
+    Retrieve all the member segments of a multi-segment network.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the multi-segment network
+    :returns: a list of tuples representing the mapped segments
+    """
+    with db_session.begin(subtransactions=True):
+        allocs = (db_session.query(
+                  n1kv_models_v2.N1kvMultiSegmentNetworkBinding).
+                  filter_by(multi_segment_id=multi_segment_id))
+        return [(a.segment1_id, a.segment2_id) for a in allocs]
+
+
+def get_multi_segment_encap_dict(db_session, multi_segment_id):
+    """
+    Retrieve the encapsulation profiles for every segment pairs bridged.
+
+    :param db_session: database session
+    :param multi_segment_id: UUID representing the multi-segment network
+    :returns: a dictionary of lists containing the segment pairs in sets
+    """
+    with db_session.begin(subtransactions=True):
+        encap_dict = {}
+        allocs = (db_session.query(
+                  n1kv_models_v2.N1kvMultiSegmentNetworkBinding).
+                  filter_by(multi_segment_id=multi_segment_id))
+        for alloc in allocs:
+            if alloc.encap_profile_name not in encap_dict:
+                encap_dict[alloc.encap_profile_name] = []
+            seg_pair = (alloc.segment1_id, alloc.segment2_id)
+            encap_dict[alloc.encap_profile_name].append(seg_pair)
+        return encap_dict
+
+
+def get_trunk_network_binding(db_session, trunk_segment_id, segment_pair):
+    """
+    Retrieve trunk network binding.
+
+    :param db_session: database session
+    :param trunk_segment_id: UUID representing the trunk network whose binding
+                             is to fetch
+    :param segment_pair: set containing the segment_id and dot1qtag
+    :returns: binding object
+    """
+    try:
+        (segment_id, dot1qtag) = segment_pair
+        return (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding).
+                filter_by(trunk_segment_id=trunk_segment_id,
+                          segment_id=segment_id,
+                          dot1qtag=dot1qtag)).one()
+    except exc.NoResultFound:
+        raise c_exc.NetworkBindingNotFound(network_id=trunk_segment_id)
+
+
+def get_trunk_members(db_session, trunk_segment_id):
+    """
+    Retrieve all the member segments of a trunk network.
+
+    :param db_session: database session
+    :param trunk_segment_id: UUID representing the trunk network
+    :returns: a list of tuples representing the segment and their
+              corresponding dot1qtag
+    """
+    with db_session.begin(subtransactions=True):
+        allocs = (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding).
+                  filter_by(trunk_segment_id=trunk_segment_id))
+        return [(a.segment_id, a.dot1qtag) for a in allocs]
+
+
+def is_trunk_member(db_session, segment_id):
+    """
+    Checks if a segment is a member of a trunk segment.
+
+    :param db_session: database session
+    :param segment_id: UUID of the segment to be checked
+    :returns: boolean
+    """
+    with db_session.begin(subtransactions=True):
+        ret = (db_session.query(n1kv_models_v2.N1kvTrunkSegmentBinding).
+               filter_by(segment_id=segment_id).first())
+        return bool(ret)
+
+
+def is_multi_segment_member(db_session, segment_id):
+    """
+    Checks if a segment is a member of a multi-segment network.
+
+    :param db_session: database session
+    :param segment_id: UUID of the segment to be checked
+    :returns: boolean
+    """
+    with db_session.begin(subtransactions=True):
+        ret1 = (db_session.query(
+                n1kv_models_v2.N1kvMultiSegmentNetworkBinding).
+                filter_by(segment1_id=segment_id).first())
+        ret2 = (db_session.query(
+                n1kv_models_v2.N1kvMultiSegmentNetworkBinding).
+                filter_by(segment2_id=segment_id).first())
+        return bool(ret1 or ret2)
+
+
 def get_network_binding(db_session, network_id):
     """
     Retrieve network binding.
@@ -59,13 +283,14 @@ def get_network_binding(db_session, network_id):
 
 def add_network_binding(db_session, network_id, network_type,
                         physical_network, segmentation_id,
-                        multicast_ip, network_profile_id):
+                        multicast_ip, network_profile_id, add_segments):
     """
     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 network_type: string representing type of network (VLAN, VXLAN,
+                         MULTI_SEGMENT or TRUNK)
     :param physical_network: Only applicable for VLAN networks. It
                              represents a L2 Domain
     :param segmentation_id: integer representing VLAN or VXLAN ID
@@ -75,6 +300,8 @@ def add_network_binding(db_session, network_id, network_type,
                          IDs.
     :param network_profile_id: network profile ID based on which this network
                                is created
+    :param add_segments: List of segment UUIDs in pairs to be added to either a
+                         multi-segment or trunk network
     """
     with db_session.begin(subtransactions=True):
         binding = n1kv_models_v2.N1kvNetworkBinding(
@@ -85,6 +312,12 @@ def add_network_binding(db_session, network_id, network_type,
             multicast_ip=multicast_ip,
             profile_id=network_profile_id)
         db_session.add(binding)
+        if add_segments is None:
+            pass
+        elif network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+            add_multi_segment_binding(db_session, network_id, add_segments)
+        elif network_type == c_const.NETWORK_TYPE_TRUNK:
+            add_trunk_segment_binding(db_session, network_id, add_segments)
 
 
 def get_segment_range(network_profile):
@@ -262,6 +495,8 @@ def reserve_vlan(db_session, network_profile):
                  filter(and_(
                         n1kv_models_v2.N1kvVlanAllocation.vlan_id >= seg_min,
                         n1kv_models_v2.N1kvVlanAllocation.vlan_id <= seg_max,
+                        n1kv_models_v2.N1kvVlanAllocation.physical_network ==
+                        network_profile['physical_network'],
                         n1kv_models_v2.N1kvVlanAllocation.allocated == False)
                         )).first()
         if alloc:
@@ -314,8 +549,9 @@ def alloc_network(db_session, network_profile_id):
                                               network_profile_id)
         if network_profile.segment_type == c_const.NETWORK_TYPE_VLAN:
             return reserve_vlan(db_session, network_profile)
-        else:
+        if network_profile.segment_type == c_const.NETWORK_TYPE_VXLAN:
             return reserve_vxlan(db_session, network_profile)
+        return (None, network_profile.segment_type, 0, "0.0.0.0")
 
 
 def reserve_specific_vlan(db_session, physical_network, vlan_id):
@@ -601,14 +837,17 @@ def create_network_profile(db_session, 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"]}
+                  "segment_type": network_profile["segment_type"]}
         if network_profile["segment_type"] == c_const.NETWORK_TYPE_VLAN:
             kwargs["physical_network"] = network_profile["physical_network"]
+            kwargs["segment_range"] = network_profile["segment_range"]
         elif network_profile["segment_type"] == c_const.NETWORK_TYPE_VXLAN:
             kwargs["multicast_ip_index"] = 0
             kwargs["multicast_ip_range"] = network_profile[
                 "multicast_ip_range"]
+            kwargs["segment_range"] = network_profile["segment_range"]
+        elif network_profile["segment_type"] == c_const.NETWORK_TYPE_TRUNK:
+            kwargs["sub_type"] = network_profile["sub_type"]
         net_profile = n1kv_models_v2.NetworkProfile(**kwargs)
         db_session.add(net_profile)
         return net_profile
@@ -659,8 +898,7 @@ def _get_network_profiles(**kwargs):
     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)
+    return db_session.query(n1kv_models_v2.NetworkProfile)
 
 
 def create_policy_profile(policy_profile):
@@ -730,9 +968,8 @@ def _profile_binding_exists(tenant_id, profile_id, profile_type):
 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
+    return (db_session.query(n1kv_models_v2.ProfileBinding).filter_by(
+        tenant_id=tenant_id, profile_id=profile_id).one())
 
 
 def get_profile_binding(tenant_id, profile_id):
@@ -773,8 +1010,7 @@ def _get_profile_bindings(profile_type=None):
         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)
+    return db_session.query(n1kv_models_v2.ProfileBinding)
 
 
 class NetworkProfile_db_mixin(object):
@@ -815,6 +1051,7 @@ class NetworkProfile_db_mixin(object):
         res = {"id": network_profile["id"],
                "name": network_profile["name"],
                "segment_type": network_profile["segment_type"],
+               "sub_type": network_profile["sub_type"],
                "segment_range": network_profile["segment_range"],
                "multicast_ip_index": network_profile["multicast_ip_index"],
                "multicast_ip_range": network_profile["multicast_ip_range"],
@@ -888,13 +1125,12 @@ class NetworkProfile_db_mixin(object):
             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:
+        if 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))
+        return self._make_network_profile_dict(
+            update_network_profile(context.session, id, p))
 
     def get_network_profile(self, context, id, fields=None):
         """
@@ -930,11 +1166,10 @@ class NetworkProfile_db_mixin(object):
             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)
+        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):
         """
@@ -992,19 +1227,41 @@ class NetworkProfile_db_mixin(object):
 
         :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"
+        if any(net_p[arg] == "" for arg in ["segment_type"]):
+            msg = _("arguments segment_type 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")
+        segment_type = net_p["segment_type"].lower()
+        if segment_type not in [c_const.NETWORK_TYPE_VLAN,
+                                c_const.NETWORK_TYPE_VXLAN,
+                                c_const.NETWORK_TYPE_TRUNK,
+                                c_const.NETWORK_TYPE_MULTI_SEGMENT]:
+            msg = _("segment_type should either be vlan, vxlan, "
+                    "multi-segment or trunk")
             LOG.exception(msg)
             raise q_exc.InvalidInput(error_message=msg)
-        self._validate_segment_range(net_p)
-        if _segment_type == c_const.NETWORK_TYPE_VLAN:
+        if segment_type == c_const.NETWORK_TYPE_VLAN:
+            if "physical_network" not in net_p:
+                msg = _("argument physical_network missing "
+                        "for network profile")
+                LOG.exception(msg)
+                raise q_exc.InvalidInput(error_message=msg)
+        if segment_type == c_const.NETWORK_TYPE_TRUNK:
+            if "sub_type" not in net_p:
+                msg = _("argument sub_type missing "
+                        "for trunk network profile")
+                LOG.exception(msg)
+                raise q_exc.InvalidInput(error_message=msg)
+        if segment_type in [c_const.NETWORK_TYPE_VLAN,
+                            c_const.NETWORK_TYPE_VXLAN]:
+            if "segment_range" not in net_p:
+                msg = _("argument segment_range missing "
+                        "for network profile")
+                LOG.exception(msg)
+                raise q_exc.InvalidInput(error_message=msg)
+            self._validate_segment_range(net_p)
+        if segment_type != c_const.NETWORK_TYPE_VXLAN:
             net_p["multicast_ip_range"] = "0.0.0.0"
 
     def _validate_segment_range_uniqueness(self, context, net_p):
@@ -1018,28 +1275,30 @@ class NetworkProfile_db_mixin(object):
         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
+            profiles = _get_network_profiles()
         if profiles:
-            for prfl in profiles:
-                name = prfl.name
-                segment_range = prfl.segment_range
+            for profile in profiles:
+                name = profile.name
+                segment_range = profile.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)
+                if (c_const.NETWORK_TYPE_MULTI_SEGMENT in
+                    [profile.segment_type, net_p["segment_type"]] or
+                    c_const.NETWORK_TYPE_TRUNK in
+                    [profile.segment_type, net_p["segment_type"]]):
+                    continue
                 seg_min, seg_max = self._get_segment_range(
                     net_p["segment_range"])
-                prfl_seg_min, prfl_seg_max = self._get_segment_range(
+                profile_seg_min, profile_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))):
+                if ((profile_seg_min <= seg_min <= profile_seg_max) or
+                    (profile_seg_min <= seg_max <= profile_seg_max) or
+                    ((seg_min <= profile_seg_min) and
+                     (seg_max >= profile_seg_max))):
                     msg = _("segment range overlaps with another profile")
                     LOG.exception(msg)
                     raise q_exc.InvalidInput(error_message=msg)
@@ -1149,13 +1408,12 @@ class PolicyProfile_db_mixin(object):
             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:
+        if 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))
+        return self._make_policy_profile_dict(
+            update_policy_profile(context.session, id, p))
 
     def add_policy_profile_tenant(self, policy_profile_id, tenant_id):
         """
index 75b710a372eebd0274afe7e3b960be6e48d5acb9..98af2528134739312cb4e17f5c543de714d3fb4f 100644 (file)
@@ -15,6 +15,7 @@
 #    under the License.
 #
 # @author: Abhishek Raut, Cisco Systems Inc.
+# @author: Rudrajit Tapadar, Cisco Systems Inc.
 
 import sqlalchemy as sa
 
@@ -95,7 +96,8 @@ class NetworkProfile(model_base.BASEV2, models_v2.HasId):
     """
     Nexus1000V Network Profiles
 
-        segment_type - VLAN, VXLAN
+        segment_type - VLAN, VXLAN, TRUNK, MULTI_SEGMENT
+        sub_type - TRUNK_VLAN, TRUNK_VXLAN
         segment_range - '<integer>-<integer>'
         multicast_ip_index - <integer>
         multicast_ip_range - '<ip>-<ip>'
@@ -106,8 +108,12 @@ class NetworkProfile(model_base.BASEV2, models_v2.HasId):
     name = sa.Column(sa.String(255))
     segment_type = sa.Column(sa.Enum(cisco_constants.NETWORK_TYPE_VLAN,
                                      cisco_constants.NETWORK_TYPE_VXLAN,
+                                     cisco_constants.NETWORK_TYPE_TRUNK,
+                                     cisco_constants.
+                                     NETWORK_TYPE_MULTI_SEGMENT,
                                      name='segment_type'),
                              nullable=False)
+    sub_type = sa.Column(sa.String(255))
     segment_range = sa.Column(sa.String(255))
     multicast_ip_index = sa.Column(sa.Integer, default=0)
     multicast_ip_range = sa.Column(sa.String(255))
@@ -142,3 +148,30 @@ class ProfileBinding(model_base.BASEV2):
                           primary_key=True,
                           default=cisco_constants.TENANT_ID_NOT_SET)
     profile_id = sa.Column(sa.String(36), primary_key=True)
+
+
+class N1kvTrunkSegmentBinding(model_base.BASEV2):
+
+    """Represents binding of segments in trunk networks."""
+    __tablename__ = 'cisco_n1kv_trunk_segments'
+
+    trunk_segment_id = sa.Column(sa.String(36),
+                                 sa.ForeignKey('networks.id',
+                                 ondelete="CASCADE"),
+                                 primary_key=True)
+    segment_id = sa.Column(sa.String(36), nullable=False, primary_key=True)
+    dot1qtag = sa.Column(sa.String(36), nullable=False, primary_key=True)
+
+
+class N1kvMultiSegmentNetworkBinding(model_base.BASEV2):
+
+    """Represents binding of segments in multi-segment networks."""
+    __tablename__ = 'cisco_n1kv_multi_segments'
+
+    multi_segment_id = sa.Column(sa.String(36),
+                                 sa.ForeignKey('networks.id',
+                                 ondelete="CASCADE"),
+                                 primary_key=True)
+    segment1_id = sa.Column(sa.String(36), nullable=False, primary_key=True)
+    segment2_id = sa.Column(sa.String(36), nullable=False, primary_key=True)
+    encap_profile_name = sa.Column(sa.String(36))
index 6fa5b244faf83265888def34418fb00a27eaea68..fdf89e2a3862587ce59414dda242443abda641b2 100644 (file)
@@ -24,6 +24,9 @@ from neutron.api.v2 import attributes
 
 PROFILE_ID = 'n1kv:profile_id'
 MULTICAST_IP = 'n1kv:multicast_ip'
+SEGMENT_ADD = 'n1kv:segment_add'
+SEGMENT_DEL = 'n1kv:segment_del'
+MEMBER_SEGMENTS = 'n1kv:member_segments'
 
 EXTENDED_ATTRIBUTES_2_0 = {
     'networks': {
@@ -34,6 +37,15 @@ EXTENDED_ATTRIBUTES_2_0 = {
         MULTICAST_IP: {'allow_post': True, 'allow_put': True,
                        'default': attributes.ATTR_NOT_SPECIFIED,
                        'is_visible': True},
+        SEGMENT_ADD: {'allow_post': True, 'allow_put': True,
+                      'default': attributes.ATTR_NOT_SPECIFIED,
+                      'is_visible': True},
+        SEGMENT_DEL: {'allow_post': True, 'allow_put': True,
+                      'default': attributes.ATTR_NOT_SPECIFIED,
+                      'is_visible': True},
+        MEMBER_SEGMENTS: {'allow_post': True, 'allow_put': True,
+                          'default': attributes.ATTR_NOT_SPECIFIED,
+                          'is_visible': True},
     },
     'ports': {
         PROFILE_ID: {'allow_post': True, 'allow_put': True,
index f52b5de59062d49a553ed9b19998dc2566800b29..e21773f5fdf5fe7a84f0ce9ba3bff03ea59dd9ca 100644 (file)
@@ -16,6 +16,7 @@
 #
 # @author: Abhishek Raut, Cisco Systems, Inc.
 # @author: Sergey Sudakovich, Cisco Systems, Inc.
+# @author: Rudrajit Tapadar, Cisco Systems, Inc.
 
 from neutron.api import extensions
 from neutron.api.v2 import attributes
@@ -33,6 +34,9 @@ RESOURCE_ATTRIBUTE_MAP = {
                  'is_visible': True, 'default': ''},
         'segment_type': {'allow_post': True, 'allow_put': True,
                          'is_visible': True, 'default': ''},
+        'sub_type': {'allow_post': True, 'allow_put': True,
+                     'is_visible': True,
+                     'default': attributes.ATTR_NOT_SPECIFIED},
         'segment_range': {'allow_post': True, 'allow_put': True,
                           'is_visible': True, 'default': ''},
         'multicast_ip_range': {'allow_post': True, 'allow_put': True,
@@ -82,7 +86,7 @@ class Network_profile(extensions.ExtensionDescriptor):
 
     @classmethod
     def get_resources(cls):
-        """Returns Ext Resources."""
+        """Returns Extended Resources."""
         exts = []
         plugin = manager.NeutronManager.get_plugin()
         for resource_name in ['network_profile', 'network_profile_binding']:
index ceee221372b8afc3b4d30ac6a4e417fac53b73a8..af3c2508309fa2b5c1803a725df6190b938f2802 100644 (file)
@@ -69,7 +69,7 @@ class Policy_profile(extensions.ExtensionDescriptor):
 
     @classmethod
     def get_resources(cls):
-        """Returns Ext Resources."""
+        """Returns Extended Resources."""
         exts = []
         plugin = manager.NeutronManager.get_plugin()
         for resource_name in ['policy_profile', 'policy_profile_binding']:
index 7ce976bc749e475efdf1be9c3d4da32a19f5c85e..7e25aa86ea52320d8590ada12636e92645a10463 100644 (file)
@@ -15,6 +15,7 @@
 #    under the License.
 #
 # @author: Abhishek Raut, Cisco Systems, Inc.
+# @author: Rudrajit Tapadar, Cisco Systems, Inc.
 
 import base64
 import httplib2
@@ -117,7 +118,9 @@ class Client(object):
             "networks": "network",
             "ports": "port",
             "set": "instance",
-            "subnets": "subnet"
+            "subnets": "subnet",
+            "mappings": "mapping",
+            "segments": "segment"
         }
     }
 
@@ -138,6 +141,9 @@ class Client(object):
     logical_networks_path = "/logical-network"
     logical_network_path = "/logical-network/%s"
     events_path = "/kvm/events"
+    clusters_path = "/cluster"
+    encap_profiles_path = "/encapsulation-profile"
+    encap_profile_path = "/encapsulation-profile/%s"
 
     def __init__(self, **kwargs):
         """Initialize a new client for the plugin."""
@@ -171,7 +177,7 @@ class Client(object):
 
         :param network: network dict
         """
-        body = {'name': network['name'] + '_bd',
+        body = {'name': network['name'] + c_const.BRIDGE_DOMAIN_SUFFIX,
                 'segmentId': network[providernet.SEGMENTATION_ID],
                 'groupIp': network[n1kv_profile.MULTICAST_IP], }
         return self._post(self.bridge_domains_path,
@@ -199,7 +205,20 @@ class Client(object):
         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'
+            body['bridgeDomain'] = (network['name'] +
+                                    c_const.BRIDGE_DOMAIN_SUFFIX)
+        if network_profile['segment_type'] == c_const.NETWORK_TYPE_TRUNK:
+            body['mode'] = c_const.NETWORK_TYPE_TRUNK
+            body['segmentType'] = network_profile['sub_type']
+            if network_profile['sub_type'] == c_const.NETWORK_TYPE_VLAN:
+                body['addSegments'] = network['add_segment_list']
+                body['delSegments'] = network['del_segment_list']
+            else:
+                body['encapProfile'] = (network['name'] +
+                                        c_const.ENCAPSULATION_PROFILE_SUFFIX)
+        else:
+            body['mode'] = 'access'
+            body['segmentType'] = network_profile['segment_type']
         return self._post(self.network_segments_path,
                           body=body)
 
@@ -498,3 +517,37 @@ class Client(object):
         auth = base64.encodestring("%s:%s" % (username, password))
         header = {"Authorization": "Basic %s" % auth}
         return header
+
+    def get_clusters(self):
+        """Fetches a list of all vxlan gateway clusters."""
+        return self._get(self.clusters_path)
+
+    def create_encapsulation_profile(self, encap):
+        """
+        Create an encapsulation profile on VSM.
+
+        :param encap: encapsulation dict
+        """
+        body = {'name': encap['name'],
+                'addMappings': encap['add_segment_list'],
+                'delMappings': encap['del_segment_list']}
+        return self._post(self.encap_profiles_path,
+                          body=body)
+
+    def update_encapsulation_profile(self, context, profile_name, body):
+        """
+        Adds a vlan to bridge-domain mapping to an encapsulation profile.
+
+        :param profile_name: Name of the encapsulation profile
+        :param body: mapping dictionary
+        """
+        return self._post(self.encap_profile_path
+                          % (profile_name), body=body)
+
+    def delete_encapsulation_profile(self, name):
+        """
+        Delete an encapsulation profile on VSM.
+
+        :param name: name of the encapsulation profile to be deleted
+        """
+        return self._delete(self.encap_profile_path % (name))
index 5af9b2ea5675cd8e40ecde32b9c0957175ea4c1e..b3cb55f80cb600d5e7977ced4a1db36681d6a6fd 100644 (file)
@@ -30,6 +30,7 @@ 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.common import utils
 from neutron.db import agents_db
 from neutron.db import agentschedulers_db
 from neutron.db import db_base_plugin_v2
@@ -41,6 +42,7 @@ 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.openstack.common import uuidutils as uuidutils
 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
@@ -281,6 +283,14 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         elif binding.network_type == c_const.NETWORK_TYPE_VLAN:
             network[providernet.PHYSICAL_NETWORK] = binding.physical_network
             network[providernet.SEGMENTATION_ID] = binding.segmentation_id
+        elif binding.network_type == c_const.NETWORK_TYPE_TRUNK:
+            network[providernet.PHYSICAL_NETWORK] = binding.physical_network
+            network[providernet.SEGMENTATION_ID] = None
+            network[n1kv_profile.MULTICAST_IP] = None
+        elif binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+            network[providernet.PHYSICAL_NETWORK] = None
+            network[providernet.SEGMENTATION_ID] = None
+            network[n1kv_profile.MULTICAST_IP] = None
 
     def _process_provider_create(self, context, attrs):
         network_type = attrs.get(providernet.NETWORK_TYPE)
@@ -356,6 +366,283 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         msg = _("plugin does not support updating provider attributes")
         raise q_exc.InvalidInput(error_message=msg)
 
+    def _get_cluster(self, segment1, segment2, clusters):
+        """
+        Returns a cluster to apply the segment mapping
+
+        :param segment1: UUID of segment to be mapped
+        :param segment2: UUID of segment to be mapped
+        :param clusters: List of clusters
+        """
+        for cluster in sorted(clusters, key=lambda k: k['size']):
+            for mapping in cluster[c_const.MAPPINGS]:
+                for segment in mapping[c_const.SEGMENTS]:
+                    if segment1 in segment or segment2 in segment:
+                        break
+                else:
+                    cluster['size'] += 2
+                    return cluster['encapProfileName']
+                break
+        return
+
+    def _extend_mapping_dict(self, context, mapping_dict, segment):
+        """
+        Extends a mapping dictionary by populating dot1q tag and
+        bridge-domain name.
+
+        :param context: neutron api request context
+        :param mapping_dict: dictionary to populate values
+        :param segment: id of the segment being populated
+        """
+        net = self.get_network(context, segment)
+        if net[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VLAN:
+            mapping_dict['dot1q'] = str(net[providernet.SEGMENTATION_ID])
+        else:
+            mapping_dict['bridgeDomain'] = (net['name'] +
+                                            c_const.BRIDGE_DOMAIN_SUFFIX)
+
+    def _send_add_multi_segment_request(self, context, net_id, segment_pairs):
+        """
+        Send Add multi-segment network request to VSM.
+
+        :param context: neutron api request context
+        :param net_id: UUID of the multi-segment network
+        :param segment_pairs: List of segments in UUID pairs
+                              that need to be bridged
+        """
+
+        if not segment_pairs:
+            return
+
+        session = context.session
+        n1kvclient = n1kv_client.Client()
+        clusters = n1kvclient.get_clusters()
+        online_clusters = []
+        encap_dict = {}
+        for cluster in clusters['body'][c_const.SET]:
+            cluster = cluster[c_const.PROPERTIES]
+            if cluster[c_const.STATE] == c_const.ONLINE:
+                cluster['size'] = 0
+                for mapping in cluster[c_const.MAPPINGS]:
+                    cluster['size'] += (
+                        len(mapping[c_const.SEGMENTS]))
+                online_clusters.append(cluster)
+        for (segment1, segment2) in segment_pairs:
+            encap_profile = self._get_cluster(segment1, segment2,
+                                              online_clusters)
+            if encap_profile is not None:
+                if encap_profile in encap_dict:
+                    profile_dict = encap_dict[encap_profile]
+                else:
+                    profile_dict = {'name': encap_profile,
+                                    'addMappings': [],
+                                    'delMappings': []}
+                    encap_dict[encap_profile] = profile_dict
+                mapping_dict = {}
+                self._extend_mapping_dict(context,
+                                          mapping_dict, segment1)
+                self._extend_mapping_dict(context,
+                                          mapping_dict, segment2)
+                profile_dict['addMappings'].append(mapping_dict)
+                n1kv_db_v2.add_multi_segment_encap_profile_name(session,
+                                                                net_id,
+                                                                (segment1,
+                                                                segment2),
+                                                                encap_profile)
+            else:
+                raise cisco_exceptions.NoClusterFound
+
+        for profile in encap_dict:
+            n1kvclient.update_encapsulation_profile(context, profile,
+                                                    encap_dict[profile])
+
+    def _send_del_multi_segment_request(self, context, net_id, segment_pairs):
+        """
+        Send Delete multi-segment network request to VSM.
+
+        :param context: neutron api request context
+        :param net_id: UUID of the multi-segment network
+        :param segment_pairs: List of segments in UUID pairs
+                              whose bridging needs to be removed
+        """
+        if not segment_pairs:
+            return
+        session = context.session
+        encap_dict = {}
+        n1kvclient = n1kv_client.Client()
+        for (segment1, segment2) in segment_pairs:
+            binding = (
+                n1kv_db_v2.get_multi_segment_network_binding(session, net_id,
+                                                             (segment1,
+                                                             segment2)))
+            encap_profile = binding['encap_profile_name']
+            if encap_profile in encap_dict:
+                profile_dict = encap_dict[encap_profile]
+            else:
+                profile_dict = {'name': encap_profile,
+                                'addMappings': [],
+                                'delMappings': []}
+                encap_dict[encap_profile] = profile_dict
+            mapping_dict = {}
+            self._extend_mapping_dict(context,
+                                      mapping_dict, segment1)
+            self._extend_mapping_dict(context,
+                                      mapping_dict, segment2)
+            profile_dict['delMappings'].append(mapping_dict)
+
+        for profile in encap_dict:
+            n1kvclient.update_encapsulation_profile(context, profile,
+                                                    encap_dict[profile])
+
+    def _get_encap_segments(self, context, segment_pairs):
+        """
+        Get the list of segments in encapsulation profile format.
+
+        :param context: neutron api request context
+        :param segment_pairs: List of segments that need to be bridged
+        """
+        member_list = []
+        for pair in segment_pairs:
+            (segment, dot1qtag) = pair
+            member_dict = {}
+            net = self.get_network(context, segment)
+            member_dict['bridgeDomain'] = (net['name'] +
+                                           c_const.BRIDGE_DOMAIN_SUFFIX)
+            member_dict['dot1q'] = dot1qtag
+            member_list.append(member_dict)
+        return member_list
+
+    def _populate_member_segments(self, context, network, segment_pairs, oper):
+        """
+        Populate trunk network dict with member segments.
+
+        :param context: neutron api request context
+        :param network: Dictionary containing the trunk network information
+        :param segment_pairs: List of segments in UUID pairs
+                              that needs to be trunked
+        :param oper: Operation to be performed
+        """
+        LOG.debug(_('_populate_member_segments %s'), segment_pairs)
+        trunk_list = []
+        for (segment, dot1qtag) in segment_pairs:
+            net = self.get_network(context, segment)
+            member_dict = {'segment': net['name'],
+                           'dot1qtag': dot1qtag}
+            trunk_list.append(member_dict)
+        if oper == n1kv_profile.SEGMENT_ADD:
+            network['add_segment_list'] = trunk_list
+        elif oper == n1kv_profile.SEGMENT_DEL:
+            network['del_segment_list'] = trunk_list
+
+    def _parse_multi_segments(self, context, attrs, param):
+        """
+        Parse the multi-segment network attributes
+
+        :param context: neutron api request context
+        :param attrs: Attributes of the network
+        :param param: Additional parameter indicating an add
+                      or del operation
+        :returns: List of segment UUIDs in set pairs
+        """
+        pair_list = []
+        valid_seg_types = [c_const.NETWORK_TYPE_VLAN,
+                           c_const.NETWORK_TYPE_VXLAN]
+        segments = attrs.get(param)
+        if not attributes.is_attr_set(segments):
+            return pair_list
+        for pair in segments.split(','):
+            segment1, sep, segment2 = pair.partition(':')
+            if (uuidutils.is_uuid_like(segment1) and
+                    uuidutils.is_uuid_like(segment2)):
+                binding1 = n1kv_db_v2.get_network_binding(context.session,
+                                                          segment1)
+                binding2 = n1kv_db_v2.get_network_binding(context.session,
+                                                          segment2)
+                if (binding1.network_type not in valid_seg_types or
+                        binding2.network_type not in valid_seg_types or
+                        binding1.network_type == binding2.network_type):
+                    msg = _("Invalid pairing supplied")
+                    raise q_exc.InvalidInput(error_message=msg)
+                else:
+                    pair_list.append((segment1, segment2))
+            else:
+                LOG.debug(_('Invalid UUID supplied in %s'), pair)
+                msg = _("Invalid UUID supplied")
+                raise q_exc.InvalidInput(error_message=msg)
+        return pair_list
+
+    def _parse_trunk_segments(self, context, attrs, param, physical_network,
+                              sub_type):
+        """
+        Parse the trunk network attributes
+
+        :param context: neutron api request context
+        :param attrs: Attributes of the network
+        :param param: Additional parameter indicating an add
+                      or del operation
+        :param physical_network: Physical network of the trunk segment
+        :param sub_type: Sub-type of the trunk segment
+        :returns: List of segment UUIDs and dot1qtag (for vxlan) in set pairs
+        """
+        pair_list = []
+        segments = attrs.get(param)
+        if not attributes.is_attr_set(segments):
+            return pair_list
+        for pair in segments.split(','):
+            segment, sep, dot1qtag = pair.partition(':')
+            if sub_type == c_const.NETWORK_TYPE_VLAN:
+                dot1qtag = ''
+            if uuidutils.is_uuid_like(segment):
+                binding = n1kv_db_v2.get_network_binding(context.session,
+                                                         segment)
+                if binding.network_type == c_const.NETWORK_TYPE_TRUNK:
+                    msg = _("Cannot add a trunk segment '%s' as a member of "
+                            "another trunk segment") % segment
+                    raise q_exc.InvalidInput(error_message=msg)
+                elif binding.network_type == c_const.NETWORK_TYPE_VLAN:
+                    if sub_type == c_const.NETWORK_TYPE_VXLAN:
+                        msg = _("Cannot add vlan segment '%s' as a member of "
+                                "a vxlan trunk segment") % segment
+                        raise q_exc.InvalidInput(error_message=msg)
+                    if not physical_network:
+                        physical_network = binding.physical_network
+                    elif physical_network != binding.physical_network:
+                        msg = _("Network UUID '%s' belongs to a different "
+                                "physical network") % segment
+                        raise q_exc.InvalidInput(error_message=msg)
+                elif binding.network_type == c_const.NETWORK_TYPE_VXLAN:
+                    if sub_type == c_const.NETWORK_TYPE_VLAN:
+                        msg = _("Cannot add vxlan segment '%s' as a member of "
+                                "a vlan trunk segment") % segment
+                        raise q_exc.InvalidInput(error_message=msg)
+                    try:
+                        if not utils.is_valid_vlan_tag(int(dot1qtag)):
+                            msg = _("Vlan tag '%s' is out of range") % dot1qtag
+                            raise q_exc.InvalidInput(error_message=msg)
+                    except ValueError:
+                        msg = _("Vlan tag '%s' is not an integer "
+                                "value") % dot1qtag
+                        raise q_exc.InvalidInput(error_message=msg)
+                pair_list.append((segment, dot1qtag))
+            else:
+                LOG.debug(_('%s is not a valid uuid'), segment)
+                msg = _("'%s' is not a valid UUID") % segment
+                raise q_exc.InvalidInput(error_message=msg)
+        return pair_list
+
+    def _extend_network_dict_member_segments(self, context, network):
+        """Add the extended parameter member segments to the network."""
+        members = []
+        binding = n1kv_db_v2.get_network_binding(context.session,
+                                                 network['id'])
+        if binding.network_type == c_const.NETWORK_TYPE_TRUNK:
+            members = n1kv_db_v2.get_trunk_members(context.session,
+                                                   network['id'])
+        elif binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+            members = n1kv_db_v2.get_multi_segment_members(context.session,
+                                                           network['id'])
+        network[n1kv_profile.MEMBER_SEGMENTS] = members
+
     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,
@@ -435,13 +722,15 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         n1kvclient = n1kv_client.Client()
         n1kvclient.delete_network_segment_pool(profile['name'])
 
-    def _send_create_network_request(self, context, network):
+    def _send_create_network_request(self, context, network, segment_pairs):
         """
         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
+        :param segment_pairs: List of segments in UUID pairs
+                              that need to be bridged
         """
         LOG.debug(_('_send_create_network_request: %s'), network['id'])
         profile = self.get_network_profile(context,
@@ -449,36 +738,110 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         n1kvclient = n1kv_client.Client()
         if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VXLAN:
             n1kvclient.create_bridge_domain(network)
+        if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK:
+            self._populate_member_segments(context, network, segment_pairs,
+                                           n1kv_profile.SEGMENT_ADD)
+            network['del_segment_list'] = []
+            if profile['sub_type'] == c_const.NETWORK_TYPE_VXLAN:
+                encap_dict = {'name': (network['name'] +
+                                       c_const.ENCAPSULATION_PROFILE_SUFFIX),
+                              'add_segment_list': (
+                                  self._get_encap_segments(context,
+                                                           segment_pairs)),
+                              'del_segment_list': []}
+                n1kvclient.create_encapsulation_profile(encap_dict)
         n1kvclient.create_network_segment(network, profile)
 
-    def _send_update_network_request(self, db_session, network):
+    def _send_update_network_request(self, context, network, add_segments,
+                                     del_segments):
         """
         Send update network request to VSM.
 
+        :param context: neutron api request context
         :param network: network dictionary
+        :param add_segments: List of segments bindings
+                             that need to be deleted
+        :param del_segments: List of segments bindings
+                             that need to be deleted
         """
         LOG.debug(_('_send_update_network_request: %s'), network['id'])
+        db_session = context.session
         profile = n1kv_db_v2.get_network_profile(
             db_session, network[n1kv_profile.PROFILE_ID])
+        n1kvclient = n1kv_client.Client()
         body = {'name': network['name'],
                 'id': network['id'],
                 'networkDefinition': profile['name'],
-                'vlan': network[providernet.SEGMENTATION_ID]}
-        n1kvclient = n1kv_client.Client()
+                'vlan': network[providernet.SEGMENTATION_ID],
+                'mode': 'access',
+                'segmentType': profile['segment_type'],
+                'addSegments': [],
+                'delSegments': []}
+        if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK:
+            self._populate_member_segments(context, network, add_segments,
+                                           n1kv_profile.SEGMENT_ADD)
+            self._populate_member_segments(context, network, del_segments,
+                                           n1kv_profile.SEGMENT_DEL)
+            body['mode'] = c_const.NETWORK_TYPE_TRUNK
+            body['segmentType'] = profile['sub_type']
+            body['addSegments'] = network['add_segment_list']
+            body['delSegments'] = network['del_segment_list']
+            LOG.debug(_('add_segments=%s'), body['addSegments'])
+            LOG.debug(_('del_segments=%s'), body['delSegments'])
+            if profile['sub_type'] == c_const.NETWORK_TYPE_VXLAN:
+                encap_profile = (network['name'] +
+                                 c_const.ENCAPSULATION_PROFILE_SUFFIX)
+                encap_dict = {'name': encap_profile,
+                              'addMappings': (
+                                  self._get_encap_segments(context,
+                                                           add_segments)),
+                              'delMappings': (
+                                  self._get_encap_segments(context,
+                                                           del_segments))}
+                n1kvclient.update_encapsulation_profile(context, encap_profile,
+                                                        encap_dict)
         n1kvclient.update_network_segment(network['name'], body)
 
-    def _send_delete_network_request(self, network):
+    def _send_delete_network_request(self, context, network):
         """
         Send delete network request to VSM.
 
         Delete bridge domain if network is of type VXLAN.
+        Delete encapsulation profile if network is of type VXLAN Trunk.
+        :param context: neutron api request context
         :param network: network dictionary
         """
         LOG.debug(_('_send_delete_network_request: %s'), network['id'])
         n1kvclient = n1kv_client.Client()
+        session = context.session
         if network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_VXLAN:
-            name = network['name'] + '_bd'
+            name = network['name'] + c_const.BRIDGE_DOMAIN_SUFFIX
             n1kvclient.delete_bridge_domain(name)
+        elif network[providernet.NETWORK_TYPE] == c_const.NETWORK_TYPE_TRUNK:
+            profile = self.get_network_profile(
+                context, network[n1kv_profile.PROFILE_ID])
+            if profile['sub_type'] == c_const.NETWORK_TYPE_VXLAN:
+                profile_name = (network['name'] +
+                                c_const.ENCAPSULATION_PROFILE_SUFFIX)
+                n1kvclient.delete_encapsulation_profile(profile_name)
+        elif (network[providernet.NETWORK_TYPE] ==
+                c_const.NETWORK_TYPE_MULTI_SEGMENT):
+            encap_dict = n1kv_db_v2.get_multi_segment_encap_dict(session,
+                                                                 network['id'])
+            for profile in encap_dict:
+                profile_dict = {'name': profile,
+                                'addSegments': [],
+                                'delSegments': []}
+                for segment_pair in encap_dict[profile]:
+                    mapping_dict = {}
+                    (segment1, segment2) = segment_pair
+                    self._extend_mapping_dict(context,
+                                              mapping_dict, segment1)
+                    self._extend_mapping_dict(context,
+                                              mapping_dict, segment2)
+                    profile_dict['delSegments'].append(mapping_dict)
+                n1kvclient.update_encapsulation_profile(context, profile,
+                                                        profile_dict)
         n1kvclient.delete_network_segment(network['name'])
 
     def _send_create_subnet_request(self, context, subnet):
@@ -616,6 +979,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                                                           network['network'])
         self._add_dummy_profile_only_if_testing(network)
         profile_id = self._process_network_profile(context, network['network'])
+        segment_pairs = None
         LOG.debug(_('create network: profile_id=%s'), profile_id)
         session = context.session
         with session.begin(subtransactions=True):
@@ -632,8 +996,24 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                            'net_type': network_type,
                            'seg_id': segmentation_id,
                            'multicast_ip': multicast_ip})
-                if not segmentation_id:
-                    raise q_exc.TenantNetworksDisabled()
+                if network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+                    segment_pairs = (
+                        self._parse_multi_segments(context, network['network'],
+                                                   n1kv_profile.SEGMENT_ADD))
+                    LOG.debug(_('seg list %s '), segment_pairs)
+                elif network_type == c_const.NETWORK_TYPE_TRUNK:
+                    network_profile = self.get_network_profile(context,
+                                                               profile_id)
+                    segment_pairs = (
+                        self._parse_trunk_segments(context, network['network'],
+                                                   n1kv_profile.SEGMENT_ADD,
+                                                   physical_network,
+                                                   network_profile['sub_type']
+                                                   ))
+                    LOG.debug(_('seg list %s '), segment_pairs)
+                else:
+                    if not segmentation_id:
+                        raise q_exc.TenantNetworksDisabled()
             else:
                 # provider network
                 if network_type == c_const.NETWORK_TYPE_VLAN:
@@ -655,13 +1035,19 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                                            physical_network,
                                            segmentation_id,
                                            multicast_ip,
-                                           profile_id)
+                                           profile_id,
+                                           segment_pairs)
             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)
+            if network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+                self._send_add_multi_segment_request(context, net['id'],
+                                                     segment_pairs)
+            else:
+                self._send_create_network_request(context, net, segment_pairs)
+                # note - exception will rollback entire transaction
         except(cisco_exceptions.VSMError,
                cisco_exceptions.VSMConnectionFailed):
             super(N1kvNeutronPluginV2, self).delete_network(context, net['id'])
@@ -679,15 +1065,52 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         :returns: updated network object
         """
         self._check_provider_update(context, network['network'])
+        add_segments = []
+        del_segments = []
 
         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'])
+            binding = n1kv_db_v2.get_network_binding(session, id)
+            if binding.network_type == c_const.NETWORK_TYPE_MULTI_SEGMENT:
+                add_segments = (
+                    self._parse_multi_segments(context, network['network'],
+                                               n1kv_profile.SEGMENT_ADD))
+                n1kv_db_v2.add_multi_segment_binding(session,
+                                                     net['id'], add_segments)
+                del_segments = (
+                    self._parse_multi_segments(context, network['network'],
+                                               n1kv_profile.SEGMENT_DEL))
+                self._send_add_multi_segment_request(context, net['id'],
+                                                     add_segments)
+                self._send_del_multi_segment_request(context, net['id'],
+                                                     del_segments)
+                n1kv_db_v2.del_multi_segment_binding(session,
+                                                     net['id'], del_segments)
+            elif binding.network_type == c_const.NETWORK_TYPE_TRUNK:
+                network_profile = self.get_network_profile(context,
+                                                           binding.profile_id)
+                add_segments = (
+                    self._parse_trunk_segments(context, network['network'],
+                                               n1kv_profile.SEGMENT_ADD,
+                                               binding.physical_network,
+                                               network_profile['sub_type']))
+                n1kv_db_v2.add_trunk_segment_binding(session,
+                                                     net['id'], add_segments)
+                del_segments = (
+                    self._parse_trunk_segments(context, network['network'],
+                                               n1kv_profile.SEGMENT_DEL,
+                                               binding.physical_network,
+                                               network_profile['sub_type']))
+                n1kv_db_v2.del_trunk_segment_binding(session,
+                                                     net['id'], del_segments)
             self._extend_network_dict_provider(context, net)
             self._extend_network_dict_profile(context, net)
-        self._send_update_network_request(context.session, net)
+        if binding.network_type not in [c_const.NETWORK_TYPE_MULTI_SEGMENT]:
+            self._send_update_network_request(context, net, add_segments,
+                                              del_segments)
         LOG.debug(_("Updated network: %s"), net['id'])
         return net
 
@@ -702,6 +1125,20 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         with session.begin(subtransactions=True):
             binding = n1kv_db_v2.get_network_binding(session, id)
             network = self.get_network(context, id)
+            if n1kv_db_v2.is_trunk_member(session, id):
+                msg = _("Cannot delete a network "
+                        "that is a member of a trunk segment")
+                raise q_exc.InvalidInput(error_message=msg)
+            if n1kv_db_v2.is_multi_segment_member(session, id):
+                msg = _("Cannot delete a network "
+                        "that is a member of a multi-segment network")
+                raise q_exc.InvalidInput(error_message=msg)
+            if self.agent_vsm:
+                try:
+                    self._send_delete_network_request(context, network)
+                except(cisco_exceptions.VSMError,
+                       cisco_exceptions.VSMConnectionFailed):
+                    LOG.debug(_('Delete failed in VSM'))
             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,
@@ -712,8 +1149,6 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
                                         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):
@@ -728,6 +1163,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         net = super(N1kvNeutronPluginV2, self).get_network(context, id, None)
         self._extend_network_dict_provider(context, net)
         self._extend_network_dict_profile(context, net)
+        self._extend_network_dict_member_segments(context, net)
         return self._fields(net, fields)
 
     def get_networks(self, context, filters=None, fields=None):
@@ -969,19 +1405,20 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
         _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)
+        if _network_profile['segment_type'] in [c_const.NETWORK_TYPE_VLAN,
+                                                c_const.NETWORK_TYPE_VXLAN]:
+            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)
+            else:
+                self.vxlan_id_ranges = [(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,
@@ -1018,8 +1455,7 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2,
             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)))
+            self.delete_vxlan_ranges = [(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 a1855a200503644c1f9d1d09cd7f5cc9ec787fce..35d291c30f7794e09dba19db448b80d9ce2754d0 100644 (file)
@@ -16,6 +16,7 @@
 #
 # @author: Juergen Brendel, Cisco Systems Inc.
 # @author: Abhishek Raut, Cisco Systems Inc.
+# @author: Rudrajit Tapadar, Cisco Systems Inc.
 
 from sqlalchemy.orm import exc as s_exc
 from testtools import matchers
@@ -48,6 +49,8 @@ SEGMENT_RANGE_MIN_OVERLAP = '210-230'
 SEGMENT_RANGE_MAX_OVERLAP = '190-209'
 SEGMENT_RANGE_OVERLAP = '190-230'
 TEST_NETWORK_ID = 'abcdefghijklmnopqrstuvwxyz'
+TEST_NETWORK_ID2 = 'abcdefghijklmnopqrstuvwxy2'
+TEST_NETWORK_ID3 = 'abcdefghijklmnopqrstuvwxy3'
 TEST_NETWORK_PROFILE = {'name': 'test_profile',
                         'segment_type': 'vlan',
                         'physical_network': 'physnet1',
@@ -62,6 +65,14 @@ TEST_NETWORK_PROFILE_VXLAN = {'name': 'test_profile',
                               'multicast_ip_range': '239.0.0.70-239.0.0.80'}
 TEST_POLICY_PROFILE = {'id': '4a417990-76fb-11e2-bcfd-0800200c9a66',
                        'name': 'test_policy_profile'}
+TEST_NETWORK_PROFILE_MULTI_SEGMENT = {'name': 'test_profile',
+                                      'segment_type': 'multi-segment'}
+TEST_NETWORK_PROFILE_VLAN_TRUNK = {'name': 'test_profile',
+                                   'segment_type': 'trunk',
+                                   'sub_type': 'vlan'}
+TEST_NETWORK_PROFILE_VXLAN_TRUNK = {'name': 'test_profile',
+                                    'segment_type': 'trunk',
+                                    'sub_type': 'vxlan'}
 
 
 def _create_test_network_profile_if_not_there(session,
@@ -398,7 +409,7 @@ class NetworkBindingsTest(test_plugin.NeutronDbPluginV2TestCase):
             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)
+                PHYS_NET, 1234, '0.0.0.0', p.id, None)
             binding = n1kv_db_v2.get_network_binding(
                 self.session, TEST_NETWORK_ID)
             self.assertIsNotNone(binding)
@@ -407,6 +418,224 @@ class NetworkBindingsTest(test_plugin.NeutronDbPluginV2TestCase):
             self.assertEqual(binding.physical_network, PHYS_NET)
             self.assertEqual(binding.segmentation_id, 1234)
 
+    def test_create_multi_segment_network(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,
+                TEST_NETWORK_PROFILE_MULTI_SEGMENT)
+            n1kv_db_v2.add_network_binding(
+                self.session, TEST_NETWORK_ID, 'multi-segment',
+                None, 0, '0.0.0.0', p.id, None)
+            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, 'multi-segment')
+            self.assertIsNone(binding.physical_network)
+            self.assertEqual(binding.segmentation_id, 0)
+
+    def test_add_multi_segment_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,
+                TEST_NETWORK_PROFILE_MULTI_SEGMENT)
+            n1kv_db_v2.add_network_binding(
+                self.session, TEST_NETWORK_ID, 'multi-segment',
+                None, 0, '0.0.0.0', p.id,
+                [(TEST_NETWORK_ID2, TEST_NETWORK_ID3)])
+            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, 'multi-segment')
+            self.assertIsNone(binding.physical_network)
+            self.assertEqual(binding.segmentation_id, 0)
+            ms_binding = (n1kv_db_v2.get_multi_segment_network_binding(
+                          self.session, TEST_NETWORK_ID,
+                          (TEST_NETWORK_ID2, TEST_NETWORK_ID3)))
+            self.assertIsNotNone(ms_binding)
+            self.assertEqual(ms_binding.multi_segment_id, TEST_NETWORK_ID)
+            self.assertEqual(ms_binding.segment1_id, TEST_NETWORK_ID2)
+            self.assertEqual(ms_binding.segment2_id, TEST_NETWORK_ID3)
+            ms_members = (n1kv_db_v2.get_multi_segment_members(
+                          self.session, TEST_NETWORK_ID))
+            self.assertEqual(ms_members,
+                             [(TEST_NETWORK_ID2, TEST_NETWORK_ID3)])
+            self.assertTrue(n1kv_db_v2.is_multi_segment_member(
+                            self.session, TEST_NETWORK_ID2))
+            self.assertTrue(n1kv_db_v2.is_multi_segment_member(
+                            self.session, TEST_NETWORK_ID3))
+            n1kv_db_v2.del_multi_segment_binding(
+                self.session, TEST_NETWORK_ID,
+                [(TEST_NETWORK_ID2, TEST_NETWORK_ID3)])
+            ms_members = (n1kv_db_v2.get_multi_segment_members(
+                          self.session, TEST_NETWORK_ID))
+            self.assertEqual(ms_members, [])
+
+    def test_create_vlan_trunk_network(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,
+                TEST_NETWORK_PROFILE_VLAN_TRUNK)
+            n1kv_db_v2.add_network_binding(
+                self.session, TEST_NETWORK_ID, 'trunk',
+                None, 0, '0.0.0.0', p.id, None)
+            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, 'trunk')
+            self.assertIsNone(binding.physical_network)
+            self.assertEqual(binding.segmentation_id, 0)
+
+    def test_create_vxlan_trunk_network(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,
+                TEST_NETWORK_PROFILE_VXLAN_TRUNK)
+            n1kv_db_v2.add_network_binding(
+                self.session, TEST_NETWORK_ID, 'trunk',
+                None, 0, '0.0.0.0', p.id, None)
+            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, 'trunk')
+            self.assertIsNone(binding.physical_network)
+            self.assertEqual(binding.segmentation_id, 0)
+
+    def test_add_vlan_trunk_binding(self):
+        with self.network() as network1:
+            with self.network() as network2:
+                TEST_NETWORK_ID = network1['network']['id']
+
+                self.assertRaises(c_exc.NetworkBindingNotFound,
+                                  n1kv_db_v2.get_network_binding,
+                                  self.session,
+                                  TEST_NETWORK_ID)
+                TEST_NETWORK_ID2 = network2['network']['id']
+                self.assertRaises(c_exc.NetworkBindingNotFound,
+                                  n1kv_db_v2.get_network_binding,
+                                  self.session,
+                                  TEST_NETWORK_ID2)
+                p_v = _create_test_network_profile_if_not_there(self.session)
+                n1kv_db_v2.add_network_binding(
+                    self.session, TEST_NETWORK_ID2, 'vlan',
+                    PHYS_NET, 1234, '0.0.0.0', p_v.id, None)
+                p = _create_test_network_profile_if_not_there(
+                    self.session,
+                    TEST_NETWORK_PROFILE_VLAN_TRUNK)
+                n1kv_db_v2.add_network_binding(
+                    self.session, TEST_NETWORK_ID, 'trunk',
+                    None, 0, '0.0.0.0', p.id, [(TEST_NETWORK_ID2, 0)])
+                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, 'trunk')
+                self.assertEqual(binding.physical_network, PHYS_NET)
+                self.assertEqual(binding.segmentation_id, 0)
+                t_binding = (n1kv_db_v2.get_trunk_network_binding(
+                             self.session, TEST_NETWORK_ID,
+                             (TEST_NETWORK_ID2, 0)))
+                self.assertIsNotNone(t_binding)
+                self.assertEqual(t_binding.trunk_segment_id, TEST_NETWORK_ID)
+                self.assertEqual(t_binding.segment_id, TEST_NETWORK_ID2)
+                self.assertEqual(t_binding.dot1qtag, '0')
+                t_members = (n1kv_db_v2.get_trunk_members(
+                    self.session, TEST_NETWORK_ID))
+                self.assertEqual(t_members,
+                                 [(TEST_NETWORK_ID2, '0')])
+                self.assertTrue(n1kv_db_v2.is_trunk_member(
+                                self.session, TEST_NETWORK_ID2))
+                n1kv_db_v2.del_trunk_segment_binding(
+                    self.session, TEST_NETWORK_ID,
+                    [(TEST_NETWORK_ID2, '0')])
+                t_members = (n1kv_db_v2.get_multi_segment_members(
+                    self.session, TEST_NETWORK_ID))
+                self.assertEqual(t_members, [])
+
+    def test_add_vxlan_trunk_binding(self):
+        with self.network() as network1:
+            with self.network() as network2:
+                TEST_NETWORK_ID = network1['network']['id']
+
+                self.assertRaises(c_exc.NetworkBindingNotFound,
+                                  n1kv_db_v2.get_network_binding,
+                                  self.session,
+                                  TEST_NETWORK_ID)
+                TEST_NETWORK_ID2 = network2['network']['id']
+                self.assertRaises(c_exc.NetworkBindingNotFound,
+                                  n1kv_db_v2.get_network_binding,
+                                  self.session,
+                                  TEST_NETWORK_ID2)
+                p_v = _create_test_network_profile_if_not_there(
+                    self.session, TEST_NETWORK_PROFILE_VXLAN_TRUNK)
+                n1kv_db_v2.add_network_binding(
+                    self.session, TEST_NETWORK_ID2, 'vxlan',
+                    None, 5100, '224.10.10.10', p_v.id, None)
+                p = _create_test_network_profile_if_not_there(
+                    self.session,
+                    TEST_NETWORK_PROFILE_VXLAN_TRUNK)
+                n1kv_db_v2.add_network_binding(
+                    self.session, TEST_NETWORK_ID, 'trunk',
+                    None, 0, '0.0.0.0', p.id,
+                    [(TEST_NETWORK_ID2, 5)])
+                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, 'trunk')
+                self.assertIsNone(binding.physical_network)
+                self.assertEqual(binding.segmentation_id, 0)
+                t_binding = (n1kv_db_v2.get_trunk_network_binding(
+                             self.session, TEST_NETWORK_ID,
+                             (TEST_NETWORK_ID2, '5')))
+                self.assertIsNotNone(t_binding)
+                self.assertEqual(t_binding.trunk_segment_id, TEST_NETWORK_ID)
+                self.assertEqual(t_binding.segment_id, TEST_NETWORK_ID2)
+                self.assertEqual(t_binding.dot1qtag, '5')
+                t_members = (n1kv_db_v2.get_trunk_members(
+                    self.session, TEST_NETWORK_ID))
+                self.assertEqual(t_members,
+                                 [(TEST_NETWORK_ID2, '5')])
+                self.assertTrue(n1kv_db_v2.is_trunk_member(
+                    self.session, TEST_NETWORK_ID2))
+                n1kv_db_v2.del_trunk_segment_binding(
+                    self.session, TEST_NETWORK_ID,
+                    [(TEST_NETWORK_ID2, '5')])
+                t_members = (n1kv_db_v2.get_multi_segment_members(
+                    self.session, TEST_NETWORK_ID))
+                self.assertEqual(t_members, [])
+
 
 class NetworkProfileTests(base.BaseTestCase,
                           n1kv_db_v2.NetworkProfile_db_mixin):
@@ -436,6 +665,63 @@ class NetworkProfileTests(base.BaseTestCase,
                          db_profile.multicast_ip_range)
         n1kv_db_v2.delete_network_profile(self.session, _db_profile.id)
 
+    def test_create_multi_segment_network_profile(self):
+        _db_profile = (n1kv_db_v2.create_network_profile(
+                       self.session, TEST_NETWORK_PROFILE_MULTI_SEGMENT))
+        self.assertIsNotNone(_db_profile)
+        db_profile = (self.session.query(n1kv_models_v2.NetworkProfile).
+                      filter_by(
+                      name=TEST_NETWORK_PROFILE_MULTI_SEGMENT['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_vlan_trunk_network_profile(self):
+        _db_profile = (n1kv_db_v2.create_network_profile(
+                       self.session, TEST_NETWORK_PROFILE_VLAN_TRUNK))
+        self.assertIsNotNone(_db_profile)
+        db_profile = (self.session.query(n1kv_models_v2.NetworkProfile).
+                      filter_by(name=TEST_NETWORK_PROFILE_VLAN_TRUNK['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)
+        self.assertEqual(_db_profile.sub_type, db_profile.sub_type)
+        n1kv_db_v2.delete_network_profile(self.session, _db_profile.id)
+
+    def test_create_vxlan_trunk_network_profile(self):
+        _db_profile = (n1kv_db_v2.create_network_profile(
+                       self.session, TEST_NETWORK_PROFILE_VXLAN_TRUNK))
+        self.assertIsNotNone(_db_profile)
+        db_profile = (self.session.query(n1kv_models_v2.NetworkProfile).
+                      filter_by(name=TEST_NETWORK_PROFILE_VXLAN_TRUNK['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)
+        self.assertEqual(_db_profile.sub_type, db_profile.sub_type)
+        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)