From 08767946cb4441bdb3386cf848d80618529f2c4a Mon Sep 17 00:00:00 2001 From: Simon Dodsley Date: Tue, 15 Aug 2017 15:27:34 -0400 Subject: [PATCH] Add Pure Storage Snap module (#28027) * Add Pure Storage Snap module * Use one line GPL license https://github.com/ansible/community/wiki/Testing:-boilerplate,-wildcard-imports,-and-get_exception --- .../storage/purestorage/purefa_snap.py | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 lib/ansible/modules/storage/purestorage/purefa_snap.py diff --git a/lib/ansible/modules/storage/purestorage/purefa_snap.py b/lib/ansible/modules/storage/purestorage/purefa_snap.py new file mode 100644 index 00000000000..05184c8bcf0 --- /dev/null +++ b/lib/ansible/modules/storage/purestorage/purefa_snap.py @@ -0,0 +1,228 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# 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.0', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: purefa_snap +version_added: 2.4 +short_description: Create or Delete volume snapshots on Pure Storage FlashArray +description: + - This module creates or deletes volume snapshots and creates volumes from snapshots on Pure Storage FlashArray. +author: Simon Dodsley (@simondodsley) +options: + name: + description: + - Source volume name of snapshot + required: true + suffix: + description: + - Suffix of snapshot name + required: false + target: + description: + - Name of target volume if creating from snapshot + required: false + overwrite: + description: + - Define whether to overwrite existing volume when creating from snapshot + required: false + default: false + type: bool + state: + description: + - Create or delete volume snapshot + required: false + default: present + choices: [ "present", "absent", "copy" ] + eradicate: + description: + - Define whether to eradicate the snapshot on delete or leave in trash + required: false + default: false + type: bool +extends_documentation_fragment: + - purestorage +''' + +EXAMPLES = ''' +- name: Create snapshot foo.ansible + purefa_snap: + name: foo + suffix: ansible + state: present + fa_url: 10.10.10.2 + fa_api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Create R/W clone foo_clone from snapshot foo.snap + purefa_snap: + name: foo + suffix: snap + target: foo_clone + state: copy + fa_url: 10.10.10.2 + fa_api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Overwrite existing volume foo_clone with snapshot foo.snap + purefa_snap: + name: foo + suffix: snap + target: foo_clone + state: copy + overwrite: true + fa_url: 10.10.10.2 + fa_api_token: e31060a7-21fc-e277-6240-25983c6c4592 + +- name: Delete and eradicate snapshot named foo.snap + purefa_snap: + name: foo + suffix: snap + eradicate: true + state: absent + fa_url: 10.10.10.2 + fa_api_token: e31060a7-21fc-e277-6240-25983c6c4592''' + +RETURN = ''' +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.pure import get_system, purefa_argument_spec + +from datetime import datetime + +HAS_PURESTORAGE = True +try: + from purestorage import purestorage +except ImportError: + HAS_PURESTORAGE = False + + +def get_volume(module, array): + """Return Volume or None""" + try: + return array.get_volume(module.params['name']) + except: + return None + + +def get_target(module, array): + """Return Volume or None""" + try: + return array.get_volume(module.params['target']) + except: + return None + + +def get_snapshot(module, array): + """Return Snapshot or None""" + try: + snapname = module.params['name'] + "." + module.params['suffix'] + for s in array.get_volume(module.params['name'], snap='true'): + if s['name'] == snapname: + return snapname + break + except: + return None + + +def create_snapshot(module, array): + """Create Snapshot""" + if not module.check_mode: + array.create_snapshot(module.params['name'], suffix=module.params['suffix']) + module.exit_json(changed=True) + + +def create_from_snapshot(module, array): + """Create Volume from Snapshot""" + if not module.check_mode: + source = module.params['name'] + "." + module.params['suffix'] + tgt = get_target(module, array) + if tgt is None: + changed = True + array.copy_volume(source, + module.params['target']) + elif tgt is not None and module.params['overwrite']: + changed = True + array.copy_volume(source, + module.params['target'], + overwrite=module.params['overwrite']) + elif tgt is not None and not module.params['overwrite']: + changed = False + module.exit_json(changed=changed) + + +def update_snapshot(module, array): + """Update Snapshot""" + changed = False + module.exit_json(changed=changed) + + +def delete_snapshot(module, array): + """ Delete Snapshot""" + if not module.check_mode: + snapname = module.params['name'] + "." + module.params['suffix'] + array.destroy_volume(snapname) + if module.params['eradicate']: + array.eradicate_volume(snapname) + module.exit_json(changed=True) + + +def main(): + argument_spec = purefa_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + suffix=dict(), + target=dict(), + overwrite=dict(default='false', type='bool'), + eradicate=dict(default='false', type='bool'), + state=dict(default='present', choices=['present', 'absent', 'copy']), + ) + ) + + required_if = [('state', 'copy', ['target', 'suffix'])] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=True) + + if not HAS_PURESTORAGE: + module.fail_json(msg='purestorage sdk is required for this module in volume') + + if module.params['suffix'] is None: + suffix = "snap-" + str((datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()) + module.params['suffix'] = suffix.replace(".", "") + state = module.params['state'] + array = get_system(module) + volume = get_volume(module, array) + target = get_target(module, array) + snap = get_snapshot(module, array) + + if state == 'present' and volume and not snap: + create_snapshot(module, array) + elif state == 'present' and volume and snap: + update_snapshot(module, array) + elif state == 'present' and not volume: + update_snapshot(module, array) + elif state == 'copy' and snap: + create_from_snapshot(module, array) + elif state == 'copy' and not snap: + update_snapshot(module, array) + elif state == 'absent' and snap: + delete_snapshot(module, array) + elif state == 'absent' and not snap: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main()