Skip to content

Commit 0627921

Browse files
rp-dhslove
authored andcommitted
Linstor: encryption support (apache#10126)
This introduces a new encryption mode, instead of a simple bool. Now also storage driver can just provide encrypted volumes to CloudStack.
1 parent 39b388d commit 0627921

File tree

10 files changed

+580
-100
lines changed

10 files changed

+580
-100
lines changed

api/src/main/java/com/cloud/storage/Storage.java

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,21 @@ public static enum TemplateType {
139139
ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */
140140
}
141141

142+
public enum EncryptionSupport {
143+
/**
144+
* Encryption not supported.
145+
*/
146+
Unsupported,
147+
/**
148+
* Will use hypervisor encryption driver (qemu -> luks)
149+
*/
150+
Hypervisor,
151+
/**
152+
* Storage pool handles encryption and just provides an encrypted volume
153+
*/
154+
Storage
155+
}
156+
142157
/**
143158
* StoragePoolTypes carry some details about the format and capabilities of a storage pool. While not necessarily a
144159
* 1:1 with PrimaryDataStoreDriver (and for KVM agent, KVMStoragePool and StorageAdaptor) implementations, it is
@@ -150,57 +165,35 @@ public static enum TemplateType {
150165
* ensure this is available on the agent side as well. This is best done by defining the StoragePoolType in a common
151166
* package available on both management server and agent plugin jars.
152167
*/
153-
public static class StoragePoolType {
154-
private static final Map<String, StoragePoolType> map = new LinkedHashMap<>();
155-
156-
public static final StoragePoolType Filesystem = new StoragePoolType("Filesystem", false, true, true);
157-
public static final StoragePoolType NetworkFilesystem = new StoragePoolType("NetworkFilesystem", true, true, true);
158-
public static final StoragePoolType IscsiLUN = new StoragePoolType("IscsiLUN", true, false, false);
159-
public static final StoragePoolType Iscsi = new StoragePoolType("Iscsi", true, false, false);
160-
public static final StoragePoolType ISO = new StoragePoolType("ISO", false, false, false);
161-
public static final StoragePoolType LVM = new StoragePoolType("LVM", false, false, false);
162-
public static final StoragePoolType CLVM = new StoragePoolType("CLVM", true, false, false);
163-
public static final StoragePoolType RBD = new StoragePoolType("RBD", true, true, false);
164-
public static final StoragePoolType SharedMountPoint = new StoragePoolType("SharedMountPoint", true, true, true);
165-
public static final StoragePoolType VMFS = new StoragePoolType("VMFS", true, true, false);
166-
public static final StoragePoolType PreSetup = new StoragePoolType("PreSetup", true, true, false);
167-
public static final StoragePoolType EXT = new StoragePoolType("EXT", false, true, false);
168-
public static final StoragePoolType OCFS2 = new StoragePoolType("OCFS2", true, false, false);
169-
public static final StoragePoolType SMB = new StoragePoolType("SMB", true, false, false);
170-
public static final StoragePoolType Gluster = new StoragePoolType("Gluster", true, false, false);
171-
public static final StoragePoolType PowerFlex = new StoragePoolType("PowerFlex", true, true, true);
172-
public static final StoragePoolType ManagedNFS = new StoragePoolType("ManagedNFS", true, false, false);
173-
public static final StoragePoolType Linstor = new StoragePoolType("Linstor", true, true, false);
174-
public static final StoragePoolType DatastoreCluster = new StoragePoolType("DatastoreCluster", true, true, false);
175-
public static final StoragePoolType StorPool = new StoragePoolType("StorPool", true,true,true);
176-
public static final StoragePoolType FiberChannel = new StoragePoolType("FiberChannel", true,true,false);
177-
168+
public static enum StoragePoolType {
169+
Filesystem(false, true, EncryptionSupport.Hypervisor), // local directory
170+
NetworkFilesystem(true, true, EncryptionSupport.Hypervisor), // NFS
171+
IscsiLUN(true, false, EncryptionSupport.Unsupported), // shared LUN, with a clusterfs overlay
172+
Iscsi(true, false, EncryptionSupport.Unsupported), // for e.g., ZFS Comstar
173+
ISO(false, false, EncryptionSupport.Unsupported), // for iso image
174+
LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR
175+
CLVM(true, false, EncryptionSupport.Unsupported),
176+
RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD
177+
SharedMountPoint(true, true, EncryptionSupport.Hypervisor),
178+
VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage
179+
PreSetup(true, true, EncryptionSupport.Unsupported), // for XenServer, Storage Pool is set up by customers.
180+
EXT(false, true, EncryptionSupport.Unsupported), // XenServer local EXT SR
181+
OCFS2(true, false, EncryptionSupport.Unsupported),
182+
SMB(true, false, EncryptionSupport.Unsupported),
183+
Gluster(true, false, EncryptionSupport.Unsupported),
184+
PowerFlex(true, true, EncryptionSupport.Hypervisor), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS)
185+
ManagedNFS(true, false, EncryptionSupport.Unsupported),
186+
Linstor(true, true, EncryptionSupport.Storage),
187+
DatastoreCluster(true, true, EncryptionSupport.Unsupported), // for VMware, to abstract pool of clusters
188+
StorPool(true, true, EncryptionSupport.Hypervisor),
189+
FiberChannel(true, true, EncryptionSupport.Unsupported); // Fiber Channel Pool for KVM hypervisors is used to find the volume by WWN value (/dev/disk/by-id/wwn-<wwnvalue>)
178190

179191
private final String name;
180192
private final boolean shared;
181193
private final boolean overProvisioning;
182-
private final boolean encryption;
194+
private final EncryptionSupport encryption;
183195

184-
/**
185-
* New StoragePoolType, set the name to check with it in Dao (Note: Do not register it into the map of pool types).
186-
* @param name name of the StoragePoolType.
187-
*/
188-
public StoragePoolType(String name) {
189-
this.name = name;
190-
this.shared = false;
191-
this.overProvisioning = false;
192-
this.encryption = false;
193-
}
194-
195-
/**
196-
* Define a new StoragePoolType, and register it into the map of pool types known to the management server.
197-
* @param name Simple unique name of the StoragePoolType.
198-
* @param shared Storage pool is shared/accessible to multiple hypervisors
199-
* @param overProvisioning Storage pool supports overProvisioning
200-
* @param encryption Storage pool supports encrypted volumes
201-
*/
202-
public StoragePoolType(String name, boolean shared, boolean overProvisioning, boolean encryption) {
203-
this.name = name;
196+
StoragePoolType(boolean shared, boolean overProvisioning, EncryptionSupport encryption) {
204197
this.shared = shared;
205198
this.overProvisioning = overProvisioning;
206199
this.encryption = encryption;
@@ -216,6 +209,10 @@ public boolean supportsOverProvisioning() {
216209
}
217210

218211
public boolean supportsEncryption() {
212+
return encryption == EncryptionSupport.Hypervisor || encryption == EncryptionSupport.Storage;
213+
}
214+
215+
public EncryptionSupport encryptionSupportMode() {
219216
return encryption;
220217
}
221218

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3586,7 +3586,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
35863586
disk.setCacheMode(DiskDef.DiskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString().toUpperCase()));
35873587
}
35883588

3589-
if (volumeObjectTO.requiresEncryption()) {
3589+
if (volumeObjectTO.requiresEncryption() &&
3590+
pool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor ) {
35903591
String secretUuid = createLibvirtVolumeSecret(conn, volumeObjectTO.getPath(), volumeObjectTO.getPassphrase());
35913592
DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(secretUuid, QemuObject.EncryptFormat.enumValue(volumeObjectTO.getEncryptFormat()));
35923593
disk.setLibvirtDiskEncryptDetails(encryptDetails);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
import com.cloud.storage.JavaStorageLayer;
129129
import com.cloud.storage.MigrationOptions;
130130
import com.cloud.storage.ScopeType;
131+
import com.cloud.storage.Storage;
131132
import com.cloud.storage.Storage.ImageFormat;
132133
import com.cloud.storage.Storage.StoragePoolType;
133134
import com.cloud.storage.StorageLayer;
@@ -1517,7 +1518,8 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean
15171518
}
15181519
}
15191520

1520-
if (encryptDetails != null) {
1521+
if (encryptDetails != null &&
1522+
attachingPool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor) {
15211523
diskdef.setLibvirtDiskEncryptDetails(encryptDetails);
15221524
}
15231525

plugins/storage/volume/linstor/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Volume snapshots on zfs used the wrong dataset path to hide/unhide snapdev
1313

14+
## [2024-12-19]
15+
16+
### Added
17+
- Native CloudStack encryption support
18+
1419
## [2024-12-13]
1520

1621
### Fixed

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorBackupSnapshotCommandWrapper.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,23 @@ private String convertImageToQCow2(
9797
// NOTE: the qemu img will also contain the drbd metadata at the end
9898
final QemuImg qemu = new QemuImg(waitMilliSeconds);
9999
qemu.convert(srcFile, dstFile);
100-
LOGGER.info("Backup snapshot " + srcFile + " to " + dstPath);
100+
LOGGER.info("Backup snapshot '{}' to '{}'", srcPath, dstPath);
101101
return dstPath;
102102
}
103103

104104
private SnapshotObjectTO setCorrectSnapshotSize(final SnapshotObjectTO dst, final String dstPath) {
105105
final File snapFile = new File(dstPath);
106-
final long size = snapFile.exists() ? snapFile.length() : 0;
106+
long size;
107+
if (snapFile.exists()) {
108+
size = snapFile.length();
109+
} else {
110+
LOGGER.warn("Snapshot file {} does not exist. Reporting size 0", dstPath);
111+
size = 0;
112+
}
107113

108-
final SnapshotObjectTO snapshot = new SnapshotObjectTO();
109-
snapshot.setPath(dst.getPath() + File.separator + dst.getName());
110-
snapshot.setPhysicalSize(size);
111-
return snapshot;
114+
dst.setPath(dst.getPath() + File.separator + dst.getName());
115+
dst.setPhysicalSize(size);
116+
return dst;
112117
}
113118

114119
@Override
@@ -158,6 +163,7 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
158163
LOGGER.info("Backup shrunk " + dstPath + " to actual size " + src.getVolume().getSize());
159164

160165
SnapshotObjectTO snapshot = setCorrectSnapshotSize(dst, dstPath);
166+
LOGGER.info("Actual file size for '{}' is {}", dstPath, snapshot.getPhysicalSize());
161167
return new CopyCmdAnswer(snapshot);
162168
} catch (final Exception e) {
163169
final String error = String.format("Failed to backup snapshot with id [%s] with a pool %s, due to %s",
@@ -171,4 +177,4 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
171177
}
172178
}
173179
}
174-
}
180+
}

plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ private boolean tryDisconnectLinstor(String volumePath, KVMStoragePool pool)
410410
if (rsc.getFlags() != null &&
411411
rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS) &&
412412
!rsc.getFlags().contains(ApiConsts.FLAG_TIE_BREAKER)) {
413-
ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName);
413+
ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName, true);
414414
logLinstorAnswers(delAnswers);
415415
}
416416
} catch (ApiException apiEx) {

0 commit comments

Comments
 (0)