<filename>plugins/modules/dellemc_unity_tree_quota.py
#!/usr/bin/python
# Copyright: (c) 2021, DellEMC

"""Ansible module for managing quota tree on Unity"""

from __future__ import absolute_import, division, print_function

__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'
                    }

DOCUMENTATION = r'''
---
module: dellemc_unity_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.dellemc_unity.unity
author:
- <NAME> (@panigs7) <<EMAIL>>
options:
  filesystem_name:
    description:
    - The name of the filesystem for which quota tree is created.
    - For creation or modification of a quota tree either filesystem_name or
      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 filesystem_id or
      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 nas_server_name or
      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 filesystem_id or
      filesystem_name is required.
    type: str
  tree_quota_id:
    description:
    - The ID of the quota tree.
    - Either tree_quota_id or path to quota tree is required to
      view/modify/delete quota tree.
    type: str
  path:
    description:
    - The path to the quota tree.
    - Either tree_quota_id or 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 0 implies no limit.
    - One of the values of soft_limit and hard_limit can be 0, however, both cannot be both 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 0 implies no limit.
    - Both soft_limit and hard_limit cannot be 0 during creation of quota tree.
    type: int
  cap_unit:
    description:
    - Unit of soft_limit and hard_limit size.
    - It defaults to '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']
'''

EXAMPLES = r'''
  - name: Get quota tree details by quota tree id
    dellemc_unity_tree_quota:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      quota_tree_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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      quota_tree_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}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      quota_tree_id: "treequota_171798700679_10"
      state: "absent"

  - name: Delete quota tree by path
    dellemc_unity_tree_quota:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      path: "/test_new"
      state: "absent"
'''

RETURN = r'''
changed:
    description: Whether or not the resource has changed
    returned: always
    type: bool
get_quota_tree_details:
    description: Details of the quota tree.
    returned: When quota tree exists
    type: complex
    contains:
        filesystem:
            description: Filesystem details for which the quota
                         tree is created.
            type: complex
            contains:
                UnityFileSystem:
                    description: Filesystem details for which the
                                 quota tree is created.
                    type: complex
                    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
'''

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \
    import dellemc_ansible_unity_utils as utils

LOG = utils.get_logger('dellemc_unity_tree_quota')

HAS_UNITY_SDK = utils.get_unity_sdk()

UNITY_SDK_VERSION_CHECK = utils.storops_version_check()


class UnityQuotaTree(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_unity_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)

        if not HAS_UNITY_SDK:
            self.module.fail_json(msg="This Ansible module for Unity require the"
                                      " Unity python library version >= 1.2.9 to be "
                                      "installed. Please install the library "
                                      "before using this module.")

        if UNITY_SDK_VERSION_CHECK and not UNITY_SDK_VERSION_CHECK[
           'supported_version']:
            err_msg = UNITY_SDK_VERSION_CHECK['unsupported_version_message']
            LOG.error(err_msg)
            self.module.fail_json(msg=err_msg)

        self.unity_conn = utils.get_unity_unisphere_connection(
            self.module.params)

    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
        get_unity_quota_tree_details = None
        fs_id = None
        changed = False

        '''
        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=False,
            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_unity_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'),
        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 = UnityQuotaTree()
    obj.perform_module_operation()


if __name__ == '__main__':
    main()

<filename>plugins/modules/dellemc_unity_tree_quota.py
#!/usr/bin/python
# Copyright: (c) 2021, DellEMC

"""Ansible module for managing quota tree on Unity"""

from __future__ import absolute_import, division, print_function

__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'
                    }

DOCUMENTATION = r'''
---
module: dellemc_unity_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.dellemc_unity.unity
author:
- <NAME> (@panigs7) <<EMAIL>>
options:
  filesystem_name:
    description:
    - The name of the filesystem for which quota tree is created.
    - For creation or modification of a quota tree either filesystem_name or
      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 filesystem_id or
      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 nas_server_name or
      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 filesystem_id or
      filesystem_name is required.
    type: str
  tree_quota_id:
    description:
    - The ID of the quota tree.
    - Either tree_quota_id or path to quota tree is required to
      view/modify/delete quota tree.
    type: str
  path:
    description:
    - The path to the quota tree.
    - Either tree_quota_id or 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 0 implies no limit.
    - One of the values of soft_limit and hard_limit can be 0, however, both cannot be both 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 0 implies no limit.
    - Both soft_limit and hard_limit cannot be 0 during creation of quota tree.
    type: int
  cap_unit:
    description:
    - Unit of soft_limit and hard_limit size.
    - It defaults to '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']
