From 20a96bcc4e45eafa3016e6e904bb6efda6d63ea5 Mon Sep 17 00:00:00 2001 From: Zim Kalinowski Date: Thu, 21 Feb 2019 08:02:09 +0800 Subject: [PATCH] dev test labs artifacts and virtual machine modules (#51462) * dev test labs vm and artifacts * fix sanity * fixing sanity * disable artifact test for now * + vn update * fix sanity * fixed test issue * one more merge update * fixed assert --- .../azure_rm_devtestlabartifactsource.py | 365 ++++++++++++ .../azure_rm_devtestlabvirtualmachine.py | 543 ++++++++++++++++++ .../azure_rm_devtestlabvirtualnetwork.py | 13 + .../azure_rm_devtestlab/tasks/main.yml | 163 +++++- 4 files changed, 1081 insertions(+), 3 deletions(-) create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_devtestlabartifactsource.py create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualmachine.py diff --git a/lib/ansible/modules/cloud/azure/azure_rm_devtestlabartifactsource.py b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabartifactsource.py new file mode 100644 index 0000000000..70c2f83814 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabartifactsource.py @@ -0,0 +1,365 @@ +#!/usr/bin/python +# +# Copyright (c) 2019 Zim Kalinowski, (@zikalino) +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: azure_rm_devtestlabartifactsource +version_added: "2.8" +short_description: Manage Azure DevTest Labs Artifacts Source instance. +description: + - Create, update and delete instance of Azure DevTest Labs Artifacts Source. + +options: + resource_group: + description: + - The name of the resource group. + required: True + lab_name: + description: + - The name of the lab. + required: True + name: + description: + - The name of the artifact source. + required: True + display_name: + description: + - "The artifact source's display name." + uri: + description: + - "The artifact source's URI." + source_type: + description: + - "The artifact source's type." + choices: + - 'vso' + - 'github' + folder_path: + description: + - The folder containing artifacts. + arm_template_folder_path: + description: + - The folder containing Azure Resource Manager templates. + branch_ref: + description: + - "The artifact source's branch reference." + security_token: + description: + - The security token to authenticate to the artifact source. + is_enabled: + description: + - Indicates whether the artifact source is enabled. + type: bool + state: + description: + - Assert the state of the DevTest Labs Artifacts Source. + - Use 'present' to create or update an DevTest Labs Artifacts Source and 'absent' to delete it. + default: present + choices: + - absent + - present + +extends_documentation_fragment: + - azure + - azure_tags + +author: + - "Zim Kalinowski (@zikalino)" + +''' + +EXAMPLES = ''' + - name: Create (or update) DevTest Labs Artifacts Source + azure_rm_devtestlabartifactsource: + resource_group: myrg + lab_name: mylab + name: myartifacts + uri: https://github.com/myself/myrepo.git + source_type: github + folder_path: / + security_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +''' + +RETURN = ''' +id: + description: + - The identifier of the resource. + returned: always + type: str + sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myrg/providers/microsoft.devtestlab/labs/mylab/artifactsources/myartifacts +is_enabled: + description: + - Indicates whether the artifact source is enabled. + returned: always + type: bool + sample: true +''' + +import time +from ansible.module_utils.azure_rm_common import AzureRMModuleBase +from ansible.module_utils.common.dict_transformations import _snake_to_camel + +try: + from msrestazure.azure_exceptions import CloudError + from msrest.polling import LROPoller + from msrestazure.azure_operation import AzureOperationPoller + from azure.mgmt.devtestlabs import DevTestLabsClient + from msrest.serialization import Model +except ImportError: + # This is handled in azure_rm_common + pass + + +class Actions: + NoAction, Create, Update, Delete = range(4) + + +class AzureRMDevTestLabArtifactsSource(AzureRMModuleBase): + """Configuration class for an Azure RM DevTest Labs Artifacts Source resource""" + + def __init__(self): + self.module_arg_spec = dict( + resource_group=dict( + type='str', + required=True + ), + lab_name=dict( + type='str', + required=True + ), + name=dict( + type='str', + required=True + ), + display_name=dict( + type='str' + ), + uri=dict( + type='str' + ), + source_type=dict( + type='str', + choices=['vso', + 'github'] + ), + folder_path=dict( + type='str' + ), + arm_template_folder_path=dict( + type='str' + ), + branch_ref=dict( + type='str' + ), + security_token=dict( + type='str' + ), + is_enabled=dict( + type='bool' + ), + state=dict( + type='str', + default='present', + choices=['present', 'absent'] + ) + ) + + self.resource_group = None + self.lab_name = None + self.name = None + self.artifact_source = dict() + + self.results = dict(changed=False) + self.mgmt_client = None + self.state = None + self.to_do = Actions.NoAction + + required_if = [ + ('state', 'present', [ + 'source_type', 'uri', 'security_token']) + ] + + super(AzureRMDevTestLabArtifactsSource, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=True, + required_if=required_if) + + def exec_module(self, **kwargs): + """Main module execution method""" + + for key in list(self.module_arg_spec.keys()) + ['tags']: + if hasattr(self, key): + setattr(self, key, kwargs[key]) + elif kwargs[key] is not None: + self.artifact_source[key] = kwargs[key] + + if self.artifact_source.get('source_type') == 'github': + self.artifact_source['source_type'] = 'GitHub' + elif self.artifact_source.get('source_type') == 'vso': + self.artifact_source['source_type'] = 'VsoGit' + + if self.artifact_source.get('status') is not None: + self.artifact_source['status'] = 'Enabled' if self.artifact_source.get('status') else 'Disabled' + + response = None + + self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient, + base_url=self._cloud_environment.endpoints.resource_manager, + api_version='2018-10-15') + + old_response = self.get_devtestlabartifactssource() + + if not old_response: + self.log("DevTest Labs Artifacts Source instance doesn't exist") + if self.state == 'absent': + self.log("Old instance didn't exist") + else: + self.to_do = Actions.Create + else: + self.log("DevTest Labs Artifacts Source instance already exists") + if self.state == 'absent': + self.to_do = Actions.Delete + elif self.state == 'present': + self.results['old_response'] = old_response + + if self.artifact_source.get('display_name') is not None: + if self.artifact_source.get('display_name') != old_response.get('display_name'): + self.to_do = Actions.Update + else: + self.artifact_source['display_name'] = old_response.get('display_name') + + if self.artifact_source.get('source_type').lower() != old_response.get('source_type').lower(): + self.to_do = Actions.Update + + if self.artifact_source.get('uri') != old_response.get('uri'): + self.to_do = Actions.Update + + if self.artifact_source.get('branch_ref') is not None: + if self.artifact_source.get('branch_ref') != old_response.get('branch_ref'): + self.to_do = Actions.Update + else: + self.artifact_source['branch_ref'] = old_response.get('branch_ref') + + if self.artifact_source.get('status') is not None: + if self.artifact_source.get('status') != old_response.get('status'): + self.to_do = Actions.Update + else: + self.artifact_source['status'] = old_response.get('status') + + if self.artifact_source.get('folder_path') is not None: + if self.artifact_source.get('folder_path') != old_response.get('folder_path'): + self.to_do = Actions.Update + else: + self.artifact_source['folder_path'] = old_response.get('folder_path') + + if self.artifact_source.get('arm_template_folder_path') is not None: + if self.artifact_source.get('arm_template_folder_path') != old_response.get('arm_template_folder_path'): + self.to_do = Actions.Update + else: + self.artifact_source['arm_template_folder_path'] = old_response.get('arm_template_folder_path') + + if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): + self.log("Need to Create / Update the DevTest Labs Artifacts Source instance") + self.results['changed'] = True + if self.check_mode: + return self.results + response = self.create_update_devtestlabartifactssource() + self.log("Creation / Update done") + elif self.to_do == Actions.Delete: + self.log("DevTest Labs Artifacts Source instance deleted") + self.results['changed'] = True + if self.check_mode: + return self.results + self.delete_devtestlabartifactssource() + else: + self.log("DevTest Labs Artifacts Source instance unchanged") + self.results['changed'] = False + response = old_response + + if self.state == 'present': + self.results.update({ + 'id': response.get('id', None), + 'is_enabled': (response.get('status', None).lower() == 'enabled') + }) + return self.results + + def create_update_devtestlabartifactssource(self): + ''' + Creates or updates DevTest Labs Artifacts Source with the specified configuration. + + :return: deserialized DevTest Labs Artifacts Source instance state dictionary + ''' + self.log("Creating / Updating the DevTest Labs Artifacts Source instance {0}".format(self.name)) + + try: + response = self.mgmt_client.artifact_sources.create_or_update(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name, + artifact_source=self.artifact_source) + if isinstance(response, LROPoller) or isinstance(response, AzureOperationPoller): + response = self.get_poller_result(response) + + except CloudError as exc: + self.log('Error attempting to create the DevTest Labs Artifacts Source instance.') + self.fail("Error creating the DevTest Labs Artifacts Source instance: {0}".format(str(exc))) + return response.as_dict() + + def delete_devtestlabartifactssource(self): + ''' + Deletes specified DevTest Labs Artifacts Source instance in the specified subscription and resource group. + + :return: True + ''' + self.log("Deleting the DevTest Labs Artifacts Source instance {0}".format(self.name)) + try: + response = self.mgmt_client.artifact_sources.delete(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name) + except CloudError as e: + self.log('Error attempting to delete the DevTest Labs Artifacts Source instance.') + self.fail("Error deleting the DevTest Labs Artifacts Source instance: {0}".format(str(e))) + + return True + + def get_devtestlabartifactssource(self): + ''' + Gets the properties of the specified DevTest Labs Artifacts Source. + + :return: deserialized DevTest Labs Artifacts Source instance state dictionary + ''' + self.log("Checking if the DevTest Labs Artifacts Source instance {0} is present".format(self.name)) + found = False + try: + response = self.mgmt_client.artifact_sources.get(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name) + found = True + self.log("Response : {0}".format(response)) + self.log("DevTest Labs Artifacts Source instance : {0} found".format(response.name)) + except CloudError as e: + self.log('Did not find the DevTest Labs Artifacts Source instance.') + if found is True: + return response.as_dict() + + return False + + +def main(): + """Main execution""" + AzureRMDevTestLabArtifactsSource() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualmachine.py b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualmachine.py new file mode 100644 index 0000000000..e759155741 --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualmachine.py @@ -0,0 +1,543 @@ +#!/usr/bin/python +# +# Copyright (c) 2018 Zim Kalinowski, +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: azure_rm_devtestlabvirtualmachine +version_added: "2.8" +short_description: Manage Azure DevTest Lab Virtual Machine instance. +description: + - Create, update and delete instance of Azure DevTest Lab Virtual Machine. + +options: + resource_group: + description: + - The name of the resource group. + required: True + lab_name: + description: + - The name of the lab. + required: True + name: + description: + - The name of the virtual machine. + required: True + notes: + description: + - The notes of the virtual machine. + os_type: + description: + - Base type of operating system. + choices: + - windows + - linux + vm_size: + description: + - A valid Azure VM size value. For example, 'Standard_D4'. The list of choices varies depending on the + subscription and location. Check your subscription for available choices. Required when creating a VM. + - "Available values can be found here: U(https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sizes-general)" + user_name: + description: + - The user name of the virtual machine. + password: + description: + - The password of the virtual machine administrator. + ssh_key: + description: + - The SSH key of the virtual machine administrator. + lab_subnet: + description: + - An existing subnet within lab's virtual network + - It can be the subnet's resource id. + - It can be a dict which contains C(virtual_network_name) and C(name). + disallow_public_ip_address: + description: + - Indicates whether the virtual machine is to be created without a public IP address. + artifacts: + description: + - The artifacts to be installed on the virtual machine. + type: list + suboptions: + source_name: + description: + - "The artifact's source name." + source_path: + description: + - "The artifact's path in the source repository." + parameters: + description: + - The parameters of the artifact. + type: list + suboptions: + name: + description: + - The name of the artifact parameter. + value: + description: + - The value of the artifact parameter. + image: + description: + - The Microsoft Azure Marketplace image reference of the virtual machine. + suboptions: + offer: + description: + - The offer of the gallery image. + publisher: + description: + - The publisher of the gallery image. + sku: + description: + - The SKU of the gallery image. + os_type: + description: + - The OS type of the gallery image. + version: + description: + - The version of the gallery image. + expiration_date: + description: + - The expiration date for VM. + allow_claim: + description: + - Indicates whether another user can take ownership of the virtual machine. + storage_type: + description: + - Storage type to use for virtual machine. + choices: + - standard + - premium + state: + description: + - Assert the state of the Virtual Machine. + - Use 'present' to create or update an Virtual Machine and 'absent' to delete it. + default: present + choices: + - absent + - present + +extends_documentation_fragment: + - azure + - azure_tags + +author: + - "Zim Kalinowski (@zikalino)" + +''' + +EXAMPLES = ''' + - name: Create (or update) Virtual Machine + azure_rm_devtestlabvirtualmachine: + resource_group: myrg + lab_name: mylab + name: myvm + notes: Virtual machine notes.... + os_type: linux + vm_size: Standard_A2_v2 + user_name: vmadmin + password: ZSuppas$$21! + lab_subnet: + name: myvnSubnet + virtual_network_name: myvn + disallow_public_ip_address: no + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + os_type: Linux + version: latest + artifacts: + - source_name: myartifact + source_path: "/Artifacts/linux-install-mongodb" + allow_claim: no + expiration_date: "2019-02-22T01:49:12.117974Z" +''' + +RETURN = ''' +id: + description: + - The identifier of the DTL Virtual Machine resource. + returned: always + type: str + sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myrg/providers/microsoft.devtestlab/labs/mylab/virtualmachines/myvm +compute_id: + description: + - The identifier of the underlying Compute Virtual Machine resource. + returned: always + type: str + sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myrg/providers/microsoft.devtestlab/labs/mylab/virtualmachines/myvm +fqdn: + description: + - Fully qualified domain name or IP Address of the virtual machine. + returned: always + type: str + sample: myvm.eastus.cloudapp.azure.com +''' + +import time +from ansible.module_utils.azure_rm_common import AzureRMModuleBase +from ansible.module_utils.common.dict_transformations import _snake_to_camel + +try: + from msrestazure.azure_exceptions import CloudError + from msrest.polling import LROPoller + from msrestazure.azure_operation import AzureOperationPoller + from azure.mgmt.devtestlabs import DevTestLabsClient + from msrest.serialization import Model +except ImportError: + # This is handled in azure_rm_common + pass + + +class Actions: + NoAction, Create, Update, Delete = range(4) + + +class AzureRMVirtualMachine(AzureRMModuleBase): + """Configuration class for an Azure RM Virtual Machine resource""" + + def __init__(self): + self.module_arg_spec = dict( + resource_group=dict( + type='str', + required=True + ), + lab_name=dict( + type='str', + required=True + ), + name=dict( + type='str', + required=True + ), + notes=dict( + type='str' + ), + os_type=dict( + type='str', + choices=['linux', 'windows'] + ), + vm_size=dict( + type='str' + ), + user_name=dict( + type='str' + ), + password=dict( + type='str', + no_log=True + ), + ssh_key=dict( + type='str', + no_log=True + ), + lab_subnet=dict( + type='raw' + ), + disallow_public_ip_address=dict( + type='str' + ), + artifacts=dict( + type='list', + options=dict( + artifact_id=dict( + type='str' + ), + parameters=dict( + type='list', + options=dict( + name=dict( + type='str' + ), + value=dict( + type='str' + ) + ) + ) + ) + ), + image=dict( + type='dict', + options=dict( + offer=dict( + type='str' + ), + publisher=dict( + type='str' + ), + sku=dict( + type='str' + ), + os_type=dict( + type='str' + ), + version=dict( + type='str' + ) + ) + ), + expiration_date=dict( + type='str' + ), + allow_claim=dict( + type='str' + ), + storage_type=dict( + type='str', + choices=['standard', 'premium'] + ), + state=dict( + type='str', + default='present', + choices=['present', 'absent'] + ) + ) + + required_if = [ + ('state', 'present', [ + 'image', 'lab_subnet', 'vm_size', 'os_type']) + ] + + self.resource_group = None + self.lab_name = None + self.name = None + self.lab_virtual_machine = dict() + + self.results = dict(changed=False) + self.mgmt_client = None + self.state = None + self.to_do = Actions.NoAction + + super(AzureRMVirtualMachine, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=True, + supports_tags=True, + required_if=required_if) + + def exec_module(self, **kwargs): + """Main module execution method""" + + for key in list(self.module_arg_spec.keys()) + ['tags']: + if hasattr(self, key): + setattr(self, key, kwargs[key]) + elif kwargs[key] is not None: + self.lab_virtual_machine[key] = kwargs[key] + + self.lab_virtual_machine['gallery_image_reference'] = self.lab_virtual_machine.pop('image', None) + + if self.lab_virtual_machine.get('artifacts') is not None: + for artifact in self.lab_virtual_machine.get('artifacts'): + source_name = artifact.pop('source_name') + source_path = artifact.pop('source_path') + template = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DevTestLab/labs/{2}/artifactsources/{3}{4}" + artifact['artifact_id'] = template.format(self.subscription_id, self.resource_group, self.lab_name, source_name, source_path) + + self.lab_virtual_machine['size'] = self.lab_virtual_machine.pop('vm_size') + self.lab_virtual_machine['os_type'] = _snake_to_camel(self.lab_virtual_machine['os_type'], True) + + if self.lab_virtual_machine.get('storage_type'): + self.lab_virtual_machine['storage_type'] = _snake_to_camel(self.lab_virtual_machine['storage_type'], True) + + lab_subnet = self.lab_virtual_machine.pop('lab_subnet') + + if isinstance(lab_subnet, str): + vn_and_subnet = lab_subnet.split('/subnets/') + if (len(vn_and_subnet) == 2): + self.lab_virtual_machine['lab_virtual_network_id'] = vn_and_subnet[0] + self.lab_virtual_machine['lab_subnet_name'] = vn_and_subnet[1] + else: + self.fail("Invalid 'lab_subnet' resource id format") + else: + template = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DevTestLab/labs/{2}/virtualnetworks/{3}" + self.lab_virtual_machine['lab_virtual_network_id'] = template.format(self.subscription_id, + self.resource_group, + self.lab_name, + lab_subnet.get('virtual_network_name')) + self.lab_virtual_machine['lab_subnet_name'] = lab_subnet.get('name') + + response = None + + self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient, + base_url=self._cloud_environment.endpoints.resource_manager) + + old_response = self.get_virtualmachine() + + if not old_response: + self.log("Virtual Machine instance doesn't exist") + if self.state == 'absent': + self.log("Old instance didn't exist") + else: + self.to_do = Actions.Create + # get location from the lab as it has to be the same and has to be specified (why??) + lab = self.get_devtestlab() + self.lab_virtual_machine['location'] = lab['location'] + else: + self.log("Virtual Machine instance already exists") + if self.state == 'absent': + self.to_do = Actions.Delete + elif self.state == 'present': + self.lab_virtual_machine['location'] = old_response['location'] + + if old_response['size'].lower() != self.lab_virtual_machine.get('size').lower(): + self.lab_virtual_machine['size'] = old_response['size'] + self.module.warn("Property 'size' cannot be changed") + + if self.lab_virtual_machine.get('storage_type') is not None and \ + old_response['storage_type'].lower() != self.lab_virtual_machine.get('storage_type').lower(): + self.lab_virtual_machine['storage_type'] = old_response['storage_type'] + self.module.warn("Property 'storage_type' cannot be changed") + + if old_response.get('gallery_image_reference', {}) != self.lab_virtual_machine.get('gallery_image_reference', {}): + self.lab_virtual_machine['gallery_image_reference'] = old_response['gallery_image_reference'] + self.module.warn("Property 'image' cannot be changed") + + # currently artifacts can be only specified when vm is created + # and in addition we don't have detailed information, just a number of "total artifacts" + if len(self.lab_virtual_machine.get('artifacts', [])) != old_response['artifact_deployment_status']['total_artifacts']: + self.module.warn("Property 'artifacts' cannot be changed") + + if self.lab_virtual_machine.get('disallow_public_ip_address') is not None: + if old_response['disallow_public_ip_address'] != self.lab_virtual_machine.get('disallow_public_ip_address'): + self.module.warn("Property 'disallow_public_ip_address' cannot be changed") + self.lab_virtual_machine['disallow_public_ip_address'] = old_response['disallow_public_ip_address'] + + if self.lab_virtual_machine.get('allow_claim') is not None: + if old_response['allow_claim'] != self.lab_virtual_machine.get('allow_claim'): + self.module.warn("Property 'allow_claim' cannot be changed") + self.lab_virtual_machine['allow_claim'] = old_response['allow_claim'] + + if self.lab_virtual_machine.get('notes') is not None: + if old_response['notes'] != self.lab_virtual_machine.get('notes'): + self.to_do = Actions.Update + else: + self.lab_virtual_machine['notes'] = old_response['notes'] + + if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): + self.log("Need to Create / Update the Virtual Machine instance") + + self.results['changed'] = True + if self.check_mode: + return self.results + + response = self.create_update_virtualmachine() + + self.log("Creation / Update done") + elif self.to_do == Actions.Delete: + self.log("Virtual Machine instance deleted") + self.results['changed'] = True + + if self.check_mode: + return self.results + + self.delete_virtualmachine() + else: + self.log("Virtual Machine instance unchanged") + self.results['changed'] = False + response = old_response + + if self.state == 'present': + self.results.update({ + 'id': response.get('id', None), + 'compute_id': response.get('compute_id', None), + 'fqdn': response.get('fqdn', None) + }) + return self.results + + def create_update_virtualmachine(self): + ''' + Creates or updates Virtual Machine with the specified configuration. + + :return: deserialized Virtual Machine instance state dictionary + ''' + self.log("Creating / Updating the Virtual Machine instance {0}".format(self.name)) + + try: + response = self.mgmt_client.virtual_machines.create_or_update(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name, + lab_virtual_machine=self.lab_virtual_machine) + if isinstance(response, LROPoller) or isinstance(response, AzureOperationPoller): + response = self.get_poller_result(response) + + except CloudError as exc: + self.log('Error attempting to create the Virtual Machine instance.') + self.fail("Error creating the Virtual Machine instance: {0}".format(str(exc))) + return response.as_dict() + + def delete_virtualmachine(self): + ''' + Deletes specified Virtual Machine instance in the specified subscription and resource group. + + :return: True + ''' + self.log("Deleting the Virtual Machine instance {0}".format(self.name)) + try: + response = self.mgmt_client.virtual_machines.delete(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name) + except CloudError as e: + self.log('Error attempting to delete the Virtual Machine instance.') + self.fail("Error deleting the Virtual Machine instance: {0}".format(str(e))) + + if isinstance(response, LROPoller) or isinstance(response, AzureOperationPoller): + response = self.get_poller_result(response) + + return True + + def get_virtualmachine(self): + ''' + Gets the properties of the specified Virtual Machine. + + :return: deserialized Virtual Machine instance state dictionary + ''' + self.log("Checking if the Virtual Machine instance {0} is present".format(self.name)) + found = False + try: + response = self.mgmt_client.virtual_machines.get(resource_group_name=self.resource_group, + lab_name=self.lab_name, + name=self.name) + found = True + self.log("Response : {0}".format(response)) + self.log("Virtual Machine instance : {0} found".format(response.name)) + except CloudError as e: + self.log('Did not find the Virtual Machine instance.') + if found is True: + return response.as_dict() + + return False + + def get_devtestlab(self): + ''' + Gets the properties of the specified DevTest Lab. + + :return: deserialized DevTest Lab instance state dictionary + ''' + self.log("Checking if the DevTest Lab instance {0} is present".format(self.lab_name)) + try: + response = self.mgmt_client.labs.get(resource_group_name=self.resource_group, + name=self.lab_name) + self.log("Response : {0}".format(response)) + self.log("DevTest Lab instance : {0} found".format(response.name)) + return response.as_dict() + except CloudError as e: + self.fail('Did not find the DevTest Lab instance.') + return False + + +def main(): + """Main execution""" + AzureRMVirtualMachine() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualnetwork.py b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualnetwork.py index 3ab8a666c4..88f6222a96 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualnetwork.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_devtestlabvirtualnetwork.py @@ -166,6 +166,19 @@ class AzureRMDevTestLabVirtualNetwork(AzureRMModuleBase): if self.virtual_network.get('location') is None: self.virtual_network['location'] = resource_group.location + # subnet overrides for virtual network and subnet created by default + template = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/virtualNetworks/{2}/subnets/{3}" + subnet_id = template.format(self.subscription_id, + self.resource_group, + self.name, + self.name + "Subnet") + self.virtual_network['subnet_overrides'] = [{ + 'resource_id': subnet_id, + 'lab_subnet_name': self.name + "Subnet", + 'use_in_vm_creation_permission': 'Allow', + 'use_public_ip_address_permission': 'Allow' + }] + old_response = self.get_virtualnetwork() if not old_response: diff --git a/test/integration/targets/azure_rm_devtestlab/tasks/main.yml b/test/integration/targets/azure_rm_devtestlab/tasks/main.yml index 6d55be20d4..bae2f69076 100644 --- a/test/integration/targets/azure_rm_devtestlab/tasks/main.yml +++ b/test/integration/targets/azure_rm_devtestlab/tasks/main.yml @@ -2,6 +2,9 @@ set_fact: lab_name: "lab{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" vn_name: "vn{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + vm_name: "vn{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + artifacts_name: myartifacts + github_token: "{{ lookup('env','GITHUB_ACCESS_TOKEN') }}" run_once: yes - name: Create instance of Lab -- check mode @@ -96,7 +99,7 @@ - output.labs[0]['provisioning_state'] != None - output.labs[0]['vault_name'] != None -- name: Create instance of DebTest Labs virtual network +- name: Create instance of DevTest Labs virtual network azure_rm_devtestlabvirtualnetwork: resource_group: "{{ resource_group }}" lab_name: "{{ lab_name }}" @@ -109,7 +112,7 @@ that: - output.changed -- name: Update instance of DebTest Labs virtual network with same parameters +- name: Update instance of DevTest Labs virtual network with same parameters azure_rm_devtestlabvirtualnetwork: resource_group: "{{ resource_group }}" lab_name: "{{ lab_name }}" @@ -122,7 +125,7 @@ that: - output.changed == false -- name: Update instance of DebTest Labs virtual network with changed description +- name: Update instance of DevTest Labs virtual network with changed description azure_rm_devtestlabvirtualnetwork: resource_group: "{{ resource_group }}" lab_name: "{{ lab_name }}" @@ -170,6 +173,160 @@ - output.virtualnetworks[0]['description'] != None - output.virtualnetworks[0]['provisioning_state'] != None +#- name: Create instance of DevTest Labs artifacts source +# azure_rm_devtestlabartifactsource: +# resource_group: "{{ resource_group }}" +# lab_name: "{{ lab_name }}" +# name: "{{ artifacts_name }}" +# uri: https://github.com/Azure/azure_preview_modules.git +# source_type: github +# folder_path: /tasks +# security_token: "{{ github_token }}" +# register: output +#- name: Assert if the change was correctly reported +# assert: +# that: +# - output.changed + +#- name: Update instance of DevTest Labs artifacts source with same parameters +# azure_rm_devtestlabartifactsource: +# resource_group: "{{ resource_group }}" +# lab_name: "{{ lab_name }}" +# name: "{{ artifacts_name }}" +# uri: https://github.com/Azure/azure_preview_modules.git +# source_type: github +# folder_path: /tasks +# security_token: "{{ github_token }}" +# register: output +#- name: Assert that nothing was changed +# assert: +# that: +# - output.changed == false + +#- name: Update instance of DevTest Labs artifacts source, add display name, change folder +# azure_rm_devtestlabartifactsource: +# resource_group: "{{ resource_group }}" +# lab_name: "{{ lab_name }}" +# name: "{{ artifacts_name }}" +# uri: https://github.com/Azure/azure_preview_modules.git +# source_type: github +# folder_path: /library +# security_token: "{{ github_token }}" +# display_name: My Artifacts Source +# register: output +#- name: Assert that nothing was changed +# assert: +# that: +# - output.changed + +- name: Create instance of DTL Virtual Machine + azure_rm_devtestlabvirtualmachine: + resource_group: "{{ resource_group }}" + lab_name: "{{ lab_name }}" + name: "{{ vm_name }}" + notes: Virtual machine notes, just something.... + os_type: linux + vm_size: Standard_A2_v2 + user_name: dtladmin + password: ZSasfovobocu$$21! + lab_subnet: + virtual_network_name: "{{ vn_name }}" + name: "{{ vn_name }}Subnet" + disallow_public_ip_address: no + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + os_type: Linux + version: latest +# artifacts: +# - source_name: "{{ artifacts_name }}" +# source_path: "/Artifacts/linux-install-mongodb" + allow_claim: no + expiration_date: "2029-02-22T01:49:12.117974Z" + register: output + +- name: Assert that change was registered + assert: + that: + - output.changed + +- name: Update instance of DTL Virtual Machine with same parameters + azure_rm_devtestlabvirtualmachine: + resource_group: "{{ resource_group }}" + lab_name: "{{ lab_name }}" + name: "{{ vm_name }}" + notes: Virtual machine notes, just something.... + os_type: linux + vm_size: Standard_A2_v2 + user_name: dtladmin + password: ZSasfovobocu$$21! + lab_subnet: + virtual_network_name: "{{ vn_name }}" + name: "{{ vn_name }}Subnet" + disallow_public_ip_address: no + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + os_type: Linux + version: latest +# artifacts: +# - source_name: "{{ artifacts_name }}" +# source_path: "/Artifacts/linux-install-mongodb" + allow_claim: no + expiration_date: "2029-02-22T01:49:12.117974Z" + register: output + +- name: Assert that nothing has changed + assert: + that: + - output.changed == false + +- name: Update instance of DTL Virtual Machine - change notes + azure_rm_devtestlabvirtualmachine: + resource_group: "{{ resource_group }}" + lab_name: "{{ lab_name }}" + name: "{{ vm_name }}" + notes: Virtual machine notes, just something.... more text + os_type: linux + vm_size: Standard_A2_v2 + user_name: dtladmin + password: ZSasfovobocu$$21! + lab_subnet: + virtual_network_name: "{{ vn_name }}" + name: "{{ vn_name }}Subnet" + disallow_public_ip_address: no + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + os_type: Linux + version: latest +# artifacts: +# - source_name: "{{ artifacts_name }}" +# source_path: "/Artifacts/linux-install-mongodb" + allow_claim: no + expiration_date: "2029-02-22T01:49:12.117974Z" + register: output + +- name: Assert that change was registered + assert: + that: + - output.changed + +#- name: Delete instance of DevTest Labs artifacts source +# azure_rm_devtestlabartifactsource: +# resource_group: "{{ resource_group }}" +# lab_name: "{{ lab_name }}" +# name: "{{ artifacts_name }}" +# state: absent +# register: output +#- name: Assert that change was correctly registered +# assert: +# that: +# - output.changed + - name: Delete instance of Lab -- check mode azure_rm_devtestlab: resource_group: "{{ resource_group }}"