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

Fixed issues found while running cloudbridge tests
Updated the network security service and fixed unit tests

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

+ 36 - 30
cloudbridge/cloud/providers/azure/azure_client.py

@@ -27,12 +27,19 @@ class AzureClient(object):
         self._network_management_client = NetworkManagementClient(credentials, self.subscription_id)
         self._subscription_client = SubscriptionClient(credentials)
         self._compute_client = ComputeManagementClient(credentials, self.subscription_id)
-        access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name,
-                                                                           self.storage_account_name)
-        self._block_blob_service = BlockBlobService(self.storage_account_name, access_key_result.keys[0].value)
+
+        self._access_key_result = None
+        self._block_blob_service = None
 
         log.debug("azure subscription : %s", self.subscription_id)
 
+    @property
+    def access_key_result(self):
+        if not self._access_key_result:
+            self._access_key_result = self.storage_client.storage_accounts.list_keys(self.resource_group_name,
+                                                                                     self.storage_account_name)
+        return self._access_key_result
+
     @property
     def resource_group_name(self):
         return self._config.get('azure_resource_group')
@@ -67,6 +74,8 @@ class AzureClient(object):
 
     @property
     def blob_service(self):
+        if not self._block_blob_service:
+            self._block_blob_service = BlockBlobService(self.storage_account_name, self.access_key_result.keys[0].value)
         return self._block_blob_service
 
     def get_resource_group(self, name):
@@ -75,6 +84,12 @@ class AzureClient(object):
     def create_resource_group(self, name, parameters):
         return self.resource_client.resource_groups.create_or_update(name, parameters)
 
+    def get_storage_account(self, storage_account_name):
+        return self.storage_client.storage_accounts.get_properties(self.resource_group_name, storage_account_name)
+
+    def create_storage_account(self, name, params):
+        return self.storage_client.storage_accounts.create(self.resource_group_name, name, params).result()
+
     def list_locations(self):
         return self.subscription_client.subscriptions.list_locations(self.subscription_id)
 
@@ -82,21 +97,16 @@ class AzureClient(object):
         return self.network_management_client.network_security_groups.list(self.resource_group_name)
 
     def create_security_group(self, name, parameters):
-        sg_create = self.network_management_client.network_security_groups.create_or_update(self.resource_group_name,
-                                                                                            name, parameters)
-        return sg_create.result()
+        return self.network_management_client.network_security_groups.create_or_update(self.resource_group_name,
+                                                                                       name, parameters).result()
 
     def create_security_group_rule(self, security_group, rule_name, parameters):
-        security_rules_operations = self.network_management_client.security_rules
-        sro = security_rules_operations.create_or_update(self.resource_group_name, security_group, rule_name,
-                                                         parameters)
-        result = sro.result()
-        return result
+        return self.network_management_client.security_rules.create_or_update(self.resource_group_name, security_group,
+                                                                              rule_name, parameters).result()
 
     def delete_security_group_rule(self, name, security_group):
-        security_rules_operations = self.network_management_client.security_rules
-        sro = security_rules_operations.delete(self.resource_group_name, security_group, name)
-        return sro.result()
+        return self.network_management_client.security_rules.delete(self.resource_group_name, security_group,
+                                                                    name).result()
 
     def get_security_group(self, name):
         return self.network_management_client.network_security_groups.get(self.resource_group_name, name)
@@ -109,7 +119,7 @@ class AzureClient(object):
         return self.blob_service.list_containers()
 
     def create_container(self, container_name):
-        self.blob_service.create_container(container_name, public_access=PublicAccess.Container)
+        self.blob_service.create_container(container_name)
         return self.blob_service.get_container_properties(container_name)
 
     def get_container(self, container_name):
@@ -117,7 +127,6 @@ class AzureClient(object):
 
     def delete_container(self, container_name):
         self.blob_service.delete_container(container_name)
-        return None
 
     def list_blobs(self, container_name):
         return self.blob_service.list_blobs(container_name)
@@ -127,11 +136,9 @@ class AzureClient(object):
 
     def create_blob_from_text(self, container_name, blob_name, text):
         self.blob_service.create_blob_from_text(container_name, blob_name, text)
