summaryrefslogtreecommitdiffstats
path: root/ansible_collections/netapp/elementsw/plugins/modules/na_elementsw_access_group.py
blob: 467ca415ccf76cc1d5817b89e7970fc5fa30f16d (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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
#!/usr/bin/python

# (c) 2018, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or
# https://www.gnu.org/licenses/gpl-3.0.txt)

"""
Element Software Access Group Manager
"""

from __future__ import absolute_import, division, print_function
__metaclass__ = type


ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'certified'}


DOCUMENTATION = '''

module: na_elementsw_access_group

short_description: NetApp Element Software Manage Access Groups
extends_documentation_fragment:
    - netapp.elementsw.netapp.solidfire
version_added: 2.7.0
author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
description:
- Create, destroy, or update access groups on Element Software Cluster.

options:

    state:
        description:
        - Whether the specified access group should exist or not.
        choices: ['present', 'absent']
        default: present
        type: str

    from_name:
        description:
        - ID or Name of the access group to rename.
        - Required to create a new access group called 'name' by renaming 'from_name'.
        version_added: 2.8.0
        type: str

    name:
        description:
        - Name for the access group for create, modify and delete operations.
        required: True
        aliases:
        - src_access_group_id
        type: str

    initiators:
        description:
        - List of initiators to include in the access group. If unspecified, the access group will start out without configured initiators.
        type: list
        elements: str

    volumes:
        description:
        - List of volumes to initially include in the volume access group. If unspecified, the access group will start without any volumes.
        - It accepts either volume_name or volume_id
        type: list
        elements: str

    account_id:
        description:
        - Account ID for the owner of this volume.
        - It accepts either account_name or account_id
        - if account_id is digit, it will consider as account_id
        - If account_id is string, it will consider as account_name
        version_added: 2.8.0
        type: str

    virtual_network_id:
        description:
        - The ID of the Element SW Software Cluster Virtual Network to associate the access group with.
        type: int

    virtual_network_tags:
        description:
        - The tags of VLAN Virtual Network Tag to associate the access group with.
        type: list
        elements: str

    attributes:
        description: List of Name/Value pairs in JSON object format.
        type: dict

'''

EXAMPLES = """
   - name: Create Access Group
     na_elementsw_access_group:
       hostname: "{{ elementsw_hostname }}"
       username: "{{ elementsw_username }}"
       password: "{{ elementsw_password }}"
       state: present
       name: AnsibleAccessGroup
       volumes: [7,8]
       account_id: 1

   - name: Modify Access Group
     na_elementsw_access_group:
       hostname: "{{ elementsw_hostname }}"
       username: "{{ elementsw_username }}"
       password: "{{ elementsw_password }}"
       state: present
       name: AnsibleAccessGroup-Renamed
       account_id: 1
       attributes: {"volumes": [1,2,3], "virtual_network_id": 12345}

   - name: Rename Access Group
     na_elementsw_access_group:
       hostname: "{{ elementsw_hostname }}"
       username: "{{ elementsw_username }}"
       password: "{{ elementsw_password }}"
       state: present
       from_name: AnsibleAccessGroup
       name: AnsibleAccessGroup-Renamed

   - name: Delete Access Group
     na_elementsw_access_group:
       hostname: "{{ elementsw_hostname }}"
       username: "{{ elementsw_username }}"
       password: "{{ elementsw_password }}"
       state: absent
       name: 1
"""


RETURN = """

msg:
    description: Success message
    returned: success
    type: str

"""
import traceback

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.netapp.elementsw.plugins.module_utils.netapp as netapp_utils
from ansible_collections.netapp.elementsw.plugins.module_utils.netapp_elementsw_module import NaElementSWModule

HAS_SF_SDK = netapp_utils.has_sf_sdk()
try:
    import solidfire.common
except ImportError:
    HAS_SF_SDK = False


