summaryrefslogtreecommitdiff
path: root/lib/ansible/modules/storage/purestorage/purefa_pgsnap.py
blob: d672692eb61dadb7bc5189ce16a6a576121fa6c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#!/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.1',
                    'status': ['preview'],
                    'supported_by': 'community'}

DOCUMENTATION = r'''
---
module: purefa_pgsnap
version_added: '2.6'
short_description: Manage protection group snapshots on Pure Storage FlashArrays
description:
- Create or delete protection group snapshots on Pure Storage FlashArray.
- Recovery of replicated snapshots on the replica target array is enabled.
author:
- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
options:
  name:
    description:
    - The name of the source protection group.
    type: str
    required: true
  suffix:
    description:
    - Suffix of snapshot name.
  state:
    description:
    - Define whether the protection group snapshot should exist or not.
      Copy (added in 2.7) will create a full read/write clone of the
      snapshot.
    type: str
    choices: [ absent, present, copy ]
    default: present
  eradicate:
    description:
    - Define whether to eradicate the snapshot on delete or leave in trash.
    type: bool
    default: 'no'
  restore:
    description:
    - Restore a specific volume from a protection group snapshot.
    type: str
    version_added: 2.7
  overwrite:
    description:
    - Define whether to overwrite the target volume if it already exists.
    type: bool
    default: 'no'
    version_added: 2.8
  target:
    description:
    - Volume to restore a specified volume to.
    - If not supplied this will default to the volume defined in I(restore)
    type: str
    version_added: 2.8
  now:
    description: Whether to initiate a snapshot of the protection group immediately
    type: bool
    default: False
    version_added: 2.9
  apply_retention:
    description: Apply retention schedule settings to the snapshot
    type: bool
    default: False
    version_added: 2.9
  remote:
    description: Force immeadiate snapshot to remote targets
    type: bool
    default: False
    version_added: 2.9
extends_documentation_fragment:
- purestorage.fa
'''

EXAMPLES = r'''
- name: Create protection group snapshot foo.ansible
  purefa_pgsnap:
    name: foo
    suffix: ansible
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592
    state: present

- name: Delete and eradicate protection group snapshot named foo.snap
  purefa_pgsnap:
    name: foo
    suffix: snap
    eradicate: true
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592
    state: absent

- name: Restore volume data from local protection group snapshot named foo.snap to volume data2
  purefa_pgsnap:
    name: foo
    suffix: snap
    restore: data
    target: data2
    overwrite: true
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592
    state: copy

- name: Restore remote protection group snapshot arrayA:pgname.snap.data to local copy
  purefa_pgsnap:
    name: arrayA:pgname
    suffix: snap
    restore: data
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592
    state: copy

- name: Create snapshot of existing pgroup foo with suffix and force immeadiate copy to remote targets
  purefa_pgsnap:
    name: pgname
    suffix: force
    now: True
    apply_retention: True
    remote: True
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592
    state: copy
'''

