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

Added Region Service code changes

chandraMsekhar 9 лет назад
Родитель
Сommit
cf7bb5f5b9

+ 41 - 0
azure_integration_test/test_integration_azure_region_service.py

@@ -0,0 +1,41 @@
+import azure_integration_test.helpers as helpers
+
+from cloudbridge.cloud.providers.azure.resources import AzureRegion
+
+
+class AzureIntegrationRegionServiceTestCase(helpers.ProviderTestBase):
+    def test_azure_integration_region_service_list(self):
+        regions = self.provider.compute.regions.list()
+        self.assertIsNotNone(regions)
+        for region in regions:
+            print(region.id)
+            print(region.name)
+            print(region.zones)
+            self.assertIsInstance(
+                region,
+                AzureRegion,
+                "regions.list() should return a cloudbridge Region")
+            self.assertTrue(
+                region.name,
+                "Region name should be a non-empty string")
+
+    def test_azure_integration_region_service_get(self):
+        region_id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/" \
+                    "locations/koreasouth"
+        region = self.provider.compute.regions.get(region_id)
+        self.assertIsNotNone(region)
+        self.assertEqual(region.name, "koreasouth")
+
+    def test_azure_integration_region_service_get_invalid_region_id(self):
+        region_id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/" \
+                    "locations/invalid"
+        region = self.provider.compute.regions.get(region_id)
+        self.assertIsNone(region)
+
+    def test_azure_integration_region_service_current(self):
+        current_region_name = self.provider.region_name
+        print("current region: " + current_region_name)
+        region = self.provider.compute.regions.current
+        self.assertIsNotNone(region)
+        print("Region service returned Region name:" + region.name)
+        self.assertEqual(region.name, current_region_name)

+ 47 - 0
azure_test/test_azure_region_service.py

@@ -0,0 +1,47 @@
+from azure_test.helpers import ProviderTestBase
+
+from cloudbridge.cloud.providers.azure.resources import AzureRegion
+
+
+class AzureRegionServiceTestCase(ProviderTestBase):
+    def test_azure_region_service_list(self):
+        regions = self.provider.compute.regions.list()
+        self.assertIsNotNone(regions)
+        self.assertEqual(
+            len(regions), 3)
+        print("Region List - " + str(regions))
+        for region in regions:
+            print(region.id)
+            print(region.name)
+            print(region.zones)
+            self.assertIsInstance(
+                region,
+                AzureRegion,
+                "regions.list() should return a cloudbridge Region")
+            self.assertTrue(
+                region.name,
+                "Region name should be a non-empty string")
+
+    def test_azure_region_service_get(self):
+        region_id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/" \
+                    "locations/westus2"
+        region = self.provider.compute.regions.get(region_id)
+        self.assertIsNotNone(region)
+        self.assertEqual(region.name, "westus2")
+        self.assertEqual(region.zones[0].id, region.id)
+        self.assertEqual(region.zones[0].name, region.name)
+        self.assertEqual(region.zones[0].region_name, region.name)
+
+    def test_azure_region_service_get_invalid_region_id(self):
+        region_id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/" \
+                    "locations/invalid"
+        region = self.provider.compute.regions.get(region_id)
+        self.assertIsNone(region)
+
+    def test_azure_region_service_current(self):
+        current_region_name = self.provider.region_name
+        print("current region: " + current_region_name)
+        region = self.provider.compute.regions.current
+        self.assertIsNotNone(region)
+        print("Region service returned Region name:" + region.name)
+        self.assertEqual(region.name, current_region_name)

+ 18 - 18
cloudbridge/cloud/providers/azure/mock_azure_client.py

@@ -6,13 +6,12 @@ from io import BytesIO
 from azure.common import AzureException
 from azure.common import AzureException
 from azure.mgmt.compute.models import CreationData, DataDisk, \
 from azure.mgmt.compute.models import CreationData, DataDisk, \
     Disk, DiskCreateOption, Image, ManagedDiskParameters, \
     Disk, DiskCreateOption, Image, ManagedDiskParameters, \
