From 947aa523272c385f19bdb75037eee75eb210c4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Santos?= Date: Thu, 3 May 2018 15:15:39 +0100 Subject: [PATCH] Add new OpenStack module for managing cinder volume snapshots (#19122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add os_volume_snapshot Ansible Module * Add missing RETURN documentation * Fix YAML syntax * Add ANSIBLE_METADATA Signed-off-by: Mário Santos * Bump the ansible version Signed-off-by: Mário Santos * Add support for check_mode Signed-off-by: Mário Santos * Fix sanity tests Signed-off-by: Mário Santos --- .../cloud/openstack/os_volume_snapshot.py | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 lib/ansible/modules/cloud/openstack/os_volume_snapshot.py diff --git a/lib/ansible/modules/cloud/openstack/os_volume_snapshot.py b/lib/ansible/modules/cloud/openstack/os_volume_snapshot.py new file mode 100644 index 00000000000..a2dd1618936 --- /dev/null +++ b/lib/ansible/modules/cloud/openstack/os_volume_snapshot.py @@ -0,0 +1,200 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# Copyright (c) 2016, Mario Santos +# +# This module 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. +# +# This software 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 this software. If not, see . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'metadata_version': '1.1'} + +DOCUMENTATION = ''' +--- +module: os_volume_snapshot +short_description: Create/Delete Cinder Volume Snapshots +extends_documentation_fragment: openstack +version_added: "2.6" +author: "Mario Santos (@ruizink)" +description: + - Create or Delete cinder block storage volume snapshots +options: + display_name: + description: + - Name of the snapshot + required: true + aliases: ['name'] + display_description: + description: + - String describing the snapshot + required: false + default: None + aliases: ['description'] + volume: + description: + - The volume name or id to create/delete the snapshot + required: True + force: + description: + - Allows or disallows snapshot of a volume to be created when the volume + is attached to an instance. + required: false + default: False + state: + description: + - Should the resource be present or absent. + choices: [present, absent] + default: present + availability_zone: + description: + - Availability zone in which to create the snapshot. + required: false +requirements: + - "python >= 2.7" + - "shade" +''' + +EXAMPLES = ''' +# Creates a snapshot on volume 'test_volume' +- name: create and delete snapshot + hosts: localhost + tasks: + - name: create snapshot + os_volume_snapshot: + state: present + cloud: mordred + availability_zone: az2 + display_name: test_snapshot + volume: test_volume + - name: delete snapshot + os_volume_snapshot: + state: absent + cloud: mordred + availability_zone: az2 + display_name: test_snapshot + volume: test_volume +''' + +RETURN = ''' +snapshot: + description: The snapshot instance after the change + returned: success + type: dict + sample: + id: 837aca54-c0ee-47a2-bf9a-35e1b4fdac0c + name: test_snapshot + volume_id: ec646a7c-6a35-4857-b38b-808105a24be6 + size: 2 + status: available + display_name: test_snapshot +''' + +try: + import shade + HAS_SHADE = True +except ImportError: + HAS_SHADE = False + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.openstack import (openstack_full_argument_spec, + openstack_module_kwargs) + + +def _present_volume_snapshot(module, cloud): + volume = cloud.get_volume(module.params['volume']) + snapshot = cloud.get_volume_snapshot(module.params['display_name'], + filters={'volume_id': volume.id}) + if not snapshot: + snapshot = cloud.create_volume_snapshot(volume.id, + force=module.params['force'], + wait=module.params['wait'], + timeout=module.params[ + 'timeout'], + name=module.params['display_name'], + description=module.params.get( + 'display_description') + ) + module.exit_json(changed=True, snapshot=snapshot) + else: + module.exit_json(changed=False, snapshot=snapshot) + + +def _absent_volume_snapshot(module, cloud): + volume = cloud.get_volume(module.params['volume']) + snapshot = cloud.get_volume_snapshot(module.params['display_name'], + filters={'volume_id': volume.id}) + if not snapshot: + module.exit_json(changed=False) + else: + cloud.delete_volume_snapshot(name_or_id=snapshot.id, + wait=module.params['wait'], + timeout=module.params['timeout'], + ) + module.exit_json(changed=True, snapshot_id=snapshot.id) + + +def _system_state_change(module, cloud): + volume = cloud.get_volume(module.params['volume']) + snapshot = cloud.get_volume_snapshot(module.params['display_name'], + filters={'volume_id': volume.id}) + state = module.params['state'] + + if state == 'present': + return snapshot is None + if state == 'absent': + return snapshot is not None + + +def main(): + argument_spec = openstack_full_argument_spec( + display_name=dict(required=True, aliases=['name']), + display_description=dict(default=None, aliases=['description']), + volume=dict(required=True), + force=dict(required=False, default=False, type='bool'), + state=dict(default='present', choices=['absent', 'present']), + ) + + module_kwargs = openstack_module_kwargs() + module = AnsibleModule(argument_spec, + supports_check_mode=True, + **module_kwargs) + + if not HAS_SHADE: + module.fail_json(msg='shade is required for this module') + + state = module.params['state'] + + try: + cloud = shade.openstack_cloud(**module.params) + if cloud.volume_exists(module.params['volume']): + if module.check_mode: + module.exit_json(changed=_system_state_change(module, cloud)) + if state == 'present': + _present_volume_snapshot(module, cloud) + if state == 'absent': + _absent_volume_snapshot(module, cloud) + else: + module.fail_json( + msg="No volume with name or id '{0}' was found.".format( + module.params['volume'])) + except (shade.OpenStackCloudException, shade.OpenStackCloudTimeout) as e: + module.fail_json(msg=e.message) + + +if __name__ == '__main__': + main()