Skip to content

Commit 1c84ce4

Browse files
server: fix attach uploaded volume (#10267)
* server: fix attach uploaded volume Fixes #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 f652ad0 commit 1c84ce4

File tree

2 files changed

+330
-42
lines changed

2 files changed

+330
-42
lines changed

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

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,9 @@
133133
import com.cloud.dc.DataCenter;
134134
import com.cloud.dc.DataCenterVO;
135135
import com.cloud.dc.Pod;
136+
import com.cloud.dc.dao.ClusterDao;
136137
import com.cloud.dc.dao.DataCenterDao;
138+
import com.cloud.dc.dao.HostPodDao;
137139
import com.cloud.domain.Domain;
138140
import com.cloud.domain.dao.DomainDao;
139141
import com.cloud.event.ActionEvent;
@@ -153,6 +155,7 @@
153155
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
154156
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
155157
import com.cloud.offering.DiskOffering;
158+
import com.cloud.org.Cluster;
156159
import com.cloud.org.Grouping;
157160
import com.cloud.projects.Project;
158161
import com.cloud.projects.ProjectManager;
@@ -323,6 +326,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
323326
@Inject
324327
private VmWorkJobDao _workJobDao;
325328
@Inject
329+
ClusterDao clusterDao;
330+
@Inject
326331
private ClusterDetailsDao _clusterDetailsDao;
327332
@Inject
328333
private StorageManager storageMgr;
@@ -346,6 +351,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
346351
protected ProjectManager projectManager;
347352
@Inject
348353
protected StoragePoolDetailsDao storagePoolDetailsDao;
354+
@Inject
355+
HostPodDao podDao;
349356

350357

351358
protected Gson _gson;
@@ -2380,25 +2387,18 @@ public Volume attachVolumeToVM(AttachVolumeCmd command) {
23802387
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId());
23812388
}
23822389

2383-
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
2384-
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
2385-
2386-
if (volumeToAttach.isAttachedVM()) {
2387-
throw new CloudRuntimeException("This volume is already attached to a VM.");
2388-
}
2389-
2390-
UserVmVO vm = _userVmDao.findById(vmId);
2390+
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
23912391
VolumeVO existingVolumeOfVm = null;
23922392
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
2393-
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
2393+
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
23942394
if (rootVolumesOfVm.size() > 1 && template != null && !template.isDeployAsIs()) {
23952395
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
23962396
} else {
23972397
if (!rootVolumesOfVm.isEmpty()) {
23982398
existingVolumeOfVm = rootVolumesOfVm.get(0);
23992399
} else {
24002400
// locate data volume of the vm
2401-
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
2401+
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
24022402
for (VolumeVO diskVolume : diskVolumesOfVm) {
24032403
if (diskVolume.getState() != Volume.State.Allocated) {
24042404
existingVolumeOfVm = diskVolume;
@@ -2407,45 +2407,89 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
24072407
}
24082408
}
24092409
}
2410-
if (s_logger.isTraceEnabled()) {
2411-
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
2412-
if (existingVolumeOfVm != null) {
2413-
s_logger.trace(String.format(msg,
2414-
volumeToAttach.getName(), volumeToAttach.getUuid(),
2410+
if (existingVolumeOfVm == null) {
2411+
if (s_logger.isTraceEnabled()) {
2412+
s_logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
24152413
vm.getName(), vm.getUuid(),
2416-
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
2417-
existingVolumeOfVm.getPoolId()));
2414+
volumeToAttach.getName(), volumeToAttach.getUuid()));
24182415
}
2416+
return null;
24192417
}
2420-
2421-
HypervisorType rootDiskHyperType = vm.getHypervisorType();
2422-
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
2423-
2418+
if (s_logger.isTraceEnabled()) {
2419+
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
2420+
s_logger.trace(String.format(msg,
2421+
volumeToAttach.getName(), volumeToAttach.getUuid(),
2422+
vm.getName(), vm.getUuid(),
2423+
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
2424+
existingVolumeOfVm.getPoolId()));
2425+
}
2426+
return existingVolumeOfVm;
2427+
}
2428+
2429+
protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
2430+
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
2431+
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
2432+
long podId = vm.getPodIdToDeployIn();
2433+
if (clusterHostId.first() != null) {
2434+
Cluster cluster = clusterDao.findById(clusterHostId.first());
2435+
podId = cluster.getPodId();
2436+
}
2437+
Pod pod = podDao.findById(podId);
2438+
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
2439+
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
2440+
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
2441+
offering.isUseLocalStorage(), offering.isRecreatable(),
2442+
volumeToAttach.getTemplateId());
2443+
diskProfile.setHyperType(vm.getHypervisorType());
2444+
StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(),
2445+
clusterHostId.second(), vm, Collections.emptySet());
2446+
if (pool == null) {
2447+
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
2448+
}
2449+
return pool;
2450+
}
2451+
2452+
protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
24242453
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
2425-
2454+
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
2455+
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
2456+
return newVolumeOnPrimaryStorage;
2457+
}
24262458
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
2427-
StoragePoolVO destPrimaryStorage = null;
2459+
StoragePool destPrimaryStorage = null;
24282460
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
24292461
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
24302462
if (s_logger.isTraceEnabled() && destPrimaryStorage != null) {
24312463
s_logger.trace(String.format("decided on target storage: %s/%s", destPrimaryStorage.getName(), destPrimaryStorage.getUuid()));
24322464
}
24332465
}
2466+
if (destPrimaryStorage == null) {
2467+
destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
2468+
}
2469+
try {
2470+
if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
2471+
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
2472+
}
2473+
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
2474+
vm.getHypervisorType(), destPrimaryStorage);
2475+
} catch (NoTransitionException e) {
2476+
s_logger.debug("Failed to create volume on primary storage", e);
2477+
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
2478+
}
2479+
return newVolumeOnPrimaryStorage;
2480+
}
24342481

