Skip to content

Commit 7bc0d48

Browse files
author
GabrielBrascher
committed
Migrate template to target host if needed.
Fix KVM VM local storage live migration by migrating its template to the target host if needed.
1 parent 3aaf281 commit 7bc0d48

File tree

5 files changed

+237
-18
lines changed

5 files changed

+237
-18
lines changed

engine/components-api/src/main/java/com/cloud/storage/StorageManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ public interface StorageManager extends StorageService {
101101
ConfigKey.Scope.Cluster,
102102
null);
103103

104+
ConfigKey<Integer> PRIMARY_STORAGE_DOWNLOAD_WAIT = new ConfigKey<Integer>("Storage", Integer.class, "primary.storage.download.wait", "10800",
105+
"In second, timeout for download template to primary storage", false);
106+
104107
/**
105108
* Returns a comma separated list of tags for the specified storage pool
106109
* @param poolId

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/KvmNonManagedStorageDataMotionStrategy.java

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,34 @@
2929
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
3030
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
3131
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
32+
import org.apache.cloudstack.storage.command.CopyCommand;
33+
import org.apache.cloudstack.storage.datastore.DataStoreManagerImpl;
3234
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
35+
import org.apache.cloudstack.storage.to.TemplateObjectTO;
36+
import org.apache.commons.lang.StringUtils;
37+
import org.apache.log4j.Logger;
3338

3439
import com.cloud.agent.api.Answer;
3540
import com.cloud.agent.api.MigrateCommand;
3641
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
3742
import com.cloud.agent.api.storage.CreateAnswer;
3843
import com.cloud.agent.api.storage.CreateCommand;
3944
import com.cloud.agent.api.to.VirtualMachineTO;
45+
import com.cloud.exception.AgentUnavailableException;
46+
import com.cloud.exception.OperationTimedoutException;
4047
import com.cloud.host.Host;
4148
import com.cloud.hypervisor.Hypervisor.HypervisorType;
4249
import com.cloud.storage.DataStoreRole;
4350
import com.cloud.storage.DiskOfferingVO;
4451
import com.cloud.storage.Storage.StoragePoolType;
52+
import com.cloud.storage.StorageManager;
53+
import com.cloud.storage.StoragePool;
54+
import com.cloud.storage.VMTemplateStoragePoolVO;
4555
import com.cloud.storage.VolumeVO;
56+
import com.cloud.storage.dao.VMTemplatePoolDao;
4657
import com.cloud.utils.exception.CloudRuntimeException;
4758
import com.cloud.vm.DiskProfile;
59+
import com.cloud.vm.VirtualMachineManager;
4860

4961
/**
5062
* Extends {@link StorageSystemDataMotionStrategy}, allowing KVM hosts to migrate VMs with the ROOT volume on a non managed local storage pool.
@@ -54,6 +66,14 @@ public class KvmNonManagedStorageDataMotionStrategy extends StorageSystemDataMot
5466

5567
@Inject
5668
private TemplateDataFactory templateDataFactory;
69+
@Inject
70+
private VMTemplatePoolDao vmTemplatePoolDao;
71+
@Inject
72+
private DataStoreManagerImpl dataStoreManagerImpl;
73+
@Inject
74+
private VirtualMachineManager virtualMachineManager;
75+
76+
private static final Logger LOGGER = Logger.getLogger(KvmNonManagedStorageDataMotionStrategy.class);
5777

5878
/**
5979
* Uses the canHandle from the Super class {@link StorageSystemDataMotionStrategy}. If the storage pool is of file and the internalCanHandle from {@link StorageSystemDataMotionStrategy} CANT_HANDLE, returns the StrategyPriority.HYPERVISOR strategy priority. otherwise returns CANT_HANDLE.
@@ -95,7 +115,7 @@ protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Hos
95115
String templateUuid = getTemplateUuid(destVolumeInfo.getTemplateId());
96116
CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
97117

98-
Answer rootImageProvisioningAnswer = _agentMgr.easySend(destHost.getId(), rootImageProvisioningCommand);
118+
Answer rootImageProvisioningAnswer = agentManager.easySend(destHost.getId(), rootImageProvisioningCommand);
99119

100120
if (rootImageProvisioningAnswer == null) {
101121
throw new CloudRuntimeException(String.format("Migration with storage of vm [%s] failed while provisioning root image", vmTO.getName()));
@@ -140,4 +160,56 @@ protected void setVolumePath(VolumeVO volume) {
140160
protected boolean shouldMigrateVolume(StoragePoolVO sourceStoragePool, Host destHost, StoragePoolVO destStoragePool) {
141161
return sourceStoragePool.getPoolType() == StoragePoolType.Filesystem;
142162
}
163+
164+
/**
165+
* If the template is not on the target primary storage then it migrates the template.
166+
* Otherwise the execution flow fails during migration and creates a null file created on the target storage pool.
167+
*/
168+
@Override
169+
protected void migrateTemplateToTargetFilesystemStorageIfNeeded(VolumeInfo srcVolumeInfo, DataStore destDataStore, StoragePool destStoragePool,
170+
Host destHost) {
171+
VMTemplateStoragePoolVO vmTemplateStoragePoolVO = vmTemplatePoolDao.findByPoolTemplate(destStoragePool.getId(), srcVolumeInfo.getTemplateId());
172+
if (vmTemplateStoragePoolVO == null && destStoragePool.getPoolType() == StoragePoolType.Filesystem) {
173+
DataStore sourceTemplateDataStore = dataStoreManagerImpl.getImageStore(srcVolumeInfo.getDataCenterId());
174+
TemplateInfo sourceTemplateInfo = templateDataFactory.getTemplate(srcVolumeInfo.getTemplateId(), sourceTemplateDataStore);
175+
TemplateObjectTO sourceTemplate = new TemplateObjectTO(sourceTemplateInfo);
176+
177+
LOGGER.debug(String.format("Could not find template [id=%s, name=%s] on the storage pool [id=%s]; thus, the template will be migrated to the target storage pool.",
178+
srcVolumeInfo.getTemplateId(), sourceTemplateInfo.getName(), destDataStore.getId()));
179+
180+
TemplateInfo destTemplateInfo = templateDataFactory.getTemplate(srcVolumeInfo.getTemplateId(), destDataStore);
181+
final TemplateObjectTO destTemplate = new TemplateObjectTO(destTemplateInfo);
182+
183+
sendCopyCommand(destHost, sourceTemplate, destTemplate, destDataStore);
184+
}
185+
}
186+
187+
/**
188+
* Sends the CopyCommand to migrate the template to the dest host.
189+
*/
190+
protected void sendCopyCommand(Host destHost, TemplateObjectTO sourceTemplate, TemplateObjectTO destTemplate, DataStore destDataStore) {
191+
boolean executeInSequence = virtualMachineManager.getExecuteInSequence(HypervisorType.KVM);
192+
CopyCommand copyCommand = new CopyCommand(sourceTemplate, destTemplate, StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value(), executeInSequence);
193+
try {
194+
Answer copyCommandAnswer = agentManager.send(destHost.getId(), copyCommand);
195+
logInCaseOfTemplateCopyFailure(copyCommandAnswer, sourceTemplate, destDataStore);
196+
} catch (AgentUnavailableException | OperationTimedoutException e) {
197+
throw new CloudRuntimeException(String.format("Failed to copy template [id=%s, name=%s] to the primary storage pool [id=%s].", sourceTemplate.getId(),
198+
sourceTemplate.getName(), destDataStore.getId()), e);
199+
}
200+
}
201+
202+
/**
203+
* Logs in debug mode the copy command failure if the CopyCommand Answer has result as false.
204+
*/
205+
protected void logInCaseOfTemplateCopyFailure(Answer copyCommandAnswer, TemplateObjectTO sourceTemplate, DataStore destDataStore) {
206+
if (copyCommandAnswer != null && !copyCommandAnswer.getResult()) {
207+
String failureDetails = StringUtils.EMPTY;
208+
if (copyCommandAnswer.getDetails() != null) {
209+
failureDetails = "; details: " + copyCommandAnswer.getDetails();
210+
}
211+
LOGGER.debug(String.format("Failed to copy template [id=%s, name=%s] to the primary storage pool [id=%s]%s", sourceTemplate.getId(), sourceTemplate.getName(),
212+
destDataStore.getId(), failureDetails));
213+
}
214+
}
143215
}

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
138138
private static final String OPERATION_NOT_SUPPORTED = "This operation is not supported.";
139139

