From d7fbc947580d3ab7d32801668b7fb72eaa2200c6 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Tue, 16 Jan 2018 21:05:35 -0800 Subject: [PATCH] add custom_action support to azure_rm_virtualmachine (#34970) * Adds custom_data parameter to azure virtual machine resource Invoke custom_data in an integration test: This invocation of custom_data should not cause any side effects. * Bugfix: String encoding now works in both python2 and 3 * Fix pep8 violations * Use nginx to serve a text file created via custom_data and verify that that custom_data is working * fix up azure_rm_virtualmachine custom_data * tweaks #25924 * simplify string encoding fun * don't rely on external packages --- .../cloud/azure/azure_rm_virtualmachine.py | 21 +++++++ .../tasks/virtualmachine.yml | 59 ++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py index 3d36224c3d8..4997c6954ad 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py @@ -38,6 +38,12 @@ options: description: - Name of the virtual machine. required: true + custom_data: + description: + - Data which is made available to the virtual machine and used by e.g., cloud-init. + default: null + required: false + version_added: "2.5" state: description: - Assert the state of the virtual machine. @@ -591,6 +597,7 @@ azure_vm: } ''' # NOQA +import base64 import random import re @@ -601,6 +608,7 @@ except ImportError: # This is handled in azure_rm_common pass +from ansible.module_utils.basic import to_native, to_bytes from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict @@ -626,6 +634,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase): self.module_arg_spec = dict( resource_group=dict(type='str', required=True), name=dict(type='str', required=True), + custom_data=dict(type='str'), state=dict(choices=['present', 'absent'], default='present', type='str'), location=dict(type='str'), short_hostname=dict(type='str'), @@ -660,6 +669,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase): self.resource_group = None self.name = None + self.custom_data = None self.state = None self.location = None self.short_hostname = None @@ -970,6 +980,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if self.admin_password: vm_resource.os_profile.admin_password = self.admin_password + if self.custom_data: + # Azure SDK (erroneously?) wants native string type for this + vm_resource.os_profile.custom_data = to_native(base64.b64encode(to_bytes(self.custom_data))) + if self.os_type == 'Linux': vm_resource.os_profile.linux_configuration = self.compute_models.LinuxConfiguration( disable_password_authentication=disable_ssh_password @@ -1101,6 +1115,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase): if vm_dict.get('tags'): vm_resource.tags = vm_dict['tags'] + # Add custom_data, if provided + if vm_dict['properties']['osProfile'].get('customData'): + custom_data = vm_dict['properties']['osProfile']['customData'] + # Azure SDK (erroneously?) wants native string type for this + vm_resource.os_profile.custom_data = to_native(base64.b64encode(to_bytes(custom_data))) + # Add admin password, if one provided if vm_dict['properties']['osProfile'].get('adminPassword'): vm_resource.os_profile.admin_password = vm_dict['properties']['osProfile']['adminPassword'] @@ -1667,5 +1687,6 @@ class AzureRMVirtualMachine(AzureRMModuleBase): def main(): AzureRMVirtualMachine() + if __name__ == '__main__': main() diff --git a/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml b/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml index 955512871e6..85192754057 100644 --- a/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml +++ b/test/integration/targets/azure_rm_virtualmachine/tasks/virtualmachine.yml @@ -44,6 +44,20 @@ azure_rm_securitygroup: resource_group: "{{ resource_group }}" name: testvm001 + purge_rules: yes + rules: + - name: ALLOW_SSH + protocol: Tcp + destination_port_range: 22 + access: Allow + priority: 100 + direction: Inbound + - name: ALLOW_HTTP + protocol: Tcp + destination_port_range: 80 + access: Allow + priority: 110 + direction: Inbound - name: Create NIC azure_rm_networkinterface: @@ -55,6 +69,7 @@ security_group: testvm001 - name: Create virtual machine + register: output azure_rm_virtualmachine: resource_group: "{{ resource_group }}" name: testvm002 @@ -73,12 +88,52 @@ publisher: Canonical sku: 16.04-LTS version: latest - register: output + custom_data: | + #!/bin/sh + echo "custom_data was executed" > /tmp/custom_data.txt - assert: that: - azure_vm.properties.availabilitySet.id +- add_host: + name: new_azure_vm + ansible_host: '{{ output.ansible_facts.azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[0].properties.publicIPAddress.properties.ipAddress }}' + ansible_connection: paramiko # not guaranteed to have sshpass... + ansible_user: adminuser + ansible_password: Password123! + +- name: wait for SSH port to be open + wait_for: + host: '{{ hostvars["new_azure_vm"].ansible_host }}' + port: 22 + timeout: 60 + state: started + +- block: + # TODO: figure out how to make this work under ansible-test with the coverage injector + #- name: wait for host to answer on SSH + # delegate_to: new_azure_vm + # wait_for_connection: + - name: get content from custom_data script + raw: cat /tmp/custom_data.txt + register: custom_data_content + + - name: assert contents + assert: + that: custom_data_content.stdout | regex_search('custom_data was executed') + delegate_to: new_azure_vm + + # TODO: figure out how to make this work under ansible-test with the coverage injector + #- name: wait for file/content created by custom_data script + # delegate_to: new_azure_vm + # vars: + # ansible_python_interpreter: python + # wait_for: + # path: /tmp/custom_data.txt + # search_regex: ^custom_data was executed$ + # timeout: 20 + - name: Restart the virtual machine azure_rm_virtualmachine: resource_group: "{{ resource_group }}" @@ -205,4 +260,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" \ No newline at end of file + failed_when: fail_missing_custom_image_dict.msg != "Error could not find image with name invalid-image"