2435-
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
2482+
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
2483+
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
24362484

2437-
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
2438-
try {
2439-
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
2440-
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
2441-
}
2442-
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
2443-
} catch (NoTransitionException e) {
2444-
s_logger.debug("Failed to create volume on primary storage", e);
2445-
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
2446-
}
2485+
if (volumeToAttach.isAttachedVM()) {
2486+
throw new CloudRuntimeException("This volume is already attached to a VM.");
24472487
}
24482488

2489+
UserVmVO vm = _userVmDao.findById(vmId);
2490+
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
2491+
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
2492+
24492493
// reload the volume from db
24502494
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
24512495
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
@@ -2463,19 +2507,17 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
24632507
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
24642508

24652509
try {
2510+
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
24662511
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
24672512
volumeToAttachHyperType);
2468-
} catch (ConcurrentOperationException e) {
2469-
s_logger.debug("move volume failed", e);
2470-
throw new CloudRuntimeException("move volume failed", e);
2471-
} catch (StorageUnavailableException e) {
2513+
} catch (ConcurrentOperationException | StorageUnavailableException e) {
24722514
s_logger.debug("move volume failed", e);
24732515
throw new CloudRuntimeException("move volume failed", e);
24742516
}
24752517
}
24762518
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
24772519
// Getting the fresh vm object in case of volume migration to check the current state of VM
2478-
if (moveVolumeNeeded || volumeOnSecondary) {
2520+
if (moveVolumeNeeded) {
24792521
vm = _userVmDao.findById(vmId);
24802522
if (vm == null) {
24812523
throw new InvalidParameterValueException("VM not found.");
@@ -2659,9 +2701,6 @@ private void checkDeviceId(Long deviceId, VolumeInfo volumeToAttach, UserVmVO vm
26592701
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
26602702
throw new InvalidParameterValueException("Vm already has root volume attached to it");
26612703
}
2662-
if (volumeToAttach.getState() == Volume.State.Uploaded) {
2663-
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
2664-
}
26652704
}
26662705
}
26672706

0 commit comments

Comments
 (0)