Change network *_user modules to use configured_password to set a users password (#28187)

* WIP, update eos_user args

* refactor password for ios_user

* add eos tests, fix ios tests

* fixed password check

* refactor iosxr_user password

* fixed password arg for nxos

* [WIP] fix vyos_user password

* fix vyos tests

* update docs for net_user

* fix typo

* fix eos tests

* add warning when attempting to use password arg

* fix sanity/unit tests

* fix eos unit tests

* fix vyos_user aggregate

* fix typo in eos documentation string

* re add configured_password to vyos tests after rebase
This commit is contained in:
David Newswanger 2017-08-17 09:36:44 -04:00 committed by Trishna Guha
commit b818e986b6
18 changed files with 329 additions and 70 deletions

View file

@ -49,12 +49,13 @@ options:
exclusive with the C(aggregate) argument.
Please note that this option is not same as C(provider username).
version_added: "2.4"
password:
configured_password:
description:
- The password to be configured on the remote Arista EOS device. The
password needs to be provided in clear and it will be encrypted
on the device.
Please note that this option is not same as C(provider password).
version_added: "2.4"
update_password:
description:
- Since passwords are encrypted in the device running config, this
@ -125,8 +126,8 @@ EXAMPLES = """
- name: Change Password for User netop
eos_user:
name: netop
password: "{{ new_password }}"
username: netop
configured_password: "{{ new_password }}"
update_password: always
state: present
"""
@ -182,9 +183,9 @@ def map_obj_to_commands(updates, module):
if needs_update('privilege'):
add('privilege %s' % want['privilege'])
if needs_update('password'):
if needs_update('configured_password'):
if update_password == 'always' or not have:
add('secret %s' % want['password'])
add('secret %s' % want['configured_password'])
if needs_update('sshkey'):
add('sshkey %s' % want['sshkey'])
@ -233,7 +234,7 @@ def map_config_to_obj(module):
'name': user,
'state': 'present',
'nopassword': 'nopassword' in cfg,
'password': None,
'configured_password': None,
'sshkey': parse_sshkey(cfg),
'privilege': parse_privilege(cfg),
'role': parse_role(cfg)
@ -286,7 +287,7 @@ def map_params_to_obj(module):
for item in collection:
get_value = partial(get_param_value, item=item, module=module)
item['password'] = get_value('password')
item['configured_password'] = get_value('configured_password')
item['nopassword'] = get_value('nopassword')
item['privilege'] = get_value('privilege')
item['role'] = get_value('role')
@ -318,7 +319,7 @@ def main():
aggregate=dict(type='list', aliases=['collection', 'users']),
name=dict(),
password=dict(no_log=True),
configured_password=dict(no_log=True),
nopassword=dict(type='bool'),
update_password=dict(default='always', choices=['on_create', 'always']),
@ -339,6 +340,12 @@ def main():
supports_check_mode=True)
warnings = list()
if module.params['password'] and not module.params['configured_password']:
warnings.append(
'The "password" argument is used to authenticate the current connection. ' +
'To set a user password use "configured_password" instead.'
)
check_args(module, warnings)
result = {'changed': False}

View file