-    Snapshot, StorageProfile, VirtualMachine, \
-    VirtualMachineSize
-
+    Snapshot, StorageProfile, VirtualMachine
 from azure.mgmt.network.models import NetworkSecurityGroup
 from azure.mgmt.network.models import NetworkSecurityGroup
 from azure.mgmt.network.models import SecurityRule
 from azure.mgmt.network.models import SecurityRule
 from azure.mgmt.network.models import VirtualNetwork
 from azure.mgmt.network.models import VirtualNetwork
 from azure.mgmt.resource.resources.models import ResourceGroup
 from azure.mgmt.resource.resources.models import ResourceGroup
+from azure.mgmt.resource.subscriptions.models import Location
 from azure.mgmt.storage.models import StorageAccount
 from azure.mgmt.storage.models import StorageAccount
 from azure.storage.blob.models import Blob, BlobProperties, \
 from azure.storage.blob.models import Blob, BlobProperties, \
     Container
     Container
@@ -211,21 +210,22 @@ class MockAzureClient:
 
 
     images = [image1, image2]
     images = [image1, image2]
 
 
-    instance_type1 = VirtualMachineSize()
-    instance_type1.name = "instance_type1"
-    instance_type1.number_of_cores = 1
-    instance_type1.os_disk_size_in_mb = 100
-    instance_type1.memory_in_mb = 1000
-    instance_type1.max_data_disk_count = 1
+    region1 = Location()
+    region1.name = "westus2"
+    region1.id = '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/' \
+                 'locations/westus2'
+
+    region2 = Location()
+    region2.name = "koreasouth"
+    region2.id = '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/' \
+                 'locations/koreasouth'
 
 
-    instance_type2 = VirtualMachineSize()
-    instance_type2.name = "instance_type2"
-    instance_type2.number_of_cores = 2
-    instance_type2.os_disk_size_in_mb = 200
-    instance_type2.memory_in_mb = 2000
-    instance_type2.max_data_disk_count = 2
+    region3 = Location()
+    region3.name = "eastus"
+    region3.id = '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/' \
+                 'locations/eastus'
 
 
-    instance_types = [instance_type1, instance_type2]
+    regions = [region1, region2, region3]
 
 
     def __init__(self, provider):
     def __init__(self, provider):
         self._provider = provider
         self._provider = provider
@@ -542,5 +542,5 @@ class MockAzureClient:
         img = self.get_image(name)
         img = self.get_image(name)
         self.images.remove(img)
         self.images.remove(img)
 
 
-    def list_instance_types(self):
-        return self.instance_types
+    def list_locations(self):
+        return self.regions

+ 49 - 32
cloudbridge/cloud/providers/azure/resources.py

@@ -8,10 +8,9 @@ from azure.common import AzureException
 
 
 
 
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo, \
 from cloudbridge.cloud.base.resources import BaseAttachmentInfo, \
-    BaseBucket, BaseBucketObject, BaseMachineImage, BaseNetwork,\
-    BaseInstanceType, BaseMachineImage, \
-    BaseSecurityGroup, BaseSecurityGroupRule, BaseSnapshot, BaseVolume, \
-    ClientPagedResultList
+    BaseBucket, BaseBucketObject, BaseMachineImage, BaseNetwork, \
+    BasePlacementZone, BaseRegion, BaseSecurityGroup, BaseSecurityGroupRule, \
+    BaseSnapshot, BaseVolume, ClientPagedResultList
 from cloudbridge.cloud.interfaces import VolumeState
 from cloudbridge.cloud.interfaces import VolumeState
 from cloudbridge.cloud.interfaces.resources import Instance, \
 from cloudbridge.cloud.interfaces.resources import Instance, \
     MachineImageState, NetworkState, SnapshotState
     MachineImageState, NetworkState, SnapshotState
@@ -863,45 +862,63 @@ class AzureNetwork(BaseNetwork):
             return False
             return False
 
 
 
 
-class AzureInstanceType(BaseInstanceType):
+class AzureRegion(BaseRegion):
 
 
-    def __init__(self, provider, instance_type):
-        super(AzureInstanceType, self).__init__(provider)
-        self._inst_type = instance_type
+    def __init__(self, provider, azure_region):
+        super(AzureRegion, self).__init__(provider)
+        self._azure_region = azure_region
 
 
-    # @property
-    # def id(self):
-    #     return str(self._inst_dict['instance_type'])
+    @property
+    def id(self):
+        return self._azure_region.id
 
 
     @property
     @property
     def name(self):
     def name(self):
