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 (psCombos.isEmpty()) {
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 @@ -455,6 +459,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 +483,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 @@ -7,6 +7,7 @@
import org.zstack.core.asyncbatch.While;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.cloudbus.CloudBusCallBack;
import org.zstack.core.componentloader.PluginRegistry;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.Q;
import org.zstack.core.errorcode.ErrorFacade;
Expand All @@ -24,8 +25,10 @@
import org.zstack.header.storage.primary.AllocatePrimaryStorageSpaceReply;
import org.zstack.header.storage.primary.PrimaryStorageAllocationPurpose;
import org.zstack.header.storage.primary.PrimaryStorageConstant;
import org.zstack.header.storage.primary.PrimaryStorageFeature;
import org.zstack.header.storage.primary.ReleasePrimaryStorageSpaceMsg;
import org.zstack.header.vm.DiskAO;
import org.zstack.header.vm.VmAllocatePrimaryStorageExtensionPoint;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmInstanceSpec.VolumeSpec;
Expand Down Expand Up @@ -58,13 +61,21 @@ public class VmAllocatePrimaryStorageFlow implements Flow {
protected ErrorFacade errf;
@Autowired
protected HostAllocatorManager hostAllocatorMgr;
@Autowired
protected PluginRegistry pluginRgty;

@Override
public void run(final FlowTrigger trigger, final Map data) {
final List<AllocatePrimaryStorageSpaceMsg> msgs = new ArrayList<>();
final VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
HostInventory destHost = spec.getDestHost();

for (VmAllocatePrimaryStorageExtensionPoint ext :
pluginRgty.getExtensionList(VmAllocatePrimaryStorageExtensionPoint.class)) {
ext.filterPrimaryStorageCandidates(spec, spec.getCandidatePrimaryStorageUuidsForRootVolume(),
spec.getCandidatePrimaryStorageUuidsForDataVolume());
}

msgs.add(buildMessageForRootVolume(spec, destHost));
msgs.addAll(buildMessageForDataVolumes(spec, destHost));

Expand Down Expand Up @@ -147,6 +158,9 @@ private AllocatePrimaryStorageSpaceMsg buildMessageForRootVolume(final VmInstanc
rmsg.setCandidatePrimaryStorageUuids(candidatePs);
rmsg.setPossiblePrimaryStorageTypes(selectPsTypesFromSpec(spec));
}
if (disk != null && Boolean.TRUE.equals(disk.getEncrypted())) {
rmsg.addRequiredFeature(PrimaryStorageFeature.ENCRYPTED_VOLUME);
}

Set<String> tags = new HashSet<>();
if (disk != null && disk.getSystemTags() != null) {
Expand Down Expand Up @@ -180,6 +194,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);
if (deprecatedDisk != null && Boolean.TRUE.equals(deprecatedDisk.getEncrypted())) {
amsg.addRequiredFeature(PrimaryStorageFeature.ENCRYPTED_VOLUME);
}

Set<String> tags = new HashSet<>();
if (spec.getDataVolumeSystemTags() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public void run(final FlowTrigger chain, final Map data) {
} else {
amsg.setRequiredHostUuid(hinv.getUuid());
}
if (Boolean.TRUE.equals(volume.getEncrypted())) {
amsg.addRequiredFeature(PrimaryStorageFeature.ENCRYPTED_VOLUME);
}

bus.send(amsg, new CloudBusCallBack(chain) {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public void run(final FlowTrigger innerTrigger, Map data) {
amsg.setRequiredPrimaryStorageUuid(diskAO.getPrimaryStorageUuid());
amsg.setPurpose(PrimaryStorageAllocationPurpose.CreateDataVolume.toString());
amsg.setTags(diskAO.getSystemTags());
if (Boolean.TRUE.equals(diskAO.getEncrypted())) {
amsg.addRequiredFeature(PrimaryStorageFeature.ENCRYPTED_VOLUME);
}
bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(innerTrigger) {
@Override
Expand Down Expand Up @@ -280,6 +283,9 @@ public void run(final FlowTrigger innerTrigger, Map data) {
List<String> bsUuids = template.getBackupStorageRefs().stream()
.map(ImageBackupStorageRefVO::getBackupStorageUuid).collect(Collectors.toList());
amsg.setPossiblePrimaryStorageTypes(possiblePrimaryStorageTypes(bsUuids));
if (Boolean.TRUE.equals(diskAO.getEncrypted())) {
amsg.addRequiredFeature(PrimaryStorageFeature.ENCRYPTED_VOLUME);
}

bus.makeLocalServiceId(amsg, PrimaryStorageConstant.SERVICE_ID);
bus.send(amsg, new CloudBusCallBack(innerTrigger) {
Expand Down
9 changes: 9 additions & 0 deletions conf/springConfigXml/VolumeManager.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<bean id="VolumeEncryptedTrashCleanupHelper" class="org.zstack.storage.encrypt.VolumeEncryptedTrashCleanupHelper"/>

<bean id="VolumeSnapshotEncryptionHelper" class="org.zstack.storage.encrypt.VolumeSnapshotEncryptionHelper"/>
<bean id="VolumeSourceEncryptionResolver" class="org.zstack.storage.encrypt.VolumeSourceEncryptionResolver"/>

<bean id="ZbsVolumeEncryptionMaterialFactory" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionMaterialFactory"/>
<bean id="ZbsVolumeEncryptionKvmCaller" class="org.zstack.storage.encrypt.ZbsVolumeEncryptionKvmCaller"/>
Expand Down Expand Up @@ -134,6 +135,14 @@
</zstack:plugin>
</bean>

<bean id="EncryptedVolumePrimaryStorageAllocatorExtension"
class="org.zstack.storage.encrypt.EncryptedVolumePrimaryStorageAllocatorExtension">
<zstack:plugin>
<zstack:extension interface="org.zstack.header.vm.VmAllocatePrimaryStorageExtensionPoint"/>
<zstack:extension interface="org.zstack.storage.primary.PrimaryStorageFeatureAllocatorExtensionPoint"/>
</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 @@ -5,6 +5,7 @@

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -55,6 +56,13 @@ public void setRequiredFeatures(Set<PrimaryStorageFeature> requiredFeatures) {
this.requiredFeatures = requiredFeatures;
}

public void addRequiredFeature(PrimaryStorageFeature requiredFeature) {
requiredFeatures = requiredFeatures == null
? EnumSet.noneOf(PrimaryStorageFeature.class)
: EnumSet.copyOf(requiredFeatures);
requiredFeatures.add(requiredFeature);
}

public List<String> getExcludePrimaryStorageTypes() {
return excludePrimaryStorageTypes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
*/
public enum PrimaryStorageFeature {
SHARED_VOLUME,
ENCRYPTED_VOLUME,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.zstack.header.vm;

import java.util.List;

public interface VmAllocatePrimaryStorageExtensionPoint {
default void filterPrimaryStorageCandidates(VmInstanceSpec spec, List<String> rootPrimaryStorageUuids,
List<String> dataPrimaryStorageUuids) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.zstack.storage.encrypt;

import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.db.Q;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO;
import org.zstack.header.storage.addon.primary.ExternalPrimaryStorageVO_;
import org.zstack.header.storage.primary.PrimaryStorageFeature;
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 org.zstack.storage.primary.PrimaryStorageFeatureAllocatorExtensionPoint;

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

import static org.zstack.core.Platform.operr;

public class EncryptedVolumePrimaryStorageAllocatorExtension implements VmAllocatePrimaryStorageExtensionPoint,
PrimaryStorageFeatureAllocatorExtensionPoint {
private static final String ZHPS_PRIMARY_STORAGE_IDENTITY = "expon";
private static final String ZHPS_PRIMARY_STORAGE_PROTOCOL = VolumeProtocol.Vhost.name();
@Autowired
private VolumeSourceEncryptionResolver sourceEncryptionResolver;

@Override
public List<PrimaryStorageVO> allocatePrimaryStorage(Set<PrimaryStorageFeature> requiredFeatures,
List<PrimaryStorageVO> candidates) {
if (requiredFeatures.contains(PrimaryStorageFeature.ENCRYPTED_VOLUME) && !candidates.isEmpty()) {
filterEncryptedVolumeUnsupportedPrimaryStorage(candidates);
}
return candidates;
}

@Override
public ErrorCode validatePrimaryStorage(Set<PrimaryStorageFeature> requiredFeatures, PrimaryStorageVO primaryStorage) {
if (requiredFeatures.contains(PrimaryStorageFeature.ENCRYPTED_VOLUME)
&& !getEncryptedVolumeUnsupportedPrimaryStorageUuids(Collections.singletonList(primaryStorage.getUuid())).isEmpty()) {
return operr("primary storage[uuid:%s] does not support encrypted volumes", primaryStorage.getUuid());
}
return null;
}

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

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

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 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));
candidates.removeIf(unsupportedPsUuids::contains);
}

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.getRootDisk().getPrimaryStorageUuid() == null;
}

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
@@ -0,0 +1,94 @@
package org.zstack.storage.encrypt;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.zstack.core.db.Q;
import org.zstack.header.image.ImageConstant;
import org.zstack.header.image.ImageVO;
import org.zstack.header.image.ImageVO_;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO;
import org.zstack.header.storage.snapshot.VolumeSnapshotVO_;
import org.zstack.header.vm.DiskAO;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.volume.VolumeVO;
import org.zstack.header.volume.VolumeVO_;

import java.util.List;

public class VolumeSourceEncryptionResolver {
@Autowired
private VolumeSnapshotEncryptionHelper snapshotEncryptionHelper;

public void resolve(VmInstanceSpec spec) {
if (spec == null) {
return;
}

String rootImageUuid = spec.getImageSpec() == null || spec.getImageSpec().getInventory() == null
? null : spec.getImageSpec().getInventory().getUuid();
resolve(spec.getRootDisk(), rootImageUuid);
resolve(spec.getDataDisks());
resolve(spec.getDeprecatedDisksSpecs());
}

public boolean isEncrypted(String imageUuid) {
if (StringUtils.isBlank(imageUuid)) {
return false;
}

if (snapshotEncryptionHelper.hasTemporarySnapshotImageKey(imageUuid)) {
return true;
}

String imageUrl = Q.New(ImageVO.class)
.eq(ImageVO_.uuid, imageUuid)
.select(ImageVO_.url)
.findValue();
if (StringUtils.isBlank(imageUrl)) {
return false;
}

if (imageUrl.startsWith("volume://")) {
String volumeUuid = imageUrl.substring("volume://".length());
return Boolean.TRUE.equals(Q.New(VolumeVO.class)
.eq(VolumeVO_.uuid, volumeUuid)
.select(VolumeVO_.encrypted)
.findValue());
}

String snapshotUuid = getSnapshotUuid(imageUrl);
return snapshotUuid != null && Boolean.TRUE.equals(Q.New(VolumeSnapshotVO.class)
.eq(VolumeSnapshotVO_.uuid, snapshotUuid)
.select(VolumeSnapshotVO_.encrypted)
.findValue());
}

private void resolve(List<DiskAO> disks) {
if (disks != null) {
disks.forEach(disk -> resolve(disk, null));
}
}

private void resolve(DiskAO disk, String defaultImageUuid) {
if (disk == null) {
return;
}

String imageUuid = StringUtils.defaultIfBlank(disk.getTemplateUuid(), defaultImageUuid);
if (isEncrypted(imageUuid)) {
disk.setEncrypted(true);
}
}

private String getSnapshotUuid(String imageUrl) {
String snapshotUuid;
if (imageUrl.startsWith(ImageConstant.IMAGE_FROM_SNAPSHOT_SCHEMA)) {
snapshotUuid = imageUrl.substring(ImageConstant.IMAGE_FROM_SNAPSHOT_SCHEMA.length());
} else if (imageUrl.startsWith(ImageConstant.SNAPSHOT_REUSE_IMAGE_SCHEMA)) {
snapshotUuid = imageUrl.substring(ImageConstant.SNAPSHOT_REUSE_IMAGE_SCHEMA.length());
} else {
return null;
}
return snapshotUuid.length() >= 32 ? snapshotUuid.substring(0, 32) : snapshotUuid;
}
}
Loading