def get_snapmirrors(self, src_backend_name, dest_backend_name, src_flexvol_name=None, dest_flexvol_name=None): """Get info regarding SnapMirror relationship/s for given params.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) src_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = src_backend_config.netapp_vserver snapmirrors = dest_client.get_snapmirrors( src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, desired_attributes=[ 'relationship-status', 'mirror-state', 'source-vserver', 'source-volume', 'destination-vserver', 'destination-volume', 'last-transfer-end-timestamp', 'lag-time', ]) return snapmirrors
def break_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Break SnapMirror relationship. 1. Quiesce any ongoing SnapMirror transfers 2. Wait until SnapMirror finishes transfers and enters quiesced state 3. Break SnapMirror 4. Mount the destination volume so it is given a junction path """ dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # 1. Attempt to quiesce, then abort self.quiesce_then_abort(src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name) # 2. Break SnapMirror dest_client.break_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) # 3. Mount the destination volume and create a junction path dest_client.mount_flexvol(dest_flexvol_name)
def create_destination_flexvol(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Create a SnapMirror mirror target FlexVol for a given source.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver src_client = config_utils.get_client_for_backend( src_backend_name, vserver_name=src_vserver) provisioning_options = ( src_client.get_provisioning_options_from_flexvol( src_flexvol_name) ) # If the source is encrypted then the destination needs to be # encrypted too. Using is_flexvol_encrypted because it includes # a simple check to ensure that the NVE feature is supported. if src_client.is_flexvol_encrypted(src_flexvol_name, src_vserver): provisioning_options['encrypt'] = 'true' # Remove size and volume_type size = provisioning_options.pop('size', None) if not size: msg = _("Unable to read the size of the source FlexVol (%s) " "to create a SnapMirror destination.") raise na_utils.NetAppDriverException(msg % src_flexvol_name) provisioning_options.pop('volume_type', None) source_aggregate = provisioning_options.pop('aggregate') aggregate_map = self._get_replication_aggregate_map( src_backend_name, dest_backend_name) if not aggregate_map.get(source_aggregate): msg = _("Unable to find configuration matching the source " "aggregate (%s) and the destination aggregate. Option " "netapp_replication_aggregate_map may be incorrect.") raise na_utils.NetAppDriverException( message=msg % source_aggregate) destination_aggregate = aggregate_map[source_aggregate] # NOTE(gouthamr): The volume is intentionally created as a Data # Protection volume; junction-path will be added on breaking # the mirror. provisioning_options['volume_type'] = 'dp' dest_client.create_flexvol(dest_flexvol_name, destination_aggregate, size, **provisioning_options)
def resync_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Re-sync (repair / re-establish) SnapMirror relationship.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver dest_client.resync_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name)
def resume_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Resume SnapMirror relationship from a quiesced state.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver dest_client.resume_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name)
def update_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Schedule a SnapMirror update on the backend.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # Update SnapMirror dest_client.update_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name)
def test_get_backend_configuration(self): self.mock_object(utils, 'CONF') CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend) utils.CONF.list_all_sections.return_value = [self.backend] config = utils.get_backend_configuration(self.backend) self.assertEqual('fake_vserver', config.netapp_vserver)
def test_get_backend_configuration(self): self.mock_object(utils, 'CONF') CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend, enforce_type=True) utils.CONF.list_all_sections.return_value = [self.backend] config = utils.get_backend_configuration(self.backend) self.assertEqual('fake_vserver', config.netapp_vserver)
def quiesce_then_abort(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Quiesce a SnapMirror and wait with retries before aborting.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # 1. Attempt to quiesce, then abort dest_client.quiesce_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) retries = (source_backend_config.netapp_snapmirror_quiesce_timeout / QUIESCE_RETRY_INTERVAL) @utils.retry(exception.NetAppDriverException, interval=QUIESCE_RETRY_INTERVAL, retries=retries, backoff_rate=1) def wait_for_quiesced(): snapmirror = dest_client.get_snapmirrors( src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, desired_attributes=['relationship-status', 'mirror-state'])[0] if snapmirror.get('relationship-status') != 'quiesced': msg = _("SnapMirror relationship is not quiesced.") raise exception.NetAppDriverException(reason=msg) try: wait_for_quiesced() except exception.NetAppDriverException: dest_client.abort_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, clear_checkpoint=False)
def test_get_backend_configuration_different_backend_name(self): self.mock_object(utils, 'CONF') CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend) CONF.set_override('volume_backend_name', 'fake_backend_name', group=self.backend) utils.CONF.list_all_sections.return_value = [self.backend] config = utils.get_backend_configuration(self.backend) self.assertEqual('fake_vserver', config.netapp_vserver) self.assertEqual('fake_backend_name', config.volume_backend_name)
def test_get_backend_configuration_different_backend_name(self): self.mock_object(utils, 'CONF') CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend, enforce_type=True) CONF.set_override('volume_backend_name', 'fake_backend_name', group=self.backend, enforce_type=True) utils.CONF.list_all_sections.return_value = [self.backend] config = utils.get_backend_configuration(self.backend) self.assertEqual('fake_vserver', config.netapp_vserver) self.assertEqual('fake_backend_name', config.volume_backend_name)
def _complete_failover(self, source_backend_name, replication_targets, flexvols, volumes, failover_target=None): """Failover a backend to a secondary replication target.""" volume_updates = [] active_backend_name = failover_target or self._choose_failover_target( source_backend_name, flexvols, replication_targets) if active_backend_name is None: msg = _("No suitable host was found to failover.") raise exception.NetAppDriverException(msg) source_backend_config = config_utils.get_backend_configuration( source_backend_name) # 1. Start an update to try to get a last minute transfer before we # quiesce and break self.update_snapmirrors(source_backend_config, source_backend_name, flexvols) # 2. Break SnapMirrors failed_to_break = self.break_snapmirrors(source_backend_config, source_backend_name, flexvols, active_backend_name) # 3. Update cinder volumes within this host for volume in volumes: replication_status = fields.ReplicationStatus.FAILED_OVER volume_pool = volume_utils.extract_host(volume['host'], level='pool') if volume_pool in failed_to_break: replication_status = 'error' volume_update = { 'volume_id': volume['id'], 'updates': { 'replication_status': replication_status, }, } volume_updates.append(volume_update) return active_backend_name, volume_updates
def _get_replication_aggregate_map(self, src_backend_name, target_backend_name): """Get the aggregate mapping config between src and destination.""" aggregate_map = {} config = config_utils.get_backend_configuration(src_backend_name) all_replication_aggregate_maps = config.safe_get( 'netapp_replication_aggregate_map') if all_replication_aggregate_maps: for replication_aggregate_map in all_replication_aggregate_maps: if (replication_aggregate_map.get('backend_id') == target_backend_name): replication_aggregate_map.pop('backend_id') aggregate_map = replication_aggregate_map break return aggregate_map
def migrate_volume_ontap_assisted(self, volume, host, src_backend_name, src_vserver): """Migrate Cinder volume using ONTAP capabilities""" _, src_pool = volume.host.split('#') dest_backend, dest_pool = host["host"].split('#') _, dest_backend_name = dest_backend.split('@') # Check if migration occurs in the same backend. If so, a migration # between Cinder pools in the same vserver will be performed. if src_backend_name == dest_backend_name: # We should skip the operation in case source and destination pools # are the same. if src_pool == dest_pool: LOG.info('Skipping volume migration as source and destination ' 'are the same.') return True, {} updates = self._migrate_volume_to_pool(volume, src_pool, dest_pool, src_vserver, dest_backend_name) else: if not self.using_cluster_credentials: LOG.info('Storage assisted volume migration across backends ' 'requires ONTAP cluster-wide credentials. Falling ' 'back to host assisted migration.') return False, {} dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name) src_client = config_utils.get_client_for_backend(src_backend_name) # In case origin and destination backends are not pointing to the # same cluster, a host copy strategy using is required. Otherwise, # an intra-cluster operation can be done to complete the migration. src_cluster_name = src_client.get_cluster_name() dest_cluster_name = dest_client.get_cluster_name() if src_cluster_name != dest_cluster_name: LOG.info('Driver only supports storage assisted migration ' 'between pools in a same cluster. Falling back to ' 'host assisted migration.') return False, {} # if origin and destination vservers are the same, simply move # the cinder volume from one pool to the other. # Otherwise, an intra-cluster Vserver peer relationship # followed by a volume copy operation are required. # Both operations will copy data between ONTAP volumes # and won't finish in constant time as volume clones. if src_vserver == dest_vserver: # We should skip the operation in case source and # destination pools are the same if src_pool == dest_pool: LOG.info('Skipping volume migration as source and ' 'destination are the same.') return True, {} updates = self._migrate_volume_to_pool(volume, src_pool, dest_pool, src_vserver, dest_backend_name) else: updates = self._migrate_volume_to_vserver( volume, src_pool, src_vserver, dest_pool, dest_backend_config.netapp_vserver, dest_backend_name) LOG.info('Successfully migrated volume %s to host %s.', volume.id, host['host']) return True, updates
def create_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Set up a SnapMirror relationship b/w two FlexVols (cinder pools) 1. Create SnapMirror relationship 2. Initialize data transfer asynchronously If a SnapMirror relationship already exists and is broken off or quiesced, resume and re-sync the mirror. """ dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # 1. Create destination 'dp' FlexVol if it doesn't exist if not dest_client.flexvol_exists(dest_flexvol_name): self.create_destination_flexvol(src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name) # 2. Check if SnapMirror relationship exists existing_mirrors = dest_client.get_snapmirrors( src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) msg_payload = { 'src_vserver': src_vserver, 'src_volume': src_flexvol_name, 'dest_vserver': dest_vserver, 'dest_volume': dest_flexvol_name, } # 3. Create and initialize SnapMirror if it doesn't already exist if not existing_mirrors: # TODO(gouthamr): Change the schedule from hourly to a config value msg = ("Creating a SnapMirror relationship between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) dest_client.create_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, schedule='hourly') msg = ("Initializing SnapMirror transfers between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) # Initialize async transfer of the initial data dest_client.initialize_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) # 4. Try to repair SnapMirror if existing else: snapmirror = existing_mirrors[0] if snapmirror.get('mirror-state') != 'snapmirrored': try: msg = ("SnapMirror between %(src_vserver)s:%(src_volume)s " "and %(dest_vserver)s:%(dest_volume)s is in " "'%(state)s' state. Attempting to repair it.") msg_payload['state'] = snapmirror.get('mirror-state') LOG.debug(msg, msg_payload) dest_client.resume_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) dest_client.resync_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError: LOG.exception(_LE("Could not re-sync SnapMirror."))
def delete_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name, release=True): """Ensure all information about a SnapMirror relationship is removed. 1. Abort SnapMirror 2. Delete the SnapMirror 3. Release SnapMirror to cleanup SnapMirror metadata and snapshots """ dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # 1. Abort any ongoing transfers try: dest_client.abort_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, clear_checkpoint=False) except netapp_api.NaApiError: # Snapmirror is already deleted pass # 2. Delete SnapMirror Relationship and cleanup destination snapshots try: dest_client.delete_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError as e: with excutils.save_and_reraise_exception() as exc_context: if (e.code == netapp_api.EOBJECTNOTFOUND or e.code == netapp_api.ESOURCE_IS_DIFFERENT or ENTRY_DOES_NOT_EXIST in e.message): LOG.info(_LI('No SnapMirror relationship to delete.')) exc_context.reraise = False if release: # If the source is unreachable, do not perform the release try: src_client = config_utils.get_client_for_backend( src_backend_name, vserver_name=src_vserver) except Exception: src_client = None # 3. Cleanup SnapMirror relationship on source try: if src_client: src_client.release_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError as e: with excutils.save_and_reraise_exception() as exc_context: if (e.code == netapp_api.EOBJECTNOTFOUND or e.code == netapp_api.ESOURCE_IS_DIFFERENT or ENTRY_DOES_NOT_EXIST in e.message): # Handle the case where the SnapMirror is already # cleaned up exc_context.reraise = False
def create_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Set up a SnapMirror relationship b/w two FlexVols (cinder pools) 1. Create SnapMirror relationship 2. Initialize data transfer asynchronously If a SnapMirror relationship already exists and is broken off or quiesced, resume and re-sync the mirror. """ dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) src_client = config_utils.get_client_for_backend( src_backend_name, vserver_name=src_vserver) provisioning_options = ( src_client.get_provisioning_options_from_flexvol(src_flexvol_name)) pool_is_flexgroup = provisioning_options.get('is_flexgroup', False) # 1. Create destination 'dp' FlexVol if it doesn't exist if not dest_client.flexvol_exists(dest_flexvol_name): self.create_destination_flexvol( src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name, pool_is_flexgroup=pool_is_flexgroup) # 2. Check if SnapMirror relationship exists existing_mirrors = dest_client.get_snapmirrors(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) msg_payload = { 'src_vserver': src_vserver, 'src_volume': src_flexvol_name, 'dest_vserver': dest_vserver, 'dest_volume': dest_flexvol_name, } # 3. Create and initialize SnapMirror if it doesn't already exist if not existing_mirrors: # TODO(gouthamr): Change the schedule from hourly to a config value msg = ("Creating a SnapMirror relationship between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) try: dest_client.create_snapmirror( src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, schedule='hourly', relationship_type=('extended_data_protection' if pool_is_flexgroup else 'data_protection')) msg = ("Initializing SnapMirror transfers between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) # Initialize async transfer of the initial data dest_client.initialize_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError as e: with excutils.save_and_reraise_exception() as raise_ctxt: if (e.code == netapp_api.EAPIERROR and all(substr in e.message for substr in GEOMETRY_HAS_BEEN_CHANGED)): msg = _("Error creating SnapMirror. Geometry has " "changed on destination volume.") LOG.error(msg) self.delete_snapmirror(src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name) raise_ctxt.reraise = False raise na_utils.GeometryHasChangedOnDestination(msg) # 4. Try to repair SnapMirror if existing else: snapmirror = existing_mirrors[0] if snapmirror.get('mirror-state') != 'snapmirrored': try: msg = ("SnapMirror between %(src_vserver)s:%(src_volume)s " "and %(dest_vserver)s:%(dest_volume)s is in " "'%(state)s' state. Attempting to repair it.") msg_payload['state'] = snapmirror.get('mirror-state') LOG.debug(msg, msg_payload) dest_client.resume_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) dest_client.resync_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError: LOG.exception("Could not re-sync SnapMirror.")
def create_destination_flexvol(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name, pool_is_flexgroup=False): """Create a SnapMirror mirror target FlexVol for a given source.""" dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver src_client = config_utils.get_client_for_backend( src_backend_name, vserver_name=src_vserver) provisioning_options = ( src_client.get_provisioning_options_from_flexvol(src_flexvol_name)) provisioning_options.pop('is_flexgroup') # If the source is encrypted then the destination needs to be # encrypted too. Using is_flexvol_encrypted because it includes # a simple check to ensure that the NVE feature is supported. if src_client.is_flexvol_encrypted(src_flexvol_name, src_vserver): provisioning_options['encrypt'] = 'true' # Remove size and volume_type size = provisioning_options.pop('size', None) if not size: msg = _("Unable to read the size of the source FlexVol (%s) " "to create a SnapMirror destination.") raise na_utils.NetAppDriverException(msg % src_flexvol_name) provisioning_options.pop('volume_type', None) source_aggregate = provisioning_options.pop('aggregate') aggregate_map = self._get_replication_aggregate_map( src_backend_name, dest_backend_name) destination_aggregate = [] for src_aggr in source_aggregate: dst_aggr = aggregate_map.get(src_aggr, None) if dst_aggr: destination_aggregate.append(dst_aggr) else: msg = _("Unable to find configuration matching the source " "aggregate and the destination aggregate. Option " "netapp_replication_aggregate_map may be incorrect.") raise na_utils.NetAppDriverException(message=msg) # NOTE(gouthamr): The volume is intentionally created as a Data # Protection volume; junction-path will be added on breaking # the mirror. provisioning_options['volume_type'] = 'dp' if pool_is_flexgroup: compression_enabled = provisioning_options.pop( 'compression_enabled', False) # cDOT compression requires that deduplication be enabled. dedupe_enabled = provisioning_options.pop( 'dedupe_enabled', False) or compression_enabled dest_client.create_volume_async(dest_flexvol_name, destination_aggregate, size, **provisioning_options) timeout = self._get_replication_volume_online_timeout() def _wait_volume_is_online(): volume_state = dest_client.get_volume_state( flexvol_name=dest_flexvol_name) if volume_state and volume_state == 'online': raise loopingcall.LoopingCallDone() try: wait_call = loopingcall.FixedIntervalWithTimeoutLoopingCall( _wait_volume_is_online) wait_call.start(interval=5, timeout=timeout).wait() if dedupe_enabled: dest_client.enable_volume_dedupe_async(dest_flexvol_name) if compression_enabled: dest_client.enable_volume_compression_async( dest_flexvol_name) except loopingcall.LoopingCallTimeOut: msg = _("Timeout waiting destination FlexGroup to to come " "online.") raise na_utils.NetAppDriverException(msg) else: dest_client.create_flexvol(dest_flexvol_name, destination_aggregate[0], size, **provisioning_options)
def create_snapmirror(self, src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name): """Set up a SnapMirror relationship b/w two FlexVols (cinder pools) 1. Create SnapMirror relationship 2. Initialize data transfer asynchronously If a SnapMirror relationship already exists and is broken off or quiesced, resume and re-sync the mirror. """ dest_backend_config = config_utils.get_backend_configuration( dest_backend_name) dest_vserver = dest_backend_config.netapp_vserver dest_client = config_utils.get_client_for_backend( dest_backend_name, vserver_name=dest_vserver) source_backend_config = config_utils.get_backend_configuration( src_backend_name) src_vserver = source_backend_config.netapp_vserver # 1. Create destination 'dp' FlexVol if it doesn't exist if not dest_client.flexvol_exists(dest_flexvol_name): self.create_destination_flexvol(src_backend_name, dest_backend_name, src_flexvol_name, dest_flexvol_name) # 2. Check if SnapMirror relationship exists existing_mirrors = dest_client.get_snapmirrors(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) msg_payload = { 'src_vserver': src_vserver, 'src_volume': src_flexvol_name, 'dest_vserver': dest_vserver, 'dest_volume': dest_flexvol_name, } # 3. Create and initialize SnapMirror if it doesn't already exist if not existing_mirrors: # TODO(gouthamr): Change the schedule from hourly to a config value msg = ("Creating a SnapMirror relationship between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) dest_client.create_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name, schedule='hourly') msg = ("Initializing SnapMirror transfers between " "%(src_vserver)s:%(src_volume)s and %(dest_vserver)s:" "%(dest_volume)s.") LOG.debug(msg, msg_payload) # Initialize async transfer of the initial data dest_client.initialize_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) # 4. Try to repair SnapMirror if existing else: snapmirror = existing_mirrors[0] if snapmirror.get('mirror-state') != 'snapmirrored': try: msg = ("SnapMirror between %(src_vserver)s:%(src_volume)s " "and %(dest_vserver)s:%(dest_volume)s is in " "'%(state)s' state. Attempting to repair it.") msg_payload['state'] = snapmirror.get('mirror-state') LOG.debug(msg, msg_payload) dest_client.resume_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) dest_client.resync_snapmirror(src_vserver, src_flexvol_name, dest_vserver, dest_flexvol_name) except netapp_api.NaApiError: LOG.exception(_LE("Could not re-sync SnapMirror."))