-        return self._inst_type.name
-
-    # @property
-    # def family(self):
-    #     return self._inst_type.get('family')
+        return self._azure_region.name
 
 
     @property
     @property
-    def vcpus(self):
-        return self._inst_type.number_of_cores
+    def zones(self):
+        """
+            Access information about placement zones within this region.
+        """
+        # As Azure does not have zones feature, mapping the region
+        # information in the zones.
+        return [AzurePlacementZone(self._provider,
+                                   self._azure_region.id,
+                                   self._azure_region.name)]
 
 
-    @property
-    def ram(self):
-        return self._inst_type.memory_in_mb
 
 
-    @property
-    def size_root_disk(self):
-        return 0
+class AzurePlacementZone(BasePlacementZone):
+
+    def __init__(self, provider, zone, region):
+        super(AzurePlacementZone, self).__init__(provider)
+        self._azure_zone = zone
+        self._azure_region = region
 
 
     @property
     @property
-    def size_ephemeral_disks(self):
-        return self._inst_type.os_disk_size_in_mb/1024
+    def id(self):
+        """
+            Get the zone id
+            :rtype: ``str``
+            :return: ID for this zone as returned by the cloud middleware.
+        """
+        return self._azure_zone
 
 
     @property
     @property
-    def num_ephemeral_disks(self):
-        return self._inst_type.max_data_disk_count
+    def name(self):
+        """
+            Get the zone name.
+            :rtype: ``str``
+            :return: Name for this zone as returned by the cloud middleware.
+        """
+        return self._azure_region
 
 
-    # @property
-    # def extra_data(self):
-    #     return {key: val for key, val in enumerate(self._inst_type)
-    #             if key not in ["instance_type", "family", "vCPU", "memory"]}
+    @property
+    def region_name(self):
+        """
+            Get the region that this zone belongs to.
+            :rtype: ``str``
+            :return: Name of this zone's region as returned by the
+            cloud middleware
+        """
+        return self._azure_region

+ 52 - 3
cloudbridge/cloud/providers/azure/services.py

@@ -5,19 +5,30 @@ from azure.common import AzureException
 
 
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseBlockStoreService, \
 from cloudbridge.cloud.base.services import BaseBlockStoreService, \
+<<<<<<< HEAD
     BaseComputeService, BaseImageService, BaseInstanceTypesService, \
     BaseComputeService, BaseImageService, BaseInstanceTypesService, \
     BaseNetworkService, BaseObjectStoreService, \
     BaseNetworkService, BaseObjectStoreService, \
     BaseSecurityGroupService, BaseSecurityService, \
     BaseSecurityGroupService, BaseSecurityService, \
     BaseSnapshotService, BaseVolumeService
     BaseSnapshotService, BaseVolumeService
+=======
+    BaseComputeService, BaseImageService, BaseNetworkService, \
+    BaseObjectStoreService, BaseRegionService, BaseSecurityGroupService, \
+    BaseSecurityService, BaseSnapshotService, BaseVolumeService
+>>>>>>> 6ea3953... Added Region Service code changes
 from cloudbridge.cloud.interfaces.resources import PlacementZone, \
 from cloudbridge.cloud.interfaces.resources import PlacementZone, \
     Snapshot, Volume
     Snapshot, Volume
 from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 
 
 from msrestazure.azure_exceptions import CloudError
 from msrestazure.azure_exceptions import CloudError
 
 
+<<<<<<< HEAD
 from .resources import AzureBucket, AzureInstanceType,\
 from .resources import AzureBucket, AzureInstanceType,\
     AzureMachineImage, AzureNetwork, \
     AzureMachineImage, AzureNetwork, \
     AzureSecurityGroup, \
     AzureSecurityGroup, \
+=======
+from .resources import AzureBucket, AzureMachineImage, \
+    AzureNetwork, AzureRegion, AzureSecurityGroup, \
+>>>>>>> 6ea3953... Added Region Service code changes
     AzureSnapshot, AzureVolume, \
     AzureSnapshot, AzureVolume, \
     IMAGE_NAME, IMAGE_RESOURCE_ID, \
     IMAGE_NAME, IMAGE_RESOURCE_ID, \
     NETWORK_NAME, NETWORK_RESOURCE_ID, \
     NETWORK_NAME, NETWORK_RESOURCE_ID, \
