Просмотр исходного кода

Deal with creating a subnet with overlapping range

The GCE provider creates auto-mode new networks. So subnets are created
automatically. So, it is possible that when a test creates a custom subnet, even
if it is right after creating a new network, the provided CIDR block overlap
with an existing subnet. In those cases, we just return the existing subnet.

Test expectations are modified accordingly.
Ehsan Chiniforooshan 8 лет назад
Родитель
Сommit
9dd9fa943c
3 измененных файлов с 57 добавлено и 27 удалено
  1. 13 0
      cloudbridge/cloud/base/resources.py
  2. 38 22
      cloudbridge/cloud/providers/gce/services.py
  3. 6 5
      test/test_network_service.py

+ 13 - 0
cloudbridge/cloud/base/resources.py

@@ -934,6 +934,19 @@ class BaseNetwork(BaseCloudResource, BaseObjectLifeCycleMixin, Network):
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
         return "<CB-{0}: {1} ({2})>".format(self.__class__.__name__,
                                             self.id, self.name)
                                             self.id, self.name)
 
 
+    @staticmethod
+    def cidr_blocks_overlap(block1, block2):
+        common_length = min(int(block1.split('/')[1]),
+                            int(block2.split('/')[1]))
+
+        p1 = [format(int(b), '08b') for b in block1.split('/')[0].split('.')]
+        prefix1 = ''.join(p1)[:common_length]
+
+        p2 = [format(int(b), '08b') for b in block2.split('/')[0].split('.')]
+        prefix2 = ''.join(p2)[:common_length]
+
+        return prefix1 == prefix2
+
     def wait_till_ready(self, timeout=None, interval=None):
     def wait_till_ready(self, timeout=None, interval=None):
         self.wait_for(
         self.wait_for(
             [NetworkState.AVAILABLE],
             [NetworkState.AVAILABLE],

+ 38 - 22
cloudbridge/cloud/providers/gce/services.py

@@ -3,6 +3,7 @@ import uuid
 from collections import namedtuple
 from collections import namedtuple
 
 
 import cloudbridge as cb
 import cloudbridge as cb
+from cloudbridge.cloud.base.resources import BaseNetwork
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
 from cloudbridge.cloud.base.services import BaseBucketService
 from cloudbridge.cloud.base.services import BaseBucketService
@@ -902,35 +903,25 @@ class GCESubnetService(BaseSubnetService):
 
 
     def list(self, network=None, zone=None, limit=None, marker=None):
     def list(self, network=None, zone=None, limit=None, marker=None):
         region = zone.region_name if zone else self.provider.region_name
         region = zone.region_name if zone else self.provider.region_name
-        filter = None
-        if network is not None:
-            filter = 'network eq %s' % network.resource_url
-        max_result = limit if limit is not None and limit < 500 else 500
-        response = (self.provider
-                        .gce_compute
-                        .subnetworks()
-                        .list(project=self.provider.project_name,
-                              region=region,
-                              filter=filter,
-                              maxResults=max_result,
-                              pageToken=marker)
-                        .execute())
-        subnets = []
-        for subnet in response.get('items', []):
-            subnets.append(GCESubnet(self.provider, subnet))
-        if len(subnets) > max_result:
-            cb.log.warning('Expected at most %d results; got %d',
-                           max_result, len(subnets))
-        return ServerPagedResultList('nextPageToken' in response,
-                                     response.get('nextPageToken'),
-                                     False, data=subnets)
+        return ClientPagedResultList(self.provider,
+                                     self._list_all(network, region),
+                                     limit=limit, marker=marker)
 
 
     def create(self, network, cidr_block, name=None, zone=None):
     def create(self, network, cidr_block, name=None, zone=None):
         """
         """
         GCE subnets are regional. The region is inferred from the zone if a
         GCE subnets are regional. The region is inferred from the zone if a
         zone is provided; otherwise, the default region, as set in the
         zone is provided; otherwise, the default region, as set in the
         provider, is used.
         provider, is used.
+
+        If a subnet with overlapping IP range exists already, we return that
+        instead of creating a new subnet. In this case, other parameters, i.e.
+        the name and the zone, are ignored.
         """
         """
+        subnets = self._list_all(network)
+        for subnet in subnets:
+            if BaseNetwork.cidr_blocks_overlap(subnet.cidr_block, cidr_block):
+                return subnet
+
         if not name:
         if not name:
             name = 'subnet-{0}'.format(uuid.uuid4())
             name = 'subnet-{0}'.format(uuid.uuid4())
         region = self._zone_to_region(zone)
         region = self._zone_to_region(zone)
@@ -994,6 +985,31 @@ class GCESubnetService(BaseSubnetService):
                         return z.region_name
                         return z.region_name
         return self.provider.region_name
         return self.provider.region_name
 
 
+    def _list_all(self, network=None, region=None):
+        """
+        Similar to the list method, but if region is not given we list subnets
+        in all regions, not just the project default region.
+        """
+        filter = None
+        if network is not None:
+            filter = 'network eq %s' % network.resource_url
+        if region:
+            regions = [region]
+        else:
+            regions = [r.name for r in self.provider.compute.regions.list()]
+        subnets = []
+        for region in regions:
+            response = (self.provider
+                            .gce_compute
+                            .subnetworks()
+                            .list(project=self.provider.project_name,
+                                  region=region,
+                                  filter=filter)
+                            .execute())
+            for subnet in response.get('items', []):
+                subnets.append(GCESubnet(self.provider, subnet))
+        return subnets
+
 
 
 class GCPStorageService(BaseStorageService):
 class GCPStorageService(BaseStorageService):
 
 

+ 6 - 5
test/test_network_service.py

@@ -2,6 +2,7 @@ import test.helpers as helpers
 from test.helpers import ProviderTestBase
 from test.helpers import ProviderTestBase
 from test.helpers import standard_interface_tests as sit
 from test.helpers import standard_interface_tests as sit
 
 
+from cloudbridge.cloud.base.resources import BaseNetwork
 from cloudbridge.cloud.interfaces.resources import FloatingIP
 from cloudbridge.cloud.interfaces.resources import FloatingIP
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import Network
 from cloudbridge.cloud.interfaces.resources import RouterState
 from cloudbridge.cloud.interfaces.resources import RouterState
@@ -40,11 +41,11 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
             sit.check_repr(self, net)
             sit.check_repr(self, net)
 
 
             self.assertIn(
             self.assertIn(
-                net.cidr_block, ['', '10.0.0.0/16'],
+                net.cidr_block, ['', '10.0.0.0/16', '10.128.0.0/9'],
                 "Network CIDR %s does not contain the expected value."
                 "Network CIDR %s does not contain the expected value."
                 % net.cidr_block)
                 % net.cidr_block)
 
 
-            cidr = '10.0.1.0/24'
+            cidr = '{0}/24'.format(net.cidr_block.split('/')[0] or '10.0.0.0')
             sn = net.create_subnet(name=subnet_name, cidr_block=cidr,
             sn = net.create_subnet(name=subnet_name, cidr_block=cidr,
                                    zone=helpers.get_provider_test_data(
                                    zone=helpers.get_provider_test_data(
                                        self.provider, 'placement'))
                                        self.provider, 'placement'))
@@ -64,8 +65,8 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
                     " id %s." % (net.id, sn.network_id))
                     " id %s." % (net.id, sn.network_id))
 
 
                 self.assertEqual(
                 self.assertEqual(
-                    cidr, sn.cidr_block,
-                    "Subnet's CIDR %s should match the specified one %s." % (
+                    BaseNetwork.cidr_blocks_overlap(cidr, sn.cidr_block),
+                    "Subnet's CIDR %s should overlap the specified one %s." % (
                         sn.cidr_block, cidr))
                         sn.cidr_block, cidr))
 
 
     def test_crud_subnet(self):
     def test_crud_subnet(self):
@@ -146,7 +147,7 @@ class CloudNetworkServiceTestCase(ProviderTestBase):
                 name=name, cidr_block='10.0.0.0/16')
                 name=name, cidr_block='10.0.0.0/16')
             router = self.provider.networking.routers.create(network=net,
             router = self.provider.networking.routers.create(network=net,
                                                              name=name)
                                                              name=name)
-            cidr = '10.0.1.0/24'
+            cidr = '{0}/24'.format(net.cidr_block.split('/')[0] or '10.0.0.0')
             sn = net.create_subnet(name=name, cidr_block=cidr,
             sn = net.create_subnet(name=name, cidr_block=cidr,
                                    zone=helpers.get_provider_test_data(
                                    zone=helpers.get_provider_test_data(
                                        self.provider, 'placement'))
                                        self.provider, 'placement'))