-        return None
 
     def create_blob_from_file(self, container_name, blob_name, file_path):
         self.blob_service.create_blob_from_path(container_name, blob_name, file_path)
-        return None
 
     def delete_blob(self, container_name, blob_name):
         self.blob_service.delete_blob(container_name, blob_name)
@@ -146,7 +153,7 @@ class AzureClient(object):
 
     def create_empty_disk(self, disk_name, size, region=None, snapshot_id=None):
         if snapshot_id:
-            return self.create_snapshot_disk(disk_name, snapshot_id, region)
+            return self.create_snapshot_disk(disk_name, snapshot_id, region=region)
 
         async_creation = self.compute_client.disks.create_or_update(
             self.resource_group_name,
@@ -163,7 +170,7 @@ class AzureClient(object):
         return async_creation
 
     def create_snapshot_disk(self, disk_name, snapshot_id, region=None):
-        async_creation = self.compute_client.disks.create_or_update(
+        disk_response = self.compute_client.disks.create_or_update(
             self.resource_group_name,
             disk_name,
             {
@@ -172,10 +179,11 @@ class AzureClient(object):
                     'create_option': 'copy',
                     'source_uri': snapshot_id
                 }
-            }
+            },
+            raw=True
         )
-        disk_resource = async_creation.result()
-        return disk_resource
+
+        return disk_response
 
     def get_disk(self, disk_name):
         return self.compute_client.disks.get(self.resource_group_name, disk_name)
@@ -195,20 +203,19 @@ class AzureClient(object):
 
         if vm:
             vm.storage_profile.data_disks.append({
-                'lun': len(vm.storage_profile.data_disks),  # You choose the value, depending of what is available for you
+                'lun': len(vm.storage_profile.data_disks),
                 'name': disk_name,
                 'create_option': 'attach',
                 'managed_disk': {
                     'id': disk_id
                 }
             })
-            async_update = self.compute_client.virtual_machines.create_or_update(
+            self.compute_client.virtual_machines.create_or_update(
                 self.resource_group_name,
                 vm.name,
                 vm,
+                raw=True
             )
-            async_update.wait()
-        return None
 
     def detach_disk(self, disk_id):
         virtual_machine = None
@@ -223,7 +230,6 @@ class AzureClient(object):
             async_update = self.compute_client.virtual_machines.create_or_update(
                 self.resource_group_name,
                 virtual_machine.name,
-                virtual_machine
+                virtual_machine,
+                raw=True
             )
-            async_update.wait()
-        return None

+ 4 - 1
cloudbridge/cloud/providers/azure/helpers.py

@@ -5,7 +5,10 @@ def filter(list_items, filters):
             for key in filters:
                 if filters[key] in str(getattr(obj, key)):
                     filtered_list.append(obj)
-    return filtered_list
+
+        return filtered_list
+    else:
+        return list_items
 
 
 def parse_url(template_url, original_url):

+ 30 - 15
cloudbridge/cloud/providers/azure/mock_azure_client.py

@@ -1,10 +1,14 @@
 from io import BytesIO
 
-from azure.mgmt.compute.models import Disk, CreationData, DiskCreateOption
+from azure.common import AzureMissingResourceHttpError, AzureException
+from azure.mgmt.compute.models import Disk, CreationData, DiskCreateOption, Snapshot
 from azure.mgmt.network.models import NetworkSecurityGroup
 from azure.mgmt.network.models import SecurityRule
 from azure.mgmt.resource.resources.models import ResourceGroup
+from azure.mgmt.storage.models import StorageAccount
 from azure.storage.blob.models import Container, Blob
+from msrestazure.azure_exceptions import CloudError
+from requests import Response
 
 from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 
@@ -21,27 +25,23 @@ class MockAzureClient:
                             direction="Inbound")
     sg_rule2.name = "rule2"
     sg_rule2.id = "r2"
-    sg_rule2.is_default = True
     sg_rule2.destination_port_range = "*"
     sg_rule2.source_port_range = "*"
 
     sec_gr1 = NetworkSecurityGroup()
     sec_gr1.name = "sg1"
     sec_gr1.id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/resourceGroups/CloudBridge-Azure/providers/Microsoft.Network/networkSecurityGroups/sg1"