RETURN = r'''
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.pure import get_system, purefa_argument_spec

from datetime import datetime


def get_pgroup(module, array):
    """Return Protection Group or None"""
    try:
        return array.get_pgroup(module.params['name'])
    except Exception:
        return None


def get_pgroupvolume(module, array):
    """Return Protection Group Volume or None"""
    try:
        pgroup = array.get_pgroup(module.params['name'])
        for volume in pgroup['volumes']:
            if volume == module.params['restore']:
                return volume
    except Exception:
        return None


def get_rpgsnapshot(module, array):
    """Return iReplicated Snapshot or None"""
    try:
        snapname = module.params['name'] + "." + module.params['suffix'] + "." + module.params['restore']
        for snap in array.list_volumes(snap=True):
            if snap['name'] == snapname:
                return snapname
    except Exception:
        return None


def get_pgsnapshot(module, array):
    """Return Snapshot (active or deleted) or None"""
    try:
        snapname = module.params['name'] + "." + module.params['suffix']
        for snap in array.get_pgroup(module.params['name'], snap=True, pending=True):
            if snap['name'] == snapname:
                return snapname
    except Exception:
        return None


def create_pgsnapshot(module, array):
    """Create Protection Group Snapshot"""
    changed = True
    if not module.check_mode:
        try:
            if module.params['now'] and array.get_pgroup(module.params['name'])['targets'] is not None:
                array.create_pgroup_snapshot(source=module.params['name'],
                                             suffix=module.params['suffix'],
                                             snap=True,
                                             apply_retention=module.params['apply_retention'],
                                             replicate_now=module.params['remote'])
            else:
                array.create_pgroup_snapshot(source=module.params['name'],
                                             suffix=module.params['suffix'],
                                             snap=True,
                                             apply_retention=module.params['apply_retention'])
        except Exception:
            module.fail_json(msg="Snapshot of pgroup {0} failed.".format(module.params['name']))
    module.exit_json(changed=changed)


def restore_pgsnapvolume(module, array):
    """Restore a Protection Group Snapshot Volume"""
    changed = True
    if not module.check_mode:
        if ":" in module.params['name']:
            if get_rpgsnapshot(module, array)is None:
                module.fail_json(msg="Selected restore snapshot {0} does not exist in the Protection Group".format(module.params['restore']))
        else:
            if get_pgroupvolume(module, array) is None:
                module.fail_json(msg="Selected restore volume {0} does not exist in the Protection Group".format(module.params['restore']))
        volume = module.params['name'] + "." + module.params['suffix'] + "." + module.params['restore']
        try:
            array.copy_volume(volume, module.params['target'], overwrite=module.params['overwrite'])
        except Exception:
            module.fail_json(msg="Failed to restore {0} from pgroup {1}".format(volume, module.params['name']))
    module.exit_json(changed=changed)


def update_pgsnapshot(module, array):
    """Update Protection Group Snapshot"""
    changed = True
    module.exit_json(changed=changed)


def delete_pgsnapshot(module, array):
    """ Delete Protection Group Snapshot"""
    changed = True
    if not module.check_mode:
        snapname = module.params['name'] + "." + module.params['suffix']
        try:
            array.destroy_pgroup(snapname)
            if module.params['eradicate']:
                try:
                    array.eradicate_pgroup(snapname)
                except Exception:
                    module.fail_json(msg="Failed to eradicate pgroup {0}".format(snapname))
        except Exception:
            module.fail_json(msg="Failed to delete pgroup {0}".format(snapname))
    module.exit_json(changed=changed)


def main():
    argument_spec = purefa_argument_spec()
    argument_spec.update(dict(
        name=dict(type='str', required=True),
        suffix=dict(type='str'),
        restore=dict(type='str'),
        overwrite=dict(type='bool', default=False),
        target=dict(type='str'),
        eradicate=dict(type='bool', default=False),
        now=dict(type='bool', default=False),
        apply_retention=dict(type='bool', default=False),
        remote=dict(type='bool', default=False),
        state=dict(type='str', default='present', choices=['absent', 'present', 'copy']),
    ))

    required_if = [('state', 'copy', ['suffix', 'restore'])]

    module = AnsibleModule(argument_spec,
                           required_if=required_if,
                           supports_check_mode=True)

    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(".", "")

    if not module.params['target'] and module.params['restore']:
        module.params['target'] = module.params['restore']

    state = module.params['state']
    array = get_system(module)
    pgroup = get_pgroup(module, array)
    if pgroup is None:
        module.fail_json(msg="Protection Group {0} does not exist.".format(module.params['name']))
    pgsnap = get_pgsnapshot(module, array)

    if state == 'copy':
        restore_pgsnapvolume(module, array)
    elif state == 'present' and not pgsnap:
        create_pgsnapshot(module, array)
    elif state == 'present' and pgsnap:
        update_pgsnapshot(module, array)
    elif state == 'absent' and pgsnap:
        delete_pgsnapshot(module, array)
    elif state == 'absent' and not pgsnap:
        module.exit_json(changed=False)

    module.exit_json(changed=False)


if __name__ == '__main__':
    main()