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
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 @@ -5,12 +5,13 @@
import org.zstack.header.message.NeedReplyMessage;
import org.zstack.header.vm.VmInstanceInventory;

import javax.persistence.Tuple;
import javax.persistence.TupleElement;
import java.util.*;

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 +28,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<Tuple> requiredDiskCapacities = new ArrayList<>();
private boolean fullAllocate = true;
private long oldMemoryCapacity = 0;
private AllocationScene allocationScene;
Expand Down Expand Up @@ -70,6 +72,14 @@ public void addRequiredPrimaryStorageUuid(String requiredPrimaryStorageUuid) {
this.requiredPrimaryStorageUuids.add(requiredPrimaryStorageUuid);
}

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

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

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

public long getDiskSize() {
return diskSize;
return requiredDiskCapacities.stream().mapToLong(it -> it.get(1, Long.class)).sum();
}

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

private Tuple newRequiredDiskCapacity(String primaryStorageUuid, long size) {
return new Tuple() {
@Override
public <X> X get(TupleElement<X> tupleElement) {
throw new UnsupportedOperationException();
}

@Override
public Object get(String alias) {
throw new UnsupportedOperationException();
}

@Override
public <X> X get(String alias, Class<X> type) {
throw new UnsupportedOperationException();
}

@Override
public Object get(int i) {
if (i == 0) {
return primaryStorageUuid;
}
return size;
}

@Override
public <X> X get(int i, Class<X> type) {
return type.cast(get(i));
}

@Override
public Object[] toArray() {
return new Object[]{primaryStorageUuid, size};
}

@Override
public List<TupleElement<?>> getElements() {
return Collections.emptyList();
}
};
}

public String getAllocatorStrategy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.zstack.header.image.ImageInventory;
import org.zstack.header.vm.VmInstanceInventory;

import javax.persistence.Tuple;
import java.util.*;

/**
Expand All @@ -14,7 +15,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 +29,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<Tuple> requiredDiskCapacities = new ArrayList<>();
private Map<String, List<String>> backupStoragePrimaryStorageMetrics;
private boolean dryRun;
private List<String> systemTags;
Expand Down Expand Up @@ -89,6 +90,17 @@ public Set<String> getRequiredPrimaryStorageUuids() {
return requiredPrimaryStorageUuids;
}

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

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

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

public long getDiskSize() {
return diskSize;
}

public void setDiskSize(long diskSize) {
this.diskSize = diskSize;
return requiredDiskCapacities.stream().mapToLong(it -> it.get(1, Long.class)).sum();
}

public String getHypervisorType() {
Expand Down Expand Up @@ -261,7 +269,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 +287,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
Expand Up @@ -109,7 +109,8 @@ 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);
Set<String> onlyLocalStorageHostUuids = new HashSet<>();
List<String> huuids = getNeedCheckHostLocalStorageList(candidates, spec, onlyLocalStorageHostUuids);
if (huuids.isEmpty()) {
return candidates;
}
Expand All @@ -126,10 +127,15 @@ public List<HostVO> filterHostCandidates(List<HostVO> candidates, HostAllocatorS
for (LocalStorageHostRefVO ref : refs) {
String huuid = ref.getHostUuid();
String psUuid = ref.getPrimaryStorageUuid();
long requiredSize = getRequiredLocalStorageSize(spec, psUuid, onlyLocalStorageHostUuids.contains(huuid));
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 @@ -178,6 +184,21 @@ else if (VmOperation.Migrate.toString().equals(spec.getVmOperation())) {
return candidates;
}

private long getRequiredLocalStorageSize(HostAllocatorSpec spec, String psUuid, boolean onlyAttachedLocalStorage) {
long requiredSize = spec.getRequiredDiskCapacities().stream()
.filter(it -> psUuid.equals(it.get(0, String.class)))
.mapToLong(it -> it.get(1, Long.class))
.sum();

boolean hasUndeterminedPrimaryStorage = spec.getRequiredDiskCapacities().stream()
.anyMatch(it -> it.get(0, String.class) == null);
if (hasUndeterminedPrimaryStorage && onlyAttachedLocalStorage) {
return spec.getDiskSize();
}

return requiredSize;
}

private void checkLocalStorageForVmStart(VmInstanceInventory vm, List<HostVO> candidates) {
final List<String> localPS = Q.New(PrimaryStorageVO.class)
.select(PrimaryStorageVO_.uuid)
Expand Down Expand Up @@ -221,7 +242,8 @@ 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 List<String> getNeedCheckHostLocalStorageList(List<HostVO> candidates, HostAllocatorSpec spec,
Set<String> onlyLocalStorageHostUuids) {
boolean isRequireNonLocalStorage = spec.getRequiredPrimaryStorageUuids()
.stream().noneMatch(LocalStorageUtils::isLocalStorage);
Map<String, List<String>> grouped = candidates.stream().collect(
Expand All @@ -239,6 +261,9 @@ private List<String> getNeedCheckHostLocalStorageList(List<HostVO> candidates, H
}

result.addAll(entry.getValue());
if (isOnlyAttachedLocalStorage) {
onlyLocalStorageHostUuids.addAll(entry.getValue());
}
}

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,23 @@ class CreateVmHostAllocateCase extends SubCase {
)
assert null != createVmInstanceAction.call().error
}

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

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
}
}
}
}