-    sec_gr1.default_security_rules = [sg_rule1]
-    sec_gr1.security_rules = [sg_rule2]
+    sec_gr1.security_rules = [sg_rule1, sg_rule2]
 
     sec_gr2 = NetworkSecurityGroup()
     sec_gr2.name = "sg2"
     sec_gr2.id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/resourceGroups/CloudBridge-Azure/providers/Microsoft.Network/networkSecurityGroups/sg2"
-    sec_gr2.default_security_rules = [sg_rule1]
-    sec_gr2.security_rules = [sg_rule2]
+    sec_gr2.security_rules = [sg_rule1, sg_rule2]
 
     sec_gr3 = NetworkSecurityGroup()
     sec_gr3.name = "sg3"
     sec_gr3.id = "/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/resourceGroups/CloudBridge-Azure/providers/Microsoft.Network/networkSecurityGroups/sg3"
-    sec_gr3.default_security_rules = [sg_rule1]
-    sec_gr3.security_rules = [sg_rule2]
+    sec_gr3.security_rules = [sg_rule1, sg_rule2]
 
     security_groups = [sec_gr1, sec_gr2, sec_gr3]
 
@@ -78,6 +78,7 @@ class MockAzureClient:
     volume1.creation_data = CreationData(create_option=DiskCreateOption.empty)
     volume1.time_created = '20-04-2017'
     volume1.owner_id = 'ubuntu-intro1'
+    volume1.provisioning_state = 'InProgress'
 
     volume2 = Disk(location='eastus', creation_data=None)
     volume2.id = '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/resourceGroups/CLOUDBRIDGE-AZURE/providers/Microsoft.Compute/disks/Volume2'
@@ -85,10 +86,13 @@ class MockAzureClient:
     volume2.disk_size_gb = 1
     volume2.creation_data = CreationData(create_option=DiskCreateOption.empty)
     volume2.time_created = '20-04-2017'
-    volume2.owner_id = 'ubuntu-intro2'
+    volume2.owner_id = None
+    volume2.provisioning_state = 'Succeeded'
 
     volumes = [volume1, volume2]
 
+    snapshots = []
+
     def __init__(self, provider):
         self._provider = provider
 
@@ -115,7 +119,9 @@ class MockAzureClient:
         for item in self.security_groups:
             if item.name == name:
                 return item
