#!/usr/bin/python
# Copyright: (c) 2020, DellEMC
""" Ansible module for managing Filesystem Snapshots 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_filesystem_snapshot
short_description: Manage filesystem snapshot on the Unity storage system
description:
- Managing Filesystem Snapshot on the Unity storage system includes
  create filesystem snapshot, get filesystem snapshot, modify filesystem
  snapshot and delete filesystem snapshot.
version_added: "1.1.0"
extends_documentation_fragment:
  - dellemc.unity.dellemc_unity.unity
author:
- <NAME> (@kharer5) <<EMAIL>>
options:
  snapshot_name:
    description:
    - The name of the filesystem snapshot.
    - Mandatory parameter for creating a filesystem snapshot.
    - For all other operations either snapshot_name or snapshot_id
      is required.
    type: str
  snapshot_id:
    description:
    - During creation snapshot_id is auto generated.
    - For all other operations either snapshot_id or snapshot_name
      is required.
    type: str
  filesystem_name:
    description:
    - The name of the Filesystem for which snapshot is created.
    - For creation of filesystem snapshot either filesystem_name or
      filesystem_id is required.
    - Not required for other operations.
    type: str
  filesystem_id:
    description:
    - The ID of the Filesystem for which snapshot is created.
    - For creation of filesystem snapshot either filesystem_id or
      filesystem_name is required.
    - Not required for other operations.
    type: str
  nas_server_name:
    description:
    - The name of the NAS server in which the Filesystem is created.
    - For creation of filesystem snapshot either nas_server_name or
      nas_server_id is required.
    - Not required for other operations.
    type: str
  nas_server_id:
    description:
    - The ID of the NAS server in which the Filesystem is created.
    - For creation of filesystem snapshot either filesystem_id or
      filesystem_name is required.
    - Not required for other operations.
    type: str
  auto_delete:
    description:
    - This option specifies whether or not the filesystem snapshot will be
      automatically deleted.
    - If set to true, the filesystem snapshot will expire based on the pool
      auto deletion policy.
    - If set to false, the filesystem snapshot will not be auto deleted
      based on the pool auto deletion policy.
    - auto_delete can not be set to True, if expiry_time is specified.
    - If during creation neither auto_delete nor expiry_time is mentioned
      then the filesystem snapshot will be created keeping auto_delete as
      True.
    - Once the expiry_time is set, then the filesystem snapshot cannot be
      assigned to the auto delete policy.
    type: bool
  expiry_time:
    description:
    - This option is for specifying the date and time after which the
      filesystem snapshot will expire.
    - The time is to be mentioned in UTC timezone.
    - The format is "MM/DD/YYYY HH:MM". Year must be in 4 digits.
    type: str
  description:
    description:
    - The additional information about the filesystem snapshot can be
      provided using this option.
    - The description can be removed by passing an empty string.
    type: str
  fs_access_type:
    description:
    - Access type of the filesystem snapshot.
    - Required only during creation of filesystem snapshot.
    - If not given, snapshot's access type will be 'Checkpoint'.
    type: str
    choices: ['Checkpoint' , 'Protocol']
  state:
    description:
    - The state option is used to mention the existence of the filesystem
      snapshot.
    type: str
    required: True
    choices: ['absent', 'present']
notes:
  - Filesystem snapshot cannot be deleted, if it has nfs or smb share.
'''

EXAMPLES = r'''
  - name: Create Filesystem Snapshot
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      filesystem_name: "ansible_test_FS"
      nas_server_name: "lglad069"
      description: "Created using playbook"
      auto_delete: True
      fs_access_type: "Protocol"
      state: "present"

  - name: Create Filesystem Snapshot with expiry time.
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap_1"
      filesystem_name: "ansible_test_FS_1"
      nas_server_name: "lglad069"
      description: "Created using playbook"
      expiry_time: "04/15/2021 2:30"
      fs_access_type: "Protocol"
      state: "present"

  - name: Get Filesystem Snapshot Details using Name
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      state: "present"

  - name: Get Filesystem Snapshot Details using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      state: "present"

  - name: Update Filesystem Snapshot attributes
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      description: "Description updated"
      auto_delete: False
      expiry_time: "04/15/2021 5:30"
      state: "present"

  - name: Update Filesystem Snapshot attributes using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      expiry_time: "04/18/2021 8:30"
      state: "present"

  - name: Delete Filesystem Snapshot using Name
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      state: "absent"

  - name: Delete Filesystem Snapshot using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      state: "absent"
'''

RETURN = r'''
changed:
    description: Whether or not the resource has changed
    returned: always
    type: bool
filesystem_snapshot_details:
    description: Details of the filesystem snapshot.
    returned: When filesystem snapshot exists
    type: complex
    contains:
        access_type:
            description: Access type of filesystem snapshot.
            type: str
        attached_wwn:
            description: Attached WWN details.
            type: str
        creation_time:
            description: Creation time of filesystem snapshot.
            type: str
        creator_schedule:
            description: Creator schedule of filesystem snapshot.
            type: str
        creator_type:
            description: Creator type for filesystem snapshot.
            type: str
        creator_user:
            description: Creator user for filesystem snapshot.
            type: str
        description:
            description: Description of the filesystem snapshot.
            type: str
        expiration_time:
            description: Date and time after which the filesystem snapshot
                         will expire.
            type: str
        is_auto_delete:
            description: Is the filesystem snapshot is auto deleted or not.
            type: bool
        id:
            description: Unique identifier of the filesystem snapshot
                         instance.
            type: str
        name:
            description: The name of the filesystem snapshot.
            type: str
        size:
            description: Size of the filesystem snapshot.
            type: int
        filesystem_name:
            description: Name of the filesystem for which the snapshot exists.
            type: str
        filesystem_id:
            description: Id of the filesystem for which the snapshot exists.
            type: str
        nas_server_name:
            description: Name of the NAS server on which filesystem exists.
            type: str
        nas_server_id:
            description: Id of the NAS server on which filesystem exists.
            type: str

'''

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

LOG = utils.get_logger('dellemc_unity_filesystem_snapshot')

HAS_UNITY_SDK = utils.get_unity_sdk()
UNITY_SDK_VERSION_CHECK = utils.storops_version_check()

application_type = "Ansible/1.2.0"


class UnityFilesystemSnapshot(object):
    """Class with Filesystem Snapshot 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_snapshot_parameters())

        mutually_exclusive = [['snapshot_name', 'snapshot_id'],
                              ['filesystem_name', 'filesystem_id'],
                              ['nas_server_name', 'nas_server_id']]

        required_one_of = [['snapshot_name', 'snapshot_id']]
        # initialize the ansible module
        self.module = AnsibleModule(argument_spec=self.module_params,
                                    supports_check_mode=False,
                                    mutually_exclusive=mutually_exclusive,
                                    required_one_of=required_one_of)

        # result is a dictionary that contains changed status and
        # filesystem snapshot details
        self.result = {"changed": False,
                       'filesystem_snapshot_details': None}

        if not HAS_UNITY_SDK:
            self.module.fail_json(msg="Ansible modules for Unity require the"
                                      " Unity python library to be"
                                      " installed. Please install the "
                                      "library before using these modules.")

        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, application_type)
        self.snap_obj = utils.snap.UnitySnap(self.unity_conn)
        LOG.info('Connection established with the Unity Array')

    def validate_expiry_time(self, expiry_time):
        """Validates the specified expiry_time"""
        try:
            datetime.strptime(expiry_time, '%m/%d/%Y %H:%M')
        except ValueError:
            error_msg = ("expiry_time: %s, not in MM/DD/YYYY HH:MM format." %
                         expiry_time)
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def to_update(self, fs_snapshot, description=None, auto_del=None,
                  expiry_time=None, fs_access_type=None):
        """Determines whether to update the snapshot or not"""
        snap_modify_dict = dict()

        if fs_access_type and fs_access_type != fs_snapshot.access_type:
            error_message = "Modification of access type is not allowed."
            LOG.error(error_message)
            self.module.fail_json(msg=error_message)
        if expiry_time:
            # If the snapshot has is_auto_delete True,
            # Check if auto_delete in the input is either None or True
            if fs_snapshot.is_auto_delete and (auto_del is None or auto_del):
                self.module.fail_json(msg="expiry_time can be assigned when"
                                          " auto delete is False.")
        if auto_del is not None:
            if fs_snapshot.expiration_time:
                error_msg = "expiry_time for filesystem snapshot is set." \
                            " Once it is set then snapshot cannot" \
                            " be assigned to auto_delete policy."
                self.module.fail_json(msg=error_msg)
            if auto_del != fs_snapshot.is_auto_delete:
                snap_modify_dict['is_auto_delete'] = auto_del

        if description is not None and description != fs_snapshot.description:
            snap_modify_dict['description'] = description

        if to_update_expiry_time(fs_snapshot, expiry_time):
            snap_modify_dict['expiry_time'] = expiry_time
        LOG.info("Snapshot modification details: %s", snap_modify_dict)
        return snap_modify_dict

    def update_filesystem_snapshot(self, fs_snapshot, snap_modify_dict):
        try:
            duration = None
            if 'expiry_time' in snap_modify_dict \
                    and snap_modify_dict['expiry_time']:
                duration = convert_timestamp_to_sec(
                    snap_modify_dict['expiry_time'],
                    self.unity_conn.system_time)
            if duration and duration <= 0:
                self.module.fail_json(msg="expiry_time should be after"
                                          " the current system time.")
            if 'is_auto_delete' in snap_modify_dict \
                    and snap_modify_dict['is_auto_delete'] is not None:
                auto_delete = snap_modify_dict['is_auto_delete']
            else:
                auto_delete = None
            if 'description' in snap_modify_dict \
                    and (snap_modify_dict['description']
                         or len(snap_modify_dict['description']) == 0):
                description = snap_modify_dict['description']
            else:
                description = None

            fs_snapshot.modify(retentionDuration=duration,
                               isAutoDelete=auto_delete,
                               description=description)
            fs_snapshot.update()
        except Exception as e:
            error_msg = "Failed to modify filesystem snapshot" \
                        " [name: %s , id: %s] with error %s."\
                        % (fs_snapshot.name, fs_snapshot.id, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def create_filesystem_snapshot(self, snap_name, storage_id,
                                   description=None, auto_del=None,
                                   expiry_time=None, fs_access_type=None):
        try:
            duration = None
            if expiry_time:
                duration = convert_timestamp_to_sec(
                    expiry_time, self.unity_conn.system_time)
                if duration <= 0:
                    self.module.fail_json(msg="expiry_time should be after"
                                              " the current system time.")

            fs_snapshot = self.snap_obj.create(
                cli=self.unity_conn._cli, storage_resource=storage_id,
                name=snap_name, description=description,
                is_auto_delete=auto_del, retention_duration=duration,
                fs_access_type=fs_access_type)
            return fs_snapshot
        except Exception as e:
            error_msg = "Failed to create filesystem snapshot" \
                        " %s with error %s" % (snap_name, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def is_snap_has_share(self, fs_snap):
        try:
            obj = self.unity_conn.get_nfs_share(snap=fs_snap) or \
                self.unity_conn.get_cifs_share(snap=fs_snap)
            if len(obj) > 0:
                LOG.info("Snapshot has %s nfs/smb share/s", len(obj))
                return True
        except Exception as e:
            msg = "Failed to get nfs/smb share from filesystem snapshot. " \
                  "error: %s" % str(e)
            LOG.error(msg)
            self.module.fail_json(msg=msg)
        return False

    def delete_fs_snapshot(self, fs_snapshot):
        try:
            # Checking whether nfs/smb share created from fs_snapshot
            if self.is_snap_has_share(fs_snapshot):
                msg = "Filesystem snapshot cannot be deleted because it has " \
                      "nfs/smb share"
                LOG.error(msg)
                self.module.fail_json(msg=msg)
            fs_snapshot.delete()
            return None

        except Exception as e:
            error_msg = "Failed to delete filesystem snapshot" \
                        " [name: %s, id: %s] with error %s." \
                        % (fs_snapshot.name, fs_snapshot.id, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def get_fs_snapshot_obj(self, name=None, id=None):
        fs_snapshot = id if id else name
        msg = "Failed to get details of filesystem snapshot %s with error %s."
        try:
            fs_snap_obj = self.unity_conn.get_snap(name=name, _id=id)
            if fs_snap_obj and fs_snap_obj.existed:
                LOG.info("Successfully got the filesystem snapshot object "
                         "%s.", fs_snap_obj)
            else:
                fs_snap_obj = None
            return fs_snap_obj

        except utils.HttpError as e:
            if e.http_status == 401:
                cred_err = ("Incorrect username or password , %s" % e.message)
                self.module.fail_json(msg=cred_err)
            else:
                err_msg = msg % (fs_snapshot, str(e))
                LOG.error(err_msg)
                self.module.fail_json(msg=err_msg)

        except utils.UnityResourceNotFoundError as e:
            err_msg = msg % (fs_snapshot, str(e))
            LOG.error(err_msg)
            return None

        except Exception as e:
            err_msg = msg % (fs_snapshot, str(e))
            LOG.error(err_msg)
            self.module.fail_json(msg=err_msg)

    def get_filesystem_obj(self, nas_server=None, name=None, id=None):
        filesystem = 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."\
                        % (filesystem, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def get_nas_server_obj(self, name=None, id=None):
        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 create_fs_snapshot_details_dict(self, fs_snapshot):
        """ Add name and id of storage resource to filesystem snapshot
            details """

        snapshot_dict = fs_snapshot._get_properties()
        del snapshot_dict['storage_resource']

        snapshot_dict['filesystem_name'] = fs_snapshot.storage_resource.name
        snapshot_dict['filesystem_id'] = fs_snapshot.storage_resource.filesystem.id

        obj_fs = self.unity_conn.\
            get_filesystem(id=fs_snapshot.storage_resource.filesystem.id)
        if obj_fs and obj_fs.existed:
            snapshot_dict['nas_server_name'] = obj_fs.nas_server[0].name
            snapshot_dict['nas_server_id'] = obj_fs.nas_server[0].id

        return snapshot_dict

    def perform_module_operation(self):
        """
        Perform different actions on snapshot module based on parameters
        chosen in playbook
        """
        snapshot_name = self.module.params['snapshot_name']
        snapshot_id = self.module.params['snapshot_id']
        filesystem_name = self.module.params['filesystem_name']
        filesystem_id = self.module.params['filesystem_id']
        nas_server_name = self.module.params['nas_server_name']
        nas_server_id = self.module.params['nas_server_id']
        auto_delete = self.module.params['auto_delete']
        expiry_time = self.module.params['expiry_time']
        description = self.module.params['description']
        fs_access_type = self.module.params['fs_access_type']
        state = self.module.params['state']
        nas_server_resource = None
        filesystem_resource = None
        changed = False

        LOG.info("Getting Filesystem Snapshot details.")
        fs_snapshot = self.get_fs_snapshot_obj(name=snapshot_name,
                                               id=snapshot_id)

        msg = "Filesystem Snapshot details: %s." % str(fs_snapshot)
        LOG.info(msg)

        # Get NAS server Object
        if nas_server_name is not None:
            if nas_server_name == "" or nas_server_name.isspace():
                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 nas_server_id == "" or nas_server_id.isspace():
                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 filesystem_name == "" or filesystem_name.isspace():
                self.module.fail_json(msg="Invalid filesystem_name given,"
                                          " Please provide a valid name.")
            filesystem_resource = self\
                .get_filesystem_obj(nas_server=nas_server_resource,
                                    name=filesystem_name)
            fs_res_id = filesystem_resource.storage_resource.id
        elif filesystem_id is not None:
            if filesystem_id == "" or filesystem_id.isspace():
                self.module.fail_json(msg="Invalid filesystem_id given,"
                                          " Please provide a valid ID.")
            filesystem_resource = self\
                .get_filesystem_obj(id=filesystem_id)
            fs_res_id = filesystem_resource[0].storage_resource.id

        # Check for error, if user tries to create a filesystem snapshot
        # with the same name.
        if fs_snapshot and filesystem_resource and \
            (fs_snapshot.storage_resource.id
             != fs_res_id):
            self.module.fail_json(
                msg="Snapshot %s is of %s storage resource. Cannot create new"
                    " snapshot with same name for %s storage resource."
                    % (fs_snapshot.name, fs_snapshot.storage_resource.name,
                       filesystem_resource.storage_resource.name))

        # check for valid expiry_time
        if expiry_time is not None and \
                (expiry_time == "" or expiry_time.isspace()):
            self.module.fail_json(msg="Please provide valid expiry_time,"
                                      " empty expiry_time given.")
        if expiry_time:
            self.validate_expiry_time(expiry_time)

        # Check if in input auto_delete is True and expiry_time is not None
        if expiry_time and auto_delete:
            error_msg = "Cannot set expiry_time if auto_delete given as True."
            LOG.info(error_msg)
            self.module.fail_json(msg=error_msg)

        # check for fs_access_type
        if fs_access_type is not None:
            if (fs_access_type == "" or fs_access_type.isspace()):
                self.module.fail_json(msg="Please provide valid "
                                          "fs_access_type, empty "
                                          "fs_access_type given.")
            if fs_access_type == "Checkpoint":
                fs_access_type = utils.FilesystemSnapAccessTypeEnum.CHECKPOINT
            elif fs_access_type == "Protocol":
                fs_access_type = utils.FilesystemSnapAccessTypeEnum.PROTOCOL

        # Check whether to modify the filesystem snapshot or not
        fs_snap_modify_dict = dict()
        if state == 'present' and fs_snapshot:
            fs_snap_modify_dict = self\
                .to_update(fs_snapshot, description=description,
                           auto_del=auto_delete, expiry_time=expiry_time,
                           fs_access_type=fs_access_type)

        # Create Filesystem Snapshot
        if not fs_snapshot and state == "present":
            LOG.info("Creating the filesystem snapshot.")

            if snapshot_id:
                self.module.fail_json(msg="Creation of Filesystem Snapshot is"
                                          " allowed using snapshot_name only,"
                                          " snapshot_id given.")
            if snapshot_name == "" or snapshot_name.isspace():
                self.module.fail_json(msg="snapshot_name is required for"
                                          " creation of the filesystem"
                                          " snapshot, empty snapshot_name"
                                          " given.")
            if not filesystem_resource:
                self.module.fail_json(msg="filesystem_name or filesystem_id"
                                          " required to create a snapshot.")

            fs_snapshot = self.create_filesystem_snapshot(
                snapshot_name,
                fs_res_id,
                description,
                auto_delete,
                expiry_time,
                fs_access_type)
            changed = True

        # Update the Snapshot
        if fs_snapshot and state == "present" and fs_snap_modify_dict:
            LOG.info("Updating the Filesystem Snapshot.")
            self.update_filesystem_snapshot(fs_snapshot, fs_snap_modify_dict)
            changed = True

        # Delete the Filesystem Snapshot
        if state == "absent" and fs_snapshot:
            fs_snapshot = self.delete_fs_snapshot(fs_snapshot)
            changed = True

        # Add filesystem snapshot details to the result.
        if fs_snapshot:
            fs_snapshot.update()
            self.result["filesystem_snapshot_details"] = \
                self.create_fs_snapshot_details_dict(fs_snapshot)
        else:
            self.result["filesystem_snapshot_details"] = {}

        self.result["changed"] = changed
        self.module.exit_json(**self.result)


def to_update_expiry_time(fs_snapshot, expiry_time=None):
    """ Check whether to update expiry_time or not"""
    if not expiry_time:
        return False
    if fs_snapshot.expiration_time is None:
        return True
    if convert_timestamp_to_sec(expiry_time, fs_snapshot.expiration_time)\
            != 0:
        return True
    return False


def convert_timestamp_to_sec(expiry_time, snap_time):
    """Converts the time difference to seconds"""
    snap_time_str = snap_time.strftime('%m/%d/%Y %H:%M')
    snap_timestamp = datetime.strptime(snap_time_str, '%m/%d/%Y %H:%M')
    expiry_timestamp = datetime.strptime(expiry_time, "%m/%d/%Y %H:%M")
    return int((expiry_timestamp - snap_timestamp).total_seconds())


def get_unity_snapshot_parameters():
    """This method provide parameter required for the ansible filesystem
    snapshot module on Unity"""
    return dict(
        snapshot_name=dict(required=False, type='str'),
        snapshot_id=dict(required=False, type='str'),
        filesystem_name=dict(required=False, type='str'),
        filesystem_id=dict(required=False, type='str'),
        nas_server_name=dict(required=False, type='str'),
        nas_server_id=dict(required=False, type='str'),
        auto_delete=dict(required=False, type='bool'),
        expiry_time=dict(required=False, type='str'),
        description=dict(required=False, type='str'),
        fs_access_type=dict(required=False, type='str',
                            choices=['Checkpoint', 'Protocol']),
        state=dict(required=True, type='str', choices=['present', 'absent'])
    )


def main():
    """ Create Unity Filesystem Snapshot object and perform actions on it
        based on user input from playbook"""
    obj = UnityFilesystemSnapshot()
    obj.perform_module_operation()


if __name__ == '__main__':
    main()

#!/usr/bin/python
# Copyright: (c) 2020, DellEMC
""" Ansible module for managing Filesystem Snapshots 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_filesystem_snapshot
short_description: Manage filesystem snapshot on the Unity storage system
description:
- Managing Filesystem Snapshot on the Unity storage system includes
  create filesystem snapshot, get filesystem snapshot, modify filesystem
  snapshot and delete filesystem snapshot.
version_added: "1.1.0"
extends_documentation_fragment:
  - dellemc.unity.dellemc_unity.unity
author:
- <NAME> (@kharer5) <<EMAIL>>
options:
  snapshot_name:
    description:
    - The name of the filesystem snapshot.
    - Mandatory parameter for creating a filesystem snapshot.
    - For all other operations either snapshot_name or snapshot_id
      is required.
    type: str
  snapshot_id:
    description:
    - During creation snapshot_id is auto generated.
    - For all other operations either snapshot_id or snapshot_name
      is required.
    type: str
  filesystem_name:
    description:
    - The name of the Filesystem for which snapshot is created.
    - For creation of filesystem snapshot either filesystem_name or
      filesystem_id is required.
    - Not required for other operations.
    type: str
  filesystem_id:
    description:
    - The ID of the Filesystem for which snapshot is created.
    - For creation of filesystem snapshot either filesystem_id or
      filesystem_name is required.
    - Not required for other operations.
    type: str
  nas_server_name:
    description:
    - The name of the NAS server in which the Filesystem is created.
    - For creation of filesystem snapshot either nas_server_name or
      nas_server_id is required.
    - Not required for other operations.
    type: str
  nas_server_id:
    description:
    - The ID of the NAS server in which the Filesystem is created.
    - For creation of filesystem snapshot either filesystem_id or
      filesystem_name is required.
    - Not required for other operations.
    type: str
  auto_delete:
    description:
    - This option specifies whether or not the filesystem snapshot will be
      automatically deleted.
    - If set to true, the filesystem snapshot will expire based on the pool
      auto deletion policy.
    - If set to false, the filesystem snapshot will not be auto deleted
      based on the pool auto deletion policy.
    - auto_delete can not be set to True, if expiry_time is specified.
    - If during creation neither auto_delete nor expiry_time is mentioned
      then the filesystem snapshot will be created keeping auto_delete as
      True.
    - Once the expiry_time is set, then the filesystem snapshot cannot be
      assigned to the auto delete policy.
    type: bool
  expiry_time:
    description:
    - This option is for specifying the date and time after which the
      filesystem snapshot will expire.
    - The time is to be mentioned in UTC timezone.
    - The format is "MM/DD/YYYY HH:MM". Year must be in 4 digits.
    type: str
  description:
    description:
    - The additional information about the filesystem snapshot can be
      provided using this option.
    - The description can be removed by passing an empty string.
    type: str
  fs_access_type:
    description:
    - Access type of the filesystem snapshot.
    - Required only during creation of filesystem snapshot.
    - If not given, snapshot's access type will be 'Checkpoint'.
    type: str
    choices: ['Checkpoint' , 'Protocol']
  state:
    description:
    - The state option is used to mention the existence of the filesystem
      snapshot.
    type: str
    required: True
    choices: ['absent', 'present']
notes:
  - Filesystem snapshot cannot be deleted, if it has nfs or smb share.
'''

EXAMPLES = r'''
  - name: Create Filesystem Snapshot
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      filesystem_name: "ansible_test_FS"
      nas_server_name: "lglad069"
      description: "Created using playbook"
      auto_delete: True
      fs_access_type: "Protocol"
      state: "present"

  - name: Create Filesystem Snapshot with expiry time.
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap_1"
      filesystem_name: "ansible_test_FS_1"
      nas_server_name: "lglad069"
      description: "Created using playbook"
      expiry_time: "04/15/2021 2:30"
      fs_access_type: "Protocol"
      state: "present"

  - name: Get Filesystem Snapshot Details using Name
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      state: "present"

  - name: Get Filesystem Snapshot Details using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      state: "present"

  - name: Update Filesystem Snapshot attributes
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      description: "Description updated"
      auto_delete: False
      expiry_time: "04/15/2021 5:30"
      state: "present"

  - name: Update Filesystem Snapshot attributes using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      expiry_time: "04/18/2021 8:30"
      state: "present"

  - name: Delete Filesystem Snapshot using Name
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_name: "ansible_test_FS_snap"
      state: "absent"

  - name: Delete Filesystem Snapshot using ID
    dellemc_unity_filesystem_snapshot:
      unispherehost: "{{unispherehost}}"
      username: "{{username}}"
      password: "{{password}}"
      verifycert: "{{verifycert}}"
      snapshot_id: "10008000403"
      state: "absent"
'''

RETURN = r'''
changed:
    description: Whether or not the resource has changed
    returned: always
    type: bool
filesystem_snapshot_details:
    description: Details of the filesystem snapshot.
    returned: When filesystem snapshot exists
    type: complex
    contains:
        access_type:
            description: Access type of filesystem snapshot.
            type: str
        attached_wwn:
            description: Attached WWN details.
            type: str
        creation_time:
            description: Creation time of filesystem snapshot.
            type: str
        creator_schedule:
            description: Creator schedule of filesystem snapshot.
            type: str
        creator_type:
            description: Creator type for filesystem snapshot.
            type: str
        creator_user:
            description: Creator user for filesystem snapshot.
            type: str
        description:
            description: Description of the filesystem snapshot.
            type: str
        expiration_time:
            description: Date and time after which the filesystem snapshot
                         will expire.
            type: str
        is_auto_delete:
            description: Is the filesystem snapshot is auto deleted or not.
            type: bool
        id:
            description: Unique identifier of the filesystem snapshot
                         instance.
            type: str
        name:
            description: The name of the filesystem snapshot.
            type: str
        size:
            description: Size of the filesystem snapshot.
            type: int
        filesystem_name:
            description: Name of the filesystem for which the snapshot exists.
            type: str
        filesystem_id:
            description: Id of the filesystem for which the snapshot exists.
            type: str
        nas_server_name:
            description: Name of the NAS server on which filesystem exists.
            type: str
        nas_server_id:
            description: Id of the NAS server on which filesystem exists.
            type: str

'''

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

LOG = utils.get_logger('dellemc_unity_filesystem_snapshot')

HAS_UNITY_SDK = utils.get_unity_sdk()
UNITY_SDK_VERSION_CHECK = utils.storops_version_check()

application_type = "Ansible/1.2.0"


class UnityFilesystemSnapshot(object):
    """Class with Filesystem Snapshot 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_snapshot_parameters())

        mutually_exclusive = [['snapshot_name', 'snapshot_id'],
                              ['filesystem_name', 'filesystem_id'],
                              ['nas_server_name', 'nas_server_id']]

        required_one_of = [['snapshot_name', 'snapshot_id']]
        # initialize the ansible module
        self.module = AnsibleModule(argument_spec=self.module_params,
                                    supports_check_mode=False,
                                    mutually_exclusive=mutually_exclusive,
                                    required_one_of=required_one_of)

        # result is a dictionary that contains changed status and
        # filesystem snapshot details
        self.result = {"changed": False,
                       'filesystem_snapshot_details': None}

        if not HAS_UNITY_SDK:
            self.module.fail_json(msg="Ansible modules for Unity require the"
                                      " Unity python library to be"
                                      " installed. Please install the "
                                      "library before using these modules.")

        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, application_type)
        self.snap_obj = utils.snap.UnitySnap(self.unity_conn)
        LOG.info('Connection established with the Unity Array')

    def validate_expiry_time(self, expiry_time):
        """Validates the specified expiry_time"""
        try:
            datetime.strptime(expiry_time, '%m/%d/%Y %H:%M')
        except ValueError:
            error_msg = ("expiry_time: %s, not in MM/DD/YYYY HH:MM format." %
                         expiry_time)
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def to_update(self, fs_snapshot, description=None, auto_del=None,
                  expiry_time=None, fs_access_type=None):
        """Determines whether to update the snapshot or not"""
        snap_modify_dict = dict()

        if fs_access_type and fs_access_type != fs_snapshot.access_type:
            error_message = "Modification of access type is not allowed."
            LOG.error(error_message)
            self.module.fail_json(msg=error_message)
        if expiry_time:
            # If the snapshot has is_auto_delete True,
            # Check if auto_delete in the input is either None or True
            if fs_snapshot.is_auto_delete and (auto_del is None or auto_del):
                self.module.fail_json(msg="expiry_time can be assigned when"
                                          " auto delete is False.")
        if auto_del is not None:
            if fs_snapshot.expiration_time:
                error_msg = "expiry_time for filesystem snapshot is set." \
                            " Once it is set then snapshot cannot" \
                            " be assigned to auto_delete policy."
                self.module.fail_json(msg=error_msg)
            if auto_del != fs_snapshot.is_auto_delete:
                snap_modify_dict['is_auto_delete'] = auto_del

        if description is not None and description != fs_snapshot.description:
            snap_modify_dict['description'] = description

        if to_update_expiry_time(fs_snapshot, expiry_time):
            snap_modify_dict['expiry_time'] = expiry_time
        LOG.info("Snapshot modification details: %s", snap_modify_dict)
        return snap_modify_dict

    def update_filesystem_snapshot(self, fs_snapshot, snap_modify_dict):
        try:
            duration = None
            if 'expiry_time' in snap_modify_dict \
                    and snap_modify_dict['expiry_time']:
                duration = convert_timestamp_to_sec(
                    snap_modify_dict['expiry_time'],
                    self.unity_conn.system_time)
            if duration and duration <= 0:
                self.module.fail_json(msg="expiry_time should be after"
                                          " the current system time.")
            if 'is_auto_delete' in snap_modify_dict \
                    and snap_modify_dict['is_auto_delete'] is not None:
                auto_delete = snap_modify_dict['is_auto_delete']
            else:
                auto_delete = None
            if 'description' in snap_modify_dict \
                    and (snap_modify_dict['description']
                         or len(snap_modify_dict['description']) == 0):
                description = snap_modify_dict['description']
            else:
                description = None

            fs_snapshot.modify(retentionDuration=duration,
                               isAutoDelete=auto_delete,
                               description=description)
            fs_snapshot.update()
        except Exception as e:
            error_msg = "Failed to modify filesystem snapshot" \
                        " [name: %s , id: %s] with error %s."\
                        % (fs_snapshot.name, fs_snapshot.id, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def create_filesystem_snapshot(self, snap_name, storage_id,
                                   description=None, auto_del=None,
                                   expiry_time=None, fs_access_type=None):
        try:
            duration = None
            if expiry_time:
                duration = convert_timestamp_to_sec(
                    expiry_time, self.unity_conn.system_time)
                if duration <= 0:
                    self.module.fail_json(msg="expiry_time should be after"
                                              " the current system time.")

            fs_snapshot = self.snap_obj.create(
                cli=self.unity_conn._cli, storage_resource=storage_id,
                name=snap_name, description=description,
                is_auto_delete=auto_del, retention_duration=duration,
                fs_access_type=fs_access_type)
            return fs_snapshot
        except Exception as e:
            error_msg = "Failed to create filesystem snapshot" \
                        " %s with error %s" % (snap_name, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def is_snap_has_share(self, fs_snap):
        try:
            obj = self.unity_conn.get_nfs_share(snap=fs_snap) or \
                self.unity_conn.get_cifs_share(snap=fs_snap)
            if len(obj) > 0:
                LOG.info("Snapshot has %s nfs/smb share/s", len(obj))
                return True
        except Exception as e:
            msg = "Failed to get nfs/smb share from filesystem snapshot. " \
                  "error: %s" % str(e)
            LOG.error(msg)
            self.module.fail_json(msg=msg)
        return False

    def delete_fs_snapshot(self, fs_snapshot):
        try:
            # Checking whether nfs/smb share created from fs_snapshot
            if self.is_snap_has_share(fs_snapshot):
                msg = "Filesystem snapshot cannot be deleted because it has " \
                      "nfs/smb share"
                LOG.error(msg)
                self.module.fail_json(msg=msg)
            fs_snapshot.delete()
            return None

        except Exception as e:
            error_msg = "Failed to delete filesystem snapshot" \
                        " [name: %s, id: %s] with error %s." \
                        % (fs_snapshot.name, fs_snapshot.id, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def get_fs_snapshot_obj(self, name=None, id=None):
        fs_snapshot = id if id else name
        msg = "Failed to get details of filesystem snapshot %s with error %s."
        try:
            fs_snap_obj = self.unity_conn.get_snap(name=name, _id=id)
            if fs_snap_obj and fs_snap_obj.existed:
                LOG.info("Successfully got the filesystem snapshot object "
                         "%s.", fs_snap_obj)
            else:
                fs_snap_obj = None
            return fs_snap_obj

        except utils.HttpError as e:
            if e.http_status == 401:
                cred_err = ("Incorrect username or password , %s" % e.message)
                self.module.fail_json(msg=cred_err)
            else:
                err_msg = msg % (fs_snapshot, str(e))
                LOG.error(err_msg)
                self.module.fail_json(msg=err_msg)

        except utils.UnityResourceNotFoundError as e:
            err_msg = msg % (fs_snapshot, str(e))
            LOG.error(err_msg)
            return None

        except Exception as e:
            err_msg = msg % (fs_snapshot, str(e))
            LOG.error(err_msg)
            self.module.fail_json(msg=err_msg)

    def get_filesystem_obj(self, nas_server=None, name=None, id=None):
        filesystem = 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."\
                        % (filesystem, str(e))
            LOG.error(error_msg)
            self.module.fail_json(msg=error_msg)

    def get_nas_server_obj(self, name=None, id=None):
        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 create_fs_snapshot_details_dict(self, fs_snapshot):
        """ Add name and id of storage resource to filesystem snapshot
            details """

        snapshot_dict = fs_snapshot._get_properties()
        del snapshot_dict['storage_resource']

        snapshot_dict['filesystem_name'] = fs_snapshot.storage_resource.name
        snapshot_dict['filesystem_id'] = fs_snapshot.storage_resource.filesystem.id

        obj_fs = self.unity_conn.\
            get_filesystem(id=fs_snapshot.storage_resource.filesystem.id)
        if obj_fs and obj_fs.existed:
            snapshot_dict['nas_server_name'] = obj_fs.nas_server[0].name
            snapshot_dict['nas_server_id'] = obj_fs.nas_server[0].id

        return snapshot_dict

    def perform_module_operation(self):
        """
        Perform different actions on snapshot module based on parameters
        chosen in playbook
        """
        snapshot_name = self.module.params['snapshot_name']
        snapshot_id = self.module.params['snapshot_id']
        filesystem_name = self.module.params['filesystem_name']
        filesystem_id = self.module.params['filesystem_id']
        nas_server_name = self.module.params['nas_server_name']
        nas_server_id = self.module.params['nas_server_id']
        auto_delete = self.module.params['auto_delete']
        expiry_time = self.module.params['expiry_time']
        description = self.module.params['description']
        fs_access_type = self.module.params['fs_access_type']
        state = self.module.params['state']
        nas_server_resource = None
        filesystem_resource = None
        changed = False

        LOG.info("Getting Filesystem Snapshot details.")
        fs_snapshot = self.get_fs_snapshot_obj(name=snapshot_name,
                                               id=snapshot_id)

        msg = "Filesystem Snapshot details: %s." % str(fs_snapshot)
        LOG.info(msg)

        # Get NAS server Object
        if nas_server_name is not None:
            if nas_server_name == "" or nas_server_name.isspace():
                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 nas_server_id == "" or nas_server_id.isspace():
                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 filesystem_name == "" or filesystem_name.isspace():
                self.module.fail_json(msg="Invalid filesystem_name given,"
                                          " Please provide a valid name.")
            filesystem_resource = self\
                .get_filesystem_obj(nas_server=nas_server_resource,
                                    name=filesystem_name)
            fs_res_id = filesystem_resource.storage_resource.id
        elif filesystem_id is not None:
            if filesystem_id == "" or filesystem_id.isspace():
                self.module.fail_json(msg="Invalid filesystem_id given,"
                                          " Please provide a valid ID.")
            filesystem_resource = self\
                .get_filesystem_obj(id=filesystem_id)
            fs_res_id = filesystem_resource[0].storage_resource.id

        # Check for error, if user tries to create a filesystem snapshot
        # with the same name.
        if fs_snapshot and filesystem_resource and \
            (fs_snapshot.storage_resource.id
             != fs_res_id):
            self.module.fail_json(
                msg="Snapshot %s is of %s storage resource. Cannot create new"
                    " snapshot with same name for %s storage resource."
                    % (fs_snapshot.name, fs_snapshot.storage_resource.name,
                       filesystem_resource.storage_resource.name))

        # check for valid expiry_time
        if expiry_time is not None and \
                (expiry_time == "" or expiry_time.isspace()):
            self.module.fail_json(msg="Please provide valid expiry_time,"
                                      " empty expiry_time given.")
        if expiry_time:
            self.validate_expiry_time(expiry_time)

        # Check if in input auto_delete is True and expiry_time is not None
        if expiry_time and auto_delete:
            error_msg = "Cannot set expiry_time if auto_delete given as True."
            LOG.info(error_msg)
            self.module.fail_json(msg=error_msg)

        # check for fs_access_type
        if fs_access_type is not None:
            if (fs_access_type == "" or fs_access_type.isspace()):
                self.module.fail_json(msg="Please provide valid "
                                          "fs_access_type, empty "
                                          "fs_access_type given.")
            if fs_access_type == "Checkpoint":
                fs_access_type = utils.FilesystemSnapAccessTypeEnum.CHECKPOINT
            elif fs_access_type == "Protocol":
                fs_access_type = utils.FilesystemSnapAccessTypeEnum.PROTOCOL

        # Check whether to modify the filesystem snapshot or not
        fs_snap_modify_dict = dict()
        if state == 'present' and fs_snapshot:
            fs_snap_modify_dict = self\
                .to_update(fs_snapshot, description=description,
                           auto_del=auto_delete, expiry_time=expiry_time,
                           fs_access_type=fs_access_type)

        # Create Filesystem Snapshot
        if not fs_snapshot and state == "present":
            LOG.info("Creating the filesystem snapshot.")

            if snapshot_id:
                self.module.fail_json(msg="Creation of Filesystem Snapshot is"
                                          " allowed using snapshot_name only,"
                                          " snapshot_id given.")
            if snapshot_name == "" or snapshot_name.isspace():
                self.module.fail_json(msg="snapshot_name is required for"
                                          " creation of the filesystem"
                                          " snapshot, empty snapshot_name"
                                          " given.")
            if not filesystem_resource:
                self.module.fail_json(msg="filesystem_name or filesystem_id"
                                          " required to create a snapshot.")

            fs_snapshot = self.create_filesystem_snapshot(
                snapshot_name,
                fs_res_id,
                description,
                auto_delete,
                expiry_time,
                fs_access_type)
            changed = True

        # Update the Snapshot
        if fs_snapshot and state == "present" and fs_snap_modify_dict:
            LOG.info("Updating the Filesystem Snapshot.")
            self.update_filesystem_snapshot(fs_snapshot, fs_snap_modify_dict)
            changed = True

        # Delete the Filesystem Snapshot
        if state == "absent" and fs_snapshot:
            fs_snapshot = self.delete_fs_snapshot(fs_snapshot)
            changed = True

        # Add filesystem snapshot details to the result.
        if fs_snapshot:
            fs_snapshot.update()
            self.result["filesystem_snapshot_details"] = \
                self.create_fs_snapshot_details_dict(fs_snapshot)
        else:
            self.result["filesystem_snapshot_details"] = {}

        self.result["changed"] = changed
        self.module.exit_json(**self.result)


def to_update_expiry_time(fs_snapshot, expiry_time=None):
    """ Check whether to update expiry_time or not"""
    if not expiry_time:
        return False
    if fs_snapshot.expiration_time is None:
        return True
    if convert_timestamp_to_sec(expiry_time, fs_snapshot.expiration_time)\
            != 0:
        return True
    return False


def convert_timestamp_to_sec(expiry_time, snap_time):
    """Converts the time difference to seconds"""
    snap_time_str = snap_time.strftime('%m/%d/%Y %H:%M')
    snap_timestamp = datetime.strptime(snap_time_str, '%m/%d/%Y %H:%M')
    expiry_timestamp = datetime.strptime(expiry_time, "%m/%d/%Y %H:%M")
    return int((expiry_timestamp - snap_timestamp).total_seconds())


def get_unity_snapshot_parameters():
    """This method provide parameter required for the ansible filesystem
    snapshot module on Unity"""
    return dict(
        snapshot_name=dict(required=False, type='str'),
        snapshot_id=dict(required=False, type='str'),
        filesystem_name=dict(required=False, type='str'),
        filesystem_id=dict(required=False, type='str'),
        nas_server_name=dict(required=False, type='str'),
        nas_server_id=dict(required=False, type='str'),
        auto_delete=dict(required=False, type='bool'),
        expiry_time=dict(required=False, type='str'),
        description=dict(required=False, type='str'),
        fs_access_type=dict(required=False, type='str',
                            choices=['Checkpoint', 'Protocol']),
        state=dict(required=True, type='str', choices=['present', 'absent'])
    )


def main():
    """ Create Unity Filesystem Snapshot object and perform actions on it
        based on user input from playbook"""
    obj = UnityFilesystemSnapshot()
    obj.perform_module_operation()


if __name__ == '__main__':
    main()

#!/usr/bin/python # Copyright: (c) 2020, DellEMC Ansible module for managing Filesystem Snapshots on Unity --- module: dellemc_unity_filesystem_snapshot short_description: Manage filesystem snapshot on the Unity storage system description: - Managing Filesystem Snapshot on the Unity storage system includes   create filesystem snapshot, get filesystem snapshot, modify filesystem   snapshot and delete filesystem snapshot. version_added: "1.1.0" extends_documentation_fragment:   - dellemc.unity.dellemc_unity.unity author: - <NAME> (@kharer5) <<EMAIL>> options:   snapshot_name:     description:     - The name of the filesystem snapshot.     - Mandatory parameter for creating a filesystem snapshot.     - For all other operations either snapshot_name or snapshot_id       is required.     type: str   snapshot_id:     description:     - During creation snapshot_id is auto generated.     - For all other operations either snapshot_id or snapshot_name       is required.     type: str   filesystem_name:     description:     - The name of the Filesystem for which snapshot is created.     - For creation of filesystem snapshot either filesystem_name or       filesystem_id is required.     - Not required for other operations.     type: str   filesystem_id:     description:     - The ID of the Filesystem for which snapshot is created.     - For creation of filesystem snapshot either filesystem_id or       filesystem_name is required.     - Not required for other operations.     type: str   nas_server_name:     description:     - The name of the NAS server in which the Filesystem is created.     - For creation of filesystem snapshot either nas_server_name or       nas_server_id is required.     - Not required for other operations.     type: str   nas_server_id:     description:     - The ID of the NAS server in which the Filesystem is created.     - For creation of filesystem snapshot either filesystem_id or       filesystem_name is required.     - Not required for other operations.     type: str   auto_delete:     description:     - This option specifies whether or not the filesystem snapshot will be       automatically deleted.     - If set to true, the filesystem snapshot will expire based on the pool       auto deletion policy.     - If set to false, the filesystem snapshot will not be auto deleted       based on the pool auto deletion policy.     - auto_delete can not be set to True, if expiry_time is specified.     - If during creation neither auto_delete nor expiry_time is mentioned       then the filesystem snapshot will be created keeping auto_delete as       True.     - Once the expiry_time is set, then the filesystem snapshot cannot be       assigned to the auto delete policy.     type: bool   expiry_time:     description:     - This option is for specifying the date and time after which the       filesystem snapshot will expire.     - The time is to be mentioned in UTC timezone.     - The format is "MM/DD/YYYY HH:MM". Year must be in 4 digits.     type: str   description:     description:     - The additional information about the filesystem snapshot can be       provided using this option.     - The description can be removed by passing an empty string.     type: str   fs_access_type:     description:     - Access type of the filesystem snapshot.     - Required only during creation of filesystem snapshot.     - If not given, snapshot's access type will be 'Checkpoint'.     type: str     choices: ['Checkpoint' , 'Protocol']   state:     description:     - The state option is used to mention the existence of the filesystem       snapshot.     type: str     required: True     choices: ['absent', 'present'] notes:   - Filesystem snapshot cannot be deleted, if it has nfs or smb share. - name: Create Filesystem Snapshot     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_name: "ansible_test_FS_snap"       filesystem_name: "ansible_test_FS"       nas_server_name: "lglad069"       description: "Created using playbook"       auto_delete: True       fs_access_type: "Protocol"       state: "present"    - name: Create Filesystem Snapshot with expiry time.     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_name: "ansible_test_FS_snap_1"       filesystem_name: "ansible_test_FS_1"       nas_server_name: "lglad069"       description: "Created using playbook"       expiry_time: "04/15/2021 2:30"       fs_access_type: "Protocol"       state: "present"    - name: Get Filesystem Snapshot Details using Name     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_name: "ansible_test_FS_snap"       state: "present"    - name: Get Filesystem Snapshot Details using ID     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_id: "10008000403"       state: "present"    - name: Update Filesystem Snapshot attributes     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_name: "ansible_test_FS_snap"       description: "Description updated"       auto_delete: False       expiry_time: "04/15/2021 5:30"       state: "present"    - name: Update Filesystem Snapshot attributes using ID     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_id: "10008000403"       expiry_time: "04/18/2021 8:30"       state: "present"    - name: Delete Filesystem Snapshot using Name     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_name: "ansible_test_FS_snap"       state: "absent"    - name: Delete Filesystem Snapshot using ID     dellemc_unity_filesystem_snapshot:       unispherehost: "{{unispherehost}}"       username: "{{username}}"       password: "{{password}}"       verifycert: "{{verifycert}}"       snapshot_id: "10008000403"       state: "absent" changed:     description: Whether or not the resource has changed     returned: always     type: bool filesystem_snapshot_details:     description: Details of the filesystem snapshot.     returned: When filesystem snapshot exists     type: complex     contains:         access_type:             description: Access type of filesystem snapshot.             type: str         attached_wwn:             description: Attached WWN details.             type: str         creation_time:             description: Creation time of filesystem snapshot.             type: str         creator_schedule:             description: Creator schedule of filesystem snapshot.             type: str         creator_type:             description: Creator type for filesystem snapshot.             type: str         creator_user:             description: Creator user for filesystem snapshot.             type: str         description:             description: Description of the filesystem snapshot.             type: str         expiration_time:             description: Date and time after which the filesystem snapshot                          will expire.             type: str         is_auto_delete:             description: Is the filesystem snapshot is auto deleted or not.             type: bool         id:             description: Unique identifier of the filesystem snapshot                          instance.             type: str         name:             description: The name of the filesystem snapshot.             type: str         size:             description: Size of the filesystem snapshot.             type: int         filesystem_name:             description: Name of the filesystem for which the snapshot exists.             type: str         filesystem_id:             description: Id of the filesystem for which the snapshot exists.             type: str         nas_server_name:             description: Name of the NAS server on which filesystem exists.             type: str         nas_server_id:             description: Id of the NAS server on which filesystem exists.             type: str Class with Filesystem Snapshot operations Define all parameters required by this module # initialize the ansible module # result is a dictionary that contains changed status and # filesystem snapshot details Validates the specified expiry_time Determines whether to update the snapshot or not # If the snapshot has is_auto_delete True, # Check if auto_delete in the input is either None or True # Checking whether nfs/smb share created from fs_snapshot Add name and id of storage resource to filesystem snapshot             details Perform different actions on snapshot module based on parameters         chosen in playbook # Get NAS server Object #  Get Filesystem Object # Check for error, if user tries to create a filesystem snapshot # with the same name. # check for valid expiry_time # Check if in input auto_delete is True and expiry_time is not None # check for fs_access_type # Check whether to modify the filesystem snapshot or not # Create Filesystem Snapshot # Update the Snapshot # Delete the Filesystem Snapshot # Add filesystem snapshot details to the result. Check whether to update expiry_time or not Converts the time difference to seconds This method provide parameter required for the ansible filesystem     snapshot module on Unity Create Unity Filesystem Snapshot object and perform actions on it         based on user input from playbook