class ElementSWAccessGroup(object):
    """
    Element Software Volume Access Group
    """

    def __init__(self):

        self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
            from_name=dict(required=False, type='str'),
            name=dict(required=True, aliases=["src_access_group_id"], type='str'),
            initiators=dict(required=False, type='list', elements='str'),
            volumes=dict(required=False, type='list', elements='str'),
            account_id=dict(required=False, type='str'),
            virtual_network_id=dict(required=False, type='int'),
            virtual_network_tags=dict(required=False, type='list', elements='str'),
            attributes=dict(required=False, type='dict'),
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            required_if=[
                ('state', 'present', ['account_id'])
            ],
            supports_check_mode=True
        )

        input_params = self.module.params

        # Set up state variables
        self.state = input_params['state']
        self.from_name = input_params['from_name']
        self.access_group_name = input_params['name']
        self.initiators = input_params['initiators']
        self.volumes = input_params['volumes']
        self.account_id = input_params['account_id']
        self.virtual_network_id = input_params['virtual_network_id']
        self.virtual_network_tags = input_params['virtual_network_tags']
        self.attributes = input_params['attributes']

        if HAS_SF_SDK is False:
            self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
        else:
            self.sfe = netapp_utils.create_sf_connection(module=self.module)

        self.elementsw_helper = NaElementSWModule(self.sfe)

        # add telemetry attributes
        if self.attributes is not None:
            self.attributes.update(self.elementsw_helper.set_element_attributes(source='na_elementsw_access_group'))
        else:
            self.attributes = self.elementsw_helper.set_element_attributes(source='na_elementsw_access_group')

    def get_access_group(self, name):
        """
        Get Access Group
            :description: Get Access Group object for a given name

            :return: object (Group object)
            :rtype: object (Group object)
        """
        access_groups_list = self.sfe.list_volume_access_groups()
        group_obj = None

        for group in access_groups_list.volume_access_groups:
            # Check and get access_group object for a given name
            if str(group.volume_access_group_id) == name:
                group_obj = group
            elif group.name == name:
                group_obj = group

        return group_obj

    def get_account_id(self):
        # Validate account id
        # Return account_id if found, None otherwise
        try:
            account_id = self.elementsw_helper.account_exists(self.account_id)
            return account_id
        except solidfire.common.ApiServerError:
            return None

    def get_volume_ids(self):
        # Validate volume_ids
        # Return volume ids if found, fail if not found
        volume_ids = []
        for volume in self.volumes:
            volume_id = self.elementsw_helper.volume_exists(volume, self.account_id)
            if volume_id:
                volume_ids.append(volume_id)
            else:
                self.module.fail_json(msg='Specified volume %s does not exist' % volume)
        return volume_ids

    def create_access_group(self):
        """
        Create the Access Group
        """
        try:
            self.sfe.create_volume_access_group(name=self.access_group_name,
                                                initiators=self.initiators,
                                                volumes=self.volumes,
                                                virtual_network_id=self.virtual_network_id,
                                                virtual_network_tags=self.virtual_network_tags,
                                                attributes=self.attributes)
        except Exception as e:
            self.module.fail_json(msg="Error creating volume access group %s: %s" %
                                  (self.access_group_name, to_native(e)), exception=traceback.format_exc())

    def delete_access_group(self):
        """
        Delete the Access Group
        """
        try:
            self.sfe.delete_volume_access_group(volume_access_group_id=self.group_id)

        except Exception as e:
            self.module.fail_json(msg="Error deleting volume access group %s: %s" %
                                  (self.access_group_name, to_native(e)),
                                  exception=traceback.format_exc())

    def update_access_group(self):
        """
        Update the Access Group if the access_group already exists
        """
        try:
            self.sfe.modify_volume_access_group(volume_access_group_id=self.group_id,
                                                virtual_network_id=self.virtual_network_id,
                                                virtual_network_tags=self.virtual_network_tags,
                                                initiators=self.initiators,
                                                volumes=self.volumes,
                                                attributes=self.attributes)
        except Exception as e:
            self.module.fail_json(msg="Error updating volume access group %s: %s" %
                                  (self.access_group_name, to_native(e)), exception=traceback.format_exc())

    def rename_access_group(self):
        """
        Rename the Access Group to the new name
        """
        try:
            self.sfe.modify_volume_access_group(volume_access_group_id=self.from_group_id,
                                                virtual_network_id=self.virtual_network_id,
                                                virtual_network_tags=self.virtual_network_tags,
                                                name=self.access_group_name,
                                                initiators=self.initiators,
                                                volumes=self.volumes,
                                                attributes=self.attributes)
        except Exception as e:
            self.module.fail_json(msg="Error updating volume access group %s: %s" %
                                  (self.from_name, to_native(e)), exception=traceback.format_exc())

    def apply(self):
        """
        Process the access group operation on the Element Software Cluster
        """
        changed = False
        action = None

        input_account_id = self.account_id
        if self.account_id is not None:
            self.account_id = self.get_account_id()
        if self.state == 'present' and self.volumes is not None:
            if self.account_id:
                self.volumes = self.get_volume_ids()
            else:
                self.module.fail_json(msg='Error: Specified account id "%s" does not exist.' % str(input_account_id))

        group_detail = self.get_access_group(self.access_group_name)

        if group_detail is not None:
            # If access group found
            self.group_id = group_detail.volume_access_group_id

            if self.state == "absent":
                action = 'delete'
                changed = True
            else:
                # If state - present, check for any parameter of exising group needs modification.
                if self.volumes is not None and len(self.volumes) > 0:
                    # Compare the volume list
                    if not group_detail.volumes:
                        # If access group does not have any volume attached
                        action = 'update'
                        changed = True
                    else:
                        for volumeID in group_detail.volumes:
                            if volumeID not in self.volumes:
                                action = 'update'
                                changed = True
                                break

                elif self.initiators is not None and group_detail.initiators != self.initiators:
                    action = 'update'
                    changed = True

                elif self.virtual_network_id is not None or self.virtual_network_tags is not None:
                    action = 'update'
                    changed = True

        else:
            # access_group does not exist
            if self.state == "present" and self.from_name is not None:
                group_detail = self.get_access_group(self.from_name)
                if group_detail is not None:
                    # If resource pointed by from_name exists, rename the access_group to name
                    self.from_group_id = group_detail.volume_access_group_id
                    action = 'rename'
                    changed = True
                else:
                    # If resource pointed by from_name does not exists, error out
                    self.module.fail_json(msg="Resource does not exist : %s" % self.from_name)
            elif self.state == "present":
                # If from_name is not defined, Create from scratch.
                action = 'create'
                changed = True

        if changed and not self.module.check_mode:
            if action == 'create':
                self.create_access_group()
            elif action == 'rename':
                self.rename_access_group()
            elif action == 'update':
                self.update_access_group()
            elif action == 'delete':
                self.delete_access_group()

        self.module.exit_json(changed=changed)


def main():
    """
    Main function
    """
    na_elementsw_access_group = ElementSWAccessGroup()
    na_elementsw_access_group.apply()


if __name__ == '__main__':
    main()