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

Implemented azure network and object store methods

vikramdoda 9 лет назад
Родитель
Сommit
ba70a1001a

+ 1 - 0
cloudbridge/cloud/factory.py

@@ -15,6 +15,7 @@ log = logging.getLogger(__name__)
 class ProviderList(object):
     AWS = 'aws'
     OPENSTACK = 'openstack'
+    AZURE = 'azure'
 
 
 class CloudProviderFactory(object):

+ 6 - 0
cloudbridge/cloud/providers/azure/__init__.py

@@ -0,0 +1,6 @@
+"""
+Exports from this provider
+"""
+
+from .provider import AzureCloudProvider
+from .provider import MockAzureCloudProvider

+ 92 - 0
cloudbridge/cloud/providers/azure/azure_client.py

@@ -0,0 +1,92 @@
+import logging
+
+from azure.common.credentials import ServicePrincipalCredentials
+from azure.mgmt.compute import ComputeManagementClient
+from azure.mgmt.network import NetworkManagementClient
+from azure.mgmt.resource import ResourceManagementClient
+from azure.mgmt.resource.subscriptions import SubscriptionClient
+from azure.mgmt.storage import StorageManagementClient
+from azure.storage.blob import BlockBlobService
+
+log = logging.getLogger(__name__)
+
+
+class AzureClient(object):
+    def __init__(self, config):
+        self._config = config
+        self.subscription_id = config.get('azure_subscription_id')
+        credentials = ServicePrincipalCredentials(
+            client_id=config.get('azure_client_Id'),
+            secret=config.get('azure_secret'),
+            tenant=config.get('azure_tenant')
+        )
+
+        self._resource_client = ResourceManagementClient(credentials, self.subscription_id)
+        self._storage_client = StorageManagementClient(credentials, self.subscription_id)
+        self._network_management_client = NetworkManagementClient(credentials, self.subscription_id)
+        self._subscription_client = SubscriptionClient(credentials)
+        self._compute_client = ComputeManagementClient(credentials, self.subscription_id)
+
+        log.debug("azure subscription : %s", self.subscription_id)
+
+    @property
+    def resource_group_name(self):
+        return self._config.get('azure_resource_group')
+
+    @property
+    def storage_account_name(self):
+        return self._config.get('azure_storage_account_name')
+
+    @property
+    def block_blob_service(self):
+        return self._storage_client
+
+    @property
+    def storage_client(self):
+        return self._storage_client
+
+    @property
+    def subscription_client(self):
+        return self._subscription_client
+
+    @property
+    def resource_client(self):
+        return self._resource_client
+
+    @property
+    def compute_client(self):
+        return self._compute_client
+
+    @property
+    def network_management_client(self):
+        return self._network_management_client
+
+    def list_locations(self):
+        return self.subscription_client.subscriptions.list_locations(self.subscription_id)
+
+    def list_security_group(self):
+        return self.network_management_client.network_security_groups.list(self.resource_group_name)
+
+    def get_security_group(self, name):
+        return self.network_management_client.network_security_groups.get(self.resource_group_name, name)
+
+    def list_containers(self):
+        access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name, self.storage_account_name)
+        block_blob_service = BlockBlobService(self.storage_account_name, access_key_result.keys[0].value)
+        return block_blob_service.list_containers()
+
+    def create_container(self, container_name):
+        access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name, self.storage_account_name)
+        block_blob_service = BlockBlobService(self.storage_account_name, access_key_result.keys[0].value)
+        return block_blob_service.create_container(container_name)
+
+    def get_container(self, container_name):
+        access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name, self.storage_account_name)
+        block_blob_service = BlockBlobService(self.storage_account_name, access_key_result.keys[0].value)
+        return block_blob_service.get_container_properties(container_name)
+
+    def delete_container(self, container_name):
+        access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name, self.storage_account_name)
+        block_blob_service = BlockBlobService(self.storage_account_name, access_key_result.keys[0].value)
+        block_blob_service.delete_container(container_name)
+        return None

+ 20 - 0
cloudbridge/cloud/providers/azure/mock_azure_client.py

