From 76590ab8a5ec199551019f86d53d41e3550a46ae Mon Sep 17 00:00:00 2001 From: radixdlt <34097377+radixdlt@users.noreply.github.com> Date: Wed, 20 Dec 2017 21:17:57 +0000 Subject: [PATCH] azure_rm_virtualmachine_scaleset: ported PR#32367 (add custom image support) (#33422) --- .../azure/azure_rm_virtualmachine_scaleset.py | 109 +++++++++++++++--- .../tasks/main.yml | 45 ++++++++ 2 files changed, 135 insertions(+), 19 deletions(-) diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_scaleset.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_scaleset.py index 776c7340dd8..79765e6f846 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_scaleset.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine_scaleset.py @@ -88,9 +88,19 @@ options: /home//.ssh/authorized_keys. Set key_data to the actual value of the public key." image: description: - - "A dictionary describing the Marketplace image used to build the VM. Will contain keys: publisher, - offer, sku and version. NOTE: set image.version to 'latest' to get the most recent version of a given - image." + - Specifies the image used to build the VM. + - If a string, the image is sourced from a custom image based on the + name. + - 'If a dict with the keys C(publisher), C(offer), C(sku), and + C(version), the image is sourced from a Marketplace image. NOTE: + set image.version to C(latest) to get the most recent version of a + given image.' + - 'If a dict with the keys C(name) and C(resource_group), the image + is sourced from a custom image based on the C(name) and + C(resource_group) set. NOTE: the key C(resource_group) is optional + and if omitted, all images in the subscription will be searched for + by C(name).' + - Custom image support was added in Ansible 2.5 required: true os_disk_caching: description: @@ -197,6 +207,34 @@ EXAMPLES = ''' disk_size_gb: 64 caching: ReadWrite managed_disk_type: Standard_LRS + +- name: Create a VMSS with a custom image + azure_rm_virtualmachine_scaleset: + resource_group: Testing + name: testvmss + vm_size: Standard_DS1_v2 + capacity: 2 + virtual_network_name: testvnet + subnet_name: testsubnet + admin_username: adminUser + admin_password: password01 + managed_disk_type: Standard_LRS + image: customimage001 + +- name: Create a VMSS with a custom image from a particular resource group + azure_rm_virtualmachine_scaleset: + resource_group: Testing + name: testvmss + vm_size: Standard_DS1_v2 + capacity: 2 + virtual_network_name: testvnet + subnet_name: testsubnet + admin_username: adminUser + admin_password: password01 + managed_disk_type: Standard_LRS + image: + name: customimage001 + resource_group: Testing ''' RETURN = ''' @@ -345,7 +383,7 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): admin_password=dict(type='str', no_log=True), ssh_password_enabled=dict(type='bool', default=True), ssh_public_keys=dict(type='list'), - image=dict(type='dict'), + image=dict(type='raw'), os_disk_caching=dict(type='str', aliases=['disk_caching'], choices=['ReadOnly', 'ReadWrite'], default='ReadOnly'), os_type=dict(type='str', choices=['Linux', 'Windows'], default='Linux'), @@ -405,6 +443,8 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): vmss_dict = None virtual_network = None subnet = None + image_reference = None + custom_image = False resource_group = self.get_resource_group(self.resource_group) if not self.location: @@ -431,14 +471,31 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): if not key.get('path') or not key.get('key_data'): self.fail(msg) - if self.image: - if not self.image.get('publisher') or not self.image.get('offer') or not self.image.get('sku') \ - or not self.image.get('version'): - self.error("parameter error: expecting image to contain publisher, offer, sku and version keys.") - image_version = self.get_image_version() - if self.image['version'] == 'latest': - self.image['version'] = image_version.name - self.log("Using image version {0}".format(self.image['version'])) + if self.image and isinstance(self.image, dict): + if all(key in self.image for key in ('publisher', 'offer', 'sku', 'version')): + marketplace_image = self.get_marketplace_image_version() + if self.image['version'] == 'latest': + self.image['version'] = marketplace_image.name + self.log("Using image version {0}".format(self.image['version'])) + + image_reference = ImageReference( + publisher=self.image['publisher'], + offer=self.image['offer'], + sku=self.image['sku'], + version=self.image['version'] + ) + elif self.image.get('name'): + custom_image = True + image_reference = self.get_custom_image_reference( + self.image.get('name'), + self.image.get('resource_group')) + else: + self.fail("parameter error: expecting image to contain [publisher, offer, sku, version] or [name, resource_group]") + elif self.image and isinstance(self.image, str): + custom_image = True + image_reference = self.get_custom_image_reference(self.image) + elif self.image: + self.fail("parameter error: expecting image to be a string or dict not {0}".format(type(self.image).__name__)) disable_ssh_password = not self.ssh_password_enabled @@ -521,6 +578,9 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): if not self.short_hostname: self.short_hostname = self.name + if not image_reference: + self.fail("Parameter error: an image is required when creating a virtual machine.") + managed_disk = VirtualMachineScaleSetManagedDiskParameters(storage_account_type=self.managed_disk_type) vmss_resource = VirtualMachineScaleSet( @@ -545,12 +605,7 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): create_option=DiskCreateOptionTypes.from_image, caching=self.os_disk_caching, ), - image_reference=ImageReference( - publisher=self.image['publisher'], - offer=self.image['offer'], - sku=self.image['sku'], - version=self.image['version'], - ), + image_reference=image_reference, ), network_profile=VirtualMachineScaleSetNetworkProfile( network_interface_configurations=[ @@ -707,7 +762,7 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): return True - def get_image_version(self): + def get_marketplace_image_version(self): try: versions = self.compute_client.virtual_machine_images.list(self.location, self.image['publisher'], @@ -730,6 +785,22 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase): self.image['sku'], self.image['version'])) + def get_custom_image_reference(self, name, resource_group=None): + try: + if resource_group: + vm_images = self.compute_client.images.list_by_resource_group(resource_group) + else: + vm_images = self.compute_client.images.list() + except Exception as exc: + self.fail("Error fetching custom images from subscription - {0}".format(str(exc))) + + for vm_image in vm_images: + if vm_image.name == name: + self.log("Using custom image id {0}".format(vm_image.id)) + return ImageReference(id=vm_image.id) + + self.fail("Error could not find image with name {0}".format(name)) + def create_or_update_vmss(self, params): try: poller = self.compute_client.virtual_machine_scale_sets.create_or_update(self.resource_group, self.name, params) diff --git a/test/integration/targets/azure_rm_virtualmachine_scaleset/tasks/main.yml b/test/integration/targets/azure_rm_virtualmachine_scaleset/tasks/main.yml index 586cbdb7192..c6872cfc332 100644 --- a/test/integration/targets/azure_rm_virtualmachine_scaleset/tasks/main.yml +++ b/test/integration/targets/azure_rm_virtualmachine_scaleset/tasks/main.yml @@ -86,3 +86,48 @@ name: testVnet state: absent address_prefixes: "10.0.0.0/16" + +# TODO: Until we have a module to create/delete images this is the best tests +# I can do +- name: assert error thrown with invalid image dict + azure_rm_virtualmachine_scaleset: + resource_group: "{{ resource_group }}" + name: testvm002 + state: present + vm_size: Standard_DS1_v2 + image: + offer: UbuntuServer + register: fail_invalid_image_dict + failed_when: 'fail_invalid_image_dict.msg != "parameter error: expecting image to contain [publisher, offer, sku, version] or [name, resource_group]"' + +- name: assert error thrown with invalid image type + azure_rm_virtualmachine_scaleset: + resource_group: "{{ resource_group }}" + name: testvm002 + state: present + vm_size: Standard_DS1_v2 + image: + - testing + register: fail_invalid_image_type + failed_when: 'fail_invalid_image_type.msg != "parameter error: expecting image to be a string or dict not list"' + +- name: assert error finding missing custom image + azure_rm_virtualmachine_scaleset: + resource_group: "{{ resource_group }}" + name: testvm002 + state: present + vm_size: Standard_DS1_v2 + image: invalid-image + register: fail_missing_custom_image + failed_when: fail_missing_custom_image.msg != "Error could not find image with name invalid-image" + +- name: assert error finding missing custom image (dict style) + azure_rm_virtualmachine_scaleset: + resource_group: "{{ resource_group }}" + name: testvm002 + state: present + vm_size: Standard_DS1_v2 + image: + name: invalid-image + register: fail_missing_custom_image_dict + failed_when: fail_missing_custom_image_dict.msg != "Error could not find image with name invalid-image" \ No newline at end of file