diff --git a/plugins/modules/azure_rm_virtualmachine.py b/plugins/modules/azure_rm_virtualmachine.py index b1a9eab65..e6acaff88 100644 --- a/plugins/modules/azure_rm_virtualmachine.py +++ b/plugins/modules/azure_rm_virtualmachine.py @@ -86,6 +86,27 @@ - A valid Azure VM size value. For example, C(Standard_D4). - Choices vary depending on the subscription and location. Check your subscription for available choices. - Required when creating a VM. + priority: + description: + - Priority of the VM. + - C(None) is the equivalent of Regular VM. + choices: + - None + - Spot + eviction_policy: + description: + - Specifies the eviction policy for the Azure Spot virtual machine. + - Requires priority to be set to Spot. + choices: + - Deallocate + - Delete + max_price: + description: + - Specifies the maximum price you are willing to pay for a Azure Spot VM/VMSS. + - This price is in US Dollars. + - C(-1) indicates default price to be up-to on-demand. + - Requires priority to be set to Spot. + default: -1 admin_username: description: - Admin username used to access the VM after it is created. @@ -539,6 +560,21 @@ product: f5-big-ip-best publisher: f5-networks +- name: Create a VM with Spot Instance + azure_rm_virtualmachine: + resource_group: myResourceGroup + name: testvm10 + vm_size: Standard_D4 + priority: Spot + eviction_policy: Deallocate + admin_username: chouseknecht + admin_password: + image: + offer: CentOS + publisher: OpenLogic + sku: '7.1' + version: latest + - name: Power Off azure_rm_virtualmachine: resource_group: myResourceGroup @@ -797,6 +833,9 @@ def __init__(self): location=dict(type='str'), short_hostname=dict(type='str'), vm_size=dict(type='str'), + priority=dict(type='str', choices=['None', 'Spot']), + eviction_policy=dict(type='str', choices=['Deallocate', 'Delete']), + max_price=dict(type='float', default=-1), admin_username=dict(type='str'), admin_password=dict(type='str', no_log=True), ssh_password_enabled=dict(type='bool', default=True), @@ -841,6 +880,8 @@ def __init__(self): self.location = None self.short_hostname = None self.vm_size = None + self.priority = None + self.eviction_policy = None self.admin_username = None self.admin_password = None self.ssh_password_enabled = None @@ -1045,6 +1086,17 @@ def exec_module(self, **kwargs): results = vm_dict current_osdisk = vm_dict['properties']['storageProfile']['osDisk'] current_ephemeral = current_osdisk.get('diffDiskSettings', None) + current_properties = vm_dict['properties'] + + if self.priority and self.priority != current_properties.get('priority', 'None'): + self.fail('VM Priority is not updatable: requested virtual machine priority is {0}'.format(self.priority)) + if self.eviction_policy and \ + self.eviction_policy != current_properties.get('evictionPolicy', None): + self.fail('VM Eviction Policy is not updatable: requested virtual machine eviction policy is {0}'.format(self.eviction_policy)) + if self.max_price and \ + vm_dict['properties'].get('billingProfile', None) and \ + self.max_price != vm_dict['properties']['billingProfile'].get('maxPrice', None): + self.fail('VM Maximum Price is not updatable: requested virtual machine maximum price is {0}'.format(self.max_price)) if self.ephemeral_os_disk and current_ephemeral is None: self.fail('Ephemeral OS disk not updatable: virtual machine ephemeral OS disk is {0}'.format(self.ephemeral_os_disk)) @@ -1303,6 +1355,13 @@ def exec_module(self, **kwargs): zones=self.zones, ) + if self.priority == 'Spot': + vm_resource.priority = self.priority + vm_resource.eviction_policy = self.eviction_policy + vm_resource.billing_profile = self.compute_models.BillingProfile( + max_price=self.max_price + ) + if self.license_type is not None: vm_resource.license_type = self.license_type diff --git a/plugins/modules/azure_rm_virtualmachinescaleset.py b/plugins/modules/azure_rm_virtualmachinescaleset.py index f04763552..83c8f17d2 100644 --- a/plugins/modules/azure_rm_virtualmachinescaleset.py +++ b/plugins/modules/azure_rm_virtualmachinescaleset.py @@ -66,6 +66,27 @@ choices: - Manual - Automatic + priority: + description: + - Priority of the VMSS. + - C(None) is the equivalent of Regular VM. + choices: + - None + - Spot + eviction_policy: + description: + - Specifies the eviction policy for the Azure Spot virtual machine. + - Requires priority to be set to Spot. + choices: + - Deallocate + - Delete + max_price: + description: + - Specifies the maximum price you are willing to pay for a Azure Spot VM/VMSS. + - This price is in US Dollars. + - C(-1) indicates default price to be up-to on-demand. + - Requires priority to be set to Spot. + default: -1 admin_username: description: - Admin username used to access the host after it is created. Required when creating a VM. @@ -355,6 +376,23 @@ image: name: customimage001 resource_group: myResourceGroup + +- name: Create a VMSS with Spot Instance + azure_rm_virtualmachinescaleset: + resource_group: myResourceGroup + name: testvmss + vm_size: Standard_DS1_v2 + capacity: 5 + priority: Spot + eviction_policy: Deallocate + virtual_network_name: testvnet + upgrade_policy: Manual + subnet_name: testsubnet + admin_username: adminUser + admin_password: password01 + managed_disk_type: Standard_LRS + image: customimage001 + ''' RETURN = ''' @@ -495,6 +533,9 @@ def __init__(self): tier=dict(type='str', choices=['Basic', 'Standard']), capacity=dict(type='int', default=1), upgrade_policy=dict(type='str', choices=['Automatic', 'Manual']), + priority=dict(type='str', choices=['None', 'Spot']), + eviction_policy=dict(type='str', choices=['Deallocate', 'Delete']), + max_price=dict(type='float', default=-1), admin_username=dict(type='str'), admin_password=dict(type='str', no_log=True), ssh_password_enabled=dict(type='bool', default=True), @@ -535,6 +576,8 @@ def __init__(self): self.capacity = None self.tier = None self.upgrade_policy = None + self.priority = None + self.eviction_policy = None self.admin_username = None self.admin_password = None self.ssh_password_enabled = None @@ -697,6 +740,17 @@ def exec_module(self, **kwargs): results = vmss_dict current_osdisk = vmss_dict['properties']['virtualMachineProfile']['storageProfile']['osDisk'] current_ephemeral = current_osdisk.get('diffDiskSettings', None) + current_properties = vmss_dict['properties']['virtualMachineProfile'] + + if self.priority and self.priority != current_properties.get('priority', 'None'): + self.fail('VM Priority is not updatable: requested virtual machine priority is {0}'.format(self.priority)) + if self.eviction_policy and \ + self.eviction_policy != current_properties.get('evictionPolicy', None): + self.fail('VM Eviction Policy is not updatable: requested virtual machine eviction policy is {0}'.format(self.eviction_policy)) + if self.max_price and \ + vmss_dict['properties']['virtualMachineProfile'].get('billingProfile', None) and \ + self.max_price != vmss_dict['properties']['virtualMachineProfile']['billingProfile'].get('maxPrice', None): + self.fail('VM Maximum Price is not updatable: requested virtual machine maximum price is {0}'.format(self.max_price)) if self.ephemeral_os_disk and current_ephemeral is None: self.fail('Ephemeral OS disk not updatable: virtual machine scale set ephemeral OS disk is {0}'.format(self.ephemeral_os_disk)) @@ -922,6 +976,13 @@ def exec_module(self, **kwargs): zones=self.zones ) + if self.priority == 'Spot': + vmss_resource.virtual_machine_profile.priority = self.priority + vmss_resource.virtual_machine_profile.eviction_policy = self.eviction_policy + vmss_resource.virtual_machine_profile.billing_profile = self.compute_models.BillingProfile( + max_price=self.max_price + ) + if self.scale_in_policy: vmss_resource.scale_in_policy = self.gen_scale_in_policy() diff --git a/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_spot.yml b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_spot.yml new file mode 100644 index 000000000..a967e050d --- /dev/null +++ b/tests/integration/targets/azure_rm_virtualmachine/tasks/azure_test_spot.yml @@ -0,0 +1,83 @@ +- include_tasks: setup.yml + +- name: Create minimal VM with Spot Instance default values + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + priority: Spot + eviction_policy: Deallocate + 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_A1_v2 + virtual_network: "{{ network_name }}" + image: + offer: CentOS + publisher: OpenLogic + sku: '7.1' + version: latest + register: vm_output + +- name: Ensure VM was created using Spot Instance default values + assert: + that: + - azure_vm.properties.priority == 'Spot' + - azure_vm.properties.evictionPolicy == 'Deallocate' + - azure_vm.properties.billingProfile.maxPrice == -1.0 + +- name: Delete VM + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + state: absent + vm_size: Standard_A1_v2 + +- name: Create minimal VM with custom Spot Instance values + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + priority: Spot + eviction_policy: Delete + max_price: 1.0 + 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_A1_v2 + virtual_network: "{{ network_name }}" + image: + offer: CentOS + publisher: OpenLogic + sku: '7.1' + version: latest + register: vm_output + +- name: Ensure VM was created using custom spot instance values + assert: + that: + - azure_vm.properties.priority == 'Spot' + - azure_vm.properties.evictionPolicy == 'Delete' + - azure_vm.properties.billingProfile.maxPrice == 1.0 + +- name: Delete VM + azure_rm_virtualmachine: + resource_group: "{{ resource_group }}" + name: "{{ vm_name }}" + state: absent + vm_size: Standard_A1_v2 + +- 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 \ No newline at end of file diff --git a/tests/integration/targets/azure_rm_virtualmachinescaleset/tasks/main.yml b/tests/integration/targets/azure_rm_virtualmachinescaleset/tasks/main.yml index 563c8a6d2..f6459d874 100644 --- a/tests/integration/targets/azure_rm_virtualmachinescaleset/tasks/main.yml +++ b/tests/integration/targets/azure_rm_virtualmachinescaleset/tasks/main.yml @@ -94,6 +94,95 @@ name: "vmforimage{{ rpfx }}" state: absent +- name: Create VMSS with Spot Instance default value + azure_rm_virtualmachinescaleset: + resource_group: "{{ resource_group }}" + name: testVMSS{{ rpfx }} + vm_size: Standard_A1_v2 + admin_username: testuser + priority: Spot + eviction_policy: Deallocate + 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" + capacity: 1 + virtual_network_name: testVnet + subnet_name: testSubnet + upgrade_policy: Manual + tier: Standard + managed_disk_type: Standard_LRS + os_disk_caching: ReadWrite + image: + offer: CentOS + publisher: OpenLogic + sku: '7.1' + version: latest + data_disks: + - lun: 0 + disk_size_gb: 64 + caching: ReadWrite + managed_disk_type: Standard_LRS + register: results + +- name: Assert that VMSS was created using Spot Instance default values + assert: + that: + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.priority == 'Spot' + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.evictionPolicy == 'Deallocate' + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.billingProfile.maxPrice == -1.0 + +- name: Delete VMSS + azure_rm_virtualmachinescaleset: + resource_group: "{{ resource_group }}" + name: testVMSS{{ rpfx }} + state: absent + +- name: Create VMSS with custom Spot Instance values + azure_rm_virtualmachinescaleset: + resource_group: "{{ resource_group }}" + name: testVMSS{{ rpfx }} + vm_size: Standard_A1_v2 + admin_username: testuser + priority: Spot + eviction_policy: Delete + max_price: 1.0 + 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" + capacity: 1 + virtual_network_name: testVnet + subnet_name: testSubnet + upgrade_policy: Manual + tier: Standard + managed_disk_type: Standard_LRS + os_disk_caching: ReadWrite + image: + offer: CentOS + publisher: OpenLogic + sku: '7.1' + version: latest + data_disks: + - lun: 0 + disk_size_gb: 64 + caching: ReadWrite + managed_disk_type: Standard_LRS + register: results + +- name: Assert that VMSS was created using Spot Instance custom values + assert: + that: + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.priority == 'Spot' + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.evictionPolicy == 'Delete' + - results.ansible_facts.azure_vmss.properties.virtualMachineProfile.billingProfile.maxPrice == 1.0 + +- name: Delete VMSS + azure_rm_virtualmachinescaleset: + resource_group: "{{ resource_group }}" + name: testVMSS{{ rpfx }} + state: absent + - name: Create VMSS (check mode) azure_rm_virtualmachinescaleset: resource_group: "{{ resource_group }}" @@ -113,9 +202,9 @@ managed_disk_type: Standard_LRS os_disk_caching: ReadWrite image: - offer: CoreOS - publisher: CoreOS - sku: Stable + offer: CentOS + publisher: OpenLogic + sku: '7.1' version: latest data_disks: - lun: 0 @@ -139,7 +228,7 @@ - name: Assert no VMSS created in check mode assert: that: - - output_scaleset.ansible_facts.azure_vmss | length == 0 + - output_scaleset.vmss | length == 0 - name: Create VMSS azure_rm_virtualmachinescaleset: @@ -161,9 +250,9 @@ os_disk_caching: ReadWrite custom_data: "#cloud-config" image: - offer: CoreOS - publisher: CoreOS - sku: Stable + offer: CentOS + publisher: OpenLogic + sku: '7.1' version: latest data_disks: - lun: 0 @@ -197,9 +286,9 @@ os_disk_caching: ReadWrite custom_data: "#cloud-config" image: - offer: CoreOS - publisher: CoreOS - sku: Stable + offer: CentOS + publisher: OpenLogic + sku: '7.1' version: latest data_disks: - lun: 0 @@ -610,9 +699,9 @@ - path: /home/testuser/.ssh/authorized_keys key_data: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDfoYlIV4lTPZTv7hXaVwQQuqBgGs4yeNRX0SPo2+HQt9u4X7IGwrtXc0nEUm6LfaCikMH58bOL8f20NTGz285kxdFHZRcBXtqmnMz2rXwhK9gwq5h1khc+GzHtdcJXsGA4y0xuaNcidcg04jxAlN/06fwb/VYwwWTVbypNC0gpGEpWckCNm8vlDlA55sU5et0SZ+J0RKVvEaweUOeNbFZqckGPA384imfeYlADppK/7eAxqfBVadVvZG8IJk4yvATgaIENIFj2cXxqu2mQ/Bp5Wr45uApvJsFXmi+v/nkiOEV1QpLOnEwAZo6EfFS4CCQtsymxJCl1PxdJ5LD4ZOtP xiuxi.sun@qq.com" image: - offer: CoreOS - publisher: CoreOS - sku: Stable + offer: CentOS + publisher: OpenLogic + sku: '7.1' version: latest upgrade_policy: Manual enable_accelerated_networking: yes @@ -694,4 +783,4 @@ 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" + failed_when: fail_missing_custom_image_dict.msg != "Error could not find image with name invalid-image" \ No newline at end of file