From 526dca12824b38ee17eb15b5914013aea97d3dbe Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Thu, 3 Feb 2022 18:52:06 +0200 Subject: [PATCH 01/12] StorPool storage plugin Adds volume storage plugin for StorPool SDS --- client/pom.xml | 12 + .../api/storage/PrimaryDataStoreDriver.java | 24 + .../api/storage/StorageStrategyFactory.java | 2 +- .../vmsnapshot/DefaultVMSnapshotStrategy.java | 2 +- packaging/centos7/cloud.spec | 1 + packaging/centos8/cloud.spec | 1 + packaging/suse15/cloud.spec | 1 + plugins/pom.xml | 4 +- .../driver/DateraPrimaryDataStoreDriver.java | 18 + .../CloudStackPrimaryDataStoreDriverImpl.java | 18 + .../LinstorPrimaryDataStoreDriverImpl.java | 18 + .../driver/NexentaPrimaryDataStoreDriver.java | 18 + .../SamplePrimaryDataStoreDriverImpl.java | 18 + .../driver/ScaleIOPrimaryDataStoreDriver.java | 18 + .../SolidFirePrimaryDataStoreDriver.java | 18 + plugins/storage/volume/storpool/README.md | 344 +++++++ plugins/storage/volume/storpool/pom.xml | 63 ++ .../StorPoolMigrateWithVolumesCommand.java | 55 + .../StorPoolModifyStoragePoolAnswer.java | 94 ++ .../StorPoolModifyStoragePoolCommand.java | 36 + .../StorpoolBackupSnapshotCommand.java | 30 + ...poolBackupTemplateFromSnapshotCommand.java | 30 + .../api/storage/StorpoolCopyCommand.java | 60 ++ .../StorpoolCopyVolumeToSecondaryCommand.java | 30 + .../StorpoolDownloadTemplateCommand.java | 37 + .../StorpoolDownloadVolumeCommand.java | 30 + .../storage/StorpoolResizeVolumeCommand.java | 39 + .../StorPoolModifyStorageCommandWrapper.java | 144 +++ .../StorpoolBackupSnapshotCommandWrapper.java | 109 ++ ...kupTemplateFromSnapshotCommandWrapper.java | 161 +++ ...olCopyVolumeToSecondaryCommandWrapper.java | 125 +++ ...torpoolDownloadTemplateCommandWrapper.java | 133 +++ .../StorpoolDownloadVolumeCommandWrapper.java | 162 +++ .../StorpoolResizeVolumeCommandWrapper.java | 98 ++ .../kvm/storage/StorpoolStorageAdaptor.java | 384 +++++++ .../kvm/storage/StorpoolStoragePool.java | 164 +++ .../StorPoolAbandonObjectsCollector.java | 312 ++++++ .../StorpoolPrimaryDataStoreDriver.java | 950 ++++++++++++++++++ .../StorpoolPrimaryDataStoreLifeCycle.java | 316 ++++++ .../provider/StorpoolHostListener.java | 211 ++++ .../StorpoolPrimaryDataStoreProvider.java | 78 ++ .../datastore/util/StorPoolHelper.java | 262 +++++ .../storage/datastore/util/StorpoolUtil.java | 599 +++++++++++ .../motion/StorPoolDataMotionStrategy.java | 575 +++++++++++ .../StorPoolConfigurationManager.java | 41 + .../snapshot/StorpoolSnapshotStrategy.java | 288 ++++++ .../snapshot/StorpoolVMSnapshotStrategy.java | 380 +++++++ .../storage-volume-storpool/module.properties | 18 + ...spring-storage-volume-storpool-context.xml | 38 + .../cloud/storage/VolumeApiServiceImpl.java | 15 + .../cloud/tags/TaggedResourceManagerImpl.java | 34 +- 51 files changed, 6614 insertions(+), 4 deletions(-) create mode 100644 plugins/storage/volume/storpool/README.md create mode 100644 plugins/storage/volume/storpool/pom.xml create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolMigrateWithVolumesCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java create mode 100644 plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java create mode 100644 plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/module.properties create mode 100644 plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml diff --git a/client/pom.xml b/client/pom.xml index b9cbae91b7a5..aae0006174e0 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -97,6 +97,11 @@ cloud-plugin-storage-volume-linstor ${project.version} + + org.apache.cloudstack + cloud-plugin-storage-volume-storpool + ${project.version} + org.apache.cloudstack cloud-server @@ -755,6 +760,12 @@ bcpkix-jdk15on false ${project.build.directory}/lib + + + org.apache.cloudstack + cloud-plugin-storage-volume-storpool + false + ${project.build.directory}/lib org.bouncycastle @@ -799,6 +810,7 @@ org.bouncycastle:bcpkix-jdk15on org.bouncycastle:bctls-jdk15on mysql:mysql-connector-java + org.apache.cloudstack:cloud-plugin-storage-volume-storpool diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index 622dda31f987..6c767639bb80 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -103,4 +103,28 @@ enum QualityOfServiceState { MIGRATION, NO_MIGRATION } * returns true if the host can access the storage pool */ boolean canHostAccessStoragePool(Host host, StoragePool pool); + + /** + * intended for storage pools + * returns true if additional VM info is needed + */ + boolean vmInfoNeeded(); + + /** + * intended for storage pools + * provide additional info for a VM + */ + void provideVMInfo(long vmId, long volumeId); + + /** + * intended for storage pools + * returns true if the storage have to know about the VM's tags + */ + boolean vmTagsNeeded(String tagKey); + + /** + * intended for storage pools + * provide added tags of a VM + */ + void provideVMTags(long vmId, long volumeId, String tagValue); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StorageStrategyFactory.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StorageStrategyFactory.java index eac9a313278f..e309b9842be5 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StorageStrategyFactory.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StorageStrategyFactory.java @@ -39,9 +39,9 @@ public interface StorageStrategyFactory { /** * Used only for KVM hypervisors when allocating a VM snapshot * @param vmId the ID of the virtual machine + * @param rootPoolId volume pool ID * @param snapshotMemory for VM snapshots with memory * @return VMSnapshotStrategy */ VMSnapshotStrategy getVmSnapshotStrategy(Long vmId, Long rootPoolId, boolean snapshotMemory); - } diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java index c647b11d22c5..90f6ccd1f9ba 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java @@ -52,8 +52,8 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.storage.GuestOSVO; -import com.cloud.storage.VolumeVO; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.GuestOSHypervisorDao; diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec index d8ecf662c63b..4288463d2eb6 100644 --- a/packaging/centos7/cloud.spec +++ b/packaging/centos7/cloud.spec @@ -351,6 +351,7 @@ install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT install -D agent/target/transformed/cloudstack-agent.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-agent install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib +cp plugins/storage/volume/storpool/target/*.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib # Usage server mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage diff --git a/packaging/centos8/cloud.spec b/packaging/centos8/cloud.spec index e86a98781bfd..099ad59b3d58 100644 --- a/packaging/centos8/cloud.spec +++ b/packaging/centos8/cloud.spec @@ -344,6 +344,7 @@ install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT install -D agent/target/transformed/cloudstack-agent.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-agent install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib +cp plugins/storage/volume/storpool/target/*.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib # Usage server mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage diff --git a/packaging/suse15/cloud.spec b/packaging/suse15/cloud.spec index d6be798e0954..bf6ef6671092 100644 --- a/packaging/suse15/cloud.spec +++ b/packaging/suse15/cloud.spec @@ -346,6 +346,7 @@ install -D agent/target/transformed/cloudstack-agent-profile.sh ${RPM_BUILD_ROOT install -D agent/target/transformed/cloudstack-agent.logrotate ${RPM_BUILD_ROOT}%{_sysconfdir}/logrotate.d/%{name}-agent install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%name-agent/lib/cloud-plugin-hypervisor-kvm-%{_maventag}.jar cp plugins/hypervisors/kvm/target/dependencies/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib +cp plugins/storage/volume/storpool/target/*.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-agent/lib # Usage server mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/usage diff --git a/plugins/pom.xml b/plugins/pom.xml index 04c94a22f91b..961884faac63 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -123,6 +123,8 @@ storage/volume/solidfire storage/volume/scaleio storage/volume/linstor + storage/volume/storpool + storage-allocators/random @@ -211,4 +213,4 @@ - \ No newline at end of file + diff --git a/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java b/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java index 91e73aa44249..62d1e6e4c404 100644 --- a/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java @@ -1860,4 +1860,22 @@ public Pair getVolumeStats(StoragePool storagePool, String volumeId) public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } 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 56d2c3b7b2f6..f53aca0c34bc 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 @@ -491,4 +491,22 @@ public Pair getVolumeStats(StoragePool storagePool, String volumeId) public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 320860380cee..5ab551a4be77 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -765,4 +765,22 @@ public Pair getVolumeStats(StoragePool storagePool, String volumeId) public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } diff --git a/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java b/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java index 92f8938060be..8e134c6c3d61 100644 --- a/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java @@ -239,4 +239,22 @@ public Pair getStorageStats(StoragePool storagePool) { public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } diff --git a/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index a41627723437..f4e3937b668c 100644 --- a/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -265,4 +265,22 @@ public Pair getStorageStats(StoragePool storagePool) { public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } 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 8b8c7e7e3e24..8ffbabc9d3b2 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 @@ -947,4 +947,22 @@ private void alertHostSdcDisconnection(Host host) { 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); } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 6bad1efbb733..1bd1b6f3c44c 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -1619,4 +1619,22 @@ public Pair getVolumeStats(StoragePool storagePool, String volumeId) public boolean canHostAccessStoragePool(Host host, StoragePool pool) { return true; } + + @Override + public boolean vmInfoNeeded() { + return false; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return false; + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + } } diff --git a/plugins/storage/volume/storpool/README.md b/plugins/storage/volume/storpool/README.md new file mode 100644 index 000000000000..7f710732e084 --- /dev/null +++ b/plugins/storage/volume/storpool/README.md @@ -0,0 +1,344 @@ +# StorPool CloudStack Integration + +## CloudStack Overview + +### Primary and Secondary storage + +Primary storage is associated with a cluster or zone, and it stores the virtual disks for all the VMs running on hosts in that cluster/zone. + +Secondary storage stores the following: +* Templates — OS images that can be used to boot VMs and can include additional configuration information, such as installed applications +* ISO images — disc images containing data or bootable media for operating systems +* Disk volume snapshots — saved copies of VM data which can be used for data recovery or to create new templates + + +### ROOT and DATA volumes + +ROOT volumes correspond to the boot disk of a VM. They are created automatically by CloudStack during VM creation. +ROOT volumes are created based on a system disk offering, corresponding to the service offering the user VM +is based on. We may change the ROOT volume disk offering but only to another system created disk offering. + +DATA volumes correspond to additional disks. These can be created by users and then attached/detached to VMs. +DATA volumes are created based on a user-defined disk offering. + + +## Plugin Organization + +The StorPool plugin consists of two parts: + +### KVM hypervisor plugin patch + +Source directory: ./apache-cloudstack-4.17-src/plugins/hypervisors/kvm + +### StorPool primary storage plugin + +Source directory: ./apache-cloudstack-4.17.0-src/plugins/storage/volume + +There is one plugin for both the CloudStack management and agents, in the hope that having all the source +in one place will ease development and maintenance. The plugin itself though is separated into two mainly +independent parts: + + * ./src/com/... directory tree: agent related classes and commands send from management to agent + * ./src/org/... directory tree: management related classes + +The plugin is intended to be self contained and non-intrusive, thus ideally deploying it would consist of only +dropping the jar file into the appropriate places. This is the reason why all StorPool related communication +(ex. data copying, volume resize) is done with StorPool specific commands even when there is a CloudStack command +that does pretty much the same. + +Note that for the present the StorPool plugin may only be used for a single primary storage cluster; support for +multiple clusters is planned. + + +## Build, Install, Setup + +### Build + +Go to the source directory and run: + + mvn -Pdeveloper -DskipTests install + +The resulting jar file is located in the target/ subdirectory. + +Note: checkstyle errors: before compilation a code style check is performed; if this fails compilation is aborted. +In short: no trailing whitespace, indent using 4 spaces, not tabs, comment-out or remove unused imports. + +Note: Need to build both the KVM plugin and the StorPool plugin proper. + +### Install + +#### StorPool primary storage plugin + +For each CloudStack management host: + +```bash +scp ./target/cloud-plugin-storage-volume-storpool-{version}.jar {MGMT_HOST}:/usr/share/cloudstack-management/lib/ +``` + +For each CloudStack agent host: + +```bash +scp ./target/cloud-plugin-storage-volume-storpool-{version}.jar {AGENT_HOST}:/usr/share/cloudstack-agent/plugins/ +``` + +Note: CloudStack managements/agents services must be restarted after adding the plugin to the respective directories + +Note: Agents should have access to the StorPool management API, since attach and detach operations happens on the agent. +This is a CloudStack design issue, can't do much about it. + +### Setup + +#### Setting up StorPool + +Perform the StorPool installation following the StorPool Installation Guide. + +Create a template to be used by CloudStack. Must set *placeHead*, *placeAll*, *placeTail* and *replication*. +No need to set default volume size because it is determined by the CloudStack disks and services offering. + +#### Setting up a StorPool PRIMARY storage pool in CloudStack + +From the WEB UI, go to Infrastructure -> Primary Storage -> Add Primary Storage + +Scope: select Zone-Wide +Hypervisor: select KVM +Zone: pick appropriate zone. +Name: user specified name + +Protocol: select *SharedMountPoint* +Path: enter */dev/storpool* (required argument, actually not needed in practice). + +Provider: select *StorPool* +Managed: leave unchecked (currently ignored) +Capacity Bytes: used for accounting purposes only. May be more or less than the actual StorPool template capacity. +Capacity IOPS: currently not used (may use for max IOPS limitations on volumes from this pool). +URL: enter SP_API_HTTP=address:port;SP_AUTH_TOKEN=token;SP_TEMPLATE=template_name. At present one template can be used for at most one Storage Pool. + +SP_API_HTTP - address of StorPool Api +SP_AUTH_TOKEN - StorPool's token +SP_TEMPLATE - name of StorPool's template + +Storage Tags: If left blank, the StorPool storage plugin will use the pool name to create a corresponding storage tag. +This storage tag may be used later, when defining service or disk offerings. + + +## Plugin Functionality + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Plugin ActionCloudStack Actionmanagement/agentimpl. details
Create ROOT volume from ISOcreate VM from ISOmanagementcreateVolumeAsync
Create ROOT volume from Templatecreate VM from Templatemanagement + agentcopyAsync (T => T, T => V)
Create DATA volumecreate VolumemanagementcreateVolumeAsync
Attach ROOT/DATA volumestart VM (+attach/detach Volume)agentconnectPhysicalDisk
Detach ROOT/DATA volumestop VMagentdisconnectPhysicalDiskByPath
 Migrate VMagentattach + detach
Delete ROOT volumedestroy VM (expunge)managementdeleteAsync
Delete DATA volumedelete Volume (detached)managementdeleteAsync
Create ROOT/DATA volume snapshotsnapshot volumemanagement + agenttakeSnapshot + copyAsync (S => S)
Create volume from snapshootcreate volume from snapshotmanagement + agent(?)copyAsync (S => V)
Create TEMPLATE from ROOT volumecreate template from volumemanagement + agentcopyAsync (V => T)
Create TEMPLATE from snapshotcreate template from snapshotSECONDARY STORAGE 
Download volumedownload volumemanagement + agentcopyAsync (V => V)
Revert ROOT/DATA volume to snapshotrevert to snapshotmanagementrevertSnapshot
(Live) resize ROOT/DATA volumeresize volumemanagement + agentresize + StorpoolResizeCmd
Delete SNAPSHOT (ROOT/DATA)delete snapshotmanagementStorpoolSnapshotStrategy
Delete TEMPLATEdelete templateagentdeletePhysicalDisk
migrate VM/volumemigrate VM/volume to another storagemanagement/management + agentcopyAsync (V => V)
VM snapshotgroup snapshot of VM's disksmanagementStorpoolVMSnapshotStrategy takeVMSnapshot
revert VM snapshotrevert group snapshot of VM's disksmanagementStorpoolVMSnapshotStrategy revertVMSnapshot
delete VM snapshotdelete group snapshot of VM's disksmanagementStorpoolVMSnapshotStrategy deleteVMSnapshot
VM vc_policy tagvc_policy tag for all disks attached to VMmanagementStorPoolCreateTagsCmd
delete VM vc_policy tagremove vc_policy tag for all disks attached to VMmanagementStorPoolDeleteTagsCmd
+ +>NOTE: When using multicluster for each CloudStack cluster in its settings set the value of StorPool's SP_CLUSTER_ID in "sp.cluster.id". +> + +>NOTE: Secondary storage could be bypassed with Configuration setting "sp.bypass.secondary.storage" set to true.
+In this case only snapshots won't be downloaded to secondary storage. +> + +### Creating template from snapshot + +#### If bypass option is enabled + +The snapshot exists only on PRIMARY (StorPool) storage. From this snapshot it will be created a template on SECONADRY. + +#### If bypass option is disabled + +TODO: Maybe we should not use CloudStack functionality, and to use that one when bypass option is enabled + +This is independent of StorPool as snapshots exist on secondary. + +### Creating ROOT volume from templates + +When creating the first volume based on the given template, if snapshot of the template does not exists on StorPool it will be first downloaded (cached) to PRIMARY storage. +This is mapped to a StorPool snapshot so, creating succecutive volumes from the same template does not incur additional +copying of data to PRIMARY storage. + +This cached snapshot is garbage collected when the original template is deleted from CloudStack. This cleanup is done +by a background task in CloudStack. + +### Creating a ROOT volume from an ISO image + +We just need to create the volume. The ISO installation is handled by CloudStack. + +### Creating a DATA volume + +DATA volumes are created by CloudStack the first time it is attached to a VM. + +### Creating volume from snapshot + +We use the fact that the snapshot already exists on PRIMARY, so no data is copied. We will copy snapshots from SECONDARY to StorPool PRIMARY, +when there is no corresponding StorPool snapshot. + +### Resizing volumes + +We need to send a resize cmd to agent, where the VM the volume is attached to is running, so that +the resize is visible by the VM. + +### Creating snapshots + +The snapshot is first created on the PRIMARY storage (i.e. StorPool), then backed-up on SECONDARY storage +(tested with NFS secondary) if bypass option is not enabled. The original StorPool snapshot is kept, so that creating volumes from the snapshot does not need to copy +the data again to PRIMARY. When the snapshot is deleted from CloudStack so is the corresponding StorPool snapshot. + +Currently snapshots are taken in RAW format. + +### Reverting volume to snapshot + +It's handled by StorPool + +### Migrating volumes to other Storage pools + +Tested with storage pools on NFS only. + +### Virtual Machine Snapshot/Group Snapshot + +StorPool supports consistent snapshots of volumes attached to a virtual machine. + +### BW/IOPS limitations + +Max IOPS are kept in StorPool's volumes with the help of custom service offerings, by adding IOPS limits to the +corresponding system disk offering. + +CloudStack has no way to specify max BW. Do they want to be able to specify max BW only is sufficient. diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml new file mode 100644 index 000000000000..856a11b01dea --- /dev/null +++ b/plugins/storage/volume/storpool/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + cloud-plugin-storage-volume-storpool + Apache CloudStack Plugin - Storage Volume StorPool provider + + org.apache.cloudstack + cloudstack-plugins + 4.17.0.0-SNAPSHOT + ../../../pom.xml + + + + org.apache.cloudstack + cloud-engine-storage-volume + ${project.version} + + + org.apache.cloudstack + cloud-engine-storage-snapshot + ${project.version} + + + org.apache.cloudstack + cloud-plugin-hypervisor-kvm + ${project.version} + + + org.apache.cloudstack + cloud-engine-orchestration + ${project.version} + + + + + + maven-surefire-plugin + + true + + + + integration-test + + test + + + + + + + diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolMigrateWithVolumesCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolMigrateWithVolumesCommand.java new file mode 100644 index 000000000000..d002267a486e --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolMigrateWithVolumesCommand.java @@ -0,0 +1,55 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.to.VirtualMachineTO; + +public class StorPoolMigrateWithVolumesCommand extends MigrateCommand { + private List migrateDiskInfoList = new ArrayList<>(); + + public StorPoolMigrateWithVolumesCommand() { + super(); + } + + public StorPoolMigrateWithVolumesCommand(String vmName, String destIp, boolean isWindows, VirtualMachineTO vmTO, + boolean executeInSequence) { + super(vmName, destIp, isWindows, vmTO, executeInSequence); + } + + public List getMigrateDiskInfoList() { + return migrateDiskInfoList; + } + + public void setMigrateDiskInfoList(List migrateDiskInfoList) { + this.migrateDiskInfoList = migrateDiskInfoList; + } + + public boolean isMigrateStorageManaged() { + return true; + } + + public boolean isMigrateNonSharedInc() { + return false; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java new file mode 100644 index 000000000000..c27f3f813894 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolAnswer.java @@ -0,0 +1,94 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.storage.template.TemplateProp; + +public class StorPoolModifyStoragePoolAnswer extends Answer{ + private StoragePoolInfo poolInfo; + private Map templateInfo; + private String localDatastoreName; + private String poolType; + private List datastoreClusterChildren = new ArrayList<>(); + private String clusterId; + + public StorPoolModifyStoragePoolAnswer(StorPoolModifyStoragePoolCommand cmd, long capacityBytes, long availableBytes, Map tInfo, String clusterId) { + super(cmd); + result = true; + poolInfo = new StoragePoolInfo(null, cmd.getPool().getHost(), cmd.getPool().getPath(), cmd.getLocalPath(), cmd.getPool().getType(), capacityBytes, availableBytes); + templateInfo = tInfo; + this.clusterId = clusterId; + } + + public StorPoolModifyStoragePoolAnswer(String errMsg) { + super(null, false, errMsg); + } + + public void setPoolInfo(StoragePoolInfo poolInfo) { + this.poolInfo = poolInfo; + } + + public StoragePoolInfo getPoolInfo() { + return poolInfo; + } + + public void setTemplateInfo(Map templateInfo) { + this.templateInfo = templateInfo; + } + + public Map getTemplateInfo() { + return templateInfo; + } + + public void setLocalDatastoreName(String localDatastoreName) { + this.localDatastoreName = localDatastoreName; + } + + public String getLocalDatastoreName() { + return localDatastoreName; + } + + public String getPoolType() { + return poolType; + } + + public void setPoolType(String poolType) { + this.poolType = poolType; + } + + public List getDatastoreClusterChildren() { + return datastoreClusterChildren; + } + + public void setDatastoreClusterChildren(List datastoreClusterChildren) { + this.datastoreClusterChildren = datastoreClusterChildren; + } + + public String getClusterId() { + return clusterId; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java new file mode 100644 index 000000000000..abc89266e8c5 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java @@ -0,0 +1,36 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import com.cloud.agent.api.ModifyStoragePoolCommand; +import com.cloud.storage.StoragePool; + +public class StorPoolModifyStoragePoolCommand extends ModifyStoragePoolCommand { + private String volName; + + public StorPoolModifyStoragePoolCommand(boolean add, StoragePool pool, String volumeName) { + super(add, pool); + this.volName = volumeName; + } + + public String getVolume() { + return volName; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java new file mode 100644 index 000000000000..8e5d65752c96 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java @@ -0,0 +1,30 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolBackupSnapshotCommand extends StorpoolCopyCommand { + public StorpoolBackupSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + super(srcTO, dstTO, timeout, executeInSequence); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java new file mode 100644 index 000000000000..7d0569f3622d --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java @@ -0,0 +1,30 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import org.apache.cloudstack.storage.to.TemplateObjectTO; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolBackupTemplateFromSnapshotCommand extends StorpoolCopyCommand { + public StorpoolBackupTemplateFromSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + super(srcTO, dstTO, timeout, executeInSequence); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java new file mode 100644 index 000000000000..aa2ccdac8b82 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java @@ -0,0 +1,60 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolCopyCommand extends StorageSubSystemCommand { + private S srcTO; + private D dstTO; + private boolean executeInSequence = false; + + public StorpoolCopyCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + super(); + this.srcTO = (S)srcTO; + this.dstTO = (D)dstTO; + setWait(timeout); + this.executeInSequence = executeInSequence; + } + + public S getSrcTO() { + return srcTO; + } + + public D getDstTO() { + return dstTO; + } + + public int getWaitInMillSeconds() { + return getWait() * 1000; + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + @Override + public void setExecuteInSequence(final boolean inSeq) { + executeInSequence = inSeq; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java new file mode 100644 index 000000000000..7bbda38419fd --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java @@ -0,0 +1,30 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolCopyVolumeToSecondaryCommand extends StorpoolCopyCommand { + public StorpoolCopyVolumeToSecondaryCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + super(srcTO, dstTO, timeout, executeInSequence); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java new file mode 100644 index 000000000000..e1774b1b9dfb --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java @@ -0,0 +1,37 @@ +// +// 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 +// with 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 com.cloud.agent.api.storage; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolDownloadTemplateCommand extends StorpoolCopyCommand { + protected String objectType; + public StorpoolDownloadTemplateCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence, String objectType) { + super(srcTO, dstTO, timeout, executeInSequence); + this.objectType = objectType; + } + + public String getObjectType() { + return objectType; + } + public void setObjectType(String objectType) { + this.objectType = objectType; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java new file mode 100644 index 000000000000..9a668e76912e --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java @@ -0,0 +1,30 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import com.cloud.agent.api.to.DataTO; + +public class StorpoolDownloadVolumeCommand extends StorpoolCopyCommand { + public StorpoolDownloadVolumeCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + super(srcTO, dstTO, timeout, executeInSequence); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java new file mode 100644 index 000000000000..d4c06b8b95be --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java @@ -0,0 +1,39 @@ +/* + * 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 + * with 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 com.cloud.agent.api.storage; + +import com.cloud.agent.api.to.StorageFilerTO; + + +public class StorpoolResizeVolumeCommand extends ResizeVolumeCommand { + protected boolean isAttached; + protected StorpoolResizeVolumeCommand() { + super(); + } + + public StorpoolResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance, boolean isAttached) { + super(path, pool, currentSize, newSize, shrinkOk, vmInstance); + this.isAttached = isAttached; + } + + public boolean isAttached() { + return isAttached; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java new file mode 100644 index 000000000000..7a92b27a852b --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java @@ -0,0 +1,144 @@ +/* + * 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 + * with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.storage.StorPoolModifyStoragePoolAnswer; +import com.cloud.agent.api.storage.StorPoolModifyStoragePoolCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.storage.template.TemplateProp; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +@ResourceWrapper(handles = StorPoolModifyStoragePoolCommand.class) +public final class StorPoolModifyStorageCommandWrapper extends CommandWrapper { + private static final Logger log = Logger.getLogger(StorPoolModifyStorageCommandWrapper.class); + + @Override + public Answer execute(final StorPoolModifyStoragePoolCommand command, final LibvirtComputingResource libvirtComputingResource) { + String clusterId = getSpClusterId(); + if (clusterId == null) { + return new Answer(command, false, "spNotFound"); + } + try { + String volume = attachOrDetachVolume("attach", "volume", command.getVolume()); + if (volume != null) { + return new Answer(command, false, volume); + } else { + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + final KVMStoragePool storagepool = + storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() + .getUserInfo(), command.getPool().getType()); + if (storagepool == null) { + return new Answer(command, false, " Failed to create storage pool"); + } + + final Map tInfo = new HashMap(); + final StorPoolModifyStoragePoolAnswer answer = new StorPoolModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, clusterId); + + return answer; + } + } catch (Exception e) { + return new Answer(command, false, e.getMessage()); + } + } + + private String getSpClusterId() { + Script sc = new Script("storpool_confget", 0, log); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String SP_CLUSTER_ID = null; + final String err = sc.execute(parser); + if (err != null) { + final String errMsg = String.format("Could not execute storpool_confget. Error: %s", err); + log.warn(errMsg); + StorpoolStorageAdaptor.SP_LOG("Could not execute storpool_confget. Error: %s", err); + return SP_CLUSTER_ID; + } + + for (String line: parser.getLines().split("\n")) { + String[] toks = line.split("="); + if( toks.length != 2 ) { + continue; + } + if (toks[0].equals("SP_CLUSTER_ID")) { + SP_CLUSTER_ID = toks[1]; + return SP_CLUSTER_ID; + } + } + return SP_CLUSTER_ID; + } + + public String attachOrDetachVolume(String command, String type, String volumeUuid) { + final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeUuid, true); + if (name == null) { + return null; + } + + String err = null; + Script sc = new Script("storpool", 300000, log); + sc.add("-M"); + sc.add("-j"); + sc.add(command); + sc.add(type, name); + sc.add("here"); + sc.add("onRemoteAttached"); + sc.add("export"); + + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + String res = sc.execute(parser); + + if (res != null) { + if (!res.equals(Script.ERR_TIMEOUT)) { + try { + Set> obj2 = new JsonParser().parse(res).getAsJsonObject().entrySet(); + for (Entry entry : obj2) { + if (entry.getKey().equals("error")) { + res = entry.getValue().getAsJsonObject().get("name").getAsString(); + } + } + } catch (Exception e) { + } + } + + err = String.format("Unable to %s volume %s. Error: %s", command, name, res); + } + + if (err != null) { + log.warn(err); + } + return res; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java new file mode 100644 index 000000000000..5377b1c00522 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java @@ -0,0 +1,109 @@ +// +// 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 +// with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; + +import java.io.File; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.StorpoolBackupSnapshotCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = StorpoolBackupSnapshotCommand.class) +public final class StorpoolBackupSnapshotCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolBackupSnapshotCommandWrapper.class); + + @Override + public CopyCmdAnswer execute(final StorpoolBackupSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + String srcPath = null; + KVMStoragePool secondaryPool = null; + + try { + final SnapshotObjectTO src = cmd.getSrcTO(); + final SnapshotObjectTO dst = cmd.getDstTO(); + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + + SP_LOG("StorpoolBackupSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); + StorpoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); + srcPath = src.getPath(); + + final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); + + final DataStoreTO dstDataStore = dst.getDataStore(); + if (!(dstDataStore instanceof NfsTO)) { + return new CopyCmdAnswer("Backup Storpool snapshot: Only NFS secondary supported at present!"); + } + + secondaryPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl()); + + final String dstDir = secondaryPool.getLocalPath() + File.separator + dst.getPath(); + FileUtils.forceMkdir(new File(dstDir)); + + final String dstPath = dstDir + File.separator + dst.getName(); + final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.QCOW2); + + final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); + qemu.convert(srcFile, dstFile); + + SP_LOG("StorpoolBackupSnapshotCommandWrapper srcFileFormat=%s, dstFileFormat=%s", srcFile.getFormat(), dstFile.getFormat()); + final File snapFile = new File(dstPath); + final long size = snapFile.exists() ? snapFile.length() : 0; + + final SnapshotObjectTO snapshot = new SnapshotObjectTO(); + snapshot.setPath(dst.getPath() + File.separator + dst.getName()); + snapshot.setPhysicalSize(size); + + return new CopyCmdAnswer(snapshot); + } catch (final Exception e) { + final String error = "failed to backup snapshot: " + e.getMessage(); + SP_LOG(error); + s_logger.debug(error); + return new CopyCmdAnswer(error); + } finally { + if (srcPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); + } + + if (secondaryPool != null) { + try { + secondaryPool.delete(); + } catch (final Exception e) { + s_logger.debug("Failed to delete secondary storage", e); + } + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java new file mode 100644 index 000000000000..a37c16f2e240 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java @@ -0,0 +1,161 @@ +/* + * 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 + * with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +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.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StorageLayer; +import com.cloud.storage.template.Processor; +import com.cloud.storage.template.Processor.FormatInfo; +import com.cloud.storage.template.QCOW2Processor; +import com.cloud.storage.template.TemplateLocation; +import com.cloud.storage.template.TemplateProp; + +@ResourceWrapper(handles = StorpoolBackupTemplateFromSnapshotCommand.class) +public class StorpoolBackupTemplateFromSnapshotCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolBackupTemplateFromSnapshotCommandWrapper.class); + + @Override + public CopyCmdAnswer execute(final StorpoolBackupTemplateFromSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + String srcPath = null; + KVMStoragePool secondaryPool = null; + String objectType = cmd.getSrcTO().getObjectType().toString().toLowerCase(); + + try { + final DataTO src = cmd.getSrcTO(); + final TemplateObjectTO dst = cmd.getDstTO(); + String name = null; + String volumeFormatExtension = null; + + if (src instanceof SnapshotObjectTO) { + name = ((SnapshotObjectTO) src).getName(); + volumeFormatExtension = ((SnapshotObjectTO) src).getVolume().getFormat().getFileExtension(); + } else if (src instanceof VolumeObjectTO) { + name = ((VolumeObjectTO) src).getName(); + volumeFormatExtension = ((VolumeObjectTO) src).getFormat().getFileExtension(); + } else { + return new CopyCmdAnswer("Backup of a template is not supported for data object: " + src.getObjectType() ); + } + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + StorageLayer storage = libvirtComputingResource.getStorage(); + Processor processor = new QCOW2Processor(); + String _tmpltpp = "template.properties"; + + SP_LOG("StorpoolBackupTemplateFromSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); + StorpoolStorageAdaptor.attachOrDetachVolume("attach", objectType, src.getPath()); + srcPath = src.getPath(); + + final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); + + final DataStoreTO dstDataStore = dst.getDataStore(); + if (!(dstDataStore instanceof NfsTO)) { + return new CopyCmdAnswer("Backup Storpool snapshot: Only NFS secondary supported at present!"); + } + + secondaryPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl()); + + final String dstDir = secondaryPool.getLocalPath() + File.separator + dst.getPath(); + FileUtils.forceMkdir(new File(dstDir)); + + String nameWithExtension = name + "." + volumeFormatExtension; + + final String dstPath = dstDir + File.separator + nameWithExtension; + final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.QCOW2); + + final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); + qemu.convert(srcFile, dstFile); + + storage.create(dstDir, _tmpltpp); + String metaFileName = dstDir + File.separator + _tmpltpp; + File metaFile = new File(metaFileName); + + try ( FileWriter writer = new FileWriter(metaFile); + BufferedWriter bufferWriter = new BufferedWriter(writer);) { + bufferWriter.write("uniquename=" + dst.getName()); + bufferWriter.write("\n"); + bufferWriter.write("filename=" + nameWithExtension); + } + Map params = new HashMap(); + params.put(StorageLayer.InstanceConfigKey, storage); + + processor.configure("template processor", params); + + FormatInfo info = processor.process(dstDir, null, name); + TemplateLocation loc = new TemplateLocation(storage, dstDir); + loc.create(1, true, dst.getName()); + loc.addFormat(info); + loc.save(); + + TemplateProp prop = loc.getTemplateInfo(); + final TemplateObjectTO template = new TemplateObjectTO(); + template.setPath(dst.getPath() + File.separator + nameWithExtension); + template.setFormat(ImageFormat.QCOW2); + template.setSize(prop.getSize()); + template.setPhysicalSize(prop.getPhysicalSize()); + + return new CopyCmdAnswer(template); + } catch (final Exception e) { + final String error = "failed to backup snapshot: " + e.getMessage(); + SP_LOG(error); + s_logger.debug(error); + return new CopyCmdAnswer(error); + } finally { + if (srcPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", objectType, srcPath); + } + + if (secondaryPool != null) { + try { + secondaryPool.delete(); + } catch (final Exception e) { + s_logger.debug("Failed to delete secondary storage", e); + } + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java new file mode 100644 index 000000000000..0f7ea00325f5 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java @@ -0,0 +1,125 @@ +// +// 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 +// with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; + +import java.io.File; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +//import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.StorpoolCopyVolumeToSecondaryCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = StorpoolCopyVolumeToSecondaryCommand.class) +public final class StorpoolCopyVolumeToSecondaryCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolCopyVolumeToSecondaryCommandWrapper.class); + + @Override + public CopyCmdAnswer execute(final StorpoolCopyVolumeToSecondaryCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + String srcPath = null; + KVMStoragePool secondaryPool = null; + + try { + final VolumeObjectTO src = cmd.getSrcTO(); + final VolumeObjectTO dst = cmd.getDstTO(); + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + final String destVolumePath = dst.getPath(); + + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); + + StorpoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); + srcPath = src.getPath(); + + final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); + + final DataStoreTO dstDataStore = dst.getDataStore(); + + final KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr(); + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: KVMStoragePoolManager " + poolMgr); + KVMStoragePool destPool; + if( dstDataStore instanceof NfsTO ) { + destPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl()); + destPool.createFolder(destVolumePath); + storagePoolMgr.deleteStoragePool(destPool.getType(), destPool.getUuid()); + destPool = storagePoolMgr.getStoragePoolByURI(dstDataStore.getUrl() + File.separator + destVolumePath); + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: Nfs destPool=%s ",destPool); + } else if( dstDataStore instanceof PrimaryDataStoreTO ) { + PrimaryDataStoreTO primaryDst = (PrimaryDataStoreTO)dstDataStore; + destPool = poolMgr.getStoragePool(primaryDst.getPoolType(), dstDataStore.getUuid()); + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: not Nfs destPool=%s " ,destPool); + } else { + return new CopyCmdAnswer("Don't know how to copy to " + dstDataStore.getClass().getName() + ", " + dst.getPath() ); + } + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: dstName=%s, dstProvisioningType=%s, srcSize=%s, dstUUID=%s, srcUUID=%s " ,dst.getName(), dst.getProvisioningType(), src.getSize(),dst.getUuid(), src.getUuid()); + + KVMPhysicalDisk newDisk = destPool.createPhysicalDisk(dst.getUuid(), dst.getProvisioningType(), src.getSize()); + SP_LOG("NewDisk path=%s, uuid=%s ", newDisk.getPath(), dst.getUuid()); + String destPath = newDisk.getPath(); + newDisk.setPath(dst.getUuid()); + + PhysicalDiskFormat destFormat = newDisk.getFormat(); + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: KVMPhysicalDisk name=%s, format=%s, path=%s, destinationPath=%s " , newDisk.getName(), newDisk.getFormat(), newDisk.getPath(), destPath); + QemuImgFile destFile = new QemuImgFile(destPath, destFormat); + QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); + qemu.convert(srcFile, destFile); + + final File file = new File(destPath); + final long size = file.exists() ? file.length() : 0; + dst.setPath(destVolumePath + File.separator + dst.getUuid()); + dst.setSize(size); + + return new CopyCmdAnswer(dst); + } catch (final Exception e) { + final String error = "Failed to copy volume to secondary storage: " + e.getMessage(); + s_logger.debug(error); + return new CopyCmdAnswer(error); + } finally { + if (srcPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); + } + + if (secondaryPool != null) { + try { + SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: secondaryPool=%s " , secondaryPool); + secondaryPool.delete(); + } catch (final Exception e) { + s_logger.debug("Failed to delete secondary storage", e); + } + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java new file mode 100644 index 000000000000..89bc4a55e1dd --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java @@ -0,0 +1,133 @@ +// +// 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 +// with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; + +import java.io.File; +import java.util.List; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.StorpoolDownloadTemplateCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; + +@ResourceWrapper(handles = StorpoolDownloadTemplateCommand.class) +public final class StorpoolDownloadTemplateCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolDownloadTemplateCommandWrapper.class); + + @Override + public CopyCmdAnswer execute(final StorpoolDownloadTemplateCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + String dstPath = null; + KVMStoragePool secondaryPool = null; + DataTO src = cmd.getSrcTO(); + DataTO dst = cmd.getDstTO(); + + try { + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + SP_LOG("StorpoolDownloadTemplateCommandWrapper.execute: src=" + src.getPath() + " dst=" + dst.getPath()); + + final DataStoreTO srcDataStore = src.getDataStore(); + if (!(srcDataStore instanceof NfsTO)) { + return new CopyCmdAnswer("Download template to Storpool: Only NFS secondary supported at present!"); + } + + final NfsTO nfsImageStore = (NfsTO)srcDataStore; + final String tmplturl = nfsImageStore.getUrl() + File.separator + src.getPath(); + final int index = tmplturl.lastIndexOf("/"); + final String mountpoint = tmplturl.substring(0, index); + String tmpltname = null; + if (index < tmplturl.length() - 1) { + tmpltname = tmplturl.substring(index + 1); + } + + secondaryPool = storagePoolMgr.getStoragePoolByURI(mountpoint); + + KVMPhysicalDisk srcDisk = null; + + if (tmpltname == null) { + secondaryPool.refresh(); + final List disks = secondaryPool.listPhysicalDisks(); + if (disks == null || disks.isEmpty()) { + SP_LOG("Failed to get volumes from pool: " + secondaryPool.getUuid()); + return new CopyCmdAnswer("Failed to get volumes from pool: " + secondaryPool.getUuid()); + } + for (final KVMPhysicalDisk disk : disks) { + if (disk.getName().endsWith("qcow2")) { + srcDisk = disk; + break; + } + } + } else { + srcDisk = secondaryPool.getPhysicalDisk(tmpltname); + } + + if (srcDisk == null) { + SP_LOG("Failed to get template from pool: " + secondaryPool.getUuid()); + return new CopyCmdAnswer("Failed to get template from pool: " + secondaryPool.getUuid()); + } + + SP_LOG("got src path: " + srcDisk.getPath() + " srcSize " + srcDisk.getVirtualSize()); + + final QemuImgFile srcFile = new QemuImgFile(srcDisk.getPath(), srcDisk.getFormat()); + + final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); + StorpoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); + + dstPath = dst.getPath(); + StorpoolStorageAdaptor.attachOrDetachVolume("attach", cmd.getObjectType(), dstPath); + + final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.RAW); + + qemu.convert(srcFile, dstFile); + return new CopyCmdAnswer(dst); + } catch (final Exception e) { + final String error = "Failed to copy template to primary: " + e.getMessage(); + s_logger.debug(error); + return new CopyCmdAnswer(error); + } finally { + if (dstPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", cmd.getObjectType(), dstPath); + } + + if (secondaryPool != null) { + try { + secondaryPool.delete(); + } catch (final Exception e) { + s_logger.debug("Failed to delete secondary storage", e); + } + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java new file mode 100644 index 000000000000..538fe6769e48 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java @@ -0,0 +1,162 @@ +// +// 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 +// with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; + +import java.util.List; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +//import java.io.File; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.StorpoolDownloadVolumeCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.StoragePoolType; + +@ResourceWrapper(handles = StorpoolDownloadVolumeCommand.class) +public final class StorpoolDownloadVolumeCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolDownloadVolumeCommandWrapper.class); + + @Override + public CopyCmdAnswer execute(final StorpoolDownloadVolumeCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + String dstPath = null; + KVMStoragePool secondaryPool = null; + + try { + final VolumeObjectTO src = cmd.getSrcTO(); + final VolumeObjectTO dst = cmd.getDstTO(); + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + SP_LOG("StorpoolDownloadVolumeCommandWrapper.execute: src=" + src.getPath() + " srcName=" + src.getName() + " dst=" + dst.getPath()); + + final DataStoreTO srcDataStore = src.getDataStore(); + KVMPhysicalDisk srcDisk = null; + + if(srcDataStore instanceof NfsTO) { + SP_LOG("StorpoolDownloadVolumeCommandWrapper.execute: srcIsNfsTO"); + + final String tmplturl = srcDataStore.getUrl() + srcDataStore.getPathSeparator() + src.getPath(); + final int index = tmplturl.lastIndexOf("/"); + final String mountpoint = tmplturl.substring(0, index); + String tmpltname = null; + if (index < tmplturl.length() - 1) { + tmpltname = tmplturl.substring(index + 1); + } + + secondaryPool = storagePoolMgr.getStoragePoolByURI(mountpoint); + + if (tmpltname == null) { + secondaryPool.refresh(); + final List disks = secondaryPool.listPhysicalDisks(); + if (disks == null || disks.isEmpty()) { + SP_LOG("Failed to get volumes from pool: " + secondaryPool.getUuid()); + return new CopyCmdAnswer("Failed to get volumes from pool: " + secondaryPool.getUuid()); + } + for (final KVMPhysicalDisk disk : disks) { + if (disk.getName().endsWith("qcow2")) { + srcDisk = disk; + break; + } + } + } else { + srcDisk = secondaryPool.getPhysicalDisk(tmpltname); + } + } else if(srcDataStore instanceof PrimaryDataStoreTO) { + SP_LOG("SrcDisk is Primary Storage"); + PrimaryDataStoreTO primarySrc = (PrimaryDataStoreTO)srcDataStore; + SP_LOG("StorpoolDownloadVolumeCommandWrapper.execute primarySrcPoolType=%s, uuid-%s ", primarySrc.getPoolType(), primarySrc.getUuid()); + final KVMStoragePoolManager poolMgr = libvirtComputingResource.getStoragePoolMgr(); + srcDisk = poolMgr.getPhysicalDisk(primarySrc.getPoolType(), srcDataStore.getUuid(), src.getPath()); + SP_LOG("PhysicalDisk: disk=%s", srcDisk ); + } else { + return new CopyCmdAnswer("Don't know how to copy from " + srcDataStore.getClass().getName() + ", " + src.getPath() ); + } + + if (srcDisk == null) { + SP_LOG("Failed to get src volume"); + return new CopyCmdAnswer("Failed to get src volume"); + } + + SP_LOG("got src path: " + srcDisk.getPath() + " srcSize " + srcDisk.getVirtualSize()); + + String srcPath = null; + boolean isRBDPool = srcDisk.getPool().getType() == StoragePoolType.RBD; + if (isRBDPool) { + KVMStoragePool srcPool = srcDisk.getPool(); + String rbdDestPath = srcPool.getSourceDir() + "/" + srcDisk.getName(); + srcPath = KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), + srcPool.getSourcePort(), + srcPool.getAuthUserName(), + srcPool.getAuthSecret(), + rbdDestPath); + } else { + srcPath = srcDisk.getPath(); + } + final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); + + final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); + StorpoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); + + dstPath = dst.getPath(); + StorpoolStorageAdaptor.attachOrDetachVolume("attach", "volume", dstPath); + + final QemuImgFile dstFile = new QemuImgFile(dstPath, srcFile.getFormat()); + SP_LOG("SRC format=%s, DST format=%s",srcFile.getFormat(), dstFile.getFormat()); + qemu.convert(srcFile, dstFile); + SP_LOG("StorpoolDownloadVolumeCommandWrapper VolumeObjectTO format=%s, hypervisor=%s", dst.getFormat(), dst.getHypervisorType()); + if (isRBDPool) { + dst.setFormat(ImageFormat.QCOW2); + } + return new CopyCmdAnswer(dst); + } catch (final Exception e) { + final String error = "Failed to copy volume to primary: " + e.getMessage(); + SP_LOG(error); + s_logger.debug(error); + return new CopyCmdAnswer(error); + } finally { + if (dstPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", "volume", dstPath); + } + + if (secondaryPool != null) { + try { + secondaryPool.delete(); + } catch (final Exception e) { + s_logger.debug("Failed to delete secondary storage", e); + } + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java new file mode 100644 index 000000000000..e30fb184ec96 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java @@ -0,0 +1,98 @@ +// +// 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 +// with 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.storage.ResizeVolumeAnswer; +import com.cloud.agent.api.storage.StorpoolResizeVolumeCommand; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; +import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.script.Script; + + +@ResourceWrapper(handles = StorpoolResizeVolumeCommand.class) +public final class StorpoolResizeVolumeCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(StorpoolResizeVolumeCommandWrapper.class); + + @Override + public ResizeVolumeAnswer execute(final StorpoolResizeVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { + final String volid = command.getPath(); + final long newSize = command.getNewSize(); + final long currentSize = command.getCurrentSize(); + final String vmInstanceName = command.getInstanceName(); + final boolean shrinkOk = command.getShrinkOk(); + final StorageFilerTO spool = command.getPool(); + String volPath = null; + + if (currentSize == newSize) { + // nothing to do + s_logger.info("No need to resize volume: current size " + currentSize + " is same as new size " + newSize); + return new ResizeVolumeAnswer(command, true, "success", currentSize); + } + + try { + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + KVMStoragePool pool = storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid()); + + final KVMPhysicalDisk vol = pool.getPhysicalDisk(volid); + final String path = vol.getPath(); + volPath = path; + if (!command.isAttached()) { + StorpoolStorageAdaptor.attachOrDetachVolume("attach", "volume", path); + } + 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", "NOTIFYONLY"); + resizecmd.add("-r", String.valueOf(shrinkOk)); + resizecmd.add("-v", vmInstanceName); + final String result = resizecmd.execute(); + + if (result != null) { + return new ResizeVolumeAnswer(command, true, "Resize succeeded, but need reboot to notify guest"); + } + + /* 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(); + s_logger.debug("after resize, size reports as " + finalSize + ", requested " + newSize); + return new ResizeVolumeAnswer(command, true, "success", finalSize); + } catch (final Exception e) { + final String error = "Failed to resize volume: " + e.getMessage(); + s_logger.debug(error); + return new ResizeVolumeAnswer(command, false, error); + } finally { + if (!command.isAttached() && volPath != null) { + StorpoolStorageAdaptor.attachOrDetachVolume("detach", "volume", volPath); + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java new file mode 100644 index 000000000000..c9f20ed1d624 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java @@ -0,0 +1,384 @@ +// 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 +// with 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 com.cloud.hypervisor.kvm.storage; + + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.to.DiskTO; +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.ProvisioningType; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; + +@StorageAdaptorInfo(storagePoolType=StoragePoolType.SharedMountPoint) +public class StorpoolStorageAdaptor implements StorageAdaptor { + public static void SP_LOG(String fmt, Object... args) { + try (PrintWriter spLogFile = new PrintWriter(new BufferedWriter(new FileWriter("/var/log/cloudstack/agent/storpool-agent.log", true)))) { + final String line = String.format(fmt, args); + String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,ms").format(Calendar.getInstance().getTime()); + spLogFile.println(timeStamp +" "+line); + spLogFile.flush(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static final Logger log = Logger.getLogger(StorpoolStorageAdaptor.class); + + private static final Map storageUuidToStoragePool = new HashMap(); + + @Override + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType) { + SP_LOG("StorpooolStorageAdaptor.createStoragePool: uuid=%s, host=%s:%d, path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, storagePoolType); + + StorpoolStoragePool storagePool = new StorpoolStoragePool(uuid, host, port, storagePoolType, this); + storageUuidToStoragePool.put(uuid, storagePool); + return storagePool; + } + + @Override + public KVMStoragePool getStoragePool(String uuid) { + SP_LOG("StorpooolStorageAdaptor.getStoragePool: uuid=%s", uuid); + return storageUuidToStoragePool.get(uuid); + } + + @Override + public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) { + SP_LOG("StorpooolStorageAdaptor.getStoragePool: uuid=%s, refresh=%s", uuid, refreshInfo); + return storageUuidToStoragePool.get(uuid); + } + + @Override + public boolean deleteStoragePool(String uuid) { + SP_LOG("StorpooolStorageAdaptor.deleteStoragePool: uuid=%s", uuid); + return storageUuidToStoragePool.remove(uuid) != null; + } + + @Override + public boolean deleteStoragePool(KVMStoragePool pool) { + SP_LOG("StorpooolStorageAdaptor.deleteStoragePool: uuid=%s", pool.getUuid()); + return deleteStoragePool(pool.getUuid()); + } + + private static long getDeviceSize(final String devPath) { + SP_LOG("StorpooolStorageAdaptor.getDeviceSize: path=%s", devPath); + + if (getVolumeNameFromPath(devPath, true) != null) { + File file = new File(devPath); + if (!file.exists()) { + return 0; + } + Script sc = new Script("blockdev", 0, log); + sc.add("--getsize64", devPath); + + OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + + String res = sc.execute(parser); + if (res != null) { + SP_LOG("Unable to retrieve device size for %s. Res: %s", devPath, res); + + log.debug(String.format("Unable to retrieve device size for %s. Res: %s", devPath, res)); + return 0; + } + + return Long.parseLong(parser.getLine()); + } + return 0; + } + + private static boolean waitForDeviceSymlink(String devPath) { + final int numTries = 10; + final int sleepTime = 100; + + for(int i = 0; i < numTries; i++) { + if (getDeviceSize(devPath) != 0) { + return true; + } else { + try { + Thread.sleep(sleepTime); + } catch (Exception ex) { + // don't do anything + } + } + } + return false; + } + + public static String getVolumeNameFromPath(final String volumeUuid, boolean tildeNeeded) { + if (volumeUuid.startsWith("/dev/storpool/")) { + return volumeUuid.split("/")[3]; + }else if(volumeUuid.startsWith("/dev/storpool-byid/")) { + return tildeNeeded ? "~" + volumeUuid.split("/")[3] : volumeUuid.split("/")[3]; + } + + return null; + } + + public static boolean attachOrDetachVolume(String command, String type, String volumeUuid) { + final String name = getVolumeNameFromPath(volumeUuid, true); + if (name == null) { + return false; + } + + SP_LOG("StorpooolStorageAdaptor.attachOrDetachVolume: cmd=%s, type=%s, uuid=%s, name=%s", command, type, volumeUuid, name); + + final int numTries = 10; + final int sleepTime = 1000; + String err = null; + + for(int i = 0; i < numTries; i++) { + Script sc = new Script("storpool", 0, log); + sc.add("-M"); + sc.add(command); + sc.add(type, name); + sc.add("here"); + if( command.equals("attach")) { + sc.add("onRemoteAttached"); + sc.add("export"); + } + + OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + + String res = sc.execute(parser); + if (res != null) { + err = String.format("Unable to %s volume %s. Error: %s", command, name, res); + + if (command.equals("detach")) { + try { + Thread.sleep(sleepTime); + } catch (Exception ex) { + // don't do anything + } + } else { + break; + } + } else { + err = null; + break; + } + } + + if (err != null) { + SP_LOG(err); + log.warn(err); + throw new CloudRuntimeException(err); + } + + if (command.equals("attach")) { + return waitForDeviceSymlink(volumeUuid); + } else { + return true; + } + } + + public static boolean resize(String newSize, String volumeUuid ) { + final String name = getVolumeNameFromPath(volumeUuid, true); + if (name == null) { + return false; + } + + SP_LOG("StorpooolStorageAdaptor.resize: size=%s, uuid=%s, name=%s", newSize, volumeUuid, name); + + Script sc = new Script("storpool", 0, log); + sc.add("-M"); + sc.add("volume"); + sc.add(name); + sc.add("update"); + sc.add("size"); + sc.add(newSize); + sc.add("shrinkOk"); + + OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + String res = sc.execute(parser); + if (res != null) { + String err = String.format("Unable to resize volume %s. Error: %s", name, res); + SP_LOG(err); + log.warn(err); + throw new CloudRuntimeException(err); + } + + return true; + } + + @Override + public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) { + SP_LOG("StorpooolStorageAdaptor.getPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool); + + log.debug(String.format("getPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); + + final long deviceSize = getDeviceSize(volumeUuid); + + KVMPhysicalDisk physicalDisk = new KVMPhysicalDisk(volumeUuid, volumeUuid, pool); + physicalDisk.setFormat(PhysicalDiskFormat.RAW); + physicalDisk.setSize(deviceSize); + physicalDisk.setVirtualSize(deviceSize); + return physicalDisk; + } + + @Override + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details) { + SP_LOG("StorpooolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool); + + log.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); + + return attachOrDetachVolume("attach", "volume", volumeUuid); + } + + @Override + public boolean disconnectPhysicalDisk(String volumeUuid, KVMStoragePool pool) { + SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool); + + log.debug(String.format("disconnectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); + return attachOrDetachVolume("detach", "volume", volumeUuid); + } + + public boolean disconnectPhysicalDisk(Map volumeToDisconnect) { + String volumeUuid = volumeToDisconnect.get(DiskTO.UUID); + SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDisk: map. uuid=%s", volumeUuid); + return attachOrDetachVolume("detach", "volume", volumeUuid); + } + + @Override + public boolean disconnectPhysicalDiskByPath(String localPath) { + SP_LOG("StorpooolStorageAdaptor.disconnectPhysicalDiskByPath: localPath=%s", localPath); + + log.debug(String.format("disconnectPhysicalDiskByPath: localPath=%s", localPath)); + return attachOrDetachVolume("detach", "volume", localPath); + } + + // The following do not apply for StorpoolStorageAdaptor? + @Override + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + SP_LOG("StorpooolStorageAdaptor.createPhysicalDisk: uuid=%s, pool=%s, format=%s, size=%d", volumeUuid, pool, format, size); + throw new UnsupportedOperationException("Creating a physical disk is not supported."); + } + + @Override + public boolean deletePhysicalDisk(String volumeUuid, KVMStoragePool pool, Storage.ImageFormat format) { + // Should only come here when cleaning-up StorPool snapshots associated with CloudStack templates. + SP_LOG("StorpooolStorageAdaptor.deletePhysicalDisk: uuid=%s, pool=%s, format=%s", volumeUuid, pool, format); + final String name = getVolumeNameFromPath(volumeUuid, true); + if (name == null) { + final String err = String.format("StorpooolStorageAdaptor.deletePhysicalDisk: '%s' is not a StorPool volume?", volumeUuid); + SP_LOG(err); + throw new UnsupportedOperationException(err); + } + + Script sc = new Script("storpool", 0, log); + sc.add("-M"); + sc.add("snapshot", name); + sc.add("delete", name); + + OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + + String res = sc.execute(parser); + if (res != null) { + final String err = String.format("Unable to delete StorPool snapshot '%s'. Error: %s", name, res); + SP_LOG(err); + log.warn(err); + throw new UnsupportedOperationException(err); + } + return true; // apparently ignored + } + + @Override + public List listPhysicalDisks(String storagePoolUuid, KVMStoragePool pool) { + SP_LOG("StorpooolStorageAdaptor.listPhysicalDisks: uuid=%s, pool=%s", storagePoolUuid, pool); + throw new UnsupportedOperationException("Listing disks is not supported for this configuration."); + } + + @Override + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, + ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { + SP_LOG("StorpooolStorageAdaptor.createDiskFromTemplate: template=%s, name=%s, fmt=%s, ptype=%s, size=%d, dst_pool=%s, to=%d", + template, name, format, provisioningType, size, destPool.getUuid(), timeout); + throw new UnsupportedOperationException("Creating a disk from a template is not yet supported for this configuration."); + } + + @Override + public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { + SP_LOG("StorpooolStorageAdaptor.createTemplateFromDisk: disk=%s, name=%s, fmt=%s, size=%d, dst_pool=%s", disk, name, format, size, destPool.getUuid()); + throw new UnsupportedOperationException("Creating a template from a disk is not yet supported for this configuration."); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + SP_LOG("StorpooolStorageAdaptor.copyPhysicalDisk: disk=%s, name=%s, dst_pool=%s, to=%d", disk, name, destPool.getUuid(), timeout); + throw new UnsupportedOperationException("Copying a disk is not supported in this configuration."); + } + + public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool) { + SP_LOG("StorpooolStorageAdaptor.createDiskFromSnapshot: snap=%s, snap_name=%s, name=%s, dst_pool=%s", snapshot, snapshotName, name, destPool.getUuid()); + throw new UnsupportedOperationException("Creating a disk from a snapshot is not supported in this configuration."); + } + + @Override + public boolean refresh(KVMStoragePool pool) { + SP_LOG("StorpooolStorageAdaptor.refresh: pool=%s", pool); + return true; + } + + @Override + public boolean createFolder(String uuid, String path) { + SP_LOG("StorpooolStorageAdaptor.createFolder: uuid=%s, path=%s", uuid, path); + throw new UnsupportedOperationException("A folder cannot be created in this configuration."); + } + + public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, + KVMStoragePool destPool, int timeout) { + SP_LOG("StorpooolStorageAdaptor.createDiskFromSnapshot: snap=%s, snap_name=%s, name=%s, dst_pool=%s", snapshot, + snapshotName, name, destPool.getUuid()); + throw new UnsupportedOperationException( + "Creating a disk from a snapshot is not supported in this configuration."); + } + + public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, + PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + SP_LOG("StorpooolStorageAdaptor.createDiskFromTemplateBacking: template=%s, name=%s, dst_pool=%s", template, + name, destPool.getUuid()); + throw new UnsupportedOperationException( + "Creating a disk from a template is not supported in this configuration."); + } + + public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, KVMStoragePool destPool, + boolean isIso) { + SP_LOG("StorpooolStorageAdaptor.createTemplateFromDirectDownloadFile: templateFilePath=%s, dst_pool=%s", + templateFilePath, destPool.getUuid()); + throw new UnsupportedOperationException( + "Creating a template from direct download is not supported in this configuration."); + } + + public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, + KVMStoragePool destPool, ImageFormat format, int timeout) { + return null; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java new file mode 100644 index 000000000000..07e4c8268e54 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java @@ -0,0 +1,164 @@ +// 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 +// with 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 com.cloud.hypervisor.kvm.storage; + +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.StoragePoolType; + +public class StorpoolStoragePool implements KVMStoragePool { + private String _uuid; + private String _sourceHost; + private int _sourcePort; + private StoragePoolType _storagePoolType; + private StorageAdaptor _storageAdaptor; + private String _authUsername; + private String _authSecret; + private String _sourceDir; + private String _localPath; + + public StorpoolStoragePool(String uuid, String host, int port, StoragePoolType storagePoolType, StorageAdaptor storageAdaptor) { + _uuid = uuid; + _sourceHost = host; + _sourcePort = port; + _storagePoolType = storagePoolType; + _storageAdaptor = storageAdaptor; + } + + @Override + public String getUuid() { + return _uuid; + } + + @Override + public String getSourceHost() { + return _sourceHost; + } + + @Override + public int getSourcePort() { + return _sourcePort; + } + + @Override + public long getCapacity() { + return 100L*(1024L*1024L*1024L*1024L*1024L); + } + + @Override + public long getUsed() { + return 0; + } + + @Override + public long getAvailable() { + return 0; + } + + @Override + public StoragePoolType getType() { + return _storagePoolType; + } + + @Override + public String getAuthUserName() { + return _authUsername; + } + + @Override + public String getAuthSecret() { + return _authSecret; + } + + @Override + public String getSourceDir() { + return _sourceDir; + } + + @Override + public String getLocalPath() { + return _localPath; + } + + @Override + public PhysicalDiskFormat getDefaultFormat() { + return PhysicalDiskFormat.RAW; + } + + @Override + public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + return _storageAdaptor.createPhysicalDisk(name, this, format, provisioningType, size); + } + + @Override + public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size) { + return _storageAdaptor.createPhysicalDisk(name, this, null, provisioningType, size); + } + + @Override + public boolean connectPhysicalDisk(String name, Map details) { + return _storageAdaptor.connectPhysicalDisk(name, this, details); + } + + @Override + public KVMPhysicalDisk getPhysicalDisk(String volumeUuid) { + return _storageAdaptor.getPhysicalDisk(volumeUuid, this); + } + + @Override + public boolean disconnectPhysicalDisk(String volumeUuid) { + return _storageAdaptor.disconnectPhysicalDisk(volumeUuid, this); + } + + @Override + public boolean deletePhysicalDisk(String volumeUuid, Storage.ImageFormat format) { + return _storageAdaptor.deletePhysicalDisk(volumeUuid, this, format); + } + + @Override + public List listPhysicalDisks() { + return _storageAdaptor.listPhysicalDisks(_uuid, this); + } + + @Override + public boolean refresh() { + return _storageAdaptor.refresh(this); + } + + @Override + public boolean delete() { + return _storageAdaptor.deleteStoragePool(this); + } + + @Override + public boolean createFolder(String path) { + return _storageAdaptor.createFolder(_uuid, path); + } + + @Override + public boolean isExternalSnapshot() { + return false; + } + + public boolean supportsConfigDriveIso() { + return false; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java new file mode 100644 index 000000000000..bf39fc685a17 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java @@ -0,0 +1,312 @@ +/* + * 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 + * with 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.storage.collector; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.log4j.Logger; + +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.concurrency.NamedThreadFactory; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.TransactionStatus; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public class StorPoolAbandonObjectsCollector extends ManagerBase implements Configurable { + private static Logger log = Logger.getLogger(StorPoolAbandonObjectsCollector.class); + @Inject + private PrimaryDataStoreDao storagePoolDao; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + + private ScheduledExecutorService _volumeTagsUpdateExecutor; + private static final String ABANDON_LOG = "/var/log/cloudstack/management/storpool-abandoned-objects"; + + + static final ConfigKey volumeCheckupTagsInterval = new ConfigKey("Advanced", Integer.class, + "storpool.volume.tags.checkup", "86400", + "Minimal interval (in seconds) to check and report if StorPool volume exists in CloudStack volumes database", + false); + static final ConfigKey snapshotCheckupTagsInterval = new ConfigKey("Advanced", Integer.class, + "storpool.snapshot.tags.checkup", "86400", + "Minimal interval (in seconds) to check and report if StorPool snapshot exists in CloudStack snapshots database", + false); + + @Override + public String getConfigComponentName() { + return StorPoolAbandonObjectsCollector.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { volumeCheckupTagsInterval, snapshotCheckupTagsInterval }; + } + + @Override + public boolean start() { + init(); + return true; + } + + private void init() { + _volumeTagsUpdateExecutor = Executors.newScheduledThreadPool(2, + new NamedThreadFactory("StorPoolAbandonObjectsCollector")); + StorPoolHelper.appendLogger(log, ABANDON_LOG, "abandon"); + if (volumeCheckupTagsInterval.value() > 0) { + _volumeTagsUpdateExecutor.scheduleAtFixedRate(new StorPoolVolumesTagsUpdate(), + volumeCheckupTagsInterval.value(), volumeCheckupTagsInterval.value(), TimeUnit.SECONDS); + } + if (snapshotCheckupTagsInterval.value() > 0) { + _volumeTagsUpdateExecutor.scheduleAtFixedRate(new StorPoolSnapshotsTagsUpdate(), + snapshotCheckupTagsInterval.value(), snapshotCheckupTagsInterval.value(), TimeUnit.SECONDS); + } + } + + class StorPoolVolumesTagsUpdate extends ManagedContextRunnable { + + @Override + @DB + protected void runInContext() { + List spPools = storagePoolDao.findPoolsByProvider(StorpoolUtil.SP_PROVIDER_NAME); + if (spPools != null && spPools.size() > 0) { + Map volumes = new HashMap<>(); + for (StoragePoolVO storagePoolVO : spPools) { + JsonArray arr = StorpoolUtil.volumesList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + volumes.putAll(getStorPoolNamesAndCsTag(arr)); + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + + try { + PreparedStatement pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`volumes1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`volumes_on_host1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error during delete vm work job: %s", + e.getLocalizedMessage())); + } + + try { + PreparedStatement pstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes1` (name, tag) VALUES (?, ?)"); + PreparedStatement volumesOnHostpstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes_on_host1` (name, tag) VALUES (?, ?)"); + for (Map.Entry volume : volumes.entrySet()) { + if (volume.getValue().equals("volume")) { + addRecordToDb(volume.getKey(), pstmt, volume.getValue(), true); + } else if (volume.getValue().equals("check-volume-is-on-host")) { + addRecordToDb(volume.getKey(), volumesOnHostpstmt, volume.getValue(), true); + } + } + pstmt.executeBatch(); + volumesOnHostpstmt.executeBatch(); + String sql = "SELECT f.* FROM `cloud`.`volumes1` f LEFT JOIN `cloud`.`volumes` v ON f.name=v.path where v.path is NULL OR NOT state=?"; + findMissingRecordsInCS(txn, sql, "volume"); + + String sqlVolumeOnHost = "SELECT f.* FROM `cloud`.`volumes_on_host1` f LEFT JOIN `cloud`.`storage_pool_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlVolumeOnHost, "volumes_on_host"); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed due to: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error: %s", + e.getLocalizedMessage())); + } finally { + try { + PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes1`"); + pstmt.executeUpdate(); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes_on_host1`"); + pstmt.executeUpdate(); + } catch (SQLException e) { + txn.close(); + log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); + } + txn.close(); + } + } + }); + } + } + } + + class StorPoolSnapshotsTagsUpdate extends ManagedContextRunnable { + + @Override + @DB + protected void runInContext() { + List spPools = storagePoolDao.findPoolsByProvider(StorpoolUtil.SP_PROVIDER_NAME); + Map snapshots = new HashMap(); + if (spPools != null && spPools.size() > 0) { + for (StoragePoolVO storagePoolVO : spPools) { + JsonArray arr = StorpoolUtil.snapshotsList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + snapshots.putAll(getStorPoolNamesAndCsTag(arr)); + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + + try{ + PreparedStatement pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`vm_snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`vm_templates1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error during delete vm work job: %s", + e.getLocalizedMessage())); + } + + try { + PreparedStatement snapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`snapshots1` (name, tag) VALUES (?, ?)"); + PreparedStatement groupSnapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_snapshots1` (name, tag) VALUES (?, ?)"); + PreparedStatement templatePstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_templates1` (name, tag) VALUES (?, ?)"); + for (Map.Entry snapshot : snapshots.entrySet()) { + if (!snapshot.getValue().equals("group") && !snapshot.getValue().equals("template")) { + addRecordToDb(snapshot.getKey(), snapshotsPstmt, snapshot.getValue(), true); + } else if (snapshot.getValue().equals("group")) { + addRecordToDb(snapshot.getKey(), groupSnapshotsPstmt, snapshot.getValue(), true); + } else if (snapshot.getValue().equals("template")) { + addRecordToDb(snapshot.getKey(), templatePstmt, snapshot.getValue(), true); + } + } + snapshotsPstmt.executeBatch(); + groupSnapshotsPstmt.executeBatch(); + templatePstmt.executeBatch(); + + String sqlSnapshots = "SELECT f.* FROM `cloud`.`snapshots1` f LEFT JOIN `cloud`.`snapshot_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlSnapshots, "snapshot"); + + String sqlVmSnapshots = "SELECT f.* FROM `cloud`.`vm_snapshots1` f LEFT JOIN `cloud`.`vm_snapshot_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlVmSnapshots, "snapshot"); + + String sqlTemplates = "SELECT temp.*" + + " FROM `cloud`.`vm_templates1` temp" + + " LEFT JOIN `cloud`.`template_store_ref` store" + + " ON temp.name=store.local_path" + + " LEFT JOIN `cloud`.`template_spool_ref` spool" + + " ON temp.name=spool.local_path" + + " where store.local_path is NULL" + + " and spool.local_path is NULL"; + findMissingRecordsInCS(txn, sqlTemplates, "snapshot"); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed due to: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error: %s", + e.getLocalizedMessage())); + } finally { + try { + PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`snapshots1`"); + pstmt.executeUpdate(); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_snapshots1`"); + pstmt.executeUpdate(); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_templates1`"); + pstmt.executeUpdate(); + } catch (SQLException e) { + txn.close(); + log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); + } + txn.close(); + } + } + }); + } + } + } + + private void addRecordToDb(String name, PreparedStatement pstmt, String tag, boolean pathNeeded) + throws SQLException { + name = name.startsWith("~") ? name.split("~")[1] : name; + pstmt.setString(1, pathNeeded ? StorpoolUtil.devPath(name) : name); + pstmt.setString(2, tag); + pstmt.addBatch(); + } + + private void findMissingRecordsInCS(TransactionLegacy txn, String sql, String object) throws SQLException { + ResultSet rs; + PreparedStatement pstmt2 = txn.prepareStatement(sql); + if (object.equals("volume")) { + pstmt2.setString(1, "Ready"); + } + rs = pstmt2.executeQuery(); + String name = null; + while (rs.next()) { + name = rs.getString(2); + log.info(String.format( + "CloudStack does not know about StorPool %s %s, it had to be a %s", object, name, rs.getString(3))); + } + } + + private Map getStorPoolNamesAndCsTag(JsonArray arr) { + Map map = new HashMap<>(); + for (int i = 0; i < arr.size(); i++) { + String name = arr.get(i).getAsJsonObject().get("name").getAsString(); + String tag = null; + if (!name.startsWith("*") && !name.contains("@")) { + JsonObject tags = arr.get(i).getAsJsonObject().get("tags").getAsJsonObject(); + if (tags != null && tags.getAsJsonPrimitive("cs") != null && !(arr.get(i).getAsJsonObject().get("deleted") != null && arr.get(i).getAsJsonObject().get("deleted").getAsBoolean())) { + tag = tags.getAsJsonPrimitive("cs").getAsString(); + map.put(name, tag); + } + } + } + return map; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java new file mode 100644 index 000000000000..d311f9e65d2b --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java @@ -0,0 +1,950 @@ +/* + * 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 + * with 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.storage.datastore.driver; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.RemoteHostEndPoint; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; +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.storage.volume.VolumeObject; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.storage.ResizeVolumeAnswer; +import com.cloud.agent.api.storage.StorpoolBackupSnapshotCommand; +import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.storage.StorpoolCopyVolumeToSecondaryCommand; +import com.cloud.agent.api.storage.StorpoolDownloadTemplateCommand; +import com.cloud.agent.api.storage.StorpoolDownloadVolumeCommand; +import com.cloud.agent.api.storage.StorpoolResizeVolumeCommand; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.server.ResourceTag; +import com.cloud.server.ResourceTag.ResourceObjectType; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.ResizeVolumePayload; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePool; +import com.cloud.storage.VMTemplateDetailVO; +import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.VolumeDetailVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.VMTemplatePoolDaoImpl; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; + +public class StorpoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { + + private static final Logger log = Logger.getLogger(StorpoolPrimaryDataStoreDriver.class); + + @Inject + private VolumeDao volumeDao; + @Inject + private StorageManager storageMgr; + @Inject + private PrimaryDataStoreDao primaryStoreDao; + @Inject + private EndPointSelector selector; + @Inject + private ConfigurationDao configDao; + @Inject + private TemplateDataStoreDao vmTemplateDataStoreDao; + @Inject + private VMInstanceDao vmInstanceDao; + @Inject + private ClusterDao clusterDao; + @Inject + private HostDao hostDao; + @Inject + private ResourceTagDao _resourceTagDao; + @Inject + private SnapshotDetailsDao _snapshotDetailsDao; + @Inject + private SnapshotDataStoreDao snapshotDataStoreDao; + @Inject + private VolumeDetailsDao volumeDetailsDao; + @Inject + private VMTemplateDetailsDao vmTemplateDetailsDao; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject + private VMTemplatePoolDaoImpl vmTemplatePoolDao; + + @Override + public Map getCapabilities() { + return null; + } + + @Override + public DataTO getTO(DataObject data) { + return null; + } + + @Override + public DataStoreTO getStoreTO(DataStore store) { + return null; + } + + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + + @Override + public long getUsedIops(StoragePool storagePool) { + return 0; + } + + @Override + public boolean grantAccess(DataObject data, Host host, DataStore dataStore) { + log.debug("grantAccess"); + return false; + } + + @Override + public void revokeAccess(DataObject data, Host host, DataStore dataStore) { + log.debug("revokeAccess"); + } + + private void updateStoragePool(final long poolId, final long deltaUsedBytes) { + StoragePoolVO storagePool = primaryStoreDao.findById(poolId); + final long capacity = storagePool.getCapacityBytes(); + final long used = storagePool.getUsedBytes() + deltaUsedBytes; + + storagePool.setUsedBytes(used < 0 ? 0 : (used > capacity ? capacity : used)); + primaryStoreDao.update(poolId, storagePool); + } + + private String getVMInstanceUUID(Long id) { + return id != null ? vmInstanceDao.findById(id).getUuid() : null; + } + + protected void _completeResponse(final CreateObjectAnswer answer, final String err, final AsyncCompletionCallback callback) + { + final CreateCmdResult res = new CreateCmdResult(null, answer); + res.setResult(err); + callback.complete(res); + } + + protected void completeResponse(final DataTO result, final AsyncCompletionCallback callback) + { + _completeResponse(new CreateObjectAnswer(result), null, callback); + } + + protected void completeResponse(final String err, final AsyncCompletionCallback callback) + { + _completeResponse(new CreateObjectAnswer(err), err, callback); + } + + @Override + public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { + return dataObject.getSize(); + } + + @Override + public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { + return 0; + } + + @Override + public ChapInfo getChapInfo(DataObject dataObject) { + return null; + } + + @Override + public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback callback) { + String path = null; + String err = null; + if (data.getType() == DataObjectType.VOLUME) { + VolumeInfo vinfo = (VolumeInfo)data; + String name = vinfo.getUuid(); + Long size = vinfo.getSize(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); + if (resp.getError() == null) { + String volumeName = StorpoolUtil.getNameFromResponse(resp, false); + path = StorpoolUtil.devPath(volumeName); + + VolumeVO volume = volumeDao.findById(vinfo.getId()); + volume.setPoolId(dataStore.getId()); + volume.setPoolType(StoragePoolType.SharedMountPoint); + volume.setPath(path); + volumeDao.update(volume.getId(), volume); + + updateStoragePool(dataStore.getId(), size); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + } else { + err = String.format("Could not create StorPool volume %s. Error: %s", name, resp.getError()); + } + } else { + err = String.format("Invalid object type \"%s\" passed to createAsync", data.getType()); + } + + CreateCmdResult res = new CreateCmdResult(path, new Answer(null, err == null, err)); + res.setResult(err); + if (callback != null) { + callback.complete(res); + } + } + + @Override + public void resize(DataObject data, AsyncCompletionCallback callback) { + String path = null; + String err = null; + ResizeVolumeAnswer answer = null; + + if (data.getType() == DataObjectType.VOLUME) { + VolumeObject vol = (VolumeObject)data; + StoragePool pool = (StoragePool)data.getDataStore(); + ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); + + final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); + final long oldSize = vol.getSize(); + Long oldMaxIops = vol.getMaxIops(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk); + + SpApiResponse resp = StorpoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, payload.newMaxIops, conn); + if (resp.getError() != null) { + err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); + } else { + try { + StorpoolResizeVolumeCommand resizeCmd = new StorpoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), + payload.newSize, payload.shrinkOk, payload.instanceName, payload.hosts == null ? false : true); + answer = (ResizeVolumeAnswer)storageMgr.sendToPool(pool, payload.hosts, resizeCmd); + + if (answer == null || !answer.getResult()) { + err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; + } else { + path = StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false)); + + vol.setSize(payload.newSize); + vol.update(); + if (payload.newMaxIops != null) { + VolumeVO volume = volumeDao.findById(vol.getId()); + volume.setMaxIops(payload.newMaxIops); + volumeDao.update(volume.getId(), volume); + } + + updateStoragePool(vol.getPoolId(), payload.newSize - oldSize); + } + } catch (Exception e) { + log.debug("sending resize command failed", e); + err = e.toString(); + } + } + + if (err != null) { + // try restoring volume to its initial size + resp = StorpoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); + if (resp.getError() != null) { + log.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); + } + } + } else { + err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); + } + + CreateCmdResult res = new CreateCmdResult(path, answer); + res.setResult(err); + callback.complete(res); + } + + @Override + public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback callback) { + String err = null; + if (data.getType() == DataObjectType.VOLUME) { + VolumeInfo vinfo = (VolumeInfo)data; + String name = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.deleteAsync delete volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s dataStore=%s", name, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), dataStore.getUuid()); + if (name == null) { + name = vinfo.getUuid(); + } + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + + SpApiResponse resp = StorpoolUtil.volumeDelete(name, conn); + if (resp.getError() == null) { + updateStoragePool(dataStore.getId(), - vinfo.getSize()); + VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); + if (detail != null) { + volumeDetailsDao.remove(detail.getId()); + } + } else { + if (!resp.getError().getName().equalsIgnoreCase("objectDoesNotExist")) { + err = String.format("Could not delete StorPool volume %s. Error: %s", name, resp.getError()); + } + } + } else { + err = String.format("Invalid DataObjectType \"%s\" passed to deleteAsync", data.getType()); + } + + if (err != null) { + log.error(err); + StorpoolUtil.spLog(err); + } + + CommandResult res = new CommandResult(); + res.setResult(err); + callback.complete(res); + } + + private void logDataObject(final String pref, DataObject data) { + final DataStore dstore = data.getDataStore(); + String name = null; + Long size = null; + + if (data.getType() == DataObjectType.VOLUME) { + VolumeInfo vinfo = (VolumeInfo)data; + name = vinfo.getName(); + size = vinfo.getSize(); + } else if (data.getType() == DataObjectType.SNAPSHOT) { + SnapshotInfo sinfo = (SnapshotInfo)data; + name = sinfo.getName(); + size = sinfo.getSize(); + } else if (data.getType() == DataObjectType.TEMPLATE) { + TemplateInfo tinfo = (TemplateInfo)data; + name = tinfo.getName(); + size = tinfo.getSize(); + } + + StorpoolUtil.spLog("%s: name=%s, size=%s, uuid=%s, type=%s, dstore=%s:%s:%s", pref, name, size, data.getUuid(), data.getType(), dstore.getUuid(), dstore.getName(), dstore.getRole()); + } + + @Override + public boolean canCopy(DataObject srcData, DataObject dstData) { + return true; + } + + @Override + public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCallback callback) { + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc:"); + logDataObject("SRC", srcData); + logDataObject("DST", dstData); + + final DataObjectType srcType = srcData.getType(); + final DataObjectType dstType = dstData.getType(); + String err = null; + Answer answer = null; + StorageSubSystemCommand cmd = null; + + try { + if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.VOLUME) { + SnapshotInfo sinfo = (SnapshotInfo)srcData; + final String snapshotName = StorPoolHelper.getSnapshotName(srcData.getId(), srcData.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); + + VolumeInfo vinfo = (VolumeInfo)dstData; + final String volumeName = vinfo.getUuid(); + final Long size = vinfo.getSize(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpApiResponse resp = StorpoolUtil.volumeCreate(volumeName, snapshotName, size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); + if (resp.getError() == null) { + updateStoragePool(dstData.getDataStore().getId(), size); + + VolumeObjectTO to = (VolumeObjectTO)dstData.getTO(); + to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + to.setSize(size); + + answer = new CopyCmdAnswer(to); + StorpoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorpoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); + } else if (resp.getError().getName().equals("objectDoesNotExist")) { + //check if snapshot is on secondary storage + StorpoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName); + SnapshotDataStoreVO snap = snapshotDataStoreDao.findBySnapshot(sinfo.getId(), DataStoreRole.Image); + if (snap != null && StorpoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { + resp = StorpoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn); + if (resp.getError() == null) { + VolumeObjectTO dstTO = (VolumeObjectTO) dstData.getTO(); + dstTO.setSize(size); + dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + cmd = new StorpoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); + + EndPoint ep = selector.select(srcData, dstData); + if (ep == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = ep.sendMessage(cmd); + } + + if (answer != null && answer.getResult()) { + SpApiResponse resp2 = StorpoolUtil.volumeFreeze(StorpoolUtil.getNameFromResponse(resp, true), conn); + if (resp2.getError() != null) { + err = String.format("Could not freeze Storpool volume %s. Error: %s", srcData.getUuid(), resp2.getError()); + } else { + String name = StorpoolUtil.getNameFromResponse(resp, false); + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sinfo.getId(), sinfo.getUuid()); + if (snapshotDetails != null) { + StorPoolHelper.updateSnapshotDetailsValue(snapshotDetails.getId(), StorpoolUtil.devPath(name), "snapshot"); + }else { + StorPoolHelper.addSnapshotDetails(sinfo.getId(), sinfo.getUuid(), StorpoolUtil.devPath(name), _snapshotDetailsDao); + } + resp = StorpoolUtil.volumeCreate(volumeName, StorpoolUtil.getNameFromResponse(resp, true), size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); + if (resp.getError() == null) { + updateStoragePool(dstData.getDataStore().getId(), size); + + VolumeObjectTO to = (VolumeObjectTO) dstData.getTO(); + to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + to.setSize(size); + // successfully downloaded snapshot to primary storage + answer = new CopyCmdAnswer(to); + StorpoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", name, to.getUuid(), snapshotName, sinfo.getUuid()); + + } else { + err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); + } + } + } else { + err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; + } + } else { + err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); + } + } else { + err = String.format("The snapshot %s does not exists neither on primary, neither on secondary storage. Cannot create volume from snapshot", snapshotName); + } + } else { + err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); + } + } else if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.SNAPSHOT) { + // bypass secondary storage + if (StorPoolConfigurationManager.BypassSecondaryStorage.value()) { + SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); + answer = new CopyCmdAnswer(snapshot); + } else { + // copy snapshot to secondary storage (backup snapshot) + cmd = new StorpoolBackupSnapshotCommand(srcData.getTO(), dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + + final String snapName = StorpoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + try { + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapName, clusterDao); + EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); + if (ep == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = ep.sendMessage(cmd); + // if error during snapshot backup, cleanup the StorPool snapshot + if (answer != null && !answer.getResult()) { + StorpoolUtil.spLog(String.format("Error while backing-up snapshot '%s' - cleaning up StorPool snapshot. Error: %s", snapName, answer.getDetails())); + SpApiResponse resp = StorpoolUtil.snapshotDelete(snapName, conn); + if (resp.getError() != null) { + final String err2 = String.format("Failed to cleanup StorPool snapshot '%s'. Error: %s.", snapName, resp.getError()); + log.error(err2); + StorpoolUtil.spLog(err2); + } + } + } + } catch (CloudRuntimeException e) { + err = e.getMessage(); + } + } + } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.TEMPLATE) { + // create template from volume + VolumeObjectTO volume = (VolumeObjectTO) srcData.getTO(); + TemplateObjectTO template = (TemplateObjectTO) dstData.getTO(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + + String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + + + cmd = new StorpoolBackupTemplateFromSnapshotCommand(volume, template, + StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + + try { + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(volumeName, clusterDao); + EndPoint ep2 = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); + if (ep2 == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = ep2.sendMessage(cmd); + if (answer != null && answer.getResult()) { + SpApiResponse resSnapshot = StorpoolUtil.volumeSnapshot(volumeName, template.getUuid(), null, "template", "no", conn); + if (resSnapshot.getError() != null) { + log.debug(String.format("Could not snapshot volume with ID=%s", volume.getId())); + StorpoolUtil.spLog("Volume snapshot failed with error=%s", resSnapshot.getError().getDescr()); + err = resSnapshot.getError().getDescr(); + } + else { + StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), StorpoolUtil.devPath(StorpoolUtil.getSnapshotNameFromResponse(resSnapshot, false, StorpoolUtil.GLOBAL_ID)), vmTemplateDataStoreDao); + vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(srcData.getDataStore().getId()), false)); + } + }else { + err = "Could not copy template to secondary " + answer.getResult(); + } + } + }catch (CloudRuntimeException e) { + err = e.getMessage(); + } + } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.TEMPLATE) { + // copy template to primary storage + TemplateInfo tinfo = (TemplateInfo)dstData; + Long size = tinfo.getSize(); + if(size == null || size == 0) + size = 1L*1024*1024*1024; + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + + TemplateDataStoreVO templDataStoreVO = vmTemplateDataStoreDao.findByTemplate(tinfo.getId(), DataStoreRole.Image); + + String snapshotName = (templDataStoreVO != null && templDataStoreVO.getLocalDownloadPath() != null) + ? StorpoolStorageAdaptor.getVolumeNameFromPath(templDataStoreVO.getLocalDownloadPath(), true) + : null; + String name = tinfo.getUuid(); + + SpApiResponse resp = null; + if (snapshotName != null) { + //no need to copy volume from secondary, because we have it already on primary. Just need to create a child snapshot from it. + //The child snapshot is needed when configuration "storage.cleanup.enabled" is true, not to clean the base snapshot and to lose everything + resp = StorpoolUtil.volumeCreate(name, snapshotName, size, null, "no", "template", null, conn); + if (resp.getError() != null) { + err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); + } else { + String volumeNameToSnapshot = StorpoolUtil.getNameFromResponse(resp, true); + SpApiResponse resp2 = StorpoolUtil.volumeFreeze(volumeNameToSnapshot, conn); + if (resp2.getError() != null) { + err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); + } else { + StorpoolUtil.spLog("Storpool snapshot [%s] for a template exists. Creating template on Storpool with name [%s]", tinfo.getUuid(), name); + TemplateObjectTO dstTO = (TemplateObjectTO) dstData.getTO(); + dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setSize(size); + answer = new CopyCmdAnswer(dstTO); + } + } + } else { + resp = StorpoolUtil.volumeCreate(name, null, size, null, "no", "template", null, conn); + if (resp.getError() != null) { + err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); + } else { + TemplateObjectTO dstTO = (TemplateObjectTO)dstData.getTO(); + dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setSize(size); + + cmd = new StorpoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); + + EndPoint ep = selector.select(srcData, dstData); + if (ep == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = ep.sendMessage(cmd); + } + + if (answer != null && answer.getResult()) { + // successfully downloaded template to primary storage + SpApiResponse resp2 = StorpoolUtil.volumeFreeze(StorpoolUtil.getNameFromResponse(resp, true), conn); + if (resp2.getError() != null) { + err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); + } + } else { + err = answer != null ? answer.getDetails() : "Unknown error while downloading template. Null answer returned."; + } + } + } + if (err != null) { + resp = StorpoolUtil.volumeDelete(StorpoolUtil.getNameFromResponse(resp, true), conn); + if (resp.getError() != null) { + log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp.getError())); + } + } + } else if (srcType == DataObjectType.TEMPLATE && dstType == DataObjectType.VOLUME) { + // create volume from template on Storpool PRIMARY + TemplateInfo tinfo = (TemplateInfo)srcData; + + VolumeInfo vinfo = (VolumeInfo)dstData; + VMTemplateStoragePoolVO templStoragePoolVO = StorPoolHelper.findByPoolTemplate(vinfo.getPoolId(), tinfo.getId()); + final String parentName = templStoragePoolVO.getLocalDownloadPath() !=null ? StorpoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getLocalDownloadPath(), true) : StorpoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getInstallPath(), true); + final String name = vinfo.getUuid(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + + Long snapshotSize = StorpoolUtil.snapshotSize(parentName, conn); + if (snapshotSize == null) { + err = String.format("Snapshot=%s does not exist on StorPool. Will recreate it first on primary", parentName); + vmTemplatePoolDao.remove(templStoragePoolVO.getId()); + } + if (err == null) { + long size = vinfo.getSize(); + if( size < snapshotSize ) + { + StorpoolUtil.spLog(String.format("provided size is too small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, snapshotSize)); + size = snapshotSize; + } + StorpoolUtil.spLog(String.format("volume size is: %d", size)); + Long vmId = vinfo.getInstanceId(); + SpApiResponse resp = StorpoolUtil.volumeCreate(name, parentName, size, getVMInstanceUUID(vmId), + getVcPolicyTag(vmId), "volume", vinfo.getMaxIops(), conn); + if (resp.getError() == null) { + updateStoragePool(dstData.getDataStore().getId(), vinfo.getSize()); + + VolumeObjectTO to = (VolumeObjectTO) vinfo.getTO(); + to.setSize(vinfo.getSize()); + to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + + answer = new CopyCmdAnswer(to); + } else { + err = String.format("Could not create Storpool volume %s. Error: %s", name, resp.getError()); + } + } + } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.VOLUME) { + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.copyAsync src Data Store=%s", srcData.getDataStore().getDriver()); + VolumeInfo dstInfo = (VolumeInfo)dstData; + VolumeInfo srcInfo = (VolumeInfo) srcData; + + if( !(srcData.getDataStore().getDriver() instanceof StorpoolPrimaryDataStoreDriver ) ) { + // copy "VOLUME" to primary storage + String name = dstInfo.getUuid(); + Long size = dstInfo.getSize(); + if(size == null || size == 0) + size = 1L*1024*1024*1024; + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + Long vmId = srcInfo.getInstanceId(); + + SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), "volume", dstInfo.getMaxIops(), conn); + if (resp.getError() != null) { + err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); + } else { + //updateVolume(dstData.getId()); + VolumeObjectTO dstTO = (VolumeObjectTO)dstData.getTO(); + dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setSize(size); + + cmd = new StorpoolDownloadVolumeCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + + EndPoint ep = selector.select(srcData, dstData); + + if( ep == null) { + StorpoolUtil.spLog("select(srcData, dstData) returned NULL. trying srcOnly"); + ep = selector.select(srcData); // Storpool is zone + } + if (ep == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + StorpoolUtil.spLog("Sending command to %s", ep.getHostAddr()); + answer = ep.sendMessage(cmd); + + if (answer != null && answer.getResult()) { + // successfully downloaded volume to primary storage + } else { + err = answer != null ? answer.getDetails() : "Unknown error while downloading volume. Null answer returned."; + } + } + + if (err != null) { + SpApiResponse resp3 = StorpoolUtil.volumeDelete(name, conn); + if (resp3.getError() != null) { + log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp3.getError())); + } + } + } + } else { + // download volume - first copies to secondary + VolumeObjectTO srcTO = (VolumeObjectTO)srcData.getTO(); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s ", srcTO.getPath()); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST canonicalName=%s ", dstData.getDataStore().getClass().getCanonicalName()); + PrimaryDataStoreTO checkStoragePool = dstData.getTO().getDataStore() instanceof PrimaryDataStoreTO ? (PrimaryDataStoreTO)dstData.getTO().getDataStore() : null; + final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST tmpSnapName=%s ,srcUUID=%s", name, srcTO.getUuid()); + + if (checkStoragePool != null && checkStoragePool.getPoolType().equals(StoragePoolType.SharedMountPoint)) { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + String baseOn = StorpoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); + //uuid tag will be the same as srcData.uuid + String volumeName = srcData.getUuid(); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc volumeName=%s, baseOn=%s", volumeName, baseOn); + final SpApiResponse response = StorpoolUtil.volumeCopy(volumeName, baseOn, "volume", srcInfo.getMaxIops(), conn); + srcTO.setSize(srcData.getSize()); + srcTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(response, false))); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST to=%s", srcTO); + + answer = new CopyCmdAnswer(srcTO); + } else { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + final SpApiResponse resp = StorpoolUtil.volumeSnapshot(name, srcTO.getUuid(), srcInfo.getInstanceId() != null ? getVMInstanceUUID(srcInfo.getInstanceId()) : null, "temporary", null, conn); + String snapshotName = StorpoolUtil.getSnapshotNameFromResponse(resp, true, StorpoolUtil.GLOBAL_ID); + if (resp.getError() == null) { + srcTO.setPath(StorpoolUtil.devPath( + StorpoolUtil.getSnapshotNameFromResponse(resp, false, StorpoolUtil.GLOBAL_ID))); + + cmd = new StorpoolCopyVolumeToSecondaryCommand(srcTO, dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.CopyVolumeWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc command=%s ", cmd); + + try { + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapshotName, clusterDao); + EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); + StorpoolUtil.spLog("selector.select(srcData, dstData) ", ep); + if (ep == null) { + ep = selector.select(dstData); + StorpoolUtil.spLog("selector.select(srcData) ", ep); + } + + if (ep == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = ep.sendMessage(cmd); + StorpoolUtil.spLog("Answer: details=%s, result=%s", answer.getDetails(), answer.getResult()); + } + } catch (CloudRuntimeException e) { + err = e.getMessage(); + } + } else { + err = String.format("Failed to create temporary StorPool snapshot while trying to download volume %s (uuid %s). Error: %s", srcTO.getName(), srcTO.getUuid(), resp.getError()); + } + final SpApiResponse resp2 = StorpoolUtil.snapshotDelete(snapshotName, conn); + if (resp2.getError() != null) { + final String err2 = String.format("Failed to delete temporary StorPool snapshot %s. Error: %s", StorpoolUtil.getNameFromResponse(resp, true), resp2.getError()); + log.error(err2); + StorpoolUtil.spLog(err2); + } + } + } + } else { + err = String.format("Unsupported copy operation from %s (type %s) to %s (type %s)", srcData.getUuid(), srcType, dstData.getUuid(), dstType); + } + } catch (Exception e) { + StorpoolUtil.spLog("Caught exception: %s", e.toString()); + err = e.toString(); + } + + if (answer != null && !answer.getResult()) { + err = answer.getDetails(); + } + + if (err != null) { + StorpoolUtil.spLog("Failed due to %s", err); + + log.error(err); + answer = new Answer(cmd, false, err); + } + + CopyCommandResult res = new CopyCommandResult(null, answer); + res.setResult(err); + callback.complete(res); + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + String snapshotName = snapshot.getUuid(); + VolumeInfo vinfo = snapshot.getBaseVolume(); + String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + Long vmId = vinfo.getInstanceId(); + if (volumeName != null) { + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.takeSnapshot volumename=%s vmInstance=%s",volumeName, vmId); + }else { + throw new UnsupportedOperationException("The path should be: " + StorpoolUtil.SP_DEV_PATH); + } + + CreateObjectAnswer answer = null; + String err = null; + + SpApiResponse resp = StorpoolUtil.volumeSnapshot(volumeName, snapshotName, vmId != null ? getVMInstanceUUID(vmId) : null, "snapshot", null, conn); + + if (resp.getError() != null) { + err = String.format("Could not snapshot StorPool volume %s. Error %s", volumeName, resp.getError()); + answer = new CreateObjectAnswer(err); + } else { + String name = StorpoolUtil.getSnapshotNameFromResponse(resp, true, StorpoolUtil.GLOBAL_ID); + SnapshotObjectTO snapTo = (SnapshotObjectTO)snapshot.getTO(); + snapTo.setPath(StorpoolUtil.devPath(name.split("~")[1])); + answer = new CreateObjectAnswer(snapTo); + StorPoolHelper.addSnapshotDetails(snapshot.getId(), snapshot.getUuid(), snapTo.getPath(), _snapshotDetailsDao); + //add primary storage of snapshot + StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), _snapshotDetailsDao); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.takeSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", name, snapshot.getUuid(), volumeName, vinfo.getUuid()); + } + + CreateCmdResult res = new CreateCmdResult(null, answer); + res.setResult(err); + callback.complete(res); + } + + @Override + public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snapshotOnPrimaryStore, final AsyncCompletionCallback callback) { + final VolumeInfo vinfo = snapshot.getBaseVolume(); + final String snapshotName = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); + final String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.revertSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", snapshotName, snapshot.getUuid(), volumeName, vinfo.getUuid()); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + String err = null; + + VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); + if (detail != null) { + //Rename volume to its global id only if it was migrated from UUID to global id + SpApiResponse updateVolumeResponse = StorpoolUtil.volumeUpdateRename(StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), "", StorpoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); + + if (updateVolumeResponse.getError() != null) { + StorpoolUtil.spLog("Could not update StorPool's volume %s to it's globalId due to %s", StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); + err = String.format("Could not update StorPool's volume %s to it's globalId due to %s", StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); + completeResponse(err, callback); + return; + } + volumeDetailsDao.remove(detail.getId()); + } + + SpApiResponse resp = StorpoolUtil.detachAllForced(volumeName, false, conn); + if (resp.getError() != null) { + err = String.format("Could not detach StorPool volume %s due to %s", volumeName, resp.getError()); + completeResponse(err, callback); + return; + } + SpApiResponse response = StorpoolUtil.volumeRevert(volumeName, snapshotName, conn); + if (response.getError() != null) { + err = String.format( + "Could not revert StorPool volume %s to the %s snapshot: could not create the new volume: error %s", + volumeName, snapshotName, response.getError()); + completeResponse(err, callback); + return; + } + + if (vinfo.getMaxIops() != null) { + response = StorpoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); + if (response.getError() != null) { + StorpoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", response.getError().getDescr()); + } + } + + final VolumeObjectTO to = (VolumeObjectTO)vinfo.getTO(); + completeResponse(to, callback); + } + + private String getVcPolicyTag(Long vmId) { + ResourceTag resourceTag = vmId != null ? _resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorpoolUtil.SP_VC_POLICY) : null; + return resourceTag != null ? resourceTag.getValue() : null; + } + + public void handleQualityOfServiceForVolumeMigration(VolumeInfo arg0, QualityOfServiceState arg1) { + StorpoolUtil.spLog("handleQualityOfServiceForVolumeMigration with volume name=%s", arg0.getName()); + } + + + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, + AsyncCompletionCallback callback) { + copyAsync(srcData, destData, callback); + } + + public boolean canProvideStorageStats() { + return false; + } + + public Pair getStorageStats(StoragePool storagePool) { + return null; + } + + public boolean canProvideVolumeStats() { + return false; + } + + public Pair getVolumeStats(StoragePool storagePool, String volumeId) { + return null; + } + + public boolean canHostAccessStoragePool(Host host, StoragePool pool) { + return false; + } + + @Override + public boolean vmInfoNeeded() { + return true; + } + + @Override + public void provideVMInfo(long vmId, long volumeId) { + VolumeVO volume = volumeDao.findById(volumeId); + StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); + if (poolVO != null) { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); + String volName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + VMInstanceVO userVM = vmInstanceDao.findById(vmId); + String vcPolicy = getVcPolicyTag(vmId); + SpApiResponse resp = StorpoolUtil.volumeUpadateTags(volName, userVM.getId() == volume.getInstanceId() ? userVM.getUuid() : "", null, conn, vcPolicy); + if (resp.getError() != null) { + log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); + } + } + } + + @Override + public boolean vmTagsNeeded(String tagKey) { + return tagKey != null && tagKey.equals(StorpoolUtil.SP_VC_POLICY); + } + + @Override + public void provideVMTags(long vmId, long volumeId, String tagValue) { + VolumeVO volume = volumeDao.findById(volumeId); + StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); + if (poolVO != null) { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); + String volName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + SpApiResponse resp = StorpoolUtil.volumeUpadateVCTags(volName, conn, tagValue); + if (resp.getError() != null) { + log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); + } + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java new file mode 100644 index 000000000000..93837b5e85f3 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java @@ -0,0 +1,316 @@ +/* + * 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 + * with 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.storage.datastore.lifecycle; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +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.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.host.HostVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.resource.ResourceManager; +import com.cloud.storage.ScopeType; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolAutomation; +import com.cloud.storage.VMTemplateDetailVO; +import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.VMTemplatePoolDao; +import com.cloud.utils.exception.CloudRuntimeException; + +public class StorpoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { + private static final Logger log = Logger.getLogger(StorpoolPrimaryDataStoreLifeCycle.class); + + @Inject + protected PrimaryDataStoreHelper dataStoreHelper; + @Inject + protected StoragePoolAutomation storagePoolAutmation; + @Inject + private PrimaryDataStoreDao _primaryDataStoreDao; + @Inject + private ResourceManager resourceMgr; + @Inject + private StorageManager storageMgr; + @Inject + private SnapshotDao snapshotDao; + @Inject + private SnapshotDetailsDao snapshotDetailsDao; + @Inject + private VMTemplatePoolDao vmTemplatePoolDao; + @Inject + private VMTemplateDetailsDao vmTemplateDetailsDao; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + + @Override + public DataStore initialize(Map dsInfos) { + StorpoolUtil.spLog("initialize:"); + for (Map.Entry e: dsInfos.entrySet()) { + StorpoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); + } + StorpoolUtil.spLog(""); + + log.debug("initialize"); + + String name = (String)dsInfos.get("name"); + String providerName = (String)dsInfos.get("providerName"); + Long zoneId = (Long)dsInfos.get("zoneId"); + + String url = (String)dsInfos.get("url"); + SpConnectionDesc conn = new SpConnectionDesc(url); + if (conn.getHostPort() == null) + throw new IllegalArgumentException("No SP_API_HTTP"); + + if (conn.getAuthToken() == null) + throw new IllegalArgumentException("No SP_AUTH_TOKEN"); + + if (conn.getTemplateName() == null) + throw new IllegalArgumentException("No SP_TEMPLATE"); + + if (!StorpoolUtil.templateExists(conn)) { + throw new IllegalArgumentException("No such storpool template " + conn.getTemplateName() + " or credentials are invalid"); + } + + for (StoragePoolVO sp : _primaryDataStoreDao.findPoolsByProvider("StorPool")) { + List spDetails = storagePoolDetailsDao.listDetails(sp.getId()); + String host = null; + String template = null; + String authToken = null; + SpConnectionDesc old = null; + for (StoragePoolDetailVO storagePoolDetailVO : spDetails) { + switch (storagePoolDetailVO.getName()) { + case StorpoolUtil.SP_AUTH_TOKEN: + authToken = storagePoolDetailVO.getValue(); + break; + case StorpoolUtil.SP_HOST_PORT: + host = storagePoolDetailVO.getValue(); + break; + case StorpoolUtil.SP_TEMPLATE: + template = storagePoolDetailVO.getValue(); + break; + default: + break; + } + } + if (host != null && template != null && authToken != null) { + old = new SpConnectionDesc(host, authToken, template); + } else { + old = new SpConnectionDesc(sp.getUuid()); + } + if( old.getHostPort().equals(conn.getHostPort()) && old.getTemplateName().equals(conn.getTemplateName()) ) + throw new IllegalArgumentException("StorPool cluster and template already in use by pool " + sp.getName()); + } + + Long capacityBytes = (Long)dsInfos.get("capacityBytes"); + if (capacityBytes == null) { + throw new IllegalArgumentException("Capcity bytes is required"); + } + + String tags = (String)dsInfos.get("tags"); + if (tags == null || tags.isEmpty()) { + tags = name; + } + + @SuppressWarnings("unchecked") + Map details = (Map)dsInfos.get("details"); + details.put(StorpoolUtil.SP_AUTH_TOKEN, conn.getAuthToken()); + details.put(StorpoolUtil.SP_HOST_PORT, conn.getHostPort()); + details.put(StorpoolUtil.SP_TEMPLATE, conn.getTemplateName()); + + PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters(); + parameters.setName(name); + parameters.setUuid(conn.getTemplateName() + ";" + UUID.randomUUID().toString()); + parameters.setZoneId(zoneId); + parameters.setProviderName(providerName); + parameters.setType(StoragePoolType.SharedMountPoint); + parameters.setHypervisorType(HypervisorType.KVM); + parameters.setManaged(false); + parameters.setHost("n/a"); + parameters.setPort(0); + parameters.setPath(StorpoolUtil.SP_DEV_PATH); + parameters.setUsedBytes(0); + parameters.setCapacityBytes(capacityBytes); + parameters.setTags(tags); + parameters.setDetails(details); + + return dataStoreHelper.createPrimaryDataStore(parameters); + } + + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + StorpoolUtil.spLog("updateStoragePool:"); + for (Map.Entry e: details.entrySet()) { + StorpoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); + } + StorpoolUtil.spLog(""); + + log.debug("updateStoragePool"); + return; + } + @Override + public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) { + log.debug("attachHost"); + return true; + } + + @Override + public boolean attachCluster(DataStore store, ClusterScope scope) { + log.debug("attachCluster"); + if (!scope.getScopeType().equals(ScopeType.ZONE)) { + throw new UnsupportedOperationException("Only Zone-Wide scope is supported!"); + } + return true; + } + + @Override + public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) { + log.debug("attachZone"); + + if (hypervisorType != HypervisorType.KVM) { + throw new UnsupportedOperationException("Only KVM hypervisors supported!"); + } + List kvmHosts = resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.KVM, scope.getScopeId()); + for (HostVO host : kvmHosts) { + try { + storageMgr.connectHostToSharedPool(host.getId(), dataStore.getId()); + } catch (Exception e) { + log.warn(String.format("Unable to establish a connection between host %s and pool %s due to %s", host, dataStore, e)); + } + } + dataStoreHelper.attachZone(dataStore, hypervisorType); + return true; + } + + @Override + public boolean maintain(DataStore dataStore) { + log.debug("maintain"); + + storagePoolAutmation.maintain(dataStore); + dataStoreHelper.maintain(dataStore); + return true; + } + + @Override + public boolean cancelMaintain(DataStore store) { + log.debug("cancelMaintain"); + + dataStoreHelper.cancelMaintain(store); + storagePoolAutmation.cancelMaintain(store); + return true; + } + + @Override + public boolean deleteDataStore(DataStore store) { + log.debug("deleteDataStore"); + long storagePoolId = store.getId(); + + List lstSnapshots = snapshotDao.listAll(); + + if (lstSnapshots != null) { + for (SnapshotVO snapshot : lstSnapshots) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID); + + // if this snapshot belongs to the storagePool that was passed in + if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePoolId) { + throw new CloudRuntimeException("This primary storage cannot be deleted because it currently contains one or more snapshots."); + } + } + } + + List lstTemplateDetails = vmTemplateDetailsDao.listAll(); + + if (lstTemplateDetails != null) { + for (VMTemplateDetailVO vmTemplateDetailVO : lstTemplateDetails) { + if (vmTemplateDetailVO.getName().equals(StorpoolUtil.SP_STORAGE_POOL_ID) && Long.parseLong(vmTemplateDetailVO.getValue()) == storagePoolId) { + throw new CloudRuntimeException("This primary storage cannot be deleted because it currently contains one or more template snapshots."); + } + } + } + + List lstTemplatePoolRefs = vmTemplatePoolDao.listByPoolId(storagePoolId); + + if (lstTemplatePoolRefs != null) { + for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { + SpApiResponse resp = StorpoolUtil.snapshotDelete( + StorpoolStorageAdaptor.getVolumeNameFromPath(templatePoolRef.getLocalDownloadPath(), true), + StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao)); + if (resp.getError() != null) { + throw new CloudRuntimeException(String.format("Could not delete StorPool's snapshot from template_spool_ref table due to %s", resp.getError())); + } + vmTemplatePoolDao.remove(templatePoolRef.getId()); + } + } + boolean isDeleted = dataStoreHelper.deletePrimaryDataStore(store); + if (isDeleted) { + List volumesOnHosts = storagePoolDetailsDao.listDetails(storagePoolId); + for (StoragePoolDetailVO storagePoolDetailVO : volumesOnHosts) { + if (storagePoolDetailVO.getValue() != null && storagePoolDetailVO.getName().contains(StorpoolUtil.SP_VOLUME_ON_CLUSTER)) { + StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(storagePoolDetailVO.getValue(), true), + StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao)); + } + } + storagePoolDetailsDao.removeDetails(storagePoolId); + } + return isDeleted; + } + + @Override + public boolean migrateToObjectStore(DataStore store) { + log.debug("migrateToObjectStore"); + return false; + } + + @Override + public void enableStoragePool(DataStore dataStore) { + log.debug("enableStoragePool"); + dataStoreHelper.enable(dataStore); + } + + @Override + public void disableStoragePool(DataStore dataStore) { + log.debug("disableStoragePool"); + dataStoreHelper.disable(dataStore); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java new file mode 100644 index 000000000000..4a2db4606639 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java @@ -0,0 +1,211 @@ +/* + * 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 + * with 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.storage.datastore.provider; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +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.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.storage.StorPoolModifyStoragePoolAnswer; +import com.cloud.agent.api.storage.StorPoolModifyStoragePoolCommand; +import com.cloud.agent.manager.AgentAttache; +import com.cloud.alert.AlertManager; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.StorageConflictException; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolHostVO; +import com.cloud.storage.dao.StoragePoolHostDao; +import com.cloud.utils.exception.CloudRuntimeException; + +public class StorpoolHostListener implements HypervisorHostListener { + private static final Logger log = Logger.getLogger(StorpoolHostListener .class); + + @Inject + private AgentManager agentMgr; + @Inject + private DataStoreManager dataStoreMgr; + @Inject + private AlertManager alertMgr; + @Inject + private StoragePoolHostDao storagePoolHostDao; + @Inject + private PrimaryDataStoreDao primaryStoreDao; + @Inject + private HostDao hostDao; + @Inject + private ClusterDao clusterDao; + @Inject + private ClusterDetailsDao clusterDetailsDao; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + + @Override + public boolean hostConnect(long hostId, long poolId) throws StorageConflictException { + //Will update storage pool's connection details if they aren't updated in DB, before connecting pool to host + StoragePoolVO poolVO = primaryStoreDao.findById(poolId); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolId, storagePoolDetailsDao, primaryStoreDao); + + StoragePool pool = (StoragePool)this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); + + HostVO host = hostDao.findById(hostId); + StoragePoolDetailVO volumeOnPool = verifyVolumeIsOnCluster(poolId, conn, host.getClusterId()); + if (volumeOnPool == null) { + return false; + } + + if (host.isInMaintenanceStates()) { + addModifyCommandToCommandsAllowedInMaintenanceMode(); + } + + StorPoolModifyStoragePoolCommand cmd = new StorPoolModifyStoragePoolCommand(true, pool, volumeOnPool.getValue()); + final Answer answer = agentMgr.easySend(hostId, cmd); + + StoragePoolHostVO poolHost = storagePoolHostDao.findByPoolHost(pool.getId(), hostId); + + if (answer == null) { + throw new CloudRuntimeException("Unable to get an answer to the modify storage pool command" + pool.getId()); + } + + if (!answer.getResult()) { + if (answer.getDetails() != null) { + if (answer.getDetails().equals("objectDoesNotExist")) { + StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(volumeOnPool.getValue(), true), conn); + storagePoolDetailsDao.remove(volumeOnPool.getId()); + return false; + } else if (answer.getDetails().equals("spNotFound")) { + return false; + } + + } + String msg = "Unable to attach storage pool" + poolId + " to the host" + hostId; + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg); + throw new CloudRuntimeException("Unable establish connection from storage head to storage pool " + pool.getId() + " due to " + answer.getDetails() + + pool.getId()); + } + + StorpoolUtil.spLog("hostConnect: hostId=%d, poolId=%d", hostId, poolId); + + StorPoolModifyStoragePoolAnswer mspAnswer = (StorPoolModifyStoragePoolAnswer)answer; + if (mspAnswer.getLocalDatastoreName() != null && pool.isShared()) { + String datastoreName = mspAnswer.getLocalDatastoreName(); + List localStoragePools = primaryStoreDao.listLocalStoragePoolByPath(pool.getDataCenterId(), datastoreName); + for (StoragePoolVO localStoragePool : localStoragePools) { + if (datastoreName.equals(localStoragePool.getPath())) { + log.warn("Storage pool: " + pool.getId() + " has already been added as local storage: " + localStoragePool.getName()); + throw new StorageConflictException("Cannot add shared storage pool: " + pool.getId() + " because it has already been added as local storage:" + + localStoragePool.getName()); + } + } + } + + if (poolHost == null) { + poolHost = new StoragePoolHostVO(pool.getId(), hostId, mspAnswer.getPoolInfo().getLocalPath().replaceAll("//", "/")); + storagePoolHostDao.persist(poolHost); + } else { + poolHost.setLocalPath(mspAnswer.getPoolInfo().getLocalPath().replaceAll("//", "/")); + } + + StorPoolHelper.setSpClusterIdIfNeeded(hostId, mspAnswer.getClusterId(), clusterDao, hostDao, clusterDetailsDao); + + log.info("Connection established between storage pool " + pool + " and host " + hostId); + return true; + } + + private synchronized StoragePoolDetailVO verifyVolumeIsOnCluster(long poolId, SpConnectionDesc conn, long clusterId) { + StoragePoolDetailVO volumeOnPool = storagePoolDetailsDao.findDetail(poolId, StorpoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId); + if (volumeOnPool == null) { + SpApiResponse resp = StorpoolUtil.volumeCreate(conn); + if (resp.getError() != null) { + return volumeOnPool; + } + String volumeName = StorpoolUtil.getNameFromResponse(resp, false); + volumeOnPool = new StoragePoolDetailVO(poolId, StorpoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId, StorpoolUtil.devPath(volumeName), false); + storagePoolDetailsDao.persist(volumeOnPool); + } + return volumeOnPool; + } + + @Override + public boolean hostAdded(long hostId) { + return true; + } + + @Override + public boolean hostDisconnected(long hostId, long poolId) { + StorpoolUtil.spLog("hostDisconnected: hostId=%d, poolId=%d", hostId, poolId); + return true; + } + + @Override + public boolean hostAboutToBeRemoved(long hostId) { + return true; + } + + @Override + public boolean hostRemoved(long hostId, long clusterId) { + return true; + } + + //workaround: we need this "hack" to add our command StorPoolModifyStoragePoolCommand in AgentAttache.s_commandsAllowedInMaintenanceMode + //which checks the allowed commands when the host is in maintenance mode + private void addModifyCommandToCommandsAllowedInMaintenanceMode() { + + Class cls = AgentAttache.class; + try { + Field field = cls.getDeclaredField("s_commandsAllowedInMaintenanceMode"); + field.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + List allowedCmdsInMaintenance = new ArrayList(Arrays.asList(AgentAttache.s_commandsAllowedInMaintenanceMode)); + allowedCmdsInMaintenance.add(StorPoolModifyStoragePoolCommand.class.toString()); + String[] allowedCmdsInMaintenanceNew = new String[allowedCmdsInMaintenance.size()]; + allowedCmdsInMaintenance.toArray(allowedCmdsInMaintenanceNew); + Arrays.sort(allowedCmdsInMaintenanceNew); + field.set(null, allowedCmdsInMaintenanceNew); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + String err = "Could not add StorPoolModifyStoragePoolCommand to s_commandsAllowedInMaintenanceMode array due to: %s"; + StorpoolUtil.spLog(err, e.getMessage()); + log.warn(String.format(err, e.getMessage())); + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java new file mode 100644 index 000000000000..9ea963d433de --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java @@ -0,0 +1,78 @@ +/* + * 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 + * with 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.storage.datastore.provider; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; +import org.apache.cloudstack.storage.datastore.driver.StorpoolPrimaryDataStoreDriver; +import org.apache.cloudstack.storage.datastore.lifecycle.StorpoolPrimaryDataStoreLifeCycle; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; + +import com.cloud.utils.component.ComponentContext; + +public class StorpoolPrimaryDataStoreProvider implements PrimaryDataStoreProvider { + + protected DataStoreLifeCycle lifecycle; + protected DataStoreDriver driver; + protected HypervisorHostListener listener; + + StorpoolPrimaryDataStoreProvider() { + } + + @Override + public String getName() { + return StorpoolUtil.SP_PROVIDER_NAME; + } + + @Override + public DataStoreLifeCycle getDataStoreLifeCycle() { + return lifecycle; + } + + @Override + public DataStoreDriver getDataStoreDriver() { + return driver; + } + + @Override + public HypervisorHostListener getHostListener() { + return listener; + } + + @Override + public boolean configure(Map params) { + lifecycle = ComponentContext.inject(StorpoolPrimaryDataStoreLifeCycle.class); + driver = ComponentContext.inject(StorpoolPrimaryDataStoreDriver.class); + listener = ComponentContext.inject(StorpoolHostListener.class); + return true; + } + + @Override + public Set getTypes() { + Set types = new HashSet(); + types.add(DataStoreProviderType.PRIMARY); + return types; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java new file mode 100644 index 000000000000..4f51ef2bae3e --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java @@ -0,0 +1,262 @@ +/* + * 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 + * with 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.storage.datastore.util; + +import java.io.IOException; +import java.sql.PreparedStatement; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.log4j.Appender; +import org.apache.log4j.Logger; +import org.apache.log4j.PatternLayout; +import org.apache.log4j.RollingFileAppender; + +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.server.ResourceTag; +import com.cloud.server.ResourceTag.ResourceObjectType; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.dao.VMInstanceDao; + +public class StorPoolHelper { + + private static final String UPDATE_SNAPSHOT_DETAILS_VALUE = "UPDATE `cloud`.`snapshot_details` SET value=? WHERE id=?"; + private static final String UPDATE_VOLUME_DETAILS_NAME = "UPDATE `cloud`.`volume_details` SET name=? WHERE id=?"; + public static final String PrimaryStorageDownloadWait = "primary.storage.download.wait"; + public static final String CopyVolumeWait = "copy.volume.wait"; + public static final String BackupSnapshotWait = "backup.snapshot.wait"; + + public static void updateVolumeInfo(VolumeObjectTO volumeObjectTO, Long size, SpApiResponse resp, + VolumeDao volumeDao) { + String volumePath = StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false)); + VolumeVO volume = volumeDao.findById(volumeObjectTO.getId()); + if (volume != null) { + volumeObjectTO.setSize(size); + volumeObjectTO.setPath(volumePath); + volume.setSize(size); + volume.setPath(volumePath); + volumeDao.update(volumeObjectTO.getId(), volume); + } + } + + // If volume is deleted, CloudStack removes records of snapshots created on Primary storage only in database. + // That's why we keep information in snapshot_details table, about all snapshots created on StorPool and we can operate with them + public static void addSnapshotDetails(final Long id, final String uuid, final String snapshotName, + SnapshotDetailsDao snapshotDetailsDao) { + SnapshotDetailsVO details = new SnapshotDetailsVO(id, uuid, snapshotName, false); + snapshotDetailsDao.persist(details); + } + + public static String getSnapshotName(Long snapshotId, String snapshotUuid, SnapshotDataStoreDao snapshotStoreDao, + SnapshotDetailsDao snapshotDetailsDao) { + + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, snapshotUuid); + + if (snapshotDetails != null) { + return StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetails.getValue(), true); + } else { + List snapshots = snapshotStoreDao.findBySnapshotId(snapshotId); + if (!CollectionUtils.isEmpty(snapshots)) { + for (SnapshotDataStoreVO snapshotDataStoreVO : snapshots) { + String name = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDataStoreVO.getInstallPath(), true); + if (name == null) { + continue; + } else { + addSnapshotDetails(snapshotId, snapshotUuid, snapshotDataStoreVO.getInstallPath(), snapshotDetailsDao); + return name; + } + } + } + } + return null; + } + + public static void updateSnapshotDetailsValue(Long id, String valueOrName, String snapshotOrVolume) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmt = null; + try { + String sql = null; + if (snapshotOrVolume.equals("snapshot")) { + sql = UPDATE_SNAPSHOT_DETAILS_VALUE; + } else if (snapshotOrVolume.equals("volume")) { + sql = UPDATE_VOLUME_DETAILS_NAME; + } else { + StorpoolUtil.spLog("Could not update snapshot detail with id=%s", id); + } + if (sql != null) { + pstmt = txn.prepareAutoCloseStatement(sql); + pstmt.setString(1, valueOrName); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + txn.commit(); + } + } catch (Exception e) { + txn.rollback(); + StorpoolUtil.spLog("Could not update snapshot detail with id=%s", id); + } + } + + public static String getVcPolicyTag(Long vmId, ResourceTagDao resourceTagDao) { + if (vmId != null) { + ResourceTag tag = resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorpoolUtil.SP_VC_POLICY); + if (tag != null) { + return tag.getValue(); + } + } + return null; + } + + public static String getVMInstanceUUID(Long id, VMInstanceDao vmInstanceDao) { + if (id != null) { + VMInstanceVO vmInstance = vmInstanceDao.findById(id); + if (vmInstance != null) { + return vmInstance.getUuid(); + } + } + return null; + } + + public static Map addStorPoolTags(String name, String vmUuid, String csTag, String vcPolicy) { + Map tags = new HashMap<>(); + tags.put("uuid", name); + tags.put("cvm", vmUuid); + tags.put(StorpoolUtil.SP_VC_POLICY, vcPolicy); + if (csTag != null) { + tags.put("cs", csTag); + } + return tags; + } + + // Initialize custom logger for updated volume and snapshots + public static void appendLogger(Logger log, String filePath, String kindOfLog) { + Appender appender = null; + PatternLayout patternLayout = new PatternLayout(); + patternLayout.setConversionPattern("%d{YYYY-MM-dd HH:mm:ss.SSS} %m%n"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + String path = filePath + "-" + sdf.format(timestamp) + ".log"; + try { + appender = new RollingFileAppender(patternLayout, path); + log.setAdditivity(false); + log.addAppender(appender); + } catch (IOException e) { + e.printStackTrace(); + } + if (kindOfLog.equals("update")) { + StorpoolUtil.spLog( + "You can find information about volumes and snapshots, which will be updated in Database with their globalIs in %s log file", + path); + } else if (kindOfLog.equals("abandon")) { + StorpoolUtil.spLog( + "You can find information about volumes and snapshots, for which CloudStack doesn't have information in %s log file", + path); + } + } + + public static void setSpClusterIdIfNeeded(long hostId, String clusterId, ClusterDao clusterDao, HostDao hostDao, + ClusterDetailsDao clusterDetails) { + HostVO host = hostDao.findById(hostId); + if (host != null && host.getClusterId() != null) { + ClusterVO cluster = clusterDao.findById(host.getClusterId()); + ClusterDetailsVO clusterDetailsVo = clusterDetails.findDetail(cluster.getId(), + StorPoolConfigurationManager.StorPoolClusterId.key()); + if (clusterDetailsVo == null) { + clusterDetails.persist( + new ClusterDetailsVO(cluster.getId(), StorPoolConfigurationManager.StorPoolClusterId.key(), clusterId)); + } else if (clusterDetailsVo.getValue() == null || !clusterDetailsVo.getValue().equals(clusterId)) { + clusterDetailsVo.setValue(clusterId); + clusterDetails.update(clusterDetailsVo.getId(), clusterDetailsVo); + } + } + } + + public static Long findClusterIdByGlobalId(String globalId, ClusterDao clusterDao) { + List clusterVo = clusterDao.listAll(); + if (clusterVo.size() == 1) { + StorpoolUtil.spLog("There is only one cluster, sending backup to secondary command"); + return null; + } + for (ClusterVO clusterVO2 : clusterVo) { + if (globalId != null && StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()) != null + && globalId.contains(StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()).toString())) { + StorpoolUtil.spLog("Found cluster with id=%s for object with globalId=%s", clusterVO2.getId(), + globalId); + return clusterVO2.getId(); + } + } + throw new CloudRuntimeException( + "Could not find the right clusterId. to send command. To use snapshot backup to secondary for each CloudStack cluster in its settings set the value of StorPool's cluster-id in \"sp.cluster.id\"."); + } + + public static HostVO findHostByCluster(Long clusterId, HostDao hostDao) { + List host = hostDao.findByClusterId(clusterId); + return host != null ? host.get(0) : null; + } + + public static int getTimeout(String cfg, ConfigurationDao configDao) { + final ConfigurationVO value = configDao.findByName(cfg); + return NumbersUtil.parseInt(value.getValue(), Integer.parseInt(value.getDefaultValue())); + } + + public static VMTemplateStoragePoolVO findByPoolTemplate(long poolId, long templateId) { + QueryBuilder sc = QueryBuilder.create(VMTemplateStoragePoolVO.class); + sc.and(sc.entity().getPoolId(), Op.EQ, poolId); + sc.and(sc.entity().getTemplateId(), Op.EQ, templateId); + return sc.find(); + } + + public static void updateVmStoreTemplate(Long id, DataStoreRole role, String path, + TemplateDataStoreDao templStoreDao) { + TemplateDataStoreVO templ = templStoreDao.findByTemplate(id, role); + templ.setLocalDownloadPath(path); + templStoreDao.persist(templ); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java new file mode 100644 index 000000000000..8b8f4431fddd --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java @@ -0,0 +1,599 @@ +/* + * 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 + * with 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.storage.datastore.util; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +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.cloudstack.storage.to.VolumeObjectTO; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +//import org.apache.http.impl.conn.BasicClientConnectionManager; +import org.apache.log4j.Logger; + +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.OutputInterpreter; +import com.cloud.utils.script.Script; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +public class StorpoolUtil { + private static final Logger log = Logger.getLogger(StorpoolUtil.class); + + private static final File spLogFile = new File("/var/log/cloudstack/management/storpool-plugin.log"); + private static PrintWriter spLogPrinterWriter = spLogFileInitialize(); + + private static PrintWriter spLogFileInitialize() { + try { + log.info("INITIALIZE SP-LOG_FILE"); + if (spLogFile.exists()) { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + final Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + final File spLogFileRename = new File(spLogFile + "-" + sdf.format(timestamp)); + final boolean ret = spLogFile.renameTo(spLogFileRename); + if (!ret) { + log.warn("Unable to rename" + spLogFile + " to " + spLogFileRename); + } else { + log.debug("Renamed " + spLogFile + " to " + spLogFileRename); + } + } else { + spLogFile.getParentFile().mkdirs(); + } + return new PrintWriter(spLogFile); + } catch (Exception e) { + log.info("INITIALIZE SP-LOG_FILE: " + e.getMessage()); + throw new RuntimeException(e); + } + } + + public static void spLog(String fmt, Object... args) { + String timeStamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,ms").format(Calendar.getInstance().getTime()); + spLogPrinterWriter.println(String.format(timeStamp + " " + fmt, args)); + spLogPrinterWriter.flush(); + if (spLogFile.length() > 107374182400L) { + spLogPrinterWriter.close(); + spLogPrinterWriter = spLogFileInitialize(); + } + } + + public static final String SP_PROVIDER_NAME = "StorPool"; + public static final String SP_DEV_PATH = "/dev/storpool-byid/"; + public static final String SP_OLD_PATH = "/dev/storpool/"; + public static final String SP_VC_POLICY = "vc-policy"; + public static final String GLOBAL_ID = "snapshotGlobalId"; + public static final String UPDATED_DETAIL = "renamed"; + public static final String SP_STORAGE_POOL_ID = "spStoragePoolId"; + + public static final String SP_HOST_PORT = "SP_API_HTTP_HOST"; + + public static final String SP_TEMPLATE = "SP_TEMPLATE"; + + public static final String SP_AUTH_TOKEN = "SP_AUTH_TOKEN"; + + public static final String SP_VOLUME_ON_CLUSTER = "SP_VOLUME_ON_CLUSTER"; + + public static enum StorpoolRights { + RO("ro"), RW("rw"), DETACH("detach"); + + private final String name; + + private StorpoolRights(String name) { + this.name = name; + } + + public String toString() { + return name; + } + } + + public static final class SpApiError { + private String name; + private String descr; + + public SpApiError() { + } + + public String getName() { + return this.name; + } + + public String getDescr() { + return this.descr; + } + + public void setName(String name) { + this.name = name; + } + + public void setDescr(String descr) { + this.descr = descr; + } + + public String toString() { + return String.format("%s: %s", name, descr); + } + } + + public static class SpConnectionDesc { + private String hostPort; + private String authToken; + private String templateName; + + public SpConnectionDesc(String url) { + String[] urlSplit = url.split(";"); + if (urlSplit.length == 1 && !urlSplit[0].contains("=")) { + this.templateName = url; + + Script sc = new Script("storpool_confget", 0, log); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + + final String err = sc.execute(parser); + if (err != null) { + final String errMsg = String.format("Could not execute storpool_confget. Error: %s", err); + log.warn(errMsg); + throw new CloudRuntimeException(errMsg); + } + + String SP_API_HOST = null; + String SP_API_PORT = null; + + for (String line : parser.getLines().split("\n")) { + String[] toks = line.split("="); + if (toks.length != 2) { + continue; + } + + switch (toks[0]) { + case "SP_API_HTTP_HOST": + SP_API_HOST = toks[1]; + break; + + case "SP_API_HTTP_PORT": + SP_API_PORT = toks[1]; + break; + + case "SP_AUTH_TOKEN": + this.authToken = toks[1]; + break; + } + } + + if (SP_API_HOST == null) + throw new CloudRuntimeException("Invalid StorPool config. Missing SP_API_HTTP_HOST"); + if (SP_API_PORT == null) + throw new CloudRuntimeException("Invalid StorPool config. Missing SP_API_HTTP_PORT"); + if (this.authToken == null) + throw new CloudRuntimeException("Invalid StorPool config. Missing SP_AUTH_TOKEN"); + + this.hostPort = SP_API_HOST + ":" + SP_API_PORT; + } else { + for (String kv : urlSplit) { + String[] toks = kv.split("="); + if (toks.length != 2) + continue; + switch (toks[0]) { + case "SP_API_HTTP": + this.hostPort = toks[1]; + break; + + case "SP_AUTH_TOKEN": + this.authToken = toks[1]; + break; + + case "SP_TEMPLATE": + this.templateName = toks[1]; + break; + } + } + } + } + + public SpConnectionDesc(String host, String authToken2, String templateName2) { + this.hostPort = host; + this.authToken = authToken2; + this.templateName = templateName2; + } + + public String getHostPort() { + return this.hostPort; + } + + public String getAuthToken() { + return this.authToken; + } + + public String getTemplateName() { + return this.templateName; + } + } + + public static SpConnectionDesc getSpConnection(String url, long poolId, StoragePoolDetailsDao poolDetails, + PrimaryDataStoreDao storagePool) { + List details = poolDetails.listDetails(poolId); + String host = null; + String authToken = null; + String templateName = null; + for (StoragePoolDetailVO storagePoolDetailVO : details) { + switch (storagePoolDetailVO.getName()) { + case SP_HOST_PORT: + host = storagePoolDetailVO.getValue(); + break; + case SP_AUTH_TOKEN: + authToken = storagePoolDetailVO.getValue(); + break; + case SP_TEMPLATE: + templateName = storagePoolDetailVO.getValue(); + break; + } + } + if (host != null && authToken != null && templateName != null) { + return new SpConnectionDesc(host, authToken, templateName); + } else { + return updateStorageAndStorageDetails(url, poolId, poolDetails, storagePool); + } + } + + private static SpConnectionDesc updateStorageAndStorageDetails(String url, long poolId, + StoragePoolDetailsDao poolDetails, PrimaryDataStoreDao storagePool) { + SpConnectionDesc conn = new SpConnectionDesc(url); + poolDetails.persist(new StoragePoolDetailVO(poolId, SP_HOST_PORT, conn.getHostPort(), false)); + poolDetails.persist(new StoragePoolDetailVO(poolId, SP_AUTH_TOKEN, conn.getAuthToken(), false)); + poolDetails.persist(new StoragePoolDetailVO(poolId, SP_TEMPLATE, conn.getTemplateName(), false)); + StoragePoolVO pool = storagePool.findById(poolId); + pool.setUuid(conn.getTemplateName() + ";" + UUID.randomUUID().toString()); + storagePool.update(poolId, pool); + StorpoolUtil.spLog( + "Storage pool with id=%s and template's name=%s was updated and its connection details are hidden from UI.", + pool.getId(), conn.getTemplateName()); + return conn; + } + + public static class SpApiResponse { + private SpApiError error; + public JsonElement fullJson; + + public SpApiResponse() { + } + + public SpApiError getError() { + return this.error; + } + + public void setError(SpApiError error) { + this.error = error; + } + } + + public static String devPath(final String name) { + return String.format("%s%s", SP_DEV_PATH, name); + } + + private static SpApiResponse spApiRequest(HttpRequestBase req, String query, SpConnectionDesc conn) { + + if (conn == null) + conn = new SpConnectionDesc(""); + + if (conn.getHostPort() == null) { + throw new CloudRuntimeException("Invalid StorPool config. Missing SP_API_HTTP_HOST"); + } + + if (conn.getAuthToken() == null) { + throw new CloudRuntimeException("Invalid StorPool config. Missing SP_AUTH_TOKEN"); + } + + try (CloseableHttpClient httpclient = HttpClientBuilder.create().build()) { + final String qry = String.format("http://%s/ctrl/1.0/%s", conn.getHostPort(), query); + final URI uri = new URI(qry); + + req.setURI(uri); + req.addHeader("Authorization", String.format("Storpool v1:%s", conn.getAuthToken())); + + final HttpResponse resp = httpclient.execute(req); + + Gson gson = new Gson(); + BufferedReader br = new BufferedReader(new InputStreamReader(resp.getEntity().getContent())); + + JsonElement el = new JsonParser().parse(br); + + SpApiResponse apiResp = gson.fromJson(el, SpApiResponse.class); + apiResp.fullJson = el; + return apiResp; + } catch (UnsupportedEncodingException ex) { + throw new CloudRuntimeException(ex.getMessage()); + } catch (ClientProtocolException ex) { + throw new CloudRuntimeException(ex.getMessage()); + } catch (IOException ex) { + throw new CloudRuntimeException(ex.getMessage()); + } catch (URISyntaxException ex) { + throw new CloudRuntimeException(ex.getMessage()); + } + } + + private static SpApiResponse GET(String query, SpConnectionDesc conn) { + return spApiRequest(new HttpGet(), query, conn); + } + + private static SpApiResponse POST(String query, Object json, SpConnectionDesc conn) { + HttpPost req = new HttpPost(); + if (json != null) { + Gson gson = new Gson(); + String js = gson.toJson(json); + StringEntity input = new StringEntity(js, ContentType.APPLICATION_JSON); + log.info("Request:" + js); + req.setEntity(input); + } + + return spApiRequest(req, query, conn); + } + + public static boolean templateExists(SpConnectionDesc conn) { + SpApiResponse resp = GET("VolumeTemplateDescribe/" + conn.getTemplateName(), conn); + return resp.getError() == null ? true : objectExists(resp.getError()); + } + + public static boolean snapshotExists(final String name, SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/Snapshot/" + name, conn); + return resp.getError() == null ? true : objectExists(resp.getError()); + } + + public static JsonArray snapshotsList(SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/SnapshotsList", conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonArray data = obj.getAsJsonArray("data"); + return data; + } + + public static JsonArray volumesList(SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/VolumesList", conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonArray data = obj.getAsJsonArray("data"); + return data; + } + + private static boolean objectExists(SpApiError err) { + if (!err.getName().equals("objectDoesNotExist")) { + throw new CloudRuntimeException(err.getDescr()); + } + return false; + } + + public static Long snapshotSize(final String name, SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/Snapshot/" + name, conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + + if (resp.getError() != null && !objectExists(resp.getError())) { + return null; + } + JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + return data.getAsJsonPrimitive("size").getAsLong(); + } + + public static String getSnapshotClusterID(String name, SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/Snapshot/" + name, conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + + JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + JsonPrimitive clusterId = data.getAsJsonPrimitive("clusterId"); + return clusterId != null ? clusterId.getAsString() : null; + } + + public static String getVolumeClusterID(String name, SpConnectionDesc conn) { + SpApiResponse resp = GET("MultiCluster/Volume/" + name, conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + + JsonObject data = obj.getAsJsonArray("data").get(0).getAsJsonObject(); + JsonPrimitive clusterId = data.getAsJsonPrimitive("clusterId"); + return clusterId != null ? clusterId.getAsString() : null; + } + + public static SpApiResponse volumeCreate(final String name, final String parentName, final Long size, String vmUuid, + String vcPolicy, String csTag, Long iops, SpConnectionDesc conn) { + Map json = new LinkedHashMap<>(); + json.put("name", ""); + json.put("iops", iops); + json.put("parent", parentName); + json.put("size", size); + json.put("template", conn.getTemplateName()); + Map tags = StorPoolHelper.addStorPoolTags(name, vmUuid, csTag, vcPolicy); + json.put("tags", tags); + return POST("MultiCluster/VolumeCreate", json, conn); + } + + public static SpApiResponse volumeCreate(SpConnectionDesc conn) { + Map json = new LinkedHashMap<>(); + json.put("name", ""); + json.put("size", 512); + json.put("template", conn.getTemplateName()); + Map tags = new HashMap<>(); + tags.put("cs", "check-volume-is-on-host"); + json.put("tags", tags); + return POST("MultiCluster/VolumeCreate", json, conn); + } + + public static SpApiResponse volumeCopy(final String name, final String baseOn, String csTag, Long iops, + SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("baseOn", baseOn); + if (iops != null) { + json.put("iops", iops); + } + json.put("template", conn.getTemplateName()); + Map tags = StorPoolHelper.addStorPoolTags(name, null, csTag, null); + json.put("tags", tags); + return POST("MultiCluster/VolumeCreate", json, conn); + } + + public static SpApiResponse volumeUpdateRename(final String name, String newName, String uuid, + SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("rename", newName); + Map tags = new HashMap<>(); + tags.put("uuid", uuid); + json.put("tags", tags); + + return POST("MultiCluster/VolumeUpdate/" + name, json, conn); + } + + public static SpApiResponse volumeUpdate(final String name, final Long newSize, final Boolean shrinkOk, Long iops, + SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("iops", iops); + json.put("size", newSize); + json.put("shrinkOk", shrinkOk); + + return POST("MultiCluster/VolumeUpdate/" + name, json, conn); + } + + public static SpApiResponse volumeUpadateTags(final String name, final String uuid, Long iops, + SpConnectionDesc conn, String vcPolicy) { + Map json = new HashMap<>(); + Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, vcPolicy); + json.put("iops", iops); + json.put("tags", tags); + return POST("MultiCluster/VolumeUpdate/" + name, json, conn); + } + + public static SpApiResponse volumeUpadateCvmTags(final String name, final String uuid, SpConnectionDesc conn) { + Map json = new HashMap<>(); + Map tags = StorPoolHelper.addStorPoolTags(null, uuid, null, null); + json.put("tags", tags); + return POST("MultiCluster/VolumeUpdate/" + name, json, conn); + } + + public static SpApiResponse volumeUpadateVCTags(final String name, SpConnectionDesc conn, String vcPolicy) { + Map json = new HashMap<>(); + Map tags = StorPoolHelper.addStorPoolTags(null, null, null, vcPolicy); + json.put("tags", tags); + return POST("MultiCluster/VolumeUpdate/" + name, json, conn); + } + + public static SpApiResponse volumeSnapshot(final String volumeName, final String snapshotName, String vmUuid, + String csTag, String vcPolicy, SpConnectionDesc conn) { + Map json = new HashMap<>(); + Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, vcPolicy); + json.put("name", ""); + json.put("tags", tags); + + return POST("MultiCluster/VolumeSnapshot/" + volumeName, json, conn); + } + + public static SpApiResponse volumesGroupSnapshot(final List volumeTOs, final String vmUuid, + final String snapshotName, String csTag, SpConnectionDesc conn) { + Map json = new LinkedHashMap<>(); + Map tags = StorPoolHelper.addStorPoolTags(snapshotName, vmUuid, csTag, null); + List> volumes = new ArrayList<>(); + for (VolumeObjectTO volumeTO : volumeTOs) { + Map vol = new LinkedHashMap<>(); + String name = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeTO.getPath(), true); + vol.put("name", ""); + vol.put("volume", name); + volumes.add(vol); + } + json.put("tags", tags); + json.put("volumes", volumes); + log.info("json:" + json); + return POST("MultiCluster/VolumesGroupSnapshot", json, conn); + } + + public static SpApiResponse volumeRevert(final String name, final String snapshotName, SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("toSnapshot", snapshotName); + return POST("MultiCluster/VolumeRevert/" + name, json, conn); + } + + public static SpApiResponse volumeFreeze(final String volumeName, SpConnectionDesc conn) { + return POST("MultiCluster/VolumeFreeze/" + volumeName, null, conn); + } + + public static SpApiResponse volumeAcquire(final String volumeName, SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("onRemoteAttached", "detachForce"); + return POST("MultiCluster/VolumeAcquire/" + volumeName, json, conn); + } + + public static SpApiResponse volumeDelete(final String name, SpConnectionDesc conn) { + Map json = new HashMap<>(); + json.put("onAttached", "detachForce"); + return POST("MultiCluster/VolumeDelete/" + name, json, conn); + } + + public static SpApiResponse snapshotDelete(final String name, SpConnectionDesc conn) { + SpApiResponse resp = detachAllForced(name, true, conn); + return resp.getError() == null ? POST("MultiCluster/SnapshotDelete/" + name, null, conn) : resp; + } + + public static SpApiResponse detachAllForced(final String name, final boolean snapshot, SpConnectionDesc conn) { + final String type = snapshot ? "snapshot" : "volume"; + List> json = new ArrayList<>(); + Map reassignDesc = new HashMap<>(); + reassignDesc.put(type, name); + reassignDesc.put("detach", "all"); + reassignDesc.put("force", true); + json.add(reassignDesc); + + return POST("MultiCluster/VolumesReassign", json, conn); + } + + public static String getSnapshotNameFromResponse(SpApiResponse resp, boolean tildeNeeded, String globalIdOrRemote) { + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonPrimitive data = obj.getAsJsonObject("data").getAsJsonPrimitive(globalIdOrRemote); + String name = data != null ? data.getAsString() : null; + name = name != null ? !tildeNeeded ? name : "~" + name : name; + return name; + } + + public static String getNameFromResponse(SpApiResponse resp, boolean tildeNeeded) { + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonPrimitive data = obj.getAsJsonObject("data").getAsJsonPrimitive("name"); + String name = data != null ? data.getAsString() : null; + name = name != null ? name.startsWith("~") && !tildeNeeded ? name.split("~")[1] : name : name; + return name; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java new file mode 100644 index 000000000000..74a52c3fce13 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java @@ -0,0 +1,575 @@ +/* + * 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 + * with 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.storage.motion; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; +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.storage.RemoteHostEndPoint; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.commons.collections.MapUtils; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MigrateAnswer; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.MigrateCommand.MigrateDiskInfo; +import com.cloud.agent.api.ModifyTargetsAnswer; +import com.cloud.agent.api.ModifyTargetsCommand; +import com.cloud.agent.api.PrepareForMigrationCommand; +import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StorageManager; +import com.cloud.storage.VMTemplateDetailVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.GuestOSCategoryDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.VMInstanceDao; + +@Component +public class StorPoolDataMotionStrategy implements DataMotionStrategy { + private static final Logger log = Logger.getLogger(StorPoolDataMotionStrategy.class); + + @Inject + private SnapshotDataFactory _snapshotDataFactory; + @Inject + private DataStoreManager _dataStore; + @Inject + private ConfigurationDao _configDao; + @Inject + private EndPointSelector _selector; + @Inject + private TemplateDataStoreDao _templStoreDao; + @Inject + private ClusterDao _clusterDao; + @Inject + private HostDao _hostDao; + @Inject + private SnapshotDetailsDao _snapshotDetailsDao; + @Inject + private VMTemplateDetailsDao _vmTemplateDetailsDao; + @Inject + private SnapshotDataStoreDao _snapshotStoreDao; + @Inject + private StoragePoolDetailsDao _storagePoolDetails; + @Inject + private PrimaryDataStoreDao _storagePool; + @Inject + private VolumeDao _volumeDao; + @Inject + private VolumeDataFactory _volumeDataFactory; + @Inject + private VMInstanceDao _vmDao; + @Inject + private GuestOSDao _guestOsDao; + @Inject + private VolumeService _volumeService; + @Inject + private GuestOSCategoryDao _guestOsCategoryDao; + @Inject + private SnapshotDao _snapshotDao; + @Inject + private AgentManager _agentManager; + @Inject + private PrimaryDataStoreDao _storagePoolDao; + + @Override + public StrategyPriority canHandle(DataObject srcData, DataObject destData) { + DataObjectType srcType = srcData.getType(); + DataObjectType dstType = destData.getType(); + if (srcType == DataObjectType.SNAPSHOT && dstType == DataObjectType.TEMPLATE + && StorPoolConfigurationManager.BypassSecondaryStorage.value()) { + SnapshotInfo sinfo = (SnapshotInfo) srcData; + VolumeInfo volume = sinfo.getBaseVolume(); + StoragePoolVO storagePool = _storagePool.findById(volume.getPoolId()); + if (!storagePool.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + return StrategyPriority.CANT_HANDLE; + } + String snapshotName = StorPoolHelper.getSnapshotName(sinfo.getId(), sinfo.getUuid(), _snapshotStoreDao, + _snapshotDetailsDao); + StorpoolUtil.spLog("StorPoolDataMotionStrategy.canHandle snapshot name=%s", snapshotName); + if (snapshotName != null) { + return StrategyPriority.HIGHEST; + } + } + return StrategyPriority.CANT_HANDLE; + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, + AsyncCompletionCallback callback) { + SnapshotObjectTO snapshot = (SnapshotObjectTO) srcData.getTO(); + TemplateObjectTO template = (TemplateObjectTO) destData.getTO(); + DataStore store = _dataStore.getDataStore(snapshot.getVolume().getDataStore().getUuid(), + snapshot.getVolume().getDataStore().getRole()); + SnapshotInfo sInfo = _snapshotDataFactory.getSnapshot(snapshot.getId(), store); + + VolumeInfo vInfo = sInfo.getBaseVolume(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(vInfo.getDataStore().getUuid(), + vInfo.getDataStore().getId(), _storagePoolDetails, _storagePool); + String name = template.getUuid(); + String volumeName = ""; + + String parentName = StorPoolHelper.getSnapshotName(sInfo.getId(), sInfo.getUuid(), _snapshotStoreDao, + _snapshotDetailsDao); + // TODO volume tags cs - template + SpApiResponse res = StorpoolUtil.volumeCreate(name, parentName, sInfo.getSize(), null, "no", "template", null, + conn); + CopyCmdAnswer answer = null; + String err = null; + if (res.getError() != null) { + log.debug(String.format("Could not create volume from snapshot with ID=%s", snapshot.getId())); + StorpoolUtil.spLog("Volume create failed with error=%s", res.getError().getDescr()); + err = res.getError().getDescr(); + } else { + volumeName = StorpoolUtil.getNameFromResponse(res, true); + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sInfo.getId(), sInfo.getUuid()); + + snapshot.setPath(snapshotDetails.getValue()); + Command backupSnapshot = new StorpoolBackupTemplateFromSnapshotCommand(snapshot, template, + StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, _configDao), + VirtualMachineManager.ExecuteInSequence.value()); + + try { + // final String snapName = + // StorpoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) + // srcData).getPath(), true); + Long clusterId = StorPoolHelper.findClusterIdByGlobalId(parentName, _clusterDao); + EndPoint ep2 = clusterId != null + ? RemoteHostEndPoint + .getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, _hostDao)) + : _selector.select(sInfo, destData); + if (ep2 == null) { + err = "No remote endpoint to send command, check if host or ssvm is down?"; + } else { + answer = (CopyCmdAnswer) ep2.sendMessage(backupSnapshot); + if (answer != null && answer.getResult()) { + SpApiResponse resSnapshot = StorpoolUtil.volumeFreeze(volumeName, conn); + if (resSnapshot.getError() != null) { + log.debug(String.format("Could not snapshot volume with ID=%s", snapshot.getId())); + StorpoolUtil.spLog("Volume freeze failed with error=%s", resSnapshot.getError().getDescr()); + err = resSnapshot.getError().getDescr(); + StorpoolUtil.volumeDelete(volumeName, conn); + } else { + StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), + StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(res, false)), _templStoreDao); + } + } else { + err = "Could not copy template to secondary " + answer.getResult(); + StorpoolUtil.volumeDelete(StorpoolUtil.getNameFromResponse(res, true), conn); + } + } + } catch (CloudRuntimeException e) { + err = e.getMessage(); + } + } + _vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, + String.valueOf(vInfo.getDataStore().getId()), false)); + StorpoolUtil.spLog("StorPoolDataMotionStrategy.copyAsync Creating snapshot=%s for StorPool template=%s", + volumeName, conn.getTemplateName()); + final CopyCommandResult cmd = new CopyCommandResult(null, answer); + cmd.setResult(err); + callback.complete(cmd); + } + + @Override + public StrategyPriority canHandle(Map volumeMap, Host srcHost, Host destHost) { + return canHandleLiveMigrationOnStorPool(volumeMap, srcHost, destHost); + } + + final StrategyPriority canHandleLiveMigrationOnStorPool(Map volumeMap, Host srcHost, + Host destHost) { + if (srcHost.getId() != destHost.getId() && isDestinationStorPoolPrimaryStorage(volumeMap)) { + return StrategyPriority.HIGHEST; + } + return StrategyPriority.CANT_HANDLE; + } + + private boolean isDestinationStorPoolPrimaryStorage(Map volumeMap) { + if (MapUtils.isNotEmpty(volumeMap)) { + for (DataStore dataStore : volumeMap.values()) { + StoragePoolVO storagePoolVO = _storagePool.findById(dataStore.getId()); + if (storagePoolVO == null + || !storagePoolVO.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + return false; + } + } + } else { + return false; + } + return true; + } + + @Override + public void copyAsync(Map volumeDataStoreMap, VirtualMachineTO vmTO, Host srcHost, + Host destHost, AsyncCompletionCallback callback) { + String errMsg = null; + String newVolName = null; + SpConnectionDesc conn = null; + + try { + if (srcHost.getHypervisorType() != HypervisorType.KVM) { + throw new CloudRuntimeException(String.format("Invalid hypervisor type [%s]. Only KVM supported", srcHost.getHypervisorType())); + } + + VMInstanceVO vmInstance = _vmDao.findById(vmTO.getId()); + vmTO.setState(vmInstance.getState()); + List migrateDiskInfoList = new ArrayList(); + + Map migrateStorage = new HashMap<>(); + Map srcVolumeInfoToDestVolumeInfo = new HashMap<>(); + + for (Map.Entry entry : volumeDataStoreMap.entrySet()) { + VolumeInfo srcVolumeInfo = entry.getKey(); + DataStore destDataStore = entry.getValue(); + + VolumeVO srcVolume = _volumeDao.findById(srcVolumeInfo.getId()); + StoragePoolVO destStoragePool = _storagePool.findById(destDataStore.getId()); + + VolumeVO destVolume = duplicateVolumeOnAnotherStorage(srcVolume, destStoragePool); + + VolumeInfo destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore); + + destVolumeInfo.processEvent(Event.MigrationCopyRequested); + destVolumeInfo.processEvent(Event.MigrationCopySucceeded); + destVolumeInfo.processEvent(Event.MigrationRequested); + + conn = StorpoolUtil.getSpConnection(destDataStore.getUuid(), destDataStore.getId(), _storagePoolDetails, + _storagePool); + SpApiResponse resp = StorpoolUtil.volumeCreate(srcVolume.getUuid(), null, srcVolume.getSize(), + vmTO.getUuid(), null, "volume", srcVolume.getMaxIops(), conn); + + if (resp.getError() == null) { + newVolName = StorpoolUtil.getNameFromResponse(resp, true); + } + + String volumeName = StorpoolUtil.getNameFromResponse(resp, false); + destVolume.setPath(StorpoolUtil.devPath(volumeName)); + _volumeDao.update(destVolume.getId(), destVolume); + destVolume = _volumeDao.findById(destVolume.getId()); + + destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore); + + String destPath = generateDestPath(destHost, destStoragePool, destVolumeInfo); + + MigrateCommand.MigrateDiskInfo migrateDiskInfo = configureMigrateDiskInfo(srcVolumeInfo, destPath); + migrateDiskInfoList.add(migrateDiskInfo); + + migrateStorage.put(srcVolumeInfo.getPath(), migrateDiskInfo); + + srcVolumeInfoToDestVolumeInfo.put(srcVolumeInfo, destVolumeInfo); + } + + PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO); + + try { + Answer pfma = _agentManager.send(destHost.getId(), pfmc); + + if (pfma == null || !pfma.getResult()) { + String details = pfma != null ? pfma.getDetails() : "null answer returned"; + errMsg = String.format("Unable to prepare for migration due to the following: %s", details); + + throw new AgentUnavailableException(errMsg, destHost.getId()); + } + } catch (final OperationTimedoutException e) { + errMsg = String.format("Operation timed out due to %s", e.getMessage()); + throw new AgentUnavailableException(errMsg, destHost.getId()); + } + + VMInstanceVO vm = _vmDao.findById(vmTO.getId()); + boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()) + .getName().equalsIgnoreCase("Windows"); + + MigrateCommand migrateCommand = new MigrateCommand(vmTO.getName(), + destHost.getPrivateIpAddress(), isWindows, vmTO, true); + migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value()); + migrateCommand.setMigrateStorage(migrateStorage); + migrateCommand.setMigrateStorageManaged(true); + migrateCommand.setMigrateDiskInfoList(migrateDiskInfoList); + + boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value(); + + migrateCommand.setAutoConvergence(kvmAutoConvergence); + + MigrateAnswer migrateAnswer = (MigrateAnswer) _agentManager.send(srcHost.getId(), migrateCommand); + + boolean success = migrateAnswer != null && migrateAnswer.getResult(); + + handlePostMigration(success, srcVolumeInfoToDestVolumeInfo, vmTO, destHost); + + if (migrateAnswer == null) { + throw new CloudRuntimeException("Unable to get an answer to the migrate command"); + } + + if (!migrateAnswer.getResult()) { + errMsg = migrateAnswer.getDetails(); + + throw new CloudRuntimeException(errMsg); + } + } catch (AgentUnavailableException | OperationTimedoutException | CloudRuntimeException ex) { + + errMsg = String.format( + "Copy volume(s) of VM [%s] to storage(s) [%s] and VM to host [%s] failed in StorPoolDataMotionStrategy.copyAsync. Error message: [%s].", + vmTO.getId(), srcHost.getId(), destHost.getId(), ex.getMessage()); + log.error(errMsg, ex); + + throw new CloudRuntimeException(errMsg); + } finally { + if (errMsg != null) { + deleteVolumeOnFail(newVolName, conn); + } + CopyCmdAnswer copyCmdAnswer = new CopyCmdAnswer(errMsg); + + CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer); + + result.setResult(errMsg); + + callback.complete(result); + } + } + + private void deleteVolumeOnFail(String newVolName, SpConnectionDesc conn) { + if (newVolName != null && conn != null) { + StorpoolUtil.volumeDelete(newVolName, conn); + } + } + + private VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePoolVO storagePoolVO) { + Long lastPoolId = volume.getPoolId(); + + VolumeVO newVol = new VolumeVO(volume); + + newVol.setInstanceId(null); + newVol.setChainInfo(null); + newVol.setPath(null); + newVol.setFolder(null); + newVol.setPodId(storagePoolVO.getPodId()); + newVol.setPoolId(storagePoolVO.getId()); + newVol.setLastPoolId(lastPoolId); + + return _volumeDao.persist(newVol); + } + + private void handlePostMigration(boolean success, Map srcVolumeInfoToDestVolumeInfo, + VirtualMachineTO vmTO, Host destHost) { + if (!success) { + try { + PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO); + + pfmc.setRollback(true); + + Answer pfma = _agentManager.send(destHost.getId(), pfmc); + + if (pfma == null || !pfma.getResult()) { + String details = pfma != null ? pfma.getDetails() : "null answer returned"; + String msg = "Unable to rollback prepare for migration due to the following: " + details; + + throw new AgentUnavailableException(msg, destHost.getId()); + } + } catch (Exception e) { + log.debug("Failed to disconnect one or more (original) dest volumes", e); + } + } + + for (Map.Entry entry : srcVolumeInfoToDestVolumeInfo.entrySet()) { + VolumeInfo srcVolumeInfo = entry.getKey(); + VolumeInfo destVolumeInfo = entry.getValue(); + + if (success) { + srcVolumeInfo.processEvent(Event.OperationSuccessed); + destVolumeInfo.processEvent(Event.OperationSuccessed); + + _volumeDao.updateUuid(srcVolumeInfo.getId(), destVolumeInfo.getId()); + + VolumeVO volumeVO = _volumeDao.findById(destVolumeInfo.getId()); + + volumeVO.setFormat(ImageFormat.QCOW2); + + _volumeDao.update(volumeVO.getId(), volumeVO); + + try { + _volumeService.destroyVolume(srcVolumeInfo.getId()); + + srcVolumeInfo = _volumeDataFactory.getVolume(srcVolumeInfo.getId()); + + AsyncCallFuture destroyFuture = _volumeService.expungeVolumeAsync(srcVolumeInfo); + + if (destroyFuture.get().isFailed()) { + log.debug("Failed to clean up source volume on storage"); + } + } catch (Exception e) { + log.debug("Failed to clean up source volume on storage", e); + } + + // Update the volume ID for snapshots on secondary storage + if (!_snapshotDao.listByVolumeId(srcVolumeInfo.getId()).isEmpty()) { + _snapshotDao.updateVolumeIds(srcVolumeInfo.getId(), destVolumeInfo.getId()); + _snapshotStoreDao.updateVolumeIds(srcVolumeInfo.getId(), destVolumeInfo.getId()); + } + } else { + try { + disconnectHostFromVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.getPath()); + } catch (Exception e) { + log.debug("Failed to disconnect (new) dest volume", e); + } + + try { + _volumeService.revokeAccess(destVolumeInfo, destHost, destVolumeInfo.getDataStore()); + } catch (Exception e) { + log.debug("Failed to revoke access from dest volume", e); + } + + destVolumeInfo.processEvent(Event.OperationFailed); + srcVolumeInfo.processEvent(Event.OperationFailed); + + try { + _volumeService.destroyVolume(destVolumeInfo.getId()); + + destVolumeInfo = _volumeDataFactory.getVolume(destVolumeInfo.getId()); + + AsyncCallFuture destroyFuture = _volumeService.expungeVolumeAsync(destVolumeInfo); + + if (destroyFuture.get().isFailed()) { + log.debug("Failed to clean up dest volume on storage"); + } + } catch (Exception e) { + log.debug("Failed to clean up dest volume on storage", e); + } + } + } + } + + private String generateDestPath(Host destHost, StoragePoolVO destStoragePool, VolumeInfo destVolumeInfo) { + return connectHostToVolume(destHost, destVolumeInfo.getPoolId(), destVolumeInfo.getPath()); + } + + private String connectHostToVolume(Host host, long storagePoolId, String iqn) { + ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, true); + + return sendModifyTargetsCommand(modifyTargetsCommand, host.getId()).get(0); + } + + private void disconnectHostFromVolume(Host host, long storagePoolId, String iqn) { + ModifyTargetsCommand modifyTargetsCommand = getModifyTargetsCommand(storagePoolId, iqn, false); + + sendModifyTargetsCommand(modifyTargetsCommand, host.getId()); + } + + private ModifyTargetsCommand getModifyTargetsCommand(long storagePoolId, String iqn, boolean add) { + StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + + Map details = new HashMap<>(); + + details.put(ModifyTargetsCommand.IQN, iqn); + details.put(ModifyTargetsCommand.STORAGE_TYPE, storagePool.getPoolType().name()); + details.put(ModifyTargetsCommand.STORAGE_UUID, storagePool.getUuid()); + details.put(ModifyTargetsCommand.STORAGE_HOST, storagePool.getHostAddress()); + details.put(ModifyTargetsCommand.STORAGE_PORT, String.valueOf(storagePool.getPort())); + + ModifyTargetsCommand cmd = new ModifyTargetsCommand(); + + List> targets = new ArrayList<>(); + + targets.add(details); + + cmd.setTargets(targets); + cmd.setApplyToAllHostsInCluster(true); + cmd.setAdd(add); + cmd.setTargetTypeToRemove(ModifyTargetsCommand.TargetTypeToRemove.DYNAMIC); + + return cmd; + } + + private List sendModifyTargetsCommand(ModifyTargetsCommand cmd, long hostId) { + ModifyTargetsAnswer modifyTargetsAnswer = (ModifyTargetsAnswer) _agentManager.easySend(hostId, cmd); + + if (modifyTargetsAnswer == null) { + throw new CloudRuntimeException("Unable to get an answer to the modify targets command"); + } + + if (!modifyTargetsAnswer.getResult()) { + String msg = "Unable to modify targets on the following host: " + hostId; + + throw new CloudRuntimeException(msg); + } + + return modifyTargetsAnswer.getConnectedPaths(); + } + + protected MigrateCommand.MigrateDiskInfo configureMigrateDiskInfo(VolumeInfo srcVolumeInfo, String destPath) { + return new MigrateCommand.MigrateDiskInfo(srcVolumeInfo.getPath(), + MigrateCommand.MigrateDiskInfo.DiskType.BLOCK, MigrateCommand.MigrateDiskInfo.DriverType.RAW, + MigrateCommand.MigrateDiskInfo.Source.DEV, destPath); + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java new file mode 100644 index 000000000000..2ec30f1db84f --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.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 + * with 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.storage.snapshot; + +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; + +public class StorPoolConfigurationManager implements Configurable { + + public static final ConfigKey BypassSecondaryStorage = new ConfigKey(Boolean.class, "sp.bypass.secondary.storage", "Advanced", "false", + "For StorPool Managed storage backup to secondary", true, ConfigKey.Scope.Global, null); + public static final ConfigKey StorPoolClusterId = new ConfigKey(String.class, "sp.cluster.id", "Advanced", "n/a", + "For StorPool multi cluster authorization", true, ConfigKey.Scope.Cluster, null); + + @Override + public String getConfigComponentName() { + return StorPoolConfigurationManager.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId }; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java new file mode 100644 index 000000000000..8eb2b05ef6b2 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java @@ -0,0 +1,288 @@ +// 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 +// with 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.storage.snapshot; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotDetailsDao; +import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.fsm.NoTransitionException; + + +@Component +public class StorpoolSnapshotStrategy implements SnapshotStrategy { + private static final Logger log = Logger.getLogger(StorpoolSnapshotStrategy.class); + + @Inject + private SnapshotDao _snapshotDao; + @Inject + private PrimaryDataStoreDao _primaryDataStoreDao; + @Inject + private VolumeDao _volumeDao; + @Inject + private SnapshotDataStoreDao _snapshotStoreDao; + @Inject + private SnapshotDetailsDao _snapshotDetailsDao; + @Inject + private SnapshotService snapshotSvr; + @Inject + private SnapshotDataFactory snapshotDataFactory; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + + @Override + public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) { + SnapshotObject snapshotObj = (SnapshotObject) snapshotInfo; + try { + snapshotObj.processEvent(Snapshot.Event.BackupToSecondary); + snapshotObj.processEvent(Snapshot.Event.OperationSucceeded); + } catch (NoTransitionException ex) { + StorpoolUtil.spLog("Failed to change state: " + ex.toString()); + try { + snapshotObj.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException ex2) { + StorpoolUtil.spLog("Failed to change state: " + ex2.toString()); + } + } + return snapshotInfo; + } + + @Override + public boolean deleteSnapshot(Long snapshotId) { + + final SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId); + VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshotVO.getVolumeId()); + String name = StorPoolHelper.getSnapshotName(snapshotId, snapshotVO.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); + boolean res = false; + // clean-up snapshot from Storpool storage pools + StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); + if (storage.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + SpApiResponse resp = StorpoolUtil.snapshotDelete(name, conn); + if (resp.getError() != null) { + final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); + log.error(err); + StorpoolUtil.spLog(err); + } else { + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid()); + if (snapshotDetails != null) { + _snapshotDetailsDao.removeDetails(snapshotId); + } + res = deleteSnapshotFromDb(snapshotId); + StorpoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfuly=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + } + } + + return res; + } + + @Override + public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { + StorpoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op); + + if (op != SnapshotOperation.DELETE) { + return StrategyPriority.CANT_HANDLE; + } + + VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId()); + StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); + String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); + if (storage.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + if (name != null) { + StorpoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); + + return StrategyPriority.HIGHEST; + } + } + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), snapshot.getUuid()); + if (snapshotDetails != null) { + _snapshotDetailsDao.remove(snapshotDetails.getId()); + } + return StrategyPriority.CANT_HANDLE; + } + + private boolean deleteSnapshotChain(SnapshotInfo snapshot) { + log.debug("delete snapshot chain for snapshot: " + snapshot.getId()); + boolean result = false; + boolean resultIsSet = false; + try { + while (snapshot != null && + (snapshot.getState() == Snapshot.State.Destroying || snapshot.getState() == Snapshot.State.Destroyed || snapshot.getState() == Snapshot.State.Error)) { + SnapshotInfo child = snapshot.getChild(); + + if (child != null) { + log.debug("the snapshot has child, can't delete it on the storage"); + break; + } + log.debug("Snapshot: " + snapshot.getId() + " doesn't have children, so it's ok to delete it and its parents"); + SnapshotInfo parent = snapshot.getParent(); + boolean deleted = false; + if (parent != null) { + if (parent.getPath() != null && parent.getPath().equalsIgnoreCase(snapshot.getPath())) { + log.debug("for empty delta snapshot, only mark it as destroyed in db"); + snapshot.processEvent(Event.DestroyRequested); + snapshot.processEvent(Event.OperationSuccessed); + deleted = true; + if (!resultIsSet) { + result = true; + resultIsSet = true; + } + } + } + if (!deleted) { + SnapshotInfo snap = snapshotDataFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); + if (StorpoolStorageAdaptor.getVolumeNameFromPath(snap.getPath(), true) == null) { + try { + boolean r = snapshotSvr.deleteSnapshot(snapshot); + if (r) { + List cacheSnaps = snapshotDataFactory.listSnapshotOnCache(snapshot.getId()); + for (SnapshotInfo cacheSnap : cacheSnaps) { + log.debug("Delete snapshot " + snapshot.getId() + " from image cache store: " + cacheSnap.getDataStore().getName()); + cacheSnap.delete(); + } + } + if (!resultIsSet) { + result = r; + resultIsSet = true; + } + } catch (Exception e) { + log.debug("Failed to delete snapshot on storage. ", e); + } + } + } else { + result = true; + } + snapshot = parent; + } + } catch (Exception e) { + log.debug("delete snapshot failed: ", e); + } + return result; + } + + private boolean deleteSnapshotFromDb(Long snapshotId) { + SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId); + + if (snapshotVO.getState() == Snapshot.State.Allocated) { + _snapshotDao.remove(snapshotId); + return true; + } + + if (snapshotVO.getState() == Snapshot.State.Destroyed) { + return true; + } + + if (Snapshot.State.Error.equals(snapshotVO.getState())) { + List storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotId); + for (SnapshotDataStoreVO ref : storeRefs) { + _snapshotStoreDao.expunge(ref.getId()); + } + _snapshotDao.remove(snapshotId); + return true; + } + + if (snapshotVO.getState() == Snapshot.State.CreatedOnPrimary) { + snapshotVO.setState(Snapshot.State.Destroyed); + _snapshotDao.update(snapshotId, snapshotVO); + return true; + } + + if (!Snapshot.State.BackedUp.equals(snapshotVO.getState()) && !Snapshot.State.Error.equals(snapshotVO.getState()) && + !Snapshot.State.Destroying.equals(snapshotVO.getState())) { + throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is in " + snapshotVO.getState() + " Status"); + } + + SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image); + if (snapshotOnImage == null) { + log.debug("Can't find snapshot on backup storage, delete it in db"); + _snapshotDao.remove(snapshotId); + return true; + } + + SnapshotObject obj = (SnapshotObject)snapshotOnImage; + try { + obj.processEvent(Snapshot.Event.DestroyRequested); + } catch (NoTransitionException e) { + log.debug("Failed to set the state to destroying: ", e); + return false; + } + + try { + boolean result = deleteSnapshotChain(snapshotOnImage); + obj.processEvent(Snapshot.Event.OperationSucceeded); + if (result) { + SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Primary); + if (snapshotOnPrimary != null) { + snapshotOnPrimary.setState(State.Destroyed); + _snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary); + } + } + } catch (Exception e) { + log.debug("Failed to delete snapshot: ", e); + try { + obj.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + log.debug("Failed to change snapshot state: " + e.toString()); + } + return false; + } + return true; + } + + @Override + public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { + return null; + } + + @Override + public boolean revertSnapshot(SnapshotInfo snapshot) { + return false; + } + + @Override + public void postSnapshotCreation(SnapshotInfo snapshot) { + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java new file mode 100644 index 000000000000..9691c0c64c24 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java @@ -0,0 +1,380 @@ +// +//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 +//with 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.storage.snapshot; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.storage.vmsnapshot.DefaultVMSnapshotStrategy; +import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventUtils; +import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.VolumeDetailVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import com.cloud.vm.snapshot.dao.VMSnapshotDetailsDao; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +@Component +public class StorpoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { + private static final Logger log = Logger.getLogger(StorpoolVMSnapshotStrategy.class); + + @Inject + private VMSnapshotHelper vmSnapshotHelper; + @Inject + private UserVmDao userVmDao; + @Inject + private VMSnapshotDao vmSnapshotDao; + @Inject + private VolumeDao volumeDao; + @Inject + private DiskOfferingDao diskOfferingDao; + @Inject + private PrimaryDataStoreDao storagePool; + @Inject + private VMSnapshotDetailsDao vmSnapshotDetailsDao; + @Inject + private VolumeDataFactory volFactory; + @Inject + private VolumeDetailsDao volumeDetailsDao; + @Inject + private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject + private DataStoreManager dataStoreManager; + int _wait; + + @Override + public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { + log.info("KVMVMSnapshotStrategy take snapshot"); + UserVm userVm = userVmDao.findById(vmSnapshot.getVmId()); + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot; + + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.CreateRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException("No transiontion " + e.getMessage()); + } + + boolean result = false; + try { + + List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); + DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + + long prev_chain_size = 0; + long virtual_size = 0; + for (VolumeObjectTO volume : volumeTOs) { + virtual_size += volume.getSize(); + VolumeVO volumeVO = volumeDao.findById(volume.getId()); + prev_chain_size += volumeVO.getVmSnapshotChainSize() == null ? 0 : volumeVO.getVmSnapshotChainSize(); + } + + VMSnapshotTO current = null; + VMSnapshotVO currentSnapshot = vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId()); + if (currentSnapshot != null) { + current = vmSnapshotHelper.getSnapshotWithParents(currentSnapshot); + } + + if (current == null) { + vmSnapshotVO.setParent(null); + } else { + vmSnapshotVO.setParent(current.getId()); + } + + SpApiResponse resp = StorpoolUtil.volumesGroupSnapshot(volumeTOs, userVm.getUuid(), vmSnapshotVO.getUuid(), "group", conn); + JsonObject obj = resp.fullJson.getAsJsonObject(); + JsonArray snapshots = obj.getAsJsonObject("data").getAsJsonArray("snapshots"); + StorpoolUtil.spLog("Volumes=%s attached to virtual machine", volumeTOs.toString()); + for (VolumeObjectTO vol : volumeTOs) { + for (JsonElement jsonElement : snapshots) { + JsonObject snapshotObject = jsonElement.getAsJsonObject(); + String snapshot = StorpoolUtil + .devPath(snapshotObject.getAsJsonPrimitive(StorpoolUtil.GLOBAL_ID).getAsString()); + if (snapshotObject.getAsJsonPrimitive("volume").getAsString().equals(StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true)) + || snapshotObject.getAsJsonPrimitive("volumeGlobalId").getAsString().equals(StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), false))) { + VMSnapshotDetailsVO vmSnapshotDetailsVO = new VMSnapshotDetailsVO(vmSnapshot.getId(), vol.getUuid(), snapshot, false); + vmSnapshotDetailsDao.persist(vmSnapshotDetailsVO); + Long poolId = volumeDao.findById(vol.getId()).getPoolId(); + if (poolId != null) { + VMSnapshotDetailsVO vmSnapshotDetailStoragePoolId = new VMSnapshotDetailsVO( + vmSnapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(poolId), false); + vmSnapshotDetailsDao.persist(vmSnapshotDetailStoragePoolId); + } + StorpoolUtil.spLog("Snapshot=%s of volume=%s for a group snapshot=%s.", snapshot, vol.getUuid(), vmSnapshot.getUuid()); + } + } + } + + if (resp.getError() == null) { + StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.takeSnapshot answer=%s", resp.getError()); + finalizeCreate(vmSnapshotVO, volumeTOs); + result = vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + long new_chain_size = 0; + for (VolumeObjectTO volumeObjectTO : volumeTOs) { + publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_CREATE, vmSnapshot, userVm, volumeObjectTO); + new_chain_size += volumeObjectTO.getSize(); + log.info("EventTypes.EVENT_VM_SNAPSHOT_CREATE publishUsageEvent" + volumeObjectTO); + } + publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_ON_PRIMARY, vmSnapshot, userVm, new_chain_size - prev_chain_size, virtual_size); + } else { + throw new CloudRuntimeException("Could not create vm snapshot"); + } + return vmSnapshot; + } catch (Exception e) { + log.debug("Could not create VM snapshot:" + e.getMessage()); + throw new CloudRuntimeException("Could not create VM snapshot:" + e.getMessage()); + } finally { + if (!result) { + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + log.info(String.format("VMSnapshot.Event.OperationFailed vmSnapshot=%s", vmSnapshot)); + } catch (NoTransitionException nte) { + log.error("Cannot set vm state:" + nte.getMessage()); + } + } + } + } + + @Override + public StrategyPriority canHandle(VMSnapshot vmSnapshot) { + return areAllVolumesOnStorPool(vmSnapshot.getVmId()); + } + + public StrategyPriority canHandle(Long vmId, Long rootPoolId, boolean snapshotMemory) { + if (snapshotMemory) { + return StrategyPriority.CANT_HANDLE; + } + return areAllVolumesOnStorPool(vmId); + } + + private StrategyPriority areAllVolumesOnStorPool(Long vmId) { + List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmId); + if (volumeTOs == null || volumeTOs.isEmpty()) { + return StrategyPriority.CANT_HANDLE; + } + for (VolumeObjectTO volumeTO : volumeTOs) { + Long poolId = volumeTO.getPoolId(); + StoragePoolVO pool = storagePool.findById(poolId); + if (!pool.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + return StrategyPriority.CANT_HANDLE; + } + } + return StrategyPriority.HIGHEST; + } + + @Override + public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { + UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId()); + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot; + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.ExpungeRequested); + } catch (NoTransitionException e) { + log.debug("Failed to change vm snapshot state with event ExpungeRequested"); + throw new CloudRuntimeException( + "Failed to change vm snapshot state with event ExpungeRequested: " + e.getMessage()); + } + + List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId()); + DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + + SpApiResponse resp = null; + for (VolumeObjectTO volumeObjectTO : volumeTOs) { + String err = null; + VMSnapshotDetailsVO snapshotDetailsVO = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), volumeObjectTO.getUuid()); + String snapshotName = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); + if (snapshotName == null) { + err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", + vmSnapshot.getUuid(), volumeObjectTO.getUuid()); + log.error("Could not delete snapshot for vm:" + err); + } + StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.deleteVMSnapshot snapshotName=%s", snapshotName); + resp = StorpoolUtil.snapshotDelete(snapshotName, conn); + if (resp.getError() != null) { + err = String.format("Could not delete storpool vm error=%s", resp.getError()); + log.error("Could not delete snapshot for vm:" + err); + } else { + // do we need to clean database? + if (snapshotDetailsVO != null) { + vmSnapshotDetailsDao.remove(snapshotDetailsVO.getId()); + } + } + if (err != null) { + StorpoolUtil.spLog( + "StorpoolVMSnapshotStrategy.deleteVMSnapshot delete snapshot=%s of gropusnapshot=%s failed due to %s", + snapshotName, userVm.getInstanceName(), err); + throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + + userVm.getInstanceName() + " failed due to " + err); + } + } + vmSnapshotDetailsDao.removeDetails(vmSnapshot.getId()); + + finalizeDelete(vmSnapshotVO, volumeTOs); + long full_chain_size = 0; + for (VolumeObjectTO volumeTo : volumeTOs) { + publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_DELETE, vmSnapshot, userVm, volumeTo); + full_chain_size += volumeTo.getSize(); + } + publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_OFF_PRIMARY, vmSnapshot, userVm, full_chain_size, 0L); + return true; + } + + @Override + public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { + log.debug("Revert vm snapshot"); + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO) vmSnapshot; + UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId()); + + if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVO.getType() == VMSnapshot.Type.Disk) { + throw new CloudRuntimeException("Virtual machine should be in stopped state for revert operation"); + } + + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.RevertRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + boolean result = false; + try { + List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); + + DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + for (VolumeObjectTO volumeObjectTO : volumeTOs) { + String err = null; + VMSnapshotDetailsVO snapshotDetailsVO = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), + volumeObjectTO.getUuid()); + String snapshotName = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); + if (snapshotName == null) { + err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", + vmSnapshot.getUuid(), volumeObjectTO.getUuid()); + log.error("Could not delete snapshot for vm:" + err); + } + String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeObjectTO.getPath(), true); + VolumeDetailVO detail = volumeDetailsDao.findDetail(volumeObjectTO.getId(), StorpoolUtil.SP_PROVIDER_NAME); + if (detail != null) { + SpApiResponse updateVolumeResponse = StorpoolUtil.volumeUpdateRename(volumeName, "", StorpoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); + + if (updateVolumeResponse.getError() != null) { + StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.canHandle - Could not update StorPool's volume %s to it's globalId due to %s", volumeName, updateVolumeResponse.getError().getDescr()); + err = String.format("StorpoolVMSnapshotStrategy.canHandle - Could not update StorPool's volume %s to it's globalId due to %s", volumeName, updateVolumeResponse.getError().getDescr()); + } else { + volumeDetailsDao.remove(detail.getId()); + } + } + + SpApiResponse resp = StorpoolUtil.detachAllForced(volumeName, false, conn); + if (resp.getError() != null) { + err = String.format("Could not detach StorPool volume %s from a group snapshot, due to %s", + volumeName, resp.getError()); + throw new CloudRuntimeException(err); + } + resp = StorpoolUtil.volumeRevert(volumeName, snapshotName, conn); + if (resp.getError() != null) { + err = String.format("Create Could not complete revert task for volumeName=%s , and snapshotName=%s", + volumeName, snapshotName); + throw new CloudRuntimeException(err); + } + VolumeInfo vinfo = volFactory.getVolume(volumeObjectTO.getId()); + if (vinfo.getMaxIops() != null) { + resp = StorpoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); + + if (resp.getError() != null) { + StorpoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", + resp.getError().getDescr()); + } + } + } + finalizeRevert(vmSnapshotVO, volumeTOs); + result = vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + } catch (NoTransitionException e) { + String errMsg = String.format("Error while finalize create vm snapshot [%s] due to %s", vmSnapshot.getName(), e.getMessage()); + log.error(errMsg, e); + throw new CloudRuntimeException(errMsg); + } finally { + if (!result) { + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + log.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + } + } + } + return result; + } + + private void publishUsageEvents(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeObjectTO volumeTo) { + VolumeVO volume = volumeDao.findById(volumeTo.getId()); + Long diskOfferingId = volume.getDiskOfferingId(); + Long offeringId = null; + if (diskOfferingId != null) { + DiskOfferingVO offering = diskOfferingDao.findById(diskOfferingId); + if (offering != null && offering.isComputeOnly()) { + offeringId = offering.getId(); + } + } + UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), + vmSnapshot.getName(), offeringId, volume.getId(), volumeTo.getSize(), VMSnapshot.class.getName(), vmSnapshot.getUuid()); + } + + private void publishUsageEvents(String type, VMSnapshot vmSnapshot, UserVm userVm, Long vmSnapSize, Long virtualSize) { + try { + UsageEventUtils.publishUsageEvent(type, vmSnapshot.getAccountId(), userVm.getDataCenterId(), userVm.getId(), + vmSnapshot.getName(), 0L, 0L, vmSnapSize, virtualSize, VMSnapshot.class.getName(), + vmSnapshot.getUuid()); + } catch (Exception e) { + log.error("Failed to publis usage event " + type, e); + } + } +} diff --git a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/module.properties b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/module.properties new file mode 100644 index 000000000000..af4456ea373d --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/module.properties @@ -0,0 +1,18 @@ +# 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 +# with 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. +name=storage-volume-storpool +parent=storage diff --git a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml new file mode 100644 index 000000000000..ba47dfc3aeb2 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index c31daf3693c9..788a3d465b2c 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -57,10 +57,12 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; @@ -2639,6 +2641,7 @@ private Volume orchestrateDetachVolumeFromVM(long vmId, long volumeId) { if (volume.getPoolId() != null) { DataStore dataStore = dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary); volService.revokeAccess(volFactory.getVolume(volume.getId()), host, dataStore); + provideVMInfo(dataStore, vmId, volumeId); } if (volumePool != null && hostId != null) { handleTargetsForVMware(hostId, volumePool.getHostAddress(), volumePool.getPort(), volume.get_iScsiName()); @@ -3884,6 +3887,7 @@ private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, L if (attached) { ev = Volume.Event.OperationSucceeded; s_logger.debug("Volume: " + volInfo.getName() + " successfully attached to VM: " + volInfo.getAttachedVmName()); + provideVMInfo(dataStore, vm.getId(), volInfo.getId()); } else { s_logger.debug("Volume: " + volInfo.getName() + " failed to attach to VM: " + volInfo.getAttachedVmName()); } @@ -3892,6 +3896,17 @@ private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, L return _volsDao.findById(volumeToAttach.getId()); } + private void provideVMInfo(DataStore dataStore, long vmId, Long volumeId) { + DataStoreDriver dataStoreDriver = dataStore != null ? dataStore.getDriver() : null; + + if (dataStoreDriver instanceof PrimaryDataStoreDriver) { + PrimaryDataStoreDriver storageDriver = (PrimaryDataStoreDriver)dataStoreDriver; + if (storageDriver.vmInfoNeeded()) { + storageDriver.provideVMInfo(vmId, volumeId); + } + } + } + private int getMaxDataVolumesSupported(UserVmVO vm) { Long hostId = vm.getHostId(); if (hostId == null) { diff --git a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java index db6cac0e3936..4cd3a39af1cd 100644 --- a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java @@ -25,8 +25,10 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; -import com.cloud.server.ResourceManagerUtil; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; @@ -40,11 +42,14 @@ import com.cloud.network.vpc.NetworkACLVO; import com.cloud.network.vpc.VpcVO; import com.cloud.projects.ProjectVO; +import com.cloud.server.ResourceManagerUtil; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.TaggedResourceService; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -78,6 +83,10 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso AccountDao _accountDao; @Inject ResourceManagerUtil resourceManagerUtil; + @Inject + VolumeDao volumeDao; + @Inject + DataStoreManager dataStoreMgr; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -196,6 +205,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) { throw new CloudRuntimeException(String.format("tag %s already on %s with id %s", resourceTag.getKey(), resourceType.toString(), resourceId),e); } resourceTags.add(resourceTag); + if (ResourceObjectType.UserVm.equals(resourceType)) { + informStoragePoolForVmTags(id, key, value); + } } } } @@ -275,6 +287,9 @@ public void doInTransactionWithoutResult(TransactionStatus status) { _resourceTagDao.remove(tagToRemove.getId()); s_logger.debug("Removed the tag '" + tagToRemove + "' for resources (" + String.join(", ", resourceIds) + ")"); + if (ResourceObjectType.UserVm.equals(resourceType)) { + informStoragePoolForVmTags(tagToRemove.getResourceId(), tagToRemove.getKey(), tagToRemove.getValue()); + } } } }); @@ -292,4 +307,21 @@ public Map getTagsFromResource(ResourceObjectType type, long res List listResourceTags = listByResourceTypeAndId(type, resourceId); return listResourceTags == null ? null : listResourceTags.stream().collect(Collectors.toMap(ResourceTag::getKey, ResourceTag::getValue)); } + + + private void informStoragePoolForVmTags(long vmId, String key, String value) { + List volumeVos = volumeDao.findByInstance(vmId); + for (VolumeVO volume : volumeVos) { + DataStore dataStore = dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary); + PrimaryDataStoreDriver dataStoreDriver = dataStore != null + && (dataStore.getDriver() instanceof PrimaryDataStoreDriver) + ? (PrimaryDataStoreDriver) dataStore.getDriver() : null; + if (dataStoreDriver == null) { + continue; + } + if (dataStoreDriver.vmTagsNeeded(key)) { + dataStoreDriver.provideVMTags(vmId, volume.getId(), value); + } + } + } } From b794952799321ad629ffc8a3044bff24e10d3403 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Mon, 21 Feb 2022 14:03:21 +0200 Subject: [PATCH 02/12] Added support for alternative endpoint Added option to switch to alternative endpoint for SP primary storage --- .../StorPoolAbandonObjectsCollector.java | 16 +- .../StorpoolPrimaryDataStoreDriver.java | 196 ++++++++++-------- .../StorpoolPrimaryDataStoreLifeCycle.java | 13 +- .../provider/StorpoolHostListener.java | 8 +- .../storage/datastore/util/StorpoolUtil.java | 11 + .../StorPoolConfigurationManager.java | 7 +- .../snapshot/StorpoolSnapshotStrategy.java | 29 +-- .../snapshot/StorpoolVMSnapshotStrategy.java | 11 +- 8 files changed, 183 insertions(+), 108 deletions(-) diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java index bf39fc685a17..855ebe9233cd 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java @@ -110,8 +110,12 @@ protected void runInContext() { if (spPools != null && spPools.size() > 0) { Map volumes = new HashMap<>(); for (StoragePoolVO storagePoolVO : spPools) { - JsonArray arr = StorpoolUtil.volumesList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); - volumes.putAll(getStorPoolNamesAndCsTag(arr)); + try { + JsonArray arr = StorpoolUtil.volumesList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + volumes.putAll(getStorPoolNamesAndCsTag(arr)); + } catch (Exception e) { + log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); + } } Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -185,8 +189,12 @@ protected void runInContext() { Map snapshots = new HashMap(); if (spPools != null && spPools.size() > 0) { for (StoragePoolVO storagePoolVO : spPools) { - JsonArray arr = StorpoolUtil.snapshotsList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); - snapshots.putAll(getStorPoolNamesAndCsTag(arr)); + try { + JsonArray arr = StorpoolUtil.snapshotsList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + snapshots.putAll(getStorPoolNamesAndCsTag(arr)); + } catch (Exception e) { + log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); + } } Transaction.execute(new TransactionCallbackNoReturn() { @Override diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java index d311f9e65d2b..5bd8cd28cee7 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java @@ -88,7 +88,7 @@ import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.storage.dao.VMTemplateDetailsDao; -import com.cloud.storage.dao.VMTemplatePoolDaoImpl; +import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.tags.dao.ResourceTagDao; @@ -133,7 +133,7 @@ public class StorpoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Inject private StoragePoolDetailsDao storagePoolDetailsDao; @Inject - private VMTemplatePoolDaoImpl vmTemplatePoolDao; + private VMTemplatePoolDao vmTemplatePoolDao; @Override public Map getCapabilities() { @@ -221,27 +221,31 @@ public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCal String path = null; String err = null; if (data.getType() == DataObjectType.VOLUME) { - VolumeInfo vinfo = (VolumeInfo)data; - String name = vinfo.getUuid(); - Long size = vinfo.getSize(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); - SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); - if (resp.getError() == null) { - String volumeName = StorpoolUtil.getNameFromResponse(resp, false); - path = StorpoolUtil.devPath(volumeName); - - VolumeVO volume = volumeDao.findById(vinfo.getId()); - volume.setPoolId(dataStore.getId()); - volume.setPoolType(StoragePoolType.SharedMountPoint); - volume.setPath(path); - volumeDao.update(volume.getId(), volume); - - updateStoragePool(dataStore.getId(), size); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); - } else { - err = String.format("Could not create StorPool volume %s. Error: %s", name, resp.getError()); + try { + VolumeInfo vinfo = (VolumeInfo)data; + String name = vinfo.getUuid(); + Long size = vinfo.getSize(); + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); + if (resp.getError() == null) { + String volumeName = StorpoolUtil.getNameFromResponse(resp, false); + path = StorpoolUtil.devPath(volumeName); + + VolumeVO volume = volumeDao.findById(vinfo.getId()); + volume.setPoolId(dataStore.getId()); + volume.setPoolType(StoragePoolType.SharedMountPoint); + volume.setPath(path); + volumeDao.update(volume.getId(), volume); + + updateStoragePool(dataStore.getId(), size); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + } else { + err = String.format("Could not create StorPool volume %s. Error: %s", name, resp.getError()); + } + } catch (Exception e) { + err = String.format("Could not create volume due to %s", e.getMessage()); } } else { err = String.format("Invalid object type \"%s\" passed to createAsync", data.getType()); @@ -268,18 +272,19 @@ public void resize(DataObject data, AsyncCompletionCallback cal final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); final long oldSize = vol.getSize(); Long oldMaxIops = vol.getMaxIops(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk); + try { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - SpApiResponse resp = StorpoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, payload.newMaxIops, conn); - if (resp.getError() != null) { - err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); - } else { - try { - StorpoolResizeVolumeCommand resizeCmd = new StorpoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), - payload.newSize, payload.shrinkOk, payload.instanceName, payload.hosts == null ? false : true); - answer = (ResizeVolumeAnswer)storageMgr.sendToPool(pool, payload.hosts, resizeCmd); + StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk); + + SpApiResponse resp = StorpoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, payload.newMaxIops, conn); + if (resp.getError() != null) { + err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); + } else { + StorpoolResizeVolumeCommand resizeCmd = new StorpoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, + payload.instanceName, payload.hosts == null ? false : true); + answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, payload.hosts, resizeCmd); if (answer == null || !answer.getResult()) { err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; @@ -296,18 +301,17 @@ public void resize(DataObject data, AsyncCompletionCallback cal updateStoragePool(vol.getPoolId(), payload.newSize - oldSize); } - } catch (Exception e) { - log.debug("sending resize command failed", e); - err = e.toString(); } - } - - if (err != null) { - // try restoring volume to its initial size - resp = StorpoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); - if (resp.getError() != null) { - log.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); + if (err != null) { + // try restoring volume to its initial size + resp = StorpoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); + if (resp.getError() != null) { + log.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); + } } + } catch (Exception e) { + log.debug("sending resize command failed", e); + err = e.toString(); } } else { err = String.format("Invalid object type \"%s\" passed to resize", data.getType()); @@ -328,19 +332,23 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal if (name == null) { name = vinfo.getUuid(); } - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - - SpApiResponse resp = StorpoolUtil.volumeDelete(name, conn); - if (resp.getError() == null) { - updateStoragePool(dataStore.getId(), - vinfo.getSize()); - VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); - if (detail != null) { - volumeDetailsDao.remove(detail.getId()); - } - } else { - if (!resp.getError().getName().equalsIgnoreCase("objectDoesNotExist")) { - err = String.format("Could not delete StorPool volume %s. Error: %s", name, resp.getError()); + try { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + + SpApiResponse resp = StorpoolUtil.volumeDelete(name, conn); + if (resp.getError() == null) { + updateStoragePool(dataStore.getId(), - vinfo.getSize()); + VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); + if (detail != null) { + volumeDetailsDao.remove(detail.getId()); + } + } else { + if (!resp.getError().getName().equalsIgnoreCase("objectDoesNotExist")) { + err = String.format("Could not delete StorPool volume %s. Error: %s", name, resp.getError()); + } } + } catch (Exception e) { + err = String.format("Could not delete volume due to %s", e.getMessage()); } } else { err = String.format("Invalid DataObjectType \"%s\" passed to deleteAsync", data.getType()); @@ -792,31 +800,36 @@ public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback lstTemplatePoolRefs = vmTemplatePoolDao.listByPoolId(storagePoolId); + SpConnectionDesc conn = null; + try { + conn = StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + } catch (CloudRuntimeException e) { + throw e; + } + if (lstTemplatePoolRefs != null) { for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { SpApiResponse resp = StorpoolUtil.snapshotDelete( - StorpoolStorageAdaptor.getVolumeNameFromPath(templatePoolRef.getLocalDownloadPath(), true), - StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao)); + StorpoolStorageAdaptor.getVolumeNameFromPath(templatePoolRef.getLocalDownloadPath(), true), conn); if (resp.getError() != null) { throw new CloudRuntimeException(String.format("Could not delete StorPool's snapshot from template_spool_ref table due to %s", resp.getError())); } @@ -287,8 +293,7 @@ public boolean deleteDataStore(DataStore store) { List volumesOnHosts = storagePoolDetailsDao.listDetails(storagePoolId); for (StoragePoolDetailVO storagePoolDetailVO : volumesOnHosts) { if (storagePoolDetailVO.getValue() != null && storagePoolDetailVO.getName().contains(StorpoolUtil.SP_VOLUME_ON_CLUSTER)) { - StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(storagePoolDetailVO.getValue(), true), - StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao)); + StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(storagePoolDetailVO.getValue(), true), conn); } } storagePoolDetailsDao.removeDetails(storagePoolId); diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java index 4a2db4606639..12787995a145 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java @@ -82,7 +82,13 @@ public class StorpoolHostListener implements HypervisorHostListener { public boolean hostConnect(long hostId, long poolId) throws StorageConflictException { //Will update storage pool's connection details if they aren't updated in DB, before connecting pool to host StoragePoolVO poolVO = primaryStoreDao.findById(poolId); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolId, storagePoolDetailsDao, primaryStoreDao); + + SpConnectionDesc conn = null; + try { + conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolId, storagePoolDetailsDao, primaryStoreDao); + } catch (Exception e) { + return false; + } StoragePool pool = (StoragePool)this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary); diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java index 8b8f4431fddd..d9a32201ff6b 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java @@ -40,7 +40,9 @@ 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.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; @@ -256,6 +258,15 @@ public String getTemplateName() { public static SpConnectionDesc getSpConnection(String url, long poolId, StoragePoolDetailsDao poolDetails, PrimaryDataStoreDao storagePool) { + boolean isElternateEndpointEnabled = StorPoolConfigurationManager.AlternativeEndPointEnabled.valueIn(poolId); + if (isElternateEndpointEnabled) { + String alternateEndpoint = StorPoolConfigurationManager.AlternativeEndpoint.valueIn(poolId); + if (StringUtils.isNotEmpty(alternateEndpoint)) { + return new SpConnectionDesc(alternateEndpoint); + } else { + throw new CloudRuntimeException(String.format("Using an alternative endpoint of StorPool primary storage with id [%s] is enabled but no endpoint URL is provided", poolId)); + } + } List details = poolDetails.listDetails(poolId); String host = null; String authToken = null; diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java index 2ec30f1db84f..782d8133813c 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolConfigurationManager.java @@ -28,6 +28,11 @@ public class StorPoolConfigurationManager implements Configurable { "For StorPool Managed storage backup to secondary", true, ConfigKey.Scope.Global, null); public static final ConfigKey StorPoolClusterId = new ConfigKey(String.class, "sp.cluster.id", "Advanced", "n/a", "For StorPool multi cluster authorization", true, ConfigKey.Scope.Cluster, null); + public static final ConfigKey AlternativeEndPointEnabled = new ConfigKey(Boolean.class, "sp.enable.alternative.endpoint", "Advanced", "false", + "Used for StorPool primary storage, definse if there is a need to be used alternative endpoint", true, ConfigKey.Scope.StoragePool, null); + + public static final ConfigKey AlternativeEndpoint = new ConfigKey(String.class, "sp.alternative.endpoint", "Advanced", "", + "Used for StorPool primary storage for an alternative endpoint. Structure of the endpoint is - SP_API_HTTP=address:port;SP_AUTH_TOKEN=token;SP_TEMPLATE=template_name", true, ConfigKey.Scope.StoragePool, null); @Override public String getConfigComponentName() { @@ -36,6 +41,6 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId }; + return new ConfigKey[] { BypassSecondaryStorage, StorPoolClusterId, AlternativeEndPointEnabled, AlternativeEndpoint }; } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java index 8eb2b05ef6b2..2a00de3604b8 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java @@ -49,6 +49,7 @@ import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsVO; import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; @@ -100,19 +101,23 @@ public boolean deleteSnapshot(Long snapshotId) { // clean-up snapshot from Storpool storage pools StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); if (storage.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); - SpApiResponse resp = StorpoolUtil.snapshotDelete(name, conn); - if (resp.getError() != null) { - final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); - log.error(err); - StorpoolUtil.spLog(err); - } else { - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid()); - if (snapshotDetails != null) { - _snapshotDetailsDao.removeDetails(snapshotId); + try { + SpConnectionDesc conn = StorpoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + SpApiResponse resp = StorpoolUtil.snapshotDelete(name, conn); + if (resp.getError() != null) { + final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); + StorpoolUtil.spLog(err); + } else { + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid()); + if (snapshotDetails != null) { + _snapshotDetailsDao.removeDetails(snapshotId); + } + res = deleteSnapshotFromDb(snapshotId); + StorpoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfuly=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); } - res = deleteSnapshotFromDb(snapshotId); - StorpoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfuly=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + } catch (Exception e) { + String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage()); + throw new CloudRuntimeException(errMsg); } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java index 9691c0c64c24..f9ad4a0002fa 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java @@ -226,7 +226,12 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId()); DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + SpConnectionDesc conn = null; + try { + conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + } catch (CloudRuntimeException e) { + throw e; + } SpApiResponse resp = null; for (VolumeObjectTO volumeObjectTO : volumeTOs) { @@ -260,6 +265,8 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { vmSnapshotDetailsDao.removeDetails(vmSnapshot.getId()); finalizeDelete(vmSnapshotVO, volumeTOs); + vmSnapshotDao.remove(vmSnapshot.getId()); + long full_chain_size = 0; for (VolumeObjectTO volumeTo : volumeTOs) { publishUsageEvents(EventTypes.EVENT_VM_SNAPSHOT_DELETE, vmSnapshot, userVm, volumeTo); @@ -338,7 +345,7 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { } finalizeRevert(vmSnapshotVO, volumeTOs); result = vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); - } catch (NoTransitionException e) { + } catch (CloudRuntimeException | NoTransitionException e) { String errMsg = String.format("Error while finalize create vm snapshot [%s] due to %s", vmSnapshot.getName(), e.getMessage()); log.error(errMsg, e); throw new CloudRuntimeException(errMsg); From 56b9f92950f220a519b1f1f97e01b1be89636877 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Wed, 23 Feb 2022 21:25:39 +0200 Subject: [PATCH 03/12] renamed all classes from Storpool to StorPool --- ...ava => StorPoolBackupSnapshotCommand.java} | 4 +- ...oolBackupTemplateFromSnapshotCommand.java} | 4 +- ...yCommand.java => StorPoolCopyCommand.java} | 4 +- ...StorPoolCopyVolumeToSecondaryCommand.java} | 4 +- ...a => StorPoolDownloadTemplateCommand.java} | 4 +- ...ava => StorPoolDownloadVolumeCommand.java} | 4 +- ....java => StorPoolResizeVolumeCommand.java} | 6 +- ...StorPoolBackupSnapshotCommandWrapper.java} | 18 +- ...upTemplateFromSnapshotCommandWrapper.java} | 18 +- ...lCopyVolumeToSecondaryCommandWrapper.java} | 18 +- ...orPoolDownloadTemplateCommandWrapper.java} | 20 +- ...StorPoolDownloadVolumeCommandWrapper.java} | 20 +- .../StorPoolModifyStorageCommandWrapper.java | 6 +- ...> StorPoolResizeVolumeCommandWrapper.java} | 16 +- ...aptor.java => StorPoolStorageAdaptor.java} | 6 +- ...ragePool.java => StorPoolStoragePool.java} | 4 +- .../StorPoolAbandonObjectsCollector.java | 12 +- ...va => StorPoolPrimaryDataStoreDriver.java} | 288 +++++++++--------- ...=> StorPoolPrimaryDataStoreLifeCycle.java} | 54 ++-- ...istener.java => StorPoolHostListener.java} | 30 +- ... => StorPoolPrimaryDataStoreProvider.java} | 18 +- .../datastore/util/StorPoolHelper.java | 26 +- .../{StorpoolUtil.java => StorPoolUtil.java} | 14 +- .../motion/StorPoolDataMotionStrategy.java | 50 +-- ...egy.java => StorPoolSnapshotStrategy.java} | 34 +-- ...y.java => StorPoolVMSnapshotStrategy.java} | 64 ++-- 26 files changed, 373 insertions(+), 373 deletions(-) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolBackupSnapshotCommand.java => StorPoolBackupSnapshotCommand.java} (89%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolBackupTemplateFromSnapshotCommand.java => StorPoolBackupTemplateFromSnapshotCommand.java} (86%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolCopyCommand.java => StorPoolCopyCommand.java} (93%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolCopyVolumeToSecondaryCommand.java => StorPoolCopyVolumeToSecondaryCommand.java} (89%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolDownloadTemplateCommand.java => StorPoolDownloadTemplateCommand.java} (91%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolDownloadVolumeCommand.java => StorPoolDownloadVolumeCommand.java} (89%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/{StorpoolResizeVolumeCommand.java => StorPoolResizeVolumeCommand.java} (87%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolBackupSnapshotCommandWrapper.java => StorPoolBackupSnapshotCommandWrapper.java} (87%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolBackupTemplateFromSnapshotCommandWrapper.java => StorPoolBackupTemplateFromSnapshotCommandWrapper.java} (91%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolCopyVolumeToSecondaryCommandWrapper.java => StorPoolCopyVolumeToSecondaryCommandWrapper.java} (90%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolDownloadTemplateCommandWrapper.java => StorPoolDownloadTemplateCommandWrapper.java} (88%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolDownloadVolumeCommandWrapper.java => StorPoolDownloadVolumeCommandWrapper.java} (91%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/{StorpoolResizeVolumeCommandWrapper.java => StorPoolResizeVolumeCommandWrapper.java} (88%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/{StorpoolStorageAdaptor.java => StorPoolStorageAdaptor.java} (98%) rename plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/{StorpoolStoragePool.java => StorPoolStoragePool.java} (97%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/{StorpoolPrimaryDataStoreDriver.java => StorPoolPrimaryDataStoreDriver.java} (81%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/{StorpoolPrimaryDataStoreLifeCycle.java => StorPoolPrimaryDataStoreLifeCycle.java} (88%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/{StorpoolHostListener.java => StorPoolHostListener.java} (89%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/{StorpoolPrimaryDataStoreProvider.java => StorPoolPrimaryDataStoreProvider.java} (80%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/{StorpoolUtil.java => StorPoolUtil.java} (98%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/{StorpoolSnapshotStrategy.java => StorPoolSnapshotStrategy.java} (91%) rename plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/{StorpoolVMSnapshotStrategy.java => StorPoolVMSnapshotStrategy.java} (89%) diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupSnapshotCommand.java similarity index 89% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupSnapshotCommand.java index 8e5d65752c96..afdd2c270a0d 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupSnapshotCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupSnapshotCommand.java @@ -23,8 +23,8 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolBackupSnapshotCommand extends StorpoolCopyCommand { - public StorpoolBackupSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { +public class StorPoolBackupSnapshotCommand extends StorPoolCopyCommand { + public StorPoolBackupSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { super(srcTO, dstTO, timeout, executeInSequence); } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupTemplateFromSnapshotCommand.java similarity index 86% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupTemplateFromSnapshotCommand.java index 7d0569f3622d..d15357e9706f 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolBackupTemplateFromSnapshotCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolBackupTemplateFromSnapshotCommand.java @@ -23,8 +23,8 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolBackupTemplateFromSnapshotCommand extends StorpoolCopyCommand { - public StorpoolBackupTemplateFromSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { +public class StorPoolBackupTemplateFromSnapshotCommand extends StorPoolCopyCommand { + public StorPoolBackupTemplateFromSnapshotCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { super(srcTO, dstTO, timeout, executeInSequence); } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java similarity index 93% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java index aa2ccdac8b82..0610bb27e641 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java @@ -23,12 +23,12 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolCopyCommand extends StorageSubSystemCommand { +public class StorPoolCopyCommand extends StorageSubSystemCommand { private S srcTO; private D dstTO; private boolean executeInSequence = false; - public StorpoolCopyCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + public StorPoolCopyCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { super(); this.srcTO = (S)srcTO; this.dstTO = (D)dstTO; diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyVolumeToSecondaryCommand.java similarity index 89% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyVolumeToSecondaryCommand.java index 7bbda38419fd..9bf8b2eefd28 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolCopyVolumeToSecondaryCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyVolumeToSecondaryCommand.java @@ -23,8 +23,8 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolCopyVolumeToSecondaryCommand extends StorpoolCopyCommand { - public StorpoolCopyVolumeToSecondaryCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { +public class StorPoolCopyVolumeToSecondaryCommand extends StorPoolCopyCommand { + public StorPoolCopyVolumeToSecondaryCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { super(srcTO, dstTO, timeout, executeInSequence); } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadTemplateCommand.java similarity index 91% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadTemplateCommand.java index e1774b1b9dfb..6dc0db2071d6 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadTemplateCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadTemplateCommand.java @@ -21,9 +21,9 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolDownloadTemplateCommand extends StorpoolCopyCommand { +public class StorPoolDownloadTemplateCommand extends StorPoolCopyCommand { protected String objectType; - public StorpoolDownloadTemplateCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence, String objectType) { + public StorPoolDownloadTemplateCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence, String objectType) { super(srcTO, dstTO, timeout, executeInSequence); this.objectType = objectType; } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadVolumeCommand.java similarity index 89% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadVolumeCommand.java index 9a668e76912e..d670ff36fc86 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolDownloadVolumeCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolDownloadVolumeCommand.java @@ -23,8 +23,8 @@ import com.cloud.agent.api.to.DataTO; -public class StorpoolDownloadVolumeCommand extends StorpoolCopyCommand { - public StorpoolDownloadVolumeCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { +public class StorPoolDownloadVolumeCommand extends StorPoolCopyCommand { + public StorPoolDownloadVolumeCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { super(srcTO, dstTO, timeout, executeInSequence); } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolResizeVolumeCommand.java similarity index 87% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolResizeVolumeCommand.java index d4c06b8b95be..10b40bfeb091 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorpoolResizeVolumeCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolResizeVolumeCommand.java @@ -22,13 +22,13 @@ import com.cloud.agent.api.to.StorageFilerTO; -public class StorpoolResizeVolumeCommand extends ResizeVolumeCommand { +public class StorPoolResizeVolumeCommand extends ResizeVolumeCommand { protected boolean isAttached; - protected StorpoolResizeVolumeCommand() { + protected StorPoolResizeVolumeCommand() { super(); } - public StorpoolResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance, boolean isAttached) { + public StorPoolResizeVolumeCommand(String path, StorageFilerTO pool, Long currentSize, Long newSize, boolean shrinkOk, String vmInstance, boolean isAttached) { super(path, pool, currentSize, newSize, shrinkOk, vmInstance); this.isAttached = isAttached; } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java similarity index 87% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java index 5377b1c00522..20793df096be 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupSnapshotCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java @@ -19,7 +19,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; +import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.io.File; @@ -31,23 +31,23 @@ import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; -import com.cloud.agent.api.storage.StorpoolBackupSnapshotCommand; +import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; -@ResourceWrapper(handles = StorpoolBackupSnapshotCommand.class) -public final class StorpoolBackupSnapshotCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolBackupSnapshotCommand.class) +public final class StorPoolBackupSnapshotCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolBackupSnapshotCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolBackupSnapshotCommandWrapper.class); @Override - public CopyCmdAnswer execute(final StorpoolBackupSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String srcPath = null; KVMStoragePool secondaryPool = null; @@ -57,7 +57,7 @@ public CopyCmdAnswer execute(final StorpoolBackupSnapshotCommand cmd, final Libv final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); SP_LOG("StorpoolBackupSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); - StorpoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); srcPath = src.getPath(); final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); @@ -94,7 +94,7 @@ public CopyCmdAnswer execute(final StorpoolBackupSnapshotCommand cmd, final Libv return new CopyCmdAnswer(error); } finally { if (srcPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); } if (secondaryPool != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java similarity index 91% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java index a37c16f2e240..c0730f5d536e 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolBackupTemplateFromSnapshotCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java @@ -19,7 +19,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; +import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.io.BufferedWriter; import java.io.File; @@ -37,14 +37,14 @@ import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; -import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage.ImageFormat; @@ -55,13 +55,13 @@ import com.cloud.storage.template.TemplateLocation; import com.cloud.storage.template.TemplateProp; -@ResourceWrapper(handles = StorpoolBackupTemplateFromSnapshotCommand.class) -public class StorpoolBackupTemplateFromSnapshotCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolBackupTemplateFromSnapshotCommand.class) +public class StorPoolBackupTemplateFromSnapshotCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolBackupTemplateFromSnapshotCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolBackupTemplateFromSnapshotCommandWrapper.class); @Override - public CopyCmdAnswer execute(final StorpoolBackupTemplateFromSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + public CopyCmdAnswer execute(final StorPoolBackupTemplateFromSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String srcPath = null; KVMStoragePool secondaryPool = null; String objectType = cmd.getSrcTO().getObjectType().toString().toLowerCase(); @@ -87,7 +87,7 @@ public CopyCmdAnswer execute(final StorpoolBackupTemplateFromSnapshotCommand cmd String _tmpltpp = "template.properties"; SP_LOG("StorpoolBackupTemplateFromSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); - StorpoolStorageAdaptor.attachOrDetachVolume("attach", objectType, src.getPath()); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", objectType, src.getPath()); srcPath = src.getPath(); final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); @@ -146,7 +146,7 @@ public CopyCmdAnswer execute(final StorpoolBackupTemplateFromSnapshotCommand cmd return new CopyCmdAnswer(error); } finally { if (srcPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", objectType, srcPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", objectType, srcPath); } if (secondaryPool != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java similarity index 90% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java index 0f7ea00325f5..c28a8462cc73 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolCopyVolumeToSecondaryCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java @@ -19,7 +19,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; +import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.io.File; @@ -32,24 +32,24 @@ //import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; -import com.cloud.agent.api.storage.StorpoolCopyVolumeToSecondaryCommand; +import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; -@ResourceWrapper(handles = StorpoolCopyVolumeToSecondaryCommand.class) -public final class StorpoolCopyVolumeToSecondaryCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolCopyVolumeToSecondaryCommand.class) +public final class StorPoolCopyVolumeToSecondaryCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolCopyVolumeToSecondaryCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolCopyVolumeToSecondaryCommandWrapper.class); @Override - public CopyCmdAnswer execute(final StorpoolCopyVolumeToSecondaryCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + public CopyCmdAnswer execute(final StorPoolCopyVolumeToSecondaryCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String srcPath = null; KVMStoragePool secondaryPool = null; @@ -61,7 +61,7 @@ public CopyCmdAnswer execute(final StorpoolCopyVolumeToSecondaryCommand cmd, fin SP_LOG("StorpoolCopyVolumeToSecondaryCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); - StorpoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", "snapshot", src.getPath()); srcPath = src.getPath(); final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); @@ -109,7 +109,7 @@ public CopyCmdAnswer execute(final StorpoolCopyVolumeToSecondaryCommand cmd, fin return new CopyCmdAnswer(error); } finally { if (srcPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); } if (secondaryPool != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java similarity index 88% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java index 89bc4a55e1dd..b13b4936f582 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadTemplateCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java @@ -19,7 +19,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; +import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.io.File; import java.util.List; @@ -30,7 +30,7 @@ import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.log4j.Logger; -import com.cloud.agent.api.storage.StorpoolDownloadTemplateCommand; +import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; @@ -38,17 +38,17 @@ import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; -@ResourceWrapper(handles = StorpoolDownloadTemplateCommand.class) -public final class StorpoolDownloadTemplateCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolDownloadTemplateCommand.class) +public final class StorPoolDownloadTemplateCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolDownloadTemplateCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolDownloadTemplateCommandWrapper.class); @Override - public CopyCmdAnswer execute(final StorpoolDownloadTemplateCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + public CopyCmdAnswer execute(final StorPoolDownloadTemplateCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String dstPath = null; KVMStoragePool secondaryPool = null; DataTO src = cmd.getSrcTO(); @@ -103,10 +103,10 @@ public CopyCmdAnswer execute(final StorpoolDownloadTemplateCommand cmd, final Li final QemuImgFile srcFile = new QemuImgFile(srcDisk.getPath(), srcDisk.getFormat()); final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); - StorpoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); + StorPoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); dstPath = dst.getPath(); - StorpoolStorageAdaptor.attachOrDetachVolume("attach", cmd.getObjectType(), dstPath); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", cmd.getObjectType(), dstPath); final QemuImgFile dstFile = new QemuImgFile(dstPath, PhysicalDiskFormat.RAW); @@ -118,7 +118,7 @@ public CopyCmdAnswer execute(final StorpoolDownloadTemplateCommand cmd, final Li return new CopyCmdAnswer(error); } finally { if (dstPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", cmd.getObjectType(), dstPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", cmd.getObjectType(), dstPath); } if (secondaryPool != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java similarity index 91% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java index 538fe6769e48..a8ea65c6431b 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolDownloadVolumeCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java @@ -19,7 +19,7 @@ package com.cloud.hypervisor.kvm.resource.wrapper; -import static com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor.SP_LOG; +import static com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor.SP_LOG; import java.util.List; @@ -32,26 +32,26 @@ //import java.io.File; import org.apache.log4j.Logger; -import com.cloud.agent.api.storage.StorpoolDownloadVolumeCommand; +import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; -@ResourceWrapper(handles = StorpoolDownloadVolumeCommand.class) -public final class StorpoolDownloadVolumeCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolDownloadVolumeCommand.class) +public final class StorPoolDownloadVolumeCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolDownloadVolumeCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolDownloadVolumeCommandWrapper.class); @Override - public CopyCmdAnswer execute(final StorpoolDownloadVolumeCommand cmd, final LibvirtComputingResource libvirtComputingResource) { + public CopyCmdAnswer execute(final StorPoolDownloadVolumeCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String dstPath = null; KVMStoragePool secondaryPool = null; @@ -127,10 +127,10 @@ public CopyCmdAnswer execute(final StorpoolDownloadVolumeCommand cmd, final Libv final QemuImgFile srcFile = new QemuImgFile(srcPath, PhysicalDiskFormat.RAW); final QemuImg qemu = new QemuImg(cmd.getWaitInMillSeconds()); - StorpoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); + StorPoolStorageAdaptor.resize( Long.toString(srcDisk.getVirtualSize()), dst.getPath()); dstPath = dst.getPath(); - StorpoolStorageAdaptor.attachOrDetachVolume("attach", "volume", dstPath); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", "volume", dstPath); final QemuImgFile dstFile = new QemuImgFile(dstPath, srcFile.getFormat()); SP_LOG("SRC format=%s, DST format=%s",srcFile.getFormat(), dstFile.getFormat()); @@ -147,7 +147,7 @@ public CopyCmdAnswer execute(final StorpoolDownloadVolumeCommand cmd, final Libv return new CopyCmdAnswer(error); } finally { if (dstPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", "volume", dstPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", "volume", dstPath); } if (secondaryPool != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java index 7a92b27a852b..2b56e1b980b3 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java @@ -32,7 +32,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.template.TemplateProp; @@ -83,7 +83,7 @@ private String getSpClusterId() { if (err != null) { final String errMsg = String.format("Could not execute storpool_confget. Error: %s", err); log.warn(errMsg); - StorpoolStorageAdaptor.SP_LOG("Could not execute storpool_confget. Error: %s", err); + StorPoolStorageAdaptor.SP_LOG("Could not execute storpool_confget. Error: %s", err); return SP_CLUSTER_ID; } @@ -101,7 +101,7 @@ private String getSpClusterId() { } public String attachOrDetachVolume(String command, String type, String volumeUuid) { - final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeUuid, true); + final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(volumeUuid, true); if (name == null) { return null; } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolResizeVolumeCommandWrapper.java similarity index 88% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolResizeVolumeCommandWrapper.java index e30fb184ec96..9f9277768e65 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorpoolResizeVolumeCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolResizeVolumeCommandWrapper.java @@ -22,25 +22,25 @@ import org.apache.log4j.Logger; import com.cloud.agent.api.storage.ResizeVolumeAnswer; -import com.cloud.agent.api.storage.StorpoolResizeVolumeCommand; +import com.cloud.agent.api.storage.StorPoolResizeVolumeCommand; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.script.Script; -@ResourceWrapper(handles = StorpoolResizeVolumeCommand.class) -public final class StorpoolResizeVolumeCommandWrapper extends CommandWrapper { +@ResourceWrapper(handles = StorPoolResizeVolumeCommand.class) +public final class StorPoolResizeVolumeCommandWrapper extends CommandWrapper { - private static final Logger s_logger = Logger.getLogger(StorpoolResizeVolumeCommandWrapper.class); + private static final Logger s_logger = Logger.getLogger(StorPoolResizeVolumeCommandWrapper.class); @Override - public ResizeVolumeAnswer execute(final StorpoolResizeVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { + public ResizeVolumeAnswer execute(final StorPoolResizeVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { final String volid = command.getPath(); final long newSize = command.getNewSize(); final long currentSize = command.getCurrentSize(); @@ -63,7 +63,7 @@ public ResizeVolumeAnswer execute(final StorpoolResizeVolumeCommand command, fin final String path = vol.getPath(); volPath = path; if (!command.isAttached()) { - StorpoolStorageAdaptor.attachOrDetachVolume("attach", "volume", path); + StorPoolStorageAdaptor.attachOrDetachVolume("attach", "volume", path); } final Script resizecmd = new Script(libvirtComputingResource.getResizeVolumePath(), libvirtComputingResource.getCmdsTimeout(), s_logger); resizecmd.add("-s", String.valueOf(newSize)); @@ -91,7 +91,7 @@ public ResizeVolumeAnswer execute(final StorpoolResizeVolumeCommand command, fin return new ResizeVolumeAnswer(command, false, error); } finally { if (!command.isAttached() && volPath != null) { - StorpoolStorageAdaptor.attachOrDetachVolume("detach", "volume", volPath); + StorPoolStorageAdaptor.attachOrDetachVolume("detach", "volume", volPath); } } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java similarity index 98% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index c9f20ed1d624..cf4c05738823 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -40,7 +40,7 @@ import com.cloud.utils.script.Script; @StorageAdaptorInfo(storagePoolType=StoragePoolType.SharedMountPoint) -public class StorpoolStorageAdaptor implements StorageAdaptor { +public class StorPoolStorageAdaptor implements StorageAdaptor { public static void SP_LOG(String fmt, Object... args) { try (PrintWriter spLogFile = new PrintWriter(new BufferedWriter(new FileWriter("/var/log/cloudstack/agent/storpool-agent.log", true)))) { final String line = String.format(fmt, args); @@ -52,7 +52,7 @@ public static void SP_LOG(String fmt, Object... args) { } } - private static final Logger log = Logger.getLogger(StorpoolStorageAdaptor.class); + private static final Logger log = Logger.getLogger(StorPoolStorageAdaptor.class); private static final Map storageUuidToStoragePool = new HashMap(); @@ -60,7 +60,7 @@ public static void SP_LOG(String fmt, Object... args) { public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType) { SP_LOG("StorpooolStorageAdaptor.createStoragePool: uuid=%s, host=%s:%d, path=%s, userInfo=%s, type=%s", uuid, host, port, path, userInfo, storagePoolType); - StorpoolStoragePool storagePool = new StorpoolStoragePool(uuid, host, port, storagePoolType, this); + StorPoolStoragePool storagePool = new StorPoolStoragePool(uuid, host, port, storagePoolType, this); storageUuidToStoragePool.put(uuid, storagePool); return storagePool; } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java similarity index 97% rename from plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java rename to plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java index 07e4c8268e54..d031f319d198 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorpoolStoragePool.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStoragePool.java @@ -24,7 +24,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; -public class StorpoolStoragePool implements KVMStoragePool { +public class StorPoolStoragePool implements KVMStoragePool { private String _uuid; private String _sourceHost; private int _sourcePort; @@ -35,7 +35,7 @@ public class StorpoolStoragePool implements KVMStoragePool { private String _sourceDir; private String _localPath; - public StorpoolStoragePool(String uuid, String host, int port, StoragePoolType storagePoolType, StorageAdaptor storageAdaptor) { + public StorPoolStoragePool(String uuid, String host, int port, StoragePoolType storagePoolType, StorageAdaptor storageAdaptor) { _uuid = uuid; _sourceHost = host; _sourcePort = port; diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java index 855ebe9233cd..6b16de096aec 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/collector/StorPoolAbandonObjectsCollector.java @@ -38,7 +38,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import org.apache.log4j.Logger; import com.cloud.utils.component.ManagerBase; @@ -106,12 +106,12 @@ class StorPoolVolumesTagsUpdate extends ManagedContextRunnable { @Override @DB protected void runInContext() { - List spPools = storagePoolDao.findPoolsByProvider(StorpoolUtil.SP_PROVIDER_NAME); + List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); if (spPools != null && spPools.size() > 0) { Map volumes = new HashMap<>(); for (StoragePoolVO storagePoolVO : spPools) { try { - JsonArray arr = StorpoolUtil.volumesList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + JsonArray arr = StorPoolUtil.volumesList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); volumes.putAll(getStorPoolNamesAndCsTag(arr)); } catch (Exception e) { log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); @@ -185,12 +185,12 @@ class StorPoolSnapshotsTagsUpdate extends ManagedContextRunnable { @Override @DB protected void runInContext() { - List spPools = storagePoolDao.findPoolsByProvider(StorpoolUtil.SP_PROVIDER_NAME); + List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); Map snapshots = new HashMap(); if (spPools != null && spPools.size() > 0) { for (StoragePoolVO storagePoolVO : spPools) { try { - JsonArray arr = StorpoolUtil.snapshotsList(StorpoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + JsonArray arr = StorPoolUtil.snapshotsList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); snapshots.putAll(getStorPoolNamesAndCsTag(arr)); } catch (Exception e) { log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); @@ -282,7 +282,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { private void addRecordToDb(String name, PreparedStatement pstmt, String tag, boolean pathNeeded) throws SQLException { name = name.startsWith("~") ? name.split("~")[1] : name; - pstmt.setString(1, pathNeeded ? StorpoolUtil.devPath(name) : name); + pstmt.setString(1, pathNeeded ? StorPoolUtil.devPath(name) : name); pstmt.setString(2, tag); pstmt.addBatch(); } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java similarity index 81% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java index 5bd8cd28cee7..2e8d84846b2f 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorpoolPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java @@ -47,9 +47,9 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; @@ -60,12 +60,12 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ResizeVolumeAnswer; -import com.cloud.agent.api.storage.StorpoolBackupSnapshotCommand; -import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; -import com.cloud.agent.api.storage.StorpoolCopyVolumeToSecondaryCommand; -import com.cloud.agent.api.storage.StorpoolDownloadTemplateCommand; -import com.cloud.agent.api.storage.StorpoolDownloadVolumeCommand; -import com.cloud.agent.api.storage.StorpoolResizeVolumeCommand; +import com.cloud.agent.api.storage.StorPoolBackupSnapshotCommand; +import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand; +import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand; +import com.cloud.agent.api.storage.StorPoolDownloadVolumeCommand; +import com.cloud.agent.api.storage.StorPoolResizeVolumeCommand; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; @@ -73,7 +73,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.storage.DataStoreRole; @@ -98,9 +98,9 @@ import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.VMInstanceDao; -public class StorpoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { +public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver { - private static final Logger log = Logger.getLogger(StorpoolPrimaryDataStoreDriver.class); + private static final Logger log = Logger.getLogger(StorPoolPrimaryDataStoreDriver.class); @Inject private VolumeDao volumeDao; @@ -225,13 +225,13 @@ public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCal VolumeInfo vinfo = (VolumeInfo)data; String name = vinfo.getUuid(); Long size = vinfo.getSize(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); - SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", vinfo.getName(), vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + SpApiResponse resp = StorPoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vinfo.getInstanceId()), null, "volume", vinfo.getMaxIops(), conn); if (resp.getError() == null) { - String volumeName = StorpoolUtil.getNameFromResponse(resp, false); - path = StorpoolUtil.devPath(volumeName); + String volumeName = StorPoolUtil.getNameFromResponse(resp, false); + path = StorPoolUtil.devPath(volumeName); VolumeVO volume = volumeDao.findById(vinfo.getId()); volume.setPoolId(dataStore.getId()); @@ -240,7 +240,7 @@ public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCal volumeDao.update(volume.getId(), volume); updateStoragePool(dataStore.getId(), size); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.createAsync volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s, template: %s", volumeName, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), conn.getTemplateName()); } else { err = String.format("Could not create StorPool volume %s. Error: %s", name, resp.getError()); } @@ -269,27 +269,27 @@ public void resize(DataObject data, AsyncCompletionCallback cal StoragePool pool = (StoragePool)data.getDataStore(); ResizeVolumePayload payload = (ResizeVolumePayload)vol.getpayload(); - final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); + final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true); final long oldSize = vol.getSize(); Long oldMaxIops = vol.getMaxIops(); try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(data.getDataStore().getUuid(), data.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s", name, vol.getUuid(), oldSize, payload.newSize, payload.shrinkOk); - SpApiResponse resp = StorpoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, payload.newMaxIops, conn); + SpApiResponse resp = StorPoolUtil.volumeUpdate(name, payload.newSize, payload.shrinkOk, payload.newMaxIops, conn); if (resp.getError() != null) { err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError()); } else { - StorpoolResizeVolumeCommand resizeCmd = new StorpoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, + StorPoolResizeVolumeCommand resizeCmd = new StorPoolResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), vol.getSize(), payload.newSize, payload.shrinkOk, payload.instanceName, payload.hosts == null ? false : true); answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, payload.hosts, resizeCmd); if (answer == null || !answer.getResult()) { err = answer != null ? answer.getDetails() : "return a null answer, resize failed for unknown reason"; } else { - path = StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false)); + path = StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)); vol.setSize(payload.newSize); vol.update(); @@ -304,7 +304,7 @@ public void resize(DataObject data, AsyncCompletionCallback cal } if (err != null) { // try restoring volume to its initial size - resp = StorpoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); + resp = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); if (resp.getError() != null) { log.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, resp.getError())); } @@ -327,18 +327,18 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal String err = null; if (data.getType() == DataObjectType.VOLUME) { VolumeInfo vinfo = (VolumeInfo)data; - String name = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.deleteAsync delete volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s dataStore=%s", name, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), dataStore.getUuid()); + String name = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.deleteAsync delete volume: name=%s, uuid=%s, isAttached=%s vm=%s, payload=%s dataStore=%s", name, vinfo.getUuid(), vinfo.isAttachedVM(), vinfo.getAttachedVmName(), vinfo.getpayload(), dataStore.getUuid()); if (name == null) { name = vinfo.getUuid(); } try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, primaryStoreDao); - SpApiResponse resp = StorpoolUtil.volumeDelete(name, conn); + SpApiResponse resp = StorPoolUtil.volumeDelete(name, conn); if (resp.getError() == null) { updateStoragePool(dataStore.getId(), - vinfo.getSize()); - VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); + VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorPoolUtil.SP_PROVIDER_NAME); if (detail != null) { volumeDetailsDao.remove(detail.getId()); } @@ -356,7 +356,7 @@ public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCal if (err != null) { log.error(err); - StorpoolUtil.spLog(err); + StorPoolUtil.spLog(err); } CommandResult res = new CommandResult(); @@ -383,7 +383,7 @@ private void logDataObject(final String pref, DataObject data) { size = tinfo.getSize(); } - StorpoolUtil.spLog("%s: name=%s, size=%s, uuid=%s, type=%s, dstore=%s:%s:%s", pref, name, size, data.getUuid(), data.getType(), dstore.getUuid(), dstore.getName(), dstore.getRole()); + StorPoolUtil.spLog("%s: name=%s, size=%s, uuid=%s, type=%s, dstore=%s:%s:%s", pref, name, size, data.getUuid(), data.getType(), dstore.getUuid(), dstore.getName(), dstore.getRole()); } @Override @@ -393,7 +393,7 @@ public boolean canCopy(DataObject srcData, DataObject dstData) { @Override public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCallback callback) { - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc:"); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc:"); logDataObject("SRC", srcData); logDataObject("DST", dstData); @@ -411,28 +411,28 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal VolumeInfo vinfo = (VolumeInfo)dstData; final String volumeName = vinfo.getUuid(); final Long size = vinfo.getSize(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - SpApiResponse resp = StorpoolUtil.volumeCreate(volumeName, snapshotName, size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpApiResponse resp = StorPoolUtil.volumeCreate(volumeName, snapshotName, size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), size); VolumeObjectTO to = (VolumeObjectTO)dstData.getTO(); - to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); to.setSize(size); answer = new CopyCmdAnswer(to); - StorpoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorpoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); + StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", StorPoolUtil.getNameFromResponse(resp, false), to.getUuid(), snapshotName, sinfo.getUuid()); } else if (resp.getError().getName().equals("objectDoesNotExist")) { //check if snapshot is on secondary storage - StorpoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName); + StorPoolUtil.spLog("Snapshot %s does not exists on StorPool, will try to create a volume from a snopshot on secondary storage", snapshotName); SnapshotDataStoreVO snap = snapshotDataStoreDao.findBySnapshot(sinfo.getId(), DataStoreRole.Image); - if (snap != null && StorpoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { - resp = StorpoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn); + if (snap != null && StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getInstallPath(), false) == null) { + resp = StorPoolUtil.volumeCreate(srcData.getUuid(), null, size, null, "no", "snapshot", sinfo.getBaseVolume().getMaxIops(), conn); if (resp.getError() == null) { VolumeObjectTO dstTO = (VolumeObjectTO) dstData.getTO(); dstTO.setSize(size); - dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); - cmd = new StorpoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); + dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); + cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); EndPoint ep = selector.select(srcData, dstData); if (ep == null) { @@ -442,27 +442,27 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } if (answer != null && answer.getResult()) { - SpApiResponse resp2 = StorpoolUtil.volumeFreeze(StorpoolUtil.getNameFromResponse(resp, true), conn); + SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); if (resp2.getError() != null) { err = String.format("Could not freeze Storpool volume %s. Error: %s", srcData.getUuid(), resp2.getError()); } else { - String name = StorpoolUtil.getNameFromResponse(resp, false); + String name = StorPoolUtil.getNameFromResponse(resp, false); SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sinfo.getId(), sinfo.getUuid()); if (snapshotDetails != null) { - StorPoolHelper.updateSnapshotDetailsValue(snapshotDetails.getId(), StorpoolUtil.devPath(name), "snapshot"); + StorPoolHelper.updateSnapshotDetailsValue(snapshotDetails.getId(), StorPoolUtil.devPath(name), "snapshot"); }else { - StorPoolHelper.addSnapshotDetails(sinfo.getId(), sinfo.getUuid(), StorpoolUtil.devPath(name), _snapshotDetailsDao); + StorPoolHelper.addSnapshotDetails(sinfo.getId(), sinfo.getUuid(), StorPoolUtil.devPath(name), _snapshotDetailsDao); } - resp = StorpoolUtil.volumeCreate(volumeName, StorpoolUtil.getNameFromResponse(resp, true), size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); + resp = StorPoolUtil.volumeCreate(volumeName, StorPoolUtil.getNameFromResponse(resp, true), size, null, null, "volume", sinfo.getBaseVolume().getMaxIops(), conn); if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), size); VolumeObjectTO to = (VolumeObjectTO) dstData.getTO(); - to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); to.setSize(size); // successfully downloaded snapshot to primary storage answer = new CopyCmdAnswer(to); - StorpoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", name, to.getUuid(), snapshotName, sinfo.getUuid()); + StorPoolUtil.spLog("Created volume=%s with uuid=%s from snapshot=%s with uuid=%s", name, to.getUuid(), snapshotName, sinfo.getUuid()); } else { err = String.format("Could not create Storpool volume %s from snapshot %s. Error: %s", volumeName, snapshotName, resp.getError()); @@ -487,10 +487,10 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal answer = new CopyCmdAnswer(snapshot); } else { // copy snapshot to secondary storage (backup snapshot) - cmd = new StorpoolBackupSnapshotCommand(srcData.getTO(), dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + cmd = new StorPoolBackupSnapshotCommand(srcData.getTO(), dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); - final String snapName = StorpoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + final String snapName = StorPoolStorageAdaptor.getVolumeNameFromPath(((SnapshotInfo) srcData).getPath(), true); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); try { Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapName, clusterDao); EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); @@ -500,12 +500,12 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal answer = ep.sendMessage(cmd); // if error during snapshot backup, cleanup the StorPool snapshot if (answer != null && !answer.getResult()) { - StorpoolUtil.spLog(String.format("Error while backing-up snapshot '%s' - cleaning up StorPool snapshot. Error: %s", snapName, answer.getDetails())); - SpApiResponse resp = StorpoolUtil.snapshotDelete(snapName, conn); + StorPoolUtil.spLog(String.format("Error while backing-up snapshot '%s' - cleaning up StorPool snapshot. Error: %s", snapName, answer.getDetails())); + SpApiResponse resp = StorPoolUtil.snapshotDelete(snapName, conn); if (resp.getError() != null) { final String err2 = String.format("Failed to cleanup StorPool snapshot '%s'. Error: %s.", snapName, resp.getError()); log.error(err2); - StorpoolUtil.spLog(err2); + StorPoolUtil.spLog(err2); } } } @@ -517,12 +517,12 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal // create template from volume VolumeObjectTO volume = (VolumeObjectTO) srcData.getTO(); TemplateObjectTO template = (TemplateObjectTO) dstData.getTO(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); - cmd = new StorpoolBackupTemplateFromSnapshotCommand(volume, template, + cmd = new StorPoolBackupTemplateFromSnapshotCommand(volume, template, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); try { @@ -533,15 +533,15 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } else { answer = ep2.sendMessage(cmd); if (answer != null && answer.getResult()) { - SpApiResponse resSnapshot = StorpoolUtil.volumeSnapshot(volumeName, template.getUuid(), null, "template", "no", conn); + SpApiResponse resSnapshot = StorPoolUtil.volumeSnapshot(volumeName, template.getUuid(), null, "template", "no", conn); if (resSnapshot.getError() != null) { log.debug(String.format("Could not snapshot volume with ID=%s", volume.getId())); - StorpoolUtil.spLog("Volume snapshot failed with error=%s", resSnapshot.getError().getDescr()); + StorPoolUtil.spLog("Volume snapshot failed with error=%s", resSnapshot.getError().getDescr()); err = resSnapshot.getError().getDescr(); } else { - StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), StorpoolUtil.devPath(StorpoolUtil.getSnapshotNameFromResponse(resSnapshot, false, StorpoolUtil.GLOBAL_ID)), vmTemplateDataStoreDao); - vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(srcData.getDataStore().getId()), false)); + StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), StorPoolUtil.devPath(StorPoolUtil.getSnapshotNameFromResponse(resSnapshot, false, StorPoolUtil.GLOBAL_ID)), vmTemplateDataStoreDao); + vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(srcData.getDataStore().getId()), false)); } }else { err = "Could not copy template to secondary " + answer.getResult(); @@ -556,12 +556,12 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal Long size = tinfo.getSize(); if(size == null || size == 0) size = 1L*1024*1024*1024; - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); TemplateDataStoreVO templDataStoreVO = vmTemplateDataStoreDao.findByTemplate(tinfo.getId(), DataStoreRole.Image); String snapshotName = (templDataStoreVO != null && templDataStoreVO.getLocalDownloadPath() != null) - ? StorpoolStorageAdaptor.getVolumeNameFromPath(templDataStoreVO.getLocalDownloadPath(), true) + ? StorPoolStorageAdaptor.getVolumeNameFromPath(templDataStoreVO.getLocalDownloadPath(), true) : null; String name = tinfo.getUuid(); @@ -569,32 +569,32 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal if (snapshotName != null) { //no need to copy volume from secondary, because we have it already on primary. Just need to create a child snapshot from it. //The child snapshot is needed when configuration "storage.cleanup.enabled" is true, not to clean the base snapshot and to lose everything - resp = StorpoolUtil.volumeCreate(name, snapshotName, size, null, "no", "template", null, conn); + resp = StorPoolUtil.volumeCreate(name, snapshotName, size, null, "no", "template", null, conn); if (resp.getError() != null) { err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { - String volumeNameToSnapshot = StorpoolUtil.getNameFromResponse(resp, true); - SpApiResponse resp2 = StorpoolUtil.volumeFreeze(volumeNameToSnapshot, conn); + String volumeNameToSnapshot = StorPoolUtil.getNameFromResponse(resp, true); + SpApiResponse resp2 = StorPoolUtil.volumeFreeze(volumeNameToSnapshot, conn); if (resp2.getError() != null) { err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); } else { - StorpoolUtil.spLog("Storpool snapshot [%s] for a template exists. Creating template on Storpool with name [%s]", tinfo.getUuid(), name); + StorPoolUtil.spLog("Storpool snapshot [%s] for a template exists. Creating template on Storpool with name [%s]", tinfo.getUuid(), name); TemplateObjectTO dstTO = (TemplateObjectTO) dstData.getTO(); - dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); dstTO.setSize(size); answer = new CopyCmdAnswer(dstTO); } } } else { - resp = StorpoolUtil.volumeCreate(name, null, size, null, "no", "template", null, conn); + resp = StorPoolUtil.volumeCreate(name, null, size, null, "no", "template", null, conn); if (resp.getError() != null) { err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { TemplateObjectTO dstTO = (TemplateObjectTO)dstData.getTO(); - dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); dstTO.setSize(size); - cmd = new StorpoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); + cmd = new StorPoolDownloadTemplateCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value(), "volume"); EndPoint ep = selector.select(srcData, dstData); if (ep == null) { @@ -605,7 +605,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal if (answer != null && answer.getResult()) { // successfully downloaded template to primary storage - SpApiResponse resp2 = StorpoolUtil.volumeFreeze(StorpoolUtil.getNameFromResponse(resp, true), conn); + SpApiResponse resp2 = StorPoolUtil.volumeFreeze(StorPoolUtil.getNameFromResponse(resp, true), conn); if (resp2.getError() != null) { err = String.format("Could not freeze Storpool volume %s. Error: %s", name, resp2.getError()); } @@ -615,7 +615,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } } if (err != null) { - resp = StorpoolUtil.volumeDelete(StorpoolUtil.getNameFromResponse(resp, true), conn); + resp = StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(resp, true), conn); if (resp.getError() != null) { log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp.getError())); } @@ -626,11 +626,11 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal VolumeInfo vinfo = (VolumeInfo)dstData; VMTemplateStoragePoolVO templStoragePoolVO = StorPoolHelper.findByPoolTemplate(vinfo.getPoolId(), tinfo.getId()); - final String parentName = templStoragePoolVO.getLocalDownloadPath() !=null ? StorpoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getLocalDownloadPath(), true) : StorpoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getInstallPath(), true); + final String parentName = templStoragePoolVO.getLocalDownloadPath() !=null ? StorPoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getLocalDownloadPath(), true) : StorPoolStorageAdaptor.getVolumeNameFromPath(templStoragePoolVO.getInstallPath(), true); final String name = vinfo.getUuid(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - Long snapshotSize = StorpoolUtil.snapshotSize(parentName, conn); + Long snapshotSize = StorPoolUtil.snapshotSize(parentName, conn); if (snapshotSize == null) { err = String.format("Snapshot=%s does not exist on StorPool. Will recreate it first on primary", parentName); vmTemplatePoolDao.remove(templStoragePoolVO.getId()); @@ -639,19 +639,19 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal long size = vinfo.getSize(); if( size < snapshotSize ) { - StorpoolUtil.spLog(String.format("provided size is too small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, snapshotSize)); + StorPoolUtil.spLog(String.format("provided size is too small for snapshot. Provided %d, snapshot %d. Using snapshot size", size, snapshotSize)); size = snapshotSize; } - StorpoolUtil.spLog(String.format("volume size is: %d", size)); + StorPoolUtil.spLog(String.format("volume size is: %d", size)); Long vmId = vinfo.getInstanceId(); - SpApiResponse resp = StorpoolUtil.volumeCreate(name, parentName, size, getVMInstanceUUID(vmId), + SpApiResponse resp = StorPoolUtil.volumeCreate(name, parentName, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), "volume", vinfo.getMaxIops(), conn); if (resp.getError() == null) { updateStoragePool(dstData.getDataStore().getId(), vinfo.getSize()); VolumeObjectTO to = (VolumeObjectTO) vinfo.getTO(); to.setSize(vinfo.getSize()); - to.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + to.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); answer = new CopyCmdAnswer(to); } else { @@ -659,40 +659,40 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } } } else if (srcType == DataObjectType.VOLUME && dstType == DataObjectType.VOLUME) { - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.copyAsync src Data Store=%s", srcData.getDataStore().getDriver()); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.copyAsync src Data Store=%s", srcData.getDataStore().getDriver()); VolumeInfo dstInfo = (VolumeInfo)dstData; VolumeInfo srcInfo = (VolumeInfo) srcData; - if( !(srcData.getDataStore().getDriver() instanceof StorpoolPrimaryDataStoreDriver ) ) { + if( !(srcData.getDataStore().getDriver() instanceof StorPoolPrimaryDataStoreDriver ) ) { // copy "VOLUME" to primary storage String name = dstInfo.getUuid(); Long size = dstInfo.getSize(); if(size == null || size == 0) size = 1L*1024*1024*1024; - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); Long vmId = srcInfo.getInstanceId(); - SpApiResponse resp = StorpoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), "volume", dstInfo.getMaxIops(), conn); + SpApiResponse resp = StorPoolUtil.volumeCreate(name, null, size, getVMInstanceUUID(vmId), getVcPolicyTag(vmId), "volume", dstInfo.getMaxIops(), conn); if (resp.getError() != null) { err = String.format("Could not create Storpool volume for CS template %s. Error: %s", name, resp.getError()); } else { //updateVolume(dstData.getId()); VolumeObjectTO dstTO = (VolumeObjectTO)dstData.getTO(); - dstTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false))); + dstTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false))); dstTO.setSize(size); - cmd = new StorpoolDownloadVolumeCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + cmd = new StorPoolDownloadVolumeCommand(srcData.getTO(), dstTO, StorPoolHelper.getTimeout(StorPoolHelper.PrimaryStorageDownloadWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); EndPoint ep = selector.select(srcData, dstData); if( ep == null) { - StorpoolUtil.spLog("select(srcData, dstData) returned NULL. trying srcOnly"); + StorPoolUtil.spLog("select(srcData, dstData) returned NULL. trying srcOnly"); ep = selector.select(srcData); // Storpool is zone } if (ep == null) { err = "No remote endpoint to send command, check if host or ssvm is down?"; } else { - StorpoolUtil.spLog("Sending command to %s", ep.getHostAddr()); + StorPoolUtil.spLog("Sending command to %s", ep.getHostAddr()); answer = ep.sendMessage(cmd); if (answer != null && answer.getResult()) { @@ -703,7 +703,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } if (err != null) { - SpApiResponse resp3 = StorpoolUtil.volumeDelete(name, conn); + SpApiResponse resp3 = StorPoolUtil.volumeDelete(name, conn); if (resp3.getError() != null) { log.warn(String.format("Could not clean-up Storpool volume %s. Error: %s", name, resp3.getError())); } @@ -712,50 +712,50 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } else { // download volume - first copies to secondary VolumeObjectTO srcTO = (VolumeObjectTO)srcData.getTO(); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s ", srcTO.getPath()); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST canonicalName=%s ", dstData.getDataStore().getClass().getCanonicalName()); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc SRC path=%s ", srcTO.getPath()); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST canonicalName=%s ", dstData.getDataStore().getClass().getCanonicalName()); PrimaryDataStoreTO checkStoragePool = dstData.getTO().getDataStore() instanceof PrimaryDataStoreTO ? (PrimaryDataStoreTO)dstData.getTO().getDataStore() : null; - final String name = StorpoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST tmpSnapName=%s ,srcUUID=%s", name, srcTO.getUuid()); + final String name = StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST tmpSnapName=%s ,srcUUID=%s", name, srcTO.getUuid()); if (checkStoragePool != null && checkStoragePool.getPoolType().equals(StoragePoolType.SharedMountPoint)) { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - String baseOn = StorpoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dstData.getDataStore().getUuid(), dstData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + String baseOn = StorPoolStorageAdaptor.getVolumeNameFromPath(srcTO.getPath(), true); //uuid tag will be the same as srcData.uuid String volumeName = srcData.getUuid(); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc volumeName=%s, baseOn=%s", volumeName, baseOn); - final SpApiResponse response = StorpoolUtil.volumeCopy(volumeName, baseOn, "volume", srcInfo.getMaxIops(), conn); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc volumeName=%s, baseOn=%s", volumeName, baseOn); + final SpApiResponse response = StorPoolUtil.volumeCopy(volumeName, baseOn, "volume", srcInfo.getMaxIops(), conn); srcTO.setSize(srcData.getSize()); - srcTO.setPath(StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(response, false))); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST to=%s", srcTO); + srcTO.setPath(StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(response, false))); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc DST to=%s", srcTO); answer = new CopyCmdAnswer(srcTO); } else { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - final SpApiResponse resp = StorpoolUtil.volumeSnapshot(name, srcTO.getUuid(), srcInfo.getInstanceId() != null ? getVMInstanceUUID(srcInfo.getInstanceId()) : null, "temporary", null, conn); - String snapshotName = StorpoolUtil.getSnapshotNameFromResponse(resp, true, StorpoolUtil.GLOBAL_ID); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(srcData.getDataStore().getUuid(), srcData.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + final SpApiResponse resp = StorPoolUtil.volumeSnapshot(name, srcTO.getUuid(), srcInfo.getInstanceId() != null ? getVMInstanceUUID(srcInfo.getInstanceId()) : null, "temporary", null, conn); + String snapshotName = StorPoolUtil.getSnapshotNameFromResponse(resp, true, StorPoolUtil.GLOBAL_ID); if (resp.getError() == null) { - srcTO.setPath(StorpoolUtil.devPath( - StorpoolUtil.getSnapshotNameFromResponse(resp, false, StorpoolUtil.GLOBAL_ID))); + srcTO.setPath(StorPoolUtil.devPath( + StorPoolUtil.getSnapshotNameFromResponse(resp, false, StorPoolUtil.GLOBAL_ID))); - cmd = new StorpoolCopyVolumeToSecondaryCommand(srcTO, dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.CopyVolumeWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); + cmd = new StorPoolCopyVolumeToSecondaryCommand(srcTO, dstData.getTO(), StorPoolHelper.getTimeout(StorPoolHelper.CopyVolumeWait, configDao), VirtualMachineManager.ExecuteInSequence.value()); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc command=%s ", cmd); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.copyAsnc command=%s ", cmd); try { Long clusterId = StorPoolHelper.findClusterIdByGlobalId(snapshotName, clusterDao); EndPoint ep = clusterId != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(StorPoolHelper.findHostByCluster(clusterId, hostDao)) : selector.select(srcData, dstData); - StorpoolUtil.spLog("selector.select(srcData, dstData) ", ep); + StorPoolUtil.spLog("selector.select(srcData, dstData) ", ep); if (ep == null) { ep = selector.select(dstData); - StorpoolUtil.spLog("selector.select(srcData) ", ep); + StorPoolUtil.spLog("selector.select(srcData) ", ep); } if (ep == null) { err = "No remote endpoint to send command, check if host or ssvm is down?"; } else { answer = ep.sendMessage(cmd); - StorpoolUtil.spLog("Answer: details=%s, result=%s", answer.getDetails(), answer.getResult()); + StorPoolUtil.spLog("Answer: details=%s, result=%s", answer.getDetails(), answer.getResult()); } } catch (CloudRuntimeException e) { err = e.getMessage(); @@ -763,11 +763,11 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } else { err = String.format("Failed to create temporary StorPool snapshot while trying to download volume %s (uuid %s). Error: %s", srcTO.getName(), srcTO.getUuid(), resp.getError()); } - final SpApiResponse resp2 = StorpoolUtil.snapshotDelete(snapshotName, conn); + final SpApiResponse resp2 = StorPoolUtil.snapshotDelete(snapshotName, conn); if (resp2.getError() != null) { - final String err2 = String.format("Failed to delete temporary StorPool snapshot %s. Error: %s", StorpoolUtil.getNameFromResponse(resp, true), resp2.getError()); + final String err2 = String.format("Failed to delete temporary StorPool snapshot %s. Error: %s", StorPoolUtil.getNameFromResponse(resp, true), resp2.getError()); log.error(err2); - StorpoolUtil.spLog(err2); + StorPoolUtil.spLog(err2); } } } @@ -775,7 +775,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal err = String.format("Unsupported copy operation from %s (type %s) to %s (type %s)", srcData.getUuid(), srcType, dstData.getUuid(), dstType); } } catch (Exception e) { - StorpoolUtil.spLog("Caught exception: %s", e.toString()); + StorPoolUtil.spLog("Caught exception: %s", e.toString()); err = e.toString(); } @@ -784,7 +784,7 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal } if (err != null) { - StorpoolUtil.spLog("Failed due to %s", err); + StorPoolUtil.spLog("Failed due to %s", err); log.error(err); answer = new Answer(cmd, false, err); @@ -799,34 +799,34 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { String snapshotName = snapshot.getUuid(); VolumeInfo vinfo = snapshot.getBaseVolume(); - String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); Long vmId = vinfo.getInstanceId(); if (volumeName != null) { - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriver.takeSnapshot volumename=%s vmInstance=%s",volumeName, vmId); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriver.takeSnapshot volumename=%s vmInstance=%s",volumeName, vmId); } else { - throw new UnsupportedOperationException("The path should be: " + StorpoolUtil.SP_DEV_PATH); + throw new UnsupportedOperationException("The path should be: " + StorPoolUtil.SP_DEV_PATH); } CreateObjectAnswer answer = null; String err = null; try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); - SpApiResponse resp = StorpoolUtil.volumeSnapshot(volumeName, snapshotName, vmId != null ? getVMInstanceUUID(vmId) : null, "snapshot", null, conn); + SpApiResponse resp = StorPoolUtil.volumeSnapshot(volumeName, snapshotName, vmId != null ? getVMInstanceUUID(vmId) : null, "snapshot", null, conn); if (resp.getError() != null) { err = String.format("Could not snapshot StorPool volume %s. Error %s", volumeName, resp.getError()); answer = new CreateObjectAnswer(err); } else { - String name = StorpoolUtil.getSnapshotNameFromResponse(resp, true, StorpoolUtil.GLOBAL_ID); + String name = StorPoolUtil.getSnapshotNameFromResponse(resp, true, StorPoolUtil.GLOBAL_ID); SnapshotObjectTO snapTo = (SnapshotObjectTO)snapshot.getTO(); - snapTo.setPath(StorpoolUtil.devPath(name.split("~")[1])); + snapTo.setPath(StorPoolUtil.devPath(name.split("~")[1])); answer = new CreateObjectAnswer(snapTo); StorPoolHelper.addSnapshotDetails(snapshot.getId(), snapshot.getUuid(), snapTo.getPath(), _snapshotDetailsDao); //add primary storage of snapshot - StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), _snapshotDetailsDao); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.takeSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", name, snapshot.getUuid(), volumeName, vinfo.getUuid()); + StorPoolHelper.addSnapshotDetails(snapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(snapshot.getDataStore().getId()), _snapshotDetailsDao); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.takeSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", name, snapshot.getUuid(), volumeName, vinfo.getUuid()); } } catch (Exception e) { err = String.format("Could not take volume snapshot due to %s", e.getMessage()); @@ -841,40 +841,40 @@ public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { final VolumeInfo vinfo = snapshot.getBaseVolume(); final String snapshotName = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), snapshotDataStoreDao, _snapshotDetailsDao); - final String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); - StorpoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.revertSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", snapshotName, snapshot.getUuid(), volumeName, vinfo.getUuid()); + final String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true); + StorPoolUtil.spLog("StorpoolPrimaryDataStoreDriverImpl.revertSnapshot: snapshot: name=%s, uuid=%s, volume: name=%s, uuid=%s", snapshotName, snapshot.getUuid(), volumeName, vinfo.getUuid()); String err = null; SpConnectionDesc conn = null; try { - conn = StorpoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); + conn = StorPoolUtil.getSpConnection(vinfo.getDataStore().getUuid(), vinfo.getDataStore().getId(), storagePoolDetailsDao, primaryStoreDao); } catch (Exception e) { err = String.format("Could not revert volume due to %s", e.getMessage()); completeResponse(err, callback); return; } - VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorpoolUtil.SP_PROVIDER_NAME); + VolumeDetailVO detail = volumeDetailsDao.findDetail(vinfo.getId(), StorPoolUtil.SP_PROVIDER_NAME); if (detail != null) { //Rename volume to its global id only if it was migrated from UUID to global id - SpApiResponse updateVolumeResponse = StorpoolUtil.volumeUpdateRename(StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), "", StorpoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); + SpApiResponse updateVolumeResponse = StorPoolUtil.volumeUpdateRename(StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), "", StorPoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); if (updateVolumeResponse.getError() != null) { - StorpoolUtil.spLog("Could not update StorPool's volume %s to it's globalId due to %s", StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); - err = String.format("Could not update StorPool's volume %s to it's globalId due to %s", StorpoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); + StorPoolUtil.spLog("Could not update StorPool's volume %s to it's globalId due to %s", StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); + err = String.format("Could not update StorPool's volume %s to it's globalId due to %s", StorPoolStorageAdaptor.getVolumeNameFromPath(vinfo.getPath(), true), updateVolumeResponse.getError().getDescr()); completeResponse(err, callback); return; } volumeDetailsDao.remove(detail.getId()); } - SpApiResponse resp = StorpoolUtil.detachAllForced(volumeName, false, conn); + SpApiResponse resp = StorPoolUtil.detachAllForced(volumeName, false, conn); if (resp.getError() != null) { err = String.format("Could not detach StorPool volume %s due to %s", volumeName, resp.getError()); completeResponse(err, callback); return; } - SpApiResponse response = StorpoolUtil.volumeRevert(volumeName, snapshotName, conn); + SpApiResponse response = StorPoolUtil.volumeRevert(volumeName, snapshotName, conn); if (response.getError() != null) { err = String.format( "Could not revert StorPool volume %s to the %s snapshot: could not create the new volume: error %s", @@ -884,9 +884,9 @@ public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snaps } if (vinfo.getMaxIops() != null) { - response = StorpoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); + response = StorPoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); if (response.getError() != null) { - StorpoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", response.getError().getDescr()); + StorPoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", response.getError().getDescr()); } } @@ -895,12 +895,12 @@ public void revertSnapshot(final SnapshotInfo snapshot, final SnapshotInfo snaps } private String getVcPolicyTag(Long vmId) { - ResourceTag resourceTag = vmId != null ? _resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorpoolUtil.SP_VC_POLICY) : null; + ResourceTag resourceTag = vmId != null ? _resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorPoolUtil.SP_VC_POLICY) : null; return resourceTag != null ? resourceTag.getValue() : ""; } public void handleQualityOfServiceForVolumeMigration(VolumeInfo arg0, QualityOfServiceState arg1) { - StorpoolUtil.spLog("handleQualityOfServiceForVolumeMigration with volume name=%s", arg0.getName()); + StorPoolUtil.spLog("handleQualityOfServiceForVolumeMigration with volume name=%s", arg0.getName()); } @@ -940,10 +940,10 @@ public void provideVMInfo(long vmId, long volumeId) { StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); if (poolVO != null) { try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); - String volName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); + String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); VMInstanceVO userVM = vmInstanceDao.findById(vmId); - SpApiResponse resp = StorpoolUtil.volumeUpadateTags(volName, volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, getVcPolicyTag(vmId)); + SpApiResponse resp = StorPoolUtil.volumeUpadateTags(volName, volume.getInstanceId() != null ? userVM.getUuid() : "", null, conn, getVcPolicyTag(vmId)); if (resp.getError() != null) { log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); } @@ -955,7 +955,7 @@ public void provideVMInfo(long vmId, long volumeId) { @Override public boolean vmTagsNeeded(String tagKey) { - return tagKey != null && tagKey.equals(StorpoolUtil.SP_VC_POLICY); + return tagKey != null && tagKey.equals(StorPoolUtil.SP_VC_POLICY); } @Override @@ -964,9 +964,9 @@ public void provideVMTags(long vmId, long volumeId, String tagValue) { StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); if (poolVO != null) { try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); - String volName = StorpoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); - SpApiResponse resp = StorpoolUtil.volumeUpadateVCTags(volName, conn, getVcPolicyTag(vmId)); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolVO.getId(), storagePoolDetailsDao, primaryStoreDao); + String volName = StorPoolStorageAdaptor.getVolumeNameFromPath(volume.getPath(), true); + SpApiResponse resp = StorPoolUtil.volumeUpadateVCTags(volName, conn, getVcPolicyTag(vmId)); if (resp.getError() != null) { log.warn(String.format("Could not update VC policy tags of a volume with id [%s]", volume.getUuid())); } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java similarity index 88% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java index 2df06f1bfb64..8ed39327f39c 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorpoolPrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/StorPoolPrimaryDataStoreLifeCycle.java @@ -34,16 +34,16 @@ 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.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; import org.apache.log4j.Logger; import com.cloud.agent.api.StoragePoolInfo; import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.resource.ResourceManager; import com.cloud.storage.ScopeType; import com.cloud.storage.SnapshotVO; @@ -60,8 +60,8 @@ import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.utils.exception.CloudRuntimeException; -public class StorpoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { - private static final Logger log = Logger.getLogger(StorpoolPrimaryDataStoreLifeCycle.class); +public class StorPoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { + private static final Logger log = Logger.getLogger(StorPoolPrimaryDataStoreLifeCycle.class); @Inject protected PrimaryDataStoreHelper dataStoreHelper; @@ -86,11 +86,11 @@ public class StorpoolPrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCy @Override public DataStore initialize(Map dsInfos) { - StorpoolUtil.spLog("initialize:"); + StorPoolUtil.spLog("initialize:"); for (Map.Entry e: dsInfos.entrySet()) { - StorpoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); + StorPoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); } - StorpoolUtil.spLog(""); + StorPoolUtil.spLog(""); log.debug("initialize"); @@ -109,7 +109,7 @@ public DataStore initialize(Map dsInfos) { if (conn.getTemplateName() == null) throw new IllegalArgumentException("No SP_TEMPLATE"); - if (!StorpoolUtil.templateExists(conn)) { + if (!StorPoolUtil.templateExists(conn)) { throw new IllegalArgumentException("No such storpool template " + conn.getTemplateName() + " or credentials are invalid"); } @@ -121,13 +121,13 @@ public DataStore initialize(Map dsInfos) { SpConnectionDesc old = null; for (StoragePoolDetailVO storagePoolDetailVO : spDetails) { switch (storagePoolDetailVO.getName()) { - case StorpoolUtil.SP_AUTH_TOKEN: + case StorPoolUtil.SP_AUTH_TOKEN: authToken = storagePoolDetailVO.getValue(); break; - case StorpoolUtil.SP_HOST_PORT: + case StorPoolUtil.SP_HOST_PORT: host = storagePoolDetailVO.getValue(); break; - case StorpoolUtil.SP_TEMPLATE: + case StorPoolUtil.SP_TEMPLATE: template = storagePoolDetailVO.getValue(); break; default: @@ -155,9 +155,9 @@ public DataStore initialize(Map dsInfos) { @SuppressWarnings("unchecked") Map details = (Map)dsInfos.get("details"); - details.put(StorpoolUtil.SP_AUTH_TOKEN, conn.getAuthToken()); - details.put(StorpoolUtil.SP_HOST_PORT, conn.getHostPort()); - details.put(StorpoolUtil.SP_TEMPLATE, conn.getTemplateName()); + details.put(StorPoolUtil.SP_AUTH_TOKEN, conn.getAuthToken()); + details.put(StorPoolUtil.SP_HOST_PORT, conn.getHostPort()); + details.put(StorPoolUtil.SP_TEMPLATE, conn.getTemplateName()); PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters(); parameters.setName(name); @@ -169,7 +169,7 @@ public DataStore initialize(Map dsInfos) { parameters.setManaged(false); parameters.setHost("n/a"); parameters.setPort(0); - parameters.setPath(StorpoolUtil.SP_DEV_PATH); + parameters.setPath(StorPoolUtil.SP_DEV_PATH); parameters.setUsedBytes(0); parameters.setCapacityBytes(capacityBytes); parameters.setTags(tags); @@ -180,11 +180,11 @@ public DataStore initialize(Map dsInfos) { @Override public void updateStoragePool(StoragePool storagePool, Map details) { - StorpoolUtil.spLog("updateStoragePool:"); + StorPoolUtil.spLog("updateStoragePool:"); for (Map.Entry e: details.entrySet()) { - StorpoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); + StorPoolUtil.spLog(" %s=%s", e.getKey(), e.getValue()); } - StorpoolUtil.spLog(""); + StorPoolUtil.spLog(""); log.debug("updateStoragePool"); return; @@ -250,7 +250,7 @@ public boolean deleteDataStore(DataStore store) { if (lstSnapshots != null) { for (SnapshotVO snapshot : lstSnapshots) { - SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID); + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID); // if this snapshot belongs to the storagePool that was passed in if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePoolId) { @@ -263,7 +263,7 @@ public boolean deleteDataStore(DataStore store) { if (lstTemplateDetails != null) { for (VMTemplateDetailVO vmTemplateDetailVO : lstTemplateDetails) { - if (vmTemplateDetailVO.getName().equals(StorpoolUtil.SP_STORAGE_POOL_ID) && Long.parseLong(vmTemplateDetailVO.getValue()) == storagePoolId) { + if (vmTemplateDetailVO.getName().equals(StorPoolUtil.SP_STORAGE_POOL_ID) && Long.parseLong(vmTemplateDetailVO.getValue()) == storagePoolId) { throw new CloudRuntimeException("This primary storage cannot be deleted because it currently contains one or more template snapshots."); } } @@ -273,15 +273,15 @@ public boolean deleteDataStore(DataStore store) { SpConnectionDesc conn = null; try { - conn = StorpoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + conn = StorPoolUtil.getSpConnection(store.getUuid(), store.getId(), storagePoolDetailsDao, _primaryDataStoreDao); } catch (CloudRuntimeException e) { throw e; } if (lstTemplatePoolRefs != null) { for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { - SpApiResponse resp = StorpoolUtil.snapshotDelete( - StorpoolStorageAdaptor.getVolumeNameFromPath(templatePoolRef.getLocalDownloadPath(), true), conn); + SpApiResponse resp = StorPoolUtil.snapshotDelete( + StorPoolStorageAdaptor.getVolumeNameFromPath(templatePoolRef.getLocalDownloadPath(), true), conn); if (resp.getError() != null) { throw new CloudRuntimeException(String.format("Could not delete StorPool's snapshot from template_spool_ref table due to %s", resp.getError())); } @@ -292,8 +292,8 @@ public boolean deleteDataStore(DataStore store) { if (isDeleted) { List volumesOnHosts = storagePoolDetailsDao.listDetails(storagePoolId); for (StoragePoolDetailVO storagePoolDetailVO : volumesOnHosts) { - if (storagePoolDetailVO.getValue() != null && storagePoolDetailVO.getName().contains(StorpoolUtil.SP_VOLUME_ON_CLUSTER)) { - StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(storagePoolDetailVO.getValue(), true), conn); + if (storagePoolDetailVO.getValue() != null && storagePoolDetailVO.getName().contains(StorPoolUtil.SP_VOLUME_ON_CLUSTER)) { + StorPoolUtil.volumeDelete(StorPoolStorageAdaptor.getVolumeNameFromPath(storagePoolDetailVO.getValue(), true), conn); } } storagePoolDetailsDao.removeDetails(storagePoolId); diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java similarity index 89% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java index 12787995a145..11fb75ff4bff 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolHostListener.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java @@ -33,9 +33,9 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -49,15 +49,15 @@ import com.cloud.exception.StorageConflictException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.exception.CloudRuntimeException; -public class StorpoolHostListener implements HypervisorHostListener { - private static final Logger log = Logger.getLogger(StorpoolHostListener .class); +public class StorPoolHostListener implements HypervisorHostListener { + private static final Logger log = Logger.getLogger(StorPoolHostListener .class); @Inject private AgentManager agentMgr; @@ -85,7 +85,7 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep SpConnectionDesc conn = null; try { - conn = StorpoolUtil.getSpConnection(poolVO.getUuid(), poolId, storagePoolDetailsDao, primaryStoreDao); + conn = StorPoolUtil.getSpConnection(poolVO.getUuid(), poolId, storagePoolDetailsDao, primaryStoreDao); } catch (Exception e) { return false; } @@ -114,7 +114,7 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep if (!answer.getResult()) { if (answer.getDetails() != null) { if (answer.getDetails().equals("objectDoesNotExist")) { - StorpoolUtil.volumeDelete(StorpoolStorageAdaptor.getVolumeNameFromPath(volumeOnPool.getValue(), true), conn); + StorPoolUtil.volumeDelete(StorPoolStorageAdaptor.getVolumeNameFromPath(volumeOnPool.getValue(), true), conn); storagePoolDetailsDao.remove(volumeOnPool.getId()); return false; } else if (answer.getDetails().equals("spNotFound")) { @@ -128,7 +128,7 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep pool.getId()); } - StorpoolUtil.spLog("hostConnect: hostId=%d, poolId=%d", hostId, poolId); + StorPoolUtil.spLog("hostConnect: hostId=%d, poolId=%d", hostId, poolId); StorPoolModifyStoragePoolAnswer mspAnswer = (StorPoolModifyStoragePoolAnswer)answer; if (mspAnswer.getLocalDatastoreName() != null && pool.isShared()) { @@ -157,14 +157,14 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep } private synchronized StoragePoolDetailVO verifyVolumeIsOnCluster(long poolId, SpConnectionDesc conn, long clusterId) { - StoragePoolDetailVO volumeOnPool = storagePoolDetailsDao.findDetail(poolId, StorpoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId); + StoragePoolDetailVO volumeOnPool = storagePoolDetailsDao.findDetail(poolId, StorPoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId); if (volumeOnPool == null) { - SpApiResponse resp = StorpoolUtil.volumeCreate(conn); + SpApiResponse resp = StorPoolUtil.volumeCreate(conn); if (resp.getError() != null) { return volumeOnPool; } - String volumeName = StorpoolUtil.getNameFromResponse(resp, false); - volumeOnPool = new StoragePoolDetailVO(poolId, StorpoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId, StorpoolUtil.devPath(volumeName), false); + String volumeName = StorPoolUtil.getNameFromResponse(resp, false); + volumeOnPool = new StoragePoolDetailVO(poolId, StorPoolUtil.SP_VOLUME_ON_CLUSTER + "-" + clusterId, StorPoolUtil.devPath(volumeName), false); storagePoolDetailsDao.persist(volumeOnPool); } return volumeOnPool; @@ -177,7 +177,7 @@ public boolean hostAdded(long hostId) { @Override public boolean hostDisconnected(long hostId, long poolId) { - StorpoolUtil.spLog("hostDisconnected: hostId=%d, poolId=%d", hostId, poolId); + StorPoolUtil.spLog("hostDisconnected: hostId=%d, poolId=%d", hostId, poolId); return true; } @@ -210,7 +210,7 @@ private void addModifyCommandToCommandsAllowedInMaintenanceMode() { field.set(null, allowedCmdsInMaintenanceNew); } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { String err = "Could not add StorPoolModifyStoragePoolCommand to s_commandsAllowedInMaintenanceMode array due to: %s"; - StorpoolUtil.spLog(err, e.getMessage()); + StorPoolUtil.spLog(err, e.getMessage()); log.warn(String.format(err, e.getMessage())); } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolPrimaryDataStoreProvider.java similarity index 80% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolPrimaryDataStoreProvider.java index 9ea963d433de..892d95b8c922 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorpoolPrimaryDataStoreProvider.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolPrimaryDataStoreProvider.java @@ -26,24 +26,24 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; -import org.apache.cloudstack.storage.datastore.driver.StorpoolPrimaryDataStoreDriver; -import org.apache.cloudstack.storage.datastore.lifecycle.StorpoolPrimaryDataStoreLifeCycle; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; +import org.apache.cloudstack.storage.datastore.driver.StorPoolPrimaryDataStoreDriver; +import org.apache.cloudstack.storage.datastore.lifecycle.StorPoolPrimaryDataStoreLifeCycle; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import com.cloud.utils.component.ComponentContext; -public class StorpoolPrimaryDataStoreProvider implements PrimaryDataStoreProvider { +public class StorPoolPrimaryDataStoreProvider implements PrimaryDataStoreProvider { protected DataStoreLifeCycle lifecycle; protected DataStoreDriver driver; protected HypervisorHostListener listener; - StorpoolPrimaryDataStoreProvider() { + StorPoolPrimaryDataStoreProvider() { } @Override public String getName() { - return StorpoolUtil.SP_PROVIDER_NAME; + return StorPoolUtil.SP_PROVIDER_NAME; } @Override @@ -63,9 +63,9 @@ public HypervisorHostListener getHostListener() { @Override public boolean configure(Map params) { - lifecycle = ComponentContext.inject(StorpoolPrimaryDataStoreLifeCycle.class); - driver = ComponentContext.inject(StorpoolPrimaryDataStoreDriver.class); - listener = ComponentContext.inject(StorpoolHostListener.class); + lifecycle = ComponentContext.inject(StorPoolPrimaryDataStoreLifeCycle.class); + driver = ComponentContext.inject(StorPoolPrimaryDataStoreDriver.class); + listener = ComponentContext.inject(StorPoolHostListener.class); return true; } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java index 4f51ef2bae3e..aca95fb48d0c 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java @@ -33,7 +33,7 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.collections.CollectionUtils; @@ -48,7 +48,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.storage.DataStoreRole; @@ -76,7 +76,7 @@ public class StorPoolHelper { public static void updateVolumeInfo(VolumeObjectTO volumeObjectTO, Long size, SpApiResponse resp, VolumeDao volumeDao) { - String volumePath = StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(resp, false)); + String volumePath = StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(resp, false)); VolumeVO volume = volumeDao.findById(volumeObjectTO.getId()); if (volume != null) { volumeObjectTO.setSize(size); @@ -101,12 +101,12 @@ public static String getSnapshotName(Long snapshotId, String snapshotUuid, Snaps SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, snapshotUuid); if (snapshotDetails != null) { - return StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetails.getValue(), true); + return StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetails.getValue(), true); } else { List snapshots = snapshotStoreDao.findBySnapshotId(snapshotId); if (!CollectionUtils.isEmpty(snapshots)) { for (SnapshotDataStoreVO snapshotDataStoreVO : snapshots) { - String name = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDataStoreVO.getInstallPath(), true); + String name = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDataStoreVO.getInstallPath(), true); if (name == null) { continue; } else { @@ -129,7 +129,7 @@ public static void updateSnapshotDetailsValue(Long id, String valueOrName, Strin } else if (snapshotOrVolume.equals("volume")) { sql = UPDATE_VOLUME_DETAILS_NAME; } else { - StorpoolUtil.spLog("Could not update snapshot detail with id=%s", id); + StorPoolUtil.spLog("Could not update snapshot detail with id=%s", id); } if (sql != null) { pstmt = txn.prepareAutoCloseStatement(sql); @@ -140,13 +140,13 @@ public static void updateSnapshotDetailsValue(Long id, String valueOrName, Strin } } catch (Exception e) { txn.rollback(); - StorpoolUtil.spLog("Could not update snapshot detail with id=%s", id); + StorPoolUtil.spLog("Could not update snapshot detail with id=%s", id); } } public static String getVcPolicyTag(Long vmId, ResourceTagDao resourceTagDao) { if (vmId != null) { - ResourceTag tag = resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorpoolUtil.SP_VC_POLICY); + ResourceTag tag = resourceTagDao.findByKey(vmId, ResourceObjectType.UserVm, StorPoolUtil.SP_VC_POLICY); if (tag != null) { return tag.getValue(); } @@ -168,7 +168,7 @@ public static Map addStorPoolTags(String name, String vmUuid, St Map tags = new HashMap<>(); tags.put("uuid", name); tags.put("cvm", vmUuid); - tags.put(StorpoolUtil.SP_VC_POLICY, vcPolicy); + tags.put(StorPoolUtil.SP_VC_POLICY, vcPolicy); if (csTag != null) { tags.put("cs", csTag); } @@ -191,11 +191,11 @@ public static void appendLogger(Logger log, String filePath, String kindOfLog) { e.printStackTrace(); } if (kindOfLog.equals("update")) { - StorpoolUtil.spLog( + StorPoolUtil.spLog( "You can find information about volumes and snapshots, which will be updated in Database with their globalIs in %s log file", path); } else if (kindOfLog.equals("abandon")) { - StorpoolUtil.spLog( + StorPoolUtil.spLog( "You can find information about volumes and snapshots, for which CloudStack doesn't have information in %s log file", path); } @@ -221,13 +221,13 @@ public static void setSpClusterIdIfNeeded(long hostId, String clusterId, Cluster public static Long findClusterIdByGlobalId(String globalId, ClusterDao clusterDao) { List clusterVo = clusterDao.listAll(); if (clusterVo.size() == 1) { - StorpoolUtil.spLog("There is only one cluster, sending backup to secondary command"); + StorPoolUtil.spLog("There is only one cluster, sending backup to secondary command"); return null; } for (ClusterVO clusterVO2 : clusterVo) { if (globalId != null && StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()) != null && globalId.contains(StorPoolConfigurationManager.StorPoolClusterId.valueIn(clusterVO2.getId()).toString())) { - StorpoolUtil.spLog("Found cluster with id=%s for object with globalId=%s", clusterVO2.getId(), + StorPoolUtil.spLog("Found cluster with id=%s for object with globalId=%s", clusterVO2.getId(), globalId); return clusterVO2.getId(); } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java similarity index 98% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java index d9a32201ff6b..6e1530383751 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorpoolUtil.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java @@ -55,7 +55,7 @@ //import org.apache.http.impl.conn.BasicClientConnectionManager; import org.apache.log4j.Logger; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; @@ -66,8 +66,8 @@ import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; -public class StorpoolUtil { - private static final Logger log = Logger.getLogger(StorpoolUtil.class); +public class StorPoolUtil { + private static final Logger log = Logger.getLogger(StorPoolUtil.class); private static final File spLogFile = new File("/var/log/cloudstack/management/storpool-plugin.log"); private static PrintWriter spLogPrinterWriter = spLogFileInitialize(); @@ -258,8 +258,8 @@ public String getTemplateName() { public static SpConnectionDesc getSpConnection(String url, long poolId, StoragePoolDetailsDao poolDetails, PrimaryDataStoreDao storagePool) { - boolean isElternateEndpointEnabled = StorPoolConfigurationManager.AlternativeEndPointEnabled.valueIn(poolId); - if (isElternateEndpointEnabled) { + boolean isAlternateEndpointEnabled = StorPoolConfigurationManager.AlternativeEndPointEnabled.valueIn(poolId); + if (isAlternateEndpointEnabled) { String alternateEndpoint = StorPoolConfigurationManager.AlternativeEndpoint.valueIn(poolId); if (StringUtils.isNotEmpty(alternateEndpoint)) { return new SpConnectionDesc(alternateEndpoint); @@ -300,7 +300,7 @@ private static SpConnectionDesc updateStorageAndStorageDetails(String url, long StoragePoolVO pool = storagePool.findById(poolId); pool.setUuid(conn.getTemplateName() + ";" + UUID.randomUUID().toString()); storagePool.update(poolId, pool); - StorpoolUtil.spLog( + StorPoolUtil.spLog( "Storage pool with id=%s and template's name=%s was updated and its connection details are hidden from UI.", pool.getId(), conn.getTemplateName()); return conn; @@ -542,7 +542,7 @@ public static SpApiResponse volumesGroupSnapshot(final List volu List> volumes = new ArrayList<>(); for (VolumeObjectTO volumeTO : volumeTOs) { Map vol = new LinkedHashMap<>(); - String name = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeTO.getPath(), true); + String name = StorPoolStorageAdaptor.getVolumeNameFromPath(volumeTO.getPath(), true); vol.put("name", ""); vol.put("volume", name); volumes.add(vol); diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java index 74a52c3fce13..1608680e41b7 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/motion/StorPoolDataMotionStrategy.java @@ -52,9 +52,9 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; @@ -71,7 +71,7 @@ import com.cloud.agent.api.ModifyTargetsAnswer; import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.agent.api.PrepareForMigrationCommand; -import com.cloud.agent.api.storage.StorpoolBackupTemplateFromSnapshotCommand; +import com.cloud.agent.api.storage.StorPoolBackupTemplateFromSnapshotCommand; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.dc.dao.ClusterDao; @@ -153,12 +153,12 @@ public StrategyPriority canHandle(DataObject srcData, DataObject destData) { SnapshotInfo sinfo = (SnapshotInfo) srcData; VolumeInfo volume = sinfo.getBaseVolume(); StoragePoolVO storagePool = _storagePool.findById(volume.getPoolId()); - if (!storagePool.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + if (!storagePool.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { return StrategyPriority.CANT_HANDLE; } String snapshotName = StorPoolHelper.getSnapshotName(sinfo.getId(), sinfo.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); - StorpoolUtil.spLog("StorPoolDataMotionStrategy.canHandle snapshot name=%s", snapshotName); + StorPoolUtil.spLog("StorPoolDataMotionStrategy.canHandle snapshot name=%s", snapshotName); if (snapshotName != null) { return StrategyPriority.HIGHEST; } @@ -176,7 +176,7 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, SnapshotInfo sInfo = _snapshotDataFactory.getSnapshot(snapshot.getId(), store); VolumeInfo vInfo = sInfo.getBaseVolume(); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(vInfo.getDataStore().getUuid(), + SpConnectionDesc conn = StorPoolUtil.getSpConnection(vInfo.getDataStore().getUuid(), vInfo.getDataStore().getId(), _storagePoolDetails, _storagePool); String name = template.getUuid(); String volumeName = ""; @@ -184,20 +184,20 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, String parentName = StorPoolHelper.getSnapshotName(sInfo.getId(), sInfo.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); // TODO volume tags cs - template - SpApiResponse res = StorpoolUtil.volumeCreate(name, parentName, sInfo.getSize(), null, "no", "template", null, + SpApiResponse res = StorPoolUtil.volumeCreate(name, parentName, sInfo.getSize(), null, "no", "template", null, conn); CopyCmdAnswer answer = null; String err = null; if (res.getError() != null) { log.debug(String.format("Could not create volume from snapshot with ID=%s", snapshot.getId())); - StorpoolUtil.spLog("Volume create failed with error=%s", res.getError().getDescr()); + StorPoolUtil.spLog("Volume create failed with error=%s", res.getError().getDescr()); err = res.getError().getDescr(); } else { - volumeName = StorpoolUtil.getNameFromResponse(res, true); + volumeName = StorPoolUtil.getNameFromResponse(res, true); SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(sInfo.getId(), sInfo.getUuid()); snapshot.setPath(snapshotDetails.getValue()); - Command backupSnapshot = new StorpoolBackupTemplateFromSnapshotCommand(snapshot, template, + Command backupSnapshot = new StorPoolBackupTemplateFromSnapshotCommand(snapshot, template, StorPoolHelper.getTimeout(StorPoolHelper.BackupSnapshotWait, _configDao), VirtualMachineManager.ExecuteInSequence.value()); @@ -215,28 +215,28 @@ public void copyAsync(DataObject srcData, DataObject destData, Host destHost, } else { answer = (CopyCmdAnswer) ep2.sendMessage(backupSnapshot); if (answer != null && answer.getResult()) { - SpApiResponse resSnapshot = StorpoolUtil.volumeFreeze(volumeName, conn); + SpApiResponse resSnapshot = StorPoolUtil.volumeFreeze(volumeName, conn); if (resSnapshot.getError() != null) { log.debug(String.format("Could not snapshot volume with ID=%s", snapshot.getId())); - StorpoolUtil.spLog("Volume freeze failed with error=%s", resSnapshot.getError().getDescr()); + StorPoolUtil.spLog("Volume freeze failed with error=%s", resSnapshot.getError().getDescr()); err = resSnapshot.getError().getDescr(); - StorpoolUtil.volumeDelete(volumeName, conn); + StorPoolUtil.volumeDelete(volumeName, conn); } else { StorPoolHelper.updateVmStoreTemplate(template.getId(), template.getDataStore().getRole(), - StorpoolUtil.devPath(StorpoolUtil.getNameFromResponse(res, false)), _templStoreDao); + StorPoolUtil.devPath(StorPoolUtil.getNameFromResponse(res, false)), _templStoreDao); } } else { err = "Could not copy template to secondary " + answer.getResult(); - StorpoolUtil.volumeDelete(StorpoolUtil.getNameFromResponse(res, true), conn); + StorPoolUtil.volumeDelete(StorPoolUtil.getNameFromResponse(res, true), conn); } } } catch (CloudRuntimeException e) { err = e.getMessage(); } } - _vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, + _vmTemplateDetailsDao.persist(new VMTemplateDetailVO(template.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(vInfo.getDataStore().getId()), false)); - StorpoolUtil.spLog("StorPoolDataMotionStrategy.copyAsync Creating snapshot=%s for StorPool template=%s", + StorPoolUtil.spLog("StorPoolDataMotionStrategy.copyAsync Creating snapshot=%s for StorPool template=%s", volumeName, conn.getTemplateName()); final CopyCommandResult cmd = new CopyCommandResult(null, answer); cmd.setResult(err); @@ -261,7 +261,7 @@ private boolean isDestinationStorPoolPrimaryStorage(Map v for (DataStore dataStore : volumeMap.values()) { StoragePoolVO storagePoolVO = _storagePool.findById(dataStore.getId()); if (storagePoolVO == null - || !storagePoolVO.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + || !storagePoolVO.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { return false; } } @@ -305,17 +305,17 @@ public void copyAsync(Map volumeDataStoreMap, VirtualMach destVolumeInfo.processEvent(Event.MigrationCopySucceeded); destVolumeInfo.processEvent(Event.MigrationRequested); - conn = StorpoolUtil.getSpConnection(destDataStore.getUuid(), destDataStore.getId(), _storagePoolDetails, + conn = StorPoolUtil.getSpConnection(destDataStore.getUuid(), destDataStore.getId(), _storagePoolDetails, _storagePool); - SpApiResponse resp = StorpoolUtil.volumeCreate(srcVolume.getUuid(), null, srcVolume.getSize(), + SpApiResponse resp = StorPoolUtil.volumeCreate(srcVolume.getUuid(), null, srcVolume.getSize(), vmTO.getUuid(), null, "volume", srcVolume.getMaxIops(), conn); if (resp.getError() == null) { - newVolName = StorpoolUtil.getNameFromResponse(resp, true); + newVolName = StorPoolUtil.getNameFromResponse(resp, true); } - String volumeName = StorpoolUtil.getNameFromResponse(resp, false); - destVolume.setPath(StorpoolUtil.devPath(volumeName)); + String volumeName = StorPoolUtil.getNameFromResponse(resp, false); + destVolume.setPath(StorPoolUtil.devPath(volumeName)); _volumeDao.update(destVolume.getId(), destVolume); destVolume = _volumeDao.findById(destVolume.getId()); @@ -401,7 +401,7 @@ public void copyAsync(Map volumeDataStoreMap, VirtualMach private void deleteVolumeOnFail(String newVolName, SpConnectionDesc conn) { if (newVolName != null && conn != null) { - StorpoolUtil.volumeDelete(newVolName, conn); + StorPoolUtil.volumeDelete(newVolName, conn); } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java similarity index 91% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java index 2a00de3604b8..79b9d8b11e30 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java @@ -33,14 +33,14 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; @@ -54,8 +54,8 @@ @Component -public class StorpoolSnapshotStrategy implements SnapshotStrategy { - private static final Logger log = Logger.getLogger(StorpoolSnapshotStrategy.class); +public class StorPoolSnapshotStrategy implements SnapshotStrategy { + private static final Logger log = Logger.getLogger(StorPoolSnapshotStrategy.class); @Inject private SnapshotDao _snapshotDao; @@ -81,11 +81,11 @@ public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) { snapshotObj.processEvent(Snapshot.Event.BackupToSecondary); snapshotObj.processEvent(Snapshot.Event.OperationSucceeded); } catch (NoTransitionException ex) { - StorpoolUtil.spLog("Failed to change state: " + ex.toString()); + StorPoolUtil.spLog("Failed to change state: " + ex.toString()); try { snapshotObj.processEvent(Snapshot.Event.OperationFailed); } catch (NoTransitionException ex2) { - StorpoolUtil.spLog("Failed to change state: " + ex2.toString()); + StorPoolUtil.spLog("Failed to change state: " + ex2.toString()); } } return snapshotInfo; @@ -100,20 +100,20 @@ public boolean deleteSnapshot(Long snapshotId) { boolean res = false; // clean-up snapshot from Storpool storage pools StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); - if (storage.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + if (storage.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { try { - SpConnectionDesc conn = StorpoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); - SpApiResponse resp = StorpoolUtil.snapshotDelete(name, conn); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(storage.getUuid(), storage.getId(), storagePoolDetailsDao, _primaryDataStoreDao); + SpApiResponse resp = StorPoolUtil.snapshotDelete(name, conn); if (resp.getError() != null) { final String err = String.format("Failed to clean-up Storpool snapshot %s. Error: %s", name, resp.getError()); - StorpoolUtil.spLog(err); + StorPoolUtil.spLog(err); } else { SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, snapshotVO.getUuid()); if (snapshotDetails != null) { _snapshotDetailsDao.removeDetails(snapshotId); } res = deleteSnapshotFromDb(snapshotId); - StorpoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfuly=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.deleteSnapshot: executed successfuly=%s, snapshot uuid=%s, name=%s", res, snapshotVO.getUuid(), name); } } catch (Exception e) { String errMsg = String.format("Cannot delete snapshot due to %s", e.getMessage()); @@ -126,7 +126,7 @@ public boolean deleteSnapshot(Long snapshotId) { @Override public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { - StorpoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op); if (op != SnapshotOperation.DELETE) { return StrategyPriority.CANT_HANDLE; @@ -135,9 +135,9 @@ public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId()); StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); - if (storage.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + if (storage.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { if (name != null) { - StorpoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); + StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); return StrategyPriority.HIGHEST; } @@ -179,7 +179,7 @@ private boolean deleteSnapshotChain(SnapshotInfo snapshot) { } if (!deleted) { SnapshotInfo snap = snapshotDataFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); - if (StorpoolStorageAdaptor.getVolumeNameFromPath(snap.getPath(), true) == null) { + if (StorPoolStorageAdaptor.getVolumeNameFromPath(snap.getPath(), true) == null) { try { boolean r = snapshotSvr.deleteSnapshot(snapshot); if (r) { diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java similarity index 89% rename from plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java rename to plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java index f9ad4a0002fa..ec7e89a23911 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorpoolVMSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolVMSnapshotStrategy.java @@ -30,9 +30,9 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpApiResponse; -import org.apache.cloudstack.storage.datastore.util.StorpoolUtil.SpConnectionDesc; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; +import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpConnectionDesc; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.vmsnapshot.DefaultVMSnapshotStrategy; import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper; @@ -42,7 +42,7 @@ import com.cloud.agent.api.VMSnapshotTO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; -import com.cloud.hypervisor.kvm.storage.StorpoolStorageAdaptor; +import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; @@ -65,8 +65,8 @@ import com.google.gson.JsonObject; @Component -public class StorpoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { - private static final Logger log = Logger.getLogger(StorpoolVMSnapshotStrategy.class); +public class StorPoolVMSnapshotStrategy extends DefaultVMSnapshotStrategy { + private static final Logger log = Logger.getLogger(StorPoolVMSnapshotStrategy.class); @Inject private VMSnapshotHelper vmSnapshotHelper; @@ -109,7 +109,7 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); long prev_chain_size = 0; long virtual_size = 0; @@ -131,32 +131,32 @@ public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { vmSnapshotVO.setParent(current.getId()); } - SpApiResponse resp = StorpoolUtil.volumesGroupSnapshot(volumeTOs, userVm.getUuid(), vmSnapshotVO.getUuid(), "group", conn); + SpApiResponse resp = StorPoolUtil.volumesGroupSnapshot(volumeTOs, userVm.getUuid(), vmSnapshotVO.getUuid(), "group", conn); JsonObject obj = resp.fullJson.getAsJsonObject(); JsonArray snapshots = obj.getAsJsonObject("data").getAsJsonArray("snapshots"); - StorpoolUtil.spLog("Volumes=%s attached to virtual machine", volumeTOs.toString()); + StorPoolUtil.spLog("Volumes=%s attached to virtual machine", volumeTOs.toString()); for (VolumeObjectTO vol : volumeTOs) { for (JsonElement jsonElement : snapshots) { JsonObject snapshotObject = jsonElement.getAsJsonObject(); - String snapshot = StorpoolUtil - .devPath(snapshotObject.getAsJsonPrimitive(StorpoolUtil.GLOBAL_ID).getAsString()); - if (snapshotObject.getAsJsonPrimitive("volume").getAsString().equals(StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true)) - || snapshotObject.getAsJsonPrimitive("volumeGlobalId").getAsString().equals(StorpoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), false))) { + String snapshot = StorPoolUtil + .devPath(snapshotObject.getAsJsonPrimitive(StorPoolUtil.GLOBAL_ID).getAsString()); + if (snapshotObject.getAsJsonPrimitive("volume").getAsString().equals(StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), true)) + || snapshotObject.getAsJsonPrimitive("volumeGlobalId").getAsString().equals(StorPoolStorageAdaptor.getVolumeNameFromPath(vol.getPath(), false))) { VMSnapshotDetailsVO vmSnapshotDetailsVO = new VMSnapshotDetailsVO(vmSnapshot.getId(), vol.getUuid(), snapshot, false); vmSnapshotDetailsDao.persist(vmSnapshotDetailsVO); Long poolId = volumeDao.findById(vol.getId()).getPoolId(); if (poolId != null) { VMSnapshotDetailsVO vmSnapshotDetailStoragePoolId = new VMSnapshotDetailsVO( - vmSnapshot.getId(), StorpoolUtil.SP_STORAGE_POOL_ID, String.valueOf(poolId), false); + vmSnapshot.getId(), StorPoolUtil.SP_STORAGE_POOL_ID, String.valueOf(poolId), false); vmSnapshotDetailsDao.persist(vmSnapshotDetailStoragePoolId); } - StorpoolUtil.spLog("Snapshot=%s of volume=%s for a group snapshot=%s.", snapshot, vol.getUuid(), vmSnapshot.getUuid()); + StorPoolUtil.spLog("Snapshot=%s of volume=%s for a group snapshot=%s.", snapshot, vol.getUuid(), vmSnapshot.getUuid()); } } } if (resp.getError() == null) { - StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.takeSnapshot answer=%s", resp.getError()); + StorPoolUtil.spLog("StorpoolVMSnapshotStrategy.takeSnapshot answer=%s", resp.getError()); finalizeCreate(vmSnapshotVO, volumeTOs); result = vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); long new_chain_size = 0; @@ -205,7 +205,7 @@ private StrategyPriority areAllVolumesOnStorPool(Long vmId) { for (VolumeObjectTO volumeTO : volumeTOs) { Long poolId = volumeTO.getPoolId(); StoragePoolVO pool = storagePool.findById(poolId); - if (!pool.getStorageProviderName().equals(StorpoolUtil.SP_PROVIDER_NAME)) { + if (!pool.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { return StrategyPriority.CANT_HANDLE; } } @@ -228,7 +228,7 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); SpConnectionDesc conn = null; try { - conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); } catch (CloudRuntimeException e) { throw e; } @@ -237,14 +237,14 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { for (VolumeObjectTO volumeObjectTO : volumeTOs) { String err = null; VMSnapshotDetailsVO snapshotDetailsVO = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), volumeObjectTO.getUuid()); - String snapshotName = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); + String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); if (snapshotName == null) { err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", vmSnapshot.getUuid(), volumeObjectTO.getUuid()); log.error("Could not delete snapshot for vm:" + err); } - StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.deleteVMSnapshot snapshotName=%s", snapshotName); - resp = StorpoolUtil.snapshotDelete(snapshotName, conn); + StorPoolUtil.spLog("StorpoolVMSnapshotStrategy.deleteVMSnapshot snapshotName=%s", snapshotName); + resp = StorPoolUtil.snapshotDelete(snapshotName, conn); if (resp.getError() != null) { err = String.format("Could not delete storpool vm error=%s", resp.getError()); log.error("Could not delete snapshot for vm:" + err); @@ -255,7 +255,7 @@ public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { } } if (err != null) { - StorpoolUtil.spLog( + StorPoolUtil.spLog( "StorpoolVMSnapshotStrategy.deleteVMSnapshot delete snapshot=%s of gropusnapshot=%s failed due to %s", snapshotName, userVm.getInstanceName(), err); throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " @@ -297,37 +297,37 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); DataStore dataStore = dataStoreManager.getPrimaryDataStore(volumeTOs.get(0).getDataStore().getUuid()); - SpConnectionDesc conn = StorpoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); + SpConnectionDesc conn = StorPoolUtil.getSpConnection(dataStore.getUuid(), dataStore.getId(), storagePoolDetailsDao, storagePool); for (VolumeObjectTO volumeObjectTO : volumeTOs) { String err = null; VMSnapshotDetailsVO snapshotDetailsVO = vmSnapshotDetailsDao.findDetail(vmSnapshot.getId(), volumeObjectTO.getUuid()); - String snapshotName = StorpoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); + String snapshotName = StorPoolStorageAdaptor.getVolumeNameFromPath(snapshotDetailsVO.getValue(), true); if (snapshotName == null) { err = String.format("Could not find StorPool's snapshot vm snapshot uuid=%s and volume uui=%s", vmSnapshot.getUuid(), volumeObjectTO.getUuid()); log.error("Could not delete snapshot for vm:" + err); } - String volumeName = StorpoolStorageAdaptor.getVolumeNameFromPath(volumeObjectTO.getPath(), true); - VolumeDetailVO detail = volumeDetailsDao.findDetail(volumeObjectTO.getId(), StorpoolUtil.SP_PROVIDER_NAME); + String volumeName = StorPoolStorageAdaptor.getVolumeNameFromPath(volumeObjectTO.getPath(), true); + VolumeDetailVO detail = volumeDetailsDao.findDetail(volumeObjectTO.getId(), StorPoolUtil.SP_PROVIDER_NAME); if (detail != null) { - SpApiResponse updateVolumeResponse = StorpoolUtil.volumeUpdateRename(volumeName, "", StorpoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); + SpApiResponse updateVolumeResponse = StorPoolUtil.volumeUpdateRename(volumeName, "", StorPoolStorageAdaptor.getVolumeNameFromPath(detail.getValue(), false), conn); if (updateVolumeResponse.getError() != null) { - StorpoolUtil.spLog("StorpoolVMSnapshotStrategy.canHandle - Could not update StorPool's volume %s to it's globalId due to %s", volumeName, updateVolumeResponse.getError().getDescr()); + StorPoolUtil.spLog("StorpoolVMSnapshotStrategy.canHandle - Could not update StorPool's volume %s to it's globalId due to %s", volumeName, updateVolumeResponse.getError().getDescr()); err = String.format("StorpoolVMSnapshotStrategy.canHandle - Could not update StorPool's volume %s to it's globalId due to %s", volumeName, updateVolumeResponse.getError().getDescr()); } else { volumeDetailsDao.remove(detail.getId()); } } - SpApiResponse resp = StorpoolUtil.detachAllForced(volumeName, false, conn); + SpApiResponse resp = StorPoolUtil.detachAllForced(volumeName, false, conn); if (resp.getError() != null) { err = String.format("Could not detach StorPool volume %s from a group snapshot, due to %s", volumeName, resp.getError()); throw new CloudRuntimeException(err); } - resp = StorpoolUtil.volumeRevert(volumeName, snapshotName, conn); + resp = StorPoolUtil.volumeRevert(volumeName, snapshotName, conn); if (resp.getError() != null) { err = String.format("Create Could not complete revert task for volumeName=%s , and snapshotName=%s", volumeName, snapshotName); @@ -335,10 +335,10 @@ public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { } VolumeInfo vinfo = volFactory.getVolume(volumeObjectTO.getId()); if (vinfo.getMaxIops() != null) { - resp = StorpoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); + resp = StorPoolUtil.volumeUpadateTags(volumeName, null, vinfo.getMaxIops(), conn, null); if (resp.getError() != null) { - StorpoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", + StorPoolUtil.spLog("Volume was reverted successfully but max iops could not be set due to %s", resp.getError().getDescr()); } } From 428e06c7459d8253018a7e3b33c0b352debd1779 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Tue, 8 Mar 2022 18:50:04 +0200 Subject: [PATCH 04/12] Address review --- .../storage/command/CopyCmdAnswer.java | 5 + .../api/storage/PrimaryDataStoreDriver.java | 20 +- .../api/storage/VMSnapshotStrategy.java | 2 +- .../driver/DateraPrimaryDataStoreDriver.java | 8 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 8 +- .../LinstorPrimaryDataStoreDriverImpl.java | 8 +- .../driver/NexentaPrimaryDataStoreDriver.java | 8 +- .../SamplePrimaryDataStoreDriverImpl.java | 8 +- .../driver/ScaleIOPrimaryDataStoreDriver.java | 8 +- .../SolidFirePrimaryDataStoreDriver.java | 8 +- .../api/storage/StorPoolCopyCommand.java | 18 +- .../StorPoolModifyStoragePoolCommand.java | 8 +- .../StorPoolBackupSnapshotCommandWrapper.java | 8 +- ...kupTemplateFromSnapshotCommandWrapper.java | 8 +- ...olCopyVolumeToSecondaryCommandWrapper.java | 5 +- ...torPoolDownloadTemplateCommandWrapper.java | 9 +- .../StorPoolDownloadVolumeCommandWrapper.java | 6 +- .../StorPoolModifyStorageCommandWrapper.java | 7 +- .../kvm/storage/StorPoolStorageAdaptor.java | 75 +++-- .../StorPoolAbandonObjectsCollector.java | 285 +++++++++--------- .../StorPoolPrimaryDataStoreDriver.java | 10 +- .../storage/datastore/util/StorPoolUtil.java | 1 - ...spring-storage-volume-storpool-context.xml | 6 +- .../cloud/storage/VolumeApiServiceImpl.java | 4 +- .../cloud/tags/TaggedResourceManagerImpl.java | 10 +- 25 files changed, 272 insertions(+), 271 deletions(-) diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCmdAnswer.java b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCmdAnswer.java index 9b581decd667..fef272bfbd91 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCmdAnswer.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCmdAnswer.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.command; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; import com.cloud.agent.api.to.DataTO; public class CopyCmdAnswer extends Answer { @@ -37,4 +38,8 @@ public DataTO getNewData() { public CopyCmdAnswer(String errMsg) { super(null, false, errMsg); } + + public CopyCmdAnswer(Command cmd, Exception e) { + super(cmd, e); + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index 6c767639bb80..2f814eafb010 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -105,26 +105,22 @@ enum QualityOfServiceState { MIGRATION, NO_MIGRATION } boolean canHostAccessStoragePool(Host host, StoragePool pool); /** - * intended for storage pools - * returns true if additional VM info is needed + * Returns true if additional VM info is needed (intended for storage pools). */ - boolean vmInfoNeeded(); + boolean isVmInfoNeeded(); /** - * intended for storage pools - * provide additional info for a VM + * Provides additional info for a VM (intended for storage pools). */ - void provideVMInfo(long vmId, long volumeId); + void provideVmInfo(long vmId, long volumeId); /** - * intended for storage pools - * returns true if the storage have to know about the VM's tags + * Returns true if the storage have to know about the VM's tags (intended for storage pools). */ - boolean vmTagsNeeded(String tagKey); + boolean isVmTagsNeeded(String tagKey); /** - * intended for storage pools - * provide added tags of a VM + * Provide VM's tags to storage (intended for storage pools). */ - void provideVMTags(long vmId, long volumeId, String tagValue); + void provideVmTags(long vmId, long volumeId, String tagValue); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java index 223229b5ee8f..6372aa3bd949 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java @@ -30,7 +30,7 @@ public interface VMSnapshotStrategy { StrategyPriority canHandle(VMSnapshot vmSnapshot); /** - * Used only for KVM hypervisors when allocating a VM snapshot + * Verifies if the strategy can handle the VM snapshot. This method is used only for KVM hypervisors when allocating a VM snapshot. * @param vmId the ID of the virtual machine * @param snapshotMemory for VM snapshots with memory * @return StrategyPriority diff --git a/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java b/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java index 62d1e6e4c404..0002dbe1c179 100644 --- a/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/datera/src/main/java/org/apache/cloudstack/storage/datastore/driver/DateraPrimaryDataStoreDriver.java @@ -1862,20 +1862,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } 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 f53aca0c34bc..7c8b5fb22c8d 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 @@ -493,20 +493,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 5ab551a4be77..2a486a11039b 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -767,20 +767,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } diff --git a/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java b/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java index 8e134c6c3d61..84051888c5e6 100644 --- a/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/nexenta/src/main/java/org/apache/cloudstack/storage/datastore/driver/NexentaPrimaryDataStoreDriver.java @@ -241,20 +241,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } diff --git a/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index f4e3937b668c..732786047c43 100644 --- a/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/main/java/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -267,20 +267,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } 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 8ffbabc9d3b2..3bb6e0d7e63a 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 @@ -949,20 +949,20 @@ private void alertHostSdcDisconnection(Host host) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } diff --git a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 1bd1b6f3c44c..702bdc3669a0 100644 --- a/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/main/java/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -1621,20 +1621,20 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return false; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return false; } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java index 0610bb27e641..09fb1ca1dd22 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolCopyCommand.java @@ -24,24 +24,24 @@ import com.cloud.agent.api.to.DataTO; public class StorPoolCopyCommand extends StorageSubSystemCommand { - private S srcTO; - private D dstTO; + private S sourceTO; + private D destinationTO; private boolean executeInSequence = false; - public StorPoolCopyCommand(final DataTO srcTO, final DataTO dstTO, final int timeout, final boolean executeInSequence) { + public StorPoolCopyCommand(final DataTO sourceTO, final DataTO destinationTO, final int timeout, final boolean executeInSequence) { super(); - this.srcTO = (S)srcTO; - this.dstTO = (D)dstTO; + this.sourceTO = (S)sourceTO; + this.destinationTO = (D)destinationTO; setWait(timeout); this.executeInSequence = executeInSequence; } - public S getSrcTO() { - return srcTO; + public S getSourceTO() { + return sourceTO; } - public D getDstTO() { - return dstTO; + public D getDestinationTO() { + return destinationTO; } public int getWaitInMillSeconds() { diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java index abc89266e8c5..a72a4620de17 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/agent/api/storage/StorPoolModifyStoragePoolCommand.java @@ -23,14 +23,14 @@ import com.cloud.storage.StoragePool; public class StorPoolModifyStoragePoolCommand extends ModifyStoragePoolCommand { - private String volName; + private String volumeName; public StorPoolModifyStoragePoolCommand(boolean add, StoragePool pool, String volumeName) { super(add, pool); - this.volName = volumeName; + this.volumeName = volumeName; } - public String getVolume() { - return volName; + public String getVolumeName() { + return volumeName; } } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java index 20793df096be..f83a4292e208 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupSnapshotCommandWrapper.java @@ -52,8 +52,8 @@ public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final Libv KVMStoragePool secondaryPool = null; try { - final SnapshotObjectTO src = cmd.getSrcTO(); - final SnapshotObjectTO dst = cmd.getDstTO(); + final SnapshotObjectTO src = cmd.getSourceTO(); + final SnapshotObjectTO dst = cmd.getDestinationTO(); final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); SP_LOG("StorpoolBackupSnapshotCommandWrapper.execute: src=" + src.getPath() + "dst=" + dst.getPath()); @@ -88,10 +88,10 @@ public CopyCmdAnswer execute(final StorPoolBackupSnapshotCommand cmd, final Libv return new CopyCmdAnswer(snapshot); } catch (final Exception e) { - final String error = "failed to backup snapshot: " + e.getMessage(); + final String error = String.format("Failed to backup snapshot with id [%s] with a pool %s, due to %s", cmd.getSourceTO().getId(), cmd.getSourceTO().getDataStore().getUuid(), e.getMessage()); SP_LOG(error); s_logger.debug(error); - return new CopyCmdAnswer(error); + return new CopyCmdAnswer(cmd, e); } finally { if (srcPath != null) { StorPoolStorageAdaptor.attachOrDetachVolume("detach", "snapshot", srcPath); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java index c0730f5d536e..518cbb8d5e5f 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolBackupTemplateFromSnapshotCommandWrapper.java @@ -64,11 +64,11 @@ public class StorPoolBackupTemplateFromSnapshotCommandWrapper extends CommandWra public CopyCmdAnswer execute(final StorPoolBackupTemplateFromSnapshotCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String srcPath = null; KVMStoragePool secondaryPool = null; - String objectType = cmd.getSrcTO().getObjectType().toString().toLowerCase(); + String objectType = cmd.getSourceTO().getObjectType().toString().toLowerCase(); try { - final DataTO src = cmd.getSrcTO(); - final TemplateObjectTO dst = cmd.getDstTO(); + final DataTO src = cmd.getSourceTO(); + final TemplateObjectTO dst = cmd.getDestinationTO(); String name = null; String volumeFormatExtension = null; @@ -143,7 +143,7 @@ public CopyCmdAnswer execute(final StorPoolBackupTemplateFromSnapshotCommand cmd final String error = "failed to backup snapshot: " + e.getMessage(); SP_LOG(error); s_logger.debug(error); - return new CopyCmdAnswer(error); + return new CopyCmdAnswer(cmd, e); } finally { if (srcPath != null) { StorPoolStorageAdaptor.attachOrDetachVolume("detach", objectType, srcPath); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java index c28a8462cc73..29e8979bd88f 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolCopyVolumeToSecondaryCommandWrapper.java @@ -29,7 +29,6 @@ import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgFile; -//import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import com.cloud.agent.api.storage.StorPoolCopyVolumeToSecondaryCommand; @@ -54,8 +53,8 @@ public CopyCmdAnswer execute(final StorPoolCopyVolumeToSecondaryCommand cmd, fin KVMStoragePool secondaryPool = null; try { - final VolumeObjectTO src = cmd.getSrcTO(); - final VolumeObjectTO dst = cmd.getDstTO(); + final VolumeObjectTO src = cmd.getSourceTO(); + final VolumeObjectTO dst = cmd.getDestinationTO(); final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); final String destVolumePath = dst.getPath(); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java index b13b4936f582..07b08a1da2b6 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadTemplateCommandWrapper.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import com.cloud.agent.api.storage.StorPoolDownloadTemplateCommand; @@ -51,8 +52,8 @@ public final class StorPoolDownloadTemplateCommandWrapper extends CommandWrapper public CopyCmdAnswer execute(final StorPoolDownloadTemplateCommand cmd, final LibvirtComputingResource libvirtComputingResource) { String dstPath = null; KVMStoragePool secondaryPool = null; - DataTO src = cmd.getSrcTO(); - DataTO dst = cmd.getDstTO(); + DataTO src = cmd.getSourceTO(); + DataTO dst = cmd.getDestinationTO(); try { final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); @@ -79,7 +80,7 @@ public CopyCmdAnswer execute(final StorPoolDownloadTemplateCommand cmd, final Li if (tmpltname == null) { secondaryPool.refresh(); final List disks = secondaryPool.listPhysicalDisks(); - if (disks == null || disks.isEmpty()) { + if (CollectionUtils.isEmpty(disks)) { SP_LOG("Failed to get volumes from pool: " + secondaryPool.getUuid()); return new CopyCmdAnswer("Failed to get volumes from pool: " + secondaryPool.getUuid()); } @@ -115,7 +116,7 @@ public CopyCmdAnswer execute(final StorPoolDownloadTemplateCommand cmd, final Li } catch (final Exception e) { final String error = "Failed to copy template to primary: " + e.getMessage(); s_logger.debug(error); - return new CopyCmdAnswer(error); + return new CopyCmdAnswer(cmd, e); } finally { if (dstPath != null) { StorPoolStorageAdaptor.attachOrDetachVolume("detach", cmd.getObjectType(), dstPath); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java index a8ea65c6431b..d1a58a4aeb80 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolDownloadVolumeCommandWrapper.java @@ -56,8 +56,8 @@ public CopyCmdAnswer execute(final StorPoolDownloadVolumeCommand cmd, final Libv KVMStoragePool secondaryPool = null; try { - final VolumeObjectTO src = cmd.getSrcTO(); - final VolumeObjectTO dst = cmd.getDstTO(); + final VolumeObjectTO src = cmd.getSourceTO(); + final VolumeObjectTO dst = cmd.getDestinationTO(); final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); SP_LOG("StorpoolDownloadVolumeCommandWrapper.execute: src=" + src.getPath() + " srcName=" + src.getName() + " dst=" + dst.getPath()); @@ -144,7 +144,7 @@ public CopyCmdAnswer execute(final StorPoolDownloadVolumeCommand cmd, final Libv final String error = "Failed to copy volume to primary: " + e.getMessage(); SP_LOG(error); s_logger.debug(error); - return new CopyCmdAnswer(error); + return new CopyCmdAnswer(cmd, e); } finally { if (dstPath != null) { StorPoolStorageAdaptor.attachOrDetachVolume("detach", "volume", dstPath); diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java index 2b56e1b980b3..9c58e3bc42cb 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java @@ -49,10 +49,11 @@ public final class StorPoolModifyStorageCommandWrapper extends CommandWrapper spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); - if (spPools != null && spPools.size() > 0) { - Map volumes = new HashMap<>(); - for (StoragePoolVO storagePoolVO : spPools) { + if (CollectionUtils.isEmpty(spPools)) { + return; + } + Map volumes = new HashMap<>(); + for (StoragePoolVO storagePoolVO : spPools) { + try { + JsonArray arr = StorPoolUtil.volumesList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + volumes.putAll(getStorPoolNamesAndCsTag(arr)); + } catch (Exception e) { + log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage()), e); + } + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + try { - JsonArray arr = StorPoolUtil.volumesList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); - volumes.putAll(getStorPoolNamesAndCsTag(arr)); - } catch (Exception e) { - log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); + PreparedStatement pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`volumes1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`volumes_on_host1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error during delete vm work job: %s", + e.getLocalizedMessage())); } - } - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + try { + PreparedStatement pstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes1` (name, tag) VALUES (?, ?)"); + PreparedStatement volumesOnHostpstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes_on_host1` (name, tag) VALUES (?, ?)"); + for (Map.Entry volume : volumes.entrySet()) { + if (volume.getValue().equals("volume")) { + addRecordToDb(volume.getKey(), pstmt, volume.getValue(), true); + } else if (volume.getValue().equals("check-volume-is-on-host")) { + addRecordToDb(volume.getKey(), volumesOnHostpstmt, volume.getValue(), true); + } + } + pstmt.executeBatch(); + volumesOnHostpstmt.executeBatch(); + String sql = "SELECT f.* FROM `cloud`.`volumes1` f LEFT JOIN `cloud`.`volumes` v ON f.name=v.path where v.path is NULL OR NOT state=?"; + findMissingRecordsInCS(txn, sql, "volume"); + + String sqlVolumeOnHost = "SELECT f.* FROM `cloud`.`volumes_on_host1` f LEFT JOIN `cloud`.`storage_pool_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlVolumeOnHost, "volumes_on_host"); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed due to: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error: %s", + e.getLocalizedMessage())); + } finally { try { - PreparedStatement pstmt = txn.prepareAutoCloseStatement( - "CREATE TEMPORARY TABLE `cloud`.`volumes1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes1`"); pstmt.executeUpdate(); - - pstmt = txn.prepareAutoCloseStatement( - "CREATE TEMPORARY TABLE `cloud`.`volumes_on_host1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes_on_host1`"); pstmt.executeUpdate(); - } catch (SQLException e) { - log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", - e.getLocalizedMessage())); - } catch (Throwable e) { - log.info(String.format("[ignored] caught an error during delete vm work job: %s", - e.getLocalizedMessage())); - } - - try { - PreparedStatement pstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes1` (name, tag) VALUES (?, ?)"); - PreparedStatement volumesOnHostpstmt = txn.prepareStatement("INSERT INTO `cloud`.`volumes_on_host1` (name, tag) VALUES (?, ?)"); - for (Map.Entry volume : volumes.entrySet()) { - if (volume.getValue().equals("volume")) { - addRecordToDb(volume.getKey(), pstmt, volume.getValue(), true); - } else if (volume.getValue().equals("check-volume-is-on-host")) { - addRecordToDb(volume.getKey(), volumesOnHostpstmt, volume.getValue(), true); - } - } - pstmt.executeBatch(); - volumesOnHostpstmt.executeBatch(); - String sql = "SELECT f.* FROM `cloud`.`volumes1` f LEFT JOIN `cloud`.`volumes` v ON f.name=v.path where v.path is NULL OR NOT state=?"; - findMissingRecordsInCS(txn, sql, "volume"); - - String sqlVolumeOnHost = "SELECT f.* FROM `cloud`.`volumes_on_host1` f LEFT JOIN `cloud`.`storage_pool_details` v ON f.name=v.value where v.value is NULL"; - findMissingRecordsInCS(txn, sqlVolumeOnHost, "volumes_on_host"); - } catch (SQLException e) { - log.info(String.format("[ignored] SQL failed due to: %s ", - e.getLocalizedMessage())); - } catch (Throwable e) { - log.info(String.format("[ignored] caught an error: %s", - e.getLocalizedMessage())); - } finally { - try { - PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes1`"); - pstmt.executeUpdate(); - pstmt = txn.prepareStatement("DROP TABLE `cloud`.`volumes_on_host1`"); - pstmt.executeUpdate(); - } catch (SQLException e) { - txn.close(); - log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); - } txn.close(); + log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); } + txn.close(); } - }); - } + } + }); } } @@ -187,95 +189,96 @@ class StorPoolSnapshotsTagsUpdate extends ManagedContextRunnable { protected void runInContext() { List spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME); Map snapshots = new HashMap(); - if (spPools != null && spPools.size() > 0) { - for (StoragePoolVO storagePoolVO : spPools) { - try { - JsonArray arr = StorPoolUtil.snapshotsList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); - snapshots.putAll(getStorPoolNamesAndCsTag(arr)); - } catch (Exception e) { - log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); - } + if (CollectionUtils.isEmpty(spPools)) { + return; + } + for (StoragePoolVO storagePoolVO : spPools) { + try { + JsonArray arr = StorPoolUtil.snapshotsList(StorPoolUtil.getSpConnection(storagePoolVO.getUuid(), storagePoolVO.getId(), storagePoolDetailsDao, storagePoolDao)); + snapshots.putAll(getStorPoolNamesAndCsTag(arr)); + } catch (Exception e) { + log.debug(String.format("Could not collect abandon objects due to %s", e.getMessage())); } - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); - - try{ - PreparedStatement pstmt = txn.prepareAutoCloseStatement( - "CREATE TEMPORARY TABLE `cloud`.`snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); - pstmt.executeUpdate(); - - pstmt = txn.prepareAutoCloseStatement( - "CREATE TEMPORARY TABLE `cloud`.`vm_snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); - pstmt.executeUpdate(); + } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + + try{ + PreparedStatement pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`vm_snapshots1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + + pstmt = txn.prepareAutoCloseStatement( + "CREATE TEMPORARY TABLE `cloud`.`vm_templates1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); + pstmt.executeUpdate(); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error during delete vm work job: %s", + e.getLocalizedMessage())); + } - pstmt = txn.prepareAutoCloseStatement( - "CREATE TEMPORARY TABLE `cloud`.`vm_templates1`(`id` bigint unsigned NOT NULL auto_increment, `name` varchar(255) NOT NULL,`tag` varchar(255) NOT NULL, PRIMARY KEY (`id`))"); - pstmt.executeUpdate(); - } catch (SQLException e) { - log.info(String.format("[ignored] SQL failed to delete vm work job: %s ", - e.getLocalizedMessage())); - } catch (Throwable e) { - log.info(String.format("[ignored] caught an error during delete vm work job: %s", - e.getLocalizedMessage())); + try { + PreparedStatement snapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`snapshots1` (name, tag) VALUES (?, ?)"); + PreparedStatement groupSnapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_snapshots1` (name, tag) VALUES (?, ?)"); + PreparedStatement templatePstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_templates1` (name, tag) VALUES (?, ?)"); + for (Map.Entry snapshot : snapshots.entrySet()) { + if (!snapshot.getValue().equals("group") && !snapshot.getValue().equals("template")) { + addRecordToDb(snapshot.getKey(), snapshotsPstmt, snapshot.getValue(), true); + } else if (snapshot.getValue().equals("group")) { + addRecordToDb(snapshot.getKey(), groupSnapshotsPstmt, snapshot.getValue(), true); + } else if (snapshot.getValue().equals("template")) { + addRecordToDb(snapshot.getKey(), templatePstmt, snapshot.getValue(), true); + } } - + snapshotsPstmt.executeBatch(); + groupSnapshotsPstmt.executeBatch(); + templatePstmt.executeBatch(); + + String sqlSnapshots = "SELECT f.* FROM `cloud`.`snapshots1` f LEFT JOIN `cloud`.`snapshot_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlSnapshots, "snapshot"); + + String sqlVmSnapshots = "SELECT f.* FROM `cloud`.`vm_snapshots1` f LEFT JOIN `cloud`.`vm_snapshot_details` v ON f.name=v.value where v.value is NULL"; + findMissingRecordsInCS(txn, sqlVmSnapshots, "snapshot"); + + String sqlTemplates = "SELECT temp.*" + + " FROM `cloud`.`vm_templates1` temp" + + " LEFT JOIN `cloud`.`template_store_ref` store" + + " ON temp.name=store.local_path" + + " LEFT JOIN `cloud`.`template_spool_ref` spool" + + " ON temp.name=spool.local_path" + + " where store.local_path is NULL" + + " and spool.local_path is NULL"; + findMissingRecordsInCS(txn, sqlTemplates, "snapshot"); + } catch (SQLException e) { + log.info(String.format("[ignored] SQL failed due to: %s ", + e.getLocalizedMessage())); + } catch (Throwable e) { + log.info(String.format("[ignored] caught an error: %s", + e.getLocalizedMessage())); + } finally { try { - PreparedStatement snapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`snapshots1` (name, tag) VALUES (?, ?)"); - PreparedStatement groupSnapshotsPstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_snapshots1` (name, tag) VALUES (?, ?)"); - PreparedStatement templatePstmt = txn.prepareStatement("INSERT INTO `cloud`.`vm_templates1` (name, tag) VALUES (?, ?)"); - for (Map.Entry snapshot : snapshots.entrySet()) { - if (!snapshot.getValue().equals("group") && !snapshot.getValue().equals("template")) { - addRecordToDb(snapshot.getKey(), snapshotsPstmt, snapshot.getValue(), true); - } else if (snapshot.getValue().equals("group")) { - addRecordToDb(snapshot.getKey(), groupSnapshotsPstmt, snapshot.getValue(), true); - } else if (snapshot.getValue().equals("template")) { - addRecordToDb(snapshot.getKey(), templatePstmt, snapshot.getValue(), true); - } - } - snapshotsPstmt.executeBatch(); - groupSnapshotsPstmt.executeBatch(); - templatePstmt.executeBatch(); - - String sqlSnapshots = "SELECT f.* FROM `cloud`.`snapshots1` f LEFT JOIN `cloud`.`snapshot_details` v ON f.name=v.value where v.value is NULL"; - findMissingRecordsInCS(txn, sqlSnapshots, "snapshot"); - - String sqlVmSnapshots = "SELECT f.* FROM `cloud`.`vm_snapshots1` f LEFT JOIN `cloud`.`vm_snapshot_details` v ON f.name=v.value where v.value is NULL"; - findMissingRecordsInCS(txn, sqlVmSnapshots, "snapshot"); - - String sqlTemplates = "SELECT temp.*" - + " FROM `cloud`.`vm_templates1` temp" - + " LEFT JOIN `cloud`.`template_store_ref` store" - + " ON temp.name=store.local_path" - + " LEFT JOIN `cloud`.`template_spool_ref` spool" - + " ON temp.name=spool.local_path" - + " where store.local_path is NULL" - + " and spool.local_path is NULL"; - findMissingRecordsInCS(txn, sqlTemplates, "snapshot"); + PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`snapshots1`"); + pstmt.executeUpdate(); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_snapshots1`"); + pstmt.executeUpdate(); + pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_templates1`"); + pstmt.executeUpdate(); } catch (SQLException e) { - log.info(String.format("[ignored] SQL failed due to: %s ", - e.getLocalizedMessage())); - } catch (Throwable e) { - log.info(String.format("[ignored] caught an error: %s", - e.getLocalizedMessage())); - } finally { - try { - PreparedStatement pstmt = txn.prepareStatement("DROP TABLE `cloud`.`snapshots1`"); - pstmt.executeUpdate(); - pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_snapshots1`"); - pstmt.executeUpdate(); - pstmt = txn.prepareStatement("DROP TABLE `cloud`.`vm_templates1`"); - pstmt.executeUpdate(); - } catch (SQLException e) { - txn.close(); - log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); - } txn.close(); + log.info(String.format("createTemporaryVolumeTable %s", e.getMessage())); } + txn.close(); } - }); - } + } + }); } } diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java index 2e8d84846b2f..97b15d847f32 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolPrimaryDataStoreDriver.java @@ -162,13 +162,11 @@ public long getUsedIops(StoragePool storagePool) { @Override public boolean grantAccess(DataObject data, Host host, DataStore dataStore) { - log.debug("grantAccess"); return false; } @Override public void revokeAccess(DataObject data, Host host, DataStore dataStore) { - log.debug("revokeAccess"); } private void updateStoragePool(final long poolId, final long deltaUsedBytes) { @@ -930,12 +928,12 @@ public boolean canHostAccessStoragePool(Host host, StoragePool pool) { } @Override - public boolean vmInfoNeeded() { + public boolean isVmInfoNeeded() { return true; } @Override - public void provideVMInfo(long vmId, long volumeId) { + public void provideVmInfo(long vmId, long volumeId) { VolumeVO volume = volumeDao.findById(volumeId); StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); if (poolVO != null) { @@ -954,12 +952,12 @@ public void provideVMInfo(long vmId, long volumeId) { } @Override - public boolean vmTagsNeeded(String tagKey) { + public boolean isVmTagsNeeded(String tagKey) { return tagKey != null && tagKey.equals(StorPoolUtil.SP_VC_POLICY); } @Override - public void provideVMTags(long vmId, long volumeId, String tagValue) { + public void provideVmTags(long vmId, long volumeId, String tagValue) { VolumeVO volume = volumeDao.findById(volumeId); StoragePoolVO poolVO = primaryStoreDao.findById(volume.getPoolId()); if (poolVO != null) { diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java index 6e1530383751..484a9b989311 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolUtil.java @@ -52,7 +52,6 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; -//import org.apache.http.impl.conn.BasicClientConnectionManager; import org.apache.log4j.Logger; import com.cloud.hypervisor.kvm.storage.StorPoolStorageAdaptor; diff --git a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml index ba47dfc3aeb2..cf1db3a8bf22 100644 --- a/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml +++ b/plugins/storage/volume/storpool/src/main/resources/META-INF/cloudstack/storage-volume-storpool/spring-storage-volume-storpool-context.xml @@ -19,13 +19,13 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + class="org.apache.cloudstack.storage.datastore.provider.StorPoolPrimaryDataStoreProvider" /> + class="org.apache.cloudstack.storage.snapshot.StorPoolSnapshotStrategy" /> + class="org.apache.cloudstack.storage.snapshot.StorPoolVMSnapshotStrategy" /> diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 788a3d465b2c..133f295863dd 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -3901,8 +3901,8 @@ private void provideVMInfo(DataStore dataStore, long vmId, Long volumeId) { if (dataStoreDriver instanceof PrimaryDataStoreDriver) { PrimaryDataStoreDriver storageDriver = (PrimaryDataStoreDriver)dataStoreDriver; - if (storageDriver.vmInfoNeeded()) { - storageDriver.provideVMInfo(vmId, volumeId); + if (storageDriver.isVmInfoNeeded()) { + storageDriver.provideVmInfo(vmId, volumeId); } } } diff --git a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java index 4cd3a39af1cd..23e757dfe234 100644 --- a/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/tags/TaggedResourceManagerImpl.java @@ -313,14 +313,12 @@ private void informStoragePoolForVmTags(long vmId, String key, String value) { List volumeVos = volumeDao.findByInstance(vmId); for (VolumeVO volume : volumeVos) { DataStore dataStore = dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary); - PrimaryDataStoreDriver dataStoreDriver = dataStore != null - && (dataStore.getDriver() instanceof PrimaryDataStoreDriver) - ? (PrimaryDataStoreDriver) dataStore.getDriver() : null; - if (dataStoreDriver == null) { + if (dataStore == null || !(dataStore.getDriver() instanceof PrimaryDataStoreDriver)) { continue; } - if (dataStoreDriver.vmTagsNeeded(key)) { - dataStoreDriver.provideVMTags(vmId, volume.getId(), value); + PrimaryDataStoreDriver dataStoreDriver = (PrimaryDataStoreDriver) dataStore.getDriver(); + if (dataStoreDriver.isVmTagsNeeded(key)) { + dataStoreDriver.provideVmTags(vmId, volume.getId(), value); } } } From 2dc8a315c7d19f21cbe0ab6d6e1fdb8e1b240d81 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Thu, 10 Mar 2022 21:26:47 +0200 Subject: [PATCH 05/12] removed unnecessary else --- .../StorPoolModifyStorageCommandWrapper.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java index 9c58e3bc42cb..b35797083359 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/StorPoolModifyStorageCommandWrapper.java @@ -53,24 +53,23 @@ public Answer execute(final StorPoolModifyStoragePoolCommand command, final Libv return new Answer(command, false, "spNotFound"); } try { - String volume = attachOrDetachVolume("attach", "volume", command.getVolumeName()); - if (volume != null) { - return new Answer(command, false, volume); - } else { - final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); - final KVMStoragePool storagepool = - storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() - .getUserInfo(), command.getPool().getType()); - if (storagepool == null) { - log.debug(String.format("Did not find a storage pool [%s]", command.getPool().getId())); - return new Answer(command, false, " Failed to create storage pool"); - } + String result = attachOrDetachVolume("attach", "volume", command.getVolumeName()); + if (result != null) { + return new Answer(command, false, result); + } + final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); + final KVMStoragePool storagepool = + storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(), command.getPool() + .getUserInfo(), command.getPool().getType()); + if (storagepool == null) { + log.debug(String.format("Did not find a storage pool [%s]", command.getPool().getId())); + return new Answer(command, false, String.format("Failed to create storage pool [%s]", command.getPool().getId())); + } - final Map tInfo = new HashMap(); - final StorPoolModifyStoragePoolAnswer answer = new StorPoolModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, clusterId); + final Map tInfo = new HashMap(); + final StorPoolModifyStoragePoolAnswer answer = new StorPoolModifyStoragePoolAnswer(command, storagepool.getCapacity(), storagepool.getAvailable(), tInfo, clusterId); - return answer; - } + return answer; } catch (Exception e) { log.debug(String.format("Could not modify storage due to %s", e.getMessage())); return new Answer(command, e); From 4374f4d2bf1dcd925ca495c9ce63d3e538c7945b Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Thu, 10 Mar 2022 21:27:27 +0200 Subject: [PATCH 06/12] Removed check about the storage provider We don't need this check, we'll get if the snapshot is on StorPool be its name from path --- .../storage/snapshot/StorPoolSnapshotStrategy.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java index 79b9d8b11e30..f66617d8f315 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java @@ -132,15 +132,11 @@ public StrategyPriority canHandle(Snapshot snapshot, SnapshotOperation op) { return StrategyPriority.CANT_HANDLE; } - VolumeVO volume = _volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId()); - StoragePoolVO storage = _primaryDataStoreDao.findById(volume.getPoolId()); String name = StorPoolHelper.getSnapshotName(snapshot.getId(), snapshot.getUuid(), _snapshotStoreDao, _snapshotDetailsDao); - if (storage.getStorageProviderName().equals(StorPoolUtil.SP_PROVIDER_NAME)) { - if (name != null) { - StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); + if (name != null) { + StorPoolUtil.spLog("StorpoolSnapshotStrategy.canHandle: globalId=%s", name); - return StrategyPriority.HIGHEST; - } + return StrategyPriority.HIGHEST; } SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), snapshot.getUuid()); if (snapshotDetails != null) { From e19d6282c31d31cf061302c0966bbcfb147a1bb9 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Thu, 24 Mar 2022 17:09:06 +0200 Subject: [PATCH 07/12] Check that current plugin supports all functionality before upgrade CS --- .../api/storage/PrimaryDataStoreDriver.java | 11 ++++- plugins/storage/volume/storpool/pom.xml | 5 +++ .../provider/StorPoolHostListener.java | 12 ++++++ .../util/StorPoolFeaturesAndFixes.java | 40 +++++++++++++++++++ .../datastore/util/StorPoolHelper.java | 38 +++++++++++++++++- 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolFeaturesAndFixes.java diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index 2f814eafb010..540d4f6673aa 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -105,22 +105,31 @@ enum QualityOfServiceState { MIGRATION, NO_MIGRATION } boolean canHostAccessStoragePool(Host host, StoragePool pool); /** - * Returns true if additional VM info is needed (intended for storage pools). + * Used by storage pools which want to keep VMs' information + * @return true if additional VM info is needed (intended for storage pools). */ boolean isVmInfoNeeded(); /** * Provides additional info for a VM (intended for storage pools). + * E.g. the storage pool may want to keep/delete information if the volume is attached/detached to any VM. + * @param vmId The ID of the virtual machine + * @param volumeId the ID of the volume */ void provideVmInfo(long vmId, long volumeId); /** * Returns true if the storage have to know about the VM's tags (intended for storage pools). + * @param tagKey The name of the tag + * @return true if the storage have to know about the VM's tags */ boolean isVmTagsNeeded(String tagKey); /** * Provide VM's tags to storage (intended for storage pools). + * @param vmId The ID of the virtual machine + * @param volumeId The ID of the volume + * @param tagValue The value of the VM's tag */ void provideVmTags(long vmId, long volumeId, String tagValue); } diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml index 856a11b01dea..af8d521133c2 100644 --- a/plugins/storage/volume/storpool/pom.xml +++ b/plugins/storage/volume/storpool/pom.xml @@ -41,6 +41,11 @@ cloud-engine-orchestration ${project.version}
+ + org.apache.commons + commons-collections4 + 4.4 + diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java index 11fb75ff4bff..df188ff69c2c 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java @@ -32,6 +32,7 @@ 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.cloudstack.storage.datastore.util.StorPoolFeaturesAndFixes; import org.apache.cloudstack.storage.datastore.util.StorPoolHelper; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; @@ -102,6 +103,17 @@ public boolean hostConnect(long hostId, long poolId) throws StorageConflictExcep addModifyCommandToCommandsAllowedInMaintenanceMode(); } + List driverSupportedFeatures = StorPoolFeaturesAndFixes.getAllClassConstants(); + List driverFeaturesBeforeUpgrade = StorPoolHelper.listFeaturesUpdates(storagePoolDetailsDao, poolId); + boolean isCurrentVersionSupportsEverythingFromPrevious = StorPoolHelper.isPoolSupportsAllFunctionalityFromPreviousVersion(storagePoolDetailsDao, driverSupportedFeatures, driverFeaturesBeforeUpgrade, poolId); + if (!isCurrentVersionSupportsEverythingFromPrevious) { + String msg = "The current StorPool driver does not support all functionality from the one before upgrade to CS"; + StorPoolUtil.spLog("Storage pool [%s] is not connected to host [%s] because the functionality after the upgrade is not full", + poolId, hostId); + alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, pool.getDataCenterId(), pool.getPodId(), msg, msg); + return false; + } + StorPoolModifyStoragePoolCommand cmd = new StorPoolModifyStoragePoolCommand(true, pool, volumeOnPool.getValue()); final Answer answer = agentMgr.easySend(hostId, cmd); diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolFeaturesAndFixes.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolFeaturesAndFixes.java new file mode 100644 index 000000000000..f87f36c6b481 --- /dev/null +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolFeaturesAndFixes.java @@ -0,0 +1,40 @@ +/* + * 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 + * with 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.storage.datastore.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class StorPoolFeaturesAndFixes { + + public static List getAllClassConstants() { + List constants = new ArrayList<>(); + + for (Field field : StorPoolFeaturesAndFixes.class.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + constants.add(field.getName()); + } + } + return constants; + } +} diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java index aca95fb48d0c..9395f134fe10 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/util/StorPoolHelper.java @@ -23,20 +23,24 @@ import java.sql.PreparedStatement; import java.sql.Timestamp; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.impl.ConfigurationVO; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.util.StorPoolUtil.SpApiResponse; import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager; import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.log4j.Appender; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; @@ -60,6 +64,8 @@ import com.cloud.tags.dao.ResourceTagDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; @@ -259,4 +265,34 @@ public static void updateVmStoreTemplate(Long id, DataStoreRole role, String pat templ.setLocalDownloadPath(path); templStoreDao.persist(templ); } + + public static List listFeaturesUpdates(StoragePoolDetailsDao storagePoolDetails, long poolId) { + SearchBuilder sb = storagePoolDetails.createSearchBuilder(); + sb.and("pool_id", sb.entity().getResourceId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + SearchCriteria sc = sb.create(); + sc.setParameters("pool_id", poolId); + sc.setParameters("name", "SP-FEATURE" + "%"); + return storagePoolDetails.search(sc, null); + } + + public static boolean isPoolSupportsAllFunctionalityFromPreviousVersion(StoragePoolDetailsDao storagePoolDetails, List currentPluginFeatures, List poolFeaturesBeforeUpgrade, long poolId) { + if (CollectionUtils.isEmpty(currentPluginFeatures) && CollectionUtils.isEmpty(poolFeaturesBeforeUpgrade)) { + return true; + } + + List poolDetails = poolFeaturesBeforeUpgrade.stream().map(StoragePoolDetailVO::getName).collect(Collectors.toList()); + List detailsNotContainedInCurrent = new ArrayList<>(CollectionUtils.removeAll(poolDetails, currentPluginFeatures)); + List detailsNotContainedInDataBase = new ArrayList<>(CollectionUtils.removeAll(currentPluginFeatures, poolDetails)); + if (!CollectionUtils.isEmpty(detailsNotContainedInCurrent)) { + return false; + } else if (!CollectionUtils.isEmpty(detailsNotContainedInDataBase)) { + for (String features : detailsNotContainedInDataBase) { + StoragePoolDetailVO storageNewFeatures = new StoragePoolDetailVO(poolId, features, features, false); + storagePoolDetails.persist(storageNewFeatures); + } + return true; + } + return true; + } } From 6354b564010afd6131bcacbc5e2d8ad9e01497d2 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Mon, 11 Apr 2022 18:41:32 +0300 Subject: [PATCH 08/12] Smoke tests for StorPool plug-in --- .../storpool/MigrateVolumeToStorPool.py | 439 ++++ .../plugins/storpool/TestStorPoolVolumes.py | 2153 +++++++++++++++++ .../plugins/storpool/TestTagsOnStorPool.py | 576 +++++ .../plugins/storpool/TestVmSnapshots.py | 369 +++ test/integration/plugins/storpool/sp_util.py | 732 ++++++ 5 files changed, 4269 insertions(+) create mode 100644 test/integration/plugins/storpool/MigrateVolumeToStorPool.py create mode 100644 test/integration/plugins/storpool/TestStorPoolVolumes.py create mode 100644 test/integration/plugins/storpool/TestTagsOnStorPool.py create mode 100644 test/integration/plugins/storpool/TestVmSnapshots.py create mode 100644 test/integration/plugins/storpool/sp_util.py diff --git a/test/integration/plugins/storpool/MigrateVolumeToStorPool.py b/test/integration/plugins/storpool/MigrateVolumeToStorPool.py new file mode 100644 index 000000000000..e6b9fbbbe2d8 --- /dev/null +++ b/test/integration/plugins/storpool/MigrateVolumeToStorPool.py @@ -0,0 +1,439 @@ +# 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 +# with 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. + +# Import Local Modules +import pprint +import random +import subprocess +import time +import uuid + +from marvin.cloudstackAPI import (listOsTypes, + listTemplates, + listHosts, + createTemplate, + createVolume, + resizeVolume, + startVirtualMachine, + migrateVirtualMachine, + migrateVolume + ) +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING +from marvin.configGenerator import configuration, cluster +from marvin.lib.base import (Account, + Configurations, + ServiceOffering, + Snapshot, + StoragePool, + Template, + Tag, + VirtualMachine, + VmSnapshot, + Volume, + SecurityGroup, + ) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_disk_offering, + list_snapshots, + list_storage_pools, + list_volumes, + list_virtual_machines, + list_configurations, + list_service_offering, + list_clusters, + list_zones) +from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual +from nose.plugins.attrib import attr + +from storpool import spapi +from sp_util import (TestData, StorPoolHelper) + + +class TestMigrateVolumeToAnotherPool(cloudstackTestCase): + @classmethod + def setUpClass(cls): + super(TestMigrateVolumeToAnotherPool, cls).setUpClass() + try: + cls.setUpCloudStack() + except Exception: + cls.cleanUpCloudStack() + raise + + @classmethod + def setUpCloudStack(cls): + cls.spapi = spapi.Api(host="10.2.23.248", port="81", auth="6549874687", multiCluster=True) + testClient = super(TestMigrateVolumeToAnotherPool, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + + cls._cleanup = [] + + cls.unsupportedHypervisor = False + cls.hypervisor = testClient.getHypervisorInfo() + if cls.hypervisor.lower() in ("hyperv", "lxc"): + cls.unsupportedHypervisor = True + return + + cls.services = testClient.getParsedTestDataConfig() + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = None + zones = list_zones(cls.apiclient) + + for z in zones: + if z.name == cls.getClsConfig().mgtSvr[0].zone: + cls.zone = z + + assert cls.zone is not None + + td = TestData() + cls.testdata = td.testdata + cls.helper = StorPoolHelper() + storpool_primary_storage = cls.testdata[TestData.primaryStorage] + cls.template_name = storpool_primary_storage.get("name") + storpool_service_offerings = cls.testdata[TestData.serviceOffering] + + nfs_service_offerings = cls.testdata[TestData.serviceOfferingsPrimary] + ceph_service_offerings = cls.testdata[TestData.serviceOfferingsCeph] + + + storage_pool = list_storage_pools( + cls.apiclient, + name=cls.template_name + ) + + nfs_storage_pool = list_storage_pools( + cls.apiclient, + name='nfs' + ) + + ceph_primary_storage = cls.testdata[TestData.primaryStorage4] + + cls.ceph_storage_pool = list_storage_pools( + cls.apiclient, + name=ceph_primary_storage.get("name") + )[0] + + service_offerings = list_service_offering( + cls.apiclient, + name=cls.template_name + ) + nfs_service_offering = list_service_offering( + cls.apiclient, + name='nfs' + ) + + ceph_service_offering = list_service_offering( + cls.apiclient, + name=ceph_primary_storage.get("name") + ) + + disk_offerings = list_disk_offering( + cls.apiclient, + name="ssd" + ) + + cls.disk_offerings = disk_offerings[0] + if storage_pool is None: + storage_pool = StoragePool.create(cls.apiclient, storpool_primary_storage) + else: + storage_pool = storage_pool[0] + cls.storage_pool = storage_pool + if service_offerings is None: + service_offerings = ServiceOffering.create(cls.apiclient, storpool_service_offerings) + else: + service_offerings = service_offerings[0] + if nfs_service_offering is None: + nfs_service_offering = ServiceOffering.create(cls.apiclient, nfs_service_offerings) + else: + nfs_service_offering = nfs_service_offering[0] + + if ceph_service_offering is None: + ceph_service_offering = ServiceOffering.create(cls.apiclient, ceph_service_offerings) + else: + ceph_service_offering = ceph_service_offering[0] + #The version of CentOS has to be supported + template = get_template( + cls.apiclient, + cls.zone.id, + account = "system" + ) + + cls.nfs_storage_pool = nfs_storage_pool[0] + if cls.nfs_storage_pool.state == "Maintenance": + cls.nfs_storage_pool = StoragePool.cancelMaintenance(cls.apiclient, cls.nfs_storage_pool.id) + + if cls.ceph_storage_pool.state == "Maintenance": + cls.ceph_storage_pool = StoragePool.cancelMaintenance(cls.apiclient, cls.ceph_storage_pool.id) + + cls.account = cls.helper.create_account( + cls.apiclient, + cls.services["account"], + accounttype = 1, + domainid=cls.domain.id, + roleid = 1 + ) + cls._cleanup.append(cls.account) + + securitygroup = SecurityGroup.list(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid)[0] + cls.helper.set_securityGroups(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid, id = securitygroup.id) + + cls.vm = VirtualMachine.create(cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=nfs_service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + cls.vm2 = VirtualMachine.create(cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=nfs_service_offering.id, + hypervisor= cls.hypervisor, + rootdisksize=10 + ) + cls.vm3 = VirtualMachine.create(cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=nfs_service_offering.id, + hypervisor= cls.hypervisor, + rootdisksize=10 + ) + cls.vm4 = VirtualMachine.create(cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=ceph_service_offering.id, + hypervisor= cls.hypervisor, + rootdisksize=10 + ) + cls.vm5 = VirtualMachine.create(cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=ceph_service_offering.id, + hypervisor= cls.hypervisor, + rootdisksize=10 + ) + cls.storage_pool = StoragePool.update(cls.apiclient, + id=cls.storage_pool.id, + tags = ["ssd, nfs"]) + + + if template == FAILED: + assert False, "get_template() failed to return template\ + with description %s" % cls.services["ostype"] + + cls.services["domainid"] = cls.domain.id + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["templates"]["ostypeid"] = template.ostypeid + cls.services["zoneid"] = cls.zone.id + + + cls.service_offering = service_offerings + cls.nfs_service_offering = nfs_service_offering + + cls.template = template + cls.random_data_0 = random_gen(size=100) + cls.test_dir = "/tmp" + cls.random_data = "random.data" + return + + @classmethod + def tearDownClass(cls): + cls.cleanUpCloudStack() + + @classmethod + def cleanUpCloudStack(cls): + try: + if cls.nfs_storage_pool.state is not "Maintenance": + cls.nfs_storage_pool = StoragePool.enableMaintenance(cls.apiclient, cls.nfs_storage_pool.id) + + if cls.ceph_storage_pool.state is not "Maintenance": + cls.ceph_storage_pool = StoragePool.enableMaintenance(cls.apiclient, cls.ceph_storage_pool.id) + + cls.storage_pool = StoragePool.update(cls.apiclient, + id=cls.storage_pool.id, + tags = ["ssd"]) + # Cleanup resources used + 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() + + if self.unsupportedHypervisor: + self.skipTest("Skipping test because unsupported hypervisor\ + %s" % self.hypervisor) + return + + def tearDown(self): + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_1_migrate_vm_from_nfs_to_storpool(self): + ''' Test migrate virtual machine from NFS primary storage to StorPool''' + + self.vm.stop(self.apiclient, forced=True) + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.virtualmachineid = self.vm.id + cmd.storageid = self.storage_pool.id + migrated_vm = self.apiclient.migrateVirtualMachine(cmd) + volumes = list_volumes( + self.apiclient, + virtualmachineid = migrated_vm.id, + listall=True + ) + for v in volumes: + name = v.path.split("/")[3] + try: + sp_volume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + self.assertEqual(v.storageid, self.storage_pool.id, "Did not migrate virtual machine from NFS to StorPool") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_2_migrate_volume_from_nfs_to_storpool(self): + ''' Test migrate volume from NFS primary storage to StorPool''' + + self.vm2.stop(self.apiclient, forced=True) + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.vm2.id, + listall=True + ) + for v in volumes: + cmd = migrateVolume.migrateVolumeCmd() + cmd.storageid = self.storage_pool.id + cmd.volumeid = v.id + volume = self.apiclient.migrateVolume(cmd) + self.assertEqual(volume.storageid, self.storage_pool.id, "Did not migrate volume from NFS to StorPool") + + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.vm2.id, + listall=True + ) + for v in volumes: + name = v.path.split("/")[3] + try: + sp_volume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_3_migrate_volume_from_nfs_to_storpool(self): + '''Test write on disk before migrating volume from NFS primary storage + Check that data is on disk after migration''' + + try: + # Login to VM and write data to file system + ssh_client = self.vm3.get_ssh_client(reconnect = True) + + cmds = [ + "echo %s > %s/%s" % + (self.random_data_0, self.test_dir, self.random_data), + "sync", + "sleep 1", + "sync", + "sleep 1", + "cat %s/%s" % + (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.vm3.ipaddress) + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data has be write into temp file!" + ) + + self.vm3.stop(self.apiclient, forced=True) + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.vm3.id, + listall=True + ) + time.sleep(30) + for v in volumes: + cmd = migrateVolume.migrateVolumeCmd() + cmd.storageid = self.storage_pool.id + cmd.volumeid = v.id + volume = self.apiclient.migrateVolume(cmd) + self.assertEqual(volume.storageid, self.storage_pool.id, "Did not migrate volume from NFS to StorPool") + + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.vm3.id, + listall=True + ) + for v in volumes: + name = v.path.split("/")[3] + try: + sp_volume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + self.vm3.start(self.apiclient) + try: + ssh_client = self.vm3.get_ssh_client(reconnect=True) + + cmds = [ + "cat %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.vm3.ipaddress) + + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data is equal with the ramdom file!" + ) diff --git a/test/integration/plugins/storpool/TestStorPoolVolumes.py b/test/integration/plugins/storpool/TestStorPoolVolumes.py new file mode 100644 index 000000000000..bbb8a3ffbdae --- /dev/null +++ b/test/integration/plugins/storpool/TestStorPoolVolumes.py @@ -0,0 +1,2153 @@ +# 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 +# with 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. + +# Import Local Modules +from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual +from marvin.lib.base import (Account, + Cluster, + Configurations, + ServiceOffering, + Snapshot, + StoragePool, + Template, + VirtualMachine, + VmSnapshot, + Volume, + SecurityGroup, + Role, + ) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_disk_offering, + list_hosts, + list_snapshots, + list_storage_pools, + list_volumes, + list_virtual_machines, + list_configurations, + list_service_offering, + list_clusters, + list_zones) +from marvin.cloudstackAPI import (listOsTypes, + listTemplates, + listHosts, + createTemplate, + createVolume, + getVolumeSnapshotDetails, + resizeVolume, + listZones) +import time +import pprint +import random +import subprocess +from storpool import spapi +from storpool import sptypes +from marvin.configGenerator import configuration +import uuid +from sp_util import (TestData, StorPoolHelper) + +class TestStoragePool(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + super(TestStoragePool, cls).setUpClass() + try: + cls.setUpCloudStack() + except Exception: + cls.cleanUpCloudStack() + raise + + @classmethod + def setUpCloudStack(cls): + testClient = super(TestStoragePool, cls).getClsTestClient() + + cls._cleanup = [] + + cls.apiclient = testClient.getApiClient() + cls.helper = StorPoolHelper() + + cls.unsupportedHypervisor = False + cls.hypervisor = testClient.getHypervisorInfo() + if cls.hypervisor.lower() in ("hyperv", "lxc"): + cls.unsupportedHypervisor = True + return + + cls.services = testClient.getParsedTestDataConfig() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = None + zones = list_zones(cls.apiclient) + + for z in zones: + if z.name == cls.getClsConfig().mgtSvr[0].zone: + cls.zone = z + + assert cls.zone is not None + + cls.sp_template_1 = "ssd" + storpool_primary_storage = { + "name" : cls.sp_template_1, + "zoneid": cls.zone.id, + "url": "SP_API_HTTP=10.2.23.248:81;SP_AUTH_TOKEN=6549874687;SP_TEMPLATE=%s" % cls.sp_template_1, + "scope": "zone", + "capacitybytes": 564325555333, + "capacityiops": 155466, + "hypervisor": "kvm", + "provider": "StorPool", + "tags": cls.sp_template_1 + } + + cls.storpool_primary_storage = storpool_primary_storage + host, port, auth = cls.getCfgFromUrl(url = storpool_primary_storage["url"]) + cls.spapi = spapi.Api(host=host, port=port, auth=auth, multiCluster=True) + + storage_pool = list_storage_pools( + cls.apiclient, + name=storpool_primary_storage["name"] + ) + + if storage_pool is None: + newTemplate = sptypes.VolumeTemplateCreateDesc(name = storpool_primary_storage["name"],placeAll = "virtual", placeTail = "virtual", placeHead = "virtual", replication=1) + template_on_local = cls.spapi.volumeTemplateCreate(newTemplate) + + storage_pool = StoragePool.create(cls.apiclient, storpool_primary_storage) + else: + storage_pool = storage_pool[0] + cls.primary_storage = storage_pool + + + storpool_service_offerings_ssd = { + "name": cls.sp_template_1, + "displaytext": "SP_CO_2 (Min IOPS = 10,000; Max IOPS = 15,000)", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "hypervisorsnapshotreserve": 200, + "tags": cls.sp_template_1 + } + + service_offerings_ssd = list_service_offering( + cls.apiclient, + name=storpool_service_offerings_ssd["name"] + ) + + if service_offerings_ssd is None: + service_offerings_ssd = ServiceOffering.create(cls.apiclient, storpool_service_offerings_ssd) + else: + service_offerings_ssd = service_offerings_ssd[0] + + cls.service_offering = service_offerings_ssd + cls.debug(pprint.pformat(cls.service_offering)) + + + cls.sp_template_2 = "ssd2" + + storpool_primary_storage2 = { + "name" : cls.sp_template_2, + "zoneid": cls.zone.id, + "url": "SP_API_HTTP=10.2.23.248:81;SP_AUTH_TOKEN=6549874687;SP_TEMPLATE=%s" % cls.sp_template_2, + "scope": "zone", + "capacitybytes": 564325555333, + "capacityiops": 1554, + "hypervisor": "kvm", + "provider": "StorPool", + "tags": cls.sp_template_2 + } + + cls.storpool_primary_storage2 = storpool_primary_storage2 + storage_pool = list_storage_pools( + cls.apiclient, + name=storpool_primary_storage2["name"] + ) + + if storage_pool is None: + newTemplate = sptypes.VolumeTemplateCreateDesc(name = storpool_primary_storage2["name"],placeAll = "virtual", placeTail = "virtual", placeHead = "virtual", replication=1) + + template_on_local = cls.spapi.volumeTemplateCreate(newTemplate) + + storage_pool = StoragePool.create(cls.apiclient, storpool_primary_storage2) + + else: + storage_pool = storage_pool[0] + cls.primary_storage2 = storage_pool + + storpool_service_offerings_ssd2 = { + "name": cls.sp_template_2, + "displaytext": "SP_CO_2", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "tags": cls.sp_template_2 + } + + service_offerings_ssd2 = list_service_offering( + cls.apiclient, + name=storpool_service_offerings_ssd2["name"] + ) + + if service_offerings_ssd2 is None: + service_offerings_ssd2 = ServiceOffering.create(cls.apiclient, storpool_service_offerings_ssd2) + else: + service_offerings_ssd2 = service_offerings_ssd2[0] + + cls.service_offering2 = service_offerings_ssd2 + + disk_offerings = list_disk_offering( + cls.apiclient, + name="Small" + ) + + disk_offering_20 = list_disk_offering( + cls.apiclient, + name="Medium" + ) + + disk_offering_100 = list_disk_offering( + cls.apiclient, + name="Large" + ) + + + cls.disk_offerings = disk_offerings[0] + cls.disk_offering_20 = disk_offering_20[0] + cls.disk_offering_100 = disk_offering_100[0] + + #The version of CentOS has to be supported + template = get_template( + cls.apiclient, + cls.zone.id, + account = "system" + ) + + if template == FAILED: + assert False, "get_template() failed to return template\ + with description %s" % cls.services["ostype"] + + cls.services["domainid"] = cls.domain.id + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["templates"]["ostypeid"] = template.ostypeid + cls.services["zoneid"] = cls.zone.id + cls.services["diskofferingid"] = cls.disk_offerings.id + + role = Role.list(cls.apiclient, name='Admin') + + # Create VMs, VMs etc + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + domainid=cls.domain.id, + roleid = role[0].id + ) + + securitygroup = SecurityGroup.list(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid)[0] + cls.helper.set_securityGroups(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid, id = securitygroup.id) + cls._cleanup.append(cls.account) + + cls.volume_1 = Volume.create( + cls.apiclient, + {"diskname":"StorPoolDisk-1" }, + zoneid=cls.zone.id, + diskofferingid=disk_offerings[0].id, + account=cls.account.name, + domainid=cls.account.domainid, + ) + + cls.volume_2 = Volume.create( + cls.apiclient, + {"diskname":"StorPoolDisk-2" }, + zoneid=cls.zone.id, + diskofferingid=disk_offerings[0].id, + account=cls.account.name, + domainid=cls.account.domainid, + ) + + cls.volume = Volume.create( + cls.apiclient, + {"diskname":"StorPoolDisk-3" }, + zoneid=cls.zone.id, + diskofferingid=disk_offerings[0].id, + account=cls.account.name, + domainid=cls.account.domainid, + ) + + cls.virtual_machine = VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=cls.account.name, + domainid=cls.account.domainid, + zoneid=cls.zone.id, + templateid=template.id, + serviceofferingid=cls.service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + + cls.virtual_machine2 = VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=cls.account.name, + domainid=cls.account.domainid, + zoneid=cls.zone.id, + templateid=template.id, + serviceofferingid=cls.service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + + cls.vm_migrate = VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=cls.account.name, + domainid=cls.account.domainid, + zoneid=cls.zone.id, + templateid=template.id, + serviceofferingid=cls.service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + + cls.template = template + cls.hostid = cls.virtual_machine.hostid + cls.random_data_0 = random_gen(size=100) + cls.test_dir = "/tmp" + cls.random_data = "random.data" + return + + @classmethod + def tearDownClass(cls): + cls.cleanUpCloudStack() + + @classmethod + def cleanUpCloudStack(cls): + try: + # Cleanup resources used + 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() + + if self.unsupportedHypervisor: + self.skipTest("Skipping test because unsupported hypervisor\ + %s" % self.hypervisor) + return + + def tearDown(self): + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_01_snapshot_to_template(self): + ''' Create template from snapshot without bypass secondary storage + ''' + volume = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "false") + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + snapshotid = snapshot.id + ) + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + ssh_client = virtual_machine.get_ssh_client() + + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + self._cleanup.append(template) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_02_snapshot_to_template_bypass_secondary(self): + ''' Test Create Template from snapshot bypassing secondary storage + ''' + ##cls.virtual_machine + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + try: + name = volume[0].path.split("/")[3] + sp_volume = self.spapi.volumeList(volumeName = "~" + name) + except spapi.ApiError as err: + raise Exception(err) + + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snasphot in snapshot_details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + snapshotid = snapshot.id + ) + + flag = False + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + flag = True + break + else: + continue + break + + if flag is False: + raise Exception("Template does not exists in Storpool") + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + ssh_client = virtual_machine.get_ssh_client() + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + self._cleanup.append(template) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_03_snapshot_volume_with_secondary(self): + ''' + Test Create snapshot and backup to secondary + ''' + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "false") + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot_details") + except spapi.ApiError as err: + raise Exception(err) + self.assertIsNotNone(snapshot, "Could not create snapshot") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_04_snapshot_volume_bypass_secondary(self): + ''' + Test snapshot bypassing secondary + ''' + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except spapi.ApiError as err: + raise Exception(err) + self.assertIsNotNone(snapshot, "Could not create snapshot") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_05_delete_template_bypassed_secondary(self): + ''' Test delete template from snapshot bypassed secondary storage + ''' + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + try: + name = volume[0].path.split("/")[3] + sp_volume = self.spapi.volumeList(volumeName = "~" + name) + except spapi.ApiError as err: + raise Exception(err) + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + snapshotid = snapshot.id + ) + + flag = False + storpoolGlId = None + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + storpoolGlId = "~" + snap.globalId + flag = True + break + else: + continue + break + + if flag is False: + raise Exception("Template does not exists in Storpool") + + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + temp = Template.delete(template, self.apiclient, self.zone.id) + self.assertIsNone(temp, "Template was not deleted") + + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = storpoolGlId) + if sp_snapshot is not None: + self.debug("Snapshot exists on StorPool name " + storpoolGlId) + except spapi.ApiError as err: + self.debug("Do nothing the template has to be deleted") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_06_template_from_snapshot(self): + ''' Test create template bypassing secondary from snapshot which is backed up on secondary storage + ''' + ##cls.virtual_machine + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + try: + name = volume[0].path.split("/")[3] + sp_volume = self.spapi.volumeList(volumeName = "~" + name) + except spapi.ApiError as err: + raise Exception(err) + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "false") + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapsho details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + snapshotid = snapshot.id + ) + + flag = False + globalId = None + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + flag = True + globalId = snap.globalId + break + else: + continue + break + + if flag is False: + raise Exception("Template does not exists in Storpool") + + + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + temp = Template.delete(template, self.apiclient, self.zone.id) + self.assertIsNone(temp, "Template was not deleted") + + if globalId is not None: + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + globalId) + if sp_snapshot is not None: + self.debug("Snapshot exists on Storpool name " + globalId) + except spapi.ApiError as err: + self.debug("Do nothing the template has to be deleted") + else: + flag = False + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + flag = True + break + else: + continue + break + + if flag is True: + raise Exception("Template should not exists in Storpool") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_07_delete_snapshot_of_deleted_volume(self): + ''' Delete snapshot and template if volume is already deleted, not bypassing secondary + ''' + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "false") + + volume = Volume.create( + self.apiclient, + {"diskname":"StorPoolDisk-Delete" }, + zoneid = self.zone.id, + diskofferingid = self.disk_offerings.id, + account=self.account.name, + domainid=self.account.domainid, + ) + delete = volume + self.virtual_machine2.stop(self.apiclient, forced=True) + self.virtual_machine2.attach_volume( + self.apiclient, + volume + ) + self.virtual_machine2.detach_volume( + self.apiclient, + volume + ) + + volume = list_volumes(self.apiclient, id = volume.id) + + name = volume[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + except spapi.ApiError as err: + raise Exception(err) + if flag == False: + raise Exception("Could not finad snapshot in snapshot details") + except Exception as err: + raise Exception(err) + + template = self.create_template_from_snapshot(self.apiclient, self.services, snapshotid = snapshot.id) + + template_from_volume = self.create_template_from_snapshot(self.apiclient, self.services, volumeid = volume[0].id) + + Volume.delete(delete, self.apiclient, ) + Snapshot.delete(snapshot, self.apiclient) + + flag = False + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + if snapshot_details is not None: + try: + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + except spapi.ApiError as err: + flag = False + + if flag is True: + raise Exception("Snapshot was not deleted") + except Exception as err: + self.debug('Snapshot was deleted %s' % err) + + Template.delete(template, self.apiclient, zoneid = self.zone.id) + Template.delete(template_from_volume, self.apiclient, zoneid = self.zone.id) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_08_delete_snapshot_of_deleted_volume(self): + ''' Delete snapshot and template if volume is already deleted, bypassing secondary + ''' + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + + volume = Volume.create( + self.apiclient, + {"diskname":"StorPoolDisk-Delete" }, + zoneid = self.zone.id, + diskofferingid = self.disk_offerings.id, + account=self.account.name, + domainid=self.account.domainid, + ) + delete = volume + self.virtual_machine2.attach_volume( + self.apiclient, + volume + ) + self.virtual_machine2.detach_volume( + self.apiclient, + volume + ) + + volume = list_volumes(self.apiclient, id = volume.id) + + name = volume[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + if snapshot_details is not None: + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + except spapi.ApiError as err: + raise Exception(err) + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except Exception as err: + raise Exception(err) + + template = self.create_template_from_snapshot(self.apiclient, self.services, snapshotid = snapshot.id) + + Volume.delete(delete, self.apiclient, ) + Snapshot.delete(snapshot, self.apiclient) + + flag = False + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + if snapshot_details is not None: + try: + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + except spapi.ApiError as err: + flag = False + + if flag is True: + raise Exception("Snapshot was not deleted") + except Exception as err: + self.debug('Snapshot was deleted %s' % err) + + + Template.delete(template, self.apiclient, zoneid = self.zone.id) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_09_vm_from_bypassed_template(self): + '''Create virtual machine with sp.bypass.secondary.storage=false + from template created on StorPool and Secondary Storage''' + + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + + name = volume[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "true") + + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + except spapi.ApiError as err: + raise Exception(err) + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except Exception as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + snapshotid = snapshot.id + ) + self._cleanup.append(template) + + flag = False + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + flag = True + break + else: + continue + break + + if flag is False: + raise Exception("Template does not exists in Storpool") + + + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + + backup_config = Configurations.update(self.apiclient, + name = "sp.bypass.secondary.storage", + value = "false") + + vm = VirtualMachine.create( + self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid = template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10, + ) + + ssh_client = vm.get_ssh_client(reconnect=True) + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_10_create_vm_snapshots(self): + """Test to create VM snapshots + """ + volume_attached = self.virtual_machine.attach_volume( + self.apiclient, + self.volume + ) + + vol = list_volumes(self.apiclient, virtualmachineid=self.virtual_machine.id, id=volume_attached.id) + name = vol[0].path.split("/")[3] + sp_volume = self.spapi.volumeList(volumeName = "~" + name) + self.assertEqual(volume_attached.id, self.volume.id, "Is not the same volume ") + try: + # Login to VM and write data to file system + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "echo %s > %s/%s" % + (self.random_data_0, self.test_dir, self.random_data), + "sync", + "sleep 1", + "sync", + "sleep 1", + "cat %s/%s" % + (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data has be write into temp file!" + ) + + time.sleep(30) + MemorySnapshot = False + vm_snapshot = VmSnapshot.create( + self.apiclient, + self.virtual_machine.id, + MemorySnapshot, + "TestSnapshot", + "Display Text" + ) + self.assertEqual( + vm_snapshot.state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_11_revert_vm_snapshots(self): + """Test to revert VM snapshots + """ + + try: + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "rm -rf %s/%s" % (self.test_dir, self.random_data), + "ls %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + if str(result[0]).index("No such file or directory") == -1: + self.fail("Check the random data has be delete from temp file!") + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + + self.assertEqual( + list_snapshot_response[0].state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + self.virtual_machine.stop(self.apiclient, forced=True) + + VmSnapshot.revertToSnapshot( + self.apiclient, + list_snapshot_response[0].id + ) + + self.virtual_machine.start(self.apiclient) + + try: + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "cat %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data is equal with the ramdom file!" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_12_delete_vm_snapshots(self): + """Test to delete vm snapshots + """ + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + VmSnapshot.deleteVMSnapshot( + self.apiclient, + list_snapshot_response[0].id) + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + #vmid=self.virtual_machine.id, + virtualmachineid=self.virtual_machine.id, + listall=False) + self.debug('list_snapshot_response -------------------- %s' % list_snapshot_response) + + self.assertIsNone(list_snapshot_response, "snapshot is already deleted") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_13_detach_volume(self): + '''Attach volume on VM on 2nd zone''' + self.virtual_machine.stop(self.apiclient) + self.virtual_machine.detach_volume( + self.apiclient, + self.volume + ) + vol = list_volumes(self.apiclient, id=self.volume.id) + name = vol[0].path.split("/")[3] + spvolume = self.spapi.volumeList(volumeName = "~" + name) + self.assertEqual(vol[0].id, self.volume.id, "Is not the same volume ") + tags = spvolume[0].tags + for t in tags: + self.assertFalse(t.lower() == 'cvm'.lower(), "cvm tag still set on detached volume") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_14_attach_detach_volume_to_running_vm(self): + ''' Test Attach Volume To Running Virtual Machine + ''' + time.sleep(60) + self.assertEqual(VirtualMachine.RUNNING, self.virtual_machine.state, "Running") + volume = self.virtual_machine.attach_volume( + self.apiclient, + self.volume_1 + ) + print(volume) + self.assertIsNotNone(volume, "Volume is not None") + + list_vm_volumes = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + id= volume.id + ) + print(list_vm_volumes) + self.assertEqual(volume.id, list_vm_volumes[0].id, "Is true") + + name = list_vm_volumes[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + volume = self.virtual_machine.detach_volume( + self.apiclient, + self.volume_1 + ) + list_vm_volumes = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + id = volume.id + ) + + print(list_vm_volumes) + self.assertIsNone(list_vm_volumes, "Is None") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_15_resize_root_volume_on_working_vm(self): + ''' Test Resize Root volume on Running Virtual Machine + ''' + self.assertEqual(VirtualMachine.RUNNING, self.virtual_machine2.state, "Running") + volume = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine2.id, + type = "ROOT", + listall = True, + ) + volume = volume[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + self.assertEqual(volume.type, 'ROOT', "Volume is not of ROOT type") + shrinkOk = False + if volume.size > int((self.disk_offering_20.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = volume.id + cmd.size = 20 + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + + new_size = Volume.list( + self.apiclient, + id=volume.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_20.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + volume = new_size[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if volume.size > int((self.disk_offering_100.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = volume.id + cmd.size = 100 + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=volume.id + ) + + volume = new_size[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_100.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_16_resize_attached_volume_on_working_vm(self): + ''' Test Resize Volume Attached To Running Virtual Machine + ''' + self.assertEqual(VirtualMachine.RUNNING, self.virtual_machine.state, "Running") + volume = self.virtual_machine.attach_volume( + self.apiclient, + self.volume_1 + ) + + listvol = Volume.list( + self.apiclient, + id=volume.id + ) + name = listvol[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != listvol[0].size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if volume.size > int((self.disk_offering_20.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = volume.id + cmd.diskofferingid = self.disk_offering_20.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + + new_size = Volume.list( + self.apiclient, + id=volume.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_20.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + volume = new_size[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if volume.size > int((self.disk_offering_100.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = volume.id + cmd.diskofferingid = self.disk_offering_100.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=volume.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_100.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + + # return to small disk + volume = new_size[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if volume.size > int((self.disk_offerings.disksize)* (1024**3)): + shrinkOk= True + + cmd.diskofferingid = self.disk_offerings.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=volume.id + ) + + volume = new_size[0] + + name = volume.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + self.assertTrue( + (new_size[0].size == int((self.disk_offerings.disksize)*(1024**3))), + "Could not return to Small disk" + ) + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_17_attach_detach_volume_to_stopped_vm(self): + ''' Test Attach Volume To Stopped Virtual Machine + ''' + virtual_machine = self.virtual_machine.stop( + self.apiclient, + forced=True + ) + + time.sleep(60) + volume_2 = self.virtual_machine.attach_volume( + self.apiclient, + self.volume_2 + ) + list_vm_volumes = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + id= volume_2.id + ) + + name = list_vm_volumes[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + print(list_vm_volumes) + self.assertEqual(volume_2.id,list_vm_volumes[0].id, "Is true") + + time.sleep(90) + volume_2 = self.virtual_machine.detach_volume( + self.apiclient, + self.volume_2 + ) + list_vm_volumes = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + id = volume_2.id + ) + print(list_vm_volumes) + self.assertIsNone(list_vm_volumes, "Is None") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_18_resize_attached_volume(self): + ''' Test Resize Volume Attached To Virtual Machine + ''' + + shrinkOk = False + if self.volume_1.size > int((self.disk_offering_20.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = self.volume_1.id + cmd.diskofferingid = self.disk_offering_20.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + + new_size = Volume.list( + self.apiclient, + id=self.volume_1.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_20.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + self.volume_1 = new_size[0] + + name = self.volume_1.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != self.volume_1.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if self.volume_1.size > int((self.disk_offering_100.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = self.volume_1.id + cmd.diskofferingid = self.disk_offering_100.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=self.volume_1.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_100.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + + # return to small disk + self.volume_1 = new_size[0] + + name = self.volume_1.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != self.volume_1.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if self.volume_1.size > int((self.disk_offerings.disksize)* (1024**3)): + shrinkOk= True + + cmd.diskofferingid = self.disk_offerings.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=self.volume_1.id + ) + + name = new_size[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != new_size[0].size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + self.assertTrue( + (new_size[0].size == int((self.disk_offerings.disksize)*(1024**3))), + "Could not return to Small disk" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_19_resize_detached_volume(self): + ''' Test Resize Volume Detached To Virtual Machine + ''' + list_vm_volumes = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + id= self.volume_2.id + ) + + #check that the volume is not attached to VM + self.assertIsNone(list_vm_volumes, "List volumes is not None") + + shrinkOk = False + if self.volume_2.size > int((self.disk_offering_20.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = self.volume_2.id + cmd.diskofferingid = self.disk_offering_20.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + + new_size = Volume.list( + self.apiclient, + id=self.volume_2.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_20.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + self.volume_2 = new_size[0] + + name = self.volume_2.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != self.volume_2.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if self.volume_2.size > int((self.disk_offering_100.disksize) * (1024**3)): + shrinkOk= True + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = self.volume_2.id + cmd.diskofferingid = self.disk_offering_100.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=self.volume_2.id + ) + + self.assertTrue( + (new_size[0].size == int((self.disk_offering_100.disksize) * (1024**3))), + "New size is not int((self.disk_offering_20) * (1024**3)" + ) + + # return to small disk + self.volume_2 = new_size[0] + + name = self.volume_2.path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != self.volume_2.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + shrinkOk = False + if self.volume_2.size > int((self.disk_offerings.disksize)* (1024**3)): + shrinkOk= True + + cmd.diskofferingid = self.disk_offerings.id + cmd.shrinkok = shrinkOk + + self.apiclient.resizeVolume(cmd) + new_size = Volume.list( + self.apiclient, + id=self.volume_2.id + ) + + name = new_size[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + if spvolume[0].size != new_size[0].size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + self.assertTrue( + (new_size[0].size == int((self.disk_offerings.disksize)*(1024**3))), + "Could not return to Small disk" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_20_snapshot_to_volume(self): + ''' Create volume from snapshot + ''' + snapshot = Snapshot.create( + self.apiclient, + volume_id = self.volume_2.id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + volume = self.create_volume( + self.apiclient, + zoneid = self.zone.id, + snapshotid = snapshot.id, + account=self.account.name, + domainid=self.account.domainid + ) + + listvol = Volume.list( + self.apiclient, + id=volume.id + ) + name = listvol[0].path.split("/")[3] + try: + spvolume = self.spapi.volumeList(volumeName="~" + name) + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(volume, "Could not create volume from snapshot") + self.assertIsInstance(volume, Volume, "Volume is not instance of Volume") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_21_snapshot_detached_volume(self): + ''' Test Snapshot Detached Volume + ''' + self.virtual_machine.stop( + self.apiclient, + forced = True + ) + self.volume = self.virtual_machine.attach_volume( + self.apiclient, + self.volume + ) + self.assertIsNotNone(self.volume, "Attach: Is none") + self.volume = self.virtual_machine.detach_volume( + self.apiclient, + self.volume + ) + + self.assertIsNotNone(self.volume, "Detach: Is none") + + snapshot = Snapshot.create( + self.apiclient, + self.volume.id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Snapshot is None") + + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not Instance of Snappshot") + + snapshot = Snapshot.delete( + snapshot, + self.apiclient + ) + + self.assertIsNone(snapshot, "Snapshot was not deleted") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_22_snapshot_root_disk(self): + ''' Test ROOT Disk Snapshot + ''' + vm = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid = self.zone.id, + templateid = self.template.id, + serviceofferingid = self.service_offering.id, + hypervisor = self.hypervisor, + rootdisksize = 10 + ) + list_volumes_of_vm = list_volumes( + self.apiclient, + virtualmachineid = vm.id, + listall = True, + ) + self.assertIs(len(list_volumes_of_vm), 1, "VM has more disk than 1") + + snapshot = Snapshot.create( + self.apiclient, + list_volumes_of_vm[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = self.apiclient.getVolumeSnapshotDetails(cmd) + flag = False + for s in snapshot_details: + if s["snapshotDetailsName"] == snapshot.id: + name = s["snapshotDetailsValue"].split("/")[3] + sp_snapshot = self.spapi.snapshotList(snapshotName = "~" + name) + flag = True + if flag == False: + raise Exception("Could not find snapshot in snapshot details") + except spapi.ApiError as err: + raise Exception(err) + + self.assertIsNotNone(snapshot, "Snapshot is None") + + self.assertEqual(list_volumes_of_vm[0].id, snapshot.volumeid, "Snapshot is not for the same volume") + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_23_volume_to_template(self): + ''' Create Template From ROOT Volume + ''' + volume = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + + self.virtual_machine.stop(self.apiclient) + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + volumeid = volume[0].id + ) + + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + ssh_client = virtual_machine.get_ssh_client() + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + self._cleanup.append(template) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_24_migrate_vm_to_another_storage(self): + ''' Migrate VM to another Primary Storage + ''' + list_volumes_of_vm = list_volumes( + self.apiclient, + virtualmachineid = self.vm_migrate.id, + listall = True, + ) + + self.assertTrue(len(list_volumes_of_vm) == 1, "There are more volumes attached to VM") + + if list_volumes_of_vm[0].storageid is self.primary_storage.id: + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.virtualmachineid = self.vm_migrate.id + if hostid: + cmd.hostid = hostid + vm = apiclient.migrateVirtualMachine(cmd) + volume = list_volumes( + self.apiclient, + virtualmachineid = vm.id + )[0] + self.assertNotEqual(volume.storageid, self.primary_storage.id, "Could not migrate VM") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_25_migrate_volume_to_another_storage(self): + ''' Migrate Volume To Another Primary Storage + ''' + self.assertFalse(hasattr(self.volume, 'virtualmachineid') , "Volume is not detached") + + self.assertFalse(hasattr(self.volume, 'storageid') , "Volume is not detached") + volume = Volume.migrate( + self.apiclient, + volumeid = self.volume.id, + storageid = self.primary_storage2.id + ) + + self.assertIsNotNone(volume, "Volume is None") + + self.assertEqual(volume.storageid, self.primary_storage2.id, "Storage is the same") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_26_create_vm_on_another_storpool_storage(self): + """ Create Virtual Machine on another StorPool primary StoragePool""" + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=self.template.id, + serviceofferingid=self.service_offering2.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + self.assertIsNotNone(virtual_machine, "Could not create virtual machine on another Storpool primary storage") + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_27_snapshot_to_volume_of_root_disk(self): + ''' Create volume from snapshot + ''' + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + volume1 = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + snapshot = Snapshot.create( + self.apiclient, + volume_id = volume1[0].id, + account=self.account.name, + domainid=self.account.domainid, + ) + + self.assertIsNotNone(snapshot, "Could not create snapshot") + self.assertIsInstance(snapshot, Snapshot, "Snapshot is not an instance of Snapshot") + + volume = self.create_volume( + self.apiclient, + zoneid = self.zone.id, + snapshotid = snapshot.id, + account=self.account.name, + domainid=self.account.domainid + ) + + self.assertIsNotNone(volume, "Could not create volume from snapshot") + self.assertIsInstance(volume, Volume, "Volume is not instance of Volume") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_28_download_volume(self): + vol = self.volume.extract( + self.apiclient, + volume_id = self.volume.id, + zoneid = self.zone.id, + mode = "HTTP_DOWNLOAD" + ) + self.assertIsNotNone(vol, "Volume is None") + self.assertIsNotNone(vol.url, "No URL provided") + Volume.delete(vol, self.apiclient) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_29_create_vm_from_template_not_on_storpool(self): + ''' Create virtual machine from template which for some reason is deleted from StorPool, but exists in template_spoool_ref DB tables ''' + + volume = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + type = "ROOT", + listall = True, + ) + + self.virtual_machine.stop(self.apiclient) + + template = self.create_template_from_snapshot( + self.apiclient, + self.services, + volumeid = volume[0].id + ) + + virtual_machine = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + ssh_client = virtual_machine.get_ssh_client(reconnect= True) + name = 'ssd-' + template.id + flag = False + storpoolGlId = None + + sp_snapshots = self.spapi.snapshotsList() + for snap in sp_snapshots: + tags = snap.tags + for t in tags: + if tags[t] == template.id: + storpoolGlId = snap.globalId + flag = True + break + else: + continue + break + + if flag is False: + try: + sp_snapshot = self.spapi.snapshotList(snapshotName = name) + except spapi.ApiError as err: + raise Exception(err) + + + self.spapi.snapshotDelete(snapshotName ="~" + storpoolGlId) + + virtual_machine2 = VirtualMachine.create(self.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + accountid=self.account.name, + domainid=self.account.domainid, + zoneid=self.zone.id, + templateid=template.id, + serviceofferingid=self.service_offering.id, + hypervisor=self.hypervisor, + rootdisksize=10 + ) + + ssh_client = virtual_machine2.get_ssh_client(reconnect= True) + self.assertIsNotNone(template, "Template is None") + self.assertIsInstance(template, Template, "Template is instance of template") + self._cleanup.append(template) + + @classmethod + def create_volume(self, apiclient, zoneid=None, snapshotid=None, account=None, domainid=None): + """Create Volume""" + cmd = createVolume.createVolumeCmd() + cmd.name = "Test" + + if zoneid: + cmd.zoneid = zoneid + + if snapshotid: + cmd.snapshotid = snapshotid + + if account: + cmd.account=account + + if domainid: + cmd.domainid=domainid + return Volume(apiclient.createVolume(cmd).__dict__) + + @classmethod + def get_local_cluster(cls): + storpool_clusterid = subprocess.check_output(['storpool_confshow', 'CLUSTER_ID']) + clusterid = storpool_clusterid.split("=") + cls.debug(storpool_clusterid) + clusters = list_clusters(cls.apiclient) + for c in clusters: + configuration = list_configurations( + cls.apiclient, + clusterid = c.id + ) + for conf in configuration: + if conf.name == 'sp.cluster.id' and (conf.value in clusterid[1]): + return c + + @classmethod + def get_remote_cluster(cls): + storpool_clusterid = subprocess.check_output(['storpool_confshow', 'CLUSTER_ID']) + clusterid = storpool_clusterid.split("=") + cls.debug(storpool_clusterid) + clusters = list_clusters(cls.apiclient) + for c in clusters: + configuration = list_configurations( + cls.apiclient, + clusterid = c.id + ) + for conf in configuration: + if conf.name == 'sp.cluster.id' and (conf.value not in clusterid[1]): + return c + + @classmethod + def list_hosts_by_cluster_id(cls, clusterid): + """List all Hosts matching criteria""" + cmd = listHosts.listHostsCmd() + cmd.clusterid = clusterid + return(cls.apiclient.listHosts(cmd)) + + + def start(cls, vmid, hostid): + """Start the instance""" + cmd = startVirtualMachine.startVirtualMachineCmd() + cmd.id = vmid + cmd.hostid = hostid + return (cls.apiclient.startVirtualMachine(cmd)) + + + @classmethod + def create_template_from_snapshot(self, apiclient, services, snapshotid=None, volumeid=None): + """Create template from Volume""" + # Create template from Virtual machine and Volume ID + cmd = createTemplate.createTemplateCmd() + cmd.displaytext = "StorPool_Template" + cmd.name = "-".join(["StorPool-", random_gen()]) + if "ostypeid" in services: + cmd.ostypeid = services["ostypeid"] + elif "ostype" in services: + # Find OSTypeId from Os type + sub_cmd = listOsTypes.listOsTypesCmd() + sub_cmd.description = services["ostype"] + ostypes = apiclient.listOsTypes(sub_cmd) + + if not isinstance(ostypes, list): + raise Exception( + "Unable to find Ostype id with desc: %s" % + services["ostype"]) + cmd.ostypeid = ostypes[0].id + else: + raise Exception( + "Unable to find Ostype is required for creating template") + + cmd.isfeatured = True + cmd.ispublic = True + cmd.isextractable = False + + if snapshotid: + cmd.snapshotid = snapshotid + if volumeid: + cmd.volumeid = volumeid + return Template(apiclient.createTemplate(cmd).__dict__) + + @classmethod + def getCfgFromUrl(cls, url): + cfg = dict([ + option.split('=') + for option in url.split(';') + ]) + host, port = cfg['SP_API_HTTP'].split(':') + auth = cfg['SP_AUTH_TOKEN'] + return host, int(port), auth + + diff --git a/test/integration/plugins/storpool/TestTagsOnStorPool.py b/test/integration/plugins/storpool/TestTagsOnStorPool.py new file mode 100644 index 000000000000..a3a6cced58b7 --- /dev/null +++ b/test/integration/plugins/storpool/TestTagsOnStorPool.py @@ -0,0 +1,576 @@ +# 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 +# with 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. + +# Import Local Modules +import pprint +import random +import subprocess +import time +import json + +from marvin.cloudstackAPI import (listOsTypes, + listTemplates, + listHosts, + createTemplate, + createVolume, + resizeVolume, + revertSnapshot, + startVirtualMachine) +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING +from marvin.configGenerator import configuration, cluster +from marvin.lib.base import (Account, + Configurations, + ServiceOffering, + Snapshot, + StoragePool, + Template, + Tag, + VirtualMachine, + VmSnapshot, + Volume, + SecurityGroup, + ) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_disk_offering, + list_snapshots, + list_storage_pools, + list_volumes, + list_virtual_machines, + list_configurations, + list_service_offering, + list_clusters, + list_zones) +from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual +from nose.plugins.attrib import attr + +from storpool import spapi +import uuid +from sp_util import (TestData, StorPoolHelper) + +class TestStoragePool(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + super(TestStoragePool, cls).setUpClass() + try: + cls.setUpCloudStack() + except Exception: + cls.cleanUpCloudStack() + raise + + @classmethod + def setUpCloudStack(cls): + cls.spapi = spapi.Api(host="10.2.23.248", port="81", auth="6549874687", multiCluster=True) + testClient = super(TestStoragePool, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.unsupportedHypervisor = False + cls.hypervisor = testClient.getHypervisorInfo() + if cls.hypervisor.lower() in ("hyperv", "lxc"): + cls.unsupportedHypervisor = True + return + + cls._cleanup = [] + + cls.services = testClient.getParsedTestDataConfig() + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = None + zones = list_zones(cls.apiclient) + + for z in zones: + if z.name == cls.getClsConfig().mgtSvr[0].zone: + cls.zone = z + + assert cls.zone is not None + + td = TestData() + cls.testdata = td.testdata + cls.helper = StorPoolHelper() + + cls.account = cls.helper.create_account( + cls.apiclient, + cls.services["account"], + accounttype = 1, + domainid=cls.domain.id, + roleid = 1 + ) + cls._cleanup.append(cls.account) + + securitygroup = SecurityGroup.list(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid)[0] + cls.helper.set_securityGroups(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid, id = securitygroup.id) + + storpool_primary_storage = cls.testdata[TestData.primaryStorage] + + storpool_service_offerings = cls.testdata[TestData.serviceOffering] + + cls.template_name = storpool_primary_storage.get("name") + + storage_pool = list_storage_pools( + cls.apiclient, + name=cls.template_name + ) + + service_offerings = list_service_offering( + cls.apiclient, + name=cls.template_name + ) + + disk_offerings = list_disk_offering( + cls.apiclient, + name="ssd" + ) + + cls.disk_offerings = disk_offerings[0] + if storage_pool is None: + storage_pool = StoragePool.create(cls.apiclient, storpool_primary_storage) + else: + storage_pool = storage_pool[0] + cls.storage_pool = storage_pool + cls.debug(pprint.pformat(storage_pool)) + if service_offerings is None: + service_offerings = ServiceOffering.create(cls.apiclient, storpool_service_offerings) + else: + service_offerings = service_offerings[0] + #The version of CentOS has to be supported + template = get_template( + cls.apiclient, + cls.zone.id, + account = "system" + ) + + cls.debug(pprint.pformat(template)) + cls.debug(pprint.pformat(cls.hypervisor)) + + if template == FAILED: + assert False, "get_template() failed to return template\ + with description %s" % cls.services["ostype"] + + cls.services["domainid"] = cls.domain.id + cls.services["small"]["zoneid"] = cls.zone.id + cls.services["templates"]["ostypeid"] = template.ostypeid + cls.services["zoneid"] = cls.zone.id + cls.services["diskofferingid"] = cls.disk_offerings.id + + cls.service_offering = service_offerings + cls.debug(pprint.pformat(cls.service_offering)) + + cls.volume_1 = Volume.create( + cls.apiclient, + cls.services, + account=cls.account.name, + domainid=cls.account.domainid, + size=5 + ) + cls.volume_2 = Volume.create( + cls.apiclient, + cls.services, + account=cls.account.name, + domainid=cls.account.domainid, + size =5 + ) + cls.volume = Volume.create( + cls.apiclient, + cls.services, + account=cls.account.name, + domainid=cls.account.domainid, + size=5 + ) + cls.virtual_machine = VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + cls.virtual_machine2= VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + cls.template = template + cls.random_data_0 = random_gen(size=100) + cls.test_dir = "/tmp" + cls.random_data = "random.data" + return + + @classmethod + def tearDownClass(cls): + cls.cleanUpCloudStack() + + @classmethod + def cleanUpCloudStack(cls): + try: + # Cleanup resources used + 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() + + if self.unsupportedHypervisor: + self.skipTest("Skipping test because unsupported hypervisor\ + %s" % self.hypervisor) + return + + def tearDown(self): + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_01_set_vcpolicy_tag_to_vm_with_attached_disks(self): + ''' Test set vc-policy tag to VM with one attached disk + ''' + volume_attached = self.virtual_machine.attach_volume( + self.apiclient, + self.volume_1 + ) + tag = Tag.create( + self.apiclient, + resourceIds=self.virtual_machine.id, + resourceType='UserVm', + tags={'vc-policy': 'testing_vc-policy'} + ) + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine.id, listall=True) + vm_tags = vm[0].tags + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, listall=True + ) + + self.vc_policy_tags(volumes, vm_tags, vm) + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_02_set_vcpolicy_tag_to_attached_disk(self): + """ Test set vc-policy tag to new disk attached to VM""" + volume_attached = self.virtual_machine.attach_volume( + self.apiclient, + self.volume_2 + ) + volume = list_volumes(self.apiclient, id = volume_attached.id, listall=True) + name = volume[0].path.split("/")[3] + sp_volume = self.spapi.volumeList(volumeName="~" + name) + + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine.id, listall=True) + vm_tags = vm[0].tags + for vm_tag in vm_tags: + for sp_tag in sp_volume[0].tags: + if sp_tag == vm_tag.key: + self.assertEqual(sp_tag, vm_tag.key, "StorPool tag is not the same as the Virtual Machine tag") + self.assertEqual(sp_volume[0].tags[sp_tag], vm_tag.value, "StorPool tag value is not the same as the Virtual Machine tag value") + if sp_tag == 'cvm': + self.assertEqual(sp_volume[0].tags[sp_tag], vm[0].id, "cvm tag is not the expected value") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_03_create_vm_snapshot_vc_policy_tag(self): + """Test to create VM snapshots with VC policy tags + """ + volume_attached = self.virtual_machine.attach_volume( + self.apiclient, + self.volume + ) + + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, + listall=True) + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine.id, listall=True) + vm_tags = vm[0].tags + + self.vc_policy_tags(volumes, vm_tags, vm) + + + self.assertEqual(volume_attached.id, self.volume.id, "Is not the same volume ") + try: + # Login to VM and write data to file system + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "echo %s > %s/%s" % + (self.random_data_0, self.test_dir, self.random_data), + "sync", + "sleep 1", + "sync", + "sleep 1", + "cat %s/%s" % + (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data has be write into temp file!" + ) + + time.sleep(30) + MemorySnapshot = False + vm_snapshot = VmSnapshot.create( + self.apiclient, + self.virtual_machine.id, + MemorySnapshot, + "TestSnapshot", + "Display Text" + ) + self.assertEqual( + vm_snapshot.state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_04_revert_vm_snapshots_vc_policy_tag(self): + """Test to revert VM snapshots with VC policy tag + """ + + try: + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "rm -rf %s/%s" % (self.test_dir, self.random_data), + "ls %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + if str(result[0]).index("No such file or directory") == -1: + self.fail("Check the random data has be delete from temp file!") + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + + self.assertEqual( + list_snapshot_response[0].state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + self.virtual_machine.stop(self.apiclient, forced=True) + + VmSnapshot.revertToSnapshot( + self.apiclient, + list_snapshot_response[0].id + ) + + self.virtual_machine.start(self.apiclient) + + try: + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "cat %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, listall=True + ) + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine.id, listall=True) + vm_tags = vm[0].tags + + self.vc_policy_tags(volumes, vm_tags, vm) + + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data is equal with the ramdom file!" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_05_delete_vm_snapshots(self): + """Test to delete vm snapshots + """ + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + VmSnapshot.deleteVMSnapshot( + self.apiclient, + list_snapshot_response[0].id) + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + #vmid=self.virtual_machine.id, + virtualmachineid=self.virtual_machine.id, + listall=False) + self.debug('list_snapshot_response -------------------- %s' % list_snapshot_response) + + self.assertIsNone(list_snapshot_response, "snapshot is already deleted") + + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_06_remove_vcpolicy_tag_when_disk_detached(self): + """ Test remove vc-policy tag to disk detached from VM""" + time.sleep(60) + volume_detached = self.virtual_machine.detach_volume( + self.apiclient, + self.volume_2 + ) + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine.id, listall=True) + vm_tags = vm[0].tags + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, listall=True + ) + + self.vc_policy_tags( volumes, vm_tags, vm) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_07_delete_vcpolicy_tag(self): + """ Test delete vc-policy tag of VM""" + Tag.delete(self.apiclient, + resourceIds=self.virtual_machine.id, + resourceType='UserVm', + tags={'vc-policy': 'testing_vc-policy'}) + + volumes = list_volumes( + self.apiclient, + virtualmachineid = self.virtual_machine.id, listall=True + ) + for v in volumes: + name = v.path.split("/")[3] + spvolume = self.spapi.volumeList(volumeName="~" + name) + tags = spvolume[0].tags + for t in tags: + self.assertFalse(t.lower() == 'vc-policy'.lower(), "There is VC Policy tag") + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_08_vcpolicy_tag_to_reverted_disk(self): + tag = Tag.create( + self.apiclient, + resourceIds=self.virtual_machine2.id, + resourceType='UserVm', + tags={'vc-policy': 'testing_vc-policy'} + ) + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine2.id, listall=True) + vm_tags = vm[0].tags + + volume = Volume.list( + self.apiclient, + virtualmachineid = self.virtual_machine2.id, listall=True, + type = "ROOT" + ) + self.vc_policy_tags(volume, vm_tags, vm) + + snapshot = Snapshot.create( + self.apiclient, + volume[0].id, + account=self.account.name, + domainid=self.account.domainid + ) + + virtual_machine = self.virtual_machine2.stop( + self.apiclient, + forced=True + ) + + cmd = revertSnapshot.revertSnapshotCmd() + cmd.id = snapshot.id + revertedn = self.apiclient.revertSnapshot(cmd) + + vm = list_virtual_machines(self.apiclient,id = self.virtual_machine2.id) + vm_tags = vm[0].tags + + vol = list_volumes(self.apiclient, id = snapshot.volumeid, listall=True) + self.vc_policy_tags(vol, vm_tags, vm) + + + def vc_policy_tags(self, volumes, vm_tags, vm): + flag = False + for v in volumes: + name = v.path.split("/")[3] + spvolume = self.spapi.volumeList(volumeName="~" + name) + tags = spvolume[0].tags + for t in tags: + for vm_tag in vm_tags: + if t == vm_tag.key: + flag = True + self.assertEqual(tags[t], vm_tag.value, "Tags are not equal") + if t == 'cvm': + self.assertEqual(tags[t], vm[0].id, "CVM tag is not the same as vm UUID") + #self.assertEqual(tag.tags., second, msg) + self.assertTrue(flag, "There aren't volumes with vm tags") diff --git a/test/integration/plugins/storpool/TestVmSnapshots.py b/test/integration/plugins/storpool/TestVmSnapshots.py new file mode 100644 index 000000000000..b1daa93edad5 --- /dev/null +++ b/test/integration/plugins/storpool/TestVmSnapshots.py @@ -0,0 +1,369 @@ +# 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 +# with 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. + +# Import Local Modules +import random +import time + +from marvin.cloudstackAPI import (listTemplates) +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING +from marvin.lib.base import (Account, + ServiceOffering, + VirtualMachine, + VmSnapshot, + User, + Volume, + SecurityGroup, + ) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_clusters, + list_snapshots, + list_virtual_machines, + list_configurations, + list_disk_offering, + list_accounts, + list_storage_pools, + list_service_offering, + list_zones + ) +from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual, get_hypervisor_type +from nose.plugins.attrib import attr +import uuid +from sp_util import (TestData, StorPoolHelper) + +class TestVmSnapshot(cloudstackTestCase): + @classmethod + def setUpClass(cls): + super(TestVmSnapshot, cls).setUpClass() + try: + cls.setUpCloudStack() + except Exception: + cls.cleanUpCloudStack() + raise + + @classmethod + def setUpCloudStack(cls): + testClient = super(TestVmSnapshot, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls._cleanup = [] + cls.unsupportedHypervisor = False + + # Setup test data + td = TestData() + cls.testdata = td.testdata + cls.helper = StorPoolHelper() + + + cls.services = testClient.getParsedTestDataConfig() + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + cls.zone = None + zones = list_zones(cls.apiclient) + + for z in zones: + if z.name == cls.getClsConfig().mgtSvr[0].zone: + cls.zone = z + + assert cls.zone is not None + + cls.cluster = list_clusters(cls.apiclient)[0] + cls.hypervisor = get_hypervisor_type(cls.apiclient) + + #The version of CentOS has to be supported + template = get_template( + cls.apiclient, + cls.zone.id, + account = "system" + ) + + + if template == FAILED: + assert False, "get_template() failed to return template\ + with description %s" % cls.services["ostype"] + + cls.template = template + + cls.account = cls.helper.create_account( + cls.apiclient, + cls.services["account"], + accounttype = 1, + domainid=cls.domain.id, + roleid = 1 + ) + cls._cleanup.append(cls.account) + + securitygroup = SecurityGroup.list(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid)[0] + cls.helper.set_securityGroups(cls.apiclient, account = cls.account.name, domainid= cls.account.domainid, id = securitygroup.id) + + primarystorage = cls.testdata[TestData.primaryStorage] + + serviceOffering = cls.testdata[TestData.serviceOffering] + storage_pool = list_storage_pools( + cls.apiclient, + name = primarystorage.get("name") + ) + cls.primary_storage = storage_pool[0] + + disk_offering = list_disk_offering( + cls.apiclient, + name="ssd" + ) + + assert disk_offering is not None + + + service_offering_only = list_service_offering( + cls.apiclient, + name="ssd" + ) + if service_offering_only is not None: + cls.service_offering_only = service_offering_only[0] + else: + cls.service_offering_only = ServiceOffering.create( + cls.apiclient, + serviceOffering) + assert cls.service_offering_only is not None + + cls.disk_offering = disk_offering[0] + + # Create 1 data volume_1 + cls.volume = Volume.create( + cls.apiclient, + cls.testdata[TestData.volume_1], + account=cls.account.name, + domainid=cls.domain.id, + zoneid=cls.zone.id, + diskofferingid=cls.disk_offering.id, + size=10 + ) + + cls.virtual_machine = VirtualMachine.create( + cls.apiclient, + {"name":"StorPool-%s" % uuid.uuid4() }, + zoneid=cls.zone.id, + templateid=cls.template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering_only.id, + hypervisor=cls.hypervisor, + rootdisksize=10 + ) + + cls.random_data_0 = random_gen(size=100) + cls.test_dir = "/tmp" + cls.random_data = "random.data" + return + + @classmethod + def tearDownClass(cls): + cls.cleanUpCloudStack() + + @classmethod + def cleanUpCloudStack(cls): + try: + # Cleanup resources used + 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() + + if self.unsupportedHypervisor: + self.skipTest("Skipping test because unsupported hypervisor\ + %s" % self.hypervisor) + return + + def tearDown(self): + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_01_create_vm_snapshots(self): + """Test to create VM snapshots + """ + volume_attached = self.virtual_machine.attach_volume( + self.apiclient, + self.volume + ) + + self.assertEqual(volume_attached.id, self.volume.id, "Is not the same volume ") + try: + # Login to VM and write data to file system + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "echo %s > %s/%s" % + (self.random_data_0, self.test_dir, self.random_data), + "sync", + "sleep 1", + "sync", + "sleep 1", + "cat %s/%s" % + (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data has be write into temp file!" + ) + + time.sleep(30) + MemorySnapshot = False + vm_snapshot = VmSnapshot.create( + self.apiclient, + self.virtual_machine.id, + MemorySnapshot, + "TestSnapshot", + "Display Text" + ) + self.assertEqual( + vm_snapshot.state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_02_revert_vm_snapshots(self): + """Test to revert VM snapshots + """ + + try: + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "rm -rf %s/%s" % (self.test_dir, self.random_data), + "ls %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + if str(result[0]).index("No such file or directory") == -1: + self.fail("Check the random data has be delete from temp file!") + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + + self.assertEqual( + list_snapshot_response[0].state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + self.virtual_machine.stop(self.apiclient, forced=True) + + VmSnapshot.revertToSnapshot( + self.apiclient, + list_snapshot_response[0].id + ) + + self.virtual_machine.start(self.apiclient) + + try: + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "cat %s/%s" % (self.test_dir, self.random_data) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data is equal with the ramdom file!" + ) + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_03_delete_vm_snapshots(self): + """Test to delete vm snapshots + """ + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + VmSnapshot.deleteVMSnapshot( + self.apiclient, + list_snapshot_response[0].id) + + time.sleep(30) + + list_snapshot_response = VmSnapshot.list( + self.apiclient, + #vmid=self.virtual_machine.id, + virtualmachineid=self.virtual_machine.id, + listall=False) + self.debug('list_snapshot_response -------------------- %s' % list_snapshot_response) + + self.assertIsNone(list_snapshot_response, "snapshot is already deleted") diff --git a/test/integration/plugins/storpool/sp_util.py b/test/integration/plugins/storpool/sp_util.py new file mode 100644 index 000000000000..8735fbd8c25d --- /dev/null +++ b/test/integration/plugins/storpool/sp_util.py @@ -0,0 +1,732 @@ +from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.utils import random_gen, cleanup_resources, validateList, is_snapshot_on_nfs, isAlmostEqual +from marvin.lib.base import (Account, + Cluster, + Configurations, + ServiceOffering, + Snapshot, + StoragePool, + Template, + VirtualMachine, + VmSnapshot, + Volume) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_disk_offering, + list_hosts, + list_snapshots, + list_storage_pools, + list_volumes, + list_virtual_machines, + list_configurations, + list_service_offering, + list_clusters, + list_zones) +from marvin.cloudstackAPI import (listOsTypes, + listTemplates, + listHosts, + createTemplate, + createVolume, + getVolumeSnapshotDetails, + resizeVolume, + authorizeSecurityGroupIngress, + migrateVirtualMachineWithVolume, + destroyVirtualMachine, + deployVirtualMachine, + createAccount, + startVirtualMachine, + ) +import time +import pprint +import random +from marvin.configGenerator import configuration +import uuid +import logging +import subprocess +import json +from storpool import spapi +from storpool import sptypes + +class TestData(): + account = "account" + capacityBytes = "capacitybytes" + capacityIops = "capacityiops" + clusterId = "clusterId" + diskName = "diskname" + diskOffering = "diskoffering" + diskOffering2 = "diskoffering2" + cephDiskOffering = "cephDiskOffering" + nfsDiskOffering = "nfsDiskOffering" + domainId = "domainId" + hypervisor = "hypervisor" + login = "login" + mvip = "mvip" + password = "password" + port = "port" + primaryStorage = "primarystorage" + primaryStorage2 = "primarystorage2" + primaryStorage3 = "primarystorage3" + primaryStorage4 = "primaryStorage4" + provider = "provider" + serviceOffering = "serviceOffering" + serviceOfferingssd2 = "serviceOffering-ssd2" + serviceOfferingsPrimary = "serviceOfferingsPrimary" + serviceOfferingsIops = "serviceOfferingsIops" + serviceOfferingsCeph = "serviceOfferingsCeph" + scope = "scope" + StorPool = "StorPool" + storageTag = ["ssd", "ssd2"] + tags = "tags" + virtualMachine = "virtualmachine" + virtualMachine2 = "virtualmachine2" + volume_1 = "volume_1" + volume_2 = "volume_2" + volume_3 = "volume_3" + volume_4 = "volume_4" + volume_5 = "volume_5" + volume_6 = "volume_6" + volume_7 = "volume_7" + zoneId = "zoneId" + + def __init__(self): + sp_template_1 = 'ssd' + sp_template_2 = 'ssd2' + sp_template_3 = 'test-primary' + self.testdata = { + TestData.primaryStorage: { + "name": sp_template_1, + TestData.scope: "ZONE", + "url": sp_template_1, + TestData.provider: "StorPool", + "path": "/dev/storpool", + TestData.capacityBytes: 2251799813685248, + TestData.hypervisor: "KVM" + }, + TestData.primaryStorage2: { + "name": sp_template_2, + TestData.scope: "ZONE", + "url": sp_template_2, + TestData.provider: "StorPool", + "path": "/dev/storpool", + TestData.capacityBytes: 2251799813685248, + TestData.hypervisor: "KVM" + }, + TestData.primaryStorage3: { + "name": sp_template_3, + TestData.scope: "ZONE", + "url": sp_template_3, + TestData.provider: "StorPool", + "path": "/dev/storpool", + TestData.capacityBytes: 2251799813685248, + TestData.hypervisor: "KVM" + }, + TestData.primaryStorage4: { + "name": "ceph", + TestData.scope: "ZONE", + TestData.provider: "RBD", + TestData.hypervisor: "KVM" + }, + TestData.virtualMachine: { + "name": "TestVM", + "displayname": "TestVM", + "privateport": 22, + "publicport": 22, + "protocol": "tcp" + }, + TestData.virtualMachine2: { + "name": "TestVM2", + "displayname": "TestVM2", + "privateport": 22, + "publicport": 22, + "protocol": "tcp" + }, + TestData.serviceOffering:{ + "name": sp_template_1, + "displaytext": "SP_CO_2 (Min IOPS = 10,000; Max IOPS = 15,000)", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "hypervisorsnapshotreserve": 200, + "tags": sp_template_1 + }, + TestData.serviceOfferingssd2:{ + "name": sp_template_2, + "displaytext": "SP_CO_2 (Min IOPS = 10,000; Max IOPS = 15,000)", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "hypervisorsnapshotreserve": 200, + "tags": sp_template_2 + }, + TestData.serviceOfferingsPrimary:{ + "name": "nfs", + "displaytext": "SP_CO_2 (Min IOPS = 10,000; Max IOPS = 15,000)", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "hypervisorsnapshotreserve": 200, + "tags": "nfs" + }, + TestData.serviceOfferingsCeph:{ + "name": "ceph", + "displaytext": "Ceph Service offerings", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": False, + "hypervisorsnapshotreserve": 200, + "tags": "ceph" + }, + TestData.serviceOfferingsIops:{ + "name": "iops", + "displaytext": "Testing IOPS on StorPool", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 512, + "storagetype": "shared", + "customizediops": True, + "tags": sp_template_1, + }, + TestData.diskOffering: { + "name": "SP_DO_1", + "displaytext": "SP_DO_1 (5GB Min IOPS = 300; Max IOPS = 500)", + "disksize": 5, + "customizediops": False, + "miniops": 300, + "maxiops": 500, + "hypervisorsnapshotreserve": 200, + TestData.tags: sp_template_1, + "storagetype": "shared" + }, + TestData.diskOffering2: { + "name": "SP_DO_1", + "displaytext": "SP_DO_1 (5GB Min IOPS = 300; Max IOPS = 500)", + "disksize": 5, + "customizediops": False, + "miniops": 300, + "maxiops": 500, + "hypervisorsnapshotreserve": 200, + TestData.tags: sp_template_2, + "storagetype": "shared" + }, + TestData.cephDiskOffering: { + "name": "ceph", + "displaytext": "Ceph fixed disk offering", + "disksize": 5, + "customizediops": False, + "miniops": 300, + "maxiops": 500, + "hypervisorsnapshotreserve": 200, + TestData.tags: "ceph", + "storagetype": "shared" + }, + TestData.nfsDiskOffering: { + "name": "nfs", + "displaytext": "NFS fixed disk offering", + "disksize": 5, + "customizediops": False, + "miniops": 300, + "maxiops": 500, + "hypervisorsnapshotreserve": 200, + TestData.tags: "nfs", + "storagetype": "shared" + }, + TestData.volume_1: { + TestData.diskName: "test-volume-1", + }, + TestData.volume_2: { + TestData.diskName: "test-volume-2", + }, + TestData.volume_3: { + TestData.diskName: "test-volume-3", + }, + TestData.volume_4: { + TestData.diskName: "test-volume-4", + }, + TestData.volume_5: { + TestData.diskName: "test-volume-5", + }, + TestData.volume_6: { + TestData.diskName: "test-volume-6", + }, + TestData.volume_7: { + TestData.diskName: "test-volume-7", + }, + } +class StorPoolHelper(): + + @classmethod + def create_template_from_snapshot(self, apiclient, services, snapshotid=None, volumeid=None): + """Create template from Volume""" + # Create template from Virtual machine and Volume ID + cmd = createTemplate.createTemplateCmd() + cmd.displaytext = "StorPool_Template" + cmd.name = "-".join(["StorPool-", random_gen()]) + if "ostypeid" in services: + cmd.ostypeid = services["ostypeid"] + elif "ostype" in services: + # Find OSTypeId from Os type + sub_cmd = listOsTypes.listOsTypesCmd() + sub_cmd.description = services["ostype"] + ostypes = apiclient.listOsTypes(sub_cmd) + + if not isinstance(ostypes, list): + raise Exception( + "Unable to find Ostype id with desc: %s" % + services["ostype"]) + cmd.ostypeid = ostypes[0].id + else: + raise Exception( + "Unable to find Ostype is required for creating template") + + cmd.isfeatured = True + cmd.ispublic = True + cmd.isextractable = False + + if snapshotid: + cmd.snapshotid = snapshotid + if volumeid: + cmd.volumeid = volumeid + return Template(apiclient.createTemplate(cmd).__dict__) + + @classmethod + def getCfgFromUrl(self, url): + cfg = dict([ + option.split('=') + for option in url.split(';') + ]) + host, port = cfg['SP_API_HTTP'].split(':') + auth = cfg['SP_AUTH_TOKEN'] + return host, int(port), auth + + @classmethod + def get_remote_storpool_cluster(cls): + logging.debug("######################## get_remote_storpool_cluster") + + storpool_clusterid = subprocess.check_output(['storpool_confshow', 'CLUSTER_ID']).strip() + clusterid = storpool_clusterid.split("=")[1].split(".")[1] + logging.debug("######################## %s" % storpool_clusterid) + cmd = ["storpool", "-j", "cluster", "list"] + proc = subprocess.Popen(cmd,stdout=subprocess.PIPE).stdout.read() + csl = json.loads(proc) + logging.debug("######################## %s" % csl) + + clusters = csl.get("data").get("clusters") + logging.debug("######################## %s" % clusters) + + for c in clusters: + c_id = c.get("id") + if c_id != clusterid: + return c.get("name") + + @classmethod + def get_local_cluster(cls, apiclient, zoneid): + storpool_clusterid = subprocess.check_output(['storpool_confshow', 'CLUSTER_ID']) + clusterid = storpool_clusterid.split("=") + logging.debug(storpool_clusterid) + clusters = list_clusters(apiclient, zoneid = zoneid) + for c in clusters: + configuration = list_configurations( + apiclient, + clusterid = c.id + ) + for conf in configuration: + if conf.name == 'sp.cluster.id' and (conf.value in clusterid[1]): + return c + + @classmethod + def get_remote_cluster(cls, apiclient, zoneid): + storpool_clusterid = subprocess.check_output(['storpool_confshow', 'CLUSTER_ID']) + clusterid = storpool_clusterid.split("=") + logging.debug(storpool_clusterid) + clusters = list_clusters(apiclient, zoneid = zoneid) + for c in clusters: + configuration = list_configurations( + apiclient, + clusterid = c.id + ) + for conf in configuration: + if conf.name == 'sp.cluster.id' and (conf.value not in clusterid[1]): + return c + + @classmethod + def get_snapshot_template_id(self, apiclient, snapshot, storage_pool_id): + try: + cmd = getVolumeSnapshotDetails.getVolumeSnapshotDetailsCmd() + cmd.snapshotid = snapshot.id + snapshot_details = apiclient.getVolumeSnapshotDetails(cmd) + logging.debug("Snapshot details %s" % snapshot_details) + logging.debug("Snapshot with uuid %s" % snapshot.id) + for s in snapshot_details: + if s["snapshotDetailsName"] == storage_pool_id: + return s["snapshotDetailsValue"] + except Exception as err: + raise Exception(err) + + return None + + @classmethod + def getDestinationHost(self, hostsToavoid, hosts): + destinationHost = None + for host in hosts: + if host.id not in hostsToavoid: + destinationHost = host + break + return destinationHost + + + @classmethod + def getDestinationPool(self, + poolsToavoid, + migrateto, + pools + ): + """ Get destination pool which has scope same as migrateto + and which is not in avoid set + """ + + destinationPool = None + + # Get Storage Pool Id to migrate to + for storagePool in pools: + if storagePool.scope == migrateto: + if storagePool.name not in poolsToavoid: + destinationPool = storagePool + break + + return destinationPool + + @classmethod + def get_destination_pools_hosts(self, apiclient, vm, hosts): + vol_list = list_volumes( + apiclient, + virtualmachineid=vm.id, + listall=True) + # Get destination host + destinationHost = self.getDestinationHost(vm.hostid, hosts) + return destinationHost, vol_list + + @classmethod + def list_hosts_by_cluster_id(cls, apiclient, clusterid): + """List all Hosts matching criteria""" + cmd = listHosts.listHostsCmd() + cmd.clusterid = clusterid + return(apiclient.listHosts(cmd)) + + @classmethod + def set_securityGroups(cls, apiclient, account, domainid, id): + cmd = authorizeSecurityGroupIngress.authorizeSecurityGroupIngressCmd() + cmd.protocol = 'TCP' + cmd.startport = 22 + cmd.endport = 22 + cmd.cidrlist = '0.0.0.0/0' + cmd.securitygroupid = id + cmd.account = account + cmd.domainid = domainid + + apiclient.authorizeSecurityGroupIngress(cmd) + + cmd.protocol = 'ICMP' + cmd.icmptype = "-1" + cmd.icmpcode = "-1" + # Authorize to only account not CIDR + cmd.securitygroupid = id + cmd.account = account + cmd.domainid = domainid + apiclient.authorizeSecurityGroupIngress(cmd) + + @classmethod + def migrateVm(self, apiclient, vm, destinationHost): + """ + This method is to migrate a VM using migrate virtual machine API + """ + + vm.migrate( + apiclient, + hostid=destinationHost.id, + ) + vm.getState( + apiclient, + "Running" + ) + # check for the VM's host and volume's storage post migration + migrated_vm_response = list_virtual_machines(apiclient, id=vm.id) + assert isinstance(migrated_vm_response, list), "Check list virtual machines response for valid list" + + assert migrated_vm_response[0].hostid == destinationHost.id, "VM did not migrate to a specified host" + return migrated_vm_response[0] + + @classmethod + def migrateVmWithVolumes(self, apiclient, vm, destinationHost, volumes, pool): + """ + This method is used to migrate a vm and its volumes using migrate virtual machine with volume API + INPUTS: + 1. vm -> virtual machine object + 2. destinationHost -> the host to which VM will be migrated + 3. volumes -> list of volumes which are to be migrated + 4. pools -> list of destination pools + """ + vol_pool_map = {vol.id: pool.id for vol in volumes} + + cmd = migrateVirtualMachineWithVolume.migrateVirtualMachineWithVolumeCmd() + cmd.hostid = destinationHost.id + cmd.migrateto = [] + cmd.virtualmachineid = self.virtual_machine.id + for volume, pool1 in vol_pool_map.items(): + cmd.migrateto.append({ + 'volume': volume, + 'pool': pool1 + }) + apiclient.migrateVirtualMachineWithVolume(cmd) + + vm.getState( + apiclient, + "Running" + ) + # check for the VM's host and volume's storage post migration + migrated_vm_response = list_virtual_machines(apiclient, id=vm.id) + assert isinstance(migrated_vm_response, list), "Check list virtual machines response for valid list" + + assert migrated_vm_response[0].hostid == destinationHost.id, "VM did not migrate to a specified host" + + for vol in volumes: + migrated_volume_response = list_volumes( + apiclient, + virtualmachineid=migrated_vm_response[0].id, + name=vol.name, + listall=True) + assert isinstance(migrated_volume_response, list), "Check list virtual machines response for valid list" + assert migrated_volume_response[0].storageid == pool.id, "Volume did not migrate to a specified pool" + + assert str(migrated_volume_response[0].state).lower().eq('ready'), "Check migrated volume is in Ready state" + + return migrated_vm_response[0] + + @classmethod + def create_sp_template_and_storage_pool(self, apiclient, template_name, primary_storage, zoneid): + spapiRemote = spapi.Api.fromConfig() + logging.debug("================ %s" % spapiRemote) + + sp_api = spapi.Api.fromConfig(multiCluster= True) + logging.debug("================ %s" % sp_api) + + remote_cluster = self.get_remote_storpool_cluster() + logging.debug("================ %s" % remote_cluster) + + newTemplate = sptypes.VolumeTemplateCreateDesc(name = template_name, placeAll = "ssd", placeTail = "ssd", placeHead = "ssd", replication=1) + template_on_remote = spapiRemote.volumeTemplateCreate(newTemplate, clusterName = remote_cluster) + template_on_local = spapiRemote.volumeTemplateCreate(newTemplate) + storage_pool = StoragePool.create(apiclient, primary_storage, zoneid = zoneid,) + return storage_pool, spapiRemote, sp_api + + @classmethod + def destroy_vm(self, apiclient, virtualmachineid): + cmd = destroyVirtualMachine.destroyVirtualMachineCmd() + cmd.id = virtualmachineid + cmd.expunge = True + apiclient.destroyVirtualMachine(cmd) + + @classmethod + def check_storpool_volume_size(cls, volume, spapi): + name = volume.path.split("/")[3] + try: + spvolume = spapi.volumeList(volumeName = "~" + name) + if spvolume[0].size != volume.size: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + @classmethod + def check_storpool_volume_iops(cls, spapi, volume,): + name = volume.path.split("/")[3] + try: + spvolume = spapi.volumeList(volumeName = "~" + name) + logging.debug(spvolume[0].iops) + logging.debug(volume.maxiops) + if spvolume[0].iops != volume.maxiops: + raise Exception("Storpool volume size is not the same as CloudStack db size") + except spapi.ApiError as err: + raise Exception(err) + + @classmethod + def create_custom_disk(cls, apiclient, services, size = None, miniops = None, maxiops =None, diskofferingid=None, zoneid=None, account=None, domainid=None, snapshotid=None): + """Create Volume from Custom disk offering""" + cmd = createVolume.createVolumeCmd() + cmd.name = services["diskname"] + + if diskofferingid: + cmd.diskofferingid = diskofferingid + + if size: + cmd.size = size + + if miniops: + cmd.miniops = miniops + + if maxiops: + cmd.maxiops = maxiops + + if account: + cmd.account = account + + if domainid: + cmd.domainid = domainid + + if snapshotid: + cmd.snapshotid = snapshotid + + cmd.zoneid = zoneid + + return Volume(apiclient.createVolume(cmd).__dict__) + + @classmethod + def create_vm_custom(cls, apiclient, services, templateid=None, zoneid=None, + serviceofferingid=None, method='GET', hypervisor=None, + cpuNumber=None, cpuSpeed=None, memory=None, minIops=None, + maxIops=None, hostid=None, rootdisksize=None, account=None, domainid=None + ): + """Create the instance""" + + cmd = deployVirtualMachine.deployVirtualMachineCmd() + + if serviceofferingid: + cmd.serviceofferingid = serviceofferingid + elif "serviceoffering" in services: + cmd.serviceofferingid = services["serviceoffering"] + + if zoneid: + cmd.zoneid = zoneid + elif "zoneid" in services: + cmd.zoneid = services["zoneid"] + + if hypervisor: + cmd.hypervisor = hypervisor + + if hostid: + cmd.hostid = hostid + + if "displayname" in services: + cmd.displayname = services["displayname"] + + if "name" in services: + cmd.name = services["name"] + + if templateid: + cmd.templateid = templateid + elif "template" in services: + cmd.templateid = services["template"] + + cmd.details = [{}] + + if cpuNumber: + cmd.details[0]["cpuNumber"] = cpuNumber + + if cpuSpeed: + cmd.details[0]["cpuSpeed"] = cpuSpeed + + if memory: + cmd.details[0]["memory"] = memory + + if minIops: + cmd.details[0]["minIops"] = minIops + + if maxIops: + cmd.details[0]["maxIops"] = maxIops + + if rootdisksize >= 0: + cmd.details[0]["rootdisksize"] = rootdisksize + + if account: + cmd.account = account + + if domainid: + cmd.domainid = domainid + + virtual_machine = apiclient.deployVirtualMachine(cmd, method=method) + + return VirtualMachine(virtual_machine.__dict__, services) + + + @classmethod + def resize_volume(cls, apiclient, volume, shrinkOk=None, disk_offering =None, size=None, maxiops=None, miniops=None): + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = volume.id + + if disk_offering: + cmd.diskofferingid = disk_offering.id + if size: + cmd.size = size + + if maxiops: + cmd.maxiops = maxiops + if miniops: + cmd.miniops + + cmd.shrinkok = shrinkOk + apiclient.resizeVolume(cmd) + + new_size = Volume.list( + apiclient, + id=volume.id + ) + volume_size = new_size[0].size + return new_size[0] + + @classmethod + def create_account(cls, apiclient, services, accounttype=None, domainid=None, roleid=None): + """Creates an account""" + cmd = createAccount.createAccountCmd() + + # 0 - User, 1 - Root Admin, 2 - Domain Admin + if accounttype: + cmd.accounttype = accounttype + else: + cmd.accounttype = 1 + + cmd.email = services["email"] + cmd.firstname = services["firstname"] + cmd.lastname = services["lastname"] + + cmd.password = services["password"] + username = services["username"] + # Limit account username to 99 chars to avoid failure + # 6 chars start string + 85 chars apiclientid + 6 chars random string + 2 chars joining hyphen string = 99 + username = username[:6] + apiclientid = apiclient.id[-85:] if len(apiclient.id) > 85 else apiclient.id + cmd.username = "-".join([username, + random_gen(id=apiclientid, size=6)]) + + if "accountUUID" in services: + cmd.accountid = "-".join([services["accountUUID"], random_gen()]) + + if "userUUID" in services: + cmd.userid = "-".join([services["userUUID"], random_gen()]) + + if domainid: + cmd.domainid = domainid + + if roleid: + cmd.roleid = roleid + + account = apiclient.createAccount(cmd) + + return Account(account.__dict__) + + def start(cls, apiclient, vmid, hostid): + """Start the instance""" + cmd = startVirtualMachine.startVirtualMachineCmd() + cmd.id = vmid + cmd.hostid = hostid + return (apiclient.startVirtualMachine(cmd)) + From 40aa59f9969e232c10ace53a88c7b46ed1afb3d0 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Tue, 12 Apr 2022 14:17:22 +0300 Subject: [PATCH 09/12] Fixed conflicts --- .../storage/datastore/provider/StorPoolHostListener.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java index df188ff69c2c..4a5ce4012d77 100644 --- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java +++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/provider/StorPoolHostListener.java @@ -226,4 +226,9 @@ private void addModifyCommandToCommandsAllowedInMaintenanceMode() { log.warn(String.format(err, e.getMessage())); } } + + @Override + public boolean hostEnabled(long hostId) { + return true; + } } From e6cda26f5ed7850b4b590ac454cf1dedf139d11e Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Tue, 12 Apr 2022 14:51:42 +0300 Subject: [PATCH 10/12] Fixed conflicts and added missed Apache license header --- .../kvm/storage/StorPoolStorageAdaptor.java | 5 +++++ test/integration/plugins/storpool/sp_util.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index 95f41322d745..0373e3bdf825 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -380,4 +380,9 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP KVMStoragePool destPool, ImageFormat format, int timeout) { return null; } + + @Override + public boolean createFolder(String uuid, String path, String localPath) { + return false; + } } diff --git a/test/integration/plugins/storpool/sp_util.py b/test/integration/plugins/storpool/sp_util.py index 8735fbd8c25d..cf49e1cba17c 100644 --- a/test/integration/plugins/storpool/sp_util.py +++ b/test/integration/plugins/storpool/sp_util.py @@ -1,3 +1,19 @@ +# 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 +# with 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. from marvin.codes import FAILED, KVM, PASS, XEN_SERVER, RUNNING from nose.plugins.attrib import attr from marvin.cloudstackTestCase import cloudstackTestCase From 7906a56fe8a7aa9cc691a199bb6b6658063d28ab Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Tue, 12 Apr 2022 22:00:02 +0300 Subject: [PATCH 11/12] Removed whitespaces in smoke tests --- test/integration/plugins/storpool/TestStorPoolVolumes.py | 8 ++++---- test/integration/plugins/storpool/TestTagsOnStorPool.py | 2 +- test/integration/plugins/storpool/sp_util.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/plugins/storpool/TestStorPoolVolumes.py b/test/integration/plugins/storpool/TestStorPoolVolumes.py index bbb8a3ffbdae..4560038d2689 100644 --- a/test/integration/plugins/storpool/TestStorPoolVolumes.py +++ b/test/integration/plugins/storpool/TestStorPoolVolumes.py @@ -892,7 +892,7 @@ def test_08_delete_snapshot_of_deleted_volume(self): if flag == False: raise Exception("Could not find snapshot in snapshot details") except Exception as err: - raise Exception(err) + raise Exception(err) template = self.create_template_from_snapshot(self.apiclient, self.services, snapshotid = snapshot.id) @@ -1000,7 +1000,7 @@ def test_09_vm_from_bypassed_template(self): backup_config = Configurations.update(self.apiclient, name = "sp.bypass.secondary.storage", - value = "false") + value = "false") vm = VirtualMachine.create( self.apiclient, @@ -1780,7 +1780,7 @@ def test_21_snapshot_detached_volume(self): @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") def test_22_snapshot_root_disk(self): - ''' Test ROOT Disk Snapshot + ''' Test ROOT Disk Snapshot ''' vm = VirtualMachine.create(self.apiclient, {"name":"StorPool-%s" % uuid.uuid4() }, @@ -2094,7 +2094,7 @@ def get_remote_cluster(cls): def list_hosts_by_cluster_id(cls, clusterid): """List all Hosts matching criteria""" cmd = listHosts.listHostsCmd() - cmd.clusterid = clusterid + cmd.clusterid = clusterid return(cls.apiclient.listHosts(cmd)) diff --git a/test/integration/plugins/storpool/TestTagsOnStorPool.py b/test/integration/plugins/storpool/TestTagsOnStorPool.py index a3a6cced58b7..f37e4cfd5323 100644 --- a/test/integration/plugins/storpool/TestTagsOnStorPool.py +++ b/test/integration/plugins/storpool/TestTagsOnStorPool.py @@ -537,7 +537,7 @@ def test_08_vcpolicy_tag_to_reverted_disk(self): self.vc_policy_tags(volume, vm_tags, vm) snapshot = Snapshot.create( - self.apiclient, + self.apiclient, volume[0].id, account=self.account.name, domainid=self.account.domainid diff --git a/test/integration/plugins/storpool/sp_util.py b/test/integration/plugins/storpool/sp_util.py index cf49e1cba17c..101605072316 100644 --- a/test/integration/plugins/storpool/sp_util.py +++ b/test/integration/plugins/storpool/sp_util.py @@ -606,7 +606,7 @@ def create_custom_disk(cls, apiclient, services, size = None, miniops = None, ma return Volume(apiclient.createVolume(cmd).__dict__) @classmethod - def create_vm_custom(cls, apiclient, services, templateid=None, zoneid=None, + def create_vm_custom(cls, apiclient, services, templateid=None, zoneid=None, serviceofferingid=None, method='GET', hypervisor=None, cpuNumber=None, cpuSpeed=None, memory=None, minIops=None, maxIops=None, hostid=None, rootdisksize=None, account=None, domainid=None From f17253e20cc7b660ef0b18f3184ad4c2aab01c40 Mon Sep 17 00:00:00 2001 From: Slavka Peleva Date: Wed, 13 Apr 2022 15:15:37 +0300 Subject: [PATCH 12/12] Added StorPool plugin jar for Debian the StorPool jar will be included into cloudstack-agent package for Debian/Ubuntu --- debian/rules | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/rules b/debian/rules index ed1559a46dfe..b590e5d40fdc 100755 --- a/debian/rules +++ b/debian/rules @@ -41,6 +41,7 @@ override_dh_auto_install: mkdir $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib install -D plugins/hypervisors/kvm/target/cloud-plugin-hypervisor-kvm-$(VERSION).jar $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -D plugins/hypervisors/kvm/target/dependencies/* $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ + install -D plugins/storage/volume/storpool/target/cloud-plugin-storage-volume-storpool-$(VERSION).jar $(DESTDIR)/usr/share/$(PACKAGE)-agent/lib/ install -d -m0755 debian/$(PACKAGE)-agent/lib/systemd/system install -m0644 packaging/systemd/$(PACKAGE)-agent.service debian/$(PACKAGE)-agent/lib/systemd/system/$(PACKAGE)-agent.service