Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E41B6927C for ; Mon, 15 Dec 2014 08:43:31 +0000 (UTC) Received: (qmail 80875 invoked by uid 500); 15 Dec 2014 08:43:29 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 80765 invoked by uid 500); 15 Dec 2014 08:43:29 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 80408 invoked by uid 99); 15 Dec 2014 08:43:29 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 15 Dec 2014 08:43:29 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 392F09C6791; Mon, 15 Dec 2014 08:43:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: dahn@apache.org To: commits@cloudstack.apache.org Date: Mon, 15 Dec 2014 08:43:34 -0000 Message-Id: <86b1d5b4dbf44424a86847026a3570ec@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [06/20] git commit: updated refs/heads/hotfix/scp-exception to 7de94c9 CLOUDSTACK-8061: Extracting volume when it is in migrating state causes both the operations to fail. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/528bc80b Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/528bc80b Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/528bc80b Branch: refs/heads/hotfix/scp-exception Commit: 528bc80b4c03306ab2d15a8bc1bf2bb7fe404988 Parents: f5619f4 Author: Min Chen Authored: Wed Dec 10 14:16:00 2014 -0800 Committer: Min Chen Committed: Thu Dec 11 09:53:33 2014 -0800 ---------------------------------------------------------------------- .../src/com/cloud/vm/VmWorkExtractVolume.java | 38 ++++++ .../com/cloud/storage/VolumeApiServiceImpl.java | 124 ++++++++++++++++++- 2 files changed, 156 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/528bc80b/engine/components-api/src/com/cloud/vm/VmWorkExtractVolume.java ---------------------------------------------------------------------- diff --git a/engine/components-api/src/com/cloud/vm/VmWorkExtractVolume.java b/engine/components-api/src/com/cloud/vm/VmWorkExtractVolume.java new file mode 100644 index 0000000..82b5db4 --- /dev/null +++ b/engine/components-api/src/com/cloud/vm/VmWorkExtractVolume.java @@ -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; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/528bc80b/server/src/com/cloud/storage/VolumeApiServiceImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 9132f42..2683596 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -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 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 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 { + + 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 { private long _volumeId; @@ -2495,6 +2568,39 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return new VmJobVolumeOutcome(workJob,volumeId); } + public Outcome 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 migrateVolumeThroughJobQueue(final Long vmId, final long volumeId, final long destPoolId, final boolean liveMigrate) { @@ -2563,6 +2669,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } @ReflectionUse + private Pair orchestrateExtractVolume(VmWorkExtractVolume work) throws Exception { + String volUrl = orchestrateExtractVolume(work.getVolumeId(), work.getZoneId()); + return new Pair(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(volUrl)); + } + + @ReflectionUse private Pair orchestrateAttachVolumeToVM(VmWorkAttachVolume work) throws Exception { Volume vol = orchestrateAttachVolumeToVM(work.getVmId(), work.getVolumeId(), work.getDeviceId());