@@ -305,7 +316,7 @@ class AzureComputeService(BaseComputeService):
         super(AzureComputeService, self).__init__(provider)
         super(AzureComputeService, self).__init__(provider)
         self._instance_type_svc = AzureInstanceTypesService(self.provider)
         self._instance_type_svc = AzureInstanceTypesService(self.provider)
         # self._instance_svc = AzureInstanceService(self.provider)
         # self._instance_svc = AzureInstanceService(self.provider)
-        # self._region_svc = AzureRegionService(self.provider)
+        self._region_svc = AzureRegionService(self.provider)
         self._images_svc = AzureImageService(self.provider)
         self._images_svc = AzureImageService(self.provider)
 
 
     @property
     @property
@@ -323,8 +334,7 @@ class AzureComputeService(BaseComputeService):
 
 
     @property
     @property
     def regions(self):
     def regions(self):
-        raise NotImplementedError('AzureComputeService not '
-                                  'implemented this method')
+        return self._region_svc
 
 
 
 
 class AzureImageService(BaseImageService):
 class AzureImageService(BaseImageService):
@@ -438,8 +448,13 @@ class AzureNetworkService(BaseNetworkService):
 
 
     def delete(self, network_id):
     def delete(self, network_id):
         """
         """
+<<<<<<< HEAD
                 Delete an existing network.
                 Delete an existing network.
                 """
                 """
