1717
1818package org .apache .cloudstack .storage .snapshot ;
1919
20- import com .cloud .storage .DataStoreRole ;
21- import com .cloud .storage .Snapshot ;
22- import com .cloud .utils .exception .CloudRuntimeException ;
23- import com .cloud .utils .fsm .NoTransitionException ;
20+ import java .util .List ;
21+ import java .util .concurrent .ExecutionException ;
22+
23+ import javax .inject .Inject ;
24+
25+ import org .apache .log4j .Logger ;
26+ import org .springframework .stereotype .Component ;
27+
2428import org .apache .cloudstack .engine .subsystem .api .storage .CopyCommandResult ;
2529import org .apache .cloudstack .engine .subsystem .api .storage .CreateCmdResult ;
2630import org .apache .cloudstack .engine .subsystem .api .storage .DataMotionService ;
2731import org .apache .cloudstack .engine .subsystem .api .storage .DataStore ;
2832import org .apache .cloudstack .engine .subsystem .api .storage .DataStoreManager ;
2933import org .apache .cloudstack .engine .subsystem .api .storage .ObjectInDataStoreStateMachine ;
3034import org .apache .cloudstack .engine .subsystem .api .storage .ObjectInDataStoreStateMachine .Event ;
35+ import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStore ;
3136import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStoreDriver ;
3237import org .apache .cloudstack .engine .subsystem .api .storage .SnapshotDataFactory ;
3338import org .apache .cloudstack .engine .subsystem .api .storage .SnapshotInfo ;
3439import org .apache .cloudstack .engine .subsystem .api .storage .SnapshotResult ;
3540import org .apache .cloudstack .engine .subsystem .api .storage .SnapshotService ;
41+ import org .apache .cloudstack .engine .subsystem .api .storage .StorageCacheManager ;
3642import org .apache .cloudstack .engine .subsystem .api .storage .VolumeInfo ;
3743import org .apache .cloudstack .framework .async .AsyncCallFuture ;
3844import org .apache .cloudstack .framework .async .AsyncCallbackDispatcher ;
3945import org .apache .cloudstack .framework .async .AsyncCompletionCallback ;
4046import org .apache .cloudstack .framework .async .AsyncRpcContext ;
4147import org .apache .cloudstack .storage .command .CommandResult ;
4248import org .apache .cloudstack .storage .command .CopyCmdAnswer ;
43- import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStore ;
4449import org .apache .cloudstack .storage .datastore .db .SnapshotDataStoreDao ;
4550import org .apache .cloudstack .storage .datastore .db .SnapshotDataStoreVO ;
46- import org .apache .log4j .Logger ;
47- import org .springframework .stereotype .Component ;
4851
49- import javax .inject .Inject ;
50- import java .util .concurrent .ExecutionException ;
52+ import com .cloud .storage .DataStoreRole ;
53+ import com .cloud .storage .Snapshot ;
54+ import com .cloud .storage .SnapshotVO ;
55+ import com .cloud .storage .dao .SnapshotDao ;
56+ import com .cloud .storage .template .TemplateConstants ;
57+ import com .cloud .utils .exception .CloudRuntimeException ;
58+ import com .cloud .utils .fsm .NoTransitionException ;
5159
5260@ Component
5361public class SnapshotServiceImpl implements SnapshotService {
5462 private static final Logger s_logger = Logger .getLogger (SnapshotServiceImpl .class );
5563 @ Inject
64+ protected SnapshotDao _snapshotDao ;
65+ @ Inject
5666 protected SnapshotDataStoreDao _snapshotStoreDao ;
5767 @ Inject
58- SnapshotDataFactory snapshotfactory ;
68+ SnapshotDataFactory _snapshotFactory ;
5969 @ Inject
6070 DataStoreManager dataStoreMgr ;
6171 @ Inject
6272 DataMotionService motionSrv ;
73+ @ Inject
74+ StorageCacheManager _cacheMgr ;
6375
6476 static private class CreateSnapshotContext <T > extends AsyncRpcContext <T > {
6577 final SnapshotInfo snapshot ;
@@ -249,7 +261,7 @@ public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) {
249261 try {
250262 snapObj .processEvent (Snapshot .Event .BackupToSecondary );
251263
252- DataStore imageStore = this . findSnapshotImageStore (snapshot );
264+ DataStore imageStore = findSnapshotImageStore (snapshot );
253265 if (imageStore == null ) {
254266 throw new CloudRuntimeException ("can not find an image stores" );
255267 }
@@ -262,7 +274,7 @@ public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) {
262274 AsyncCallbackDispatcher <SnapshotServiceImpl , CopyCommandResult > caller = AsyncCallbackDispatcher
263275 .create (this );
264276 caller .setCallback (caller .getTarget ().copySnapshotAsyncCallback (null , null )).setContext (context );
265- this . motionSrv .copyAsync (snapshot , snapshotOnImageStore , caller );
277+ motionSrv .copyAsync (snapshot , snapshotOnImageStore , caller );
266278 } catch (Exception e ) {
267279 s_logger .debug ("Failed to copy snapshot" , e );
268280 result .setResult ("Failed to copy snapshot:" + e .toString ());
@@ -314,7 +326,7 @@ protected Void copySnapshotAsyncCallback(AsyncCallbackDispatcher<SnapshotService
314326 CopyCmdAnswer answer = (CopyCmdAnswer ) result .getAnswer ();
315327 destSnapshot .processEvent (Event .OperationSuccessed , result .getAnswer ());
316328 srcSnapshot .processEvent (Snapshot .Event .OperationSucceeded );
317- snapResult = new SnapshotResult (this . snapshotfactory .getSnapshot (destSnapshot .getId (),
329+ snapResult = new SnapshotResult (_snapshotFactory .getSnapshot (destSnapshot .getId (),
318330 destSnapshot .getDataStore ()), answer );
319331 future .complete (snapResult );
320332 } catch (Exception e ) {
@@ -403,7 +415,7 @@ public boolean deleteSnapshot(SnapshotInfo snapInfo) {
403415
404416 @ Override
405417 public boolean revertSnapshot (Long snapshotId ) {
406- SnapshotInfo snapshot = snapshotfactory .getSnapshot (snapshotId , DataStoreRole .Primary );
418+ SnapshotInfo snapshot = _snapshotFactory .getSnapshot (snapshotId , DataStoreRole .Primary );
407419 PrimaryDataStore store = (PrimaryDataStore )snapshot .getDataStore ();
408420
409421 AsyncCallFuture <SnapshotResult > future = new AsyncCallFuture <SnapshotResult >();
@@ -429,4 +441,94 @@ public boolean revertSnapshot(Long snapshotId) {
429441 return false ;
430442 }
431443
444+
445+ // This routine is used to push snapshots currently on cache store, but not in region store to region store.
446+ // used in migrating existing NFS secondary storage to S3. We chose to push all volume related snapshots to handle delta snapshots smoothly.
447+ @ Override
448+ public void syncVolumeSnapshotsToRegionStore (long volumeId , DataStore store ) {
449+ if (dataStoreMgr .isRegionStore (store )) {
450+ // list all backed up snapshots for the given volume
451+ List <SnapshotVO > snapshots = _snapshotDao .listByStatus (volumeId , Snapshot .State .BackedUp );
452+ if (snapshots != null ){
453+ for (SnapshotVO snapshot : snapshots ){
454+ syncSnapshotToRegionStore (snapshot .getId (), store );
455+ }
456+ }
457+ }
458+ }
459+
460+ // push one individual snapshots currently on cache store to region store if it is not there already
461+ private void syncSnapshotToRegionStore (long snapshotId , DataStore store ){
462+ if (s_logger .isDebugEnabled ()) {
463+ s_logger .debug ("sync snapshot " + snapshotId + " from cache to object store..." );
464+ }
465+ // if snapshot is already on region wide object store, check if it is really downloaded there (by checking install_path). Sync snapshot to region
466+ // wide store if it is not there physically.
467+ SnapshotInfo snapOnStore = _snapshotFactory .getSnapshot (snapshotId , store );
468+ if (snapOnStore == null ) {
469+ throw new CloudRuntimeException ("Cannot find an entry in snapshot_store_ref for snapshot " + snapshotId + " on region store: " + store .getName ());
470+ }
471+ if (snapOnStore .getPath () == null || snapOnStore .getPath ().length () == 0 ) {
472+ // snapshot is not on region store yet, sync to region store
473+ SnapshotInfo srcSnapshot = _snapshotFactory .getReadySnapshotOnCache (snapshotId );
474+ if (srcSnapshot == null ) {
475+ throw new CloudRuntimeException ("Cannot find snapshot " + snapshotId + " on cache store" );
476+ }
477+ AsyncCallFuture <SnapshotResult > future = syncToRegionStoreAsync (srcSnapshot , store );
478+ try {
479+ SnapshotResult result = future .get ();
480+ if (result .isFailed ()) {
481+ throw new CloudRuntimeException ("sync snapshot from cache to region wide store failed for image store " + store .getName () + ":"
482+ + result .getResult ());
483+ }
484+ _cacheMgr .releaseCacheObject (srcSnapshot ); // reduce reference count for template on cache, so it can recycled by schedule
485+ } catch (Exception ex ) {
486+ throw new CloudRuntimeException ("sync snapshot from cache to region wide store failed for image store " + store .getName ());
487+ }
488+ }
489+
490+ }
491+
492+
493+ private AsyncCallFuture <SnapshotResult > syncToRegionStoreAsync (SnapshotInfo snapshot , DataStore store ) {
494+ AsyncCallFuture <SnapshotResult > future = new AsyncCallFuture <SnapshotResult >();
495+ // no need to create entry on snapshot_store_ref here, since entries are already created when updateCloudToUseObjectStore is invoked.
496+ // But we need to set default install path so that sync can be done in the right s3 path
497+ SnapshotInfo snapshotOnStore = _snapshotFactory .getSnapshot (snapshot , store );
498+ String installPath = TemplateConstants .DEFAULT_SNAPSHOT_ROOT_DIR + "/"
499+ + snapshot .getAccountId () + "/" + snapshot .getVolumeId ();
500+ ((SnapshotObject )snapshotOnStore ).setPath (installPath );
501+ CopySnapshotContext <CommandResult > context = new CopySnapshotContext <CommandResult >(null , snapshot ,
502+ snapshotOnStore , future );
503+ AsyncCallbackDispatcher <SnapshotServiceImpl , CopyCommandResult > caller = AsyncCallbackDispatcher
504+ .create (this );
505+ caller .setCallback (caller .getTarget ().syncSnapshotCallBack (null , null )).setContext (context );
506+ motionSrv .copyAsync (snapshot , snapshotOnStore , caller );
507+ return future ;
508+ }
509+
510+ protected Void syncSnapshotCallBack (AsyncCallbackDispatcher <SnapshotServiceImpl , CopyCommandResult > callback ,
511+ CopySnapshotContext <CommandResult > context ) {
512+ CopyCommandResult result = callback .getResult ();
513+ SnapshotInfo destSnapshot = context .destSnapshot ;
514+ SnapshotResult res = new SnapshotResult (destSnapshot , null );
515+
516+ AsyncCallFuture <SnapshotResult > future = context .future ;
517+ try {
518+ if (result .isFailed ()) {
519+ res .setResult (result .getResult ());
520+ // no change to existing snapshot_store_ref, will try to re-sync later if other call triggers this sync operation
521+ } else {
522+ // this will update install path properly, next time it will not sync anymore.
523+ destSnapshot .processEvent (Event .OperationSuccessed , result .getAnswer ());
524+ }
525+ future .complete (res );
526+ } catch (Exception e ) {
527+ s_logger .debug ("Failed to process sync snapshot callback" , e );
528+ res .setResult (e .toString ());
529+ future .complete (res );
530+ }
531+
532+ return null ;
533+ }
432534}
0 commit comments