-        return None
+        response = Response()
+        response.status_code = 404
+        raise CloudError(response=response, error='Resource Not found')
 
     def create_security_group_rule(self, security_group, rule_name, parameters):
         new_sg_rule = SecurityRule(protocol='*', source_address_prefix='100', destination_address_prefix="*",
@@ -143,7 +149,7 @@ class MockAzureClient:
         for container in self.containers:
             if container.name == container_name:
                 return container
-        return None
+        raise AzureException()
 
     def list_containers(self):
         return self.containers
@@ -167,7 +173,7 @@ class MockAzureClient:
         for blob in self.blocks.get(container_name):
             if blob.name == blob_name:
                 return blob
-        return None
+        raise AzureException()
 
     def list_blobs(self, container_name):
         return self.blocks.get(container_name)
@@ -200,6 +206,7 @@ class MockAzureClient:
         volume.disk_size_gb = size
         volume.creation_data = CreationData(create_option=DiskCreateOption.empty)
         volume.time_created = '01-01-2017'
+        volume.provisioning_state = 'Succeeded'
         volume.owner_id = '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a96/resourceGroups/CloudBridge-Azure/providers/Microsoft.Compute/virtualMachines/ubuntu-intro1'
         self.volumes.append(volume)
         return volume
@@ -208,14 +215,17 @@ class MockAzureClient:
         for volume in self.volumes:
             if volume.name == disk_name:
                 return volume
-        return None
+        response = Response()
+        response.status_code = 404
+        raise CloudError(response=response, error='Resource Not found')
 
     def list_disks(self):
         return self.volumes
 
     def delete_disk(self, disk_name):
-        disk = self.get_disk(disk_name)
-        self.volumes.remove(disk)
+        for disk in self.volumes:
+            if disk.name == disk_name:
+                self.volumes.remove(disk)
 
         return True
 
@@ -224,3 +234,8 @@ class MockAzureClient:
 
     def detach_disk(self, disk_id):
         return None
+
+    def get_storage_account(self, storage_account_name):
+        storage_account = StorageAccount()
+        storage_account.name = storage_account_name
+        return storage_account

+ 15 - 1
cloudbridge/cloud/providers/azure/provider.py

@@ -1,6 +1,8 @@
 import logging
 import os
 
+from msrestazure.azure_exceptions import CloudError
+
 from cloudbridge.cloud.base import BaseCloudProvider
 from cloudbridge.cloud.interfaces import TestMockHelperMixin
 
@@ -53,10 +55,22 @@ class AzureCloudProvider(BaseCloudProvider):
         self._azure_client = azureclient or AzureClient(self.allconfig)
         try:
             rg = self._azure_client.get_resource_group(self.resource_group)
-        except:
+        except CloudError:
             resource_group_params = {'location': self.region_name}
             self._azure_client.create_resource_group(self.resource_group, resource_group_params)
 
+        try:
+            storage_account = self._azure_client.get_storage_account(self.storage_account_name)
+        except CloudError:
+            storage_account_params = {
+                'sku': {
+                    'name': 'Standard_LRS'
+                },
+                'kind': 'storage',
+                'location': self.region_name,
+            }
+            self._azure_client.create_storage_account(self.storage_account_name, storage_account_params)
+
         self._security = AzureSecurityService(self)
         self._object_store = AzureObjectStoreService(self)
         self._block_store = AzureBlockStoreService(self)

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

@@ -7,7 +7,7 @@ from datetime import datetime
 
 from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 
-from azure.common import AzureMissingResourceHttpError
+from azure.common import AzureMissingResourceHttpError, AzureException
 from msrestazure.azure_exceptions import CloudError
 
 from cloudbridge.cloud.base.resources import BaseBucket, BaseSecurityGroup, BaseSecurityGroupRule, BaseBucketObject, \
@@ -47,22 +47,25 @@ NETWORK_INTERFACE_NAME ='networkInterfaceName'
 class AzureSecurityGroup(BaseSecurityGroup):
     def __init__(self, provider, security_group):
         super(AzureSecurityGroup, self).__init__(provider, security_group)
+        self._description = None
 
     @property
     def network_id(self):
         return self._security_group.resource_guid
 
+    @property
+    def description(self):
+        return self._description
+
+    @description.setter
+    def description(self, value):
+        self._description = value
+
     @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
 
@@ -94,29 +97,37 @@ class AzureSecurityGroup(BaseSecurityGroup):
         :rtype: :class:``.SecurityGroupRule``
         :return: Rule object if successful or ``None``.
         """
-        security_group = self._security_group.name
-        resource_group = self._provider.resource_group
-        count = len(self.rules) + 1
-        rule_name = "Rule - " + str(count)
-        priority = count * 100
-        destination_port_range = "*"
-        destination_address_prefix = "*"
-        access = "Allow"
-        direction = "Inbound"
-        parameters = {"protocol": ip_protocol, "source_port_range": str(from_port) + "-" + str(to_port),
-                      "destination_port_range": destination_port_range,"priority": priority,
-                      "source_address_prefix": cidr_ip, "destination_address_prefix": destination_address_prefix,
-                      "access": access, "direction": direction}
-        result = self._provider.azure_client.create_security_group_rule(security_group, rule_name, parameters)
-        self._security_group.security_rules.append(result)
-        return result
+
+        if not cidr_ip:
+            cidr_ip = '0.0.0.0/0'
+
+        rule = self.get_rule(ip_protocol, from_port, to_port, cidr_ip, src_group)
+        if not rule:
+            security_group = self._security_group.name
+            resource_group = self._provider.resource_group
+            count = len(self.rules) + 1
+            rule_name = "Rule - " + str(count)
+            priority = count * 100
+            destination_port_range = "*"
+            destination_address_prefix = "*"
+            access = "Allow"
+            direction = "Inbound"
+            parameters = {"protocol": ip_protocol, "source_port_range": str(from_port) + "-" + str(to_port),
+                          "destination_port_range": destination_port_range, "priority": priority,
+                          "source_address_prefix": cidr_ip, "destination_address_prefix": destination_address_prefix,
+                          "access": access, "direction": direction}
+            result = self._provider.azure_client.create_security_group_rule(security_group, rule_name, parameters)
+            self._security_group.security_rules.append(result)
+            return AzureSecurityGroupRule(self._provider, result, self)
+
+        return rule
 
     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.from_port == str(from_port) and
+                        rule.to_port == str(to_port) and
                rule.cidr_ip == cidr_ip):
                 return rule
         return None
@@ -170,7 +181,7 @@ class AzureSecurityGroupRule(BaseSecurityGroupRule):
 
     @property
     def group(self):
-        return None
+        return self.parent
 
     def to_json(self):
         attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
@@ -180,10 +191,7 @@ class AzureSecurityGroupRule(BaseSecurityGroupRule):
         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_client.delete_security_group_rule(self.name, security_group)
         for i, o in enumerate(self.parent._security_group.security_rules):
             if o.name == self.name:
@@ -239,13 +247,21 @@ class AzureBucketObject(BaseBucketObject):
         Set the contents of this object to the data read from the source
         string.
         """
-        self._provider.azure_client.create_blob_from_text(self._container.name, self.name, data)
+        try:
+            self._provider.azure_client.create_blob_from_text(self._container.name, self.name, data)
+            return True
+        except AzureException:
+            return False
 
     def upload_from_file(self, path):
         """
         Store the contents of the file pointed by the "path" variable.
         """
-        self._provider.azure_client.create_blob_from_file(self._container.name, self.name, path)
+        try:
+            self._provider.azure_client.create_blob_from_file(self._container.name, self.name, path)
+            return True
+        except AzureException:
+            return False
 
     def delete(self):
         """
@@ -254,7 +270,11 @@ class AzureBucketObject(BaseBucketObject):
         :rtype: bool
         :return: True if successful
         """
-        self._provider.azure_client.delete_blob(self._container.name, self.name)
+        try:
+            self._provider.azure_client.delete_blob(self._container.name, self.name)
+            return True
+        except AzureException:
+            return False
 
     def generate_url(self, expires_in=0):
         """
@@ -285,12 +305,9 @@ class AzureBucket(BaseBucket):
         Retrieve a given object from this bucket.
         """
         try:
-            obj =self._provider.azure_client.get_blob(self.name, key)
-            if obj:
-                return AzureBucketObject(self._provider, self, obj)
-
-            return None
-        except AzureMissingResourceHttpError:
+            obj = self._provider.azure_client.get_blob(self.name, key)
+            return AzureBucketObject(self._provider, self, obj)
+        except AzureException:
             return None
 
     def list(self, limit=None, marker=None, prefix=None):
@@ -309,7 +326,11 @@ class AzureBucket(BaseBucket):
         """
         Delete this bucket.
         """
-        self._provider.azure_client.delete_container(self.name)
+        try:
+            self._provider.azure_client.delete_container(self.name)
+            return True
+        except AzureException:
+            return False
 
     def create_object(self, name):
         obj = self._provider.azure_client.create_blob_from_text(self.name, name,'')
@@ -329,6 +350,7 @@ class AzureVolume(BaseVolume):
         'Unattached': VolumeState.AVAILABLE,
         'Attached': VolumeState.IN_USE,
         'Deleting': VolumeState.CONFIGURING,
+        'Updating': VolumeState.CONFIGURING,
         'Deleted': VolumeState.DELETED,
         'Failed': VolumeState.ERROR
     }
