1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# (c) 2017, Simon Dodsley (simon@purestorage.com) 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14DOCUMENTATION = r''' 15--- 16module: purefa_snap 17version_added: '2.4' 18short_description: Manage volume snapshots on Pure Storage FlashArrays 19description: 20- Create or delete volumes and volume snapshots on Pure Storage FlashArray. 21author: 22- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> 23options: 24 name: 25 description: 26 - The name of the source volume. 27 type: str 28 required: true 29 suffix: 30 description: 31 - Suffix of snapshot name. 32 type: str 33 target: 34 description: 35 - Name of target volume if creating from snapshot. 36 type: str 37 overwrite: 38 description: 39 - Define whether to overwrite existing volume when creating from snapshot. 40 type: bool 41 default: 'no' 42 state: 43 description: 44 - Define whether the volume snapshot should exist or not. 45 choices: [ absent, copy, present ] 46 type: str 47 default: present 48 eradicate: 49 description: 50 - Define whether to eradicate the snapshot on delete or leave in trash. 51 type: bool 52 default: 'no' 53extends_documentation_fragment: 54- purestorage.fa 55''' 56 57EXAMPLES = r''' 58- name: Create snapshot foo.ansible 59 purefa_snap: 60 name: foo 61 suffix: ansible 62 fa_url: 10.10.10.2 63 api_token: e31060a7-21fc-e277-6240-25983c6c4592 64 state: present 65 66- name: Create R/W clone foo_clone from snapshot foo.snap 67 purefa_snap: 68 name: foo 69 suffix: snap 70 target: foo_clone 71 fa_url: 10.10.10.2 72 api_token: e31060a7-21fc-e277-6240-25983c6c4592 73 state: copy 74 75- name: Overwrite existing volume foo_clone with snapshot foo.snap 76 purefa_snap: 77 name: foo 78 suffix: snap 79 target: foo_clone 80 overwrite: true 81 fa_url: 10.10.10.2 82 api_token: e31060a7-21fc-e277-6240-25983c6c4592 83 state: copy 84 85- name: Delete and eradicate snapshot named foo.snap 86 purefa_snap: 87 name: foo 88 suffix: snap 89 eradicate: true 90 fa_url: 10.10.10.2 91 api_token: e31060a7-21fc-e277-6240-25983c6c4592 92 state: absent 93''' 94 95RETURN = r''' 96''' 97 98from ansible.module_utils.basic import AnsibleModule 99from ansible.module_utils.pure import get_system, purefa_argument_spec 100 101from datetime import datetime 102 103try: 104 from purestorage import purestorage 105 HAS_PURESTORAGE = True 106except ImportError: 107 HAS_PURESTORAGE = False 108 109 110def get_volume(module, array): 111 """Return Volume or None""" 112 try: 113 return array.get_volume(module.params['name']) 114 except Exception: 115 return None 116 117 118def get_target(module, array): 119 """Return Volume or None""" 120 try: 121 return array.get_volume(module.params['target']) 122 except Exception: 123 return None 124 125 126def get_snapshot(module, array): 127 """Return Snapshot or None""" 128 try: 129 snapname = module.params['name'] + "." + module.params['suffix'] 130 for s in array.get_volume(module.params['name'], snap='true'): 131 if s['name'] == snapname: 132 return snapname 133 except Exception: 134 return None 135 136 137def create_snapshot(module, array): 138 """Create Snapshot""" 139 changed = True 140 if not module.check_mode: 141 try: 142 array.create_snapshot(module.params['name'], suffix=module.params['suffix']) 143 except Exception: 144 changed = False 145 module.exit_json(changed=changed) 146 147 148def create_from_snapshot(module, array): 149 """Create Volume from Snapshot""" 150 source = module.params['name'] + "." + module.params['suffix'] 151 tgt = get_target(module, array) 152 if tgt is None: 153 changed = True 154 if not module.check_mode: 155 array.copy_volume(source, 156 module.params['target']) 157 elif tgt is not None and module.params['overwrite']: 158 changed = True 159 if not module.check_mode: 160 array.copy_volume(source, 161 module.params['target'], 162 overwrite=module.params['overwrite']) 163 elif tgt is not None and not module.params['overwrite']: 164 changed = False 165 module.exit_json(changed=changed) 166 167 168def update_snapshot(module, array): 169 """Update Snapshot""" 170 changed = False 171 module.exit_json(changed=changed) 172 173 174def delete_snapshot(module, array): 175 """ Delete Snapshot""" 176 changed = True 177 if not module.check_mode: 178 snapname = module.params['name'] + "." + module.params['suffix'] 179 try: 180 array.destroy_volume(snapname) 181 if module.params['eradicate']: 182 try: 183 array.eradicate_volume(snapname) 184 except Exception: 185 changed = False 186 except Exception: 187 changed = False 188 module.exit_json(changed=changed) 189 190 191def main(): 192 argument_spec = purefa_argument_spec() 193 argument_spec.update(dict( 194 name=dict(type='str', required=True), 195 suffix=dict(type='str'), 196 target=dict(type='str'), 197 overwrite=dict(type='bool', default=False), 198 eradicate=dict(type='bool', default=False), 199 state=dict(type='str', default='present', choices=['absent', 'copy', 'present']), 200 )) 201 202 required_if = [('state', 'copy', ['target', 'suffix'])] 203 204 module = AnsibleModule(argument_spec, 205 required_if=required_if, 206 supports_check_mode=True) 207 208 if not HAS_PURESTORAGE: 209 module.fail_json(msg='purestorage sdk is required for this module in volume') 210 211 if module.params['suffix'] is None: 212 suffix = "snap-" + str((datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()) 213 module.params['suffix'] = suffix.replace(".", "") 214 215 state = module.params['state'] 216 array = get_system(module) 217 volume = get_volume(module, array) 218 target = get_target(module, array) 219 snap = get_snapshot(module, array) 220 221 if state == 'present' and volume and not snap: 222 create_snapshot(module, array) 223 elif state == 'present' and volume and snap: 224 update_snapshot(module, array) 225 elif state == 'present' and not volume: 226 update_snapshot(module, array) 227 elif state == 'copy' and snap: 228 create_from_snapshot(module, array) 229 elif state == 'copy' and not snap: 230 update_snapshot(module, array) 231 elif state == 'absent' and snap: 232 delete_snapshot(module, array) 233 elif state == 'absent' and not snap: 234 module.exit_json(changed=False) 235 236 237if __name__ == '__main__': 238 main() 239