@@ -0,0 +1,20 @@
+from azure.mgmt.network.models import NetworkSecurityGroup
+
+
+class MockAzureClient:
+    sec_gr1 = NetworkSecurityGroup()
+    sec_gr1.name = "sec_group1"
+    sec_gr1.id = "sg1"
+    sec_gr2 = NetworkSecurityGroup()
+    sec_gr2.name = "sec_group2"
+    sec_gr2.id = "sg2"
+    sec_gr3 = NetworkSecurityGroup()
+    sec_gr3.name = "sec_group3"
+    sec_gr3.id = "sg3"
+    security_groups = [sec_gr1, sec_gr2, sec_gr3]
+
+    def __init__(self, provider):
+        self._provider = provider
+
+    def list_security_group(self, resource_group_name):
+        return self.security_groups

+ 95 - 0
cloudbridge/cloud/providers/azure/provider.py

@@ -0,0 +1,95 @@
+import logging
+import os
+
+from cloudbridge.cloud.base import BaseCloudProvider
+from cloudbridge.cloud.interfaces import TestMockHelperMixin
+
+from cloudbridge.cloud.providers.azure.azure_client import AzureClient
+from cloudbridge.cloud.providers.azure.mock_azure_client import MockAzureClient
+from cloudbridge.cloud.providers.azure.services import AzureSecurityService, AzureObjectStoreService
+
+log = logging.getLogger(__name__)
+
+
+class AzureCloudProvider(BaseCloudProvider):
+    PROVIDER_ID = 'azure'
+
+    def __init__(self, config, azureclient=None):
+        super(AzureCloudProvider, self).__init__(config)
+        self.cloud_type = 'azure'
+
+        # mandatory config values
+        self.subscription_id = self._get_config_value(
+            'azure_subscription_id', os.environ.get('AZURE_SUBSCRIPTION_ID', None))
+        self.client_Id = self._get_config_value(
+            'azure_client_Id', os.environ.get('AZURE_CLIENT_ID', None))
+        self.secret = self._get_config_value(
+            'azure_secret', os.environ.get('AZURE_SECRET', None))
+        self.tenant = self._get_config_value(
+            'azure_tenant', os.environ.get('AZURE_TENANT', None))
+
+        # optional config values
+        self.region_name = self._get_config_value(
+            'azure_region_name', os.environ.get('AZURE_REGION_NAME', 'eastus'))
+        self.resource_group = self._get_config_value(
+            'azure_resource_group', os.environ.get('AZURE_RESOURCE_GROUP', 'cloudbridge-azure'))
+
+        self.storage_account_name = self._get_config_value(
+            'azure_storage_account_name', os.environ.get('AZURE_STORAGE_ACCOUNT_NAME', 'cloudbridgeazure'))
+
+        # create a dict with both optional and mandatory configuration values to pass to the azureclient class, rather
+        # than passing the provider object and taking a dependency.
+
+        self.allconfig ={'azure_subscription_id': self.subscription_id,
+                         'azure_client_Id':  self.client_Id,
+                         'azure_secret': self.secret,
+                         'azure_tenant': self.tenant,
+                         'azure_region_name': self.region_name,
+                         'azure_resource_group':  self.resource_group,
+                         'azure_storage_account_name' : self.storage_account_name
+                         }
+
+        # TODO: implement code to validate if the resource group is available,if not create
+        self._azure_client = azureclient or AzureClient(self.allconfig)
+
+        self._security = AzureSecurityService(self)
+        self._object_store = AzureObjectStoreService(self)
+
+    @property
+    def compute(self):
+        raise NotImplementedError(
+            "AzureCloudProvider does not implement this service")
+
+    @property
+    def network(self):
+        return self._security
+
+    @property
+    def security(self):
+        raise NotImplementedError(
+            "AzureCloudProvider does not implement this service")
+
+    @property
+    def block_store(self):
+        raise NotImplementedError(
+            "AzureCloudProvider does not implement this service")
+
+    @property
+    def object_store(self):
+        return self._object_store
+
+    @property
+    def azure_client(self):
+        return self._azure_client
+
+
+class MockAzureCloudProvider(AzureCloudProvider, TestMockHelperMixin):
+    def __init__(self, config):
+        super(MockAzureCloudProvider, self).__init__(config, MockAzureClient(self))
+
+    def setUpMock(self):
+        pass
+
+    def tearDownMock(self):
+        pass
+

+ 185 - 0
cloudbridge/cloud/providers/azure/resources.py

