Skip to content

Commit 7c5eca9

Browse files
Copy template to target KVM host if needed when migrating local <> local storage (apache#3154)
* Migrate template to target host if needed. Fix KVM VM local storage live migration by migrating its template to the target host if needed. * Address reviewer and add method that updates the DB template reference * Remove deprecated Config.PrimaryStorageDownloadWait * Code formating of @Inject to follow checkstyle
1 parent 3f17671 commit 7c5eca9

8 files changed

Lines changed: 316 additions & 62 deletions

File tree

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/AncientDataMotionStrategy.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import com.cloud.host.Host;
6363
import com.cloud.hypervisor.Hypervisor;
6464
import com.cloud.storage.DataStoreRole;
65+
import com.cloud.storage.StorageManager;
6566
import com.cloud.storage.Storage.StoragePoolType;
6667
import com.cloud.storage.StoragePool;
6768
import com.cloud.storage.VolumeVO;
@@ -145,8 +146,7 @@ private Scope pickCacheScopeForCopy(DataObject srcData, DataObject destData) {
145146
}
146147

147148
protected Answer copyObject(DataObject srcData, DataObject destData, Host destHost) {
148-
String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
149-
int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
149+
int primaryStorageDownloadWait = StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value();
150150
Answer answer = null;
151151
DataObject cacheData = null;
152152
DataObject srcForCopy = srcData;
@@ -156,7 +156,8 @@ protected Answer copyObject(DataObject srcData, DataObject destData, Host destHo
156156
srcForCopy = cacheData = cacheMgr.createCacheObject(srcData, destScope);
157157
}
158158

159-
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), addFullCloneFlagOnVMwareDest(destData.getTO()), _primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
159+
CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), addFullCloneFlagOnVMwareDest(destData.getTO()), primaryStorageDownloadWait,
160+
VirtualMachineManager.ExecuteInSequence.value());
160161
EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData);
161162
if (ep == null) {
162163
String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";

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

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,36 @@
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.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
33+
import org.apache.cloudstack.storage.command.CopyCommand;
34+
import org.apache.cloudstack.storage.datastore.DataStoreManagerImpl;
3235
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
36+
import org.apache.cloudstack.storage.to.TemplateObjectTO;
37+
import org.apache.commons.lang.StringUtils;
38+
import org.apache.log4j.Logger;
3339

3440
import com.cloud.agent.api.Answer;
3541
import com.cloud.agent.api.MigrateCommand;
3642
import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo;
3743
import com.cloud.agent.api.storage.CreateAnswer;
3844
import com.cloud.agent.api.storage.CreateCommand;
3945
import com.cloud.agent.api.to.VirtualMachineTO;
46+
import com.cloud.exception.AgentUnavailableException;
47+
import com.cloud.exception.OperationTimedoutException;
4048
import com.cloud.host.Host;
4149
import com.cloud.hypervisor.Hypervisor.HypervisorType;
4250
import com.cloud.storage.DataStoreRole;
4351
import com.cloud.storage.DiskOfferingVO;
4452
import com.cloud.storage.Storage.StoragePoolType;
53+
import com.cloud.storage.StorageManager;
54+
import com.cloud.storage.StoragePool;
55+
import com.cloud.storage.VMTemplateStoragePoolVO;
56+
import com.cloud.storage.VMTemplateStorageResourceAssoc;
4557
import com.cloud.storage.VolumeVO;
58+
import com.cloud.storage.dao.VMTemplatePoolDao;
4659
import com.cloud.utils.exception.CloudRuntimeException;
4760
import com.cloud.vm.DiskProfile;
61+
import com.cloud.vm.VirtualMachineManager;
4862

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

5569
@Inject
5670
private TemplateDataFactory templateDataFactory;
71+
@Inject
72+
private VMTemplatePoolDao vmTemplatePoolDao;
73+
@Inject
74+
private DataStoreManagerImpl dataStoreManagerImpl;
75+
@Inject
76+
private VirtualMachineManager virtualMachineManager;
77+
78+
private static final Logger LOGGER = Logger.getLogger(KvmNonManagedStorageDataMotionStrategy.class);
5779

5880
/**
5981
* 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 +117,7 @@ protected String generateDestPath(VirtualMachineTO vmTO, VolumeVO srcVolume, Hos
95117
String templateUuid = getTemplateUuid(destVolumeInfo.getTemplateId());
96118
CreateCommand rootImageProvisioningCommand = new CreateCommand(diskProfile, templateUuid, destStoragePool, true);
97119

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

100122
if (rootImageProvisioningAnswer == null) {
101123
throw new CloudRuntimeException(String.format("Migration with storage of vm [%s] failed while provisioning root image", vmTO.getName()));
@@ -140,4 +162,77 @@ protected void setVolumePath(VolumeVO volume) {
140162
protected boolean shouldMigrateVolume(StoragePoolVO sourceStoragePool, Host destHost, StoragePoolVO destStoragePool) {
141163
return sourceStoragePool.getPoolType() == StoragePoolType.Filesystem;
142164
}
165+
166+
/**
167+
* If the template is not on the target primary storage then it copies the template.
168+
*/
169+
@Override
170+
protected void copyTemplateToTargetFilesystemStorageIfNeeded(VolumeInfo srcVolumeInfo, StoragePool srcStoragePool, DataStore destDataStore, StoragePool destStoragePool,
171+
Host destHost) {
172+
VMTemplateStoragePoolVO sourceVolumeTemplateStoragePoolVO = vmTemplatePoolDao.findByPoolTemplate(destStoragePool.getId(), srcVolumeInfo.getTemplateId());
173+
if (sourceVolumeTemplateStoragePoolVO == null && destStoragePool.getPoolType() == StoragePoolType.Filesystem) {
174+
DataStore sourceTemplateDataStore = dataStoreManagerImpl.getImageStore(srcVolumeInfo.getDataCenterId());
175+
TemplateInfo sourceTemplateInfo = templateDataFactory.getTemplate(srcVolumeInfo.getTemplateId(), sourceTemplateDataStore);
176+
TemplateObjectTO sourceTemplate = new TemplateObjectTO(sourceTemplateInfo);
177+
178+
LOGGER.debug(String.format("Could not find template [id=%s, name=%s] on the storage pool [id=%s]; copying the template to the target storage pool.",
179+
srcVolumeInfo.getTemplateId(), sourceTemplateInfo.getName(), destDataStore.getId()));
180+
181+
TemplateInfo destTemplateInfo = templateDataFactory.getTemplate(srcVolumeInfo.getTemplateId(), destDataStore);
182+
final TemplateObjectTO destTemplate = new TemplateObjectTO(destTemplateInfo);
183+
Answer copyCommandAnswer = sendCopyCommand(destHost, sourceTemplate, destTemplate, destDataStore);
184+
185+
if (copyCommandAnswer != null && copyCommandAnswer.getResult()) {
186+
updateTemplateReferenceIfSuccessfulCopy(srcVolumeInfo, srcStoragePool, destTemplateInfo, destDataStore);
187+
}
188+
}
189+
}
190+
191+
/**
192+
* Update the template reference on table "template_spool_ref" (VMTemplateStoragePoolVO).
193+
*/
194+
protected void updateTemplateReferenceIfSuccessfulCopy(VolumeInfo srcVolumeInfo, StoragePool srcStoragePool, TemplateInfo destTemplateInfo, DataStore destDataStore) {
195+
VMTemplateStoragePoolVO srcVolumeTemplateStoragePoolVO = vmTemplatePoolDao.findByPoolTemplate(srcStoragePool.getId(), srcVolumeInfo.getTemplateId());
196+
VMTemplateStoragePoolVO destVolumeTemplateStoragePoolVO = new VMTemplateStoragePoolVO(destDataStore.getId(), srcVolumeInfo.getTemplateId());
197+
destVolumeTemplateStoragePoolVO.setDownloadPercent(100);
198+
destVolumeTemplateStoragePoolVO.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED);
199+
destVolumeTemplateStoragePoolVO.setState(ObjectInDataStoreStateMachine.State.Ready);
200+
destVolumeTemplateStoragePoolVO.setTemplateSize(srcVolumeTemplateStoragePoolVO.getTemplateSize());
201+
destVolumeTemplateStoragePoolVO.setLocalDownloadPath(destTemplateInfo.getUuid());
202+
destVolumeTemplateStoragePoolVO.setInstallPath(destTemplateInfo.getUuid());
203+
vmTemplatePoolDao.persist(destVolumeTemplateStoragePoolVO);
204+
}
205+
206+
/**
207+
* Sends the CopyCommand to migrate the template to the dest host.
208+
*/
209+
protected Answer sendCopyCommand(Host destHost, TemplateObjectTO sourceTemplate, TemplateObjectTO destTemplate, DataStore destDataStore) {
210+
boolean executeInSequence = virtualMachineManager.getExecuteInSequence(HypervisorType.KVM);
211+
CopyCommand copyCommand = new CopyCommand(sourceTemplate, destTemplate, StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value(), executeInSequence);
212+
try {
213+
Answer copyCommandAnswer = agentManager.send(destHost.getId(), copyCommand);
214+
logInCaseOfTemplateCopyFailure(copyCommandAnswer, sourceTemplate, destDataStore);
215+
return copyCommandAnswer;
216+
} catch (AgentUnavailableException | OperationTimedoutException e) {
217+
throw new CloudRuntimeException(generateFailToCopyTemplateMessage(sourceTemplate, destDataStore), e);
218+
}
219+
}
220+
221+
private String generateFailToCopyTemplateMessage(TemplateObjectTO sourceTemplate, DataStore destDataStore) {
222+
return String.format("Failed to copy template [id=%s, name=%s] to the primary storage pool [id=%s].", sourceTemplate.getId(),
223+
sourceTemplate.getName(), destDataStore.getId());
224+
}
225+
226+
/**
227+
* Logs in debug mode the copy command failure if the CopyCommand Answer has result as false.
228+
*/
229+
protected void logInCaseOfTemplateCopyFailure(Answer copyCommandAnswer, TemplateObjectTO sourceTemplate, DataStore destDataStore) {
230+
if (copyCommandAnswer != null && !copyCommandAnswer.getResult()) {
231+
String failureDetails = StringUtils.EMPTY;
232+
if (copyCommandAnswer.getDetails() != null) {
233+
failureDetails = " Details: " + copyCommandAnswer.getDetails();
234+
}
235+
LOGGER.error(generateFailToCopyTemplateMessage(sourceTemplate, destDataStore) + failureDetails);
236+
}
237+
}
143238
}

0 commit comments

Comments
 (0)