Skip to content

Commit fe33bda

Browse files
authored
Merge branch '4.19' into fixup-extra-iso
2 parents b1af099 + 802bf5f commit fe33bda

File tree

23 files changed

+1065
-476
lines changed

23 files changed

+1065
-476
lines changed

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

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -135,34 +135,49 @@ public static enum TemplateType {
135135
ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */
136136
}
137137

138+
public enum EncryptionSupport {
139+
/**
140+
* Encryption not supported.
141+
*/
142+
Unsupported,
143+
/**
144+
* Will use hypervisor encryption driver (qemu -> luks)
145+
*/
146+
Hypervisor,
147+
/**
148+
* Storage pool handles encryption and just provides an encrypted volume
149+
*/
150+
Storage
151+
}
152+
138153
public static enum StoragePoolType {
139-
Filesystem(false, true, true), // local directory
140-
NetworkFilesystem(true, true, true), // NFS
141-
IscsiLUN(true, false, false), // shared LUN, with a clusterfs overlay
142-
Iscsi(true, false, false), // for e.g., ZFS Comstar
143-
ISO(false, false, false), // for iso image
144-
LVM(false, false, false), // XenServer local LVM SR
145-
CLVM(true, false, false),
146-
RBD(true, true, false), // http://libvirt.org/storage.html#StorageBackendRBD
147-
SharedMountPoint(true, true, true),
148-
VMFS(true, true, false), // VMware VMFS storage
149-
PreSetup(true, true, false), // for XenServer, Storage Pool is set up by customers.
150-
EXT(false, true, false), // XenServer local EXT SR
151-
OCFS2(true, false, false),
152-
SMB(true, false, false),
153-
Gluster(true, false, false),
154-
PowerFlex(true, true, true), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS)
155-
ManagedNFS(true, false, false),
156-
Linstor(true, true, false),
157-
DatastoreCluster(true, true, false), // for VMware, to abstract pool of clusters
158-
StorPool(true, true, true),
159-
FiberChannel(true, true, false); // Fiber Channel Pool for KVM hypervisors is used to find the volume by WWN value (/dev/disk/by-id/wwn-<wwnvalue>)
154+
Filesystem(false, true, EncryptionSupport.Hypervisor), // local directory
155+
NetworkFilesystem(true, true, EncryptionSupport.Hypervisor), // NFS
156+
IscsiLUN(true, false, EncryptionSupport.Unsupported), // shared LUN, with a clusterfs overlay
157+
Iscsi(true, false, EncryptionSupport.Unsupported), // for e.g., ZFS Comstar
158+
ISO(false, false, EncryptionSupport.Unsupported), // for iso image
159+
LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR
160+
CLVM(true, false, EncryptionSupport.Unsupported),
161+
RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD
162+
SharedMountPoint(true, true, EncryptionSupport.Hypervisor),
163+
VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage
164+
PreSetup(true, true, EncryptionSupport.Unsupported), // for XenServer, Storage Pool is set up by customers.
165+
EXT(false, true, EncryptionSupport.Unsupported), // XenServer local EXT SR
166+
OCFS2(true, false, EncryptionSupport.Unsupported),
167+
SMB(true, false, EncryptionSupport.Unsupported),
168+
Gluster(true, false, EncryptionSupport.Unsupported),
169+
PowerFlex(true, true, EncryptionSupport.Hypervisor), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS)
170+
ManagedNFS(true, false, EncryptionSupport.Unsupported),
171+
Linstor(true, true, EncryptionSupport.Storage),
172+
DatastoreCluster(true, true, EncryptionSupport.Unsupported), // for VMware, to abstract pool of clusters
173+
StorPool(true, true, EncryptionSupport.Hypervisor),
174+
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>)
160175

161176
private final boolean shared;
162177
private final boolean overProvisioning;
163-
private final boolean encryption;
178+
private final EncryptionSupport encryption;
164179