+=======
+            Delete an existing network.
+            """
+>>>>>>> 6ea3953... Added Region Service code changes
         try:
         try:
             params = azure_helpers.parse_url(NETWORK_RESOURCE_ID, network_id)
             params = azure_helpers.parse_url(NETWORK_RESOURCE_ID, network_id)
             network = self.provider.azure_client. \
             network = self.provider.azure_client. \
@@ -449,3 +464,37 @@ class AzureNetworkService(BaseNetworkService):
             log.exception(cloudError.message)
             log.exception(cloudError.message)
             return False
             return False
 
 
+<<<<<<< HEAD
+=======
+
+class AzureRegionService(BaseRegionService):
+    def __init__(self, provider):
+        super(AzureRegionService, self).__init__(provider)
+
+    def get(self, region_id):
+        region = None
+        for azureRegion in self.provider.azure_client.list_locations():
+            if azureRegion.id == region_id:
+                region = AzureRegion(self.provider, azureRegion)
+                break
+        return region
+
+    def list(self, limit=None, marker=None):
+        regions = [AzureRegion(self.provider, region)
+                   for region in self.provider.azure_client.list_locations()]
+        return ClientPagedResultList(self.provider, regions,
+                                     limit=limit, marker=marker)
+
+    @property
+    def current(self):
+        region = None
+        # aws sets the name returned from the aws sdk to both the id & name
+        # of BaseRegion and as such calling get() with the id works
+        # but Azure sdk returns both id & name and are set to
+        # the BaseRegion properties
+        for azureRegion in self.provider.azure_client.list_locations():
+            if azureRegion.name == self.provider.region_name:
+                region = AzureRegion(self.provider, azureRegion)
+                break
+        return region
+>>>>>>> 6ea3953... Added Region Service code changes

+ 40 - 104
test/helpers.py

@@ -1,14 +1,16 @@
-from contextlib import contextmanager
+import functools
 import os
 import os
 import sys
 import sys
 import unittest
 import unittest
-import functools
-from six import reraise
+
+from contextlib import contextmanager
 
 
 from cloudbridge.cloud.factory import CloudProviderFactory
 from cloudbridge.cloud.factory import CloudProviderFactory
 from cloudbridge.cloud.interfaces import InstanceState
 from cloudbridge.cloud.interfaces import InstanceState
 from cloudbridge.cloud.interfaces import TestMockHelperMixin
 from cloudbridge.cloud.interfaces import TestMockHelperMixin
 
 
+from six import reraise
+
 
 
 def parse_bool(val):
 def parse_bool(val):
     if val:
     if val:
@@ -19,7 +21,7 @@ def parse_bool(val):
 
 
 @contextmanager
 @contextmanager
 def cleanup_action(cleanup_func):
 def cleanup_action(cleanup_func):
-    """n csdmmnd
+    """
     Context manager to carry out a given
     Context manager to carry out a given
     cleanup action after carrying out a set
     cleanup action after carrying out a set
     of tasks, or when an exception occurs.
     of tasks, or when an exception occurs.
@@ -82,7 +84,10 @@ TEST_DATA_CONFIG = {
         "image": os.environ.get('CB_IMAGE_OS',
         "image": os.environ.get('CB_IMAGE_OS',
                                 '842b949c-ea76-48df-998d-8a41f2626243'),
                                 '842b949c-ea76-48df-998d-8a41f2626243'),
         "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.tiny'),
         "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.tiny'),
-        "placement": os.environ.get('CB_PLACEMENT_OS', 'zone-r6'),
+        "placement": os.environ.get('CB_PLACEMENT_OS', 'nova'),
+    },
+    "AzureCloudProvider": {
+        "placement": "eastus"
     }
     }
 }
 }
 
 
@@ -92,6 +97,8 @@ def get_provider_test_data(provider, key):
         return TEST_DATA_CONFIG.get("AWSCloudProvider").get(key)
         return TEST_DATA_CONFIG.get("AWSCloudProvider").get(key)
     elif "OpenStackCloudProvider" in provider.name:
     elif "OpenStackCloudProvider" in provider.name:
         return TEST_DATA_CONFIG.get("OpenStackCloudProvider").get(key)
         return TEST_DATA_CONFIG.get("OpenStackCloudProvider").get(key)
+    elif "AzureCloudProvider" in provider.name:
+        return TEST_DATA_CONFIG.get("AzureCloudProvider").get(key)
     return None
     return None
 
 
 
 
@@ -143,31 +150,31 @@ def get_test_instance(provider, name, key_pair=None, security_groups=None,
     return instance
     return instance
 
 
 
 
+def get_test_fixtures_folder():
+    return os.path.join(os.path.dirname(__file__), 'fixtures/')
+
+
+def delete_test_instance(instance):
+    if instance:
+        instance.terminate()
+        instance.wait_for([InstanceState.TERMINATED, InstanceState.UNKNOWN],
+                          terminal_states=[InstanceState.ERROR])
+
+
 def cleanup_test_resources(instance=None, network=None, security_group=None,
 def cleanup_test_resources(instance=None, network=None, security_group=None,
                            key_pair=None):
                            key_pair=None):
+    """Clean up any combination of supplied resources."""
     with cleanup_action(lambda: delete_test_network(network)
     with cleanup_action(lambda: delete_test_network(network)
                         if network else None):
                         if network else None):
         with cleanup_action(lambda: key_pair.delete() if key_pair else None):
         with cleanup_action(lambda: key_pair.delete() if key_pair else None):
             with cleanup_action(lambda: security_group.delete()
             with cleanup_action(lambda: security_group.delete()
                                 if security_group else None):
                                 if security_group else None):
-                instance.terminate()
-                instance.wait_for(
-                    [InstanceState.TERMINATED, InstanceState.UNKNOWN],
-                    terminal_states=[InstanceState.ERROR])
-
+                delete_test_instance(instance)
 
 
-class ProviderTestBase(object):
 
 
-    """
-    A dummy base class for Test Cases. Does not inherit from unittest.TestCase
-    to avoid confusing test discovery by unittest and nose2. unittest.TestCase
-    is injected as a base class by the generator, so calling the unittest
-    constructor works correctly.
-    """
+class ProviderTestBase(unittest.TestCase):
 
 
-    def __init__(self, methodName, provider):
-        unittest.TestCase.__init__(self, methodName=methodName)
-        self.provider = provider
+    _provider = None
 
 
     def setUp(self):
     def setUp(self):
         if isinstance(self.provider, TestMockHelperMixin):
         if isinstance(self.provider, TestMockHelperMixin):
@@ -176,17 +183,7 @@ class ProviderTestBase(object):
     def tearDown(self):
     def tearDown(self):
         if isinstance(self.provider, TestMockHelperMixin):
         if isinstance(self.provider, TestMockHelperMixin):
             self.provider.tearDownMock()
             self.provider.tearDownMock()
-
-
-class ProviderTestCaseGenerator():
-
-    """
-    Generates test cases for all provider - testcase combinations.
-    Detailed docs at test/__init__.py
-    """
-
-    def __init__(self, test_classes):
-        self.all_test_classes = test_classes
+        self._provider = None
 
 
     def get_provider_wait_interval(self, provider_class):
     def get_provider_wait_interval(self, provider_class):
         if issubclass(provider_class, TestMockHelperMixin):
         if issubclass(provider_class, TestMockHelperMixin):
@@ -194,80 +191,19 @@ class ProviderTestCaseGenerator():
         else:
         else:
             return 1
             return 1
 
 
-    def create_provider_instance(self, provider_class):
-        """
-        Instantiate a default provider instance. All required connection
-        settings are expected to be set as environment variables.
-        """
+    def create_provider_instance(self):
+        provider_name = os.environ.get("CB_TEST_PROVIDER", "aws")
+        use_mock_drivers = parse_bool(
+            os.environ.get("CB_USE_MOCK_PROVIDERS", "True"))
+        factory = CloudProviderFactory()
+        provider_class = factory.get_provider_class(provider_name,
+                                                    get_mock=use_mock_drivers)
         config = {'default_wait_interval':
         config = {'default_wait_interval':
                   self.get_provider_wait_interval(provider_class)}
                   self.get_provider_wait_interval(provider_class)}
         return provider_class(config)
         return provider_class(config)
 
 
-    def generate_new_test_class(self, name, testcase_class):
-        """
-        Generates a new type which inherits from the given testcase_class and
-        unittest.TestCase
-        """
-        class_name = "{0}{1}".format(name, testcase_class.__name__)
-        return type(class_name, (testcase_class, unittest.TestCase), {})
-
-    def generate_test_suite_for_provider_testcase(
-            self, provider_class, testcase_class):
-        """
-        Generate and return a suite of tests for a specific provider class and
-        testcase combination
-        """
-        testloader = unittest.TestLoader()
-        testnames = testloader.getTestCaseNames(testcase_class)
-        suite = unittest.TestSuite()
-        for name in testnames:
-            generated_cls = self.generate_new_test_class(
-                provider_class.__name__,
-                testcase_class)
-            suite.addTest(
-                generated_cls(
-                    name,
-                    self.create_provider_instance(provider_class)))
-        return suite
-
-    def generate_test_suite_for_provider(self, provider_class):
-        """
-        Generate and return a suite of all available tests for a given provider
-        class
-        """
-        suite = unittest.TestSuite()
-        suites = [
-            self.generate_test_suite_for_provider_testcase(
-                provider_class, test_class)
-            for test_class in self.all_test_classes]
-        for s in suites:
-            suite.addTest(s)
-        return suite
-
-    def generate_tests(self):
-        """
-        Generate and return a suite of tests for all provider and test class
-        combinations
-        """
-        factory = CloudProviderFactory()
-        use_mock_drivers = parse_bool(
-            os.environ.get("CB_USE_MOCK_PROVIDERS", True))
-        provider_name = os.environ.get("CB_TEST_PROVIDER", "azure")
-        if provider_name:
-            provider_classes = [
-                factory.get_provider_class(
-                    provider_name,
-                    get_mock=use_mock_drivers)]
-            if not provider_classes[0]:
-                raise ValueError(
-                    "Could not find specified test provider %s" %
-                    provider_name)
-        else:
-            provider_classes = factory.get_all_provider_classes(
-                get_mock=use_mock_drivers)
-        suite = unittest.TestSuite()
-        suites = [
-            self.generate_test_suite_for_provider(p) for p in provider_classes]
-        for s in suites:
-            suite.addTest(s)
-        return suite
+    @property
+    def provider(self):
+        if not self._provider:
+            self._provider = self.create_provider_instance()
+        return self._provider

+ 2 - 3
test/test_azure_provider.py

@@ -16,9 +16,8 @@ class AzureProviderTestCase(ProviderTestBase):
         self.assertTrue(compute.instance_types is not None,
         self.assertTrue(compute.instance_types is not None,
                         'Instance types should not be none')
                         'Instance types should not be none')
 
 
-        with self.assertRaises(NotImplementedError):
-            self.assertTrue(compute.regions is not None,
-                            'Regions should not be none')
+        self.assertTrue(compute.regions is not None,
+                        'Regions should not be none')
 
 
         network = self.provider.network
         network = self.provider.network
         self.assertTrue(network is not None,
         self.assertTrue(network is not None,