Skip to content

Commit d0929b1

Browse files
shwstpprborisstoyanov
authored andcommitted
server: fix attach uploaded volume (apache#10267)
* server: fix attach uploaded volume Fixes apache#10120 When an uploaded volume is attached to a VM for which no existing volume can be found it was resulting in error. For such volumes, server needs to find a suitable pool first and copy them to the pool from secondary store. Signed-off-by: Abhishek Kumar <[email protected]> * fix Signed-off-by: Abhishek Kumar <[email protected]> * fix Signed-off-by: Abhishek Kumar <[email protected]> * add unit tests Signed-off-by: Abhishek Kumar <[email protected]> --------- Signed-off-by: Abhishek Kumar <[email protected]> Co-authored-by: Boris Stoyanov - a.k.a Bobby <[email protected]>
1 parent 2094e0a commit d0929b1

File tree

2 files changed

+368
-44
lines changed

2 files changed

+368
-44
lines changed

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@
135135
import com.cloud.dc.DataCenter;
136136
import com.cloud.dc.DataCenterVO;
137137
import com.cloud.dc.Pod;
138+
import com.cloud.dc.dao.ClusterDao;
138139
import com.cloud.dc.dao.DataCenterDao;
140+
import com.cloud.dc.dao.HostPodDao;
139141
import com.cloud.domain.Domain;
140142
import com.cloud.domain.dao.DomainDao;
141143
import com.cloud.event.ActionEvent;
@@ -155,6 +157,7 @@
155157
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
156158
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
157159
import com.cloud.offering.DiskOffering;
160+
import com.cloud.org.Cluster;
158161
import com.cloud.org.Grouping;
159162
import com.cloud.projects.Project;
160163
import com.cloud.projects.ProjectManager;
@@ -324,6 +327,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
324327
@Inject
325328
private VmWorkJobDao _workJobDao;
326329
@Inject
330+
ClusterDao clusterDao;
331+
@Inject
327332
private ClusterDetailsDao _clusterDetailsDao;
328333
@Inject
329334
private StorageManager storageMgr;
@@ -349,6 +354,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
349354
protected StoragePoolDetailsDao storagePoolDetailsDao;
350355
@Inject
351356
private BackupDao backupDao;
357+
@Inject
358+
HostPodDao podDao;
352359

353360

354361
protected Gson _gson;
@@ -2476,25 +2483,18 @@ public Volume attachVolumeToVM(AttachVolumeCmd command) {
24762483
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId(), false);
24772484
}
24782485