165-
StoragePoolType(boolean shared, boolean overProvisioning, boolean encryption) {
180+
StoragePoolType(boolean shared, boolean overProvisioning, EncryptionSupport encryption) {
166181
this.shared = shared;
167182
this.overProvisioning = overProvisioning;
168183
this.encryption = encryption;
@@ -177,6 +192,10 @@ public boolean supportsOverProvisioning() {
177192
}
178193

179194
public boolean supportsEncryption() {
195+
return encryption == EncryptionSupport.Hypervisor || encryption == EncryptionSupport.Storage;
196+
}
197+
198+
public EncryptionSupport encryptionSupportMode() {
180199
return encryption;
181200
}
182201
}

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
@@ -3180,7 +3180,8 @@ public int compare(final DiskTO arg0, final DiskTO arg1) {
31803180
disk.setCacheMode(DiskDef.DiskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString().toUpperCase()));
31813181
}
31823182

3183-
if (volumeObjectTO.requiresEncryption()) {
3183+
if (volumeObjectTO.requiresEncryption() &&
3184+
pool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor ) {
31843185
String secretUuid = createLibvirtVolumeSecret(conn, volumeObjectTO.getPath(), volumeObjectTO.getPassphrase());
31853186
DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(secretUuid, QemuObject.EncryptFormat.enumValue(volumeObjectTO.getEncryptFormat()));
31863187
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
@@ -50,6 +50,7 @@
5050
import com.cloud.storage.JavaStorageLayer;
5151
import com.cloud.storage.MigrationOptions;
5252
import com.cloud.storage.ScopeType;
53+
import com.cloud.storage.Storage;
5354
import com.cloud.storage.Storage.ImageFormat;
5455
import com.cloud.storage.Storage.StoragePoolType;
5556
import com.cloud.storage.StorageLayer;
@@ -1452,7 +1453,8 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean
14521453
}
14531454
}
14541455

1455-
if (encryptDetails != null) {
1456+
if (encryptDetails != null &&
1457+
attachingPool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor) {
14561458
diskdef.setLibvirtDiskEncryptDetails(encryptDetails);
14571459
}
14581460

plugins/storage/volume/linstor/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,23 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2025-01-27]
9+
10+
### Fixed
11+
12+
- Use of multiple primary storages on the same linstor controller
13+
814
## [2025-01-20]
915

1016
### Fixed
1117

1218
- Volume snapshots on zfs used the wrong dataset path to hide/unhide snapdev
1319

20+
## [2024-12-19]
21+
22+
### Added
23+
- Native CloudStack encryption support
24+
1425
## [2024-12-13]
1526

1627
### Fixed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,18 +96,23 @@ private String convertImageToQCow2(
9696
// NOTE: the qemu img will also contain the drbd metadata at the end
9797
final QemuImg qemu = new QemuImg(waitMilliSeconds);
9898
qemu.convert(srcFile, dstFile);
99-
s_logger.info("Backup snapshot " + srcFile + " to " + dstPath);
99+
s_logger.info(String.format("Backup snapshot '%s' to '%s'", srcPath, dstPath));
100100
return dstPath;
101101
}
102102

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

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

