From ba7adfa6f08638fbacf932c9519436935f0cbcbe Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Wed, 29 Jun 2022 04:21:00 -0600 Subject: [PATCH] Volume encryption (#135) This PR introduces volume encryption option to service offerings and disk offerings. Fixes #136 There is a hypervisor component and a storage pool component. Hypervisors are responsible for being capable of running/using the encrypted volumes. Storage pools are responsible for being able to create, copy, resize, etc. Hypervisors will report encryption support in their details, storage pools are marked for encryption support by pool type. The initial offering for experimental release of this feature will have support for encryption on Local, NFS, SharedMountPoint, and ScaleIO storage types. When volumes choosing an encrypted offering are allocated to a pool, the pool type must be capable of supporting encryption and this is enforced. When VMs are started and they have an encrypted volume, the hypervisor must be capable of supporting encryption. Also, if volumes are attached to running VMs, the attach will only work if the hypervisor supports encryption. This change includes a few other minor changes - for example the ability to force the KVM hypervisor private IP. This was necessary in my testing of ScaleIO, where the KVM hypervisors had multiple IPs and the ScaleIO storage only functions if the hypervisor as a ScaleIO client matches IPs with what CloudStack sees as the hypervisor IP. For experimental release of this feature, some volume workflows like extract volume and migrate volume aren't supported for encrypted volumes. In the future we could support these, as well as migrating from unencrypted to encrypted offerings, and vice versa. It may also be possible to configure encryption specifics in the future, perhaps at the pool level or the offering level. Currently, there is only one workable encryption offering for KVM that is supported by Libvirt and Qemu for raw and qcow2 disk files, LUKS version 1. This PR ensures we at least store this encryption format associated with each volume, with the expectation that later we may have LUKS v2 volumes or something else. Thus we will have the information necessary to use each volume with Libvirt if/when other formats are introduced. I think the most disruptive change here is probably a refactoring of the QemuImg utility to support newer flags like --object. I've tested the change against the basic Qemu 1.5.3 that comes with EL7 and I believe it is good, but it will be nice to see the results of some functional tests. Most of the other changes are limited to changing behavior only if volume encryption is requested. Working on documentation for the CloudStack docs. One thing to note is that hypervisors that run the stock EL7 version of Qemu will not support encryption. This is tested to be detected and report properly via the CloudStack API/UI. I intend to like to have a support matrix in the CloudStack docs. I may add a few more unit tests. I'd also like some guidance on having functional tests. I'm not sure if there's a separate framework, or if Marvin is still used, or what the current thing is. * Add Qemu object flag to QemuImg create * Add apache license header to new files * Add Qemu object flag to QemuImg convert * Set host details if hypervisor supports LUKS * Add disk encrypt flag to APIs, diskoffering * Schema upgrade 4.16.0.0 to 4.16.1.0 to support vol encryption * Add Libvirt secret on disk attach, and refer to it in disk XML * Add implementation of luks volume encryption to QCOW2 and RAW disk prep * Start VMs that have encrypted volumes * Add encrypt option to service offering and root volume provisioning * Refactor volume passphrase into its own table and object * CryptSetup, use key files to pass keys instead of command line * Update storage types and allocators to select encryption support * Allow agent.properties to define the hypervisor's private IP * Implement createPhysicalDisk for ScaleIOStorageAdaptor * UI: Add encrypt options to offerings * UI module security updates * Revert "UI module security updates" - belongs in base This reverts commit a7cb7cf7f57aad38f0b5e5d67389c187b88ffd94. * Add --target-is-zero support for QemuImg * Allow qemu image options to be passed, API support convert encrypted * Switch hypervisor encryption support detection to use KeyFiles * Fixes for ScaleIO root disk encryption * Resize root disk if it won't fit encryption header * Use cryptsetup to prep raw root disks, when supported * Create qcow2 formatting if necessary during initial template copy to ScaleIO * Allow setting no cache for qemu-img during disk convert * Use 1M sparse on qemu-img convert for zero target disks * UI: Add volume encryption support to hypervisor details * QemuImg use --image-opts and --object depending on version * Only send storage commands that require encryption to hosts that support encryption * Move host encryption detail to a static constant * Update host selection to account for volume encryption support Only attach volumes if encryption requirements are met * Ensure resizeVolume won't allow changing encryption * Catch edge cases for clearing passphrase when volume is removed * Disable volume migration and extraction for encrypted volumes * Register volume secret on destination host during live migration * Fix configdrive path editing during live migration * Ensure configdrive path is edited properly during live migration * Pass along and store volume encryption format during creation * Fixes for rebase * Fix tests after rebase * Add unit tests for DeploymentPlanningManagerImpl to support encryption * Deployment planner tests for encryption support on last host * Add deployment tests for encryption when calling planner * Added Libvirt DiskDef test for encryption details * Add test for KeyFile utility * Add CryptSetup tests * Add QemuImageOptionsTest * add smoke tests for API level changes on create/list offerings * Fix schema upgrade, do disk_offering_view first * Fix UI to show hypervisor encryption support * Load details into hostVO before trying to query them for encryption * Remove whitespace in CreateNetworkOfferingTest * Move QemuImageOptions to use constants for flag keys * Set physical disk encrypt format during createDiskFromTemplate in KVM Agent * Whitespace in AbstractStoragePoolAllocator * Fix whitespace in VolumeDaoImpl * Support old Qemu in convert * Log how long it takes to generate a passphrase during volume creation * Move passphrase generation to async portion of createVolume * Revert "Allow agent.properties to define the hypervisor's private IP" This reverts commit 6ea9377505f0e5ff9839156771a241aaa1925e70. * Updated ScaleIO/PowerFlex storage plugin to support separate (storage) network for Host(KVM) SDC connection. (#144) * Added smoke tests for volume encryption (in KVM). (#149) * Updated ScaleIO pool unit tests. * Some improvements/fixes for code smells (in ScaleIO storage plugin). * Updated review changes for ScaleIO improvements. * Updated host response parameter 'encryptionsupported' in the UI. * Move passphrase generation for the volume to async portion, while deploying VM (#158) * Move passphrase generation for the volume to async portion, while deploying VM. * Updated logs, to include volume details. * Fix schema upgrade, create passphrase table first * Fixed the DB upgrade issue (as noticed in the logs below.) DEBUG [c.c.u.d.ScriptRunner] (main:null) (logid:) CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'passphrase', 'id') ERROR [c.c.u.d.ScriptRunner] (main:null) (logid:) Error executing: CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'passphrase', 'id') ERROR [c.c.u.d.ScriptRunner] (main:null) (logid:) java.sql.SQLException: Failed to open the referenced table 'passphrase' ERROR [c.c.u.DatabaseUpgradeChecker] (main:null) (logid:) Unable to execute upgrade script * Fixes for snapshots with encrypted qcow2 Fixes #159 #160 #163 * Support create/delete encrypted snapshots of encrypted qcow2 volumes * Select endpoints that support encryption when snapshotting encrypted volumes * Update revert snapshot to be compatible with encrypted snapshots * Disallow volume and template create from encrypted vols/snapshots * Disallow VM memory snapshots on encrypted vols. Fixes #157 * Fix for TemplateManagerImpl unit test failure * Support offline resize of encrypted volumes. Fixes #168 * Fix for resize volume unit tests * Updated libvirt resize volume unit tests * Support volume encryption on kvm only, and passphrase generation refactor (#169) * Fail deploy VM when ROOT/DATA volume's offering has encryption enabled, on non-KVM hypervisors * Fail attach volume when volume's offering has encryption enabled, on non-KVM hypervisors * Refactor passphrase generation for volume * Apply encryption to dest volume for live local storage migration fixes #161 * Apply encryption to data volumes during live storage migration Fixes #161 * Use the same encryption passphrase id for migrating volumes * Pass secret consumer during storage migration prepare Fix for #161 * Fixes create / delete volume snapshot issue, for stopped VMs * Block volume snapshot if encrypted and VM is running Fixes #159 * Block snap schedules on encrypted volumes Fix for #159 * Support cryptsetup where luks type defaults to 2 Fixes #170 * Modify domain XML secret UUID when storage migrating VM Fix for #172 * Remove any libvirt secrets on VM stop and post migration Fix for #172 * Update disk profile with encryption requirement from the disk offering (#176) Update disk profile with encryption requirement from the disk offering and some code improvements * Updated review changes / javadoc in ScaleIOUtil Co-authored-by: Marcus Sorensen Co-authored-by: Suresh Kumar Anaparti Co-authored-by: Suresh Kumar Anaparti --- .../java/com/cloud/agent/api/to/DiskTO.java | 1 + api/src/main/java/com/cloud/host/Host.java | 1 + .../java/com/cloud/offering/DiskOffering.java | 4 + .../main/java/com/cloud/storage/Storage.java | 44 +- .../main/java/com/cloud/storage/Volume.java | 8 + .../main/java/com/cloud/vm/DiskProfile.java | 12 + .../apache/cloudstack/api/ApiConstants.java | 3 + .../admin/offering/CreateDiskOfferingCmd.java | 12 + .../offering/CreateServiceOfferingCmd.java | 11 + .../user/offering/ListDiskOfferingsCmd.java | 8 + .../offering/ListServiceOfferingsCmd.java | 8 + .../user/snapshot/CreateSnapshotCmd.java | 4 + .../api/response/DiskOfferingResponse.java | 6 + .../cloudstack/api/response/HostResponse.java | 15 + .../api/response/ServiceOfferingResponse.java | 6 + .../agent/api/ModifyStoragePoolAnswer.java | 8 +- .../agent/api/ModifyStoragePoolCommand.java | 15 + .../api/storage/ResizeVolumeCommand.java | 24 + .../cloudstack/storage/to/VolumeObjectTO.java | 23 + .../api/storage/EndPointSelector.java | 8 + .../subsystem/api/storage/VolumeInfo.java | 2 + .../com/cloud/storage/StorageManager.java | 2 +- .../orchestration/VolumeOrchestrator.java | 53 +- .../com/cloud/storage/DiskOfferingVO.java | 9 + .../main/java/com/cloud/storage/VolumeVO.java | 19 +- .../java/com/cloud/storage/dao/VolumeDao.java | 7 + .../com/cloud/storage/dao/VolumeDaoImpl.java | 13 + .../cloudstack/secret/PassphraseVO.java | 55 ++ .../cloudstack/secret/dao/PassphraseDao.java | 7 + .../secret/dao/PassphraseDaoImpl.java | 7 + ...spring-engine-schema-core-daos-context.xml | 1 + .../db/schema-41600to41610-cleanup.sql | 2 +- .../META-INF/db/schema-41600to41610.sql | 179 ++++++ .../motion/AncientDataMotionStrategy.java | 38 +- .../storage/motion/DataMotionServiceImpl.java | 10 + .../StorageSystemDataMotionStrategy.java | 36 +- .../AbstractStoragePoolAllocator.java | 21 +- .../endpoint/DefaultEndPointSelector.java | 56 +- .../storage/volume/VolumeObject.java | 70 ++- .../storage/volume/VolumeServiceImpl.java | 8 + plugins/hypervisors/kvm/pom.xml | 45 +- .../kvm/rn2-hyperd-lapp01.rno.apple.com | Bin 0 -> 599851 bytes .../resource/LibvirtComputingResource.java | 128 +++- .../kvm/resource/LibvirtDomainXMLParser.java | 10 + .../kvm/resource/LibvirtSecretDef.java | 4 + .../hypervisor/kvm/resource/LibvirtVMDef.java | 27 +- .../wrapper/LibvirtCreateCommandWrapper.java | 4 +- ...ivateTemplateFromVolumeCommandWrapper.java | 2 +- .../wrapper/LibvirtMigrateCommandWrapper.java | 13 + ...ibvirtModifyStoragePoolCommandWrapper.java | 4 +- ...virtPrepareForMigrationCommandWrapper.java | 20 +- .../LibvirtResizeVolumeCommandWrapper.java | 85 ++- .../LibvirtRevertSnapshotCommandWrapper.java | 20 +- .../wrapper/LibvirtStopCommandWrapper.java | 4 + .../kvm/storage/IscsiAdmStorageAdaptor.java | 22 +- .../kvm/storage/IscsiAdmStoragePool.java | 9 +- .../kvm/storage/KVMPhysicalDisk.java | 10 + .../kvm/storage/KVMStoragePool.java | 6 +- .../kvm/storage/KVMStoragePoolManager.java | 50 +- .../kvm/storage/KVMStorageProcessor.java | 221 +++++-- .../kvm/storage/LibvirtStorageAdaptor.java | 121 ++-- .../kvm/storage/LibvirtStoragePool.java | 13 +- .../kvm/storage/LinstorStorageAdaptor.java | 59 +- .../kvm/storage/LinstorStoragePool.java | 16 +- .../kvm/storage/ManagedNfsStorageAdaptor.java | 13 +- .../kvm/storage/ScaleIOStorageAdaptor.java | 143 ++++- .../kvm/storage/ScaleIOStoragePool.java | 35 +- .../kvm/storage/StorageAdaptor.java | 9 +- .../utils/cryptsetup/CryptSetup.java | 108 ++++ .../utils/cryptsetup/CryptSetupException.java | 9 + .../cloudstack/utils/cryptsetup/KeyFile.java | 60 ++ .../utils/qemu/QemuImageOptions.java | 65 ++ .../apache/cloudstack/utils/qemu/QemuImg.java | 303 ++++++++-- .../cloudstack/utils/qemu/QemuObject.java | 132 +++++ .../LibvirtComputingResourceTest.java | 44 +- .../resource/LibvirtDomainXMLParserTest.java | 20 + .../kvm/resource/LibvirtVMDefTest.java | 20 + .../LibvirtMigrateCommandWrapperTest.java | 35 ++ .../kvm/storage/ScaleIOStoragePoolTest.java | 75 ++- .../utils/cryptsetup/CryptSetupTest.java | 53 ++ .../utils/cryptsetup/KeyFileTest.java | 31 + .../utils/qemu/QemuImageOptionsTest.java | 43 ++ .../cloudstack/utils/qemu/QemuImgTest.java | 40 +- .../cloudstack/utils/qemu/QemuObjectTest.java | 41 ++ .../CloudStackPrimaryDataStoreDriverImpl.java | 55 +- .../client/ScaleIOGatewayClient.java | 8 +- .../client/ScaleIOGatewayClientImpl.java | 39 +- .../driver/ScaleIOPrimaryDataStoreDriver.java | 219 +++++-- .../ScaleIOPrimaryDataStoreLifeCycle.java | 51 +- .../provider/ScaleIOHostListener.java | 68 ++- .../storage/datastore/util/ScaleIOUtil.java | 74 ++- .../ScaleIOPrimaryDataStoreLifeCycleTest.java | 7 +- .../com/cloud/api/query/QueryManagerImpl.java | 10 + .../query/dao/DiskOfferingJoinDaoImpl.java | 1 + .../query/dao/ServiceOfferingJoinDaoImpl.java | 1 + .../api/query/vo/DiskOfferingJoinVO.java | 5 + .../api/query/vo/ServiceOfferingJoinVO.java | 5 + .../ConfigurationManagerImpl.java | 12 +- .../deploy/DeploymentPlanningManagerImpl.java | 56 +- .../com/cloud/storage/StorageManagerImpl.java | 14 +- .../cloud/storage/VolumeApiServiceImpl.java | 47 +- .../storage/snapshot/SnapshotManagerImpl.java | 12 + .../cloud/template/TemplateManagerImpl.java | 10 + .../java/com/cloud/vm/UserVmManagerImpl.java | 9 + .../vm/snapshot/VMSnapshotManagerImpl.java | 6 + .../DeploymentPlanningManagerImplTest.java | 360 ++++++++++- .../storage/VolumeApiServiceImplTest.java | 56 +- .../listener/StoragePoolMonitorTest.java | 2 +- .../test/resources/createNetworkOffering.xml | 1 + test/integration/smoke/test_disk_offerings.py | 50 +- .../smoke/test_service_offerings.py | 54 +- test/integration/smoke/test_volumes.py | 557 +++++++++++++++++- ui/package.json | 4 +- ui/public/locales/en.json | 3 + ui/src/config/section/offering.js | 4 +- ui/src/views/infra/HostInfo.vue | 8 + ui/src/views/offering/AddComputeOffering.vue | 8 +- ui/src/views/offering/AddDiskOffering.vue | 8 +- .../main/java/com/cloud/utils/UuidUtils.java | 5 +- 119 files changed, 4306 insertions(+), 511 deletions(-) create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/secret/PassphraseVO.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDao.java create mode 100644 engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDaoImpl.java create mode 100644 plugins/hypervisors/kvm/rn2-hyperd-lapp01.rno.apple.com create mode 100644 plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetup.java create mode 100644 plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupException.java create mode 100644 plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/KeyFile.java create mode 100644 plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImageOptions.java create mode 100644 plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuObject.java create mode 100644 plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupTest.java create mode 100644 plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/KeyFileTest.java create mode 100644 plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImageOptionsTest.java create mode 100644 plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuObjectTest.java diff --git a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java index 7b3d10bc4db..d22df2df172 100644 --- a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java @@ -40,6 +40,7 @@ public class DiskTO { public static final String VMDK = "vmdk"; public static final String EXPAND_DATASTORE = "expandDatastore"; public static final String TEMPLATE_RESIGN = "templateResign"; + public static final String SECRET_CONSUMER_DETAIL = "storageMigrateSecretConsumer"; private DataTO data; private Long diskSeq; diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index e5a3889ff18..7563bc3b742 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -53,6 +53,7 @@ public interface Host extends StateObject, Identity, Partition, HAResour } } public static final String HOST_UEFI_ENABLE = "host.uefi.enable"; + public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; /** * @return name of the machine. diff --git a/api/src/main/java/com/cloud/offering/DiskOffering.java b/api/src/main/java/com/cloud/offering/DiskOffering.java index fd21118e4a2..13ba9a31394 100644 --- a/api/src/main/java/com/cloud/offering/DiskOffering.java +++ b/api/src/main/java/com/cloud/offering/DiskOffering.java @@ -153,4 +153,8 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId void setCacheMode(DiskCacheMode cacheMode); Type getType(); + + boolean getEncrypt(); + + void setEncrypt(boolean encrypt); } diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index d44628a198a..f053620ba51 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -130,32 +130,34 @@ public class Storage { } public static enum StoragePoolType { - Filesystem(false, true), // local directory - NetworkFilesystem(true, true), // NFS - IscsiLUN(true, false), // shared LUN, with a clusterfs overlay - Iscsi(true, false), // for e.g., ZFS Comstar - ISO(false, false), // for iso image - LVM(false, false), // XenServer local LVM SR - CLVM(true, false), - RBD(true, true), // http://libvirt.org/storage.html#StorageBackendRBD - SharedMountPoint(true, false), - VMFS(true, true), // VMware VMFS storage - PreSetup(true, true), // for XenServer, Storage Pool is set up by customers. - EXT(false, true), // XenServer local EXT SR - OCFS2(true, false), - SMB(true, false), - Gluster(true, false), - PowerFlex(true, true), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS) - ManagedNFS(true, false), - Linstor(true, true), - DatastoreCluster(true, true); // for VMware, to abstract pool of clusters + Filesystem(false, true, true), // local directory + NetworkFilesystem(true, true, true), // NFS + IscsiLUN(true, false, false), // shared LUN, with a clusterfs overlay + Iscsi(true, false, false), // for e.g., ZFS Comstar + ISO(false, false, false), // for iso image + LVM(false, false, false), // XenServer local LVM SR + CLVM(true, false, false), + RBD(true, true, false), // http://libvirt.org/storage.html#StorageBackendRBD + SharedMountPoint(true, false, true), + VMFS(true, true, false), // VMware VMFS storage + PreSetup(true, true, false), // for XenServer, Storage Pool is set up by customers. + EXT(false, true, false), // XenServer local EXT SR + OCFS2(true, false, false), + SMB(true, false, false), + Gluster(true, false, false), + PowerFlex(true, true, true), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS) + ManagedNFS(true, false, false), + Linstor(true, true, false), + DatastoreCluster(true, true, false); // for VMware, to abstract pool of clusters private final boolean shared; private final boolean overprovisioning; + private final boolean encryption; - StoragePoolType(boolean shared, boolean overprovisioning) { + StoragePoolType(boolean shared, boolean overprovisioning, boolean encryption) { this.shared = shared; this.overprovisioning = overprovisioning; + this.encryption = encryption; } public boolean isShared() { @@ -165,6 +167,8 @@ public class Storage { public boolean supportsOverProvisioning() { return overprovisioning; } + + public boolean supportsEncryption() { return encryption; } } public static List getNonSharedStoragePoolTypes() { diff --git a/api/src/main/java/com/cloud/storage/Volume.java b/api/src/main/java/com/cloud/storage/Volume.java index a863c3e989b..32d1f3f54be 100644 --- a/api/src/main/java/com/cloud/storage/Volume.java +++ b/api/src/main/java/com/cloud/storage/Volume.java @@ -243,4 +243,12 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba boolean isDisplay(); boolean isDeployAsIs(); + + public Long getPassphraseId(); + + public void setPassphraseId(Long id); + + public String getEncryptFormat(); + + public void setEncryptFormat(String encryptFormat); } diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java index 175a92afaf9..7e0484de0e5 100644 --- a/api/src/main/java/com/cloud/vm/DiskProfile.java +++ b/api/src/main/java/com/cloud/vm/DiskProfile.java @@ -42,6 +42,7 @@ public class DiskProfile { private Long iopsReadRate; private Long iopsWriteRate; private String cacheMode; + private boolean requiresEncryption; private HypervisorType hyperType; @@ -61,6 +62,12 @@ public class DiskProfile { this.volumeId = volumeId; } + public DiskProfile(long volumeId, Volume.Type type, String name, long diskOfferingId, long size, String[] tags, boolean useLocalStorage, boolean recreatable, + Long templateId, boolean requiresEncryption) { + this(volumeId, type, name, diskOfferingId, size, tags, useLocalStorage, recreatable, templateId); + this.requiresEncryption = requiresEncryption; + } + public DiskProfile(Volume vol, DiskOffering offering, HypervisorType hyperType) { this(vol.getId(), vol.getVolumeType(), @@ -73,6 +80,7 @@ public class DiskProfile { null); this.hyperType = hyperType; this.provisioningType = offering.getProvisioningType(); + this.requiresEncryption = offering.getEncrypt() || vol.getPassphraseId() != null; } public DiskProfile(DiskProfile dp) { @@ -227,4 +235,8 @@ public class DiskProfile { public String getCacheMode() { return cacheMode; } + + public boolean requiresEncryption() { return requiresEncryption; } + + public void setEncryption(boolean encrypt) { this.requiresEncryption = encrypt; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 7580bc238d7..4f80e47b9e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -105,6 +105,9 @@ public class ApiConstants { public static final String CUSTOM_JOB_ID = "customjobid"; public static final String CURRENT_START_IP = "currentstartip"; public static final String CURRENT_END_IP = "currentendip"; + public static final String ENCRYPT = "encrypt"; + public static final String ENCRYPT_ROOT = "encryptroot"; + public static final String ENCRYPTION_SUPPORTED = "encryptionsupported"; public static final String MIN_IOPS = "miniops"; public static final String MAX_IOPS = "maxiops"; public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index 4a25cef26c2..45a258f144d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -160,9 +160,14 @@ public class CreateDiskOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.STORAGE_POLICY, type = CommandType.UUID, entityType = VsphereStoragePoliciesResponse.class,required = false, description = "Name of the storage policy defined at vCenter, this is applicable only for VMware", since = "4.15") private Long storagePolicy; + @Parameter(name = ApiConstants.ENCRYPT, type = CommandType.BOOLEAN, required=false, description = "Volumes using this offering should be encrypted") + private Boolean encrypt; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "details to specify disk offering parameters", since = "4.16") private Map details; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -199,6 +204,13 @@ public class CreateDiskOfferingCmd extends BaseCmd { return maxIops; } + public boolean getEncrypt() { + if (encrypt == null) { + return false; + } + return encrypt; + } + public List getDomainIds() { if (CollectionUtils.isNotEmpty(domainIds)) { Set set = new LinkedHashSet<>(domainIds); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index b30156097f3..e7a3fb562ff 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -227,6 +227,9 @@ public class CreateServiceOfferingCmd extends BaseCmd { description = "true if virtual machine needs to be dynamically scalable of cpu or memory") protected Boolean isDynamicScalingEnabled; + @Parameter(name = ApiConstants.ENCRYPT_ROOT, type = CommandType.BOOLEAN, description = "VMs using this offering require root volume encryption", since="4.16") + private Boolean encryptRoot; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -449,6 +452,14 @@ public class CreateServiceOfferingCmd extends BaseCmd { return isDynamicScalingEnabled == null ? true : isDynamicScalingEnabled; } + + public boolean getEncryptRoot() { + if (encryptRoot != null) { + return encryptRoot; + } + return false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java index 92b8676b469..d01d85d3928 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java @@ -50,6 +50,12 @@ public class ListDiskOfferingsCmd extends BaseListDomainResourcesCmd { since = "4.13") private Long zoneId; + @Parameter(name = ApiConstants.ENCRYPT, + type = CommandType.BOOLEAN, + description = "listed offerings support disk encryption", + since = "4.16") + private Boolean encrypt; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +72,8 @@ public class ListDiskOfferingsCmd extends BaseListDomainResourcesCmd { return zoneId; } + public Boolean getEncrypt() { return encrypt; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java index 91cac0937d4..517b678d36d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java @@ -83,6 +83,12 @@ public class ListServiceOfferingsCmd extends BaseListDomainResourcesCmd { since = "4.15") private Integer cpuSpeed; + @Parameter(name = ApiConstants.ENCRYPT_ROOT, + type = CommandType.BOOLEAN, + description = "listed offerings support root disk encryption", + since = "4.16") + private Boolean encryptRoot; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -123,6 +129,8 @@ public class ListServiceOfferingsCmd extends BaseListDomainResourcesCmd { return cpuSpeed; } + public Boolean getEncryptRoot() { return encryptRoot; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index c9215703880..1cc806f6f8e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -226,6 +226,10 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Snapshot from volume [%s] was not found in database.", getVolumeUuid())); } } catch (Exception e) { + if (e.getCause() instanceof UnsupportedOperationException) { + throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, String.format("Failed to create snapshot due to unsupported operation: %s", e.getCause().getMessage())); + } + String errorMessage = "Failed to create snapshot due to an internal error creating snapshot for volume " + getVolumeUuid(); s_logger.error(errorMessage, e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java index 5f61060a014..8054405e6cd 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java @@ -156,6 +156,10 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "the vsphere storage policy tagged to the disk offering in case of VMware", since = "4.15") private String vsphereStoragePolicy; + @SerializedName(ApiConstants.ENCRYPT) + @Param(description = "Whether disks using this offering will be encrypted on primary storage") + private Boolean encrypt; + @SerializedName(ApiConstants.DETAILS) @Param(description = "additional key/value details tied with this disk offering", since = "4.16.1") private Map details; @@ -369,6 +373,8 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { this.vsphereStoragePolicy = vsphereStoragePolicy; } + public void setEncrypt(Boolean encrypt) { this.encrypt = encrypt; } + public void setDetails(Map details) { this.details = details; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java index fcf0870bcdd..69c25c5fa79 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java @@ -270,6 +270,10 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "true if the host has capability to support UEFI boot") private Boolean uefiCapabilty; + @SerializedName(ApiConstants.ENCRYPTION_SUPPORTED) + @Param(description = "true if the host supports encryption") + private Boolean encryptionSupported; + @Override public String getObjectId() { return this.getId(); @@ -540,6 +544,13 @@ public class HostResponse extends BaseResponseWithAnnotations { this.setUefiCapabilty(new Boolean(false)); // in case of existing host which is not scanned for UEFI capability } + if (detailsCopy.containsKey(Host.HOST_VOLUME_ENCRYPTION)) { + this.setEncryptionSupported(Boolean.parseBoolean((String) detailsCopy.get(Host.HOST_VOLUME_ENCRYPTION))); + detailsCopy.remove(Host.HOST_VOLUME_ENCRYPTION); + } else { + this.setEncryptionSupported(new Boolean(false)); // default + } + this.details = detailsCopy; } @@ -725,4 +736,8 @@ public class HostResponse extends BaseResponseWithAnnotations { public void setUefiCapabilty(Boolean hostCapability) { this.uefiCapabilty = hostCapability; } + + public void setEncryptionSupported(Boolean encryptionSupported) { + this.encryptionSupported = encryptionSupported; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index ea9d8eef7a0..224ff0b6577 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -208,6 +208,10 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if virtual machine needs to be dynamically scalable of cpu or memory", since = "4.16") private Boolean dynamicScalingEnabled; + @SerializedName(ApiConstants.ENCRYPT_ROOT) + @Param(description = "true if virtual machine root disk will be encrypted on storage", since = "4.16") + private Boolean encryptRoot; + public ServiceOfferingResponse() { } @@ -486,4 +490,6 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public void setEncryptRoot(Boolean encrypt) { this.encryptRoot = encrypt; } } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java index be84cce152d..552ffb85aaf 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java @@ -30,14 +30,18 @@ public class ModifyStoragePoolAnswer extends Answer { private Map templateInfo; private String localDatastoreName; private String poolType; - private List datastoreClusterChildren = new ArrayList<>();; + private List datastoreClusterChildren = new ArrayList<>(); public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, long capacityBytes, long availableBytes, Map tInfo) { + this(cmd, capacityBytes, availableBytes, tInfo, null); + } + + public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, long capacityBytes, long availableBytes, Map tInfo, Map details) { super(cmd); result = true; - poolInfo = new StoragePoolInfo(null, cmd.getPool().getHost(), cmd.getPool().getPath(), cmd.getLocalPath(), cmd.getPool().getType(), capacityBytes, availableBytes); + poolInfo = new StoragePoolInfo(null, cmd.getPool().getHost(), cmd.getPool().getPath(), cmd.getLocalPath(), cmd.getPool().getType(), capacityBytes, availableBytes, details); templateInfo = tInfo; } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java index c2ab0ab7f36..ad05fe1d615 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java @@ -20,6 +20,7 @@ package com.cloud.agent.api; import java.io.File; +import java.util.Map; import java.util.UUID; import com.cloud.agent.api.to.StorageFilerTO; @@ -32,6 +33,7 @@ public class ModifyStoragePoolCommand extends Command { private StorageFilerTO pool; private String localPath; private String storagePath; + private Map details; public ModifyStoragePoolCommand(boolean add, StoragePool pool, String localPath) { this.add = add; @@ -39,6 +41,11 @@ public class ModifyStoragePoolCommand extends Command { this.localPath = localPath; } + public ModifyStoragePoolCommand(boolean add, StoragePool pool, String localPath, Map details) { + this(add, pool, localPath); + this.details = details; + } + public ModifyStoragePoolCommand(boolean add, StoragePool pool) { this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); } @@ -67,6 +74,14 @@ public class ModifyStoragePoolCommand extends Command { return storagePath; } + public void setDetails(Map details) { + this.details = details; + } + + public Map getDetails() { + return details; + } + @Override public boolean executeInSequence() { return false; diff --git a/core/src/main/java/com/cloud/agent/api/storage/ResizeVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/ResizeVolumeCommand.java index 70d4d3ebab4..db867698e91 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/ResizeVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/ResizeVolumeCommand.java @@ -20,8 +20,11 @@ package com.cloud.agent.api.storage; import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; import com.cloud.agent.api.to.StorageFilerTO; +import java.util.Arrays; + public class ResizeVolumeCommand extends Command { private String path; private StorageFilerTO pool; @@ -35,6 +38,10 @@ public class ResizeVolumeCommand extends Command { private boolean managed; private String iScsiName; + @LogLevel(LogLevel.Log4jLevel.Off) + private byte[] passphrase; + private String encryptFormat; + protected ResizeVolumeCommand() { } @@ -48,6 +55,13 @@ public class ResizeVolumeCommand extends Command { this.managed = false; } + public ResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance, + String chainInfo, byte[] passphrase, String encryptFormat) { + this(path, pool, currentSize, newSize, shrinkOk, vmInstance, chainInfo); + this.passphrase = passphrase; + this.encryptFormat = encryptFormat; + } + public ResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance, String chainInfo) { this(path, pool, currentSize, newSize, shrinkOk, vmInstance); this.chainInfo = chainInfo; @@ -89,6 +103,16 @@ public class ResizeVolumeCommand extends Command { public String getChainInfo() {return chainInfo; } + public String getEncryptFormat() { return encryptFormat; } + + public byte[] getPassphrase() { return passphrase; } + + public void clearPassphrase() { + if (this.passphrase != null) { + Arrays.fill(this.passphrase, (byte) 0); + } + } + /** * {@inheritDoc} */ diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java index 36c35e57273..7fb12d9afd0 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.storage.to; +import com.cloud.agent.api.LogLevel; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import com.cloud.agent.api.to.DataObjectType; @@ -30,6 +31,8 @@ import com.cloud.storage.MigrationOptions; import com.cloud.storage.Storage; import com.cloud.storage.Volume; +import java.util.Arrays; + public class VolumeObjectTO implements DataTO { private String uuid; private Volume.Type volumeType; @@ -68,6 +71,10 @@ public class VolumeObjectTO implements DataTO { private String updatedDataStoreUUID; private String vSphereStoragePolicyId; + @LogLevel(LogLevel.Log4jLevel.Off) + private byte[] passphrase; + private String encryptFormat; + public VolumeObjectTO() { } @@ -110,6 +117,8 @@ public class VolumeObjectTO implements DataTO { this.directDownload = volume.isDirectDownload(); this.deployAsIs = volume.isDeployAsIs(); this.vSphereStoragePolicyId = volume.getvSphereStoragePolicyId(); + this.passphrase = volume.getPassphrase(); + this.encryptFormat = volume.getEncryptFormat(); } public String getUuid() { @@ -357,4 +366,18 @@ public class VolumeObjectTO implements DataTO { public void setvSphereStoragePolicyId(String vSphereStoragePolicyId) { this.vSphereStoragePolicyId = vSphereStoragePolicyId; } + + public String getEncryptFormat() { return encryptFormat; } + + public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; } + + public byte[] getPassphrase() { return passphrase; } + + public void setPassphrase(byte[] passphrase) { this.passphrase = passphrase; } + + public void clearPassphrase() { + if (this.passphrase != null) { + Arrays.fill(this.passphrase, (byte) 0); + } + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java index ec272501998..6f6e79d067e 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java @@ -23,14 +23,22 @@ import java.util.List; public interface EndPointSelector { EndPoint select(DataObject srcData, DataObject destData); + EndPoint select(DataObject srcData, DataObject destData, boolean encryptionSupportRequired); + EndPoint select(DataObject srcData, DataObject destData, StorageAction action); + EndPoint select(DataObject srcData, DataObject destData, StorageAction action, boolean encryptionSupportRequired); + EndPoint select(DataObject object); EndPoint select(DataStore store); + EndPoint select(DataObject object, boolean encryptionSupportRequired); + EndPoint select(DataObject object, StorageAction action); + EndPoint select(DataObject object, StorageAction action, boolean encryptionSupportRequired); + List selectAll(DataStore store); List findAllEndpointsForScope(DataStore store); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index eafc3b7e85c..a22b66af193 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -92,4 +92,6 @@ public interface VolumeInfo extends DataObject, Volume { String getDeployAsIsConfiguration(); public String getvSphereStoragePolicyId(); + + public byte[] getPassphrase(); } diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index c00273ee762..821809b39f4 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -267,7 +267,7 @@ public interface StorageManager extends StorageService { boolean registerHostListener(String providerUuid, HypervisorHostListener listener); - void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException; + boolean connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException; void disconnectHostFromSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException; diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 9c1e998459d..e42720266f9 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -37,6 +37,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.secret.dao.PassphraseDao; +import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd; import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin; import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd; @@ -233,6 +235,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati private SecondaryStorageVmDao secondaryStorageVmDao; @Inject VolumeApiService _volumeApiService; + @Inject + PassphraseDao _passphraseDao; private final StateMachine2 _volStateMachine; protected List _storagePoolAllocators; @@ -266,7 +270,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati // Find a destination storage pool with the specified criteria DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); DiskProfile dskCh = new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(), - diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null); + diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, (diskOffering.getEncrypt() || volume.getPassphraseId() != null)); dskCh.setHyperType(dataDiskHyperType); storageMgr.setDiskProfileThrottling(dskCh, null, diskOffering); @@ -300,6 +304,13 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati newVol.setInstanceId(oldVol.getInstanceId()); newVol.setRecreatable(oldVol.isRecreatable()); newVol.setFormat(oldVol.getFormat()); + + if (oldVol.getPassphraseId() != null) { + PassphraseVO passphrase =_passphraseDao.persist(new PassphraseVO()); + passphrase.clearPassphrase(); + newVol.setPassphraseId(passphrase.getId()); + } + return _volsDao.persist(newVol); } @@ -414,6 +425,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati Pair pod = null; DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); + if (diskOffering.getEncrypt()) { + VolumeVO vol = (VolumeVO) volume; + volume = setPassphraseForVolumeEncryption(vol); + } DataCenter dc = _entityMgr.findById(DataCenter.class, volume.getDataCenterId()); DiskProfile dskCh = new DiskProfile(volume, diskOffering, snapshot.getHypervisorType()); @@ -576,6 +591,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } protected DiskProfile createDiskCharacteristics(VolumeInfo volume, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) { + boolean requiresEncryption = diskOffering.getEncrypt() || volume.getPassphraseId() != null; if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) { TemplateDataStoreVO ss = _vmTemplateStoreDao.findByTemplateZoneDownloadStatus(template.getId(), dc.getId(), VMTemplateStorageResourceAssoc.Status.DOWNLOADED); if (ss == null) { @@ -583,10 +599,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } return new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), ss.getSize(), diskOffering.getTagsArray(), diskOffering.isUseLocalStorage(), - diskOffering.isRecreatable(), Storage.ImageFormat.ISO != template.getFormat() ? template.getId() : null); + diskOffering.isRecreatable(), Storage.ImageFormat.ISO != template.getFormat() ? template.getId() : null, requiresEncryption); } else { return new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(), - diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null); + diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, requiresEncryption); } } @@ -640,8 +656,16 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati storageMgr.setDiskProfileThrottling(dskCh, null, diskOffering); } - if (diskOffering != null && diskOffering.isCustomized()) { - dskCh.setSize(size); + if (diskOffering != null) { + if (diskOffering.isCustomized()) { + dskCh.setSize(size); + } + + VolumeVO vol = _volsDao.findById(volume.getId()); + if (diskOffering.getEncrypt()) { + setPassphraseForVolumeEncryption(vol); + volume = volFactory.getVolume(volume.getId()); + } } dskCh.setHyperType(hyperType); @@ -679,7 +703,6 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati throw new CloudRuntimeException("create volume failed:" + result.getResult()); } } - return result.getVolume(); } catch (InterruptedException e) { s_logger.error("create volume failed", e); @@ -1524,6 +1547,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati destPool = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); } if (vol.getState() == Volume.State.Allocated || vol.getState() == Volume.State.Creating) { + DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, vol.getDiskOfferingId()); + if (diskOffering.getEncrypt()) { + vol = setPassphraseForVolumeEncryption(vol); + } newVol = vol; } else { newVol = switchVolume(vol, vm); @@ -1631,6 +1658,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati return new Pair(newVol, destPool); } + private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) { + if (volume.getPassphraseId() != null) { + return volume; + } + s_logger.debug("Creating passphrase for the volume: " + volume.getName()); + long startTime = System.currentTimeMillis(); + PassphraseVO passphrase = _passphraseDao.persist(new PassphraseVO()); + passphrase.clearPassphrase(); + volume.setPassphraseId(passphrase.getId()); + long finishTime = System.currentTimeMillis(); + s_logger.debug("Creating and persisting passphrase took: " + (finishTime - startTime) + " ms for the volume: " + volume.toString()); + return _volsDao.persist(volume); + } + @Override public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException, StorageAccessException { diff --git a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java index 952d3014209..0e6bf0a431b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java @@ -138,6 +138,8 @@ public class DiskOfferingVO implements DiskOffering { @Column(name = "iops_write_rate_max_length") private Long iopsWriteRateMaxLength; + @Column(name = "encrypt") + private boolean encrypt; @Column(name = "cache_mode", updatable = true, nullable = false) @Enumerated(value = EnumType.STRING) @@ -585,7 +587,14 @@ public class DiskOfferingVO implements DiskOffering { return hypervisorSnapshotReserve; } + @Override + public boolean getEncrypt() { return encrypt; } + + @Override + public void setEncrypt(boolean encrypt) { this.encrypt = encrypt; } + public boolean isShared() { return !useLocalStorage; } + } diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index 1d8611625b7..43b3fc4191e 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -32,11 +32,12 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @Entity @Table(name = "volumes") @@ -170,6 +171,12 @@ public class VolumeVO implements Volume { @Transient private boolean deployAsIs; + @Column(name = "passphrase_id") + private Long passphraseId; + + @Column(name = "encrypt_format") + private String encryptFormat; + // Real Constructor public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size, @@ -496,7 +503,7 @@ public class VolumeVO implements Volume { @Override public String toString() { - return new StringBuilder("Vol[").append(id).append("|vm=").append(instanceId).append("|").append(volumeType).append("]").toString(); + return new StringBuilder("Vol[").append(id).append("|name=").append(name).append("|vm=").append(instanceId).append("|").append(volumeType).append("]").toString(); } @Override @@ -648,4 +655,12 @@ public class VolumeVO implements Volume { public String getVolumeDescription(){ return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "uuid"); } + + public Long getPassphraseId() { return passphraseId; } + + public void setPassphraseId(Long id) { this.passphraseId = id; } + + public String getEncryptFormat() { return encryptFormat; } + + public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java index 9eb623a7bd6..417695b8f2d 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -102,6 +102,13 @@ public interface VolumeDao extends GenericDao, StateDao findIncludingRemovedByZone(long zoneId); + /** + * Lists all volumes using a given passphrase ID + * @param passphraseId + * @return list of volumes + */ + List listVolumesByPassphraseId(long passphraseId); + /** * Gets the Total Primary Storage space allocated for an account * diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index d934f80dc4e..d6160c4d586 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -25,6 +25,7 @@ import java.util.List; import javax.inject.Inject; +import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -68,6 +69,8 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected GenericSearchBuilder secondaryStorageSearch; @Inject ResourceTagDao _tagsDao; + @Inject + PassphraseDao _passphraseDao; protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; // need to account for zone-wide primary storage where storage_pool has @@ -373,6 +376,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol AllFieldsSearch.and("updateTime", AllFieldsSearch.entity().getUpdated(), SearchCriteria.Op.LT); AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ); AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), Op.EQ); + AllFieldsSearch.and("passphraseId", AllFieldsSearch.entity().getPassphraseId(), Op.EQ); AllFieldsSearch.done(); RootDiskStateSearch = createSearchBuilder(); @@ -656,16 +660,25 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol } } + @Override + public List listVolumesByPassphraseId(long passphraseId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("passphraseId", passphraseId); + return listBy(sc); + } + @Override @DB public boolean remove(Long id) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); + s_logger.debug(String.format("Removing volume %s from DB", id)); VolumeVO entry = findById(id); if (entry != null) { _tagsDao.removeByIdAndType(id, ResourceObjectType.Volume); } boolean result = super.remove(id); + txn.commit(); return result; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/secret/PassphraseVO.java b/engine/schema/src/main/java/org/apache/cloudstack/secret/PassphraseVO.java new file mode 100644 index 00000000000..44557b97abb --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/secret/PassphraseVO.java @@ -0,0 +1,55 @@ +package org.apache.cloudstack.secret; + +import com.cloud.utils.db.Encrypt; +import com.cloud.utils.exception.CloudRuntimeException; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Base64; + +@Entity +@Table(name = "passphrase") +public class PassphraseVO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "passphrase") + @Encrypt + private byte[] passphrase; + + public PassphraseVO() { + try { + SecureRandom random = SecureRandom.getInstanceStrong(); + byte[] temporary = new byte[48]; // 48 byte random passphrase buffer + this.passphrase = new byte[64]; // 48 byte random passphrase as base64 for usability + random.nextBytes(temporary); + Base64.getEncoder().encode(temporary, this.passphrase); + Arrays.fill(temporary, (byte) 0); // clear passphrase from buffer + } catch (NoSuchAlgorithmException ex ) { + throw new CloudRuntimeException("Volume encryption requested but system is missing specified algorithm to generate passphrase"); + } + } + + public PassphraseVO(PassphraseVO existing) { + this.passphrase = existing.getPassphrase(); + } + + public void clearPassphrase() { + if (this.passphrase != null) { + Arrays.fill(this.passphrase, (byte) 0); + } + } + + public byte[] getPassphrase() { return this.passphrase; } + + public Long getId() { return this.id; } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDao.java b/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDao.java new file mode 100644 index 00000000000..fa3b1c2d2bd --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDao.java @@ -0,0 +1,7 @@ +package org.apache.cloudstack.secret.dao; + +import org.apache.cloudstack.secret.PassphraseVO; +import com.cloud.utils.db.GenericDao; + +public interface PassphraseDao extends GenericDao { +} \ No newline at end of file diff --git a/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDaoImpl.java new file mode 100644 index 00000000000..f54d1e8f8b8 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/secret/dao/PassphraseDaoImpl.java @@ -0,0 +1,7 @@ +package org.apache.cloudstack.secret.dao; + +import org.apache.cloudstack.secret.PassphraseVO; +import com.cloud.utils.db.GenericDaoBase; + +public class PassphraseDaoImpl extends GenericDaoBase implements PassphraseDao { +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 508b01c2b57..3447657ae3a 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -295,4 +295,5 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610-cleanup.sql index 9db01dd374a..9993611d497 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610-cleanup.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610-cleanup.sql @@ -17,4 +17,4 @@ --; -- Schema upgrade cleanup from 4.16.0.0 to 4.16.1.0 ---; \ No newline at end of file +--; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql index f5934ef8756..894a6a59e07 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql @@ -208,3 +208,182 @@ CREATE VIEW `cloud`.`event_view` AS `cloud`.`projects` ON projects.project_account_id = event.account_id LEFT JOIN `cloud`.`event` eve ON event.start_id = eve.id; + +-- Add passphrase table +CREATE TABLE IF NOT EXISTS `cloud`.`passphrase` ( + `id` bigint unsigned NOT NULL auto_increment, + `passphrase` varchar(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add foreign key procedure to link volumes to passphrase table +DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`; +CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY` ( + IN in_table_name VARCHAR(200), + IN in_foreign_table_name VARCHAR(200), + IN in_foreign_column_name VARCHAR(200) +) +BEGIN + DECLARE CONTINUE HANDLER FOR 1005 BEGIN END; SET @ddl = CONCAT('ALTER TABLE ', in_table_name); SET @ddl = CONCAT(@ddl, ' ', ' ADD CONSTRAINT '); SET @ddl = CONCAT(@ddl, 'fk_', in_foreign_table_name, '_', in_foreign_column_name); SET @ddl = CONCAT(@ddl, ' FOREIGN KEY (', in_foreign_table_name, '_', in_foreign_column_name, ')'); SET @ddl = CONCAT(@ddl, ' REFERENCES ', in_foreign_table_name, '(', in_foreign_column_name, ')'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END; + +-- Add passphrase column to volumes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'passphrase_id', 'bigint unsigned DEFAULT NULL COMMENT ''encryption passphrase id'' '); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.volumes', 'passphrase', 'id'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'encrypt_format', 'varchar(64) DEFAULT NULL COMMENT ''encryption format'' '); + +-- Add encrypt column to disk_offering +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.disk_offering', 'encrypt', 'tinyint(1) DEFAULT 0 COMMENT ''volume encrypt requested'' '); + +DROP VIEW IF EXISTS `cloud`.`disk_offering_view`; +CREATE VIEW `cloud`.`disk_offering_view` AS +SELECT + `disk_offering`.`id` AS `id`, + `disk_offering`.`uuid` AS `uuid`, + `disk_offering`.`name` AS `name`, + `disk_offering`.`display_text` AS `display_text`, + `disk_offering`.`provisioning_type` AS `provisioning_type`, + `disk_offering`.`disk_size` AS `disk_size`, + `disk_offering`.`min_iops` AS `min_iops`, + `disk_offering`.`max_iops` AS `max_iops`, + `disk_offering`.`created` AS `created`, + `disk_offering`.`tags` AS `tags`, + `disk_offering`.`customized` AS `customized`, + `disk_offering`.`customized_iops` AS `customized_iops`, + `disk_offering`.`removed` AS `removed`, + `disk_offering`.`use_local_storage` AS `use_local_storage`, + `disk_offering`.`system_use` AS `system_use`, + `disk_offering`.`hv_ss_reserve` AS `hv_ss_reserve`, + `disk_offering`.`bytes_read_rate` AS `bytes_read_rate`, + `disk_offering`.`bytes_read_rate_max` AS `bytes_read_rate_max`, + `disk_offering`.`bytes_read_rate_max_length` AS `bytes_read_rate_max_length`, + `disk_offering`.`bytes_write_rate` AS `bytes_write_rate`, + `disk_offering`.`bytes_write_rate_max` AS `bytes_write_rate_max`, + `disk_offering`.`bytes_write_rate_max_length` AS `bytes_write_rate_max_length`, + `disk_offering`.`iops_read_rate` AS `iops_read_rate`, + `disk_offering`.`iops_read_rate_max` AS `iops_read_rate_max`, + `disk_offering`.`iops_read_rate_max_length` AS `iops_read_rate_max_length`, + `disk_offering`.`iops_write_rate` AS `iops_write_rate`, + `disk_offering`.`iops_write_rate_max` AS `iops_write_rate_max`, + `disk_offering`.`iops_write_rate_max_length` AS `iops_write_rate_max_length`, + `disk_offering`.`cache_mode` AS `cache_mode`, + `disk_offering`.`sort_key` AS `sort_key`, + `disk_offering`.`type` AS `type`, + `disk_offering`.`display_offering` AS `display_offering`, + `disk_offering`.`state` AS `state`, + `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + `disk_offering`.`encrypt` AS `encrypt`, + GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, + GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, + GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, + GROUP_CONCAT(DISTINCT(domain.path)) AS domain_path, + GROUP_CONCAT(DISTINCT(zone.id)) AS zone_id, + GROUP_CONCAT(DISTINCT(zone.uuid)) AS zone_uuid, + GROUP_CONCAT(DISTINCT(zone.name)) AS zone_name +FROM + `cloud`.`disk_offering` + LEFT JOIN + `cloud`.`disk_offering_details` AS `domain_details` ON `domain_details`.`offering_id` = `disk_offering`.`id` AND `domain_details`.`name`='domainid' + LEFT JOIN + `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) + LEFT JOIN + `cloud`.`disk_offering_details` AS `zone_details` ON `zone_details`.`offering_id` = `disk_offering`.`id` AND `zone_details`.`name`='zoneid' + LEFT JOIN + `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + LEFT JOIN + `cloud`.`disk_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`offering_id` = `disk_offering`.`id` + AND `vsphere_storage_policy`.`name` = 'storagepolicy' +WHERE + `disk_offering`.`state`='Active' +GROUP BY + `disk_offering`.`id`; + +-- Add encrypt field to service_offering_view +DROP VIEW IF EXISTS `cloud`.`service_offering_view`; +CREATE VIEW `cloud`.`service_offering_view` AS + SELECT + `service_offering`.`id` AS `id`, + `disk_offering`.`uuid` AS `uuid`, + `disk_offering`.`name` AS `name`, + `disk_offering`.`display_text` AS `display_text`, + `disk_offering`.`provisioning_type` AS `provisioning_type`, + `disk_offering`.`created` AS `created`, + `disk_offering`.`tags` AS `tags`, + `disk_offering`.`removed` AS `removed`, + `disk_offering`.`use_local_storage` AS `use_local_storage`, + `disk_offering`.`system_use` AS `system_use`, + `disk_offering`.`customized_iops` AS `customized_iops`, + `disk_offering`.`min_iops` AS `min_iops`, + `disk_offering`.`max_iops` AS `max_iops`, + `disk_offering`.`hv_ss_reserve` AS `hv_ss_reserve`, + `disk_offering`.`bytes_read_rate` AS `bytes_read_rate`, + `disk_offering`.`bytes_read_rate_max` AS `bytes_read_rate_max`, + `disk_offering`.`bytes_read_rate_max_length` AS `bytes_read_rate_max_length`, + `disk_offering`.`bytes_write_rate` AS `bytes_write_rate`, + `disk_offering`.`bytes_write_rate_max` AS `bytes_write_rate_max`, + `disk_offering`.`bytes_write_rate_max_length` AS `bytes_write_rate_max_length`, + `disk_offering`.`iops_read_rate` AS `iops_read_rate`, + `disk_offering`.`iops_read_rate_max` AS `iops_read_rate_max`, + `disk_offering`.`iops_read_rate_max_length` AS `iops_read_rate_max_length`, + `disk_offering`.`iops_write_rate` AS `iops_write_rate`, + `disk_offering`.`iops_write_rate_max` AS `iops_write_rate_max`, + `disk_offering`.`iops_write_rate_max_length` AS `iops_write_rate_max_length`, + `disk_offering`.`cache_mode` AS `cache_mode`, + `disk_offering`.`disk_size` AS `root_disk_size`, + `disk_offering`.`encrypt` AS `encrypt_root`, + `service_offering`.`cpu` AS `cpu`, + `service_offering`.`speed` AS `speed`, + `service_offering`.`ram_size` AS `ram_size`, + `service_offering`.`nw_rate` AS `nw_rate`, + `service_offering`.`mc_rate` AS `mc_rate`, + `service_offering`.`ha_enabled` AS `ha_enabled`, + `service_offering`.`limit_cpu_use` AS `limit_cpu_use`, + `service_offering`.`host_tag` AS `host_tag`, + `service_offering`.`default_use` AS `default_use`, + `service_offering`.`vm_type` AS `vm_type`, + `service_offering`.`sort_key` AS `sort_key`, + `service_offering`.`is_volatile` AS `is_volatile`, + `service_offering`.`deployment_planner` AS `deployment_planner`, + `service_offering`.`dynamic_scaling_enabled` AS `dynamic_scaling_enabled`, + `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, + GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, + GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, + GROUP_CONCAT(DISTINCT(domain.path)) AS domain_path, + GROUP_CONCAT(DISTINCT(zone.id)) AS zone_id, + GROUP_CONCAT(DISTINCT(zone.uuid)) AS zone_uuid, + GROUP_CONCAT(DISTINCT(zone.name)) AS zone_name, + IFNULL(`min_compute_details`.`value`, `cpu`) AS min_cpu, + IFNULL(`max_compute_details`.`value`, `cpu`) AS max_cpu, + IFNULL(`min_memory_details`.`value`, `ram_size`) AS min_memory, + IFNULL(`max_memory_details`.`value`, `ram_size`) AS max_memory + FROM + `cloud`.`service_offering` + INNER JOIN + `cloud`.`disk_offering_view` AS `disk_offering` ON service_offering.id = disk_offering.id + LEFT JOIN + `cloud`.`service_offering_details` AS `domain_details` ON `domain_details`.`service_offering_id` = `disk_offering`.`id` AND `domain_details`.`name`='domainid' + LEFT JOIN + `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) + LEFT JOIN + `cloud`.`service_offering_details` AS `zone_details` ON `zone_details`.`service_offering_id` = `disk_offering`.`id` AND `zone_details`.`name`='zoneid' + LEFT JOIN + `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + LEFT JOIN + `cloud`.`service_offering_details` AS `min_compute_details` ON `min_compute_details`.`service_offering_id` = `disk_offering`.`id` + AND `min_compute_details`.`name` = 'mincpunumber' + LEFT JOIN + `cloud`.`service_offering_details` AS `max_compute_details` ON `max_compute_details`.`service_offering_id` = `disk_offering`.`id` + AND `max_compute_details`.`name` = 'maxcpunumber' + LEFT JOIN + `cloud`.`service_offering_details` AS `min_memory_details` ON `min_memory_details`.`service_offering_id` = `disk_offering`.`id` + AND `min_memory_details`.`name` = 'minmemory' + LEFT JOIN + `cloud`.`service_offering_details` AS `max_memory_details` ON `max_memory_details`.`service_offering_id` = `disk_offering`.`id` + AND `max_memory_details`.`name` = 'maxmemory' + LEFT JOIN + `cloud`.`service_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`service_offering_id` = `disk_offering`.`id` + AND `vsphere_storage_policy`.`name` = 'storagepolicy' + WHERE + `disk_offering`.`state`='Active' + GROUP BY + `service_offering`.`id`; diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 74de7652a1f..0d85f5e07cd 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -315,7 +315,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { protected Answer cloneVolume(DataObject template, DataObject volume) { CopyCommand cmd = new CopyCommand(template.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(volume.getTO()), 0, VirtualMachineManager.ExecuteInSequence.value()); try { - EndPoint ep = selector.select(volume.getDataStore()); + EndPoint ep = selector.select(volume, anyVolumeRequiresEncryption(volume)); Answer answer = null; if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; @@ -350,14 +350,15 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { if (srcData instanceof VolumeInfo && ((VolumeInfo)srcData).isDirectDownload()) { bypassSecondaryStorage = true; } + boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData); if (cacheStore == null) { if (bypassSecondaryStorage) { CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _copyvolumewait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(srcData, destData); + EndPoint ep = selector.select(srcData, destData, encryptionRequired); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { @@ -394,9 +395,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { objOnImageStore.processEvent(Event.CopyingRequested); CopyCommand cmd = new CopyCommand(objOnImageStore.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), _copyvolumewait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(objOnImageStore, destData); + EndPoint ep = selector.select(objOnImageStore, destData, encryptionRequired); if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { @@ -426,10 +427,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } else { DataObject cacheData = cacheMgr.createCacheObject(srcData, destScope); CopyCommand cmd = new CopyCommand(cacheData.getTO(), destData.getTO(), _copyvolumewait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(cacheData, destData); + EndPoint ep = selector.select(cacheData, destData, encryptionRequired); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { @@ -456,10 +457,12 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { command.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString()); } + boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData); + EndPoint ep = selector.select(srcData, StorageAction.MIGRATEVOLUME); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(command, false, errMsg); } else { @@ -582,6 +585,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } Map options = new HashMap(); options.put("fullSnapshot", fullSnapshot.toString()); + boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData); Answer answer = null; try { if (needCacheStorage(srcData, destData)) { @@ -591,7 +595,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { CopyCommand cmd = new CopyCommand(srcData.getTO(), addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value()); cmd.setCacheTO(cacheData.getTO()); cmd.setOptions(options); - EndPoint ep = selector.select(srcData, destData); + EndPoint ep = selector.select(srcData, destData, encryptionRequired); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -603,7 +607,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { addFullCloneAndDiskprovisiongStrictnessFlagOnVMwareDest(destData.getTO()); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value()); cmd.setOptions(options); - EndPoint ep = selector.select(srcData, destData, StorageAction.BACKUPSNAPSHOT); + EndPoint ep = selector.select(srcData, destData, StorageAction.BACKUPSNAPSHOT, encryptionRequired); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -634,4 +638,18 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { result.setResult("Unsupported operation requested for copying data."); callback.complete(result); } + + /** + * Does any object require encryption support? + */ + private boolean anyVolumeRequiresEncryption(DataObject ... objects) { + for (DataObject o : objects) { + if (o instanceof VolumeInfo && ((VolumeInfo) o).getPassphraseId() != null) { + return true; + } else if (o instanceof SnapshotInfo && ((SnapshotInfo) o).getBaseVolume().getPassphraseId() != null) { + return true; + } + } + return false; + } } diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java index a62921c9d94..7a34b199cfc 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -53,6 +54,8 @@ public class DataMotionServiceImpl implements DataMotionService { StorageStrategyFactory storageStrategyFactory; @Inject VolumeDao volDao; + @Inject + PassphraseDao passphraseDao; @Override public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { @@ -98,7 +101,14 @@ public class DataMotionServiceImpl implements DataMotionService { volDao.update(sourceVO.getId(), sourceVO); destinationVO.setState(Volume.State.Expunged); destinationVO.setRemoved(new Date()); + Long passphraseId = destinationVO.getPassphraseId(); + destinationVO.setPassphraseId(null); volDao.update(destinationVO.getId(), destinationVO); + + if (passphraseId != null) { + passphraseDao.remove(passphraseId); + } + } @Override diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index aee1f75c352..0a4858cae73 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -59,6 +59,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.ResignatureAnswer; @@ -161,6 +162,8 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { @Inject private HostDao _hostDao; @Inject + private PassphraseDao _passphraseDao; + @Inject protected PrimaryDataStoreDao _storagePoolDao; @Inject private SnapshotDao _snapshotDao; @@ -1735,9 +1738,13 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { */ protected MigrationOptions createLinkedCloneMigrationOptions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo, String srcVolumeBackingFile, String srcPoolUuid, Storage.StoragePoolType srcPoolType) { VMTemplateStoragePoolVO ref = templatePoolDao.findByPoolTemplate(destVolumeInfo.getPoolId(), srcVolumeInfo.getTemplateId(), null); - boolean updateBackingFileReference = ref == null; - String backingFile = ref != null ? ref.getInstallPath() : srcVolumeBackingFile; - return new MigrationOptions(srcPoolUuid, srcPoolType, backingFile, updateBackingFileReference); + + // if template exists on destination, use it as the backing file + if (ref != null) { + return new MigrationOptions(destVolumeInfo.getDataStore().getUuid(), destVolumeInfo.getStoragePoolType(), ref.getInstallPath(), false); + } else { + return new MigrationOptions(srcPoolUuid, srcPoolType, srcVolumeBackingFile, true); + } } /** @@ -1874,6 +1881,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { migrateDiskInfo = configureMigrateDiskInfo(srcVolumeInfo, destPath); migrateDiskInfo.setSourceDiskOnStorageFileSystem(isStoragePoolTypeOfFile(sourceStoragePool)); migrateDiskInfoList.add(migrateDiskInfo); + prepareDiskWithSecretConsumerDetail(vmTO, srcVolumeInfo, destVolumeInfo.getPath()); } migrateStorage.put(srcVolumeInfo.getPath(), migrateDiskInfo); @@ -2123,6 +2131,11 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { newVol.setPoolId(storagePoolVO.getId()); newVol.setLastPoolId(lastPoolId); + if (volume.getPassphraseId() != null) { + newVol.setPassphraseId(volume.getPassphraseId()); + newVol.setEncryptFormat(volume.getEncryptFormat()); + } + return _volumeDao.persist(newVol); } @@ -2206,6 +2219,23 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { } } + /** + * Include some destination volume info in vmTO, required for some PrepareForMigrationCommand processing + * + */ + protected void prepareDiskWithSecretConsumerDetail(VirtualMachineTO vmTO, VolumeInfo srcVolume, String destPath) { + if (vmTO.getDisks() != null) { + LOGGER.debug(String.format("Preparing VM TO '%s' disks with migration data", vmTO)); + Arrays.stream(vmTO.getDisks()).filter(diskTO -> diskTO.getData().getId() == srcVolume.getId()).forEach( diskTO -> { + Map details = diskTO.getDetails(); + if (diskTO.getDetails() == null) { + diskTO.setDetails(new HashMap<>()); + } + diskTO.getDetails().put(DiskTO.SECRET_CONSUMER_DETAIL, destPath); + }); + } + } + /** * At a high level: The source storage cannot be managed and * the destination storages can be all managed or all not managed, not mixed. diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index af206a7378e..c708ef75e23 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -26,17 +26,14 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.exception.StorageUnavailableException; -import com.cloud.storage.StoragePoolStatus; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.log4j.Logger; - import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.log4j.Logger; import com.cloud.capacity.Capacity; import com.cloud.capacity.dao.CapacityDao; @@ -44,10 +41,12 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.StorageUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StorageUtil; import com.cloud.storage.Volume; import com.cloud.storage.dao.VolumeDao; @@ -220,7 +219,6 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement } protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, DeploymentPlan plan) { - if (s_logger.isDebugEnabled()) { s_logger.debug("Checking if storage pool is suitable, name: " + pool.getName() + " ,poolId: " + pool.getId()); } @@ -231,6 +229,13 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return false; } + if (dskCh.requiresEncryption() && !pool.getPoolType().supportsEncryption()) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Storage pool type '%s' doesn't support encryption required for volume, skipping this pool", pool.getPoolType())); + } + return false; + } + Long clusterId = pool.getClusterId(); if (clusterId != null) { ClusterVO cluster = clusterDao.findById(clusterId); diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java index 6a903e4235a..2a0f7102c50 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java @@ -65,6 +65,8 @@ import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; +import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION; + @Component public class DefaultEndPointSelector implements EndPointSelector { private static final Logger s_logger = Logger.getLogger(DefaultEndPointSelector.class); @@ -72,11 +74,14 @@ public class DefaultEndPointSelector implements EndPointSelector { private HostDao hostDao; @Inject private DedicatedResourceDao dedicatedResourceDao; + + private static final String volEncryptColumnName = "volume_encryption_support"; private final String findOneHostOnPrimaryStorage = "select t.id from " - + "(select h.id, cd.value " + + "(select h.id, cd.value, hd.value as " + volEncryptColumnName + " " + "from host h join storage_pool_host_ref s on h.id = s.host_id " + "join cluster c on c.id=h.cluster_id " + "left join cluster_details cd on c.id=cd.cluster_id and cd.name='" + CapacityManager.StorageOperationsExcludeCluster.key() + "' " + + "left join host_details hd on h.id=hd.host_id and hd.name='" + HOST_VOLUME_ENCRYPTION + "' " + "where h.status = 'Up' and h.type = 'Routing' and h.resource_state = 'Enabled' and s.pool_id = ? "; private String findOneHypervisorHostInScopeByType = "select h.id from host h where h.status = 'Up' and h.hypervisor_type = ? "; @@ -118,8 +123,12 @@ public class DefaultEndPointSelector implements EndPointSelector { } } - @DB protected EndPoint findEndPointInScope(Scope scope, String sqlBase, Long poolId) { + return findEndPointInScope(scope, sqlBase, poolId, false); + } + + @DB + protected EndPoint findEndPointInScope(Scope scope, String sqlBase, Long poolId, boolean volumeEncryptionSupportRequired) { StringBuilder sbuilder = new StringBuilder(); sbuilder.append(sqlBase); @@ -142,8 +151,13 @@ public class DefaultEndPointSelector implements EndPointSelector { dedicatedHosts = dedicatedResourceDao.listAllHosts(); } - // TODO: order by rand() is slow if there are lot of hosts sbuilder.append(") t where t.value<>'true' or t.value is null"); //Added for exclude cluster's subquery + + if (volumeEncryptionSupportRequired) { + sbuilder.append(String.format(" and t.%s='true'", volEncryptColumnName)); + } + + // TODO: order by rand() is slow if there are lot of hosts sbuilder.append(" ORDER by "); if (dedicatedHosts.size() > 0) { moveDedicatedHostsToLowerPriority(sbuilder, dedicatedHosts); @@ -208,7 +222,7 @@ public class DefaultEndPointSelector implements EndPointSelector { } } - protected EndPoint findEndPointForImageMove(DataStore srcStore, DataStore destStore) { + protected EndPoint findEndPointForImageMove(DataStore srcStore, DataStore destStore, boolean volumeEncryptionSupportRequired) { // find any xenserver/kvm host in the scope Scope srcScope = srcStore.getScope(); Scope destScope = destStore.getScope(); @@ -233,17 +247,22 @@ public class DefaultEndPointSelector implements EndPointSelector { poolId = destStore.getId(); } } - return findEndPointInScope(selectedScope, findOneHostOnPrimaryStorage, poolId); + return findEndPointInScope(selectedScope, findOneHostOnPrimaryStorage, poolId, volumeEncryptionSupportRequired); } @Override public EndPoint select(DataObject srcData, DataObject destData) { + return select( srcData, destData, false); + } + + @Override + public EndPoint select(DataObject srcData, DataObject destData, boolean volumeEncryptionSupportRequired) { DataStore srcStore = srcData.getDataStore(); DataStore destStore = destData.getDataStore(); if (moveBetweenPrimaryImage(srcStore, destStore)) { - return findEndPointForImageMove(srcStore, destStore); + return findEndPointForImageMove(srcStore, destStore, volumeEncryptionSupportRequired); } else if (moveBetweenPrimaryDirectDownload(srcStore, destStore)) { - return findEndPointForImageMove(srcStore, destStore); + return findEndPointForImageMove(srcStore, destStore, volumeEncryptionSupportRequired); } else if (moveBetweenCacheAndImage(srcStore, destStore)) { // pick ssvm based on image cache dc DataStore selectedStore = null; @@ -274,6 +293,11 @@ public class DefaultEndPointSelector implements EndPointSelector { @Override public EndPoint select(DataObject srcData, DataObject destData, StorageAction action) { + return select(srcData, destData, action, false); + } + + @Override + public EndPoint select(DataObject srcData, DataObject destData, StorageAction action, boolean encryptionRequired) { s_logger.error("IR24 select BACKUPSNAPSHOT from primary to secondary " + srcData.getId() + " dest=" + destData.getId()); if (action == StorageAction.BACKUPSNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) { SnapshotInfo srcSnapshot = (SnapshotInfo)srcData; @@ -293,7 +317,7 @@ public class DefaultEndPointSelector implements EndPointSelector { } } } - return select(srcData, destData); + return select(srcData, destData, encryptionRequired); } protected EndPoint findEndpointForPrimaryStorage(DataStore store) { @@ -350,6 +374,15 @@ public class DefaultEndPointSelector implements EndPointSelector { return sc.list(); } + @Override + public EndPoint select(DataObject object, boolean encryptionSupportRequired) { + DataStore store = object.getDataStore(); + if (store.getRole() == DataStoreRole.Primary) { + return findEndPointInScope(store.getScope(), findOneHostOnPrimaryStorage, store.getId(), encryptionSupportRequired); + } + throw new CloudRuntimeException(String.format("Storage role %s doesn't support encryption", store.getRole())); + } + @Override public EndPoint select(DataObject object) { DataStore store = object.getDataStore(); @@ -415,6 +448,11 @@ public class DefaultEndPointSelector implements EndPointSelector { @Override public EndPoint select(DataObject object, StorageAction action) { + return select(object, action, false); + } + + @Override + public EndPoint select(DataObject object, StorageAction action, boolean encryptionRequired) { if (action == StorageAction.TAKESNAPSHOT) { SnapshotInfo snapshotInfo = (SnapshotInfo)object; if (snapshotInfo.getHypervisorType() == Hypervisor.HypervisorType.KVM) { @@ -446,7 +484,7 @@ public class DefaultEndPointSelector implements EndPointSelector { } } } - return select(object); + return select(object, encryptionRequired); } @Override diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java index 29405be79be..7f82ed16f08 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -22,6 +22,11 @@ import javax.inject.Inject; import com.cloud.dc.VsphereStoragePolicyVO; import com.cloud.dc.dao.VsphereStoragePolicyDao; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import org.apache.cloudstack.secret.dao.PassphraseDao; +import org.apache.cloudstack.secret.PassphraseVO; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.MigrationOptions; import com.cloud.storage.VMTemplateVO; @@ -101,6 +106,8 @@ public class VolumeObject implements VolumeInfo { DiskOfferingDetailsDao diskOfferingDetailsDao; @Inject VsphereStoragePolicyDao vsphereStoragePolicyDao; + @Inject + PassphraseDao passphraseDao; private Object payload; private MigrationOptions migrationOptions; @@ -660,11 +667,13 @@ public class VolumeObject implements VolumeInfo { } protected void updateVolumeInfo(VolumeObjectTO newVolume, VolumeVO volumeVo, boolean setVolumeSize, boolean setFormat) { - String previousValues = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(volumeVo, "path", "size", "format", "poolId"); + String previousValues = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(volumeVo, "path", "size", "format", "encryptFormat", "poolId"); volumeVo.setPath(newVolume.getPath()); Long newVolumeSize = newVolume.getSize(); + volumeVo.setEncryptFormat(newVolume.getEncryptFormat()); + if (newVolumeSize != null && setVolumeSize) { volumeVo.setSize(newVolumeSize); } @@ -674,7 +683,7 @@ public class VolumeObject implements VolumeInfo { volumeVo.setPoolId(getDataStore().getId()); volumeDao.update(volumeVo.getId(), volumeVo); - String newValues = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(volumeVo, "path", "size", "format", "poolId"); + String newValues = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(volumeVo, "path", "size", "format", "encryptFormat", "poolId"); s_logger.debug(String.format("Updated %s from %s to %s ", volumeVo.getVolumeDescription(), previousValues, newValues)); } @@ -833,4 +842,61 @@ public class VolumeObject implements VolumeInfo { public Class getEntityType() { return Volume.class; } + + @Override + public Long getPassphraseId() { + return volumeVO.getPassphraseId(); + } + + @Override + public void setPassphraseId(Long id) { + volumeVO.setPassphraseId(id); + } + + /** + * Removes passphrase reference from underlying volume. Also removes the associated passphrase entry if it is the last user. + */ + public void deletePassphrase() { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + Long passphraseId = volumeVO.getPassphraseId(); + if (passphraseId != null) { + volumeVO.setPassphraseId(null); + volumeDao.persist(volumeVO); + + s_logger.debug(String.format("Checking to see if we can delete passphrase id %s", passphraseId)); + List volumes = volumeDao.listVolumesByPassphraseId(passphraseId); + + if (volumes != null && !volumes.isEmpty()) { + s_logger.debug("Other volumes use this passphrase, skipping deletion"); + return; + } + + s_logger.debug(String.format("Deleting passphrase %s", passphraseId)); + passphraseDao.remove(passphraseId); + } + } + }); + } + + /** + * Looks up passphrase from underlying volume. + * @return passphrase as bytes + */ + public byte[] getPassphrase() { + PassphraseVO passphrase = passphraseDao.findById(volumeVO.getPassphraseId()); + if (passphrase != null) { + return passphrase.getPassphrase(); + } + return null; + } + + @Override + public String getEncryptFormat() { return volumeVO.getEncryptFormat(); } + + @Override + public void setEncryptFormat(String encryptFormat) { + volumeVO.setEncryptFormat(encryptFormat); + } } diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 92526846729..9ba159738e9 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Random; import javax.inject.Inject; +import org.apache.cloudstack.secret.dao.PassphraseDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import org.apache.cloudstack.annotation.AnnotationService; @@ -194,6 +195,8 @@ public class VolumeServiceImpl implements VolumeService { private StorageManager _storageMgr; @Inject private AnnotationDao annotationDao; + @Inject + private PassphraseDao passphraseDao; private final static String SNAPSHOT_ID = "SNAPSHOT_ID"; @@ -443,6 +446,11 @@ public class VolumeServiceImpl implements VolumeService { try { if (result.isSuccess()) { vo.processEvent(Event.OperationSuccessed); + + if (vo.getPassphraseId() != null) { + vo.deletePassphrase(); + } + if (canVolumeBeRemoved(vo.getId())) { s_logger.info("Volume " + vo.getId() + " is not referred anywhere, remove it from volumes table"); volDao.remove(vo.getId()); diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index 880cffd3f05..86806c3b3d2 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -108,10 +108,53 @@ maven-surefire-plugin - **/Qemu*.java + **/QemuImg*.java + + + + skip.libvirt.tests + + + skip.libvirt.tests + true + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/dependencies + runtime + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/QemuImg*.java + **/LibvirtComputingResourceTest.java + + + + + + + diff --git a/plugins/hypervisors/kvm/rn2-hyperd-lapp01.rno.apple.com b/plugins/hypervisors/kvm/rn2-hyperd-lapp01.rno.apple.com new file mode 100644 index 0000000000000000000000000000000000000000..0af63eeb9997943f960b5d9aea48d32c1b52ea32 GIT binary patch literal 599851 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 wH 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

Vf|{W>n)&k;&8S9}w!Ce0 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ϡY-KmKvYUfF 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 0) { + String secretUuid = createLibvirtVolumeSecret(conn, volumeObjectTO.getPath(), volumeObjectTO.getPassphrase()); + DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(secretUuid, QemuObject.EncryptFormat.enumValue(volumeObjectTO.getEncryptFormat())); + disk.setLibvirtDiskEncryptDetails(encryptDetails); + } } if (vm.getDevices() == null) { s_logger.error("There is no devices for" + vm); @@ -3360,6 +3377,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.setCluster(_clusterId); cmd.setGatewayIpAddress(_localGateway); cmd.setIqn(getIqn()); + cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption())); if (cmd.getHostDetails().containsKey("Host.OS")) { _hostDistro = cmd.getHostDetails().get("Host.OS"); @@ -4634,6 +4652,47 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return true; } + /** + * Test host for volume encryption support + * @return boolean + */ + public boolean hostSupportsVolumeEncryption() { + // test qemu-img + Path testFile = Paths.get(javaTempDir, UUID.randomUUID().toString()).normalize().toAbsolutePath(); + String objectName = "sec0"; + + Map options = new HashMap(); + List passphraseObjects = new ArrayList<>(); + QemuImgFile file = new QemuImgFile(testFile.toString(), 64<<20, PhysicalDiskFormat.QCOW2); + + + try (KeyFile keyFile = new KeyFile(UUID.randomUUID().toString().getBytes())){ + QemuImg qemu = new QemuImg(0); + passphraseObjects.add(QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.QCOW2, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options)); + qemu.create(file, null, options, passphraseObjects); + s_logger.info("Host's qemu install supports encryption"); + } catch (QemuImgException | IOException | LibvirtException ex) { + s_logger.info("Host's qemu install doesn't support encryption", ex); + return false; + } + + // cleanup + try { + Files.deleteIfExists(testFile); + } catch (IOException ex) { + s_logger.warn(String.format("Failed to clean up test file '%s'", testFile.toAbsolutePath()), ex); + } + + // test cryptsetup + CryptSetup crypt = new CryptSetup(); + if (!crypt.isSupported()) { + s_logger.info("Host can't run cryptsetup"); + return false; + } + + return true; + } + public boolean isSecureMode(String bootMode) { if (StringUtils.isNotBlank(bootMode) && "secure".equalsIgnoreCase(bootMode)) { return true; @@ -4672,8 +4731,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public void setBackingFileFormat(String volPath) { final int timeout = 0; QemuImgFile file = new QemuImgFile(volPath); - QemuImg qemu = new QemuImg(timeout); + try{ + QemuImg qemu = new QemuImg(timeout); Map info = qemu.info(file); String backingFilePath = info.get(QemuImg.BACKING_FILE); String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT); @@ -4714,4 +4774,70 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv VcpuInfo vcpus[] = dm.getVcpusInfo(); return Arrays.stream(vcpus).filter(vcpu -> vcpu.state.equals(VcpuInfo.VcpuState.VIR_VCPU_RUNNING)).count(); } + + /** + * Set up a libvirt secret for a volume. If Libvirt says that a secret already exists for this volume path, we use its uuid. + * The UUID of the secret needs to be prescriptive such that we can register the same UUID on target host during live migration + * + * @param conn libvirt connection + * @param consumer identifier for volume in secret + * @param data secret contents + * @return uuid of matching secret for volume + * @throws LibvirtException + */ + public String createLibvirtVolumeSecret(Connect conn, String consumer, byte[] data) throws LibvirtException { + String secretUuid = null; + LibvirtSecretDef secretDef = new LibvirtSecretDef(LibvirtSecretDef.Usage.VOLUME, generateSecretUUIDFromString(consumer)); + secretDef.setVolumeVolume(consumer); + secretDef.setPrivate(true); + secretDef.setEphemeral(true); + + try { + Secret secret = conn.secretDefineXML(secretDef.toString()); + secret.setValue(data); + secretUuid = secret.getUUIDString(); + secret.free(); + } catch (LibvirtException ex) { + if (ex.getMessage().contains("already defined for use")) { + Match match = new Match(); + if (UuidUtils.REGEX.matches(ex.getMessage(), match)) { + secretUuid = match.getCapturedText(0); + s_logger.info(String.format("Reusing previously defined secret '%s' for volume '%s'", secretUuid, consumer)); + } else { + throw ex; + } + } else { + throw ex; + } + } + + return secretUuid; + } + + public void removeLibvirtVolumeSecret(Connect conn, String secretUuid) throws LibvirtException { + try { + Secret secret = conn.secretLookupByUUIDString(secretUuid); + secret.undefine(); + } catch (LibvirtException ex) { + if (ex.getMessage().contains("Secret not found")) { + s_logger.debug(String.format("Secret uuid %s doesn't exist", secretUuid)); + return; + } + throw ex; + } + s_logger.debug(String.format("Undefined secret %s", secretUuid)); + } + + public void cleanOldSecretsByDiskDef(Connect conn, List disks) throws LibvirtException { + for (DiskDef disk : disks) { + DiskDef.LibvirtDiskEncryptDetails encryptDetails = disk.getLibvirtDiskEncryptDetails(); + if (encryptDetails != null) { + removeLibvirtVolumeSecret(conn, encryptDetails.getPassphraseUuid()); + } + } + } + + public static String generateSecretUUIDFromString(String seed) { + return UUID.nameUUIDFromBytes(seed.getBytes()).toString(); + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index bb3f7132f22..fdb4ad99433 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -26,6 +26,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.w3c.dom.Document; @@ -194,6 +195,15 @@ public class LibvirtDomainXMLParser { } } + NodeList encryption = disk.getElementsByTagName("encryption"); + if (encryption.getLength() != 0) { + Element encryptionElement = (Element) encryption.item(0); + String passphraseUuid = getAttrValue("secret", "uuid", encryptionElement); + QemuObject.EncryptFormat encryptFormat = QemuObject.EncryptFormat.enumValue(encryptionElement.getAttribute("format")); + DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(passphraseUuid, encryptFormat); + def.setLibvirtDiskEncryptDetails(encryptDetails); + } + diskDefs.add(def); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtSecretDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtSecretDef.java index 80c08e9d86d..9596b40dec6 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtSecretDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtSecretDef.java @@ -55,10 +55,14 @@ public class LibvirtSecretDef { return _ephemeral; } + public void setEphemeral(boolean ephemeral) { _ephemeral = ephemeral; } + public boolean getPrivate() { return _private; } + public void setPrivate(boolean isPrivate) { _private = isPrivate; } + public String getUuid() { return _uuid; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 7c65f7970ad..09f0e6851e0 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -559,6 +560,19 @@ public class LibvirtVMDef { } public static class DiskDef { + public static class LibvirtDiskEncryptDetails { + String passphraseUuid; + QemuObject.EncryptFormat encryptFormat; + + public LibvirtDiskEncryptDetails(String passphraseUuid, QemuObject.EncryptFormat encryptFormat) { + this.passphraseUuid = passphraseUuid; + this.encryptFormat = encryptFormat; + } + + public String getPassphraseUuid() { return this.passphraseUuid; } + public QemuObject.EncryptFormat getEncryptFormat() { return this.encryptFormat; } + } + public enum DeviceType { FLOPPY("floppy"), DISK("disk"), CDROM("cdrom"), LUN("lun"); String _type; @@ -714,6 +728,7 @@ public class LibvirtVMDef { private boolean qemuDriver = true; private DiscardType _discard = DiscardType.IGNORE; private IoDriver ioDriver; + private LibvirtDiskEncryptDetails _encryptDetails; public DiscardType getDiscard() { return _discard; @@ -1026,6 +1041,10 @@ public class LibvirtVMDef { this._serial = serial; } + public void setLibvirtDiskEncryptDetails(LibvirtDiskEncryptDetails details) { this._encryptDetails = details; } + + public LibvirtDiskEncryptDetails getLibvirtDiskEncryptDetails() { return this._encryptDetails; } + @Override public String toString() { StringBuilder diskBuilder = new StringBuilder(); @@ -1093,7 +1112,13 @@ public class LibvirtVMDef { diskBuilder.append("/>\n"); if (_serial != null && !_serial.isEmpty() && _deviceType != DeviceType.LUN) { - diskBuilder.append("" + _serial + ""); + diskBuilder.append("" + _serial + "\n"); + } + + if (_encryptDetails != null) { + diskBuilder.append("\n"); + diskBuilder.append("\n"); + diskBuilder.append("\n"); } if ((_deviceType != DeviceType.CDROM) && diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java index bfa557308e7..bac5551129a 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java @@ -59,13 +59,13 @@ public final class LibvirtCreateCommandWrapper extends CommandWrapper tInfo = new HashMap(); - final ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo); + final ModifyStoragePoolAnswer answer = new ModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, storagepool.getDetails()); return answer; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java index 38cd9958d7c..8e87b02568f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPrepareForMigrationCommandWrapper.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.cloudstack.storage.configdrive.ConfigDrive; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; import org.libvirt.Connect; @@ -88,14 +89,31 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp /* setup disks, e.g for iso */ final DiskTO[] volumes = vm.getDisks(); for (final DiskTO volume : volumes) { + final DataTO data = volume.getData(); + if (volume.getType() == Volume.Type.ISO) { - final DataTO data = volume.getData(); if (data != null && data.getPath() != null && data.getPath().startsWith(ConfigDrive.CONFIGDRIVEDIR)) { libvirtComputingResource.getVolumePath(conn, volume, vm.isConfigDriveOnHostCache()); } else { libvirtComputingResource.getVolumePath(conn, volume); } } + + if (data instanceof VolumeObjectTO) { + final VolumeObjectTO volumeObjectTO = (VolumeObjectTO)data; + + if (volumeObjectTO.getPassphrase() != null && volumeObjectTO.getPassphrase().length > 0) { + String secretConsumer = volumeObjectTO.getPath(); + if (volume.getDetails() != null && volume.getDetails().containsKey(DiskTO.SECRET_CONSUMER_DETAIL)) { + secretConsumer = volume.getDetails().get(DiskTO.SECRET_CONSUMER_DETAIL); + } + String secretUuid = libvirtComputingResource.createLibvirtVolumeSecret(conn, secretConsumer, volumeObjectTO.getPassphrase()); + s_logger.debug(String.format("Created libvirt secret %s for disk %s", secretUuid, volumeObjectTO.getPath())); + volumeObjectTO.clearPassphrase(); + } else { + s_logger.debug(String.format("disk %s has no passphrase or encryption", volumeObjectTO)); + } + } } skipDisconnect = true; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java index 685fea9f1bc..d1f0e1489b1 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java @@ -19,9 +19,22 @@ package com.cloud.hypervisor.kvm.resource.wrapper; +import static com.cloud.utils.NumbersUtil.toHumanReadableSize; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.utils.cryptsetup.KeyFile; +import org.apache.cloudstack.utils.qemu.QemuImageOptions; +import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.log4j.Logger; import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.DomainInfo; import org.libvirt.LibvirtException; import org.libvirt.StorageVol; @@ -39,8 +52,6 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; -import static com.cloud.utils.NumbersUtil.toHumanReadableSize; - /* * Uses a local script now, eventually support for virStorageVolResize() will maybe work on qcow2 and lvm and we can do this in libvirt calls */ @@ -51,7 +62,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper 0 ) { + s_logger.debug("Invoking qemu-img to resize an offline, encrypted volume"); + List passphraseObjects = new ArrayList<>(); + try (KeyFile keyFile = new KeyFile(command.getPassphrase())) { + QemuObject.EncryptFormat encryptFormat = QemuObject.EncryptFormat.enumValue(command.getEncryptFormat()); + passphraseObjects.add( + QemuObject.prepareSecretForQemuImg(vol.getFormat(), encryptFormat, keyFile.toString(), "sec0", null) + ); + QemuImg q = new QemuImg(libvirtComputingResource.getCmdsTimeout()); + QemuImageOptions imgOptions = new QemuImageOptions(vol.getFormat(), path,"sec0"); + q.resize(imgOptions, passphraseObjects, newSize); + } catch (QemuImgException | LibvirtException ex) { + throw new CloudRuntimeException("Failed to run qemu-img for resize", ex); + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create keyfile for encrypted resize", ex); + } finally { + command.clearPassphrase(); + } + } else { + s_logger.debug("Invoking resize script to handle type " + type); + final Script resizecmd = new Script(libvirtComputingResource.getResizeVolumePath(), libvirtComputingResource.getCmdsTimeout(), s_logger); + resizecmd.add("-s", String.valueOf(newSize)); + resizecmd.add("-c", String.valueOf(currentSize)); + resizecmd.add("-p", path); + resizecmd.add("-t", type); + resizecmd.add("-r", String.valueOf(shrinkOk)); + resizecmd.add("-v", vmInstanceName); + final String result = resizecmd.execute(); + + if (result != null) { + if(type.equals(notifyOnlyType)) { + return new ResizeVolumeAnswer(command, true, "Resize succeeded, but need reboot to notify guest"); + } else { + return new ResizeVolumeAnswer(command, false, result); + } } } /* fetch new size as seen from libvirt, don't want to assume anything */ pool = storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid()); pool.refresh(); - final long finalSize = pool.getPhysicalDisk(volid).getVirtualSize(); + final long finalSize = pool.getPhysicalDisk(volumeId).getVirtualSize(); s_logger.debug("after resize, size reports as: " + toHumanReadableSize(finalSize) + ", requested: " + toHumanReadableSize(newSize)); return new ResizeVolumeAnswer(command, true, "success", finalSize); } catch (final CloudRuntimeException e) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java index 3807e5ca931..049269be60e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRevertSnapshotCommandWrapper.java @@ -20,6 +20,11 @@ package com.cloud.hypervisor.kvm.resource.wrapper; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import org.apache.cloudstack.storage.command.RevertSnapshotCommand; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; @@ -44,7 +49,6 @@ import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; @ResourceWrapper(handles = RevertSnapshotCommand.class) public final class LibvirtRevertSnapshotCommandWrapper extends CommandWrapper { @@ -106,14 +110,12 @@ public final class LibvirtRevertSnapshotCommandWrapper extends CommandWrapper 0) { for (final DiskDef disk : disks) { libvirtComputingResource.cleanupDisk(disk); + DiskDef.LibvirtDiskEncryptDetails diskEncryptDetails = disk.getLibvirtDiskEncryptDetails(); + if (diskEncryptDetails != null) { + libvirtComputingResource.removeLibvirtVolumeSecret(conn, diskEncryptDetails.getPassphraseUuid()); + } } } else { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java index 389a2c717b7..ad22f2016e2 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java @@ -21,11 +21,11 @@ import java.util.List; import java.util.Map; import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.log4j.Logger; - -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.libvirt.LibvirtException; import com.cloud.agent.api.to.DiskTO; import com.cloud.storage.Storage; @@ -35,7 +35,6 @@ import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -import org.libvirt.LibvirtException; @StorageAdaptorInfo(storagePoolType=StoragePoolType.Iscsi) public class IscsiAdmStorageAdaptor implements StorageAdaptor { @@ -44,7 +43,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { private static final Map MapStorageUuidToStoragePool = new HashMap<>(); @Override - public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType) { + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map details) { IscsiAdmStoragePool storagePool = new IscsiAdmStoragePool(uuid, host, port, storagePoolType, this); MapStorageUuidToStoragePool.put(uuid, storagePool); @@ -75,7 +74,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { // called from LibvirtComputingResource.execute(CreateCommand) // does not apply for iScsiAdmStorageAdaptor @Override - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } @@ -384,7 +383,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, - KVMStoragePool destPool, int timeout) { + KVMStoragePool destPool, int timeout, byte[] passphrase) { throw new UnsupportedOperationException("Creating a disk from a template is not yet supported for this configuration."); } @@ -394,8 +393,12 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String destVolumeUuid, KVMStoragePool destPool, int timeout) { - QemuImg q = new QemuImg(timeout); + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + return copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String destVolumeUuid, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase) { QemuImgFile srcFile; @@ -414,6 +417,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { QemuImgFile destFile = new QemuImgFile(destDisk.getPath(), destDisk.getFormat()); try { + QemuImg q = new QemuImg(timeout); q.convert(srcFile, destFile); } catch (QemuImgException | LibvirtException ex) { String msg = "Failed to copy data from " + srcDisk.getPath() + " to " + @@ -443,7 +447,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java index 8e4af764cd6..7c054f6905e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java @@ -87,7 +87,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool { // from LibvirtComputingResource.createDiskFromTemplate(KVMPhysicalDisk, String, PhysicalDiskFormat, long, KVMStoragePool) // does not apply for iScsiAdmStoragePool @Override - public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } @@ -95,7 +95,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool { // from KVMStorageProcessor.createVolume(CreateObjectCommand) // does not apply for iScsiAdmStoragePool @Override - public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } @@ -170,4 +170,9 @@ public class IscsiAdmStoragePool implements KVMStoragePool { public boolean supportsConfigDriveIso() { return false; } + + @Override + public Map getDetails() { + return null; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java index 5b4a61058d5..5187abf3fb8 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java @@ -17,6 +17,7 @@ package com.cloud.hypervisor.kvm.storage; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuObject; public class KVMPhysicalDisk { private String path; @@ -49,6 +50,7 @@ public class KVMPhysicalDisk { private PhysicalDiskFormat format; private long size; private long virtualSize; + private QemuObject.EncryptFormat qemuEncryptFormat; public KVMPhysicalDisk(String path, String name, KVMStoragePool pool) { this.path = path; @@ -101,4 +103,12 @@ public class KVMPhysicalDisk { this.path = path; } + public QemuObject.EncryptFormat getQemuEncryptFormat() { + return this.qemuEncryptFormat; + } + + public void setQemuEncryptFormat(QemuObject.EncryptFormat format) { + this.qemuEncryptFormat = format; + } + } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java index 46d78e5f6b3..3bff9c9852e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java @@ -25,9 +25,9 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; public interface KVMStoragePool { - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size); + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase); - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size); + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size, byte[] passphrase); public boolean connectPhysicalDisk(String volumeUuid, Map details); @@ -72,4 +72,6 @@ public interface KVMStoragePool { public boolean createFolder(String path); public boolean supportsConfigDriveIso(); + + public Map getDetails(); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index d6c49d295e0..9053f0618ce 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -56,8 +56,9 @@ public class KVMStoragePoolManager { String userInfo; boolean type; StoragePoolType poolType; + Map details; - public StoragePoolInformation(String name, String host, int port, String path, String userInfo, StoragePoolType poolType, boolean type) { + public StoragePoolInformation(String name, String host, int port, String path, String userInfo, StoragePoolType poolType, Map details, boolean type) { this.name = name; this.host = host; this.port = port; @@ -65,6 +66,7 @@ public class KVMStoragePoolManager { this.userInfo = userInfo; this.type = type; this.poolType = poolType; + this.details = details; } } @@ -270,7 +272,7 @@ public class KVMStoragePoolManager { } catch (Exception e) { StoragePoolInformation info = _storagePools.get(uuid); if (info != null) { - pool = createStoragePool(info.name, info.host, info.port, info.path, info.userInfo, info.poolType, info.type); + pool = createStoragePool(info.name, info.host, info.port, info.path, info.userInfo, info.poolType, info.details, info.type); } else { throw new CloudRuntimeException("Could not fetch storage pool " + uuid + " from libvirt due to " + e.getMessage()); } @@ -300,7 +302,7 @@ public class KVMStoragePoolManager { } // secondary storage registers itself through here - return createStoragePool(uuid, sourceHost, 0, sourcePath, "", protocol, false); + return createStoragePool(uuid, sourceHost, 0, sourcePath, "", protocol, null, false); } public KVMPhysicalDisk getPhysicalDisk(StoragePoolType type, String poolUuid, String volName) { @@ -341,20 +343,27 @@ public class KVMStoragePoolManager { public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type) { // primary storage registers itself through here - return createStoragePool(name, host, port, path, userInfo, type, true); + return createStoragePool(name, host, port, path, userInfo, type, null, true); + } + + /** + * Primary Storage registers itself through here + */ + public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details) { + return createStoragePool(name, host, port, path, userInfo, type, details, true); } //Note: due to bug CLOUDSTACK-4459, createStoragepool can be called in parallel, so need to be synced. - private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, boolean primaryStorage) { + private synchronized KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details, boolean primaryStorage) { StorageAdaptor adaptor = getStorageAdaptor(type); - KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type); + KVMStoragePool pool = adaptor.createStoragePool(name, host, port, path, userInfo, type, details); // LibvirtStorageAdaptor-specific statement if (type == StoragePoolType.NetworkFilesystem && primaryStorage) { KVMHABase.NfsStoragePool nfspool = new KVMHABase.NfsStoragePool(pool.getUuid(), host, path, pool.getLocalPath(), PoolType.PrimaryStorage); _haMonitor.addStoragePool(nfspool); } - StoragePoolInformation info = new StoragePoolInformation(name, host, port, path, userInfo, type, primaryStorage); + StoragePoolInformation info = new StoragePoolInformation(name, host, port, path, userInfo, type, details, primaryStorage); addStoragePool(pool.getUuid(), info); return pool; } @@ -377,35 +386,35 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, Storage.ProvisioningType provisioningType, - KVMStoragePool destPool, int timeout) { - return createDiskFromTemplate(template, name, provisioningType, destPool, template.getSize(), timeout); + KVMStoragePool destPool, int timeout, byte[] passphrase) { + return createDiskFromTemplate(template, name, provisioningType, destPool, template.getSize(), timeout, passphrase); } public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, Storage.ProvisioningType provisioningType, - KVMStoragePool destPool, long size, int timeout) { + KVMStoragePool destPool, long size, int timeout, byte[] passphrase) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); // LibvirtStorageAdaptor-specific statement if (destPool.getType() == StoragePoolType.RBD) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, provisioningType, - size, destPool, timeout); + size, destPool, timeout, passphrase); } else if (destPool.getType() == StoragePoolType.CLVM) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, provisioningType, - size, destPool, timeout); + size, destPool, timeout, passphrase); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, provisioningType, - size, destPool, timeout); + size, destPool, timeout, passphrase); } else if (destPool.getType() == StoragePoolType.PowerFlex || destPool.getType() == StoragePoolType.Linstor) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, provisioningType, - size, destPool, timeout); + size, destPool, timeout, passphrase); } else { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, provisioningType, - size, destPool, timeout); + size, destPool, timeout, passphrase); } } @@ -416,7 +425,12 @@ public class KVMStoragePoolManager { public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); - return adaptor.copyPhysicalDisk(disk, name, destPool, timeout); + return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase) { + StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); + return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, srcPassphrase, dstPassphrase); } public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool, int timeout) { @@ -425,9 +439,9 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk createDiskWithTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, - KVMStoragePool destPool, int timeout) { + KVMStoragePool destPool, int timeout, byte[] passphrase) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); - return adaptor.createDiskFromTemplateBacking(template, name, format, size, destPool, timeout); + return adaptor.createDiskFromTemplateBacking(template, name, format, size, destPool, timeout, passphrase); } public KVMPhysicalDisk createPhysicalDiskFromDirectDownloadTemplate(String templateFilePath, String destTemplatePath, KVMStoragePool destPool, Storage.ImageFormat format, int timeout) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 9cd6c5af03d..3d8acddc89c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -29,6 +29,7 @@ import java.net.URISyntaxException; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -64,10 +65,13 @@ import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.cryptsetup.KeyFile; +import org.apache.cloudstack.utils.qemu.QemuImageOptions; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.commons.collections.MapUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; @@ -410,7 +414,7 @@ public class KVMStorageProcessor implements StorageProcessor { s_logger.warn("Failed to connect new volume at path: " + path + ", in storage pool id: " + primaryStore.getUuid()); } - vol = storagePoolMgr.copyPhysicalDisk(BaseVol, path != null ? path : volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds()); + vol = storagePoolMgr.copyPhysicalDisk(BaseVol, path != null ? path : volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds(), null, volume.getPassphrase()); storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path); } else { @@ -420,7 +424,7 @@ public class KVMStorageProcessor implements StorageProcessor { } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), volume.getProvisioningType(), - BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds()); + BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds(), volume.getPassphrase()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -429,6 +433,9 @@ public class KVMStorageProcessor implements StorageProcessor { final VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(vol.getName()); newVol.setSize(volume.getSize()); + if (vol.getQemuEncryptFormat() != null) { + newVol.setEncryptFormat(vol.getQemuEncryptFormat().toString()); + } if (vol.getFormat() == PhysicalDiskFormat.RAW) { newVol.setFormat(ImageFormat.RAW); @@ -442,6 +449,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to create volume: ", e); return new CopyCmdAnswer(e.toString()); + } finally { + volume.clearPassphrase(); } } @@ -512,6 +521,7 @@ public class KVMStorageProcessor implements StorageProcessor { return new CopyCmdAnswer(e.toString()); } finally { + srcVol.clearPassphrase(); if (secondaryStoragePool != null) { storagePoolMgr.deleteStoragePool(secondaryStoragePool.getType(), secondaryStoragePool.getUuid()); } @@ -558,6 +568,8 @@ public class KVMStorageProcessor implements StorageProcessor { s_logger.debug("Failed to copyVolumeFromPrimaryToSecondary: ", e); return new CopyCmdAnswer(e.toString()); } finally { + srcVol.clearPassphrase(); + destVol.clearPassphrase(); if (secondaryStoragePool != null) { storagePoolMgr.deleteStoragePool(secondaryStoragePool.getType(), secondaryStoragePool.getUuid()); } @@ -685,6 +697,7 @@ public class KVMStorageProcessor implements StorageProcessor { s_logger.debug("Failed to createTemplateFromVolume: ", e); return new CopyCmdAnswer(e.toString()); } finally { + volume.clearPassphrase(); if (secondaryStorage != null) { secondaryStorage.delete(); } @@ -930,6 +943,8 @@ public class KVMStorageProcessor implements StorageProcessor { Connect conn = null; KVMPhysicalDisk snapshotDisk = null; KVMStoragePool primaryPool = null; + + final VolumeObjectTO srcVolume = snapshot.getVolume(); try { conn = LibvirtConnection.getConnectionByVmName(vmName); @@ -989,18 +1004,41 @@ public class KVMStorageProcessor implements StorageProcessor { return new CopyCmdAnswer(e.toString()); } } else { - final Script command = new Script(_manageSnapshotPath, cmd.getWaitInMillSeconds(), s_logger); - command.add("-b", snapshotDisk.getPath()); - command.add(NAME_OPTION, snapshotName); - command.add("-p", snapshotDestPath); - if (isCreatedFromVmSnapshot) { - descName = UUID.randomUUID().toString(); - } - command.add("-t", descName); - final String result = command.execute(); - if (result != null) { - s_logger.debug("Failed to backup snaptshot: " + result); - return new CopyCmdAnswer(result); + /* if encrypted qcow2 file, use qemu-img directly. Otherwise call manage snapshot script */ + if (qemuVolumeHasEncryption(srcVolume)) { + List passphraseObjects = new ArrayList<>(); + try (KeyFile keyFile = new KeyFile(srcVolume.getPassphrase())) { + Map options = new HashMap(); + passphraseObjects.add( + QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.QCOW2, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options) + ); + secondaryStoragePool.createFolder(snapshotRelPath); + QemuImg q = new QemuImg(cmd.getWaitInMillSeconds()); + QemuImageOptions imgOptions = new QemuImageOptions(PhysicalDiskFormat.QCOW2, snapshotDisk.getPath(),"sec0"); + QemuImgFile sourceFile = new QemuImgFile(snapshotDisk.getPath(), PhysicalDiskFormat.QCOW2); + QemuImgFile destFile = new QemuImgFile(snapshotDestPath + File.separator + snapshotName, PhysicalDiskFormat.QCOW2); + q.convert(sourceFile, destFile, options, passphraseObjects, imgOptions, snapshotName, false); + } catch (QemuImgException ex) { + throw new CloudRuntimeException("Failed to run qemu-img for snapshot backup", ex); + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create keyfile for encrypted snapshot backup", ex); + } catch (LibvirtException ex) { + throw new CloudRuntimeException("Failed to query libvirt during snapshot backup", ex); + } + } else { + final Script command = new Script(_manageSnapshotPath, cmd.getWaitInMillSeconds(), s_logger); + command.add("-b", snapshotDisk.getPath()); + command.add(NAME_OPTION, snapshotName); + command.add("-p", snapshotDestPath); + if (isCreatedFromVmSnapshot) { + descName = UUID.randomUUID().toString(); + } + command.add("-t", descName); + final String result = command.execute(); + if (result != null) { + s_logger.debug("Failed to backup snaptshot: " + result); + return new CopyCmdAnswer(result); + } } final File snapFile = new File(snapshotDestPath + "/" + descName); if(snapFile.exists()){ @@ -1012,10 +1050,7 @@ public class KVMStorageProcessor implements StorageProcessor { newSnapshot.setPath(snapshotRelPath + File.separator + descName); newSnapshot.setPhysicalSize(size); return new CopyCmdAnswer(newSnapshot); - } catch (final LibvirtException e) { - s_logger.debug("Failed to backup snapshot: ", e); - return new CopyCmdAnswer(e.toString()); - } catch (final CloudRuntimeException e) { + } catch (final LibvirtException | CloudRuntimeException e) { s_logger.debug("Failed to backup snapshot: ", e); return new CopyCmdAnswer(e.toString()); } finally { @@ -1059,11 +1094,17 @@ public class KVMStorageProcessor implements StorageProcessor { } } else { if (primaryPool.getType() != StoragePoolType.RBD) { - deleteSnapshotViaManageSnapshotScript(snapshotName, snapshotDisk); + if (qemuVolumeHasEncryption(srcVolume)) { + deleteSnapshotViaQemuImg(srcVolume, snapshotDisk.getPath(), snapshotName, cmd.getWaitInMillSeconds()); + } else { + deleteSnapshotViaManageSnapshotScript(snapshotName, snapshotDisk); + } } } } catch (final Exception ex) { s_logger.error("Failed to delete snapshots on primary", ex); + } finally { + srcVolume.clearPassphrase(); } } @@ -1077,6 +1118,30 @@ public class KVMStorageProcessor implements StorageProcessor { } } + private boolean qemuVolumeHasEncryption(VolumeObjectTO volume) { + return volume.getEncryptFormat() != null && QemuObject.EncryptFormat.enumValue(volume.getEncryptFormat()) == QemuObject.EncryptFormat.LUKS && volume.getPassphrase() != null; + } + + private void deleteSnapshotViaQemuImg(final VolumeObjectTO volume, final String path, final String snapshotName, final int timeout) { + List passphraseObjects = new ArrayList<>(); + try (KeyFile keyFile = new KeyFile(volume.getPassphrase())) { + passphraseObjects.add( + QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.QCOW2, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", null) + ); + QemuImg q = new QemuImg(timeout); + QemuImageOptions imgOptions = new QemuImageOptions(PhysicalDiskFormat.QCOW2, path,"sec0"); + q.deleteSnapshot(imgOptions, snapshotName, passphraseObjects); + } catch (QemuImgException ex) { + throw new CloudRuntimeException("Failed to run qemu-img for deleting snapshot", ex); + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create keyfile for deleting encrypted snapshot", ex); + } catch (LibvirtException ex) { + throw new CloudRuntimeException("Failed to call Libvirt during deleting snapshot", ex); + } finally { + volume.clearPassphrase(); + } + } + private void deleteSnapshotViaManageSnapshotScript(final String snapshotName, KVMPhysicalDisk snapshotDisk) { final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger); command.add(MANAGE_SNAPSTHOT_DESTROY_OPTION, snapshotDisk.getPath()); @@ -1232,7 +1297,7 @@ public class KVMStorageProcessor implements StorageProcessor { final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength, final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate, final Long iopsReadRateMax, final Long iopsReadRateMaxLength, - final Long iopsWriteRate, final Long iopsWriteRateMax, final Long iopsWriteRateMaxLength, final String cacheMode) throws LibvirtException, InternalErrorException { + final Long iopsWriteRate, final Long iopsWriteRateMax, final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails) throws LibvirtException, InternalErrorException { List disks = null; Domain dm = null; DiskDef diskdef = null; @@ -1306,6 +1371,10 @@ public class KVMStorageProcessor implements StorageProcessor { diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, busT); } + if (encryptDetails != null) { + diskdef.setLibvirtDiskEncryptDetails(encryptDetails); + } + if ((bytesReadRate != null) && (bytesReadRate > 0)) { diskdef.setBytesReadRate(bytesReadRate); } @@ -1363,19 +1432,27 @@ public class KVMStorageProcessor implements StorageProcessor { final PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getDataStore(); final String vmName = cmd.getVmName(); final String serial = resource.diskUuidToSerial(vol.getUuid()); + try { final Connect conn = LibvirtConnection.getConnectionByVmName(vmName); + DiskDef.LibvirtDiskEncryptDetails encryptDetails = null; + if (vol.getPassphrase() != null && vol.getPassphrase().length > 0) { + String secretUuid = resource.createLibvirtVolumeSecret(conn, vol.getPath(), vol.getPassphrase()); + encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(secretUuid, QemuObject.EncryptFormat.enumValue(vol.getEncryptFormat())); + vol.clearPassphrase(); + } storagePoolMgr.connectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath(), disk.getDetails()); final KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); final String volCacheMode = vol.getCacheMode() == null ? null : vol.getCacheMode().toString(); + s_logger.debug(String.format("Attaching physical disk %s with format %s", phyDisk.getPath(), phyDisk.getFormat())); attachOrDetachDisk(conn, true, vmName, phyDisk, disk.getDiskSeq().intValue(), serial, vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(), vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(), vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(), - vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode); + vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, encryptDetails); return new AttachAnswer(disk); } catch (final LibvirtException e) { @@ -1388,6 +1465,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to attach volume: " + vol.getPath() + ", due to ", e); return new AttachAnswer(e.toString()); + } finally { + vol.clearPassphrase(); } } @@ -1408,7 +1487,7 @@ public class KVMStorageProcessor implements StorageProcessor { vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(), vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(), vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(), - vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode); + vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null); storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); @@ -1422,6 +1501,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to detach volume: " + vol.getPath() + ", due to ", e); return new DettachAnswer(e.toString()); + } finally { + vol.clearPassphrase(); } } @@ -1440,7 +1521,7 @@ public class KVMStorageProcessor implements StorageProcessor { destTemplate = primaryPool.getPhysicalDisk(srcBackingFilePath); } return storagePoolMgr.createDiskWithTemplateBacking(destTemplate, volume.getUuid(), format, volume.getSize(), - primaryPool, timeout); + primaryPool, timeout, volume.getPassphrase()); } /** @@ -1448,7 +1529,7 @@ public class KVMStorageProcessor implements StorageProcessor { */ protected KVMPhysicalDisk createFullCloneVolume(MigrationOptions migrationOptions, VolumeObjectTO volume, KVMStoragePool primaryPool, PhysicalDiskFormat format) { s_logger.debug("For VM migration with full-clone volume: Creating empty stub disk for source disk " + migrationOptions.getSrcVolumeUuid() + " and size: " + toHumanReadableSize(volume.getSize()) + " and format: " + format); - return primaryPool.createPhysicalDisk(volume.getUuid(), format, volume.getProvisioningType(), volume.getSize()); + return primaryPool.createPhysicalDisk(volume.getUuid(), format, volume.getProvisioningType(), volume.getSize(), volume.getPassphrase()); } @Override @@ -1470,25 +1551,25 @@ public class KVMStorageProcessor implements StorageProcessor { } MigrationOptions migrationOptions = volume.getMigrationOptions(); - if (migrationOptions != null) { + if (isLinkedCloneMigration(migrationOptions)) { String srcStoreUuid = migrationOptions.getSrcPoolUuid(); StoragePoolType srcPoolType = migrationOptions.getSrcPoolType(); KVMStoragePool srcPool = storagePoolMgr.getStoragePool(srcPoolType, srcStoreUuid); int timeout = migrationOptions.getTimeout(); - - if (migrationOptions.getType() == MigrationOptions.Type.LinkedClone) { - vol = createLinkedCloneVolume(migrationOptions, srcPool, primaryPool, volume, format, timeout); - } else if (migrationOptions.getType() == MigrationOptions.Type.FullClone) { - vol = createFullCloneVolume(migrationOptions, volume, primaryPool, format); - } + vol = createLinkedCloneVolume(migrationOptions, srcPool, primaryPool, volume, format, timeout); + } else if (isFullCloneMigration(migrationOptions)) { + vol = createFullCloneVolume(migrationOptions, volume, primaryPool, format); } else { vol = primaryPool.createPhysicalDisk(volume.getUuid(), format, - volume.getProvisioningType(), disksize); + volume.getProvisioningType(), disksize, volume.getPassphrase()); } final VolumeObjectTO newVol = new VolumeObjectTO(); if(vol != null) { newVol.setPath(vol.getName()); + if (vol.getQemuEncryptFormat() != null) { + newVol.setEncryptFormat(vol.getQemuEncryptFormat().toString()); + } } newVol.setSize(volume.getSize()); newVol.setFormat(ImageFormat.valueOf(format.toString().toUpperCase())); @@ -1497,9 +1578,19 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final Exception e) { s_logger.debug("Failed to create volume: ", e); return new CreateObjectAnswer(e.toString()); + } finally { + volume.clearPassphrase(); } } + protected static boolean isLinkedCloneMigration(MigrationOptions options) { + return options != null && options.getType() == MigrationOptions.Type.LinkedClone; + } + + protected static boolean isFullCloneMigration(MigrationOptions options) { + return options != null && options.getType() == MigrationOptions.Type.FullClone; + } + protected static final MessageFormat SnapshotXML = new MessageFormat(" " + " {0}" + " " + " {1}" + " " + " "); @@ -1523,6 +1614,10 @@ public class KVMStorageProcessor implements StorageProcessor { } } + if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && qemuVolumeHasEncryption(volume)) { + throw new CloudRuntimeException("VM is running, encrypted volume snapshots aren't supported"); + } + final KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); final KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), volume.getPath()); @@ -1577,13 +1672,32 @@ public class KVMStorageProcessor implements StorageProcessor { } } else { /* VM is not running, create a snapshot by ourself */ - final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger); - command.add(MANAGE_SNAPSTHOT_CREATE_OPTION, disk.getPath()); - command.add(NAME_OPTION, snapshotName); - final String result = command.execute(); - if (result != null) { - s_logger.debug("Failed to manage snapshot: " + result); - return new CreateObjectAnswer("Failed to manage snapshot: " + result); + /* if we have a Qemu image that is LUKS encrypted, use direct qemu-img call to snapshot. Otherwise call the snapshot script as usual */ + if (qemuVolumeHasEncryption(volume)) { + List passphraseObjects = new ArrayList<>(); + try (KeyFile keyFile = new KeyFile(volume.getPassphrase())) { + passphraseObjects.add( + QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.QCOW2, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", null) + ); + QemuImg q = new QemuImg(cmd.getWait()); + QemuImageOptions imgOptions = new QemuImageOptions(PhysicalDiskFormat.QCOW2, disk.getPath(),"sec0"); + q.snapshot(imgOptions, snapshotName, passphraseObjects); + } catch (QemuImgException ex) { + throw new CloudRuntimeException("Failed to run qemu-img for snapshot", ex); + } catch (IOException ex) { + throw new CloudRuntimeException("Failed to create keyfile for encrypted snapshot", ex); + } finally { + volume.clearPassphrase(); + } + } else { + final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger); + command.add(MANAGE_SNAPSTHOT_CREATE_OPTION, disk.getPath()); + command.add(NAME_OPTION, snapshotName); + final String result = command.execute(); + if (result != null) { + s_logger.debug("Failed to manage snapshot: " + result); + return new CreateObjectAnswer("Failed to manage snapshot: " + result); + } } } } @@ -1595,6 +1709,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final LibvirtException e) { s_logger.debug("Failed to manage snapshot: ", e); return new CreateObjectAnswer("Failed to manage snapshot: " + e.toString()); + } finally { + volume.clearPassphrase(); } } @@ -1625,18 +1741,20 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to delete volume: ", e); return new Answer(null, false, e.toString()); + } finally { + vol.clearPassphrase(); } } @Override public Answer createVolumeFromSnapshot(final CopyCommand cmd) { + final DataTO srcData = cmd.getSrcTO(); + final SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData; + final VolumeObjectTO volume = snapshot.getVolume(); try { - final DataTO srcData = cmd.getSrcTO(); - final SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData; final DataTO destData = cmd.getDestTO(); final PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); final DataStoreTO imageStore = srcData.getDataStore(); - final VolumeObjectTO volume = snapshot.getVolume(); if (!(imageStore instanceof NfsTO || imageStore instanceof PrimaryDataStoreTO)) { return new CopyCmdAnswer("unsupported protocol"); @@ -1665,6 +1783,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to createVolumeFromSnapshot: ", e); return new CopyCmdAnswer(e.toString()); + } finally { + volume.clearPassphrase(); } } @@ -1791,10 +1911,10 @@ public class KVMStorageProcessor implements StorageProcessor { @Override public Answer deleteSnapshot(final DeleteCommand cmd) { String snap_full_name = ""; + SnapshotObjectTO snapshotTO = (SnapshotObjectTO) cmd.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) snapshotTO.getDataStore(); + VolumeObjectTO volume = snapshotTO.getVolume(); try { - SnapshotObjectTO snapshotTO = (SnapshotObjectTO) cmd.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) snapshotTO.getDataStore(); - VolumeObjectTO volume = snapshotTO.getVolume(); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), volume.getPath()); String snapshotFullPath = snapshotTO.getPath(); @@ -1823,7 +1943,11 @@ public class KVMStorageProcessor implements StorageProcessor { } } else if (primaryPool.getType() == StoragePoolType.NetworkFilesystem || primaryPool.getType() == StoragePoolType.Filesystem) { s_logger.info(String.format("Deleting snapshot (id=%s, name=%s, path=%s, storage type=%s) on primary storage", snapshotTO.getId(), snapshotTO.getName(), snapshotTO.getPath(), primaryPool.getType())); - deleteSnapshotViaManageSnapshotScript(snapshotName, disk); + if (qemuVolumeHasEncryption(volume)) { + deleteSnapshotViaQemuImg(volume, disk.getPath(), snapshotName, cmd.getWait()); + } else { + deleteSnapshotViaManageSnapshotScript(snapshotName, disk); + } } else { s_logger.warn("Operation not implemented for storage pool type of " + primaryPool.getType().toString()); throw new InternalErrorException("Operation not implemented for storage pool type of " + primaryPool.getType().toString()); @@ -1840,6 +1964,8 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (Exception e) { s_logger.error("Failed to remove snapshot " + snap_full_name + ", with exception: " + e.toString()); return new Answer(cmd, false, "Failed to remove snapshot " + snap_full_name); + } finally { + volume.clearPassphrase(); } } @@ -2012,6 +2138,9 @@ public class KVMStorageProcessor implements StorageProcessor { } catch (final CloudRuntimeException e) { s_logger.debug("Failed to copyVolumeFromPrimaryToPrimary: ", e); return new CopyCmdAnswer(e.toString()); + } finally { + srcVol.clearPassphrase(); + destVol.clearPassphrase(); } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 008cb72c355..4ed5dd1b6a4 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -17,6 +17,7 @@ package com.cloud.hypervisor.kvm.storage; import java.io.File; +import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; @@ -24,10 +25,12 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import org.apache.cloudstack.utils.cryptsetup.KeyFile; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.libvirt.Connect; @@ -104,9 +107,9 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, - KVMStoragePool destPool, int timeout) { - String volumeDesc = String.format("volume [%s], with template backing [%s], in pool [%s] (%s), with size [%s]", name, template.getName(), destPool.getUuid(), - destPool.getType(), size); + KVMStoragePool destPool, int timeout, byte[] passphrase) { + String volumeDesc = String.format("volume [%s], with template backing [%s], in pool [%s] (%s), with size [%s] and encryption is %s", name, template.getName(), destPool.getUuid(), + destPool.getType(), size, passphrase != null && passphrase.length > 0); if (!poolTypesThatEnableCreateDiskFromTemplateBacking.contains(destPool.getType())) { s_logger.info(String.format("Skipping creation of %s due to pool type is none of the following types %s.", volumeDesc, poolTypesThatEnableCreateDiskFromTemplateBacking.stream() @@ -125,12 +128,23 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { String destPoolLocalPath = destPool.getLocalPath(); String destPath = String.format("%s%s%s", destPoolLocalPath, destPoolLocalPath.endsWith("/") ? "" : "/", name); - try { + Map options = new HashMap(); + List passphraseObjects = new ArrayList<>(); + try (KeyFile keyFile = new KeyFile(passphrase)) { QemuImgFile destFile = new QemuImgFile(destPath, format); destFile.setSize(size); QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); - new QemuImg(timeout).create(destFile, backingFile); - } catch (QemuImgException e) { + + if (keyFile.isSet()) { + passphraseObjects.add(QemuObject.prepareSecretForQemuImg(format, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options)); + } + s_logger.debug(String.format("Passphrase is staged to keyFile: %s", keyFile.isSet())); + + QemuImg qemu = new QemuImg(timeout); + qemu.create(destFile, backingFile, options, passphraseObjects); + Map info = qemu.info(destFile); + } catch (QemuImgException | LibvirtException | IOException e) { + // why don't we throw an exception here? I guess we fail to find the volume later and that results in a failure returned? s_logger.error(String.format("Failed to create %s in [%s] due to [%s].", volumeDesc, destPath, e.getMessage()), e); } @@ -563,7 +577,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } @Override - public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type) { + public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details) { s_logger.info("Attempting to create storage pool " + name + " (" + type.toString() + ") in libvirt"); StoragePool sp = null; @@ -743,7 +757,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, - PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { s_logger.info("Attempting to create volume " + name + " (" + pool.getType().toString() + ") in pool " + pool.getUuid() + " with size " + toHumanReadableSize(size)); @@ -755,11 +769,9 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { case Filesystem: switch (format) { case QCOW2: - return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size); case RAW: - return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size); + return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size, passphrase); case DIR: - return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); case TAR: return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); default: @@ -803,37 +815,50 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { private KVMPhysicalDisk createPhysicalDiskByQemuImg(String name, KVMStoragePool pool, - PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { String volPath = pool.getLocalPath() + "/" + name; String volName = name; long virtualSize = 0; long actualSize = 0; + QemuObject.EncryptFormat encryptFormat = null; + List passphraseObjects = new ArrayList<>(); final int timeout = 0; QemuImgFile destFile = new QemuImgFile(volPath); destFile.setFormat(format); destFile.setSize(size); - QemuImg qemu = new QemuImg(timeout); Map options = new HashMap(); if (pool.getType() == StoragePoolType.NetworkFilesystem){ options.put("preallocation", QemuImg.PreallocationType.getPreallocationType(provisioningType).toString()); } - try{ - qemu.create(destFile, options); + try (KeyFile keyFile = new KeyFile(passphrase)) { + QemuImg qemu = new QemuImg(timeout); + if (keyFile.isSet()) { + passphraseObjects.add(QemuObject.prepareSecretForQemuImg(format, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options)); + + // make room for encryption header on raw format, use LUKS + if (format == PhysicalDiskFormat.RAW) { + destFile.setSize(destFile.getSize() - (16<<20)); + destFile.setFormat(PhysicalDiskFormat.LUKS); + } + + encryptFormat = QemuObject.EncryptFormat.LUKS; + } + qemu.create(destFile, null, options, passphraseObjects); Map info = qemu.info(destFile); virtualSize = Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE)); actualSize = new File(destFile.getFileName()).length(); - } catch (QemuImgException | LibvirtException e) { - s_logger.error("Failed to create " + volPath + - " due to a failed executing of qemu-img: " + e.getMessage()); + } catch (QemuImgException | LibvirtException | IOException e) { + throw new CloudRuntimeException(String.format("Failed to create %s due to a failed execution of qemu-img", volPath), e); } KVMPhysicalDisk disk = new KVMPhysicalDisk(volPath, volName, pool); disk.setFormat(format); disk.setSize(actualSize); disk.setVirtualSize(virtualSize); + disk.setQemuEncryptFormat(encryptFormat); return disk; } @@ -975,7 +1000,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { */ @Override public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, - String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { + String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { s_logger.info("Creating volume " + name + " from template " + template.getName() + " in pool " + destPool.getUuid() + " (" + destPool.getType().toString() + ") with size " + toHumanReadableSize(size)); @@ -985,12 +1010,14 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (destPool.getType() == StoragePoolType.RBD) { disk = createDiskFromTemplateOnRBD(template, name, format, provisioningType, size, destPool, timeout); } else { - try { + try (KeyFile keyFile = new KeyFile(passphrase)){ String newUuid = name; - disk = destPool.createPhysicalDisk(newUuid, format, provisioningType, template.getVirtualSize()); + List passphraseObjects = new ArrayList<>(); + disk = destPool.createPhysicalDisk(newUuid, format, provisioningType, template.getVirtualSize(), passphrase); if (disk == null) { throw new CloudRuntimeException("Failed to create disk from template " + template.getName()); } + if (template.getFormat() == PhysicalDiskFormat.TAR) { Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath(), timeout); // TO BE FIXED to aware provisioningType } else if (template.getFormat() == PhysicalDiskFormat.DIR) { @@ -1007,32 +1034,45 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } Map options = new HashMap(); options.put("preallocation", QemuImg.PreallocationType.getPreallocationType(provisioningType).toString()); + + + if (keyFile.isSet()) { + passphraseObjects.add(QemuObject.prepareSecretForQemuImg(format, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options)); + disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS); + } switch(provisioningType){ case THIN: QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); - qemu.create(destFile, backingFile, options); + qemu.create(destFile, backingFile, options, passphraseObjects); break; case SPARSE: case FAT: QemuImgFile srcFile = new QemuImgFile(template.getPath(), template.getFormat()); - qemu.convert(srcFile, destFile, options, null); + qemu.convert(srcFile, destFile, options, passphraseObjects, null, false); break; } } else if (format == PhysicalDiskFormat.RAW) { + PhysicalDiskFormat destFormat = PhysicalDiskFormat.RAW; + Map options = new HashMap(); + + if (keyFile.isSet()) { + destFormat = PhysicalDiskFormat.LUKS; + disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS); + passphraseObjects.add(QemuObject.prepareSecretForQemuImg(destFormat, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options)); + } + QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat()); - QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW); + QemuImgFile destFile = new QemuImgFile(disk.getPath(), destFormat); if (size > template.getVirtualSize()) { destFile.setSize(size); } else { destFile.setSize(template.getVirtualSize()); } QemuImg qemu = new QemuImg(timeout); - Map options = new HashMap(); - qemu.convert(sourceFile, destFile, options, null); + qemu.convert(sourceFile, destFile, options, passphraseObjects, null, false); } - } catch (QemuImgException | LibvirtException e) { - s_logger.error("Failed to create " + disk.getPath() + - " due to a failed executing of qemu-img: " + e.getMessage()); + } catch (QemuImgException | LibvirtException | IOException e) { + throw new CloudRuntimeException(String.format("Failed to create %s due to a failed execution of qemu-img", disk.getPath()), e); } } @@ -1067,7 +1107,6 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } - QemuImg qemu = new QemuImg(timeout); QemuImgFile srcFile; QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), @@ -1076,10 +1115,10 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { disk.getPath())); destFile.setFormat(format); - if (srcPool.getType() != StoragePoolType.RBD) { srcFile = new QemuImgFile(template.getPath(), template.getFormat()); try{ + QemuImg qemu = new QemuImg(timeout); qemu.convert(srcFile, destFile); } catch (QemuImgException | LibvirtException e) { s_logger.error("Failed to create " + disk.getPath() + @@ -1241,6 +1280,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } } + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + return copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + /** * This copies a volume from Primary Storage to Secondary Storage * @@ -1248,7 +1292,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { * in ManagementServerImpl shows that the destPool is always a Secondary Storage Pool */ @Override - public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase) { /** With RBD you can't run qemu-img convert with an existing RBD image as destination @@ -1269,9 +1313,9 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { s_logger.debug("copyPhysicalDisk: disk size:" + toHumanReadableSize(disk.getSize()) + ", virtualsize:" + toHumanReadableSize(disk.getVirtualSize())+" format:"+disk.getFormat()); if (destPool.getType() != StoragePoolType.RBD) { if (disk.getFormat() == PhysicalDiskFormat.TAR) { - newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, Storage.ProvisioningType.THIN, disk.getVirtualSize()); + newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null); } else { - newDisk = destPool.createPhysicalDisk(name, Storage.ProvisioningType.THIN, disk.getVirtualSize()); + newDisk = destPool.createPhysicalDisk(name, Storage.ProvisioningType.THIN, disk.getVirtualSize(), null); } } else { newDisk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + name, name, destPool); @@ -1283,7 +1327,13 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { String destPath = newDisk.getPath(); PhysicalDiskFormat destFormat = newDisk.getFormat(); - QemuImg qemu = new QemuImg(timeout); + QemuImg qemu; + + try { + qemu = new QemuImg(timeout); + } catch (QemuImgException | LibvirtException ex ) { + throw new CloudRuntimeException("Failed to create qemu-img command", ex); + } QemuImgFile srcFile = null; QemuImgFile destFile = null; @@ -1462,5 +1512,4 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { private void deleteDirVol(LibvirtStoragePool pool, StorageVol vol) throws LibvirtException { Script.runSimpleBashScript("rm -r --interactive=never " + vol.getPath()); } - } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index b2e8decfcb1..33c6f0ef393 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -110,15 +110,15 @@ public class LibvirtStoragePool implements KVMStoragePool { @Override public KVMPhysicalDisk createPhysicalDisk(String name, - PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { return this._storageAdaptor - .createPhysicalDisk(name, this, format, provisioningType, size); + .createPhysicalDisk(name, this, format, provisioningType, size, passphrase); } @Override - public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { return this._storageAdaptor.createPhysicalDisk(name, this, - this.getDefaultFormat(), provisioningType, size); + this.getDefaultFormat(), provisioningType, size, passphrase); } @Override @@ -279,4 +279,9 @@ public class LibvirtStoragePool implements KVMStoragePool { } return false; } + + @Override + public Map getDetails() { + return null; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index dc00601674b..9f57083e9a9 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -16,6 +16,26 @@ // under the License. package com.cloud.hypervisor.kvm.storage; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.StringJoiner; + +import javax.annotation.Nonnull; + +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.log4j.Logger; +import org.libvirt.LibvirtException; + +import com.cloud.storage.Storage; +import com.cloud.utils.exception.CloudRuntimeException; import com.linbit.linstor.api.ApiClient; import com.linbit.linstor.api.ApiException; import com.linbit.linstor.api.Configuration; @@ -33,25 +53,6 @@ import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.StoragePool; import com.linbit.linstor.api.model.VolumeDefinition; -import javax.annotation.Nonnull; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.StringJoiner; - -import com.cloud.storage.Storage; -import com.cloud.utils.exception.CloudRuntimeException; -import org.apache.cloudstack.utils.qemu.QemuImg; -import org.apache.cloudstack.utils.qemu.QemuImgException; -import org.apache.cloudstack.utils.qemu.QemuImgFile; -import org.apache.log4j.Logger; -import org.libvirt.LibvirtException; - @StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor) public class LinstorStorageAdaptor implements StorageAdaptor { private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class); @@ -174,7 +175,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { @Override public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, - Storage.StoragePoolType type) + Storage.StoragePoolType type, Map details) { s_logger.debug(String.format( "Linstor createStoragePool: name: '%s', host: '%s', path: %s, userinfo: %s", name, host, path, userInfo)); @@ -197,7 +198,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, QemuImg.PhysicalDiskFormat format, - Storage.ProvisioningType provisioningType, long size) + Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { final String rscName = getLinstorRscName(name); LinstorStoragePool lpool = (LinstorStoragePool) pool; @@ -377,7 +378,8 @@ public class LinstorStorageAdaptor implements StorageAdaptor { Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, - int timeout) + int timeout, + byte[] passphrase) { s_logger.info("Linstor: createDiskFromTemplate"); return copyPhysicalDisk(template, name, destPool, timeout); @@ -401,23 +403,28 @@ public class LinstorStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout) + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + return copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase) { s_logger.debug("Linstor: copyPhysicalDisk"); final QemuImg.PhysicalDiskFormat sourceFormat = disk.getFormat(); final String sourcePath = disk.getPath(); - final QemuImg qemu = new QemuImg(timeout); final QemuImgFile srcFile = new QemuImgFile(sourcePath, sourceFormat); final KVMPhysicalDisk dstDisk = destPools.createPhysicalDisk( - name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.FAT, disk.getVirtualSize()); + name, QemuImg.PhysicalDiskFormat.RAW, Storage.ProvisioningType.FAT, disk.getVirtualSize(), null); final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath()); destFile.setFormat(dstDisk.getFormat()); destFile.setSize(disk.getVirtualSize()); try { + final QemuImg qemu = new QemuImg(timeout); qemu.convert(srcFile, destFile); } catch (QemuImgException | LibvirtException e) { s_logger.error(e); @@ -460,7 +467,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { QemuImg.PhysicalDiskFormat format, long size, KVMStoragePool destPool, - int timeout) + int timeout, byte[] passphrase) { s_logger.debug("Linstor: createDiskFromTemplateBacking"); return null; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java index 0e8a4ed5bed..5bc60fd2399 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStoragePool.java @@ -19,9 +19,10 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; import java.util.Map; -import com.cloud.storage.Storage; import org.apache.cloudstack.utils.qemu.QemuImg; +import com.cloud.storage.Storage; + public class LinstorStoragePool implements KVMStoragePool { private final String _uuid; private final String _sourceHost; @@ -42,15 +43,15 @@ public class LinstorStoragePool implements KVMStoragePool { @Override public KVMPhysicalDisk createPhysicalDisk(String name, QemuImg.PhysicalDiskFormat format, - Storage.ProvisioningType provisioningType, long size) + Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { - return _storageAdaptor.createPhysicalDisk(name, this, format, provisioningType, size); + return _storageAdaptor.createPhysicalDisk(name, this, format, provisioningType, size, passphrase); } @Override - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size) + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { - return _storageAdaptor.createPhysicalDisk(volumeUuid,this, getDefaultFormat(), provisioningType, size); + return _storageAdaptor.createPhysicalDisk(volumeUuid,this, getDefaultFormat(), provisioningType, size, passphrase); } @Override @@ -185,6 +186,11 @@ public class LinstorStoragePool implements KVMStoragePool { return false; } + @Override + public Map getDetails() { + return null; + } + public String getResourceGroup() { return _resourceGroup; } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java index 6db2f82beb4..6af43d50d24 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java @@ -55,7 +55,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { } @Override - public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType) { + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map details) { LibvirtStoragePool storagePool = new LibvirtStoragePool(uuid, path, StoragePoolType.ManagedNFS, this, null); storagePool.setSourceHost(host); @@ -291,6 +291,11 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + return copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase) { throw new UnsupportedOperationException("Copying a disk is not supported in this configuration."); } @@ -315,7 +320,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } @@ -325,7 +330,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, byte[] passphrase) { return null; } @@ -335,7 +340,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index 59eaab0f2a1..81ec46e0083 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -19,6 +19,8 @@ package com.cloud.hypervisor.kvm.storage; import java.io.File; import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -26,11 +28,17 @@ import java.util.Map; import java.util.UUID; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; +import org.apache.cloudstack.utils.cryptsetup.CryptSetup; +import org.apache.cloudstack.utils.cryptsetup.CryptSetupException; +import org.apache.cloudstack.utils.cryptsetup.KeyFile; +import org.apache.cloudstack.utils.qemu.QemuImageOptions; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.log4j.Logger; +import org.libvirt.LibvirtException; import com.cloud.storage.Storage; import com.cloud.storage.StorageLayer; @@ -39,7 +47,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; import com.google.common.base.Strings; -import org.libvirt.LibvirtException; @StorageAdaptorInfo(storagePoolType= Storage.StoragePoolType.PowerFlex) public class ScaleIOStorageAdaptor implements StorageAdaptor { @@ -103,11 +110,27 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } KVMPhysicalDisk disk = new KVMPhysicalDisk(diskFilePath, volumePath, pool); - disk.setFormat(QemuImg.PhysicalDiskFormat.RAW); + + // try to discover format as written to disk, rather than assuming raw. + // We support qcow2 for stored primary templates, disks seen as other should be treated as raw. + QemuImg qemu = new QemuImg(0); + QemuImgFile qemuFile = new QemuImgFile(diskFilePath); + Map details = qemu.info(qemuFile); + String detectedFormat = details.getOrDefault(QemuImg.FILE_FORMAT, "none"); + if (detectedFormat.equalsIgnoreCase(QemuImg.PhysicalDiskFormat.QCOW2.toString())) { + disk.setFormat(QemuImg.PhysicalDiskFormat.QCOW2); + } else { + disk.setFormat(QemuImg.PhysicalDiskFormat.RAW); + } long diskSize = getPhysicalDiskSize(diskFilePath); disk.setSize(diskSize); - disk.setVirtualSize(diskSize); + + if (details.containsKey(QemuImg.VIRTUAL_SIZE)) { + disk.setVirtualSize(Long.parseLong(details.get(QemuImg.VIRTUAL_SIZE))); + } else { + disk.setVirtualSize(diskSize); + } return disk; } catch (Exception e) { @@ -117,8 +140,8 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } @Override - public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type) { - ScaleIOStoragePool storagePool = new ScaleIOStoragePool(uuid, host, port, path, type, this); + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, Storage.StoragePoolType type, Map details) { + ScaleIOStoragePool storagePool = new ScaleIOStoragePool(uuid, host, port, path, type, details, this); MapStorageUuidToStoragePool.put(uuid, storagePool); return storagePool; } @@ -128,9 +151,37 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { return MapStorageUuidToStoragePool.remove(uuid) != null; } + /** + * ScaleIO doesn't need to communicate with the hypervisor normally to create a volume. This is used only to prepare a ScaleIO data disk for encryption. + * @param name disk path + * @param pool pool + * @param format disk format + * @param provisioningType provisioning type + * @param size disk size + * @param passphrase passphrase + * @return the disk object + */ @Override - public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { - return null; + public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { + if (passphrase == null || passphrase.length == 0) { + return null; + } + + if(!connectPhysicalDisk(name, pool, null)) { + throw new CloudRuntimeException(String.format("Failed to ensure disk %s was present", name)); + } + + KVMPhysicalDisk disk = getPhysicalDisk(name, pool); + + try { + CryptSetup crypt = new CryptSetup(); + crypt.luksFormat(passphrase, CryptSetup.LuksType.LUKS, disk.getPath()); + disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS); + } catch (CryptSetupException ex) { + throw new CloudRuntimeException("Failed to set up encryption for block device " + disk.getPath(), ex); + } + + return disk; } @Override @@ -228,7 +279,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } @@ -244,6 +295,11 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + return copyPhysicalDisk(disk, name, destPool, timeout, null, null); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[]dstPassphrase) { if (Strings.isNullOrEmpty(name) || disk == null || destPool == null) { LOGGER.error("Unable to copy physical disk due to insufficient data"); throw new CloudRuntimeException("Unable to copy physical disk due to insufficient data"); @@ -261,18 +317,49 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { destDisk.setVirtualSize(disk.getVirtualSize()); destDisk.setSize(disk.getSize()); - QemuImg qemu = new QemuImg(timeout); + QemuImg qemu = null; QemuImgFile srcFile = null; QemuImgFile destFile = null; + String srcKeyName = "sec0"; + String destKeyName = "sec1"; + List qemuObjects = new ArrayList<>(); + Map options = new HashMap(); + CryptSetup cryptSetup = null; - try { - srcFile = new QemuImgFile(disk.getPath(), disk.getFormat()); - destFile = new QemuImgFile(destDisk.getPath(), destDisk.getFormat()); + try (KeyFile srcKey = new KeyFile(srcPassphrase); KeyFile dstKey = new KeyFile(dstPassphrase)){ + qemu = new QemuImg(timeout, true, true); + String srcPath = disk.getPath(); + String destPath = destDisk.getPath(); + QemuImg.PhysicalDiskFormat destFormat = destDisk.getFormat(); + QemuImageOptions qemuImageOpts = new QemuImageOptions(srcPath); - LOGGER.debug("Starting copy from source disk image " + srcFile.getFileName() + " to PowerFlex volume: " + destDisk.getPath()); - qemu.convert(srcFile, destFile, true); + if (srcKey.isSet()) { + qemuObjects.add(QemuObject.prepareSecretForQemuImg(disk.getFormat(), null , srcKey.toString(), srcKeyName, options)); + qemuImageOpts = new QemuImageOptions(disk.getFormat(), srcPath, srcKeyName); + } + + if (dstKey.isSet()) { + if (qemu.supportsSkipZeros()) { + // format and open luks device rather than letting qemu do a slow copy of full image + cryptSetup = new CryptSetup(); + cryptSetup.luksFormat(dstPassphrase, CryptSetup.LuksType.LUKS, destDisk.getPath()); + cryptSetup.open(dstPassphrase, CryptSetup.LuksType.LUKS, destDisk.getPath(), name); + destPath = String.format("/dev/mapper/%s", name); + } else { + qemuObjects.add(QemuObject.prepareSecretForQemuImg(destDisk.getFormat(), null, dstKey.toString(), destKeyName, options)); + destFormat = QemuImg.PhysicalDiskFormat.LUKS; + } + destDisk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS); + } + + srcFile = new QemuImgFile(srcPath, disk.getFormat()); + destFile = new QemuImgFile(destPath, destFormat); + + boolean forceSourceFormat = srcFile.getFormat() == QemuImg.PhysicalDiskFormat.RAW; + LOGGER.debug(String.format("Starting copy from source disk %s(%s) to PowerFlex volume %s(%s), forcing source format is %b", srcFile.getFileName(), srcFile.getFormat(), destFile.getFileName(), destFile.getFormat(), forceSourceFormat)); + qemu.convert(srcFile, destFile, options, qemuObjects, qemuImageOpts,null, forceSourceFormat); LOGGER.debug("Succesfully converted source disk image " + srcFile.getFileName() + " to PowerFlex volume: " + destDisk.getPath()); - } catch (QemuImgException | LibvirtException e) { + } catch (QemuImgException | LibvirtException | IOException | CryptSetupException e) { try { Map srcInfo = qemu.info(srcFile); LOGGER.debug("Source disk info: " + Arrays.asList(srcInfo)); @@ -283,6 +370,14 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { String errMsg = String.format("Unable to convert/copy from %s to %s, due to: %s", disk.getName(), name, ((Strings.isNullOrEmpty(e.getMessage())) ? "an unknown error" : e.getMessage())); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg, e); + } finally { + if (cryptSetup != null) { + try { + cryptSetup.close(name); + } catch (CryptSetupException ex) { + LOGGER.warn("Failed to clean up LUKS disk after copying disk", ex); + } + } } return destDisk; @@ -309,7 +404,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, QemuImg.PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, QemuImg.PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) { return null; } @@ -346,6 +441,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { QemuImgFile srcFile = null; QemuImgFile destFile = null; try { + QemuImg qemu = new QemuImg(timeout, true, true); destDisk = destPool.getPhysicalDisk(destTemplatePath); if (destDisk == null) { LOGGER.error("Failed to find the disk: " + destTemplatePath + " of the storage pool: " + destPool.getUuid()); @@ -368,14 +464,21 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { } srcFile = new QemuImgFile(srcTemplateFilePath, srcFileFormat); - destFile = new QemuImgFile(destDisk.getPath(), destDisk.getFormat()); + qemu.info(srcFile); + /** + * Even though the disk itself is raw, we store templates on ScaleIO the raw volumes in qcow2 format. + * This improves performance by reading/writing less data to volume, saves the unused space for encryption header, and + * nicely encapsulates VM images that might contain LUKS data (as opposed to converting to raw which would look like a LUKS volume). + */ + destFile = new QemuImgFile(destDisk.getPath(), QemuImg.PhysicalDiskFormat.QCOW2); + destFile.setSize(srcFile.getSize()); LOGGER.debug("Starting copy from source downloaded template " + srcFile.getFileName() + " to PowerFlex template volume: " + destDisk.getPath()); - QemuImg qemu = new QemuImg(timeout); + qemu.create(destFile); qemu.convert(srcFile, destFile); - LOGGER.debug("Succesfully converted source downloaded template " + srcFile.getFileName() + " to PowerFlex template volume: " + destDisk.getPath()); + LOGGER.debug("Successfully converted source downloaded template " + srcFile.getFileName() + " to PowerFlex template volume: " + destDisk.getPath()); } catch (QemuImgException | LibvirtException e) { - LOGGER.error("Failed to convert from " + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " + e.getMessage(), e); + LOGGER.error("Failed to convert. The error was: " + e.getMessage(), e); destDisk = null; } finally { Script.runSimpleBashScript("rm -f " + srcTemplateFilePath); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java index 4ead92d6a0d..cf977f5467b 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePool.java @@ -20,6 +20,8 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; import java.util.Map; +import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; +import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; import org.apache.cloudstack.utils.qemu.QemuImg; import com.cloud.storage.Storage; @@ -34,8 +36,9 @@ public class ScaleIOStoragePool implements KVMStoragePool { private long capacity; private long used; private long available; + private Map details; - public ScaleIOStoragePool(String uuid, String host, int port, String path, Storage.StoragePoolType poolType, StorageAdaptor adaptor) { + public ScaleIOStoragePool(String uuid, String host, int port, String path, Storage.StoragePoolType poolType, Map poolDetails, StorageAdaptor adaptor) { this.uuid = uuid; sourceHost = host; sourcePort = port; @@ -45,15 +48,34 @@ public class ScaleIOStoragePool implements KVMStoragePool { capacity = 0; used = 0; available = 0; + details = poolDetails; + addSDCDetails(); + } + + private void addSDCDetails() { + if (details == null || !details.containsKey(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID)) { + return; + } + + String storageSystemId = details.get(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID); + String sdcId = ScaleIOUtil.getSdcId(storageSystemId); + if (sdcId != null) { + details.put(ScaleIOGatewayClient.SDC_ID, sdcId); + } else { + String sdcGuId = ScaleIOUtil.getSdcGuid(); + if (sdcGuId != null) { + details.put(ScaleIOGatewayClient.SDC_GUID, sdcGuId); + } + } } @Override - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { - return null; + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, QemuImg.PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { + return this.storageAdaptor.createPhysicalDisk(volumeUuid, this, format, provisioningType, size, passphrase); } @Override - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size) { + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size, byte[] passphrase) { return null; } @@ -178,4 +200,9 @@ public class ScaleIOStoragePool implements KVMStoragePool { public boolean supportsConfigDriveIso() { return false; } + + @Override + public Map getDetails() { + return this.details; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index 570c2070c75..ca0f77f4512 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -35,12 +35,12 @@ public interface StorageAdaptor { // it with info from local disk, and return it public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool); - public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type); + public KVMStoragePool createStoragePool(String name, String host, int port, String path, String userInfo, StoragePoolType type, Map details); public boolean deleteStoragePool(String uuid); public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, - PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size); + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase); // given disk path (per database) and pool, prepare disk on host public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details); @@ -58,13 +58,14 @@ public interface StorageAdaptor { public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, - KVMStoragePool destPool, int timeout); + KVMStoragePool destPool, int timeout, byte[] passphrase); public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool); public List listPhysicalDisks(String storagePoolUuid, KVMStoragePool pool); public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout); + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] dstPassphrase); public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool, int timeout); @@ -80,7 +81,7 @@ public interface StorageAdaptor { */ KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, - KVMStoragePool destPool, int timeout); + KVMStoragePool destPool, int timeout, byte[] passphrase); /** * Create physical disk on Primary Storage from direct download template on the host (in temporary location) diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetup.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetup.java new file mode 100644 index 00000000000..6489ade23fd --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetup.java @@ -0,0 +1,108 @@ +package org.apache.cloudstack.utils.cryptsetup; + +import com.cloud.utils.script.Script; + +import java.io.IOException; + +public class CryptSetup { + protected String _commandPath = "cryptsetup"; + + /** + * LuksType represents the possible types that can be passed to cryptsetup. + * NOTE: Only "luks1" is currently supported with Libvirt, so while + * this utility may be capable of creating various types, care should + * be taken to use types that work for the use case. + */ + public enum LuksType { + LUKS("luks1"), LUKS2("luks2"), PLAIN("plain"), TCRYPT("tcrypt"), BITLK("bitlk"); + + String luksType; + + LuksType(String type) { this.luksType = type; } + + @Override + public String toString() { + return luksType; + } + } + + public CryptSetup(final String commandPath) { + _commandPath = commandPath; + } + + public CryptSetup() {} + + public void open(byte[] passphrase, LuksType luksType, String diskPath, String diskName) throws CryptSetupException { + try(KeyFile key = new KeyFile(passphrase)) { + final Script script = new Script(_commandPath); + script.add("open"); + script.add("--key-file"); + script.add(key.toString()); + script.add("--allow-discards"); + script.add(diskPath); + script.add(diskName); + + final String result = script.execute(); + if (result != null) { + throw new CryptSetupException(result); + } + } catch (IOException ex) { + throw new CryptSetupException(String.format("Failed to open encrypted device at '%s'", diskPath), ex); + } + } + + public void close(String diskName) throws CryptSetupException { + final Script script = new Script(_commandPath); + script.add("close"); + script.add(diskName); + + final String result = script.execute(); + if (result != null) { + throw new CryptSetupException(result); + } + } + + /** + * Formats a file using cryptsetup + * @param passphrase + * @param luksType + * @param diskPath + * @throws CryptSetupException + */ + public void luksFormat(byte[] passphrase, LuksType luksType, String diskPath) throws CryptSetupException { + try(KeyFile key = new KeyFile(passphrase)) { + final Script script = new Script(_commandPath); + script.add("luksFormat"); + script.add("-q"); + script.add("--force-password"); + script.add("--key-file"); + script.add(key.toString()); + script.add("--type"); + script.add(luksType.toString()); + script.add(diskPath); + + final String result = script.execute(); + if (result != null) { + throw new CryptSetupException(result); + } + } catch (IOException ex) { + throw new CryptSetupException(String.format("Failed to format encrypted device at '%s'", diskPath), ex); + } + } + + public boolean isSupported() { + final Script script = new Script(_commandPath); + script.add("--usage"); + final String result = script.execute(); + return result == null; + } + + public boolean isLuks(String filePath) { + final Script script = new Script(_commandPath); + script.add("isLuks"); + script.add(filePath); + + final String result = script.execute(); + return result == null; + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupException.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupException.java new file mode 100644 index 00000000000..210413d03cc --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupException.java @@ -0,0 +1,9 @@ +package org.apache.cloudstack.utils.cryptsetup; + +public class CryptSetupException extends Exception { + public CryptSetupException(String message) { + super(message); + } + + public CryptSetupException(String message, Exception ex) { super(message, ex); } +} diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/KeyFile.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/KeyFile.java new file mode 100644 index 00000000000..c11de561cf4 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/cryptsetup/KeyFile.java @@ -0,0 +1,60 @@ +package org.apache.cloudstack.utils.cryptsetup; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; + +public class KeyFile implements Closeable { + private Path filePath = null; + + /** + * KeyFile represents a temporary file for storing data + * to pass to commands, as an alternative to putting sensitive + * data on the command line. + * @param key byte array of content for the KeyFile + * @throws IOException as the IOException for creating KeyFile + */ + public KeyFile(byte[] key) throws IOException { + if (key != null && key.length > 0) { + Set permissions = PosixFilePermissions.fromString("rw-------"); + filePath = Files.createTempFile("keyfile", ".tmp", PosixFilePermissions.asFileAttribute(permissions)); + Files.write(filePath, key); + } + } + + public Path getPath() { + return filePath; + } + + public boolean isSet() { + return filePath != null; + } + + /** + * Converts the keyfile to the absolute path String where it is located + * @return absolute path as String + */ + @Override + public String toString() { + if (filePath != null) { + return filePath.toAbsolutePath().toString(); + } + return null; + } + + /** + * Deletes the underlying key file + * @throws IOException as the IOException for deleting the underlying key file + */ + @Override + public void close() throws IOException { + if (isSet()) { + Files.delete(filePath); + filePath = null; + } + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImageOptions.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImageOptions.java new file mode 100644 index 00000000000..1b5e4eff261 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImageOptions.java @@ -0,0 +1,65 @@ +package org.apache.cloudstack.utils.qemu; + +import com.google.common.base.Joiner; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +public class QemuImageOptions { + private Map params = new HashMap<>(); + private static final String FILENAME_PARAM_KEY = "file.filename"; + private static final String LUKS_KEY_SECRET_PARAM_KEY = "key-secret"; + private static final String QCOW2_KEY_SECRET_PARAM_KEY = "encrypt.key-secret"; + + public QemuImageOptions(String filePath) { + params.put(FILENAME_PARAM_KEY, filePath); + } + + /** + * Constructor for self-crafting the full map of parameters + * @param params the map of parameters + */ + public QemuImageOptions(Map params) { + this.params = params; + } + + /** + * Constructor for crafting image options that may contain a secret or format + * @param format optional format, renders as "driver" option + * @param filePath required path of image + * @param secretName optional secret name for image. Secret only applies for QCOW2 or LUKS format + */ + public QemuImageOptions(QemuImg.PhysicalDiskFormat format, String filePath, String secretName) { + params.put(FILENAME_PARAM_KEY, filePath); + if (secretName != null && !secretName.isBlank()) { + switch (format) { + case QCOW2: + params.put(QCOW2_KEY_SECRET_PARAM_KEY, secretName); + break; + case LUKS: + params.put(LUKS_KEY_SECRET_PARAM_KEY, secretName); + break; + } + } + if (format != null) { + params.put("driver", format.toString()); + } + } + + public void setFormat(QemuImg.PhysicalDiskFormat format) { + if (format != null) { + params.put("driver", format.toString()); + } + } + + /** + * Converts QemuObject into the command strings required by qemu-img flags + * @return array of strings representing command flag and value (--object) + */ + public String[] toCommandFlag() { + Map sorted = new TreeMap<>(params); + String paramString = Joiner.on(",").withKeyValueSeparator("=").join(sorted); + return new String[] {"--image-opts", paramString}; + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java index 7de09a3a935..385c53af4f7 100644 --- a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -16,17 +16,21 @@ // under the License. package org.apache.cloudstack.utils.qemu; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; +import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.lang.StringUtils; +import org.libvirt.LibvirtException; + import com.cloud.hypervisor.kvm.resource.LibvirtConnection; import com.cloud.storage.Storage; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.NotImplementedException; -import org.libvirt.LibvirtException; public class QemuImg { public final static String BACKING_FILE = "backing_file"; @@ -35,11 +39,18 @@ public class QemuImg { public final static String FILE_FORMAT = "file_format"; public final static String IMAGE = "image"; public final static String VIRTUAL_SIZE = "virtual_size"; + public final static String ENCRYPT_FORMAT = "encrypt.format"; + public final static String ENCRYPT_KEY_SECRET = "encrypt.key-secret"; + public final static String TARGET_ZERO_FLAG = "--target-is-zero"; + public final static long QEMU_2_10 = 2010000; /* The qemu-img binary. We expect this to be in $PATH */ public String _qemuImgPath = "qemu-img"; private String cloudQemuImgPath = "cloud-qemu-img"; private int timeout; + private boolean skipZero = false; + private boolean noCache = false; + private long version; private String getQemuImgPathScript = String.format("which %s >& /dev/null; " + "if [ $? -gt 0 ]; then echo \"%s\"; else echo \"%s\"; fi", @@ -47,7 +58,7 @@ public class QemuImg { /* Shouldn't we have KVMPhysicalDisk and LibvirtVMDef read this? */ public static enum PhysicalDiskFormat { - RAW("raw"), QCOW2("qcow2"), VMDK("vmdk"), FILE("file"), RBD("rbd"), SHEEPDOG("sheepdog"), HTTP("http"), HTTPS("https"), TAR("tar"), DIR("dir"); + RAW("raw"), QCOW2("qcow2"), VMDK("vmdk"), FILE("file"), RBD("rbd"), SHEEPDOG("sheepdog"), HTTP("http"), HTTPS("https"), TAR("tar"), DIR("dir"), LUKS("luks"); String format; private PhysicalDiskFormat(final String format) { @@ -90,8 +101,41 @@ public class QemuImg { } } - public QemuImg(final int timeout) { + /** + * Create a QemuImg object that supports skipping target zeroes + * We detect this support via qemu-img help since support can + * be backported rather than found in a specific version. + * + * @param timeout script timeout, default 0 + * @param skipZeroIfSupported Don't write zeroes to target device during convert, if supported by qemu-img + * @param noCache Ensure we flush writes to target disk (useful for block device targets) + */ + public QemuImg(final int timeout, final boolean skipZeroIfSupported, final boolean noCache) throws QemuImgException, LibvirtException { + if (skipZeroIfSupported) { + final Script s = new Script(_qemuImgPath, timeout); + s.add("--help"); + + final OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + final String result = s.execute(parser); + + // Older Qemu returns output in result due to --help reporting error status + if (result != null) { + if (result.contains(TARGET_ZERO_FLAG)) { + this.skipZero = true; + } + } else { + if (parser.getLines().contains(TARGET_ZERO_FLAG)) { + this.skipZero = true; + } + } + } this.timeout = timeout; + this.noCache = noCache; + this.version = LibvirtConnection.getConnection().getVersion(); + } + + public QemuImg(final int timeout) throws LibvirtException, QemuImgException { + this(timeout, false, false); } public void setTimeout(final int timeout) { @@ -106,7 +150,8 @@ public class QemuImg { * A alternative path to the qemu-img binary * @return void */ - public QemuImg(final String qemuImgPath) { + public QemuImg(final String qemuImgPath) throws LibvirtException, QemuImgException { + this(0, false, false); _qemuImgPath = qemuImgPath; } @@ -132,9 +177,35 @@ public class QemuImg { * @return void */ public void create(final QemuImgFile file, final QemuImgFile backingFile, final Map options) throws QemuImgException { + create(file, backingFile, options, null); + } + + /** + * Create a new image + * + * This method calls 'qemu-img create' + * + * @param file + * The file to create + * @param backingFile + * A backing file if used (for example with qcow2) + * @param options + * Options for the create. Takes a Map with key value + * pairs which are passed on to qemu-img without validation. + * @param qemuObjects + * Pass list of qemu Object to create - see objects in qemu man page + * @return void + */ + public void create(final QemuImgFile file, final QemuImgFile backingFile, final Map options, final List qemuObjects) throws QemuImgException { final Script s = new Script(_qemuImgPath, timeout); s.add("create"); + if (this.version >= QEMU_2_10 && qemuObjects != null) { + for (QemuObject o : qemuObjects) { + s.add(o.toCommandFlag()); + } + } + if (options != null && !options.isEmpty()) { s.add("-o"); final StringBuilder optionsStr = new StringBuilder(); @@ -244,6 +315,63 @@ public class QemuImg { */ public void convert(final QemuImgFile srcFile, final QemuImgFile destFile, final Map options, final String snapshotName, final boolean forceSourceFormat) throws QemuImgException, LibvirtException { + convert(srcFile, destFile, options, null, snapshotName, forceSourceFormat); + } + + /** + * Convert a image from source to destination + * + * This method calls 'qemu-img convert' and takes five objects + * as an argument. + * + * + * @param srcFile + * The source file + * @param destFile + * The destination file + * @param options + * Options for the convert. Takes a Map with key value + * pairs which are passed on to qemu-img without validation. + * @param qemuObjects + * Pass qemu Objects to create - see objects in qemu man page + * @param snapshotName + * If it is provided, convertion uses it as parameter + * @param forceSourceFormat + * If true, specifies the source format in the conversion cmd + * @return void + */ + public void convert(final QemuImgFile srcFile, final QemuImgFile destFile, + final Map options, final List qemuObjects, final String snapshotName, final boolean forceSourceFormat) throws QemuImgException, LibvirtException { + QemuImageOptions imageOpts = new QemuImageOptions(srcFile.getFormat(), srcFile.getFileName(), null); + convert(srcFile, destFile, options, qemuObjects, imageOpts, snapshotName, forceSourceFormat); + } + + /** + * Convert a image from source to destination + * + * This method calls 'qemu-img convert' and takes five objects + * as an argument. + * + * + * @param srcFile + * The source file + * @param destFile + * The destination file + * @param options + * Options for the convert. Takes a Map with key value + * pairs which are passed on to qemu-img without validation. + * @param qemuObjects + * Pass qemu Objects to convert - see objects in qemu man page + * @param srcImageOpts + * pass qemu --image-opts to convert + * @param snapshotName + * If it is provided, convertion uses it as parameter + * @param forceSourceFormat + * If true, specifies the source format in the conversion cmd + * @return void + */ + public void convert(final QemuImgFile srcFile, final QemuImgFile destFile, + final Map options, final List qemuObjects, final QemuImageOptions srcImageOpts, final String snapshotName, final boolean forceSourceFormat) throws QemuImgException { Script script = new Script(_qemuImgPath, timeout); if (StringUtils.isNotBlank(snapshotName)) { String qemuPath = Script.runSimpleBashScript(getQemuImgPathScript); @@ -251,41 +379,56 @@ public class QemuImg { } script.add("convert"); - Long version = LibvirtConnection.getConnection().getVersion(); - if (version >= 2010000) { - script.add("-U"); - } - // autodetect source format unless specified explicitly - if (forceSourceFormat) { - script.add("-f"); - script.add(srcFile.getFormat().toString()); + if (skipZero && Files.exists(Paths.get(destFile.getFileName()))) { + script.add("-n"); + script.add(TARGET_ZERO_FLAG); + script.add("-W"); + // with target-is-zero we skip zeros in 1M chunks for compatibility + script.add("-S"); + script.add("1M"); } script.add("-O"); script.add(destFile.getFormat().toString()); - if (options != null && !options.isEmpty()) { - script.add("-o"); - final StringBuffer optionsBuffer = new StringBuffer(); - for (final Map.Entry option : options.entrySet()) { - optionsBuffer.append(option.getKey()).append('=').append(option.getValue()).append(','); - } - String optionsStr = optionsBuffer.toString(); - optionsStr = optionsStr.replaceAll(",$", ""); - script.add(optionsStr); - } + addScriptOptionsFromMap(options, script); if (StringUtils.isNotBlank(snapshotName)) { - if (!forceSourceFormat) { - script.add("-f"); - script.add(srcFile.getFormat().toString()); + if (this.version >= QEMU_2_10) { + script.add("-l"); + } else { + script.add("-s"); } - script.add("-s"); script.add(snapshotName); } - script.add(srcFile.getFileName()); + if (noCache) { + script.add("-t"); + script.add("none"); + } + + if (this.version >= QEMU_2_10) { + script.add("-U"); + + if (forceSourceFormat) { + srcImageOpts.setFormat(srcFile.getFormat()); + } + script.add(srcImageOpts.toCommandFlag()); + + if (qemuObjects != null) { + for (QemuObject o : qemuObjects) { + script.add(o.toCommandFlag()); + } + } + } else { + if (forceSourceFormat) { + script.add("-f"); + script.add(srcFile.getFormat().toString()); + } + script.add(srcFile.getFileName()); + } + script.add(destFile.getFileName()); final String result = script.execute(); @@ -407,8 +550,7 @@ public class QemuImg { public Map info(final QemuImgFile file) throws QemuImgException, LibvirtException { final Script s = new Script(_qemuImgPath); s.add("info"); - Long version = LibvirtConnection.getConnection().getVersion(); - if (version >= 2010000) { + if (this.version >= QEMU_2_10) { s.add("-U"); } s.add(file.getFileName()); @@ -436,12 +578,72 @@ public class QemuImg { info.put(key, value); } } + + // set some missing attributes in passed file, if found + if (info.containsKey(VIRTUAL_SIZE) && file.getSize() == 0L) { + file.setSize(Long.parseLong(info.get(VIRTUAL_SIZE))); + } + + if (info.containsKey(FILE_FORMAT) && file.getFormat() == null) { + file.setFormat(PhysicalDiskFormat.valueOf(info.get(FILE_FORMAT).toUpperCase())); + } + return info; } - /* List, apply, create or delete snapshots in image */ - public void snapshot() throws QemuImgException { + /* create snapshots in image */ + public void snapshot(final QemuImageOptions srcImageOpts, final String snapshotName, final List qemuObjects) throws QemuImgException { + final Script s = new Script(_qemuImgPath, timeout); + s.add("snapshot"); + s.add("-c"); + s.add(snapshotName); + for (QemuObject o : qemuObjects) { + s.add(o.toCommandFlag()); + } + + s.add(srcImageOpts.toCommandFlag()); + + final String result = s.execute(); + if (result != null) { + throw new QemuImgException(result); + } + } + + /* delete snapshots in image */ + public void deleteSnapshot(final QemuImageOptions srcImageOpts, final String snapshotName, final List qemuObjects) throws QemuImgException { + final Script s = new Script(_qemuImgPath, timeout); + s.add("snapshot"); + s.add("-d"); + s.add(snapshotName); + + for (QemuObject o : qemuObjects) { + s.add(o.toCommandFlag()); + } + + s.add(srcImageOpts.toCommandFlag()); + + final String result = s.execute(); + if (result != null) { + // support idempotent delete calls, if no snapshot exists we are good. + if (result.contains("snapshot not found") || result.contains("Can't find the snapshot")) { + return; + } + throw new QemuImgException(result); + } + } + + private void addScriptOptionsFromMap(Map options, Script s) { + if (options != null && !options.isEmpty()) { + s.add("-o"); + final StringBuffer optionsBuffer = new StringBuffer(); + for (final Map.Entry option : options.entrySet()) { + optionsBuffer.append(option.getKey()).append('=').append(option.getValue()).append(','); + } + String optionsStr = optionsBuffer.toString(); + optionsStr = optionsStr.replaceAll(",$", ""); + s.add(optionsStr); + } } /* Changes the backing file of an image */ @@ -512,6 +714,33 @@ public class QemuImg { s.execute(); } + /** + * Resize an image, new style flags/options + * + * @param imageOptions + * Qemu style image options for the image to resize + * @param qemuObjects + * Qemu style options (e.g. for passing secrets) + * @param size + * The absolute final size of the image + */ + public void resize(final QemuImageOptions imageOptions, final List qemuObjects, final long size) throws QemuImgException { + final Script s = new Script(_qemuImgPath); + s.add("resize"); + + for (QemuObject o : qemuObjects) { + s.add(o.toCommandFlag()); + } + + s.add(imageOptions.toCommandFlag()); + s.add(Long.toString(size)); + + final String result = s.execute(); + if (result != null) { + throw new QemuImgException(result); + } + } + /** * Resize an image * @@ -528,4 +757,12 @@ public class QemuImg { public void resize(final QemuImgFile file, final long size) throws QemuImgException { this.resize(file, size, false); } + + /** + * Does qemu-img support --target-is-zero + * @return boolean + */ + public boolean supportsSkipZeros() { + return this.skipZero; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuObject.java b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuObject.java new file mode 100644 index 00000000000..e2b71f0f269 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuObject.java @@ -0,0 +1,132 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.utils.qemu; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Joiner; + +public class QemuObject { + private final ObjectType type; + private final Map params; + + public enum ObjectParameter { + DATA("data"), + FILE("file"), + FORMAT("format"), + ID("id"), + IV("iv"), + KEYID("keyid"); + + private final String parameter; + + ObjectParameter(String param) { + this.parameter = param; + } + + @Override + public String toString() {return parameter; } + } + + /** + * Supported qemu encryption formats. + * NOTE: Only "luks" is currently supported with Libvirt, so while + * this utility may be capable of creating various formats, care should + * be taken to use types that work for the use case. + */ + public enum EncryptFormat { + LUKS("luks"), + AES("aes"); + + private final String format; + + EncryptFormat(String format) { this.format = format; } + + EncryptFormat(QemuImg.PhysicalDiskFormat format) { + this.format = format.toString(); + } + + @Override + public String toString() { return format;} + + public static EncryptFormat enumValue(String value) { + if (StringUtils.isBlank(value)) { + return LUKS; // default encryption format + } + return EncryptFormat.valueOf(value.toUpperCase()); + } + } + + public enum ObjectType { + SECRET("secret"); + + private final String objectType; + + ObjectType(String objectType) { + this.objectType = objectType; + } + + @Override + public String toString() { + return objectType; + } + } + + public QemuObject(ObjectType type, Map params) { + this.type = type; + this.params = params; + } + + /** + * Converts QemuObject into the command strings required by qemu-img flags + * @return array of strings representing command flag and value (--object) + */ + public String[] toCommandFlag() { + Map sorted = new TreeMap<>(params); + String paramString = Joiner.on(",").withKeyValueSeparator("=").join(sorted); + return new String[] {"--object", String.format("%s,%s", type, paramString) }; + } + + /** + * Creates a QemuObject with the correct parameters for passing encryption secret details to qemu-img + * @param format the image format to use + * @param encryptFormat the encryption format to use (luks) + * @param keyFilePath the path to the file containing encryption key + * @param secretName the name to use for the secret + * @param options the options map for qemu-img (-o flag) + * @return the QemuObject containing encryption parameters + */ + public static QemuObject prepareSecretForQemuImg(QemuImg.PhysicalDiskFormat format, EncryptFormat encryptFormat, String keyFilePath, String secretName, Map options) { + Map params = new HashMap<>(); + params.put(QemuObject.ObjectParameter.ID, secretName); + params.put(QemuObject.ObjectParameter.FILE, keyFilePath); + + if (options != null) { + if (format == QemuImg.PhysicalDiskFormat.QCOW2) { + options.put("encrypt.key-secret", secretName); + options.put("encrypt.format", encryptFormat.toString()); + } else if (format == QemuImg.PhysicalDiskFormat.RAW || format == QemuImg.PhysicalDiskFormat.LUKS) { + options.put("key-secret", secretName); + } + } + return new QemuObject(QemuObject.ObjectType.SECRET, params); + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 3632cf299d3..b951f997909 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -56,6 +56,7 @@ import javax.xml.xpath.XPathFactory; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.linux.CPUStat; import org.apache.cloudstack.utils.linux.MemStat; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; @@ -76,6 +77,7 @@ import org.libvirt.LibvirtException; import org.libvirt.MemoryStatistic; import org.libvirt.NodeInfo; import org.libvirt.StorageVol; +import org.libvirt.VcpuInfo; import org.libvirt.jna.virDomainMemoryStats; import org.mockito.BDDMockito; import org.mockito.Mock; @@ -207,8 +209,6 @@ import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.Type; -import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; -import org.libvirt.VcpuInfo; @RunWith(PowerMockRunner.class) @PrepareForTest(value = {MemStat.class}) @@ -2146,7 +2146,7 @@ public class LibvirtComputingResourceTest { when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(poolManager); when(poolManager.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(primary); - when(primary.createPhysicalDisk(diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), diskCharacteristics.getSize())).thenReturn(vol); + when(primary.createPhysicalDisk(diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), diskCharacteristics.getSize(), null)).thenReturn(vol); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2205,7 +2205,7 @@ public class LibvirtComputingResourceTest { when(poolManager.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(primary); when(primary.getPhysicalDisk(command.getTemplateUrl())).thenReturn(baseVol); - when(poolManager.createDiskFromTemplate(baseVol, diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), primary, baseVol.getSize(), 0)).thenReturn(vol); + when(poolManager.createDiskFromTemplate(baseVol, diskCharacteristics.getPath(), diskCharacteristics.getProvisioningType(), primary, baseVol.getSize(), 0,null)).thenReturn(vol); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -2765,7 +2765,7 @@ public class LibvirtComputingResourceTest { when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr); when(storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() - .getUserInfo(), command.getPool().getType())).thenReturn(kvmStoragePool); + .getUserInfo(), command.getPool().getType(), command.getDetails())).thenReturn(kvmStoragePool); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); @@ -2776,7 +2776,7 @@ public class LibvirtComputingResourceTest { verify(libvirtComputingResource, times(1)).getStoragePoolMgr(); verify(storagePoolMgr, times(1)).createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() - .getUserInfo(), command.getPool().getType()); + .getUserInfo(), command.getPool().getType(), command.getDetails()); } @Test @@ -2788,7 +2788,7 @@ public class LibvirtComputingResourceTest { when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr); when(storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() - .getUserInfo(), command.getPool().getType())).thenReturn(null); + .getUserInfo(), command.getPool().getType(), command.getDetails())).thenReturn(null); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); @@ -2799,7 +2799,7 @@ public class LibvirtComputingResourceTest { verify(libvirtComputingResource, times(1)).getStoragePoolMgr(); verify(storagePoolMgr, times(1)).createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() - .getUserInfo(), command.getPool().getType()); + .getUserInfo(), command.getPool().getType(), command.getDetails()); } @Test @@ -4845,6 +4845,10 @@ public class LibvirtComputingResourceTest { final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class); final Connect conn = Mockito.mock(Connect.class); final StorageVol v = Mockito.mock(StorageVol.class); + final Domain vm = Mockito.mock(Domain.class); + final DomainInfo info = Mockito.mock(DomainInfo.class); + final DomainState state = DomainInfo.DomainState.VIR_DOMAIN_RUNNING; + info.state = state; when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr); when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool); @@ -4858,9 +4862,11 @@ public class LibvirtComputingResourceTest { try { when(libvirtUtilitiesHelper.getConnection()).thenReturn(conn); when(conn.storageVolLookupByPath(path)).thenReturn(v); + when(libvirtUtilitiesHelper.getConnectionByVmName(vmInstance)).thenReturn(conn); + when(conn.domainLookupByName(vmInstance)).thenReturn(vm); + when(vm.getInfo()).thenReturn(info); when(conn.getLibVirVersion()).thenReturn(10010l); - } catch (final LibvirtException e) { fail(e.getMessage()); } @@ -4873,9 +4879,10 @@ public class LibvirtComputingResourceTest { verify(libvirtComputingResource, times(1)).getStoragePoolMgr(); - verify(libvirtComputingResource, times(1)).getLibvirtUtilitiesHelper(); + verify(libvirtComputingResource, times(2)).getLibvirtUtilitiesHelper(); try { verify(libvirtUtilitiesHelper, times(1)).getConnection(); + verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(vmInstance); } catch (final LibvirtException e) { fail(e.getMessage()); } @@ -4896,6 +4903,11 @@ public class LibvirtComputingResourceTest { final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); final KVMPhysicalDisk vol = Mockito.mock(KVMPhysicalDisk.class); final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class); + final Connect conn = Mockito.mock(Connect.class); + final Domain vm = Mockito.mock(Domain.class); + final DomainInfo info = Mockito.mock(DomainInfo.class); + final DomainState state = DomainInfo.DomainState.VIR_DOMAIN_RUNNING; + info.state = state; when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr); when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool); @@ -4904,6 +4916,15 @@ public class LibvirtComputingResourceTest { when(storagePool.getType()).thenReturn(StoragePoolType.Linstor); when(vol.getFormat()).thenReturn(PhysicalDiskFormat.RAW); + when(libvirtComputingResource.getLibvirtUtilitiesHelper()).thenReturn(libvirtUtilitiesHelper); + try { + when(libvirtUtilitiesHelper.getConnectionByVmName(vmInstance)).thenReturn(conn); + when(conn.domainLookupByName(vmInstance)).thenReturn(vm); + when(vm.getInfo()).thenReturn(info); + } catch (final LibvirtException e) { + fail(e.getMessage()); + } + final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -4913,9 +4934,10 @@ public class LibvirtComputingResourceTest { verify(libvirtComputingResource, times(1)).getStoragePoolMgr(); verify(libvirtComputingResource, times(0)).getResizeScriptType(storagePool, vol); - verify(libvirtComputingResource, times(0)).getLibvirtUtilitiesHelper(); + verify(libvirtComputingResource, times(1)).getLibvirtUtilitiesHelper(); try { verify(libvirtUtilitiesHelper, times(0)).getConnection(); + verify(libvirtUtilitiesHelper, times(1)).getConnectionByVmName(vmInstance); } catch (final LibvirtException e) { fail(e.getMessage()); } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java index f2ba293436e..ccab4b01c33 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParserTest.java @@ -29,6 +29,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.RngDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.WatchDogDef; import junit.framework.TestCase; +import org.apache.cloudstack.utils.qemu.QemuObject; public class LibvirtDomainXMLParserTest extends TestCase { @@ -51,6 +52,10 @@ public class LibvirtDomainXMLParserTest extends TestCase { String diskLabel ="vda"; String diskPath = "/var/lib/libvirt/images/my-test-image.qcow2"; + String diskLabel2 ="vdb"; + String diskPath2 = "/var/lib/libvirt/images/my-test-image2.qcow2"; + String secretUuid = "5644d664-a238-3a9b-811c-961f609d29f4"; + String xml = "" + "s-2970-VM" + "4d2c1526-865d-4fc9-a1ac-dbd1801a22d0" + @@ -87,6 +92,16 @@ public class LibvirtDomainXMLParserTest extends TestCase { "" + "

" + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
" + + "" + "" + "" + "" + @@ -200,6 +215,11 @@ public class LibvirtDomainXMLParserTest extends TestCase { assertEquals(deviceType, disks.get(diskId).getDeviceType()); assertEquals(diskFormat, disks.get(diskId).getDiskFormatType()); + DiskDef.LibvirtDiskEncryptDetails encryptDetails = disks.get(1).getLibvirtDiskEncryptDetails(); + assertNotNull(encryptDetails); + assertEquals(QemuObject.EncryptFormat.LUKS, encryptDetails.getEncryptFormat()); + assertEquals(secretUuid, encryptDetails.getPassphraseUuid()); + List channels = parser.getChannels(); for (int i = 0; i < channels.size(); i++) { assertEquals(channelType, channels.get(i).getChannelType()); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java index b0eaad4f269..594460e8702 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java @@ -23,6 +23,7 @@ import java.io.File; import java.util.Arrays; import java.util.List; import java.util.Scanner; +import java.util.UUID; import junit.framework.TestCase; @@ -30,6 +31,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ChannelDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SCSIDef; import org.apache.cloudstack.utils.linux.MemStat; +import org.apache.cloudstack.utils.qemu.QemuObject; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -191,6 +193,24 @@ public class LibvirtVMDefTest extends TestCase { assertEquals(xmlDef, expectedXml); } + @Test + public void testDiskDefWithEncryption() { + String passphraseUuid = UUID.randomUUID().toString(); + DiskDef disk = new DiskDef(); + DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(passphraseUuid, QemuObject.EncryptFormat.LUKS); + disk.defBlockBasedDisk("disk1", 1, DiskDef.DiskBus.VIRTIO); + disk.setLibvirtDiskEncryptDetails(encryptDetails); + String expectedXML = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + assertEquals(disk.toString(), expectedXML); + } + @Test public void testDiskDefWithBurst() { String filePath = "/var/lib/libvirt/images/disk.qcow2"; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java index 23f0ff91128..37c4ec2918f 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java @@ -759,6 +759,41 @@ public class LibvirtMigrateCommandWrapperTest { assertXpath(doc, "/domain/devices/disk/driver/@type", "raw"); } + @Test + public void testReplaceStorageWithSecrets() throws Exception { + Map mapMigrateStorage = new HashMap(); + + final String xmlDesc = + "" + + " " + + " \n" + + " \n" + + " \n" + + " \n" + + " bf8621b3027c497d963b\n" + + " \n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " " + + ""; + + final String volumeFile = "3530f749-82fd-458e-9485-a357e6e541db"; + String newDiskPath = "/mnt/2d0435e1-99e0-4f1d-94c0-bee1f6f8b99e/" + volumeFile; + MigrateDiskInfo diskInfo = new MigrateDiskInfo("123456", DiskType.BLOCK, DriverType.RAW, Source.FILE, newDiskPath); + mapMigrateStorage.put("/mnt/07eb495b-5590-3877-9fb7-23c6e9a40d40/bf8621b3-027c-497d-963b-06319650f048", diskInfo); + final String result = libvirtMigrateCmdWrapper.replaceStorage(xmlDesc, mapMigrateStorage, false); + final String expectedSecretUuid = LibvirtComputingResource.generateSecretUUIDFromString(volumeFile); + + InputStream in = IOUtils.toInputStream(result); + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + Document doc = docBuilder.parse(in); + assertXpath(doc, "/domain/devices/disk/encryption/secret/@uuid", expectedSecretUuid); + } + public void testReplaceStorageXmlDiskNotManagedStorage() throws ParserConfigurationException, TransformerException, SAXException, IOException { final LibvirtMigrateCommandWrapper lw = new LibvirtMigrateCommandWrapper(); String destDisk1FileName = "XXXXXXXXXXXXXX"; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java index 4f18c38a164..6c2f560ecff 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java @@ -26,7 +26,10 @@ import static org.mockito.Mockito.when; import java.io.File; import java.io.FileFilter; +import java.util.HashMap; +import java.util.Map; +import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; @@ -42,8 +45,9 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; +import com.cloud.utils.script.Script; -@PrepareForTest(ScaleIOUtil.class) +@PrepareForTest({ScaleIOUtil.class, Script.class}) @RunWith(PowerMockRunner.class) public class ScaleIOStoragePoolTest { @@ -57,10 +61,13 @@ public class ScaleIOStoragePoolTest { @Before public void setUp() throws Exception { final String uuid = "345fc603-2d7e-47d2-b719-a0110b3732e6"; + final String systemId = "218ce1797566a00f"; final StoragePoolType type = StoragePoolType.PowerFlex; + Map details = new HashMap(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); adapter = spy(new ScaleIOStorageAdaptor(storageLayer)); - pool = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type, adapter); + pool = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type, details, adapter); } @After @@ -69,28 +76,64 @@ public class ScaleIOStoragePoolTest { @Test public void testAttributes() { - assertEquals(pool.getCapacity(), 0); - assertEquals(pool.getUsed(), 0); - assertEquals(pool.getAvailable(), 0); - assertEquals(pool.getUuid(), "345fc603-2d7e-47d2-b719-a0110b3732e6"); - assertEquals(pool.getSourceHost(), "192.168.1.19"); - assertEquals(pool.getSourcePort(), 443); - assertEquals(pool.getSourceDir(), "a519be2f00000000"); - assertEquals(pool.getType(), StoragePoolType.PowerFlex); + assertEquals(0, pool.getCapacity()); + assertEquals(0, pool.getUsed()); + assertEquals(0, pool.getAvailable()); + assertEquals("345fc603-2d7e-47d2-b719-a0110b3732e6", pool.getUuid()); + assertEquals("192.168.1.19", pool.getSourceHost()); + assertEquals(443, pool.getSourcePort()); + assertEquals("a519be2f00000000", pool.getSourceDir()); + assertEquals(StoragePoolType.PowerFlex, pool.getType()); + assertEquals("218ce1797566a00f", pool.getDetails().get(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID)); pool.setCapacity(131072); pool.setUsed(24576); pool.setAvailable(106496); - assertEquals(pool.getCapacity(), 131072); - assertEquals(pool.getUsed(), 24576); - assertEquals(pool.getAvailable(), 106496); + assertEquals(131072, pool.getCapacity()); + assertEquals(24576, pool.getUsed()); + assertEquals(106496, pool.getAvailable()); + } + + @Test + public void testSdcIdAttribute() { + final String uuid = "345fc603-2d7e-47d2-b719-a0110b3732e6"; + final String systemId = "218ce1797566a00f"; + final String sdcId = "301b852c00000003"; + final StoragePoolType type = StoragePoolType.PowerFlex; + Map details = new HashMap(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + + PowerMockito.mockStatic(Script.class); + when(Script.runSimpleBashScript("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(sdcId); + + ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type, details, adapter); + assertEquals(systemId, pool1.getDetails().get(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID)); + assertEquals(sdcId, pool1.getDetails().get(ScaleIOGatewayClient.SDC_ID)); + } + + @Test + public void testSdcGuidAttribute() { + final String uuid = "345fc603-2d7e-47d2-b719-a0110b3732e6"; + final String systemId = "218ce1797566a00f"; + final String sdcGuid = "B0E3BFB8-C20B-43BF-93C8-13339E85AA50"; + final StoragePoolType type = StoragePoolType.PowerFlex; + Map details = new HashMap(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + + PowerMockito.mockStatic(Script.class); + when(Script.runSimpleBashScript("/opt/emc/scaleio/sdc/bin/drv_cfg --query_mdms|grep 218ce1797566a00f|awk '{print $5}'")).thenReturn(null); + when(Script.runSimpleBashScript("/opt/emc/scaleio/sdc/bin/drv_cfg --query_guid")).thenReturn(sdcGuid); + + ScaleIOStoragePool pool1 = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type, details, adapter); + assertEquals(systemId, pool1.getDetails().get(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID)); + assertEquals(sdcGuid, pool1.getDetails().get(ScaleIOGatewayClient.SDC_GUID)); } @Test public void testDefaults() { - assertEquals(pool.getDefaultFormat(), PhysicalDiskFormat.RAW); - assertEquals(pool.getType(), StoragePoolType.PowerFlex); + assertEquals(PhysicalDiskFormat.RAW, pool.getDefaultFormat()); + assertEquals(StoragePoolType.PowerFlex, pool.getType()); assertNull(pool.getAuthUserName()); assertNull(pool.getAuthSecret()); @@ -145,7 +188,7 @@ public class ScaleIOStoragePoolTest { disk.setSize(8192); disk.setVirtualSize(8192); - assertEquals(disk.getPath(), "/dev/disk/by-id/emc-vol-218ce1797566a00f-6c3362b500000001"); + assertEquals("/dev/disk/by-id/emc-vol-218ce1797566a00f-6c3362b500000001", disk.getPath()); when(adapter.getPhysicalDisk(volumeId, pool)).thenReturn(disk); diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupTest.java new file mode 100644 index 00000000000..e031178a465 --- /dev/null +++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/CryptSetupTest.java @@ -0,0 +1,53 @@ +package org.apache.cloudstack.utils.cryptsetup; + +import org.apache.cloudstack.secret.PassphraseVO; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; + +public class CryptSetupTest { + CryptSetup cryptSetup = new CryptSetup(); + + @Before + public void setup() { + Assume.assumeTrue(cryptSetup.isSupported()); + } + + @Test + public void cryptSetupTest() throws IOException, CryptSetupException { + Set permissions = PosixFilePermissions.fromString("rw-------"); + Path path = Files.createTempFile("cryptsetup", ".tmp",PosixFilePermissions.asFileAttribute(permissions)); + + // create a 1MB file to use as a crypt device + RandomAccessFile file = new RandomAccessFile(path.toFile(),"rw"); + file.setLength(10<<20); + file.close(); + + String filePath = path.toAbsolutePath().toString(); + PassphraseVO passphrase = new PassphraseVO(); + + cryptSetup.luksFormat(passphrase.getPassphrase(), CryptSetup.LuksType.LUKS, filePath); + + Assert.assertTrue(cryptSetup.isLuks(filePath)); + + Assert.assertTrue(Files.deleteIfExists(path)); + } + + @Test + public void cryptSetupNonLuksTest() throws IOException { + Set permissions = PosixFilePermissions.fromString("rw-------"); + Path path = Files.createTempFile("cryptsetup", ".tmp",PosixFilePermissions.asFileAttribute(permissions)); + + Assert.assertFalse(cryptSetup.isLuks(path.toAbsolutePath().toString())); + Assert.assertTrue(Files.deleteIfExists(path)); + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/KeyFileTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/KeyFileTest.java new file mode 100644 index 00000000000..887aff4369d --- /dev/null +++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/cryptsetup/KeyFileTest.java @@ -0,0 +1,31 @@ +package org.apache.cloudstack.utils.cryptsetup; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public class KeyFileTest { + + @Test + public void keyFileTest() throws IOException { + byte[] contents = "the quick brown fox".getBytes(); + KeyFile keyFile = new KeyFile(contents); + System.out.printf("New test KeyFile at %s%n", keyFile); + Path path = keyFile.getPath(); + + Assert.assertTrue(keyFile.isSet()); + + // check contents + byte[] fileContents = Files.readAllBytes(path); + Assert.assertArrayEquals(contents, fileContents); + + // delete file on close + keyFile.close(); + + Assert.assertFalse("key file was not cleaned up", Files.exists(path)); + Assert.assertFalse("key file is still set", keyFile.isSet()); + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImageOptionsTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImageOptionsTest.java new file mode 100644 index 00000000000..d891cd696c8 --- /dev/null +++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImageOptionsTest.java @@ -0,0 +1,43 @@ +package org.apache.cloudstack.utils.qemu; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +public class QemuImageOptionsTest { + @Parameterized.Parameters + public static Collection data() { + String imagePath = "/path/to/file"; + String secretName = "secretname"; + return Arrays.asList(new Object[][] { + { null, imagePath, null, new String[]{"--image-opts","file.filename=/path/to/file"} }, + { QemuImg.PhysicalDiskFormat.QCOW2, imagePath, null, new String[]{"--image-opts",String.format("driver=qcow2,file.filename=%s", imagePath)} }, + { QemuImg.PhysicalDiskFormat.RAW, imagePath, secretName, new String[]{"--image-opts",String.format("driver=raw,file.filename=%s", imagePath)} }, + { QemuImg.PhysicalDiskFormat.QCOW2, imagePath, secretName, new String[]{"--image-opts", String.format("driver=qcow2,encrypt.key-secret=%s,file.filename=%s", secretName, imagePath)} }, + { QemuImg.PhysicalDiskFormat.LUKS, imagePath, secretName, new String[]{"--image-opts", String.format("driver=luks,file.filename=%s,key-secret=%s", imagePath, secretName)} } + }); + } + + public QemuImageOptionsTest(QemuImg.PhysicalDiskFormat format, String filePath, String secretName, String[] expected) { + this.format = format; + this.filePath = filePath; + this.secretName = secretName; + this.expected = expected; + } + + private final QemuImg.PhysicalDiskFormat format; + private final String filePath; + private final String secretName; + private final String[] expected; + + @Test + public void qemuImageOptionsFileNameTest() { + QemuImageOptions options = new QemuImageOptions(format, filePath, secretName); + Assert.assertEquals(expected, options.toCommandFlag()); + } +} diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImgTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImgTest.java index 335a5dd9c4a..cb7f6919e36 100644 --- a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImgTest.java +++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuImgTest.java @@ -18,11 +18,17 @@ package org.apache.cloudstack.utils.qemu; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import com.cloud.utils.script.Script; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -32,7 +38,6 @@ import org.junit.Test; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.libvirt.LibvirtException; - @Ignore public class QemuImgTest { @@ -94,7 +99,34 @@ public class QemuImgTest { } @Test - public void testCreateSparseVolume() throws QemuImgException { + public void testCreateWithSecretObject() throws QemuImgException, LibvirtException { + Path testFile = Paths.get("/tmp/", UUID.randomUUID().toString()).normalize().toAbsolutePath(); + long size = 1<<30; // 1 Gi + + Map objectParams = new HashMap<>(); + objectParams.put(QemuObject.ObjectParameter.ID, "sec0"); + objectParams.put(QemuObject.ObjectParameter.DATA, UUID.randomUUID().toString()); + + Map options = new HashMap(); + + options.put(QemuImg.ENCRYPT_FORMAT, "luks"); + options.put(QemuImg.ENCRYPT_KEY_SECRET, "sec0"); + + List qObjects = new ArrayList<>(); + qObjects.add(new QemuObject(QemuObject.ObjectType.SECRET, objectParams)); + + QemuImgFile file = new QemuImgFile(testFile.toString(), size, PhysicalDiskFormat.QCOW2); + QemuImg qemu = new QemuImg(0); + qemu.create(file, null, options, qObjects); + + Map info = qemu.info(file); + assertEquals("yes", info.get("encrypted")); + + assertTrue(testFile.toFile().delete()); + } + + @Test + public void testCreateSparseVolume() throws QemuImgException, LibvirtException { String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; /* 10TB virtual_size */ @@ -204,7 +236,7 @@ public class QemuImgTest { } @Test(expected = QemuImgException.class) - public void testCreateAndResizeFail() throws QemuImgException { + public void testCreateAndResizeFail() throws QemuImgException, LibvirtException { String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; long startSize = 20480; @@ -224,7 +256,7 @@ public class QemuImgTest { } @Test(expected = QemuImgException.class) - public void testCreateAndResizeZero() throws QemuImgException { + public void testCreateAndResizeZero() throws QemuImgException, LibvirtException { String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; long startSize = 20480; diff --git a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuObjectTest.java b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuObjectTest.java new file mode 100644 index 00000000000..316da622b84 --- /dev/null +++ b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/qemu/QemuObjectTest.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.utils.qemu; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(MockitoJUnitRunner.class) +public class QemuObjectTest { + @Test + public void ToStringTest() { + Map params = new HashMap<>(); + params.put(QemuObject.ObjectParameter.ID, "sec0"); + params.put(QemuObject.ObjectParameter.FILE, "/dev/shm/file"); + QemuObject qObject = new QemuObject(QemuObject.ObjectType.SECRET, params); + + String[] flag = qObject.toCommandFlag(); + Assert.assertEquals(2, flag.length); + Assert.assertEquals("--object", flag[0]); + Assert.assertEquals("secret,file=/dev/shm/file,id=sec0", flag[1]); + } +} diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index fff15fd0ca9..362b8d8ca4c 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -138,10 +138,11 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } CreateObjectCommand cmd = new CreateObjectCommand(volume.getTO()); - EndPoint ep = epSelector.select(volume); + boolean encryptionRequired = anyVolumeRequiresEncryption(volume); + EndPoint ep = epSelector.select(volume, encryptionRequired); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send CreateObjectCommand, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { @@ -200,9 +201,6 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } else { result.setAnswer(answer); } - } catch (StorageUnavailableException e) { - s_logger.debug("failed to create volume", e); - errMsg = e.toString(); } catch (Exception e) { s_logger.debug("failed to create volume", e); errMsg = e.toString(); @@ -260,6 +258,8 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback) { + s_logger.debug(String.format("Copying volume %s(%s) to %s(%s)", srcdata.getId(), srcdata.getType(), destData.getId(), destData.getType())); + boolean encryptionRequired = anyVolumeRequiresEncryption(srcdata, destData); DataStore store = destData.getDataStore(); if (store.getRole() == DataStoreRole.Primary) { if ((srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE)) { @@ -280,13 +280,14 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri DataObject srcData = templateDataFactory.getTemplate(srcdata.getId(), imageStore); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, true); - EndPoint ep = epSelector.select(srcData, destData); + EndPoint ep = epSelector.select(srcData, destData, encryptionRequired); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send CopyCommand, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { + s_logger.debug(String.format("Sending copy command to endpoint %s, where encryption support is %s", ep.getHostAddr(), encryptionRequired ? "required" : "not required")); answer = ep.sendMessage(cmd); } CopyCommandResult result = new CopyCommandResult("", answer); @@ -294,10 +295,10 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } else if (srcdata.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.VOLUME) { SnapshotObjectTO srcTO = (SnapshotObjectTO) srcdata.getTO(); CopyCommand cmd = new CopyCommand(srcTO, destData.getTO(), StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value(), true); - EndPoint ep = epSelector.select(srcdata, destData); + EndPoint ep = epSelector.select(srcdata, destData, encryptionRequired); CopyCmdAnswer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); s_logger.error(errMsg); answer = new CopyCmdAnswer(errMsg); } else { @@ -342,6 +343,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri @Override public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { CreateCmdResult result = null; + s_logger.debug("Taking snapshot of "+ snapshot); try { SnapshotObjectTO snapshotTO = (SnapshotObjectTO) snapshot.getTO(); Object payload = snapshot.getPayload(); @@ -350,10 +352,13 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri snapshotTO.setQuiescevm(snapshotPayload.getQuiescevm()); } + boolean encryptionRequired = anyVolumeRequiresEncryption(snapshot); CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO); - EndPoint ep = epSelector.select(snapshot, StorageAction.TAKESNAPSHOT); + EndPoint ep = epSelector.select(snapshot, StorageAction.TAKESNAPSHOT, encryptionRequired); Answer answer = null; + s_logger.debug("Taking snapshot of "+ snapshot + " and encryption required is " + encryptionRequired); + if (ep == null) { String errMsg = "No remote endpoint to send createObjectCommand, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -407,16 +412,22 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri VolumeObject vol = (VolumeObject) data; StoragePool pool = (StoragePool) data.getDataStore(); ResizeVolumePayload resizeParameter = (ResizeVolumePayload) vol.getpayload(); + boolean encryptionRequired = anyVolumeRequiresEncryption(vol); + long [] endpointsToRunResize = resizeParameter.hosts; - ResizeVolumeCommand resizeCmd = - new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), resizeParameter.newSize, resizeParameter.shrinkOk, - resizeParameter.instanceName, vol.getChainInfo()); + // if hosts are provided, they are where the VM last ran. We can use that. + if (endpointsToRunResize == null || endpointsToRunResize.length == 0) { + EndPoint ep = epSelector.select(data, encryptionRequired); + endpointsToRunResize = new long[] {ep.getId()}; + } + ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), + resizeParameter.newSize, resizeParameter.shrinkOk, resizeParameter.instanceName, vol.getChainInfo(), vol.getPassphrase(), vol.getEncryptFormat()); if (pool.getParent() != 0) { resizeCmd.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString()); } CreateCmdResult result = new CreateCmdResult(null, null); try { - ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, resizeParameter.hosts, resizeCmd); + ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, endpointsToRunResize, resizeCmd); if (answer != null && answer.getResult()) { long finalSize = answer.getNewSize(); s_logger.debug("Resize: volume started at size: " + toHumanReadableSize(vol.getSize()) + " and ended at size: " + toHumanReadableSize(finalSize)); @@ -435,6 +446,8 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } catch (Exception e) { s_logger.debug("sending resize command failed", e); result.setResult(e.toString()); + } finally { + resizeCmd.clearPassphrase(); } callback.complete(result); @@ -492,4 +505,18 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + /** + * Does any object require encryption support? + */ + private boolean anyVolumeRequiresEncryption(DataObject ... objects) { + for (DataObject o : objects) { + if (o instanceof VolumeInfo && ((VolumeInfo) o).getPassphraseId() != null) { + return true; + } else if (o instanceof SnapshotInfo && ((SnapshotInfo) o).getBaseVolume().getPassphraseId() != null) { + return true; + } + } + return false; + } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java index f497b10127d..73b69bdef4f 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClient.java @@ -38,6 +38,8 @@ public interface ScaleIOGatewayClient { String GATEWAY_API_PASSWORD = "powerflex.gw.password"; String STORAGE_POOL_NAME = "powerflex.storagepool.name"; String STORAGE_POOL_SYSTEM_ID = "powerflex.storagepool.system.id"; + String SDC_ID = "powerflex.sdc.id"; + String SDC_GUID = "powerflex.sdc.guid"; static ScaleIOGatewayClient getClient(final String url, final String username, final String password, final boolean validateCertificate, final int timeout, final int maxConnections) throws NoSuchAlgorithmException, KeyManagementException, URISyntaxException { @@ -81,8 +83,10 @@ public interface ScaleIOGatewayClient { // SDC APIs List listSdcs(); Sdc getSdc(String sdcId); + String getSdcIdByGuid(String sdcGuid); Sdc getSdcByIp(String ipAddress); Sdc getConnectedSdcByIp(String ipAddress); - List listConnectedSdcIps(); - boolean isSdcConnected(String ipAddress); + boolean haveConnectedSdcs(); + boolean isSdcConnected(String sdcId); + boolean isSdcConnectedByIP(String ipAddress); } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java index fa195414b67..5566dbcdb0d 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/client/ScaleIOGatewayClientImpl.java @@ -1013,6 +1013,24 @@ public class ScaleIOGatewayClientImpl implements ScaleIOGatewayClient { return get("/instances/Sdc::" + sdcId, Sdc.class); } + @Override + public String getSdcIdByGuid(String sdcGuid) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(sdcGuid), "SDC Guid cannot be null"); + + List sdcs = listSdcs(); + if (sdcs == null) { + return null; + } + + for (Sdc sdc : sdcs) { + if (sdcGuid.equalsIgnoreCase(sdc.getSdcGuid())) { + return sdc.getId(); + } + } + + return null; + } + @Override public Sdc getSdcByIp(String ipAddress) { Preconditions.checkArgument(!Strings.isNullOrEmpty(ipAddress), "IP address cannot be null"); @@ -1035,28 +1053,35 @@ public class ScaleIOGatewayClientImpl implements ScaleIOGatewayClient { } @Override - public List listConnectedSdcIps() { - List sdcIps = new ArrayList<>(); + public boolean haveConnectedSdcs() { List sdcs = listSdcs(); if(sdcs != null) { for (Sdc sdc : sdcs) { if (MDM_CONNECTED_STATE.equalsIgnoreCase(sdc.getMdmConnectionState())) { - sdcIps.add(sdc.getSdcIp()); + return true; } } } - return sdcIps; + return false; } @Override - public boolean isSdcConnected(String ipAddress) { + public boolean isSdcConnected(String sdcId) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(sdcId), "SDC Id cannot be null"); + + Sdc sdc = getSdc(sdcId); + return (sdc != null && MDM_CONNECTED_STATE.equalsIgnoreCase(sdc.getMdmConnectionState())); + } + + @Override + public boolean isSdcConnectedByIP(String ipAddress) { Preconditions.checkArgument(!Strings.isNullOrEmpty(ipAddress), "IP address cannot be null"); List sdcs = listSdcs(); - if(sdcs != null) { + if (sdcs != null) { for (Sdc sdc : sdcs) { - if (ipAddress.equalsIgnoreCase(sdc.getSdcIp()) && MDM_CONNECTED_STATE.equalsIgnoreCase(sdc.getMdmConnectionState())) { + if (sdc != null && ipAddress.equalsIgnoreCase(sdc.getSdcIp()) && MDM_CONNECTED_STATE.equalsIgnoreCase(sdc.getMdmConnectionState())) { return true; } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java index 318e82de04d..8229b21a51a 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java @@ -41,7 +41,7 @@ import org.apache.cloudstack.storage.RemoteHostEndPoint; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectAnswer; -import org.apache.cloudstack.storage.datastore.api.Sdc; +import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.datastore.api.StoragePoolStatistics; import org.apache.cloudstack.storage.datastore.api.VolumeStatistics; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; @@ -54,7 +54,10 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.ScaleIOUtil; import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -64,6 +67,7 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.alert.AlertManager; import com.cloud.configuration.Config; import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; import com.cloud.server.ManagementServerImpl; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; @@ -71,11 +75,13 @@ import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; @@ -96,6 +102,8 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject + private StoragePoolHostDao storagePoolHostDao; + @Inject private VolumeDao volumeDao; @Inject private VolumeDetailsDao volumeDetailsDao; @@ -109,6 +117,8 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { private AlertManager alertMgr; @Inject private ConfigurationDao configDao; + @Inject + private HostDao hostDao; public ScaleIOPrimaryDataStoreDriver() { @@ -144,38 +154,38 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { iopsLimit = ScaleIOUtil.MINIMUM_ALLOWED_IOPS_LIMIT; } - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { alertHostSdcDisconnection(host); throw new CloudRuntimeException("Unable to grant access to volume: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - return client.mapVolumeToSdcWithLimits(ScaleIOUtil.getVolumePath(volume.getPath()), sdc.getId(), iopsLimit, bandwidthLimitInKbps); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + return client.mapVolumeToSdcWithLimits(ScaleIOUtil.getVolumePath(volume.getPath()), sdcId, iopsLimit, bandwidthLimitInKbps); } else if (DataObjectType.TEMPLATE.equals(dataObject.getType())) { final VMTemplateStoragePoolVO templatePoolRef = vmTemplatePoolDao.findByPoolTemplate(dataStore.getId(), dataObject.getId(), null); LOGGER.debug("Granting access for PowerFlex template volume: " + templatePoolRef.getInstallPath()); - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { alertHostSdcDisconnection(host); throw new CloudRuntimeException("Unable to grant access to template: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - return client.mapVolumeToSdc(ScaleIOUtil.getVolumePath(templatePoolRef.getInstallPath()), sdc.getId()); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + return client.mapVolumeToSdc(ScaleIOUtil.getVolumePath(templatePoolRef.getInstallPath()), sdcId); } else if (DataObjectType.SNAPSHOT.equals(dataObject.getType())) { SnapshotInfo snapshot = (SnapshotInfo) dataObject; LOGGER.debug("Granting access for PowerFlex volume snapshot: " + snapshot.getPath()); - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { alertHostSdcDisconnection(host); throw new CloudRuntimeException("Unable to grant access to snapshot: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - return client.mapVolumeToSdc(ScaleIOUtil.getVolumePath(snapshot.getPath()), sdc.getId()); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + return client.mapVolumeToSdc(ScaleIOUtil.getVolumePath(snapshot.getPath()), sdcId); } return false; @@ -184,6 +194,11 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } + private boolean grantAccess(DataObject dataObject, EndPoint ep, DataStore dataStore) { + Host host = hostDao.findById(ep.getId()); + return grantAccess(dataObject, host, dataStore); + } + @Override public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) { try { @@ -191,41 +206,64 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { final VolumeVO volume = volumeDao.findById(dataObject.getId()); LOGGER.debug("Revoking access for PowerFlex volume: " + volume.getPath()); - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { throw new CloudRuntimeException("Unable to revoke access for volume: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(volume.getPath()), sdc.getId()); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(volume.getPath()), sdcId); } else if (DataObjectType.TEMPLATE.equals(dataObject.getType())) { final VMTemplateStoragePoolVO templatePoolRef = vmTemplatePoolDao.findByPoolTemplate(dataStore.getId(), dataObject.getId(), null); LOGGER.debug("Revoking access for PowerFlex template volume: " + templatePoolRef.getInstallPath()); - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { throw new CloudRuntimeException("Unable to revoke access for template: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(templatePoolRef.getInstallPath()), sdc.getId()); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(templatePoolRef.getInstallPath()), sdcId); } else if (DataObjectType.SNAPSHOT.equals(dataObject.getType())) { SnapshotInfo snapshot = (SnapshotInfo) dataObject; LOGGER.debug("Revoking access for PowerFlex volume snapshot: " + snapshot.getPath()); - final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); - final Sdc sdc = client.getConnectedSdcByIp(host.getPrivateIpAddress()); - if (sdc == null) { + final String sdcId = getConnectedSdc(dataStore.getId(), host.getId()); + if (StringUtils.isBlank(sdcId)) { throw new CloudRuntimeException("Unable to revoke access for snapshot: " + dataObject.getId() + ", no Sdc connected with host ip: " + host.getPrivateIpAddress()); } - client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(snapshot.getPath()), sdc.getId()); + final ScaleIOGatewayClient client = getScaleIOClient(dataStore.getId()); + client.unmapVolumeFromSdc(ScaleIOUtil.getVolumePath(snapshot.getPath()), sdcId); } } catch (Exception e) { LOGGER.warn("Failed to revoke access due to: " + e.getMessage(), e); } } + private void revokeAccess(DataObject dataObject, EndPoint ep, DataStore dataStore) { + Host host = hostDao.findById(ep.getId()); + revokeAccess(dataObject, host, dataStore); + } + + private String getConnectedSdc(long poolId, long hostId) { + try { + StoragePoolHostVO poolHostVO = storagePoolHostDao.findByPoolHost(poolId, hostId); + if (poolHostVO == null) { + return null; + } + + final ScaleIOGatewayClient client = getScaleIOClient(poolId); + if (client.isSdcConnected(poolHostVO.getLocalPath())) { + return poolHostVO.getLocalPath(); + } + } catch (Exception e) { + LOGGER.warn("Couldn't check SDC connection for the host: " + hostId + " and storage pool: " + poolId + " due to " + e.getMessage(), e); + } + + return null; + } + @Override public long getUsedBytes(StoragePool storagePool) { long usedSpaceBytes = 0; @@ -393,7 +431,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } - private String createVolume(VolumeInfo volumeInfo, long storagePoolId) { + private CreateObjectAnswer createVolume(VolumeInfo volumeInfo, long storagePoolId) { LOGGER.debug("Creating PowerFlex volume"); StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); @@ -426,7 +464,8 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { volume.setPoolType(Storage.StoragePoolType.PowerFlex); volume.setFormat(Storage.ImageFormat.RAW); volume.setPoolId(storagePoolId); - volumeDao.update(volume.getId(), volume); + VolumeObject createdObject = VolumeObject.getVolumeObject(volumeInfo.getDataStore(), volume); + createdObject.update(); long capacityBytes = storagePool.getCapacityBytes(); long usedBytes = storagePool.getUsedBytes(); @@ -434,7 +473,35 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); storagePoolDao.update(storagePoolId, storagePool); - return volumePath; + CreateObjectAnswer answer = new CreateObjectAnswer(createdObject.getTO()); + + // if volume needs to be set up with encryption, do it now. + if (anyVolumeRequiresEncryption(volumeInfo)) { + LOGGER.debug(String.format("Setting up encryption for volume %s", volumeInfo)); + VolumeObjectTO prepVolume = (VolumeObjectTO) createdObject.getTO(); + prepVolume.setPath(volumePath); + prepVolume.setUuid(volumePath); + CreateObjectCommand cmd = new CreateObjectCommand(prepVolume); + EndPoint ep = selector.select(volumeInfo, true); + if (ep == null) { + throw new CloudRuntimeException("No remote endpoint to send PowerFlex volume encryption preparation"); + } else { + try { + grantAccess(createdObject, ep, volumeInfo.getDataStore()); + answer = (CreateObjectAnswer) ep.sendMessage(cmd); + if (!answer.getResult()) { + throw new CloudRuntimeException("Failed to set up encryption on PowerFlex volume: " + answer.getDetails()); + } + } finally { + revokeAccess(createdObject, ep, volumeInfo.getDataStore()); + prepVolume.clearPassphrase(); + } + } + } else { + LOGGER.debug(String.format("No encryption configured for data volume %s", volumeInfo)); + } + + return answer; } catch (Exception e) { String errMsg = "Unable to create PowerFlex Volume due to " + e.getMessage(); LOGGER.warn(errMsg); @@ -490,16 +557,21 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback callback) { String scaleIOVolumePath = null; String errMsg = null; + Answer answer = new Answer(null, false, "not started"); try { if (dataObject.getType() == DataObjectType.VOLUME) { LOGGER.debug("createAsync - creating volume"); - scaleIOVolumePath = createVolume((VolumeInfo) dataObject, dataStore.getId()); + CreateObjectAnswer createAnswer = createVolume((VolumeInfo) dataObject, dataStore.getId()); + scaleIOVolumePath = createAnswer.getData().getPath(); + answer = createAnswer; } else if (dataObject.getType() == DataObjectType.TEMPLATE) { LOGGER.debug("createAsync - creating template"); scaleIOVolumePath = createTemplateVolume((TemplateInfo)dataObject, dataStore.getId()); + answer = new Answer(null, true, "created template"); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync"; LOGGER.error(errMsg); + answer = new Answer(null, false, errMsg); } } catch (Exception ex) { errMsg = ex.getMessage(); @@ -507,10 +579,11 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { if (callback == null) { throw ex; } + answer = new Answer(null, false, errMsg); } if (callback != null) { - CreateCmdResult result = new CreateCmdResult(scaleIOVolumePath, new Answer(null, errMsg == null, errMsg)); + CreateCmdResult result = new CreateCmdResult(scaleIOVolumePath, answer); result.setResult(errMsg); callback.complete(result); } @@ -585,6 +658,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { Answer answer = null; String errMsg = null; + CopyCommandResult result; try { DataStore srcStore = srcData.getDataStore(); @@ -592,51 +666,72 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { if (srcStore.getRole() == DataStoreRole.Primary && (destStore.getRole() == DataStoreRole.Primary && destData.getType() == DataObjectType.VOLUME)) { if (srcData.getType() == DataObjectType.TEMPLATE) { answer = copyTemplateToVolume(srcData, destData, destHost); - if (answer == null) { - errMsg = "No answer for copying template to PowerFlex volume"; - } else if (!answer.getResult()) { - errMsg = answer.getDetails(); - } } else if (srcData.getType() == DataObjectType.VOLUME) { if (isSameScaleIOStorageInstance(srcStore, destStore)) { answer = migrateVolume(srcData, destData); } else { answer = copyVolume(srcData, destData, destHost); } - - if (answer == null) { - errMsg = "No answer for migrate PowerFlex volume"; - } else if (!answer.getResult()) { - errMsg = answer.getDetails(); - } } else { errMsg = "Unsupported copy operation from src object: (" + srcData.getType() + ", " + srcData.getDataStore() + "), dest object: (" + destData.getType() + ", " + destData.getDataStore() + ")"; LOGGER.warn(errMsg); + answer = new Answer(null, false, errMsg); } } else { errMsg = "Unsupported copy operation"; + LOGGER.warn(errMsg); + answer = new Answer(null, false, errMsg); } } catch (Exception e) { LOGGER.debug("Failed to copy due to " + e.getMessage(), e); errMsg = e.toString(); + answer = new Answer(null, false, errMsg); } - CopyCommandResult result = new CopyCommandResult(null, answer); - result.setResult(errMsg); + result = new CopyCommandResult(null, answer); callback.complete(result); } + /** + * Responsible for copying template on ScaleIO primary to root disk + * @param srcData dataobject representing the template + * @param destData dataobject representing the target root disk + * @param destHost host to use for copy + * @return answer + */ private Answer copyTemplateToVolume(DataObject srcData, DataObject destData, Host destHost) { + /* If encryption is requested, since the template object is not encrypted we need to grow the destination disk to accommodate the new headers. + * Data stores of file type happen automatically, but block device types have to handle it. Unfortunately for ScaleIO this means we add a whole 8GB to + * the original size, but only if we are close to an 8GB boundary. + */ + LOGGER.debug(String.format("Copying template %s to volume %s", srcData.getId(), destData.getId())); + VolumeInfo destInfo = (VolumeInfo) destData; + boolean encryptionRequired = anyVolumeRequiresEncryption(destData); + if (encryptionRequired) { + if (needsExpansionForEncryptionHeader(srcData.getSize(), destData.getSize())) { + long newSize = destData.getSize() + (1<<30); + LOGGER.debug(String.format("Destination volume %s(%s) is configured for encryption. Resizing to fit headers, new size %s will be rounded up to nearest 8Gi", destInfo.getId(), destData.getSize(), newSize)); + ResizeVolumePayload p = new ResizeVolumePayload(newSize, destInfo.getMinIops(), destInfo.getMaxIops(), + destInfo.getHypervisorSnapshotReserve(), false, destInfo.getAttachedVmName(), null, true); + destInfo.addPayload(p); + resizeVolume(destInfo); + } else { + LOGGER.debug(String.format("Template %s has size %s, ok for volume %s with size %s", srcData.getId(), srcData.getSize(), destData.getId(), destData.getSize())); + } + } else { + LOGGER.debug(String.format("Destination volume is not configured for encryption, skipping encryption prep. Volume: %s", destData.getId())); + } + // Copy PowerFlex/ScaleIO template to volume LOGGER.debug(String.format("Initiating copy from PowerFlex template volume on host %s", destHost != null ? destHost.getId() : "")); int primaryStorageDownloadWait = StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value(); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value()); Answer answer = null; - EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcData.getDataStore()); + EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcData, encryptionRequired); if (ep == null) { - String errorMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errorMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); LOGGER.error(errorMsg); answer = new Answer(cmd, false, errorMsg); } else { @@ -655,9 +750,10 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), copyVolumeWait, VirtualMachineManager.ExecuteInSequence.value()); Answer answer = null; - EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcData.getDataStore()); + boolean encryptionRequired = anyVolumeRequiresEncryption(srcData, destData); + EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcData, encryptionRequired); if (ep == null) { - String errorMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errorMsg = String.format("No remote endpoint to send command, unable to find a valid endpoint. Requires encryption support: %s", encryptionRequired); LOGGER.error(errorMsg); answer = new Answer(cmd, false, errorMsg); } else { @@ -930,8 +1026,12 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { } try { + StoragePoolHostVO poolHostVO = storagePoolHostDao.findByPoolHost(pool.getId(), host.getId()); + if (poolHostVO == null) { + return false; + } final ScaleIOGatewayClient client = getScaleIOClient(pool.getId()); - return client.isSdcConnected(host.getPrivateIpAddress()); + return client.isSdcConnected(poolHostVO.getLocalPath()); } catch (Exception e) { LOGGER.warn("Unable to check the host: " + host.getId() + " access to storage pool: " + pool.getId() + " due to " + e.getMessage(), e); return false; @@ -947,4 +1047,27 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver { String msg = "SDC not connected on the host: " + host.getId() + ", reconnect the SDC to MDM"; alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC disconnected on host: " + host.getUuid(), msg); } + + /** + * Does the destination size fit the source size plus an encryption header? + * @param srcSize size of source + * @param dstSize size of destination + * @return true if resize is required + */ + private boolean needsExpansionForEncryptionHeader(long srcSize, long dstSize) { + int headerSize = 32<<20; // ensure we have 32MiB for encryption header + return srcSize + headerSize > dstSize; + } + + /** + * Does any object require encryption support? + */ + private boolean anyVolumeRequiresEncryption(DataObject ... objects) { + for (DataObject o : objects) { + if (o instanceof VolumeInfo && ((VolumeInfo) o).getPassphraseId() != null) { + return true; + } + } + return false; + } } diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java index edebdac7929..65831e4ec3d 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycle.java @@ -258,22 +258,9 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc throw new CloudRuntimeException("Unsupported hypervisor type: " + cluster.getHypervisorType().toString()); } - List connectedSdcIps = null; - try { - ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStore.getId(), storagePoolDetailsDao); - connectedSdcIps = client.listConnectedSdcIps(); - } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { - LOGGER.error("Failed to create storage pool", e); - throw new CloudRuntimeException("Failed to establish connection with PowerFlex Gateway to create storage pool"); - } - - if (connectedSdcIps == null || connectedSdcIps.isEmpty()) { - LOGGER.debug("No connected SDCs found for the PowerFlex storage pool"); - throw new CloudRuntimeException("Failed to create storage pool as connected SDCs not found"); - } + checkConnectedSdcs(dataStore.getId()); PrimaryDataStoreInfo primaryDataStoreInfo = (PrimaryDataStoreInfo) dataStore; - List hostsInCluster = resourceManager.listAllUpAndEnabledHosts(Host.Type.Routing, primaryDataStoreInfo.getClusterId(), primaryDataStoreInfo.getPodId(), primaryDataStoreInfo.getDataCenterId()); if (hostsInCluster.isEmpty()) { @@ -285,8 +272,7 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc List poolHosts = new ArrayList(); for (HostVO host : hostsInCluster) { try { - if (connectedSdcIps.contains(host.getPrivateIpAddress())) { - storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId()); + if (storageMgr.connectHostToSharedPool(host.getId(), primaryDataStoreInfo.getId())) { poolHosts.add(host); } } catch (Exception e) { @@ -315,27 +301,14 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc throw new CloudRuntimeException("Unsupported hypervisor type: " + hypervisorType.toString()); } - List connectedSdcIps = null; - try { - ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStore.getId(), storagePoolDetailsDao); - connectedSdcIps = client.listConnectedSdcIps(); - } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { - LOGGER.error("Failed to create storage pool", e); - throw new CloudRuntimeException("Failed to establish connection with PowerFlex Gateway to create storage pool"); - } - - if (connectedSdcIps == null || connectedSdcIps.isEmpty()) { - LOGGER.debug("No connected SDCs found for the PowerFlex storage pool"); - throw new CloudRuntimeException("Failed to create storage pool as connected SDCs not found"); - } + checkConnectedSdcs(dataStore.getId()); LOGGER.debug("Attaching the pool to each of the hosts in the zone: " + scope.getScopeId()); List hosts = resourceManager.listAllUpAndEnabledHostsInOneZoneByHypervisor(hypervisorType, scope.getScopeId()); List poolHosts = new ArrayList(); for (HostVO host : hosts) { try { - if (connectedSdcIps.contains(host.getPrivateIpAddress())) { - storageMgr.connectHostToSharedPool(host.getId(), dataStore.getId()); + if (storageMgr.connectHostToSharedPool(host.getId(), dataStore.getId())) { poolHosts.add(host); } } catch (Exception e) { @@ -352,6 +325,22 @@ public class ScaleIOPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCyc return true; } + private void checkConnectedSdcs(Long dataStoreId) { + boolean haveConnectedSdcs = false; + try { + ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(dataStoreId, storagePoolDetailsDao); + haveConnectedSdcs = client.haveConnectedSdcs(); + } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { + LOGGER.error(String.format("Failed to create storage pool for datastore: %s", dataStoreId), e); + throw new CloudRuntimeException(String.format("Failed to establish connection with PowerFlex Gateway to create storage pool for datastore: %s", dataStoreId)); + } + + if (!haveConnectedSdcs) { + LOGGER.debug(String.format("No connected SDCs found for the PowerFlex storage pool of datastore: %s", dataStoreId)); + throw new CloudRuntimeException(String.format("Failed to create storage pool as connected SDCs not found for datastore: %s", dataStoreId)); + } + } + @Override public boolean maintain(DataStore store) { storagePoolAutomation.maintain(store); diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java index f6722314a5c..475e2b2c6c5 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/provider/ScaleIOHostListener.java @@ -21,6 +21,8 @@ package org.apache.cloudstack.storage.datastore.provider; import java.net.URISyntaxException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; import javax.inject.Inject; @@ -30,6 +32,8 @@ import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClient; import org.apache.cloudstack.storage.datastore.client.ScaleIOGatewayClientConnectionPool; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -69,7 +73,41 @@ public class ScaleIOHostListener implements HypervisorHostListener { return false; } - if (!isHostSdcConnected(host.getPrivateIpAddress(), poolId)) { + StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); + + String systemId = _storagePoolDetailsDao.findDetail(poolId, ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID).getValue(); + if (systemId == null) { + throw new CloudRuntimeException("Failed to get the system id for PowerFlex storage pool " + storagePool.getName()); + } + Map details = new HashMap<>(); + details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); + + ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool, storagePool.getPath(), details); + ModifyStoragePoolAnswer answer = sendModifyStoragePoolCommand(cmd, storagePool, hostId); + Map poolDetails = answer.getPoolInfo().getDetails(); + if (MapUtils.isEmpty(poolDetails)) { + String msg = "SDC details not found on the host: " + hostId + ", (re)install SDC and restart agent"; + s_logger.warn(msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC not found on host: " + host.getUuid(), msg); + return false; + } + + String sdcId = null; + if (poolDetails.containsKey(ScaleIOGatewayClient.SDC_ID)) { + sdcId = poolDetails.get(ScaleIOGatewayClient.SDC_ID); + } else if (poolDetails.containsKey(ScaleIOGatewayClient.SDC_GUID)) { + String sdcGuid = poolDetails.get(ScaleIOGatewayClient.SDC_GUID); + sdcId = getHostSdcId(sdcGuid, poolId); + } + + if (StringUtils.isBlank(sdcId)) { + String msg = "Couldn't retrieve SDC details from the host: " + hostId + ", (re)install SDC and restart agent"; + s_logger.warn(msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC details not found on host: " + host.getUuid(), msg); + return false; + } + + if (!isHostSdcConnected(sdcId, poolId)) { s_logger.warn("SDC not connected on the host: " + hostId); String msg = "SDC not connected on the host: " + hostId + ", reconnect the SDC to MDM and restart agent"; _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "SDC disconnected on host: " + host.getUuid(), msg); @@ -78,27 +116,39 @@ public class ScaleIOHostListener implements HypervisorHostListener { StoragePoolHostVO storagePoolHost = _storagePoolHostDao.findByPoolHost(poolId, hostId); if (storagePoolHost == null) { - storagePoolHost = new StoragePoolHostVO(poolId, hostId, ""); + storagePoolHost = new StoragePoolHostVO(poolId, hostId, sdcId); _storagePoolHostDao.persist(storagePoolHost); + } else { + storagePoolHost.setLocalPath(sdcId); + _storagePoolHostDao.update(storagePoolHost.getId(), storagePoolHost); } - StoragePool storagePool = (StoragePool)_dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); - ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, storagePool); - sendModifyStoragePoolCommand(cmd, storagePool, hostId); + s_logger.info("Connection established between storage pool: " + storagePool + " and host: " + hostId); return true; } - private boolean isHostSdcConnected(String hostIpAddress, long poolId) { + private String getHostSdcId(String sdcGuid, long poolId) { + try { + s_logger.debug(String.format("Try to get host SDC Id for pool: %s, with SDC guid %s", poolId, sdcGuid)); + ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(poolId, _storagePoolDetailsDao); + return client.getSdcIdByGuid(sdcGuid); + } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { + s_logger.error(String.format("Failed to get host SDC Id for pool: %s", poolId), e); + throw new CloudRuntimeException(String.format("Failed to establish connection with PowerFlex Gateway to get host SDC Id for pool: %s", poolId)); + } + } + + private boolean isHostSdcConnected(String sdcId, long poolId) { try { ScaleIOGatewayClient client = ScaleIOGatewayClientConnectionPool.getInstance().getClient(poolId, _storagePoolDetailsDao); - return client.isSdcConnected(hostIpAddress); + return client.isSdcConnected(sdcId); } catch (NoSuchAlgorithmException | KeyManagementException | URISyntaxException e) { s_logger.error("Failed to check host sdc connection", e); throw new CloudRuntimeException("Failed to establish connection with PowerFlex Gateway to check host sdc connection"); } } - private void sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { + private ModifyStoragePoolAnswer sendModifyStoragePoolCommand(ModifyStoragePoolCommand cmd, StoragePool storagePool, long hostId) { Answer answer = _agentMgr.easySend(hostId, cmd); if (answer == null) { @@ -116,7 +166,7 @@ public class ScaleIOHostListener implements HypervisorHostListener { assert (answer instanceof ModifyStoragePoolAnswer) : "ModifyStoragePoolAnswer expected ; Pool = " + storagePool.getId() + " Host = " + hostId; - s_logger.info("Connection established between storage pool " + storagePool + " and host: " + hostId); + return (ModifyStoragePoolAnswer) answer; } @Override diff --git a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java index 0180f17cdd7..e9fb01b392a 100644 --- a/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java +++ b/plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/util/ScaleIOUtil.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.storage.datastore.util; import org.apache.log4j.Logger; +import com.cloud.utils.UuidUtils; import com.cloud.utils.script.Script; import com.google.common.base.Strings; @@ -47,11 +48,31 @@ public class ScaleIOUtil { private static final String SDC_HOME_PATH = getSdcHomePath(); private static final String RESCAN_CMD = "drv_cfg --rescan"; + + /** + * Cmd for querying volumes in SDC + * Sample output for cmd: drv_cfg --query_vols: + * Retrieved 2 volume(s) + * VOL-ID 6c33633100000009 MDM-ID 218ce1797566a00f + * VOL-ID 6c3362a30000000a MDM-ID 218ce1797566a00f + */ private static final String QUERY_VOLUMES_CMD = "drv_cfg --query_vols"; - // Sample output for cmd: drv_cfg --query_vols: - // Retrieved 2 volume(s) - // VOL-ID 6c33633100000009 MDM-ID 218ce1797566a00f - // VOL-ID 6c3362a30000000a MDM-ID 218ce1797566a00f + + /** + * Cmd for querying guid in SDC + * Sample output for cmd: drv_cfg --query_guid: + * B0E3BFB8-C20B-43BF-93C8-13339E85AA50 + */ + private static final String QUERY_GUID_CMD = "drv_cfg --query_guid"; + + /** + * Cmd for querying MDMs in SDC + * Sample output for cmd: drv_cfg --query_mdms: + * Retrieved 2 mdm(s) + * MDM-ID 3ef46cbf2aaf5d0f SDC ID 6b18479c00000003 INSTALLATION ID 68ab55462cbb3ae4 IPs [0]-x.x.x.x [1]-x.x.x.x + * MDM-ID 2e706b2740ec200f SDC ID 301b852c00000003 INSTALLATION ID 33f8662e7a5c1e6c IPs [0]-x.x.x.x [1]-x.x.x.x + */ + private static final String QUERY_MDMS_CMD = "drv_cfg --query_mdms"; public static String getSdcHomePath() { String sdcHomePath = DEFAULT_SDC_HOME_PATH; @@ -97,6 +118,51 @@ public class ScaleIOUtil { return result; } + public static String getSdcGuid() { + String queryGuidCmd = ScaleIOUtil.SDC_HOME_PATH + "/bin/" + ScaleIOUtil.QUERY_GUID_CMD; + String result = Script.runSimpleBashScript(queryGuidCmd); + if (result == null) { + LOGGER.warn("Failed to get SDC guid"); + return null; + } + + if (result.isEmpty()) { + LOGGER.warn("No SDC guid retrieved"); + return null; + } + + if (!UuidUtils.validateUUID(result)) { + LOGGER.warn("Invalid SDC guid: " + result); + return null; + } + + return result; + } + + public static String getSdcId(String mdmId) { + //query_mdms outputs "MDM-ID SDC ID INSTALLATION ID IPs [0]-x.x.x.x [1]-x.x.x.x" for a MDM with ID: + String queryMdmsCmd = ScaleIOUtil.SDC_HOME_PATH + "/bin/" + ScaleIOUtil.QUERY_MDMS_CMD; + queryMdmsCmd += "|grep " + mdmId + "|awk '{print $5}'"; + String result = Script.runSimpleBashScript(queryMdmsCmd); + if (result == null) { + LOGGER.warn("Failed to get SDC Id, for the MDM: " + mdmId); + return null; + } + + if (result.isEmpty()) { + LOGGER.warn("No SDC Id retrieved, for the MDM: " + mdmId); + return null; + } + + String sdcIdRegEx = "^[0-9a-fA-F]{16}$"; + if (!result.matches(sdcIdRegEx)) { + LOGGER.warn("Invalid SDC Id: " + result + " retrieved, for the MDM: " + mdmId); + return null; + } + + return result; + } + public static final String getVolumePath(String volumePathWithName) { if (Strings.isNullOrEmpty(volumePathWithName)) { return volumePathWithName; diff --git a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java index eed82ff0ed2..6cc7b874557 100644 --- a/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java +++ b/plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/ScaleIOPrimaryDataStoreLifeCycleTest.java @@ -22,7 +22,6 @@ package org.apache.cloudstack.storage.datastore.lifecycle; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -140,11 +139,7 @@ public class ScaleIOPrimaryDataStoreLifeCycleTest { ScaleIOGatewayClientImpl client = mock(ScaleIOGatewayClientImpl.class); when(ScaleIOGatewayClientConnectionPool.getInstance().getClient(1L, storagePoolDetailsDao)).thenReturn(client); - List connectedSdcIps = new ArrayList<>(); - connectedSdcIps.add("192.168.1.1"); - connectedSdcIps.add("192.168.1.2"); - when(client.listConnectedSdcIps()).thenReturn(connectedSdcIps); - when(client.isSdcConnected(anyString())).thenReturn(true); + when(client.haveConnectedSdcs()).thenReturn(true); final ZoneScope scope = new ZoneScope(1L); diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index bedfd30e140..1113e873c09 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -2925,6 +2925,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Boolean isRootAdmin = _accountMgr.isRootAdmin(account.getAccountId()); Boolean isRecursive = cmd.isRecursive(); Long zoneId = cmd.getZoneId(); + Boolean encrypt = cmd.getEncrypt(); // Keeping this logic consistent with domain specific zones // if a domainId is provided, we just return the disk offering // associated with this domain @@ -2971,6 +2972,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("name", SearchCriteria.Op.EQ, name); } + if (encrypt != null) { + sc.addAnd("encrypt", SearchCriteria.Op.EQ, encrypt); + } + if (zoneId != null) { SearchBuilder sb = _diskOfferingJoinDao.createSearchBuilder(); sb.and("zoneId", sb.entity().getZoneId(), Op.FIND_IN_SET); @@ -3055,6 +3060,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer cpuNumber = cmd.getCpuNumber(); Integer memory = cmd.getMemory(); Integer cpuSpeed = cmd.getCpuSpeed(); + Boolean encryptRoot = cmd.getEncryptRoot(); SearchCriteria sc = _srvOfferingJoinDao.createSearchCriteria(); if (!_accountMgr.isRootAdmin(caller.getId()) && isSystem) { @@ -3159,6 +3165,10 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("systemUse", SearchCriteria.Op.EQ, isSystem); } + if (encryptRoot != null) { + sc.addAnd("encryptRoot", SearchCriteria.Op.EQ, encryptRoot); + } + if (name != null) { sc.addAnd("name", SearchCriteria.Op.EQ, name); } diff --git a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java index 002f6226fd4..7a59c9cbbbd 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java @@ -107,6 +107,7 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase filteredDomainIds = filterChildSubDomains(domainIds); @@ -2955,6 +2955,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati offering.setCustomizedIops(isCustomizedIops); offering.setMinIops(minIops); offering.setMaxIops(maxIops); + offering.setEncrypt(encryptRoot); setBytesRate(offering, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength); setIopsRate(offering, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength); @@ -3268,7 +3269,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength, Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength, - final Integer hypervisorSnapshotReserve, String cacheMode, final Map details, final Long storagePolicyID) { + final Integer hypervisorSnapshotReserve, String cacheMode, final Map details, final Long storagePolicyID, final boolean encrypt) { long diskSize = 0;// special case for custom disk offerings long maxVolumeSizeInGb = VolumeOrchestrationService.MaxVolumeSize.value(); if (numGibibytes != null && numGibibytes <= 0) { @@ -3350,6 +3351,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); } + newDiskOffering.setEncrypt(encrypt); newDiskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId()); @@ -3364,6 +3366,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati detailsVO.add(new DiskOfferingDetailVO(offering.getId(), ApiConstants.ZONE_ID, String.valueOf(zoneId), false)); } } + if (MapUtils.isNotEmpty(details)) { details.forEach((key, value) -> { boolean displayDetail = !key.equals(Volume.BANDWIDTH_LIMIT_IN_MBPS) && !key.equals(Volume.IOPS_LIMIT); @@ -3459,6 +3462,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final Long iopsWriteRateMaxLength = cmd.getIopsWriteRateMaxLength(); final Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve(); final String cacheMode = cmd.getCacheMode(); + final boolean encrypt = cmd.getEncrypt(); validateMaxRateEqualsOrGreater(iopsReadRate, iopsReadRateMax, IOPS_READ_RATE); validateMaxRateEqualsOrGreater(iopsWriteRate, iopsWriteRateMax, IOPS_WRITE_RATE); @@ -3472,7 +3476,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength, - hypervisorSnapshotReserve, cacheMode, details, storagePolicyId); + hypervisorSnapshotReserve, cacheMode, details, storagePolicyId, encrypt); } /** diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 3dc45b68584..aa685b74d14 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -277,7 +277,7 @@ StateListener, Configurable { long ram_requested = offering.getRamSize() * 1024L * 1024L; VirtualMachine vm = vmProfile.getVirtualMachine(); DataCenter dc = _dcDao.findById(vm.getDataCenterId()); - + boolean volumesRequireEncryption = anyVolumeRequiresEncryption(_volsDao.findByInstance(vm.getId())); if (vm.getType() == VirtualMachine.Type.User || vm.getType() == VirtualMachine.Type.DomainRouter) { checkForNonDedicatedResources(vmProfile, dc, avoids); @@ -299,7 +299,7 @@ StateListener, Configurable { if (plan.getHostId() != null && haVmTag == null) { Long hostIdSpecified = plan.getHostId(); if (s_logger.isDebugEnabled()) { - s_logger.debug("DeploymentPlan has host_id specified, choosing this host and making no checks on this host: " + hostIdSpecified); + s_logger.debug("DeploymentPlan has host_id specified, choosing this host: " + hostIdSpecified); } HostVO host = _hostDao.findById(hostIdSpecified); if (host != null && StringUtils.isNotBlank(uefiFlag) && "yes".equalsIgnoreCase(uefiFlag)) { @@ -340,6 +340,14 @@ StateListener, Configurable { Map> suitableVolumeStoragePools = result.first(); List readyAndReusedVolumes = result.second(); + _hostDao.loadDetails(host); + if (volumesRequireEncryption && !Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION))) { + s_logger.warn(String.format("VM's volumes require encryption support, and provided host %s can't handle it", host)); + return null; + } else { + s_logger.debug(String.format("Volume encryption requirements are met by provided host %s", host)); + } + // choose the potential pool for this VM for this host if (!suitableVolumeStoragePools.isEmpty()) { List suitableHosts = new ArrayList(); @@ -405,6 +413,8 @@ StateListener, Configurable { s_logger.debug("This VM has last host_id specified, trying to choose the same host: " + vm.getLastHostId()); HostVO host = _hostDao.findById(vm.getLastHostId()); + _hostDao.loadHostTags(host); + _hostDao.loadDetails(host); ServiceOfferingDetailsVO offeringDetails = null; if (host == null) { s_logger.debug("The last host of this VM cannot be found"); @@ -422,6 +432,8 @@ StateListener, Configurable { if(!_resourceMgr.isGPUDeviceAvailable(host.getId(), groupName.getValue(), offeringDetails.getValue())){ s_logger.debug("The last host of this VM does not have required GPU devices available"); } + } else if (volumesRequireEncryption && !Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION))) { + s_logger.warn(String.format("The last host of this VM %s does not support volume encryption, which is required by this VM.", host)); } else { if (host.getStatus() == Status.Up) { if (checkVmProfileAndHost(vmProfile, host)) { @@ -526,14 +538,12 @@ StateListener, Configurable { resetAvoidSet(plannerAvoidOutput, plannerAvoidInput); - dest = - checkClustersforDestination(clusterList, vmProfile, plan, avoids, dc, getPlannerUsage(planner, vmProfile, plan, avoids), plannerAvoidOutput); + dest = checkClustersforDestination(clusterList, vmProfile, plan, avoids, dc, getPlannerUsage(planner, vmProfile, plan, avoids), plannerAvoidOutput); if (dest != null) { return dest; } // reset the avoid input to the planners resetAvoidSet(avoids, plannerAvoidOutput); - } else { return null; } @@ -543,6 +553,13 @@ StateListener, Configurable { long hostId = dest.getHost().getId(); avoids.addHost(dest.getHost().getId()); + if (volumesRequireEncryption && !Boolean.parseBoolean(_hostDetailsDao.findDetail(hostId, Host.HOST_VOLUME_ENCRYPTION).getValue())) { + s_logger.warn(String.format("VM's volumes require encryption support, and the planner-provided host %s can't handle it", dest.getHost())); + continue; + } else { + s_logger.debug(String.format("VM's volume encryption requirements are met by host %s", dest.getHost())); + } + if (checkIfHostFitsPlannerUsage(hostId, DeploymentPlanner.PlannerResourceUsage.Shared)) { // found destination return dest; @@ -557,10 +574,18 @@ StateListener, Configurable { } } } - return dest; } + protected boolean anyVolumeRequiresEncryption(List volumes) { + for (Volume volume : volumes) { + if (volume.getPassphraseId() != null) { + return true; + } + } + return false; + } + private boolean isDeployAsIs(VirtualMachine vm) { long templateId = vm.getTemplateId(); VMTemplateVO template = templateDao.findById(templateId); @@ -667,7 +692,7 @@ StateListener, Configurable { return null; } - private boolean checkVmProfileAndHost(final VirtualMachineProfile vmProfile, final HostVO host) { + protected boolean checkVmProfileAndHost(final VirtualMachineProfile vmProfile, final HostVO host) { ServiceOffering offering = vmProfile.getServiceOffering(); if (offering.getHostTag() != null) { _hostDao.loadHostTags(host); @@ -880,14 +905,13 @@ StateListener, Configurable { } @DB - private boolean checkIfHostFitsPlannerUsage(final long hostId, final PlannerResourceUsage resourceUsageRequired) { + protected boolean checkIfHostFitsPlannerUsage(final long hostId, final PlannerResourceUsage resourceUsageRequired) { // TODO Auto-generated method stub // check if this host has been picked up by some other planner // exclusively // if planner can work with shared host, check if this host has // been marked as 'shared' // else if planner needs dedicated host, - PlannerHostReservationVO reservationEntry = _plannerHostReserveDao.findByHostId(hostId); if (reservationEntry != null) { final long id = reservationEntry.getId(); @@ -1225,7 +1249,6 @@ StateListener, Configurable { if (!suitableVolumeStoragePools.isEmpty()) { Pair> potentialResources = findPotentialDeploymentResources(suitableHosts, suitableVolumeStoragePools, avoid, resourceUsageRequired, readyAndReusedVolumes, plan.getPreferredHosts(), vmProfile.getVirtualMachine()); - if (potentialResources != null) { Host host = _hostDao.findById(potentialResources.first().getId()); Map storageVolMap = potentialResources.second(); @@ -1414,6 +1437,7 @@ StateListener, Configurable { List allVolumes = new ArrayList<>(); allVolumes.addAll(volumesOrderBySizeDesc); + for (StoragePool storagePool : suitablePools) { haveEnoughSpace = false; hostCanAccessPool = false; @@ -1495,12 +1519,22 @@ StateListener, Configurable { } } - if (hostCanAccessPool && haveEnoughSpace && hostAffinityCheck && checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired)) { + HostVO potentialHostVO = _hostDao.findById(potentialHost.getId()); + _hostDao.loadDetails(potentialHostVO); + + boolean hostHasEncryption = Boolean.parseBoolean(potentialHostVO.getDetail(Host.HOST_VOLUME_ENCRYPTION)); + boolean hostMeetsEncryptionRequirements = !anyVolumeRequiresEncryption(new ArrayList<>(volumesOrderBySizeDesc)) || hostHasEncryption; + boolean plannerUsageFits = checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired); + + if (hostCanAccessPool && haveEnoughSpace && hostAffinityCheck && hostMeetsEncryptionRequirements && plannerUsageFits) { s_logger.debug("Found a potential host " + "id: " + potentialHost.getId() + " name: " + potentialHost.getName() + " and associated storage pools for this VM"); volumeAllocationMap.clear(); return new Pair>(potentialHost, storage); } else { + if (!hostMeetsEncryptionRequirements) { + s_logger.debug("Potential host " + potentialHost + " did not meet encryption requirements of all volumes"); + } avoid.addHost(potentialHost.getId()); } } diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 400f9534996..3359146d25d 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -45,11 +45,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; -import com.cloud.agent.api.GetStoragePoolCapabilitiesAnswer; -import com.cloud.agent.api.GetStoragePoolCapabilitiesCommand; -import com.cloud.network.router.VirtualNetworkApplianceManager; -import com.cloud.server.StatsCollector; -import com.cloud.upgrade.SystemVmTemplateRegistration; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiConstants; @@ -124,6 +119,8 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.DeleteStoragePoolCommand; +import com.cloud.agent.api.GetStoragePoolCapabilitiesAnswer; +import com.cloud.agent.api.GetStoragePoolCapabilitiesCommand; import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.GetVolumeStatsAnswer; @@ -175,6 +172,7 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.org.Grouping; @@ -182,6 +180,7 @@ import com.cloud.org.Grouping.AllocationState; import com.cloud.resource.ResourceState; import com.cloud.server.ConfigurationServer; import com.cloud.server.ManagementServer; +import com.cloud.server.StatsCollector; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; @@ -199,6 +198,7 @@ import com.cloud.storage.listener.StoragePoolMonitor; import com.cloud.storage.listener.VolumeStateListener; import com.cloud.template.TemplateManager; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.upgrade.SystemVmTemplateRegistration; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.ResourceLimitService; @@ -1095,14 +1095,14 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } @Override - public void connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException { + public boolean connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException { StoragePool pool = (StoragePool)_dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); assert (pool.isShared()) : "Now, did you actually read the name of this method?"; s_logger.debug("Adding pool " + pool.getName() + " to host " + hostId); DataStoreProvider provider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); HypervisorHostListener listener = hostListeners.get(provider.getName()); - listener.hostConnect(hostId, pool.getId()); + return listener.hostConnect(hostId, pool.getId()); } @Override diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index a9f95e6b6fe..a8c22e8cebc 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -73,6 +73,7 @@ import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; import org.apache.cloudstack.storage.command.DettachCommand; @@ -120,6 +121,7 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.StorageUnavailableException; import com.cloud.gpu.GPU; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; @@ -291,6 +293,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic public TaggedResourceService taggedResourceService; @Inject VirtualMachineManager virtualMachineManager; + @Inject + PassphraseDao _passphraseDao; protected Gson _gson; @@ -755,6 +759,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId()); + // Don't support creating templates from encrypted volumes (yet) + if (parentVolume.getPassphraseId() != null) { + throw new UnsupportedOperationException("Cannot create new volumes from encrypted volume snapshots"); + } + if (zoneId == null) { // if zoneId is not provided, we default to create volume in the same zone as the snapshot zone. zoneId = snapshotCheck.getDataCenterId(); @@ -854,6 +863,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } volume = _volsDao.persist(volume); + if (cmd.getSnapshotId() == null && displayVolume) { // for volume created from snapshot, create usage event after volume creation UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId, null, size, @@ -1059,6 +1069,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Requested disk offering has been removed."); } + if (newDiskOffering.getEncrypt() != diskOffering.getEncrypt()) { + throw new InvalidParameterValueException( + String.format("Current disk offering's encryption(%s) does not match target disk offering's encryption(%s)", diskOffering.getEncrypt(), newDiskOffering.getEncrypt()) + ); + } + if (diskOffering.getTags() != null) { if (!com.cloud.utils.StringUtils.areTagsEqual(diskOffering.getTags(), newDiskOffering.getTags())) { throw new InvalidParameterValueException("The tags on the new and old disk offerings must match."); @@ -1763,6 +1779,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume."); } + HypervisorType rootDiskHyperType = vm.getHypervisorType(); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId()); + if (diskOffering.getEncrypt() && rootDiskHyperType != HypervisorType.KVM) { + throw new InvalidParameterValueException("Volume's disk offering has encryption enabled, but volume encryption is not supported for hypervisor type " + rootDiskHyperType); + } + // Check that the device ID is valid if (deviceId != null) { // validate ROOT volume type @@ -1794,7 +1816,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // offering not allowed DataCenterVO dataCenter = _dcDao.findById(volumeToAttach.getDataCenterId()); if (!dataCenter.isLocalStorageEnabled()) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId()); if (diskOffering.isUseLocalStorage()) { throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it"); } @@ -1829,7 +1850,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - HypervisorType rootDiskHyperType = vm.getHypervisorType(); HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId()); StoragePoolVO volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId()); @@ -2364,6 +2384,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic vm = _vmInstanceDao.findById(instanceId); } + if (vol.getPassphraseId() != null) { + throw new InvalidParameterValueException("Migration of encrypted volumes is unsupported"); + } + // Check that Vm to which this volume is attached does not have VM Snapshots // OfflineVmwareMigration: consider if this is needed and desirable if (vm != null && _vmSnapshotDao.findByVm(vm.getId()).size() > 0) { @@ -2806,6 +2830,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); } + if (volume.getEncryptFormat() != null && volume.getAttachedVM() != null && volume.getAttachedVM().getState() != State.Stopped) { + s_logger.debug(String.format("Refusing to take snapshot of encrypted volume (%s) on running VM (%s)", volume, volume.getAttachedVM())); + throw new UnsupportedOperationException("Volume snapshots for encrypted volumes are not supported if VM is running"); + } + CreateSnapshotPayload payload = new CreateSnapshotPayload(); payload.setSnapshotId(snapshotId); @@ -2982,6 +3011,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw ex; } + if (volume.getPassphraseId() != null) { + throw new InvalidParameterValueException("Extraction of encrypted volumes is unsupported"); + } + if (volume.getVolumeType() != Volume.Type.DATADISK) { // Datadisk dont have any template dependence. @@ -3312,6 +3345,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic sendCommand = true; } + if (host != null) { + _hostDao.loadDetails(host); + boolean hostSupportsEncryption = Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION)); + if (volumeToAttach.getPassphraseId() != null) { + if (!hostSupportsEncryption) { + throw new CloudRuntimeException(errorMsg + " because target host " + host + " doesn't support volume encryption"); + } + } + } + if (volumeToAttachStoragePool != null) { verifyManagedStorage(volumeToAttachStoragePool.getId(), hostId); } diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 72815d42290..e1c69804984 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -96,6 +96,7 @@ import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.TaggedResourceService; import com.cloud.storage.CreateSnapshotPayload; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot.Type; @@ -110,6 +111,7 @@ import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotPolicyDao; import com.cloud.storage.dao.SnapshotScheduleDao; @@ -172,6 +174,8 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement @Inject DomainDao _domainDao; @Inject + DiskOfferingDao _diskOfferingDao; + @Inject StorageManager _storageMgr; @Inject SnapshotScheduler _snapSchedMgr; @@ -836,6 +840,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement throw new InvalidParameterValueException("Failed to create snapshot policy, unable to find a volume with id " + volumeId); } + // For now, volumes with encryption don't support snapshot schedules, because they will fail when VM is running + DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()); + if (diskOffering == null) { + throw new InvalidParameterValueException(String.format("Failed to find disk offering for the volume [%s]", volume.getUuid())); + } else if(diskOffering.getEncrypt()) { + throw new UnsupportedOperationException(String.format("Encrypted volumes don't support snapshot schedules, cannot create snapshot policy for the volume [%s]", volume.getUuid())); + } + String volumeDescription = volume.getVolumeDescription(); _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume); diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index d7d38922917..dc7b262fca2 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -1806,6 +1806,11 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // check permissions _accountMgr.checkAccess(caller, null, true, volume); + // Don't support creating templates from encrypted volumes (yet) + if (volume.getPassphraseId() != null) { + throw new UnsupportedOperationException("Cannot create templates from encrypted volumes"); + } + // If private template is created from Volume, check that the volume // will not be active when the private template is // created @@ -1829,6 +1834,11 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // Volume could be removed so find including removed to record source template id. volume = _volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId()); + // Don't support creating templates from encrypted volumes (yet) + if (volume != null && volume.getPassphraseId() != null) { + throw new UnsupportedOperationException("Cannot create templates from snapshots of encrypted volumes"); + } + // check permissions _accountMgr.checkAccess(caller, null, true, snapshot); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0be2e482b2a..41b47eb5882 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3826,6 +3826,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); + if (offering.getEncrypt() && hypervisorType != HypervisorType.KVM) { + throw new InvalidParameterValueException("Root volume encryption is not supported for hypervisor type " + hypervisorType); + } + if (offering.isDynamic()) { offering.setDynamicFlag(true); validateCustomParameters(offering, customParameters); @@ -3844,6 +3848,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (diskOffering == null) { throw new InvalidParameterValueException("Specified disk offering cannot be found"); } + + if (diskOffering.getEncrypt() && hypervisorType != HypervisorType.KVM) { + throw new InvalidParameterValueException("Volume encryption is not supported for hypervisor type " + hypervisorType); + } + if (diskOffering.isCustomized()) { if (diskSize == null) { throw new InvalidParameterValueException("This disk offering requires a custom size specified"); diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index 636a1ae4d7a..898f4593e57 100644 --- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -394,6 +394,12 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme throw new InvalidParameterValueException("All volumes of the VM: " + userVmVo.getUuid() + " should be on the same PowerFlex storage pool"); } } + + // disallow KVM snapshots for VMs if root volume is encrypted (Qemu crash) + if (rootVolume.getPassphraseId() != null && userVmVo.getState() == State.Running && snapshotMemory) { + throw new UnsupportedOperationException("Cannot create VM memory snapshots on KVM from encrypted root volumes"); + } + } // check access diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index 97e15de6f84..e58a5c68734 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -23,29 +23,43 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.DataCenter; +import com.cloud.gpu.GPU; import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.Storage; +import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfileImpl; import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.collections.CollectionUtils; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; @@ -162,12 +176,30 @@ public class DeploymentPlanningManagerImplTest { @Inject HostPodDao hostPodDao; + @Inject + VolumeDao volDao; + + @Inject + HostDao hostDao; + + @Inject + CapacityManager capacityMgr; + + @Inject + ServiceOfferingDetailsDao serviceOfferingDetailsDao; + + @Inject + ClusterDetailsDao clusterDetailsDao; + @Mock Host host; - private static long dataCenterId = 1L; - private static long hostId = 1l; - private static final long ADMIN_ACCOUNT_ROLE_ID = 1l; + private static final long dataCenterId = 1L; + private static final long instanceId = 123L; + private static final long hostId = 0L; + private static final long podId = 2L; + private static final long clusterId = 3L; + private static final long ADMIN_ACCOUNT_ROLE_ID = 1L; @BeforeClass public static void setUp() throws ConfigurationException { @@ -179,7 +211,7 @@ public class DeploymentPlanningManagerImplTest { ComponentContext.initComponentsLifeCycle(); - PlannerHostReservationVO reservationVO = new PlannerHostReservationVO(200L, 1L, 2L, 3L, PlannerResourceUsage.Shared); + PlannerHostReservationVO reservationVO = new PlannerHostReservationVO(hostId, dataCenterId, podId, clusterId, PlannerResourceUsage.Shared); Mockito.when(_plannerHostReserveDao.persist(Matchers.any(PlannerHostReservationVO.class))).thenReturn(reservationVO); Mockito.when(_plannerHostReserveDao.findById(Matchers.anyLong())).thenReturn(reservationVO); Mockito.when(_affinityGroupVMMapDao.countAffinityGroupsForVm(Matchers.anyLong())).thenReturn(0L); @@ -190,9 +222,12 @@ public class DeploymentPlanningManagerImplTest { VMInstanceVO vm = new VMInstanceVO(); Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm); + Mockito.when(vmProfile.getId()).thenReturn(instanceId); Mockito.when(vmDetailsDao.listDetailsKeyPairs(Matchers.anyLong())).thenReturn(null); + Mockito.when(volDao.findByInstance(Matchers.anyLong())).thenReturn(new ArrayList<>()); + Mockito.when(_dcDao.findById(Matchers.anyLong())).thenReturn(dc); Mockito.when(dc.getId()).thenReturn(dataCenterId); @@ -439,6 +474,323 @@ public class DeploymentPlanningManagerImplTest { Assert.assertTrue(avoids.getClustersToAvoid().contains(expectedClusterId)); } + @Test + public void volumesRequireEncryptionTest() { + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + VolumeVO vol2 = new VolumeVO("vol2", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.DATADISK); + VolumeVO vol3 = new VolumeVO("vol3", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.DATADISK); + vol2.setPassphraseId(1L); + + List volumes = List.of(vol1, vol2, vol3); + Assert.assertTrue("Volumes require encryption, but not reporting", _dpm.anyVolumeRequiresEncryption(volumes)); + } + + @Test + public void volumesDoNotRequireEncryptionTest() { + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + VolumeVO vol2 = new VolumeVO("vol2", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.DATADISK); + VolumeVO vol3 = new VolumeVO("vol3", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.DATADISK); + + List volumes = List.of(vol1, vol2, vol3); + Assert.assertFalse("Volumes do not require encryption, but reporting they do", _dpm.anyVolumeRequiresEncryption(volumes)); + } + + /** + * Root requires encryption, chosen host supports it + */ + @Test + public void passEncRootProvidedHostSupportingEncryptionTest() { + HostVO host = new HostVO("host"); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "true"); + }}; + host.setDetails(hostDetails); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, hostId, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertEquals(dest.getHost(), host); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Root requires encryption, chosen host does not support it + */ + @Test + public void failEncRootProvidedHostNotSupportingEncryptionTest() { + HostVO host = new HostVO("host"); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "false"); + }}; + host.setDetails(hostDetails); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, hostId, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertNull("Destination should be null since host doesn't support encryption and root requires it", dest); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Root does not require encryption, chosen host does not support it + */ + @Test + public void passNoEncRootProvidedHostNotSupportingEncryptionTest() { + HostVO host = new HostVO("host"); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "false"); + }}; + host.setDetails(hostDetails); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, hostId, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertEquals(dest.getHost(), host); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Root does not require encryption, chosen host does support it + */ + @Test + public void passNoEncRootProvidedHostSupportingEncryptionTest() { + HostVO host = new HostVO("host"); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "true"); + }}; + host.setDetails(hostDetails); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, hostId, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertEquals(dest.getHost(), host); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Root requires encryption, last host supports it + */ + @Test + public void passEncRootLastHostSupportingEncryptionTest() { + HostVO host = Mockito.spy(new HostVO("host")); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "true"); + }}; + host.setDetails(hostDetails); + Mockito.when(host.getStatus()).thenReturn(Status.Up); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + VMInstanceVO vm = (VMInstanceVO) vmProfile.getVirtualMachine(); + vm.setLastHostId(hostId); + + // host id is null here so we pick up last host id + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, null, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertEquals(dest.getHost(), host); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * Root requires encryption, last host does not support it + */ + @Test + public void failEncRootLastHostNotSupportingEncryptionTest() { + HostVO host = Mockito.spy(new HostVO("host")); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "false"); + }}; + host.setDetails(hostDetails); + Mockito.when(host.getStatus()).thenReturn(Status.Up); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + setupMocksForPlanDeploymentHostTests(host, vol1); + + VMInstanceVO vm = (VMInstanceVO) vmProfile.getVirtualMachine(); + vm.setLastHostId(hostId); + // host id is null here so we pick up last host id + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, null, null, null); + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); + Assert.assertNull("Destination should be null since last host doesn't support encryption and root requires it", dest); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Test + public void passEncRootPlannerHostSupportingEncryptionTest() { + HostVO host = Mockito.spy(new HostVO("host")); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "true"); + }}; + host.setDetails(hostDetails); + Mockito.when(host.getStatus()).thenReturn(Status.Up); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + DeploymentClusterPlanner planner = setupMocksForPlanDeploymentHostTests(host, vol1); + + // host id is null here so we pick up last host id + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, null, null, null); + + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, planner); + Assert.assertEquals(host, dest.getHost()); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Test + public void failEncRootPlannerHostSupportingEncryptionTest() { + HostVO host = Mockito.spy(new HostVO("host")); + Map hostDetails = new HashMap<>() {{ + put(Host.HOST_VOLUME_ENCRYPTION, "false"); + }}; + host.setDetails(hostDetails); + Mockito.when(host.getStatus()).thenReturn(Status.Up); + + VolumeVO vol1 = new VolumeVO("vol1", dataCenterId,podId,1L,1L, instanceId,"folder","path",ProvisioningType.THIN, (long)10<<30, Volume.Type.ROOT); + vol1.setPassphraseId(1L); + + DeploymentClusterPlanner planner = setupMocksForPlanDeploymentHostTests(host, vol1); + + // host id is null here so we pick up last host id + DataCenterDeployment plan = new DataCenterDeployment(dataCenterId, podId, clusterId, null, null, null); + + try { + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, planner); + Assert.assertNull("Destination should be null since last host doesn't support encryption and root requires it", dest); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + // This is so ugly but everything is so intertwined... + private DeploymentClusterPlanner setupMocksForPlanDeploymentHostTests(HostVO host, VolumeVO vol1) { + long diskOfferingId = 345L; + List volumeVOs = new ArrayList<>(); + List volumes = new ArrayList<>(); + vol1.setDiskOfferingId(diskOfferingId); + volumes.add(vol1); + volumeVOs.add(vol1); + + DiskOfferingVO diskOffering = new DiskOfferingVO(); + diskOffering.setEncrypt(true); + + VMTemplateVO template = new VMTemplateVO(); + template.setFormat(Storage.ImageFormat.QCOW2); + + host.setClusterId(clusterId); + + StoragePool pool = new StoragePoolVO(); + + Map> suitableVolumeStoragePools = new HashMap<>() {{ + put(vol1, List.of(pool)); + }}; + + Pair>, List> suitable = new Pair<>(suitableVolumeStoragePools, volumes); + + ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, + false, false, false, "test dpm", ProvisioningType.THIN, false, false, + null, false, VirtualMachine.Type.User, null, "FirstFitPlanner", true); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); + Mockito.when(vmProfile.getHypervisorType()).thenReturn(HypervisorType.KVM); + Mockito.when(hostDao.findById(hostId)).thenReturn(host); + Mockito.doNothing().when(hostDao).loadDetails(host); + Mockito.doReturn(volumeVOs).when(volDao).findByInstance(ArgumentMatchers.anyLong()); + Mockito.doReturn(suitable).when(_dpm).findSuitablePoolsForVolumes( + ArgumentMatchers.any(VirtualMachineProfile.class), + ArgumentMatchers.any(DataCenterDeployment.class), + ArgumentMatchers.any(ExcludeList.class), + ArgumentMatchers.anyInt() + ); + + ClusterVO clusterVO = new ClusterVO(); + clusterVO.setHypervisorType(HypervisorType.KVM.toString()); + Mockito.when(_clusterDao.findById(ArgumentMatchers.anyLong())).thenReturn(clusterVO); + + Mockito.doReturn(List.of(host)).when(_dpm).findSuitableHosts( + ArgumentMatchers.any(VirtualMachineProfile.class), + ArgumentMatchers.any(DeploymentPlan.class), + ArgumentMatchers.any(ExcludeList.class), + ArgumentMatchers.anyInt() + ); + + Map suitableVolumeStoragePoolMap = new HashMap<>() {{ + put(vol1, pool); + }}; + Mockito.doReturn(true).when(_dpm).hostCanAccessSPool(ArgumentMatchers.any(Host.class), ArgumentMatchers.any(StoragePool.class)); + + Pair> potentialResources = new Pair<>(host, suitableVolumeStoragePoolMap); + + Mockito.when(capacityMgr.checkIfHostReachMaxGuestLimit(host)).thenReturn(false); + Mockito.when(capacityMgr.checkIfHostHasCpuCapability(ArgumentMatchers.anyLong(), ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())).thenReturn(true); + Mockito.when(capacityMgr.checkIfHostHasCapacity( + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyLong(), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.anyFloat(), + ArgumentMatchers.anyFloat(), + ArgumentMatchers.anyBoolean() + )).thenReturn(true); + Mockito.when(serviceOfferingDetailsDao.findDetail(vmProfile.getServiceOfferingId(), GPU.Keys.vgpuType.toString())).thenReturn(null); + + Mockito.doReturn(true).when(_dpm).checkVmProfileAndHost(vmProfile, host); + Mockito.doReturn(true).when(_dpm).checkIfHostFitsPlannerUsage(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(PlannerResourceUsage.class)); + Mockito.when(clusterDetailsDao.findDetail(ArgumentMatchers.anyLong(), ArgumentMatchers.anyString())).thenReturn(new ClusterDetailsVO(clusterId, "mock", "1")); + + DeploymentClusterPlanner planner = Mockito.spy(new FirstFitPlanner()); + try { + Mockito.doReturn(List.of(clusterId), List.of()).when(planner).orderClusters( + ArgumentMatchers.any(VirtualMachineProfile.class), + ArgumentMatchers.any(DeploymentPlan.class), + ArgumentMatchers.any(ExcludeList.class) + ); + } catch (Exception ex) { + ex.printStackTrace(); + } + + return planner; + } + private DataCenter prepareAvoidDisabledTests() { DataCenter dc = Mockito.mock(DataCenter.class); Mockito.when(dc.getId()).thenReturn(123l); diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index f317ea8acf3..b5f460ee3c4 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -35,9 +35,6 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; -import com.cloud.api.query.dao.ServiceOfferingJoinDao; -import com.cloud.api.query.vo.ServiceOfferingJoinVO; -import com.cloud.storage.dao.VMTemplateDao; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; @@ -73,6 +70,8 @@ import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; +import com.cloud.api.query.dao.ServiceOfferingJoinDao; +import com.cloud.api.query.vo.ServiceOfferingJoinVO; import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.DataCenterVO; @@ -85,7 +84,9 @@ import com.cloud.org.Grouping; import com.cloud.serializer.GsonHelper; import com.cloud.server.TaggedResourceService; import com.cloud.storage.Volume.Type; +import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.StoragePoolTagsDao; +import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.Account; @@ -158,6 +159,8 @@ public class VolumeApiServiceImplTest { private VMTemplateDao templateDao; @Mock private ServiceOfferingJoinDao serviceOfferingJoinDao; + @Mock + private DiskOfferingDao _diskOfferingDao; private DetachVolumeCmd detachCmd = new DetachVolumeCmd(); private Class _detachCmdClass = detachCmd.getClass(); @@ -269,6 +272,7 @@ public class VolumeApiServiceImplTest { VolumeVO correctRootVolumeVO = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); when(volumeDaoMock.findById(6L)).thenReturn(correctRootVolumeVO); + when(volumeDaoMock.getHypervisorType(6L)).thenReturn(HypervisorType.XenServer); // managed root volume VolumeInfo managedVolume = Mockito.mock(VolumeInfo.class); @@ -291,7 +295,7 @@ public class VolumeApiServiceImplTest { when(userVmDaoMock.findById(4L)).thenReturn(vmHavingRootVolume); List vols = new ArrayList(); vols.add(new VolumeVO()); - when(volumeDaoMock.findByInstanceAndDeviceId(4L, 0L)).thenReturn(vols); + lenient().when(volumeDaoMock.findByInstanceAndDeviceId(4L, 0L)).thenReturn(vols); // volume in uploaded state VolumeInfo uploadedVolume = Mockito.mock(VolumeInfo.class); @@ -309,6 +313,27 @@ public class VolumeApiServiceImplTest { upVolume.setState(Volume.State.Uploaded); when(volumeDaoMock.findById(8L)).thenReturn(upVolume); + UserVmVO kvmVm = new UserVmVO(4L, "vm", "vm", 1, HypervisorType.KVM, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); + kvmVm.setState(State.Running); + kvmVm.setDataCenterId(1L); + when(userVmDaoMock.findById(4L)).thenReturn(kvmVm); + + VolumeVO volumeOfKvmVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 4L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); + volumeOfKvmVm.setPoolId(1L); + lenient().when(volumeDaoMock.findById(9L)).thenReturn(volumeOfKvmVm); + lenient().when(volumeDaoMock.getHypervisorType(9L)).thenReturn(HypervisorType.KVM); + + VolumeVO dataVolumeVO = new VolumeVO("data", 1L, 1L, 1L, 1L, 2L, "data", "data", Storage.ProvisioningType.THIN, 1, null, null, "data", Type.DATADISK); + lenient().when(volumeDaoMock.findById(10L)).thenReturn(dataVolumeVO); + + VolumeInfo dataVolume = Mockito.mock(VolumeInfo.class); + when(dataVolume.getId()).thenReturn(10L); + when(dataVolume.getDataCenterId()).thenReturn(1L); + when(dataVolume.getVolumeType()).thenReturn(Volume.Type.DATADISK); + when(dataVolume.getInstanceId()).thenReturn(null); + when(dataVolume.getState()).thenReturn(Volume.State.Allocated); + when(volumeDataFactoryMock.getVolume(10L)).thenReturn(dataVolume); + // helper dao methods mock when(_vmSnapshotDao.findByVm(any(Long.class))).thenReturn(new ArrayList()); when(_vmInstanceDao.findById(any(Long.class))).thenReturn(stoppedVm); @@ -322,6 +347,10 @@ public class VolumeApiServiceImplTest { txn.close("runVolumeDaoImplTest"); } + DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); + when(diskOffering.getEncrypt()).thenReturn(false); + when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering); + // helper methods mock lenient().doNothing().when(accountManagerMock).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class)); doNothing().when(_jobMgr).updateAsyncJobAttachment(any(Long.class), any(String.class), any(Long.class)); @@ -415,6 +444,25 @@ public class VolumeApiServiceImplTest { volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L); } + // Negative test - attach data volume, to the vm on non-kvm hypervisor + @Test(expected = InvalidParameterValueException.class) + public void attachDiskWithEncryptEnabledOfferingonNonKVM() throws NoSuchFieldException, IllegalAccessException { + DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); + when(diskOffering.getEncrypt()).thenReturn(true); + when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering); + volumeApiServiceImpl.attachVolumeToVM(2L, 10L, 1L); + } + + // Positive test - attach data volume, to the vm on kvm hypervisor + @Test + public void attachDiskWithEncryptEnabledOfferingOnKVM() throws NoSuchFieldException, IllegalAccessException { + thrown.expect(NullPointerException.class); + DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); + when(diskOffering.getEncrypt()).thenReturn(true); + when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering); + volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L); + } + // volume not Ready @Test(expected = InvalidParameterValueException.class) public void testTakeSnapshotF1() throws ResourceAllocationException { diff --git a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java index b9bbe068ab6..0f9b999a0fe 100644 --- a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java +++ b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java @@ -63,7 +63,7 @@ public class StoragePoolMonitorTest { Mockito.when(poolDao.listBy(nullable(Long.class), nullable(Long.class), nullable(Long.class), Mockito.any(ScopeType.class))).thenReturn(Collections.singletonList(pool)); Mockito.when(poolDao.findZoneWideStoragePoolsByTags(Mockito.anyLong(), Mockito.any(String[].class))).thenReturn(Collections.emptyList()); Mockito.when(poolDao.findZoneWideStoragePoolsByHypervisor(Mockito.anyLong(), Mockito.any(Hypervisor.HypervisorType.class))).thenReturn(Collections.emptyList()); - Mockito.doNothing().when(storageManager).connectHostToSharedPool(host.getId(), pool.getId()); + Mockito.doReturn(true).when(storageManager).connectHostToSharedPool(host.getId(), pool.getId()); storagePoolMonitor.processConnect(host, cmd, false); diff --git a/server/src/test/resources/createNetworkOffering.xml b/server/src/test/resources/createNetworkOffering.xml index 897d4dac305..ea127d741f1 100644 --- a/server/src/test/resources/createNetworkOffering.xml +++ b/server/src/test/resources/createNetworkOffering.xml @@ -62,4 +62,5 @@ + diff --git a/test/integration/smoke/test_disk_offerings.py b/test/integration/smoke/test_disk_offerings.py index 660dd30024d..dc23a52a026 100644 --- a/test/integration/smoke/test_disk_offerings.py +++ b/test/integration/smoke/test_disk_offerings.py @@ -45,7 +45,7 @@ class TestCreateDiskOffering(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(tags=["advanced", "basic", "eip", "sg", "advancedns", "smoke"], required_hardware="false") + @attr(tags=["advanced", "basic", "eip", "sg", "advancedns", "smoke", "diskencrypt"], required_hardware="false") def test_01_create_disk_offering(self): """Test to create disk offering @@ -87,6 +87,11 @@ class TestCreateDiskOffering(cloudstackTestCase): self.services["disk_offering"]["name"], "Check name in createServiceOffering" ) + self.assertEqual( + disk_response.encrypt, + False, + "Ensure disk encryption is false by default" + ) return @attr(hypervisor="kvm") @@ -294,6 +299,49 @@ class TestCreateDiskOffering(cloudstackTestCase): return + @attr(tags = ["advanced", "basic", "eip", "sg", "advancedns", "simulator", "smoke", "diskencrypt"]) + def test_08_create_encrypted_disk_offering(self): + """Test to create an encrypted type disk offering""" + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + + disk_offering = DiskOffering.create( + self.apiclient, + self.services["disk_offering"], + name="disk-encrypted", + encrypt="true" + ) + self.cleanup.append(disk_offering) + + self.debug("Created Disk offering with ID: %s" % disk_offering.id) + + list_disk_response = list_disk_offering( + self.apiclient, + id=disk_offering.id + ) + + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_disk_response), + 0, + "Check Disk offering is created" + ) + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.encrypt, + True, + "Check if encrypt is set after createServiceOffering" + ) + return + class TestDiskOfferings(cloudstackTestCase): def setUp(self): diff --git a/test/integration/smoke/test_service_offerings.py b/test/integration/smoke/test_service_offerings.py index 3a942a10b62..a7723d575ad 100644 --- a/test/integration/smoke/test_service_offerings.py +++ b/test/integration/smoke/test_service_offerings.py @@ -67,7 +67,7 @@ class TestCreateServiceOffering(cloudstackTestCase): "smoke", "basic", "eip", - "sg"], + "sg", "diskencrypt"], required_hardware="false") def test_01_create_service_offering(self): """Test to create service offering""" @@ -128,6 +128,11 @@ class TestCreateServiceOffering(cloudstackTestCase): self.services["service_offerings"]["tiny"]["name"], "Check name in createServiceOffering" ) + self.assertEqual( + list_service_response[0].encryptroot, + False, + "Ensure encrypt is false by default" + ) return @attr( @@ -301,6 +306,53 @@ class TestCreateServiceOffering(cloudstackTestCase): ) return + @attr( + tags=[ + "advanced", + "advancedns", + "smoke", + "basic", + "eip", + "sg", + "diskencrypt"], + required_hardware="false") + def test_05_create_service_offering_with_root_encryption_type(self): + """Test to create service offering with root encryption""" + + # Validate the following: + # 1. createServiceOfferings should return a valid information + # for newly created offering + + service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"], + name="tiny-encrypted-root", + encryptRoot=True + ) + self.cleanup.append(service_offering) + + self.debug( + "Created service offering with ID: %s" % + service_offering.id) + + list_service_response = list_service_offering( + self.apiclient, + id=service_offering.id + ) + + self.assertNotEqual( + len(list_service_response), + 0, + "Check Service offering is created" + ) + + self.assertEqual( + list_service_response[0].encryptroot, + True, + "Check encrypt root is true" + ) + return + class TestServiceOfferings(cloudstackTestCase): diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 13082859682..5bd0dd6e502 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -43,9 +43,12 @@ from marvin.lib.common import (get_domain, find_storage_pool_type, get_pod, list_disk_offering) -from marvin.lib.utils import checkVolumeSize +from marvin.lib.utils import (cleanup_resources, checkVolumeSize) from marvin.lib.utils import (format_volume_to_ext3, wait_until) +from marvin.sshClient import SshClient +import xml.etree.ElementTree as ET +from lxml import etree from nose.plugins.attrib import attr @@ -1005,3 +1008,555 @@ class TestVolumes(cloudstackTestCase): "Offering name did not match with the new one " ) return + + +class TestVolumeEncryption(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestVolumeEncryption, cls).getClsTestClient() + cls.apiclient = cls.testClient.getApiClient() + cls.services = cls.testClient.getParsedTestDataConfig() + cls._cleanup = [] + + cls.unsupportedHypervisor = False + cls.hypervisor = cls.testClient.getHypervisorInfo() + if cls.hypervisor.lower() not in ['kvm']: + # Volume Encryption currently supported for KVM hypervisor + cls.unsupportedHypervisor = True + return + + # Get Zone and Domain + cls.domain = get_domain(cls.apiclient) + cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) + + cls.services['mode'] = cls.zone.networktype + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + + # Get template + template = get_suitable_test_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"], + cls.hypervisor + ) + if template == FAILED: + assert False, "get_suitable_test_template() failed to return template with description %s" % cls.services["ostype"] + + cls.services["template"] = template.id + cls.services["diskname"] = cls.services["volume"]["diskname"] + + cls.hostConfig = cls.config.__dict__["zones"][0].__dict__["pods"][0].__dict__["clusters"][0].__dict__["hosts"][0].__dict__ + + # Create Account + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + + # Create Service Offering + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"] + ) + cls._cleanup.append(cls.service_offering) + + # Create Service Offering with encryptRoot true + cls.service_offering_encrypt = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"], + name="Small Encrypted Instance", + encryptroot=True + ) + cls._cleanup.append(cls.service_offering_encrypt) + + # Create Disk Offering + cls.disk_offering = DiskOffering.create( + cls.apiclient, + cls.services["disk_offering"] + ) + cls._cleanup.append(cls.disk_offering) + + # Create Disk Offering with encrypt true + cls.disk_offering_encrypt = DiskOffering.create( + cls.apiclient, + cls.services["disk_offering"], + name="Encrypted", + encrypt=True + ) + cls._cleanup.append(cls.disk_offering_encrypt) + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.apiclient, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + if self.unsupportedHypervisor: + self.skipTest("Skipping test as volume encryption is not supported for hypervisor %s" % self.hypervisor) + + if not self.does_host_with_encryption_support_exists(): + self.skipTest("Skipping test as no host exists with volume encryption support") + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "smoke", "diskencrypt"], required_hardware="true") + def test_01_root_volume_encryption(self): + """Test Root Volume Encryption + + # Validate the following + # 1. Create VM using the service offering with encryptroot true + # 2. Verify VM created and Root Volume + # 3. Create Data Volume using the disk offering with encrypt false + # 4. Verify Data Volume + """ + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_encrypt.id, + mode=self.services["mode"] + ) + self.cleanup.append(virtual_machine) + self.debug("Created VM with ID: %s" % virtual_machine.id) + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + self.assertEqual( + vm_response.state, + 'Running', + msg="VM is not in Running state" + ) + + self.check_volume_encryption(virtual_machine, 1) + + volume = Volume.create( + self.apiclient, + self.services, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created a volume with ID: %s" % volume.id) + + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + + self.debug("Attaching volume (ID: %s) to VM (ID: %s)" % (volume.id, virtual_machine.id)) + + virtual_machine.attach_volume( + self.apiclient, + volume + ) + + try: + ssh = virtual_machine.get_ssh_client() + self.debug("Rebooting VM %s" % virtual_machine.id) + ssh.execute("reboot") + except Exception as e: + self.fail("SSH access failed for VM %s - %s" % (virtual_machine.ipaddress, e)) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) " % vm.id) + timeout = timeout - 1 + + vol_sz = str(list_volume_response[0].size) + ssh = virtual_machine.get_ssh_client( + reconnect=True + ) + + # Get the updated volume information + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + + volume_name = "/dev/vd" + chr(ord('a') + int(list_volume_response[0].deviceid)) + self.debug(" Using KVM volume_name: %s" % (volume_name)) + ret = checkVolumeSize(ssh_handle=ssh, volume_name=volume_name, size_to_verify=vol_sz) + self.debug(" Volume Size Expected %s Actual :%s" % (vol_sz, ret[1])) + virtual_machine.detach_volume(self.apiclient, volume) + self.assertEqual(ret[0], SUCCESS, "Check if promised disk size actually available") + time.sleep(self.services["sleep"]) + + @attr(tags=["advanced", "smoke", "diskencrypt"], required_hardware="true") + def test_02_data_volume_encryption(self): + """Test Data Volume Encryption + + # Validate the following + # 1. Create VM using the service offering with encryptroot false + # 2. Verify VM created and Root Volume + # 3. Create Data Volume using the disk offering with encrypt true + # 4. Verify Data Volume + """ + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + self.cleanup.append(virtual_machine) + self.debug("Created VM with ID: %s" % virtual_machine.id) + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + self.assertEqual( + vm_response.state, + 'Running', + msg="VM is not in Running state" + ) + + volume = Volume.create( + self.apiclient, + self.services, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_encrypt.id + ) + self.debug("Created a volume with ID: %s" % volume.id) + + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + + self.debug("Attaching volume (ID: %s) to VM (ID: %s)" % (volume.id, virtual_machine.id)) + + virtual_machine.attach_volume( + self.apiclient, + volume + ) + + try: + ssh = virtual_machine.get_ssh_client() + self.debug("Rebooting VM %s" % virtual_machine.id) + ssh.execute("reboot") + except Exception as e: + self.fail("SSH access failed for VM %s - %s" % (virtual_machine.ipaddress, e)) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) " % vm.id) + timeout = timeout - 1 + + vol_sz = str(list_volume_response[0].size) + ssh = virtual_machine.get_ssh_client( + reconnect=True + ) + + # Get the updated volume information + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + + volume_name = "/dev/vd" + chr(ord('a') + int(list_volume_response[0].deviceid)) + self.debug(" Using KVM volume_name: %s" % (volume_name)) + ret = checkVolumeSize(ssh_handle=ssh, volume_name=volume_name, size_to_verify=vol_sz) + self.debug(" Volume Size Expected %s Actual :%s" % (vol_sz, ret[1])) + + self.check_volume_encryption(virtual_machine, 1) + + virtual_machine.detach_volume(self.apiclient, volume) + self.assertEqual(ret[0], SUCCESS, "Check if promised disk size actually available") + time.sleep(self.services["sleep"]) + + @attr(tags=["advanced", "smoke", "diskencrypt"], required_hardware="true") + def test_03_root_and_data_volume_encryption(self): + """Test Root and Data Volumes Encryption + + # Validate the following + # 1. Create VM using the service offering with encryptroot true + # 2. Verify VM created and Root Volume + # 3. Create Data Volume using the disk offering with encrypt true + # 4. Verify Data Volume + """ + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services, + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering_encrypt.id, + diskofferingid=self.disk_offering_encrypt.id, + mode=self.services["mode"] + ) + self.cleanup.append(virtual_machine) + self.debug("Created VM with ID: %s" % virtual_machine.id) + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + self.assertEqual( + vm_response.state, + 'Running', + msg="VM is not in Running state" + ) + + self.check_volume_encryption(virtual_machine, 2) + + volume = Volume.create( + self.apiclient, + self.services, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering_encrypt.id + ) + self.debug("Created a volume with ID: %s" % volume.id) + + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + + self.debug("Attaching volume (ID: %s) to VM (ID: %s)" % (volume.id, virtual_machine.id)) + + virtual_machine.attach_volume( + self.apiclient, + volume + ) + + try: + ssh = virtual_machine.get_ssh_client() + self.debug("Rebooting VM %s" % virtual_machine.id) + ssh.execute("reboot") + except Exception as e: + self.fail("SSH access failed for VM %s - %s" % (virtual_machine.ipaddress, e)) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) " % vm.id) + timeout = timeout - 1 + + vol_sz = str(list_volume_response[0].size) + ssh = virtual_machine.get_ssh_client( + reconnect=True + ) + + # Get the updated volume information + list_volume_response = Volume.list( + self.apiclient, + id=volume.id) + + volume_name = "/dev/vd" + chr(ord('a') + int(list_volume_response[0].deviceid)) + self.debug(" Using KVM volume_name: %s" % (volume_name)) + ret = checkVolumeSize(ssh_handle=ssh, volume_name=volume_name, size_to_verify=vol_sz) + self.debug(" Volume Size Expected %s Actual :%s" % (vol_sz, ret[1])) + + self.check_volume_encryption(virtual_machine, 3) + + virtual_machine.detach_volume(self.apiclient, volume) + self.assertEqual(ret[0], SUCCESS, "Check if promised disk size actually available") + time.sleep(self.services["sleep"]) + + def does_host_with_encryption_support_exists(self): + hosts = Host.list( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + hypervisor='KVM', + state='Up') + + for host in hosts: + if host.encryptionsupported: + return True + + return False + + def check_volume_encryption(self, virtual_machine, volumes_count): + hosts = Host.list(self.apiclient, id=virtual_machine.hostid) + if len(hosts) != 1: + assert False, "Could not find host with id " + virtual_machine.hostid + + host = hosts[0] + instance_name = virtual_machine.instancename + + self.assertIsNotNone(host, "Host should not be None") + self.assertIsNotNone(instance_name, "Instance name should not be None") + + ssh_client = SshClient( + host=host.ipaddress, + port=22, + user=self.hostConfig['username'], + passwd=self.hostConfig['password']) + + virsh_cmd = 'virsh dumpxml %s' % instance_name + xml_res = ssh_client.execute(virsh_cmd) + xml_as_str = ''.join(xml_res) + parser = etree.XMLParser(remove_blank_text=True) + virshxml_root = ET.fromstring(xml_as_str, parser=parser) + + encryption_format = virshxml_root.findall(".devices/disk/encryption[@format='luks']") + self.assertIsNotNone(encryption_format, "The volume encryption format is not luks") + self.assertEqual( + len(encryption_format), + volumes_count, + "Check the number of volumes encrypted with luks format" + ) + + secret_type = virshxml_root.findall(".devices/disk/encryption/secret[@type='passphrase']") + self.assertIsNotNone(secret_type, "The volume encryption secret type is not passphrase") + self.assertEqual( + len(secret_type), + volumes_count, + "Check the number of encrypted volumes with passphrase secret type" + ) diff --git a/ui/package.json b/ui/package.json index f063a0f8c91..3879ee92a6c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -40,12 +40,12 @@ "@fortawesome/vue-fontawesome": "^2.0.2", "ant-design-vue": "~1.7.3", "antd-theme-webpack-plugin": "^1.3.9", - "axios": "^0.21.4", + "axios": "^0.21.1", "babel-plugin-require-context-hook": "^1.0.0", "core-js": "^3.6.5", "enquire.js": "^2.1.6", "js-cookie": "^2.2.1", - "lodash": "^4.17.21", + "lodash": "^4.17.15", "md5": "^2.2.1", "moment": "^2.26.0", "npm-check-updates": "^6.0.1", diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 4e8d2b42ea6..db6f1e1274e 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -900,6 +900,8 @@ "label.enable.vpn": "Enable Remote Access VPN", "label.enabling.vpn": "Enabling VPN", "label.enabling.vpn.access": "Enabling VPN Access", +"label.encrypt": "Encrypt", +"label.encryptroot": "Encrypt Root Disk", "label.end": "End", "label.end.ip": "End IP", "label.end.reserved.system.ip": "End Reserved system IP", @@ -2394,6 +2396,7 @@ "label.volume": "Volume", "label.volume.details": "Volume details", "label.volume.empty": "No data volumes attached to this VM", +"label.volume.encryption.support": "Volume Encryption Supported", "label.volume.ids": "Volume ID's", "label.volume.migrated": "Volume migrated", "label.volume.volumefileupload.description": "Click or drag file to this area to upload", diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 6a09e6f8040..59f99c8a9b7 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -31,7 +31,7 @@ export default { params: { isrecursive: 'true' }, columns: ['name', 'displaytext', 'cpunumber', 'cpuspeed', 'memory', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled'] + var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'encryptroot'] if (store.getters.apis.createServiceOffering && store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') @@ -141,7 +141,7 @@ export default { params: { isrecursive: 'true' }, columns: ['name', 'displaytext', 'disksize', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'tags', 'domain', 'zone', 'created'] + var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'tags', 'domain', 'zone', 'created', 'encrypt'] if (store.getters.apis.createDiskOffering && store.getters.apis.createDiskOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') diff --git a/ui/src/views/infra/HostInfo.vue b/ui/src/views/infra/HostInfo.vue index 4ab73b51a20..c30a75463f5 100644 --- a/ui/src/views/infra/HostInfo.vue +++ b/ui/src/views/infra/HostInfo.vue @@ -40,6 +40,14 @@ + +
+ {{ $t('label.volume.encryption.support') }} +
+ {{ host.encryptionsupported }} +
+
+
{{ $t('label.hosttags') }} diff --git a/ui/src/views/offering/AddComputeOffering.vue b/ui/src/views/offering/AddComputeOffering.vue index bcc09c1e9d5..8faf7fcec5e 100644 --- a/ui/src/views/offering/AddComputeOffering.vue +++ b/ui/src/views/offering/AddComputeOffering.vue @@ -425,6 +425,10 @@ + + + + @@ -552,6 +556,7 @@ export default { qosType: '', isCustomizedDiskIops: false, isPublic: true, + isEncrypted: false, selectedDomains: [], domains: [], domainLoading: false, @@ -751,7 +756,8 @@ export default { customized: values.offeringtype !== 'fixed', offerha: values.offerha === true, limitcpuuse: values.limitcpuuse === true, - dynamicscalingenabled: values.dynamicscalingenabled + dynamicscalingenabled: values.dynamicscalingenabled, + encryptroot: values.isencrypted } // custom fields (begin) diff --git a/ui/src/views/offering/AddDiskOffering.vue b/ui/src/views/offering/AddDiskOffering.vue index 26cb04bbdb8..54b924b5056 100644 --- a/ui/src/views/offering/AddDiskOffering.vue +++ b/ui/src/views/offering/AddDiskOffering.vue @@ -260,6 +260,10 @@ + + + + @@ -375,6 +379,7 @@ export default { storagePolicies: null, storageTagLoading: false, isPublic: true, + isEncrypted: false, domains: [], domainLoading: false, zones: [], @@ -501,7 +506,8 @@ export default { storageType: values.storagetype, cacheMode: values.writecachetype, provisioningType: values.provisioningtype, - customized: values.customdisksize + customized: values.customdisksize, + encrypt: values.isencrypted } if (values.customdisksize !== true) { params.disksize = values.disksize diff --git a/utils/src/main/java/com/cloud/utils/UuidUtils.java b/utils/src/main/java/com/cloud/utils/UuidUtils.java index e733eff6da3..5b24c28e284 100644 --- a/utils/src/main/java/com/cloud/utils/UuidUtils.java +++ b/utils/src/main/java/com/cloud/utils/UuidUtils.java @@ -24,13 +24,14 @@ import org.apache.xerces.impl.xpath.regex.RegularExpression; public class UuidUtils { + public static RegularExpression REGEX = new RegularExpression("[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"); + public final static String first(String uuid) { return uuid.substring(0, uuid.indexOf('-')); } public static boolean validateUUID(String uuid) { - RegularExpression regex = new RegularExpression("[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}"); - return regex.matches(uuid); + return REGEX.matches(uuid); } /**