@@ -0,0 +1,185 @@
+"""
+DataTypes used by this provider
+"""
+import inspect
+import json
+
+from cloudbridge.cloud.base.resources import BaseBucket, BaseSecurityGroup, BaseSecurityGroupRule
+
+
+class AzureSecurityGroup(BaseSecurityGroup):
+    def __init__(self, provider, security_group):
+        super(AzureSecurityGroup, self).__init__(provider, security_group)
+
+    @property
+    def network_id(self):
+        return self._security_group.resource_guid
+
+    @property
+    def rules(self):
+        security_group_rules = []
+        for rule in self._security_group.default_security_rules:
+            if rule.direction == "Inbound":
+                sg_rule = AzureSecurityGroupRule(self._provider, rule, self)
+                sg_rule.is_default = True
+                security_group_rules.append(sg_rule)
+        for custom_rule in self._security_group.security_rules:
+            sg_custom_rule = AzureSecurityGroupRule(self._provider, custom_rule, self)
+            sg_custom_rule.is_default = False
+            security_group_rules.append(sg_custom_rule)
+        return security_group_rules
+
+    def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
+                 cidr_ip=None, src_group=None):
+        """
+        Create a security group rule.
+
+        You need to pass in either ``src_group`` OR ``ip_protocol``,
+        ``from_port``, ``to_port``, and ``cidr_ip``.  In other words, either
+        you are authorizing another group or you are authorizing some
+        ip-based rule.
+
+        :type ip_protocol: str
+        :param ip_protocol: Either ``tcp`` | ``udp`` | ``icmp``
+
+        :type from_port: int
+        :param from_port: The beginning port number you are enabling
+
+        :type to_port: int
+        :param to_port: The ending port number you are enabling
+
+        :type cidr_ip: str or list of strings
+        :param cidr_ip: The CIDR block you are providing access to.
+
+        :type src_group: ``object`` of :class:`.SecurityGroup`
+        :param src_group: The Security Group you are granting access to.
+
+        :rtype: :class:``.SecurityGroupRule``
+        :return: Rule object if successful or ``None``.
+        """
+
+        return None
+
+    def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
+                 cidr_ip=None, src_group=None):
+        for rule in self.rules:
+            if (rule.ip_protocol == ip_protocol and
+               rule.from_port == from_port and
+               rule.to_port == to_port and
+               rule.cidr_ip == cidr_ip):
+                return rule
+        return None
+
+    def to_json(self):
+        attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
+        js = {k: v for(k, v) in attr if not k.startswith('_')}
+        json_rules = [r.to_json() for r in self.rules]
+        js['rules'] = [json.loads(r) for r in json_rules]
+        if js.get('network_id'):
+            js.pop('network_id')  # Omit for consistency across cloud providers
+        return json.dumps(js, sort_keys=True)
+
+
+class AzureSecurityGroupRule(BaseSecurityGroupRule):
+
+    def __init__(self, provider, rule, parent):
+        super(AzureSecurityGroupRule, self).__init__(provider, rule, parent)
+
+    @property
+    def name(self):
+        return self._rule.name
+
+    @property
+    def id(self):
+        return self._rule.id
+
+    @property
+    def ip_protocol(self):
+        return self._rule.protocol
+
+    @property
+    def from_port(self):
+        if self._rule.source_port_range == '*':
+            return self._rule.source_port_range
+        source_port_range = self._rule.source_port_range
+        port_range_split = source_port_range.split('-', 1)
+        return port_range_split[0]
+
+    @property
+    def to_port(self):
+        if self._rule.source_port_range == '*':
+            return self._rule.source_port_range
+        source_port_range = self._rule.source_port_range
+        port_range_split = source_port_range.split('-', 1)
+        return port_range_split[1]
+
+    @property
+    def cidr_ip(self):
+        return self._rule.destination_address_prefix
+
+    @property
+    def group(self):
+        return None
+
+    def to_json(self):
+        attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
+        js = {k: v for(k, v) in attr if not k.startswith('_')}
+        js['group'] = self.group.id if self.group else ''
+        js['parent'] = self.parent.id if self.parent else ''
+        return json.dumps(js, sort_keys=True)
+
+    def delete(self):
+        if self.is_default:
+            raise Exception('Default Security Rules cannot be deleted!')
+        security_group = self.parent.name
+        resource_group = self._provider.resource_group
+        sro = self._provider.azure_wrapper.delete_security_group_rule(self.name, resource_group, security_group)
+        for i, o in enumerate(self.parent._security_group.security_rules):
+            if o.name == self.name:
+                del self.parent._security_group.security_rules[i]
+                break
+
+
+class AzureBucket(BaseBucket):
+
+    def __init__(self, provider, bucket):
+        super(AzureBucket, self).__init__(provider)
+        self._bucket = bucket
+
+    @property
+    def id(self):
+        return self._bucket.name
+
+    @property
+    def name(self):
+        """
+        Get this bucket's name.
+        """
+        return self._bucket.name
+
+    def get(self, key):
+        """
+        Retrieve a given object from this bucket.
+        """
+        pass
+
+    def list(self, limit=None, marker=None):
+        """
+        List all objects within this bucket.
+
+        :rtype: BucketObject
+        :return: List of all available BucketObjects within this bucket.
+        """
+        pass
+
+    def delete(self, delete_contents=False):
+        """
+        Delete this bucket.
+        """
+        pass
+
+    def create_object(self, name):
+        return None
+
+    def exists(self, name):
+        pass

