class ElementOSVolumeClone(object): """ Contains methods to parse arguments, derive details of Element Software objects and send requests to Element OS via the Solidfire SDK """ def __init__(self): """ Parse arguments, setup state variables, check parameters and ensure SDK is installed """ self._size_unit_map = netapp_utils.SF_BYTE_MAP self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() self.argument_spec.update( dict( name=dict(required=True), src_volume_id=dict(required=True), src_snapshot_id=dict(), account_id=dict(required=True), attributes=dict(type='dict', default=None), size=dict(type='int'), size_unit=dict(default='gb', choices=[ 'bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb' ], type='str'), access=dict(type='str', default=None, choices=[ 'readOnly', 'readWrite', 'locked', 'replicationTarget' ]), )) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) parameters = self.module.params # set up state variables self.name = parameters['name'] self.src_volume_id = parameters['src_volume_id'] self.src_snapshot_id = parameters['src_snapshot_id'] self.account_id = parameters['account_id'] self.attributes = parameters['attributes'] self.size_unit = parameters['size_unit'] if parameters['size'] is not None: self.size = parameters['size'] * \ self._size_unit_map[self.size_unit] else: self.size = None self.access = parameters['access'] if HAS_SF_SDK is False: self.module.fail_json( msg="Unable to import the SolidFire Python SDK") else: self.sfe = netapp_utils.create_sf_connection(module=self.module) self.elementsw_helper = NaElementSWModule(self.sfe) # add telemetry attributes if self.attributes is not None: self.attributes.update( self.elementsw_helper.set_element_attributes( source='na_elementsw_volume_clone')) else: self.attributes = self.elementsw_helper.set_element_attributes( source='na_elementsw_volume_clone') def get_account_id(self): """ Return account id if found """ try: # Update and return self.account_id self.account_id = self.elementsw_helper.account_exists( self.account_id) return self.account_id except Exception as err: self.module.fail_json(msg="Error: account_id %s does not exist" % self.account_id, exception=to_native(err)) def get_snapshot_id(self): """ Return snapshot details if found """ src_snapshot = self.elementsw_helper.get_snapshot( self.src_snapshot_id, self.src_volume_id) # Update and return self.src_snapshot_id if src_snapshot is not None: self.src_snapshot_id = src_snapshot.snapshot_id # Return src_snapshot return self.src_snapshot_id return None def get_src_volume_id(self): """ Return volume id if found """ src_vol_id = self.elementsw_helper.volume_exists( self.src_volume_id, self.account_id) if src_vol_id is not None: # Update and return self.volume_id self.src_volume_id = src_vol_id # Return src_volume_id return self.src_volume_id return None def clone_volume(self): """Clone Volume from source""" try: self.sfe.clone_volume(volume_id=self.src_volume_id, name=self.name, new_account_id=self.account_id, new_size=self.size, access=self.access, snapshot_id=self.src_snapshot_id, attributes=self.attributes) except Exception as err: self.module.fail_json(msg="Error creating clone %s of size %s" % (self.name, self.size), exception=to_native(err)) def apply(self): """Perform pre-checks, call functions and exit""" changed = False result_message = "" if self.get_account_id() is None: self.module.fail_json(msg="Account id not found: %s" % (self.account_id)) # there is only one state. other operations # are part of the volume module # ensure that a volume with the clone name # isn't already present if self.elementsw_helper.volume_exists(self.name, self.account_id) is None: # check for the source volume if self.get_src_volume_id() is not None: # check for a valid snapshot if self.src_snapshot_id and not self.get_snapshot_id(): self.module.fail_json(msg="Snapshot id not found: %s" % (self.src_snapshot_id)) # change required changed = True else: self.module.fail_json(msg="Volume id not found %s" % (self.src_volume_id)) if changed: if self.module.check_mode: result_message = "Check mode, skipping changes" else: self.clone_volume() result_message = "Volume cloned" self.module.exit_json(changed=changed, msg=result_message)
class ElementOSSnapshotRestore(object): """ Element OS Restore from snapshot """ def __init__(self): self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() self.argument_spec.update( dict(account_id=dict(required=True, type='str'), src_volume_id=dict(required=True, type='str'), dest_volume_name=dict(required=True, type='str'), src_snapshot_id=dict(required=True, type='str'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) input_params = self.module.params self.account_id = input_params['account_id'] self.src_volume_id = input_params['src_volume_id'] self.dest_volume_name = input_params['dest_volume_name'] self.src_snapshot_id = input_params['src_snapshot_id'] if HAS_SF_SDK is False: self.module.fail_json( msg="Unable to import the SolidFire Python SDK") else: self.sfe = netapp_utils.create_sf_connection(module=self.module) self.elementsw_helper = NaElementSWModule(self.sfe) # add telemetry attributes self.attributes = self.elementsw_helper.set_element_attributes( source='na_elementsw_snapshot_restore') def get_account_id(self): """ Get account id if found """ try: # Update and return self.account_id self.account_id = self.elementsw_helper.account_exists( self.account_id) return self.account_id except Exception as err: self.module.fail_json(msg="Error: account_id %s does not exist" % self.account_id, exception=to_native(err)) def get_snapshot_id(self): """ Return snapshot details if found """ src_snapshot = self.elementsw_helper.get_snapshot( self.src_snapshot_id, self.src_volume_id) # Update and return self.src_snapshot_id if src_snapshot: self.src_snapshot_id = src_snapshot.snapshot_id # Return self.src_snapshot_id return self.src_snapshot_id return None def restore_snapshot(self): """ Restore Snapshot to Volume """ try: self.sfe.clone_volume(volume_id=self.src_volume_id, name=self.dest_volume_name, snapshot_id=self.src_snapshot_id, attributes=self.attributes) except Exception as exception_object: self.module.fail_json(msg='Error restore snapshot %s' % (to_native(exception_object)), exception=traceback.format_exc()) def apply(self): """ Check, process and initiate restore snapshot to volume operation """ changed = False result_message = None snapshot_detail = None self.get_account_id() src_vol_id = self.elementsw_helper.volume_exists( self.src_volume_id, self.account_id) if src_vol_id is not None: # Update self.src_volume_id self.src_volume_id = src_vol_id if self.get_snapshot_id() is not None: # Addressing idempotency by comparing volume does not exist with same volume name if self.elementsw_helper.volume_exists( self.dest_volume_name, self.account_id) is None: self.restore_snapshot() changed = True else: result_message = "No changes requested, Skipping changes" else: self.module.fail_json(msg="Snapshot id not found %s" % self.src_snapshot_id) else: self.module.fail_json(msg="Volume id not found %s" % self.src_volume_id) self.module.exit_json(changed=changed, msg=result_message)
class ElementOSSnapshot(object): """ Element OS Snapshot Manager """ def __init__(self): self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, choices=['present', 'absent'], default='present'), account_id=dict(required=True, type='str'), name=dict(required=False, type='str'), src_volume_id=dict(required=True, type='str'), retention=dict(required=False, type='str'), src_snapshot_id=dict(required=False, type='str'), enable_remote_replication=dict(required=False, type='bool'), expiration_time=dict(required=False, type='str'), snap_mirror_label=dict(required=False, type='str'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) input_params = self.module.params self.state = input_params['state'] self.name = input_params['name'] self.account_id = input_params['account_id'] self.src_volume_id = input_params['src_volume_id'] self.src_snapshot_id = input_params['src_snapshot_id'] self.retention = input_params['retention'] self.properties_provided = False self.expiration_time = input_params['expiration_time'] if input_params['expiration_time'] is not None: self.properties_provided = True self.enable_remote_replication = input_params[ 'enable_remote_replication'] if input_params['enable_remote_replication'] is not None: self.properties_provided = True self.snap_mirror_label = input_params['snap_mirror_label'] if input_params['snap_mirror_label'] is not None: self.properties_provided = True if self.state == 'absent' and self.src_snapshot_id is None: self.module.fail_json( msg="Please provide required parameter : snapshot_id") if HAS_SF_SDK is False: self.module.fail_json( msg="Unable to import the SolidFire Python SDK") else: self.sfe = netapp_utils.create_sf_connection(module=self.module) self.elementsw_helper = NaElementSWModule(self.sfe) # add telemetry attributes self.attributes = self.elementsw_helper.set_element_attributes( source='na_elementsw_snapshot') def get_account_id(self): """ Return account id if found """ try: # Update and return self.account_id self.account_id = self.elementsw_helper.account_exists( self.account_id) return self.account_id except Exception as err: self.module.fail_json(msg="Error: account_id %s does not exist" % self.account_id, exception=to_native(err)) def get_src_volume_id(self): """ Return volume id if found """ src_vol_id = self.elementsw_helper.volume_exists( self.src_volume_id, self.account_id) if src_vol_id is not None: # Update and return self.volume_id self.src_volume_id = src_vol_id # Return src_volume_id return self.src_volume_id return None def get_snapshot(self, name=None): """ Return snapshot details if found """ src_snapshot = None if name is not None: src_snapshot = self.elementsw_helper.get_snapshot( name, self.src_volume_id) elif self.src_snapshot_id is not None: src_snapshot = self.elementsw_helper.get_snapshot( self.src_snapshot_id, self.src_volume_id) if src_snapshot is not None: # Update self.src_snapshot_id self.src_snapshot_id = src_snapshot.snapshot_id # Return src_snapshot return src_snapshot def create_snapshot(self): """ Create Snapshot """ try: self.sfe.create_snapshot( volume_id=self.src_volume_id, snapshot_id=self.src_snapshot_id, name=self.name, enable_remote_replication=self.enable_remote_replication, retention=self.retention, snap_mirror_label=self.snap_mirror_label, attributes=self.attributes) except Exception as exception_object: self.module.fail_json(msg='Error creating snapshot %s' % (to_native(exception_object)), exception=traceback.format_exc()) def modify_snapshot(self): """ Modify Snapshot Properties """ try: self.sfe.modify_snapshot( snapshot_id=self.src_snapshot_id, expiration_time=self.expiration_time, enable_remote_replication=self.enable_remote_replication, snap_mirror_label=self.snap_mirror_label) except Exception as exception_object: self.module.fail_json(msg='Error modify snapshot %s' % (to_native(exception_object)), exception=traceback.format_exc()) def delete_snapshot(self): """ Delete Snapshot """ try: self.sfe.delete_snapshot(snapshot_id=self.src_snapshot_id) except Exception as exception_object: self.module.fail_json(msg='Error delete snapshot %s' % (to_native(exception_object)), exception=traceback.format_exc()) def apply(self): """ Check, process and initiate snapshot operation """ changed = False snapshot_delete = False snapshot_create = False snapshot_modify = False result_message = None self.get_account_id() # Dont proceed if source volume is not found if self.get_src_volume_id() is None: self.module.fail_json(msg="Volume id not found %s" % self.src_volume_id) # Get snapshot details using source volume snapshot_detail = self.get_snapshot() if snapshot_detail: if self.properties_provided: if self.expiration_time != snapshot_detail.expiration_time: changed = True else: # To preserve value in case parameter expiration_time is not defined/provided. self.expiration_time = snapshot_detail.expiration_time if self.enable_remote_replication != snapshot_detail.enable_remote_replication: changed = True else: # To preserve value in case parameter enable_remote_Replication is not defined/provided. self.enable_remote_replication = snapshot_detail.enable_remote_replication if self.snap_mirror_label != snapshot_detail.snap_mirror_label: changed = True else: # To preserve value in case parameter snap_mirror_label is not defined/provided. self.snap_mirror_label = snapshot_detail.snap_mirror_label if self.account_id is None or self.src_volume_id is None or self.module.check_mode: changed = False result_message = "Check mode, skipping changes" elif self.state == 'absent' and snapshot_detail is not None: self.delete_snapshot() changed = True elif self.state == 'present' and snapshot_detail is not None: if changed: self.modify_snapshot() # Modify Snapshot properties elif not self.properties_provided: if self.name is not None: snapshot = self.get_snapshot(self.name) # If snapshot with name already exists return without performing any action if snapshot is None: self.create_snapshot( ) # Create Snapshot using parent src_snapshot_id changed = True else: self.create_snapshot() changed = True elif self.state == 'present': if self.name is not None: snapshot = self.get_snapshot(self.name) # If snapshot with name already exists return without performing any action if snapshot is None: self.create_snapshot( ) # Create Snapshot using parent src_snapshot_id changed = True else: self.create_snapshot() changed = True else: changed = False result_message = "No changes requested, skipping changes" self.module.exit_json(changed=changed, msg=result_message)