'''

EXAMPLES = r'''
  - name: Get quota tree details by quota tree id
    dellemc_unity_tree_quota:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      quota_tree_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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      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}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      quota_tree_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}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      quota_tree_id: "treequota_171798700679_10"
      state: "absent"

  - name: Delete quota tree by path
    dellemc_unity_tree_quota:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      filesystem_id: "fs_2171"
      path: "/test_new"
      state: "absent"
'''

RETURN = r'''
changed:
    description: Whether or not the resource has changed
    returned: always
    type: bool
get_quota_tree_details:
    description: Details of the quota tree.
    returned: When quota tree exists
    type: complex
    contains:
        filesystem:
            description: Filesystem details for which the quota
                         tree is created.
            type: complex
            contains:
                UnityFileSystem:
                    description: Filesystem details for which the
                                 quota tree is created.
                    type: complex
                    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
'''

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \
    import dellemc_ansible_unity_utils as utils

LOG = utils.get_logger('dellemc_unity_tree_quota')

HAS_UNITY_SDK = utils.get_unity_sdk()

UNITY_SDK_VERSION_CHECK = utils.storops_version_check()


class UnityQuotaTree(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_unity_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)

        if not HAS_UNITY_SDK:
            self.module.fail_json(msg="This Ansible module for Unity require the"
                                      " Unity python library version >= 1.2.9 to be "
                                      "installed. Please install the library "
                                      "before using this module.")

        if UNITY_SDK_VERSION_CHECK and not UNITY_SDK_VERSION_CHECK[
           'supported_version']:
            err_msg = UNITY_SDK_VERSION_CHECK['unsupported_version_message']
            LOG.error(err_msg)
            self.module.fail_json(msg=err_msg)

        self.unity_conn = utils.get_unity_unisphere_connection(
            self.module.params)

    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
        get_unity_quota_tree_details = None
        fs_id = None
        changed = False

        '''
        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=False,
            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_unity_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'),
        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 = UnityQuotaTree()
    obj.perform_module_operation()


if __name__ == '__main__':
    main()

#!/usr/bin/python # Copyright: (c) 2021, DellEMC Ansible module for managing quota tree on Unity --- module: dellemc_unity_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.dellemc_unity.unity author: - <NAME> (@panigs7) <<EMAIL>> options:   filesystem_name:     description:     - The name of the filesystem for which quota tree is created.     - For creation or modification of a quota tree either filesystem_name or       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 filesystem_id or       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 nas_server_name or       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 filesystem_id or       filesystem_name is required.     type: str   tree_quota_id:     description:     - The ID of the quota tree.     - Either tree_quota_id or path to quota tree is required to       view/modify/delete quota tree.     type: str   path:     description:     - The path to the quota tree.     - Either tree_quota_id or 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 0 implies no limit.     - One of the values of soft_limit and hard_limit can be 0, however, both cannot be both 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 0 implies no limit.     - Both soft_limit and hard_limit cannot be 0 during creation of quota tree.     type: int   cap_unit:     description:     - Unit of soft_limit and hard_limit size.     - It defaults to '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'] - name: Get quota tree details by quota tree id     dellemc_unity_tree_quota:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       quota_tree_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}}"       verifycert: "{{verifycert}}"       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}}"       verifycert: "{{verifycert}}"       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}}"       verifycert: "{{verifycert}}"       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}}"       verifycert: "{{verifycert}}"       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}}"       verifycert: "{{verifycert}}"       filesystem_id: "fs_2171"       quota_tree_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}}"       verifycert: "{{verifycert}}"       filesystem_id: "fs_2171"       quota_tree_id: "treequota_171798700679_10"       state: "absent"    - name: Delete quota tree by path     dellemc_unity_tree_quota:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       filesystem_id: "fs_2171"       path: "/test_new"       state: "absent" changed:     description: Whether or not the resource has changed     returned: always     type: bool get_quota_tree_details:     description: Details of the quota tree.     returned: When quota tree exists     type: complex     contains:         filesystem:             description: Filesystem details for which the quota                          tree is created.             type: complex             contains:                 UnityFileSystem:                     description: Filesystem details for which the                                  quota tree is created.                     type: complex                     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 Class with Quota Tree operations Define all parameters required by this module # initialize the Ansible module 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. 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. Display quota tree attributes             :param tree_quota_id: Quota tree ID             :return: Quota tree dict to display 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. Get nas server details.             :param name: Nas server name.             :param id: Nas server ID.             :return: Dict containing nas server details if it exists. 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. Delete quota tree of a filesystem.         :param tree_quota_id: ID of quota tree         :return: Boolean whether quota tree is deleted Perform different actions on quota tree module based on parameters         passed in the playbook result is a dictionary to contain end state and quota tree details Get NAS server Object Get filesystem Object Validate path to quota tree Create quota tree Modify quota tree Delete quota tree Get quota tree details This method provide parameters required for the ansible        quota tree module on Unity Create Unity quota tree object and perform action on it         based on user input from playbook