Server IP : 85.214.239.14 / Your IP : 3.12.162.86 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Mon Sep 30 15:36:27 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/dellemc/unity/plugins/modules/ |
Upload File : |
#!/usr/bin/python # Copyright: (c) 2020, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """ Ansible module for managing Filesystem Snapshots on Unity""" from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' --- module: 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.unity author: - Rajshree Khare (@kharer5) <ansible.team@dell.com> options: snapshot_name: description: - The name of the filesystem snapshot. - Mandatory parameter for creating a filesystem snapshot. - For all other operations either I(snapshot_name) or I(snapshot_id) is required. type: str snapshot_id: description: - During creation snapshot_id is auto generated. - For all other operations either I(snapshot_id) or I(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 I(filesystem_name) or I(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 I(filesystem_id) or I(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 I(nas_server_name) or I(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 I(filesystem_id) or I(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 C(true), the filesystem snapshot will expire based on the pool auto deletion policy. - If set to C(false), the filesystem snapshot will not be auto deleted based on the pool auto deletion policy. - Option I(auto_delete) can not be set to C(true), if I(expiry_time) is specified. - If during creation neither I(auto_delete) nor I(expiry_time) is mentioned then the filesystem snapshot will be created keeping I(auto_delete) as C(true). - Once the I(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 C(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. - The I(check_mode) is not supported. ''' EXAMPLES = r''' - name: Create Filesystem Snapshot dellemc.unity.filesystem_snapshot: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" 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}}" validate_certs: "{{validate_certs}}" 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}}" validate_certs: "{{validate_certs}}" 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}}" validate_certs: "{{validate_certs}}" snapshot_id: "10008000403" state: "present" - name: Update Filesystem Snapshot attributes dellemc.unity.filesystem_snapshot: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" 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}}" validate_certs: "{{validate_certs}}" 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}}" validate_certs: "{{validate_certs}}" snapshot_name: "ansible_test_FS_snap" state: "absent" - name: Delete Filesystem Snapshot using ID dellemc.unity.filesystem_snapshot: unispherehost: "{{unispherehost}}" username: "{{username}}" password: "{{password}}" validate_certs: "{{validate_certs}}" snapshot_id: "10008000403" state: "absent" ''' RETURN = r''' changed: description: Whether or not the resource has changed. returned: always type: bool sample: true filesystem_snapshot_details: description: Details of the filesystem snapshot. returned: When filesystem snapshot exists type: dict 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 sample: { "access_type": "FilesystemSnapAccessTypeEnum.CHECKPOINT", "attached_wwn": null, "creation_time": "2022-10-21 04:42:53.951000+00:00", "creator_schedule": null, "creator_type": "SnapCreatorTypeEnum.USER_CUSTOM", "creator_user": { "id": "user_admin" }, "description": "Created using playbook", "existed": true, "expiration_time": null, "filesystem_id": "fs_137", "filesystem_name": "test", "hash": 8739894572587, "host_access": null, "id": "171798721695", "io_limit_policy": null, "is_auto_delete": true, "is_modifiable": false, "is_modified": false, "is_read_only": true, "is_system_snap": false, "last_writable_time": null, "lun": null, "name": "test_FS_snap_1", "nas_server_id": "nas_1", "nas_server_name": "lglad072", "parent_snap": null, "size": 107374182400, "snap_group": null, "state": "SnapStateEnum.READY" } ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \ import utils from datetime import datetime LOG = utils.get_logger('filesystem_snapshot') application_type = "Ansible/1.6.0" class FilesystemSnapshot(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_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) utils.ensure_required_libs(self.module) # result is a dictionary that contains changed status and # filesystem snapshot details self.result = {"changed": False, 'filesystem_snapshot_details': {}} 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 the snapshot has is_auto_delete True, # Check if auto_delete in the input is either None or True if expiry_time and 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) or (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_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 = FilesystemSnapshot() obj.perform_module_operation() if __name__ == '__main__': main()