#!/usr/bin/python # Copyright: (c) 2022, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Ansible module for managing NFS server on Unity""" from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' module: nfsserver version_added: '1.4.0' short_description: Manage NFS server on Unity storage system description: - Managing the NFS server on the Unity storage system includes creating NFS server, getting NFS server details and deleting NFS server attributes. extends_documentation_fragment: - dellemc.unity.unity author: - Meenakshi Dembi (@dembim) options: nas_server_name: description: - Name of the NAS server on which NFS server will be hosted. type: str nas_server_id: description: - ID of the NAS server on which NFS server will be hosted. type: str nfs_server_id: description: - ID of the NFS server. type: str host_name: description: - Host name of the NFS server. type: str nfs_v4_enabled: description: - Indicates whether the NFSv4 is enabled on the NAS server. type: bool is_secure_enabled: description: - Indicates whether the secure NFS is enabled. type: bool kerberos_domain_controller_type: description: - Type of Kerberos Domain Controller used for secure NFS service. choices: [CUSTOM, UNIX, WINDOWS] type: str kerberos_domain_controller_username: description: - Kerberos Domain Controller administrator username. type: str kerberos_domain_controller_password: description: - Kerberos Domain Controller administrator password. type: str is_extended_credentials_enabled: description: - Indicates whether support for more than 16 unix groups in a Unix credential. type: bool remove_spn_from_kerberos: description: - Indicates whether to remove the SPN from Kerberos Domain Controller. default: true type: bool state: description: - Define whether the NFS server should exist or not. choices: [absent, present] required: true type: str notes: - The I(check_mode) is supported. - Modify operation for NFS Server is not supported. - When I(kerberos_domain_controller_type) is C(UNIX), I(kdc_type) in I(nfs_server_details) output is displayed as C(null). ''' EXAMPLES = r''' - name: Create NFS server with kdctype as Windows dellemc.unity.nfsserver: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" nas_server_name: "dummy_nas" host_name: "dummy_nas23" is_secure_enabled: True kerberos_domain_controller_type: "WINDOWS" kerberos_domain_controller_username: "administrator" kerberos_domain_controller_password: "Password123!" is_extended_credentials_enabled: True nfs_v4_enabled: True state: "present" - name: Create NFS server with kdctype as Unix dellemc.unity.nfsserver: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" nas_server_name: "dummy_nas" host_name: "dummy_nas23" is_secure_enabled: True kerberos_domain_controller_type: "UNIX" is_extended_credentials_enabled: True nfs_v4_enabled: True state: "present" - name: Get NFS server details dellemc.unity.nfsserver: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" nas_server_name: "dummy_nas" state: "present" - name: Delete NFS server dellemc.unity.nfsserver: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" nas_server_name: "dummy_nas" kerberos_domain_controller_username: "administrator" kerberos_domain_controller_password: "Password123!" unjoin_server_account: False state: "absent" ''' RETURN = r''' changed: description: Whether or not the resource has changed. returned: always type: bool sample: true nfs_server_details: description: Details of the NFS server. returned: When NFS server exists type: dict contains: credentials_cache_ttl: description: Credential cache refresh timeout. Resolution is in minutes. Default value is 15 minutes. type: str existed: description: Indicates if NFS Server exists. type: bool host_name: description: Host name of the NFS server. type: str id: description: Unique identifier of the NFS Server instance. type: str is_extended_credentials_enabled: description: Indicates whether the NFS server supports more than 16 Unix groups in a Unix credential. type: bool is_secure_enabled: description: Indicates whether secure NFS is enabled on the NFS server. type: bool kdc_type: description: Type of Kerberos Domain Controller used for secure NFS service. type: str nfs_v4_enabled: description: Indicates whether NFSv4 is enabled on the NAS server. type: bool servicee_principal_name: description: The Service Principal Name (SPN) for the NFS Server. type: str sample: { "credentials_cache_ttl": "0:15:00", "existed": true, "file_interfaces": { "UnityFileInterfaceList": [ { "UnityFileInterface": { "hash": 8778980109421, "id": "if_37" } } ] }, "hash": 8778980109388, "host_name": "dummy_nas23.pie.lab.emc.com", "id": "nfs_51", "is_extended_credentials_enabled": true, "is_secure_enabled": true, "kdc_type": "KdcTypeEnum.WINDOWS", "nas_server": { "UnityNasServer": { "hash": 8778980109412 } }, "nfs_v4_enabled": true, "servicee_principal_name": null } ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \ import utils LOG = utils.get_logger('nfsserver') application_type = "Ansible/1.6.0" class NFSServer(object): """Class with NFS server operations""" def __init__(self): """Define all parameters required by this module""" self.module_params = utils.get_unity_management_host_parameters() self.module_params.update(get_nfs_server_parameters()) mutually_exclusive = [['nas_server_name', 'nas_server_id']] required_one_of = [['nfs_server_id', 'nas_server_name', 'nas_server_id']] # initialize the Ansible module self.module = AnsibleModule( argument_spec=self.module_params, supports_check_mode=True, mutually_exclusive=mutually_exclusive, required_one_of=required_one_of ) utils.ensure_required_libs(self.module) self.unity_conn = utils.get_unity_unisphere_connection( self.module.params, application_type) LOG.info('Check Mode Flag %s', self.module.check_mode) def get_nfs_server_details(self, nfs_server_id=None, nas_server_id=None): """Get NFS server details. :param: nfs_server_id: The ID of the NFS server :param: nas_server_id: The name of the NAS server :return: Dict containing NFS server details if exists """ LOG.info("Getting NFS server details") try: if nfs_server_id: nfs_server_details = self.unity_conn.get_nfs_server(_id=nfs_server_id) return nfs_server_details._get_properties() elif nas_server_id: nfs_server_details = self.unity_conn.get_nfs_server(nas_server=nas_server_id) if len(nfs_server_details) > 0: return process_dict(nfs_server_details._get_properties()) return None except utils.HttpError as e: if e.http_status == 401: msg = 'Incorrect username or password provided.' LOG.error(msg) self.module.fail_json(msg=msg) else: err_msg = "Failed to get details of NFS Server" \ " with error {0}".format(str(e)) LOG.error(err_msg) self.module.fail_json(msg=err_msg) except utils.UnityResourceNotFoundError as e: err_msg = "Failed to get details of NFS Server" \ " with error {0}".format(str(e)) LOG.error(err_msg) return None def get_nfs_server_instance(self, nfs_server_id): """Get NFS server instance. :param: nfs_server_id: The ID of the NFS server :return: Return NFS server instance if exists """ try: nfs_server_obj = self.unity_conn.get_nfs_server(_id=nfs_server_id) return nfs_server_obj except Exception as e: error_msg = "Failed to get the NFS server %s instance" \ " with error %s" % (nfs_server_id, str(e)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) def delete_nfs_server(self, nfs_server_id, skip_unjoin=None, domain_username=None, domain_password=None): """Delete NFS server. :param: nfs_server_id: The ID of the NFS server :param: skip_unjoin: Flag indicating whether to unjoin SMB server account from AD before deletion :param: domain_username: The domain username :param: domain_password: The domain password :return: Return True if NFS server is deleted """ LOG.info("Deleting NFS server") try: if not self.module.check_mode: nfs_obj = self.get_nfs_server_instance(nfs_server_id=nfs_server_id) nfs_obj.delete(skip_kdc_unjoin=skip_unjoin, username=domain_username, password=domain_password) return True except Exception as e: msg = "Failed to delete NFS server: %s with error: %s" % (nfs_server_id, str(e)) LOG.error(msg) self.module.fail_json(msg=msg) def get_nas_server_id(self, nas_server_name): """Get NAS server ID. :param: nas_server_name: The name of NAS server :return: Return NAS server ID if exists """ LOG.info("Getting NAS server ID") try: obj_nas = self.unity_conn.get_nas_server(name=nas_server_name) return obj_nas.get_id() except Exception as e: msg = "Failed to get details of NAS server: %s with error: %s" % (nas_server_name, str(e)) LOG.error(msg) self.module.fail_json(msg=msg) def is_modification_required(self, is_extended_credentials_enabled, nfs_server_details): """Check if modification is required in existing NFS server :param: is_extended_credentials_enabled: Indicates whether the NFS server supports more than 16 Unix groups in a Unix credential. :param: nfs_server_details: NFS server details :return: True if modification is required """ LOG.info("Checking if any modification is required") # Check for Extend Credential if is_extended_credentials_enabled is not None and \ is_extended_credentials_enabled != nfs_server_details['is_extended_credentials_enabled']: return True def create_nfs_server(self, nas_server_id, host_name=None, nfs_v4_enabled=None, is_secure_enabled=None, kerberos_domain_controller_type=None, kerberos_domain_controller_username=None, kerberos_domain_controller_password=None, is_extended_credentials_enabled=None): """Create NFS server. :param: nas_server_id: The ID of NAS server. :param: host_name: Name of NFS Server. :param: nfs_v4_enabled: Indicates whether the NFSv4 is enabled on the NAS server. :param: is_secure_enabled: Indicates whether the secure NFS is enabled. :param: kerberos_domain_controller_type: Type of Kerberos Domain Controller used for secure NFS service. :param: kerberos_domain_controller_username: Kerberos Domain Controller administrator username. :param: kerberos_domain_controller_password: Kerberos Domain Controller administrator password. :param: is_extended_credentials_enabled: Indicates whether support for more than 16 unix groups in a Unix credential. """ LOG.info("Creating NFS server") try: if not self.module.check_mode: kdc_enum_type = get_enum_kdctype(kerberos_domain_controller_type) if kerberos_domain_controller_type == "UNIX": is_extended_credentials_enabled = None is_secure_enabled = None utils.UnityNfsServer.create(cli=self.unity_conn._cli, nas_server=nas_server_id, host_name=host_name, nfs_v4_enabled=nfs_v4_enabled, is_secure_enabled=is_secure_enabled, kdc_type=kdc_enum_type, kdc_username=kerberos_domain_controller_username, kdc_password=kerberos_domain_controller_password, is_extended_credentials_enabled=is_extended_credentials_enabled) return True except Exception as e: msg = "Failed to create NFS server with on NAS Server %s with error: %s" % (nas_server_id, str(e)) LOG.error(msg) self.module.fail_json(msg=msg) def validate_input_params(self): param_list = ["nfs_server_id", "nas_server_id", "nas_server_name", "host_name", "kerberos_domain_controller_username", "kerberos_domain_controller_password"] for param in param_list: msg = "Please provide valid value for: %s" % param if self.module.params[param] is not None and len(self.module.params[param].strip()) == 0: errmsg = msg.format(param) self.module.fail_json(msg=errmsg) def perform_module_operation(self): """ Perform different actions on NFS server module based on parameters passed in the playbook """ nfs_server_id = self.module.params['nfs_server_id'] nas_server_id = self.module.params['nas_server_id'] nas_server_name = self.module.params['nas_server_name'] host_name = self.module.params['host_name'] nfs_v4_enabled = self.module.params['nfs_v4_enabled'] is_secure_enabled = self.module.params['is_secure_enabled'] kerberos_domain_controller_type = self.module.params['kerberos_domain_controller_type'] kerberos_domain_controller_username = self.module.params['kerberos_domain_controller_username'] kerberos_domain_controller_password = self.module.params['kerberos_domain_controller_password'] is_extended_credentials_enabled = self.module.params['is_extended_credentials_enabled'] remove_spn_from_kerberos = self.module.params['remove_spn_from_kerberos'] state = self.module.params['state'] # result is a dictionary that contains changed status and NFS server details result = dict( changed=False, nfs_server_details={} ) modify_flag = False self.validate_input_params() if nas_server_name: nas_server_id = self.get_nas_server_id(nas_server_name) nfs_server_details = self.get_nfs_server_details(nfs_server_id=nfs_server_id, nas_server_id=nas_server_id) # Check if modification is required if nfs_server_details and state == 'present': modify_flag = self.is_modification_required(is_extended_credentials_enabled, nfs_server_details) if modify_flag: self.module.fail_json(msg="Modification of NFS Server parameters is not supported through Ansible module") if not nfs_server_details and state == 'present': if not nas_server_id: self.module.fail_json(msg="Please provide nas server id/name to create NFS server.") result['changed'] = self.create_nfs_server(nas_server_id, host_name, nfs_v4_enabled, is_secure_enabled, kerberos_domain_controller_type, kerberos_domain_controller_username, kerberos_domain_controller_password, is_extended_credentials_enabled) if state == 'absent' and nfs_server_details: skip_unjoin = not remove_spn_from_kerberos result['changed'] = self.delete_nfs_server(nfs_server_details["id"], skip_unjoin, kerberos_domain_controller_username, kerberos_domain_controller_password) if state == 'present': result['nfs_server_details'] = self.get_nfs_server_details(nfs_server_id=nfs_server_id, nas_server_id=nas_server_id) self.module.exit_json(**result) def get_nfs_server_parameters(): """This method provide parameters required for the ansible NFS server module on Unity""" return dict( nfs_server_id=dict(type='str'), host_name=dict(type='str'), nfs_v4_enabled=dict(type='bool'), is_secure_enabled=dict(type='bool'), kerberos_domain_controller_type=dict(type='str', choices=['UNIX', 'WINDOWS', 'CUSTOM']), kerberos_domain_controller_username=dict(type='str'), kerberos_domain_controller_password=dict(type='str', no_log=True), nas_server_name=dict(type='str'), nas_server_id=dict(type='str'), is_extended_credentials_enabled=dict(type='bool'), remove_spn_from_kerberos=dict(default=True, type='bool'), state=dict(required=True, type='str', choices=['present', 'absent']), ) def get_enum_kdctype(kerberos_domain_controller_type): """Getting correct enum values for kerberos_domain_controller_type :param: kerberos_domain_controller_type: Type of Kerberos Domain Controller used for secure NFS service. :return: enum value for kerberos_domain_controller_type. """ if utils.KdcTypeEnum[kerberos_domain_controller_type]: kerberos_domain_controller_type = utils.KdcTypeEnum[kerberos_domain_controller_type] return kerberos_domain_controller_type def process_dict(nfs_server_details): """Process NFS server details. :param: nfs_server_details: Dict containing NFS server details :return: Processed dict containing NFS server details """ param_list = ['credentials_cache_ttl', 'file_interfaces', 'host_name', 'id', 'kdc_type', 'nas_server', 'is_secure_enabled', 'is_extended_credentials_enabled', 'nfs_v4_enabled', 'servicee_principal_name'] for param in param_list: if param in nfs_server_details and param == 'credentials_cache_ttl': nfs_server_details[param] = str(nfs_server_details[param][0]) else: nfs_server_details[param] = nfs_server_details[param][0] return nfs_server_details def main(): """Create Unity NFS server object and perform action on it based on user input from playbook""" obj = NFSServer() obj.perform_module_operation() if __name__ == '__main__': main()