+ 88 - 0
cloudbridge/cloud/providers/azure/services.py

@@ -0,0 +1,88 @@
+from cloudbridge.cloud.base.resources import ClientPagedResultList
+from cloudbridge.cloud.base.services import BaseObjectStoreService, BaseSecurityGroupService, BaseSecurityService
+
+from .resources import AzureBucket, AzureSecurityGroup
+
+class AzureSecurityService(BaseSecurityService):
+    def __init__(self, provider):
+        super(AzureSecurityService, self).__init__(provider)
+
+        # Initialize provider services
+        #self._key_pairs = AzureKeyPairService(provider)
+        self._security_groups = AzureSecurityGroupService(provider)
+
+    @property
+    def key_pairs(self):
+        """
+        Provides access to key pairs for this provider.
+
+        :rtype: ``object`` of :class:`.KeyPairService`
+        :return: a KeyPairService object
+        """
+        return None
+
+    @property
+    def security_groups(self):
+        """
+        Provides access to security groups for this provider.
+
+        :rtype: ``object`` of :class:`.SecurityGroupService`
+        :return: a SecurityGroupService object
+        """
+        return self._security_groups
+
+
+class AzureSecurityGroupService(BaseSecurityGroupService):
+
+    def __init__(self, provider):
+        super(AzureSecurityGroupService, self).__init__(provider)
+
+    def get(self, sg_id):
+        for item in self.provider.azure_client.list_security_group(self.provider.resource_group):
+            if item.id == sg_id:
+                return AzureSecurityGroup(self.provider, item)
+        return None
+
+    def list(self, limit=None, marker=None):
+        nsglist = self.provider.azure_client.list_security_group(self.provider.resource_group)
+        network_security_group = [AzureSecurityGroup(self.provider, sg)
+                                  for sg in nsglist]
+        return ClientPagedResultList(self.provider, network_security_group, limit, marker)
+
+    def create(self, name, description, network_id):
+        raise NotImplementedError(
+            "AzureSecurityGroupService does not implement this method")
+
+    def find(self, name, limit=None, marker=None):
+        raise NotImplementedError(
+            "AzureSecurityGroupService does not implement this method")
+
+    def delete(self, group_id):
+        raise NotImplementedError(
+            "AzureSecurityGroupService does not implement this method")
+
+
+class AzureObjectStoreService(BaseObjectStoreService):
+
+    def __init__(self, provider):
+        super(AzureObjectStoreService, self).__init__(provider)
+
+    def get(self, bucket_id):
+        raise NotImplementedError(
+            "AzureObjectStoreService does not implement this service")
+
+    def find(self, name, limit=None, marker=None):
+        object_store = self.provider.azure_client.get_container(name)
+        object_stores =[]
+        if object_store:
+            object_stores.append(AzureBucket(self.provider, object_store))
+
+        return ClientPagedResultList(self.provider, object_stores,
+                                     limit=limit, marker=marker)
+
+    def list(self, limit=None, marker=None):
+        raise NotImplementedError(
+            "AzureObjectStoreService does not implement this method")
+
+    def create(self, name, location=None):
+        self.provider.azure_client.create_container(name)

+ 13 - 0
execute.py

