]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add partial specs support in ML2 for vlan provider networks
authorCedric Brandily <zzelle@gmail.com>
Mon, 16 Jun 2014 20:43:13 +0000 (22:43 +0200)
committerCedric Brandily <zzelle@gmail.com>
Sat, 19 Jul 2014 08:40:49 +0000 (10:40 +0200)
ML2 provider networks partial specs let admins choose some provider
network attributes and let neutron choose remaining attributes. This
change provides the implementation for VLAN provider networks.

In practice, for VLAN provider networks provider:physical_network
and provider:segmentation_id choices can be delegated to neutron,
in such case neutron will try to find a network in tenant network
pools which respects provided provider attributes.

DocImpact

Related to blueprint provider-network-partial-specs
Partial-Bug: #1330562

Change-Id: I2c52c71167edaa153b2e04681273e2f1be8d03aa

16 files changed:
neutron/common/exceptions.py
neutron/plugins/ml2/driver_api.py
neutron/plugins/ml2/drivers/helpers.py [new file with mode: 0644]
neutron/plugins/ml2/drivers/type_flat.py
neutron/plugins/ml2/drivers/type_gre.py
neutron/plugins/ml2/drivers/type_local.py
neutron/plugins/ml2/drivers/type_vlan.py
neutron/plugins/ml2/drivers/type_vxlan.py
neutron/plugins/ml2/managers.py
neutron/plugins/ml2/plugin.py
neutron/tests/unit/ml2/test_helpers.py [new file with mode: 0644]
neutron/tests/unit/ml2/test_type_flat.py
neutron/tests/unit/ml2/test_type_gre.py
neutron/tests/unit/ml2/test_type_local.py
neutron/tests/unit/ml2/test_type_vlan.py
neutron/tests/unit/ml2/test_type_vxlan.py

index 7fa63affd698cc643a499dee7c1299638b12f35d..3124ea781934d932b92644b810e0e78b36b16f6e 100644 (file)
@@ -179,6 +179,11 @@ class NoNetworkAvailable(ResourceExhausted):
                 "No tenant network is available for allocation.")
 
 
+class NoNetworkFoundInMaximumAllowedAttempts(ServiceUnavailable):
+    message = _("Unable to create the network. "
+                "No available network found in maximum allowed attempts.")
+
+
 class SubnetMismatchForPort(BadRequest):
     message = _("Subnet on port %(port_id)s does not match "
                 "the requested subnet %(subnet_id)s")
