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 @@ -159,6 +159,10 @@ public void run(final FlowTrigger trigger, final Map data) {

// local + non-local, need to automatically allocate the primary storage
List<List<String>> psCombos = getPrimaryStorageCombinationFromSpec(spec, localPsUuids, nonLocalPsUuids);
if (hasUnavailablePrimaryStorageCombination(psCombos, needCreateDataVolume(spec))) {
trigger.fail(Platform.operr("no primary storage candidate is available"));
return;
}
new While<>(psCombos).each((combo, whileCompletion) -> {
spec.setRequiredPrimaryStorageUuidForRootVolume(combo.get(0));
spec.setRequiredPrimaryStorageUuidForDataVolume(combo.get(1));
Expand Down Expand Up @@ -421,6 +425,23 @@ public void handle(ErrorCode errCode, Map data) {
chain.start();
}

private boolean hasUnavailablePrimaryStorageCombination(List<List<String>> psCombos, boolean needCreateDataVolume) {
if (psCombos.isEmpty()) {
return true;
}

for (List<String> combo : psCombos) {
if (combo.get(0) == null || combo.get(0).isEmpty()) {
return true;
}
if (needCreateDataVolume && (combo.get(1) == null || combo.get(1).isEmpty())) {
return true;
}
}

return false;
}

private List<List<String>> getPrimaryStorageCombinationFromSpec(VmInstanceSpec spec, List<String> localPsUuids, List<String> nonLocalPsUuids) {
List<String> availPsForRootVolume = new ArrayList<String>();
availPsForRootVolume.addAll(localPsUuids);
Expand Down Expand Up @@ -455,6 +476,7 @@ private List<List<String>> getPrimaryStorageCombinationFromSpec(VmInstanceSpec s
} else {
dataPs.add(null);
}
filterPrimaryStorageCandidates(spec, rootPs, dataPs);

List<List<String>> finalPsCombos = new ArrayList<>();
if (autoAllocateRootVolumePs && autoAllocateDataVolumePs) {
Expand All @@ -478,6 +500,13 @@ private List<List<String>> getPrimaryStorageCombinationFromSpec(VmInstanceSpec s
return finalPsCombos;
}

private void filterPrimaryStorageCandidates(VmInstanceSpec spec, List<String> rootPs, List<String> dataPs) {
for (VmAllocatePrimaryStorageExtensionPoint ext :
pluginRgty.getExtensionList(VmAllocatePrimaryStorageExtensionPoint.class)) {
ext.filterPrimaryStorageCandidates(spec, rootPs, dataPs);
}
}

private void sortPrimaryStorages(final List<String> psUuids, String strategy, VmInstanceSpec.ImageSpec imageSpec) {
List<PrimaryStorageVO> primaryStorageVOS = psUuids.stream().map(uuid -> dbf.findByUuid(uuid, PrimaryStorageVO.class)).collect(Collectors.toList());
for (PrimaryStorageSortExtensionPoint ext : pluginRgty.getExtensionList(PrimaryStorageSortExtensionPoint.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ private AllocatePrimaryStorageSpaceMsg buildMessageForRootVolume(final VmInstanc
} else {
rmsg.setCandidatePrimaryStorageUuids(candidatePs);
rmsg.setPossiblePrimaryStorageTypes(selectPsTypesFromSpec(spec));
rmsg.setEncryptedVolumeAutoAllocation(disk != null && Boolean.TRUE.equals(disk.getEncrypted()));
}

Set<String> tags = new HashSet<>();
Expand Down Expand Up @@ -180,6 +181,9 @@ private List<AllocatePrimaryStorageSpaceMsg> buildMessageForDataVolumes(final Vm
amsg.setAllocationStrategy(PrimaryStorageConstant.DEFAULT_PRIMARY_STORAGE_ALLOCATION_STRATEGY_TYPE);
amsg.setPurpose(PrimaryStorageAllocationPurpose.CreateDataVolume.toString());
amsg.setDiskOfferingUuid(deprecatedDisk != null ? deprecatedDisk.getDiskOfferingUuid() : null);
amsg.setEncryptedVolumeAutoAllocation(deprecatedDisk != null
&& Boolean.TRUE.equals(deprecatedDisk.getEncrypted())
&& deprecatedDisk.getPrimaryStorageUuid() == null);

Set<String> tags = new HashSet<>();
if (spec.getDataVolumeSystemTags() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public void run(final FlowTrigger chain, final Map data) {
amsg.setSize(volume.getSize());
amsg.setPurpose(PrimaryStorageAllocationPurpose.CreateDataVolume.toString());
amsg.setDiskOfferingUuid(volume.getDiskOfferingUuid());
amsg.setEncryptedVolumeAutoAllocation(Boolean.TRUE.equals(volume.getEncrypted())
&& volume.getPrimaryStorageUuid() == null);
amsg.setServiceId(bus.makeLocalServiceId(PrimaryStorageConstant.SERVICE_ID));

if (volume.isShareable()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1190,13 +1190,60 @@ private void validate(APICreateVmInstanceMsg msg) {

validatePsWhetherSameCluster(msg);
validateDataDiskAOs(msg);
validateEncryptedVolumePrimaryStorage(msg);

if (msg.getAllocatorStrategy() != null && !HostAllocatorStrategyType.hasType(msg.getAllocatorStrategy())) {
throw new ApiMessageInterceptionException(
argerr("unsupported host allocation strategy[%s]", msg.getAllocatorStrategy()));
}
}

private void validateEncryptedVolumePrimaryStorage(APICreateVmInstanceMsg msg) {
if (CollectionUtils.isEmpty(msg.getDiskAOs())) {
return;
}

DiskAO rootDiskAO = findOneOrNull(msg.getDiskAOs(), DiskAO::isBoot);
if (rootDiskAO != null) {
validateEncryptedVolumePrimaryStorage(rootDiskAO.getEncrypted(), msg.getPrimaryStorageUuidForRootVolume());
}

for (DiskAO diskAO : msg.getDiskAOs()) {
validateEncryptedVolumePrimaryStorage(diskAO.getEncrypted(), diskAO.getPrimaryStorageUuid());
}

if (msg.getSystemTags() == null || msg.getSystemTags().isEmpty()) {
return;
}

String primaryStorageUuidForDataVolume = SystemTagUtils.findTagValue(msg.getSystemTags(),
VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME,
VmSystemTags.PRIMARY_STORAGE_UUID_FOR_DATA_VOLUME_TOKEN);
if (primaryStorageUuidForDataVolume == null) {
return;
}

for (DiskAO diskAO : msg.getDiskAOs()) {
if (!diskAO.isBoot()) {
validateEncryptedVolumePrimaryStorage(diskAO.getEncrypted(), primaryStorageUuidForDataVolume);
}
}
}

private void validateEncryptedVolumePrimaryStorage(Boolean encrypted, String primaryStorageUuid) {
if (!Boolean.TRUE.equals(encrypted) || primaryStorageUuid == null) {
return;
}

for (VmAllocatePrimaryStorageExtensionPoint ext :
pluginRgty.getExtensionList(VmAllocatePrimaryStorageExtensionPoint.class)) {
if (!ext.isPrimaryStorageSupportedForEncryptedVolume(primaryStorageUuid)) {
throw new ApiMessageInterceptionException(operr(
"primary storage[uuid:%s] does not support encrypted volume", primaryStorageUuid));
}
}
}

private void validateDataDiskAOs(APICreateVmInstanceMsg msg) {
if (CollectionUtils.isEmpty(msg.getDiskAOs())) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ public void run(final FlowTrigger innerTrigger, Map data) {
amsg.setRequiredPrimaryStorageUuid(diskAO.getPrimaryStorageUuid());
amsg.setPurpose(PrimaryStorageAllocationPurpose.CreateDataVolume.toString());
amsg.setTags(diskAO.getSystemTags());
amsg.setEncryptedVolumeAutoAllocation(Boolean.TRUE.equals(diskAO.getEncrypted())
&& diskAO.getPrimaryStorageUuid() == null);
bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(innerTrigger) {
@Override
Expand Down Expand Up @@ -281,6 +283,8 @@ public void run(final FlowTrigger innerTrigger, Map data) {
List<String> bsUuids = template.getBackupStorageRefs().stream()
.map(ImageBackupStorageRefVO::getBackupStorageUuid).collect(Collectors.toList());
amsg.setPossiblePrimaryStorageTypes(possiblePrimaryStorageTypes(bsUuids));
amsg.setEncryptedVolumeAutoAllocation(Boolean.TRUE.equals(diskAO.getEncrypted())
&& diskAO.getPrimaryStorageUuid() == null);

bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(innerTrigger) {
Expand Down
7 changes: 7 additions & 0 deletions conf/springConfigXml/VolumeManager.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@
</zstack:plugin>
</bean>

<bean id="EncryptedVolumePrimaryStorageAllocatorExtension"
class="org.zstack.storage.encrypt.EncryptedVolumePrimaryStorageAllocatorExtension">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.vm.VmAllocatePrimaryStorageExtensionPoint"/>
</zstack:plugin>
</bean>

<bean id="VolumeEncryptedStartExtension" class="org.zstack.storage.encrypt.VolumeEncryptedStartExtension">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.vm.VmBeforeStartOnHypervisorExtensionPoint"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class AllocatePrimaryStorageMsg extends NeedReplyMessage {
private String volumeUuid;
private boolean noOverProvisioning;
private String purpose;
private boolean encryptedVolumeAutoAllocation;

public Set<PrimaryStorageFeature> getRequiredFeatures() {
return requiredFeatures;
Expand Down Expand Up @@ -251,4 +252,12 @@ public String getVolumeUuid() {
public void setVolumeUuid(String volumeUuid) {
this.volumeUuid = volumeUuid;
}

public boolean isEncryptedVolumeAutoAllocation() {
return encryptedVolumeAutoAllocation;
}

public void setEncryptedVolumeAutoAllocation(boolean encryptedVolumeAutoAllocation) {
this.encryptedVolumeAutoAllocation = encryptedVolumeAutoAllocation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class PrimaryStorageAllocationSpec {
private List<String> excludePrimaryStorageTypes;
private String backupStorageUuid;
private Set<PrimaryStorageFeature> requiredFeatures;
private boolean encryptedVolumeAutoAllocation;

public Set<PrimaryStorageFeature> getRequiredFeatures() {
return requiredFeatures;
Expand Down Expand Up @@ -169,4 +170,12 @@ public long getTotalSize() {
public void setTotalSize(Long totalSize) {
this.totalSize = totalSize;
}

public boolean isEncryptedVolumeAutoAllocation() {
return encryptedVolumeAutoAllocation;
}

public void setEncryptedVolumeAutoAllocation(boolean encryptedVolumeAutoAllocation) {
this.encryptedVolumeAutoAllocation = encryptedVolumeAutoAllocation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.zstack.header.vm;

import org.zstack.header.storage.primary.PrimaryStorageAllocationSpec;
import org.zstack.header.storage.primary.PrimaryStorageVO;

import java.util.List;

public interface VmAllocatePrimaryStorageExtensionPoint {
default void filterPrimaryStorageCandidates(PrimaryStorageAllocationSpec spec, List<PrimaryStorageVO> candidates) {
}

default void filterPrimaryStorageCandidates(VmInstanceSpec spec, List<String> rootPrimaryStorageUuids,
List<String> dataPrimaryStorageUuids) {
}

default boolean isPrimaryStorageSupportedForEncryptedVolume(String primaryStorageUuid) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package org.zstack.storage.encrypt;

import org.zstack.core.db.Q;
import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO;
import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO_;
import org.zstack.header.storage.primary.PrimaryStorageAllocationSpec;
import org.zstack.header.storage.primary.PrimaryStorageVO;
import org.zstack.header.vm.DiskAO;
import org.zstack.header.vm.VmAllocatePrimaryStorageExtensionPoint;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.volume.VolumeProtocol;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class EncryptedVolumePrimaryStorageAllocatorExtension implements VmAllocatePrimaryStorageExtensionPoint {
private static final String ZHPS_PRIMARY_STORAGE_IDENTITY = "expon";
private static final String ZHPS_PRIMARY_STORAGE_PROTOCOL = VolumeProtocol.Vhost.name();

@Override
public void filterPrimaryStorageCandidates(PrimaryStorageAllocationSpec spec, List<PrimaryStorageVO> candidates) {
if (!requiresEncryptedVolumeAutoPsFilter(spec, candidates)) {
return;
}

filterEncryptedVolumeUnsupportedPrimaryStorage(candidates);
}

@Override
public void filterPrimaryStorageCandidates(VmInstanceSpec spec, List<String> rootPrimaryStorageUuids,
List<String> dataPrimaryStorageUuids) {
if (requiresEncryptedRootVolumeAutoPsFilter(spec, rootPrimaryStorageUuids)) {
filterEncryptedVolumeUnsupportedPrimaryStorageUuids(rootPrimaryStorageUuids);
}

if (requiresEncryptedDataVolumeAutoPsFilter(spec, dataPrimaryStorageUuids)) {
filterEncryptedVolumeUnsupportedPrimaryStorageUuids(dataPrimaryStorageUuids);
}
}

@Override
public boolean isPrimaryStorageSupportedForEncryptedVolume(String primaryStorageUuid) {
return !isEncryptedVolumeUnsupportedPrimaryStorage(primaryStorageUuid);
}

private void filterEncryptedVolumeUnsupportedPrimaryStorage(List<PrimaryStorageVO> candidates) {
List<String> psUuids = candidates.stream().map(PrimaryStorageVO::getUuid).collect(Collectors.toList());
Set<String> unsupportedPsUuids = new HashSet<>(getEncryptedVolumeUnsupportedPrimaryStorageUuids(psUuids));
candidates.removeIf(ps -> unsupportedPsUuids.contains(ps.getUuid()));
}

private boolean requiresEncryptedVolumeAutoPsFilter(PrimaryStorageAllocationSpec spec, List<PrimaryStorageVO> candidates) {
return spec != null && spec.isEncryptedVolumeAutoAllocation()
&& candidates != null && !candidates.isEmpty();
}

private boolean requiresEncryptedRootVolumeAutoPsFilter(VmInstanceSpec spec, List<String> candidates) {
return spec != null && isEncryptedAutoAllocatedRootVolume(spec)
&& candidates != null && !candidates.isEmpty();
}

private boolean requiresEncryptedDataVolumeAutoPsFilter(VmInstanceSpec spec, List<String> candidates) {
return spec != null && hasEncryptedAutoAllocatedDataVolume(spec)
&& candidates != null && !candidates.isEmpty();
}

private void filterEncryptedVolumeUnsupportedPrimaryStorageUuids(List<String> candidates) {
Set<String> unsupportedPsUuids = new HashSet<>(getEncryptedVolumeUnsupportedPrimaryStorageUuids(candidates));
filterPrimaryStorageUuids(candidates, unsupportedPsUuids);
}

private void filterPrimaryStorageUuids(List<String> candidates, Set<String> unsupportedPsUuids) {
candidates.removeIf(unsupportedPsUuids::contains);
}

private boolean isEncryptedVolumeUnsupportedPrimaryStorage(String primaryStorageUuid) {
return primaryStorageUuid != null
&& !getEncryptedVolumeUnsupportedPrimaryStorageUuids(Collections.singletonList(primaryStorageUuid)).isEmpty();
}

private boolean hasEncryptedAutoAllocatedDataVolume(VmInstanceSpec spec) {
return spec.getNonTemplateDeprecatedDisksSpecs() != null
&& spec.getNonTemplateDeprecatedDisksSpecs().stream().anyMatch(disk -> isEncrypted(disk)
&& disk.getPrimaryStorageUuid() == null);
}

private boolean isEncryptedAutoAllocatedRootVolume(VmInstanceSpec spec) {
return isEncrypted(spec.getRootDisk())
&& spec.getCandidatePrimaryStorageUuidsForRootVolume().isEmpty();
}

private boolean isEncrypted(DiskAO disk) {
return disk != null && Boolean.TRUE.equals(disk.getEncrypted());
}

protected List<String> getEncryptedVolumeUnsupportedPrimaryStorageUuids(List<String> psUuids) {
Q query = Q.New(ExternalPrimaryStorageVO.class)
.select(ExternalPrimaryStorageVO_.uuid)
.eq(ExternalPrimaryStorageVO_.identity, ZHPS_PRIMARY_STORAGE_IDENTITY)
.eq(ExternalPrimaryStorageVO_.defaultProtocol, ZHPS_PRIMARY_STORAGE_PROTOCOL);
if (psUuids != null) {
query.in(ExternalPrimaryStorageVO_.uuid, psUuids);
}
return query.listValues();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.db.SQL;
Expand All @@ -15,6 +16,7 @@
import org.zstack.header.storage.backup.BackupStorageVO;
import org.zstack.header.storage.primary.*;
import org.zstack.header.storage.primary.PrimaryStorageConstant.AllocatorParams;
import org.zstack.header.vm.VmAllocatePrimaryStorageExtensionPoint;
import org.zstack.header.vo.ResourceVO;
import org.zstack.utils.Utils;
import org.zstack.utils.gson.JSONObjectUtil;
Expand All @@ -41,6 +43,8 @@ public class PrimaryStorageMainAllocatorFlow extends NoRollbackFlow {
protected ErrorFacade errf;
@Autowired
protected PrimaryStorageOverProvisioningManager ratioMgr;
@Autowired
protected PluginRegistry pluginRgty;

private class Result {
List<PrimaryStorageVO> result;
Expand Down Expand Up @@ -151,6 +155,7 @@ private Result allocate(Map data) {
}
}

filterPrimaryStorageCandidates(spec, vos);

List<PrimaryStorageVO> res = new ArrayList<>();
if (PrimaryStorageAllocationPurpose.CreateNewVm.toString().equals(spec.getPurpose())) {
Expand All @@ -171,6 +176,13 @@ private Result allocate(Map data) {
return ret;
}

private void filterPrimaryStorageCandidates(PrimaryStorageAllocationSpec spec, List<PrimaryStorageVO> candidates) {
for (VmAllocatePrimaryStorageExtensionPoint ext :
pluginRgty.getExtensionList(VmAllocatePrimaryStorageExtensionPoint.class)) {
ext.filterPrimaryStorageCandidates(spec, candidates);
}
}

@Transactional(readOnly = true)
private Collection<? extends PrimaryStorageVO> considerImageCache(PrimaryStorageAllocationSpec spec, List<PrimaryStorageVO> vos) {
if (spec.getImageUuid() == null) {
Expand Down
Loading