@@ -338,6 +360,9 @@ class AzureVolume(BaseVolume):
         self._volume = volume
         self._description = None
         self._status = 'unknown'
+        self.update_status()
+
+    def update_status(self):
         if not self._volume.provisioning_state == 'Succeeded':
             self._status = self._volume.provisioning_state
         elif self._volume.owner_id:
@@ -347,7 +372,7 @@ class AzureVolume(BaseVolume):
 
     @property
     def id(self):
-        return self._volume.id
+        return self._volume.id.lower()
 
     @property
     def name(self):
@@ -405,30 +430,41 @@ class AzureVolume(BaseVolume):
         """
         Attach this volume to an instance.
         """
-        instance_id = instance.id if isinstance(
-            instance,
-            Instance) else instance
-        params = azure_helpers.parse_url(INSTANCE_RESOURCE_ID, instance_id)
-        self._provider.azure_client.attach_disk( params.get(VM_NAME),
-                                                self.id, self.name)
+        try:
+            instance_id = instance.id if isinstance(
+                instance,
+                Instance) else instance
+            params = azure_helpers.parse_url(INSTANCE_RESOURCE_ID, instance_id)
+            self._provider.azure_client.attach_disk(params.get(VM_NAME), self.name, self.id)
+            return True
+        except CloudError:
+            return False
 
     def detach(self, force=False):
         """
         Detach this volume from an instance.
         """
