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
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'community'}
14
15
16DOCUMENTATION = '''
17---
18module: purefb_fs
19version_added: "2.6"
20short_description:  Manage filesystemon Pure Storage FlashBlade`
21description:
22    - This module manages filesystems on Pure Storage FlashBlade.
23author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
24options:
25  name:
26    description:
27      - Filesystem Name.
28    required: true
29    type: str
30  state:
31    description:
32      - Create, delete or modifies a filesystem.
33    required: false
34    default: present
35    type: str
36    choices: [ "present", "absent" ]
37  eradicate:
38    description:
39      - Define whether to eradicate the filesystem on delete or leave in trash.
40    required: false
41    type: bool
42    default: false
43  size:
44    description:
45      - Volume size in M, G, T or P units. See examples.
46    type: str
47    required: false
48    default: 32G
49  nfsv3:
50    description:
51      - Define whether to NFSv3 protocol is enabled for the filesystem.
52    required: false
53    type: bool
54    default: true
55    version_added: 2.9
56  nfsv4:
57    description:
58      - Define whether to NFSv4.1 protocol is enabled for the filesystem.
59    required: false
60    type: bool
61    default: true
62    version_added: 2.9
63  nfs:
64    description:
65      - (Deprecate) Define whether to NFSv3 protocol is enabled for the filesystem.
66      - This option will be deprecated in 2.10, use I(nfsv3) instead.
67    required: false
68    type: bool
69    default: true
70  nfs_rules:
71    description:
72      - Define the NFS rules in operation.
73    required: false
74    default: '*(rw,no_root_squash)'
75    type: str
76  smb:
77    description:
78      - Define whether to SMB protocol is enabled for the filesystem.
79    required: false
80    type: bool
81    default: false
82  http:
83    description:
84      - Define whether to HTTP/HTTPS protocol is enabled for the filesystem.
85    required: false
86    type: bool
87    default: false
88  snapshot:
89    description:
90      - Define whether a snapshot directory is enabled for the filesystem.
91    required: false
92    type: bool
93    default: false
94  fastremove:
95    description:
96      - Define whether the fast remove directory is enabled for the filesystem.
97    required: false
98    type: bool
99    default: false
100  hard_limit:
101    description:
102      - Define whether the capacity for a filesystem is a hard limit.
103      - CAUTION This will cause the filesystem to go Read-Only if the
104        capacity has already exceeded the logical size of the filesystem.
105    required: false
106    type: bool
107    default: false
108    version_added: 2.8
109  user_quota:
110    description:
111      - Default quota in M, G, T or P units for a user under this file system.
112    required: false
113    type: str
114    version_added: 2.9
115  group_quota:
116    description:
117      - Default quota in M, G, T or P units for a group under this file system.
118    required: false
119    type: str
120    version_added: 2.9
121extends_documentation_fragment:
122    - purestorage.fb
123'''
124
125EXAMPLES = '''
126- name: Create new filesystem named foo
127  purefb_fs:
128    name: foo
129    size: 1T
130    state: present
131    fb_url: 10.10.10.2
132    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
133
134- name: Delete filesystem named foo
135  purefb_fs:
136    name: foo
137    state: absent
138    fb_url: 10.10.10.2
139    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
140
141- name: Recover filesystem named foo
142  purefb_fs:
143    name: foo
144    state: present
145    fb_url: 10.10.10.2
146    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
147
148- name: Eradicate filesystem named foo
149  purefb_fs:
150    name: foo
151    state: absent
152    eradicate: true
153    fb_url: 10.10.10.2
154    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
155
156- name: Modify attributes of an existing filesystem named foo
157  purefb_fs:
158    name: foo
159    size: 2T
160    nfsv3 : false
161    nfsv4 : true
162    user_quota: 10K
163    group_quota: 25M
164    nfs_rules: '*(ro)'
165    snapshot: true
166    fastremove: true
167    hard_limit: true
168    smb: true
169    state: present
170    fb_url: 10.10.10.2
171    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641'''
172
173RETURN = '''
174'''
175
176HAS_PURITY_FB = True
177try:
178    from purity_fb import FileSystem, ProtocolRule, NfsRule
179except ImportError:
180    HAS_PURITY_FB = False
181
182from ansible.module_utils.basic import AnsibleModule, human_to_bytes
183from ansible.module_utils.pure import get_blade, purefb_argument_spec
184
185
186HARD_LIMIT_API_VERSION = '1.4'
187NFSV4_API_VERSION = '1.6'
188
189
190def get_fs(module, blade):
191    """Return Filesystem or None"""
192    fsys = []
193    fsys.append(module.params['name'])
194    try:
195        res = blade.file_systems.list_file_systems(names=fsys)
196        return res.items[0]
197    except Exception:
198        return None
199
200
201def create_fs(module, blade):
202    """Create Filesystem"""
203    changed = True
204    if not module.check_mode:
205        try:
206
207            if not module.params['size']:
208                module.params['size'] = '32G'
209
210            size = human_to_bytes(module.params['size'])
211            nfsv3 = module.params['nfs'] if module.params['nfsv3'] is None else module.params['nfsv3']
212
213            if module.params['user_quota']:
214                user_quota = human_to_bytes(module.params['user_quota'])
215            else:
216                user_quota = None
217            if module.params['group_quota']:
218                group_quota = human_to_bytes(module.params['group_quota'])
219            else:
220                group_quota = None
221
222            api_version = blade.api_version.list_versions().versions
223            if HARD_LIMIT_API_VERSION in api_version:
224                if NFSV4_API_VERSION in api_version:
225                    fs_obj = FileSystem(name=module.params['name'],
226                                        provisioned=size,
227                                        fast_remove_directory_enabled=module.params['fastremove'],
228                                        hard_limit_enabled=module.params['hard_limit'],
229                                        snapshot_directory_enabled=module.params['snapshot'],
230                                        nfs=NfsRule(v3_enabled=nfsv3,
231                                                    v4_1_enabled=module.params['nfsv4'],
232                                                    rules=module.params['nfs_rules']),
233                                        smb=ProtocolRule(enabled=module.params['smb']),
234                                        http=ProtocolRule(enabled=module.params['http']),
235                                        default_user_quota=user_quota,
236                                        default_group_quota=group_quota
237                                        )
238                else:
239                    fs_obj = FileSystem(name=module.params['name'],
240                                        provisioned=size,
241                                        fast_remove_directory_enabled=module.params['fastremove'],
242                                        hard_limit_enabled=module.params['hard_limit'],
243                                        snapshot_directory_enabled=module.params['snapshot'],
244                                        nfs=NfsRule(enabled=nfsv3, rules=module.params['nfs_rules']),
245                                        smb=ProtocolRule(enabled=module.params['smb']),
246                                        http=ProtocolRule(enabled=module.params['http'])
247                                        )
248            else:
249                fs_obj = FileSystem(name=module.params['name'],
250                                    provisioned=size,
251                                    fast_remove_directory_enabled=module.params['fastremove'],
252                                    snapshot_directory_enabled=module.params['snapshot'],
253                                    nfs=NfsRule(enabled=module.params['nfs'], rules=module.params['nfs_rules']),
254                                    smb=ProtocolRule(enabled=module.params['smb']),
255                                    http=ProtocolRule(enabled=module.params['http'])
256                                    )
257            blade.file_systems.create_file_systems(fs_obj)
258        except Exception:
259            module.fail_json(msg="Failed to create filesystem {0}.".format(module.params['name']))
260    module.exit_json(changed=changed)
261
262
263def modify_fs(module, blade):
264    """Modify Filesystem"""
265    changed = True
266    if not module.check_mode:
267        mod_fs = False
268        nfsv3 = module.params['nfs'] if module.params['nfsv3'] is None else module.params['nfsv3']
269        attr = {}
270        if module.params['user_quota']:
271            user_quota = human_to_bytes(module.params['user_quota'])
272        if module.params['group_quota']:
273            group_quota = human_to_bytes(module.params['group_quota'])
274        fsys = get_fs(module, blade)
275        if fsys.destroyed:
276            attr['destroyed'] = False
277            mod_fs = True
278        if module.params['size']:
279            if human_to_bytes(module.params['size']) != fsys.provisioned:
280                attr['provisioned'] = human_to_bytes(module.params['size'])
281                mod_fs = True
282        api_version = blade.api_version.list_versions().versions
283        if NFSV4_API_VERSION in api_version:
284            if nfsv3 and not fsys.nfs.v3_enabled:
285                attr['nfs'] = NfsRule(v3_enabled=nfsv3)
286                mod_fs = True
287            if not nfsv3 and fsys.nfs.v3_enabled:
288                attr['nfs'] = NfsRule(v3_enabled=nfsv3)
289                mod_fs = True
290            if module.params['nfsv4'] and not fsys.nfs.v4_1_enabled:
291                attr['nfs'] = NfsRule(v4_1_enabled=module.params['nfsv4'])
292                mod_fs = True
293            if not module.params['nfsv4'] and fsys.nfs.v4_1_enabled:
294                attr['nfs'] = NfsRule(v4_1_enabled=module.params['nfsv4'])
295                mod_fs = True
296            if nfsv3 or module.params['nfsv4'] and fsys.nfs.v3_enabled or fsys.nfs.v4_1_enabled:
297                if fsys.nfs.rules != module.params['nfs_rules']:
298                    attr['nfs'] = NfsRule(rules=module.params['nfs_rules'])
299                    mod_fs = True
300            if module.params['user_quota'] and user_quota != fsys.default_user_quota:
301                attr['default_user_quota'] = user_quota
302                mod_fs = True
303            if module.params['group_quota'] and group_quota != fsys.default_group_quota:
304                attr['default_group_quota'] = group_quota
305                mod_fs = True
306        else:
307            if nfsv3 and not fsys.nfs.enabled:
308                attr['nfs'] = NfsRule(enabled=nfsv3)
309                mod_fs = True
310            if not nfsv3 and fsys.nfs.enabled:
311                attr['nfs'] = NfsRule(enabled=nfsv3)
312                mod_fs = True
313            if nfsv3 and fsys.nfs.enabled:
314                if fsys.nfs.rules != module.params['nfs_rules']:
315                    attr['nfs'] = NfsRule(rules=module.params['nfs_rules'])
316                    mod_fs = True
317        if module.params['smb'] and not fsys.smb.enabled:
318            attr['smb'] = ProtocolRule(enabled=module.params['smb'])
319            mod_fs = True
320        if not module.params['smb'] and fsys.smb.enabled:
321            attr['smb'] = ProtocolRule(enabled=module.params['smb'])
322            mod_fs = True
323        if module.params['http'] and not fsys.http.enabled:
324            attr['http'] = ProtocolRule(enabled=module.params['http'])
325            mod_fs = True
326        if not module.params['http'] and fsys.http.enabled:
327            attr['http'] = ProtocolRule(enabled=module.params['http'])
328            mod_fs = True
329        if module.params['snapshot'] and not fsys.snapshot_directory_enabled:
330            attr['snapshot_directory_enabled'] = module.params['snapshot']
331            mod_fs = True
332        if not module.params['snapshot'] and fsys.snapshot_directory_enabled:
333            attr['snapshot_directory_enabled'] = module.params['snapshot']
334            mod_fs = True
335        if module.params['fastremove'] and not fsys.fast_remove_directory_enabled:
336            attr['fast_remove_directory_enabled'] = module.params['fastremove']
337            mod_fs = True
338        if not module.params['fastremove'] and fsys.fast_remove_directory_enabled:
339            attr['fast_remove_directory_enabled'] = module.params['fastremove']
340            mod_fs = True
341        api_version = blade.api_version.list_versions().versions
342        if HARD_LIMIT_API_VERSION in api_version:
343            if not module.params['hard_limit'] and fsys.hard_limit_enabled:
344                attr['hard_limit_enabled'] = module.params['hard_limit']
345                mod_fs = True
346            if module.params['hard_limit'] and not fsys.hard_limit_enabled:
347                attr['hard_limit_enabled'] = module.params['hard_limit']
348                mod_fs = True
349        if mod_fs:
350            n_attr = FileSystem(**attr)
351            try:
352                blade.file_systems.update_file_systems(name=module.params['name'], attributes=n_attr)
353            except Exception:
354                module.fail_json(msg="Failed to update filesystem {0}.".format(module.params['name']))
355        else:
356            changed = False
357    module.exit_json(changed=changed)
358
359
360def delete_fs(module, blade):
361    """ Delete Filesystem"""
362    changed = True
363    if not module.check_mode:
364        try:
365            api_version = blade.api_version.list_versions().versions
366            if NFSV4_API_VERSION in api_version:
367                blade.file_systems.update_file_systems(name=module.params['name'],
368                                                       attributes=FileSystem(nfs=NfsRule(v3_enabled=False,
369                                                                                         v4_1_enabled=False),
370                                                                             smb=ProtocolRule(enabled=False),
371                                                                             http=ProtocolRule(enabled=False),
372                                                                             destroyed=True)
373                                                       )
374            else:
375                blade.file_systems.update_file_systems(name=module.params['name'],
376                                                       attributes=FileSystem(nfs=NfsRule(enabled=False),
377                                                                             smb=ProtocolRule(enabled=False),
378                                                                             http=ProtocolRule(enabled=False),
379                                                                             destroyed=True)
380                                                       )
381
382            if module.params['eradicate']:
383                try:
384                    blade.file_systems.delete_file_systems(module.params['name'])
385                except Exception:
386                    module.fail_json(msg="Failed to delete filesystem {0}.".format(module.params['name']))
387        except Exception:
388            module.fail_json(msg="Failed to update filesystem {0} prior to deletion.".format(module.params['name']))
389    module.exit_json(changed=changed)
390
391
392def eradicate_fs(module, blade):
393    """ Eradicate Filesystem"""
394    changed = True
395    if not module.check_mode:
396        try:
397            blade.file_systems.delete_file_systems(module.params['name'])
398        except Exception:
399            module.fail_json(msg="Failed to eradicate filesystem {0}.".format(module.params['name']))
400    module.exit_json(changed=changed)
401
402
403def main():
404    argument_spec = purefb_argument_spec()
405    argument_spec.update(
406        dict(
407            name=dict(required=True),
408            eradicate=dict(default='false', type='bool'),
409            nfs=dict(removed_in_version='2.10', default='true', type='bool'),
410            nfsv3=dict(default='true', type='bool'),
411            nfsv4=dict(default='true', type='bool'),
412            nfs_rules=dict(default='*(rw,no_root_squash)'),
413            smb=dict(default='false', type='bool'),
414            http=dict(default='false', type='bool'),
415            snapshot=dict(default='false', type='bool'),
416            fastremove=dict(default='false', type='bool'),
417            hard_limit=dict(default='false', type='bool'),
418            user_quota=dict(type='str'),
419            group_quota=dict(type='str'),
420            state=dict(default='present', choices=['present', 'absent']),
421            size=dict()
422        )
423    )
424
425    mutually_exclusive = [['nfs', 'nfsv3']]
426
427    module = AnsibleModule(argument_spec,
428                           mutually_exclusive=mutually_exclusive,
429                           supports_check_mode=True)
430
431    if not HAS_PURITY_FB:
432        module.fail_json(msg='purity_fb sdk is required for this module')
433
434    state = module.params['state']
435    blade = get_blade(module)
436    fsys = get_fs(module, blade)
437
438    if state == 'present' and not fsys:
439        create_fs(module, blade)
440    elif state == 'present' and fsys:
441        modify_fs(module, blade)
442    elif state == 'absent' and fsys and not fsys.destroyed:
443        delete_fs(module, blade)
444    elif state == 'absent' and fsys and fsys.destroyed and module.params['eradicate']:
445        eradicate_fs(module, blade)
446    elif state == 'absent' and not fsys:
447        module.exit_json(changed=False)
448
449
450if __name__ == '__main__':
451    main()
452