azure_rm_virtualmachine: add custom image support (#32367)
* azure_rm_virtualmachine: added support for specifying custom image * Use separate parameter for custom_image, add very basic test * missed the version_added tag for doco * removed whitespace I accidentally left in * merged custom image into the image dict and added more tests * added one more test
This commit is contained in:
parent
85f727ad4b
commit
abc4210a33
2 changed files with 132 additions and 28 deletions
|
@ -92,9 +92,19 @@ options:
|
||||||
/home/<admin username>/.ssh/authorized_keys. Set key_data to the actual value of the public key."
|
/home/<admin username>/.ssh/authorized_keys. Set key_data to the actual value of the public key."
|
||||||
image:
|
image:
|
||||||
description:
|
description:
|
||||||
- "A dictionary describing the Marketplace image used to build the VM. Will contain keys: publisher,
|
- Specifies the image used to build the VM.
|
||||||
offer, sku and version. NOTE: set image.version to 'latest' to get the most recent version of a given
|
- If a string, the image is sourced from a custom image based on the
|
||||||
image."
|
name.
|
||||||
|
- 'If a dict with the keys C(publisher), C(offer), C(sku), and
|
||||||
|
C(version), the image is sourced from a Marketplace image. NOTE:
|
||||||
|
set image.version to C(latest) to get the most recent version of a
|
||||||
|
given image.'
|
||||||
|
- 'If a dict with the keys C(name) and C(resource_group), the image
|
||||||
|
is sourced from a custom image based on the C(name) and
|
||||||
|
C(resource_group) set. NOTE: the key C(resource_group) is optional
|
||||||
|
and if omitted, all images in the subscription will be search for
|
||||||
|
by C(name).'
|
||||||
|
- Custom image support was added in Ansible 2.5
|
||||||
required: true
|
required: true
|
||||||
storage_account_name:
|
storage_account_name:
|
||||||
description:
|
description:
|
||||||
|
@ -344,10 +354,10 @@ EXAMPLES = '''
|
||||||
storage_container: osdisk
|
storage_container: osdisk
|
||||||
storage_blob: osdisk.vhd
|
storage_blob: osdisk.vhd
|
||||||
image:
|
image:
|
||||||
offer: CoreOS
|
offer: CoreOS
|
||||||
publisher: CoreOS
|
publisher: CoreOS
|
||||||
sku: Stable
|
sku: Stable
|
||||||
version: latest
|
version: latest
|
||||||
data_disks:
|
data_disks:
|
||||||
- lun: 0
|
- lun: 0
|
||||||
disk_size_gb: 64
|
disk_size_gb: 64
|
||||||
|
@ -358,6 +368,26 @@ EXAMPLES = '''
|
||||||
storage_container_name: datadisk2
|
storage_container_name: datadisk2
|
||||||
storage_blob_name: datadisk2.vhd
|
storage_blob_name: datadisk2.vhd
|
||||||
|
|
||||||
|
- name: Create a VM with a custom image
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: Testing
|
||||||
|
name: testvm001
|
||||||
|
vm_size: Standard_DS1_v2
|
||||||
|
admin_username: adminUser
|
||||||
|
admin_password: password01
|
||||||
|
image: customimage001
|
||||||
|
|
||||||
|
- name: Create a VM with a custom image from a particular resource group
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: Testing
|
||||||
|
name: testvm001
|
||||||
|
vm_size: Standard_DS1_v2
|
||||||
|
admin_username: adminUser
|
||||||
|
admin_password: password01
|
||||||
|
image:
|
||||||
|
name: customimage001
|
||||||
|
resource_group: Testing
|
||||||
|
|
||||||
- name: Power Off
|
- name: Power Off
|
||||||
azure_rm_virtualmachine:
|
azure_rm_virtualmachine:
|
||||||
resource_group: Testing
|
resource_group: Testing
|
||||||
|
@ -606,7 +636,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
admin_password=dict(type='str', no_log=True),
|
admin_password=dict(type='str', no_log=True),
|
||||||
ssh_password_enabled=dict(type='bool', default=True),
|
ssh_password_enabled=dict(type='bool', default=True),
|
||||||
ssh_public_keys=dict(type='list'),
|
ssh_public_keys=dict(type='list'),
|
||||||
image=dict(type='dict'),
|
image=dict(type='raw'),
|
||||||
storage_account_name=dict(type='str', aliases=['storage_account']),
|
storage_account_name=dict(type='str', aliases=['storage_account']),
|
||||||
storage_container_name=dict(type='str', aliases=['storage_container'], default='vhds'),
|
storage_container_name=dict(type='str', aliases=['storage_container'], default='vhds'),
|
||||||
storage_blob_name=dict(type='str', aliases=['storage_blob']),
|
storage_blob_name=dict(type='str', aliases=['storage_blob']),
|
||||||
|
@ -689,6 +719,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
data_disk_requested_vhd_uri = None
|
data_disk_requested_vhd_uri = None
|
||||||
disable_ssh_password = None
|
disable_ssh_password = None
|
||||||
vm_dict = None
|
vm_dict = None
|
||||||
|
image_reference = None
|
||||||
|
custom_image = False
|
||||||
|
|
||||||
resource_group = self.get_resource_group(self.resource_group)
|
resource_group = self.get_resource_group(self.resource_group)
|
||||||
if not self.location:
|
if not self.location:
|
||||||
|
@ -717,14 +749,31 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
if not key.get('path') or not key.get('key_data'):
|
if not key.get('path') or not key.get('key_data'):
|
||||||
self.fail(msg)
|
self.fail(msg)
|
||||||
|
|
||||||
if self.image:
|
if self.image and isinstance(self.image, dict):
|
||||||
if not self.image.get('publisher') or not self.image.get('offer') or not self.image.get('sku') \
|
if all(key in self.image for key in ('publisher', 'offer', 'sku', 'version')):
|
||||||
or not self.image.get('version'):
|
marketplace_image = self.get_marketplace_image_version()
|
||||||
self.fail("parameter error: expecting image to contain publisher, offer, sku and version keys.")
|
if self.image['version'] == 'latest':
|
||||||
image_version = self.get_image_version()
|
self.image['version'] = marketplace_image.name
|
||||||
if self.image['version'] == 'latest':
|
self.log("Using image version {0}".format(self.image['version']))
|
||||||
self.image['version'] = image_version.name
|
|
||||||
self.log("Using image version {0}".format(self.image['version']))
|
image_reference = ImageReference(
|
||||||
|
publisher=self.image['publisher'],
|
||||||
|
offer=self.image['offer'],
|
||||||
|
sku=self.image['sku'],
|
||||||
|
version=self.image['version']
|
||||||
|
)
|
||||||
|
elif self.image.get('name'):
|
||||||
|
custom_image = True
|
||||||
|
image_reference = self.get_custom_image_reference(
|
||||||
|
self.image.get('name'),
|
||||||
|
self.image.get('resource_group'))
|
||||||
|
else:
|
||||||
|
self.fail("parameter error: expecting image to contain [publisher, offer, sku, version] or [name, resource_group]")
|
||||||
|
elif self.image and isinstance(self.image, str):
|
||||||
|
custom_image = True
|
||||||
|
image_reference = self.get_custom_image_reference(self.image)
|
||||||
|
elif self.image:
|
||||||
|
self.fail("parameter error: expecting image to be a string or dict not {0}".format(type(self.image).__name__))
|
||||||
|
|
||||||
if self.plan:
|
if self.plan:
|
||||||
if not self.plan.get('name') or not self.plan.get('product') or not self.plan.get('publisher'):
|
if not self.plan.get('name') or not self.plan.get('product') or not self.plan.get('publisher'):
|
||||||
|
@ -841,7 +890,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
if disable_ssh_password and not self.ssh_public_keys:
|
if disable_ssh_password and not self.ssh_public_keys:
|
||||||
self.fail("Parameter error: ssh_public_keys required when disabling SSH password.")
|
self.fail("Parameter error: ssh_public_keys required when disabling SSH password.")
|
||||||
|
|
||||||
if not self.image:
|
if not image_reference:
|
||||||
self.fail("Parameter error: an image is required when creating a virtual machine.")
|
self.fail("Parameter error: an image is required when creating a virtual machine.")
|
||||||
|
|
||||||
# Get defaults
|
# Get defaults
|
||||||
|
@ -869,12 +918,15 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
nics = [NetworkInterfaceReference(id=id) for id in network_interfaces]
|
nics = [NetworkInterfaceReference(id=id) for id in network_interfaces]
|
||||||
|
|
||||||
# os disk
|
# os disk
|
||||||
if not self.managed_disk_type:
|
if self.managed_disk_type:
|
||||||
managed_disk = None
|
|
||||||
vhd = VirtualHardDisk(uri=requested_vhd_uri)
|
|
||||||
else:
|
|
||||||
vhd = None
|
vhd = None
|
||||||
managed_disk = ManagedDiskParameters(storage_account_type=self.managed_disk_type)
|
managed_disk = ManagedDiskParameters(storage_account_type=self.managed_disk_type)
|
||||||
|
elif custom_image:
|
||||||
|
vhd = None
|
||||||
|
managed_disk = None
|
||||||
|
else:
|
||||||
|
vhd = VirtualHardDisk(uri=requested_vhd_uri)
|
||||||
|
managed_disk = None
|
||||||
|
|
||||||
plan = None
|
plan = None
|
||||||
if self.plan:
|
if self.plan:
|
||||||
|
@ -899,12 +951,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
create_option=DiskCreateOptionTypes.from_image,
|
create_option=DiskCreateOptionTypes.from_image,
|
||||||
caching=self.os_disk_caching,
|
caching=self.os_disk_caching,
|
||||||
),
|
),
|
||||||
image_reference=ImageReference(
|
image_reference=image_reference,
|
||||||
publisher=self.image['publisher'],
|
|
||||||
offer=self.image['offer'],
|
|
||||||
sku=self.image['sku'],
|
|
||||||
version=self.image['version'],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
network_profile=NetworkProfile(
|
network_profile=NetworkProfile(
|
||||||
network_interfaces=nics
|
network_interfaces=nics
|
||||||
|
@ -1349,7 +1396,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc)))
|
self.fail("Error deleting blob {0}:{1} - {2}".format(container_name, blob_name, str(exc)))
|
||||||
|
|
||||||
def get_image_version(self):
|
def get_marketplace_image_version(self):
|
||||||
try:
|
try:
|
||||||
versions = self.compute_client.virtual_machine_images.list(self.location,
|
versions = self.compute_client.virtual_machine_images.list(self.location,
|
||||||
self.image['publisher'],
|
self.image['publisher'],
|
||||||
|
@ -1372,6 +1419,22 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
|
||||||
self.image['sku'],
|
self.image['sku'],
|
||||||
self.image['version']))
|
self.image['version']))
|
||||||
|
|
||||||
|
def get_custom_image_reference(self, name, resource_group=None):
|
||||||
|
try:
|
||||||
|
if resource_group:
|
||||||
|
vm_images = self.compute_client.images.list_by_resource_group(resource_group)
|
||||||
|
else:
|
||||||
|
vm_images = self.compute_client.images.list()
|
||||||
|
except Exception as exc:
|
||||||
|
self.fail("Error fetching custom images from subscription - {0}".format(str(exc)))
|
||||||
|
|
||||||
|
for vm_image in vm_images:
|
||||||
|
if vm_image.name == name:
|
||||||
|
self.log("Using custom image id {0}".format(vm_image.id))
|
||||||
|
return ImageReference(id=vm_image.id)
|
||||||
|
|
||||||
|
self.fail("Error could not find image with name {0}".format(name))
|
||||||
|
|
||||||
def get_storage_account(self, name):
|
def get_storage_account(self, name):
|
||||||
try:
|
try:
|
||||||
account = self.storage_client.storage_accounts.get_properties(self.resource_group,
|
account = self.storage_client.storage_accounts.get_properties(self.resource_group,
|
||||||
|
|
|
@ -155,3 +155,44 @@
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that: azure_publicipaddresses | length == 0
|
that: azure_publicipaddresses | length == 0
|
||||||
|
|
||||||
|
# TODO: Until we have a module to create/delete images this is the best tests
|
||||||
|
# I can do
|
||||||
|
- name: assert error thrown with invalid image dict
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: testvm002
|
||||||
|
state: present
|
||||||
|
image:
|
||||||
|
offer: UbuntuServer
|
||||||
|
register: fail_invalid_image_dict
|
||||||
|
failed_when: 'fail_invalid_image_dict.msg != "parameter error: expecting image to contain [publisher, offer, sku, version] or [name, resource_group]"'
|
||||||
|
|
||||||
|
- name: assert error thrown with invalid image type
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: testvm002
|
||||||
|
state: present
|
||||||
|
image:
|
||||||
|
- testing
|
||||||
|
register: fail_invalid_image_type
|
||||||
|
failed_when: 'fail_invalid_image_type.msg != "parameter error: expecting image to be a string or dict not list"'
|
||||||
|
|
||||||
|
- name: assert error finding missing custom image
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: testvm002
|
||||||
|
state: present
|
||||||
|
image: invalid-image
|
||||||
|
register: fail_missing_custom_image
|
||||||
|
failed_when: fail_missing_custom_image.msg != "Error could not find image with name invalid-image"
|
||||||
|
|
||||||
|
- name: assert error finding missing custom image (dict style)
|
||||||
|
azure_rm_virtualmachine:
|
||||||
|
resource_group: "{{ resource_group }}"
|
||||||
|
name: testvm002
|
||||||
|
state: present
|
||||||
|
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"
|
Loading…
Reference in a new issue