@ -48,7 +48,7 @@ options:
This argument accepts a string value and is mutually exclusive
with the C(aggregate) argument.
Please note that this option is not same as C(provider username).
password:
configured_password:
description:
- The password to be configured on the Cisco IOS device. The
password needs to be provided in clear and it will be encrypted
@ -127,7 +127,7 @@ EXAMPLES = """
- name: Change Password for User netop
ios_user:
name: netop
password: "{{ new_password }}"
configured_password: "{{ new_password }}"
update_password: always
state: present
@ -207,9 +207,9 @@ def map_obj_to_commands(updates, module):
if needs_update(want, have, 'privilege'):
add(commands, want, 'privilege %s' % want['privilege'])
if needs_update(want, have, 'password'):
if needs_update(want, have, 'configured_password'):
if update_password == 'always' or not have:
add(commands, want, 'secret %s' % want['password'])
add(commands, want, 'secret %s' % want['configured_password'])
if needs_update(want, have, 'nopassword'):
if want['nopassword']:
@ -249,7 +249,7 @@ def map_config_to_obj(module):
'name': user,
'state': 'present',
'nopassword': 'nopassword' in cfg,
'password': None,
'configured_password': None,
'privilege': parse_privilege(cfg),
'view': parse_view(cfg)
}
@ -301,7 +301,7 @@ def map_params_to_obj(module):
for item in aggregate:
get_value = partial(get_param_value, item=item, module=module)
item['password'] = get_value('password')
item['configured_password'] = get_value('configured_password')
item['nopassword'] = get_value('nopassword')
item['privilege'] = get_value('privilege')
item['view'] = get_value('view')
@ -330,7 +330,7 @@ def main():
element_spec = dict(
name=dict(),
password=dict(no_log=True),
configured_password=dict(no_log=True),
nopassword=dict(type='bool'),
update_password=dict(default='always', choices=['on_create', 'always']),
@ -360,6 +360,12 @@ def main():
supports_check_mode=True)
warnings = list()
if module.params['password'] and not module.params['configured_password']:
warnings.append(
'The "password" argument is used to authenticate the current connection. ' +
'To set a user password use "configured_password" instead.'
)
check_args(module, warnings)
result = {'changed': False}

View file

@ -37,7 +37,7 @@ options:
This argument accepts a string value and is mutually exclusive
with the C(aggregate) argument.
Please note that this option is not same as C(provider username).
password:
configured_password:
description:
- The password to be configured on the Cisco IOS XR device. The
password needs to be provided in clear and it will be encrypted
@ -81,7 +81,7 @@ EXAMPLES = """
- name: create a new user
iosxr_user:
name: ansible
password: test
configured_password: test
state: present
- name: remove all users except admin
iosxr_user:
@ -96,7 +96,7 @@ EXAMPLES = """
- name: Change Password for User netop
iosxr_user:
name: netop
password: "{{ new_password }}"
configured_password: "{{ new_password }}"
update_password: always
state: present
"""
@ -142,16 +142,16 @@ def map_obj_to_commands(updates, module):
user_cmd = 'username ' + name
commands.append(user_cmd)
if w['password']:
commands.append(user_cmd + ' secret ' + w['password'])
if w['configured_password']:
commands.append(user_cmd + ' secret ' + w['configured_password'])
if w['group']:
commands.append(user_cmd + ' group ' + w['group'])
elif state == 'present' and obj_in_have:
user_cmd = 'username ' + name
if module.params['update_password'] == 'always' and w['password']:
commands.append(user_cmd + ' secret ' + w['password'])
if module.params['update_password'] == 'always' and w['configured_password']:
commands.append(user_cmd + ' secret ' + w['configured_password'])
if w['group'] and w['group'] != obj_in_have['group']:
commands.append(user_cmd + ' group ' + w['group'])
@ -181,7 +181,7 @@ def map_config_to_obj(module):
obj = {
'name': name,
'state': 'present',
'password': None,
'configured_password': None,
'group': group
}
instances.append(obj)
@ -232,7 +232,7 @@ def map_params_to_obj(module):
for item in aggregate:
get_value = partial(get_param_value, item=item, module=module)
item['password'] = get_value('password')
item['configured_password'] = get_value('configured_password')
item['group'] = get_value('group')
item['state'] = get_value('state')
objects.append(item)
@ -247,7 +247,7 @@ def main():
aggregate=dict(type='list', aliases=['users', 'collection']),
name=dict(),
password=dict(no_log=True),
configured_password=dict(no_log=True),
update_password=dict(default='always', choices=['on_create', 'always']),
group=dict(aliases=['role']),
@ -264,6 +264,12 @@ def main():
supports_check_mode=True)
warnings = list()
if module.params['password'] and not module.params['configured_password']:
warnings.append(
'The "password" argument is used to authenticate the current connection. ' +
'To set a user password use "configured_password" instead.'
)
check_args(module, warnings)
result = {'changed': False}

View file

