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: purefb_snap 17version_added: '2.6' 18short_description: Manage filesystem snapshots on Pure Storage FlashBlades 19description: 20- Create or delete volumes and filesystem snapshots on Pure Storage FlashBlades. 21author: 22- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> 23options: 24 name: 25 description: 26 - The name of the source filesystem. 27 required: true 28 type: str 29 suffix: 30 description: 31 - Suffix of snapshot name. 32 type: str 33 state: 34 description: 35 - Define whether the filesystem snapshot should exist or not. 36 choices: [ absent, present ] 37 default: present 38 type: str 39 eradicate: 40 description: 41 - Define whether to eradicate the snapshot on delete or leave in trash. 42 type: bool 43 default: 'no' 44extends_documentation_fragment: 45- purestorage.fb 46''' 47 48EXAMPLES = r''' 49- name: Create snapshot foo.ansible 50 purefb_snap: 51 name: foo 52 suffix: ansible 53 fb_url: 10.10.10.2 54 fb_api_token: e31060a7-21fc-e277-6240-25983c6c4592 55 state: present 56 57- name: Delete snapshot named foo.snap 58 purefb_snap: 59 name: foo 60 suffix: snap 61 fb_url: 10.10.10.2 62 fb_api_token: e31060a7-21fc-e277-6240-25983c6c4592 63 state: absent 64 65- name: Recover deleted snapshot foo.ansible 66 purefb_snap: 67 name: foo 68 suffix: ansible 69 fb_url: 10.10.10.2 70 fb_api_token: e31060a7-21fc-e277-6240-25983c6c4592 71 state: present 72 73- name: Eradicate snapshot named foo.snap 74 purefb_snap: 75 name: foo 76 suffix: snap 77 eradicate: true 78 fb_url: 10.10.10.2 79 fb_api_token: e31060a7-21fc-e277-6240-25983c6c4592 80 state: absent 81''' 82 83RETURN = r''' 84''' 85 86from ansible.module_utils.basic import AnsibleModule 87from ansible.module_utils.pure import get_blade, purefb_argument_spec 88 89from datetime import datetime 90 91HAS_PURITY_FB = True 92try: 93 from purity_fb import FileSystemSnapshot, SnapshotSuffix 94except ImportError: 95 HAS_PURITY_FB = False 96 97 98def get_fs(module, blade): 99 """Return Filesystem or None""" 100 fs = [] 101 fs.append(module.params['name']) 102 try: 103 res = blade.file_systems.list_file_systems(names=fs) 104 return res.items[0] 105 except Exception: 106 return None 107 108 109def get_fssnapshot(module, blade): 110 """Return Snapshot or None""" 111 try: 112 filt = 'source=\'' + module.params['name'] + '\' and suffix=\'' + module.params['suffix'] + '\'' 113 res = blade.file_system_snapshots.list_file_system_snapshots(filter=filt) 114 return res.items[0] 115 except Exception: 116 return None 117 118 119def create_snapshot(module, blade): 120 """Create Snapshot""" 121 if not module.check_mode: 122 source = [] 123 source.append(module.params['name']) 124 try: 125 blade.file_system_snapshots.create_file_system_snapshots(sources=source, suffix=SnapshotSuffix(module.params['suffix'])) 126 changed = True 127 except Exception: 128 changed = False 129 module.exit_json(changed=changed) 130 131 132def recover_snapshot(module, blade): 133 """Recover deleted Snapshot""" 134 if not module.check_mode: 135 snapname = module.params['name'] + "." + module.params['suffix'] 136 new_attr = FileSystemSnapshot(destroyed=False) 137 try: 138 blade.file_system_snapshots.update_file_system_snapshots(name=snapname, attributes=new_attr) 139 changed = True 140 except Exception: 141 changed = False 142 module.exit_json(changed=changed) 143 144 145def update_snapshot(module, blade): 146 """Update Snapshot""" 147 changed = False 148 module.exit_json(changed=changed) 149 150 151def delete_snapshot(module, blade): 152 """ Delete Snapshot""" 153 if not module.check_mode: 154 snapname = module.params['name'] + "." + module.params['suffix'] 155 new_attr = FileSystemSnapshot(destroyed=True) 156 try: 157 blade.file_system_snapshots.update_file_system_snapshots(name=snapname, attributes=new_attr) 158 changed = True 159 if module.params['eradicate']: 160 try: 161 blade.file_system_snapshots.delete_file_system_snapshots(name=snapname) 162 changed = True 163 except Exception: 164 changed = False 165 except Exception: 166 changed = False 167 module.exit_json(changed=changed) 168 169 170def eradicate_snapshot(module, blade): 171 """ Eradicate Snapshot""" 172 if not module.check_mode: 173 snapname = module.params['name'] + "." + module.params['suffix'] 174 try: 175 blade.file_system_snapshots.delete_file_system_snapshots(name=snapname) 176 changed = True 177 except Exception: 178 changed = False 179 module.exit_json(changed=changed) 180 181 182def main(): 183 argument_spec = purefb_argument_spec() 184 argument_spec.update( 185 dict( 186 name=dict(required=True), 187 suffix=dict(type='str'), 188 eradicate=dict(default='false', type='bool'), 189 state=dict(default='present', choices=['present', 'absent']) 190 ) 191 ) 192 193 module = AnsibleModule(argument_spec, 194 supports_check_mode=True) 195 196 if not HAS_PURITY_FB: 197 module.fail_json(msg='purity_fb sdk is required for this module') 198 199 if module.params['suffix'] is None: 200 suffix = "snap-" + str((datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()) 201 module.params['suffix'] = suffix.replace(".", "") 202 203 state = module.params['state'] 204 blade = get_blade(module) 205 fs = get_fs(module, blade) 206 snap = get_fssnapshot(module, blade) 207 208 if state == 'present' and fs and not fs.destroyed and not snap: 209 create_snapshot(module, blade) 210 elif state == 'present' and fs and not fs.destroyed and snap and not snap.destroyed: 211 update_snapshot(module, blade) 212 elif state == 'present' and fs and not fs.destroyed and snap and snap.destroyed: 213 recover_snapshot(module, blade) 214 elif state == 'present' and fs and fs.destroyed: 215 update_snapshot(module, blade) 216 elif state == 'present' and not fs: 217 update_snapshot(module, blade) 218 elif state == 'absent' and snap and not snap.destroyed: 219 delete_snapshot(module, blade) 220 elif state == 'absent' and snap and snap.destroyed: 221 eradicate_snapshot(module, blade) 222 elif state == 'absent' and not snap: 223 module.exit_json(changed=False) 224 225 226if __name__ == '__main__': 227 main() 228