140140
@Inject
141-
protected AgentManager _agentMgr;
141+
protected AgentManager agentManager;
142142
@Inject private ConfigurationDao _configDao;
143143
@Inject private DataStoreManager dataStoreMgr;
144144
@Inject
@@ -953,7 +953,7 @@ else if (HypervisorType.VMware.equals(snapshotInfo.getHypervisorType()) || Hyper
953953

954954
copyCommand.setOptions(srcDetails);
955955

956-
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
956+
copyCmdAnswer = (CopyCmdAnswer)agentManager.send(hostVO.getId(), copyCommand);
957957

958958
if (!copyCmdAnswer.getResult()) {
959959
errMsg = copyCmdAnswer.getDetails();
@@ -1120,7 +1120,7 @@ private void handleCreateNonManagedVolumeFromManagedSnapshot(SnapshotInfo snapsh
11201120

11211121
copyCommand.setOptions(srcDetails);
11221122

1123-
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
1123+
copyCmdAnswer = (CopyCmdAnswer)agentManager.send(hostVO.getId(), copyCommand);
11241124

11251125
if (!copyCmdAnswer.getResult()) {
11261126
errMsg = copyCmdAnswer.getDetails();
@@ -1609,7 +1609,7 @@ private CopyCmdAnswer copyImageToVolume(DataObject srcDataObject, VolumeInfo des
16091609

16101610
copyCommand.setOptions2(destDetails);
16111611

1612-
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
1612+
copyCmdAnswer = (CopyCmdAnswer)agentManager.send(hostVO.getId(), copyCommand);
16131613
}
16141614
catch (CloudRuntimeException | AgentUnavailableException | OperationTimedoutException ex) {
16151615
String msg = "Failed to copy image : ";
@@ -1724,6 +1724,8 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
17241724
continue;
17251725
}
17261726

1727+
migrateTemplateToTargetFilesystemStorageIfNeeded(srcVolumeInfo, destDataStore, destStoragePool, destHost);
1728+
17271729
VolumeVO destVolume = duplicateVolumeOnAnotherStorage(srcVolume, destStoragePool);
17281730
VolumeInfo destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
17291731

@@ -1763,7 +1765,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
17631765
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
17641766

17651767
try {
1766-
Answer pfma = _agentMgr.send(destHost.getId(), pfmc);
1768+
Answer pfma = agentManager.send(destHost.getId(), pfmc);
17671769

17681770
if (pfma == null || !pfma.getResult()) {
17691771
String details = pfma != null ? pfma.getDetails() : "null answer returned";
@@ -1791,7 +1793,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
17911793

17921794
migrateCommand.setAutoConvergence(kvmAutoConvergence);
17931795

1794-
MigrateAnswer migrateAnswer = (MigrateAnswer)_agentMgr.send(srcHost.getId(), migrateCommand);
1796+
MigrateAnswer migrateAnswer = (MigrateAnswer)agentManager.send(srcHost.getId(), migrateCommand);
17951797

17961798
boolean success = migrateAnswer != null && migrateAnswer.getResult();
17971799

@@ -1859,14 +1861,21 @@ protected void setVolumePath(VolumeVO volume) {
18591861
volume.setPath(volume.get_iScsiName());
18601862
}
18611863

1864+
/**
1865+
* For this strategy it is not necessary to check and migrate the template; however, classes that extend this one may need to check if the template is on the target storage pool (and if not then migrate) before migrating the VM.
1866+
*/
1867+
protected void migrateTemplateToTargetFilesystemStorageIfNeeded(VolumeInfo srcVolumeInfo, DataStore destDataStore, StoragePool destStoragePool, Host destHost) {
1868+
// This method is used by classes that extend this one
1869+
}
1870+
18621871
private void handlePostMigration(boolean success, Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo, VirtualMachineTO vmTO, Host destHost) {
18631872
if (!success) {
18641873
try {
18651874
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
18661875

18671876
pfmc.setRollback(true);
18681877

1869-
Answer pfma = _agentMgr.send(destHost.getId(), pfmc);
1878+
Answer pfma = agentManager.send(destHost.getId(), pfmc);
18701879

18711880
if (pfma == null || !pfma.getResult()) {
18721881
String details = pfma != null ? pfma.getDetails() : "null answer returned";
@@ -2007,7 +2016,7 @@ private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String
20072016
}
20082017

20092018
private List<String> sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) {
2010-
ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer)_agentMgr.easySend(hostId, cmd);
2019+
ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer)agentManager.easySend(hostId, cmd);
20112020

20122021
if (modifyTargetsAnswer == null) {
20132022
throw new CloudRuntimeException("Unable to get an answer to the modify targets command");
@@ -2125,7 +2134,7 @@ private void handleCreateTemplateFromManagedVolume(VolumeInfo volumeInfo, Templa
21252134

21262135
copyCommand.setOptions(srcDetails);
21272136

2128-
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
2137+
copyCmdAnswer = (CopyCmdAnswer)agentManager.send(hostVO.getId(), copyCommand);
21292138

21302139
if (!copyCmdAnswer.getResult()) {
21312140
errMsg = copyCmdAnswer.getDetails();
@@ -2389,7 +2398,7 @@ private CopyCmdAnswer performResignature(DataObject dataObj, HostVO hostVO, Map<
23892398
try {
23902399
_volumeService.grantAccess(dataObj, hostVO, dataStore);
23912400

2392-
answer = (ResignatureAnswer)_agentMgr.send(hostVO.getId(), command);
2401+
answer = (ResignatureAnswer)agentManager.send(hostVO.getId(), command);
23932402
}
23942403
catch (CloudRuntimeException | AgentUnavailableException | OperationTimedoutException ex) {
23952404
keepGrantedAccess = false;
@@ -2466,7 +2475,7 @@ private String migrateVolumeForKVM(VolumeInfo srcVolumeInfo, VolumeInfo destVolu
24662475

24672476
_volumeService.grantAccess(destVolumeInfo, hostVO, destVolumeInfo.getDataStore());
24682477

2469-
MigrateVolumeAnswer migrateVolumeAnswer = (MigrateVolumeAnswer)_agentMgr.send(hostVO.getId(), migrateVolumeCommand);
2478+
MigrateVolumeAnswer migrateVolumeAnswer = (MigrateVolumeAnswer)agentManager.send(hostVO.getId(), migrateVolumeCommand);
24702479

24712480
if (migrateVolumeAnswer == null || !migrateVolumeAnswer.getResult()) {
24722481
if (migrateVolumeAnswer != null && !StringUtils.isEmpty(migrateVolumeAnswer.getDetails())) {
@@ -2534,7 +2543,7 @@ private String copyManagedVolumeToSecondaryStorage(VolumeInfo srcVolumeInfo, Vol
25342543
_volumeService.grantAccess(srcVolumeInfo, hostVO, srcVolumeInfo.getDataStore());
25352544
}
25362545

2537-
CopyVolumeAnswer copyVolumeAnswer = (CopyVolumeAnswer)_agentMgr.send(hostVO.getId(), copyVolumeCommand);
2546+
CopyVolumeAnswer copyVolumeAnswer = (CopyVolumeAnswer)agentManager.send(hostVO.getId(), copyVolumeCommand);
25382547

25392548
if (copyVolumeAnswer == null || !copyVolumeAnswer.getResult()) {
25402549
if (copyVolumeAnswer != null && !StringUtils.isEmpty(copyVolumeAnswer.getDetails())) {
@@ -2623,7 +2632,7 @@ private CopyCmdAnswer performCopyOfVdi(VolumeInfo volumeInfo, SnapshotInfo snaps
26232632

26242633
copyCommand.setOptions2(destDetails);
26252634

2626-
copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
2635+
copyCmdAnswer = (CopyCmdAnswer)agentManager.send(hostVO.getId(), copyCommand);
26272636
}
26282637
catch (CloudRuntimeException | AgentUnavailableException | OperationTimedoutException ex) {
26292638
String msg = "Failed to perform VDI copy : ";

0 commit comments

Comments
 (0)