index 2384b0cf9d68c7cece3ec2b7e89be8a429d667ad..3b7e210acb404db5347f9eb9183f2fd597abc92c 100644 (file)
@@ -88,7 +88,8 @@ class TypeDriver(object):
         """Reserve resource associated with a provider network segment.
 
         :param session: database session
-        :param segment: segment dictionary using keys defined above
+        :param segment: segment dictionary
+        :returns: segment dictionary
 
         Called inside transaction context on session to reserve the
         type-specific resource for a provider network segment. The
diff --git a/neutron/plugins/ml2/drivers/helpers.py b/neutron/plugins/ml2/drivers/helpers.py
new file mode 100644 (file)
index 0000000..9e73a8b
--- /dev/null
@@ -0,0 +1,140 @@
+# Copyright (c) 2014 Thales Services SAS
+# 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
+#    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.
+
+from oslo.db import exception as db_exc
+
+from neutron.common import exceptions as exc
+from neutron.openstack.common import log
+from neutron.plugins.ml2 import driver_api as api
+
+
+# Number of retries to find a valid segment candidate and allocate it
+DB_MAX_RETRIES = 10
+
+
+LOG = log.getLogger(__name__)
+
+
+class TypeDriverHelper(api.TypeDriver):
+    """TypeDriver Helper for segment allocation.
+
+    Provide methods helping to perform segment allocation fully or partially
+    specified.
+    """
+
+    def __init__(self, model):
+        self.model = model
+        self.primary_keys = set(dict(model.__table__.columns))
+        self.primary_keys.remove("allocated")
+
+    def allocate_fully_specified_segment(self, session, **raw_segment):
+        """Allocate segment fully specified by raw_segment.
+
+        If segment exists, then try to allocate it and return db object
+        If segment does not exists, then try to create it and return db object
+        If allocation/creation failed, then return None
+        """
+
+        network_type = self.get_type()
+        try:
+            with session.begin(subtransactions=True):
+                alloc = (session.query(self.model).filter_by(**raw_segment).
+                         first())
+                if alloc:
+                    if alloc.allocated:
+                        # Segment already allocated
+                        return
+                    else:
+                        # Segment not allocated
+                        LOG.debug("%(type)s segment %(segment)s allocate "
+                                  "started ",
+                                  type=network_type, segment=raw_segment)
+                        count = (session.query(self.model).
+                                 filter_by(allocated=False, **raw_segment).
+                                 update({"allocated": True}))
+                        if count:
+                            LOG.debug("%(type)s segment %(segment)s allocate "
+                                      "done ",
+                                      type=network_type, segment=raw_segment)
+                            return alloc
+
+                        # Segment allocated or deleted since select
+                        LOG.debug("%(type)s segment %(segment)s allocate "
+                                  "failed: segment has been allocated or "
+                                  "deleted",
+                                  type=network_type, segment=raw_segment)
+
+                # Segment to create or already allocated
+                LOG.debug("%(type)s segment %(segment)s create started",
+                          type=network_type, segment=raw_segment)
+                alloc = self.model(allocated=True, **raw_segment)
+                alloc.save(session)
+                LOG.debug("%(type)s segment %(segment)s create done",
+                          type=network_type, segment=raw_segment)
+
+        except db_exc.DBDuplicateEntry:
+            # Segment already allocated (insert failure)
+            alloc = None
+            LOG.debug("%(type)s segment %(segment)s create failed",
+                      type=network_type, segment=raw_segment)
+
+        return alloc
+
+    def allocate_partially_specified_segment(self, session, **filters):
+        """Allocate model segment from pool partially specified by filters.
+
+        Return allocated db object or None.
+        """
+
+        network_type = self.get_type()
+        with session.begin(subtransactions=True):
+            select = (session.query(self.model).
+                      filter_by(allocated=False, **filters))
+
+            # Selected segment can be allocated before update by someone else,
+            # We retry until update success or DB_MAX_RETRIES retries
+            for attempt in range(1, DB_MAX_RETRIES + 1):
+                alloc = select.first()
+
+                if not alloc:
+                    # No resource available
+                    return
+
+                raw_segment = dict((k, alloc[k]) for k in self.primary_keys)
+                LOG.debug("%(type)s segment allocate from pool, attempt "
+                          "%(attempt)s started with %(segment)s ",
+                          type=network_type, attempt=attempt,
+                          segment=raw_segment)
+                count = (session.query(self.model).
+                         filter_by(allocated=False, **raw_segment).
+                         update({"allocated": True}))
+                if count:
+                    LOG.debug("%(type)s segment allocate from pool, attempt "
+                              "%(attempt)s success with %(segment)s ",
+                              type=network_type, attempt=attempt,
+                              segment=raw_segment)
+                    return alloc
+
+                # Segment allocated since select
+                LOG.debug("Allocate %(type)s segment from pool, "
+                          "attempt %(attempt)s failed with segment "
+                          "%(segment)s",
+                          type=network_type, attempt=attempt,
+                          segment=raw_segment)
+
+        LOG.warning(_("Allocate %(type)s segment from pool failed "
+                      "after %(number)s failed attempts"),
+                    {"type": network_type, "number": DB_MAX_RETRIES})
+        raise exc.NoNetworkFoundInMaximumAllowedAttempts
index 4597fe8945988473b1cbc22a20201d8f1f0a7e8f..ec9c4edbfdc13f0f4ef8e60ac799b9a4445c494d 100644 (file)
@@ -14,6 +14,7 @@
 #    under the License.
 
 from oslo.config import cfg
+from oslo.db import exception as db_exc
 import sqlalchemy as sa
 
 from neutron.common import exceptions as exc
@@ -100,17 +101,14 @@ class FlatTypeDriver(api.TypeDriver):
         physical_network = segment[api.PHYSICAL_NETWORK]
         with session.begin(subtransactions=True):
             try:
-                alloc = (session.query(FlatAllocation).
-                         filter_by(physical_network=physical_network).
-                         with_lockmode('update').
-                         one())
-                raise exc.FlatNetworkInUse(
-                    physical_network=physical_network)
-            except sa.orm.exc.NoResultFound:
                 LOG.debug(_("Reserving flat network on physical "
                             "network %s"), physical_network)
                 alloc = FlatAllocation(physical_network=physical_network)
-                session.add(alloc)
+                alloc.save(session)
+            except db_exc.DBDuplicateEntry:
+                raise exc.FlatNetworkInUse(
+                    physical_network=physical_network)
+        return segment
 
     def allocate_tenant_segment(self, session):
         # Tenant flat networks are not supported.
index b9a00c0cb86b3a1741b1f8bafff2eab27b661323..1330a48f0d7b5a874c992cf75a2311280acf33bd 100644 (file)
@@ -91,6 +91,7 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
                 alloc = GreAllocation(gre_id=segmentation_id)
                 alloc.allocated = True
                 session.add(alloc)
+        return segment
 
     def allocate_tenant_segment(self, session):
         with session.begin(subtransactions=True):
index e0281a24534331147c59cddb48a539e0534b3dbc..df22302f96b13c36239572b2de47ab0e21cc84e5 100644 (file)
@@ -48,7 +48,7 @@ class LocalTypeDriver(api.TypeDriver):
 
     def reserve_provider_segment(self, session, segment):
         # No resources to reserve
-        pass
+        return segment
 
     def allocate_tenant_segment(self, session):
         # No resources to allocate
index c35ba3ce4be59a09f81722a6062bcc774e23990a..35cbace21cf236cd8b93cc125823c09487bb4757 100644 (file)
@@ -28,6 +28,7 @@ from neutron.openstack.common import log
 from neutron.plugins.common import constants as p_const
 from neutron.plugins.common import utils as plugin_utils
 from neutron.plugins.ml2 import driver_api as api
+from neutron.plugins.ml2.drivers import helpers
 
 LOG = log.getLogger(__name__)
 
@@ -67,7 +68,7 @@ class VlanAllocation(model_base.BASEV2):
     allocated = sa.Column(sa.Boolean, nullable=False)
 
 
-class VlanTypeDriver(api.TypeDriver):
+class VlanTypeDriver(helpers.TypeDriverHelper):
     """Manage state for VLAN networks with ML2.
 
     The VlanTypeDriver implements the 'vlan' network_type. VLAN