113118
@Override
@@ -157,6 +162,7 @@ public CopyCmdAnswer execute(LinstorBackupSnapshotCommand cmd, LibvirtComputingR
157162
s_logger.info("Backup shrunk " + dstPath + " to actual size " + src.getVolume().getSize());
158163

159164
SnapshotObjectTO snapshot = setCorrectSnapshotSize(dst, dstPath);
165+
s_logger.info(String.format("Actual file size for '%s' is %d", dstPath, snapshot.getPhysicalSize()));
160166
return new CopyCmdAnswer(snapshot);
161167
} catch (final Exception e) {
162168
final String error = String.format("Failed to backup snapshot with id [%s] with a pool %s, due to %s",

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

Lines changed: 109 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
import com.linbit.linstor.api.model.VolumeDefinition;
6161

6262
import java.io.File;
63+
import java.io.FileInputStream;
64+
import java.io.IOException;
65+
import java.nio.file.Files;
66+
import java.nio.file.Path;
67+
import java.nio.file.Paths;
6368

6469
@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor)
6570
public class LinstorStorageAdaptor implements StorageAdaptor {
@@ -198,10 +203,10 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
198203
final DevelopersApi api = getLinstorAPI(pool);
199204

200205
try {
201-
List<ResourceDefinition> definitionList = api.resourceDefinitionList(
202-
Collections.singletonList(rscName), null, null, null);
206+
ResourceDefinition resourceDefinition = LinstorUtil.findResourceDefinition(
207+
api, rscName, lpool.getResourceGroup());
203208

204-
if (definitionList.isEmpty()) {
209+
if (resourceDefinition == null) {
205210
ResourceGroupSpawn rgSpawn = new ResourceGroupSpawn();
206211
rgSpawn.setResourceDefinitionName(rscName);
207212
rgSpawn.addVolumeSizesItem(size / 1024); // linstor uses KiB
@@ -211,22 +216,28 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
211216
handleLinstorApiAnswers(answers, "Linstor: Unable to spawn resource.");
212217
}
213218

219+
String foundRscName = resourceDefinition != null ? resourceDefinition.getName() : rscName;
220+
214221
// query linstor for the device path
215222
List<ResourceWithVolumes> resources = api.viewResources(
216223
Collections.emptyList(),
217-
Collections.singletonList(rscName),
224+
Collections.singletonList(foundRscName),
218225
Collections.emptyList(),
219226
null,
220227
null,
221228
null);
222229

223-
makeResourceAvailable(api, rscName, false);
230+
makeResourceAvailable(api, foundRscName, false);
224231

225232
if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) {
226233
final String devPath = resources.get(0).getVolumes().get(0).getDevicePath();
227234
s_logger.info("Linstor: Created drbd device: " + devPath);
228235
final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devPath, name, pool);
229236
kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW);
237+
long allocatedKib = resources.get(0).getVolumes().get(0).getAllocatedSizeKib() != null ?
238+
resources.get(0).getVolumes().get(0).getAllocatedSizeKib() : 0;
239+
kvmDisk.setSize(allocatedKib >= 0 ? allocatedKib * 1024 : 0);
240+
kvmDisk.setVirtualSize(size);
230241
return kvmDisk;
231242
} else {
232243
s_logger.error("Linstor: viewResources didn't return resources or volumes.");
@@ -407,7 +418,7 @@ private boolean tryDisconnectLinstor(String volumePath, KVMStoragePool pool)
407418
if (rsc.getFlags() != null &&
408419
rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS) &&
409420
!rsc.getFlags().contains(ApiConsts.FLAG_TIE_BREAKER)) {
410-
ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName);
421+
ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName, true);
411422
logLinstorAnswers(delAnswers);
412423
}
413424
} catch (ApiException apiEx) {
@@ -470,21 +481,56 @@ public boolean disconnectPhysicalDiskByPath(String localPath)
470481
return false;
471482
}
472483

484+
/**
485+
* Decrements the aux property key for template resource and deletes or just deletes if not template resource.
486+
* @param api
487+
* @param rscName
488+
* @param rscGrpName
489+
* @return
490+
* @throws ApiException
491+
*/
492+
private boolean deRefOrDeleteResource(DevelopersApi api, String rscName, String rscGrpName) throws ApiException {
493+
boolean deleted = false;
494+
List<ResourceDefinition> existingRDs = LinstorUtil.getRDListStartingWith(api, rscName);
495+
for (ResourceDefinition rd : existingRDs) {
496+
int expectedProps = 0; // if it is a non template resource, we don't expect any _cs-template-for- prop
497+
String propKey = LinstorUtil.getTemplateForAuxPropKey(rscGrpName);
498+
if (rd.getProps().containsKey(propKey)) {
499+
ResourceDefinitionModify rdm = new ResourceDefinitionModify();
500+
rdm.deleteProps(Collections.singletonList(propKey));
501+
api.resourceDefinitionModify(rd.getName(), rdm);
502+
expectedProps = 1;
503+
}
504+
505+
// if there is only one template-for property left for templates, the template isn't needed anymore
506+
// or if it isn't a template anyway, it will not have this Aux property
507+
// _cs-template-for- poperties work like a ref-count.
508+
if (rd.getProps().keySet().stream()
509+
.filter(key -> key.startsWith("Aux/" + LinstorUtil.CS_TEMPLATE_FOR_PREFIX))
510+
.count() == expectedProps) {
511+
ApiCallRcList answers = api.resourceDefinitionDelete(rd.getName());
512+
checkLinstorAnswersThrow(answers);
513+
deleted = true;
514+
}
515+
}
516+
return deleted;
517+
}
518+
473519
@Override
474520
public boolean deletePhysicalDisk(String name, KVMStoragePool pool, Storage.ImageFormat format)
475521
{
476522
s_logger.debug("Linstor: deletePhysicalDisk " + name);
477523
final DevelopersApi api = getLinstorAPI(pool);
524+
final String rscName = getLinstorRscName(name);
525+
final LinstorStoragePool linstorPool = (LinstorStoragePool) pool;
526+
String rscGrpName = linstorPool.getResourceGroup();
478527

479528
try {
480-
final String rscName = getLinstorRscName(name);
481-
s_logger.debug("Linstor: delete resource definition " + rscName);
482-
ApiCallRcList answers = api.resourceDefinitionDelete(rscName);
483-
handleLinstorApiAnswers(answers, "Linstor: Unable to delete resource definition " + rscName);
529+
return deRefOrDeleteResource(api, rscName, rscGrpName);
484530
} catch (ApiException apiEx) {
531+
s_logger.error("Linstor: ApiEx - " + apiEx.getMessage());
485532
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
486533
}
487-
return true;
488534
}
489535

