@@ -1964,7 +1964,7 @@ protected MigrationOptions createLinkedCloneMigrationOptions(VolumeInfo srcVolum
19641964 Storage .StoragePoolType srcPoolType = srcPool .getPoolType ();
19651965 Long srcPoolClusterId = srcPool .getClusterId ();
19661966 VMTemplateStoragePoolVO ref = templatePoolDao .findByPoolTemplate (destVolumeInfo .getPoolId (), srcVolumeInfo .getTemplateId (), null );
1967- boolean updateBackingFileReference = ref == null ;
1967+ boolean updateBackingFileReference = ref == null || ! StringUtils . equals ( ref . getInstallPath (), srcVolumeBackingFile ) ;
19681968 String backingFile = !updateBackingFileReference ? ref .getInstallPath () : srcVolumeBackingFile ;
19691969 ScopeType scopeType = srcVolumeInfo .getDataStore ().getScope ().getScopeType ();
19701970 return new MigrationOptions (srcPoolUuid , srcPoolType , backingFile , updateBackingFileReference , scopeType , srcPoolClusterId );
@@ -2009,6 +2009,34 @@ protected void setVolumeMigrationOptions(VolumeInfo srcVolumeInfo, VolumeInfo de
20092009 destVolumeInfo .setMigrationOptions (migrationOptions );
20102010 }
20112011
2012+ /**
2013+ * KVM/libvirt selects linked-clone or full-clone storage migration for the whole VM migration request.
2014+ * If any disk is backed by a direct-download template, force the request to full clone so libvirt does
2015+ * not use incremental shared-backing semantics for a disk whose backing chain is not guaranteed on the destination.
2016+ */
2017+ protected boolean shouldForceFullCloneMigration (Map <VolumeInfo , DataStore > volumeDataStoreMap , Host destHost ) {
2018+ for (Map .Entry <VolumeInfo , DataStore > entry : volumeDataStoreMap .entrySet ()) {
2019+ VolumeInfo srcVolumeInfo = entry .getKey ();
2020+ DataStore destDataStore = entry .getValue ();
2021+ StoragePoolVO sourceStoragePool = _storagePoolDao .findById (srcVolumeInfo .getPoolId ());
2022+ StoragePoolVO destStoragePool = _storagePoolDao .findById (destDataStore .getId ());
2023+
2024+ if (shouldSkipVolumeMigration (sourceStoragePool , destHost , destStoragePool )) {
2025+ continue ;
2026+ }
2027+
2028+ if (srcVolumeInfo .isDirectDownload ()) {
2029+ return true ;
2030+ }
2031+ }
2032+ return false ;
2033+ }
2034+
2035+ protected boolean shouldSkipVolumeMigration (StoragePoolVO sourceStoragePool , Host destHost , StoragePoolVO destStoragePool ) {
2036+ return (sourceStoragePool .getId () == destStoragePool .getId () && sourceStoragePool .getPoolType () == Storage .StoragePoolType .PowerFlex ) ||
2037+ !shouldMigrateVolume (sourceStoragePool , destHost , destStoragePool );
2038+ }
2039+
20122040 /**
20132041 * For each disk to migrate:
20142042 * <ul>
@@ -2036,6 +2064,10 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20362064 List <MigrateDiskInfo > migrateDiskInfoList = new ArrayList <MigrateDiskInfo >();
20372065
20382066 Map <String , MigrateCommand .MigrateDiskInfo > migrateStorage = new HashMap <>();
2067+ boolean forceFullCloneMigration = shouldForceFullCloneMigration (volumeDataStoreMap , destHost );
2068+ if (forceFullCloneMigration ) {
2069+ logger .info ("Using full clone live storage migration for VM [{}] because one or more migrated volumes are backed by direct-download templates." , vmTO );
2070+ }
20392071
20402072 boolean managedStorageDestination = false ;
20412073 boolean migrateNonSharedInc = false ;
@@ -2047,16 +2079,12 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20472079 StoragePoolVO destStoragePool = _storagePoolDao .findById (destDataStore .getId ());
20482080 StoragePoolVO sourceStoragePool = _storagePoolDao .findById (srcVolumeInfo .getPoolId ());
20492081
2050- // do not initiate migration for the same PowerFlex/ScaleIO pool
2051- if (sourceStoragePool .getId () == destStoragePool .getId () && sourceStoragePool .getPoolType () == Storage .StoragePoolType .PowerFlex ) {
2052- continue ;
2053- }
2054-
2055- if (!shouldMigrateVolume (sourceStoragePool , destHost , destStoragePool )) {
2082+ if (shouldSkipVolumeMigration (sourceStoragePool , destHost , destStoragePool )) {
20562083 continue ;
20572084 }
20582085
2059- MigrationOptions .Type migrationType = decideMigrationTypeAndCopyTemplateIfNeeded (destHost , vmInstance , srcVolumeInfo , sourceStoragePool , destStoragePool , destDataStore );
2086+ MigrationOptions .Type migrationType = decideMigrationTypeAndCopyTemplateIfNeeded (destHost , vmInstance , srcVolumeInfo , sourceStoragePool , destStoragePool ,
2087+ destDataStore , forceFullCloneMigration );
20602088 migrateNonSharedInc = migrateNonSharedInc || MigrationOptions .Type .LinkedClone .equals (migrationType );
20612089
20622090 VolumeVO destVolume = duplicateVolumeOnAnotherStorage (srcVolume , destStoragePool );
@@ -2068,6 +2096,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20682096 destVolumeInfo .processEvent (Event .MigrationCopySucceeded );
20692097 // move the volume from Ready to Migrating
20702098 destVolumeInfo .processEvent (Event .MigrationRequested );
2099+ srcVolumeInfoToDestVolumeInfo .put (srcVolumeInfo , destVolumeInfo );
20712100
20722101 setVolumeMigrationOptions (srcVolumeInfo , destVolumeInfo , vmTO , srcHost , destStoragePool , migrationType );
20732102
@@ -2112,8 +2141,6 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
21122141 prepareDiskWithSecretConsumerDetail (vmTO , srcVolumeInfo , destVolumeInfo .getPath ());
21132142
21142143 migrateStorage .put (srcVolumeInfo .getPath (), migrateDiskInfo );
2115-
2116- srcVolumeInfoToDestVolumeInfo .put (srcVolumeInfo , destVolumeInfo );
21172144 }
21182145
21192146 PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand (vmTO );
@@ -2211,7 +2238,13 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
22112238 }
22122239 }
22132240
2214- private MigrationOptions .Type decideMigrationTypeAndCopyTemplateIfNeeded (Host destHost , VMInstanceVO vmInstance , VolumeInfo srcVolumeInfo , StoragePoolVO sourceStoragePool , StoragePoolVO destStoragePool , DataStore destDataStore ) {
2241+ protected MigrationOptions .Type decideMigrationTypeAndCopyTemplateIfNeeded (Host destHost , VMInstanceVO vmInstance , VolumeInfo srcVolumeInfo , StoragePoolVO sourceStoragePool ,
2242+ StoragePoolVO destStoragePool , DataStore destDataStore , boolean forceFullCloneMigration ) {
2243+ if (forceFullCloneMigration ) {
2244+ logger .debug ("Skipping linked clone migration for volume [{}] because the migration request includes a direct-download backed volume." , srcVolumeInfo .getId ());
2245+ return MigrationOptions .Type .FullClone ;
2246+ }
2247+
22152248 VMTemplateVO vmTemplate = _vmTemplateDao .findById (vmInstance .getTemplateId ());
22162249 String srcVolumeBackingFile = getVolumeBackingFile (srcVolumeInfo );
22172250 if (StringUtils .isNotBlank (srcVolumeBackingFile ) && supportStoragePoolType (destStoragePool .getPoolType (), StoragePoolType .Filesystem ) &&
@@ -2449,7 +2482,11 @@ private VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePoolVO st
24492482 protected String connectHostToVolume (Host host , long storagePoolId , String iqn ) {
24502483 ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand (storagePoolId , iqn , true );
24512484
2452- return sendModifyTargetsCommand (modifyTargetsCommand , host .getId ()).get (0 );
2485+ List <String > connectedPaths = sendModifyTargetsCommand (modifyTargetsCommand , host .getId ());
2486+ if (CollectionUtils .isEmpty (connectedPaths )) {
2487+ throw new CloudRuntimeException (String .format ("Unable to modify targets on the following host: %s because no connected path was returned for target [%s]." , host .getId (), iqn ));
2488+ }
2489+ return connectedPaths .get (0 );
24532490 }
24542491
24552492 private void disconnectHostFromVolume (Host host , long storagePoolId , String iqn ) {
@@ -2484,14 +2521,25 @@ private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String
24842521 }
24852522
24862523 private List <String > sendModifyTargetsCommand (ModifyTargetsCommand cmd , long hostId ) {
2487- ModifyTargetsAnswer modifyTargetsAnswer = ( ModifyTargetsAnswer ) agentManager .easySend (hostId , cmd );
2524+ Answer answer = agentManager .easySend (hostId , cmd );
24882525
2489- if (modifyTargetsAnswer == null ) {
2526+ if (answer == null ) {
24902527 throw new CloudRuntimeException ("Unable to get an answer to the modify targets command" );
24912528 }
24922529
2530+ if (!(answer instanceof ModifyTargetsAnswer )) {
2531+ String details = StringUtils .defaultIfBlank (answer .getDetails (),
2532+ String .format ("Unexpected answer type returned: %s" , answer .getClass ().getName ()));
2533+ throw new CloudRuntimeException (String .format ("Unable to modify targets on the following host: %s due to [%s]" , hostId , details ));
2534+ }
2535+
2536+ ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer )answer ;
2537+
24932538 if (!modifyTargetsAnswer .getResult ()) {
24942539 String msg = "Unable to modify targets on the following host: " + hostId ;
2540+ if (StringUtils .isNotBlank (modifyTargetsAnswer .getDetails ())) {
2541+ msg = String .format ("%s due to [%s]" , msg , modifyTargetsAnswer .getDetails ());
2542+ }
24952543
24962544 throw new CloudRuntimeException (msg );
24972545 }
@@ -2504,14 +2552,25 @@ private List<String> sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hos
25042552 */
25052553 protected void updateCopiedTemplateReference (VolumeInfo srcVolumeInfo , VolumeInfo destVolumeInfo ) {
25062554 VMTemplateStoragePoolVO ref = templatePoolDao .findByPoolTemplate (srcVolumeInfo .getPoolId (), srcVolumeInfo .getTemplateId (), null );
2507- VMTemplateStoragePoolVO newRef = new VMTemplateStoragePoolVO (destVolumeInfo .getPoolId (), ref .getTemplateId (), null );
2508- newRef .setDownloadPercent (100 );
2509- newRef .setDownloadState (VMTemplateStorageResourceAssoc .Status .DOWNLOADED );
2510- newRef .setState (ObjectInDataStoreStateMachine .State .Ready );
2511- newRef .setTemplateSize (ref .getTemplateSize ());
2512- newRef .setLocalDownloadPath (ref .getLocalDownloadPath ());
2513- newRef .setInstallPath (ref .getInstallPath ());
2514- templatePoolDao .persist (newRef );
2555+ if (ref == null ) {
2556+ throw new CloudRuntimeException (String .format ("Unable to update copied template reference because source template reference was not found for pool [%s] and template [%s]." ,
2557+ srcVolumeInfo .getPoolId (), srcVolumeInfo .getTemplateId ()));
2558+ }
2559+ VMTemplateStoragePoolVO destRef = templatePoolDao .findByPoolTemplate (destVolumeInfo .getPoolId (), ref .getTemplateId (), null );
2560+ if (destRef == null ) {
2561+ destRef = new VMTemplateStoragePoolVO (destVolumeInfo .getPoolId (), ref .getTemplateId (), null );
2562+ }
2563+ destRef .setDownloadPercent (100 );
2564+ destRef .setDownloadState (VMTemplateStorageResourceAssoc .Status .DOWNLOADED );
2565+ destRef .setState (ObjectInDataStoreStateMachine .State .Ready );
2566+ destRef .setTemplateSize (ref .getTemplateSize ());
2567+ destRef .setLocalDownloadPath (ref .getLocalDownloadPath ());
2568+ destRef .setInstallPath (ref .getInstallPath ());
2569+ if (destRef .getId () == 0 ) {
2570+ templatePoolDao .persist (destRef );
2571+ } else {
2572+ templatePoolDao .update (destRef .getId (), destRef );
2573+ }
25152574 }
25162575
25172576 /**
0 commit comments