Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server: fix pod retrieval during volume attach #10324

Merged
merged 8 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 83 additions & 42 deletions server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.Pod;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
Expand All @@ -153,6 +155,7 @@
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
import com.cloud.offering.DiskOffering;
import com.cloud.org.Cluster;
import com.cloud.org.Grouping;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
Expand Down Expand Up @@ -323,6 +326,8 @@
@Inject
private VmWorkJobDao _workJobDao;
@Inject
ClusterDao clusterDao;
@Inject
private ClusterDetailsDao _clusterDetailsDao;
@Inject
private StorageManager storageMgr;
Expand All @@ -346,6 +351,8 @@
protected ProjectManager projectManager;
@Inject
protected StoragePoolDetailsDao storagePoolDetailsDao;
@Inject
HostPodDao podDao;


protected Gson _gson;
Expand Down Expand Up @@ -2380,25 +2387,18 @@
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId());
}

private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);

if (volumeToAttach.isAttachedVM()) {
throw new CloudRuntimeException("This volume is already attached to a VM.");
}

UserVmVO vm = _userVmDao.findById(vmId);
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
VolumeVO existingVolumeOfVm = null;
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
if (rootVolumesOfVm.size() > 1 && template != null && !template.isDeployAsIs()) {
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
} else {
if (!rootVolumesOfVm.isEmpty()) {
existingVolumeOfVm = rootVolumesOfVm.get(0);
} else {
// locate data volume of the vm
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
for (VolumeVO diskVolume : diskVolumesOfVm) {
if (diskVolume.getState() != Volume.State.Allocated) {
existingVolumeOfVm = diskVolume;
Expand All @@ -2407,45 +2407,91 @@
}
}
}
if (s_logger.isTraceEnabled()) {
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
if (existingVolumeOfVm != null) {
s_logger.trace(String.format(msg,
volumeToAttach.getName(), volumeToAttach.getUuid(),
if (existingVolumeOfVm == null) {
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",

Check warning on line 2412 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2412 was not covered by tests
vm.getName(), vm.getUuid(),
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
existingVolumeOfVm.getPoolId()));
volumeToAttach.getName(), volumeToAttach.getUuid()));

Check warning on line 2414 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2414 was not covered by tests
}
return null;
}

HypervisorType rootDiskHyperType = vm.getHypervisorType();
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());

if (s_logger.isTraceEnabled()) {
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
s_logger.trace(String.format(msg,
volumeToAttach.getName(), volumeToAttach.getUuid(),
vm.getName(), vm.getUuid(),
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
existingVolumeOfVm.getPoolId()));

Check warning on line 2424 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java#L2419-L2424

Added lines #L2419 - L2424 were not covered by tests
}
return existingVolumeOfVm;
}

protected StoragePool getSuitablePoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
Long podId = vm.getPodIdToDeployIn();
if (clusterHostId.first() != null) {
Cluster cluster = clusterDao.findById(clusterHostId.first());
podId = cluster.getPodId();
}
Pod pod = podDao.findById(podId);
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
offering.isUseLocalStorage(), offering.isRecreatable(),
volumeToAttach.getTemplateId());
diskProfile.setHyperType(vm.getHypervisorType());
return _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(),
clusterHostId.second(), vm, Collections.emptySet());
}

protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;

boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
return newVolumeOnPrimaryStorage;
}
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
StoragePoolVO destPrimaryStorage = null;
StoragePool destPrimaryStorage = null;
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
if (s_logger.isTraceEnabled() && destPrimaryStorage != null) {
s_logger.trace(String.format("decided on target storage: %s/%s", destPrimaryStorage.getName(), destPrimaryStorage.getUuid()));
}
}

boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;

if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
try {
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
if (destPrimaryStorage == null) {
destPrimaryStorage = getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
if (destPrimaryStorage == null) {
if (Volume.State.Allocated.equals(volumeToAttach.getState()) && State.Stopped.equals(vm.getState())) {
return newVolumeOnPrimaryStorage;
}
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage", e);
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
}
}
try {
if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
}
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
vm.getHypervisorType(), destPrimaryStorage);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage", e);
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
}
return newVolumeOnPrimaryStorage;
}

private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);

Check warning on line 2485 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java#L2484-L2485

Added lines #L2484 - L2485 were not covered by tests

if (volumeToAttach.isAttachedVM()) {
throw new CloudRuntimeException("This volume is already attached to a VM.");

Check warning on line 2488 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2488 was not covered by tests
}

UserVmVO vm = _userVmDao.findById(vmId);
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);

Check warning on line 2493 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java#L2491-L2493

Added lines #L2491 - L2493 were not covered by tests

// reload the volume from db
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
Expand All @@ -2463,19 +2509,17 @@
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());

try {
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());

Check warning on line 2512 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2512 was not covered by tests
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
volumeToAttachHyperType);
} catch (ConcurrentOperationException e) {
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
} catch (StorageUnavailableException e) {
} catch (ConcurrentOperationException | StorageUnavailableException e) {

Check warning on line 2515 in server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

View check run for this annotation

Codecov / codecov/patch

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

Added line #L2515 was not covered by tests
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
}
}
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
// Getting the fresh vm object in case of volume migration to check the current state of VM
if (moveVolumeNeeded || volumeOnSecondary) {
if (moveVolumeNeeded) {
vm = _userVmDao.findById(vmId);
if (vm == null) {
throw new InvalidParameterValueException("VM not found.");
Expand Down Expand Up @@ -2659,9 +2703,6 @@
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
throw new InvalidParameterValueException("Vm already has root volume attached to it");
}
if (volumeToAttach.getState() == Volume.State.Uploaded) {
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
}
}
}

Expand Down
Loading
Loading