@@ -79,6 +80,7 @@ class VlanTypeDriver(api.TypeDriver):
     """
 
     def __init__(self):
+        super(VlanTypeDriver, self).__init__(VlanAllocation)
         self._parse_network_vlan_ranges()
 
     def _parse_network_vlan_ranges(self):
@@ -160,25 +162,27 @@ class VlanTypeDriver(api.TypeDriver):
         self._sync_vlan_allocations()
         LOG.info(_("VlanTypeDriver initialization complete"))
 
+    def is_partial_segment(self, segment):
+        return segment.get(api.SEGMENTATION_ID) is None
+
     def validate_provider_segment(self, segment):
         physical_network = segment.get(api.PHYSICAL_NETWORK)
-        if not physical_network:
-            msg = _("physical_network required for VLAN provider network")
-            raise exc.InvalidInput(error_message=msg)
-        if physical_network not in self.network_vlan_ranges:
-            msg = (_("physical_network '%s' unknown for VLAN provider network")
-                   % physical_network)
-            raise exc.InvalidInput(error_message=msg)
-
         segmentation_id = segment.get(api.SEGMENTATION_ID)
-        if segmentation_id is None:
-            msg = _("segmentation_id required for VLAN provider network")
-            raise exc.InvalidInput(error_message=msg)
-        if not utils.is_valid_vlan_tag(segmentation_id):
-            msg = (_("segmentation_id out of range (%(min)s through "
-                     "%(max)s)") %
-                   {'min': q_const.MIN_VLAN_TAG,
-                    'max': q_const.MAX_VLAN_TAG})
+        if physical_network:
+            if physical_network not in self.network_vlan_ranges:
+                msg = (_("physical_network '%s' unknown "
+                         " for VLAN provider network") % physical_network)
+                raise exc.InvalidInput(error_message=msg)
+            if segmentation_id:
+                if not utils.is_valid_vlan_tag(segmentation_id):
+                    msg = (_("segmentation_id out of range (%(min)s through "
+                             "%(max)s)") %
+                           {'min': q_const.MIN_VLAN_TAG,
+                            'max': q_const.MAX_VLAN_TAG})
+                    raise exc.InvalidInput(error_message=msg)
+        elif segmentation_id:
+            msg = _("segmentation_id requires physical_network for VLAN "
+                    "provider network")
             raise exc.InvalidInput(error_message=msg)
 
         for key, value in segment.items():
@@ -189,48 +193,36 @@ class VlanTypeDriver(api.TypeDriver):
                 raise exc.InvalidInput(error_message=msg)
 
     def reserve_provider_segment(self, session, segment):
-        physical_network = segment[api.PHYSICAL_NETWORK]
-        vlan_id = segment[api.SEGMENTATION_ID]
-        with session.begin(subtransactions=True):
-            try:
-                alloc = (session.query(VlanAllocation).
-                         filter_by(physical_network=physical_network,
-                                   vlan_id=vlan_id).
-                         with_lockmode('update').
-                         one())
-                if alloc.allocated:
-                    raise exc.VlanIdInUse(vlan_id=vlan_id,
-                                          physical_network=physical_network)
-                LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
-                            "network %(physical_network)s from pool"),
-                          {'vlan_id': vlan_id,
-                           'physical_network': physical_network})
-                alloc.allocated = True
-            except sa.orm.exc.NoResultFound:
-                LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
-                            "network %(physical_network)s outside pool"),
-                          {'vlan_id': vlan_id,
-                           'physical_network': physical_network})
-                alloc = VlanAllocation(physical_network=physical_network,
-                                       vlan_id=vlan_id,
-                                       allocated=True)
-                session.add(alloc)
+        filters = {}
+        physical_network = segment.get(api.PHYSICAL_NETWORK)
+        if physical_network is not None:
+            filters['physical_network'] = physical_network
+            vlan_id = segment.get(api.SEGMENTATION_ID)
+            if vlan_id is not None:
+                filters['vlan_id'] = vlan_id
+
+        if self.is_partial_segment(segment):
+            alloc = self.allocate_partially_specified_segment(
+                session, **filters)
+            if not alloc:
+                raise exc.NoNetworkAvailable
+        else:
+            alloc = self.allocate_fully_specified_segment(
+                session, **filters)
+            if not alloc:
+                raise exc.VlanIdInUse(**filters)
+
+        return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
+                api.PHYSICAL_NETWORK: alloc.physical_network,
+                api.SEGMENTATION_ID: alloc.vlan_id}
 
     def allocate_tenant_segment(self, session):
-        with session.begin(subtransactions=True):
-            alloc = (session.query(VlanAllocation).
-                     filter_by(allocated=False).
-                     with_lockmode('update').
-                     first())
-            if alloc:
-                LOG.debug(_("Allocating vlan %(vlan_id)s on physical network "
-                            "%(physical_network)s from pool"),
-                          {'vlan_id': alloc.vlan_id,
-                           'physical_network': alloc.physical_network})
-                alloc.allocated = True
-                return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
-                        api.PHYSICAL_NETWORK: alloc.physical_network,
-                        api.SEGMENTATION_ID: alloc.vlan_id}
+        alloc = self.allocate_partially_specified_segment(session)
+        if not alloc:
+            return
+        return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
+                api.PHYSICAL_NETWORK: alloc.physical_network,
+                api.SEGMENTATION_ID: alloc.vlan_id}
 
     def release_segment(self, session, segment):
         physical_network = segment[api.PHYSICAL_NETWORK]
index b5aeadbb622ebc158a40d9a6dff6d4b87a5d0269..918a9b6bc3cd12d008c788a6c36ae70e4db877de 100644 (file)
@@ -99,6 +99,7 @@ class VxlanTypeDriver(type_tunnel.TunnelTypeDriver):
                 alloc = VxlanAllocation(vxlan_vni=segmentation_id)
                 alloc.allocated = True
                 session.add(alloc)
+        return segment
 
     def allocate_tenant_segment(self, session):
         with session.begin(subtransactions=True):
index 13df6732e2fce29da6d85b2a67f54542dd6d72e4..d7b64fa4e6b9fa0e62928dd84fc88b66f15b2778 100644 (file)
@@ -85,7 +85,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
     def reserve_provider_segment(self, session, segment):
         network_type = segment.get(api.NETWORK_TYPE)
         driver = self.drivers.get(network_type)
-        driver.obj.reserve_provider_segment(session, segment)
+        return driver.obj.reserve_provider_segment(session, segment)
 
     def allocate_tenant_segment(self, session):
         for network_type in self.tenant_network_types:
index 4a46bf5482961caace5b8f6cb63def68d4342a46..c29114a8471a83a0b0792fbd490901785d97c56a 100644 (file)
@@ -378,8 +378,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
             # to TypeManager.
             if segments:
                 for segment in segments:
-                    self.type_manager.reserve_provider_segment(session,
-                                                               segment)
+                    segment = self.type_manager.reserve_provider_segment(
+                        session, segment)
                     db.add_network_segment(session, network_id, segment)
             else:
                 segment = self.type_manager.allocate_tenant_segment(session)
diff --git a/neutron/tests/unit/ml2/test_helpers.py b/neutron/tests/unit/ml2/test_helpers.py
new file mode 100644 (file)
index 0000000..24f8eea
--- /dev/null
@@ -0,0 +1,141 @@
+# Copyright (c) 2014 Thales Services SAS
+# 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
+#    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.
+
+import mock
+from sqlalchemy.orm import query
+
+from neutron.common import exceptions as exc
+import neutron.db.api as db
+from neutron.plugins.ml2.drivers import helpers
+from neutron.plugins.ml2.drivers import type_vlan
+from neutron.tests import base
+
+
+TENANT_NET = 'phys_net2'
+VLAN_MIN = 200
+VLAN_MAX = 209
+VLAN_OUTSIDE = 100
+NETWORK_VLAN_RANGES = {
+    TENANT_NET: [(VLAN_MIN, VLAN_MAX)],
+}
+
+
+class HelpersTest(base.BaseTestCase):
+
+    def setUp(self):
+        super(HelpersTest, self).setUp()
+        db.configure_db()
+        self.driver = type_vlan.VlanTypeDriver()
+        self.driver.network_vlan_ranges = NETWORK_VLAN_RANGES
+        self.driver._sync_vlan_allocations()
+        self.session = db.get_session()
+        self.addCleanup(db.clear_db)
+
+    def check_raw_segment(self, expected, observed):
+        for key, value in expected.items():
+            self.assertEqual(value, observed[key])
+
+    def test_primary_keys(self):
+        self.assertEqual(set(['physical_network', 'vlan_id']),
+                         self.driver.primary_keys)
+
+    def test_allocate_specific_unallocated_segment_in_pools(self):
+        expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
+        observed = self.driver.allocate_fully_specified_segment(self.session,
+                                                                **expected)
+        self.check_raw_segment(expected, observed)
+
+    def test_allocate_specific_allocated_segment_in_pools(self):
+        raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
+        self.driver.allocate_fully_specified_segment(self.session,
+                                                     **raw_segment)
+        observed = self.driver.allocate_fully_specified_segment(self.session,
+                                                                **raw_segment)
+        self.assertIsNone(observed)
+
+    def test_allocate_specific_finally_allocated_segment_in_pools(self):
+        # Test case: allocate a specific unallocated segment in pools but
+        # the segment is allocated concurrently between select and update
+
+        raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
+        with mock.patch.object(query.Query, 'update', return_value=0):
+            observed = self.driver.allocate_fully_specified_segment(
+                self.session, **raw_segment)
+            self.assertIsNone(observed)
+
+    def test_allocate_specific_unallocated_segment_outside_pools(self):
+        expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE)
+        observed = self.driver.allocate_fully_specified_segment(self.session,
+                                                                **expected)
+        self.check_raw_segment(expected, observed)
+
+    def test_allocate_specific_allocated_segment_outside_pools(self):
+        raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE)
+        self.driver.allocate_fully_specified_segment(self.session,
+                                                     **raw_segment)
+        observed = self.driver.allocate_fully_specified_segment(self.session,
+                                                                **raw_segment)
+        self.assertIsNone(observed)
+
+    def test_allocate_specific_finally_unallocated_segment_outside_pools(self):
+        # Test case: allocate a specific allocated segment in pools but
+        # the segment is concurrently unallocated after select or update
+
+        expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
+        with mock.patch.object(self.driver.model, 'save'):
+            observed = self.driver.allocate_fully_specified_segment(
+                self.session, **expected)
+            self.check_raw_segment(expected, observed)
+
+    def test_allocate_partial_segment_without_filters(self):
+        expected = dict(physical_network=TENANT_NET)
+        observed = self.driver.allocate_partially_specified_segment(
+            self.session)
+        self.check_raw_segment(expected, observed)
+
+    def test_allocate_partial_segment_with_filter(self):
+        expected = dict(physical_network=TENANT_NET)
+        observed = self.driver.allocate_partially_specified_segment(
+            self.session, **expected)
+        self.check_raw_segment(expected, observed)
+
+    def test_allocate_partial_segment_no_resource_available(self):
+        for i in range(VLAN_MIN, VLAN_MAX + 1):
+            self.driver.allocate_partially_specified_segment(self.session)
+        observed = self.driver.allocate_partially_specified_segment(
+            self.session)
+        self.assertIsNone(observed)
+
+    def test_allocate_partial_segment_outside_pools(self):
+        raw_segment = dict(physical_network='other_phys_net')
+        observed = self.driver.allocate_partially_specified_segment(
+            self.session, **raw_segment)
+        self.assertIsNone(observed)
+
+    def test_allocate_partial_segment_first_attempt_fails(self):
+        expected = dict(physical_network=TENANT_NET)
+        with mock.patch.object(query.Query, 'update', side_effect=[0, 1]):
+            observed = self.driver.allocate_partially_specified_segment(
+                self.session, **expected)
+            self.check_raw_segment(expected, observed)
+
+    def test_allocate_partial_segment_all_attempts_fail(self):
+        with mock.patch.object(query.Query, 'update', return_value=0):
+            with mock.patch.object(helpers.LOG, 'warning') as log_warning:
+                self.assertRaises(
+                    exc.NoNetworkFoundInMaximumAllowedAttempts,
+                    self.driver.allocate_partially_specified_segment,
+                    self.session)
+                log_warning.assert_called_once_with(mock.ANY, mock.ANY)
index 759424e8ffd0796eb05075985e67a9781c4b5cbf..35c3a46121c79d81fa4da20aa78e83ed8229ccd0 100644 (file)
@@ -85,8 +85,8 @@ class FlatTypeTest(base.BaseTestCase):
     def test_reserve_provider_segment(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT,
                    api.PHYSICAL_NETWORK: 'flat_net1'}
-        self.driver.reserve_provider_segment(self.session, segment)
-        alloc = self._get_allocation(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        alloc = self._get_allocation(self.session, observed)
         self.assertEqual(segment[api.PHYSICAL_NETWORK], alloc.physical_network)
 
     def test_release_segment(self):
index 36420e2b6e1a068bfb8462d74424fe1d1bb3da19..b27fbdbbf20b793abb3b9d128ecea0c903f56586 100644 (file)
@@ -112,9 +112,9 @@ class GreTypeTest(base.BaseTestCase):
         segment = {api.NETWORK_TYPE: 'gre',
                    api.PHYSICAL_NETWORK: 'None',
                    api.SEGMENTATION_ID: 101}
-        self.driver.reserve_provider_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
         alloc = self.driver.get_gre_allocation(self.session,
-                                               segment[api.SEGMENTATION_ID])
+                                               observed[api.SEGMENTATION_ID])
         self.assertTrue(alloc.allocated)
 
         with testtools.ExpectedException(exc.TunnelIdInUse):
@@ -122,18 +122,18 @@ class GreTypeTest(base.BaseTestCase):
 
         self.driver.release_segment(self.session, segment)
         alloc = self.driver.get_gre_allocation(self.session,
-                                               segment[api.SEGMENTATION_ID])
+                                               observed[api.SEGMENTATION_ID])
         self.assertFalse(alloc.allocated)
 
         segment[api.SEGMENTATION_ID] = 1000
-        self.driver.reserve_provider_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
         alloc = self.driver.get_gre_allocation(self.session,
-                                               segment[api.SEGMENTATION_ID])
+                                               observed[api.SEGMENTATION_ID])
         self.assertTrue(alloc.allocated)
 
         self.driver.release_segment(self.session, segment)
         alloc = self.driver.get_gre_allocation(self.session,
-                                               segment[api.SEGMENTATION_ID])
+                                               observed[api.SEGMENTATION_ID])
         self.assertIsNone(alloc)
 
     def test_allocate_tenant_segment(self):
index 8d835bfd435a255ea60f465022414cab5b3f815b..f36b853514bf7d5715d96414ef32383681a4f4d0 100644 (file)
@@ -47,8 +47,13 @@ class LocalTypeTest(base.BaseTestCase):
 
     def test_reserve_provider_segment(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
-        self.driver.reserve_provider_segment(self.session, segment)
-        self.driver.release_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        self.assertEqual(segment, observed)
+
+    def test_release_provider_segment(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        self.driver.release_segment(self.session, observed)
 
     def test_allocate_tenant_segment(self):
         expected = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
index deb86c0afcb50b52ee05f6d2f9bff0c790b6bd95..b36bc42e9f61f5bfc5fb750180189e3b79583620 100644 (file)
@@ -53,22 +53,34 @@ class VlanTypeTest(base.BaseTestCase):
             physical_network=segment[api.PHYSICAL_NETWORK],
             vlan_id=segment[api.SEGMENTATION_ID]).first()
 
+    def test_partial_segment_is_partial_segment(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
+        self.assertTrue(self.driver.is_partial_segment(segment))
+
+    def test_specific_segment_is_not_partial_segment(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
+                   api.PHYSICAL_NETWORK: PROVIDER_NET,
+                   api.SEGMENTATION_ID: 1}
+        self.assertFalse(self.driver.is_partial_segment(segment))
+
     def test_validate_provider_segment(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
                    api.PHYSICAL_NETWORK: PROVIDER_NET,
                    api.SEGMENTATION_ID: 1}
         self.assertIsNone(self.driver.validate_provider_segment(segment))
 
-    def test_validate_provider_segment_with_missing_physical_network(self):
+    def test_validate_provider_segment_without_segmentation_id(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
-                   api.SEGMENTATION_ID: 1}
-        self.assertRaises(exc.InvalidInput,
-                          self.driver.validate_provider_segment,
-                          segment)
+                   api.PHYSICAL_NETWORK: TENANT_NET}
+        self.driver.validate_provider_segment(segment)
 
-    def test_validate_provider_segment_with_missing_segmentation_id(self):
+    def test_validate_provider_segment_without_physical_network(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
+        self.driver.validate_provider_segment(segment)
+
+    def test_validate_provider_segment_with_missing_physical_network(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
-                   api.PHYSICAL_NETWORK: PROVIDER_NET}
+                   api.SEGMENTATION_ID: 1}
         self.assertRaises(exc.InvalidInput,
                           self.driver.validate_provider_segment,
                           segment)
@@ -129,19 +141,19 @@ class VlanTypeTest(base.BaseTestCase):
                    api.SEGMENTATION_ID: 101}
         alloc = self._get_allocation(self.session, segment)
         self.assertIsNone(alloc)
-        self.driver.reserve_provider_segment(self.session, segment)
-        alloc = self._get_allocation(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        alloc = self._get_allocation(self.session, observed)
         self.assertTrue(alloc.allocated)
 
     def test_reserve_provider_segment_already_allocated(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
                    api.PHYSICAL_NETWORK: PROVIDER_NET,
                    api.SEGMENTATION_ID: 101}
-        self.driver.reserve_provider_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
         self.assertRaises(exc.VlanIdInUse,
                           self.driver.reserve_provider_segment,
                           self.session,
-                          segment)
+                          observed)
 
     def test_reserve_provider_segment_in_tenant_pools(self):
         segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
@@ -149,9 +161,38 @@ class VlanTypeTest(base.BaseTestCase):
                    api.SEGMENTATION_ID: VLAN_MIN}
         alloc = self._get_allocation(self.session, segment)
         self.assertFalse(alloc.allocated)
-        self.driver.reserve_provider_segment(self.session, segment)
-        alloc = self._get_allocation(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        alloc = self._get_allocation(self.session, observed)
+        self.assertTrue(alloc.allocated)
+
+    def test_reserve_provider_segment_without_segmentation_id(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
+                   api.PHYSICAL_NETWORK: TENANT_NET}
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        alloc = self._get_allocation(self.session, observed)
+        self.assertTrue(alloc.allocated)
+        vlan_id = observed[api.SEGMENTATION_ID]
+        self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
+        self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
+
+    def test_reserve_provider_segment_without_physical_network(self):
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
+        observed = self.driver.reserve_provider_segment(self.session, segment)
+        alloc = self._get_allocation(self.session, observed)
         self.assertTrue(alloc.allocated)
+        vlan_id = observed[api.SEGMENTATION_ID]
+        self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
+        self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
+        self.assertEqual(TENANT_NET, observed[api.PHYSICAL_NETWORK])
+
+    def test_reserve_provider_segment_all_allocateds(self):
+        for __ in range(VLAN_MIN, VLAN_MAX + 1):
+            self.driver.allocate_tenant_segment(self.session)
+        segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
+        self.assertRaises(exc.NoNetworkAvailable,
+                          self.driver.reserve_provider_segment,
+                          self.session,
+                          segment)
 
     def test_allocate_tenant_segment(self):
         for __ in range(VLAN_MIN, VLAN_MAX + 1):
index 1242df2abc6aedbf14a0e965662a450f2fddb1df..836feef174bb364593803e4ee8804ece6e28603f 100644 (file)
@@ -120,9 +120,9 @@ class VxlanTypeTest(base.BaseTestCase):
         segment = {api.NETWORK_TYPE: 'vxlan',
                    api.PHYSICAL_NETWORK: 'None',
                    api.SEGMENTATION_ID: 101}
-        self.driver.reserve_provider_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
         alloc = self.driver.get_vxlan_allocation(self.session,
-                                                 segment[api.SEGMENTATION_ID])
+                                                 observed[api.SEGMENTATION_ID])
         self.assertTrue(alloc.allocated)
 
         with testtools.ExpectedException(exc.TunnelIdInUse):
@@ -130,18 +130,18 @@ class VxlanTypeTest(base.BaseTestCase):
 
         self.driver.release_segment(self.session, segment)
         alloc = self.driver.get_vxlan_allocation(self.session,
-                                                 segment[api.SEGMENTATION_ID])
+                                                 observed[api.SEGMENTATION_ID])
         self.assertFalse(alloc.allocated)
 
         segment[api.SEGMENTATION_ID] = 1000
-        self.driver.reserve_provider_segment(self.session, segment)
+        observed = self.driver.reserve_provider_segment(self.session, segment)
         alloc = self.driver.get_vxlan_allocation(self.session,
-                                                 segment[api.SEGMENTATION_ID])
+                                                 observed[api.SEGMENTATION_ID])
         self.assertTrue(alloc.allocated)
 
         self.driver.release_segment(self.session, segment)
         alloc = self.driver.get_vxlan_allocation(self.session,
-                                                 segment[api.SEGMENTATION_ID])
+                                                 observed[api.SEGMENTATION_ID])
         self.assertIsNone(alloc)
 
     def test_allocate_tenant_segment(self):