#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright: (c) 2020, Infinidat # 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 from infi.dtypes.iqn import make_iscsi_name ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = r''' --- module: infini_user version_added: '2.10' short_description: Create, Delete and Modify a User on Infinibox description: - This module creates, deletes or modifies a user on Infinibox. author: David Ohlemacher (@ohlemacher) options: user_name: description: - The new user's Name. Once a user is created, the user_name may not be changed from this module. It may be changed from the UI or from infinishell. required: true user_email: description: - The new user's Email address required: true user_password: description: - The new user's password required: true user_role: description: - The user's role required: true choices: [ "admin", "pool_admin", "read_only" ] user_enabled: description: - Specify whether to enable the user type: bool required: false default: true user_pool: description: - Use with role==pool_admin. Specify the new user's pool. required: False state: description: - Creates/Modifies user when present or removes when absent required: false default: present choices: [ "stat", "reset_password", "present", "absent" ] extends_documentation_fragment: - infinibox ''' EXAMPLES = r''' - name: Create new user infini_user: user_name: foo_user user_email: foo@example.com user_password: secret2 user_role: pool_admin user_enabled: false pool: foo_pool state: present password: secret1 system: ibox001 ''' # RETURN = r''' # ''' from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible_collections.infinidat.infinibox.plugins.module_utils.infinibox import \ HAS_INFINISDK, api_wrapper, infinibox_argument_spec, \ get_system, get_user, get_pool, unixMillisecondsToDate, merge_two_dicts @api_wrapper def create_user(module, system): if not module.check_mode: user = system.users.create(name=module.params['user_name'], password=module.params['user_password'], email=module.params['user_email'], enabled=module.params['user_enabled'], ) # Set the user's role user.update_role(module.params['user_role']) if module.params['user_pool']: assert module.params['user_role'] == 'pool_admin', \ 'user_pool set, but role is not pool_admin' # Add the user to the pool's owners pool = system.pools.get(name=module.params['user_pool']) add_user_to_pool_owners(user, pool) changed = True return changed def add_user_to_pool_owners(user, pool): """ Find the current list of pool owners and add user using pool.set_owners(). set_owners() replaces the current owners with the list of new owners. So, get owners, add user, then set owners. Further, we need to know if the owners changed. Use sets of owners to compare. """ #print("add_user_to_pool_owners(): start") changed = False pool_fields = pool.get_fields(from_cache=True, raw_value=True) pool_owners = pool_fields.get('owners', []) #print('pool_owners:', pool_owners, 'pool_owners type:', type(pool_owners)) #print('user:', user) #print('pool:', pool) pool_owners_set = set(pool_owners) #print('pool_owners_set:', pool_owners_set) new_pool_owners_set = pool_owners_set.copy() new_pool_owners_set.add(user.id) #print('new_pool_owners_set:', new_pool_owners_set) if pool_owners_set != new_pool_owners_set: pool.set_owners([user]) #(pool_owners.append(user)) changed = True #print("changed:", changed) #print("add_user_to_pool_owners(): end") return changed def remove_user_from_pool_owners(user, pool): changed = False pool_fields = pool.get_fields(from_cache=True, raw_value=True) pool_owners = pool_fields.get('owners', []) try: pool_owners.remove(user) pool.set_owners(pool_owners) changed = True except ValueError: pass # User is not a pool owner return changed @api_wrapper def update_user(module, system, user): #print("update_user()") assert user is not None, "Cannot update user. User not found." changed = False fields = user.get_fields(from_cache=True, raw_value=True) if fields.get('role') != module.params['user_role'].upper(): user.update_field('role', module.params['user_role']) changed = True if fields.get('enabled') != module.params['user_enabled']: user.update_field('enabled', module.params['user_enabled']) changed = True if fields.get('email') != module.params['user_email']: user.update_field('email', module.params['user_email']) changed = True if module.params['user_pool']: try: pool_name = module.params['user_pool'] pool = system.pools.get(name=pool_name) except Exception as err: module.fail_json(msg='Cannot find pool {0}: {1}'.format(pool_name, err)) if add_user_to_pool_owners(user, pool): changed = True return changed @api_wrapper def reset_user_password(module, system, user): #print("update_user()") assert user is not None, "Cannot change user password. User not found." user.update_password(module.params['user_password']) @api_wrapper def delete_user(module, user): if not user: return False changed = True if not module.check_mode: # May raise APICommandFailed if mapped, etc. user.delete() return changed def get_sys_user(module): system = get_system(module) user = get_user(module, system) #print("get_sys_user(): user:", user) return (system, user) def get_user_fields(user): pools = user.get_owned_pools() pool_names = [pool.get_field('name') for pool in pools] fields = user.get_fields(from_cache=True, raw_value=True) field_dict = dict( id=user.id, enabled=fields.get('enabled', None), role=fields.get('role', None), email=fields.get('email', None), pools=pool_names, ) return field_dict def handle_stat(module): system, user = get_sys_user(module) user_name = module.params["user_name"] if not user: module.fail_json(msg='User {0} not found'.format(user_name)) field_dict = get_user_fields(user) result = dict( changed=False, msg='User stat found' ) result = merge_two_dicts(result, field_dict) module.exit_json(**result) def handle_present(module): system, user = get_sys_user(module) user_name = module.params["user_name"] if not user: changed = create_user(module, system) msg='User {0} created'.format(user_name) else: changed = update_user(module, system, user) if changed: msg='User {0} updated'.format(user_name) else: msg='User {0} update required no changes'.format(user_name) module.exit_json(changed=changed, msg=msg) def handle_absent(module): system, user = get_sys_user(module) user_name = module.params["user_name"] if not user: changed = False msg="User {0} already absent".format(user_name) else: changed = delete_user(module, user) msg="User {0} removed".format(user_name) module.exit_json(changed=changed, msg=msg) def handle_reset_password(module): system, user = get_sys_user(module) user_name = module.params["user_name"] if not user: msg = 'Cannot change password. User {0} not found'.format(user_name) module.fail_json(msg=msg) else: reset_user_password(module, system, user) msg='User {0} password changed'.format(user_name) module.exit_json(changed=True, msg=msg) def execute_state(module): state = module.params['state'] try: if state == 'stat': handle_stat(module) elif state == 'present': handle_present(module) elif state == 'absent': handle_absent(module) elif state == 'reset_password': handle_reset_password(module) else: module.fail_json(msg='Internal handler error. Invalid state: {0}'.format(state)) finally: system = get_system(module) system.logout() def check_options(module): state = module.params['state'] user_role = module.params['user_role'] user_pool = module.params['user_pool'] if state == 'present': if user_role == 'pool_admin' and not user_pool: module.fail_json(msg='user_role "pool_admin" requires a user_pool to be provided') if user_role != 'pool_admin' and user_pool: module.fail_json(msg='Only user_role "pool_admin" should have a user_pool provided') valid_keys = ['user_email', 'user_password', 'user_role', 'user_enabled'] for valid_key in valid_keys: try: _ = module.params[valid_key] except KeyError: msg = 'For state "present", options {0} are required'.format(", ".join(valid_keys)) module.fail_json(msg=msg) elif state == 'reset_password': if not module.params['user_password']: msg = 'For state "reset_password", user_password is required' def main(): argument_spec = infinibox_argument_spec() argument_spec.update( dict( user_name=dict(required=True), user_email=dict(required=False), user_password=dict(required=False, no_log=True), user_role=dict(required=False, choices=['admin', 'pool_admin', 'read_only']), user_enabled=dict(required=False, type='bool'), user_pool=dict(required=False), state=dict(default='present', choices=['stat', 'reset_password', 'present', 'absent']), ) ) module = AnsibleModule(argument_spec, supports_check_mode=True) if not HAS_INFINISDK: module.fail_json(msg=missing_required_lib('infinisdk')) check_options(module) execute_state(module) if __name__ == '__main__': main()