From 2c81e05baa77d9fd5d029aa8bc34267a09f0c6d7 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Tue, 14 Feb 2017 16:01:16 -0500 Subject: Add NetApp ONTAP user module (#20723) * Add NetApp ONTAP user module * Make changes to adhere to community guidelines * Fix PEP8 issue --- lib/ansible/modules/storage/netapp/na_cdot_user.py | 311 +++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 lib/ansible/modules/storage/netapp/na_cdot_user.py diff --git a/lib/ansible/modules/storage/netapp/na_cdot_user.py b/lib/ansible/modules/storage/netapp/na_cdot_user.py new file mode 100644 index 0000000000..bcf89844de --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_cdot_user.py @@ -0,0 +1,311 @@ +#!/usr/bin/python + +# (c) 2017, NetApp, Inc +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'version': '1.0'} + +DOCUMENTATION = ''' + +module: na_cdot_users + +short_description: useradmin configuration and management +extends_documentation_fragment: + - netapp.ontap +version_added: '2.3' +author: Sumit Kumar (sumit4@netapp.com) + +description: +- Create or destroy users. + +options: + + state: + description: + - Whether the specified user should exist or not. + required: true + choices: ['present', 'absent'] + + name: + description: + - The name of the user to manage. + required: true + + application: + description: + - Applications to grant access to. + required: true + choices: ['console', 'http','ontapi','rsh','snmp','sp','ssh','telnet'] + + authentication_method: + description: + - Authentication method for the application. + - Not all authentication methods are valid for an application. + - Valid authentication methods for each application are as denoted in I(authentication_choices_description). + authentication_choices_description: + - password for console application + - password, domain, nsswitch, cert for http application. + - password, domain, nsswitch, cert for ontapi application. + - community for snmp application (when creating SNMPv1 and SNMPv2 users). + - usm and community for snmp application (when creating SNMPv3 users). + - password for sp application. + - password for rsh application. + - password for telnet application. + - password, publickey, domain, nsswitch for ssh application. + required: true + choices: ['community', 'password', 'publickey', 'domain', 'nsswitch', 'usm'] + + set_password: + description: + - Password for the user account. + - It is ignored for creating snmp users, but is required for creating non-snmp users. + - For an existing user, this value will be used as the new password. + default: None + + role_name: + description: + - The name of the role. + note: required when C(state=present) + + + vserver: + description: + - The name of the vserver to use. + required: true + +''' + +EXAMPLES = """ + + - name: Create User + na_cdot_user: + state: present + name: SampleUser + application: ssh + authentication_method: password + set_password: apn1242183u1298u41 + role_name: vsadmin + vserver: ansibleVServer + hostname: "{{ netapp_hostname }}" + username: "{{ netapp_username }}" + password: "{{ netapp_password }}" + +""" + +RETURN = """ + +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.pycompat24 import get_exception +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppCDOTUser(object): + """ + Common operations to manage users and roles. + """ + + def __init__(self): + self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=True, choices=['present', 'absent']), + name=dict(required=True, type='str'), + + application=dict(required=True, type='str', choices=[ + 'console', 'http', 'ontapi', 'rsh', + 'snmp', 'sp', 'ssh', 'telnet']), + authentication_method=dict(required=True, type='str', + choices=['community', 'password', + 'publickey', 'domain', + 'nsswitch', 'usm']), + set_password=dict(required=False, type='str', default=None), + role_name=dict(required=False, type='str'), + + vserver=dict(required=True, type='str'), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + required_if=[ + ('state', 'present', ['role_name']) + ], + supports_check_mode=True + ) + + p = self.module.params + + # set up state variables + self.state = p['state'] + self.name = p['name'] + + self.application = p['application'] + self.authentication_method = p['authentication_method'] + self.set_password = p['set_password'] + self.role_name = p['role_name'] + + self.vserver = p['vserver'] + + if HAS_NETAPP_LIB is False: + self.module.fail_json(msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_ontap_zapi(module=self.module) + + def get_user(self): + """ + Checks if the user exists. + + :return: + True if user found + False if user is not found + :rtype: bool + """ + + security_login_get_iter = netapp_utils.zapi.NaElement('security-login-get-iter') + query_details = netapp_utils.zapi.NaElement.create_node_with_children( + 'security-login-account-info', **{'vserver': self.vserver, + 'user-name': self.name, + 'application': self.application, + 'authentication-method': + self.authentication_method}) + + query = netapp_utils.zapi.NaElement('query') + query.add_child_elem(query_details) + security_login_get_iter.add_child_elem(query) + + try: + result = self.server.invoke_successfully(security_login_get_iter, + enable_tunneling=False) + + if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: + return True + else: + return False + + except netapp_utils.zapi.NaApiError: + e = get_exception() + # Error 16034 denotes a user not being found. + if str(e.code) == "16034": + return False + else: + self.module.fail_json(msg='Error getting user %s' % self.name, exception=str(e)) + + def create_user(self): + user_create = netapp_utils.zapi.NaElement.create_node_with_children( + 'security-login-create', **{'vserver': self.vserver, + 'user-name': self.name, + 'application': self.application, + 'authentication-method': + self.authentication_method, + 'role-name': self.role_name}) + if self.set_password is not None: + user_create.add_new_child('password', self.set_password) + + try: + self.server.invoke_successfully(user_create, + enable_tunneling=False) + except netapp_utils.zapi.NaApiError: + err = get_exception() + self.module.fail_json(msg='Error creating user %s' % self.name, exception=str(err)) + + def delete_user(self): + user_delete = netapp_utils.zapi.NaElement.create_node_with_children( + 'security-login-delete', **{'vserver': self.vserver, + 'user-name': self.name, + 'application': self.application, + 'authentication-method': + self.authentication_method}) + + try: + self.server.invoke_successfully(user_delete, + enable_tunneling=False) + except netapp_utils.zapi.NaApiError: + err = get_exception() + self.module.fail_json(msg='Error removing user %s' % self.name, exception=str(err)) + + def change_password(self): + """ + Changes the password + + :return: + True if password updated + False if password is not updated + :rtype: bool + """ + self.server.set_vserver(self.vserver) + modify_password = netapp_utils.zapi.NaElement.create_node_with_children( + 'security-login-modify-password', **{ + 'new-password': str(self.set_password), + 'user-name': self.name}) + try: + self.server.invoke_successfully(modify_password, + enable_tunneling=True) + except netapp_utils.zapi.NaApiError: + e = get_exception() + if str(e.code) == '13114': + return False + else: + err = get_exception() + self.module.fail_json(msg='Error setting password for user %s' % self.name, exception=str(err)) + + self.server.set_vserver(None) + return True + + def apply(self): + property_changed = False + password_changed = False + user_exists = self.get_user() + + if user_exists: + if self.state == 'absent': + property_changed = True + + elif self.state == 'present': + if self.set_password is not None: + password_changed = self.change_password() + else: + if self.state == 'present': + # Check if anything needs to be updated + property_changed = True + + if property_changed: + if self.module.check_mode: + pass + else: + if self.state == 'present': + if not user_exists: + self.create_user() + + # Add ability to update parameters. + + elif self.state == 'absent': + self.delete_user() + + changed = property_changed or password_changed + self.module.exit_json(changed=changed) + + +def main(): + v = NetAppCDOTUser() + v.apply() + +if __name__ == '__main__': + main() -- cgit v1.2.1