@@ -0,0 +1,13 @@
+from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList
+
+config = {'azure_subscription_id': '7904d702-e01c-4826-8519-f5a25c866a96',
+          'azure_client_Id': '69621fe1-f59f-43de-8799-269007c76b95',
+          'azure_secret': 'Orcw9U5Kd4cUDntDABg0dygN32RQ4FGBYyLRaJ/BlrM=',
+          'azure_tenant': '75ec242e-054d-4b22-98a9-a4602ebb6027'
+          }
+
+provider = CloudProviderFactory().create_provider(ProviderList.AZURE, config)
+
+containers = provider.object_store.find('vhds')
+#new_container = provider.object_store.create('mysharedfiles')
+print(len(containers))

+ 2 - 1
setup.py

@@ -24,6 +24,7 @@ REQS_BASE = [
     'retrying>=1.3.3'
 ]
 REQS_AWS = ['boto3']
+REQS_AZURE = ['azure>=2.0.0rc6']
 REQS_OPENSTACK = [
     'openstacksdk',
     'python-novaclient>=7.0.0',
@@ -33,7 +34,7 @@ REQS_OPENSTACK = [
     'python-neutronclient>=6.0.0',
     'python-keystoneclient>=3.13.0'
 ]
-REQS_FULL = REQS_BASE + REQS_AWS + REQS_OPENSTACK
+REQS_FULL = REQS_BASE + REQS_AWS + REQS_AZURE + REQS_OPENSTACK
 # httpretty is required with/for moto 1.0.0 or AWS tests fail
 REQS_DEV = ([
     'tox>=2.1.1',

+ 273 - 0
test/helpers.py

@@ -0,0 +1,273 @@
+from contextlib import contextmanager
+import os
+import sys
+import unittest
+import functools
+from six import reraise
+
+from cloudbridge.cloud.factory import CloudProviderFactory
+from cloudbridge.cloud.interfaces import InstanceState
+from cloudbridge.cloud.interfaces import TestMockHelperMixin
+
+
+def parse_bool(val):
+    if val:
+        return str(val).upper() in ['TRUE', 'YES']
+    else:
+        return False
+
+
+@contextmanager
+def cleanup_action(cleanup_func):
+    """n csdmmnd
+    Context manager to carry out a given
+    cleanup action after carrying out a set
+    of tasks, or when an exception occurs.
+    If any errors occur during the cleanup
+    action, those are ignored, and the original
+    traceback is preserved.
+
+    :params func: This function is called if
+    an exception occurs or at the end of the
+    context block. If any exceptions raised
+        by func are ignored.
+    Usage:
+        with cleanup_action(lambda e: print("Oops!")):
+            do_something()
+    """
+    try:
+        yield
+    except Exception:
+        ex_class, ex_val, ex_traceback = sys.exc_info()
+        try:
+            cleanup_func()
+        except Exception as e:
+            print("Error during exception cleanup: {0}".format(e))
+        reraise(ex_class, ex_val, ex_traceback)
+    try:
+        cleanup_func()
+    except Exception as e:
+        print("Error during cleanup: {0}".format(e))
+
+
+def skipIfNoService(services):
+    """
+    A decorator for skipping tests if the provider
+    does not implement a given service.
+    """
+    def wrap(func):
+        """
+        The actual wrapper
+        """
+        @functools.wraps(func)
+        def wrapper(self, *args, **kwargs):
+            provider = getattr(self, 'provider')
+            if provider:
+                for service in services:
+                    if not provider.has_service(service):
+                        self.skipTest("Skipping test because '%s' service is"
+                                      " not implemented" % (service,))
+            func(self, *args, **kwargs)
+        return wrapper
+    return wrap
+
+
+TEST_DATA_CONFIG = {
+    "AWSCloudProvider": {
+        "image": os.environ.get('CB_IMAGE_AWS', 'ami-5ac2cd4d'),
+        "instance_type": os.environ.get('CB_INSTANCE_TYPE_AWS', 't2.nano'),
+        "placement": os.environ.get('CB_PLACEMENT_AWS', 'us-east-1a'),
+    },
+    "OpenStackCloudProvider": {
+        "image": os.environ.get('CB_IMAGE_OS',
+                                '842b949c-ea76-48df-998d-8a41f2626243'),
+        "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.tiny'),
+        "placement": os.environ.get('CB_PLACEMENT_OS', 'zone-r6'),
+    }
+}
+
+
+def get_provider_test_data(provider, key):
+    if "AWSCloudProvider" in provider.name:
+        return TEST_DATA_CONFIG.get("AWSCloudProvider").get(key)
+    elif "OpenStackCloudProvider" in provider.name:
+        return TEST_DATA_CONFIG.get("OpenStackCloudProvider").get(key)
+    return None
+
+
+def create_test_network(provider, name):
+    """
+    Create a network with one subnet, returning the network and subnet objects.
+    """
+    net = provider.network.create(name=name)
+    cidr_block = (net.cidr_block).split('/')[0] or '10.0.0.1'
+    sn = net.create_subnet(cidr_block='{0}/28'.format(cidr_block), name=name,
+                           zone=get_provider_test_data(provider, 'placement'))
+    return net, sn
+
+
+def delete_test_network(network):
+    """
+    Delete the supplied network, first deleting any contained subnets.
+    """
+    with cleanup_action(lambda: network.delete()):
+        for sn in network.subnets():
+            sn.delete()
+
+
+def create_test_instance(
+        provider, instance_name, subnet, zone=None, launch_config=None,
+        key_pair=None, security_groups=None):
+    return provider.compute.instances.create(
+        instance_name,
+        get_provider_test_data(provider, 'image'),
+        get_provider_test_data(provider, 'instance_type'),
+        subnet=subnet,
+        zone=zone,
+        key_pair=key_pair,
+        security_groups=security_groups,
+        launch_config=launch_config)
+
+
+def get_test_instance(provider, name, key_pair=None, security_groups=None,
+                      subnet=None):
+    launch_config = None
+    instance = create_test_instance(
+        provider,
+        name,
+        subnet=subnet,
+        key_pair=key_pair,
+        security_groups=security_groups,
+        launch_config=launch_config)
+    instance.wait_till_ready()
+    return instance
+
+
+def cleanup_test_resources(instance=None, network=None, security_group=None,
+                           key_pair=None):
+    with cleanup_action(lambda: delete_test_network(network)
+                        if network else None):
+        with cleanup_action(lambda: key_pair.delete() if key_pair else None):
+            with cleanup_action(lambda: security_group.delete()
+                                if security_group else None):
+                instance.terminate()
+                instance.wait_for(
+                    [InstanceState.TERMINATED, InstanceState.UNKNOWN],
+                    terminal_states=[InstanceState.ERROR])
+
+
+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.
+    """
+
+    def __init__(self, methodName, provider):
+        unittest.TestCase.__init__(self, methodName=methodName)
+        self.provider = provider
+
+    def setUp(self):
+        if isinstance(self.provider, TestMockHelperMixin):
+            self.provider.setUpMock()
+
+    def tearDown(self):
+        if isinstance(self.provider, TestMockHelperMixin):
+            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
+
+    def get_provider_wait_interval(self, provider_class):
+        if issubclass(provider_class, TestMockHelperMixin):
+            return 0
+        else:
+            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.
+        """
+        config = {'default_wait_interval':
+                  self.get_provider_wait_interval(provider_class)}
+        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

+ 31 - 0
test/test_azure_security_service.py

@@ -0,0 +1,31 @@
+import json
+import unittest
+import uuid
+
+from cloudbridge.cloud.interfaces import TestMockHelperMixin
+
+from test.helpers import ProviderTestBase
+import test.helpers as helpers
+
+class AzureSecurityServiceTestCase(ProviderTestBase):
+    def __init__(self, methodName, provider):
+        super(AzureSecurityServiceTestCase, self).__init__(
+            methodName=methodName, provider=provider)
+
+    @helpers.skipIfNoService(['security.security_groups'])
+    def test_azure_security_group_list(self):
+        print(self.provider)
+        sgl = self.provider.security.security_groups.list()
+        found_sg = [g.name for g in sgl]
+        print("List( " + "Name-" + sgl[0].name + "  Id-" + sgl[0].id + " )")
+        self.assertTrue(
+            len(sgl) == 3,
+            "Security group {0} should have been deleted but still exists.")
+
+    @helpers.skipIfNoService(['security.security_groups'])
+    def test_azure_security_group_get(self):
+        sgl = self.provider.security.security_groups.get("sg2")
+        print("Get ( " + "Name - " + sgl.name + "  Id - " + sgl.id + " )")
+        self.assertTrue(
+            sgl.name == "sec_group2",
+            "Security group {0} should have been deleted but still exists.")