summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Coca <bcoca@ansible.com>2016-04-26 10:02:42 -0400
committerBrian Coca <bcoca@ansible.com>2016-04-26 10:02:42 -0400
commitff8355b45b39ddb0f0a51fe3323c4529add9ab48 (patch)
tree88ad2a5d718fb04ef349ed7ca6ad0ea4246a77ea
parent11892d5f8af0150e24f9bd3945239b6fc098972f (diff)
downloadansible-modules-core-revert-3488-azure_rm_virtualmachine.tar.gz
Revert "Adding module azure_rm_virtualmachine"revert-3488-azure_rm_virtualmachine
-rw-r--r--cloud/azure/azure_rm_virtualmachine.py1316
1 files changed, 0 insertions, 1316 deletions
diff --git a/cloud/azure/azure_rm_virtualmachine.py b/cloud/azure/azure_rm_virtualmachine.py
deleted file mode 100644
index ff85208f..00000000
--- a/cloud/azure/azure_rm_virtualmachine.py
+++ /dev/null
@@ -1,1316 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (c) 2016 Matt Davis, <mdavis@ansible.com>
-# Chris Houseknecht, <house@redhat.com>
-#
-# This file is part of Ansible
-#
-# Ansible is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Ansible is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-#
-
-DOCUMENTATION = '''
----
-module: azure_rm_virtualmachine
-
-version_added: "2.1"
-
-short_description: Manage Azure virtual machines.
-
-description:
- - Create, update, stop and start a virtual machine. Provide an existing storage account and network interface or
- allow the module to create these for you. If you choose not to provide a network interface, the resource group
- must contain a virtual network with at least one subnet.
- - Currently requires an image found in the Azure Marketplace. Use azure_rm_virtualmachineimage_facts module
- to discover the publisher, offer, sku and version of a particular image.
-
-options:
- resource_group:
- description:
- - Name of the resource group containing the virtual machine.
- required: true
- name:
- description:
- - Name of the virtual machine.
- required: true
- state:
- description:
- - Assert the state of the virtual machine.
- - State 'present' will check that the machine exists with the requested configuration. If the configuration
- of the existing machine does not match, the machine will be updated. Use options started, stopped,
- deallocated and restarted to change the machine's power state.
- - State 'absent' will remove the virtual machine.
- default: present
- required: false
- choices:
- - absent
- - present
- started:
- description:
- - Use with state 'present' to start the machine.
- default: true
- required: false
- stopped:
- description:
- - Use with state 'present' to stop the machine.
- default: false
- required: false
- deallocated:
- description:
- - Use with state 'present' to put the VM in a deallocated state.
- default: false
- required: false
- restarted:
- description:
- - Use with state 'present' to restart a running VM.
- default: false
- required: false
- location:
- description:
- - Valid Azure location. Defaults to location of the resource group.
- default: null
- required: false
- short_hostname:
- description:
- - Name assigned internally to the host. On a linux VM this is the name returned by the `hostname` command.
- When creating a virtual machine, short_hostname defaults to name.
- default: null
- required: false
- 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.
- default: Standard_D1
- required: false
- admin_username:
- description:
- - Admin username used to access the host after it is created. Required when creating a VM.
- default: null
- required: false
- admin_password:
- description:
- - Password for the admin username. Not required if the os_type is Linux and SSH password authentication
- is disabled by setting ssh_password_enabled to false.
- default: null
- required: false
- ssh_password_enabled:
- description:
- - When the os_type is Linux, setting ssh_password_enabled to false will disable SSH password authentication
- and require use of SSH keys.
- default: true
- required: false
- ssh_public_keys:
- description:
- - "For os_type Linux provide a list of SSH keys. Each item in the list should be a dictionary where the
- dictionary contains two keys: path and key_data. Set the path to the default location of the
- authorized_keys files. On an Enterprise Linux host, for example, the path will be
- /home/<admin username>/.ssh/authorized_keys. Set key_data to the actual value of the public key."
- default: null
- required: false
- 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."
- required: true
- storage_account_name:
- description:
- - Name of an existing storage account that supports creation of VHD blobs. If not specified for a new VM,
- a new storage account named <vm name>01 will be created using storage type 'Standard_LRS'.
- default: null
- required: false
- storage_container_name:
- description:
- - Name of the container to use within the storage account to store VHD blobs. If no name is specified a
- default container will created.
- default: vhds
- required: false
- storage_blob_name:
- description:
- - Name fo the storage blob used to hold the VM's OS disk image. If no name is provided, defaults to
- the VM name + '.vhd'. If you provide a name, it must end with '.vhd'
- aliases:
- - storage_blob
- default: null
- required: false
- os_disk_caching:
- description:
- - Type of OS disk caching.
- choices:
- - ReadOnly
- - ReadWrite
- default: ReadOnly
- aliases:
- - disk_caching
- required: false
- os_type:
- description:
- - Base type of operating system.
- choices:
- - Windows
- - Linux
- default:
- - Linux
- required: false
- public_ip_allocation_method:
- description:
- - If a public IP address is created when creating the VM (beacuse a Network Interface was not provided),
- determines if the public IP address remains permanently associated with the Network Interface. If set
- to 'Dynamic' the public IP address may change any time the VM is rebooted or power cycled.
- choices:
- - Dynamic
- - Static
- default:
- - Static
- aliases:
- - public_ip_allocation
- required: false
- open_ports:
- description:
- - If a network interface is created when creating the VM, a security group will be created as well. For
- Linux hosts a rule will be added to the security group allowing inbound TCP connections to the default
- SSH port 22, and for Windows hosts ports 3389 and 5986 will be opened. Override the default open ports by
- providing a list of ports.
- default: null
- required: false
- network_interface_names:
- description:
- - List of existing network interface names to add to the VM. If a network interface name is not provided
- when the VM is created, a default network interface will be created. In order for the module to create
- a network interface, at least one Virtual Network with one Subnet must exist.
- default: null
- required: false
- virtual_network_name:
- description:
- - When creating a virtual machine, if a network interface name is not provided, one will be created.
- The new network interface will be assigned to the first virtual network found in the resource group.
- Use this parameter to provide a specific virtual network instead.
- aliases:
- - virtual_network
- default: null
- required: false
- subnet_name:
- description:
- - When creating a virtual machine, if a network interface name is not provided, one will be created.
- The new network interface will be assigned to the first subnet found in the virtual network.
- Use this parameter to provide a specific subnet instead.
- aliases:
- - virtual_network
- default: null
- required: false
- delete_network_interfaces:
- description:
- - When removing a VM using state 'absent', also remove any network interfaces associate with the VM.
- default: true
- aliases:
- - delete_nics
- required: false
- delete_virtual_storage:
- description:
- - When removing a VM using state 'absent', also remove any storage blobs associated with the VM.
- default: true
- aliases:
- - delete_vhd
- required: false
- delete_public_ips:
- description:
- - When removing a VM using state 'absent', also remove any public IP addresses associate with the VM.
- default: true
- required: false
- tags:
- description:
- - "Dictionary of string:string pairs to assign as metadata to the object. Metadata tags on the object
- will be updated with any provided values. To remove tags use the purge_tags option."
- default: null
- required: false
- purge_tags:
- description:
- - Use to remove tags from an object. Any tags not found in the tags parameter will be removed from
- the object's metadata.
- default: false
- required: false
-
-extends_documentation_fragment:
- - azure
-
-author:
- - "Chris Houseknecht (@chouseknecht)"
- - "Matt Davis (@nitzmahone)"
-
-'''
-EXAMPLES = '''
-
-- name: Create VM with defaults
- azure_rm_virtualmachine:
- resource_group: Testing
- name: testvm10
- admin_username: chouseknecht
- admin_password: <your password here>
- image:
- offer: CentOS
- publisher: OpenLogic
- sku: '7.1'
- version: latest
-
-- name: Create a VM with exiting storage account and NIC
- azure_rm_virtualmachine:
- resource_group: Testing
- name: testvm002
- vm_size: Standard_D4
- storage_account: testaccount001
- admin_username: adminUser
- ssh_public_keys:
- path: /home/adminUser/.ssh/authorized_keys
- key_data: < insert yor ssh public key here... >
- network_interfaces: testvm001
- image:
- offer: CentOS
- publisher: OpenLogic
- sku: '7.1'
- version: latest
-
-- name: Power Off
- azure_rm_virtualmachine:
- resource_group: Testing
- name: testvm002
- stopped: yes
-
-- name: Deallocate
- azure_rm_virtualmachine:
- resource_group: Testing
- name: testvm002
- deallocated: yes
-
-- name: Power On
- azure_rm_virtualmachine:
- resource_group:
- name: testvm002
-
-- name: Restart
- azure_rm_virtualmachine:
- resource_group:
- name: testvm002
- restarted: yes
-
-'''
-
-RETURN = '''
-actions:
- description: List of descriptive actions performed by the module.
- returned: always
- type: list
- sample: [
- "Powered on virtual machine testvm10"
- ]
-powerstate:
- description: Indicates if the state is running, stopped, deallocated
- returned: always
- type: string
- sample: running
-state:
- description: Facts about the current state of the object.
- returned: always
- type: dict
- sample: {
- "properties": {
- "hardwareProfile": {
- "vmSize": "Standard_D1"
- },
- "instanceView": {
- "disks": [
- {
- "name": "testvm10.vhd",
- "statuses": [
- {
- "code": "ProvisioningState/succeeded",
- "displayStatus": "Provisioning succeeded",
- "level": "Info",
- "time": "2016-03-30T07:11:16.187272Z"
- }
- ]
- }
- ],
- "statuses": [
- {
- "code": "ProvisioningState/succeeded",
- "displayStatus": "Provisioning succeeded",
- "level": "Info",
- "time": "2016-03-30T20:33:38.946916Z"
- },
- {
- "code": "PowerState/running",
- "displayStatus": "VM running",
- "level": "Info"
- }
- ],
- "vmAgent": {
- "extensionHandlers": [],
- "statuses": [
- {
- "code": "ProvisioningState/succeeded",
- "displayStatus": "Ready",
- "level": "Info",
- "message": "GuestAgent is running and accepting new configurations.",
- "time": "2016-03-30T20:31:16.000Z"
- }
- ],
- "vmAgentVersion": "WALinuxAgent-2.0.16"
- }
- },
- "networkProfile": {
- "networkInterfaces": [
- {
- "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkInterfaces/testvm10_NIC01",
- "name": "testvm10_NIC01",
- "properties": {
- "dnsSettings": {
- "appliedDnsServers": [],
- "dnsServers": []
- },
- "enableIPForwarding": false,
- "ipConfigurations": [
- {
- "etag": "W/\"041c8c2a-d5dd-4cd7-8465-9125cfbe2cf8\"",
- "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkInterfaces/testvm10_NIC01/ipConfigurations/default",
- "name": "default",
- "properties": {
- "privateIPAddress": "10.10.0.5",
- "privateIPAllocationMethod": "Dynamic",
- "provisioningState": "Succeeded",
- "publicIPAddress": {
- "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/publicIPAddresses/testvm10_PIP01",
- "name": "testvm10_PIP01",
- "properties": {
- "idleTimeoutInMinutes": 4,
- "ipAddress": "13.92.246.197",
- "ipConfiguration": {
- "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkInterfaces/testvm10_NIC01/ipConfigurations/default"
- },
- "provisioningState": "Succeeded",
- "publicIPAllocationMethod": "Static",
- "resourceGuid": "3447d987-ca0d-4eca-818b-5dddc0625b42"
- }
- }
- }
- }
- ],
- "macAddress": "00-0D-3A-12-AA-14",
- "primary": true,
- "provisioningState": "Succeeded",
- "resourceGuid": "10979e12-ccf9-42ee-9f6d-ff2cc63b3844",
- "virtualMachine": {
- "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Compute/virtualMachines/testvm10"
- }
- }
- }
- ]
- },
- "osProfile": {
- "adminUsername": "chouseknecht",
- "computerName": "test10",
- "linuxConfiguration": {
- "disablePasswordAuthentication": false
- },
- "secrets": []
- },
- "provisioningState": "Succeeded",
- "storageProfile": {
- "dataDisks": [],
- "imageReference": {
- "offer": "CentOS",
- "publisher": "OpenLogic",
- "sku": "7.1",
- "version": "7.1.20160308"
- },
- "osDisk": {
- "caching": "ReadOnly",
- "createOption": "fromImage",
- "name": "testvm10.vhd",
- "osType": "Linux",
- "vhd": {
- "uri": "https://testvm10sa1.blob.core.windows.net/vhds/testvm10.vhd"
- }
- }
- }
- },
- "type": "Microsoft.Compute/virtualMachines"
- }
-'''
-
-import random
-
-from ansible.module_utils.basic import *
-from ansible.module_utils.azure_rm_common import *
-
-try:
- from msrestazure.azure_exceptions import CloudError
- from azure.common import AzureMissingResourceHttpError
- from azure.mgmt.compute.models import NetworkInterfaceReference, VirtualMachine, HardwareProfile, \
- StorageProfile, OSProfile, OSDisk, VirtualHardDisk, ImageReference, NetworkProfile, LinuxConfiguration, \
- SshConfiguration, SshPublicKey
- from azure.mgmt.network.models import PublicIPAddress, NetworkSecurityGroup, SecurityRule, NetworkInterface, \
- NetworkInterfaceIPConfiguration, Subnet
- from azure.mgmt.storage.models import AccountType, AccountStatus, StorageAccountCreateParameters
- from azure.mgmt.compute.models.compute_management_client_enums import CachingTypes, DiskCreateOptionTypes, \
- VirtualMachineSizeTypes
-except ImportError:
- # This is handled in azure_rm_common
- pass
-
-
-AZURE_OBJECT_CLASS = 'VirtualMachine'
-
-
-def extract_names_from_blob_uri(blob_uri):
- # HACK: ditch this once python SDK supports get by URI
- m = re.match('^https://(?P<accountname>[^\.]+)\.blob\.core\.windows\.net/'
- '(?P<containername>[^/]+)/(?P<blobname>.+)$', blob_uri)
- if not m:
- raise Exception("unable to parse blob uri '%s'" % blob_uri)
- extracted_names = m.groupdict()
- return extracted_names
-
-
-class AzureRMVirtualMachine(AzureRMModuleBase):
-
- def __init__(self):
-
- self.module_arg_spec = dict(
- resource_group=dict(type='str', required=True),
- name=dict(type='str', required=True),
- state=dict(choices=['present', 'absent'], default='present', type='str'),
- location=dict(type='str'),
- short_hostname=dict(type='str'),
- vm_size=dict(type='str', choices=[], default='Standard_D1'),
- admin_username=dict(type='str'),
- admin_password=dict(type='str', ),
- ssh_password_enabled=dict(type='bool', default=True),
- ssh_public_keys=dict(type='list'),
- image=dict(type='dict'),
- storage_account_name=dict(type='str', aliases=['storage_account']),
- storage_container_name=dict(type='str', aliases=['storage_container'], default='vhds'),
- storage_blob_name=dict(type='str', aliases=['storage_blob']),
- os_disk_caching=dict(type='str', aliases=['disk_caching'], choices=['ReadOnly', 'ReadWrite'],
- default='ReadOnly'),
- os_type=dict(type='str', choices=['Linux', 'Windows'], default='Linux'),
- public_ip_allocation_method=dict(type='str', choices=['Dynamic', 'Static'], default='Static',
- aliases=['public_ip_allocation']),
- open_ports=dict(type='list'),
- network_interface_names=dict(type='list', aliases=['network_interfaces']),
- delete_network_interfaces=dict(type='bool', default=True, aliases=['delete_nics']),
- delete_virtual_storage=dict(type='bool', default=True, aliases=['delete_vhd']),
- delete_public_ips=dict(type='bool', default=True),
- virtual_network_name=dict(type='str', aliases=['virtual_network']),
- subnet_name=dict(type='str', aliases=['subnet']),
- deallocated=dict(type='bool', default=False),
- restarted=dict(type='bool', default=False),
- started=dict(type='bool', default=True),
- stopped=dict(type='bool', default=False),
- )
-
- for key in VirtualMachineSizeTypes:
- self.module_arg_spec['vm_size']['choices'].append(getattr(key, 'value'))
-
- self.resource_group = None
- self.name = None
- self.state = None
- self.location = None
- self.short_hostname = None
- self.vm_size = None
- self.admin_username = None
- self.admin_password = None
- self.ssh_password_enabled = None
- self.ssh_public_keys = None
- self.image = None
- self.storage_account_name = None
- self.storage_container_name = None
- self.storage_blob_name = None
- self.os_type = None
- self.os_disk_caching = None
- self.network_interface_names = None
- self.delete_network_interfaces = None
- self.delete_virtual_storage = None
- self.delete_public_ips = None
- self.tags = None
- self.force = None
- self.public_ip_allocation_method = None
- self.open_ports = None
- self.virtual_network_name = None
- self.subnet_name = None
- self.deallocated = None
- self.restarted = None
- self.started = None
- self.stopped = None
- self.differences = None
-
- self.results = dict(
- changed=False,
- actions=[],
- powerstate_change=None,
- state=dict()
- )
-
- super(AzureRMVirtualMachine, self).__init__(derived_arg_spec=self.module_arg_spec,
- supports_check_mode=True)
-
- def exec_module(self, **kwargs):
-
- for key in self.module_arg_spec.keys() + ['tags']:
- setattr(self, key, kwargs[key])
-
- changed = False
- powerstate_change = None
- results = dict()
- vm = None
- network_interfaces = []
- requested_vhd_uri = None
- disable_ssh_password = None
- vm_dict = None
-
- resource_group = self.get_resource_group(self.resource_group)
- if not self.location:
- # Set default location
- self.location = resource_group.location
-
- if self.state == 'present':
- # Verify parameters and resolve any defaults
-
- if self.vm_size and not self.vm_size_is_valid():
- self.fail("Parameter error: vm_size {0} is not valid for your subscription and location.".foramt(
- self.vm_size
- ))
-
- if self.network_interface_names:
- for name in self.network_interface_names:
- nic = self.get_network_interface(name)
- network_interfaces.append(nic.id)
-
- if self.ssh_public_keys:
- msg = "Parameter error: expecting ssh_public_keys to be a list of type dict where " \
- "each dict contains keys: path, key_data."
- for key in self.ssh_public_keys:
- if not isinstance(key, dict):
- self.fail(msg)
- 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 not self.storage_blob_name:
- self.storage_blob_name = self.name + '.vhd'
-
- if self.storage_account_name:
- self.get_storage_account(self.storage_account_name)
-
- requested_vhd_uri = 'https://{0}.blob.core.windows.net/{1}/{2}'.format(self.storage_account_name,
- self.storage_container_name,
- self.storage_blob_name)
-
- disable_ssh_password = not self.ssh_password_enabled
-
- 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)
- vm_dict = self.serialize_vm(vm)
-
- if self.state == 'present':
- differences = []
- current_nics = []
- results = vm_dict
-
- # Try to determine if the VM needs to be updated
- if self.network_interface_names:
- for nic in vm_dict['properties']['networkProfile']['networkInterfaces']:
- current_nics.append(nic['id'])
-
- if set(current_nics) != set(network_interfaces):
- self.log('CHANGED: virtual machine {0} - network interfaces are different.'.format(self.name))
- differences.append('Network Interfaces')
- updated_nics = [dict(id=id) for id in network_interfaces]
- vm_dict['properties']['networkProfile']['networkInterfaces'] = updated_nics
- changed = True
-
- if self.os_disk_caching and \
- self.os_disk_caching != vm_dict['properties']['storageProfile']['osDisk']['caching']:
- self.log('CHANGED: virtual machine {0} - OS disk caching'.format(self.name))
- differences.append('OS Disk caching')
- changed = True
- vm_dict['properties']['storageProfile']['osDisk']['caching'] = self.os_disk_caching
-
- update_tags, vm_dict['tags'] = self.update_tags(vm_dict.get('tags', dict()))
- if update_tags:
- differences.append('Tags')
- changed = True
-
- if self.short_hostname and self.short_hostname != vm_dict['properties']['osProfile']['computerName']:
- self.log('CHANGED: virtual machine {0} - short hostname'.format(self.name))
- differences.append('Short Hostname')
- changed = True
- vm_dict['properties']['osProfile']['computerName'] = self.short_hostname
-
- if self.started and vm_dict['powerstate'] != 'running':
- self.log("CHANGED: virtual machine {0} not running and requested state 'running'".format(self.name))
- changed = True
- powerstate_change = 'poweron'
- elif self.state == 'present' and vm_dict['powerstate'] == 'running' and self.restarted:
- self.log("CHANGED: virtual machine {0} {1} and requested state 'restarted'"
- .format(self.name, vm_dict['powerstate']))
- changed = True
- powerstate_change = 'restarted'
- elif self.state == 'present' and self.deallocated and vm_dict['powerstate'] != 'deallocated':
- self.log("CHANGED: virtual machine {0} {1} and requested state 'deallocated'"
- .format(self.name, vm_dict['powerstate']))
- changed = True
- powerstate_change = 'deallocated'
- elif self.stopped and vm_dict['powerstate'] == 'running':
- self.log("CHANGED: virtual machine {0} running and requested state 'stopped'".format(self.name))
- changed = True
- powerstate_change = 'poweroff'
-
- self.differences = differences
-
- elif self.state == 'absent':
- self.log("CHANGED: virtual machine {0} exists and requested state is 'absent'".format(self.name))
- results = dict()
- changed = True
-
- except CloudError:
- self.log('Virtual machine {0} does not exist'.format(self.name))
- if self.state == 'present':
- self.log("CHANGED: virtual machine does not exist but state is present." \
- .format(self.name))
- changed = True
-
- self.results['changed'] = changed
- self.results['state'] = results
- self.results['powerstate_change'] = powerstate_change
-
- if self.check_mode:
- return self.results
-
- if changed:
- if self.state == 'present':
- if not vm:
- # Create the VM
- self.log("Create virtual machine {0}".format(self.name))
- self.results['actions'].append('Created VM {0}'.format(self.name))
-
- # Validate parameters
- if not self.admin_username:
- self.fail("Parameter error: admin_username required when creating a virtual machine.")
-
- if self.os_type == 'Linux':
- if disable_ssh_password and not self.ssh_public_keys:
- self.fail("Parameter error: ssh_public_keys required when disabling SSH password.")
-
- if not self.image:
- self.fail("Parameter error: an image is required when creating a virtual machine.")
-
- # Get defaults
- if not self.network_interface_names:
- default_nic = self.create_default_nic()
- self.log("network interface:")
- self.log(self.serialize_obj(default_nic, 'NetworkInterface'), pretty_print=True)
- network_interfaces = [default_nic.id]
-
- if not self.storage_account_name:
- storage_account = self.create_default_storage_account()
- self.log("storage account:")
- self.log(self.serialize_obj(storage_account, 'StorageAccount'), pretty_print=True)
- requested_vhd_uri = 'https://{0}.blob.core.windows.net/{1}/{2}'.format(
- storage_account.name,
- self.storage_container_name,
- self.storage_blob_name)
-
- if not self.short_hostname:
- self.short_hostname = self.name
-
- nics = [NetworkInterfaceReference(id=id) for id in network_interfaces]
- vhd = VirtualHardDisk(uri=requested_vhd_uri)
- vm_resource = VirtualMachine(
- location=self.location,
- name=self.name,
- tags=self.tags,
- os_profile=OSProfile(
- admin_username=self.admin_username,
- computer_name=self.short_hostname,
- ),
- hardware_profile=HardwareProfile(
- vm_size=self.vm_size
- ),
- storage_profile=StorageProfile(
- os_disk=OSDisk(
- self.storage_blob_name,
- vhd,
- 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'],
- ),
- ),
- network_profile=NetworkProfile(
- network_interfaces=nics
- ),
- )
-
- if self.admin_password:
- vm_resource.os_profile.admin_password = self.admin_password
-
- if self.os_type == 'Linux':
- vm_resource.os_profile.linux_configuration = LinuxConfiguration(
- disable_password_authentication=disable_ssh_password
- )
- if self.ssh_public_keys:
- ssh_config = SshConfiguration()
- ssh_config.public_keys = \
- [SshPublicKey(path=key['path'], key_data=key['key_data']) for key in self.ssh_public_keys]
- vm_resource.os_profile.linux_configuration.ssh = ssh_config
-
- self.log("Create virtual machine with parameters:")
- self.log(self.serialize_obj(vm_resource, 'VirtualMachine'), pretty_print=True)
- self.results['state'] = self.create_or_update_vm(vm_resource)
-
- elif self.differences and len(self.differences) > 0:
- # Update the VM based on detected config differences
-
- self.log("Update virtual machine {0}".format(self.name))
- self.results['actions'].append('Updated VM {0}'.format(self.name))
-
- nics = [NetworkInterfaceReference(id=interface['id'])
- for interface in vm_dict['properties']['networkProfile']['networkInterfaces']]
- vhd = VirtualHardDisk(uri=vm_dict['properties']['storageProfile']['osDisk']['vhd']['uri'])
- vm_resource = VirtualMachine(
- id=vm_dict['id'],
- location=vm_dict['location'],
- name=vm_dict['name'],
- type=vm_dict['type'],
- os_profile=OSProfile(
- admin_username=vm_dict['properties']['osProfile']['adminUsername'],
- computer_name=vm_dict['properties']['osProfile']['computerName']
- ),
- hardware_profile=HardwareProfile(
- vm_size=vm_dict['properties']['hardwareProfile']['vmSize']
- ),
- storage_profile=StorageProfile(
- os_disk=OSDisk(
- vm_dict['properties']['storageProfile']['osDisk']['name'],
- vhd,
- vm_dict['properties']['storageProfile']['osDisk']['createOption'],
- os_type=vm_dict['properties']['storageProfile']['osDisk']['osType'],
- caching=vm_dict['properties']['storageProfile']['osDisk']['caching']
- ),
- image_reference=ImageReference(
- publisher=vm_dict['properties']['storageProfile']['imageReference']['publisher'],
- offer=vm_dict['properties']['storageProfile']['imageReference']['offer'],
- sku=vm_dict['properties']['storageProfile']['imageReference']['sku'],
- version=vm_dict['properties']['storageProfile']['imageReference']['version']
- ),
- ),
- network_profile=NetworkProfile(
- network_interfaces=nics
- ),
- )
-
- if vm_dict.get('tags'):
- vm_resource.tags = vm_dict['tags']
-
- # Add admin password, if one provided
- if vm_dict['properties']['osProfile'].get('adminPassword'):
- vm_resource.os_profile.admin_password = vm_dict['properties']['osProfile']['adminPassword']
-
- # Add linux configuration, if applicable
- linux_config = vm_dict['properties']['osProfile'].get('linuxConfiguration')
- if linux_config:
- ssh_config = linux_config.get('ssh', None)
- vm_resource.os_profile.linux_configuration = LinuxConfiguration(
- disable_password_authentication=linux_config.get('disablePasswordAuthentication', False)
- )
- if ssh_config:
- public_keys = ssh_config.get('publicKeys')
- if public_keys:
- vm_resource.os_profile.linux_configuration.ssh = SshConfiguration(public_keys=[])
- for key in public_keys:
- vm_resource.os_profile.linux_configuration.ssh.public_keys.append(
- SshConfiguration(
- path=key['path'],
- key_data=key['keyData']
- )
- )
- self.log("Update virtual machine with parameters:")
- self.log(self.serialize_obj(vm_resource, 'VirtualMachine'), pretty_print=True)
- self.results['state'] = self.create_or_update_vm(vm_resource)
-
- # Make sure we leave the machine in requested power state
- if powerstate_change == 'poweron' and self.results['state']['powerstate'] != 'running':
- # Attempt to power on the machine
- self.power_on_vm()
- self.results['state'] = self.serialize_vm(self.get_vm())
-
- elif powerstate_change == 'poweroff' and self.results['state']['powerstate'] == 'running':
- # Attempt to power off the machine
- self.power_off_vm()
- self.results['state'] = self.serialize_vm(self.get_vm())
-
- elif powerstate_change == 'restarted':
- self.restart_vm()
- self.results['state'] = self.serialize_vm(self.get_vm())
-
- elif powerstate_change == 'deallocated':
- self.deallocate_vm()
- self.results['state'] = self.serialize_vm(self.get_vm())
-
- elif self.state == 'absent':
- # delete the VM
- self.log("Delete virtual machine {0}".format(self.name))
- self.results['state']['status'] = 'Deleted'
- self.delete_vm(vm)
-
- return self.results
-
- def get_vm(self):
- '''
- Get the VM with expanded instanceView
-
- :return: VirtualMachine object
- '''
- try:
- vm = self.compute_client.virtual_machines.get(self.resource_group, self.name, expand='instanceview')
- return vm
- except Exception as exc:
- self.fail("Error getting virtual machine (0) - {1}".format(self.name, str(exc)))
-
- def serialize_vm(self, vm):
- '''
- Convert a VirtualMachine object to dict.
-
- :param vm: VirtualMachine object
- :return: dict
- '''
- result = self.serialize_obj(vm, AZURE_OBJECT_CLASS)
- result['powerstate'] = next((s.code.replace('PowerState/', '')
- for s in vm.instance_view.statuses if s.code.startswith('PowerState')), None)
-
- # Expand network interfaces to include config properties
- for interface in vm.network_profile.network_interfaces:
- int_dict = azure_id_to_dict(interface.id)
- nic = self.get_network_interface(int_dict['networkInterfaces'])
- for interface_dict in result['properties']['networkProfile']['networkInterfaces']:
- if interface_dict['id'] == interface.id:
- nic_dict = self.serialize_obj(nic, 'NetworkInterface')
- interface_dict['name'] = int_dict['networkInterfaces']
- interface_dict['properties'] = nic_dict['properties']
-
- # Expand public IPs to include config porperties
- for interface in result['properties']['networkProfile']['networkInterfaces']:
- for config in interface['properties']['ipConfigurations']:
- if config['properties'].get('publicIPAddress'):
- pipid_dict = azure_id_to_dict(config['properties']['publicIPAddress']['id'])
- try:
- pip = self.network_client.public_ip_addresses.get(self.resource_group,
- pipid_dict['publicIPAddresses'])
- except Exception as exc:
- self.fail("Error fetching public ip {0} - {1}".format(pipid_dict['publicIPAddresses'],
- str(exc)))
- pip_dict = self.serialize_obj(pip, 'PublicIPAddress')
- config['properties']['publicIPAddress']['name'] = pipid_dict['publicIPAddresses']
- config['properties']['publicIPAddress']['properties'] = pip_dict['properties']
-
- self.log(result, pretty_print=True)
- if self.state != 'absent' and not result['powerstate']:
- self.fail("Failed to determine PowerState of virtual machine {0}".format(self.name))
- return result
-
- 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)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error powering off virtual machine {0} - {1}".format(self.name, str(exc)))
- return True
-
- 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)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error powering on virtual machine {0} - {1}".format(self.name, str(exc)))
- return True
-
- 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)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error restarting virtual machine {0} - {1}".format(self.name, str(exc)))
- return True
-
- 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)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error deallocating virtual machine {0} - {1}".format(self.name, str(exc)))
- return True
-
- def delete_vm(self, vm):
- vhd_uris = []
- nic_names = []
- pip_names = []
-
- if self.delete_virtual_storage:
- # store the attached vhd info so we can nuke it after the VM is gone
- self.log('Storing VHD URI for deletion')
- vhd_uris.append(vm.storage_profile.os_disk.vhd.uri)
- self.log("VHD URIs to delete: {0}".format(', '.join(vhd_uris)))
- self.results['deleted_vhd_uris'] = vhd_uris
-
- if self.delete_network_interfaces:
- # store the attached nic info so we can nuke them after the VM is gone
- self.log('Storing NIC names for deletion.')
- for interface in vm.network_profile.network_interfaces:
- id_dict = azure_id_to_dict(interface.id)
- nic_names.append(id_dict['networkInterfaces'])
- self.log('NIC names to delete {0}'.format(', '.join(nic_names)))
- self.results['deleted_network_interfaces'] = nic_names
- if self.delete_public_ips:
- # also store each nic's attached public IPs and delete after the NIC is gone
- for name in nic_names:
- nic = self.get_network_interface(name)
- for ipc in nic.ip_configurations:
- if ipc.public_ip_address:
- pip_dict = azure_id_to_dict(ipc.public_ip_address.id)
- pip_names.append(pip_dict['publicIPAddresses'])
- self.log('Public IPs to delete are {0}'.format(', '.join(pip_names)))
- self.results['deleted_public_ips'] = pip_names
-
- 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)
- # wait for the poller to finish
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error deleting virtual machine {0} - {1}".format(self.name, str(exc)))
-
- # TODO: parallelize nic, vhd, and public ip deletions with begin_deleting
- # TODO: best-effort to keep deleting other linked resources if we encounter an error
- if self.delete_virtual_storage:
- self.log('Deleting virtual storage')
- self.delete_vm_storage(vhd_uris)
-
- if self.delete_network_interfaces:
- self.log('Deleting network interfaces')
- for name in nic_names:
- self.delete_nic(name)
-
- if self.delete_public_ips:
- self.log('Deleting public IPs')
- for name in pip_names:
- self.delete_pip(name)
- return True
-
- def get_network_interface(self, name):
- try:
- nic = self.network_client.network_interfaces.get(self.resource_group, name)
- return nic
- except Exception as exc:
- self.fail("Error fetching network interface {0} - {1}".format(name, str(exc)))
-
- def delete_nic(self, name):
- self.log("Deleting network interface {0}".format(name))
- self.results['actions'].append("Deleted network interface {0}".format(name))
- try:
- poller = self.network_client.network_interfaces.delete(self.resource_group, name)
- except Exception as exc:
- self.fail("Error deleting network interface {0} - {1}".format(name, str(exc)))
- self.get_poller_result(poller)
- # Delete doesn't return anything. If we get this far, assume success
- return True
-
- def delete_pip(self, name):
- self.results['actions'].append("Deleted public IP {0}".format(name))
- try:
- poller = self.network_client.public_ip_addresses.delete(self.resource_group, name)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error deleting {0} - {1}".format(name, str(exc)))
- # Delete returns nada. If we get here, assume that all is well.
- return True
-
- def delete_vm_storage(self, vhd_uris):
- for uri in vhd_uris:
- self.log("Extracting info from blob uri '{0}'".format(uri))
- try:
- blob_parts = extract_names_from_blob_uri(uri)
- except Exception as exc:
- self.fail("Error parsing blob URI {0}".format(str(exc)))
- storage_account_name = blob_parts['accountname']
- container_name = blob_parts['containername']
- blob_name = blob_parts['blobname']
-
- blob_client = self.get_blob_client(self.resource_group, storage_account_name)
-
- self.log("Delete blob {0}:{1}".format(container_name, blob_name))
- self.results['actions'].append("Deleted blob {0}:{1}".format(container_name, blob_name))
- try:
- blob_client.delete_blob(container_name, blob_name)
- except Exception as exc:
- self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc)))
-
- def get_image_version(self):
- try:
- versions = self.compute_client.virtual_machine_images.list(self.location,
- self.image['publisher'],
- self.image['offer'],
- self.image['sku'])
- except Exception as exc:
- self.fail("Error fetching image {0} {1} {2} - {4}".format(self.image['publisher'],
- self.image['offer'],
- self.image['sku'],
- str(exc)))
- if versions and len(versions) > 0:
- if self.image['version'] == 'latest':
- return versions[len(versions) - 1]
- for version in versions:
- if version.name == self.image['version']:
- return version
-
- self.fail("Error could not find image {0} {1} {2} {3}".format(self.image['publisher'],
- self.image['offer'],
- self.image['sku'],
- self.image['version']))
-
- def get_storage_account(self, name):
- try:
- account = self.storage_client.storage_accounts.get_properties(self.resource_group,
- name)
- return account
- except Exception as exc:
- self.fail("Error fetching storage account {0} - {1}".format(self.storage_account_name, str(exc)))
-
- def create_or_update_vm(self, params):
- try:
- poller = self.compute_client.virtual_machines.create_or_update(self.resource_group, self.name, params)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error creating or updating virtual machine {0} - {1}".format(self.name, str(exc)))
- return self.serialize_vm(self.get_vm())
-
- def vm_size_is_valid(self):
- '''
- Validate self.vm_size against the list of virtual machine sizes available for the account and location.
-
- :return: boolean
- '''
- try:
- sizes = self.compute_client.virtual_machine_sizes.list(self.location)
- except Exception as exc:
- self.fail("Error retrieving available machine sizes - {0}".format(str(exc)))
- for size in sizes:
- if size.name == self.vm_size:
- return True
- return False
-
- def create_default_storage_account(self):
- '''
- Create a default storage account <vm name>XXXX, where XXXX is a random number. If <vm name>XXXX exists, use it.
- Otherwise, create one.
-
- :return: storage account object
- '''
- account = None
- valid_name = False
-
- # Attempt to find a valid storage account name
- for i in range(0, 5):
- rand = random.randrange(1000, 9999)
- storage_account_name = self.name[:20] + str(rand)
- if self.check_storage_account_name(storage_account_name):
- valid_name = True
- break
-
- if not valid_name:
- self.fail("Failed to create a unique storage account name for {0}. Try using a different VM name."
- .format(self.name))
-
- try:
- account = self.storage_client.storage_accounts.get_properties(self.resource_group, storage_account_name)
- except CloudError:
- pass
-
- if account:
- self.log("Storage account {0} found.".format(storage_account_name))
- self.check_provisioning_state(account)
- return account
-
- parameters = StorageAccountCreateParameters(account_type='Standard_LRS', location=self.location)
- 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)
- self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Failed to create storage account: {0} - {1}".format(storage_account_name, str(exc)))
- return self.get_storage_account(storage_account_name)
-
- 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)
- except Exception as exc:
- self.fail("Error checking storage account name availability for {0} - {1}".format(name, str(exc)))
- return response.name_available
-
- def create_default_nic(self):
- '''
- Create a default Network Interface <vm name>01. Requires an existing virtual network
- with one subnet. If NIC <vm name>01 exists, use it. Otherwise, create one.
-
- :return: NIC object
- '''
-
- network_interface_name = self.name + '01'
- nic = None
-
- self.log("Create default NIC {0}".format(network_interface_name))
- self.log("Check to see if NIC {0} exists".format(network_interface_name))
- try:
- nic = self.network_client.network_interfaces.get(self.resource_group, network_interface_name)
- except CloudError:
- pass
-
- if nic:
- self.log("NIC {0} found.".format(network_interface_name))
- self.check_provisioning_state(nic)
- return nic
-
- self.log("NIC {0} does not exist.".format(network_interface_name))
-
- if self.virtual_network_name:
- try:
- self.network_client.virtual_networks.list(self.resource_group, self.virtual_network_name)
- virtual_network_name = self.virtual_network_name
- except Exception as exc:
- self.fail("Error: fetching virtual network {0} - {1}".format(self.virtual_network_name, str(exc)))
- else:
- # Find a virtual network
- no_vnets_msg = "Error: unable to find virtual network in resource group {0}. A virtual network " \
- "with at least one subnet must exist in order to create a NIC for the virtual " \
- "machine.".format(self.resource_group)
-
- virtual_network_name = None
- try:
- vnets = self.network_client.virtual_networks.list(self.resource_group)
- except CloudError:
- self.log('cloud error!')
- self.fail(no_vnets_msg)
-
- for vnet in vnets:
- virtual_network_name = vnet.name
- self.log('vnet name: {0}'.format(vnet.name))
- break
-
- if not virtual_network_name:
- self.fail(no_vnets_msg)
-
- if self.subnet_name:
- try:
- subnet = self.network_client.subnets.get(self.resource_group, virtual_network_name)
- subnet_id = subnet.id
- except Exception 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 " \
- "with at least one subnet must exist in order to create a NIC for the virtual " \
- "machine.".format(virtual_network_name)
-
- subnet_id = None
- try:
- subnets = self.network_client.subnets.list(self.resource_group, virtual_network_name)
- except CloudError:
- self.fail(no_subnets_msg)
-
- for subnet in subnets:
- subnet_id = subnet.id
- self.log('subnet id: {0}'.format(subnet_id))
- break
-
- if not subnet_id:
- self.fail(no_subnets_msg)
-
- self.results['actions'].append('Created default public IP {0}'.format(self.name + '01'))
- pip = self.create_default_pip(self.resource_group, self.location, self.name, self.public_ip_allocation_method)
-
- self.results['actions'].append('Created default security group {0}'.format(self.name + '01'))
- group = self.create_default_securitygroup(self.resource_group, self.location, self.name, self.os_type,
- self.open_ports)
-
- parameters = NetworkInterface(
- location=self.location,
- name=network_interface_name,
- ip_configurations=[
- NetworkInterfaceIPConfiguration(
- name='default',
- private_ip_allocation_method='Dynamic',
- )
- ]
- )
- parameters.ip_configurations[0].subnet = Subnet(id=subnet_id)
- parameters.network_security_group = NetworkSecurityGroup(id=group.id,
- name=group.name,
- location=group.location,
- resource_guid=group.resource_guid)
- parameters.ip_configurations[0].public_ip_address = PublicIPAddress(id=pip.id,
- name=pip.name,
- location=pip.location,
- resource_guid=pip.resource_guid)
-
- self.log("Creating NIC {0}".format(network_interface_name))
- self.log(self.serialize_obj(parameters, 'NetworkInterface'), pretty_print=True)
- self.results['actions'].append("Created NIC {0}".format(network_interface_name))
- try:
- poller = self.network_client.network_interfaces.create_or_update(self.resource_group,
- network_interface_name,
- parameters)
- new_nic = self.get_poller_result(poller)
- except Exception as exc:
- self.fail("Error creating network interface {0} - {1}".format(network_interface_name, str(exc)))
- return new_nic
-
-
-def main():
- AzureRMVirtualMachine()
-
-if __name__ == '__main__':
- main()
-