def test_get_client_for_backend(self): self.mock_object(utils, 'get_backend_configuration', return_value=self.config) utils.get_client_for_backend(self.backend) self.mock_cmode_client.assert_called_once_with( hostname='fake_hostname', password='******', username='******', transport_type='https', port=8866, trace=mock.ANY, vserver=None, api_trace_pattern="fake_regex")
def test_get_client_for_backend(self): self.mock_object(utils, 'get_backend_configuration', mock.Mock(return_value=self.config)) utils.get_client_for_backend(self.backend) self.mock_cmode_client.assert_called_once_with( hostname='fake_hostname', password='******', username='******', transport_type='https', port=8866, trace=mock.ANY, vserver=None)
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 test_get_client_for_backend_with_vserver(self): self.mock_object(utils, 'get_backend_configuration', mock.Mock(return_value=self.config)) CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend, enforce_type=True) utils.get_client_for_backend(self.backend) self.mock_cmode_client.assert_called_once_with( hostname='fake_hostname', password='******', username='******', transport_type='https', port=8866, trace=mock.ANY, vserver='fake_vserver')
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 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 create_vserver_peer(self, src_vserver, src_backend_name, dest_vserver, peer_applications): """Create a vserver peer relationship""" src_client = config_utils.get_client_for_backend( src_backend_name, vserver_name=src_vserver) vserver_peers = src_client.get_vserver_peers(src_vserver, dest_vserver) if not vserver_peers: src_client.create_vserver_peer( src_vserver, dest_vserver, vserver_peer_application=peer_applications) LOG.debug( "Vserver peer relationship created between %(src)s " "and %(dest)s. Peering application set to %(app)s.", { 'src': src_vserver, 'dest': dest_vserver, 'app': peer_applications }) return None for vserver_peer in vserver_peers: if all(app in vserver_peer['applications'] for app in peer_applications): LOG.debug("Found vserver peer relationship between %s and %s.", src_vserver, dest_vserver) return None msg = _("Vserver peer relationship found between %(src)s and %(dest)s " "but peering application %(app)s isn't defined.") raise na_utils.NetAppDriverException(msg % { 'src': src_vserver, 'dest': dest_vserver, 'app': peer_applications })
def test_get_client_for_backend_with_vserver(self): self.mock_object(utils, 'get_backend_configuration', return_value=self.config) CONF.set_override('netapp_vserver', 'fake_vserver', group=self.backend) utils.get_client_for_backend(self.backend) self.mock_cmode_client.assert_called_once_with( hostname='fake_hostname', password='******', username='******', transport_type='https', port=8866, trace=mock.ANY, vserver='fake_vserver')
def _update_zapi_client(self, backend_name): """Set cDOT API client for the specified config backend stanza name.""" self.zapi_client = dot_utils.get_client_for_backend(backend_name) self.vserver = self.zapi_client.vserver self.ssc_library._update_for_failover(self.zapi_client, self._get_flexvol_to_pool_map()) ssc = self.ssc_library.get_ssc() self.perf_library._update_for_failover(self.zapi_client, ssc)
def _update_zapi_client(self, backend_name): """Set cDOT API client for the specified config backend stanza name.""" self.zapi_client = cmode_utils.get_client_for_backend(backend_name) self.vserver = self.zapi_client.vserver self.ssc_library._update_for_failover(self.zapi_client, self._get_flexvol_to_pool_map()) ssc = self.ssc_library.get_ssc() self.perf_library._update_for_failover(self.zapi_client, ssc)
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 do_setup(self, context): super(NetAppBlockStorageCmodeLibrary, self).do_setup(context) na_utils.check_flags(self.REQUIRED_CMODE_FLAGS, self.configuration) # cDOT API client self.zapi_client = cmode_utils.get_client_for_backend(self.failed_over_backend_name or self.backend_name) self.vserver = self.zapi_client.vserver # Performance monitoring library self.perf_library = perf_cmode.PerformanceCmodeLibrary(self.zapi_client) # Storage service catalog self.ssc_library = capabilities.CapabilitiesLibrary( self.driver_protocol, self.vserver, self.zapi_client, self.configuration )
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 do_setup(self, context): """Do the customized set up on client for cluster mode.""" super(NetAppCmodeNfsDriver, self).do_setup(context) na_utils.check_flags(self.REQUIRED_CMODE_FLAGS, self.configuration) # cDOT API client self.zapi_client = cmode_utils.get_client_for_backend( self.failed_over_backend_name or self.backend_name) self.vserver = self.zapi_client.vserver # Performance monitoring library self.perf_library = perf_cmode.PerformanceCmodeLibrary( self.zapi_client) # Storage service catalog self.ssc_library = capabilities.CapabilitiesLibrary( 'nfs', self.vserver, self.zapi_client, self.configuration)
def do_setup(self, context): super(NetAppBlockStorageCmodeLibrary, self).do_setup(context) na_utils.check_flags(self.REQUIRED_CMODE_FLAGS, self.configuration) # cDOT API client self.zapi_client = dot_utils.get_client_for_backend( self.failed_over_backend_name or self.backend_name) self.vserver = self.zapi_client.vserver # Performance monitoring library self.perf_library = perf_cmode.PerformanceCmodeLibrary( self.zapi_client) # Storage service catalog self.ssc_library = capabilities.CapabilitiesLibrary( self.driver_protocol, self.vserver, self.zapi_client, self.configuration)
def _cancel_lun_copy(self, job_uuid, volume, dest_pool, dest_backend_name): """Cancel an on-going lun copy operation.""" try: # NOTE(sfernand): Another approach would be first checking if # the copy operation isn't in `destroying` or `destroyed` states # before issuing cancel. self.zapi_client.cancel_lun_copy(job_uuid) except na_utils.NetAppDriverException: dest_client = dot_utils.get_client_for_backend(dest_backend_name) lun_path = '/vol/%s/%s' % (dest_pool, volume.name) try: dest_client.destroy_lun(lun_path) except Exception: LOG.warning( 'Error cleaning up LUN %s in destination volume. ' 'Verify if destination volume still exists in ' 'pool %s and delete it manually to avoid unused ' 'resources.', lun_path, dest_pool)
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 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_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_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 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."))