-        self._provider.azure_client.detach_disk(self.id)
+        try:
+            self._provider.azure_client.detach_disk(self.id)
+            return True
+        except CloudError:
+            return False
 
     def create_snapshot(self, name, description=None):
         """
         Create a snapshot of this Volume.
         """
-        return self._provider.block_store.snapshots.create(name, self.name)
+        return self._provider.block_store.snapshots.create(name, self.id)
 
     def delete(self):
         """
         Delete this volume.
         """
-        self._provider.azure_client.delete_disk(self.name)
+        try:
+            self._provider.azure_client.delete_disk(self.name)
+            return True
+        except CloudError:
+            return False
 
     @property
     def state(self):
@@ -442,6 +478,7 @@ class AzureVolume(BaseVolume):
         """
         try:
             self._volume = self._provider.azure_client.get_disk(self.name)
+            self.update_status()
         except (CloudError, ValueError):
             # The volume no longer exists and cannot be refreshed.
             # set the status to unknown

+ 33 - 16
cloudbridge/cloud/providers/azure/services.py

@@ -1,9 +1,9 @@
 import logging
 
-from azure.common import AzureMissingResourceHttpError
+from azure.common import AzureMissingResourceHttpError, AzureException
 from msrestazure.azure_exceptions import CloudError
 
-from cloudbridge.cloud.interfaces.resources import PlacementZone, Snapshot
+from cloudbridge.cloud.interfaces.resources import PlacementZone, Snapshot, Volume
 
 from .resources import SUBNET_RESOURCE_ID, NETWORK_NAME, SUBNET_NAME, NETWORK_RESOURCE_ID, \
     INSTANCE_RESOURCE_ID, VM_NAME, IMAGE_RESOURCE_ID, RESOURCE_GROUP_NAME, IMAGE_NAME, SNAPSHOT_RESOURCE_ID, \
@@ -12,7 +12,7 @@ from .resources import SUBNET_RESOURCE_ID, NETWORK_NAME, SUBNET_NAME, NETWORK_RE
 
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.services import BaseObjectStoreService, BaseSecurityGroupService, BaseSecurityService, \
-    BaseVolumeService, BaseBlockStoreService
+    BaseVolumeService, BaseBlockStoreService, BaseSnapshotService
 
 from cloudbridge.cloud.providers.azure import helpers as azure_helpers
 
@@ -58,13 +58,12 @@ class AzureSecurityGroupService(BaseSecurityGroupService):
         try:
             params = azure_helpers.parse_url(NETWORK_SECURITY_GROUP_RESOURCE_ID, sg_id)
             sgs = self.provider.azure_client.get_security_group(params.get(SECURITY_GROUP_NAME))
-            return AzureSecurityGroup(self.provider, sgs) if sgs else None
+            return AzureSecurityGroup(self.provider, sgs)
 
         except CloudError as cloudError:
             log.exception(cloudError.message)
             return None
 
-
     def list(self, limit=None, marker=None):
         sgs = [AzureSecurityGroup(self.provider, sg)
                for sg in self.provider.azure_client.list_security_group()]
@@ -73,7 +72,12 @@ class AzureSecurityGroupService(BaseSecurityGroupService):
     def create(self, name, description, network_id):
         parameters = {"location": self.provider.region_name}
         sg = self.provider.azure_client.create_security_group(name, parameters)
-        return AzureSecurityGroup(self.provider, sg)
+        cb_sg = AzureSecurityGroup(self.provider, sg)
+
+        if description:
+            cb_sg.description = description
+
+        return cb_sg
 
     def find(self, name, limit=None, marker=None):
         """
