Skip to content

Commit

Permalink
Add an ec2_snapshot_import module
Browse files Browse the repository at this point in the history
This uses the boto3 import_snapshot function to import a disk image from
s3 as a snapshot. It also allows setting name:value tags on the new
snapshot.
  • Loading branch information
bcl committed Sep 23, 2019
1 parent f6a51b3 commit 91ead5c
Showing 1 changed file with 265 additions and 0 deletions.
265 changes: 265 additions & 0 deletions lib/ansible/modules/cloud/amazon/ec2_snapshot_import.py
@@ -0,0 +1,265 @@
#!/usr/bin/python
# Copyright (C) 2019 Red Hat, Inc.
# 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: ec2_snapshot_import
short_description: Imports a disk into an EBS snapshot
description:
- Imports a disk into an EBS snapshot
version_added: "2.10"
options:
description:
description:
- description of the import snapshot task
required: false
type: str
format:
description:
- The format of the disk image being imported.
required: true
type: str
url:
description:
- The URL to the Amazon S3-based disk image being imported. It can either be a https URL (https://..) or an Amazon S3 URL (s3://..).
Either C(url) or C(s3_bucket) and C(s3_key) are required.
required: false
type: str
s3_bucket:
description:
- The name of the S3 bucket where the disk image is located.
- C(s3_bucket) and C(s3_key) are required together if C(url) is not used.
required: false
type: str
s3_key:
description:
- The file name of the disk image.
- C(s3_bucket) and C(s3_key) are required together if C(url) is not used.
required: false
type: str
encrypted:
description:
- Whether or not the destination Snapshot should be encrypted.
type: bool
default: 'no'
kms_key_id:
description:
- KMS key id used to encrypt snapshot. If not specified, defaults to EBS Customer Master Key (CMK) for that account.
required: false
type: str
role_name:
description:
- The name of the role to use when not using the default role, 'vmimport'.
required: false
type: str
wait:
description:
- wait for the snapshot to be ready
type: bool
required: false
default: yes
wait_timeout:
description:
- how long before wait gives up, in seconds
- specify 0 to wait forever
required: false
type: int
default: 900
tags:
description:
- A hash/dictionary of tags to add to the new Snapshot; '{"key":"value"}' and '{"key":"value","key":"value"}'
required: false
type: dict
sample:
Name: Snapshot-Name
author: "Brian C. Lane (@bcl)"
extends_documentation_fragment:
- aws
- ec2
'''

EXAMPLES = '''
# Import an S3 object as a snapshot
ec2_snapshot_import:
description: simple-http-server
format: raw
s3_bucket: mybucket
s3_key: server-image.ami
wait: yes
tags:
Name: Snapshot-Name
'''

RETURN = '''
snapshot_id:
description: id of the created snapshot
returned: when snapshot is created
type: str
sample: "snap-1234abcd"
description:
description: description of snapshot
returned: when snapshot is created
type: str
sample: "simple-http-server"
format:
description: format of the disk image being imported
returned: when snapshot is created
type: str
sample: "raw"
disk_image_size:
description: size of the disk image being imported, in bytes.
returned: when snapshot is created
type: float
sample: 3836739584.0
user_bucket:
description: S3 bucket with the image to import
returned: when snapshot is created
type: dict
sample: {
"s3_bucket": "mybucket",
"s3_key": "server-image.ami"
}
status:
description: status of the import operation
returned: when snapshot is created
type: str
sample: "completed"
'''


import time

from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import camel_dict_to_snake_dict

try:
import botocore
except ImportError:
pass


def wait_for_import_snapshot(connection, wait_timeout, import_task_id):

params = {
'ImportTaskIds': [import_task_id]
}
start_time = time.time()
while True:
status = connection.describe_import_snapshot_tasks(**params)

# What are the valid status values?
if len(status['ImportSnapshotTasks']) > 1:
raise RuntimeError("Should only be 1 Import Snapshot Task with this id.")

task = status['ImportSnapshotTasks'][0]
if task['SnapshotTaskDetail']['Status'] in ['completed']:
return status

if time.time() - start_time > wait_timeout:
raise RuntimeError('Wait timeout exceeded (%s sec)' % wait_timeout)

time.sleep(5)


def import_snapshot(module, connection):
description = module.params.get('description')
image_format = module.params.get('format')
url = module.params.get('url')
s3_bucket = module.params.get('s3_bucket')
s3_key = module.params.get('s3_key')
encrypted = module.params.get('encrypted')
kms_key_id = module.params.get('kms_key_id')
role_name = module.params.get('role_name')
wait = module.params.get('wait')
wait_timeout = module.params.get('wait_timeout')
tags = module.params.get('tags')

if not url and not (s3_bucket and s3_key):
module.fail_json(msg='url or s3_bucket and s3key are required.')
if all([url, s3_bucket, s3_key]):
module.fail_json(msg='url or s3_bucket and s3key are required, not all 3.')

if module.check_mode:
module.exit_json(changed=True, msg="IMPORT operation skipped - running in check mode")

try:
params = {
'Description': description,
'DiskContainer': {
'Description': description,
'Format': image_format,
},
'Encrypted': encrypted
}
if url:
params['DiskContainer']['Url'] = url
else:
params['DiskContainer']['UserBucket'] = {
'S3Bucket': s3_bucket,
'S3Key': s3_key
}
if kms_key_id:
params['KmsKeyId'] = kms_key_id
if role_name:
params['RoleName'] = role_name

task = connection.import_snapshot(**params)
import_task_id = task['ImportTaskId']
detail = task['SnapshotTaskDetail']

if wait:
status = wait_for_import_snapshot(connection, wait_timeout, import_task_id)
detail = status['ImportSnapshotTasks'][0]['SnapshotTaskDetail']

if tags:
connection.create_tags(
Resources=[detail["SnapshotId"]],
Tags=[{'Key': k, 'Value': v} for k, v in tags.items()]
)
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError, RuntimeError) as e:
module.fail_json_aws(e, msg="Error importing image")

module.exit_json(changed=True, **camel_dict_to_snake_dict(detail))


def snapshot_import_ansible_module():
argument_spec = dict(
description=dict(default=''),
wait=dict(type='bool', default=True),
wait_timeout=dict(type='int', default=900),
format=dict(required=True),
url=dict(),
s3_bucket=dict(),
s3_key=dict(),
encrypted=dict(type='bool', default=False),
kms_key_id=dict(),
role_name=dict(),
tags=dict(type='dict')
)
return AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True,
mutually_exclusive=[['s3_bucket', 'url']],
required_one_of=[['s3_bucket', 'url']],
required_together=[['s3_bucket', 's3_key']]
)


def main():
module = snapshot_import_ansible_module()
connection = module.client('ec2')
import_snapshot(module, connection)


if __name__ == '__main__':
main()

0 comments on commit 91ead5c

Please sign in to comment.