Selaa lähdekoodia

Wrapped reference and added retry for tests

Alexandru Mahmoud 7 vuotta sitten
vanhempi
sitoutus
c6d33f8616

+ 26 - 10
cloudbridge/cloud/providers/azure/azure_client.py

@@ -4,6 +4,7 @@ from io import BytesIO
 
 from azure.common.credentials import ServicePrincipalCredentials
 from azure.mgmt.compute import ComputeManagementClient
+from azure.mgmt.devtestlabs.models import GalleryImageReference
 from azure.mgmt.network import NetworkManagementClient
 from azure.mgmt.resource import ResourceManagementClient
 from azure.mgmt.resource.subscriptions import SubscriptionClient
@@ -79,8 +80,14 @@ VOLUME_NAME = 'diskName'
 # Listing possible somewhat through:
 # azure.mgmt.devtestlabs.operations.GalleryImageOperations
 gallery_image_references = \
-    ['Canonical/UbuntuServer/16.04.0-LTS/latest',
-     'Canonical/UbuntuServer/14.04.5-LTS/latest']
+    [GalleryImageReference(publisher='Canonical',
+                           offer='UbuntuServer',
+                           sku='16.04.0-LTS',
+                           version='latest'),
+     GalleryImageReference(publisher='Canonical',
+                           offer='UbuntuServer',
+                           sku='14.04.5-LTS',
+                           version='latest')]
 
 
 class AzureClient(object):
@@ -376,6 +383,12 @@ class AzureClient(object):
             raw=True
         )
 
+    def is_gallery_image(self, image_id):
+        url_params = azure_helpers.parse_url(IMAGE_RESOURCE_ID,
+                                             image_id)
+        # If it is a gallery image, it will always have an offer
+        return 'offer' in url_params
+
     def create_image(self, name, params):
         return self.compute_client.images. \
             create_or_update(self.resource_group, name,
@@ -384,20 +397,23 @@ class AzureClient(object):
     def delete_image(self, image_id):
         url_params = azure_helpers.parse_url(IMAGE_RESOURCE_ID,
                                              image_id)
-        if len(url_params) <= 3:
+        if not self.is_gallery_image(image_id):
             name = url_params.get(IMAGE_NAME)
             self.compute_client.images.delete(self.resource_group, name).wait()
 
     def list_images(self):
-        return self.compute_client.images. \
-            list_by_resource_group(self.resource_group) \
-            + gallery_image_references
+        return list(self.compute_client.images.
+                    list_by_resource_group(self.resource_group)).\
+                     append(gallery_image_references)
 
     def get_image(self, image_id):
         url_params = azure_helpers.parse_url(IMAGE_RESOURCE_ID,
                                              image_id)
-        if len(url_params) > 3:
-            return url_params
+        if self.is_gallery_image(image_id):
+            return GalleryImageReference(publisher=url_params['publisher'],
+                                         offer=url_params['offer'],
+                                         sku=url_params['sku'],
+                                         version=url_params['version'])
         else:
             name = url_params.get(IMAGE_NAME)
             return self.compute_client.images.get(self.resource_group, name)
@@ -405,8 +421,8 @@ class AzureClient(object):
     def update_image_tags(self, image_id, tags):
         url_params = azure_helpers.parse_url(IMAGE_RESOURCE_ID,
                                              image_id)
-        if len(url_params) > 3:
-            return 1
+        if self.is_gallery_image(image_id):
+            return True
         else:
             name = url_params.get(IMAGE_NAME)
             return self.compute_client.images. \

+ 20 - 14
cloudbridge/cloud/providers/azure/provider.py

@@ -32,7 +32,7 @@ class AzureCloudProvider(BaseCloudProvider):
         # optional config values
         self.region_name = self._get_config_value(
             'azure_region_name', os.environ.get('AZURE_REGION_NAME',
-                                                'eastus'))
+                                                'westus2'))
         self.resource_group = self._get_config_value(
             'azure_resource_group', os.environ.get('AZURE_RESOURCE_GROUP',
                                                    'cloudbridge'))
@@ -111,16 +111,22 @@ class AzureCloudProvider(BaseCloudProvider):
             self._azure_client.create_resource_group(self.resource_group,
                                                      resource_group_params)
 
-        try:
-            self._azure_client.get_storage_account(self.storage_account)
-        except CloudError:
-            storage_account_params = {
-                'sku': {
-                    'name': 'Standard_LRS'
-                },
-                'kind': 'storage',
-                'location': self.region_name,
-            }
-            self._azure_client. \
-                create_storage_account(self.storage_account,
-                                       storage_account_params)
+        for i in range(5):
+            try:
+                self._azure_client.get_storage_account(self.storage_account)
+                break
+            except CloudError:
+                storage_account_params = {
+                    'sku': {
+                        'name': 'Standard_LRS'
+                    },
+                    'kind': 'storage',
+                    'location': self.region_name,
+                }
+                try:
+                    self._azure_client. \
+                        create_storage_account(self.storage_account,
+                                               storage_account_params)
+                    break
+                except CloudError:
+                    pass

+ 38 - 24
cloudbridge/cloud/providers/azure/resources.py

@@ -6,6 +6,7 @@ import logging
 import uuid
 
 from azure.common import AzureException
+from azure.mgmt.devtestlabs.models import GalleryImageReference
 from azure.mgmt.network.models import NetworkSecurityGroup
 
 import cloudbridge.cloud.base.helpers as cb_helpers
@@ -676,12 +677,13 @@ class AzureMachineImage(BaseMachineImage):
         # Image can be either a dict for public image reference
         # or the Azure iamge object
         self._image = image
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             self._state = self._image.provisioning_state
         else:
-            self._state = 'AVAILABLE'
+            self._state = 'SUCCEEDED'
 
-        if not isinstance(self._image, dict) and not self._image.tags:
+        if not isinstance(self._image, GalleryImageReference) \
+           and not self._image.tags:
             self._image.tags = {}
 
     @property
@@ -692,17 +694,21 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``str``
         :return: ID for this instance as returned by the cloud middleware.
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return self._image.id
         else:
             return self._image['offer']
 
     @property
     def resource_id(self):
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return self._image.id
         else:
-            return self._image['offer']
+            reference_dict = self._image.as_dict()
+            return '/'.join([reference_dict['publisher'],
+                             reference_dict['offer'],
+                             reference_dict['sku'],
+                             reference_dict['version']])
 
     @property
     def name(self):
@@ -712,17 +718,21 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``str``
         :return: Name for this image as returned by the cloud middleware.
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return self._image.tags.get('Name', self._image.name)
         else:
-            return self._image['offer']
+            reference_dict = self._image.as_dict()
+            return ':'.join([reference_dict['publisher'],
+                             reference_dict['offer'],
+                             reference_dict['sku'],
+                             reference_dict['version']])
 
     @name.setter
     def name(self, value):
         """
         Set the image name.
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             self.assert_valid_resource_name(value)
             self._image.tags.update(Name=value)
             self._provider.azure_client. \
@@ -736,17 +746,18 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``str``
         :return: Description for this image as returned by the cloud middleware
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return self._image.tags.get('Description', None)
         else:
-            return 'Public Image'
+            return 'Public gallery image from the Azure Marketplace: '\
+                    + self.name
 
     @description.setter
     def description(self, value):
         """
-        Set the image name.
+        Set the image description.
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             self._image.tags.update(Description=value)
             self._provider.azure_client. \
                 update_image_tags(self.id, self._image.tags)
@@ -762,23 +773,33 @@ class AzureMachineImage(BaseMachineImage):
         :rtype: ``int``
         :return: The minimum disk size needed by this image
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return self._image.storage_profile.os_disk.disk_size_gb or 0
+        else:
+            return 0
 
     def delete(self):
         """
         Delete this image
         """
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             self._provider.azure_client.delete_image(self.id)
 
     @property
     def state(self):
-        if not isinstance(self._image, dict):
+        if not isinstance(self._image, GalleryImageReference):
             return AzureMachineImage.IMAGE_STATE_MAP.get(
                 self._state, MachineImageState.UNKNOWN)
         else:
-            return MachineImageState.RUNNING
+            return MachineImageState.AVAILABLE
+
+    @property
+    def is_gallery_image(self):
+        """
+        Returns true if the image is a public reference and false if it
+        is a private image in the resource group.
+        """
+        return isinstance(self._image, GalleryImageReference)
 
     def refresh(self):
         """
@@ -794,13 +815,6 @@ class AzureMachineImage(BaseMachineImage):
                 # image no longer exists
                 self._state = "unknown"
 
-    def isgalleryimage(self):
-        """
-        Returns true if the image is a public reference and false if it
-        is a private image in the resource group.
-        """
-        return isinstance(self._image, dict)
-
 
 class AzureGatewayContainer(BaseGatewayContainer):
     def __init__(self, provider, network):

+ 2 - 2
cloudbridge/cloud/providers/azure/services.py

@@ -603,8 +603,8 @@ class AzureInstanceService(BaseInstanceService):
     def _create_storage_profile(self, image, launch_config, instance_name,
                                 zone_id):
 
-        if image.isgalleryimage:
-            reference = image._image
+        if image.is_gallery_image:
+            reference = image._image.as_dict()
             storage_profile = {
                 'image_reference': {
                     'publisher': reference['publisher'],

+ 0 - 0
.codeclimate.yml → codeclimate.yml


+ 1 - 0
setup.py

@@ -27,6 +27,7 @@ REQS_AWS = ['boto3']
 REQS_AZURE = ['msrest>=0.4.7',
               'msrestazure>=0.4.7',
               'azure-common>=1.1.5',
+              'azure-mgmt-devtestlabs>=1.0.0',
               'azure-mgmt-resource>=1.0.0rc1',
               'azure-mgmt-compute>=1.0.0rc1',
               'azure-mgmt-network>=1.0.0rc1',

+ 3 - 5
test/helpers/__init__.py

@@ -86,18 +86,16 @@ TEST_DATA_CONFIG = {
     },
     "OpenStackCloudProvider": {
         "image": os.environ.get('CB_IMAGE_OS',
-                                '842b949c-ea76-48df-998d-8a41f2626243'),
+                                'acb53109-941f-4593-9bf8-4a53cb9e0739'),
         "vm_type": os.environ.get('CB_VM_TYPE_OS', 'm1.tiny'),
         "placement": os.environ.get('CB_PLACEMENT_OS', 'zone-r1'),
     },
     "AzureCloudProvider": {
         "placement":
-            os.environ.get('CB_PLACEMENT_AZURE', 'eastus'),
+            os.environ.get('CB_PLACEMENT_AZURE', 'westus2'),
         "image":
             os.environ.get('CB_IMAGE_AZURE',
-                           '/subscriptions/7904d702-e01c-4826-8519-f5a25c866a9'
-                           '6/resourceGroups/cloudbridge/providers/Microsoft.C'
-                           'ompute/images/cb-test-image'),
+                           'Canonical/UbuntuServer/16.04.0-LTS/latest'),
         "vm_type":
             os.environ.get('CB_VM_TYPE_AZURE', 'Basic_A2'),
     }