Server IP : 85.214.239.14 / Your IP : 3.129.70.54 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/netapp/ontap/plugins/modules/ |
Upload File : |
#!/usr/bin/python # (c) 2018-2022, NetApp, Inc # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' na_ontap_snapshot ''' from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = ''' module: na_ontap_snapshot short_description: NetApp ONTAP manage Snapshots extends_documentation_fragment: - netapp.ontap.netapp.na_ontap version_added: 2.6.0 author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com> description: - Create/Modify/Delete ONTAP snapshots options: state: description: - If you want to create/modify a snapshot, or delete it. choices: ['present', 'absent'] type: str default: present snapshot: description: - Name of the snapshot to be managed. - The maximum string length is 256 characters. required: true type: str from_name: description: - Name of the existing snapshot to be renamed to. version_added: 2.8.0 type: str volume: description: - Name of the volume on which the snapshot is to be created. required: true type: str async_bool: description: - If true, the snapshot is to be created asynchronously. type: bool comment: description: - A human readable comment attached with the snapshot. - The size of the comment can be at most 255 characters. type: str snapmirror_label: description: - A human readable SnapMirror Label attached with the snapshot. - Size of the label can be at most 31 characters. - Supported with REST on Ontap 9.7 or higher. type: str ignore_owners: description: - if this field is true, snapshot will be deleted even if some other processes are accessing it. type: bool snapshot_instance_uuid: description: - The 128 bit unique snapshot identifier expressed in the form of UUID. type: str vserver: description: - The Vserver name required: true type: str expiry_time: description: - Snapshot expire time, only available with REST. - format should be in the timezone configured with cluster. type: str version_added: 21.8.0 ''' EXAMPLES = """ - name: create SnapShot tags: - create netapp.ontap.na_ontap_snapshot: state: present snapshot: "{{ snapshot name }}" volume: "{{ vol name }}" comment: "i am a comment" expiry_time: "2022-02-04T14:00:00-05:00" vserver: "{{ vserver name }}" username: "{{ netapp username }}" password: "{{ netapp password }}" hostname: "{{ netapp hostname }}" - name: delete SnapShot tags: - delete netapp.ontap.na_ontap_snapshot: state: absent snapshot: "{{ snapshot name }}" volume: "{{ vol name }}" vserver: "{{ vserver name }}" username: "{{ netapp username }}" password: "{{ netapp password }}" hostname: "{{ netapp hostname }}" - name: modify SnapShot tags: - modify netapp.ontap.na_ontap_snapshot: state: present snapshot: "{{ snapshot name }}" comment: "New comments are great" volume: "{{ vol name }}" vserver: "{{ vserver name }}" username: "{{ netapp username }}" password: "{{ netapp password }}" hostname: "{{ netapp hostname }}" """ RETURN = """ """ import traceback import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native from ansible_collections.netapp.ontap.plugins.module_utils import rest_generic from ansible_collections.netapp.ontap.plugins.module_utils import rest_volume class NetAppOntapSnapshot: """ Creates, modifies, and deletes a Snapshot """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), from_name=dict(required=False, type='str'), snapshot=dict(required=True, type="str"), volume=dict(required=True, type="str"), async_bool=dict(required=False, type="bool"), comment=dict(required=False, type="str"), snapmirror_label=dict(required=False, type="str"), ignore_owners=dict(required=False, type="bool"), snapshot_instance_uuid=dict(required=False, type="str"), vserver=dict(required=True, type="str"), expiry_time=dict(required=False, type="str") )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.rest_api = netapp_utils.OntapRestAPI(self.module) unsupported_rest_properties = ['async_bool', 'ignore_owners', 'snapshot_instance_uuid'] self.use_rest = self.rest_api.is_rest_supported_properties(self.parameters, unsupported_rest_properties, [['snapmirror_label', (9, 7)]]) if not self.use_rest: if self.parameters.get('expiry_time'): self.module.fail_json(msg="expiry_time is currently only supported with REST on Ontap 9.6 or higher") if not netapp_utils.has_netapp_lib(): self.module.fail_json(msg=netapp_utils.netapp_lib_is_required()) self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver']) def get_snapshot(self, snapshot_name=None, volume_id=None): """ Checks to see if a snapshot exists or not :return: Return True if a snapshot exists, False if it doesn't """ if self.use_rest: api = ('storage/volumes/%s/snapshots' % volume_id) params = { 'svm.name': self.parameters['vserver'], 'fields': 'uuid,comment,expiry_time,volume,name', } if self.parameters.get('snapmirror_label'): params['fields'] += ',snapmirror_label' params['name'] = snapshot_name or self.parameters['snapshot'] snapshot, error = rest_generic.get_one_record(self.rest_api, api, params) if error: self.module.fail_json(msg='Error fetching snapshot %s: %s' % (params['name'], to_native(error)), exception=traceback.format_exc()) if snapshot: return { 'uuid': snapshot['uuid'], 'snapshot': snapshot['name'], 'snapmirror_label': snapshot.get('snapmirror_label'), 'expiry_time': snapshot.get('expiry_time'), 'comment': snapshot.get('comment') } return None else: if snapshot_name is None: snapshot_name = self.parameters['snapshot'] snapshot_obj = netapp_utils.zapi.NaElement("snapshot-get-iter") desired_attr = netapp_utils.zapi.NaElement("desired-attributes") snapshot_info = netapp_utils.zapi.NaElement('snapshot-info') comment = netapp_utils.zapi.NaElement('comment') snapmirror_label = netapp_utils.zapi.NaElement('snapmirror-label') # add more desired attributes that are allowed to be modified snapshot_info.add_child_elem(comment) snapshot_info.add_child_elem(snapmirror_label) desired_attr.add_child_elem(snapshot_info) snapshot_obj.add_child_elem(desired_attr) # compose query query = netapp_utils.zapi.NaElement("query") snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info") snapshot_info_obj.add_new_child("name", snapshot_name) snapshot_info_obj.add_new_child("volume", self.parameters['volume']) snapshot_info_obj.add_new_child("vserver", self.parameters['vserver']) query.add_child_elem(snapshot_info_obj) snapshot_obj.add_child_elem(query) try: result = self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching snapshot %s: %s' % (snapshot_name, to_native(error)), exception=traceback.format_exc()) return_value = None if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) == 1: attributes_list = result.get_child_by_name('attributes-list') snap_info = attributes_list.get_child_by_name('snapshot-info') return_value = { 'comment': snap_info.get_child_content('comment'), 'snapmirror_label': None } if snap_info.get_child_by_name('snapmirror-label'): return_value['snapmirror_label'] = snap_info.get_child_content('snapmirror-label') return return_value def create_snapshot(self, volume_id=None): """ Creates a new snapshot """ if self.use_rest: api = ('storage/volumes/%s/snapshots' % volume_id) body = { 'name': self.parameters['snapshot'], 'svm': { 'name': self.parameters['vserver'] } } if self.parameters.get('comment'): body['comment'] = self.parameters['comment'] if self.parameters.get('snapmirror_label'): body['snapmirror_label'] = self.parameters['snapmirror_label'] if self.parameters.get('expiry_time'): body['expiry_time'] = self.parameters['expiry_time'] response, error = rest_generic.post_async(self.rest_api, api, body) if error: self.module.fail_json(msg="Error when creating snapshot: %s" % error) else: snapshot_obj = netapp_utils.zapi.NaElement("snapshot-create") # set up required variables to create a snapshot snapshot_obj.add_new_child("snapshot", self.parameters['snapshot']) snapshot_obj.add_new_child("volume", self.parameters['volume']) # Set up optional variables to create a snapshot if self.parameters.get('async_bool'): snapshot_obj.add_new_child("async", str(self.parameters['async_bool'])) if self.parameters.get('comment'): snapshot_obj.add_new_child("comment", self.parameters['comment']) if self.parameters.get('snapmirror_label'): snapshot_obj.add_new_child( "snapmirror-label", self.parameters['snapmirror_label']) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating snapshot %s: %s' % (self.parameters['snapshot'], to_native(error)), exception=traceback.format_exc()) def delete_snapshot(self, volume_id=None, uuid=None): """ Deletes an existing snapshot """ if self.use_rest: api = ('storage/volumes/%s/snapshots/%s' % (volume_id, uuid)) response, error = rest_generic.delete_async(self.rest_api, api, None) if error: self.module.fail_json(msg="Error when deleting snapshot: %s" % error) else: snapshot_obj = netapp_utils.zapi.NaElement("snapshot-delete") # Set up required variables to delete a snapshot snapshot_obj.add_new_child("snapshot", self.parameters['snapshot']) snapshot_obj.add_new_child("volume", self.parameters['volume']) # set up optional variables to delete a snapshot if self.parameters.get('ignore_owners'): snapshot_obj.add_new_child("ignore-owners", str(self.parameters['ignore_owners'])) if self.parameters.get('snapshot_instance_uuid'): snapshot_obj.add_new_child("snapshot-instance-uuid", self.parameters['snapshot_instance_uuid']) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting snapshot %s: %s' % (self.parameters['snapshot'], to_native(error)), exception=traceback.format_exc()) def modify_snapshot(self, volume_id=None, uuid=None, rename=False): """ Modify an existing snapshot :return: """ if self.use_rest: api = 'storage/volumes/%s/snapshots/%s' % (volume_id, uuid) body = {'name': self.parameters['snapshot']} if rename else {} if self.parameters.get('comment'): body['comment'] = self.parameters['comment'] if self.parameters.get('snapmirror_label'): body['snapmirror_label'] = self.parameters['snapmirror_label'] if self.parameters.get('expiry_time'): body['expiry_time'] = self.parameters['expiry_time'] response, error = rest_generic.patch_async(self.rest_api, api, None, body) if error: self.module.fail_json(msg="Error when modifying snapshot: %s" % error) else: snapshot_obj = netapp_utils.zapi.NaElement("snapshot-modify-iter") # Create query object, this is the existing object query = netapp_utils.zapi.NaElement("query") snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info") snapshot_info_obj.add_new_child("name", self.parameters['snapshot']) snapshot_info_obj.add_new_child("vserver", self.parameters['vserver']) query.add_child_elem(snapshot_info_obj) snapshot_obj.add_child_elem(query) # this is what we want to modify in the snapshot object attributes = netapp_utils.zapi.NaElement("attributes") snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info") snapshot_info_obj.add_new_child("name", self.parameters['snapshot']) if self.parameters.get('comment'): snapshot_info_obj.add_new_child("comment", self.parameters['comment']) if self.parameters.get('snapmirror_label'): snapshot_info_obj.add_new_child("snapmirror-label", self.parameters['snapmirror_label']) attributes.add_child_elem(snapshot_info_obj) snapshot_obj.add_child_elem(attributes) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying snapshot %s: %s' % (self.parameters['snapshot'], to_native(error)), exception=traceback.format_exc()) def rename_snapshot(self): """ Rename the sanpshot """ snapshot_obj = netapp_utils.zapi.NaElement("snapshot-rename") # set up required variables to rename a snapshot snapshot_obj.add_new_child("current-name", self.parameters['from_name']) snapshot_obj.add_new_child("new-name", self.parameters['snapshot']) snapshot_obj.add_new_child("volume", self.parameters['volume']) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error renaming snapshot %s to %s: %s' % (self.parameters['from_name'], self.parameters['snapshot'], to_native(error)), exception=traceback.format_exc()) def get_volume_uuid(self): """ Get a volume's UUID :return: uuid of the volume """ response, error = rest_volume.get_volume(self.rest_api, self.parameters['vserver'], self.parameters['volume']) if error is not None: self.module.fail_json(msg="Error getting volume info: %s" % error) return response['uuid'] if response else None def apply(self): """ Check to see which play we should run """ volume_id = None uuid = None current = None if not self.use_rest: current = self.get_snapshot() else: volume_id = self.get_volume_uuid() if volume_id is None: self.module.fail_json(msg="Error: volume %s not found for vserver %s." % (self.parameters['volume'], self.parameters['vserver'])) current = self.get_snapshot(volume_id=volume_id) rename = False modify = {} cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action == 'create' and self.parameters.get('from_name'): current = self.get_snapshot(self.parameters['from_name'], volume_id=volume_id) if current is None: self.module.fail_json(msg='Error renaming snapshot: %s - no snapshot with from_name: %s.' % (self.parameters['snapshot'], self.parameters['from_name'])) rename = True cd_action = None if cd_action is None: modify = self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed and not self.module.check_mode: uuid = current['uuid'] if current and self.use_rest else None if rename and not self.use_rest: # with REST, rename forces a change in modify for 'name' self.rename_snapshot() if cd_action == 'create': self.create_snapshot(volume_id=volume_id) elif cd_action == 'delete': self.delete_snapshot(volume_id=volume_id, uuid=uuid) elif modify: self.modify_snapshot(volume_id=volume_id, uuid=uuid, rename=rename) result = netapp_utils.generate_result(self.na_helper.changed, cd_action, modify) self.module.exit_json(**result) def main(): """ Creates, modifies, and deletes a Snapshot """ obj = NetAppOntapSnapshot() obj.apply() if __name__ == '__main__': main()