@ -51,6 +51,13 @@ options:
exclusive with the C(aggregate) argument.
required: false
default: null
configured_password:
description:
- The password to be configured on the network device. The
password needs to be provided in cleartext and it will be encrypted
on the device.
Please note that this option is not same as C(provider password).
version_added: "2.4"
update_password:
description:
- Since passwords are encrypted in the device running config, this
@ -178,9 +185,9 @@ def map_obj_to_commands(updates, module):
if want['state'] == 'present' and not have:
commands.append('username %s' % want['name'])
if needs_update('password'):
if needs_update('configured_password'):
if update_password == 'always' or not have:
add('password %s' % want['password'])
add('password %s' % want['configured_password'])
if needs_update('sshkey'):
add('sshkey %s' % want['sshkey'])
@ -221,7 +228,7 @@ def map_config_to_obj(module):
for item in to_list(data['TABLE_template']['ROW_template']):
objects.append({
'name': item['usr_name'],
'password': parse_password(item),
'configured_password': parse_password(item),
'sshkey': item.get('sshkey_info'),
'roles': parse_roles(item),
'state': 'present'
@ -266,7 +273,7 @@ def map_params_to_obj(module):
for item in collection:
get_value = partial(get_param_value, item=item, module=module)
item.update({
'password': get_value('password'),
'configured_password': get_value('configured_password'),
'sshkey': get_value('sshkey'),
'roles': get_value('roles'),
'state': get_value('state')
@ -302,7 +309,7 @@ def main():
aggregate=dict(type='list', no_log=True, aliases=['collection', 'users']),
name=dict(),
password=dict(no_log=True),
configured_password=dict(no_log=True),
update_password=dict(default='always', choices=['on_create', 'always']),
roles=dict(type='list', aliases=['role']),
@ -325,6 +332,12 @@ def main():
result = {'changed': False}
warnings = list()
if module.params['password'] and not module.params['configured_password']:
warnings.append(
'The "password" argument is used to authenticate the current connection. ' +
'To set a user password use "configured_password" instead.'
)
check_args(module, warnings)
result['warnings'] = warnings

View file

@ -37,7 +37,7 @@ options:
This argument accepts a string value and is mutually exclusive
with the C(aggregate) argument.
Please note that this option is not same as C(provider username).
password:
configured_password:
description:
- The password to be configured on the remote network device. The
password needs to be provided in clear and it will be encrypted

View file

@ -53,7 +53,7 @@ options:
- The C(full_name) argument provides the full name of the user
account to be created on the remote device. This argument accepts
any text string value.
password:
configured_password:
description:
- The password to be configured on the VyOS device. The
password needs to be provided in clear and it will be encrypted
@ -95,7 +95,7 @@ EXAMPLES = """
- name: create a new user
vyos_user:
name: ansible
password: password
configured_password: password
state: present
- name: remove all users except admin
vyos_user:
@ -110,7 +110,7 @@ EXAMPLES = """
- name: Change Password for User netop
vyos_user:
name: netop
password: "{{ new_password }}"
configured_password: "{{ new_password }}"
update_password: always
state: present
"""
@ -166,9 +166,9 @@ def spec_to_commands(updates, module):
if needs_update(want, have, 'full_name'):
add(commands, want, "full-name %s" % want['full_name'])
if needs_update(want, have, 'password'):
if needs_update(want, have, 'configured_password'):
if update_password == 'always' or not have:
add(commands, want, 'authentication plaintext-password %s' % want['password'])
add(commands, want, 'authentication plaintext-password %s' % want['configured_password'])
return commands
@ -203,7 +203,7 @@ def config_to_dict(module):
obj = {
'name': user,
'state': 'present',
'password': None,
'configured_password': None,
'level': parse_level(cfg),
'full_name': parse_full_name(cfg)
}
@ -231,20 +231,20 @@ def map_params_to_obj(module):
if not module.params['name'] and module.params['purge']:
return list()
else:
aggregatelist = [{'name': module.params['name']}]
users = [{'name': module.params['name']}]
else:
aggregatelist = list()
users = list()
for item in aggregate:
if not isinstance(item, dict):
aggregatelist.append({'name': item})
users.append({'name': item})
else:
aggregatelist.append(item)
users.append(item)
objects = list()
for item in aggregatelist:
for item in users:
get_value = partial(get_param_value, item=item, module=module)
item['password'] = get_value('password')
item['configured_password'] = get_value('configured_password')
item['full_name'] = get_value('full_name')
item['level'] = get_value('level')
item['state'] = get_value('state')
@ -275,7 +275,7 @@ def main():
full_name=dict(),
level=dict(aliases=['role']),
password=dict(no_log=True),
configured_password=dict(no_log=True),
update_password=dict(default='always', choices=['on_create', 'always']),
purge=dict(type='bool', default=False),
@ -301,6 +301,12 @@ def main():
supports_check_mode=True)
warnings = list()
if module.params['password'] and not module.params['configured_password']:
warnings.append(
'The "password" argument is used to authenticate the current connection. ' +
'To set a user password use "configured_password" instead.'
)
check_args(module, warnings)
result = {'changed': False}