lib/ansible/modules/cloud/alicloud/ali_image.py (307 lines of code) (raw):
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017-present Alibaba Group Holding Limited. He Guimin <heguimin36@163.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# 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/.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: ali_image
short_description: Create or delete user-defined image
description:
- Create user-defined image from ECS instance, snapshot or disk_mapping; delete user-defined image
options:
state:
description:
- create or delete user-defined image
default: 'present'
choices: ['present', 'absent']
type: str
instance_id:
description:
- The ECS instance ID. A custom image is created from the specified instance.
aliases: ['instance']
type: str
snapshot_id:
description:
- The system disk snapshot ID. A custom image is created from the specified snapshot.
required: true
aliases: ['snapshot']
type: str
image_name:
description:
- The name of the image, [2, 128] English or Chinese characters.
It must begin with an uppercase/lowercase letter or a Chinese character, and may contain numbers, "_" or "-".
It cannot begin with http:// or https://.
aliases: ['name']
type: str
description:
description:
- The description of the image, with a length limit of [0, 256] characters.
Leaving it blank means null, which is the default value.
It cannot begin with http:// or https://.
type: str
image_version:
description:
- The version number of the image, with a length limit of [1, 40] English characters.
aliases: ['version']
type: str
disk_mapping:
description:
- An optional list of device hashes/dictionaries with multiple snapshots configurations.
It can combine multiple snapshots to be an image template.
suboptions:
snapshot_id:
description:
- The snapshot ID. Only one system disk's snapshot can be specified
disk_size:
description:
- Size of the disk, in the range [5-2000GB]
type: list
elements: dict
wait:
description:
- wait for the Image to be available.
type: bool
default: False
wait_timeout:
description:
- how long before wait gives up, in seconds
default: 300
type: int
image_id:
description:
- Image ID to be deregistered.
type: str
requirements:
- "python >= 3.6"
- "footmark >= 1.15.0"
extends_documentation_fragment:
- alicloud
author:
- "He Guimin (@xiaozhu36)"
'''
EXAMPLES = '''
# basic provisioning example to create image using ecs instance
- name: Create image form ecs instance
ali_image:
instance_id: '{{ instance_id }}'
register: result
- name: Create image using snapshot
ali_image:
snapshot_id: '{{ snapshot_id }}'
- name: Create image using disk mapping
ali_image:
disk_mapping: '{{ disk_mapping }}'
- name: Create image with disk mapping and version
ali_image:
image_name: '{{ image_name }}'
image_version: '{{ image_version }}'
description: '{{ description }}'
disk_mapping: '{{ disk_mapping }}'
wait: '{{ wait }}'
wait_timeout: '{{ wait_timeout }}'
- name: delete image
ali_image:
image_id: '{{ image_id }}'
state: 'absent'
'''
RETURN = '''
image:
description: Details about the image that was created.
returned: except on delete
type: dict
sample: {
"disk_device_mappings": {
"disk_device_mapping": [
{
"device": "/dev/xvda",
"format": "",
"import_ossbucket": "",
"import_ossobject": "",
"size": "40",
"snapshot_id": "s-2zeddnvf7uhw3xw3its6",
"type": "system"
}
]
},
"id": "m-2zee2i7regbnhhawrwc2",
"image_name": "image_test",
"launch_time": "2017-10-17T09:33:02Z",
"platform": "CentOS",
"region_id": "cn-beijing",
"size": 40,
"status": "Available"
}
image_id:
description: return the created image id
returned: except on absent
type: str
sample: "m-2ze43t8c5urc4mvkx3fp"
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.alicloud_ecs import ecs_argument_spec, ecs_connect
HAS_FOOTMARK = False
try:
from footmark.exception import ECSResponseError
HAS_FOOTMARK = True
except ImportError:
HAS_FOOTMARK = False
def get_image_detail(image):
"""
Method call to get image details
:param image: Image object to Describe
:return: return id, status and object of disk
"""
if image:
return {'id': image.image_id,
'image_name': image.image_name,
'size': image.size,
'region_id': image.region,
'disk_device_mappings': image.disk_device_mappings,
'status': image.status,
'platform': image.platform,
'launch_time': image.creation_time
}
def create_image(module, ecs, snapshot_id, image_name, image_version, description, instance_id,
disk_mapping, client_token, wait, wait_timeout):
"""
Create a user-defined image with snapshots.
:param module: Ansible module object
:param ecs: authenticated ecs connection object
:param snapshot_id: A user-defined image is created from the specified snapshot.
:param image_name: image name which is to be created
:param image_version: version of image
:param description: description of the image
:param instance_id: the specified instance_id
:param disk_mapping: list relating device and disk
:param client_token: Used to ensure the idempotence of the request
:param wait: Used to indicate wait for instance to be running before running
:param wait_timeout: Used to indicate how long to wait, default 300
:return: id of image
"""
changed = False
if image_name:
if len(image_name) < 2 or len(image_name) > 128:
module.fail_json(msg='image_name must be 2 - 128 characters long')
if image_name.startswith('http://') or image_name.startswith('https://'):
module.fail_json(msg='image_name can not start with http:// or https://')
if image_version:
if image_version.isdigit():
if int(image_version) < 1 or int(image_version) > 40:
module.fail_json(msg='The permitted range of image_version is between 1 - 40')
else:
module.fail_json(msg='The permitted range of image_version is between 1 - 40, entered value is {0}'
.format(image_version))
if disk_mapping:
for mapping in disk_mapping:
if mapping:
if 'snapshot_id' not in mapping:
module.fail_json(msg='The snapshot_id of system disk is needed for disk mapping.')
if not('disk_size' in mapping or 'snapshot_id' in mapping):
module.fail_json(msg='The disk_size and snapshot_id parameters '
'are valid for disk mapping.')
if 'disk_size' in mapping:
map_disk = mapping['disk_size']
if map_disk:
if str(map_disk).isdigit():
if int(map_disk) < 5 or int(map_disk) > 2000:
module.fail_json(msg='The permitted range of disk-size is 5 GB - 2000 GB ')
else:
module.fail_json(msg='The disk_size must be an integer value, entered value is {0}'.format(
map_disk))
if not snapshot_id and not instance_id and not disk_mapping:
module.fail_json(msg='Either of SnapshotId or InstanceId or disk_mapping, must be present for '
'create image operation to get performed')
if (snapshot_id and instance_id) or (snapshot_id and disk_mapping) or (instance_id and disk_mapping):
module.fail_json(msg='Only 1 of SnapshotId or InstanceId or disk_mapping, must be present for '
'create image operation to get performed')
# call to create_image method in footmark
try:
changed, image_id, results, request_id = ecs.create_image(snapshot_id=snapshot_id, image_name=image_name,
image_version=image_version, description=description,
instance_id=instance_id, disk_mapping=disk_mapping,
client_token=client_token, wait=wait,
wait_timeout=wait_timeout)
if 'error code' in str(results).lower():
module.fail_json(changed=changed, msg=results)
except ECSResponseError as e:
module.fail_json(msg='Unable to create image, error: {0}'.format(e))
return changed, image_id, results, request_id
def main():
argument_spec = ecs_argument_spec()
argument_spec.update(dict(
image_id=dict(type='str'),
snapshot_id=dict(type='str', aliases=['snapshot']),
description=dict(type='str'),
image_name=dict(type='str', aliases=['name']),
image_version=dict(type='str', aliases=['version']),
disk_mapping=dict(type='list', elements='dict'),
instance_id=dict(aliases=['instance']),
state=dict(default='present', choices=['present', 'absent'], type='str'),
wait=dict(default=False, type='bool'),
wait_timeout=dict(type='int', default=300)
))
module = AnsibleModule(argument_spec=argument_spec)
if HAS_FOOTMARK is False:
module.fail_json(msg='footmark required for the module alicloud_security_group.')
ecs = ecs_connect(module)
state = module.params['state']
image_id = module.params['image_id']
image_name = module.params['image_name']
changed = False
current_image = None
try:
if image_id:
images = ecs.get_all_images(image_id=image_id)
if images and len(images) == 1:
current_image = images[0]
elif image_name and state == 'absent':
images = ecs.get_all_images(image_name=image_name)
if images:
if len(images) == 1:
current_image = images[0]
else:
images_ids = []
for i in images:
images_ids.append(i.id)
module.fail_json(msg="There is too many images match name '{0}', "
"please use image_id or a new image_name to specify a unique image."
"Matched images ids are: {1}".format(image_name, images_ids))
elif state == 'absent':
images = ecs.get_all_images()
if images and len(images) > 0:
current_image = images[0]
except ECSResponseError as e:
module.fail_json(msg='Error in get_all_images: %s' % str(e))
if state == 'absent':
if not current_image:
module.fail_json(msg="Please use valid image_id or image_name to specify one image for deleting.")
try:
changed_img = current_image.delete()
module.exit_json(changed=changed_img, image_id=current_image.id, image=get_image_detail(current_image))
except Exception as e:
module.fail_json(msg='Deleting a image is failed, error: {0}'.format(e))
if not current_image:
snapshot_id = module.params['snapshot_id']
image_version = module.params['image_version']
description = module.params['description']
disk_mapping = module.params['disk_mapping']
instance_id = module.params['instance_id']
wait = module.params['wait']
wait_timeout = module.params['wait_timeout']
try:
# Calling create_image method
client_token = "Ansible-Alicloud-%s-%s" % (hash(str(module.params)), str(time.time()))
changed, image_id, results, request_id = create_image(module=module, ecs=ecs, snapshot_id=snapshot_id,
image_name=image_name, image_version=image_version,
description=description, instance_id=instance_id,
disk_mapping=disk_mapping, client_token=client_token,
wait=wait, wait_timeout=wait_timeout)
images = ecs.get_all_images(image_id=image_id)
if images:
if len(images) == 1:
current_image = images[0]
module.exit_json(changed=changed, image_id=image_id, image=get_image_detail(current_image) )
except Exception as e:
module.fail_json(msg='Creating a new image is failed, error: {0}'.format(e))
if __name__ == '__main__':
main()