Skip to content
Closed
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
25 changes: 12 additions & 13 deletions compute/src/main/java/org/zstack/compute/vm/VmAllocateHostFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,33 +52,23 @@ public class VmAllocateHostFlow implements Flow {
@Autowired
protected VmInstanceExtensionPointEmitter extEmitter;

private long getTotalDataDiskSize(VmInstanceSpec spec) {
long size = 0;
for (DiskOfferingInventory dinv : spec.getDataDiskOfferings()) {
size += dinv.getDiskSize();
}
return size;
}

protected AllocateHostMsg prepareMsg(VmInstanceSpec spec) {
DesignatedAllocateHostMsg msg = new DesignatedAllocateHostMsg();

List<DiskOfferingInventory> diskOfferings = new ArrayList<>();
ImageInventory image = spec.getImageSpec().getInventory();
long diskSize;
long rootDiskSize;
if (image == null || (image.getMediaType() != null && image.getMediaType().equals(ImageMediaType.ISO.toString()))) {
DiskOfferingVO dvo = dbf.findByUuid(spec.getRootDiskOffering().getUuid(), DiskOfferingVO.class);
diskSize = dvo.getDiskSize();
rootDiskSize = dvo.getDiskSize();
diskOfferings.add(DiskOfferingInventory.valueOf(dvo));
} else {
diskSize = image.getSize();
rootDiskSize = image.getSize();
}
diskSize += getTotalDataDiskSize(spec);
diskOfferings.addAll(spec.getDataDiskOfferings());
msg.setSoftAvoidHostUuids(spec.getSoftAvoidHostUuids());
msg.setAvoidHostUuids(spec.getAvoidHostUuids());
msg.setDiskOfferings(diskOfferings);
msg.setDiskSize(diskSize);
msg.setCpuCapacity(spec.getVmInventory().getCpuNum());
msg.setMemoryCapacity(spec.getVmInventory().getMemorySize());
msg.setClusterUuids(spec.getRequiredClusterUuids());
Expand Down Expand Up @@ -136,6 +126,15 @@ public String call(L3NetworkInventory arg) {
msg.getRequiredPrimaryStorageUuids().addAll(spec.getDiskAOs().stream()
.map(APICreateVmInstanceMsg.DiskAO::getPrimaryStorageUuid).filter(Objects::nonNull).collect(Collectors.toList()));
}
String rootPsUuid = spec.getCandidatePrimaryStorageUuidsForRootVolume().size() == 1 ?
spec.getCandidatePrimaryStorageUuidsForRootVolume().get(0) : null;
msg.addRequiredDiskCapacity(rootPsUuid, rootDiskSize);

String dataPsUuid = spec.getCandidatePrimaryStorageUuidsForDataVolume().size() == 1 ?
spec.getCandidatePrimaryStorageUuidsForDataVolume().get(0) : null;
for (DiskOfferingInventory dinv : spec.getDataDiskOfferings()) {
msg.addRequiredDiskCapacity(dataPsUuid, dinv.getDiskSize());
}
return msg;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
public class AllocateHostMsg extends NeedReplyMessage {
private long cpuCapacity;
private long memoryCapacity;
private long diskSize;
private String allocatorStrategy;
private List<String> avoidHostUuids;
private List<String> softAvoidHostUuids;
Expand All @@ -27,6 +26,7 @@ public class AllocateHostMsg extends NeedReplyMessage {
private Set<String> requiredPrimaryStorageUuids = new HashSet<>();
// for each set in the list, the primary storage inside is optional
private final List<Set<String>> optionalPrimaryStorageUuids = new ArrayList<>();
private final List<RequiredDiskCapacity> requiredDiskCapacities = new ArrayList<>();
private boolean fullAllocate = true;
private long oldMemoryCapacity = 0;
private AllocationScene allocationScene;
Expand Down Expand Up @@ -70,6 +70,14 @@ public void addRequiredPrimaryStorageUuid(String requiredPrimaryStorageUuid) {
this.requiredPrimaryStorageUuids.add(requiredPrimaryStorageUuid);
}

public List<RequiredDiskCapacity> getRequiredDiskCapacities() {
return requiredDiskCapacities;
}

public void addRequiredDiskCapacity(String primaryStorageUuid, long size) {
requiredDiskCapacities.add(new RequiredDiskCapacity(primaryStorageUuid, size));
}

public String getRequiredBackupStorageUuid() {
return requiredBackupStorageUuid;
}
Expand Down Expand Up @@ -143,11 +151,13 @@ public void setMemoryCapacity(long memoryCapacity) {
}

public long getDiskSize() {
return diskSize;
return requiredDiskCapacities.stream().mapToLong(RequiredDiskCapacity::getSize).sum();
}

// Compatibility entry for callers that cannot determine primary storage yet.
public void setDiskSize(long diskSize) {
this.diskSize = diskSize;
requiredDiskCapacities.clear();
requiredDiskCapacities.add(new RequiredDiskCapacity(null, diskSize));
}

public String getAllocatorStrategy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public class HostAllocatorSpec {
private long cpuCapacity;
private long memoryCapacity;
private List<String> l3NetworkUuids;
private long diskSize;
private String hypervisorType;
private String allocatorStrategy;
private VmInstanceInventory vmInstance;
Expand All @@ -29,6 +28,7 @@ public class HostAllocatorSpec {
private Set<String> requiredPrimaryStorageUuids = new HashSet<>();
// for each set in the list, the primary storage inside is optional
private final List<Set<String>> optionalPrimaryStorageUuids = new ArrayList<>();
private final List<RequiredDiskCapacity> requiredDiskCapacities = new ArrayList<>();
private Map<String, List<String>> backupStoragePrimaryStorageMetrics;
private boolean dryRun;
private List<String> systemTags;
Expand Down Expand Up @@ -89,6 +89,17 @@ public Set<String> getRequiredPrimaryStorageUuids() {
return requiredPrimaryStorageUuids;
}

public List<RequiredDiskCapacity> getRequiredDiskCapacities() {
return requiredDiskCapacities;
}

public void setRequiredDiskCapacities(List<RequiredDiskCapacity> requiredDiskCapacities) {
this.requiredDiskCapacities.clear();
if (requiredDiskCapacities != null) {
this.requiredDiskCapacities.addAll(requiredDiskCapacities);
}
}

public List<Set<String>> getOptionalPrimaryStorageUuids() {
return optionalPrimaryStorageUuids;
}
Expand Down Expand Up @@ -192,11 +203,7 @@ public void setL3NetworkUuids(List<String> l3NetworkUuids) {
}

public long getDiskSize() {
return diskSize;
}

public void setDiskSize(long diskSize) {
this.diskSize = diskSize;
return requiredDiskCapacities.stream().mapToLong(RequiredDiskCapacity::getSize).sum();
}

public String getHypervisorType() {
Expand Down Expand Up @@ -261,7 +268,6 @@ public static HostAllocatorSpec fromAllocationMsg(AllocateHostMsg msg) {
spec.setAvoidHostUuids(msg.getAvoidHostUuids());
spec.setSoftAvoidHostUuids(msg.getSoftAvoidHostUuids());
spec.setCpuCapacity(msg.getCpuCapacity());
spec.setDiskSize(msg.getDiskSize());
spec.setListAllHosts(msg.isListAllHosts());
spec.setDryRun(msg.isDryRun());
spec.setFullAllocate(msg.isFullAllocate());
Expand All @@ -280,6 +286,7 @@ public static HostAllocatorSpec fromAllocationMsg(AllocateHostMsg msg) {
spec.setAllowNoL3Networks(msg.isAllowNoL3Networks());
spec.setRequiredBackupStorageUuid(msg.getRequiredBackupStorageUuid());
spec.setRequiredPrimaryStorageUuids(msg.getRequiredPrimaryStorageUuids());
spec.setRequiredDiskCapacities(msg.getRequiredDiskCapacities());
msg.getOptionalPrimaryStorageUuids().forEach(spec::addOptionalPrimaryStorageUuids);
spec.setAllocationScene(msg.getAllocationScene());
spec.setArchitecture(msg.getArchitecture());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.zstack.header.allocator;

public class RequiredDiskCapacity {
private String primaryStorageUuid;
private long size;

public RequiredDiskCapacity() {
}

public RequiredDiskCapacity(String primaryStorageUuid, long size) {
this.primaryStorageUuid = primaryStorageUuid;
this.size = size;
}

public String getPrimaryStorageUuid() {
return primaryStorageUuid;
}

public void setPrimaryStorageUuid(String primaryStorageUuid) {
this.primaryStorageUuid = primaryStorageUuid;
}

public long getSize() {
return size;
}

public void setSize(long size) {
this.size = size;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.zstack.header.allocator.HostAllocatorFilterExtensionPoint;
import org.zstack.header.allocator.HostAllocatorSpec;
import org.zstack.header.allocator.HostAllocatorStrategyExtensionPoint;
import org.zstack.header.allocator.RequiredDiskCapacity;
import org.zstack.header.errorcode.OperationFailureException;
import org.zstack.header.host.HostInventory;
import org.zstack.header.host.HostVO;
Expand Down Expand Up @@ -109,13 +110,13 @@ public List<HostVO> filterHostCandidates(List<HostVO> candidates, HostAllocatorS
long reservedCapacity = SizeUtils.sizeStringToBytes(PrimaryStorageGlobalConfig.RESERVED_CAPACITY.value());

if (VmOperation.NewCreate.toString().equals(spec.getVmOperation()) || VmOperation.MigrateVolume.toString().equals(spec.getVmOperation())) {
List<String> huuids = getNeedCheckHostLocalStorageList(candidates, spec);
if (huuids.isEmpty()) {
Map<String, Boolean> needCheckLocalStorageHosts = getNeedCheckLocalStorageHosts(candidates, spec);
if (needCheckLocalStorageHosts.isEmpty()) {
return candidates;
}

SimpleQuery<LocalStorageHostRefVO> q = dbf.createQuery(LocalStorageHostRefVO.class);
q.add(LocalStorageHostRefVO_.hostUuid, Op.IN, huuids);
q.add(LocalStorageHostRefVO_.hostUuid, Op.IN, needCheckLocalStorageHosts.keySet());
if (!spec.getRequiredPrimaryStorageUuids().isEmpty()) {
q.add(LocalStorageHostRefVO_.primaryStorageUuid, Op.IN, spec.getRequiredPrimaryStorageUuids());
}
Expand All @@ -126,10 +127,20 @@ public List<HostVO> filterHostCandidates(List<HostVO> candidates, HostAllocatorS
for (LocalStorageHostRefVO ref : refs) {
String huuid = ref.getHostUuid();
String psUuid = ref.getPrimaryStorageUuid();
boolean onlyLocalStorage = needCheckLocalStorageHosts.get(huuid);
long requiredSize = spec.getRequiredDiskCapacities().stream()
.filter(it -> psUuid.equals(it.getPrimaryStorageUuid()) ||
(onlyLocalStorage && it.getPrimaryStorageUuid() == null))
.mapToLong(RequiredDiskCapacity::getSize)
.sum();
if (requiredSize == 0) {
continue;
}

// check primary storage capacity and host physical capacity
boolean capacityChecked = PrimaryStorageCapacityChecker.New(psUuid,
ref.getAvailableCapacity(), ref.getTotalPhysicalCapacity(), ref.getAvailablePhysicalCapacity())
.checkRequiredSize(spec.getDiskSize());
.checkRequiredSize(requiredSize);

if (!capacityChecked) {
addHostPrimaryStorageBlacklist(huuid, psUuid, spec);
Expand Down Expand Up @@ -212,7 +223,7 @@ private void checkLocalStorageForVmStart(VmInstanceInventory vm, List<HostVO> ca
}

/**
* @return hostUuid list
* @return hostUuid and whether the host is attached to local storage only
* <p>
* Just check it :
* The current cluster is mounted only local storage
Expand All @@ -221,7 +232,7 @@ private void checkLocalStorageForVmStart(VmInstanceInventory vm, List<HostVO> ca
* Negative impact
* In the case of local + non-local and no ps specified (non-local is Disconnected/Disabled, or non-local capacity not enough), the allocated host may not have enough disks
*/
private List<String> getNeedCheckHostLocalStorageList(List<HostVO> candidates, HostAllocatorSpec spec) {
private Map<String, Boolean> getNeedCheckLocalStorageHosts(List<HostVO> candidates, HostAllocatorSpec spec) {
boolean isRequireNonLocalStorage = spec.getRequiredPrimaryStorageUuids()
.stream().noneMatch(LocalStorageUtils::isLocalStorage);
Map<String, List<String>> grouped = candidates.stream().collect(
Expand All @@ -231,14 +242,16 @@ private List<String> getNeedCheckHostLocalStorageList(List<HostVO> candidates, H
)
);

List<String> result = new ArrayList<>();
Map<String, Boolean> result = new HashMap<>();
for (Map.Entry<String, List<String>> entry : grouped.entrySet()) {
boolean isOnlyAttachedLocalStorage = LocalStorageUtils.isOnlyAttachedLocalStorage(entry.getKey());
if (!isOnlyAttachedLocalStorage && (isRequireNonLocalStorage || spec.isDryRun())) {
continue;
}

result.addAll(entry.getValue());
for (String hostUuid : entry.getValue()) {
result.put(hostUuid, isOnlyAttachedLocalStorage);
}
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ class CreateVmHostAllocateCase extends SubCase {
env.create {
testGetCandidateZonesClustersHostsForCreatingVm()

testCreateVmAssignLocalAndNfs()

testCreateVmAssignNfs()
}
}
Expand All @@ -142,6 +144,60 @@ class CreateVmHostAllocateCase extends SubCase {
assert 2 == hosts.size()
}

void testCreateVmAssignLocalAndNfs() {
InstanceOfferingInventory instanceOffering = env.inventoryByName("instanceOffering") as InstanceOfferingInventory
DiskOfferingInventory diskOffering = env.inventoryByName("diskOffering") as DiskOfferingInventory
ImageInventory image = env.inventoryByName("image") as ImageInventory
L3NetworkInventory l3 = env.inventoryByName("l3") as L3NetworkInventory
HostInventory host = env.inventoryByName("kvm")
PrimaryStorageInventory nfs = env.inventoryByName("nfs")
PrimaryStorageInventory local = env.inventoryByName("local")

CreateVmInstanceAction rootAndDataLocalAction = new CreateVmInstanceAction(
name : "rootAndDataLocalVm",
instanceOfferingUuid : instanceOffering.uuid,
imageUuid : image.uuid,
l3NetworkUuids : [l3.uuid],
hostUuid : host.uuid,
dataDiskOfferingUuids : [diskOffering.uuid],
primaryStorageUuidForRootVolume : local.uuid,
systemTags : [VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME.instantiateTag([(VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME_TOKEN): local.uuid])],
sessionId : currentEnvSpec.session.uuid
)
assert null != rootAndDataLocalAction.call().error

CreateVmInstanceAction rootLocalDataNfsAction = new CreateVmInstanceAction(
name : "rootLocalDataNfsVm",
instanceOfferingUuid : instanceOffering.uuid,
imageUuid : image.uuid,
l3NetworkUuids : [l3.uuid],
hostUuid : host.uuid,
dataDiskOfferingUuids : [diskOffering.uuid],
primaryStorageUuidForRootVolume : local.uuid,
systemTags : [VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME.instantiateTag([(VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME_TOKEN): nfs.uuid])],
sessionId : currentEnvSpec.session.uuid
)
CreateVmInstanceAction.Result rootLocalDataNfsResult = rootLocalDataNfsAction.call()
assert null == rootLocalDataNfsResult.error
checkVmRootDiskPs(rootLocalDataNfsResult.value.inventory, local.uuid)
checkVmDataDiskPs(rootLocalDataNfsResult.value.inventory, nfs.uuid)

CreateVmInstanceAction rootLocalDataUnspecifiedAction = new CreateVmInstanceAction(
name : "rootLocalDataUnspecifiedVm",
instanceOfferingUuid : instanceOffering.uuid,
imageUuid : image.uuid,
l3NetworkUuids : [l3.uuid],
hostUuid : host.uuid,
dataDiskOfferingUuids : [diskOffering.uuid],
primaryStorageUuidForRootVolume : local.uuid,
sessionId : currentEnvSpec.session.uuid
)
CreateVmInstanceAction.Result rootLocalDataUnspecifiedResult = rootLocalDataUnspecifiedAction.call()
assert null == rootLocalDataUnspecifiedResult.error
checkVmRootDiskPs(rootLocalDataUnspecifiedResult.value.inventory, local.uuid)
checkVmDataDiskPs(rootLocalDataUnspecifiedResult.value.inventory, nfs.uuid)
}

void testCreateVmAssignNfs(){
InstanceOfferingInventory instanceOffering = env.inventoryByName("instanceOffering") as InstanceOfferingInventory
DiskOfferingInventory diskOffering = env.inventoryByName("diskOffering") as DiskOfferingInventory
Expand Down Expand Up @@ -184,4 +240,19 @@ class CreateVmHostAllocateCase extends SubCase {
)
assert null != createVmInstanceAction.call().error
}

void checkVmRootDiskPs(VmInstanceInventory vm, String psUuid) {
VolumeInventory rootDisk = vm.allVolumes.find { it.uuid == vm.rootVolumeUuid }
assert rootDisk != null
assert psUuid == rootDisk.primaryStorageUuid
}

void checkVmDataDiskPs(VmInstanceInventory vm, String psUuid) {
assert vm.allVolumes.size() > 1
for (VolumeInventory disk : vm.allVolumes) {
if (disk.uuid != vm.rootVolumeUuid) {
assert psUuid == disk.primaryStorageUuid
}
}
}
}