]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Make allocation_pools attribute of subnet updateable by PUT
authormarios <marios@redhat.com>
Fri, 13 Dec 2013 16:57:28 +0000 (18:57 +0200)
committermarios <marios@redhat.com>
Mon, 9 Jun 2014 16:40:37 +0000 (19:40 +0300)
Bug 1111572 was filed about a failed update (PUT) on
'allocation_pools' of subnet. This is currently not allowed by the
neutron API (hence DocImpact below). Following discussion on the
bug and subsequently, it seems this is a desirable feature.

This review makes the allocation_pools attribute of subnet
updateable by PUT. The semantics are that the entire allocation
pools attribute is replaced by the provided parameter (see
provided tests for details).

Unit tests added that exercise successful update of
allocation_pools with sane params and update using erroneous
allocation_pools that fall outside the subnet cidr.

DocImpact

Closes-Bug: 1111572
Change-Id: I47a3a71d0d196b76eda46b1d960193fb60417ba9
Co-Authored-By: Robert Collins <rbtcollins@hp.com>
neutron/api/v2/attributes.py
neutron/db/db_base_plugin_v2.py
neutron/db/models_v2.py
neutron/tests/unit/plumgrid/test_plumgrid_plugin.py
neutron/tests/unit/test_db_plugin.py

index 852263ce0889de01f5c9074bd00580a25b7edb23..5c4479f23bd42a7ac563575cf3a746c83ea2eebe 100644 (file)
@@ -704,8 +704,7 @@ RESOURCE_ATTRIBUTE_MAP = {
                        'default': ATTR_NOT_SPECIFIED,
                        'validate': {'type:ip_address_or_none': None},
                        'is_visible': True},
