From 8ea9fc911d14faaf1d5480e71e5a1c42fbbbf18c Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 5 Feb 2024 13:27:15 +0530 Subject: [PATCH] StoragePoolType as class (#8544) * StoragePoolType as a class * Fix agent side StoragePoolType enum to class * Handle StoragePoolType for StoragePoolJoinVO * Since StoragePoolType is a class, it cannot be converted by @Enumerated annotation. Implemented conveter class and logic to utilize @Convert annotation. * Fix UserVMJoinVO for StoragePoolType * fixed missing imports * Since StoragePoolType is a class, it cannot be converted by @Enumerated annotation. Implemented conveter class and logic to utilize @Convert annotation. * Fixed equals for the enum. * removed not needed try/catch for prepareAttribute * Added license to the file. * Implemented "supportsPhysicalDiskCopy" for storage adaptor. Co-authored-by: mprokopchuk * Add javadoc to StoragePoolType class * Add unit test for StoragePoolType comparisons * StoragePoolType "==" and ".equals()" fix. * Fix StoragePoolType for FiberChannelAdapter * Fix for abstract storage adaptor set up issue * review comments * Pass StoragePoolType object for poolType dao attribute --------- Co-authored-by: Marcus Sorensen Co-authored-by: mprokopchuk Co-authored-by: mprokopchuk --- .../main/java/com/cloud/storage/Storage.java | 131 ++++++++++++++---- .../java/com/cloud/storage/StorageTest.java | 9 ++ .../transport/StoragePoolTypeAdaptor.java | 53 +++++++ .../java/com/cloud/serializer/GsonHelper.java | 3 + .../cloud/agent/transport/RequestTest.java | 4 +- .../entity/api/db/EngineHostVO.java | 3 + .../src/main/java/com/cloud/host/HostVO.java | 3 + .../main/java/com/cloud/storage/VolumeVO.java | 8 +- .../cloud/util/StoragePoolTypeConverter.java | 40 ++++++ .../datastore/db/PrimaryDataStoreDao.java | 3 +- .../datastore/db/PrimaryDataStoreDaoImpl.java | 3 +- .../storage/datastore/db/StoragePoolVO.java | 10 +- .../storage/volume/VolumeServiceTest.java | 5 +- .../com/cloud/utils/db/GenericDaoBase.java | 61 +++++++- .../kvm/storage/FiberChannelAdapter.java | 6 +- .../kvm/storage/IscsiAdmStorageAdaptor.java | 6 +- .../kvm/storage/KVMStoragePoolManager.java | 59 +++++--- .../kvm/storage/KVMStorageProcessor.java | 2 +- .../kvm/storage/LibvirtStorageAdaptor.java | 39 +++--- .../kvm/storage/ManagedNfsStorageAdaptor.java | 3 + .../kvm/storage/ScaleIOStorageAdaptor.java | 12 +- .../kvm/storage/StorageAdaptor.java | 9 ++ .../kvm/storage/StorageAdaptorInfo.java | 3 - .../kvm/storage/ScaleIOStoragePoolTest.java | 7 +- .../cloud/simulator/MockStoragePoolVO.java | 6 +- .../simulator/dao/MockStoragePoolDaoImpl.java | 2 +- ...oudStackPrimaryDataStoreLifeCycleImpl.java | 2 +- .../kvm/storage/LinstorStorageAdaptor.java | 6 +- .../kvm/storage/StorPoolStorageAdaptor.java | 6 +- .../api/query/dao/StoragePoolJoinDaoImpl.java | 42 +++--- .../cloud/api/query/vo/StoragePoolJoinVO.java | 4 +- .../com/cloud/api/query/vo/UserVmJoinVO.java | 4 +- .../com/cloud/storage/StorageManagerImpl.java | 6 +- .../listener/StoragePoolMonitorTest.java | 2 + 34 files changed, 440 insertions(+), 122 deletions(-) create mode 100644 core/src/main/java/com/cloud/agent/transport/StoragePoolTypeAdaptor.java create mode 100644 engine/schema/src/main/java/com/cloud/util/StoragePoolTypeConverter.java diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index 8a2ec1a8905..2ef424adcb0 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -16,10 +16,14 @@ // under the License. package com.cloud.storage; -import org.apache.commons.lang.NotImplementedException; - import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.lang.NotImplementedException; +import org.apache.commons.lang3.StringUtils; public class Storage { public static enum ImageFormat { @@ -135,37 +139,72 @@ public class Storage { ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */ } - public static enum StoragePoolType { - Filesystem(false, true, true), // local directory - NetworkFilesystem(true, true, true), // NFS - IscsiLUN(true, false, false), // shared LUN, with a clusterfs overlay - Iscsi(true, false, false), // for e.g., ZFS Comstar - ISO(false, false, false), // for iso image - LVM(false, false, false), // XenServer local LVM SR - CLVM(true, false, false), - RBD(true, true, false), // http://libvirt.org/storage.html#StorageBackendRBD - SharedMountPoint(true, false, true), - VMFS(true, true, false), // VMware VMFS storage - PreSetup(true, true, false), // for XenServer, Storage Pool is set up by customers. - EXT(false, true, false), // XenServer local EXT SR - OCFS2(true, false, false), - SMB(true, false, false), - Gluster(true, false, false), - PowerFlex(true, true, true), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS) - ManagedNFS(true, false, false), - Linstor(true, true, false), - DatastoreCluster(true, true, false), // for VMware, to abstract pool of clusters - StorPool(true, true, true), - FiberChannel(true, true, false); // Fiber Channel Pool for KVM hypervisors is used to find the volume by WWN value (/dev/disk/by-id/wwn-) + /** + * StoragePoolTypes carry some details about the format and capabilities of a storage pool. While not necessarily a + * 1:1 with PrimaryDataStoreDriver (and for KVM agent, KVMStoragePool and StorageAdaptor) implementations, it is + * often used to decide which storage plugin or storage command to call, so it may be necessary for new storage + * plugins to add a StoragePoolType. This can be done by adding it below, or by creating a new public static final + * instance of StoragePoolType in the plugin itself, which registers it with the map. + * + * Note that if the StoragePoolType is for KVM and defined in plugin code rather than below, care must be taken to + * ensure this is available on the agent side as well. This is best done by defining the StoragePoolType in a common + * package available on both management server and agent plugin jars. + */ + public static class StoragePoolType { + private static final Map map = new LinkedHashMap<>(); + public static final StoragePoolType Filesystem = new StoragePoolType("Filesystem", false, true, true); + public static final StoragePoolType NetworkFilesystem = new StoragePoolType("NetworkFilesystem", true, true, true); + public static final StoragePoolType IscsiLUN = new StoragePoolType("IscsiLUN", true, false, false); + public static final StoragePoolType Iscsi = new StoragePoolType("Iscsi", true, false, false); + public static final StoragePoolType ISO = new StoragePoolType("ISO", false, false, false); + public static final StoragePoolType LVM = new StoragePoolType("LVM", false, false, false); + public static final StoragePoolType CLVM = new StoragePoolType("CLVM", true, false, false); + public static final StoragePoolType RBD = new StoragePoolType("RBD", true, true, false); + public static final StoragePoolType SharedMountPoint = new StoragePoolType("SharedMountPoint", true, false, true); + public static final StoragePoolType VMFS = new StoragePoolType("VMFS", true, true, false); + public static final StoragePoolType PreSetup = new StoragePoolType("PreSetup", true, true, false); + public static final StoragePoolType EXT = new StoragePoolType("EXT", false, true, false); + public static final StoragePoolType OCFS2 = new StoragePoolType("OCFS2", true, false, false); + public static final StoragePoolType SMB = new StoragePoolType("SMB", true, false, false); + public static final StoragePoolType Gluster = new StoragePoolType("Gluster", true, false, false); + public static final StoragePoolType PowerFlex = new StoragePoolType("PowerFlex", true, true, true); + public static final StoragePoolType ManagedNFS = new StoragePoolType("ManagedNFS", true, false, false); + public static final StoragePoolType Linstor = new StoragePoolType("Linstor", true, true, false); + public static final StoragePoolType DatastoreCluster = new StoragePoolType("DatastoreCluster", true, true, false); + public static final StoragePoolType StorPool = new StoragePoolType("StorPool", true,true,true); + public static final StoragePoolType FiberChannel = new StoragePoolType("FiberChannel", true,true,false); + + + private final String name; private final boolean shared; private final boolean overprovisioning; private final boolean encryption; - StoragePoolType(boolean shared, boolean overprovisioning, boolean encryption) { + /** + * New StoragePoolType, set the name to check with it in Dao (Note: Do not register it into the map of pool types). + * @param name name of the StoragePoolType. + */ + public StoragePoolType(String name) { + this.name = name; + this.shared = false; + this.overprovisioning = false; + this.encryption = false; + } + + /** + * Define a new StoragePoolType, and register it into the map of pool types known to the management server. + * @param name Simple unique name of the StoragePoolType. + * @param shared Storage pool is shared/accessible to multiple hypervisors + * @param overprovisioning Storage pool supports overprovisioning + * @param encryption Storage pool supports encrypted volumes + */ + public StoragePoolType(String name, boolean shared, boolean overprovisioning, boolean encryption) { + this.name = name; this.shared = shared; this.overprovisioning = overprovisioning; this.encryption = encryption; + addStoragePoolType(this); } public boolean isShared() { @@ -177,6 +216,48 @@ public class Storage { } public boolean supportsEncryption() { return encryption; } + + private static void addStoragePoolType(StoragePoolType storagePoolType) { + map.putIfAbsent(storagePoolType.name, storagePoolType); + } + + public static StoragePoolType[] values() { + return map.values().toArray(StoragePoolType[]::new).clone(); + } + + public static StoragePoolType valueOf(String name) { + if (StringUtils.isBlank(name)) { + return null; + } + + StoragePoolType storage = map.get(name); + if (storage == null) { + throw new IllegalArgumentException("StoragePoolType '" + name + "' not found"); + } + return storage; + } + + @Override + public String toString() { + return name; + } + + public String name() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StoragePoolType that = (StoragePoolType) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } public static List getNonSharedStoragePoolTypes() { diff --git a/api/src/test/java/com/cloud/storage/StorageTest.java b/api/src/test/java/com/cloud/storage/StorageTest.java index 951ee9fe76a..5f40db46dea 100644 --- a/api/src/test/java/com/cloud/storage/StorageTest.java +++ b/api/src/test/java/com/cloud/storage/StorageTest.java @@ -74,4 +74,13 @@ public class StorageTest { Assert.assertTrue(StoragePoolType.DatastoreCluster.supportsOverProvisioning()); Assert.assertTrue(StoragePoolType.Linstor.supportsOverProvisioning()); } + + @Test + public void equalityTest() { + StoragePoolType t1 = StoragePoolType.NetworkFilesystem; + StoragePoolType t2 = StoragePoolType.NetworkFilesystem; + Assert.assertTrue(t1 == StoragePoolType.NetworkFilesystem); + Assert.assertTrue(t1.equals(StoragePoolType.NetworkFilesystem)); + Assert.assertFalse(t1.equals(StoragePoolType.EXT)); + } } diff --git a/core/src/main/java/com/cloud/agent/transport/StoragePoolTypeAdaptor.java b/core/src/main/java/com/cloud/agent/transport/StoragePoolTypeAdaptor.java new file mode 100644 index 00000000000..635f6d06cf5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/transport/StoragePoolTypeAdaptor.java @@ -0,0 +1,53 @@ +// 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.transport; + +import com.cloud.storage.Storage.StoragePoolType; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import java.lang.reflect.Type; + +/** + * {@link StoragePoolType} acts as extendable set of singleton objects and should return same result when used "==" + * or {@link Object#equals(Object)}. + * To support that, need to return existing object for a given name instead of creating new. + */ +public class StoragePoolTypeAdaptor implements JsonDeserializer, JsonSerializer { + @Override + public StoragePoolType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (json instanceof JsonPrimitive && ((JsonPrimitive) json).isString()) { + return StoragePoolType.valueOf(json.getAsString()); + } + return null; + } + + @Override + public JsonElement serialize(StoragePoolType src, Type typeOfSrc, JsonSerializationContext context) { + String name = src.name(); + if (name == null) { + return new JsonNull(); + } + return new JsonPrimitive(name); + } +} diff --git a/core/src/main/java/com/cloud/serializer/GsonHelper.java b/core/src/main/java/com/cloud/serializer/GsonHelper.java index 7ac85d39317..c3653e5089c 100644 --- a/core/src/main/java/com/cloud/serializer/GsonHelper.java +++ b/core/src/main/java/com/cloud/serializer/GsonHelper.java @@ -37,6 +37,8 @@ import com.cloud.agent.transport.InterfaceTypeAdaptor; import com.cloud.agent.transport.LoggingExclusionStrategy; import com.cloud.agent.transport.Request.NwGroupsCommandTypeAdaptor; import com.cloud.agent.transport.Request.PortConfigListTypeAdaptor; +import com.cloud.agent.transport.StoragePoolTypeAdaptor; +import com.cloud.storage.Storage; import com.cloud.utils.Pair; public class GsonHelper { @@ -69,6 +71,7 @@ public class GsonHelper { }.getType(), new PortConfigListTypeAdaptor()); builder.registerTypeAdapter(new TypeToken>() { }.getType(), new NwGroupsCommandTypeAdaptor()); + builder.registerTypeAdapter(Storage.StoragePoolType.class, new StoragePoolTypeAdaptor()); Gson gson = builder.create(); dsAdaptor.initGson(gson); dtAdaptor.initGson(gson); diff --git a/core/src/test/java/com/cloud/agent/transport/RequestTest.java b/core/src/test/java/com/cloud/agent/transport/RequestTest.java index 21766ba038f..6eada0226fc 100644 --- a/core/src/test/java/com/cloud/agent/transport/RequestTest.java +++ b/core/src/test/java/com/cloud/agent/transport/RequestTest.java @@ -255,7 +255,7 @@ public class RequestTest extends TestCase { public void testGoodCommand() { s_logger.info("Testing good Command"); String content = "[{\"com.cloud.agent.api.GetVolumeStatsCommand\":{\"volumeUuids\":[\"dcc860ac-4a20-498f-9cb3-bab4d57aa676\"]," - + "\"poolType\":\"NetworkFilesystem\",\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]"; + + "\"poolType\":{\"name\":\"NetworkFilesystem\"},\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]"; Request sreq = new Request(Version.v2, 1L, 2L, 3L, 1L, (short)1, content); sreq.setSequence(1); Command cmds[] = sreq.getCommands(); @@ -266,7 +266,7 @@ public class RequestTest extends TestCase { public void testBadCommand() { s_logger.info("Testing Bad Command"); String content = "[{\"com.cloud.agent.api.SomeJunkCommand\":{\"volumeUuids\":[\"dcc860ac-4a20-498f-9cb3-bab4d57aa676\"]," - + "\"poolType\":\"NetworkFilesystem\",\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]"; + + "\"poolType\":{\"name\":\"NetworkFilesystem\"},\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]"; Request sreq = new Request(Version.v2, 1L, 2L, 3L, 1L, (short)1, content); sreq.setSequence(1); Command cmds[] = sreq.getCommands(); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java index 846b4157786..5315bca4b9a 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; @@ -45,6 +46,7 @@ import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceState; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.util.StoragePoolTypeConverter; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.StateMachine; @@ -126,6 +128,7 @@ public class EngineHostVO implements EngineHost, Identity { private String resource; @Column(name = "fs_type") + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType fsType; @Column(name = "available") diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java b/engine/schema/src/main/java/com/cloud/host/HostVO.java index 697401ad069..b7cac8ff8cb 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorType; import javax.persistence.Entity; @@ -44,6 +45,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; import com.cloud.resource.ResourceState; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.util.StoragePoolTypeConverter; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; import java.util.Arrays; @@ -130,6 +132,7 @@ public class HostVO implements Host { private String resource; @Column(name = "fs_type") + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType fsType; @Column(name = "available") diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index 0bd71ea6d86..d8dfbb6f076 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -20,6 +20,7 @@ import java.util.Date; import java.util.UUID; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -32,6 +33,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.util.StoragePoolTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import com.cloud.storage.Storage.ProvisioningType; @@ -114,7 +116,7 @@ public class VolumeVO implements Volume { Type volumeType = Volume.Type.UNKNOWN; @Column(name = "pool_type") - @Enumerated(EnumType.STRING) + @Convert(converter = StoragePoolTypeConverter.class) StoragePoolType poolType; @Column(name = GenericDao.REMOVED_COLUMN) @@ -331,9 +333,7 @@ public class VolumeVO implements Volume { this.poolType = poolType; } - public StoragePoolType getPoolType() { - return poolType; - } + public StoragePoolType getPoolType() { return poolType; } @Override public long getDomainId() { diff --git a/engine/schema/src/main/java/com/cloud/util/StoragePoolTypeConverter.java b/engine/schema/src/main/java/com/cloud/util/StoragePoolTypeConverter.java new file mode 100644 index 00000000000..ab4148ec834 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/util/StoragePoolTypeConverter.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 +// 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.util; + +import com.cloud.storage.Storage.StoragePoolType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +/** + * Converts {@link StoragePoolType} to and from {@link String} using {@link StoragePoolType#name()}. + * + * @author mprokopchuk + */ +@Converter +public class StoragePoolTypeConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(StoragePoolType attribute) { + return attribute != null ? attribute.name() : null; + } + + @Override + public StoragePoolType convertToEntityAttribute(String dbData) { + return dbData != null ? StoragePoolType.valueOf(dbData) : null; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index 8f77b4ba63e..cfd12862c83 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -21,6 +21,7 @@ import java.util.Map; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolStatus; import com.cloud.utils.db.GenericDao; @@ -130,7 +131,7 @@ public interface PrimaryDataStoreDao extends GenericDao { Integer countAll(); - List findPoolsByStorageType(String storageType); + List findPoolsByStorageType(Storage.StoragePoolType storageType); List listStoragePoolsWithActiveVolumesByOfferingId(long offeringid); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index af7dbdc0225..bcd2d27ad63 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -31,6 +31,7 @@ import org.apache.commons.collections.CollectionUtils; import com.cloud.host.Status; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolHostVO; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StoragePoolTagVO; @@ -619,7 +620,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } @Override - public List findPoolsByStorageType(String storageType) { + public List findPoolsByStorageType(Storage.StoragePoolType storageType) { SearchCriteria sc = AllFieldSearch.create(); sc.setParameters("poolType", storageType); return listBy(sc); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java index 926b8a5a01d..27179a8467e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/StoragePoolVO.java @@ -21,11 +21,13 @@ import com.cloud.storage.ScopeType; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; +import com.cloud.util.StoragePoolTypeConverter; import com.cloud.utils.UriUtils; import com.cloud.utils.db.Encrypt; import com.cloud.utils.db.GenericDao; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -57,7 +59,7 @@ public class StoragePoolVO implements StoragePool { private String uuid = null; @Column(name = "pool_type", updatable = false, nullable = false, length = 32) - @Enumerated(value = EnumType.STRING) + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType poolType; @Column(name = GenericDao.CREATED_COLUMN) @@ -180,8 +182,8 @@ public class StoragePoolVO implements StoragePool { return poolType; } - public void setPoolType(StoragePoolType protocol) { - poolType = protocol; + public void setPoolType(StoragePoolType poolType) { + this.poolType = poolType; } @Override @@ -273,7 +275,7 @@ public class StoragePoolVO implements StoragePool { @Override public String getPath() { String updatedPath = path; - if (poolType == StoragePoolType.SMB) { + if (poolType.equals(StoragePoolType.SMB)) { updatedPath = UriUtils.getUpdateUri(updatedPath, false); if (updatedPath.contains("password") && updatedPath.contains("?")) { updatedPath = updatedPath.substring(0, updatedPath.indexOf('?')); diff --git a/engine/storage/volume/src/test/java/org/apache/cloudstack/storage/volume/VolumeServiceTest.java b/engine/storage/volume/src/test/java/org/apache/cloudstack/storage/volume/VolumeServiceTest.java index ee4b77c269c..f4c6df7dd40 100644 --- a/engine/storage/volume/src/test/java/org/apache/cloudstack/storage/volume/VolumeServiceTest.java +++ b/engine/storage/volume/src/test/java/org/apache/cloudstack/storage/volume/VolumeServiceTest.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.storage.volume; +import com.cloud.storage.Storage; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; @@ -185,7 +186,9 @@ public class VolumeServiceTest extends TestCase{ public void validateDestroySourceVolumeAfterMigrationExpungeSourceVolumeAfterMigrationThrowExceptionReturnFalse() throws ExecutionException, InterruptedException{ VolumeObject volumeObject = new VolumeObject(); - volumeObject.configure(null, new VolumeVO() {}); + VolumeVO vo = new VolumeVO() {}; + vo.setPoolType(Storage.StoragePoolType.Filesystem); + volumeObject.configure(null, vo); List exceptions = new ArrayList<>(Arrays.asList(new InterruptedException(), new ExecutionException() {})); diff --git a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java index 6bf36df0941..b46b82f50b0 100644 --- a/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java +++ b/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java @@ -42,13 +42,16 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import javax.naming.ConfigurationException; +import javax.persistence.AttributeConverter; import javax.persistence.AttributeOverride; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.EmbeddedId; import javax.persistence.EntityExistsException; import javax.persistence.EnumType; @@ -123,6 +126,7 @@ public abstract class GenericDaoBase extends Compone protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT"); protected final static Map, GenericDao> s_daoMaps = new ConcurrentHashMap, GenericDao>(71); + private final ConversionSupport _conversionSupport; protected Class _entityBeanType; protected String _table; @@ -287,6 +291,7 @@ public abstract class GenericDaoBase extends Compone } } + _conversionSupport = new ConversionSupport(); setRunLevel(ComponentLifecycle.RUN_LEVEL_SYSTEM); } @@ -649,6 +654,9 @@ public abstract class GenericDaoBase extends Compone } } else if (type == byte[].class) { field.set(entity, rs.getBytes(index)); + } else if (field.getDeclaredAnnotation(Convert.class) != null) { + Object val = _conversionSupport.convertToEntityAttribute(field, rs.getObject(index)); + field.set(entity, val); } else { field.set(entity, rs.getObject(index)); } @@ -1386,7 +1394,7 @@ public abstract class GenericDaoBase extends Compone } String stackTrace = ExceptionUtils.getStackTrace(new CloudRuntimeException(String.format("The query to count all the records of [%s] resulted in a value smaller than" - + " the result set's size [count of records: %s, result set's size: %s]. Using the result set's size instead.", _entityBeanType, + + " the result set's size [count of records: %s, result set's size: %s]. Using the result set's size instead.", _entityBeanType, count, resultSetSize))); s_logger.warn(stackTrace); @@ -1595,7 +1603,10 @@ public abstract class GenericDaoBase extends Compone return; } } - if (attr.field.getType() == String.class) { + if(attr.field.getDeclaredAnnotation(Convert.class) != null) { + Object val = _conversionSupport.convertToDatabaseColumn(attr.field, value); + pstmt.setObject(j, val); + } else if (attr.field.getType() == String.class) { final String str = (String)value; if (str == null) { pstmt.setString(j, null); @@ -2221,4 +2232,50 @@ public abstract class GenericDaoBase extends Compone return sql; } + + /** + * Support conversion between DB and Entity values. + * Detects whether field is annotated with {@link Convert} annotation and use converter instance from the annotation. + */ + static class ConversionSupport { + /** + * Contains cache of {@link AttributeConverter} instances. + */ + private static final Map, AttributeConverter> s_converterCacheMap = new ConcurrentHashMap<>(); + + /** + * Checks whether field annotated with {@link Convert} annotation and tries to convert source value with converter. + * + * @param field Entity field + * @param value DB value + * @return converted value if field is annotated with {@link Convert} or original value otherwise + */ + private T convertToEntityAttribute(Field field, Object value) { + return (T) getConverter(field).map(converter -> converter.convertToEntityAttribute(value)).orElse(value); + } + + /** + * Checks whether field annotated with {@link Convert} annotation and tries to convert source value with converter. + * + * @param field Entity field + * @param value Entity value + * @return converted value if field is annotated with {@link Convert} or original value otherwise + */ + private T convertToDatabaseColumn(Field field, Object value) { + return (T) getConverter(field).map(converter -> converter.convertToDatabaseColumn(value)).orElse(value); + } + + private Optional> getConverter(Field field) { + return Optional.of(field).map(f -> f.getAnnotation(Convert.class)).map(Convert::converter).filter(AttributeConverter.class::isAssignableFrom).map(converterType -> { + return (AttributeConverter) s_converterCacheMap.computeIfAbsent(converterType, ct -> { + try { + return (AttributeConverter) ct.getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new CloudRuntimeException("Unable to create converter for the class " + converterType, e); + } + }); + }); + } + } + } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/FiberChannelAdapter.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/FiberChannelAdapter.java index be7cb727ad7..83636b9a9c3 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/FiberChannelAdapter.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/FiberChannelAdapter.java @@ -19,12 +19,16 @@ package com.cloud.hypervisor.kvm.storage; import com.cloud.storage.Storage; import com.cloud.utils.exception.CloudRuntimeException; -@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.FiberChannel) public class FiberChannelAdapter extends MultipathSCSIAdapterBase { public FiberChannelAdapter() { LOGGER.info("Loaded FiberChannelAdapter for StorageLayer"); } + @Override + public Storage.StoragePoolType getStoragePoolType() { + return Storage.StoragePoolType.FiberChannel; + } + @Override public KVMStoragePool getStoragePool(String uuid) { KVMStoragePool pool = MapStorageUuidToStoragePool.get(uuid); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java index f980cd295bd..46cca60ac4e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java @@ -36,7 +36,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -@StorageAdaptorInfo(storagePoolType=StoragePoolType.Iscsi) public class IscsiAdmStorageAdaptor implements StorageAdaptor { private static final Logger s_logger = Logger.getLogger(IscsiAdmStorageAdaptor.class); @@ -51,6 +50,11 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { return storagePool; } + @Override + public StoragePoolType getStoragePoolType() { + return StoragePoolType.Iscsi; + } + @Override public KVMStoragePool getStoragePool(String uuid) { return MapStorageUuidToStoragePool.get(uuid); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index b1842f38da2..679407e2f58 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.hypervisor.kvm.storage; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -98,27 +100,45 @@ public class KVMStoragePoolManager { public KVMStoragePoolManager(StorageLayer storagelayer, KVMHAMonitor monitor) { this._haMonitor = monitor; this._storageMapper.put("libvirt", new LibvirtStorageAdaptor(storagelayer)); - // add other storage adaptors here - // this._storageMapper.put("newadaptor", new NewStorageAdaptor(storagelayer)); - this._storageMapper.put(StoragePoolType.ManagedNFS.toString(), new ManagedNfsStorageAdaptor(storagelayer)); - this._storageMapper.put(StoragePoolType.PowerFlex.toString(), new ScaleIOStorageAdaptor(storagelayer)); + // add other storage adaptors manually here - // add any adaptors that wish to register themselves via annotation + // add any adaptors that wish to register themselves via call to adaptor.getStoragePoolType() Reflections reflections = new Reflections("com.cloud.hypervisor.kvm.storage"); - Set> storageAdaptors = reflections.getSubTypesOf(StorageAdaptor.class); - for (Class storageAdaptor : storageAdaptors) { - StorageAdaptorInfo info = storageAdaptor.getAnnotation(StorageAdaptorInfo.class); - if (info != null && info.storagePoolType() != null) { - if (this._storageMapper.containsKey(info.storagePoolType().toString())) { - s_logger.warn(String.format("Duplicate StorageAdaptor type %s, not loading %s", info.storagePoolType().toString(), storageAdaptor.getName())); + Set> storageAdaptorClasses = reflections.getSubTypesOf(StorageAdaptor.class); + for (Class storageAdaptorClass : storageAdaptorClasses) { + s_logger.debug("Checking pool type for adaptor " + storageAdaptorClass.getName()); + if (Modifier.isAbstract(storageAdaptorClass.getModifiers()) || storageAdaptorClass.isInterface()) { + s_logger.debug("Skipping registration of abstract class / interface " + storageAdaptorClass.getName()); + continue; + } + if (storageAdaptorClass.isAssignableFrom(LibvirtStorageAdaptor.class)) { + s_logger.debug("Skipping re-registration of LibvirtStorageAdaptor"); + continue; + } + try { + Constructor storageLayerConstructor = Arrays.stream(storageAdaptorClass.getConstructors()) + .filter(c -> c.getParameterCount() == 1) + .filter(c -> c.getParameterTypes()[0].isAssignableFrom(StorageLayer.class)) + .findFirst().orElse(null); + StorageAdaptor adaptor; + + if (storageLayerConstructor == null) { + adaptor = storageAdaptorClass.getDeclaredConstructor().newInstance(); } else { - try { - s_logger.info(String.format("adding storage adaptor for %s", storageAdaptor.getName())); - this._storageMapper.put(info.storagePoolType().toString(), storageAdaptor.getDeclaredConstructor().newInstance()); - } catch (Exception ex) { - throw new CloudRuntimeException(ex.toString()); + adaptor = (StorageAdaptor) storageLayerConstructor.newInstance(storagelayer); + } + + StoragePoolType storagePoolType = adaptor.getStoragePoolType(); + if (storagePoolType != null) { + if (this._storageMapper.containsKey(storagePoolType.toString())) { + s_logger.warn(String.format("Duplicate StorageAdaptor type %s, not loading %s", storagePoolType, storageAdaptorClass.getName())); + } else { + s_logger.info(String.format("Adding storage adaptor for %s", storageAdaptorClass.getName())); + this._storageMapper.put(storagePoolType.toString(), adaptor); } } + } catch (Exception ex) { + throw new CloudRuntimeException("Failed to set up storage adaptors", ex); } } @@ -127,6 +147,13 @@ public class KVMStoragePoolManager { } } + /** + * Returns true if physical disk copy functionality supported. + */ + public boolean supportsPhysicalDiskCopy(StoragePoolType type) { + return getStorageAdaptor(type).supportsPhysicalDiskCopy(type); + } + public boolean connectPhysicalDisk(StoragePoolType type, String poolUuid, String volPath, Map details) { StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = adaptor.getStoragePool(poolUuid); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 1be4a8b6185..f958a1b181d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -403,7 +403,7 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), volume.getSize(), cmd.getWaitInMillSeconds()); - } if (primaryPool.getType() == StoragePoolType.PowerFlex) { + } if (storagePoolMgr.supportsPhysicalDiskCopy(primaryPool.getType())) { Map details = primaryStore.getDetails(); String path = details != null ? details.get("managedStoreTarget") : null; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index bdaa419c698..83a5c06cb5b 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -459,6 +459,12 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { return parser.parseStorageVolumeXML(volDefXML); } + @Override + public StoragePoolType getStoragePoolType() { + // This is mapped manually in KVMStoragePoolManager + return null; + } + @Override public KVMStoragePool getStoragePool(String uuid) { return this.getStoragePool(uuid, false); @@ -775,23 +781,22 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { s_logger.info("Attempting to create volume " + name + " (" + pool.getType().toString() + ") in pool " + pool.getUuid() + " with size " + toHumanReadableSize(size)); - switch (pool.getType()) { - case RBD: - return createPhysicalDiskByLibVirt(name, pool, PhysicalDiskFormat.RAW, provisioningType, size); - case NetworkFilesystem: - case Filesystem: - switch (format) { - case QCOW2: - case RAW: - return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size, passphrase); - case DIR: - case TAR: - return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); - default: - throw new CloudRuntimeException("Unexpected disk format is specified."); - } - default: - return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); + StoragePoolType poolType = pool.getType(); + if (poolType.equals(StoragePoolType.RBD)) { + return createPhysicalDiskByLibVirt(name, pool, PhysicalDiskFormat.RAW, provisioningType, size); + } else if (poolType.equals(StoragePoolType.NetworkFilesystem) || poolType.equals(StoragePoolType.Filesystem)) { + switch (format) { + case QCOW2: + case RAW: + return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size, passphrase); + case DIR: + case TAR: + return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); + default: + throw new CloudRuntimeException("Unexpected disk format is specified."); + } + } else { + return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java index b23dd9a1790..62029fa1e34 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java @@ -65,6 +65,9 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor { return storagePool; } + @Override + public StoragePoolType getStoragePoolType() { return StoragePoolType.ManagedNFS; } + @Override public KVMStoragePool getStoragePool(String uuid) { return getStoragePool(uuid, false); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index b85be041e48..2cd68753e01 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -41,22 +41,19 @@ import org.apache.log4j.Logger; import org.libvirt.LibvirtException; import com.cloud.storage.Storage; -import com.cloud.storage.StorageLayer; import com.cloud.storage.StorageManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; import org.apache.commons.lang3.StringUtils; -@StorageAdaptorInfo(storagePoolType= Storage.StoragePoolType.PowerFlex) public class ScaleIOStorageAdaptor implements StorageAdaptor { private static final Logger LOGGER = Logger.getLogger(ScaleIOStorageAdaptor.class); private static final Map MapStorageUuidToStoragePool = new HashMap<>(); private static final int DEFAULT_DISK_WAIT_TIME_IN_SECS = 60; - private StorageLayer storageLayer; - public ScaleIOStorageAdaptor(StorageLayer storagelayer) { - storageLayer = storagelayer; + public ScaleIOStorageAdaptor() { + } @Override @@ -70,6 +67,11 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor { return pool; } + @Override + public Storage.StoragePoolType getStoragePoolType() { + return Storage.StoragePoolType.PowerFlex; + } + @Override public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) { return getStoragePool(uuid); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index ecf8691c6ed..34bf08f4496 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -26,6 +26,8 @@ import com.cloud.storage.Storage.StoragePoolType; public interface StorageAdaptor { + StoragePoolType getStoragePoolType(); + public KVMStoragePool getStoragePool(String uuid); // Get the storage pool from libvirt, but control if libvirt should refresh the pool (can take a long time) @@ -91,4 +93,11 @@ public interface StorageAdaptor { * @param timeout */ KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, KVMStoragePool destPool, Storage.ImageFormat format, int timeout); + + /** + * Returns true if storage adaptor supports physical disk copy functionality. + */ + default boolean supportsPhysicalDiskCopy(StoragePoolType type) { + return StoragePoolType.PowerFlex == type; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptorInfo.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptorInfo.java index fce976535f6..8d4b0c60a86 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptorInfo.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/StorageAdaptorInfo.java @@ -22,10 +22,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import com.cloud.storage.Storage.StoragePoolType; - @Retention(RetentionPolicy.RUNTIME) @Target({ TYPE }) public @interface StorageAdaptorInfo { - StoragePoolType storagePoolType(); } diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java index 492bc275a3f..7989f9e6a69 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/ScaleIOStoragePoolTest.java @@ -37,14 +37,12 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import com.cloud.storage.Storage.StoragePoolType; -import com.cloud.storage.StorageLayer; import com.cloud.utils.script.Script; @RunWith(MockitoJUnitRunner.class) @@ -54,9 +52,6 @@ public class ScaleIOStoragePoolTest { StorageAdaptor adapter; - @Mock - StorageLayer storageLayer; - @Before public void setUp() throws Exception { final String uuid = "345fc603-2d7e-47d2-b719-a0110b3732e6"; @@ -65,7 +60,7 @@ public class ScaleIOStoragePoolTest { Map details = new HashMap(); details.put(ScaleIOGatewayClient.STORAGE_POOL_SYSTEM_ID, systemId); - adapter = spy(new ScaleIOStorageAdaptor(storageLayer)); + adapter = spy(new ScaleIOStorageAdaptor()); pool = new ScaleIOStoragePool(uuid, "192.168.1.19", 443, "a519be2f00000000", type, details, adapter); } diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockStoragePoolVO.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockStoragePoolVO.java index 46cd0e47a7b..385ed3273ac 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockStoragePoolVO.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/MockStoragePoolVO.java @@ -17,14 +17,14 @@ package com.cloud.simulator; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.util.StoragePoolTypeConverter; import org.apache.cloudstack.api.InternalIdentity; import com.cloud.storage.Storage.StoragePoolType; @@ -50,7 +50,7 @@ public class MockStoragePoolVO implements InternalIdentity { private String hostGuid; @Column(name = "pool_type") - @Enumerated(value = EnumType.STRING) + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType poolType; public MockStoragePoolVO() { diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockStoragePoolDaoImpl.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockStoragePoolDaoImpl.java index ea9bcc4063c..c656b84289d 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockStoragePoolDaoImpl.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/simulator/dao/MockStoragePoolDaoImpl.java @@ -52,7 +52,7 @@ public class MockStoragePoolDaoImpl extends GenericDaoBase sc = hostguidSearch.create(); sc.setParameters("hostguid", hostUuid); - sc.setParameters("type", StoragePoolType.Filesystem.toString()); + sc.setParameters("type", StoragePoolType.Filesystem); return findOneBy(sc); } diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java index 685565d73b0..bad57068a73 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java @@ -263,7 +263,7 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore parameters.setPort(port); parameters.setPath(hostPath); } else { - StoragePoolType type = Enum.valueOf(StoragePoolType.class, scheme); + StoragePoolType type = StoragePoolType.valueOf(scheme); if (type != null) { parameters.setType(type); diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 917b7c0ed0c..deee5fb6c41 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -54,7 +54,6 @@ import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.StoragePool; import com.linbit.linstor.api.model.VolumeDefinition; -@StorageAdaptorInfo(storagePoolType=Storage.StoragePoolType.Linstor) public class LinstorStorageAdaptor implements StorageAdaptor { private static final Logger s_logger = Logger.getLogger(LinstorStorageAdaptor.class); private static final Map MapStorageUuidToStoragePool = new HashMap<>(); @@ -66,6 +65,11 @@ public class LinstorStorageAdaptor implements StorageAdaptor { return new DevelopersApi(client); } + @Override + public Storage.StoragePoolType getStoragePoolType() { + return Storage.StoragePoolType.Linstor; + } + private static String getLinstorRscName(String name) { return LinstorUtil.RSC_PREFIX + name; } 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 273f088d77f..d8f77ac88c6 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 @@ -39,7 +39,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; -@StorageAdaptorInfo(storagePoolType=StoragePoolType.StorPool) 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)))) { @@ -65,6 +64,11 @@ public class StorPoolStorageAdaptor implements StorageAdaptor { return storagePool; } + @Override + public StoragePoolType getStoragePoolType() { + return StoragePoolType.StorPool; + } + @Override public KVMStoragePool getStoragePool(String uuid) { SP_LOG("StorPoolStorageAdaptor.getStoragePool: uuid=%s", uuid); diff --git a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index e75e86108c7..c587eb40f18 100644 --- a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -16,6 +16,27 @@ // under the License. package com.cloud.api.query.dao; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.annotation.AnnotationService; +import org.apache.cloudstack.annotation.dao.AnnotationDao; +import org.apache.cloudstack.api.response.StoragePoolResponse; +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.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.capacity.CapacityManager; @@ -33,25 +54,6 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import org.apache.cloudstack.annotation.AnnotationService; -import org.apache.cloudstack.annotation.dao.AnnotationDao; -import org.apache.cloudstack.api.response.StoragePoolResponse; -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.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; @Component public class StoragePoolJoinDaoImpl extends GenericDaoBase implements StoragePoolJoinDao { @@ -337,7 +339,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("poolType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("poolType", SearchCriteria.Op.LIKE, new Storage.StoragePoolType("%" + keyword + "%")); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } diff --git a/server/src/main/java/com/cloud/api/query/vo/StoragePoolJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/StoragePoolJoinVO.java index 5eb04d2e00d..85e904be973 100644 --- a/server/src/main/java/com/cloud/api/query/vo/StoragePoolJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/StoragePoolJoinVO.java @@ -19,12 +19,14 @@ package com.cloud.api.query.vo; import java.util.Date; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.util.StoragePoolTypeConverter; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -64,7 +66,7 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I private StoragePoolStatus status; @Column(name = "pool_type") - @Enumerated(value = EnumType.STRING) + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType poolType; @Column(name = GenericDao.CREATED_COLUMN) diff --git a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java index a465e8990e7..dbad73a39bb 100644 --- a/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/UserVmJoinVO.java @@ -22,6 +22,7 @@ import java.util.Map; import javax.persistence.AttributeOverride; import javax.persistence.Column; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -38,6 +39,7 @@ import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume; import com.cloud.user.Account; +import com.cloud.util.StoragePoolTypeConverter; import com.cloud.utils.db.GenericDao; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; @@ -256,7 +258,7 @@ public class UserVmJoinVO extends BaseViewWithTagInformationVO implements Contro private String poolUuid; @Column(name = "pool_type", updatable = false, nullable = false, length = 32) - @Enumerated(value = EnumType.STRING) + @Convert(converter = StoragePoolTypeConverter.class) private StoragePoolType poolType; @Column(name = "volume_id") diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 04889d0b2a8..8bcb2ac3956 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -2133,7 +2133,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C StoragePoolInfo childStoragePoolInfo = childDataStoreAnswer.getPoolInfo(); StoragePoolVO dataStoreVO = getExistingPoolByUuid(childStoragePoolInfo.getUuid()); if (dataStoreVO == null && childDataStoreAnswer.getPoolType().equalsIgnoreCase("NFS")) { - List nfsStoragePools = _storagePoolDao.findPoolsByStorageType(StoragePoolType.NetworkFilesystem.toString()); + List nfsStoragePools = _storagePoolDao.findPoolsByStorageType(StoragePoolType.NetworkFilesystem); for (StoragePoolVO storagePool : nfsStoragePools) { String storagePoolUUID = storagePool.getUuid(); if (childStoragePoolInfo.getName().equalsIgnoreCase(storagePoolUUID.replaceAll("-", ""))) { @@ -2193,7 +2193,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C StoragePoolInfo childStoragePoolInfo = childDataStoreAnswer.getPoolInfo(); StoragePoolVO dataStoreVO = _storagePoolDao.findPoolByUUID(childStoragePoolInfo.getUuid()); if (dataStoreVO == null && childDataStoreAnswer.getPoolType().equalsIgnoreCase("NFS")) { - List nfsStoragePools = _storagePoolDao.findPoolsByStorageType(StoragePoolType.NetworkFilesystem.toString()); + List nfsStoragePools = _storagePoolDao.findPoolsByStorageType(StoragePoolType.NetworkFilesystem); for (StoragePoolVO storagePool : nfsStoragePools) { String storagePoolUUID = storagePool.getUuid(); if (childStoragePoolInfo.getName().equalsIgnoreCase(storagePoolUUID.replaceAll("-", ""))) { @@ -2474,7 +2474,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C @DB public StoragePoolVO findLocalStorageOnHost(long hostId) { SearchCriteria sc = LocalStorageSearch.create(); - sc.setParameters("type", new Object[] {StoragePoolType.Filesystem, StoragePoolType.LVM}); + sc.setParameters("type", StoragePoolType.Filesystem, StoragePoolType.LVM); sc.setJoinParameters("poolHost", "hostId", hostId); List storagePools = _storagePoolDao.search(sc, null); if (!storagePools.isEmpty()) { diff --git a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java index fa6b71d0cb2..3be6e02d04b 100644 --- a/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java +++ b/server/src/test/java/com/cloud/storage/listener/StoragePoolMonitorTest.java @@ -21,6 +21,7 @@ import com.cloud.exception.StorageUnavailableException; import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage; import com.cloud.storage.StorageManagerImpl; import com.cloud.storage.StoragePoolStatus; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -53,6 +54,7 @@ public class StoragePoolMonitorTest { pool.setScope(ScopeType.CLUSTER); pool.setStatus(StoragePoolStatus.Up); pool.setId(123L); + pool.setPoolType(Storage.StoragePoolType.Filesystem); cmd = new StartupRoutingCommand(); cmd.setHypervisorType(Hypervisor.HypervisorType.KVM); }