PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/netusers.py |
""" Network Users ============= Manage the users configuration on network devices via the NAPALM proxy. :codeauthor: Mircea Ulinic <ping@mirceaulinic.net> :maturity: new :depends: napalm :platform: unix Dependencies ------------ - :mod:`NAPALM proxy minion <salt.proxy.napalm>` - :mod:`Users configuration management module <salt.modules.napalm_users>` .. versionadded:: 2016.11.0 """ import copy import logging import salt.utils.json import salt.utils.napalm log = logging.getLogger(__name__) # ---------------------------------------------------------------------------------------------------------------------- # state properties # ---------------------------------------------------------------------------------------------------------------------- __virtualname__ = "netusers" # ---------------------------------------------------------------------------------------------------------------------- # global variables # ---------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------- # property functions # ---------------------------------------------------------------------------------------------------------------------- def __virtual__(): """ NAPALM library must be installed for this module to work and run in a (proxy) minion. """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- def _retrieve_users(): """Retrieves configured users""" return __salt__["users.config"]() def _ordered_dict_to_dict(probes): """.""" return salt.utils.json.loads(salt.utils.json.dumps(probes)) def _expand_users(device_users, common_users): """Creates a longer list of accepted users on the device.""" expected_users = copy.deepcopy(common_users) expected_users.update(device_users) return expected_users def _check_users(users): """Checks if the input dictionary of users is valid.""" messg = "" valid = True for user, user_details in users.items(): if not user_details: valid = False messg += f"Please provide details for username {user}.\n" continue if not ( isinstance(user_details.get("level"), int) or 0 <= user_details.get("level") <= 15 ): # warn! messg += ( "Level must be a integer between 0 and 15 for username {user}. Will" " assume 0.\n".format(user=user) ) return valid, messg def _compute_diff(configured, expected): """Computes the differences between the actual config and the expected config""" diff = {"add": {}, "update": {}, "remove": {}} configured_users = set(configured.keys()) expected_users = set(expected.keys()) add_usernames = expected_users - configured_users remove_usernames = configured_users - expected_users common_usernames = expected_users & configured_users add = {username: expected.get(username) for username in add_usernames} remove = {username: configured.get(username) for username in remove_usernames} update = {} for username in common_usernames: user_configuration = configured.get(username) user_expected = expected.get(username) if user_configuration == user_expected: continue update[username] = {} for field, field_value in user_expected.items(): if user_configuration.get(field) != field_value: update[username][field] = field_value diff.update({"add": add, "update": update, "remove": remove}) return diff def _set_users(users): """Calls users.set_users.""" return __salt__["users.set_users"](users, commit=False) def _update_users(users): """Calls users.set_users.""" return __salt__["users.set_users"](users, commit=False) def _delete_users(users): """Calls users.delete_users.""" return __salt__["users.delete_users"](users, commit=False) # ---------------------------------------------------------------------------------------------------------------------- # callable functions # ---------------------------------------------------------------------------------------------------------------------- def managed(name, users=None, defaults=None): """ Manages the configuration of the users on the device, as specified in the state SLS file. Users not defined in that file will be removed whilst users not configured on the device, will be added. SLS Example: .. code-block:: yaml netusers_example: netusers.managed: - users: admin: level: 15 password: $1$knmhgPPv$g8745biu4rb.Zf.IT.F/U1 sshkeys: [] restricted: level: 1 password: $1$j34j5k4b$4d5SVjTiz1l.Zf.IT.F/K7 martin: level: 15 password: '' sshkeys: - ssh-dss AAAAB3NzaC1kc3MAAACBAK9dP3KariMlM/JmFW9rTSm5cXs4nR0+o6fTHP9o+bOLXMBTP8R4vwWHh0w JPjQmJYafAqZTnlgi0srGjyifFwPtODppDWLCgLe2M4LXnu3OMqknr54w344zPHP3iFwWxHrBrZKtCjO8LhbWCa+ X528+i87t6r5e4ersdfxgchvjbknlio87t6r5drcfhgjhbknio8976tycv7t86ftyiu87Oz1nKsKuNzm2csoUQlJ trmRfpjsOPNookmOz5wG0YxhwDmKeo6fWK+ATk1OiP+QT39fn4G77j8o+e4WAwxM570s35Of/vV0zoOccj753sXn pvJenvwpM2H6o3a9ALvehAJKWodAgZT7X8+iu786r5drtycghvjbiu78t+wAAAIBURwSPZVElXe+9a43sF6M4ysT 7Xv+6wTsa8q86E3+RYyu8O2ObI2kwNLC3/HTgFniE/YqRG+WJac81/VHWQNP822gns8RVrWKjqBktmQoEm7z5yy0 bkjui78675dytcghvjkoi9y7t867ftcuvhbuu9t78gy/v+zvMmv8KvQgHg jonathan: level: 15 password: '' sshkeys: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcgxE6HZF/xjFtIt0thEDKPjFJxW9BpZtTVstYbDgGR9zPkHG ZJT/j345jk345jk453jk43545j35nl3kln34n5kl4ghv3/JzWt/0Js5KZp/51KRNCs9O4t07qaoqwpLB15GwLfEX Bx9dW26zc4O+hi6754trxcfghvjbo98765drt/LYIEg0KSQPWyJEK1g31gacbxN7Ab006xeHh7rv7HtXF6zH3WId Uhq9rtdUag6kYnv6qvjG7sbCyHGYu5vZB7GytnNuVNbZuI+RdFvmHSnErV9HCu9xZBq6DBb+sESMS4s7nFcsruMo edb+BAc3aww0naeWpogjSt+We7y2N CLI Example: .. code-block:: bash salt 'edge01.kix01' state.sls router.users Output example (raw python - can be reused in other modules): .. code-block:: python { 'netusers_|-netusers_example_|-netusers_example_|-managed': { 'comment': 'Configuration updated!', 'name': 'netusers_example', 'start_time': '10:57:08.678811', '__id__': 'netusers_example', 'duration': 1620.982, '__run_num__': 0, 'changes': { 'updated': { 'admin': { 'level': 15 }, 'restricted': { 'level': 1 }, 'martin': { 'sshkeys': [ 'ssh-dss AAAAB3NzaC1kc3MAAACBAK9dP3KariMlM/JmFW9rTSm5cXs4nR0+o6fTHP9o+bOLXMBTP8R4vwWHh0w JPjQmJYafAqZTnlgi0srGjyifFwPtODppDWLCgLe2M4LXnu3OMqknr54w344zPHP3iFwWxHrBrZKtCjO8LhbWCa+ X528+i87t6r5e4ersdfxgchvjbknlio87t6r5drcfhgjhbknio8976tycv7t86ftyiu87Oz1nKsKuNzm2csoUQlJ trmRfpjsOPNookmOz5wG0YxhwDmKeo6fWK+ATk1OiP+QT39fn4G77j8o+e4WAwxM570s35Of/vV0zoOccj753sXn pvJenvwpM2H6o3a9ALvehAJKWodAgZT7X8+iu786r5drtycghvjbiu78t+wAAAIBURwSPZVElXe+9a43sF6M4ysT 7Xv+6wTsa8q86E3+RYyu8O2ObI2kwNLC3/HTgFniE/YqRG+WJac81/VHWQNP822gns8RVrWKjqBktmQoEm7z5yy0 bkjui78675dytcghvjkoi9y7t867ftcuvhbuu9t78gy/v+zvMmv8KvQgHg' ] } }, 'added': { 'jonathan': { 'password': '', 'sshkeys': [ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcgxE6HZF/xjFtIt0thEDKPjFJxW9BpZtTVstYbDgGR9zPkHG ZJT/j345jk345jk453jk43545j35nl3kln34n5kl4ghv3/JzWt/0Js5KZp/51KRNCs9O4t07qaoqwpLB15GwLfEX Bx9dW26zc4O+hi6754trxcfghvjbo98765drt/LYIEg0KSQPWyJEK1g31gacbxN7Ab006xeHh7rv7HtXF6zH3WId Uhq9rtdUag6kYnv6qvjG7sbCyHGYu5vZB7GytnNuVNbZuI+RdFvmHSnErV9HCu9xZBq6DBb+sESMS4s7nFcsruMo edb+BAc3aww0naeWpogjSt+We7y2N' ], 'level': 15 } }, 'removed': { } }, 'result': True } } CLI Output: .. code-block:: bash edge01.kix01: ---------- ID: netusers_example Function: netusers.managed Result: True Comment: Configuration updated! Started: 11:03:31.957725 Duration: 1220.435 ms Changes: ---------- added: ---------- jonathan: ---------- level: 15 password: sshkeys: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDcgxE6HZF/xjFtIt0thEDKPjFJxW9BpZtTVstYbDgG R9zPkHGZJT/j345jk345jk453jk43545j35nl3kln34n5kl4ghv3/JzWt/0Js5KZp/51KRNCs9O4t07qao qwpLB15GwLfEXBx9dW26zc4O+hi6754trxcfghvjbo98765drt/LYIEg0KSQPWyJEK1g31gacbxN7Ab006 xeHh7rv7HtXF6zH3WIdUhq9rtdUag6kYnv6qvjG7sbCyHGYu5vZB7GytnNuVNbZuI+RdFvmHSnErV9HCu9 xZBq6DBb+sESMS4s7nFcsruMoedb+BAc3aww0naeWpogjSt+We7y2N removed: ---------- updated: ---------- martin: ---------- sshkeys: - ssh-dss AAAAB3NzaC1kc3MAAACBAK9dP3KariMlM/JmFW9rTSm5cXs4nR0+o6fTHP9o+bOLXMBTP8R4 vwWHh0wJPjQmJYafAqZTnlgi0srGjyifFwPtODppDWLCgLe2M4LXnu3OMqknr54w344zPHP3iFwWxHrBrZ KtCjO8LhbWCa+X528+i87t6r5e4ersdfxgchvjbknlio87t6r5drcfhgjhbknio8976tycv7t86ftyiu87 Oz1nKsKuNzm2csoUQlJtrmRfpjsOPNookmOz5wG0YxhwDmKeo6fWK+ATk1OiP+QT39fn4G77j8o+e4WAwx M570s35Of/vV0zoOccj753sXnpvJenvwpM2H6o3a9ALvehAJKWodAgZT7X8+iu786r5drtycghvjbiu78t +wAAAIBURwSPZVElXe+9a43sF6M4ysT7Xv+6wTsa8q86E3+RYyu8O2ObI2kwNLC3/HTgFniE/YqRG+WJac 81/VHWQNP822gns8RVrWKjqBktmQoEm7z5yy0bkjui78675dytcghvjkoi9y7t867ftcuvhbuu9t78gy/v +zvMmv8KvQgHg admin: ---------- level: 15 restricted: ---------- level: 1 Summary for edge01.kix01 ------------ Succeeded: 1 (changed=1) Failed: 0 ------------ Total states run: 1 Total run time: 1.220 s """ result = False comment = "" changes = {} ret = {"name": name, "changes": changes, "result": result, "comment": comment} users = _ordered_dict_to_dict(users) defaults = _ordered_dict_to_dict(defaults) expected_users = _expand_users(users, defaults) valid, message = _check_users(expected_users) if not valid: # check and clean ret["comment"] = "Please provide a valid configuration: {error}".format( error=message ) return ret # ----- Retrieve existing users configuration and determine differences -------------------------------------------> users_output = _retrieve_users() if not users_output.get("result"): ret["comment"] = "Cannot retrieve users from the device: {reason}".format( reason=users_output.get("comment") ) return ret configured_users = users_output.get("out", {}) if configured_users == expected_users: ret.update({"comment": "Users already configured as needed.", "result": True}) return ret diff = _compute_diff(configured_users, expected_users) users_to_add = diff.get("add", {}) users_to_update = diff.get("update", {}) users_to_remove = diff.get("remove", {}) changes = { "added": users_to_add, "updated": users_to_update, "removed": users_to_remove, } ret.update({"changes": changes}) if __opts__["test"] is True: ret.update( {"result": None, "comment": "Testing mode: configuration was not changed!"} ) return ret # <---- Retrieve existing NTP peers and determine peers to be added/removed ---------------------------------------> # ----- Call _set_users and _delete_users as needed ---------------------------------------------------------------> expected_config_change = False successfully_changed = True if users_to_add: _set = _set_users(users_to_add) if _set.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False comment += "Cannot configure new users: {reason}".format( reason=_set.get("comment") ) if users_to_update: _update = _update_users(users_to_update) if _update.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False comment += "Cannot update the users configuration: {reason}".format( reason=_update.get("comment") ) if users_to_remove: _delete = _delete_users(users_to_remove) if _delete.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False comment += "Cannot remove users: {reason}".format( reason=_delete.get("comment") ) # <---- Call _set_users and _delete_users as needed ---------------------------------------------------------------- # ----- Try to commit changes -------------------------------------------------------------------------------------> if expected_config_change and successfully_changed: config_result, config_comment = __salt__["net.config_control"]() result = config_result comment += config_comment # <---- Try to commit changes -------------------------------------------------------------------------------------- if expected_config_change and result and not comment: comment = "Configuration updated!" ret.update({"result": result, "comment": comment}) return ret