Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class MigrateVolumeCommand extends Command {
String attachedVmName;
Volume.Type volumeType;
String hostGuidInTargetCluster;
Long newReadRateIops;
Long newWriteRateIops;

private DataTO srcData;
private DataTO destData;
Expand Down Expand Up @@ -150,4 +152,20 @@ public int getWaitInMillSeconds() {
public boolean isReconcile() {
return true;
}

public Long getNewWriteRateIops() {
return newWriteRateIops;
}

public void setNewWriteRateIops(Long newWriteRateIops) {
this.newWriteRateIops = newWriteRateIops;
}

public Long getNewReadRateIops() {
return newReadRateIops;
}

public void setNewReadRateIops(Long newReadRateIops) {
this.newReadRateIops = newReadRateIops;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public class ResizeVolumeCommand extends Command {
private boolean shrinkOk;
private String vmInstance;
private String chainInfo;
private Long newReadRateIops;
private Long newWriteRateIops;

/* For managed storage */
private boolean managed;
Expand Down Expand Up @@ -70,11 +72,17 @@ public ResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, L
public ResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance,
boolean isManaged, String iScsiName) {
this(path, pool, currentSize, newSize, shrinkOk, vmInstance);

this.iScsiName = iScsiName;
this.managed = isManaged;
}

public ResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance,
String chainInfo, byte[] passphrase, String encryptFormat, Long newReadRateIops, Long newWriteRateIops) {
this(path, pool, currentSize, newSize, shrinkOk, vmInstance, chainInfo, passphrase, encryptFormat);
this.newReadRateIops = newReadRateIops;
this.newWriteRateIops = newWriteRateIops;
}

public String getPath() {
return path;
}
Expand Down Expand Up @@ -120,4 +128,12 @@ public void clearPassphrase() {
public boolean executeInSequence() {
return false;
}

public Long getNewReadRateIops() {
return newReadRateIops;
}

public Long getNewWriteRateIops() {
return newWriteRateIops;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId,

VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, UserVm vm) throws StorageUnavailableException;

Volume migrateVolume(Volume volume, StoragePool destPool) throws StorageUnavailableException;
Volume migrateVolume(Volume volume, StoragePool destPool, DiskOffering newDiskOffering) throws StorageUnavailableException;

Volume liveMigrateVolume(Volume volume, StoragePool destPool);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ public class VmWorkResizeVolume extends VmWork {
private Integer newHypervisorSnapshotReserve;
private Long newServiceOfferingId;
private boolean shrinkOk;
private Long newReadRateIops;
private Long newWriteRateIops;

public VmWorkResizeVolume(long userId, long accountId, long vmId, String handlerName, long volumeId, long currentSize, long newSize,
Long newMinIops, Long newMaxIops, Integer newHypervisorSnapshotReserve, Long newServiceOfferingId, boolean shrinkOk) {
Long newMinIops, Long newMaxIops, Integer newHypervisorSnapshotReserve, Long newServiceOfferingId, boolean shrinkOk, Long newReadRateIops, Long newWriteRateIops) {
super(userId, accountId, vmId, handlerName);

this.volumeId = volumeId;
Expand All @@ -40,6 +42,8 @@ public VmWorkResizeVolume(long userId, long accountId, long vmId, String handler
this.newHypervisorSnapshotReserve = newHypervisorSnapshotReserve;
this.newServiceOfferingId = newServiceOfferingId;
this.shrinkOk = shrinkOk;
this.newReadRateIops = newReadRateIops;
this.newWriteRateIops = newWriteRateIops;
}

public long getVolumeId() {
Expand Down Expand Up @@ -71,4 +75,12 @@ public boolean isShrinkOk() {
}

public Integer getNewHypervisorSnapshotReserve() { return newHypervisorSnapshotReserve; }

public Long getNewReadRateIops() {
return newReadRateIops;
}

public Long getNewWriteRateIops() {
return newWriteRateIops;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ public VolumeInfo moveVolume(VolumeInfo volumeInfo, long destPoolDcId, Long dest
throw new CloudRuntimeException(String.format("Failed to find a storage pool with enough capacity to move the volume [%s] to.", volumeToString));
}

Volume newVol = migrateVolume(volumeInfo, destPool);
Volume newVol = migrateVolume(volumeInfo, destPool, diskOffering);
return volFactory.getVolume(newVol.getId());
}

Expand Down Expand Up @@ -1419,13 +1419,14 @@ private void checkConcurrentJobsPerDatastoreThreshhold(final StoragePool destPoo

@Override
@DB
public Volume migrateVolume(Volume volume, StoragePool destPool) throws StorageUnavailableException {
public Volume migrateVolume(Volume volume, StoragePool destPool, DiskOffering newDiskOffering) throws StorageUnavailableException {
String volumeToString = getVolumeIdentificationInfos(volume);

VolumeInfo vol = volFactory.getVolume(volume.getId());
if (vol == null) {
throw new CloudRuntimeException(String.format("Volume migration failed because volume [%s] is null.", volumeToString));
}
vol.addPayload(newDiskOffering);
if (destPool == null) {
throw new CloudRuntimeException("Volume migration failed because the destination storage pool is not available.");
}
Expand Down Expand Up @@ -1554,7 +1555,7 @@ public boolean storageMigration(VirtualMachineProfile vm, Map<Volume, StoragePoo
}
logger.debug("Offline VM migration was not done up the stack in VirtualMachineManager. Trying to migrate the VM here.");
for (Map.Entry<Volume, StoragePool> entry : volumeStoragePoolMap.entrySet()) {
Volume result = migrateVolume(entry.getKey(), entry.getValue());
Volume result = migrateVolume(entry.getKey(), entry.getValue(), null);
if (result == null) {
return false;
}
Expand Down Expand Up @@ -2019,7 +2020,7 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto
} else if (task.type == VolumeTaskType.MIGRATE) {
store = (PrimaryDataStore) dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
updateVolumeSize(store, task.volume);
vol = migrateVolume(task.volume, store);
vol = migrateVolume(task.volume, store, null);
} else if (task.type == VolumeTaskType.RECREATE) {
Pair<VolumeVO, DataStore> result = recreateVolume(task.volume, vm, dest);
store = (PrimaryDataStore) dataStoreMgr.getDataStore(result.second().getId(), DataStoreRole.Primary);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ protected Answer migrateVolumeToPool(DataObject srcData, DataObject destData) {
StoragePool srcPool = (StoragePool)dataStoreMgr.getDataStore(srcData.getDataStore().getId(), DataStoreRole.Primary);
StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary);
MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool, volume.getAttachedVmName(), volume.getVolumeType(), waitInterval, volume.getChainInfo());

if (srcPool.getParent() != 0) {
command.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.UUID;
import java.util.stream.Collectors;

import com.vmware.vim25.StorageIOAllocationInfo;
import com.cloud.agent.api.CleanupVMCommand;
import javax.naming.ConfigurationException;
import javax.xml.datatype.XMLGregorianCalendar;
Expand Down Expand Up @@ -78,6 +79,7 @@
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.logging.log4j.ThreadContext;
import org.apache.commons.lang3.ObjectUtils;
import org.joda.time.Duration;

import com.cloud.agent.IAgentControl;
Expand Down Expand Up @@ -874,6 +876,8 @@ private Answer execute(ResizeVolumeCommand cmd) {
boolean managed = cmd.isManaged();
String poolUUID = cmd.getPoolUuid();
String chainInfo = cmd.getChainInfo();
Long newReadRateIops = cmd.getNewReadRateIops();
Long newWriteRateIops = cmd.getNewWriteRateIops();
boolean useWorkerVm = false;

VmwareContext context = getServiceContext();
Expand All @@ -888,8 +892,6 @@ private Answer execute(ResizeVolumeCommand cmd) {
oldSize / (float) ResourceType.bytesToMiB, newSize / (float) ResourceType.bytesToMiB, vmName);
logger.error(errorMsg);
throw new Exception(errorMsg);
} else if (newSize == oldSize) {
return new ResizeVolumeAnswer(cmd, true, "success", newSize * ResourceType.bytesToKiB);
}

if (vmName.equalsIgnoreCase("none")) {
Expand Down Expand Up @@ -983,6 +985,8 @@ private Answer execute(ResizeVolumeCommand cmd) {
VirtualDisk disk = getDiskAfterResizeDiskValidations(vmMo, path);
String vmdkAbsFile = VmwareHelper.getAbsoluteVmdkFile(disk);

setDiskIops(disk, newReadRateIops, newWriteRateIops);

if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) {
vmMo.updateAdapterTypeIfRequired(vmdkAbsFile);
}
Expand Down Expand Up @@ -1034,6 +1038,22 @@ private Answer execute(ResizeVolumeCommand cmd) {
}
}

/**
* Sets the disk IOPS which is the sum of read rate IOPS and write rate IOPS; if they are null, the IOPS limit is set to -1 (unlimited).
*/
private void setDiskIops(VirtualDisk disk, Long newReadRateIops, Long newWriteRateIops) {
StorageIOAllocationInfo storageIOAllocation = new StorageIOAllocationInfo();
Long iops = -1L;

if (ObjectUtils.allNotNull(newReadRateIops, newWriteRateIops) && newReadRateIops > 0 && newWriteRateIops > 0) {
iops = newReadRateIops + newWriteRateIops;
}

storageIOAllocation.setLimit(iops);
logger.debug(LogUtils.logGsonWithoutException("Setting [%s] as the IOPS limit of disk [%s].", iops == -1L ? "unlimited" : iops, disk));
disk.setStorageIOAllocation(storageIOAllocation);
}

private VirtualDisk getDiskAfterResizeDiskValidations(VirtualMachineMO vmMo, String volumePath) throws Exception {
Pair<VirtualDisk, String> vdisk = vmMo.getDiskDevice(volumePath);
if (vdisk == null) {
Expand Down Expand Up @@ -4426,7 +4446,7 @@ protected GetVolumeStatsAnswer execute(GetVolumeStatsCommand cmd) {
for (String diskPath : disks) {
DatastoreFile file = new DatastoreFile(diskPath);
VirtualMachineMO vmMo = dcMo.findVm(file.getDir());
Pair<VirtualDisk, String> vds = vmMo.getDiskDevice(file.getFileName(), true);
Pair<VirtualDisk, String> vds = vmMo.getDiskDevice(file.getFileName(), true, false);
long virtualsize = vds.first().getCapacityInKB() * 1024;
long physicalsize = primaryStorageDatastoreMo.fileDiskSize(file.getPath());
if (statEntry.containsKey(chainInfo)) {
Expand Down Expand Up @@ -5179,6 +5199,8 @@ private Answer execute(MigrateVolumeCommand cmd) {
volumePath = vmMo.getVmdkFileBaseName(disk);
}
}

setDiskIops(cmd, vmMo, volumePath);
VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder();
chainInfo = _gson.toJson(diskInfoBuilder.getDiskInfoByBackingFileBaseName(volumePath, targetDsMo.getName()));
MigrateVolumeAnswer answer = new MigrateVolumeAnswer(cmd, true, null, volumePath);
Expand All @@ -5191,6 +5213,40 @@ private Answer execute(MigrateVolumeCommand cmd) {
}
}

/**
* Sets the disk IOPS limitation, if the {@link MigrateVolumeCommand} did not specify this limitation, then it is set to -1 (unlimited).
*/
private void setDiskIops(MigrateVolumeCommand cmd, VirtualMachineMO vmMo, String volumePath) throws Exception {
Comment on lines +5217 to +5219
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure that every time the volume is migrated we inform the IOPS limitation?

Long newIops = -1L;
Long readRateIops = cmd.getNewReadRateIops();
Long writeRateIops = cmd.getNewWriteRateIops();

if (ObjectUtils.allNotNull(readRateIops, writeRateIops) && readRateIops > 0 && writeRateIops > 0) {
newIops = readRateIops + writeRateIops;
}

VirtualDisk disk = vmMo.getDiskDevice(volumePath, true, true).first();

try {
logger.debug(LogUtils.logGsonWithoutException("Trying to change disk [%s] IOPS to [%s].", disk, newIops));
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();

StorageIOAllocationInfo storageIOAllocation = new StorageIOAllocationInfo();
storageIOAllocation.setLimit(newIops);
disk.setStorageIOAllocation(storageIOAllocation);

deviceConfigSpec.setDevice(disk);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.EDIT);
vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
vmMo.configureVm(vmConfigSpec);
} catch (Exception e) {
String vmwareDocumentation = "https://kb.vmware.com/s/article/68164";
logger.error(LogUtils.logGsonWithoutException("Failed to change disk [%s] IOPS to [%s] due to [%s]. This happens when the disk controller is IDE." +
" Please read this documentation for more information: [%s]. ", disk, newIops, e.getMessage(), vmwareDocumentation), e);
}
}

private Pair<VirtualDisk, String> getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception {
Pair<VirtualDisk, String> deviceInfo = vmMo.getDiskDevice(srcDiskName);
if (deviceInfo == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.inject.Inject;

import com.cloud.agent.api.to.DiskTO;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.Storage;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
Expand Down Expand Up @@ -251,6 +252,18 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, As
, sourcePool
, targetPool
, hostIdForVmAndHostGuidInTargetCluster.second(), ((VolumeObjectTO) srcData.getTO()).getChainInfo());

VolumeInfo volume = (VolumeInfo) srcData;
if (volume.getpayload() instanceof DiskOfferingVO) {
DiskOfferingVO offering = (DiskOfferingVO) volume.getpayload();

Long offeringReadRateIops = offering.getIopsReadRate();
Long offeringWriteRateIops = offering.getIopsWriteRate();

cmd.setNewReadRateIops(offeringReadRateIops);
cmd.setNewWriteRateIops(offeringWriteRateIops);
}

if (sourcePool.getParent() != 0) {
cmd.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> cal
endpointsToRunResize = new long[] {ep.getId()};
}
ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(),
resizeParameter.newSize, resizeParameter.shrinkOk, resizeParameter.instanceName, vol.getChainInfo(), vol.getPassphrase(), vol.getEncryptFormat());
resizeParameter.newSize, resizeParameter.shrinkOk, resizeParameter.instanceName, vol.getChainInfo(), vol.getPassphrase(), vol.getEncryptFormat(),
resizeParameter.newReadRateIops, resizeParameter.newWriteRateIops);
if (pool.getParent() != 0) {
resizeCmd.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class ResizeVolumePayload {
public final Long newMinIops;
public final Long newMaxIops;
public Long newDiskOfferingId;
public Long newReadRateIops;
public Long newWriteRateIops;
public final Integer newHypervisorSnapshotReserve;
public final boolean shrinkOk;
public final String instanceName;
Expand All @@ -41,10 +43,12 @@ public ResizeVolumePayload(Long newSize, Long newMinIops, Long newMaxIops, Integ
this.newDiskOfferingId = null;
}

public ResizeVolumePayload(Long newSize, Long newMinIops, Long newMaxIops, Long newDiskOfferingId, Integer newHypervisorSnapshotReserve, boolean shrinkOk,
String instanceName, long[] hosts, boolean isManaged) {
public ResizeVolumePayload(Long newSize, Long newMinIops, Long newMaxIops, Long newDiskOfferingId, Long newReadRateIops, Long newWriteRateIops,
Integer newHypervisorSnapshotReserve, boolean shrinkOk, String instanceName, long[] hosts, boolean isManaged) {
this(newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, shrinkOk, instanceName, hosts, isManaged);
this.newDiskOfferingId = newDiskOfferingId;
this.newReadRateIops = newReadRateIops;
this.newWriteRateIops = newWriteRateIops;
}

public Long getNewDiskOfferingId() {
Expand Down
Loading
Loading