-        #TODO(salvatore-orlando): Enable PUT on allocation_pools
-        'allocation_pools': {'allow_post': True, 'allow_put': False,
+        'allocation_pools': {'allow_post': True, 'allow_put': True,
                              'default': ATTR_NOT_SPECIFIED,
                              'validate': {'type:ip_pools': None},
                              'is_visible': True},
index 7b3ae166ada775abf53e920048a9ab31e00f183d..4d83957712f9046172dc9ad4f4f06d46ef1d310f 100644 (file)
@@ -1213,6 +1213,21 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
 
         return self._make_subnet_dict(subnet)
 
+    def _update_subnet_allocation_pools(self, context, id, s):
+        context.session.query(models_v2.IPAllocationPool).filter_by(
+            subnet_id=id).delete()
+        new_pools = [models_v2.IPAllocationPool(
+            first_ip=p['start'], last_ip=p['end'],
+            subnet_id=id) for p in s['allocation_pools']]
+        context.session.add_all(new_pools)
+        NeutronDbPluginV2._rebuild_availability_ranges(context, [s])
+        #Gather new pools for result:
+        result_pools = [{'start': pool['start'],
+                         'end': pool['end']}
+                        for pool in s['allocation_pools']]
+        del s['allocation_pools']
+        return result_pools
+
     def update_subnet(self, context, id, subnet):
         """Update the subnet with new info.
 
@@ -1222,6 +1237,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
         s = subnet['subnet']
         changed_host_routes = False
         changed_dns = False
+        changed_allocation_pools = False
         db_subnet = self._get_subnet(context, id)
         # Fill 'ip_version' and 'allocation_pools' fields with the current
         # value since _validate_subnet() expects subnet spec has 'ip_version'
@@ -1288,6 +1304,12 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                          'nexthop': route_str.partition("_")[2]})
                 del s["host_routes"]
 
+            if "allocation_pools" in s:
+                self._validate_allocation_pools(s['allocation_pools'],
+                                                s['cidr'])
+                changed_allocation_pools = True
+                new_pools = self._update_subnet_allocation_pools(context,
+                                                                 id, s)
             subnet = self._get_subnet(context, id)
             subnet.update(s)
         result = self._make_subnet_dict(subnet)
@@ -1296,6 +1318,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
             result['dns_nameservers'] = new_dns
         if changed_host_routes:
             result['host_routes'] = new_routes
+        if changed_allocation_pools:
+            result['allocation_pools'] = new_pools
         return result
 
     def delete_subnet(self, context, id):
index ab6ecfd1d03488ffa9f3e11484a3403e0fe181a7..53efc66926d366701cf45bfc8459e50048294188 100644 (file)
@@ -79,7 +79,7 @@ class IPAllocationPool(model_base.BASEV2, HasId):
     available_ranges = orm.relationship(IPAvailabilityRange,
                                         backref='ipallocationpool',
                                         lazy="joined",
-                                        cascade='delete')
+                                        cascade='all, delete-orphan')
 
     def __repr__(self):
         return "%s - %s" % (self.first_ip, self.last_ip)
index 79acbae2413fe72e2994e4df01745e6e88ff7af2..92fa937c5000d9ab734290b089da4a7d610899b3 100644 (file)
@@ -81,18 +81,17 @@ class TestPlumgridPluginPortsV2(test_plugin.TestPortsV2,
 
 class TestPlumgridPluginSubnetsV2(test_plugin.TestSubnetsV2,
                                   PLUMgridPluginV2TestCase):
-    def test_create_subnet_default_gw_conflict_allocation_pool_returns_409(
-            self):
-        self.skipTest("Plugin does not support Neutron allocation process")
-
-    def test_create_subnet_defaults(self):
-        self.skipTest("Plugin does not support Neutron allocation process")
+    _unsupported = (
+        'test_create_subnet_default_gw_conflict_allocation_pool_returns_409',
+        'test_create_subnet_defaults', 'test_create_subnet_gw_values',
+        'test_update_subnet_gateway_in_allocation_pool_returns_409',
+        'test_update_subnet_allocation_pools',
+        'test_update_subnet_allocation_pools_invalid_pool_for_cidr')
 
-    def test_create_subnet_gw_values(self):
-        self.skipTest("Plugin does not support Neutron allocation process")
-
-    def test_update_subnet_gateway_in_allocation_pool_returns_409(self):
-        self.skipTest("Plugin does not support Neutron allocation process")
+    def setUp(self):
+        if self._testMethodName in self._unsupported:
+            self.skipTest("Plugin does not support Neutron allocation process")
+        super(TestPlumgridPluginSubnetsV2, self).setUp()
 
 
 class TestPlumgridPluginPortBinding(PLUMgridPluginV2TestCase,
index d1ff9a90ce1d904593a737ee928163c774eb9daa..a9c07e6fee35ca1b0e7e5f88dbf5fa0f1f3091b2 100644 (file)
@@ -3333,6 +3333,56 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
                 self.assertEqual(res.status_int,
                                  webob.exc.HTTPClientError.code)
 
+    def test_update_subnet_allocation_pools(self):
+        """Test that we can successfully update with sane params.
+
+        This will create a subnet with specified allocation_pools
+        Then issue an update (PUT) to update these using correct
+        (i.e. non erroneous) params. Finally retrieve the updated
+        subnet and verify.
+        """
+        allocation_pools = [{'start': '192.168.0.2', 'end': '192.168.0.254'}]
+        with self.network() as network:
+            with self.subnet(network=network,
+                             allocation_pools=allocation_pools,
+                             cidr='192.168.0.0/24') as subnet:
+                data = {'subnet': {'allocation_pools': [
+                        {'start': '192.168.0.10', 'end': '192.168.0.20'},
+                        {'start': '192.168.0.30', 'end': '192.168.0.40'}]}}
+                req = self.new_update_request('subnets', data,
+                                              subnet['subnet']['id'])
+                #check res code but then do GET on subnet for verification
+                res = req.get_response(self.api)
+                self.assertEqual(res.status_code, 200)
+                req = self.new_show_request('subnets', subnet['subnet']['id'],
+                                            self.fmt)
+                res = self.deserialize(self.fmt, req.get_response(self.api))
+                self.assertEqual(len(res['subnet']['allocation_pools']), 2)
+                res_vals = res['subnet']['allocation_pools'][0].values() +\
+                    res['subnet']['allocation_pools'][1].values()
+                for pool_val in ['10', '20', '30', '40']:
+                    self.assertTrue('192.168.0.%s' % (pool_val) in res_vals)
+
+    #updating alloc pool to something outside subnet.cidr
+    def test_update_subnet_allocation_pools_invalid_pool_for_cidr(self):
+        """Test update alloc pool to something outside subnet.cidr.
+
+        This makes sure that an erroneous allocation_pool specified
+        in a subnet update (outside subnet cidr) will result in an error.
+        """
+        allocation_pools = [{'start': '192.168.0.2', 'end': '192.168.0.254'}]
+        with self.network() as network:
+            with self.subnet(network=network,
+                             allocation_pools=allocation_pools,
+                             cidr='192.168.0.0/24') as subnet:
+                data = {'subnet': {'allocation_pools': [
+                        {'start': '10.0.0.10', 'end': '10.0.0.20'}]}}
+                req = self.new_update_request('subnets', data,
+                                              subnet['subnet']['id'])
+                res = req.get_response(self.api)
+                self.assertEqual(res.status_int,
+                                 webob.exc.HTTPClientError.code)
+
     def test_show_subnet(self):
         with self.network() as network:
             with self.subnet(network=network) as subnet: