diff --git a/lib/ansible/module_utils/azure_rm_common_ext.py b/lib/ansible/module_utils/azure_rm_common_ext.py index bb7af27f03..ab3c31c796 100644 --- a/lib/ansible/module_utils/azure_rm_common_ext.py +++ b/lib/ansible/module_utils/azure_rm_common_ext.py @@ -18,7 +18,7 @@ class AzureRMModuleBaseExt(AzureRMModuleBase): for name in spec.keys(): # first check if option was passed param = body.get(name) - if not param: + if param is None: if spec[name].get('purgeIfNone', False): body.pop(name, None) continue @@ -27,6 +27,13 @@ class AzureRMModuleBaseExt(AzureRMModuleBase): if pattern: if pattern == 'camelize': param = _snake_to_camel(param, True) + elif isinstance(pattern, list): + normalized = None + for p in pattern: + normalized = self.normalize_resource_id(param, p) + body[name] = normalized + if normalized is not None: + break else: param = self.normalize_resource_id(param, pattern) body[name] = param diff --git a/lib/ansible/modules/cloud/azure/azure_rm_galleryimageversion.py b/lib/ansible/modules/cloud/azure/azure_rm_galleryimageversion.py index 76f77f5c4c..65ef8ffb51 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_galleryimageversion.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_galleryimageversion.py @@ -47,6 +47,62 @@ options: description: - Resource location. type: str + storage_profile: + description: + - Storage profile + required: true + version_added: "2.10" + type: dict + suboptions: + source_image: + description: + - Reference to managed image or gallery image version + - Could be resource ID to managed image, or dictionary containing I(resource_group) and I(name) + - Could be resource ID to image version, or dictionary containing I(resource_group),I(gallery_name), I(gallery_image_name) and I(version) + - Mutual exclusive with os_disk and data_disks + type: raw + os_disk: + description: + - os disk snapshot + - Mutual exclusive with source_image + type: raw + suboptions: + source: + description: + - Reference to os disk snapshot. Could be resource ID or dictionary containing I(resource_group) and I(name) + type: str + host_caching: + description: + - host disk caching + type: str + default: None + choices: + - None + - ReadOnly + - ReadWrite + data_disks: + description: + - list of data disk snapshot + - Mutual exclusive with source_image + type: list + suboptions: + source: + description: + - Reference to data disk snapshot. Could be resource ID or dictionary containing I(resource_group) and I(name) + type: str + lun: + description: + - lun of the data disk + type: int + host_caching: + description: + - host disk caching + type: str + default: None + choices: + - None + - ReadOnly + - ReadWrite publishing_profile: description: - Publishing profile. @@ -75,10 +131,12 @@ options: type: str managed_image: description: - - Managed image reference, could be resource ID, or dictionary containing I(resource_group) and I(name). + - Managed image reference, could be resource ID, or dictionary containing I(resource_group) and I(name) + - Obsolete since 2.10, use storage_profile instead snapshot: description: - Source snapshot to be used. + - Obsolete since 2.10, use storage_profile instead replica_count: description: - The number of replicas of the Image Version to be created per region. @@ -118,27 +176,76 @@ author: ''' EXAMPLES = ''' -- name: Create or update a simple gallery Image Version. +- name: Create a gallery image version form a managed image azure_rm_galleryimageversion: resource_group: myResourceGroup - gallery_name: myGallery1283 - gallery_image_name: myImage - name: 10.1.3 - location: West US + gallery_name: myGallery + gallery_image_name: myGalleryImage + name: 1.1.0 + location: East US publishing_profile: end_of_life_date: "2020-10-01t00:00:00+00:00" exclude_from_latest: yes - replica_count: 3 + replica_count: 4 storage_account_type: Standard_LRS target_regions: - name: West US regional_replica_count: 1 - name: East US - regional_replica_count: 2 - storage_account_type: Standard_ZRS - managed_image: - name: myImage - resource_group: myResourceGroup + regional_replica_count: 3 + storage_account_type: Standard_LRS + storage_profile: + source_image: /subscriptions/sub123/resourceGroups/group123/providers/Microsoft.Compute/images/myOsImage + +- name: Create a gallery image version from another gallery image version + azure_rm_galleryimageversion: + resource_group: myResourceGroup + gallery_name: myGallery + gallery_image_name: myGalleryImage + name: 1.2.0 + location: East US + publishing_profile: + end_of_life_date: "2020-10-01t00:00:00+00:00" + exclude_from_latest: yes + replica_count: 4 + storage_account_type: Standard_LRS + target_regions: + - name: West US + regional_replica_count: 1 + - name: East US + regional_replica_count: 3 + storage_account_type: Standard_LRS + storage_profile: + source_image: + version: 1.1.0 + gallery_name: myGallery2 + gallery_image_name: myGalleryImage2 + +- name: Create gallery image by using one os dist snapshot and zero or many data disk snapshots + azure_rm_galleryimageversion: + resource_group: myRsourceGroup + gallery_name: myGallery + gallery_image_name: myGalleryImage + name: 3.4.0 + location: East US + publishing_profile: + end_of_life_date: "2020-10-01t00:00:00+00:00" + exclude_from_latest: yes + replica_count: 1 + storage_account_type: Standard_LRS + target_regions: + - name: East US + regional_replica_count: 1 + storage_account_type: Standard_LRS + storage_profile: + os_disk: + source: "/subscriptions/mySub/resourceGroups/myGroup/providers/Microsoft.Compute/snapshots/os_snapshot_vma" + data_disks: + - lun: 0 + source: + name: data_snapshot_vma + - lun: 1 + source: "/subscriptions/mySub/resourceGroups/myGroup/providers/Microsoft.Compute/snapshots/data_snapshot_vmb" ''' RETURN = ''' @@ -153,10 +260,8 @@ id: import time import json -import re from ansible.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt from ansible.module_utils.azure_rm_common_rest import GenericRestClient -from copy import deepcopy try: from msrestazure.azure_exceptions import CloudError except ImportError: @@ -201,6 +306,70 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt): disposition='/', comparison='location' ), + storage_profile=dict( + type='dict', + updatable=False, + disposition='/properties/storageProfile', + comparison='ignore', + options=dict( + source_image=dict( + type='raw', + disposition='source/id', + purgeIfNone=True, + pattern=[('/subscriptions/{subscription_id}/resourceGroups' + '/{resource_group}/providers/Microsoft.Compute' + '/images/{name}'), + ('/subscriptions/{subscription_id}/resourceGroups' + '/{resource_group}/providers/Microsoft.Compute' + '/galleries/{gallery_name}/images/{gallery_image_name}' + '/versions/{version}')] + ), + os_disk=dict( + type='dict', + disposition='osDiskImage', + purgeIfNone=True, + comparison='ignore', + options=dict( + source=dict( + type='raw', + disposition='source/id', + pattern=('/subscriptions/{subscription_id}/resourceGroups' + '/{resource_group}/providers/Microsoft.Compute' + '/snapshots/{name}') + ), + host_caching=dict( + type='str', + disposition='hostCaching', + default="None", + choices=["ReadOnly", "ReadWrite", "None"] + ) + ) + ), + data_disks=dict( + type='list', + disposition='dataDiskImages', + purgeIfNone=True, + options=dict( + lun=dict( + type='int' + ), + source=dict( + type='raw', + disposition="source/id", + pattern=('/subscriptions/{subscription_id}/resourceGroups' + '/{resource_group}/providers/Microsoft.Compute' + '/snapshots/{name}') + ), + host_caching=dict( + type='str', + disposition='hostCaching', + default="None", + choices=["ReadOnly", "ReadWrite", "None"] + ) + ) + ) + ) + ), publishing_profile=dict( type='dict', disposition='/properties/publishingProfile', @@ -297,6 +466,14 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt): self.inflate_parameters(self.module_arg_spec, self.body, 0) + # keep backward compatibility + snapshot = self.body.get('properties', {}).get('publishingProfile', {}).pop('snapshot', None) + if snapshot is not None: + self.body['properties'].setdefault('storageProfile', {}).setdefault('osDiskImage', {}).setdefault('source', {})['id'] = snapshot + managed_image = self.body.get('properties', {}).get('publishingProfile', {}).pop('managed_image', None) + if managed_image: + self.body['properties'].setdefault('storageProfile', {}).setdefault('source', {})['id'] = managed_image + old_response = None response = None @@ -348,14 +525,6 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt): if not self.default_compare(modifiers, self.body, old_response, '', self.results): self.to_do = Actions.Update - # fix for differences between version 2019-03-01 and 2019-07-01 - snapshot = self.body.get('properties', {}).get('publishingProfile', {}).pop('snapshot', None) - if snapshot is not None: - self.body['properties'].setdefault('storageProfile', {}).setdefault('osDiskImage', {}).setdefault('source', {})['id'] = snapshot - managed_image = self.body.get('properties', {}).get('publishingProfile', {}).pop('managed_image', None) - if managed_image: - self.body['properties'].setdefault('storageProfile', {}).setdefault('source', {})['id'] = managed_image - if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): self.log('Need to Create / Update the GalleryImageVersion instance') diff --git a/test/integration/targets/azure_rm_gallery/tasks/main.yml b/test/integration/targets/azure_rm_gallery/tasks/main.yml index 4f18cb88cb..000f9e8855 100644 --- a/test/integration/targets/azure_rm_gallery/tasks/main.yml +++ b/test/integration/targets/azure_rm_gallery/tasks/main.yml @@ -310,6 +310,10 @@ that: - output.changed +- name: pasue 2 minutes, wait for deletion complete + pause: + minutes: 2 + - name: Delete gallery image azure_rm_galleryimage: resource_group: "{{ resource_group }}" @@ -322,6 +326,10 @@ that: - output.changed +- name: pasue 2 minutes, wait for deletion complete + pause: + minutes: 2 + - name: Delete gallery azure_rm_gallery: resource_group: "{{ resource_group }}"