diff --git a/api/src/main/java/com/cloud/exception/StorageAccessException.java b/api/src/main/java/com/cloud/exception/StorageAccessException.java index eefbcf5518a..d54d77d66f1 100644 --- a/api/src/main/java/com/cloud/exception/StorageAccessException.java +++ b/api/src/main/java/com/cloud/exception/StorageAccessException.java @@ -26,7 +26,7 @@ import com.cloud.utils.SerialVersionUID; public class StorageAccessException extends RuntimeException { private static final long serialVersionUID = SerialVersionUID.StorageAccessException; - public StorageAccessException(String message) { - super(message); + public StorageAccessException(String message, Exception causer) { + super(message, causer); } } diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index c997f5e1dbf..05b8b3ab7a8 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -16,14 +16,10 @@ // under the License. package com.cloud.storage; -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; + +import java.util.ArrayList; +import java.util.List; public class Storage { public static enum ImageFormat { @@ -139,6 +135,21 @@ public class Storage { ISODISK /* Template corresponding to a iso (non root disk) present in an OVA */ } + public enum EncryptionSupport { + /** + * Encryption not supported. + */ + Unsupported, + /** + * Will use hypervisor encryption driver (qemu -> luks) + */ + Hypervisor, + /** + * Storage pool handles encryption and just provides an encrypted volume + */ + Storage + } + /** * 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 @@ -150,61 +161,37 @@ public class Storage { * 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 enum StoragePoolType { + Filesystem(false, true, EncryptionSupport.Hypervisor), // local directory + NetworkFilesystem(true, true, EncryptionSupport.Hypervisor), // NFS + IscsiLUN(true, false, EncryptionSupport.Unsupported), // shared LUN, with a clusterfs overlay + Iscsi(true, false, EncryptionSupport.Unsupported), // for e.g., ZFS Comstar + ISO(false, false, EncryptionSupport.Unsupported), // for iso image + LVM(false, false, EncryptionSupport.Unsupported), // XenServer local LVM SR + CLVM(true, false, EncryptionSupport.Unsupported), + RBD(true, true, EncryptionSupport.Unsupported), // http://libvirt.org/storage.html#StorageBackendRBD + SharedMountPoint(true, true, EncryptionSupport.Hypervisor), + VMFS(true, true, EncryptionSupport.Unsupported), // VMware VMFS storage + PreSetup(true, true, EncryptionSupport.Unsupported), // for XenServer, Storage Pool is set up by customers. + EXT(false, true, EncryptionSupport.Unsupported), // XenServer local EXT SR + OCFS2(true, false, EncryptionSupport.Unsupported), + SMB(true, false, EncryptionSupport.Unsupported), + Gluster(true, false, EncryptionSupport.Unsupported), + PowerFlex(true, true, EncryptionSupport.Hypervisor), // Dell EMC PowerFlex/ScaleIO (formerly VxFlexOS) + ManagedNFS(true, false, EncryptionSupport.Unsupported), + Linstor(true, true, EncryptionSupport.Storage), + DatastoreCluster(true, true, EncryptionSupport.Unsupported), // for VMware, to abstract pool of clusters + StorPool(true, true, EncryptionSupport.Hypervisor), + FiberChannel(true, true, EncryptionSupport.Unsupported); // Fiber Channel Pool for KVM hypervisors is used to find the volume by WWN value (/dev/disk/by-id/wwn-) - 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, true, 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; + private final EncryptionSupport 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; + StoragePoolType(boolean shared, boolean overProvisioning, EncryptionSupport encryption) { this.shared = shared; this.overProvisioning = overProvisioning; this.encryption = encryption; - addStoragePoolType(this); } public boolean isShared() { @@ -216,50 +203,12 @@ public class Storage { } public boolean supportsEncryption() { + return encryption == EncryptionSupport.Hypervisor || encryption == EncryptionSupport.Storage; + } + + public EncryptionSupport encryptionSupportMode() { 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/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index efbf9d9d78e..2c729c3cbc7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -57,6 +57,7 @@ public class ApiConstants { public static final String BASE64_IMAGE = "base64image"; public static final String BGP_PEERS = "bgppeers"; public static final String BGP_PEER_IDS = "bgppeerids"; + public static final String BATCH_SIZE = "batchsize"; public static final String BITS = "bits"; public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; @@ -476,11 +477,12 @@ public class ApiConstants { public static final String STATE = "state"; public static final String STATS = "stats"; public static final String STATUS = "status"; - public static final String STORAGE_TYPE = "storagetype"; - public static final String STORAGE_POLICY = "storagepolicy"; - public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; public static final String STORAGE_CAPABILITIES = "storagecapabilities"; public static final String STORAGE_CUSTOM_STATS = "storagecustomstats"; + public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; + public static final String STORAGE_POLICY = "storagepolicy"; + public static final String STORAGE_POOL = "storagepool"; + public static final String STORAGE_TYPE = "storagetype"; public static final String SUBNET = "subnet"; public static final String OWNER = "owner"; public static final String SWAP_OWNER = "swapowner"; @@ -953,7 +955,6 @@ public class ApiConstants { public static final String AUTOSCALE_VMGROUP_NAME = "autoscalevmgroupname"; public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername"; public static final String BAREMETAL_RCT_URL = "baremetalrcturl"; - public static final String BATCH_SIZE = "batchsize"; public static final String UCS_DN = "ucsdn"; public static final String GSLB_PROVIDER = "gslbprovider"; public static final String EXCLUSIVE_GSLB_PROVIDER = "isexclusivegslbprovider"; @@ -1190,6 +1191,7 @@ public class ApiConstants { "a boolean or a numeric value: if it results in a boolean value, the tariff value will be applied according to the result; if it results in a numeric value, the " + "numeric value will be applied; if the result is neither a boolean nor a numeric value, the tariff will not be applied. If the rule is not informed, the tariff " + "value will be applied."; + public static final String VMWARE_DC = "vmwaredc"; /** * This enum specifies IO Drivers, each option controls specific policies on I/O. diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java index 62bcc07b16d..a3563d5a3e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java @@ -152,7 +152,7 @@ public class HostResponse extends BaseResponseWithAnnotations { @Deprecated @SerializedName("memoryallocated") @Param(description = "the amount of the host's memory currently allocated") - private long memoryAllocated; + private Long memoryAllocated; @SerializedName("memoryallocatedpercentage") @Param(description = "the amount of the host's memory currently allocated in percentage") @@ -407,7 +407,7 @@ public class HostResponse extends BaseResponseWithAnnotations { this.memWithOverprovisioning=memWithOverprovisioning; } - public void setMemoryAllocated(long memoryAllocated) { + public void setMemoryAllocated(Long memoryAllocated) { this.memoryAllocated = memoryAllocated; } @@ -687,8 +687,8 @@ public class HostResponse extends BaseResponseWithAnnotations { return memoryTotal; } - public long getMemoryAllocated() { - return memoryAllocated; + public Long getMemoryAllocated() { + return memoryAllocated == null ? 0 : memoryAllocated; } public void setMemoryAllocatedPercentage(String memoryAllocatedPercentage) { diff --git a/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java b/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java new file mode 100644 index 00000000000..a4d73a8b164 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CleanupVMCommand.java @@ -0,0 +1,46 @@ +// +// 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; + +/** + * This command will destroy a leftover VM during the expunge process if it wasn't destroyed before. + * + */ +public class CleanupVMCommand extends Command { + String vmName; + boolean executeInSequence; + + public CleanupVMCommand(String vmName) { + this(vmName, false); + } + public CleanupVMCommand(String vmName, boolean executeInSequence) { + this.vmName = vmName; + this.executeInSequence = executeInSequence; + } + + @Override + public boolean executeInSequence() { + return executeInSequence; + } + + public String getVmName() { + return vmName; + } +} diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 06061908888..db0119febde 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -1807,7 +1807,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati try { volService.grantAccess(volFactory.getVolume(newVol.getId()), host, destPool); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to the volume [%s] on host [%s].", newVolToString, host)); + throw new StorageAccessException(String.format("Unable to grant access to the volume [%s] on host [%s].", newVolToString, host), e); } } @@ -1847,7 +1847,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati try { volService.grantAccess(volFactory.getVolume(volumeId), host, volumeStore); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to volume [%s] on host [%s].", volToString, host)); + throw new StorageAccessException(String.format("Unable to grant access to volume [%s] on host [%s].", volToString, host), e); } } @@ -1928,7 +1928,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati try { volService.grantAccess(volFactory.getVolume(vol.getId()), host, store); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to volume [%s] on host [%s].", volToString, host)); + throw new StorageAccessException(String.format("Unable to grant access to volume [%s] on host [%s].", volToString, host), e); } } else { grantVolumeAccessToHostIfNeeded(store, vol.getId(), host, volToString); diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java index 5daab544b21..27cea8d5c2d 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDao.java @@ -20,8 +20,9 @@ import java.util.Collection; import java.util.Map; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface ClusterDetailsDao extends GenericDao { +public interface ClusterDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long clusterId); void persist(long clusterId, Map details); diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java index a4f6acb9057..7650b40dd2f 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsDaoImpl.java @@ -28,12 +28,13 @@ import org.apache.cloudstack.framework.config.ScopedConfigStorage; import org.apache.commons.collections.CollectionUtils; import com.cloud.utils.crypt.DBEncryptionUtil; -import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + +public class ClusterDetailsDaoImpl extends ResourceDetailsDaoBase implements ClusterDetailsDao, ScopedConfigStorage { -public class ClusterDetailsDaoImpl extends GenericDaoBase implements ClusterDetailsDao, ScopedConfigStorage { protected final SearchBuilder ClusterSearch; protected final SearchBuilder DetailSearch; @@ -44,11 +45,11 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase findDetails(long clusterId) { SearchCriteria sc = ClusterSearch.create(); @@ -91,7 +97,7 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase(); } SearchBuilder sb = createSearchBuilder(); - sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getResourceId(), SearchCriteria.Op.EQ); sb.and("name", sb.entity().getName(), SearchCriteria.Op.IN); sb.done(); SearchCriteria sc = sb.create(); diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java index 6eb9e7466a7..b213f8f2594 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterDetailsVO.java @@ -23,11 +23,11 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "cluster_details") -public class ClusterDetailsVO implements InternalIdentity { +public class ClusterDetailsVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -35,7 +35,7 @@ public class ClusterDetailsVO implements InternalIdentity { private long id; @Column(name = "cluster_id") - private long clusterId; + private long resourceId; @Column(name = "name") private String name; @@ -47,13 +47,14 @@ public class ClusterDetailsVO implements InternalIdentity { } public ClusterDetailsVO(long clusterId, String name, String value) { - this.clusterId = clusterId; + this.resourceId = clusterId; this.name = name; this.value = value; } - public long getClusterId() { - return clusterId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -64,6 +65,11 @@ public class ClusterDetailsVO implements InternalIdentity { return value; } + @Override + public boolean isDisplay() { + return true; + } + public void setValue(String value) { this.value = value; } diff --git a/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java b/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java index df5a2283baa..6f803cc9f2f 100644 --- a/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/domain/DomainDetailVO.java @@ -23,18 +23,18 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "domain_details") -public class DomainDetailVO implements InternalIdentity { +public class DomainDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "domain_id") - private long domainId; + private long resourceId; @Column(name = "name") private String name; @@ -46,13 +46,14 @@ public class DomainDetailVO implements InternalIdentity { } public DomainDetailVO(long domainId, String name, String value) { - this.domainId = domainId; + this.resourceId = domainId; this.name = name; this.value = value; } - public long getDomainId() { - return domainId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -63,6 +64,11 @@ public class DomainDetailVO implements InternalIdentity { return value; } + @Override + public boolean isDisplay() { + return true; + } + public void setValue(String value) { this.value = value; } diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java index 6b53e49764e..ae149ff4381 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDao.java @@ -20,8 +20,9 @@ import java.util.Map; import com.cloud.domain.DomainDetailVO; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface DomainDetailsDao extends GenericDao { +public interface DomainDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long domainId); void persist(long domainId, Map details); @@ -31,6 +32,4 @@ public interface DomainDetailsDao extends GenericDao { void deleteDetails(long domainId); void update(long domainId, Map details); - - String getActualValue(DomainDetailVO domainDetailVO); } diff --git a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java index b9721a2e58c..5b4e4c591ff 100644 --- a/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/domain/dao/DomainDetailsDaoImpl.java @@ -25,19 +25,17 @@ import javax.inject.Inject; import org.apache.cloudstack.framework.config.ConfigKey.Scope; import org.apache.cloudstack.framework.config.ScopedConfigStorage; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.config.impl.ConfigurationVO; import com.cloud.domain.DomainDetailVO; import com.cloud.domain.DomainVO; -import com.cloud.utils.crypt.DBEncryptionUtil; -import com.cloud.utils.db.GenericDaoBase; 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 org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -public class DomainDetailsDaoImpl extends GenericDaoBase implements DomainDetailsDao, ScopedConfigStorage { +public class DomainDetailsDaoImpl extends ResourceDetailsDaoBase implements DomainDetailsDao, ScopedConfigStorage { protected final SearchBuilder domainSearch; @Inject @@ -47,14 +45,14 @@ public class DomainDetailsDaoImpl extends GenericDaoBase i protected DomainDetailsDaoImpl() { domainSearch = createSearchBuilder(); - domainSearch.and("domainId", domainSearch.entity().getDomainId(), Op.EQ); + domainSearch.and("domainId", domainSearch.entity().getResourceId(), Op.EQ); domainSearch.done(); } @Override public Map findDetails(long domainId) { QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); - sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + sc.and(sc.entity().getResourceId(), Op.EQ, domainId); List results = sc.list(); Map details = new HashMap(results.size()); for (DomainDetailVO r : results) { @@ -80,11 +78,16 @@ public class DomainDetailsDaoImpl extends GenericDaoBase i @Override public DomainDetailVO findDetail(long domainId, String name) { QueryBuilder sc = QueryBuilder.create(DomainDetailVO.class); - sc.and(sc.entity().getDomainId(), Op.EQ, domainId); + sc.and(sc.entity().getResourceId(), Op.EQ, domainId); sc.and(sc.entity().getName(), Op.EQ, name); return sc.find(); } + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new DomainDetailVO(resourceId, key, value)); + } + @Override public void deleteDetails(long domainId) { SearchCriteria sc = domainSearch.create(); @@ -129,13 +132,4 @@ public class DomainDetailsDaoImpl extends GenericDaoBase i } return vo == null ? null : getActualValue(vo); } - - @Override - public String getActualValue(DomainDetailVO domainDetailVO) { - ConfigurationVO configurationVO = _configDao.findByName(domainDetailVO.getName()); - if (configurationVO != null && configurationVO.isEncrypted()) { - return DBEncryptionUtil.decrypt(domainDetailVO.getValue()); - } - return domainDetailVO.getValue(); - } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java index 376933f92e7..2d9656b5b0a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolDetailsDaoImpl.java @@ -46,7 +46,7 @@ public class StoragePoolDetailsDaoImpl extends ResourceDetailsDaoBase templateSizes = readTemplatePropertiesSizes(templatePath + "/template.properties"); + updateSeededTemplateDetails(templDataStoreVO.getTemplateId(), templDataStoreVO.getDataStoreId(), + templateSizes.first(), templateSizes.second()); LOGGER.info("SystemVM template already seeded, skipping registration"); return true; } @@ -542,6 +545,21 @@ public class SystemVmTemplateRegistration { } } + public void updateSeededTemplateDetails(long templateId, long storeId, long size, long physicalSize) { + VMTemplateVO template = vmTemplateDao.findById(templateId); + template.setSize(size); + vmTemplateDao.update(template.getId(), template); + + TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeId, template.getId()); + templateDataStoreVO.setSize(size); + templateDataStoreVO.setPhysicalSize(physicalSize); + templateDataStoreVO.setLastUpdated(new Date(DateUtil.currentGMTTime().getTime())); + boolean updated = templateDataStoreDao.update(templateDataStoreVO.getId(), templateDataStoreVO); + if (!updated) { + throw new CloudRuntimeException("Failed to update template_store_ref entry for seeded systemVM template"); + } + } + public void updateSystemVMEntries(Long templateId, Hypervisor.HypervisorType hypervisorType) { vmInstanceDao.updateSystemVmTemplateId(templateId, hypervisorType); } @@ -555,7 +573,7 @@ public class SystemVmTemplateRegistration { } } - private static void readTemplateProperties(String path, SystemVMTemplateDetails details) { + private static Pair readTemplatePropertiesSizes(String path) { File tmpFile = new File(path); Long size = null; Long physicalSize = 0L; @@ -574,8 +592,13 @@ public class SystemVmTemplateRegistration { } catch (IOException ex) { LOGGER.warn("Failed to read from template.properties", ex); } - details.setSize(size); - details.setPhysicalSize(physicalSize); + return new Pair<>(size, physicalSize); + } + + public static void readTemplateProperties(String path, SystemVMTemplateDetails details) { + Pair templateSizes = readTemplatePropertiesSizes(path); + details.setSize(templateSizes.first()); + details.setPhysicalSize(templateSizes.second()); } private void updateTemplateTablesOnFailure(long templateId) { @@ -799,7 +822,7 @@ public class SystemVmTemplateRegistration { TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateId); if (templateDataStoreVO != null) { String installPath = templateDataStoreVO.getInstallPath(); - if (validateIfSeeded(storeUrlAndId.first(), installPath, nfsVersion)) { + if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) { continue; } } diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java index 863f6c96008..aa6e49666dd 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailVO.java @@ -23,18 +23,18 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; -import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "account_details") -public class AccountDetailVO implements InternalIdentity { +public class AccountDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "account_id") - private long accountId; + private long resourceId; @Column(name = "name") private String name; @@ -46,13 +46,14 @@ public class AccountDetailVO implements InternalIdentity { } public AccountDetailVO(long accountId, String name, String value) { - this.accountId = accountId; + this.resourceId = accountId; this.name = name; this.value = value; } - public long getAccountId() { - return accountId; + @Override + public long getResourceId() { + return resourceId; } public String getName() { @@ -63,6 +64,11 @@ public class AccountDetailVO implements InternalIdentity { return value; } + @Override + public boolean isDisplay() { + return true; + } + public void setValue(String value) { this.value = value; } diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java index 514433e8068..65bbe1670a8 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDao.java @@ -19,8 +19,9 @@ package com.cloud.user; import java.util.Map; import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; -public interface AccountDetailsDao extends GenericDao { +public interface AccountDetailsDao extends GenericDao, ResourceDetailsDao { Map findDetails(long accountId); void persist(long accountId, Map details); @@ -34,6 +35,4 @@ public interface AccountDetailsDao extends GenericDao { * they will get created */ void update(long accountId, Map details); - - String getActualValue(AccountDetailVO accountDetailVO); } diff --git a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java index 510270ad7bf..535b5eae390 100644 --- a/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/AccountDetailsDaoImpl.java @@ -27,22 +27,20 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; import org.apache.cloudstack.framework.config.ScopedConfigStorage; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.config.impl.ConfigurationVO; import com.cloud.domain.DomainDetailVO; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDetailsDao; import com.cloud.user.dao.AccountDao; -import com.cloud.utils.crypt.DBEncryptionUtil; -import com.cloud.utils.db.GenericDaoBase; 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 org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -public class AccountDetailsDaoImpl extends GenericDaoBase implements AccountDetailsDao, ScopedConfigStorage { +public class AccountDetailsDaoImpl extends ResourceDetailsDaoBase implements AccountDetailsDao, ScopedConfigStorage { protected final SearchBuilder accountSearch; @Inject @@ -56,16 +54,16 @@ public class AccountDetailsDaoImpl extends GenericDaoBase protected AccountDetailsDaoImpl() { accountSearch = createSearchBuilder(); - accountSearch.and("accountId", accountSearch.entity().getAccountId(), Op.EQ); + accountSearch.and("accountId", accountSearch.entity().getResourceId(), Op.EQ); accountSearch.done(); } @Override public Map findDetails(long accountId) { QueryBuilder sc = QueryBuilder.create(AccountDetailVO.class); - sc.and(sc.entity().getAccountId(), Op.EQ, accountId); + sc.and(sc.entity().getResourceId(), Op.EQ, accountId); List results = sc.list(); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (AccountDetailVO r : results) { details.put(r.getName(), r.getValue()); } @@ -89,11 +87,16 @@ public class AccountDetailsDaoImpl extends GenericDaoBase @Override public AccountDetailVO findDetail(long accountId, String name) { QueryBuilder sc = QueryBuilder.create(AccountDetailVO.class); - sc.and(sc.entity().getAccountId(), Op.EQ, accountId); + sc.and(sc.entity().getResourceId(), Op.EQ, accountId); sc.and(sc.entity().getName(), Op.EQ, name); return sc.find(); } + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new AccountDetailVO(resourceId, key, value)); + } + @Override public void deleteDetails(long accountId) { SearchCriteria sc = accountSearch.create(); @@ -153,13 +156,4 @@ public class AccountDetailsDaoImpl extends GenericDaoBase } return value; } - - @Override - public String getActualValue(AccountDetailVO accountDetailVO) { - ConfigurationVO configurationVO = _configDao.findByName(accountDetailVO.getName()); - if (configurationVO != null && configurationVO.isEncrypted()) { - return DBEncryptionUtil.decrypt(accountDetailVO.getValue()); - } - return accountDetailVO.getValue(); - } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java index 6d0d9378c7c..6b6fe200c10 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java @@ -53,7 +53,7 @@ public interface ResourceDetailsDao extends GenericDao * Removes all details for the resource specified * @param resourceId */ - public void removeDetails(long resourceId); + void removeDetails(long resourceId); /** @@ -76,7 +76,7 @@ public interface ResourceDetailsDao extends GenericDao * @param resourceId * @return list of details each implementing ResourceDetail interface */ - public List listDetails(long resourceId); + List listDetails(long resourceId); /** * List details for resourceId having display field = forDisplay value passed in @@ -84,21 +84,23 @@ public interface ResourceDetailsDao extends GenericDao * @param forDisplay * @return */ - public List listDetails(long resourceId, boolean forDisplay); + List listDetails(long resourceId, boolean forDisplay); - public Map listDetailsKeyPairs(long resourceId); + Map listDetailsKeyPairs(long resourceId); Map listDetailsKeyPairs(long resourceId, List keys); - public Map listDetailsKeyPairs(long resourceId, boolean forDisplay); + Map listDetailsKeyPairs(long resourceId, boolean forDisplay); Map listDetailsVisibility(long resourceId); - public void saveDetails(List details); + void saveDetails(List details); - public void addDetail(long resourceId, String key, String value, boolean display); + void addDetail(long resourceId, String key, String value, boolean display); - public List findResourceIdsByNameAndValueIn(String name, Object[] values); + List findResourceIdsByNameAndValueIn(String name, Object[] values); - public long batchExpungeForResources(List ids, Long batchSize); + long batchExpungeForResources(List ids, Long batchSize); + + String getActualValue(ResourceDetail resourceDetail); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java index f2e156f225a..29d3f88fd90 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java @@ -21,9 +21,9 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.apache.cloudstack.api.ResourceDetail; import org.apache.commons.collections.CollectionUtils; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; @@ -31,7 +31,17 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; +import org.apache.cloudstack.api.ResourceDetail; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.config.impl.ConfigurationVO; + +import javax.inject.Inject; + public abstract class ResourceDetailsDaoBase extends GenericDaoBase implements ResourceDetailsDao { + + @Inject + private ConfigurationDao configDao; + private SearchBuilder AllFieldsSearch; public ResourceDetailsDaoBase() { @@ -76,8 +86,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("value", value); } - List results = search(sc, null); - return results; + return search(sc, null); } public Map listDetailsKeyPairs(long resourceId) { @@ -85,7 +94,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("resourceId", resourceId); List results = search(sc, null); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (R result : results) { details.put(result.getName(), result.getValue()); } @@ -122,8 +131,7 @@ public abstract class ResourceDetailsDaoBase extends G SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); - List results = search(sc, null); - return results; + return search(sc, null); } public void removeDetails(long resourceId) { @@ -185,7 +193,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("display", forDisplay); List results = search(sc, null); - Map details = new HashMap(results.size()); + Map details = new HashMap<>(results.size()); for (R result : results) { details.put(result.getName(), result.getValue()); } @@ -197,8 +205,7 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("resourceId", resourceId); sc.setParameters("display", forDisplay); - List results = search(sc, null); - return results; + return search(sc, null); } @Override @@ -230,4 +237,13 @@ public abstract class ResourceDetailsDaoBase extends G sc.setParameters("ids", ids.toArray()); return batchExpunge(sc, batchSize); } + + @Override + public String getActualValue(ResourceDetail resourceDetail) { + ConfigurationVO configurationVO = configDao.findByName(resourceDetail.getName()); + if (configurationVO != null && configurationVO.isEncrypted()) { + return DBEncryptionUtil.decrypt(resourceDetail.getValue()); + } + return resourceDetail.getValue(); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java index 14830490600..97e41949a57 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDetailsDaoImpl.java @@ -36,7 +36,6 @@ import com.cloud.utils.db.TransactionLegacy; @Component public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase implements ImageStoreDetailsDao, ScopedConfigStorage { - protected final SearchBuilder storeSearch; public ImageStoreDetailsDaoImpl() { @@ -67,7 +66,7 @@ public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase details = listBy(sc); - Map detailsMap = new HashMap(); + Map detailsMap = new HashMap<>(); for (ImageStoreDetailVO detail : details) { String name = detail.getName(); String value = detail.getValue(); @@ -110,9 +109,14 @@ public class ImageStoreDetailsDaoImpl extends ResourceDetailsDaoBase key) { + ImageStoreDetailVO vo = findDetail(id, key.key()); + return vo == null ? null : getActualValue(vo); + } + @Override public void addDetail(long resourceId, String key, String value, boolean display) { super.addDetail(new ImageStoreDetailVO(resourceId, key, value, display)); } - } 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 07b0b8b517c..cb7313954dc 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 @@ -755,7 +755,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase if (keyword != null) { SearchCriteria ssc = createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("poolType", SearchCriteria.Op.LIKE, new Storage.StoragePoolType("%" + keyword + "%")); + ssc.addOr("poolType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index 3456731ef1c..2f1227a91a5 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -40,6 +40,7 @@ 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.DataStoreCapabilities; +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.EndPointSelector; @@ -1534,6 +1535,16 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { verifyFormat(templateInfo.getFormat()); } + // this blurb handles the case where the storage system can clone a volume from a template + String canCloneVolumeFromTemplate = templateInfo.getDataStore().getDriver().getCapabilities().get("CAN_CLONE_VOLUME_FROM_TEMPLATE"); + if (canCloneVolumeFromTemplate != null && canCloneVolumeFromTemplate.toLowerCase().equals("true")) { + DataStoreDriver driver = templateInfo.getDataStore().getDriver(); + driver.createAsync(volumeInfo.getDataStore(), volumeInfo, null); + volumeInfo = _volumeDataFactory.getVolume(volumeInfo.getId(), volumeInfo.getDataStore()); + driver.copyAsync(templateInfo, volumeInfo, null); + return; + } + HostVO hostVO = null; final boolean computeClusterSupportsVolumeClone; @@ -1641,7 +1652,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { errMsg = "Create volume from template failed: " + ex.getMessage(); } - throw new CloudRuntimeException(errMsg); + throw new CloudRuntimeException(errMsg, ex); } finally { if (copyCmdAnswer == null) { @@ -2634,7 +2645,7 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy { catch (Exception ex) { errMsg = ex.getMessage(); - throw new CloudRuntimeException(errMsg); + throw new CloudRuntimeException(errMsg, ex); } finally { if (copyCmdAnswer == null) { diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index bf67be91108..26bef607c9b 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -622,7 +622,7 @@ public class VolumeServiceImpl implements VolumeService { try { Thread.sleep(sleepTime * 1000); } catch (InterruptedException e) { - logger.debug("waiting for template download been interrupted: " + e.toString()); + logger.debug("waiting for template download been interrupted: " + e); } tries--; } @@ -691,7 +691,6 @@ public class VolumeServiceImpl implements VolumeService { } _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); } - return; } protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher callback, ManagedCreateBaseImageContext context) { @@ -1039,7 +1038,7 @@ public class VolumeServiceImpl implements VolumeService { try { grantAccess(templateOnPrimary, destHost, destPrimaryDataStore); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to template: %s on host: %s", templateOnPrimary.getImage(), destHost)); + throw new StorageAccessException(String.format("Unable to grant access to template: %s on host: %s", templateOnPrimary.getImage(), destHost), e); } templateOnPrimary.processEvent(Event.CopyingRequested); @@ -1161,7 +1160,7 @@ public class VolumeServiceImpl implements VolumeService { try { grantAccess(srcTemplateOnPrimary, destHost, destPrimaryDataStore); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to src template: %s on host: %s", srcTemplateOnPrimary, destHost)); + throw new StorageAccessException(String.format("Unable to grant access to src template: %s on host: %s", srcTemplateOnPrimary, destHost), e); } _volumeDetailsDao.addDetail(volumeInfo.getId(), volumeDetailKey, String.valueOf(templatePoolRef.getId()), false); @@ -1408,7 +1407,7 @@ public class VolumeServiceImpl implements VolumeService { try { grantAccess(templateOnPrimary, destHost, destPrimaryDataStore); } catch (Exception e) { - throw new StorageAccessException(String.format("Unable to grant access to template: %s on host: %s", templateOnPrimary, destHost)); + throw new StorageAccessException(String.format("Unable to grant access to template: %s on host: %s", templateOnPrimary, destHost), e); } templateOnPrimary.processEvent(Event.CopyingRequested); diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java index b913033259c..170e3b40e94 100644 --- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/CloudStackExtendedLifeCycle.java @@ -39,7 +39,7 @@ import com.cloud.utils.mgmt.ManagementBean; public class CloudStackExtendedLifeCycle extends AbstractBeanCollector { - Map> sorted = new TreeMap>(); + Map> sorted = new TreeMap<>(); public CloudStackExtendedLifeCycle() { super(); @@ -80,13 +80,8 @@ public class CloudStackExtendedLifeCycle extends AbstractBeanCollector { ManagementBean mbean = (ManagementBean)lifecycle; try { JmxUtil.registerMBean(mbean); - } catch (MalformedObjectNameException e) { - logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (InstanceAlreadyExistsException e) { - logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (MBeanRegistrationException e) { - logger.warn("Unable to register MBean: " + mbean.getName(), e); - } catch (NotCompliantMBeanException e) { + } catch (MalformedObjectNameException | InstanceAlreadyExistsException | + MBeanRegistrationException | NotCompliantMBeanException e) { logger.warn("Unable to register MBean: " + mbean.getName(), e); } logger.info("Registered MBean: " + mbean.getName()); @@ -129,6 +124,7 @@ public class CloudStackExtendedLifeCycle extends AbstractBeanCollector { throw new CloudRuntimeException(e); } catch (Exception e) { logger.error("Error on configuring bean {} - {}", lifecycle.getName(), e.getMessage(), e); + throw new CloudRuntimeException(e); } } }); @@ -141,7 +137,7 @@ public class CloudStackExtendedLifeCycle extends AbstractBeanCollector { Set set = sorted.get(lifecycle.getRunLevel()); if (set == null) { - set = new HashSet(); + set = new HashSet<>(); sorted.put(lifecycle.getRunLevel(), set); } @@ -169,12 +165,7 @@ public class CloudStackExtendedLifeCycle extends AbstractBeanCollector { } } - @Override - public int getPhase() { - return 2000; - } - - private static interface WithComponentLifeCycle { + private interface WithComponentLifeCycle { public void with(ComponentLifecycle lifecycle); } } diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java index 19d1fe3acc5..00e19304657 100644 --- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java @@ -48,7 +48,7 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App * can use this. */ String registryBeanName; - Set beans = new HashSet(); + Set beans = new HashSet<>(); Class typeClass; ApplicationContext applicationContext; Set excludes = null; @@ -79,7 +79,7 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App protected synchronized void loadExcluded() { Properties props = applicationContext.getBean("DefaultConfigProperties", Properties.class); - excludes = new HashSet(); + excludes = new HashSet<>(); for (String exclude : props.getProperty(EXTENSION_EXCLUDE, "").trim().split("\\s*,\\s*")) { if (StringUtils.hasText(exclude)) { excludes.add(exclude); @@ -109,10 +109,15 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App while (iter.hasNext()) { Object next = iter.next(); - if (registry.register(next)) { - logger.debug("Registered " + next); - } else { - iter.remove(); + try { + if (registry.register(next)) { + logger.debug("Registered " + next); + } else { + logger.warn("Bean registration failed for " + next.toString()); + iter.remove(); + } + } catch (Throwable e) { + logger.warn("Bean registration attempt resulted in an exception for " + next.toString(), e); } } } diff --git a/plugins/acl/dynamic-role-based/src/main/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java b/plugins/acl/dynamic-role-based/src/main/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java index 030e0bcf014..6bbd25bb440 100644 --- a/plugins/acl/dynamic-role-based/src/main/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java +++ b/plugins/acl/dynamic-role-based/src/main/java/org/apache/cloudstack/acl/DynamicRoleBasedAPIAccessChecker.java @@ -47,7 +47,7 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API private RoleService roleService; private List services; - private Map> annotationRoleBasedApisMap = new HashMap>(); + private Map> annotationRoleBasedApisMap = new HashMap<>(); private LazyCache accountCache; private LazyCache>> rolePermissionsCache; @@ -56,7 +56,7 @@ public class DynamicRoleBasedAPIAccessChecker extends AdapterBase implements API protected DynamicRoleBasedAPIAccessChecker() { super(); for (RoleType roleType : RoleType.values()) { - annotationRoleBasedApisMap.put(roleType, new HashSet()); + annotationRoleBasedApisMap.put(roleType, new HashSet<>()); } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 47edd2eff18..773df59431d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3133,7 +3133,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv disk.setLogicalBlockIOSize(pool.getSupportedLogicalBlockSize()); disk.setPhysicalBlockIOSize(pool.getSupportedPhysicalBlockSize()); - if (diskBusType == DiskDef.DiskBus.SCSI ) { + if (diskBusType == DiskDef.DiskBus.SCSI || diskBusType == DiskDef.DiskBus.VIRTIOBLK) { disk.setQemuDriver(true); disk.setDiscard(DiscardType.UNMAP); } @@ -3204,7 +3204,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv disk.setCacheMode(DiskDef.DiskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString().toUpperCase())); } - if (volumeObjectTO.requiresEncryption()) { + if (volumeObjectTO.requiresEncryption() && + pool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor ) { String secretUuid = createLibvirtVolumeSecret(conn, volumeObjectTO.getPath(), volumeObjectTO.getPassphrase()); DiskDef.LibvirtDiskEncryptDetails encryptDetails = new DiskDef.LibvirtDiskEncryptDetails(secretUuid, QemuObject.EncryptFormat.enumValue(volumeObjectTO.getEncryptFormat())); disk.setLibvirtDiskEncryptDetails(encryptDetails); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 39373ab6e3b..c34c151aad2 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -248,7 +248,9 @@ public class LibvirtVMDef { guestDef.append("\n"); } } - guestDef.append("\n"); + if (_arch == null || !_arch.equals("aarch64")) { + guestDef.append("\n"); + } guestDef.append("\n"); if (iothreads) { guestDef.append(String.format("%s", NUMBER_OF_IOTHREADS)); @@ -678,7 +680,7 @@ public class LibvirtVMDef { } public enum DiskBus { - IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML("uml"), FDC("fdc"), SATA("sata"); + IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML("uml"), FDC("fdc"), SATA("sata"), VIRTIOBLK("virtio-blk"); String _bus; DiskBus(String bus) { diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java index 9495646bad5..70b1715cc20 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java @@ -122,7 +122,10 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra instance.setName(domain.getName()); instance.setCpuCores((int) LibvirtComputingResource.countDomainRunningVcpus(domain)); - instance.setCpuSpeed(parser.getCpuTuneDef().getShares()/instance.getCpuCores()); + + if (parser.getCpuTuneDef() != null && instance.getCpuCores() != null) { + instance.setCpuSpeed(parser.getCpuTuneDef().getShares()/instance.getCpuCores()); + } if (parser.getCpuModeDef() != null) { instance.setCpuCoresPerSocket(parser.getCpuModeDef().getCoresPerSocket()); 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 92e4570170e..5e62671dd22 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 @@ -125,6 +125,7 @@ import com.cloud.hypervisor.kvm.resource.wrapper.LibvirtUtilitiesHelper; import com.cloud.storage.JavaStorageLayer; import com.cloud.storage.MigrationOptions; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; @@ -161,7 +162,7 @@ public class KVMStorageProcessor implements StorageProcessor { /** * Time interval before rechecking virsh commands */ - private long waitDelayForVirshCommands = 1000l; + private final long waitDelayForVirshCommands = 1000L; public KVMStorageProcessor(final KVMStoragePoolManager storagePoolMgr, final LibvirtComputingResource resource) { this.storagePoolMgr = storagePoolMgr; @@ -258,7 +259,7 @@ public class KVMStorageProcessor implements StorageProcessor { logger.debug("Copying template to primary storage, template format is " + tmplVol.getFormat() ); final KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - KVMPhysicalDisk primaryVol = null; + KVMPhysicalDisk primaryVol; if (destData instanceof VolumeObjectTO) { final VolumeObjectTO volume = (VolumeObjectTO)destData; // pass along volume's target size if it's bigger than template's size, for storage types that copy template rather than cloning on deploy @@ -277,8 +278,13 @@ public class KVMStorageProcessor implements StorageProcessor { String path = derivePath(primaryStore, destData, details); - if (!storagePoolMgr.connectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path, details)) { + if (path == null) { + path = destTempl.getUuid(); + } + + if (path != null && !storagePoolMgr.connectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path, details)) { logger.warn("Failed to connect physical disk at path: {}, in storage pool [id: {}, name: {}]", path, primaryStore.getUuid(), primaryStore.getName()); + return new PrimaryStorageDownloadAnswer("Failed to spool template disk at path: " + path + ", in storage pool id: " + primaryStore.getUuid()); } primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, path != null ? path : destTempl.getUuid(), primaryPool, cmd.getWaitInMillSeconds()); @@ -336,12 +342,13 @@ public class KVMStorageProcessor implements StorageProcessor { } private String derivePath(PrimaryDataStoreTO primaryStore, DataTO destData, Map details) { - String path = null; + String path; if (primaryStore.getPoolType() == StoragePoolType.FiberChannel) { path = destData.getPath(); } else { path = details != null ? details.get("managedStoreTarget") : null; } + return path; } @@ -390,8 +397,7 @@ public class KVMStorageProcessor implements StorageProcessor { logger.debug("Using templates disk size of " + toHumanReadableSize(templateVol.getVirtualSize()) + "since size passed was " + toHumanReadableSize(size)); } - final KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, volUuid, primaryPool, timeout); - return primaryVol; + return storagePoolMgr.copyPhysicalDisk(templateVol, volUuid, primaryPool, timeout); } catch (final CloudRuntimeException e) { logger.error("Failed to download template to primary storage", e); return null; @@ -410,9 +416,9 @@ public class KVMStorageProcessor implements StorageProcessor { final DataStoreTO imageStore = template.getDataStore(); final VolumeObjectTO volume = (VolumeObjectTO)destData; final PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); - KVMPhysicalDisk BaseVol = null; - KVMStoragePool primaryPool = null; - KVMPhysicalDisk vol = null; + KVMPhysicalDisk BaseVol; + KVMStoragePool primaryPool; + KVMPhysicalDisk vol; try { primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); @@ -420,7 +426,7 @@ public class KVMStorageProcessor implements StorageProcessor { String templatePath = template.getPath(); if (primaryPool.getType() == StoragePoolType.CLVM) { - templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; + templatePath = imageStore.getUrl() + File.separator + templatePath; vol = templateToPrimaryDownload(templatePath, primaryPool, volume.getUuid(), volume.getSize(), cmd.getWaitInMillSeconds()); } if (storagePoolMgr.supportsPhysicalDiskCopy(primaryPool.getType())) { Map details = primaryStore.getDetails(); @@ -778,15 +784,19 @@ public class KVMStorageProcessor implements StorageProcessor { KVMStoragePool secondaryStorage = null; + String path = null; try { // look for options indicating an overridden path or IQN. Used when snapshots have to be // temporarily copied on the manaaged storage device before the actual copy to target object Map details = cmd.getOptions(); - String path = details != null ? details.get(DiskTO.PATH) : null; + path = details != null ? details.get(DiskTO.PATH) : null; if (path == null) { path = details != null ? details.get(DiskTO.IQN) : null; if (path == null) { - new CloudRuntimeException("The 'path' or 'iqn' field must be specified."); + path = srcData.getPath(); + if (path == null) { + new CloudRuntimeException("The 'path' or 'iqn' field must be specified."); + } } } @@ -849,8 +859,6 @@ public class KVMStorageProcessor implements StorageProcessor { loc.addFormat(info); loc.save(); - storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path); - TemplateObjectTO newTemplate = new TemplateObjectTO(); newTemplate.setPath(templateFolder + File.separator + templateName + ".qcow2"); @@ -870,6 +878,10 @@ public class KVMStorageProcessor implements StorageProcessor { return new CopyCmdAnswer(ex.toString()); } finally { + if (path != null) { + storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path); + } + if (secondaryStorage != null) { secondaryStorage.delete(); } @@ -1045,7 +1057,9 @@ public class KVMStorageProcessor implements StorageProcessor { command.add(NAME_OPTION, snapshotName); command.add("-p", snapshotDestPath); - descName = UUID.randomUUID().toString(); + if (isCreatedFromVmSnapshot) { + descName = UUID.randomUUID().toString(); + } command.add("-t", descName); final String result = command.execute(); @@ -1415,12 +1429,14 @@ public class KVMStorageProcessor implements StorageProcessor { if (disk.getDeviceType() == DeviceType.DISK) { if (disk.getBusType() == DiskDef.DiskBus.SCSI) { busT = DiskDef.DiskBus.SCSI; + } else if (disk.getBusType() == DiskDef.DiskBus.VIRTIOBLK) { + busT = DiskDef.DiskBus.VIRTIOBLK; } break; } } diskdef = new DiskDef(); - if (busT == DiskDef.DiskBus.SCSI) { + if (busT == DiskDef.DiskBus.SCSI || busT == DiskDef.DiskBus.VIRTIOBLK) { diskdef.setQemuDriver(true); diskdef.setDiscard(DiscardType.UNMAP); } @@ -1459,7 +1475,8 @@ public class KVMStorageProcessor implements StorageProcessor { } } - if (encryptDetails != null) { + if (encryptDetails != null && + attachingPool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor) { diskdef.setLibvirtDiskEncryptDetails(encryptDetails); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java index 0cf8ce0018d..7ba29ffc26e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java @@ -162,6 +162,13 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor { KVMPhysicalDisk disk = new KVMPhysicalDisk(address.getPath(), address.toString(), pool); disk.setFormat(QemuImg.PhysicalDiskFormat.RAW); + // validate we have a connection, if not we need to connect first. + if (!isConnected(address.getPath())) { + if (!connectPhysicalDisk(address, pool, null)) { + throw new CloudRuntimeException("Unable to connect to volume " + address.getPath()); + } + } + long diskSize = getPhysicalDiskSize(address.getPath()); disk.setSize(diskSize); disk.setVirtualSize(diskSize); @@ -199,6 +206,10 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor { // we expect WWN values in the volumePath so need to convert it to an actual physical path AddressInfo address = this.parseAndValidatePath(volumePath); + return connectPhysicalDisk(address, pool, details); + } + + private boolean connectPhysicalDisk(AddressInfo address, KVMStoragePool pool, Map details) { // validate we have a connection id - we can't proceed without that if (address.getConnectionId() == null) { LOGGER.error("Unable to connect volume with address [" + address.getPath() + "] of the storage pool: " + pool.getUuid() + " - connection id is not set in provided path"); @@ -510,6 +521,18 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor { return false; } + boolean isConnected(String path) { + // run a command to test if this is a binary device at this path + Script blockTest = new Script("/bin/test", LOGGER); + blockTest.add("-b", path); + blockTest.execute(); + int rc = blockTest.getExitValue(); + if (rc == 0) { + return true; + } + return false; + } + long getPhysicalDiskSize(String diskPath) { if (StringUtils.isEmpty(diskPath)) { return 0; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 7faebb01b6b..753bce31c25 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -6531,4 +6531,14 @@ public class LibvirtComputingResourceTest { assertEquals(DiskDef.DiscardType.UNMAP, rootDisk.getDiscard()); } } + + @Test + public void testGetDiskModelFromVMDetailVirtioBlk() { + VirtualMachineTO virtualMachineTO = Mockito.mock(VirtualMachineTO.class); + Map details = new HashMap<>(); + details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, "virtio-blk"); + Mockito.when(virtualMachineTO.getDetails()).thenReturn(details); + DiskDef.DiskBus diskBus = libvirtComputingResourceSpy.getDiskModelFromVMDetail(virtualMachineTO); + assertEquals(DiskDef.DiskBus.VIRTIOBLK, diskBus); + } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index b8bf08cd01a..c02513f4889 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import com.cloud.agent.api.CleanupVMCommand; import javax.inject.Inject; import com.cloud.agent.api.to.NfsTO; @@ -370,6 +371,13 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co return tokens[0] + "@" + vCenterIp; } + @Override public List finalizeExpunge(VirtualMachine vm) { + List commands = new ArrayList(); + final CleanupVMCommand cleanupVMCommand = new CleanupVMCommand(vm.getInstanceName(), true); + commands.add(cleanupVMCommand); + return commands; + } + @Override public List finalizeExpungeNics(VirtualMachine vm, List nics) { List commands = new ArrayList(); List nicVOs = nicDao.listByVmId(vm.getId()); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java index bbac78b879a..6bb473536b8 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java @@ -22,12 +22,15 @@ import com.cloud.dc.VmwareDatacenterVO; import com.cloud.dc.VsphereStoragePolicy; import com.cloud.exception.DiscoveryException; import com.cloud.exception.ResourceInUseException; +import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.storage.StoragePool; +import com.cloud.utils.Pair; import com.cloud.utils.component.PluggableService; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd; import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcVmsCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcHostsCmd; import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd; import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd; @@ -53,5 +56,7 @@ public interface VmwareDatacenterService extends PluggableService { List listVsphereStoragePolicyCompatibleStoragePools(ListVsphereStoragePolicyCompatiblePoolsCmd cmd); - List listVMsInDatacenter(ListVmwareDcVmsCmd cmd); + List listHostsInDatacenter(ListVmwareDcHostsCmd cmd); + + Pair> listVMsInDatacenter(ListVmwareDcVmsCmd cmd); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 561034155c2..b64422482aa 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -19,10 +19,12 @@ package com.cloud.hypervisor.vmware.manager; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.rmi.RemoteException; import java.time.Duration; import java.time.Instant; @@ -43,10 +45,11 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; -import com.cloud.hypervisor.vmware.util.VmwareClient; import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd; import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcVmsCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcHostsCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcItems; import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd; import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd; @@ -86,6 +89,7 @@ import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.ClusterVSMMapVO; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.VmwareDatacenter; import com.cloud.dc.VsphereStoragePolicy; import com.cloud.dc.VsphereStoragePolicyVO; import com.cloud.dc.dao.ClusterDao; @@ -111,7 +115,8 @@ import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.vmware.LegacyZoneVO; import com.cloud.hypervisor.vmware.VmwareCleanupMaid; -import com.cloud.dc.VmwareDatacenter; +import com.cloud.hypervisor.vmware.util.VmwareClient; +import com.cloud.hypervisor.vmware.util.VmwareClientException; import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.dc.VmwareDatacenterVO; import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; @@ -168,9 +173,16 @@ import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.VMInstanceDao; + +// TODO move these items upstream? import com.vmware.pbm.PbmProfile; import com.vmware.vim25.AboutInfo; import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.InvalidLocaleFaultMsg; +import com.vmware.vim25.InvalidLoginFaultMsg; +import com.vmware.vim25.RuntimeFaultFaultMsg; +import com.vmware.vim25.InvalidPropertyFaultMsg; +import org.jetbrains.annotations.NotNull; public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener, VmwareDatacenterService, Configurable { @@ -245,11 +257,11 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private StorageLayer _storage; private final String _privateNetworkVSwitchName = "vSwitch0"; - private int _portsPerDvPortGroup = DEFAULT_PORTS_PER_DV_PORT_GROUP; + private final int _portsPerDvPortGroup = DEFAULT_PORTS_PER_DV_PORT_GROUP; private boolean _fullCloneFlag; private boolean _instanceNameFlag; private String _serviceConsoleName; - private String _managemetPortGroupName; + private String _managementPortGroupName; private String _defaultSystemVmNicAdapterType = VirtualEthernetCardType.E1000.toString(); private String _recycleHungWorker = "false"; private int _additionalPortRangeStart; @@ -263,7 +275,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private final Random _rand = new Random(System.currentTimeMillis()); - private static ScheduledExecutorService templateCleanupScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vmware-FullyClonedTemplateCheck"));; + private static final ScheduledExecutorService templateCleanupScheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Vmware-FullyClonedTemplateCheck")); private final VmwareStorageManager _storageMgr; private final GlobalLock _exclusiveOpLock = GlobalLock.getInternLock("vmware.exclusive.op"); @@ -347,9 +359,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw _serviceConsoleName = "Service Console"; } - _managemetPortGroupName = _configDao.getValue(Config.VmwareManagementPortGroup.key()); - if (_managemetPortGroupName == null) { - _managemetPortGroupName = "Management Network"; + _managementPortGroupName = _configDao.getValue(Config.VmwareManagementPortGroup.key()); + if (_managementPortGroupName == null) { + _managementPortGroupName = "Management Network"; } _defaultSystemVmNicAdapterType = _configDao.getValue(Config.VmwareSystemVmNicDeviceType.key()); @@ -448,7 +460,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw logger.info("Preparing network on host " + hostMo.getContext().toString() + " for " + privateTrafficLabel); VirtualSwitchType vsType = VirtualSwitchType.getType(vSwitchType); - //The management network is probably always going to be a physical network with islation type of vlans, so assume BroadcastDomainType VLAN + //The management network is probably always going to be a physical network with isolation type of vlans, so assume BroadcastDomainType VLAN if (VirtualSwitchType.StandardVirtualSwitch == vsType) { HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, 180000, false, BroadcastDomainType.Vlan, null, null); } @@ -457,7 +469,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw AboutInfo about = hostMo.getHostAboutInfo(); if (about != null) { String version = about.getApiVersion(); - if (version != null && (version.equals("4.0") || version.equals("4.1")) && _portsPerDvPortGroup < DEFAULT_PORTS_PER_DV_PORT_GROUP_VSPHERE4_x) { + if (version != null && (version.equals("4.0") || version.equals("4.1")) ) { // && _portsPerDvPortGroup < DEFAULT_PORTS_PER_DV_PORT_GROUP_VSPHERE4_x) portsPerDvPortGroup = DEFAULT_PORTS_PER_DV_PORT_GROUP_VSPHERE4_x; } } @@ -480,7 +492,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } URI uriForHost = new URI(UriUtils.encodeURIComponent(clusterDetails.get("url") + "/" + host.getName())); - morSrcHost = serviceContext.getHostMorByPath(URLDecoder.decode(uriForHost.getPath(), "UTF-8")); + morSrcHost = serviceContext.getHostMorByPath(URLDecoder.decode(uriForHost.getPath(), StandardCharsets.UTF_8)); if (morSrcHost == null) { return null; } @@ -496,19 +508,18 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw throw new CloudRuntimeException("Invalid serviceContext"); } ManagedObjectReference mor = serviceContext.getHostMorByPath(hostInventoryPath); - String privateTrafficLabel = null; + String privateTrafficLabel; privateTrafficLabel = serviceContext.getStockObject("privateTrafficLabel"); if (privateTrafficLabel == null) { privateTrafficLabel = _privateNetworkVSwitchName; } if (mor != null) { - List returnedHostList = new ArrayList(); + List returnedHostList = new ArrayList<>(); if (mor.getType().equals("ComputeResource")) { List hosts = serviceContext.getVimClient().getDynamicProperty(mor, "host"); - assert (hosts != null && hosts.size() > 0); - + assert (CollectionUtils.isNullOrEmpty(hosts)); // For ESX host, we need to enable host firewall to allow VNC access HostMO hostMo = new HostMO(serviceContext, hosts.get(0)); @@ -519,8 +530,8 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw List hosts = serviceContext.getVimClient().getDynamicProperty(mor, "host"); assert (hosts != null); - if (hosts.size() > 0) { - AboutInfo about = (AboutInfo)(serviceContext.getVimClient().getDynamicProperty(hosts.get(0), "config.product")); + if (!hosts.isEmpty()) { + AboutInfo about = serviceContext.getVimClient().getDynamicProperty(hosts.get(0), "config.product"); String version = about.getApiVersion(); int maxHostsPerCluster = _hvCapabilitiesDao.getMaxHostsPerCluster(HypervisorType.VMware, version); if (hosts.size() > maxHostsPerCluster) { @@ -549,7 +560,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw returnedHostList.add(mor); return returnedHostList; } else { - logger.error("Unsupport host type " + mor.getType() + ":" + mor.getValue() + " from inventory path: " + hostInventoryPath); + logger.error("Unsupport host type {}:{} from inventory path: {}", mor.getType(), mor.getValue(), hostInventoryPath); return null; } } @@ -614,13 +625,13 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public String getManagementPortGroupName() { - return _managemetPortGroupName; + return _managementPortGroupName; } @Override public String getManagementPortGroupByHost(HostMO hostMo) throws Exception { if (hostMo.getHostType() == VmwareHostType.ESXi) { - return _managemetPortGroupName; + return _managementPortGroupName; } return _serviceConsoleName; } @@ -630,7 +641,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw params.put("vmware.create.full.clone", _fullCloneFlag); params.put("vm.instancename.flag", _instanceNameFlag); params.put("service.console.name", _serviceConsoleName); - params.put("management.portgroup.name", _managemetPortGroupName); + params.put("management.portgroup.name", _managementPortGroupName); params.put("vmware.root.disk.controller", _rootDiskController); params.put("vmware.data.disk.controller", _dataDiskController); params.put("vmware.recycle.hung.wokervm", _recycleHungWorker); @@ -657,23 +668,23 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw return false; } - String tokens[] = workerTag.split("-"); + String[] tokens = workerTag.split("-"); if (tokens.length != 3) { logger.error("Invalid worker VM tag " + workerTag); return false; } long startTick = Long.parseLong(tokens[0]); - long msid = Long.parseLong(tokens[1]); - long runid = Long.parseLong(tokens[2]); + long msId = Long.parseLong(tokens[1]); + long runId = Long.parseLong(tokens[2]); - if (msHostPeerDao.countStateSeenInPeers(msid, runid, ManagementServerHost.State.Down) > 0) { + if (msHostPeerDao.countStateSeenInPeers(msId, runId, ManagementServerHost.State.Down) > 0) { if (logger.isInfoEnabled()) logger.info("Worker VM's owner management server node has been detected down from peer nodes, recycle it"); return true; } - if (runid != clusterManager.getManagementRunId(msid)) { + if (runId != clusterManager.getManagementRunId(msId)) { if (logger.isInfoEnabled()) logger.info("Worker VM's owner management server has changed runid, recycle it"); return true; @@ -710,7 +721,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw File patchFolder = new File(mountPoint + "/systemvm"); if (!patchFolder.exists()) { if (!patchFolder.mkdirs()) { - String msg = "Unable to create systemvm folder on secondary storage. location: " + patchFolder.toString(); + String msg = "Unable to create systemvm folder on secondary storage. location: " + patchFolder; logger.error(msg); throw new CloudRuntimeException(msg); } @@ -729,7 +740,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } catch (IOException e) { logger.error("Unexpected exception ", e); - String msg = "Unable to copy systemvm ISO on secondary storage. src location: " + srcIso.toString() + ", dest location: " + destIso; + String msg = "Unable to copy systemvm ISO on secondary storage. src location: " + srcIso + ", dest location: " + destIso; logger.error(msg); throw new CloudRuntimeException(msg); } @@ -771,9 +782,8 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw isoFile = new File("/usr/share/cloudstack-common/vms/systemvm.iso"); } - assert (isoFile != null); if (!isoFile.exists()) { - logger.error("Unable to locate systemvm.iso in your setup at " + isoFile.toString()); + logger.error("Unable to locate systemvm.iso in your setup at " + isoFile); } return isoFile; } @@ -788,16 +798,16 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw if (keyFile == null || !keyFile.exists()) { keyFile = new File("/usr/share/cloudstack-common/scripts/vm/systemvm/id_rsa.cloud"); } - assert (keyFile != null); + if (!keyFile.exists()) { - logger.error("Unable to locate id_rsa.cloud in your setup at " + keyFile.toString()); + logger.error("Unable to locate id_rsa.cloud in your setup at " + keyFile); } return keyFile; } @Override public String getMountPoint(String storageUrl, String nfsVersion) { - String mountPoint = null; + String mountPoint; synchronized (_storageMounts) { mountPoint = _storageMounts.get(storageUrl); if (mountPoint != null) { @@ -827,7 +837,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw String mountPoint = null; long mshostId = ManagementServerNode.getManagementServerId(); for (int i = 0; i < 10; i++) { - String mntPt = parent + File.separator + String.valueOf(mshostId) + "." + Integer.toHexString(_rand.nextInt(Integer.MAX_VALUE)); + String mntPt = parent + File.separator + mshostId + "." + Integer.toHexString(_rand.nextInt(Integer.MAX_VALUE)); File file = new File(mntPt); if (!file.exists()) { if (_storage.mkdir(mntPt)) { @@ -852,10 +862,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw for (String mountPoint : mounts) { logger.info("umount NFS mount from previous session: " + mountPoint); - String result = null; Script command = new Script(true, "umount", _timeout, logger); command.add(mountPoint); - result = command.execute(); + String result = command.execute(); if (result != null) { logger.warn("Unable to umount " + mountPoint + " due to " + result); } @@ -873,10 +882,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw for (String mountPoint : _storageMounts.values()) { logger.info("umount NFS mount: " + mountPoint); - String result = null; Script command = new Script(true, "umount", _timeout, logger); command.add(mountPoint); - result = command.execute(); + String result = command.execute(); if (result != null) { logger.warn("Unable to umount " + mountPoint + " due to " + result); } @@ -894,8 +902,8 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw return null; } - Script script = null; - String result = null; + Script script; + String result; Script command = new Script(true, "mount", _timeout, logger); command.add("-t", "nfs"); if (nfsVersion != null){ @@ -982,11 +990,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) { - if (cmd instanceof StartupCommand) { + if (cmd != null) { if (host.getHypervisorType() == HypervisorType.VMware) { updateClusterNativeHAState(host, cmd); - } else { - return; } } } @@ -1056,16 +1062,16 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public Pair getAddiionalVncPortRange() { - return new Pair(_additionalPortRangeStart, _additionalPortRangeSize); + return new Pair<>(_additionalPortRangeStart, _additionalPortRangeSize); } @Override public Map getNexusVSMCredentialsByClusterId(Long clusterId) { - CiscoNexusVSMDeviceVO nexusVSM = null; - ClusterVSMMapVO vsmMapVO = null; + CiscoNexusVSMDeviceVO nexusVSM; + ClusterVSMMapVO vsmMapVO; vsmMapVO = _vsmMapDao.findByClusterId(clusterId); - long vsmId = 0; + long vsmId; if (vsmMapVO != null) { vsmId = vsmMapVO.getVsmId(); logger.info("vsmId is " + vsmId); @@ -1076,7 +1082,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw return null; } - Map nexusVSMCredentials = new HashMap(); + Map nexusVSMCredentials = new HashMap<>(); if (nexusVSM != null) { nexusVSMCredentials.put("vsmip", nexusVSM.getipaddr()); nexusVSMCredentials.put("vsmusername", nexusVSM.getUserName()); @@ -1103,7 +1109,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public List> getCommands() { - List> cmdList = new ArrayList>(); + List> cmdList = new ArrayList<>(); cmdList.add(AddVmwareDcCmd.class); cmdList.add(UpdateVmwareDcCmd.class); cmdList.add(RemoveVmwareDcCmd.class); @@ -1112,13 +1118,14 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw cmdList.add(ListVsphereStoragePoliciesCmd.class); cmdList.add(ListVsphereStoragePolicyCompatiblePoolsCmd.class); cmdList.add(ListVmwareDcVmsCmd.class); + cmdList.add(ListVmwareDcHostsCmd.class); return cmdList; } @Override @DB public VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws ResourceInUseException { - VmwareDatacenterVO vmwareDc = null; + VmwareDatacenterVO vmwareDc; Long zoneId = cmd.getZoneId(); String userName = cmd.getUsername(); String password = cmd.getPassword(); @@ -1174,10 +1181,10 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw checkIfDcIsUsed(vCenterHost, vmwareDcName, zoneId); VmwareContext context = null; - DatacenterMO dcMo = null; + DatacenterMO dcMo; String dcCustomFieldValue; boolean addDcCustomFieldDef = false; - boolean dcInUse = false; + boolean dcInUse; String guid; ManagedObjectReference dcMor; try { @@ -1210,7 +1217,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Map zone with vmware datacenter vmwareDcZoneMap = new VmwareDatacenterZoneMapVO(zoneId, vmwareDc.getId()); - vmwareDcZoneMap = vmwareDatacenterZoneMapDao.persist(vmwareDcZoneMap); + vmwareDatacenterZoneMapDao.persist(vmwareDcZoneMap); // Set custom field for this DC if (addDcCustomFieldDef) { @@ -1230,7 +1237,6 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw if (context != null) { context.close(); } - context = null; } importVsphereStoragePoliciesInternal(zoneId, vmwareDc.getId()); return vmwareDc; @@ -1255,9 +1261,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw * Check if DC is already part of zone * In that case vmware_data_center table should have the DC and a dc zone mapping should exist * - * @param vCenterHost - * @param vmwareDcName - * @param zoneId + * @param vCenterHost the vcenter appliance hostname + * @param vmwareDcName the name of the vmware DC + * @param zoneId zone that the DC should be connected to * @throws ResourceInUseException if the DC can not be used. */ private void checkIfDcIsUsed(String vCenterHost, String vmwareDcName, Long zoneId) throws ResourceInUseException { @@ -1265,7 +1271,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw vmwareDc = vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost); if (vmwareDc != null) { VmwareDatacenterZoneMapVO mapping = vmwareDatacenterZoneMapDao.findByVmwareDcId(vmwareDc.getId()); - if (mapping != null && Long.compare(zoneId, mapping.getZoneId()) == 0) { + if (mapping != null && zoneId == mapping.getZoneId()) { throw new ResourceInUseException(String.format("This DC (%s) is already part of other CloudStack zone (%d). Cannot add this DC to more zones.", vmwareDc.getUuid(), zoneId)); } } @@ -1274,7 +1280,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override @ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "updating VMware datacenter") public VmwareDatacenter updateVmwareDatacenter(UpdateVmwareDcCmd cmd) { - final Long zoneId = cmd.getZoneId(); + final long zoneId = cmd.getZoneId(); final String userName = cmd.getUsername(); final String password = cmd.getPassword(); final String vCenterHost = cmd.getVcenter(); @@ -1302,7 +1308,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } vmwareDc.setGuid(String.format("%s@%s", vmwareDc.getVmwareDatacenterName(), vmwareDc.getVcenterHost())); - return Transaction.execute(new TransactionCallback() { + return Transaction.execute(new TransactionCallback<>() { @Override public VmwareDatacenter doInTransaction(TransactionStatus status) { if (vmwareDcDao.update(vmwareDc.getId(), vmwareDc)) { @@ -1351,7 +1357,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw String vCenterHost; String userName; String password; - DatacenterMO dcMo = null; + DatacenterMO dcMo; final VmwareDatacenterZoneMapVO vmwareDcZoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId); // Check if zone is associated with VMware DC if (vmwareDcZoneMap == null) { @@ -1388,11 +1394,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw throw new DiscoveryException(msg); } - assert (dcMo != null); - // Reset custom field property cloud.zone over this DC dcMo.setCustomFieldValue(CustomFieldConstants.CLOUD_ZONE, "false"); - logger.info("Sucessfully reset custom field property cloud.zone over DC " + vmwareDcName); + logger.info("Sucessfully reset custom field property cloud.zone over DC {}", vmwareDcName); } catch (Exception e) { String msg = "Unable to reset custom field property cloud.zone over DC " + vmwareDcName + " due to : " + VmwareHelper.getExceptionMessage(e); logger.error(msg); @@ -1401,7 +1405,6 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw if (context != null) { context.close(); } - context = null; } return true; } @@ -1422,7 +1425,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private void validateZoneWithResources(Long zoneId, String errStr) throws ResourceInUseException { // Check if zone has resources? - For now look for clusters List clusters = clusterDao.listByZoneId(zoneId); - if (clusters != null && clusters.size() > 0) { + if (!CollectionUtils.isNullOrEmpty(clusters)) { // Look for VMware hypervisor. for (ClusterVO cluster : clusters) { if (cluster.getHypervisorType().equals(HypervisorType.VMware)) { @@ -1443,9 +1446,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } @Override - public List listVmwareDatacenters(ListVmwareDcsCmd cmd) throws CloudRuntimeException, InvalidParameterValueException { + public List listVmwareDatacenters(ListVmwareDcsCmd cmd) throws CloudRuntimeException { Long zoneId = cmd.getZoneId(); - List vmwareDcList = new ArrayList(); + List vmwareDcList = new ArrayList<>(); VmwareDatacenterZoneMapVO vmwareDcZoneMap; VmwareDatacenterVO vmwareDatacenter; long vmwareDcId; @@ -1503,7 +1506,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw String vCenterHost = vmwareDatacenter.getVcenterHost(); String userName = vmwareDatacenter.getUser(); String password = vmwareDatacenter.getPassword(); - List storageProfiles = null; + List storageProfiles; try { logger.debug(String.format("Importing vSphere Storage Policies for the vmware DC %d in zone %d", vmwareDcId, zoneId)); VmwareContext context = VmwareContextFactory.getContext(vCenterHost, userName, password); @@ -1531,16 +1534,15 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw List allStoragePolicies = vsphereStoragePolicyDao.listAll(); List finalStorageProfiles = storageProfiles; List needToMarkRemoved = allStoragePolicies.stream() - .filter(existingPolicy -> !finalStorageProfiles.stream() - .anyMatch(storageProfile -> storageProfile.getProfileId().getUniqueId().equals(existingPolicy.getPolicyId()))) + .filter(existingPolicy -> finalStorageProfiles.stream() + .noneMatch(storageProfile -> storageProfile.getProfileId().getUniqueId().equals(existingPolicy.getPolicyId()))) .collect(Collectors.toList()); for (VsphereStoragePolicyVO storagePolicy : needToMarkRemoved) { vsphereStoragePolicyDao.remove(storagePolicy.getId()); } - List storagePolicies = vsphereStoragePolicyDao.listAll(); - return storagePolicies; + return vsphereStoragePolicyDao.listAll(); } @Override @@ -1586,13 +1588,87 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } @Override - public List listVMsInDatacenter(ListVmwareDcVmsCmd cmd) { + public List listHostsInDatacenter(ListVmwareDcHostsCmd cmd) { + VcenterData vmwaredc = getVcenterData(cmd); + + try { + VmwareContext context = getVmwareContext(vmwaredc); + DatacenterMO dcMo = getDatacenterMO(context, vmwaredc); + return dcMo.getAllHostsOnDatacenter(); + } catch (RuntimeFaultFaultMsg | URISyntaxException | VmwareClientException | InvalidLocaleFaultMsg | + InvalidLoginFaultMsg | InvalidPropertyFaultMsg e) { + String errorMsg = String.format("Error retrieving stopped VMs from the VMware VC %s datacenter %s: %s", + vmwaredc.vcenter, vmwaredc.datacenterName, e.getMessage()); + logger.error(errorMsg, e); + throw new CloudRuntimeException(errorMsg); + } + + } + + @Override + public Pair> listVMsInDatacenter(ListVmwareDcVmsCmd cmd) { + Integer maxObjects = cmd.getBatchSize(); + String token = cmd.getToken(); + String host = cmd.getHost(); + + VcenterData vmwaredc = getVcenterData(cmd); + + try { + VmwareContext context = getVmwareContext(vmwaredc); + + DatacenterMO dcMo = getDatacenterMO(context, vmwaredc); + + if (com.cloud.utils.StringUtils.isNotBlank(host)) { + ManagedObjectReference hostMor = dcMo.findHost(host); + if (hostMor == null) { + throw new VmwareClientException(String.format("No host '%s' found on DC: %s.", host, dcMo.getName())); + } + HostMO hostMo = new HostMO(context, hostMor); + return hostMo.getVms(maxObjects, token); + } else { + return dcMo.getVms(maxObjects, token); + } + } catch (InvalidParameterValueException | VmwareClientException | InvalidLocaleFaultMsg | InvalidLoginFaultMsg | + RuntimeFaultFaultMsg | URISyntaxException | InvalidPropertyFaultMsg | InvocationTargetException | + NoSuchMethodException | IllegalAccessException e) { + String errorMsg = String.format("Error retrieving stopped VMs from the VMware VC %s datacenter %s: %s", + vmwaredc.vcenter, vmwaredc.datacenterName, e.getMessage()); + logger.error(errorMsg, e); + throw new CloudRuntimeException(errorMsg); + } + } + + @NotNull + private DatacenterMO getDatacenterMO(VmwareContext context, VcenterData vmwaredc) throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg { + DatacenterMO dcMo = new DatacenterMO(context, vmwaredc.datacenterName); + ManagedObjectReference dcMor = dcMo.getMor(); + if (dcMor == null) { + String msg = String.format("Unable to find VMware datacenter %s in vCenter %s", + vmwaredc.datacenterName, vmwaredc.vcenter); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + return dcMo; + } + + @NotNull + private VmwareContext getVmwareContext(VcenterData vmwaredc) throws RuntimeFaultFaultMsg, URISyntaxException, VmwareClientException, InvalidLocaleFaultMsg, InvalidLoginFaultMsg { + logger.debug(String.format("Connecting to the VMware datacenter %s at vCenter %s to retrieve VMs", + vmwaredc.datacenterName, vmwaredc.vcenter)); + String serviceUrl = String.format("https://%s/sdk/vimService", vmwaredc.vcenter); + VmwareClient vimClient = new VmwareClient(vmwaredc.vcenter); + vimClient.connect(serviceUrl, vmwaredc.username, vmwaredc.password); + VmwareContext context = new VmwareContext(vimClient, vmwaredc.vcenter); + return context; + } + + @NotNull + private VcenterData getVcenterData(ListVmwareDcItems cmd) { String vcenter = cmd.getVcenter(); String datacenterName = cmd.getDatacenterName(); String username = cmd.getUsername(); String password = cmd.getPassword(); Long existingVcenterId = cmd.getExistingVcenterId(); - String keyword = cmd.getKeyword(); if ((existingVcenterId == null && StringUtils.isBlank(vcenter)) || (existingVcenterId != null && StringUtils.isNotBlank(vcenter))) { @@ -1613,37 +1689,27 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw username = vmwareDc.getUser(); password = vmwareDc.getPassword(); } + VcenterData vmwaredc = new VcenterData(vcenter, datacenterName, username, password); + return vmwaredc; + } - try { - logger.debug(String.format("Connecting to the VMware datacenter %s at vCenter %s to retrieve VMs", - datacenterName, vcenter)); - String serviceUrl = String.format("https://%s/sdk/vimService", vcenter); - VmwareClient vimClient = new VmwareClient(vcenter); - vimClient.connect(serviceUrl, username, password); - VmwareContext context = new VmwareContext(vimClient, vcenter); + private static class VcenterData { + public final String vcenter; + public final String datacenterName; + public final String username; + public final String password; - DatacenterMO dcMo = new DatacenterMO(context, datacenterName); - ManagedObjectReference dcMor = dcMo.getMor(); - if (dcMor == null) { - String msg = String.format("Unable to find VMware datacenter %s in vCenter %s", - datacenterName, vcenter); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - List instances = dcMo.getAllVmsOnDatacenter(); - return StringUtils.isBlank(keyword) ? instances : - instances.stream().filter(x -> x.getName().toLowerCase().contains(keyword.toLowerCase())).collect(Collectors.toList()); - } catch (Exception e) { - String errorMsg = String.format("Error retrieving stopped VMs from the VMware VC %s datacenter %s: %s", - vcenter, datacenterName, e.getMessage()); - logger.error(errorMsg, e); - throw new CloudRuntimeException(errorMsg); + public VcenterData(String vcenter, String datacenterName, String username, String password) { + this.vcenter = vcenter; + this.datacenterName = datacenterName; + this.username = username; + this.password = password; } } @Override public boolean hasNexusVSM(Long clusterId) { - ClusterVSMMapVO vsmMapVo = null; + ClusterVSMMapVO vsmMapVo; vsmMapVo = _vsmMapDao.findByClusterId(clusterId); if (vsmMapVo == null) { @@ -1693,7 +1759,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } /** - * This task is to cleanup templates from primary storage that are otherwise not cleaned by the {@link com.cloud.storage.StorageManagerImpl.StorageGarbageCollector}. + * This task is to clean-up templates from primary storage that are otherwise not cleaned by the {@see com.cloud.storage.StorageManagerImpl.StorageGarbageCollector}. * it is called at regular intervals when storage.template.cleanup.enabled == true * It collect all templates that * - are deleted from cloudstack diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 512715988bb..08fa4b438f6 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -45,6 +45,7 @@ import java.util.TimeZone; import java.util.UUID; import java.util.stream.Collectors; +import com.cloud.agent.api.CleanupVMCommand; import javax.naming.ConfigurationException; import javax.xml.datatype.XMLGregorianCalendar; @@ -583,6 +584,8 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes return execute((ResizeVolumeCommand) cmd); } else if (clz == UnregisterVMCommand.class) { return execute((UnregisterVMCommand) cmd); + } else if (clz == CleanupVMCommand.class) { + return execute((CleanupVMCommand) cmd); } else if (cmd instanceof StorageSubSystemCommand) { checkStorageProcessorAndHandlerNfsVersionAttribute((StorageSubSystemCommand) cmd); return storageHandler.handleStorageCommands((StorageSubSystemCommand) cmd); @@ -5796,6 +5799,26 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes return new Answer(cmd, true, "success"); } + protected Answer execute(CleanupVMCommand cmd) { + VmwareContext context = getServiceContext(); + VmwareHypervisorHost hyperHost = getHyperHost(context); + + try { + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName()); + if (vmMo == null) { + String msg = String.format("VM [%s] not found on vCenter, cleanup not needed.", cmd.getVmName()); + logger.debug(msg); + return new Answer(cmd, true, msg); + } + vmMo.destroy(); + String msg = String.format("VM [%s] remnants on vCenter cleaned up.", cmd.getVmName()); + logger.debug(msg); + return new Answer(cmd, true, msg); + } catch (Exception e) { + return new Answer(cmd, false, createLogMessageException(e, cmd)); + } + } + protected Answer execute(UnregisterVMCommand cmd) { VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java index 6f783e0a0fd..aa2e5ac6f3a 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/AddVmwareDcCmd.java @@ -36,8 +36,8 @@ import com.cloud.dc.VmwareDatacenterVO; import com.cloud.user.Account; import com.cloud.utils.exception.CloudRuntimeException; -@APICommand(name = "addVmwareDc", description = "Adds a VMware datacenter to specified zone", responseObject = VmwareDatacenterResponse.class, - requestHasSensitiveInfo = true, responseHasSensitiveInfo = false) +@APICommand(name = "addVmwareDc", description = "Adds a Vmware datacenter to specified zone", + responseObject = VmwareDatacenterResponse.class, responseHasSensitiveInfo = false) public class AddVmwareDcCmd extends BaseCmd { @Inject @@ -45,7 +45,7 @@ public class AddVmwareDcCmd extends BaseCmd { - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of VMware datacenter to be added to specified zone.") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of Vmware datacenter to be added to specified zone.") private String name; @Parameter(name = ApiConstants.VCENTER, @@ -54,10 +54,10 @@ public class AddVmwareDcCmd extends BaseCmd { description = "The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") private String vCenter; - @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = false, description = "The Username required to connect to resource.") + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The Username required to connect to resource.") private String username; - @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = false, description = "The password for specified username.") + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for specified username.") private String password; @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "The Zone ID.") @@ -99,7 +99,7 @@ public class AddVmwareDcCmd extends BaseCmd { response.setResponseName(getCommandName()); response.setObjectName("vmwaredc"); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add VMware Datacenter to zone."); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Vmware Datacenter to zone."); } this.setResponseObject(response); } catch (DiscoveryException ex) { diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ImportVsphereStoragePoliciesCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ImportVsphereStoragePoliciesCmd.java index 0d8d5d6fa07..40b479f809c 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ImportVsphereStoragePoliciesCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ImportVsphereStoragePoliciesCmd.java @@ -48,8 +48,6 @@ import java.util.List; authorized = {RoleType.Admin}) public class ImportVsphereStoragePoliciesCmd extends BaseCmd { - - @Inject public VmwareDatacenterService _vmwareDatacenterService; @@ -74,6 +72,13 @@ public class ImportVsphereStoragePoliciesCmd extends BaseCmd { List storagePolicies = _vmwareDatacenterService.importVsphereStoragePolicies(this); final ListResponse responseList = new ListResponse<>(); + final List storagePoliciesResponseList = getVsphereStoragePoliciesResponses(storagePolicies, dataCenter); + responseList.setResponses(storagePoliciesResponseList); + responseList.setResponseName(getCommandName()); + setResponseObject(responseList); + } + + private static List getVsphereStoragePoliciesResponses(List storagePolicies, DataCenter dataCenter) { final List storagePoliciesResponseList = new ArrayList<>(); for (VsphereStoragePolicy storagePolicy : storagePolicies) { final VsphereStoragePoliciesResponse storagePoliciesResponse = new VsphereStoragePoliciesResponse(); @@ -86,9 +91,7 @@ public class ImportVsphereStoragePoliciesCmd extends BaseCmd { storagePoliciesResponseList.add(storagePoliciesResponse); } - responseList.setResponses(storagePoliciesResponseList); - responseList.setResponseName(getCommandName()); - setResponseObject(responseList); + return storagePoliciesResponseList; } @Override diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcHostsCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcHostsCmd.java new file mode 100644 index 00000000000..6f193c9c1b2 --- /dev/null +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcHostsCmd.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 org.apache.cloudstack.api.command.admin.zone; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.vmware.VmwareDatacenterService; +import com.cloud.hypervisor.vmware.mo.HostMO; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +import com.vmware.vim25.InvalidPropertyFaultMsg; +import com.vmware.vim25.RuntimeFaultFaultMsg; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VmwareDatacenterResponse; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listVmwareDcHosts", responseObject = VmwareRequestResponse.class, + description = "Lists the VMs in a Vmware Datacenter", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListVmwareDcHostsCmd extends BaseCmd implements ListVmwareDcItems { + + @Inject + public VmwareDatacenterService _vmwareDatacenterService; + + @Parameter(name = ApiConstants.EXISTING_VCENTER_ID, + type = CommandType.UUID, + entityType = VmwareDatacenterResponse.class, + description = "UUID of a linked existing vCenter") + private Long existingVcenterId; + + @Parameter(name = ApiConstants.VCENTER, + type = CommandType.STRING, + description = "The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") + private String vcenter; + + @Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING, description = "Name of Vmware datacenter.") + private String datacenterName; + + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The Username required to connect to resource.") + private String username; + + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for specified username.") + private String password; + + public String getVcenter() { + return vcenter; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getDatacenterName() { + return datacenterName; + } + + public Long getExistingVcenterId() { + return existingVcenterId; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + checkParameters(); + try { + List hosts = _vmwareDatacenterService.listHostsInDatacenter(this); + List baseResponseList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(hosts)) { + for (HostMO vmwareHost : hosts) { + HostResponse resp = createHostResponse(vmwareHost); + baseResponseList.add(resp); + } + } + VmwareRequestResponse response = new VmwareRequestResponse<>(); + response.setResponses(baseResponseList, baseResponseList.size()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (CloudRuntimeException | InvalidPropertyFaultMsg | RuntimeFaultFaultMsg | InvocationTargetException | + NoSuchMethodException | IllegalAccessException e) { + String errorMsg = String.format("Error retrieving VMs from Vmware VC: %s", e.getMessage()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); + } + } + + private HostResponse createHostResponse(HostMO hostInstance) throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg, InvocationTargetException, NoSuchMethodException, IllegalAccessException { + HostResponse response = new HostResponse(); + response.setHypervisor(Hypervisor.HypervisorType.VMware.toString()); + response.setName(hostInstance.getHostName()); + response.setObjectName("host"); + return response; + } + + private void checkParameters() { + if ((existingVcenterId == null && vcenter == null) || (existingVcenterId != null && vcenter != null)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, + "Please provide an existing vCenter ID or a vCenter IP/Name, parameters are mutually exclusive"); + } + if (existingVcenterId == null && StringUtils.isAnyBlank(vcenter, datacenterName, username, password)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, + "Please set all the information for a vCenter IP/Name, datacenter, username and password"); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcItems.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcItems.java new file mode 100644 index 00000000000..580fb3bad9b --- /dev/null +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcItems.java @@ -0,0 +1,29 @@ +// 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.api.command.admin.zone; + +public interface ListVmwareDcItems { + String getVcenter(); + + String getDatacenterName(); + + String getUsername(); + + String getPassword(); + + Long getExistingVcenterId(); +} diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java index 4dd1b4beb09..544e756fe80 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcVmsCmd.java @@ -23,15 +23,15 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UnmanagedInstanceResponse; import org.apache.cloudstack.api.response.VmwareDatacenterResponse; import org.apache.cloudstack.vm.UnmanagedInstanceTO; @@ -42,10 +42,10 @@ import javax.inject.Inject; import java.util.ArrayList; import java.util.List; -@APICommand(name = "listVmwareDcVms", responseObject = UnmanagedInstanceResponse.class, - description = "Lists the VMs in a VMware Datacenter", +@APICommand(name = "listVmwareDcVms", responseObject = VmwareRequestResponse.class, + description = "Lists the VMs in a Vmware Datacenter", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ListVmwareDcVmsCmd extends BaseListCmd { +public class ListVmwareDcVmsCmd extends BaseCmd implements ListVmwareDcItems { @Inject public VmwareDatacenterService _vmwareDatacenterService; @@ -61,7 +61,7 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { description = "The name/ip of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") private String vcenter; - @Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING, description = "Name of VMware datacenter.") + @Parameter(name = ApiConstants.DATACENTER_NAME, type = CommandType.STRING, description = "Name of Vmware datacenter.") private String datacenterName; @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "The Username required to connect to resource.") @@ -70,6 +70,18 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "The password for specified username.") private String password; + @Parameter(name = ApiConstants.HOST, type = CommandType.STRING, description = "get only the VMs from the specified host.") + private String host; + + @Parameter(name = ApiConstants.BATCH_SIZE, type = CommandType.INTEGER, description = "The maximum number of results to return.") + private Integer batchSize; + + @Parameter(name = ApiConstants.TOKEN, type = CommandType.STRING, + description = "For listVmwareDcVms, if the maximum number of results (the `batchsize`) is exceeded, " + + " a token is returned. This token can be used in subsequent calls to retrieve more results." + + " As long as a token is returned, more results can be retrieved.") + private String token; + public String getVcenter() { return vcenter; } @@ -82,6 +94,18 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { return password; } + public Integer getBatchSize() { + return batchSize; + } + + public String getHost() { + return host; + } + + public String getToken() { + return token; + } + public String getDatacenterName() { return datacenterName; } @@ -94,7 +118,8 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { checkParameters(); try { - List vms = _vmwareDatacenterService.listVMsInDatacenter(this); + Pair> results = _vmwareDatacenterService.listVMsInDatacenter(this); + List vms = results.second(); List baseResponseList = new ArrayList<>(); if (CollectionUtils.isNotEmpty(vms)) { for (UnmanagedInstanceTO vmwareVm : vms) { @@ -102,16 +127,13 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { baseResponseList.add(resp); } } - List pagingList = com.cloud.utils.StringUtils.applyPagination(baseResponseList, this.getStartIndex(), this.getPageSizeVal()); - if (CollectionUtils.isEmpty(pagingList)) { - pagingList = baseResponseList; - } - ListResponse response = new ListResponse<>(); - response.setResponses(pagingList, baseResponseList.size()); + VmwareRequestResponse response = new VmwareRequestResponse<>(); + response.setResponses(baseResponseList, baseResponseList.size()); response.setResponseName(getCommandName()); + response.setToken(results.first()); setResponseObject(response); } catch (CloudRuntimeException e) { - String errorMsg = String.format("Error retrieving VMs from VMware VC: %s", e.getMessage()); + String errorMsg = String.format("Error retrieving VMs from Vmware VC: %s", e.getMessage()); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); } } @@ -134,6 +156,6 @@ public class ListVmwareDcVmsCmd extends BaseListCmd { @Override public String getCommandName() { - return "listvmwaredcvmsresponse"; + return "listVmwareDcVmsResponse".toLowerCase(); } } diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcsCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcsCmd.java index 4c7f2a5c7d8..9cafda955ca 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcsCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVmwareDcsCmd.java @@ -42,7 +42,7 @@ import com.cloud.dc.VmwareDatacenter; import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.user.Account; -@APICommand(name = "listVmwareDcs", responseObject = VmwareDatacenterResponse.class, description = "Retrieves VMware DC(s) associated with a zone.", +@APICommand(name = "listVmwareDcs", responseObject = VmwareDatacenterResponse.class, description = "Retrieves Vmware DC(s) associated with a zone.", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListVmwareDcsCmd extends BaseListCmd { @@ -50,7 +50,6 @@ public class ListVmwareDcsCmd extends BaseListCmd { public VmwareDatacenterService _vmwareDatacenterService; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -73,20 +72,27 @@ public class ListVmwareDcsCmd extends BaseListCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { - List vmwareDcList = null; + List vmwareDcList; try { vmwareDcList = _vmwareDatacenterService.listVmwareDatacenters(this); } catch (InvalidParameterValueException ie) { throw new InvalidParameterValueException("Invalid zone id " + getZoneId()); } catch (Exception e) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to find associated VMware DCs associated with zone " + getZoneId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to find associated Vmware DCs associated with zone " + getZoneId()); } - ListResponse response = new ListResponse(); - List vmwareDcResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List vmwareDcResponses = getVmwareDatacenterResponses(vmwareDcList); + response.setResponses(vmwareDcResponses); + response.setResponseName(getCommandName()); + setResponseObject(response); + } - if (vmwareDcList != null && vmwareDcList.size() > 0) { + private List getVmwareDatacenterResponses(List vmwareDcList) { + List vmwareDcResponses = new ArrayList<>(); + + if (vmwareDcList != null && !vmwareDcList.isEmpty()) { for (VmwareDatacenter vmwareDc : vmwareDcList) { VmwareDatacenterResponse vmwareDcResponse = new VmwareDatacenterResponse(); @@ -94,14 +100,12 @@ public class ListVmwareDcsCmd extends BaseListCmd { vmwareDcResponse.setVcenter(vmwareDc.getVcenterHost()); vmwareDcResponse.setName(vmwareDc.getVmwareDatacenterName()); vmwareDcResponse.setZoneId(getZoneId()); - vmwareDcResponse.setObjectName("VMwareDC"); + vmwareDcResponse.setObjectName(ApiConstants.VMWARE_DC); vmwareDcResponses.add(vmwareDcResponse); } } - response.setResponses(vmwareDcResponses); - response.setResponseName(getCommandName()); - setResponseObject(response); + return vmwareDcResponses; } @Override diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePoliciesCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePoliciesCmd.java index c8527b1ec34..35631ba1315 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePoliciesCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePoliciesCmd.java @@ -47,8 +47,6 @@ import java.util.List; authorized = {RoleType.Admin}) public class ListVsphereStoragePoliciesCmd extends BaseCmd { - - @Inject public VmwareDatacenterService _vmwareDatacenterService; @@ -73,6 +71,13 @@ public class ListVsphereStoragePoliciesCmd extends BaseCmd { List storagePolicies = _vmwareDatacenterService.listVsphereStoragePolicies(this); final ListResponse responseList = new ListResponse<>(); + final List storagePoliciesResponseList = getVsphereStoragePoliciesResponses(storagePolicies, dataCenter); + responseList.setResponses(storagePoliciesResponseList); + responseList.setResponseName(getCommandName()); + setResponseObject(responseList); + } + + private static List getVsphereStoragePoliciesResponses(List storagePolicies, DataCenter dataCenter) { final List storagePoliciesResponseList = new ArrayList<>(); for (VsphereStoragePolicy storagePolicy : storagePolicies) { final VsphereStoragePoliciesResponse storagePoliciesResponse = new VsphereStoragePoliciesResponse(); @@ -81,13 +86,11 @@ public class ListVsphereStoragePoliciesCmd extends BaseCmd { storagePoliciesResponse.setName(storagePolicy.getName()); storagePoliciesResponse.setPolicyId(storagePolicy.getPolicyId()); storagePoliciesResponse.setDescription(storagePolicy.getDescription()); - storagePoliciesResponse.setObjectName("StoragePolicy"); + storagePoliciesResponse.setObjectName(ApiConstants.STORAGE_POLICY); storagePoliciesResponseList.add(storagePoliciesResponse); } - responseList.setResponses(storagePoliciesResponseList); - responseList.setResponseName(getCommandName()); - setResponseObject(responseList); + return storagePoliciesResponseList; } @Override diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePolicyCompatiblePoolsCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePolicyCompatiblePoolsCmd.java index d66972ded2e..ab697fbad67 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePolicyCompatiblePoolsCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/ListVsphereStoragePolicyCompatiblePoolsCmd.java @@ -68,7 +68,7 @@ public class ListVsphereStoragePolicyCompatiblePoolsCmd extends BaseListCmd { List poolResponses = new ArrayList<>(); for (StoragePool pool : pools) { StoragePoolResponse poolResponse = _responseGenerator.createStoragePoolForMigrationResponse(pool); - poolResponse.setObjectName("storagepool"); + poolResponse.setObjectName(ApiConstants.STORAGE_POOL); poolResponses.add(poolResponse); } response.setResponses(poolResponses); diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java index a503d860feb..d85fbacf2db 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/RemoveVmwareDcCmd.java @@ -34,7 +34,7 @@ import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.user.Account; import com.cloud.utils.exception.CloudRuntimeException; -@APICommand(name = "removeVmwareDc", responseObject = SuccessResponse.class, description = "Remove a VMware datacenter from a zone.", +@APICommand(name = "removeVmwareDc", responseObject = SuccessResponse.class, description = "Remove a Vmware datacenter from a zone.", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class RemoveVmwareDcCmd extends BaseCmd { @@ -47,7 +47,7 @@ public class RemoveVmwareDcCmd extends BaseCmd { type = CommandType.UUID, entityType = ZoneResponse.class, required = true, - description = "The id of Zone from which VMware datacenter has to be removed.") + description = "The id of Zone from which Vmware datacenter has to be removed.") private Long zoneId; public Long getZoneId() { @@ -63,7 +63,7 @@ public class RemoveVmwareDcCmd extends BaseCmd { response.setResponseName(getCommandName()); setResponseObject(response); } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove VMware datacenter from zone"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Vmware datacenter from zone"); } } catch (ResourceInUseException ex) { logger.warn("The zone has one or more resources (like cluster), hence not able to remove VMware datacenter from zone." diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java index bb818985277..5e02d5a96c2 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java @@ -33,12 +33,11 @@ import com.cloud.dc.VmwareDatacenter; import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.user.Account; -@APICommand(name = "updateVmwareDc", description = "Updates a VMware datacenter details for a zone", +@APICommand(name = "updateVmwareDc", description = "Updates a Vmware datacenter details for a zone", responseObject = VmwareDatacenterResponse.class, responseHasSensitiveInfo = false, since = "4.12.0", authorized = {RoleType.Admin}) public class UpdateVmwareDcCmd extends BaseCmd { - @Inject public VmwareDatacenterService vmwareDatacenterService; @@ -51,7 +50,7 @@ public class UpdateVmwareDcCmd extends BaseCmd { private Long zoneId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, - description = "VMware datacenter name.") + description = "Vmware datacenter name.") private String name; @Parameter(name = ApiConstants.VCENTER, type = CommandType.STRING, @@ -106,13 +105,13 @@ public class UpdateVmwareDcCmd extends BaseCmd { public void execute() { final VmwareDatacenter vmwareDatacenter = vmwareDatacenterService.updateVmwareDatacenter(this); if (vmwareDatacenter == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VMware datacenter"); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Vmware datacenter"); } final VmwareDatacenterResponse response = new VmwareDatacenterResponse(); response.setId(vmwareDatacenter.getUuid()); response.setName(vmwareDatacenter.getVmwareDatacenterName()); response.setResponseName(getCommandName()); - response.setObjectName("vmwaredc"); + response.setObjectName(ApiConstants.VMWARE_DC); setResponseObject(response); } diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java new file mode 100644 index 00000000000..81c58ef27ba --- /dev/null +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java @@ -0,0 +1,38 @@ +// 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.api.command.admin.zone; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.response.ListResponse; + +public class VmwareRequestResponse extends ListResponse { + @SerializedName(ApiConstants.TOKEN) + @Param(description = "The Vmware API token to use for retrieving further responses with") + private String token; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index 076bd105728..29ecde477a4 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -23,10 +23,8 @@ import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; @@ -206,14 +204,12 @@ public class KubernetesClusterActionWorker { protected String getControlNodeLoginUser() { List vmMapVOList = getKubernetesClusterVMMaps(); - if (vmMapVOList.size() > 0) { + if (!vmMapVOList.isEmpty()) { long vmId = vmMapVOList.get(0).getVmId(); UserVmVO userVM = userVmDao.findById(vmId); if (userVM == null) { throw new CloudRuntimeException("Failed to find login user, Unable to log in to node to fetch details"); } - Set vm = new HashSet<>(); - vm.add(userVM.getName()); UserVmDetailVO vmDetail = userVmDetailsDao.findDetail(vmId, VmDetailConstants.CKS_CONTROL_NODE_LOGIN_USER); if (vmDetail != null && !org.apache.commons.lang3.StringUtils.isEmpty(vmDetail.getValue())) { return vmDetail.getValue(); @@ -309,7 +305,7 @@ public class KubernetesClusterActionWorker { } protected KubernetesClusterVmMapVO addKubernetesClusterVm(final long kubernetesClusterId, final long vmId, boolean isControlNode) { - return Transaction.execute(new TransactionCallback() { + return Transaction.execute(new TransactionCallback<>() { @Override public KubernetesClusterVmMapVO doInTransaction(TransactionStatus status) { KubernetesClusterVmMapVO newClusterVmMap = new KubernetesClusterVmMapVO(kubernetesClusterId, vmId, isControlNode); @@ -361,7 +357,12 @@ public class KubernetesClusterActionWorker { } IpAddress address = ipAddressDao.findByUuid(detailsVO.getValue()); if (address == null || !Objects.equals(network.getVpcId(), address.getVpcId())) { - logger.warn(String.format("Public IP with ID: %s linked to the Kubernetes cluster: %s is not usable", detailsVO.getValue(), kubernetesCluster.getName())); + logger.warn("Public IP with ID: {} linked to the Kubernetes cluster: {} is not usable", detailsVO.getValue(), kubernetesCluster.getName()); + if (address == null) { + logger.warn("Public IP with ID: {} was not found by uuid", detailsVO.getValue()); + } else { + logger.warn("Public IP with ID: {} was associated with vpc {} instead of {}", detailsVO.getValue(), address.getVpcId(), network.getVpcId()); + } return null; } return address; @@ -517,8 +518,7 @@ public class KubernetesClusterActionWorker { } protected List getKubernetesClusterVMMaps() { - List clusterVMs = kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId()); - return clusterVMs; + return kubernetesClusterVmMapDao.listByClusterId(kubernetesCluster.getId()); } protected List getKubernetesClusterVMMapsForNodes(List nodeIds) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java index 74e8b0c9b23..0195a5a7916 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java @@ -193,7 +193,7 @@ public class KubernetesClusterUtil { while (System.currentTimeMillis() < timeoutTime) { try { Pair result = SshHelper.sshExecute(ipAddress, port, user, - sshKeyFile, null, "sudo cat /etc/kubernetes/admin.conf", + sshKeyFile, null, "sudo cat /etc/kubernetes/user.conf 2>/dev/null || sudo cat /etc/kubernetes/admin.conf", 10000, 10000, 10000); if (result.first() && StringUtils.isNotEmpty(result.second())) { diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index 419a7f983ee..fb247eef5df 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -5,12 +5,23 @@ All notable changes to Linstor CloudStack plugin will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2025-01-27] + +### Fixed + +- Use of multiple primary storages on the same linstor controller + ## [2025-01-20] ### Fixed - Volume snapshots on zfs used the wrong dataset path to hide/unhide snapdev +## [2024-12-19] + +### Added +- Native CloudStack encryption support + ## [2024-12-13] ### Fixed diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorBackupSnapshotCommandWrapper.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorBackupSnapshotCommandWrapper.java index 08f61262914..fab4829da55 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorBackupSnapshotCommandWrapper.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorBackupSnapshotCommandWrapper.java @@ -97,18 +97,23 @@ public final class LinstorBackupSnapshotCommandWrapper // NOTE: the qemu img will also contain the drbd metadata at the end final QemuImg qemu = new QemuImg(waitMilliSeconds); qemu.convert(srcFile, dstFile); - LOGGER.info("Backup snapshot " + srcFile + " to " + dstPath); + LOGGER.info("Backup snapshot '{}' to '{}'", srcPath, dstPath); return dstPath; } private SnapshotObjectTO setCorrectSnapshotSize(final SnapshotObjectTO dst, final String dstPath) { final File snapFile = new File(dstPath); - final long size = snapFile.exists() ? snapFile.length() : 0; + long size; + if (snapFile.exists()) { + size = snapFile.length(); + } else { + LOGGER.warn("Snapshot file {} does not exist. Reporting size 0", dstPath); + size = 0; + } - final SnapshotObjectTO snapshot = new SnapshotObjectTO(); - snapshot.setPath(dst.getPath() + File.separator + dst.getName()); - snapshot.setPhysicalSize(size); - return snapshot; + dst.setPath(dst.getPath() + File.separator + dst.getName()); + dst.setPhysicalSize(size); + return dst; } @Override @@ -158,6 +163,7 @@ public final class LinstorBackupSnapshotCommandWrapper LOGGER.info("Backup shrunk " + dstPath + " to actual size " + src.getVolume().getSize()); SnapshotObjectTO snapshot = setCorrectSnapshotSize(dst, dstPath); + LOGGER.info("Actual file size for '{}' is {}", dstPath, snapshot.getPhysicalSize()); return new CopyCmdAnswer(snapshot); } catch (final Exception e) { final String error = String.format("Failed to backup snapshot with id [%s] with a pool %s, due to %s", 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 292e939de49..ab8e5f4ee7b 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 @@ -61,6 +61,11 @@ import com.linbit.linstor.api.model.Volume; import com.linbit.linstor.api.model.VolumeDefinition; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; public class LinstorStorageAdaptor implements StorageAdaptor { protected Logger logger = LogManager.getLogger(getClass()); @@ -202,10 +207,10 @@ public class LinstorStorageAdaptor implements StorageAdaptor { final DevelopersApi api = getLinstorAPI(pool); try { - List definitionList = api.resourceDefinitionList( - Collections.singletonList(rscName), null, null, null); + ResourceDefinition resourceDefinition = LinstorUtil.findResourceDefinition( + api, rscName, lpool.getResourceGroup()); - if (definitionList.isEmpty()) { + if (resourceDefinition == null) { ResourceGroupSpawn rgSpawn = new ResourceGroupSpawn(); rgSpawn.setResourceDefinitionName(rscName); rgSpawn.addVolumeSizesItem(size / 1024); // linstor uses KiB @@ -215,22 +220,28 @@ public class LinstorStorageAdaptor implements StorageAdaptor { handleLinstorApiAnswers(answers, "Linstor: Unable to spawn resource."); } + String foundRscName = resourceDefinition != null ? resourceDefinition.getName() : rscName; + // query linstor for the device path List resources = api.viewResources( Collections.emptyList(), - Collections.singletonList(rscName), + Collections.singletonList(foundRscName), Collections.emptyList(), null, null, null); - makeResourceAvailable(api, rscName, false); + makeResourceAvailable(api, foundRscName, false); if (!resources.isEmpty() && !resources.get(0).getVolumes().isEmpty()) { final String devPath = resources.get(0).getVolumes().get(0).getDevicePath(); logger.info("Linstor: Created drbd device: " + devPath); final KVMPhysicalDisk kvmDisk = new KVMPhysicalDisk(devPath, name, pool); kvmDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW); + long allocatedKib = resources.get(0).getVolumes().get(0).getAllocatedSizeKib() != null ? + resources.get(0).getVolumes().get(0).getAllocatedSizeKib() : 0; + kvmDisk.setSize(allocatedKib >= 0 ? allocatedKib * 1024 : 0); + kvmDisk.setVirtualSize(size); return kvmDisk; } else { logger.error("Linstor: viewResources didn't return resources or volumes."); @@ -410,7 +421,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { if (rsc.getFlags() != null && rsc.getFlags().contains(ApiConsts.FLAG_DRBD_DISKLESS) && !rsc.getFlags().contains(ApiConsts.FLAG_TIE_BREAKER)) { - ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName); + ApiCallRcList delAnswers = api.resourceDelete(rsc.getName(), localNodeName, true); logLinstorAnswers(delAnswers); } } catch (ApiException apiEx) { @@ -473,21 +484,56 @@ public class LinstorStorageAdaptor implements StorageAdaptor { return false; } + /** + * Decrements the aux property key for template resource and deletes or just deletes if not template resource. + * @param api + * @param rscName + * @param rscGrpName + * @return + * @throws ApiException + */ + private boolean deRefOrDeleteResource(DevelopersApi api, String rscName, String rscGrpName) throws ApiException { + boolean deleted = false; + List existingRDs = LinstorUtil.getRDListStartingWith(api, rscName); + for (ResourceDefinition rd : existingRDs) { + int expectedProps = 0; // if it is a non template resource, we don't expect any _cs-template-for- prop + String propKey = LinstorUtil.getTemplateForAuxPropKey(rscGrpName); + if (rd.getProps().containsKey(propKey)) { + ResourceDefinitionModify rdm = new ResourceDefinitionModify(); + rdm.deleteProps(Collections.singletonList(propKey)); + api.resourceDefinitionModify(rd.getName(), rdm); + expectedProps = 1; + } + + // if there is only one template-for property left for templates, the template isn't needed anymore + // or if it isn't a template anyway, it will not have this Aux property + // _cs-template-for- poperties work like a ref-count. + if (rd.getProps().keySet().stream() + .filter(key -> key.startsWith("Aux/" + LinstorUtil.CS_TEMPLATE_FOR_PREFIX)) + .count() == expectedProps) { + ApiCallRcList answers = api.resourceDefinitionDelete(rd.getName()); + checkLinstorAnswersThrow(answers); + deleted = true; + } + } + return deleted; + } + @Override public boolean deletePhysicalDisk(String name, KVMStoragePool pool, Storage.ImageFormat format) { logger.debug("Linstor: deletePhysicalDisk " + name); final DevelopersApi api = getLinstorAPI(pool); + final String rscName = getLinstorRscName(name); + final LinstorStoragePool linstorPool = (LinstorStoragePool) pool; + String rscGrpName = linstorPool.getResourceGroup(); try { - final String rscName = getLinstorRscName(name); - logger.debug("Linstor: delete resource definition " + rscName); - ApiCallRcList answers = api.resourceDefinitionDelete(rscName); - handleLinstorApiAnswers(answers, "Linstor: Unable to delete resource definition " + rscName); + return deRefOrDeleteResource(api, rscName, rscGrpName); } catch (ApiException apiEx) { + logger.error("Linstor: ApiEx - " + apiEx.getMessage()); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); } - return true; } @Override @@ -561,6 +607,56 @@ public class LinstorStorageAdaptor implements StorageAdaptor { return false; } + /** + * Checks if the given disk is the SystemVM template, by checking its properties file in the same directory. + * The initial systemvm template resource isn't created on the management server, but + * we now need to know if the systemvm template is used, while copying. + * @param disk + * @return True if it is the systemvm template disk, else false. + */ + private static boolean isSystemTemplate(KVMPhysicalDisk disk) { + Path diskPath = Paths.get(disk.getPath()); + Path propFile = diskPath.getParent().resolve("template.properties"); + if (Files.exists(propFile)) { + java.util.Properties templateProps = new java.util.Properties(); + try { + templateProps.load(new FileInputStream(propFile.toFile())); + String desc = templateProps.getProperty("description"); + if (desc.startsWith("SystemVM Template")) { + return true; + } + } catch (IOException e) { + return false; + } + } + return false; + } + + /** + * Conditionally sets the correct aux properties for templates or basic resources. + * @param api + * @param srcDisk + * @param destPool + * @param name + */ + private void setRscDfnAuxProperties( + DevelopersApi api, KVMPhysicalDisk srcDisk, KVMStoragePool destPool, String name) { + // if it is the initial systemvm disk copy, we need to apply the _cs-template-for property. + if (isSystemTemplate(srcDisk)) { + applyAuxProps(api, name, "SystemVM Template", null); + LinstorStoragePool linPool = (LinstorStoragePool) destPool; + final String rscName = getLinstorRscName(name); + try { + LinstorUtil.setAuxTemplateForProperty(api, rscName, linPool.getResourceGroup()); + } catch (ApiException apiExc) { + logger.error("Error setting aux template for property for {}", rscName); + logLinstorAnswers(apiExc.getApiCallRcList()); + } + } else { + applyAuxProps(api, name, srcDisk.getDispName(), srcDisk.getVmName()); + } + } + @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase, Storage.ProvisioningType provisioningType) { @@ -574,15 +670,14 @@ public class LinstorStorageAdaptor implements StorageAdaptor { name, QemuImg.PhysicalDiskFormat.RAW, provisioningType, disk.getVirtualSize(), null); final DevelopersApi api = getLinstorAPI(destPools); - applyAuxProps(api, name, disk.getDispName(), disk.getVmName()); + setRscDfnAuxProperties(api, disk, destPools, name); logger.debug("Linstor.copyPhysicalDisk: dstPath: {}", dstDisk.getPath()); final QemuImgFile destFile = new QemuImgFile(dstDisk.getPath()); destFile.setFormat(dstDisk.getFormat()); destFile.setSize(disk.getVirtualSize()); - boolean zeroedDevice = resourceSupportZeroBlocks(destPools, LinstorUtil.RSC_PREFIX + name); - + boolean zeroedDevice = resourceSupportZeroBlocks(destPools, getLinstorRscName(name)); try { final QemuImg qemu = new QemuImg(timeout, zeroedDevice, true); qemu.convert(srcFile, destFile); 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 8abd8fd8bd2..1afba442be9 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 @@ -21,11 +21,16 @@ import com.linbit.linstor.api.CloneWaiter; import com.linbit.linstor.api.DevelopersApi; import com.linbit.linstor.api.model.ApiCallRc; import com.linbit.linstor.api.model.ApiCallRcList; +import com.linbit.linstor.api.model.AutoSelectFilter; +import com.linbit.linstor.api.model.LayerType; import com.linbit.linstor.api.model.Properties; import com.linbit.linstor.api.model.ResourceDefinition; import com.linbit.linstor.api.model.ResourceDefinitionCloneRequest; import com.linbit.linstor.api.model.ResourceDefinitionCloneStarted; import com.linbit.linstor.api.model.ResourceDefinitionCreate; + +import com.linbit.linstor.api.model.ResourceDefinitionModify; +import com.linbit.linstor.api.model.ResourceGroup; import com.linbit.linstor.api.model.ResourceGroupSpawn; import com.linbit.linstor.api.model.ResourceMakeAvailable; import com.linbit.linstor.api.model.Snapshot; @@ -34,6 +39,7 @@ import com.linbit.linstor.api.model.VolumeDefinition; import com.linbit.linstor.api.model.VolumeDefinitionModify; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.inject.Inject; import java.util.Arrays; @@ -43,6 +49,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ResizeVolumeAnswer; @@ -66,6 +73,7 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.Volume; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; @@ -85,6 +93,7 @@ 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.DataStoreCapabilities; +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.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; @@ -93,6 +102,7 @@ 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.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -103,8 +113,12 @@ import org.apache.cloudstack.storage.snapshot.SnapshotObject; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; + import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.commons.collections.CollectionUtils; + +import java.nio.charset.StandardCharsets; public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { protected Logger logger = LogManager.getLogger(getClass()); @@ -216,7 +230,7 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver } throw new CloudRuntimeException("Linstor: Unable to delete resource definition: " + rscDefName); } - logger.info(String.format("Linstor: Deleted resource %s", rscDefName)); + logger.info("Linstor: Deleted resource {}", rscDefName); } catch (ApiException apiEx) { logger.error("Linstor: ApiEx - " + apiEx.getMessage()); @@ -394,22 +408,166 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver storagePoolVO.getUserInfo() : "DfltRscGrp"; } - private String createResourceBase( - String rscName, long sizeInBytes, String volName, String vmName, DevelopersApi api, String rscGrp) { - ResourceGroupSpawn rscGrpSpawn = new ResourceGroupSpawn(); - rscGrpSpawn.setResourceDefinitionName(rscName); - rscGrpSpawn.addVolumeSizesItem(sizeInBytes / 1024); + /** + * Returns the layerlist of the resourceGroup with encryption(LUKS) added above STORAGE. + * If the resourceGroup layer list already contains LUKS this layer list will be returned. + * @param api Linstor developers API + * @param resourceGroup Resource group to get the encryption layer list + * @return layer list with LUKS added + */ + public List getEncryptedLayerList(DevelopersApi api, String resourceGroup) { + try { + List rscGrps = api.resourceGroupList( + Collections.singletonList(resourceGroup), Collections.emptyList(), null, null); + if (CollectionUtils.isEmpty(rscGrps)) { + throw new CloudRuntimeException( + String.format("Resource Group %s not found on Linstor cluster.", resourceGroup)); + } + + final ResourceGroup rscGrp = rscGrps.get(0); + List layers = Arrays.asList(LayerType.DRBD, LayerType.LUKS, LayerType.STORAGE); + List curLayerStack = rscGrp.getSelectFilter() != null ? + rscGrp.getSelectFilter().getLayerStack() : Collections.emptyList(); + if (CollectionUtils.isNotEmpty(curLayerStack)) { + layers = curLayerStack.stream().map(LayerType::valueOf).collect(Collectors.toList()); + if (!layers.contains(LayerType.LUKS)) { + layers.add(layers.size() - 1, LayerType.LUKS); // lowest layer is STORAGE + } + } + return layers; + } catch (ApiException e) { + throw new CloudRuntimeException( + String.format("Resource Group %s not found on Linstor cluster.", resourceGroup)); + } + } + + /** + * Spawns a new Linstor resource with the given arguments. + * @param api + * @param newRscName + * @param sizeInBytes + * @param isTemplate + * @param rscGrpName + * @param volName + * @param vmName + * @throws ApiException + */ + private void spawnResource( + DevelopersApi api, String newRscName, long sizeInBytes, boolean isTemplate, String rscGrpName, + String volName, String vmName, @Nullable Long passPhraseId, @Nullable byte[] passPhrase) throws ApiException + { + ResourceGroupSpawn rscGrpSpawn = new ResourceGroupSpawn(); + rscGrpSpawn.setResourceDefinitionName(newRscName); + rscGrpSpawn.addVolumeSizesItem(sizeInBytes / 1024); + if (passPhraseId != null) { + AutoSelectFilter asf = new AutoSelectFilter(); + List luksLayers = getEncryptedLayerList(api, rscGrpName); + asf.setLayerStack(luksLayers.stream().map(LayerType::toString).collect(Collectors.toList())); + rscGrpSpawn.setSelectFilter(asf); + if (passPhrase != null) { + String utf8Passphrase = new String(passPhrase, StandardCharsets.UTF_8); + rscGrpSpawn.setVolumePassphrases(Collections.singletonList(utf8Passphrase)); + } + } + + if (isTemplate) { + Properties props = new Properties(); + props.put(LinstorUtil.getTemplateForAuxPropKey(rscGrpName), "true"); + rscGrpSpawn.setResourceDefinitionProps(props); + } + + logger.info("Linstor: Spawn resource " + newRscName); + ApiCallRcList answers = api.resourceGroupSpawn(rscGrpName, rscGrpSpawn); + checkLinstorAnswersThrow(answers); + + answers = LinstorUtil.applyAuxProps(api, newRscName, volName, vmName); + checkLinstorAnswersThrow(answers); + } + + /** + * Condition if a template resource can be shared with the given resource group. + * @param tgtRscGrp + * @param tgtLayerStack + * @param rg + * @return True if the template resource can be shared, else false. + */ + private boolean canShareTemplateForResourceGroup( + ResourceGroup tgtRscGrp, List tgtLayerStack, ResourceGroup rg) { + List rgLayerStack = rg.getSelectFilter() != null ? + rg.getSelectFilter().getLayerStack() : null; + return Objects.equals(tgtLayerStack, rgLayerStack) && + Objects.equals(tgtRscGrp.getSelectFilter().getStoragePoolList(), + rg.getSelectFilter().getStoragePoolList()); + } + + /** + * Searches for a shareable template for this rscGrpName and sets the aux template property. + * @param api + * @param rscName + * @param rscGrpName + * @param existingRDs + * @return + * @throws ApiException + */ + private boolean foundShareableTemplate( + DevelopersApi api, String rscName, String rscGrpName, + List> existingRDs) throws ApiException { + if (!existingRDs.isEmpty()) { + ResourceGroup tgtRscGrp = api.resourceGroupList( + Collections.singletonList(rscGrpName), null, null, null).get(0); + List tgtLayerStack = tgtRscGrp.getSelectFilter() != null ? + tgtRscGrp.getSelectFilter().getLayerStack() : null; + + // check if there is already a template copy, that we could reuse + // this means if select filters are similar enough to allow cloning from + for (Pair rdPair : existingRDs) { + ResourceGroup rg = rdPair.second(); + if (canShareTemplateForResourceGroup(tgtRscGrp, tgtLayerStack, rg)) { + LinstorUtil.setAuxTemplateForProperty(api, rscName, rscGrpName); + return true; + } + } + } + return false; + } + + /** + * Creates a new Linstor resource. + * @param rscName + * @param sizeInBytes + * @param volName + * @param vmName + * @param api + * @param rscGrp + * @param poolId + * @param isTemplate indicates if the resource is a template + * @return true if a new resource was created, false if it already existed or was reused. + */ + private boolean createResourceBase( + String rscName, long sizeInBytes, String volName, String vmName, + @Nullable Long passPhraseId, @Nullable byte[] passPhrase, DevelopersApi api, + String rscGrp, long poolId, boolean isTemplate) + { try { - logger.info("Linstor: Spawn resource " + rscName); - ApiCallRcList answers = api.resourceGroupSpawn(rscGrp, rscGrpSpawn); - checkLinstorAnswersThrow(answers); + logger.debug("createRscBase: {} :: {} :: {}", rscName, rscGrp, isTemplate); + List> existingRDs = LinstorUtil.getRDAndRGListStartingWith(api, rscName); - answers = LinstorUtil.applyAuxProps(api, rscName, volName, vmName); - checkLinstorAnswersThrow(answers); - - return LinstorUtil.getDevicePath(api, rscName); + String fullRscName = String.format("%s-%d", rscName, poolId); + boolean alreadyCreated = existingRDs.stream() + .anyMatch(p -> p.first().getName().equalsIgnoreCase(fullRscName)) || + existingRDs.stream().anyMatch(p -> p.first().getProps().containsKey(LinstorUtil.getTemplateForAuxPropKey(rscGrp))); + if (!alreadyCreated) { + boolean createNewRsc = !foundShareableTemplate(api, rscName, rscGrp, existingRDs); + if (createNewRsc) { + String newRscName = existingRDs.isEmpty() ? rscName : fullRscName; + spawnResource(api, newRscName, sizeInBytes, isTemplate, rscGrp, + volName, vmName, passPhraseId, passPhrase); + } + return createNewRsc; + } + return false; } catch (ApiException apiEx) { logger.error("Linstor: ApiEx - " + apiEx.getMessage()); @@ -422,8 +580,9 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver final String rscGrp = getRscGrp(storagePoolVO); final String rscName = LinstorUtil.RSC_PREFIX + vol.getUuid(); - String deviceName = createResourceBase( - rscName, vol.getSize(), vol.getName(), vol.getAttachedVmName(), linstorApi, rscGrp); + createResourceBase( + rscName, vol.getSize(), vol.getName(), vol.getAttachedVmName(), vol.getPassphraseId(), vol.getPassphrase(), + linstorApi, rscGrp, storagePoolVO.getId(), false); try { @@ -450,20 +609,83 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver } } + /** + * Update resource-definitions resource-group to the correct one if it isn't already the intended. + * @param api Linstor api + * @param rscName resource name to check the resource group + * @param tgtRscGrp resource group name to set + * @throws ApiException exception if any api error occurred + */ + private void updateRscGrpIfNecessary(DevelopersApi api, String rscName, String tgtRscGrp) throws ApiException { + List rscDfns = api.resourceDefinitionList( + Collections.singletonList(rscName), null, null, null); + if (rscDfns != null && !rscDfns.isEmpty()) { + ResourceDefinition rscDfn = rscDfns.get(0); + + if (!rscDfn.getResourceGroupName().equalsIgnoreCase(tgtRscGrp)) { + ResourceDefinitionModify rdm = new ResourceDefinitionModify(); + rdm.setResourceGroup(tgtRscGrp); + ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm); + + if (answers.hasError()) { + String bestError = LinstorUtil.getBestErrorMessage(answers); + logger.error("Update resource group on {} error: {}", rscName, bestError); + throw new CloudRuntimeException(bestError); + } else { + logger.info("Successfully changed resource group to {} on {}", tgtRscGrp, rscName); + } + } + } + } + + /** + * If a resource is cloned, all properties are cloned too, but the _cs-template-for properties, + * should only stay on the template resource, so delete them in this method. + * @param api + * @param rscName + * @throws ApiException + */ + private void deleteTemplateForProps( + DevelopersApi api, String rscName) throws ApiException { + List rdList = api.resourceDefinitionList( + Collections.singletonList(rscName), null, null, null); + + if (CollectionUtils.isNotEmpty(rdList)) { + ResourceDefinitionModify rdm = new ResourceDefinitionModify(); + List deleteProps = rdList.get(0).getProps().keySet().stream() + .filter(key -> key.startsWith("Aux/" + LinstorUtil.CS_TEMPLATE_FOR_PREFIX)) + .collect(Collectors.toList()); + rdm.deleteProps(deleteProps); + ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm); + checkLinstorAnswers(answers); + } + } + private String cloneResource(long csCloneId, VolumeInfo volumeInfo, StoragePoolVO storagePoolVO) { // get the cached template on this storage VMTemplateStoragePoolVO tmplPoolRef = _vmTemplatePoolDao.findByPoolTemplate( storagePoolVO.getId(), csCloneId, null); if (tmplPoolRef != null) { - final String cloneRes = LinstorUtil.RSC_PREFIX + tmplPoolRef.getLocalDownloadPath(); + final String templateRscName = LinstorUtil.RSC_PREFIX + tmplPoolRef.getLocalDownloadPath(); final String rscName = LinstorUtil.RSC_PREFIX + volumeInfo.getUuid(); final DevelopersApi linstorApi = LinstorUtil.getLinstorAPI(storagePoolVO.getHostAddress()); try { - logger.info("Clone resource definition " + cloneRes + " to " + rscName); + ResourceDefinition templateRD = LinstorUtil.findResourceDefinition( + linstorApi, templateRscName, getRscGrp(storagePoolVO)); + final String cloneRes = templateRD != null ? templateRD.getName() : templateRscName; + logger.info("Clone resource definition {} to {}", cloneRes, rscName); ResourceDefinitionCloneRequest cloneRequest = new ResourceDefinitionCloneRequest(); cloneRequest.setName(rscName); + if (volumeInfo.getPassphraseId() != null) { + List encryptionLayer = getEncryptedLayerList(linstorApi, getRscGrp(storagePoolVO)); + cloneRequest.setLayerList(encryptionLayer); + if (volumeInfo.getPassphrase() != null) { + String utf8Passphrase = new String(volumeInfo.getPassphrase(), StandardCharsets.UTF_8); + cloneRequest.setVolumePassphrases(Collections.singletonList(utf8Passphrase)); + } + } ResourceDefinitionCloneStarted cloneStarted = linstorApi.resourceDefinitionClone( cloneRes, cloneRequest); @@ -479,6 +701,9 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver resizeResource(linstorApi, rscName, volumeInfo.getSize()); } + updateRscGrpIfNecessary(linstorApi, rscName, getRscGrp(storagePoolVO)); + + deleteTemplateForProps(linstorApi, rscName); LinstorUtil.applyAuxProps(linstorApi, rscName, volumeInfo.getName(), volumeInfo.getAttachedVmName()); applyQoSSettings(storagePoolVO, linstorApi, rscName, volumeInfo.getMaxIops()); @@ -906,41 +1131,74 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver return LinstorUtil.getDevicePath(api, restoredName); } + /** + * Updates the template_spool_ref DB entry to indicate that this template was fully downloaded and is ready. + * @param templateId + * @param destTemplateInfoUuid + * @param destDataStoreId + * @param templateSize + */ + private void updateTemplateSpoolRef( + long templateId, String destTemplateInfoUuid, long destDataStoreId, long templateSize) { + VMTemplateStoragePoolVO destVolumeTemplateStoragePoolVO = _vmTemplatePoolDao.findByPoolTemplate( + destDataStoreId, templateId, null); + if (destVolumeTemplateStoragePoolVO == null) { + throw new CloudRuntimeException( + String.format("Unable to find template_spool_ref entry for pool_id %d and template_id %d", + destDataStoreId, templateId)); + } + destVolumeTemplateStoragePoolVO.setDownloadPercent(100); + destVolumeTemplateStoragePoolVO.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED); + destVolumeTemplateStoragePoolVO.setState(ObjectInDataStoreStateMachine.State.Ready); + destVolumeTemplateStoragePoolVO.setTemplateSize(templateSize); + destVolumeTemplateStoragePoolVO.setLocalDownloadPath(destTemplateInfoUuid); + destVolumeTemplateStoragePoolVO.setInstallPath(destTemplateInfoUuid); + _vmTemplatePoolDao.persist(destVolumeTemplateStoragePoolVO); + } + private Answer copyTemplate(DataObject srcData, DataObject dstData) { TemplateInfo tInfo = (TemplateInfo) dstData; final StoragePoolVO pool = _storagePoolDao.findById(dstData.getDataStore().getId()); final DevelopersApi api = LinstorUtil.getLinstorAPI(pool.getHostAddress()); final String rscName = LinstorUtil.RSC_PREFIX + dstData.getUuid(); - createResourceBase( + boolean newCreated = createResourceBase( LinstorUtil.RSC_PREFIX + dstData.getUuid(), tInfo.getSize(), tInfo.getName(), "", + null, + null, api, - getRscGrp(pool)); + getRscGrp(pool), + pool.getId(), + true); - int nMaxExecutionMinutes = NumbersUtil.parseInt( - _configDao.getValue(Config.SecStorageCmdExecutionTimeMax.key()), 30); - CopyCommand cmd = new CopyCommand( - srcData.getTO(), - dstData.getTO(), - nMaxExecutionMinutes * 60 * 1000, - VirtualMachineManager.ExecuteInSequence.value()); Answer answer; + if (newCreated) { + int nMaxExecutionMinutes = NumbersUtil.parseInt( + _configDao.getValue(Config.SecStorageCmdExecutionTimeMax.key()), 30); + CopyCommand cmd = new CopyCommand( + srcData.getTO(), + dstData.getTO(), + nMaxExecutionMinutes * 60 * 1000, + VirtualMachineManager.ExecuteInSequence.value()); - try { - Optional optEP = getLinstorEP(api, rscName); - if (optEP.isPresent()) { - answer = optEP.get().sendMessage(cmd); - } - else { - answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint."); + try { + Optional optEP = getLinstorEP(api, rscName); + if (optEP.isPresent()) { + answer = optEP.get().sendMessage(cmd); + } else { + answer = new Answer(cmd, false, "Unable to get matching Linstor endpoint."); + deleteResourceDefinition(pool, rscName); + } + } catch (ApiException exc) { + logger.error("copy template failed: ", exc); deleteResourceDefinition(pool, rscName); + throw new CloudRuntimeException(exc.getBestMessage()); } - } catch (ApiException exc) { - logger.error("copy template failed: ", exc); - deleteResourceDefinition(pool, rscName); - throw new CloudRuntimeException(exc.getBestMessage()); + } else { + updateTemplateSpoolRef(dstData.getId(), tInfo.getUuid(), dstData.getDataStore().getId(), srcData.getSize()); + answer = new Answer(new CopyCmdAnswer(dstData.getTO())); } return answer; } diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index cbdc9940b80..87b31d70554 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -26,6 +26,7 @@ import com.linbit.linstor.api.model.Node; import com.linbit.linstor.api.model.Properties; import com.linbit.linstor.api.model.ProviderKind; import com.linbit.linstor.api.model.Resource; +import com.linbit.linstor.api.model.ResourceDefinition; import com.linbit.linstor.api.model.ResourceDefinitionModify; import com.linbit.linstor.api.model.ResourceGroup; import com.linbit.linstor.api.model.ResourceWithVolumes; @@ -37,8 +38,11 @@ import javax.annotation.Nonnull; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; +import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -49,6 +53,7 @@ public class LinstorUtil { public final static String PROVIDER_NAME = "Linstor"; public static final String RSC_PREFIX = "cs-"; public static final String RSC_GROUP = "resourceGroup"; + public static final String CS_TEMPLATE_FOR_PREFIX = "_cs-template-for-"; public static final String TEMP_VOLUME_ID = "tempVolumeId"; @@ -288,4 +293,114 @@ public class LinstorUtil { } return answers; } + + /** + * Returns all resource definitions that start with the given `startWith` name. + * @param api + * @param startWith startWith String + * @return a List with all ResourceDefinition starting with `startWith` + * @throws ApiException + */ + public static List getRDListStartingWith(DevelopersApi api, String startWith) + throws ApiException + { + List rscDfns = api.resourceDefinitionList(null, null, null, null); + + return rscDfns.stream() + .filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(startWith.toLowerCase())) + .collect(Collectors.toList()); + } + + /** + * Returns a pair list of resource-definitions with ther 1:1 mapped resource-group objects that start with the + * resource name `startWith` + * @param api + * @param startWith + * @return + * @throws ApiException + */ + public static List> getRDAndRGListStartingWith(DevelopersApi api, String startWith) + throws ApiException + { + List foundRDs = getRDListStartingWith(api, startWith); + + List rscGrpStrings = foundRDs.stream() + .map(ResourceDefinition::getResourceGroupName) + .collect(Collectors.toList()); + + Map rscGrps = api.resourceGroupList(rscGrpStrings, null, null, null).stream() + .collect(Collectors.toMap(ResourceGroup::getName, rscGrp -> rscGrp)); + + return foundRDs.stream() + .map(rd -> new Pair<>(rd, rscGrps.get(rd.getResourceGroupName()))) + .collect(Collectors.toList()); + } + + /** + * The full name of template-for aux property key. + * @param rscGrpName + * @return + */ + public static String getTemplateForAuxPropKey(String rscGrpName) { + return String.format("Aux/%s%s", CS_TEMPLATE_FOR_PREFIX, rscGrpName); + } + + /** + * Template resource should have a _cs-template-for-... property, that indicates to which resource-group + * this template belongs, it works like a refcount to keep it alive if there are still such properties on the + * template resource. That methods set the correct property on the given resource. + * @param api + * @param rscName Resource name to set the property. + * @param rscGrpName Resource group this template should belong too. + * @throws ApiException + */ + public static void setAuxTemplateForProperty(DevelopersApi api, String rscName, String rscGrpName) + throws ApiException + { + ResourceDefinitionModify rdm = new ResourceDefinitionModify(); + Properties props = new Properties(); + String propKey = LinstorUtil.getTemplateForAuxPropKey(rscGrpName); + props.put(propKey, "true"); + rdm.setOverrideProps(props); + ApiCallRcList answers = api.resourceDefinitionModify(rscName, rdm); + + if (answers.hasError()) { + String bestError = LinstorUtil.getBestErrorMessage(answers); + LOGGER.error("Set {} on {} error: {}", propKey, rscName, bestError); + throw new CloudRuntimeException(bestError); + } else { + LOGGER.info("Set {} property on {}", propKey, rscName); + } + } + + /** + * Find the correct resource definition to clone from. + * There could be multiple resource definitions for the same template, with the same prefix. + * This method searches for which resource group the resource definition was intended and returns that. + * If no exact resource definition could be found, we return the first with a similar name as a fallback. + * If there is not even one with the correct prefix, we return null. + * @param api + * @param rscName + * @param rscGrpName + * @return The resource-definition to clone from, if no template and no match, return null. + * @throws ApiException + */ + public static ResourceDefinition findResourceDefinition(DevelopersApi api, String rscName, String rscGrpName) + throws ApiException { + List rscDfns = api.resourceDefinitionList(null, null, null, null); + + List rdsStartingWith = rscDfns.stream() + .filter(rscDfn -> rscDfn.getName().toLowerCase().startsWith(rscName.toLowerCase())) + .collect(Collectors.toList()); + + if (rdsStartingWith.isEmpty()) { + return null; + } + + Optional rd = rdsStartingWith.stream() + .filter(rscDfn -> rscDfn.getProps().containsKey(LinstorUtil.getTemplateForAuxPropKey(rscGrpName))) + .findFirst(); + + return rd.orElseGet(() -> rdsStartingWith.get(0)); + } } diff --git a/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImplTest.java b/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImplTest.java new file mode 100644 index 00000000000..75276739468 --- /dev/null +++ b/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImplTest.java @@ -0,0 +1,87 @@ +// 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 com.linbit.linstor.api.ApiException; +import com.linbit.linstor.api.DevelopersApi; +import com.linbit.linstor.api.model.AutoSelectFilter; +import com.linbit.linstor.api.model.LayerType; +import com.linbit.linstor.api.model.ResourceGroup; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LinstorPrimaryDataStoreDriverImplTest { + + private DevelopersApi api; + + @InjectMocks + private LinstorPrimaryDataStoreDriverImpl linstorPrimaryDataStoreDriver; + + @Before + public void setUp() { + api = mock(DevelopersApi.class); + } + + @Test + public void testGetEncryptedLayerList() throws ApiException { + ResourceGroup dfltRscGrp = new ResourceGroup(); + dfltRscGrp.setName("DfltRscGrp"); + + ResourceGroup bCacheRscGrp = new ResourceGroup(); + bCacheRscGrp.setName("BcacheGrp"); + AutoSelectFilter asf = new AutoSelectFilter(); + asf.setLayerStack(Arrays.asList(LayerType.DRBD.name(), LayerType.BCACHE.name(), LayerType.STORAGE.name())); + asf.setStoragePool("nvmePool"); + bCacheRscGrp.setSelectFilter(asf); + + ResourceGroup encryptedGrp = new ResourceGroup(); + encryptedGrp.setName("EncryptedGrp"); + AutoSelectFilter asf2 = new AutoSelectFilter(); + asf2.setLayerStack(Arrays.asList(LayerType.DRBD.name(), LayerType.LUKS.name(), LayerType.STORAGE.name())); + asf2.setStoragePool("ssdPool"); + encryptedGrp.setSelectFilter(asf2); + + when(api.resourceGroupList(Collections.singletonList("DfltRscGrp"), Collections.emptyList(), null, null)) + .thenReturn(Collections.singletonList(dfltRscGrp)); + when(api.resourceGroupList(Collections.singletonList("BcacheGrp"), Collections.emptyList(), null, null)) + .thenReturn(Collections.singletonList(bCacheRscGrp)); + when(api.resourceGroupList(Collections.singletonList("EncryptedGrp"), Collections.emptyList(), null, null)) + .thenReturn(Collections.singletonList(encryptedGrp)); + + List layers = linstorPrimaryDataStoreDriver.getEncryptedLayerList(api, "DfltRscGrp"); + Assert.assertEquals(Arrays.asList(LayerType.DRBD, LayerType.LUKS, LayerType.STORAGE), layers); + + layers = linstorPrimaryDataStoreDriver.getEncryptedLayerList(api, "BcacheGrp"); + Assert.assertEquals(Arrays.asList(LayerType.DRBD, LayerType.BCACHE, LayerType.LUKS, LayerType.STORAGE), layers); + + layers = linstorPrimaryDataStoreDriver.getEncryptedLayerList(api, "EncryptedGrp"); + Assert.assertEquals(Arrays.asList(LayerType.DRBD, LayerType.LUKS, LayerType.STORAGE), layers); + } +} diff --git a/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/util/LinstorUtilTest.java b/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/util/LinstorUtilTest.java new file mode 100644 index 00000000000..55f0c6ebe6d --- /dev/null +++ b/plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/datastore/util/LinstorUtilTest.java @@ -0,0 +1,127 @@ +// 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 com.linbit.linstor.api.ApiException; +import com.linbit.linstor.api.DevelopersApi; +import com.linbit.linstor.api.model.AutoSelectFilter; +import com.linbit.linstor.api.model.Node; +import com.linbit.linstor.api.model.Properties; +import com.linbit.linstor.api.model.ProviderKind; +import com.linbit.linstor.api.model.ResourceGroup; +import com.linbit.linstor.api.model.StoragePool; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class LinstorUtilTest { + + private static final String LINSTOR_URL_TEST = "devnull.com:3370"; + private DevelopersApi api; + + private Node mockNode(String name) { + Node nodeMock = new Node(); + nodeMock.setName(name); + + return nodeMock; + } + + private StoragePool mockStoragePool(String name, String node, ProviderKind kind) { + StoragePool sp = new StoragePool(); + sp.setStoragePoolName(name); + sp.setNodeName(node); + sp.setProviderKind(kind); + return sp; + } + + @Before + public void setUp() throws ApiException { + api = mock(DevelopersApi.class); + + when(api.nodeList(Collections.emptyList(), Collections.emptyList(), null, null)) + .thenReturn(Arrays.asList(mockNode("nodeA"), mockNode("nodeB"), mockNode("nodeC"))); + + ResourceGroup csGroup = new ResourceGroup(); + csGroup.setName("cloudstack"); + AutoSelectFilter asf = new AutoSelectFilter(); + asf.setPlaceCount(2); + csGroup.setSelectFilter(asf); + when(api.resourceGroupList(Collections.singletonList("cloudstack"), null, null, null)) + .thenReturn(Collections.singletonList(csGroup)); + + when(api.viewStoragePools(Collections.emptyList(), null, null, null, null, true)) + .thenReturn(Arrays.asList( + mockStoragePool("thinpool", "nodeA", ProviderKind.LVM_THIN), + mockStoragePool("thinpool", "nodeB", ProviderKind.LVM_THIN), + mockStoragePool("thinpool", "nodeC", ProviderKind.LVM_THIN) + )); + +// when(LinstorUtil.getLinstorAPI(LINSTOR_URL_TEST)).thenReturn(api); + } + + @Test + public void testGetLinstorNodeNames() throws ApiException { + List linstorNodes = LinstorUtil.getLinstorNodeNames(api); + Assert.assertEquals(Arrays.asList("nodeA", "nodeB", "nodeC"), linstorNodes); + } + + @Test + public void testGetSnapshotPath() { + { + StoragePool spLVMThin = new StoragePool(); + Properties lvmThinProps = new Properties(); + lvmThinProps.put("StorDriver/StorPoolName", "storage/storage-thin"); + spLVMThin.setProps(lvmThinProps); + spLVMThin.setProviderKind(ProviderKind.LVM_THIN); + String snapPath = LinstorUtil.getSnapshotPath(spLVMThin, "cs-cb32532a-dd8f-47e0-a81c-8a75573d3545", "snap3"); + Assert.assertEquals("/dev/mapper/storage-cs--cb32532a--dd8f--47e0--a81c--8a75573d3545_00000_snap3", snapPath); + } + + { + StoragePool spZFS = new StoragePool(); + Properties zfsProps = new Properties(); + zfsProps.put("StorDriver/StorPoolName", "linstorPool"); + spZFS.setProps(zfsProps); + spZFS.setProviderKind(ProviderKind.ZFS); + + String snapPath = LinstorUtil.getSnapshotPath(spZFS, "cs-cb32532a-dd8f-47e0-a81c-8a75573d3545", "snap2"); + Assert.assertEquals("zfs://linstorPool/cs-cb32532a-dd8f-47e0-a81c-8a75573d3545_00000@snap2", snapPath); + } + } + + @Test + public void testGetRscGroupStoragePools() throws ApiException { + List storagePools = LinstorUtil.getRscGroupStoragePools(api, "cloudstack"); + + List names = storagePools.stream() + .map(sp -> String.format("%s::%s", sp.getNodeName(), sp.getStoragePoolName())) + .collect(Collectors.toList()); + Assert.assertEquals(names, Arrays.asList("nodeA::thinpool", "nodeB::thinpool", "nodeC::thinpool")); + } +} diff --git a/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java b/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java index 2c0fccc2131..445799f0c04 100644 --- a/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java +++ b/plugins/storage/volume/primera/src/main/java/org/apache/cloudstack/storage/datastore/adapter/primera/PrimeraAdapter.java @@ -146,16 +146,18 @@ public class PrimeraAdapter implements ProviderAdapter { } // determine volume type based on offering - // THIN: tpvv=true, reduce=false - // SPARSE: tpvv=true, reduce=true - // THICK: tpvv=false, tpZeroFill=true (not supported) + // tpvv -- thin provisioned virtual volume (no deduplication) + // reduce -- thin provisioned virtual volume (with duplication and compression, also known as DECO) + // these are the only choices with newer Primera devices + // we will use THIN for the deduplicated/compressed type and SPARSE for thin-only without dedup/compress + // note: DECO/reduce type must be at least 16GB in size if (diskOffering != null) { if (diskOffering.getType() == ProvisioningType.THIN) { - request.setTpvv(true); - request.setReduce(false); - } else if (diskOffering.getType() == ProvisioningType.SPARSE) { request.setTpvv(false); request.setReduce(true); + } else if (diskOffering.getType() == ProvisioningType.SPARSE) { + request.setTpvv(true); + request.setReduce(false); } else if (diskOffering.getType() == ProvisioningType.FAT) { throw new RuntimeException("This storage provider does not support FAT provisioned volumes"); } @@ -166,8 +168,16 @@ public class PrimeraAdapter implements ProviderAdapter { } } else { // default to deduplicated volume - request.setReduce(true); request.setTpvv(false); + request.setReduce(true); + } + + if (request.getReduce() == true) { + // check if sizeMiB is less than 16GB adjust up to 16GB. The AdaptiveDatastoreDriver will automatically + // update this on the cloudstack side to match + if (request.getSizeMiB() < 16 * 1024) { + request.setSizeMiB(16 * 1024); + } } request.setComment(ProviderVolumeNamer.generateObjectComment(context, dataIn)); @@ -185,8 +195,11 @@ public class PrimeraAdapter implements ProviderAdapter { if (host == null) { throw new RuntimeException("Unable to find host " + hostname + " on storage provider"); } - request.setHostname(host.getName()); + // check if we already have a vlun for requested host + Integer vlun = hasVlun(hostname, hostname); + if (vlun == null) { + request.setHostname(host.getName()); request.setVolumeName(dataIn.getExternalName()); request.setAutoLun(true); // auto-lun returned here: Location: /api/v1/vluns/test_vv02,252,mysystem,2:2:4 @@ -198,7 +211,13 @@ public class PrimeraAdapter implements ProviderAdapter { if (toks.length <2) { throw new RuntimeException("Attach volume failed with invalid location response to vlun add command on storage provider. Provided location: " + location); } - return toks[1]; + try { + vlun = Integer.parseInt(toks[1]); + } catch (NumberFormatException e) { + throw new RuntimeException("VLUN attach request succeeded but the VLUN value is not a valid number: " + toks[1]); + } + } + return vlun.toString(); } /** @@ -233,6 +252,20 @@ public class PrimeraAdapter implements ProviderAdapter { } } + private Integer hasVlun(String externalName, String hostname) { + PrimeraVlunList list = getVluns(externalName); + if (list != null && list.getMembers().size() > 0) { + for (PrimeraVlun vlun: list.getMembers()) { + if (hostname != null) { + if (vlun.getHostname().equals(hostname) || vlun.getHostname().equals(hostname.split("\\.")[0])) { + return vlun.getLun(); + } + } + } + } + return null; + } + public void removeVlun(String name, Integer lunid, String hostString) { // hostString can be a hostname OR "set:". It is stored this way // in the appliance and returned as the vlun's name/string. diff --git a/pom.xml b/pom.xml index a2a81a82c3e..1087ff95531 100644 --- a/pom.xml +++ b/pom.xml @@ -175,7 +175,7 @@ 10.1 2.6.6 0.6.0 - 0.5.2 + 0.6.0 0.10.2 3.4.4_1 4.0.1 diff --git a/scripts/storage/multipath/cleanStaleMaps.sh b/scripts/storage/multipath/cleanStaleMaps.sh index 90b9bef5a8d..c1ded42943c 100755 --- a/scripts/storage/multipath/cleanStaleMaps.sh +++ b/scripts/storage/multipath/cleanStaleMaps.sh @@ -22,10 +22,18 @@ # ############################################################################################# +SCRIPT_NAME=$(basename "$0") + +if [[ $(pgrep -f ${SCRIPT_NAME}) != "$$" ]]; then + echo "Another instance of ${SCRIPT_NAME} is already running! Exiting" + exit +fi + + cd $(dirname $0) for WWID in $(multipathd list maps status | awk '{ if ($4 == 0) { print substr($1,2); }}'); do - ./removeVolume.sh ${WWID} + ./disconnectVolume.sh ${WWID} done exit 0 diff --git a/scripts/storage/multipath/disconnectVolume.sh b/scripts/storage/multipath/disconnectVolume.sh index 067e561f8a3..f894076927f 100755 --- a/scripts/storage/multipath/disconnectVolume.sh +++ b/scripts/storage/multipath/disconnectVolume.sh @@ -66,6 +66,9 @@ fi logger -t CS_SCSI_VOL_REMOVE "${WWID} successfully purged from multipath along with slave devices" +# Added to give time for the event to be fired to the server +sleep 10 + echo "$(date): ${WWID} removed" exit 0 diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 40249dda17a..26c5cd4e10f 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -355,263 +355,263 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q private static final String ID_FIELD = "id"; @Inject - private AccountManager accountMgr; + AccountManager accountMgr; @Inject - private ProjectManager _projectMgr; + ProjectManager _projectMgr; @Inject - private DomainDao _domainDao; + DomainDao _domainDao; @Inject - private DomainJoinDao _domainJoinDao; + DomainJoinDao _domainJoinDao; @Inject - private UserAccountJoinDao _userAccountJoinDao; + UserAccountJoinDao _userAccountJoinDao; @Inject - private EventDao eventDao; + EventDao eventDao; @Inject - private EventJoinDao _eventJoinDao; + EventJoinDao _eventJoinDao; @Inject - private ResourceTagJoinDao _resourceTagJoinDao; + ResourceTagJoinDao _resourceTagJoinDao; @Inject - private InstanceGroupJoinDao _vmGroupJoinDao; + InstanceGroupJoinDao _vmGroupJoinDao; @Inject - private UserVmJoinDao _userVmJoinDao; + UserVmJoinDao _userVmJoinDao; @Inject - private UserVmDao userVmDao; + UserVmDao userVmDao; @Inject - private VMInstanceDao _vmInstanceDao; + VMInstanceDao _vmInstanceDao; @Inject - private SecurityGroupJoinDao _securityGroupJoinDao; + SecurityGroupJoinDao _securityGroupJoinDao; @Inject - private SecurityGroupVMMapDao securityGroupVMMapDao; + SecurityGroupVMMapDao securityGroupVMMapDao; @Inject - private DomainRouterJoinDao _routerJoinDao; + DomainRouterJoinDao _routerJoinDao; @Inject - private ProjectInvitationJoinDao _projectInvitationJoinDao; + ProjectInvitationJoinDao _projectInvitationJoinDao; @Inject - private ProjectJoinDao _projectJoinDao; + ProjectJoinDao _projectJoinDao; @Inject - private ProjectDao _projectDao; + ProjectDao _projectDao; @Inject - private ProjectAccountDao _projectAccountDao; + ProjectAccountDao _projectAccountDao; @Inject - private ProjectAccountJoinDao _projectAccountJoinDao; + ProjectAccountJoinDao _projectAccountJoinDao; @Inject - private HostJoinDao hostJoinDao; + HostJoinDao hostJoinDao; @Inject - private VolumeJoinDao _volumeJoinDao; + VolumeJoinDao _volumeJoinDao; @Inject - private AccountDao _accountDao; + AccountDao _accountDao; @Inject - private AccountJoinDao _accountJoinDao; + AccountJoinDao _accountJoinDao; @Inject - private AsyncJobJoinDao _jobJoinDao; + AsyncJobJoinDao _jobJoinDao; @Inject - private StoragePoolJoinDao _poolJoinDao; + StoragePoolJoinDao _poolJoinDao; @Inject - private StoragePoolTagsDao _storageTagDao; + StoragePoolTagsDao _storageTagDao; @Inject - private HostTagsDao _hostTagDao; + HostTagsDao _hostTagDao; @Inject - private ImageStoreJoinDao _imageStoreJoinDao; + ImageStoreJoinDao _imageStoreJoinDao; @Inject - private DiskOfferingJoinDao _diskOfferingJoinDao; + DiskOfferingJoinDao _diskOfferingJoinDao; @Inject - private DiskOfferingDetailsDao _diskOfferingDetailsDao; + DiskOfferingDetailsDao _diskOfferingDetailsDao; @Inject - private ServiceOfferingJoinDao _srvOfferingJoinDao; + ServiceOfferingJoinDao _srvOfferingJoinDao; @Inject - private ServiceOfferingDao _srvOfferingDao; + ServiceOfferingDao _srvOfferingDao; @Inject - private ServiceOfferingDetailsDao _srvOfferingDetailsDao; + ServiceOfferingDetailsDao _srvOfferingDetailsDao; @Inject - private DiskOfferingDao _diskOfferingDao; + DiskOfferingDao _diskOfferingDao; @Inject - private DataCenterJoinDao _dcJoinDao; + DataCenterJoinDao _dcJoinDao; @Inject - private DomainRouterDao _routerDao; + DomainRouterDao _routerDao; @Inject - private HighAvailabilityManager _haMgr; + HighAvailabilityManager _haMgr; @Inject - private VMTemplateDao _templateDao; + VMTemplateDao _templateDao; @Inject - private TemplateJoinDao _templateJoinDao; + TemplateJoinDao _templateJoinDao; @Inject - private ResourceManager _resourceMgr; + ResourceManager _resourceMgr; @Inject - private ResourceMetaDataService _resourceMetaDataMgr; + ResourceMetaDataService _resourceMetaDataMgr; @Inject - private ResourceManagerUtil resourceManagerUtil; + ResourceManagerUtil resourceManagerUtil; @Inject - private AffinityGroupVMMapDao _affinityGroupVMMapDao; + AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject - private AffinityGroupJoinDao _affinityGroupJoinDao; + AffinityGroupJoinDao _affinityGroupJoinDao; @Inject - private DedicatedResourceDao _dedicatedDao; + DedicatedResourceDao _dedicatedDao; @Inject - private DomainManager _domainMgr; + DomainManager _domainMgr; @Inject - private AffinityGroupDomainMapDao _affinityGroupDomainMapDao; + AffinityGroupDomainMapDao _affinityGroupDomainMapDao; @Inject - private ResourceTagDao resourceTagDao; + ResourceTagDao resourceTagDao; @Inject - private DataStoreManager dataStoreManager; + DataStoreManager dataStoreManager; @Inject ManagementServerJoinDao managementServerJoinDao; @Inject - public VpcVirtualNetworkApplianceService routerService; + VpcVirtualNetworkApplianceService routerService; @Inject - private ResponseGenerator responseGenerator; + ResponseGenerator responseGenerator; @Inject - private RouterHealthCheckResultDao routerHealthCheckResultDao; + RouterHealthCheckResultDao routerHealthCheckResultDao; @Inject - private PrimaryDataStoreDao storagePoolDao; + PrimaryDataStoreDao storagePoolDao; @Inject - private StoragePoolDetailsDao _storagePoolDetailsDao; + StoragePoolDetailsDao _storagePoolDetailsDao; @Inject - private ProjectInvitationDao projectInvitationDao; + ProjectInvitationDao projectInvitationDao; @Inject - private TemplateDataStoreDao templateDataStoreDao; + TemplateDataStoreDao templateDataStoreDao; @Inject - private VMTemplatePoolDao templatePoolDao; + VMTemplatePoolDao templatePoolDao; @Inject - private SnapshotDataStoreDao snapshotDataStoreDao; + SnapshotDataStoreDao snapshotDataStoreDao; @Inject - private UserDao userDao; + UserDao userDao; @Inject - private VirtualMachineManager virtualMachineManager; + VirtualMachineManager virtualMachineManager; @Inject - private VolumeDao volumeDao; + VolumeDao volumeDao; @Inject - private ResourceIconDao resourceIconDao; + ResourceIconDao resourceIconDao; @Inject StorageManager storageManager; @Inject - private ManagementServerHostDao msHostDao; + ManagementServerHostDao msHostDao; @Inject - private SecondaryStorageHeuristicDao secondaryStorageHeuristicDao; + SecondaryStorageHeuristicDao secondaryStorageHeuristicDao; @Inject - private NetworkDao networkDao; + NetworkDao networkDao; @Inject - private IPAddressDao ipAddressDao; + IPAddressDao ipAddressDao; @Inject - private NicDao nicDao; + NicDao nicDao; @Inject - private HostDao hostDao; + HostDao hostDao; @Inject - private OutOfBandManagementDao outOfBandManagementDao; + OutOfBandManagementDao outOfBandManagementDao; @Inject - private InstanceGroupVMMapDao instanceGroupVMMapDao; + InstanceGroupVMMapDao instanceGroupVMMapDao; @Inject - private AffinityGroupVMMapDao affinityGroupVMMapDao; + AffinityGroupVMMapDao affinityGroupVMMapDao; @Inject - private UserVmDetailsDao userVmDetailsDao; + UserVmDetailsDao userVmDetailsDao; @Inject - private SSHKeyPairDao sshKeyPairDao; + SSHKeyPairDao sshKeyPairDao; @Inject - private BackupOfferingDao backupOfferingDao; + BackupOfferingDao backupOfferingDao; @Inject - private AutoScaleVmGroupDao autoScaleVmGroupDao; + AutoScaleVmGroupDao autoScaleVmGroupDao; @Inject - private AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao; + AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao; @Inject - private SnapshotJoinDao snapshotJoinDao; + SnapshotJoinDao snapshotJoinDao; @Inject - private ObjectStoreDao objectStoreDao; + ObjectStoreDao objectStoreDao; @Inject - private BucketDao bucketDao; + BucketDao bucketDao; @Inject EntityManager entityManager; @Inject - private PublicIpQuarantineDao publicIpQuarantineDao; + PublicIpQuarantineDao publicIpQuarantineDao; @Inject - private StoragePoolHostDao storagePoolHostDao; + StoragePoolHostDao storagePoolHostDao; @Inject - private ClusterDao clusterDao; + ClusterDao clusterDao; @Inject - private ManagementServerHostPeerJoinDao mshostPeerJoinDao; + ManagementServerHostPeerJoinDao mshostPeerJoinDao; private SearchCriteria getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) { @@ -663,7 +663,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForUsers(ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException { Pair, Integer> result = searchForUsersInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); if (CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) { responseView = ResponseView.Full; } @@ -833,7 +833,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForEvents(ListEventsCmd cmd) { Pair, Integer> result = searchForEventsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List eventResponses = ViewResponseHelper.createEventResponse(result.first().toArray(new EventJoinVO[result.first().size()])); response.setResponses(eventResponses, result.second()); return response; @@ -905,7 +905,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } } - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); @@ -996,7 +996,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.setParameters("archived", cmd.getArchived()); } - Pair, Integer> eventPair = null; + Pair, Integer> eventPair; // event_view will not have duplicate rows for each event, so // searchAndCount should be good enough. if ((entryTime != null) && (duration != null)) { @@ -1033,7 +1033,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse listTags(ListTagsCmd cmd) { Pair, Integer> tags = listTagsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List tagResponses = ViewResponseHelper.createResourceTagResponse(false, tags.first().toArray(new ResourceTagJoinVO[tags.first().size()])); response.setResponses(tagResponses, tags.second()); return response; @@ -1041,7 +1041,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q private Pair, Integer> listTagsInternal(ListTagsCmd cmd) { Account caller = CallContext.current().getCallingAccount(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); String key = cmd.getKey(); String value = cmd.getValue(); String resourceId = cmd.getResourceId(); @@ -1061,7 +1061,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } } - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), projectId, permittedAccounts, domainIdRecursiveListProject, listAll, false); Long domainId = domainIdRecursiveListProject.first(); @@ -1113,14 +1113,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.setParameters("customer", customerName); } - Pair, Integer> result = _resourceTagJoinDao.searchAndCount(sc, searchFilter); - return result; + return _resourceTagJoinDao.searchAndCount(sc, searchFilter); } @Override public ListResponse searchForVmGroups(ListVMGroupsCmd cmd) { Pair, Integer> groups = searchForVmGroupsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List grpResponses = ViewResponseHelper.createInstanceGroupResponse(groups.first().toArray(new InstanceGroupJoinVO[groups.first().size()])); response.setResponses(grpResponses, groups.second()); return response; @@ -1132,9 +1131,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q String keyword = cmd.getKeyword(); Account caller = CallContext.current().getCallingAccount(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); @@ -1328,7 +1327,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Filter searchFilter = new Filter(UserVmVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); - List ids = null; + List ids; if (cmd.getId() != null) { if (cmd.getIds() != null && !cmd.getIds().isEmpty()) { throw new InvalidParameterValueException("Specify either id or ids but not both parameters"); @@ -1694,7 +1693,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForSecurityGroups(ListSecurityGroupsCmd cmd) { Pair, Integer> result = searchForSecurityGroupsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List routerResponses = ViewResponseHelper.createSecurityGroupResponses(result.first()); response.setResponses(routerResponses, result.second()); return response; @@ -1706,7 +1705,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q String securityGroup = cmd.getSecurityGroupName(); Long id = cmd.getId(); Object keyword = cmd.getKeyword(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); Map tags = cmd.getTags(); if (instanceId != null) { @@ -1718,7 +1717,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return listSecurityGroupRulesByVM(instanceId.longValue(), cmd.getStartIndex(), cmd.getPageSizeVal()); } - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); @@ -1777,7 +1776,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sgIds[i++] = v.getId(); } List sgs = _securityGroupJoinDao.searchByIds(sgIds); - return new Pair, Integer>(sgs, count); + return new Pair<>(sgs, count); } private Pair, Integer> listSecurityGroupRulesByVM(long vmId, long pageInd, long pageSize) { @@ -1786,7 +1785,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer count = sgVmMappingPair.second(); if (count.intValue() == 0) { // handle empty result cases - return new Pair, Integer>(new ArrayList(), count); + return new Pair<>(new ArrayList<>(), count); } List sgVmMappings = sgVmMappingPair.first(); Long[] sgIds = new Long[sgVmMappings.size()]; @@ -1795,14 +1794,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sgIds[i++] = sgVm.getSecurityGroupId(); } List sgs = _securityGroupJoinDao.searchByIds(sgIds); - return new Pair, Integer>(sgs, count); + return new Pair<>(sgs, count); } @Override public ListResponse searchForRouters(ListRoutersCmd cmd) { Pair, Integer> result = searchForRoutersInternal(cmd, cmd.getId(), cmd.getRouterName(), cmd.getState(), cmd.getZoneId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), cmd.getKeyword(), cmd.getNetworkId(), cmd.getVpcId(), cmd.getForVpc(), cmd.getRole(), cmd.getVersion(), cmd.isHealthCheckFailed()); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List routerResponses = ViewResponseHelper.createDomainRouterResponse(result.first().toArray(new DomainRouterJoinVO[result.first().size()])); if (VirtualNetworkApplianceManager.RouterHealthChecksEnabled.value()) { for (DomainRouterResponse res : routerResponses) { @@ -1822,7 +1821,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q public ListResponse searchForInternalLbVms(ListInternalLBVMsCmd cmd) { Pair, Integer> result = searchForRoutersInternal(cmd, cmd.getId(), cmd.getRouterName(), cmd.getState(), cmd.getZoneId(), cmd.getPodId(), null, cmd.getHostId(), cmd.getKeyword(), cmd.getNetworkId(), cmd.getVpcId(), cmd.getForVpc(), cmd.getRole(), null, null); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List routerResponses = ViewResponseHelper.createDomainRouterResponse(result.first().toArray(new DomainRouterJoinVO[result.first().size()])); if (VirtualNetworkApplianceManager.RouterHealthChecksEnabled.value()) { for (DomainRouterResponse res : routerResponses) { @@ -1843,9 +1842,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Long hostId, String keyword, Long networkId, Long vpcId, Boolean forVpc, String role, String version, Boolean isHealthCheckFailed) { Account caller = CallContext.current().getCallingAccount(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); @@ -1901,7 +1900,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sb.and("routerId", sb.entity().getId(), SearchCriteria.Op.NIN); } } else if (isHealthCheckFailed) { - return new Pair, Integer>(Collections.emptyList(), 0); + return new Pair<>(Collections.emptyList(), 0); } } @@ -1981,13 +1980,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q vrIds[i++] = v.getId(); } List vrs = _routerJoinDao.searchByIds(vrIds); - return new Pair, Integer>(vrs, count); + return new Pair<>(vrs, count); } @Override public ListResponse listProjects(ListProjectsCmd cmd) { Pair, Integer> projects = listProjectsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List projectResponses = ViewResponseHelper.createProjectResponse(cmd.getDetails(), projects.first().toArray(new ProjectJoinVO[projects.first().size()])); response.setResponses(projectResponses, projects.second()); return response; @@ -2155,13 +2154,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q prjIds[i++] = v.getId(); } List prjs = _projectJoinDao.searchByIds(prjIds); - return new Pair, Integer>(prjs, count); + return new Pair<>(prjs, count); } @Override public ListResponse listProjectInvitations(ListProjectInvitationsCmd cmd) { Pair, Integer> invites = listProjectInvitationsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List projectInvitationResponses = ViewResponseHelper.createProjectInvitationResponse(invites.first().toArray(new ProjectInvitationJoinVO[invites.first().size()])); response.setResponses(projectInvitationResponses, invites.second()); @@ -2183,9 +2182,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Account caller = CallContext.current().getCallingAccount(); User callingUser = CallContext.current().getCallingUser(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); + Ternary domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null); accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, listAll, true); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); @@ -2237,7 +2236,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse listProjectAccounts(ListProjectAccountsCmd cmd) { Pair, Integer> projectAccounts = listProjectAccountsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List projectResponses = ViewResponseHelper.createProjectAccountResponse(projectAccounts.first().toArray(new ProjectAccountJoinVO[projectAccounts.first().size()])); response.setResponses(projectResponses, projectAccounts.second()); return response; @@ -2311,7 +2310,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // Right now it is handled separately outside this QueryService logger.debug(">>>Searching for hosts>>>"); Pair, Integer> hosts = searchForServersInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); logger.debug(">>>Generating Response>>>"); List hostResponses = ViewResponseHelper.createHostResponse(cmd.getDetails(), hosts.first().toArray(new HostJoinVO[hosts.first().size()])); response.setResponses(hostResponses, hosts.second()); @@ -2723,7 +2722,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForDomains(ListDomainsCmd cmd) { Pair, Integer> result = searchForDomainsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); ResponseView respView = ResponseView.Restricted; if (cmd instanceof ListDomainsCmdByAdmin) { @@ -2825,7 +2824,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForAccounts(ListAccountsCmd cmd) { Pair, Integer> result = searchForAccountsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); ResponseView respView = ResponseView.Restricted; if (cmd instanceof ListAccountsCmdByAdmin) { @@ -3011,7 +3010,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForAsyncJobs(ListAsyncJobsCmd cmd) { Pair, Integer> result = searchForAsyncJobsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List jobResponses = ViewResponseHelper.createAsyncJobResponse(result.first().toArray(new AsyncJobJoinVO[result.first().size()])); response.setResponses(jobResponses, result.second()); return response; @@ -3021,9 +3020,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Account caller = CallContext.current().getCallingAccount(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), null, permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); @@ -3183,7 +3182,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse searchForStorageTags(ListStorageTagsCmd cmd) { Pair, Integer> result = searchForStorageTagsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List tagResponses = ViewResponseHelper.createStorageTagResponse(result.first().toArray(new StoragePoolTagVO[result.first().size()])); response.setResponses(tagResponses, result.second()); @@ -3218,13 +3217,13 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q List vrs = _storageTagDao.searchByIds(vrIds); - return new Pair, Integer>(vrs, count); + return new Pair<>(vrs, count); } @Override public ListResponse searchForHostTags(ListHostTagsCmd cmd) { Pair, Integer> result = searchForHostTagsInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List tagResponses = ViewResponseHelper.createHostTagResponse(result.first().toArray(new HostTagVO[result.first().size()])); response.setResponses(tagResponses, result.second()); @@ -3259,7 +3258,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q List vrs = _hostTagDao.searchByIds(vrIds); - return new Pair, Integer>(vrs, count); + return new Pair<>(vrs, count); } @Override @@ -3342,14 +3341,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q vrIds[i++] = v.getId(); } List vrs = _imageStoreJoinDao.searchByIds(vrIds); - return new Pair, Integer>(vrs, count); + return new Pair<>(vrs, count); } @Override public ListResponse searchForSecondaryStagingStores(ListSecondaryStagingStoresCmd cmd) { Pair, Integer> result = searchForCacheStoresInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List poolResponses = ViewResponseHelper.createImageStoreResponse(result.first().toArray(new ImageStoreJoinVO[result.first().size()])); response.setResponses(poolResponses, result.second()); @@ -3421,7 +3420,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q vrIds[i++] = v.getId(); } List vrs = _imageStoreJoinDao.searchByIds(vrIds); - return new Pair, Integer>(vrs, count); + return new Pair<>(vrs, count); } @@ -4245,7 +4244,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse listDataCenters(ListZonesCmd cmd) { Pair, Integer> result = listDataCentersInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); ResponseView respView = ResponseView.Restricted; if (cmd instanceof ListZonesCmdByAdmin || CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) { @@ -4332,7 +4331,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // list // find all domain Id up to root domain for this account - List domainIds = new ArrayList(); + List domainIds = new ArrayList<>(); DomainVO domainRecord = _domainDao.findById(account.getDomainId()); if (domainRecord == null) { logger.error("Could not find the domainId for account: {}", account); @@ -4372,7 +4371,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // it was decided to return all zones for the domain admin, and // everything above till root, as well as zones till the domain // leaf - List domainIds = new ArrayList(); + List domainIds = new ArrayList<>(); DomainVO domainRecord = _domainDao.findById(account.getDomainId()); if (domainRecord == null) { logger.error("Could not find the domainId for account: {}", account); @@ -4412,8 +4411,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // one VM running there Boolean available = cmd.isAvailable(); if (account != null) { - if ((available != null) && Boolean.FALSE.equals(available)) { - Set dcIds = new HashSet(); // data centers with + if (Boolean.FALSE.equals(available)) { + Set dcIds = new HashSet<>(); // data centers with // at least one VM // running List routers = _routerDao.listBy(account.getId()); @@ -4421,7 +4420,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q dcIds.add(router.getDataCenterId()); } if (dcIds.size() == 0) { - return new Pair, Integer>(new ArrayList(), 0); + return new Pair<>(new ArrayList<>(), 0); } else { sc.addAnd("id", SearchCriteria.Op.IN, dcIds.toArray()); } @@ -4445,7 +4444,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q private List removeDedicatedZoneNotSuitabe(List domainIds) { // remove dedicated zone of other domain - List dedicatedZoneIds = new ArrayList(); + List dedicatedZoneIds = new ArrayList<>(); List dedicatedResources = _dedicatedDao.listZonesNotInDomainIds(domainIds); for (DedicatedResourceVO dr : dedicatedResources) { if (dr != null) { @@ -4486,7 +4485,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Override public ListResponse listTemplates(ListTemplatesCmd cmd) { Pair, Integer> result = searchForTemplatesInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); ResponseView respView = ResponseView.Restricted; if (cmd instanceof ListTemplatesCmdByAdmin) { @@ -4514,14 +4513,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q listAll = true; } - List permittedAccountIds = new ArrayList(); + List permittedAccountIds = new ArrayList<>(); Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters( caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false ); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); for (Long accountId : permittedAccountIds) { permittedAccounts.add(accountMgr.getAccount(accountId)); } @@ -4559,11 +4558,11 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q if (!isIso) { hypers = _resourceMgr.listAvailHypervisorInZone(null); if (hypers == null || hypers.isEmpty()) { - return new Pair, Integer>(new ArrayList(), 0); + return new Pair<>(new ArrayList<>(), 0); } } - VMTemplateVO template = null; + VMTemplateVO template; Filter searchFilter = new Filter(TemplateJoinVO.class, "sortKey", SortKeyAscending.value(), startIndex, pageSize); searchFilter.addOrderBy(TemplateJoinVO.class, "tempZonePair", SortKeyAscending.value()); @@ -4634,7 +4633,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("id", SearchCriteria.Op.EQ, templateId); } else { - DomainVO domain = null; + DomainVO domain; if (!permittedAccounts.isEmpty()) { domain = _domainDao.findById(permittedAccounts.get(0).getDomainId()); } else { @@ -4656,15 +4655,15 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("domainPath", SearchCriteria.Op.LIKE, domain.getPath() + "%"); } - List relatedDomainIds = new ArrayList(); - List permittedAccountIds = new ArrayList(); + List relatedDomainIds = new ArrayList<>(); + List permittedAccountIds = new ArrayList<>(); if (!permittedAccounts.isEmpty()) { for (Account account : permittedAccounts) { permittedAccountIds.add(account.getId()); boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community); // get all parent domain ID's all the way till root domain - DomainVO domainTreeNode = null; + DomainVO domainTreeNode; //if template filter is featured, or community, all child domains should be included in search if (publicTemplates) { domainTreeNode = _domainDao.findById(Domain.ROOT_DOMAIN); @@ -4895,7 +4894,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q // sc.addAnd("removed", SearchCriteria.Op.NULL); // search unique templates and find details by Ids - Pair, Integer> uniqueTmplPair = null; + Pair, Integer> uniqueTmplPair; if (showRemovedTmpl) { uniqueTmplPair = _templateJoinDao.searchIncludingRemovedAndCount(sc, searchFilter); } else { @@ -4926,7 +4925,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return templateDataPair; } List templateData = templateDataPair.first(); - List templates = null; + List templates; if (showUnique) { Long[] templateIds = templateData.stream().map(template -> template.getId()).toArray(Long[]::new); templates = _templateJoinDao.findByDistinctIds(templateIds); @@ -4935,7 +4934,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q templates = _templateJoinDao.searchByTemplateZonePair(showRemoved, templateZonePairs); } - return new Pair, Integer>(templates, count); + return new Pair<>(templates, count); } @Override @@ -5056,7 +5055,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q if (HypervisorType.KVM.equals(hypervisorType)) { options.put(VmDetailConstants.CPU_THREAD_PER_CORE, Collections.emptyList()); options.put(VmDetailConstants.NIC_ADAPTER, Arrays.asList("e1000", "virtio", "rtl8139", "vmxnet3", "ne2k_pci")); - options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, Arrays.asList("osdefault", "ide", "scsi", "virtio")); + options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, Arrays.asList("osdefault", "ide", "scsi", "virtio", "virtio-blk")); options.put(VmDetailConstants.VIDEO_HARDWARE, Arrays.asList("cirrus", "vga", "qxl", "virtio")); options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList()); options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific")); @@ -5110,8 +5109,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q return listAffinityGroupsByVM(vmId.longValue(), startIndex, pageSize); } - List permittedAccounts = new ArrayList(); - Ternary ternary = new Ternary(domainId, isRecursive, null); + List permittedAccounts = new ArrayList<>(); + Ternary ternary = new Ternary<>(domainId, isRecursive, null); accountMgr.buildACLSearchParameters(caller, affinityGroupId, accountName, projectId, permittedAccounts, ternary, listAll, false); @@ -5127,7 +5126,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Pair, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, searchFilter); // search group details by ids - List affinityGroups = new ArrayList(); + List affinityGroups = new ArrayList<>(); Integer count = uniqueGroupsPair.second(); if (count.intValue() != 0) { @@ -5143,7 +5142,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q if (!permittedAccounts.isEmpty()) { // add domain level affinity groups if (domainId != null) { - SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); Pair, Integer> groupsPair = listDomainLevelAffinityGroups(scDomain, searchFilter, domainId); affinityGroups.addAll(groupsPair.first()); @@ -5152,7 +5151,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q for (Long permAcctId : permittedAccounts) { Account permittedAcct = _accountDao.findById(permAcctId); - SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); Pair, Integer> groupsPair = listDomainLevelAffinityGroups(scDomain, searchFilter, permittedAcct.getDomainId()); affinityGroups.addAll(groupsPair.first()); @@ -5161,14 +5160,14 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } } else if (((permittedAccounts.isEmpty()) && (domainId != null) && isRecursive)) { // list all domain level affinity groups for the domain admin case - SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); Pair, Integer> groupsPair = listDomainLevelAffinityGroups(scDomain, searchFilter, domainId); affinityGroups.addAll(groupsPair.first()); count += groupsPair.second(); } - return new Pair, Integer>(affinityGroups, count); + return new Pair<>(affinityGroups, count); } @@ -5254,7 +5253,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer count = agVmMappingPair.second(); if (count.intValue() == 0) { // handle empty result cases - return new Pair, Integer>(new ArrayList(), count); + return new Pair<>(new ArrayList<>(), count); } List agVmMappings = agVmMappingPair.first(); Long[] agIds = new Long[agVmMappings.size()]; @@ -5263,11 +5262,11 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q agIds[i++] = agVm.getAffinityGroupId(); } List ags = _affinityGroupJoinDao.searchByIds(agIds); - return new Pair, Integer>(ags, count); + return new Pair<>(ags, count); } private Pair, Integer> listDomainLevelAffinityGroups(SearchCriteria sc, Filter searchFilter, long domainId) { - List affinityGroupIds = new ArrayList(); + List affinityGroupIds = new ArrayList<>(); Set allowedDomains = _domainMgr.getDomainParentIds(domainId); List maps = _affinityGroupDomainMapDao.listByDomain(allowedDomains.toArray()); @@ -5290,7 +5289,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer count = uniqueGroupsPair.second(); if (count.intValue() == 0) { // empty result - return new Pair<>(new ArrayList(), 0); + return new Pair<>(new ArrayList<>(), 0); } List uniqueGroups = uniqueGroupsPair.first(); Long[] vrIds = new Long[uniqueGroups.size()]; @@ -5301,7 +5300,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q List vrs = _affinityGroupJoinDao.searchByIds(vrIds); return new Pair<>(vrs, count); } else { - return new Pair<>(new ArrayList(), 0); + return new Pair<>(new ArrayList<>(), 0); } } @@ -5332,7 +5331,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } } - List detailList = new ArrayList(); + List detailList = new ArrayList<>(); ResourceDetail requestedDetail = null; if (key == null) { @@ -5346,7 +5345,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q detailList = _resourceMetaDataMgr.getDetails(resourceType, key, value, forDisplay); } - List responseList = new ArrayList(); + List responseList = new ArrayList<>(); if (requestedDetail != null) { ResourceDetailResponse detailResponse = createResourceDetailsResponse(requestedDetail, resourceType); responseList.add(detailResponse); @@ -5596,7 +5595,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", SortKeyAscending.value(), startIndex, pageSize); List permittedAccountIds = new ArrayList<>(); - Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); + Ternary domainIdRecursiveListProject = new Ternary<>(domainId, isRecursive, null); accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccountIds, domainIdRecursiveListProject, listAll, false); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); domainId = domainIdRecursiveListProject.first(); @@ -5729,7 +5728,6 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Integer count = snapshotDataPair.second(); if (count == 0) { - // empty result return snapshotDataPair; } List snapshotData = snapshotDataPair.first(); @@ -5739,13 +5737,12 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } else { snapshots = snapshotJoinDao.searchBySnapshotStorePair(snapshotData.stream().map(SnapshotJoinVO::getSnapshotStorePair).toArray(String[]::new)); } - return new Pair<>(snapshots, count); } public ListResponse searchForObjectStores(ListObjectStoragePoolsCmd cmd) { Pair, Integer> result = searchForObjectStoresInternal(cmd); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); List poolResponses = ViewResponseHelper.createObjectStoreResponse(result.first().toArray(new ObjectStoreVO[result.first().size()])); response.setResponses(poolResponses, result.second()); @@ -5830,7 +5827,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q Long startIndex = cmd.getStartIndex(); Long pageSize = cmd.getPageSizeVal(); Account caller = CallContext.current().getCallingAccount(); - List permittedAccounts = new ArrayList(); + List permittedAccounts = new ArrayList<>(); // Verify parameters if (id != null) { @@ -5842,7 +5839,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q List ids = getIdsListFromCmd(cmd.getId(), cmd.getIds()); - Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), + Ternary domainIdRecursiveListProject = new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null); accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); diff --git a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java index 4e916e66ae7..25dfbfe6714 100644 --- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDao.java @@ -23,10 +23,7 @@ import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.SnapshotResponse; import com.cloud.api.query.vo.SnapshotJoinVO; -import com.cloud.utils.Pair; -import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import com.cloud.utils.db.SearchCriteria; public interface SnapshotJoinDao extends GenericDao { @@ -34,8 +31,7 @@ public interface SnapshotJoinDao extends GenericDao { SnapshotResponse setSnapshotResponse(SnapshotResponse snapshotResponse, SnapshotJoinVO snapshot); - Pair, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter); - List searchBySnapshotStorePair(String... pairs); + List findByDistinctIds(Long zoneId, Long... ids); } diff --git a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java index 04816ec87b9..2944e69c22a 100644 --- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java @@ -18,6 +18,8 @@ package com.cloud.api.query.dao; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,6 +36,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.query.QueryService; +import org.apache.commons.collections.CollectionUtils; + import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseHelper; import com.cloud.api.query.vo.SnapshotJoinVO; @@ -44,7 +48,6 @@ import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.user.Account; import com.cloud.user.AccountService; -import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -53,11 +56,11 @@ import com.cloud.vm.VMInstanceVO; public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation implements SnapshotJoinDao { @Inject - private AccountService accountService; + AccountService accountService; @Inject - private AnnotationDao annotationDao; + AnnotationDao annotationDao; @Inject - private ConfigurationDao configDao; + ConfigurationDao configDao; @Inject SnapshotDataFactory snapshotDataFactory; @@ -85,7 +88,7 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter) { - List objects = searchIncludingRemoved(sc, filter, null, false); - Integer count = getDistinctCount(sc); - return new Pair<>(objects, count); - } - @Override public List searchBySnapshotStorePair(String... pairs) { // set detail batch query size @@ -243,14 +239,33 @@ public class SnapshotJoinDaoImpl extends GenericDaoBaseWithTagInformation findById(Long zoneId, long id) { + SearchBuilder sb = createSearchBuilder(); + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("id", id); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + List snapshotJoinVOS = search(sc, null); + if (CollectionUtils.isEmpty(snapshotJoinVOS)) { + return null; + } + snapshotJoinVOS.sort(Comparator.comparing(SnapshotJoinVO::getSnapshotStorePair)); + return Collections.singletonList(snapshotJoinVOS.get(0)); + } + @Override public List findByDistinctIds(Long zoneId, Long... ids) { if (ids == null || ids.length == 0) { return new ArrayList<>(); } - + if (ids.length == 1) { + return findById(zoneId, ids[0]); + } Filter searchFilter = new Filter(SnapshotJoinVO.class, "snapshotStorePair", QueryService.SortKeyAscending.value(), null, null); - SearchCriteria sc = snapshotIdsSearch.create(); if (zoneId != null) { sc.setParameters("zoneId", zoneId); diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 8eb5c5203ec..a0c367ad72e 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -7225,10 +7225,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati sc.addAnd("id", SearchCriteria.Op.EQ, id); } - if (tags != null) { - sc.addAnd("tags", SearchCriteria.Op.EQ, tags); - } - if (isTagged != null) { if (isTagged) { sc.addAnd("tags", SearchCriteria.Op.NNULL); @@ -7237,6 +7233,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + if (tags != null) { + if (GuestType.Shared.name().equalsIgnoreCase(guestIpType)) { + SearchCriteria tagsSc = networkOfferingJoinDao.createSearchCriteria(); + tagsSc.addAnd("tags", SearchCriteria.Op.EQ, tags); + tagsSc.addOr("isDefault", SearchCriteria.Op.EQ, true); + sc.addAnd("tags", SearchCriteria.Op.SC, tagsSc); + } else { + sc.addAnd("tags", SearchCriteria.Op.EQ, tags); + } + } + if (zoneId != null) { SearchBuilder sb = networkOfferingJoinDao.createSearchBuilder(); sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.FIND_IN_SET); @@ -7297,7 +7304,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati boolean addOffering = true; List checkForProviders = new ArrayList(); - if (checkForTags && ! checkNetworkOfferingTags(pNtwkTags, allowNullTag, offering.getTags())) { + if (checkForTags && !checkNetworkOfferingTags(pNtwkTags, allowNullTag, offering.getTags())) { continue; } diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index 5f6cae31699..6cc18aaef54 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -934,8 +934,12 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi @Override public UserDataServiceProvider getUserDataUpdateProvider(Network network) { - String userDataProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData); + if (network == null) { + logger.warn("No network details, can't fetch user data provider"); + return null; + } + String userDataProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData); if (userDataProvider == null) { logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName()); return null; diff --git a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java index 6ef84a04b9f..e1f850978d5 100644 --- a/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java +++ b/server/src/main/java/com/cloud/network/element/ConfigDriveNetworkElement.java @@ -325,8 +325,12 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle try { final Network network = _networkMgr.getNetwork(nic.getNetworkId()); final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network); + if (userDataUpdateProvider == null) { + logger.warn("Failed to get user data provider"); + return false; + } final Provider provider = userDataUpdateProvider.getProvider(); - if (provider.equals(Provider.ConfigDrive)) { + if (Provider.ConfigDrive.equals(provider)) { try { return deleteConfigDriveIso(vm); } catch (ResourceUnavailableException e) { @@ -341,8 +345,13 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle @Override public boolean prepareMigration(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) { - if (_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) { - logger.trace(String.format("[prepareMigration] for vm: %s", vm)); + final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network); + if (userDataUpdateProvider == null) { + logger.warn("Failed to prepare for migration, can't get user data provider"); + return false; + } + if (Provider.ConfigDrive.equals(userDataUpdateProvider.getProvider())) { + logger.trace(String.format("[prepareMigration] for vm: %s", vm.getInstanceName())); try { if (isConfigDriveIsoOnHostCache(vm.getId())) { vm.setConfigDriveLocation(Location.HOST); @@ -392,7 +401,11 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle } private void recreateConfigDriveIso(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { - if (nic.isDefaultNic() && _networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) { + final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network); + if (userDataUpdateProvider == null) { + return; + } + if (nic.isDefaultNic() && Provider.ConfigDrive.equals(userDataUpdateProvider.getProvider())) { DiskTO diskToUse = null; for (DiskTO disk : vm.getDisks()) { if (disk.getType() == Volume.Type.ISO && disk.getPath() != null && disk.getPath().contains("configdrive")) { diff --git a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java index d4aeae23a2d..263ff523ab6 100644 --- a/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VirtualRouterElement.java @@ -769,7 +769,12 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ @Override public boolean saveHypervisorHostname(NicProfile nicProfile, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { - if (_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.VirtualRouter) && vm.getVirtualMachine().getType() == VirtualMachine.Type.User) { + final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network); + if (userDataUpdateProvider == null) { + logger.warn("Failed to update hypervisor host details, can't get user data provider"); + return false; + } + if (Provider.VirtualRouter.equals(userDataUpdateProvider.getProvider()) && vm.getVirtualMachine().getType() == VirtualMachine.Type.User) { VirtualMachine uvm = vm.getVirtualMachine(); UserVmVO destVm = _userVmDao.findById(uvm.getId()); VirtualMachineProfile profile = null; diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 03f24dde27d..74b7f6f358b 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -3622,8 +3622,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C templateVO = _templateStoreDao.findByStoreTemplate(store.getId(), templateId); if (templateVO != null) { try { - if (SystemVmTemplateRegistration.validateIfSeeded( - url, templateVO.getInstallPath(), nfsVersion)) { + if (systemVmTemplateRegistration.validateIfSeeded( + templateVO, url, templateVO.getInstallPath(), nfsVersion)) { continue; } } catch (Exception e) { diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index a371a064701..496643f0bdf 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -2482,10 +2482,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return existingVolumeOfVm; } - protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) { + protected StoragePool getSuitablePoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) { DataCenter zone = _dcDao.findById(vm.getDataCenterId()); Pair clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false); - long podId = vm.getPodIdToDeployIn(); + Long podId = vm.getPodIdToDeployIn(); if (clusterHostId.first() != null) { Cluster cluster = clusterDao.findById(clusterHostId.first()); podId = cluster.getPodId(); @@ -2497,12 +2497,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic offering.isUseLocalStorage(), offering.isRecreatable(), volumeToAttach.getTemplateId()); diskProfile.setHyperType(vm.getHypervisorType()); - StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(), + return _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(), clusterHostId.second(), vm, Collections.emptySet()); - if (pool == null) { - throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState())); - } - return pool; } protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) { @@ -2520,7 +2516,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } if (destPrimaryStorage == null) { - destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + destPrimaryStorage = getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + if (destPrimaryStorage == null) { + if (Volume.State.Allocated.equals(volumeToAttach.getState()) && State.Stopped.equals(vm.getState())) { + return newVolumeOnPrimaryStorage; + } + throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState())); + } } try { if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) { diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 1422f39f1fe..a3ae3fff01f 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -498,8 +498,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (apiNameList == null) { long startTime = System.nanoTime(); - apiNameList = new ArrayList(); - Set> cmdClasses = new LinkedHashSet>(); + apiNameList = new ArrayList<>(); + Set> cmdClasses = new LinkedHashSet<>(); for (PluggableService service : services) { logger.debug(String.format("getting api commands of service: %s", service.getClass().getName())); cmdClasses.addAll(service.getCommands()); @@ -513,7 +513,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } protected List createApiNameList(Set> cmdClasses) { - List apiNameList = new ArrayList(); + List apiNameList = new ArrayList<>(); for (Class cmdClass : cmdClasses) { APICommand apiCmdAnnotation = cmdClass.getAnnotation(APICommand.class); @@ -698,7 +698,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return; } - HashMap> domains = new HashMap>(); + HashMap> domains = new HashMap<>(); for (ControlledEntity entity : entities) { long domainId = entity.getDomainId(); @@ -713,7 +713,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M List toBeChecked = domains.get(entity.getDomainId()); // for templates, we don't have to do cross domains check if (toBeChecked == null) { - toBeChecked = new ArrayList(); + toBeChecked = new ArrayList<>(); domains.put(domainId, toBeChecked); } toBeChecked.add(entity); @@ -722,7 +722,11 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M for (SecurityChecker checker : _securityCheckers) { if (checker.checkAccess(caller, entity, accessType, apiName)) { if (logger.isDebugEnabled()) { - logger.debug("Access to " + entity + " granted to " + caller + " by " + checker.getName()); + User user = CallContext.current().getCallingUser(); + String userName = ""; + if (user != null) + userName = user.getUsername(); + logger.debug("Access to {} granted to {} by {} on behalf of user {}", entity, caller, checker.getName(), userName); } granted = true; break; @@ -1023,12 +1027,12 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M logger.debug("Deleting networks for account {}", account); List networks = _networkDao.listByOwner(accountId); if (networks != null) { - Collections.sort(networks, new Comparator() { + Collections.sort(networks, new Comparator<>() { @Override public int compare(NetworkVO network1, NetworkVO network2) { if (network1.getGuestType() != network2.getGuestType() && Network.GuestType.Isolated.equals(network2.getGuestType())) { return -1; - }; + } return 1; } }); @@ -1300,7 +1304,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M final String accountNameFinal = accountName; final Long domainIdFinal = domainId; final String accountUUIDFinal = accountUUID; - Pair pair = Transaction.execute(new TransactionCallback>() { + Pair pair = Transaction.execute(new TransactionCallback<>() { @Override public Pair doInTransaction(TransactionStatus status) { // create account @@ -1323,7 +1327,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M user.setRegistrationToken(registrationToken); } - return new Pair(user.getId(), account); + return new Pair<>(user.getId(), account); } }); @@ -1332,7 +1336,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // create correct account and group association based on accountType if (accountType != Account.Type.PROJECT) { - Map accountGroupMap = new HashMap(); + Map accountGroupMap = new HashMap<>(); accountGroupMap.put(account.getId(), (long) (accountType.ordinal() + 1)); _messageBus.publish(_name, MESSAGE_ADD_ACCOUNT_EVENT, PublishScope.LOCAL, accountGroupMap); } @@ -1476,7 +1480,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) { throw new CloudRuntimeException(String.format("The user %s already exists in domain %s", userName, domain)); } - UserVO user = null; + UserVO user; user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source); return user; } @@ -1731,7 +1735,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M Long callingUserId = CallContext.current().getCallingUserId(); Account callingAccount = CallContext.current().getCallingAccount(); ActionEventUtils.onActionEvent(callingUserId, callingAccount.getAccountId(), callingAccount.getDomainId(), - EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the User to " + access.toString(), + EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the User to " + access, user.getId(), ApiCommandResourceType.User.toString()); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit"); @@ -1747,7 +1751,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M Long callingUserId = CallContext.current().getCallingUserId(); Account callingAccount = CallContext.current().getCallingAccount(); ActionEventUtils.onActionEvent(callingUserId, callingAccount.getAccountId(), callingAccount.getDomainId(), - EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the Account to " + access.toString(), + EventTypes.API_KEY_ACCESS_UPDATE, "Api key access was changed for the Account to " + access, account.getId(), ApiCommandResourceType.Account.toString()); } catch (IllegalArgumentException ex) { throw new InvalidParameterValueException("ApiKeyAccess value can only be Enabled/Disabled/Inherit"); @@ -1837,7 +1841,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M checkAccess(caller, AccessType.OperateEntry, true, account); - boolean success = Transaction.execute(new TransactionCallback() { + boolean success = Transaction.execute(new TransactionCallback<>() { @Override public Boolean doInTransaction(TransactionStatus status) { boolean success = doSetUserStatus(userId, State.ENABLED); @@ -1892,7 +1896,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // make sure the account is enabled too // if the user is either locked already or disabled already, don't change state...only lock currently enabled // users - boolean success = true; + boolean success; if (user.getState().equals(State.LOCKED)) { // already locked...no-op return _userAccountDao.findById(userId); @@ -1995,7 +1999,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M public AccountVO enableAccount(String accountName, Long domainId, Long accountId) { // Check if account exists - Account account = null; + Account account; if (accountId != null) { account = _accountDao.findById(accountId); } else { @@ -2021,7 +2025,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return _accountDao.findById(account.getId()); } else { - throw new CloudRuntimeException(String.format("Unable to enable account %s in domain %s", account, accountName, _domainMgr.getDomain(domainId))); + throw new CloudRuntimeException(String.format("Unable to enable account %s[%s] in domain %s", accountName, account.getUuid(), _domainMgr.getDomain(domainId))); } } @@ -2030,7 +2034,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M public AccountVO lockAccount(String accountName, Long domainId, Long accountId) { Account caller = getCurrentCallingAccount(); - Account account = null; + Account account; if (accountId != null) { account = _accountDao.findById(accountId); } else { @@ -2051,7 +2055,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M CallContext.current().putContextParameter(Account.class, account.getUuid()); return _accountDao.findById(account.getId()); } else { - throw new CloudRuntimeException(String.format("Unable to lock account %s by accountId: %d OR by name: %s in domain %d", account, accountId, accountName, _domainMgr.getDomain(domainId))); + throw new CloudRuntimeException(String.format("Unable to lock account %s by accountId: %d OR by name: %s in domain %s", account, accountId, accountName, _domainMgr.getDomain(domainId))); } } @@ -2060,7 +2064,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M public AccountVO disableAccount(String accountName, Long domainId, Long accountId) throws ConcurrentOperationException, ResourceUnavailableException { Account caller = getCurrentCallingAccount(); - Account account = null; + Account account; if (accountId != null) { account = _accountDao.findById(accountId); } else { @@ -2097,8 +2101,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M String networkDomain = cmd.getNetworkDomain(); final Map details = cmd.getDetails(); - boolean success = false; - Account account = null; + boolean success; + Account account; if (accountId != null) { account = _accountDao.findById(accountId); } else { @@ -2159,7 +2163,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M if (roleNotFound) { throw new InvalidParameterValueException(String.format("Role with ID '%s' is not " + "found or not available for the account '%s' in the domain '%s'.", - roleId.toString(), account, _domainMgr.getDomain(domainId))); + roleId, account, _domainMgr.getDomain(domainId))); } Role role = roleService.findRole(roleId); @@ -2244,7 +2248,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return true; // no need to create a new user object for this user } - return Transaction.execute(new TransactionCallback() { + return Transaction.execute(new TransactionCallback<>() { @Override public Boolean doInTransaction(TransactionStatus status) { UserVO newUser = new UserVO(user); @@ -2560,7 +2564,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } // Create the account - return Transaction.execute(new TransactionCallback() { + return Transaction.execute(new TransactionCallback<>() { @Override public AccountVO doInTransaction(TransactionStatus status) { AccountVO account = _accountDao.persist(new AccountVO(accountName, domainId, networkDomain, accountType, roleId, uuid)); @@ -2650,12 +2654,12 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M long tolerance = Long.parseLong(singleSignOnTolerance); String signature = null; long timestamp = 0L; - String unsignedRequest = null; + String unsignedRequest; StringBuffer unsignedRequestBuffer = new StringBuffer(); // - build a request string with sorted params, make sure it's all lowercase // - sign the request, verify the signature is the same - List parameterNames = new ArrayList(); + List parameterNames = new ArrayList<>(); for (Object paramNameObj : requestParameters.keySet()) { parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later @@ -2780,7 +2784,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M UserAccount userAccount = _userAccountDao.getUserAccount(username, domainId); boolean authenticated = false; - HashSet actionsOnFailedAuthenticaion = new HashSet(); + HashSet actionsOnFailedAuthenticaion = new HashSet<>(); User.Source userSource = userAccount != null ? userAccount.getSource() : User.Source.UNKNOWN; for (UserAuthenticator authenticator : _userAuthenticators) { final String[] secretCodeArray = (String[])requestParameters.get(ApiConstants.SECRET_CODE); @@ -2886,7 +2890,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M preventRootDomainAdminAccessToRootAdminKeys(caller, account); checkAccess(caller, account); - Map keys = new HashMap(); + Map keys = new HashMap<>(); keys.put("apikey", user.getApiKey()); keys.put("secretkey", user.getSecretKey()); @@ -2898,7 +2902,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } } - return new Pair>(apiKeyAccess, keys); + return new Pair<>(apiKeyAccess, keys); } protected void preventRootDomainAdminAccessToRootAdminKeys(User caller, ControlledEntity account) { @@ -2999,8 +3003,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M try { UserVO updatedUser = _userDao.createForUpdate(); - String encodedKey = null; - Pair userAcct = null; + String encodedKey; + Pair userAcct; int retryLimit = 10; do { // FIXME: what algorithm should we use for API keys? @@ -3026,9 +3030,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M private String createUserSecretKey(long userId) { try { UserVO updatedUser = _userDao.createForUpdate(); - String encodedKey = null; + String encodedKey; int retryLimit = 10; - UserVO userBySecretKey = null; + UserVO userBySecretKey; do { KeyGenerator generator = KeyGenerator.getInstance("HmacSHA1"); SecretKey key = generator.generateKey(); @@ -3136,8 +3140,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M throw new InvalidParameterValueException("Account and projectId can't be specified together"); } - Account userAccount = null; - Domain domain = null; + Account userAccount; + Domain domain; if (domainId != null) { userAccount = _accountDao.findActiveAccount(accountName, domainId); domain = _domainDao.findById(domainId); @@ -3262,7 +3266,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @Override public List listAclGroupsByAccount(Long accountId) { if (_querySelectors == null || _querySelectors.size() == 0) { - return new ArrayList(); + return new ArrayList<>(); } QuerySelector qs = _querySelectors.get(0); @@ -3522,7 +3526,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } protected UserTwoFactorAuthenticationSetupResponse disableTwoFactorAuthentication(Long userId, Account caller, Account owner) { - UserVO userVO = null; + UserVO userVO; if (userId != null) { userVO = validateUser(userId); owner = _accountService.getActiveAccountById(userVO.getAccountId()); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index f497c881015..021c6ff6226 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -134,8 +134,8 @@ import org.apache.cloudstack.storage.template.VnfTemplateManager; import org.apache.cloudstack.userdata.UserDataManager; import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.ParserUtils; -import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.cloudstack.vm.schedule.VMScheduleManager; +import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.math.NumberUtils; @@ -2140,7 +2140,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Unable to Scale VM, since disk offering strictness flag is not same for new service offering and old service offering"); } - if (currentServiceOffering.getDiskOfferingStrictness() && currentServiceOffering.getDiskOfferingId() != newServiceOffering.getDiskOfferingId()) { + if (currentServiceOffering.getDiskOfferingStrictness() && !currentServiceOffering.getDiskOfferingId().equals(newServiceOffering.getDiskOfferingId())) { throw new InvalidParameterValueException("Unable to Scale VM, since disk offering id associated with the old service offering is not same for new service offering"); } @@ -4526,7 +4526,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } if (customParameters.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { - Long rootDiskSize = rootDiskSizeCustomParam * GiB_TO_BYTES; + Long rootDiskSize = NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), -1); + if (rootDiskSize <= 0) { + throw new InvalidParameterValueException("Root disk size should be a positive number."); + } + rootDiskSize = rootDiskSizeCustomParam * GiB_TO_BYTES; _volumeService.validateVolumeSizeInBytes(rootDiskSize); return rootDiskSize; } else { diff --git a/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.java b/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.java new file mode 100644 index 00000000000..c1159d3aa88 --- /dev/null +++ b/server/src/test/java/com/cloud/api/query/dao/SnapshotJoinDaoImplTest.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.api.query.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.api.query.vo.SnapshotJoinVO; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class SnapshotJoinDaoImplTest { + + @Spy + @InjectMocks + SnapshotJoinDaoImpl snapshotJoinDao = new SnapshotJoinDaoImpl(); + + @Mock + SearchCriteria mockSearchCriteria; + + @Before + public void setUp() { + SnapshotJoinVO mockSnap = mock(SnapshotJoinVO.class); + SearchBuilder mockSearchBuilder = mock(SearchBuilder.class); + when(mockSearchBuilder.entity()).thenReturn(mockSnap); + doReturn(mockSearchBuilder).when(snapshotJoinDao).createSearchBuilder(); + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + } + + @Test + public void testFindById_WithNullZoneId_EmptyResult() { + Long zoneId = null; + long id = 1L; + doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNull(result); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria, never()).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithValidZoneId_EmptyResult() { + Long zoneId = 1L; + long id = 1L; + doReturn(Collections.emptyList()).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNull(result); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithValidResults() { + Long zoneId = 1L; + long id = 1L; + SnapshotJoinVO snapshot1 = mock(SnapshotJoinVO.class); + when(snapshot1.getSnapshotStorePair()).thenReturn("Primary_1"); + SnapshotJoinVO snapshot2 = mock(SnapshotJoinVO.class); + when(snapshot2.getSnapshotStorePair()).thenReturn("Image_1"); + doReturn(Arrays.asList(snapshot1, snapshot2)).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(zoneId, id); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals("Image_1", result.get(0).getSnapshotStorePair()); + verify(mockSearchCriteria).setParameters("id", id); + verify(mockSearchCriteria).setParameters("zoneId", zoneId); + } + + @Test + public void testFindById_WithNullResults() { + long id = 1L; + doReturn(null).when(snapshotJoinDao).search(mockSearchCriteria, null); + List result = snapshotJoinDao.findById(null, id); + assertNull(result); + } +} diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 23b79e4ab03..a55f9b26a2c 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -2038,13 +2038,13 @@ public class VolumeApiServiceImplTest { when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet()))) .thenReturn(pool); - StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + StoragePool result = volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); Assert.assertNotNull(result); Assert.assertEquals(pool, result); } - @Test(expected = CloudRuntimeException.class) - public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoPoolFound_ThrowsException() { + @Test + public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoSuitablePoolFound_ReturnsNull() { VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); UserVmVO vm = Mockito.mock(UserVmVO.class); DataCenterVO zone = mockZone(); @@ -2059,11 +2059,11 @@ public class VolumeApiServiceImplTest { when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet()))) .thenReturn(null); - volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + Assert.assertNull(volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm)); } @Test - public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoCluster() { + public void testGetSuitablePoolForAllocatedOrUploadedVolumeForAttach_NoCluster() { VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); UserVmVO vm = Mockito.mock(UserVmVO.class); DataCenterVO zone = mockZone(); @@ -2077,7 +2077,7 @@ public class VolumeApiServiceImplTest { when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(null), eq(2L), eq(vm), eq(Collections.emptySet()))) .thenReturn(pool); - StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + StoragePool result = volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); Assert.assertNotNull(result); Assert.assertEquals(pool, result); } @@ -2123,7 +2123,7 @@ public class VolumeApiServiceImplTest { UserVmVO vm = Mockito.mock(UserVmVO.class); StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) - .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + .getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class); try { Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage( @@ -2134,7 +2134,7 @@ public class VolumeApiServiceImplTest { } VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null); Assert.assertSame(newVolumeOnPrimaryStorage, result); - verify(volumeApiServiceImpl).getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + verify(volumeApiServiceImpl).getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); } @Test(expected = InvalidParameterValueException.class) @@ -2145,7 +2145,7 @@ public class VolumeApiServiceImplTest { StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex); Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) - .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + .getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null); } @@ -2157,7 +2157,7 @@ public class VolumeApiServiceImplTest { StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) - .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + .getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); try { Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage)) .thenThrow(new NoTransitionException("Mocked exception")); @@ -2169,4 +2169,35 @@ public class VolumeApiServiceImplTest { ); Assert.assertTrue(exception.getMessage().contains("Failed to create volume on primary storage")); } + + @Test + public void testCreateVolumeOnSecondaryForAttachIfNeeded_NoSuitablePool_ThrowsException() { + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded); + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.doReturn(null).when(volumeApiServiceImpl) + .getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + CloudRuntimeException exception = Assert.assertThrows(CloudRuntimeException.class, () -> + volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null) + ); + Assert.assertTrue(exception.getMessage().startsWith("Failed to find a primary storage for volume")); + } + + @Test + public void testCreateVolumeOnSecondaryForAttachIfNeeded_NoSuitablePool_ReturnSameVolumeInfo() { + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated); + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getState()).thenReturn(State.Stopped); + Mockito.doReturn(null).when(volumeApiServiceImpl) + .getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); + VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null); + Assert.assertSame(volumeToAttach, result); + try { + Mockito.verify(volumeOrchestrationService, Mockito.never()).createVolumeOnPrimaryStorage(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any()); + } catch (NoTransitionException e) { + Assert.fail(); + } + } } diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/LocalNfsSecondaryStorageResource.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/LocalNfsSecondaryStorageResource.java index ab55f654515..8c7d082c33e 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/LocalNfsSecondaryStorageResource.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/LocalNfsSecondaryStorageResource.java @@ -40,11 +40,6 @@ public class LocalNfsSecondaryStorageResource extends NfsSecondaryStorageResourc this._inSystemVM = false; } - @Override - public void setParentPath(String path) { - this._parent = path; - } - @Override public Answer executeRequest(Command cmd) { return super.executeRequest(cmd); @@ -57,7 +52,7 @@ public class LocalNfsSecondaryStorageResource extends NfsSecondaryStorageResourc String dir = mountUri(uri, nfsVersion); return _parent + "/" + dir; } catch (Exception e) { - String msg = "GetRootDir for " + secUrl + " failed due to " + e.toString(); + String msg = "GetRootDir for " + secUrl + " failed due to " + e; logger.error(msg, e); throw new CloudRuntimeException(msg); } @@ -75,14 +70,14 @@ public class LocalNfsSecondaryStorageResource extends NfsSecondaryStorageResourc // Change permissions for the mountpoint - seems to bypass authentication Script script = new Script(true, "chmod", _timeout, logger); - script.add("777", localRootPath); + script.add("1777", localRootPath); String result = script.execute(); if (result != null) { String errMsg = "Unable to set permissions for " + localRootPath + " due to " + result; logger.error(errMsg); throw new CloudRuntimeException(errMsg); } - logger.debug("Successfully set 777 permission for " + localRootPath); + logger.debug("Successfully set 1777 permission for " + localRootPath); // XXX: Adding the check for creation of snapshots dir here. Might have // to move it somewhere more logical later. diff --git a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py index 0d1c31ac14b..1c4695df806 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsAddress.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsAddress.py @@ -19,15 +19,16 @@ import logging from netaddr import IPAddress, IPNetwork import subprocess import time + from . import CsHelper from .CsDatabag import CsDataBag from .CsApp import CsApache, CsDnsmasq, CsPasswdSvc from .CsRoute import CsRoute from .CsRule import CsRule +from .CsStaticRoutes import CsStaticRoutes VRRP_TYPES = ['guest'] - class CsAddress(CsDataBag): def compare(self): @@ -556,8 +557,10 @@ class CsIP: (self.dev, guestNetworkCidr, self.address['gateway'], self.dev)]) if self.is_private_gateway(): - self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % + self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % (self.address['network'], self.dev, self.dev)]) + self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % + (self.address['network'], self.dev)]) self.fw.append(["filter", "", "-A ACL_INBOUND_%s -j DROP" % self.dev]) self.fw.append(["mangle", "", "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" % @@ -565,6 +568,23 @@ class CsIP: self.fw.append(["mangle", "front", "-A PREROUTING -s %s -d %s -m state --state NEW -j MARK --set-xmark %s/0xffffffff" % (self.cl.get_vpccidr(), self.address['network'], hex(100 + int(self.dev[3:])))]) + + static_routes = CsStaticRoutes("staticroutes", self.config) + if static_routes: + for item in static_routes.get_bag(): + if item == "id": + continue + static_route = static_routes.get_bag()[item] + if static_route['ip_address'] == self.address['public_ip'] and not static_route['revoke']: + self.fw.append(["mangle", "", + "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" % + (self.dev, static_route['network'], static_route['ip_address'], self.dev)]) + self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % + (static_route['network'], self.dev, self.dev)]) + self.fw.append(["filter", "front", + "-A FORWARD -d %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % + (static_route['network'], self.dev)]) + if self.address["source_nat"]: self.fw.append(["nat", "front", "-A POSTROUTING -o %s -j SNAT --to-source %s" % diff --git a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py index 46259dcb356..2ce181c17a6 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py @@ -139,8 +139,7 @@ class CsDhcp(CsDataBag): # Listen Address if self.cl.is_redundant(): listen_address.append(gateway) - else: - listen_address.append(ip) + listen_address.append(ip) # Add localized "data-server" records in /etc/hosts for VPC routers if self.config.is_vpc() or self.config.is_router(): self.add_host(gateway, "%s data-server" % CsHelper.get_hostname()) diff --git a/test/integration/smoke/test_dynamicroles.py b/test/integration/smoke/test_dynamicroles.py index e404835fbb8..19248d28391 100644 --- a/test/integration/smoke/test_dynamicroles.py +++ b/test/integration/smoke/test_dynamicroles.py @@ -74,6 +74,7 @@ class TestData(object): "listApis": "allow", "listAccounts": "allow", "listClusters": "deny", + "*VmwareDc*": "allow", "*VM*": "allow", "*Host*": "deny" } diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 29c917b5174..0f5b26160a3 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -3263,6 +3263,7 @@ "message.license.agreements.not.accepted": "License agreements not accepted.", "message.linstor.resourcegroup.description": "Linstor resource group to use for primary storage.", "message.list.zone.vmware.datacenter.empty": "No VMware Datacenter exists in the selected Zone", +"message.list.zone.vmware.hosts.empty": "No VMware hosts were found in the selected Datacenter", "message.listnsp.not.return.providerid": "error: listNetworkServiceProviders API doesn't return VirtualRouter provider ID.", "message.load.host.failed": "Failed to load hosts.", "message.loadbalancer.stickypolicy.configuration": "Customize the load balancer stickiness policy:", diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 5de6d78d263..55aeb19317b 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'compute', @@ -100,6 +101,7 @@ export default { label: 'label.vm.add', docHelp: 'adminguide/virtual_machines.html#creating-vms', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/DeployVM.vue') }, { @@ -225,6 +227,10 @@ export default { args: ['virtualmachineid', 'backupofferingid'], show: (record) => { return !record.backupofferingid }, mapping: { + backupofferingid: { + api: 'listBackupOfferings', + params: (record) => { return { zoneid: record.zoneid } } + }, virtualmachineid: { value: (record, params) => { return record.id } } @@ -569,6 +575,7 @@ export default { docHelp: 'plugins/cloudstack-kubernetes-service.html#creating-a-new-kubernetes-cluster', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/compute/CreateKubernetesCluster.vue'))) }, { @@ -697,6 +704,7 @@ export default { icon: 'plus-outlined', label: 'label.new.autoscale.vmgroup', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/CreateAutoScaleVmGroup.vue') }, { @@ -787,6 +795,7 @@ export default { icon: 'plus-outlined', label: 'label.new.instance.group', listView: true, + show: isZoneCreated, args: ['name'] }, { diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js index 5b561ef877c..6001480513b 100644 --- a/ui/src/config/section/image.js +++ b/ui/src/config/section/image.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'image', @@ -110,16 +111,17 @@ export default { docHelp: 'adminguide/templates.html#uploading-templates-from-a-remote-http-server', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadTemplate.vue'))) }, { api: 'registerTemplate', icon: 'cloud-upload-outlined', label: 'label.upload.template.from.local', - show: () => { return 'getUploadParamsForTemplate' in store.getters.apis }, docHelp: 'adminguide/templates.html#uploading-templates-and-isos-from-a-local-computer', listView: true, popup: true, + show: () => { return isZoneCreated() && 'getUploadParamsForTemplate' in store.getters.apis }, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadTemplate.vue'))) }, { @@ -270,13 +272,14 @@ export default { docHelp: 'adminguide/templates.html#id10', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/RegisterOrUploadIso.vue'))) }, { api: 'registerIso', icon: 'cloud-upload-outlined', label: 'label.upload.iso.from.local', - show: () => { return 'getUploadParamsForIso' in store.getters.apis }, + show: () => { return isZoneCreated() && 'getUploadParamsForIso' in store.getters.apis }, docHelp: 'adminguide/templates.html#id10', listView: true, popup: true, @@ -389,6 +392,7 @@ export default { label: 'label.kubernetes.version.add', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/image/AddKubernetesSupportedVersion.vue'))) }, { diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index f3a706bbcc1..1e09c318b01 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -19,6 +19,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import tungsten from '@/assets/icons/tungsten.svg?inline' import { isAdmin } from '@/role' +import { isZoneCreated } from '@/utils/zone' export default { name: 'network', @@ -123,7 +124,7 @@ export default { listView: true, popup: true, show: () => { - if (!store.getters.zones || store.getters.zones.length === 0) { + if (!isZoneCreated()) { return false } const AdvancedZones = store.getters.zones.filter(zone => zone.networktype === 'Advanced') @@ -245,6 +246,7 @@ export default { icon: 'plus-outlined', label: 'label.add.vpc', docHelp: 'adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateVpc.vue'))) @@ -306,7 +308,7 @@ export default { component: shallowRef(defineAsyncComponent(() => import('@/views/network/IngressEgressRuleConfigure.vue'))) }], show: () => { - if (!store.getters.zones || store.getters.zones.length === 0) { + if (!isZoneCreated()) { return false } const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) @@ -394,6 +396,7 @@ export default { label: 'label.vnf.appliance.add', docHelp: 'adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances', listView: true, + show: isZoneCreated, component: () => import('@/views/compute/DeployVnfAppliance.vue') }, { @@ -941,6 +944,7 @@ export default { label: 'label.add.vpn.gateway', docHelp: 'adminguide/networking_and_traffic.html#creating-a-vpn-gateway-for-the-vpc', listView: true, + show: isZoneCreated, args: ['vpcid'] }, { @@ -1116,6 +1120,7 @@ export default { icon: 'plus-outlined', label: 'label.add.vpn.user', listView: true, + show: isZoneCreated, args: (record, store) => { if (store.userInfo.roletype === 'User') { return ['username', 'password'] @@ -1195,6 +1200,7 @@ export default { docHelp: 'adminguide/networking_and_traffic.html#creating-and-updating-a-vpn-customer-gateway', listView: true, popup: true, + show: isZoneCreated, component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateVpnCustomerGateway.vue'))) }, { @@ -1384,12 +1390,7 @@ export default { component: shallowRef(defineAsyncComponent(() => import('@/views/network/GuestVlanNetworksTab.vue'))), show: (record) => { return (record.allocationstate === 'Allocated') } }], - show: () => { - if (!store.getters.zones || store.getters.zones.length === 0) { - return false - } - return true - } + show: isZoneCreated } ] } diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 3ab890d530a..37f2a4b55e0 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -17,6 +17,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' +import { isZoneCreated } from '@/utils/zone' export default { name: 'storage', @@ -103,6 +104,7 @@ export default { icon: 'plus-outlined', docHelp: 'adminguide/storage.html#creating-a-new-volume', label: 'label.action.create.volume', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/CreateVolume.vue'))) @@ -112,7 +114,7 @@ export default { icon: 'cloud-upload-outlined', docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine', label: 'label.upload.volume.from.local', - show: () => { return 'getUploadParamsForVolume' in store.getters.apis }, + show: () => { return isZoneCreated() && 'getUploadParamsForVolume' in store.getters.apis }, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UploadLocalVolume.vue'))) @@ -122,6 +124,7 @@ export default { icon: 'link-outlined', docHelp: 'adminguide/storage.html#uploading-an-existing-volume-to-a-virtual-machine', label: 'label.upload.volume.from.url', + show: isZoneCreated, listView: true, popup: true, component: shallowRef(defineAsyncComponent(() => import('@/views/storage/UploadVolume.vue'))) diff --git a/ui/src/utils/zone.js b/ui/src/utils/zone.js new file mode 100644 index 00000000000..266bf9df445 --- /dev/null +++ b/ui/src/utils/zone.js @@ -0,0 +1,25 @@ +// 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 store from '@/store' + +export function isZoneCreated () { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + return true +} diff --git a/ui/src/views/network/CreateSharedNetworkForm.vue b/ui/src/views/network/CreateSharedNetworkForm.vue index b0c7203f833..c7ff7eab510 100644 --- a/ui/src/views/network/CreateSharedNetworkForm.vue +++ b/ui/src/views/network/CreateSharedNetworkForm.vue @@ -793,6 +793,10 @@ export default { if (this.scopeType === 'domain') { params.domainid = this.selectedDomain.id } + console.log(params?.tags?.length === 0) + if (!params?.tags || params.tags.length === 0) { + params.istagged = false + } this.handleNetworkOfferingChange(null) this.networkOfferings = [] api('listNetworkOfferings', params).then(json => { diff --git a/ui/src/views/tools/ManageInstances.vue b/ui/src/views/tools/ManageInstances.vue index 8ab81e35667..91a9e8f4193 100644 --- a/ui/src/views/tools/ManageInstances.vue +++ b/ui/src/views/tools/ManageInstances.vue @@ -1198,6 +1198,7 @@ export default { } else { params.existingvcenterid = this.selectedVmwareVcenter.existingvcenterid } + params.host = this.selectedVmwareVcenter.host } api(apiName, params).then(json => { diff --git a/ui/src/views/tools/SelectVmwareVcenter.vue b/ui/src/views/tools/SelectVmwareVcenter.vue index 78e38f107f3..1d25657b11b 100644 --- a/ui/src/views/tools/SelectVmwareVcenter.vue +++ b/ui/src/views/tools/SelectVmwareVcenter.vue @@ -89,6 +89,7 @@ @@ -98,6 +99,7 @@ @@ -107,6 +109,7 @@ @@ -116,14 +119,36 @@ +
+ + + + {{ 'ESXi: ' + opt.name }} + + + +
+ {{ $t('message.list.zone.vmware.hosts.empty') }} +
+