Server IP : 85.214.239.14 / Your IP : 3.131.82.143 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Mon Sep 30 15:36:27 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/lib/python3/dist-packages/ansible_collections/dellemc/unity/plugins/modules/ |
Upload File : |
#!/usr/bin/python # Copyright: (c) 2021, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Ansible module for managing quota tree on Unity""" from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' --- module: tree_quota short_description: Manage quota tree on the Unity storage system description: - Managing Quota tree on the Unity storage system includes Create quota tree, Get quota tree, Modify quota tree and Delete quota tree. version_added: '1.2.0' extends_documentation_fragment: - dellemc.unity.unity author: - Spandita Panigrahi (@panigs7) <ansible.team@dell.com> options: filesystem_name: description: - The name of the filesystem for which quota tree is created. - For creation or modification of a quota tree either I(filesystem_name) or I(filesystem_id) is required. type: str filesystem_id: description: - The ID of the filesystem for which the quota tree is created. - For creation of a quota tree either I(filesystem_id) or I(filesystem_name) is required. type: str nas_server_name: description: - The name of the NAS server in which the filesystem is created. - For creation of a quota tree either I(nas_server_name) or I(nas_server_id) is required. type: str nas_server_id: description: - The ID of the NAS server in which the filesystem is created. - For creation of a quota tree either I(filesystem_id) or I(filesystem_name) is required. type: str tree_quota_id: description: - The ID of the quota tree. - Either I(tree_quota_id) or I(path) to quota tree is required to view/modify/delete quota tree. type: str path: description: - The path to the quota tree. - Either I(tree_quota_id) or I(path) to quota tree is required to create/view/modify/delete a quota tree. - Path must start with a forward slash '/'. type: str hard_limit: description: - Hard limitation for a quota tree on the total space available. If exceeded, users in quota tree cannot write data. - Value C(0) implies no limit. - One of the values of I(soft_limit) and I(hard_limit) can be C(0), however, both cannot be both C(0) during creation of a quota tree. type: int soft_limit: description: - Soft limitation for a quota tree on the total space available. If exceeded, notification will be sent to users in the quota tree for the grace period mentioned, beyond which users cannot use space. - Value C(0) implies no limit. - Both I(soft_limit) and I(hard_limit) cannot be C(0) during creation of quota tree. type: int cap_unit: description: - Unit of I(soft_limit) and I(hard_limit) size. - It defaults to C(GB) if not specified. choices: ['MB', 'GB', 'TB'] type: str description: description: - Description of a quota tree. type: str state: description: - The state option is used to mention the existence of the filesystem quota tree. type: str required: true choices: ['absent', 'present'] notes: - The I(check_mode) is not supported. ''' EXAMPLES = r''' - name: Get quota tree details by quota tree id dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" tree_quota_id: "treequota_171798700679_10" state: "present" - name: Get quota tree details by quota tree path dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_name: "fs_2171" nas_server_id: "nas_21" path: "/test" state: "present" - name: Create quota tree for a filesystem with filesystem id dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_id: "fs_2171" hard_limit: 6 cap_unit: "TB" soft_limit: 5 path: "/test_new" state: "present" - name: Create quota tree for a filesystem with filesystem name dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_name: "Test_filesystem" nas_server_name: "lglad068" hard_limit: 6 cap_unit: "TB" soft_limit: 5 path: "/test_new" state: "present" - name: Modify quota tree limit usage by quota tree path dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" path: "/test_new" hard_limit: 10 cap_unit: "TB" soft_limit: 8 state: "present" - name: Modify quota tree by quota tree id dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_id: "fs_2171" tree_quota_id: "treequota_171798700679_10" hard_limit: 12 cap_unit: "TB" soft_limit: 10 state: "present" - name: Delete quota tree by quota tree id dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_id: "fs_2171" tree_quota_id: "treequota_171798700679_10" state: "absent" - name: Delete quota tree by path dellemc.unity.tree_quota: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" filesystem_id: "fs_2171" path: "/test_new" state: "absent" ''' RETURN = r''' changed: description: Whether or not the resource has changed. returned: always type: bool sample: True get_tree_quota_details: description: Details of the quota tree. returned: When quota tree exists type: dict contains: filesystem: description: Filesystem details for which the quota tree is created. type: dict contains: UnityFileSystem: description: Filesystem details for which the quota tree is created. type: dict contains: id: description: ID of the filesystem for which the quota tree is create. type: str description: description: Description of the quota tree. type: str path: description: Path to quota tree. A valid path must start with a forward slash '/'. It is mandatory while creating a quota tree. type: str hard_limit: description: Hard limit of quota tree. If the quota tree's space usage exceeds the hard limit, users in quota tree cannot write data. type: int soft_limit: description: Soft limit of the quota tree. If the quota tree's space usage exceeds the soft limit, the storage system starts to count down based on the specified grace period. type: int id: description: Quota tree ID. type: str size_used: description: Size of used space in the filesystem by the user files. type: int gp_left: description: The grace period left after the soft limit for the user quota is exceeded. type: int state: description: State of the quota tree. type: int sample: { "description": "", "existed": true, "filesystem": { "UnityFileSystem": { "hash": 8788549469862, "id": "fs_137", "name": "test", "nas_server": { "id": "nas_1", "name": "lglad072" } } }, "gp_left": null, "hard_limit": "6.0 TB", "hash": 8788549497558, "id": "treequota_171798694897_1", "path": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", "size_used": 0, "soft_limit": "5.0 TB", "state": 0 } ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \ import utils LOG = utils.get_logger('tree_quota') application_type = "Ansible/1.6.0" class QuotaTree(object): """Class with Quota Tree 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_quota_tree_parameters()) mutually_exclusive = [['filesystem_name', 'filesystem_id'], ['nas_server_name', 'nas_server_id']] # initialize the Ansible module self.module = AnsibleModule( argument_spec=self.module_params, supports_check_mode=False, mutually_exclusive=mutually_exclusive) utils.ensure_required_libs(self.module) self.unity_conn = utils.get_unity_unisphere_connection( self.module.params, application_type) def check_quota_tree_is_present(self, fs_id, path, tree_quota_id): """ Check if quota tree is present in filesystem. :param fs_id: ID of filesystem where quota tree is searched. :param path: Path to the quota tree :param tree_quota_id: ID of the quota tree :return: ID of quota tree if it exists else None. """ if tree_quota_id is None and path is None: return None all_tree_quota = self.unity_conn.get_tree_quota(filesystem=fs_id, id=tree_quota_id, path=path) if tree_quota_id and len(all_tree_quota) == 0 \ and self.module.params['state'] == "present": errormsg = "Tree quota %s does not exist." % tree_quota_id LOG.error(errormsg) self.module.fail_json(msg=errormsg) if len(all_tree_quota) > 0: msg = "Quota tree with id %s is present in filesystem %s" % (all_tree_quota[0].id, fs_id) LOG.info(msg) return all_tree_quota[0].id else: return None def create_quota_tree(self, fs_id, soft_limit, hard_limit, unit, path, description): """ Create quota tree of a filesystem. :param fs_id: ID of filesystem where quota tree is to be created. :param soft_limit: Soft limit :param hard_limit: Hard limit :param unit: Unit of soft limit and hard limit :param path: Path to quota tree :param description: Description for quota tree :return: Dict containing new quota tree details. """ if soft_limit is None and hard_limit is None: errormsg = "Both soft limit and hard limit cannot be empty. " \ "Please provide atleast one to create quota tree." LOG.error(errormsg) self.module.fail_json(msg=errormsg) soft_limit_in_bytes = utils.get_size_bytes(soft_limit, unit) hard_limit_in_bytes = utils.get_size_bytes(hard_limit, unit) try: obj_tree_quota = self.unity_conn.create_tree_quota(filesystem_id=fs_id, hard_limit=hard_limit_in_bytes, soft_limit=soft_limit_in_bytes, path=path, description=description) LOG.info("Successfully created quota tree") if obj_tree_quota: return obj_tree_quota else: return None except Exception as e: errormsg = "Create quota tree operation at path {0} failed in filesystem {1}" \ " with error {2}".format(path, fs_id, str(e)) LOG.error(errormsg) self.module.fail_json(msg=errormsg) def get_filesystem_tree_quota_display_attributes(self, tree_quota_id): """Display quota tree attributes :param tree_quota_id: Quota tree ID :return: Quota tree dict to display """ try: tree_quota_obj = self.unity_conn.get_tree_quota(_id=tree_quota_id) tree_quota_details = tree_quota_obj._get_properties() if tree_quota_obj and tree_quota_obj.existed: tree_quota_details['soft_limit'] = utils. \ convert_size_with_unit(int(tree_quota_details['soft_limit'])) tree_quota_details['hard_limit'] = utils. \ convert_size_with_unit(int(tree_quota_details['hard_limit'])) tree_quota_details['filesystem']['UnityFileSystem']['name'] = \ tree_quota_obj.filesystem.name tree_quota_details['filesystem']['UnityFileSystem'].update( {'nas_server': {'name': tree_quota_obj.filesystem.nas_server.name, 'id': tree_quota_obj.filesystem.nas_server.id}}) return tree_quota_details except Exception as e: errormsg = "Failed to display quota tree details {0} with " \ "error {1}".format(tree_quota_obj.id, str(e)) LOG.error(errormsg) self.module.fail_json(msg=errormsg) def get_filesystem(self, nas_server=None, name=None, id=None): """ Get filesystem details. :param nas_server: Nas server object. :param name: Name of filesystem. :param id: ID of filesystem. :return: Dict containing filesystem details if it exists. """ id_or_name = id if id else name try: obj_fs = None if name: if not nas_server: err_msg = "NAS Server is required to get the FileSystem." LOG.error(err_msg) self.module.fail_json(msg=err_msg) obj_fs = self.unity_conn.get_filesystem(name=name, nas_server=nas_server) if obj_fs and obj_fs.existed: LOG.info("Successfully got the filesystem object %s.", obj_fs) return obj_fs if id: if nas_server: obj_fs = self.unity_conn \ .get_filesystem(id=id, nas_server=nas_server) else: obj_fs = self.unity_conn.get_filesystem(id=id) if obj_fs and obj_fs.existed: LOG.info("Successfully got the filesystem object %s.", obj_fs) return obj_fs except Exception as e: error_msg = "Failed to get filesystem %s with error %s." \ % (id_or_name, str(e)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) def get_nas_server_obj(self, name=None, id=None): """ Get nas server details. :param name: Nas server name. :param id: Nas server ID. :return: Dict containing nas server details if it exists. """ nas_server = id if id else name error_msg = ("Failed to get NAS server %s." % nas_server) try: obj_nas = self.unity_conn.get_nas_server(_id=id, name=name) if name and obj_nas.existed: LOG.info("Successfully got the NAS server object %s.", obj_nas) return obj_nas elif id and obj_nas.existed: LOG.info("Successfully got the NAS server object %s.", obj_nas) return obj_nas else: LOG.error(error_msg) self.module.fail_json(msg=error_msg) except Exception as e: error_msg = "Failed to get NAS server %s with error %s." \ % (nas_server, str(e)) LOG.error(error_msg) self.module.fail_json(msg=error_msg) def modify_tree_quota(self, tree_quota_id, soft_limit, hard_limit, unit, description): """ Modify quota tree of filesystem. :param tree_quota_id: ID of the quota tree :param soft_limit: Soft limit :param hard_limit: Hard limit :param unit: Unit of soft limit and hard limit :param description: Description of quota tree :return: Boolean value whether modify quota tree operation is successful. """ try: if soft_limit is None and hard_limit is None: return False tree_quota_obj = self.unity_conn.get_tree_quota(tree_quota_id)._get_properties() if soft_limit is None: soft_limit_in_bytes = tree_quota_obj['soft_limit'] else: soft_limit_in_bytes = utils.get_size_bytes(soft_limit, unit) if hard_limit is None: hard_limit_in_bytes = tree_quota_obj['hard_limit'] else: hard_limit_in_bytes = utils.get_size_bytes(hard_limit, unit) if description is None: description = tree_quota_obj['description'] if tree_quota_obj: if tree_quota_obj['soft_limit'] == soft_limit_in_bytes and \ tree_quota_obj['hard_limit'] == hard_limit_in_bytes and \ tree_quota_obj['description'] == description: return False else: modify_tree_quota = self.unity_conn.modify_tree_quota(tree_quota_id=tree_quota_id, hard_limit=hard_limit_in_bytes, soft_limit=soft_limit_in_bytes, description=description) LOG.info("Successfully modified quota tree") if modify_tree_quota: return True except Exception as e: errormsg = "Modify quota tree operation {0} failed" \ " with error {1}".format(tree_quota_id, str(e)) LOG.error(errormsg) self.module.fail_json(msg=errormsg) def delete_tree_quota(self, tree_quota_id): """ Delete quota tree of a filesystem. :param tree_quota_id: ID of quota tree :return: Boolean whether quota tree is deleted """ try: delete_tree_quota_obj = self.unity_conn.delete_tree_quota(tree_quota_id=tree_quota_id) if delete_tree_quota_obj: return True except Exception as e: errormsg = "Delete operation of quota tree id:{0} " \ "failed with error {1}".format(tree_quota_id, str(e)) LOG.error(errormsg) self.module.fail_json(msg=errormsg) def perform_module_operation(self): """ Perform different actions on quota tree module based on parameters passed in the playbook """ filesystem_id = self.module.params['filesystem_id'] filesystem_name = self.module.params['filesystem_name'] nas_server_name = self.module.params['nas_server_name'] nas_server_id = self.module.params['nas_server_id'] cap_unit = self.module.params['cap_unit'] state = self.module.params['state'] hard_limit = self.module.params['hard_limit'] soft_limit = self.module.params['soft_limit'] path = self.module.params['path'] description = self.module.params['description'] tree_quota_id = self.module.params['tree_quota_id'] create_tree_quota_obj = None nas_server_resource = None fs_id = None ''' result is a dictionary to contain end state and quota tree details ''' result = dict( changed=False, create_tree_quota=False, modify_tree_quota=False, get_tree_quota_details={}, delete_tree_quota=False ) if (soft_limit or hard_limit) and cap_unit is None: cap_unit = 'GB' if soft_limit and utils.is_size_negative(soft_limit): error_message = "Invalid soft_limit provided, " \ "must be greater than or equal to 0" LOG.error(error_message) self.module.fail_json(msg=error_message) if hard_limit and utils.is_size_negative(hard_limit): error_message = "Invalid hard_limit provided, " \ "must be greater than or equal to 0" LOG.error(error_message) self.module.fail_json(msg=error_message) ''' Get NAS server Object ''' if nas_server_name is not None: if utils.is_input_empty(nas_server_name): self.module.fail_json(msg="Invalid nas_server_name given," " Please provide a valid name.") nas_server_resource = self \ .get_nas_server_obj(name=nas_server_name) elif nas_server_id is not None: if utils.is_input_empty(nas_server_id): self.module.fail_json(msg="Invalid nas_server_id given," " Please provide a valid ID.") nas_server_resource = self.get_nas_server_obj(id=nas_server_id) ''' Get filesystem Object ''' if filesystem_name is not None: if utils.is_input_empty(filesystem_name): self.module.fail_json(msg="Invalid filesystem_name given," " Please provide a valid name.") filesystem_obj = self \ .get_filesystem(nas_server=nas_server_resource, name=filesystem_name) fs_id = filesystem_obj.id elif filesystem_id is not None: if utils.is_input_empty(filesystem_id): self.module.fail_json(msg="Invalid filesystem_id given," " Please provide a valid ID.") filesystem_obj = self \ .get_filesystem(id=filesystem_id) if filesystem_obj: fs_id = filesystem_obj[0].id else: self.module.fail_json(msg="Filesystem does not exist.") ''' Validate path to quota tree ''' if path is not None: if utils.is_input_empty(path): self.module.fail_json(msg=" Please provide a valid path.") elif not path.startswith('/'): self.module.fail_json(msg="The path is relative to the root of the file system " "and must start with a forward slash '/'.") if filesystem_id is None and filesystem_name is None: self.module.fail_json(msg="Please provide either filesystem_name or fileystem_id.") quota_tree_id_present = self.check_quota_tree_is_present(fs_id, path, tree_quota_id) tree_quota_id = quota_tree_id_present ''' Create quota tree ''' if (filesystem_id or filesystem_name) and path is not None and state == "present": if not tree_quota_id: LOG.info("Creating quota tree") create_tree_quota_obj = self.create_quota_tree(fs_id, soft_limit, hard_limit, cap_unit, path, description) if create_tree_quota_obj: tree_quota_id = create_tree_quota_obj.id result['create_tree_quota'] = True ''' Modify quota tree ''' if tree_quota_id and state == "present": LOG.info("Modifying quota tree") result['modify_tree_quota'] = self.modify_tree_quota(tree_quota_id, soft_limit, hard_limit, cap_unit, description) ''' Delete quota tree ''' if tree_quota_id is not None and state == "absent": LOG.info("Deleting quota tree") result['delete_tree_quota'] = self.delete_tree_quota(tree_quota_id) ''' Get quota tree details ''' if state == "present" and tree_quota_id is not None: result['get_tree_quota_details'] = self.get_filesystem_tree_quota_display_attributes(tree_quota_id) else: result['get_tree_quota_details'] = {} if result['create_tree_quota'] or result['modify_tree_quota'] or result['delete_tree_quota']: result['changed'] = True self.module.exit_json(**result) def get_quota_tree_parameters(): """This method provide parameters required for the ansible quota tree module on Unity""" return dict( filesystem_id=dict(required=False, type='str'), filesystem_name=dict(required=False, type='str'), state=dict(required=True, type='str', choices=['present', 'absent']), hard_limit=dict(required=False, type='int'), soft_limit=dict(required=False, type='int'), cap_unit=dict(required=False, type='str', choices=['MB', 'GB', 'TB']), tree_quota_id=dict(required=False, type='str'), nas_server_name=dict(required=False, type='str'), nas_server_id=dict(required=False, type='str'), path=dict(required=False, type='str', no_log=True), description=dict(required=False, type='str') ) def main(): """ Create Unity quota tree object and perform action on it based on user input from playbook""" obj = QuotaTree() obj.perform_module_operation() if __name__ == '__main__': main()