Skip to content

Commit 0f0a1ba

Browse files
committed
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]>
1 parent 9967bb3 commit 0f0a1ba

File tree

1 file changed

+72
-36
lines changed

1 file changed

+72
-36
lines changed

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

Lines changed: 72 additions & 36 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,86 @@ 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) {
2410+
if (existingVolumeOfVm != null) {
2411+
if (s_logger.isTraceEnabled()) {
2412+
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
24132413
s_logger.trace(String.format(msg,
24142414
volumeToAttach.getName(), volumeToAttach.getUuid(),
24152415
vm.getName(), vm.getUuid(),
24162416
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
24172417
existingVolumeOfVm.getPoolId()));
24182418
}
24192419
}
2420+
if (s_logger.isTraceEnabled()) {
2421+
s_logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
2422+
vm.getName(), vm.getUuid(),
2423+
volumeToAttach.getName(), volumeToAttach.getUuid()));
2424+
}
2425+
return null;
2426+
}
24202427

2421-
HypervisorType rootDiskHyperType = vm.getHypervisorType();
2422-
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
2428+
protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
2429+
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
2430+
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
2431+
long podId = vm.getPodIdToDeployIn();
2432+
if (clusterHostId.first() != null) {
2433+
Cluster cluster = clusterDao.findById(clusterHostId.first());
2434+
podId = cluster.getPodId();
2435+
}
2436+
Pod pod = podDao.findById(podId);
2437+
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
2438+
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
2439+
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
2440+
offering.isUseLocalStorage(), offering.isRecreatable(),
2441+
volumeToAttach.getTemplateId());
2442+
StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(), clusterHostId.second(), vm, null);
2443+
if (pool == null) {
2444+
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
2445+
}
2446+
return pool;
2447+
}
24232448

2449+
protected VolumeInfo createVolumeOnSecondaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
24242450
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
2425-
2451+
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
2452+
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
2453+
return newVolumeOnPrimaryStorage;
2454+
}
24262455
//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;
2456+
StoragePool destPrimaryStorage = null;
24282457
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
24292458
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
24302459
if (s_logger.isTraceEnabled() && destPrimaryStorage != null) {
24312460
s_logger.trace(String.format("decided on target storage: %s/%s", destPrimaryStorage.getName(), destPrimaryStorage.getUuid()));
24322461
}
24332462
}
2463+
if (destPrimaryStorage == null) {
2464+
destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
2465+
}
2466+
try {
2467+
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
2468+
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
2469+
}
2470+
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
2471+
vm.getHypervisorType(), destPrimaryStorage);
2472+
} catch (NoTransitionException e) {
2473+
s_logger.debug("Failed to create volume on primary storage", e);
2474+
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
2475+
}
2476+
return newVolumeOnPrimaryStorage;
2477+
}
24342478

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

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-
}
2482+
if (volumeToAttach.isAttachedVM()) {
2483+
throw new CloudRuntimeException("This volume is already attached to a VM.");
24472484
}
24482485

2486+
UserVmVO vm = _userVmDao.findById(vmId);
2487+
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
2488+
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnSecondaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
2489+
24492490
// reload the volume from db
24502491
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
24512492
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
@@ -2463,19 +2504,17 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
24632504
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
24642505

24652506
try {
2507+
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
24662508
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
24672509
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) {
2510+
} catch (ConcurrentOperationException | StorageUnavailableException e) {
24722511
s_logger.debug("move volume failed", e);
24732512
throw new CloudRuntimeException("move volume failed", e);
24742513
}
24752514
}
24762515
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
24772516
// Getting the fresh vm object in case of volume migration to check the current state of VM
2478-
if (moveVolumeNeeded || volumeOnSecondary) {
2517+
if (moveVolumeNeeded) {
24792518
vm = _userVmDao.findById(vmId);
24802519
if (vm == null) {
24812520
throw new InvalidParameterValueException("VM not found.");
@@ -2659,9 +2698,6 @@ private void checkDeviceId(Long deviceId, VolumeInfo volumeToAttach, UserVmVO vm
26592698
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
26602699
throw new InvalidParameterValueException("Vm already has root volume attached to it");
26612700
}
2662-
if (volumeToAttach.getState() == Volume.State.Uploaded) {
2663-
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
2664-
}
26652701
}
26662702
}
26672703

0 commit comments

Comments
 (0)