CLOUDSTACK-8061: Extracting volume when it is in migrating state causes

both the operations to fail.
This commit is contained in:
Min Chen 2014-12-10 14:16:00 -08:00
parent f5619f428b
commit 528bc80b4c
2 changed files with 156 additions and 6 deletions

View File

@ -0,0 +1,38 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.vm;
public class VmWorkExtractVolume extends VmWork {
private static final long serialVersionUID = -565778516928408602L;
private long volumeId;
private long zoneId;
public VmWorkExtractVolume(long userId, long accountId, long vmId, String handlerName, long volumeId, long zoneId) {
super(userId, accountId, vmId, handlerName);
this.volumeId = volumeId;
this.zoneId = zoneId;
}
public long getVolumeId() {
return volumeId;
}
public long getZoneId() {
return zoneId;
}
}

View File

@ -141,6 +141,7 @@ import com.cloud.vm.VmWork;
import com.cloud.vm.VmWorkAttachVolume;
import com.cloud.vm.VmWorkConstants;
import com.cloud.vm.VmWorkDetachVolume;
import com.cloud.vm.VmWorkExtractVolume;
import com.cloud.vm.VmWorkJobHandler;
import com.cloud.vm.VmWorkJobHandlerProxy;
import com.cloud.vm.VmWorkMigrateVolume;
@ -2040,14 +2041,70 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return volumeStoreRef.getExtractUrl();
}
dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
ImageStoreEntity secStore = (ImageStoreEntity)dataStoreMgr.getImageStore(zoneId);
secStore.getUri();
VMInstanceVO vm = null;
if (volume.getInstanceId() != null) {
vm = _vmInstanceDao.findById(volume.getInstanceId());
}
if (vm != null) {
// serialize VM operation
AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
// avoid re-entrance
VmWorkJobVO placeHolder = null;
placeHolder = createPlaceHolderWork(vm.getId());
try {
return orchestrateExtractVolume(volume.getId(), zoneId);
} finally {
_workJobDao.expunge(placeHolder.getId());
}
} else {
Outcome<String> outcome = extractVolumeThroughJobQueue(vm.getId(), volume.getId(), zoneId);
try {
outcome.get();
} catch (InterruptedException e) {
throw new RuntimeException("Operation is interrupted", e);
} catch (java.util.concurrent.ExecutionException e) {
throw new RuntimeException("Execution excetion", e);
}
Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob());
if (jobResult != null) {
if (jobResult instanceof ConcurrentOperationException)
throw (ConcurrentOperationException)jobResult;
else if (jobResult instanceof RuntimeException)
throw (RuntimeException)jobResult;
else if (jobResult instanceof Throwable)
throw new RuntimeException("Unexpected exception", (Throwable)jobResult);
}
// retrieve the entity url from job result
if (jobResult != null && jobResult instanceof String) {
return (String)jobResult;
}
return null;
}
}
return orchestrateExtractVolume(volume.getId(), zoneId);
}
private String orchestrateExtractVolume(long volumeId, long zoneId) {
// get latest volume state to make sure that it is not updated by other parallel operations
VolumeVO volume = _volsDao.findById(volumeId);
if (volume == null || volume.getState() != Volume.State.Ready) {
throw new InvalidParameterValueException("Volume to be extracted has been removed or not in right state!");
}
// perform extraction
ImageStoreEntity secStore = (ImageStoreEntity)dataStoreMgr.getImageStore(zoneId);
String value = _configDao.getValue(Config.CopyVolumeWait.toString());
NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));
// Copy volume from primary to secondary storage
VolumeInfo srcVol = volFactory.getVolume(volume.getId());
VolumeInfo srcVol = volFactory.getVolume(volumeId);
AsyncCallFuture<VolumeApiResult> cvAnswer = volService.copyVolume(srcVol, secStore);
// Check if you got a valid answer.
VolumeApiResult cvResult = null;
@ -2068,11 +2125,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
VolumeInfo vol = cvResult.getVolume();
String extractUrl = secStore.createEntityExtractUrl(vol.getPath(), vol.getFormat(), vol);
volumeStoreRef = _volumeStoreDao.findByVolume(volumeId);
VolumeDataStoreVO volumeStoreRef = _volumeStoreDao.findByVolume(volumeId);
volumeStoreRef.setExtractUrl(extractUrl);
volumeStoreRef.setExtractUrlCreated(DateUtil.now());
_volumeStoreDao.update(volumeStoreRef.getId(), volumeStoreRef);
return extractUrl;
}
@ -2347,6 +2403,23 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
_storagePoolAllocators = storagePoolAllocators;
}
public class VmJobVolumeUrlOutcome extends OutcomeImpl<String> {
public VmJobVolumeUrlOutcome(final AsyncJob job) {
super(String.class, job, VmJobCheckInterval.value(), new Predicate() {
@Override
public boolean checkCondition() {
AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId());
assert (jobVo != null);
if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS)
return true;
return false;
}
}, AsyncJob.Topics.JOB_STATE);
}
}
public class VmJobVolumeOutcome extends OutcomeImpl<Volume> {
private long _volumeId;
@ -2495,6 +2568,39 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return new VmJobVolumeOutcome(workJob,volumeId);
}
public Outcome<String> extractVolumeThroughJobQueue(final Long vmId, final long volumeId,
final long zoneId) {
final CallContext context = CallContext.current();
final User callingUser = context.getCallingUser();
final Account callingAccount = context.getCallingAccount();
final VMInstanceVO vm = _vmInstanceDao.findById(vmId);
VmWorkJobVO workJob = new VmWorkJobVO(context.getContextId());
workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
workJob.setCmd(VmWorkExtractVolume.class.getName());
workJob.setAccountId(callingAccount.getId());
workJob.setUserId(callingUser.getId());
workJob.setStep(VmWorkJobVO.Step.Starting);
workJob.setVmType(VirtualMachine.Type.Instance);
workJob.setVmInstanceId(vm.getId());
workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
// save work context info (there are some duplications)
VmWorkExtractVolume workInfo = new VmWorkExtractVolume(callingUser.getId(), callingAccount.getId(), vm.getId(),
VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, zoneId);
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
return new VmJobVolumeUrlOutcome(workJob);
}
public Outcome<Volume> migrateVolumeThroughJobQueue(final Long vmId, final long volumeId,
final long destPoolId, final boolean liveMigrate) {
@ -2562,6 +2668,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return new VmJobSnapshotOutcome(workJob,snapshotId);
}
@ReflectionUse
private Pair<JobInfo.Status, String> orchestrateExtractVolume(VmWorkExtractVolume work) throws Exception {
String volUrl = orchestrateExtractVolume(work.getVolumeId(), work.getZoneId());
return new Pair<JobInfo.Status, String>(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(volUrl));
}
@ReflectionUse
private Pair<JobInfo.Status, String> orchestrateAttachVolumeToVM(VmWorkAttachVolume work) throws Exception {
Volume vol = orchestrateAttachVolumeToVM(work.getVmId(), work.getVolumeId(), work.getDeviceId());