1#!/usr/local/bin/python3.8
2
3# # Copyright 2020 Hewlett Packard Enterprise Development LP
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6# file except in compliance with the License. You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software distributed
11# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
12# OF ANY KIND, either express or implied. See the License for the specific
13# language governing permissions and limitations under the License.
14
15# author Alok Ranjan (alok.ranjan2@hpe.com)
16
17from __future__ import absolute_import, division, print_function
18__metaclass__ = type
19
20DOCUMENTATION = r'''
21---
22author:
23  - HPE Nimble Storage Ansible Team (@ar-india) <nimble-dcs-storage-automation-eng@hpe.com>
24description: Manage the users on an HPE Nimble Storage group.
25module: hpe_nimble_user
26options:
27  auth_password:
28    required: False
29    type: str
30    description:
31    - Authorization password for changing password.
32  change_name:
33    required: False
34    type: str
35    description:
36    - Change name of the existing user.
37  description:
38    required: False
39    type: str
40    description:
41    - Description of the user.
42  disabled:
43    required: False
44    type: bool
45    description:
46    - User is currently disabled.
47  email_addr:
48    required: False
49    type: str
50    description:
51    - Email address of the user.
52  full_name:
53    required: False
54    type: str
55    description:
56    - Fully qualified name of the user.
57  inactivity_timeout:
58    required: False
59    type: int
60    default: 0
61    description:
62    - The amount of time that the user session is inactive before timing out. A value of 0 indicates that the timeout is taken from the group setting.
63  name:
64    required: True
65    type: str
66    description:
67    - Name of the user.
68  user_password:
69    required: False
70    type: str
71    description:
72    - User's login password.
73  role:
74    required: False
75    choices:
76    - administrator
77    - poweruser
78    - operator
79    - guest
80    type: str
81    description:
82    - Role of the user. Default is 'guest'.
83  state:
84    required: True
85    choices:
86    -  create
87    -  present
88    -  absent
89    type: str
90    description:
91    - The user operation.
92  unlock:
93    required: False
94    type: bool
95    description:
96    - Unlock the user.
97extends_documentation_fragment: hpe.nimble.hpe_nimble
98short_description: Manage the HPE Nimble Storage users
99version_added: "1.0.0"
100notes:
101  - This module does not support C(check_mode).
102'''
103
104EXAMPLES = r'''
105
106# if state is create, then create user, fails if it exist or cannot create
107# if state is present, then create user if not present, else success
108- name: Create user
109  hpe.nimble.hpe_nimble_user:
110    host: "{{ host }}"
111    username: "{{ username }}"
112    password: "{{ password }}"
113    name: "{{ name }}"
114    description: "{{ description }}"
115    state: "{{ state | default('present') }}"
116
117- name: Delete user
118  hpe.nimble.hpe_nimble_user:
119    host: "{{ host }}"
120    username: "{{ username }}"
121    password: "{{ password }}"
122    name: "{{ name }}"
123    state: "absent"
124
125- name: Unlock user
126  hpe.nimble.hpe_nimble_user:
127    host: "{{ host }}"
128    username: "{{ username }}"
129    password: "{{ password }}"
130    name: "{{ name }}"
131    state: "present"
132    unlock: true
133
134'''
135RETURN = r'''
136'''
137
138from ansible.module_utils.basic import AnsibleModule
139try:
140    from nimbleclient.v1 import client
141except ImportError:
142    client = None
143from ansible_collections.hpe.nimble.plugins.module_utils.hpe_nimble import __version__ as NIMBLE_ANSIBLE_VERSION
144import ansible_collections.hpe.nimble.plugins.module_utils.hpe_nimble as utils
145
146
147def create_user(
148        client_obj,
149        user_name,
150        password,
151        **kwargs):
152
153    if utils.is_null_or_empty(user_name):
154        return (False, False, "Create user failed as user is not present.", {}, {})
155    if utils.is_null_or_empty(password):
156        return (False, False, "Create user failed as password is not present.", {}, {})
157
158    try:
159        user_resp = client_obj.users.get(id=None, name=user_name)
160        if utils.is_null_or_empty(user_resp):
161            params = utils.remove_null_args(**kwargs)
162            user_resp = client_obj.users.create(name=user_name, password=password, **params)
163            return (True, True, f"User '{user_name}' created successfully.", {}, user_resp.attrs)
164        else:
165            return (False, False, f"User '{user_name}' cannot be created as it is already present in given state.", {}, {})
166    except Exception as ex:
167        return (False, False, f"User creation failed | {ex}", {}, {})
168
169
170def update_user(
171        client_obj,
172        user_name,
173        **kwargs):
174
175    if utils.is_null_or_empty(user_name):
176        return (False, False, "Update user failed as user is not present.", {}, {})
177
178    try:
179        user_resp = client_obj.users.get(id=None, name=user_name)
180        if utils.is_null_or_empty(user_resp):
181            return (False, False, f"User '{user_name}' cannot be updated as it is not present.", {}, {})
182
183        changed_attrs_dict, params = utils.remove_unchanged_or_null_args(user_resp, **kwargs)
184        if changed_attrs_dict.__len__() > 0:
185            user_resp = client_obj.users.update(id=user_resp.attrs.get("id"), **params)
186            return (True, True, f"User '{user_name}' already present. Modified the following attributes '{changed_attrs_dict}'",
187                    changed_attrs_dict, user_resp.attrs)
188        else:
189            return (True, False, f"User '{user_resp.attrs.get('name')}' already present in given state.", {}, user_resp.attrs)
190    except Exception as ex:
191        return (False, False, f"User update failed | {ex}", {}, {})
192
193
194def delete_user(
195        client_obj,
196        user_name):
197
198    if utils.is_null_or_empty(user_name):
199        return (False, False, "Delete user failed as user is not present.", {})
200
201    try:
202        user_resp = client_obj.users.get(id=None, name=user_name)
203        if utils.is_null_or_empty(user_resp):
204            return (False, False, f"User '{user_name}' cannot be deleted as it is not present.", {})
205
206        client_obj.users.delete(id=user_resp.attrs.get("id"))
207        return (True, True, f"Deleted user '{user_name}' successfully.", {})
208    except Exception as ex:
209        return (False, False, f"Delete user failed | {ex}", {})
210
211
212def unlock_user(
213        client_obj,
214        user_name):
215
216    if utils.is_null_or_empty(user_name):
217        return (False, False, "Unlock user failed as user name is not present.", {})
218
219    try:
220        user_resp = client_obj.users.get(id=None, name=user_name)
221        if utils.is_null_or_empty(user_resp):
222            return (False, False, f"User '{user_name}' cannot be unlocked as it is not present.", {})
223
224        client_obj.users.unlock(id=user_resp.attrs.get("id"))
225        return (True, True, f"Unlocked user '{user_name}' successfully.", {})
226    except Exception as ex:
227        return (False, False, f"Unlock user failed | {ex}", {})
228
229
230def main():
231
232    fields = {
233        "state": {
234            "required": True,
235            "choices": ['create',
236                        'present',
237                        'absent'
238                        ],
239            "type": "str"
240        },
241        "change_name": {
242            "required": False,
243            "type": "str"
244        },
245        "name": {
246            "required": True,
247            "type": "str"
248        },
249        "description": {
250            "required": False,
251            "type": "str"
252        },
253        "role": {
254            "required": False,
255            "choices": ['administrator',
256                        'poweruser',
257                        'operator',
258                        'guest'
259                        ],
260            "type": "str"
261        },
262        "user_password": {
263            "required": False,
264            "type": "str",
265            "no_log": True
266        },
267        "inactivity_timeout": {
268            "required": False,
269            "type": "int"
270        },
271        "full_name": {
272            "required": False,
273            "type": "str"
274        },
275        "email_addr": {
276            "required": False,
277            "type": "str"
278        },
279        "disabled": {
280            "required": False,
281            "type": "bool"
282        },
283        "auth_password": {
284            "required": False,
285            "type": "str",
286            "no_log": True
287        },
288        "unlock": {
289            "required": False,
290            "type": "bool"
291        }
292    }
293    default_fields = utils.basic_auth_arg_fields()
294    fields.update(default_fields)
295    required_if = [('state', 'create', ['user_password'])]
296
297    module = AnsibleModule(argument_spec=fields, required_if=required_if)
298    if client is None:
299        module.fail_json(msg='Python nimble-sdk could not be found.')
300
301    hostname = module.params["host"]
302    username = module.params["username"]
303    password = module.params["password"]
304    state = module.params["state"]
305    user_name = module.params["name"]
306    change_name = module.params["change_name"]
307    description = module.params["description"]
308    role = module.params["role"]
309    user_password = module.params["user_password"]
310    inactivity_timeout = module.params["inactivity_timeout"]
311    full_name = module.params["full_name"]
312    email_addr = module.params["email_addr"]
313    disabled = module.params["disabled"]
314    auth_password = module.params["auth_password"]
315    unlock = module.params["unlock"]
316
317    if (username is None or password is None or hostname is None):
318        module.fail_json(
319            msg="Missing variables: hostname, username and password is mandatory.")
320
321    # defaults
322    return_status = changed = False
323    msg = "No task to run."
324    resp = None
325    try:
326        client_obj = client.NimOSClient(
327            hostname,
328            username,
329            password,
330            f"HPE Nimble Ansible Modules v{NIMBLE_ANSIBLE_VERSION}"
331        )
332
333        # States
334        if ((unlock is None or unlock is False) and (state == "create" or state == "present")):
335            if not client_obj.users.get(id=None, name=user_name) or state == "create":
336                return_status, changed, msg, changed_attrs_dict, resp = create_user(
337                    client_obj,
338                    user_name,
339                    user_password,
340                    description=description,
341                    role=role,
342                    inactivity_timeout=inactivity_timeout,
343                    full_name=full_name,
344                    email_addr=email_addr,
345                    disabled=disabled)
346            else:
347                # update op
348                return_status, changed, msg, changed_attrs_dict, resp = update_user(
349                    client_obj,
350                    user_name,
351                    name=change_name,
352                    password=user_password,
353                    description=description,
354                    role=role,
355                    inactivity_timeout=inactivity_timeout,
356                    full_name=full_name,
357                    email_addr=email_addr,
358                    disabled=disabled,
359                    auth_password=auth_password)
360
361        elif state == "absent":
362            return_status, changed, msg, changed_attrs_dict = delete_user(client_obj, user_name)
363
364        elif state == "present" and unlock is True:
365            return_status, changed, msg, changed_attrs_dict = unlock_user(client_obj, user_name)
366
367    except Exception as ex:
368        # failed for some reason.
369        msg = str(ex)
370
371    if return_status:
372        if utils.is_null_or_empty(resp):
373            module.exit_json(return_status=return_status, changed=changed, msg=msg)
374        else:
375            module.exit_json(return_status=return_status, changed=changed, msg=msg, attrs=resp)
376    else:
377        module.fail_json(return_status=return_status, changed=changed, msg=msg)
378
379
380if __name__ == '__main__':
381    main()
382