diff --git a/plugins/modules/azure_rm_virtualmachine.py b/plugins/modules/azure_rm_virtualmachine.py index e9b27dbda..84c8a80a0 100644 --- a/plugins/modules/azure_rm_virtualmachine.py +++ b/plugins/modules/azure_rm_virtualmachine.py @@ -809,12 +809,14 @@ import base64 import random import re +import time try: + from azure.core.exceptions import ResourceNotFoundError + from azure.core.polling import LROPoller from msrestazure.azure_exceptions import CloudError from azure.core.exceptions import ResourceNotFoundError from msrestazure.tools import parse_resource_id - from msrest.polling import LROPoller except ImportError: # This is handled in azure_rm_common pass @@ -1108,7 +1110,18 @@ def exec_module(self, **kwargs): try: self.log("Fetching virtual machine {0}".format(self.name)) vm = self.compute_client.virtual_machines.get(self.resource_group, self.name, expand='instanceview') - self.check_provisioning_state(vm, self.state) + retry_count = 0 + while True: + if retry_count == 10: + self.fail("Error {0} has a provisioning state of Updating. Expecting state to be Successed.".format(self.name)) + + if vm.provisioning_state == 'Updating': + retry_count = retry_count + 1 + time.sleep(300) + vm = self.compute_client.virtual_machines.get(self.resource_group, self.name, expand='instanceview') + else: + break + vm_dict = self.serialize_vm(vm) if self.state == 'present': @@ -1265,7 +1278,7 @@ def exec_module(self, **kwargs): results = dict() changed = True - except CloudError: + except ResourceNotFoundError: self.log('Virtual machine {0} does not exist'.format(self.name)) if self.state == 'present': self.log("CHANGED: virtual machine {0} does not exist but state is 'present'.".format(self.name)) @@ -1797,7 +1810,7 @@ def power_off_vm(self): self.log("Powered off virtual machine {0}".format(self.name)) self.results['actions'].append("Powered off virtual machine {0}".format(self.name)) try: - poller = self.compute_client.virtual_machines.power_off(self.resource_group, self.name) + poller = self.compute_client.virtual_machines.begin_power_off(self.resource_group, self.name) self.get_poller_result(poller) except Exception as exc: self.fail("Error powering off virtual machine {0} - {1}".format(self.name, str(exc))) @@ -1807,7 +1820,7 @@ def power_on_vm(self): self.results['actions'].append("Powered on virtual machine {0}".format(self.name)) self.log("Power on virtual machine {0}".format(self.name)) try: - poller = self.compute_client.virtual_machines.start(self.resource_group, self.name) + poller = self.compute_client.virtual_machines.begin_start(self.resource_group, self.name) self.get_poller_result(poller) except Exception as exc: self.fail("Error powering on virtual machine {0} - {1}".format(self.name, str(exc))) @@ -1817,7 +1830,7 @@ def restart_vm(self): self.results['actions'].append("Restarted virtual machine {0}".format(self.name)) self.log("Restart virtual machine {0}".format(self.name)) try: - poller = self.compute_client.virtual_machines.restart(self.resource_group, self.name) + poller = self.compute_client.virtual_machines.begin_restart(self.resource_group, self.name) self.get_poller_result(poller) except Exception as exc: self.fail("Error restarting virtual machine {0} - {1}".format(self.name, str(exc))) @@ -1827,7 +1840,7 @@ def deallocate_vm(self): self.results['actions'].append("Deallocated virtual machine {0}".format(self.name)) self.log("Deallocate virtual machine {0}".format(self.name)) try: - poller = self.compute_client.virtual_machines.deallocate(self.resource_group, self.name) + poller = self.compute_client.virtual_machines.begin_deallocate(self.resource_group, self.name) self.get_poller_result(poller) except Exception as exc: self.fail("Error deallocating virtual machine {0} - {1}".format(self.name, str(exc))) @@ -1911,7 +1924,7 @@ def delete_vm(self, vm): self.log("Deleting virtual machine {0}".format(self.name)) self.results['actions'].append("Deleted virtual machine {0}".format(self.name)) try: - poller = self.compute_client.virtual_machines.delete(self.resource_group, self.name) + poller = self.compute_client.virtual_machines.begin_delete(self.resource_group, self.name) # wait for the poller to finish self.get_poller_result(poller) except Exception as exc: @@ -2027,8 +2040,7 @@ def get_marketplace_image_version(self): self.image['publisher'], self.image['offer'], self.image['sku'], - top=1, - orderby='name desc') + orderby='name') except Exception as exc: self.fail("Error fetching image {0} {1} {2} - {3}".format(self.image['publisher'], self.image['offer'], @@ -2086,7 +2098,7 @@ def get_storage_account(self, resource_group, name): def create_or_update_vm(self, params, remove_autocreated_on_failure): try: - poller = self.compute_client.virtual_machines.create_or_update(self.resource_group, self.name, params) + poller = self.compute_client.virtual_machines.begin_create_or_update(self.resource_group, self.name, params) self.get_poller_result(poller) except Exception as exc: if remove_autocreated_on_failure: @@ -2149,7 +2161,7 @@ def create_default_storage_account(self, vm_dict=None): try: account = self.storage_client.storage_accounts.get_properties(self.resource_group, storage_account_name) - except CloudError: + except Exception: pass if account: @@ -2164,7 +2176,7 @@ def create_default_storage_account(self, vm_dict=None): self.log("Creating storage account {0} in location {1}".format(storage_account_name, self.location)) self.results['actions'].append("Created storage account {0}".format(storage_account_name)) try: - poller = self.storage_client.storage_accounts.create(self.resource_group, storage_account_name, parameters) + poller = self.storage_client.storage_accounts.begin_create(self.resource_group, storage_account_name, parameters) self.get_poller_result(poller) except Exception as exc: self.fail("Failed to create storage account: {0} - {1}".format(storage_account_name, str(exc))) @@ -2174,7 +2186,8 @@ def create_default_storage_account(self, vm_dict=None): def check_storage_account_name(self, name): self.log("Checking storage account name availability for {0}".format(name)) try: - response = self.storage_client.storage_accounts.check_name_availability(name) + account_name = self.storage_models.StorageAccountCheckNameAvailabilityParameters(name=name) + response = self.storage_client.storage_accounts.check_name_availability(account_name) if response.reason == 'AccountNameInvalid': raise Exception("Invalid default storage account name: {0}".format(name)) except Exception as exc: @@ -2247,7 +2260,7 @@ def create_default_nic(self): try: subnet = self.network_client.subnets.get(virtual_network_resource_group, virtual_network_name, self.subnet_name) subnet_id = subnet.id - except Exception as exc: + except CloudError as exc: self.fail("Error: fetching subnet {0} - {1}".format(self.subnet_name, str(exc))) else: no_subnets_msg = "Error: unable to find a subnet in virtual network {0}. A virtual network " \ @@ -2257,7 +2270,8 @@ def create_default_nic(self): subnet_id = None try: subnets = self.network_client.subnets.list(virtual_network_resource_group, virtual_network_name) - except ResourceNotFoundError: + except Exception: + self.fail(no_subnets_msg) for subnet in subnets: diff --git a/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_latest.yml b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_latest.yml new file mode 100644 index 000000000..812ef48d3 --- /dev/null +++ b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_latest.yml @@ -0,0 +1,69 @@ +- include_tasks: setup.yml + +- name: List available versions for UbuntuServer image + azure_rm_virtualmachineimage_info: + location: eastus + publisher: Canonical + offer: UbuntuServer + sku: 16.04-LTS + register: image_list + +- name: Get latest UbuntuServer image name + set_fact: + latest_image_name: "{{ (image_list['vmimages'] | map(attribute='name') | sort(reverse=True))[0] }}" + +- name: Create minimal VM with defaults + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + admin_username: "testuser" + ssh_password_enabled: false + ssh_public_keys: + - path: /home/testuser/.ssh/authorized_keys + key_data: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfoYlIV4lTPZTv7hXaVwQQuqBgGs4yeNRX0SPo2+HQt9u4X7IGwrtXc0nEUm6LfaCikMH58bOL8f20NTGz285kxdFHZRcBXtqmnMz2rXwhK9gwq5h1khc+GzHtdcJXsGA4y0xuaNcidcg04jxAlN/06fwb/VYwwWTVbypNC0gpGEpWckCNm8vlDlA55sU5et0SZ+J0RKVvEaweUOeNbFZqckGPA384imfeYlADppK/7eAxqfBVadVvZG8IJk4yvATgaIENIFj2cXxqu2mQ/Bp5Wr45uApvJsFXmi+v/nkiOEV1QpLOnEwAZo6EfFS4CCQtsymxJCl1PxdJ5LD4ZOtP xiuxi.sun@qq.com" + vm_size: Standard_B1ms + virtual_network: "{{ network_name }}" + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + version: latest + register: vm_output + +- name: Ensure VM was created using the latest UbuntuServer image version / name + assert: + that: + - vm_output.azure_vm.properties.storageProfile.imageReference.version == latest_image_name + +- name: Delete VM + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + remove_on_absent: all_autocreated + state: absent + +- name: Destroy subnet + azure_rm_subnet: + resource_group: "{{ resource_group }}" + virtual_network: "{{ network_name }}" + name: "{{ subnet_name }}" + state: absent + +- name: Destroy virtual network + azure_rm_virtualnetwork: + resource_group: "{{ resource_group }}" + name: "{{ network_name }}" + state: absent + +- name: Destroy availability set + azure_rm_availabilityset: + resource_group: "{{ resource_group }}" + name: "{{ availability_set }}" + state: absent + +- name: Destroy storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account }}" + force_delete_nonempty: yes + state: absent diff --git a/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_specific.yml b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_specific.yml new file mode 100644 index 000000000..12fc28154 --- /dev/null +++ b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_image_specific.yml @@ -0,0 +1,61 @@ +- include_tasks: setup.yml + +- name: Set specific UbuntuServer image version + set_fact: + specific_image_name: "16.04.202104140" + +- name: Create minimal VM with defaults + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + admin_username: "testuser" + ssh_password_enabled: false + ssh_public_keys: + - path: /home/testuser/.ssh/authorized_keys + key_data: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfoYlIV4lTPZTv7hXaVwQQuqBgGs4yeNRX0SPo2+HQt9u4X7IGwrtXc0nEUm6LfaCikMH58bOL8f20NTGz285kxdFHZRcBXtqmnMz2rXwhK9gwq5h1khc+GzHtdcJXsGA4y0xuaNcidcg04jxAlN/06fwb/VYwwWTVbypNC0gpGEpWckCNm8vlDlA55sU5et0SZ+J0RKVvEaweUOeNbFZqckGPA384imfeYlADppK/7eAxqfBVadVvZG8IJk4yvATgaIENIFj2cXxqu2mQ/Bp5Wr45uApvJsFXmi+v/nkiOEV1QpLOnEwAZo6EfFS4CCQtsymxJCl1PxdJ5LD4ZOtP xiuxi.sun@qq.com" + vm_size: Standard_B1ms + virtual_network: "{{ network_name }}" + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + version: "{{ specific_image_name }}" + register: vm_output + +- name: Ensure VM was created using the specific UbuntuServer image version / name + assert: + that: + - vm_output.azure_vm.properties.storageProfile.imageReference.version == specific_image_name + +- name: Delete VM + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + remove_on_absent: all_autocreated + state: absent + +- name: Destroy subnet + azure_rm_subnet: + resource_group: "{{ resource_group }}" + virtual_network: "{{ network_name }}" + name: "{{ subnet_name }}" + state: absent + +- name: Destroy virtual network + azure_rm_virtualnetwork: + resource_group: "{{ resource_group }}" + name: "{{ network_name }}" + state: absent + +- name: Destroy availability set + azure_rm_availabilityset: + resource_group: "{{ resource_group }}" + name: "{{ availability_set }}" + state: absent + +- name: Destroy storage account + azure_rm_storageaccount: + resource_group: "{{ resource_group }}" + name: "{{ storage_account }}" + force_delete_nonempty: yes + state: absent