1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/network/icx/icx_user.py

391 lines
13 KiB
Python
Raw Normal View History

2020-03-09 10:11:07 +01:00
#!/usr/bin/python
# Copyright: Ansible Project
# 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 = '''
---
module: icx_user
author: "Ruckus Wireless (@Commscope)"
short_description: Manage the user accounts on Ruckus ICX 7000 series switches.
description:
- This module creates or updates user account on network devices. It allows playbooks to manage
either individual usernames or the aggregate of usernames in the
current running config. It also supports purging usernames from the
configuration that are not explicitly defined.
notes:
- Tested against ICX 10.1.
- For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html).
options:
aggregate:
description:
- The set of username objects to be configured on the remote
ICX device. The list entries can either be the username
or a hash of username and properties. This argument is mutually
exclusive with the C(name) argument.
aliases: ['users', 'collection']
type: list
suboptions:
name:
description:
- The username to be configured on the ICX device.
required: true
type: str
configured_password:
description: The password to be configured on the ICX device.
type: str
update_password:
description:
- This argument will instruct the module when to change the password. When
set to C(always), the password will always be updated in the device
and when set to C(on_create) the password will be updated only if
the username is created.
choices: ['on_create', 'always']
type: str
privilege:
description:
- The privilege level to be granted to the user
choices: ['0', '4', '5']
type: str
nopassword:
description:
- Defines the username without assigning
a password. This will allow the user to login to the system
without being authenticated by a password.
type: bool
state:
description:
- Configures the state of the username definition
as it relates to the device operational configuration. When set
to I(present), the username(s) should be configured in the device active
configuration and when set to I(absent) the username(s) should not be
in the device active configuration
choices: ['present', 'absent']
type: str
access_time:
description:
- This parameter indicates the time the file's access time should be set to.
Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now.
Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch
type: str
check_running_config:
description:
- Check running configuration. This can be set as environment variable.
Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter.
type: bool
name:
description:
- The username to be configured on the ICX device.
required: true
type: str
configured_password:
description: The password to be configured on the ICX device.
type: str
update_password:
description:
- This argument will instruct the module when to change the password. When
set to C(always), the password will always be updated in the device
and when set to C(on_create) the password will be updated only if
the username is created.
default: always
choices: ['on_create', 'always']
type: str
privilege:
description:
- The privilege level to be granted to the user
default: 0
choices: ['0', '4', '5']
type: str
nopassword:
description:
- Defines the username without assigning
a password. This will allow the user to login to the system
without being authenticated by a password.
type: bool
purge:
description:
- If set to true module will remove any previously
configured usernames on the device except the current defined set of users.
type: bool
default: false
state:
description:
- Configures the state of the username definition
as it relates to the device operational configuration. When set
to I(present), the username(s) should be configured in the device active
configuration and when set to I(absent) the username(s) should not be
in the device active configuration
default: present
choices: ['present', 'absent']
type: str
access_time:
description:
- This parameter indicates the time the file's access time should be set to.
Should be preserve when no modification is required, YYYYMMDDHHMM.SS when using default time format, or now.
Default is None meaning that preserve is the default for state=[file,directory,link,hard] and now is default for state=touch
type: str
check_running_config:
description:
- Check running configuration. This can be set as environment variable.
Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter.
type: bool
default: yes
'''
EXAMPLES = """
- name: create a new user without password
icx_user:
name: user1
nopassword: true
- name: create a new user with password
icx_user:
name: user1
configured_password: 'newpassword'
- name: remove users
icx_user:
name: user1
state: absent
- name: set user privilege level to 5
icx_user:
name: user1
privilege: 5
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- username ansible nopassword
- username ansible password-string alethea123
- no username ansible
- username ansible privilege 5
- username ansible enable
"""
from copy import deepcopy
import re
import base64
import hashlib
from functools import partial
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.connection import exec_command
from ansible.module_utils.six import iteritems
from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config
def get_param_value(key, item, module):
if not item.get(key):
value = module.params[key]
else:
value_type = module.argument_spec[key].get('type', 'str')
type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type]
type_checker(item[key])
value = item[key]
validator = globals().get('validate_%s' % key)
if all((value, validator)):
validator(value, module)
return value
def map_params_to_obj(module):
users = module.params['aggregate']
if not users:
if not module.params['name'] and module.params['purge']:
return list()
elif not module.params['name']:
module.fail_json(msg='username is required')
else:
aggregate = [{'name': module.params['name']}]
else:
aggregate = list()
for item in users:
if not isinstance(item, dict):
aggregate.append({'name': item})
elif 'name' not in item:
module.fail_json(msg='name is required')
else:
aggregate.append(item)
objects = list()
for item in aggregate:
get_value = partial(get_param_value, item=item, module=module)
item['configured_password'] = get_value('configured_password')
item['nopassword'] = get_value('nopassword')
item['privilege'] = get_value('privilege')
item['state'] = get_value('state')
objects.append(item)
return objects
def parse_privilege(data):
match = re.search(r'privilege (\S)', data, re.M)
if match:
return match.group(1)
def map_config_to_obj(module):
compare = module.params['check_running_config']
data = get_config(module, flags=['| include username'], compare=compare)
match = re.findall(r'(?:^(?:u|\s{2}u))sername (\S+)', data, re.M)
if not match:
return list()
instances = list()
for user in set(match):
regex = r'username %s .+$' % user
cfg = re.findall(regex, data, re.M)
cfg = '\n'.join(cfg)
obj = {
'name': user,
'state': 'present',
'nopassword': 'nopassword' in cfg,
'configured_password': None,
'privilege': parse_privilege(cfg)
}
instances.append(obj)
return instances
def map_obj_to_commands(updates, module):
commands = list()
state = module.params['state']
update_password = module.params['update_password']
def needs_update(want, have, x):
return want.get(x) and (want.get(x) != have.get(x))
def add(command, want, x):
command.append('username %s %s' % (want['name'], x))
for update in updates:
want, have = update
if want['state'] == 'absent':
commands.append(user_del_cmd(want['name']))
if needs_update(want, have, 'privilege'):
add(commands, want, 'privilege %s password %s' % (want['privilege'], want['configured_password']))
else:
if needs_update(want, have, 'configured_password'):
if update_password == 'always' or not have:
add(commands, want, '%spassword %s' % ('privilege ' + str(have.get('privilege')) +
" " if have.get('privilege') is not None else '', want['configured_password']))
if needs_update(want, have, 'nopassword'):
if want['nopassword']:
add(commands, want, 'nopassword')
if needs_update(want, have, 'access_time'):
add(commands, want, 'access-time %s' % want['access_time'])
if needs_update(want, have, 'expiry_days'):
add(commands, want, 'expires %s' % want['expiry_days'])
return commands
def update_objects(want, have):
updates = list()
for entry in want:
item = next((i for i in have if i['name'] == entry['name']), None)
if all((item is None, entry['state'] == 'present')):
updates.append((entry, {}))
elif all((have == [], entry['state'] == 'absent')):
for key, value in iteritems(entry):
if key not in ['update_password']:
updates.append((entry, item))
break
elif item:
for key, value in iteritems(entry):
if key not in ['update_password']:
if value is not None and value != item.get(key):
updates.append((entry, item))
break
return updates
def user_del_cmd(username):
return 'no username %s' % username
def main():
"""entry point for module execution
"""
element_spec = dict(
name=dict(),
configured_password=dict(no_log=True),
nopassword=dict(type='bool', default=False),
update_password=dict(default='always', choices=['on_create', 'always']),
privilege=dict(type='str', choices=['0', '4', '5']),
access_time=dict(type='str'),
state=dict(default='present', choices=['present', 'absent']),
check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG']))
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec['name'] = dict(required=True)
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['users', 'collection']),
purge=dict(type='bool', default=False)
)
argument_spec.update(element_spec)
mutually_exclusive = [('name', 'aggregate')]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
result = {'changed': False}
exec_command(module, 'skip')
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands(update_objects(want, have), module)
if module.params['purge']:
want_users = [x['name'] for x in want]
have_users = [x['name'] for x in have]
for item in set(have_users).difference(want_users):
if item != 'admin':
commands.append(user_del_cmd(item))
result["commands"] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()