490536
@Override
@@ -558,6 +604,56 @@ private boolean resourceSupportZeroBlocks(KVMStoragePool destPool, String resNam
558604
return false;
559605
}
560606

607+
/**
608+
* Checks if the given disk is the SystemVM template, by checking its properties file in the same directory.
609+
* The initial systemvm template resource isn't created on the management server, but
610+
* we now need to know if the systemvm template is used, while copying.
611+
* @param disk
612+
* @return True if it is the systemvm template disk, else false.
613+
*/
614+
private static boolean isSystemTemplate(KVMPhysicalDisk disk) {
615+
Path diskPath = Paths.get(disk.getPath());
616+
Path propFile = diskPath.getParent().resolve("template.properties");
617+
if (Files.exists(propFile)) {
618+
java.util.Properties templateProps = new java.util.Properties();
619+
try {
620+
templateProps.load(new FileInputStream(propFile.toFile()));
621+
String desc = templateProps.getProperty("description");
622+
if (desc.startsWith("SystemVM Template")) {
623+
return true;
624+
}
625+
} catch (IOException e) {
626+
return false;
627+
}
628+
}
629+
return false;
630+
}
631+
632+
/**
633+
* Conditionally sets the correct aux properties for templates or basic resources.
634+
* @param api
635+
* @param srcDisk
636+
* @param destPool
637+
* @param name
638+
*/
639+
private void setRscDfnAuxProperties(
640+
DevelopersApi api, KVMPhysicalDisk srcDisk, KVMStoragePool destPool, String name) {
641+
// if it is the initial systemvm disk copy, we need to apply the _cs-template-for property.
642+
if (isSystemTemplate(srcDisk)) {
643+
applyAuxProps(api, name, "SystemVM Template", null);
644+
LinstorStoragePool linPool = (LinstorStoragePool) destPool;
645+
final String rscName = getLinstorRscName(name);
646+
try {
647+
LinstorUtil.setAuxTemplateForProperty(api, rscName, linPool.getResourceGroup());
648+
} catch (ApiException apiExc) {
649+
s_logger.error(String.format("Error setting aux template for property for %s", rscName));
650+
logLinstorAnswers(apiExc.getApiCallRcList());
651+
}
652+
} else {
653+
applyAuxProps(api, name, srcDisk.getDispName(), srcDisk.getVmName());
654+
}
655+
}
656+
561657
@Override
562658
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase, Storage.ProvisioningType provisioningType)
563659
{
@@ -571,15 +667,14 @@ public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMSt
571667
name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null);
572668

573669
final DevelopersApi api = getLinstorAPI(destPools);
574-
applyAuxProps(api, name, disk.getDispName(), disk.getVmName());
670+
setRscDfnAuxProperties(api, disk, destPools, name);
575671

576672
s_logger.debug(String.format("Linstor.copyPhysicalDisk: dstPath: %s", dstDisk.getPath()));
577673
final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath());
578674
destFile.setFormat(dstDisk.getFormat());
579675
destFile.setSize(disk.getVirtualSize());
580676

581-
boolean zeroedDevice = resourceSupportZeroBlocks(destPools, LinstorUtil.RSC_PREFIX + name);
582-
677+
boolean zeroedDevice = resourceSupportZeroBlocks(destPools, getLinstorRscName(name));
583678
try {
584679
final QemuImg qemu = new QemuImg(timeout, zeroedDevice, true);
585680
qemu.convert(srcFile, destFile);

0 commit comments

Comments
 (0)