mirror of https://github.com/ansible/ansible.git
Onyx username (#63897)
* Support username command to config users and edit capabilities * Add disconnected param to disconnect all session for such user * Edit yaml examples, add types to docs and edit the version * Add fullname attribute * Add no_log to password field and mutually_execlusive relations * Fix pep8 issue * remove debug linepull/65905/head
parent
557c8ab161
commit
ec0885cf05
@ -0,0 +1,290 @@
|
|||||||
|
#!/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: onyx_username
|
||||||
|
version_added: "2.10"
|
||||||
|
author: "Anas Shami (@anass)"
|
||||||
|
short_description: Configure username module
|
||||||
|
description:
|
||||||
|
- This module provides declarative management of users/roles
|
||||||
|
on Mellanox ONYX network devices.
|
||||||
|
notes:
|
||||||
|
options:
|
||||||
|
username:
|
||||||
|
description:
|
||||||
|
- Create/Edit user using username
|
||||||
|
type: str
|
||||||
|
required: True
|
||||||
|
full_name:
|
||||||
|
description:
|
||||||
|
- Set the full name of this user
|
||||||
|
type: str
|
||||||
|
nopassword:
|
||||||
|
description:
|
||||||
|
- Clear password for such user
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
password:
|
||||||
|
description:
|
||||||
|
- Set password fot such user
|
||||||
|
type: str
|
||||||
|
encrypted_password:
|
||||||
|
description:
|
||||||
|
- Decide the type of setted password (plain text or encrypted)
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
capability:
|
||||||
|
description:
|
||||||
|
- Grant capability to this user account
|
||||||
|
type: str
|
||||||
|
choices: ['monitor', 'unpriv', 'v_admin', 'admin']
|
||||||
|
reset_capability:
|
||||||
|
description:
|
||||||
|
- Reset capability to this user account
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
disconnected:
|
||||||
|
description:
|
||||||
|
- Disconnect all sessions of this user
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
disabled:
|
||||||
|
description:
|
||||||
|
- Disable means of logging into this account
|
||||||
|
type: str
|
||||||
|
choices: ['none', 'login', 'password', 'all']
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- Set state of the given account
|
||||||
|
default: present
|
||||||
|
type: str
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = """
|
||||||
|
- name: create new user
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
|
||||||
|
- name: set the user full-name
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
full_name: anasshami
|
||||||
|
|
||||||
|
- name: set the user encrypted password
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
password: 12345
|
||||||
|
encrypted_password: True
|
||||||
|
|
||||||
|
- name: set the user capability
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
capability: monitor
|
||||||
|
|
||||||
|
- name: reset the user capability
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
reset_capability: True
|
||||||
|
|
||||||
|
- name: remove the user configuration
|
||||||
|
onyx_username:
|
||||||
|
username: anass
|
||||||
|
state: absent
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
commands:
|
||||||
|
description: The list of configuration mode commands to send to the device.
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample:
|
||||||
|
- username *
|
||||||
|
- username * password *
|
||||||
|
- username * nopassword
|
||||||
|
- username * disable login
|
||||||
|
- username * capability admin
|
||||||
|
- no username *
|
||||||
|
- no username * disable
|
||||||
|
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.network.onyx.onyx import BaseOnyxModule, show_cmd
|
||||||
|
|
||||||
|
|
||||||
|
class OnyxUsernameModule(BaseOnyxModule):
|
||||||
|
ACCOUNT_STATE = {
|
||||||
|
'Account locked out': dict(disabled='all'),
|
||||||
|
'No password required for login': dict(nopassword=True),
|
||||||
|
'Local password login disabled': dict(disabled='password'),
|
||||||
|
'Account disabled': dict(disabled='all')
|
||||||
|
}
|
||||||
|
ENCRYPTED_ID = 7
|
||||||
|
|
||||||
|
def init_module(self):
|
||||||
|
"""
|
||||||
|
module initialization
|
||||||
|
"""
|
||||||
|
element_spec = dict()
|
||||||
|
|
||||||
|
argument_spec = dict(state=dict(choices=['absent', 'present'], default='present'),
|
||||||
|
username=dict(type='str', required=True),
|
||||||
|
disabled=dict(choices=['none', 'login', 'password', 'all']),
|
||||||
|
capability=dict(choices=['monitor', 'unpriv', 'v_admin', 'admin']),
|
||||||
|
nopassword=dict(type='bool', default=False),
|
||||||
|
password=dict(type='str', no_log=True),
|
||||||
|
encrypted_password=dict(type='bool', default=False),
|
||||||
|
reset_capability=dict(type="bool", default=False),
|
||||||
|
disconnected=dict(type='bool', default=False),
|
||||||
|
full_name=dict(type='str'))
|
||||||
|
argument_spec.update(element_spec)
|
||||||
|
self._module = AnsibleModule(
|
||||||
|
argument_spec=argument_spec,
|
||||||
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=[['password', 'nopassword']])
|
||||||
|
|
||||||
|
def get_required_config(self):
|
||||||
|
self._required_config = dict()
|
||||||
|
module_params = self._module.params
|
||||||
|
params = {}
|
||||||
|
''' Requred/Default fields '''
|
||||||
|
params['username'] = module_params.get('username')
|
||||||
|
params['state'] = module_params.get('state')
|
||||||
|
params['encrypted_password'] = module_params.get('encrypted_password')
|
||||||
|
params['reset_capability'] = module_params.get('reset_capability')
|
||||||
|
''' Other fields '''
|
||||||
|
for key, value in module_params.items():
|
||||||
|
if value is not None:
|
||||||
|
params[key] = value
|
||||||
|
self.validate_param_values(params)
|
||||||
|
self._required_config = params
|
||||||
|
|
||||||
|
def _get_username_config(self):
|
||||||
|
return show_cmd(self._module, "show usernames", json_fmt=True, fail_on_error=False)
|
||||||
|
|
||||||
|
def _set_current_config(self, users_config):
|
||||||
|
'''
|
||||||
|
users_config ex:
|
||||||
|
{
|
||||||
|
admin": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "No password required for login",
|
||||||
|
"FULL NAME": "System Administrator"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
if not users_config:
|
||||||
|
return
|
||||||
|
current_config = self._current_config
|
||||||
|
for username, config in users_config.items():
|
||||||
|
config_json = config[0]
|
||||||
|
current_config[username] = current_config.get(username, {})
|
||||||
|
account_status = config_json.get('ACCOUNT STATUS')
|
||||||
|
status_value = self.ACCOUNT_STATE.get(account_status)
|
||||||
|
|
||||||
|
if status_value is not None:
|
||||||
|
# None for enabled account with password account "Password set (SHA512 | MD5)" so we won't change any attribute here.
|
||||||
|
current_config[username].update(status_value)
|
||||||
|
current_config[username].update({
|
||||||
|
'capability': config_json.get('CAPABILITY'),
|
||||||
|
'full_name': config_json.get('FULL NAME')
|
||||||
|
})
|
||||||
|
|
||||||
|
def load_current_config(self):
|
||||||
|
self._current_config = dict()
|
||||||
|
users_config = self._get_username_config()
|
||||||
|
self._set_current_config(users_config)
|
||||||
|
|
||||||
|
def generate_commands(self):
|
||||||
|
current_config, required_config = self._current_config, self._required_config
|
||||||
|
username = required_config.get('username')
|
||||||
|
current_user = current_config.get(username)
|
||||||
|
if current_user is not None:
|
||||||
|
'''created account we just need to edit his attributes'''
|
||||||
|
full_name = required_config.get('full_name')
|
||||||
|
if full_name is not None and current_user.get('full_name') != full_name:
|
||||||
|
self._commands.append("username {0} full-name {1}".format(username, full_name))
|
||||||
|
|
||||||
|
disabled = required_config.get('disabled')
|
||||||
|
if disabled is not None and current_user.get('disabled') != disabled:
|
||||||
|
if disabled == 'none':
|
||||||
|
self._commands.append("no username {0} disable".format(username))
|
||||||
|
elif disabled == 'all':
|
||||||
|
self._commands.append("username {0} disable".format(username))
|
||||||
|
else:
|
||||||
|
self._commands.append("username {0} disable {1}".format(username, disabled))
|
||||||
|
|
||||||
|
state = required_config.get('state')
|
||||||
|
if state == 'absent': # this will remove the user
|
||||||
|
self._commands.append("no username {0}".format(username))
|
||||||
|
|
||||||
|
capability = required_config.get('capability')
|
||||||
|
if capability is not None and current_user.get('capability') != capability:
|
||||||
|
self._commands.append("username {0} capability {1}".format(username, capability))
|
||||||
|
|
||||||
|
reset_capability = required_config.get('reset_capability')
|
||||||
|
if reset_capability:
|
||||||
|
self._commands.append("no username {0} capability".format(username))
|
||||||
|
|
||||||
|
password = required_config.get('password')
|
||||||
|
if password is not None:
|
||||||
|
encrypted = required_config.get('encrypted_password')
|
||||||
|
if encrypted:
|
||||||
|
self._commands.append("username {0} password {1} {2}".format(username, self.ENCRYPTED_ID, password))
|
||||||
|
else:
|
||||||
|
self._commands.append("username {0} password {1}".format(username, password))
|
||||||
|
|
||||||
|
nopassword = required_config.get('nopassword')
|
||||||
|
if nopassword and nopassword != current_user.get('nopassword', False):
|
||||||
|
self._commands.append("username {0} nopassword".format(username))
|
||||||
|
|
||||||
|
disconnected = required_config.get('disconnected')
|
||||||
|
if disconnected:
|
||||||
|
self._commands.append("username {0} disconnect".format(username))
|
||||||
|
else:
|
||||||
|
'''create new account if we have valid inforamtion, just check for username, capability, full_name, password'''
|
||||||
|
|
||||||
|
capability = required_config.get('capability')
|
||||||
|
password = required_config.get('password')
|
||||||
|
full_name = required_config.get('full_name')
|
||||||
|
if capability is not None or password is not None or full_name is not None:
|
||||||
|
if capability is not None:
|
||||||
|
self._commands.append("username {0} capability {1}".format(username, capability))
|
||||||
|
|
||||||
|
if password is not None:
|
||||||
|
encrypted = required_config.get('encrypted_password')
|
||||||
|
if encrypted:
|
||||||
|
self._commands.append("username {0} password {1} {2} ".format(username, self.ENCRYPTED_ID, password))
|
||||||
|
else:
|
||||||
|
self._commands.append("username {0} password {1}".format(username, password))
|
||||||
|
|
||||||
|
if full_name is not None:
|
||||||
|
self._commands.append("username {0} full-name {1}".format(username, full_name))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._commands.append("username {0}".format(username))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
""" main entry point for module execution
|
||||||
|
"""
|
||||||
|
OnyxUsernameModule.main()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"xmladmin": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "Password set (SHA512)",
|
||||||
|
"FULL NAME": "XML Admin User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"monitor": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "monitor",
|
||||||
|
"ACCOUNT STATUS": "Password set (SHA512)",
|
||||||
|
"FULL NAME": "System Monitor"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"admin": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "No password required for login",
|
||||||
|
"FULL NAME": "System Administrator"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anass": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "Password set (SHA512)",
|
||||||
|
"FULL NAME": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "No password required for login",
|
||||||
|
"FULL NAME": "Root User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anassh": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "admin",
|
||||||
|
"ACCOUNT STATUS": "Account disabled",
|
||||||
|
"FULL NAME": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"xmluser": [
|
||||||
|
{
|
||||||
|
"CAPABILITY": "monitor",
|
||||||
|
"ACCOUNT STATUS": "Password set (SHA512)",
|
||||||
|
"FULL NAME": "XML Monitor User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
#
|
||||||
|
# Copyright: Ansible Project
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
from units.compat.mock import patch
|
||||||
|
from ansible.modules.network.onyx import onyx_username
|
||||||
|
from units.modules.utils import set_module_args
|
||||||
|
from .onyx_module import TestOnyxModule, load_fixture
|
||||||
|
|
||||||
|
|
||||||
|
class TestOnyxUsernameModule(TestOnyxModule):
|
||||||
|
|
||||||
|
module = onyx_username
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.enabled = False
|
||||||
|
super(TestOnyxUsernameModule, self).setUp()
|
||||||
|
self.mock_get_config = patch.object(
|
||||||
|
onyx_username.OnyxUsernameModule, "_get_username_config")
|
||||||
|
self.get_config = self.mock_get_config.start()
|
||||||
|
|
||||||
|
self.mock_load_config = patch(
|
||||||
|
'ansible.module_utils.network.onyx.onyx.load_config')
|
||||||
|
self.load_config = self.mock_load_config.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestOnyxUsernameModule, self).tearDown()
|
||||||
|
self.mock_get_config.stop()
|
||||||
|
self.mock_load_config.stop()
|
||||||
|
|
||||||
|
def load_fixtures(self, commands=None, transport='cli'):
|
||||||
|
config_file = 'onyx_username_show.cfg'
|
||||||
|
self.get_config.return_value = load_fixture(config_file)
|
||||||
|
self.load_config.return_value = None
|
||||||
|
|
||||||
|
def test_new_username(self):
|
||||||
|
set_module_args(dict(username='test'))
|
||||||
|
commands = ['username test']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_change_full_username(self):
|
||||||
|
set_module_args(dict(username='anass', full_name="anasshami"))
|
||||||
|
commands = ['username anass full-name anasshami']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_change_username_password(self):
|
||||||
|
set_module_args(dict(username='anass', password="12345"))
|
||||||
|
commands = ['username anass password 12345']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_change_username_password_encrypted(self):
|
||||||
|
set_module_args(dict(username='anass', password="12345", encrypted_password=True))
|
||||||
|
commands = ['username anass password 7 12345']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_disable_username(self):
|
||||||
|
set_module_args(dict(username='anass', disabled="all"))
|
||||||
|
commands = ['username anass disable']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_disable_username_login(self):
|
||||||
|
set_module_args(dict(username='anass', disabled="login"))
|
||||||
|
commands = ['username anass disable login']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_disable_username_password(self):
|
||||||
|
set_module_args(dict(username='anass', disabled="password"))
|
||||||
|
commands = ['username anass disable password']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_change_username_capability(self):
|
||||||
|
set_module_args(dict(username='anass', capability="monitor"))
|
||||||
|
commands = ['username anass capability monitor']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_disconnect_username(self):
|
||||||
|
set_module_args(dict(username='anass', disconnected=True))
|
||||||
|
commands = ['username anass disconnect']
|
||||||
|
self.execute_module(changed=True, commands=commands)
|
||||||
|
|
||||||
|
def test_no_change_username_capability(self):
|
||||||
|
set_module_args(dict(username='anass', capability="admin"))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_no_change_username_disabled(self):
|
||||||
|
set_module_args(dict(username='anassh', disabled="all"))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_no_change_username_nopass(self):
|
||||||
|
set_module_args(dict(username='admin', nopassword=True))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_no_change_full_username(self):
|
||||||
|
set_module_args(dict(username='admin', full_name="System Administrator"))
|
||||||
|
self.execute_module(changed=False)
|
Loading…
Reference in New Issue