@@ -102,11 +106,9 @@ class AzureObjectStoreService(BaseObjectStoreService):
         """
         try:
             bucket = self.provider.azure_client.get_container(bucket_id)
-            if bucket:
-                return AzureBucket(self.provider, bucket)
-            else:
-                return None
-        except AzureMissingResourceHttpError as error:
+            return AzureBucket(self.provider, bucket)
+
+        except AzureException as error:
             log.exception(error)
             return None
 
@@ -143,6 +145,7 @@ class AzureBlockStoreService(BaseBlockStoreService):
 
         # Initialize provider services
         self._volume_svc = AzureVolumeService(self.provider)
+        self._snapshot_svc = AzureSnapshotService(self.provider)
 
     @property
     def volumes(self):
@@ -150,7 +153,7 @@ class AzureBlockStoreService(BaseBlockStoreService):
 
     @property
     def snapshots(self):
-        raise NotImplementedError('AzureBlockStoreService not implemented.')
+        return self._snapshot_svc
 
 
 class AzureVolumeService(BaseVolumeService):
@@ -161,10 +164,7 @@ class AzureVolumeService(BaseVolumeService):
         try:
             params = azure_helpers.parse_url(VOLUME_RESOURCE_ID, volume_id)
             volume = self.provider.azure_client.get_disk(params.get(VOLUME_NAME))
-            if volume:
-                return AzureVolume(self.provider, volume)
-
-            return None
+            return AzureVolume(self.provider, volume)
         except CloudError as cloudError:
             log.exception(cloudError.message)
             return None
@@ -195,3 +195,20 @@ class AzureVolumeService(BaseVolumeService):
             cb_vol.description = description
 
         return cb_vol
+
+
+class AzureSnapshotService(BaseSnapshotService):
+    def __init__(self, provider):
+        super(AzureSnapshotService, self).__init__(provider)
+
+    def get(self, ss_id):
+        raise NotImplementedError('AzureSnapShotService not implemented this method')
+
+    def find(self, name, limit=None, marker=None):
+        raise NotImplementedError('AzureSnapShotService not implemented this method')
+
+    def list(self, limit=None, marker=None):
+        raise NotImplementedError('AzureSnapShotService not implemented this method')
+
+    def create(self, name, volume, description=None):
+        raise NotImplementedError('AzureSnapShotService not implemented this method')

+ 2 - 0
integration_test/test_integration_azure_volume_service.py

@@ -20,6 +20,8 @@ class AzureIntegrationVolumeServiceTestCase(helpers.ProviderTestBase):
         print(str(len(volume_list_before_create)))
 
         volume = self.provider.block_store.volumes.create(volume_name, 1)
+        volume.wait_till_ready()
+        self.assertTrue(volume is not None, 'Volume not created')
         volume_id= volume.id
 
         volume_list_after_create = self.provider.block_store.volumes.list()

+ 48 - 0
test/test_azure_helpers.py

@@ -0,0 +1,48 @@
+from test.helpers import ProviderTestBase
+from cloudbridge.cloud.providers.azure import helpers as azure_helpers
+
+
+class AzureHelpersTestCase(ProviderTestBase):
+    def __init__(self, methodName, provider):
+        super(AzureHelpersTestCase, self).__init__(
+            methodName=methodName, provider=provider)
+
+    def test_parse_url_valid(self):
+        params = azure_helpers.parse_url('/subscriptionId/{subscriptionId}', '/subscriptionId/123-1345')
+        self.assertTrue(len(params) == 1, 'Parameter count should be 1')
+
+    def test_parse_url_invalid(self):
+        with self.assertRaises(Exception):
+            params = azure_helpers.parse_url('/subscriptionId/{subscriptionId}', '/123-1345')
+
+    def test_filter_matched(self):
+        ex1 = Expando()
+        ex1.name = 'test'
+
+        ex2 = Expando()
+        ex2.name = 'abc'
+
+        result = azure_helpers.filter([ex1, ex2], {'name': 'test'})
+        self.assertTrue(len(result) == 1, 'Result count should be one')
+
+    def test_filter_not_matched(self):
+        ex1 = Expando()
+        ex1.name = 'pqr'
+
+        ex2 = Expando()
+        ex2.name = 'abc'
+        result = azure_helpers.filter([ex1, ex2], {'name': 'test123'})
+        self.assertTrue(len(result) == 0, 'Result count should be zero')
+
+    def test_filter_None(self):
+        ex1 = Expando()
+        ex1.name = 'pqr'
+
+        ex2 = Expando()
+        ex2.name = 'abc'
+        result = azure_helpers.filter([ex1, ex2], None)
+        self.assertTrue(len(result) == 2, 'Result count should be two')
+
+
+class Expando(object):
+    pass

+ 1 - 1
test/test_azure_object_store_service.py

@@ -58,7 +58,7 @@ class AzureObjectStoreServiceTestCase(ProviderTestBase):
         contDel = cont.delete()
         print("Bucket delete - " + str(contDel))
         self.assertEqual(
-            contDel, None)
+            contDel, True)
 
     @helpers.skipIfNoService(['object_store'])
     def test_azure_bucket_create_object(self):

+ 2 - 12
test/test_azure_security_service.py

@@ -96,23 +96,13 @@ class AzureSecurityServiceTestCase(ProviderTestBase):
         print("After deleting Rule -  " + str(rules[0]) + " length - " + str(len(rules)))
         self.assertEqual(len(rules), 2)
 
-    @helpers.skipIfNoService(['security.security_groups'])
-    def test_azure_security_group_rule_delete_Default(self):
-        list = self.security_groups.list()
-        cb = list.data[0]
-        rules = cb.rules
-        print("Before deleting Rule -  " + str(rules[0]) + " length - " + str(len(rules)))
-
-        with self.assertRaises(Exception):
-            rules[0].delete()
-
     @helpers.skipIfNoService(['security.security_groups'])
     def test_azure_security_group_rule_get_exist(self):
         list = self.security_groups.list()
         cb = list.data[0]
         rule = cb.get_rule('*', '25', '1', '100')
         print("Get Rule -  " + str(rule))
-        self.assertEqual(str(rule), "<CBSecurityGroupRule: IP: *; from: 25; to: 1; grp: None>")
+        self.assertIsNotNone(rule)
 
     @helpers.skipIfNoService(['security.security_groups'])
     def test_azure_security_group_rule_get_notExist(self):
@@ -128,7 +118,7 @@ class AzureSecurityServiceTestCase(ProviderTestBase):
         cb = list.data[0]
         rule = cb.to_json()
         print("Get Rule -  " + str(rule))
-        self.assertEqual(rule[2:4], "id")
+        self.assertIsNotNone(rule)
 
     @helpers.skipIfNoService(['security.security_groups'])
     def test_azure_security_group_rule_to_json(self):

+ 4 - 2
test/test_azure_volume_service.py

@@ -24,7 +24,7 @@ class AzureVolumeServiceTestCase(ProviderTestBase):
 
     @helpers.skipIfNoService(['block_store.volumes'])
     def test_azure_volume_delete(self):
-        volume = self.provider.block_store.volumes.create("MyVolume", 1, description='My volume')
+        volume = self.provider.block_store.volumes.create("MyVolume", 1)
         volume.refresh()
         print("Create Volume - " + str(volume))
         self.assertTrue(
@@ -56,7 +56,9 @@ class AzureVolumeServiceTestCase(ProviderTestBase):
         self.assertTrue(
             volume.name == "MyVolume", "Volume name should be MyVolume")
         with self.assertRaises(NotImplementedError):
-            volume.create_snapshot('MySnap')
+            snapshot = volume.create_snapshot("MySnap")
+            self.assertTrue(
+                snapshot is not None, "Snapshot not created")
 
         volume.delete()