2479-
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
2480-
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
2481-
2482-
if (volumeToAttach.isAttachedVM()) {
2483-
throw new CloudRuntimeException("This volume is already attached to a VM.");
2484-
}
2485-
2486-
UserVmVO vm = _userVmDao.findById(vmId);
2486+
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
24872487
VolumeVO existingVolumeOfVm = null;
24882488
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
2489-
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
2489+
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
24902490
if (rootVolumesOfVm.size() > 1 && template != null && !template.isDeployAsIs()) {
24912491
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
24922492
} else {
24932493
if (!rootVolumesOfVm.isEmpty()) {
24942494
existingVolumeOfVm = rootVolumesOfVm.get(0);
24952495
} else {
24962496
// locate data volume of the vm
2497-
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
2497+
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
24982498
for (VolumeVO diskVolume : diskVolumesOfVm) {
24992499
if (diskVolume.getState() != Volume.State.Allocated) {
25002500
existingVolumeOfVm = diskVolume;
@@ -2503,40 +2503,94 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
25032503
}
25042504
}
25052505
}
2506-
if (logger.isTraceEnabled()) {
2507-
if (existingVolumeOfVm != null) {
2508-
logger.trace("attaching volume {} to a VM {} with an existing volume {} on primary storage {}",
2509-
volumeToAttach, vm, existingVolumeOfVm, _storagePoolDao.findById(existingVolumeOfVm.getPoolId()));
2506+
if (existingVolumeOfVm == null) {
2507+
if (logger.isTraceEnabled()) {
2508+
logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
2509+
vm.getName(), vm.getUuid(),
2510+
volumeToAttach.getName(), volumeToAttach.getUuid()));
25102511
}
2512+
return null;
25112513
}
2512-
2513-
HypervisorType rootDiskHyperType = vm.getHypervisorType();
2514-
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
2515-
2514+
if (logger.isTraceEnabled()) {
2515+
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
2516+
logger.trace(String.format(msg,
2517+
volumeToAttach.getName(), volumeToAttach.getUuid(),
2518+
vm.getName(), vm.getUuid(),
2519+
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
2520+
existingVolumeOfVm.getPoolId()));
2521+
}
2522+
return existingVolumeOfVm;
2523+
}
2524+
2525+
protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
2526+
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
2527+
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
2528+
long podId = vm.getPodIdToDeployIn();
2529+
if (clusterHostId.first() != null) {
2530+
Cluster cluster = clusterDao.findById(clusterHostId.first());
2531+
podId = cluster.getPodId();
2532+
}
2533+
Pod pod = podDao.findById(podId);
2534+
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
2535+
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
2536+
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
2537+
offering.isUseLocalStorage(), offering.isRecreatable(),
2538+
volumeToAttach.getTemplateId());
2539+
diskProfile.setHyperType(vm.getHypervisorType());
2540+
StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(),
2541+
clusterHostId.second(), vm, Collections.emptySet());
2542+
if (pool == null) {
2543+
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
2544+
}
2545+
return pool;
2546+
}
2547+
2548+
protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
25162549
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
2517-
2550+
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
2551+
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
2552+
return newVolumeOnPrimaryStorage;
2553+
}
25182554
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
2519-
StoragePoolVO destPrimaryStorage = null;
2555+
StoragePool destPrimaryStorage = null;
25202556
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
25212557
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
25222558
if (logger.isTraceEnabled() && destPrimaryStorage != null) {
25232559
logger.trace("decided on target storage: {}", destPrimaryStorage);
25242560
}
25252561
}
2526-
2527-
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
2528-
2529-
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
2530-
try {
2531-
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
2532-
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
2562+
if (destPrimaryStorage == null) {
2563+
destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
2564+
if (destPrimaryStorage == null) {
2565+
if (Volume.State.Allocated.equals(volumeToAttach.getState()) && State.Stopped.equals(vm.getState())) {
2566+
return newVolumeOnPrimaryStorage;
25332567
}
2534-
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
2535-
} catch (NoTransitionException e) {
2536-
logger.debug("Failed to create volume on primary storage", e);
2537-
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
2568+
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
2569+
}
2570+
}
2571+
try {
2572+
if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
2573+
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
25382574
}
2575+
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
2576+
vm.getHypervisorType(), destPrimaryStorage);
2577+
} catch (NoTransitionException e) {
2578+
s_logger.debug("Failed to create volume on primary storage", e);
2579+
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
25392580
}
2581+
return newVolumeOnPrimaryStorage;
2582+
}
2583+
2584+
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
2585+
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
2586+
2587+
if (volumeToAttach.isAttachedVM()) {
2588+
throw new CloudRuntimeException("This volume is already attached to a VM.");
2589+
}
2590+
2591+
UserVmVO vm = _userVmDao.findById(vmId);
2592+
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
2593+
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
25402594

25412595
// reload the volume from db
25422596
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
@@ -2555,19 +2609,17 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
25552609
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
25562610

25572611
try {
2612+
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
25582613
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
25592614
volumeToAttachHyperType);
2560-
} catch (ConcurrentOperationException e) {
2561-
logger.debug("move volume failed", e);
2562-
throw new CloudRuntimeException("move volume failed", e);
2563-
} catch (StorageUnavailableException e) {
2615+
} catch (ConcurrentOperationException | StorageUnavailableException e) {
25642616
logger.debug("move volume failed", e);
25652617
throw new CloudRuntimeException("move volume failed", e);
25662618
}
25672619
}
25682620
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
25692621
// Getting the fresh vm object in case of volume migration to check the current state of VM
2570-
if (moveVolumeNeeded || volumeOnSecondary) {
2622+
if (moveVolumeNeeded) {
25712623
vm = _userVmDao.findById(vmId);
25722624
if (vm == null) {
25732625
throw new InvalidParameterValueException("VM not found.");
@@ -2752,9 +2804,6 @@ private void checkDeviceId(Long deviceId, VolumeInfo volumeToAttach, UserVmVO vm
27522804
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
27532805
throw new InvalidParameterValueException("Vm already has root volume attached to it");
27542806
}
2755-
if (volumeToAttach.getState() == Volume.State.Uploaded) {
2756-
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
2757-
}
27582807
}
27592808
}
27602809

0 commit comments

Comments
 (0)