diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index c140a1e1c22a..1cbe28f4ddee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -105,6 +105,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") private String displayName; + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", + since="4.19.0.0") + protected String password; + //Owner information @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") private String accountName; @@ -464,6 +468,10 @@ public Long getZoneId() { return zoneId; } + public String getPassword() { + return password; + } + public List getNetworkIds() { if (MapUtils.isNotEmpty(vAppNetworks)) { if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java index e275a988cd15..1cf4c929b32d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -56,8 +57,7 @@ public class ResetVMPasswordCmd extends BaseAsyncCmd implements UserCmd { required=true, description="The ID of the virtual machine") private Long id; - // unexposed parameter needed for serializing/deserializing the command - @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, expose=false) + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The new password of the virtual machine. If null, a random password will be generated for the VM.", since="4.19.0") protected String password; @@ -118,7 +118,14 @@ public Long getApiResourceId() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException { - password = _mgr.generateRandomPassword(); + password = getPassword(); + UserVm vm = _responseGenerator.findUserVmById(getId()); + if (StringUtils.isBlank(password)) { + password = _mgr.generateRandomPassword(); + s_logger.debug(String.format("Resetting VM [%s] password to a randomly generated password.", vm.getUuid())); + } else { + s_logger.debug(String.format("Resetting VM [%s] password to password defined by user.", vm.getUuid())); + } CallContext.current().setEventDetails("Vm Id: " + getId()); UserVm result = _userVmService.resetVMPassword(this, password); if (result != null){ diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index 243579e2f710..d34f949372ae 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -163,6 +163,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "the domain name of the network owner") private String domain; + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "path of the Domain the network belongs to", since = "4.19.0.0") + private String domainPath; + @SerializedName("isdefault") @Param(description = "true if network is default, false otherwise") private Boolean isDefault; @@ -420,6 +424,10 @@ public void setDomainName(String domain) { this.domain = domain; } + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + public void setNetworkOfferingAvailability(String networkOfferingAvailability) { this.networkOfferingAvailability = networkOfferingAvailability; } diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java index 1bee45c477d2..70a3eb29bc7a 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java @@ -19,29 +19,6 @@ package org.apache.cloudstack.direct.download; -import com.cloud.utils.Pair; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; -import com.cloud.utils.storage.QCOW2Utils; -import org.apache.cloudstack.utils.security.SSLUtils; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.io.IOUtils; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.commons.collections.MapUtils; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -60,6 +37,32 @@ import java.util.List; import java.util.Map; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import org.apache.cloudstack.utils.security.SSLUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import com.cloud.utils.Pair; +import com.cloud.utils.UriUtils; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.utils.storage.QCOW2Utils; + public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl { protected CloseableHttpClient httpsClient; @@ -183,8 +186,7 @@ public Long getRemoteFileSize(String url, String format) { SSLContext context = getSSLContext(); urlConnection.setSSLSocketFactory(context.getSocketFactory()); urlConnection.connect(); - boolean isCompressed = !url.endsWith("qcow2"); - return QCOW2Utils.getVirtualSize(urlObj.openStream(), isCompressed); + return QCOW2Utils.getVirtualSize(urlObj.openStream(), UriUtils.isUrlForCompressedFile(url)); } catch (IOException e) { throw new CloudRuntimeException(String.format("Cannot obtain qcow2 virtual size due to: %s", e.getMessage()), e); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java index bdb2534b62d0..42c00231aac1 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java @@ -164,5 +164,5 @@ public interface VMInstanceDao extends GenericDao, StateDao< void updateSystemVmTemplateId(long templateId, Hypervisor.HypervisorType hypervisorType); - List listByHostOrLastHostOrHostPod(long hostId, long podId); + List listByHostOrLastHostOrHostPod(List hostIds, long podId); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index 0e3d4bdde8f1..916687baeb4d 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -989,19 +989,19 @@ public void updateSystemVmTemplateId(long templateId, Hypervisor.HypervisorType } @Override - public List listByHostOrLastHostOrHostPod(long hostId, long podId) { + public List listByHostOrLastHostOrHostPod(List hostIds, long podId) { SearchBuilder sb = createSearchBuilder(); - sb.or().op("hostId", sb.entity().getHostId(), Op.EQ); - sb.or("lastHostId", sb.entity().getLastHostId(), Op.EQ); - sb.and().op("hostIdNull", sb.entity().getHostId(), SearchCriteria.Op.NULL); + sb.and().op("hostId", sb.entity().getHostId(), Op.IN); + sb.or("lastHostId", sb.entity().getLastHostId(), Op.IN); + sb.or().op("hostIdNull", sb.entity().getHostId(), SearchCriteria.Op.NULL); sb.and("lastHostIdNull", sb.entity().getHostId(), SearchCriteria.Op.NULL); sb.and("podId", sb.entity().getPodIdToDeployIn(), Op.EQ); sb.cp(); sb.cp(); sb.done(); SearchCriteria sc = sb.create(); - sc.setParameters("hostId", String.valueOf(hostId)); - sc.setParameters("lastHostId", String.valueOf(hostId)); + sc.setParameters("hostId", hostIds.toArray()); + sc.setParameters("lastHostId", hostIds.toArray()); sc.setParameters("podId", String.valueOf(podId)); return listBy(sc); } diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 4d106f5c4f6a..0c65eb045336 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -364,7 +364,7 @@ protected Void deleteSnapshotCallback(AsyncCallbackDispatcher volumeTOs = vmSnapshotHelper.getVolumeTOList(vmId); - - for (VolumeObjectTO volumeTO : volumeTOs) { - Long poolId = volumeTO.getPoolId(); - Storage.StoragePoolType poolType = vmSnapshotHelper.getStoragePoolType(poolId); - if (poolType == Storage.StoragePoolType.NetworkFilesystem || poolType == Storage.StoragePoolType.Filesystem) { - return true; - } - } - return false; - } } diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java index be81b5fe04d4..96d57ee04258 100644 --- a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java +++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDao.java @@ -55,4 +55,6 @@ public interface ManagementServerHostDao extends GenericDao listOrphanMsids(); ManagementServerHostVO findOneInUpState(Filter filter); + + ManagementServerHostVO findOneByLongestRuntime(); } diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java index 74f8481a31d9..715dfe26bae6 100644 --- a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java +++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java @@ -25,6 +25,7 @@ import java.util.TimeZone; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import com.cloud.cluster.ClusterInvalidSessionException; @@ -204,6 +205,7 @@ protected ManagementServerHostDaoImpl() { StateSearch = createSearchBuilder(); StateSearch.and("state", StateSearch.entity().getState(), SearchCriteria.Op.IN); + StateSearch.and("runid", StateSearch.entity().getRunid(), SearchCriteria.Op.GT); StateSearch.done(); } @@ -272,4 +274,14 @@ public ManagementServerHostVO findOneInUpState(Filter filter) { return null; } + @Override + public ManagementServerHostVO findOneByLongestRuntime() { + SearchCriteria sc = StateSearch.create(); + sc.setParameters("state", ManagementServerHost.State.Up); + sc.setParameters("runid", 0); + Filter filter = new Filter(ManagementServerHostVO.class, "runid", true, 0L, 1L); + List msHosts = listBy(sc, filter); + return CollectionUtils.isNotEmpty(msHosts) ? msHosts.get(0) : null; + } + } diff --git a/pom.xml b/pom.xml index d595723a9ffd..2acabaa79e7c 100644 --- a/pom.xml +++ b/pom.xml @@ -170,7 +170,7 @@ 2.6.6 0.6.0 0.3.0 - 0.9.12 + 0.10.2 3.4.4_1 4.0.1 1.7.0 diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 7ed31e21bc38..5b723a51cc88 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -2572,6 +2572,10 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) Domain domain = ApiDBUtils.findDomainById(domainNetworkDetails.first()); if (domain != null) { response.setDomainId(domain.getUuid()); + + StringBuilder domainPath = new StringBuilder("ROOT"); + (domainPath.append(domain.getPath())).deleteCharAt(domainPath.length() - 1); + response.setDomainPath(domainPath.toString()); } } response.setSubdomainAccess(domainNetworkDetails.second()); 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 761192da3236..4e85323a716f 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -198,6 +198,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.ha.HighAvailabilityManager; +import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.RouterHealthCheckResult; @@ -1092,7 +1093,12 @@ private Pair, Integer> searchForUserVMsInternal(ListVMsCmd cm sb.and("stateNIN", sb.entity().getState(), SearchCriteria.Op.NIN); sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); - sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + if (clusterId != null) { + sb.and().op("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.or("clusterHostId", sb.entity().getHostId(), Op.IN); + sb.or("clusterLastHostId", sb.entity().getLastHostId(), Op.IN); + sb.cp(); + } sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); sb.and("hostIdEQ", sb.entity().getHostId(), SearchCriteria.Op.EQ); sb.and("templateId", sb.entity().getTemplateId(), SearchCriteria.Op.EQ); @@ -1301,6 +1307,10 @@ private Pair, Integer> searchForUserVMsInternal(ListVMsCmd cm if (clusterId != null) { sc.setParameters("clusterId", clusterId); + List hosts = _hostJoinDao.findByClusterId((Long)clusterId, Host.Type.Routing); + List hostIds = hosts.stream().map(HostJoinVO::getId).collect(Collectors.toList()); + sc.setParameters("clusterHostId", hostIds.toArray()); + sc.setParameters("clusterLastHostId", hostIds.toArray()); } if (hostId != null) { diff --git a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index 695521e5272a..4e838510010f 100644 --- a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java @@ -124,7 +124,7 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla protected String allocationAlgorithm = "random"; protected String globalDeploymentPlanner = "FirstFitPlanner"; - protected String[] implicitHostTags; + protected String[] implicitHostTags = new String[0]; @Override public List orderClusters(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) throws InsufficientServerCapacityException { @@ -214,11 +214,11 @@ private void reorderClustersBasedOnImplicitTags(List clusterList, int requ Long uniqueTags; for (Long clusterId : clusterList) { uniqueTags = (long) 0; - List hostList = capacityDao.listHostsWithEnoughCapacity(requiredCpu, requiredRam, clusterId, Host.Type.Routing.toString()); - if (!hostList.isEmpty() && implicitHostTags.length > 0) { - uniqueTags = new Long(hostTagsDao.getDistinctImplicitHostTags(hostList, implicitHostTags).size()); - uniqueTags = uniqueTags + getHostsByCapability(hostList, Host.HOST_UEFI_ENABLE); - } + List hostList = capacityDao.listHostsWithEnoughCapacity(requiredCpu, requiredRam, clusterId, Host.Type.Routing.toString()); + if (!hostList.isEmpty() && implicitHostTags.length > 0) { + uniqueTags = new Long(hostTagsDao.getDistinctImplicitHostTags(hostList, implicitHostTags).size()); + uniqueTags = uniqueTags + getHostsByCapability(hostList, Host.HOST_UEFI_ENABLE); + } UniqueTagsInClusterMap.put(clusterId, uniqueTags); } Collections.sort(clusterList, new Comparator() { diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java index de8a3ff3c83c..2f69ac6e9ba7 100644 --- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java @@ -1964,7 +1964,7 @@ private void checkAutoScaleVmGroupName(String groupName) { private boolean startNewVM(long vmId) { try { CallContext.current().setEventDetails("Vm Id: " + vmId); - userVmMgr.startVirtualMachine(vmId, null, null, null); + userVmMgr.startVirtualMachine(vmId, null, new HashMap<>(), null); } catch (final ResourceUnavailableException ex) { s_logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 903b851f9184..959a0dc3bb2c 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -31,6 +31,9 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.cluster.ManagementServerHostVO; +import com.cloud.cluster.dao.ManagementServerHostDao; +import com.cloud.utils.db.GlobalLock; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -44,6 +47,8 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.user.ResourceReservation; +import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -162,6 +167,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim private VpcDao _vpcDao; @Inject private VlanDao _vlanDao; + @Inject + private ManagementServerHostDao managementServerHostDao; protected GenericSearchBuilder templateSizeSearch; protected GenericSearchBuilder snapshotSizeSearch; @@ -1171,6 +1178,26 @@ public ResourceCountCheckTask() { @Override protected void runInContext() { + GlobalLock lock = GlobalLock.getInternLock("ResourceCheckTask"); + try { + if (lock.lock(30)) { + try { + ManagementServerHostVO msHost = managementServerHostDao.findOneByLongestRuntime(); + if (msHost == null || (msHost.getMsid() != ManagementServerNode.getManagementServerId())) { + s_logger.trace("Skipping the resource counters recalculation task on this management server"); + return; + } + runResourceCheckTaskInternal(); + } finally { + lock.unlock(); + } + } + } finally { + lock.releaseRef(); + } + } + + private void runResourceCheckTaskInternal() { s_logger.info("Started resource counters recalculation periodic task."); List domains; List accounts; @@ -1192,9 +1219,12 @@ protected void runInContext() { } for (ResourceType type : ResourceType.values()) { - recalculateDomainResourceCountInContext(Domain.ROOT_DOMAIN, type); - for (Domain domain : domains) { - recalculateDomainResourceCount(domain.getId(), type); + if (CollectionUtils.isEmpty(domains)) { + recalculateDomainResourceCountInContext(Domain.ROOT_DOMAIN, type); + } else { + for (Domain domain : domains) { + recalculateDomainResourceCount(domain.getId(), type); + } } // run through the accounts in the root domain @@ -1202,6 +1232,7 @@ protected void runInContext() { recalculateAccountResourceCountInContext(account.getId(), type); } } + s_logger.info("Finished resource counters recalculation periodic task."); } private void recalculateDomainResourceCountInContext(long domainId, ResourceType type) { diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 4bdb8f2861e9..56b028720d2c 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -1289,26 +1289,27 @@ public void cleanupStorage(boolean recurring) { try { List unusedTemplatesInPool = _tmpltMgr.getUnusedTemplatesInPool(pool); - s_logger.debug("Storage pool garbage collector found " + unusedTemplatesInPool.size() + " templates to clean up in storage pool: " + pool.getName()); + s_logger.debug(String.format("Storage pool garbage collector found [%s] templates to be cleaned up in storage pool [%s].", unusedTemplatesInPool.size(), pool.getName())); for (VMTemplateStoragePoolVO templatePoolVO : unusedTemplatesInPool) { if (templatePoolVO.getDownloadState() != VMTemplateStorageResourceAssoc.Status.DOWNLOADED) { - s_logger.debug("Storage pool garbage collector is skipping template with ID: " + templatePoolVO.getTemplateId() + " on pool " + templatePoolVO.getPoolId() - + " because it is not completely downloaded."); + s_logger.debug(String.format("Storage pool garbage collector is skipping template [%s] clean up on pool [%s] " + + "because it is not completely downloaded.", templatePoolVO.getTemplateId(), templatePoolVO.getPoolId())); continue; } if (!templatePoolVO.getMarkedForGC()) { templatePoolVO.setMarkedForGC(true); _vmTemplatePoolDao.update(templatePoolVO.getId(), templatePoolVO); - s_logger.debug("Storage pool garbage collector has marked template with ID: " + templatePoolVO.getTemplateId() + " on pool " + templatePoolVO.getPoolId() - + " for garbage collection."); + s_logger.debug(String.format("Storage pool garbage collector has marked template [%s] on pool [%s] " + + "for garbage collection.", templatePoolVO.getTemplateId(), templatePoolVO.getPoolId())); continue; } _tmpltMgr.evictTemplateFromStoragePool(templatePoolVO); } } catch (Exception e) { - s_logger.warn("Problem cleaning up primary storage pool " + pool, e); + s_logger.error(String.format("Failed to clean up primary storage pool [%s] due to: [%s].", pool, e.getMessage())); + s_logger.debug(String.format("Failed to clean up primary storage pool [%s].", pool), e); } } } @@ -1316,10 +1317,39 @@ public void cleanupStorage(boolean recurring) { //destroy snapshots in destroying state in snapshot_store_ref List ssSnapshots = _snapshotStoreDao.listByState(ObjectInDataStoreStateMachine.State.Destroying); for (SnapshotDataStoreVO ssSnapshotVO : ssSnapshots) { + String snapshotUuid = null; + SnapshotVO snapshot = null; + + if (s_logger.isDebugEnabled()) { + snapshot = _snapshotDao.findById(ssSnapshotVO.getSnapshotId()); + if (snapshot == null) { + s_logger.warn(String.format("Did not find snapshot [%s] in destroying state; therefore, it cannot be destroyed.", ssSnapshotVO.getSnapshotId())); + continue; + } + + snapshotUuid = snapshot.getUuid(); + } + try { - _snapshotService.deleteSnapshot(snapshotFactory.getSnapshot(ssSnapshotVO.getSnapshotId(), DataStoreRole.Image)); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Verifying if snapshot [%s] is in destroying state in any image data store.", snapshotUuid)); + } + + SnapshotInfo snapshotInfo = snapshotFactory.getSnapshot(ssSnapshotVO.getSnapshotId(), DataStoreRole.Image); + + if (snapshotInfo != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Snapshot [%s] in destroying state found in image data store [%s]; therefore, it will be destroyed.", snapshotUuid, snapshotInfo.getDataStore().getUuid())); + } + _snapshotService.deleteSnapshot(snapshotInfo); + } else if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Did not find snapshot [%s] in destroying state in any image data store.", snapshotUuid)); + } } catch (Exception e) { - s_logger.debug("Failed to delete snapshot: " + ssSnapshotVO.getId() + " from storage"); + s_logger.error(String.format("Failed to delete snapshot [%s] from storage due to: [%s].", ssSnapshotVO.getSnapshotId(), e.getMessage())); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Failed to delete snapshot [%s] from storage.", snapshotUuid), e); + } } } cleanupSecondaryStorage(recurring); @@ -1343,7 +1373,8 @@ public void cleanupStorage(boolean recurring) { // system, but not necessary. handleManagedStorage(vol); } catch (Exception e) { - s_logger.warn("Unable to destroy host-side clustered file system " + vol.getUuid(), e); + s_logger.error(String.format("Unable to destroy host-side clustered file system [%s] due to: [%s].", vol.getUuid(), e.getMessage())); + s_logger.debug(String.format("Unable to destroy host-side clustered file system [%s].", vol.getUuid()), e); } try { @@ -1352,10 +1383,11 @@ public void cleanupStorage(boolean recurring) { volService.ensureVolumeIsExpungeReady(vol.getId()); volService.expungeVolumeAsync(volumeInfo); } else { - s_logger.debug("Volume " + vol.getUuid() + " is already destroyed"); + s_logger.debug(String.format("Volume [%s] is already destroyed.", vol.getUuid())); } } catch (Exception e) { - s_logger.warn("Unable to destroy volume " + vol.getUuid(), e); + s_logger.error(String.format("Unable to destroy volume [%s] due to: [%s].", vol.getUuid(), e.getMessage())); + s_logger.debug(String.format("Unable to destroy volume [%s].", vol.getUuid()), e); } } @@ -1369,7 +1401,8 @@ public void cleanupStorage(boolean recurring) { } _snapshotDao.expunge(snapshotVO.getId()); } catch (Exception e) { - s_logger.warn("Unable to destroy snapshot " + snapshotVO.getUuid(), e); + s_logger.error(String.format("Unable to destroy snapshot [%s] due to: [%s].", snapshotVO.getUuid(), e.getMessage())); + s_logger.debug(String.format("Unable to destroy snapshot [%s].", snapshotVO.getUuid()), e); } } @@ -1378,14 +1411,14 @@ public void cleanupStorage(boolean recurring) { for (VolumeDataStoreVO volumeDataStore : volumeDataStores) { VolumeVO volume = volumeDao.findById(volumeDataStore.getVolumeId()); if (volume == null) { - s_logger.warn("Uploaded volume with id " + volumeDataStore.getVolumeId() + " not found, so cannot be destroyed"); + s_logger.warn(String.format("Uploaded volume [%s] not found, so cannot be destroyed.", volumeDataStore.getVolumeId())); continue; } try { DataStore dataStore = _dataStoreMgr.getDataStore(volumeDataStore.getDataStoreId(), DataStoreRole.Image); EndPoint ep = _epSelector.select(dataStore, volumeDataStore.getExtractUrl()); if (ep == null) { - s_logger.warn("There is no secondary storage VM for image store " + dataStore.getName() + ", cannot destroy uploaded volume " + volume.getUuid()); + s_logger.warn(String.format("There is no secondary storage VM for image store [%s], cannot destroy uploaded volume [%s].", dataStore.getName(), volume.getUuid())); continue; } Host host = _hostDao.findById(ep.getId()); @@ -1397,17 +1430,18 @@ public void cleanupStorage(boolean recurring) { // expunge volume from secondary if volume is on image store VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image); if (volOnSecondary != null) { - s_logger.info("Expunging volume " + volume.getUuid() + " uploaded using HTTP POST from secondary data store"); + s_logger.info(String.format("Expunging volume [%s] uploaded using HTTP POST from secondary data store.", volume.getUuid())); AsyncCallFuture future = volService.expungeVolumeAsync(volOnSecondary); VolumeApiResult result = future.get(); if (!result.isSuccess()) { - s_logger.warn("Failed to expunge volume " + volume.getUuid() + " from the image store " + dataStore.getName() + " due to: " + result.getResult()); + s_logger.warn(String.format("Failed to expunge volume [%s] from the image store [%s] due to: [%s].", volume.getUuid(), dataStore.getName(), result.getResult())); } } } } } catch (Throwable th) { - s_logger.warn("Unable to destroy uploaded volume " + volume.getUuid() + ". Error details: " + th.getMessage()); + s_logger.error(String.format("Unable to destroy uploaded volume [%s] due to: [%s].", volume.getUuid(), th.getMessage())); + s_logger.debug(String.format("Unable to destroy uploaded volume [%s].", volume.getUuid()), th); } } @@ -1416,14 +1450,14 @@ public void cleanupStorage(boolean recurring) { for (TemplateDataStoreVO templateDataStore : templateDataStores) { VMTemplateVO template = _templateDao.findById(templateDataStore.getTemplateId()); if (template == null) { - s_logger.warn("Uploaded template with id " + templateDataStore.getTemplateId() + " not found, so cannot be destroyed"); + s_logger.warn(String.format("Uploaded template [%s] not found, so cannot be destroyed.", templateDataStore.getTemplateId())); continue; } try { DataStore dataStore = _dataStoreMgr.getDataStore(templateDataStore.getDataStoreId(), DataStoreRole.Image); EndPoint ep = _epSelector.select(dataStore, templateDataStore.getExtractUrl()); if (ep == null) { - s_logger.warn("There is no secondary storage VM for image store " + dataStore.getName() + ", cannot destroy uploaded template " + template.getUuid()); + s_logger.warn(String.format("Cannot destroy uploaded template [%s] as there is no secondary storage VM for image store [%s].", template.getUuid(), dataStore.getName())); continue; } Host host = _hostDao.findById(ep.getId()); @@ -1432,7 +1466,7 @@ public void cleanupStorage(boolean recurring) { AsyncCallFuture future = _imageSrv.deleteTemplateAsync(tmplFactory.getTemplate(template.getId(), dataStore)); TemplateApiResult result = future.get(); if (!result.isSuccess()) { - s_logger.warn("Failed to delete template " + template.getUuid() + " from the image store " + dataStore.getName() + " due to: " + result.getResult()); + s_logger.warn(String.format("Failed to delete template [%s] from image store [%s] due to: [%s]", template.getUuid(), dataStore.getName(), result.getResult())); continue; } // remove from template_zone_ref @@ -1456,7 +1490,8 @@ public void cleanupStorage(boolean recurring) { } } } catch (Throwable th) { - s_logger.warn("Unable to destroy uploaded template " + template.getUuid() + ". Error details: " + th.getMessage()); + s_logger.error(String.format("Unable to destroy uploaded template [%s] due to: [%s].", template.getUuid(), th.getMessage())); + s_logger.debug(String.format("Unable to destroy uploaded template [%s].", template.getUuid()), th); } } cleanupInactiveTemplates(); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index eb6adfb5fb5d..a3b2fbbda8ae 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -137,6 +137,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.log4j.Logger; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.w3c.dom.Document; @@ -1179,9 +1180,8 @@ private UserVm rebootVirtualMachine(long userId, long vmId, boolean enterSetup, private UserVm forceRebootVirtualMachine(long vmId, long hostId, boolean enterSetup) { try { if (stopVirtualMachine(vmId, false) != null) { - Map params = null; + Map params = new HashMap<>(); if (enterSetup) { - params = new HashMap(); params.put(VirtualMachineProfile.Param.BootIntoSetup, Boolean.TRUE); } return startVirtualMachine(vmId, null, null, hostId, params, null, false).first(); @@ -4883,6 +4883,11 @@ public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableExc if (cmd.getBootIntoSetup() != null) { additionalParams.put(VirtualMachineProfile.Param.BootIntoSetup, cmd.getBootIntoSetup()); } + + if (StringUtils.isNotBlank(cmd.getPassword())) { + additionalParams.put(VirtualMachineProfile.Param.VmPassword, cmd.getPassword()); + } + return startVirtualMachine(vmId, podId, clusterId, hostId, diskOfferingMap, additionalParams, cmd.getDeploymentPlanner()); } @@ -5273,21 +5278,21 @@ public void finalizeStop(VirtualMachineProfile profile, Answer answer) { } @Override - public Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams, String deploymentPlannerToUse) - throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { + public Pair> startVirtualMachine(long vmId, Long hostId, @NotNull Map additionalParams, + String deploymentPlannerToUse) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { return startVirtualMachine(vmId, null, null, hostId, additionalParams, deploymentPlannerToUse); } @Override public Pair> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, - Map additionalParams, String deploymentPlannerToUse) + @NotNull Map additionalParams, String deploymentPlannerToUse) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true); } @Override public Pair> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, - Map additionalParams, String deploymentPlannerToUse, boolean isExplicitHost) + @NotNull Map additionalParams, String deploymentPlannerToUse, boolean isExplicitHost) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { // Input validation final Account callerAccount = CallContext.current().getCallingAccount(); @@ -5386,15 +5391,7 @@ public Pair> startVirtualMach // Check that the password was passed in and is valid template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - String password = "saved_password"; - if (template.isEnablePassword()) { - if (vm.getDetail("password") != null) { - password = DBEncryptionUtil.decrypt(vm.getDetail("password")); - } else { - password = _mgr.generateRandomPassword(); - vm.setPassword(password); - } - } + String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); if (!validPassword(password)) { throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); @@ -5407,7 +5404,7 @@ public Pair> startVirtualMach params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); } - if(null != additionalParams && additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { + if(additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { if (! HypervisorType.VMware.equals(vm.getHypervisorType())) { throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); } @@ -5450,6 +5447,39 @@ public Pair> startVirtualMach return vmParamPair; } + /** + * If the template is password enabled and the VM already has a password, returns it. + * If the template is password enabled and the VM does not have a password, sets the password to the password defined by the user and returns it. If no password is informed, + * sets it to a random password and returns it. + * If the template is not password enabled, returns saved_password. + * @param newPassword The new password informed by the user in order to set the password of the VM. + * @param vm The VM to retrieve the password from. + * @param template The template to be checked if the password is enabled or not. + * @return The password of the VM or saved_password. + */ + protected String getCurrentVmPasswordOrDefineNewPassword(String newPassword, UserVmVO vm, VMTemplateVO template) { + String password = "saved_password"; + + if (template.isEnablePassword()) { + if (vm.getDetail("password") != null) { + s_logger.debug(String.format("Decrypting VM [%s] current password.", vm)); + password = DBEncryptionUtil.decrypt(vm.getDetail("password")); + } else if (StringUtils.isNotBlank(newPassword)) { + s_logger.debug(String.format("A password for VM [%s] was informed. Setting VM password to value defined by user.", vm)); + password = newPassword; + vm.setPassword(password); + } else { + s_logger.debug(String.format("Setting VM [%s] password to a randomly generated password.", vm)); + password = _mgr.generateRandomPassword(); + vm.setPassword(password); + } + } else if (StringUtils.isNotBlank(newPassword)) { + s_logger.debug(String.format("A password was informed; however, the template [%s] is not password enabled. Ignoring the parameter.", template)); + } + + return password; + } + private Map createParameterInParameterMap(Map params, Map parameterMap, VirtualMachineProfile.Param parameter, Object parameterValue) { if (s_logger.isTraceEnabled()) { diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 12665a7db7b4..752ad5a9fba9 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -357,8 +357,11 @@ private List getAdditionalNameFilters(Cluster cluster) { return additionalNameFilter; } - private List getHostManagedVms(Host host) { - List instances = vmDao.listByHostOrLastHostOrHostPod(host.getId(), host.getPodId()); + private List getHostsManagedVms(List hosts) { + if (CollectionUtils.isEmpty(hosts)) { + return new ArrayList<>(); + } + List instances = vmDao.listByHostOrLastHostOrHostPod(hosts.stream().map(HostVO::getId).collect(Collectors.toList()), hosts.get(0).getPodId()); List managedVms = instances.stream().map(VMInstanceVO::getInstanceName).collect(Collectors.toList()); return managedVms; } @@ -1026,6 +1029,24 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI return userVm; } + private HashMap getUnmanagedInstancesForHost(HostVO host, String instanceName, List managedVms) { + HashMap unmanagedInstances = new HashMap<>(); + if (host.isInMaintenanceStates()) { + return unmanagedInstances; + } + + GetUnmanagedInstancesCommand command = new GetUnmanagedInstancesCommand(); + command.setInstanceName(instanceName); + command.setManagedInstancesNames(managedVms); + Answer answer = agentManager.easySend(host.getId(), command); + if (!(answer instanceof GetUnmanagedInstancesAnswer)) { + return unmanagedInstances; + } + GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = (GetUnmanagedInstancesAnswer) answer; + unmanagedInstances = unmanagedInstancesAnswer.getUnmanagedInstances(); + return unmanagedInstances; + } + private Cluster basicAccessChecks(Long clusterId) { final Account caller = CallContext.current().getCallingAccount(); if (caller.getType() != Account.Type.ADMIN) { @@ -1055,24 +1076,11 @@ public ListResponse listUnmanagedInstances(ListUnmana } List hosts = resourceManager.listHostsInClusterByStatus(clusterId, Status.Up); List additionalNameFilters = getAdditionalNameFilters(cluster); + List managedVms = new ArrayList<>(additionalNameFilters); + managedVms.addAll(getHostsManagedVms(hosts)); List responses = new ArrayList<>(); for (HostVO host : hosts) { - if (host.isInMaintenanceStates()) { - continue; - } - List managedVms = new ArrayList<>(); - managedVms.addAll(additionalNameFilters); - managedVms.addAll(getHostManagedVms(host)); - - GetUnmanagedInstancesCommand command = new GetUnmanagedInstancesCommand(); - command.setInstanceName(cmd.getName()); - command.setManagedInstancesNames(managedVms); - Answer answer = agentManager.easySend(host.getId(), command); - if (!(answer instanceof GetUnmanagedInstancesAnswer)) { - continue; - } - GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = (GetUnmanagedInstancesAnswer) answer; - HashMap unmanagedInstances = new HashMap<>(unmanagedInstancesAnswer.getUnmanagedInstances()); + HashMap unmanagedInstances = getUnmanagedInstancesForHost(host, cmd.getName(), managedVms); Set keys = unmanagedInstances.keySet(); for (String key : keys) { UnmanagedInstanceTO instance = unmanagedInstances.get(key); @@ -1099,7 +1107,7 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { throw new InvalidParameterValueException("Instance name cannot be empty"); } if (cmd.getDomainId() != null && StringUtils.isEmpty(cmd.getAccountName())) { - throw new InvalidParameterValueException("domainid parameter must be specified with account parameter"); + throw new InvalidParameterValueException(String.format("%s parameter must be specified with %s parameter", ApiConstants.DOMAIN_ID, ApiConstants.ACCOUNT)); } final Account owner = accountService.getActiveAccountById(cmd.getEntityOwnerId()); long userId = CallContext.current().getCallingUserId(); @@ -1107,7 +1115,7 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { if (CollectionUtils.isNotEmpty(userVOs)) { userId = userVOs.get(0).getId(); } - VMTemplateVO template = null; + VMTemplateVO template; final Long templateId = cmd.getTemplateId(); if (templateId == null) { template = templateDao.findByName(VM_IMPORT_DEFAULT_TEMPLATE_NAME); @@ -1170,59 +1178,49 @@ public UserVmResponse importUnmanagedInstance(ImportUnmanagedInstanceCmd cmd) { List hosts = resourceManager.listHostsInClusterByStatus(clusterId, Status.Up); UserVm userVm = null; List additionalNameFilters = getAdditionalNameFilters(cluster); + List managedVms = new ArrayList<>(additionalNameFilters); + managedVms.addAll(getHostsManagedVms(hosts)); for (HostVO host : hosts) { - if (host.isInMaintenanceStates()) { - continue; - } - List managedVms = new ArrayList<>(); - managedVms.addAll(additionalNameFilters); - managedVms.addAll(getHostManagedVms(host)); - GetUnmanagedInstancesCommand command = new GetUnmanagedInstancesCommand(instanceName); - command.setManagedInstancesNames(managedVms); - Answer answer = agentManager.easySend(host.getId(), command); - if (!(answer instanceof GetUnmanagedInstancesAnswer)) { - continue; - } - GetUnmanagedInstancesAnswer unmanagedInstancesAnswer = (GetUnmanagedInstancesAnswer) answer; - HashMap unmanagedInstances = unmanagedInstancesAnswer.getUnmanagedInstances(); + HashMap unmanagedInstances = getUnmanagedInstancesForHost(host, cmd.getName(), managedVms); if (MapUtils.isEmpty(unmanagedInstances)) { continue; } Set names = unmanagedInstances.keySet(); for (String name : names) { - if (instanceName.equals(name)) { - UnmanagedInstanceTO unmanagedInstance = unmanagedInstances.get(name); - if (unmanagedInstance == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve details for unmanaged VM: %s", name)); + if (!instanceName.equals(name)) { + continue; + } + UnmanagedInstanceTO unmanagedInstance = unmanagedInstances.get(name); + if (unmanagedInstance == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve details for unmanaged VM: %s", name)); + } + if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME)) { + String osName = unmanagedInstance.getOperatingSystem(); + GuestOS guestOS = null; + if (StringUtils.isNotEmpty(osName)) { + guestOS = guestOSDao.findOneByDisplayName(osName); } - if (template.getName().equals(VM_IMPORT_DEFAULT_TEMPLATE_NAME)) { - String osName = unmanagedInstance.getOperatingSystem(); - GuestOS guestOS = null; - if (StringUtils.isNotEmpty(osName)) { - guestOS = guestOSDao.findOneByDisplayName(osName); - } - GuestOSHypervisor guestOSHypervisor = null; + GuestOSHypervisor guestOSHypervisor = null; + if (guestOS != null) { + guestOSHypervisor = guestOSHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), host.getHypervisorType().toString(), host.getHypervisorVersion()); + } + if (guestOSHypervisor == null && StringUtils.isNotEmpty(unmanagedInstance.getOperatingSystemId())) { + guestOSHypervisor = guestOSHypervisorDao.findByOsNameAndHypervisor(unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion()); + } + if (guestOSHypervisor == null) { if (guestOS != null) { - guestOSHypervisor = guestOSHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), host.getHypervisorType().toString(), host.getHypervisorVersion()); - } - if (guestOSHypervisor == null && StringUtils.isNotEmpty(unmanagedInstance.getOperatingSystemId())) { - guestOSHypervisor = guestOSHypervisorDao.findByOsNameAndHypervisor(unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion()); - } - if (guestOSHypervisor == null) { - if (guestOS != null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to find hypervisor guest OS ID: %s details for unmanaged VM: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", guestOS.getUuid(), name, host.getHypervisorType().toString(), host.getHypervisorVersion())); - } - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve guest OS details for unmanaged VM: %s with OS name: %s, OS ID: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", name, osName, unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion())); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to find hypervisor guest OS ID: %s details for unmanaged VM: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", guestOS.getUuid(), name, host.getHypervisorType().toString(), host.getHypervisorVersion())); } - template.setGuestOSId(guestOSHypervisor.getGuestOsId()); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Unable to retrieve guest OS details for unmanaged VM: %s with OS name: %s, OS ID: %s for hypervisor: %s version: %s. templateid parameter can be used to assign template for VM", name, osName, unmanagedInstance.getOperatingSystemId(), host.getHypervisorType().toString(), host.getHypervisorVersion())); } - userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, - template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, - serviceOffering, dataDiskOfferingMap, - nicNetworkMap, nicIpAddressMap, - details, cmd.getMigrateAllowed(), forced); - break; + template.setGuestOSId(guestOSHypervisor.getGuestOsId()); } + userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, + template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, + serviceOffering, dataDiskOfferingMap, + nicNetworkMap, nicIpAddressMap, + details, cmd.getMigrateAllowed(), forced); + break; } if (userVm != null) { break; diff --git a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java index 0c65f1a46944..aaf0f254d418 100644 --- a/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/as/AutoScaleManagerImplTest.java @@ -99,7 +99,6 @@ import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VmStats; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; @@ -1495,8 +1494,6 @@ public void testDoScaleUp() throws ResourceUnavailableException, InsufficientCap when(autoScaleVmGroupDao.updateState(vmGroupId, AutoScaleVmGroup.State.ENABLED, AutoScaleVmGroup.State.SCALING)).thenReturn(true); when(autoScaleVmGroupDao.updateState(vmGroupId, AutoScaleVmGroup.State.SCALING, AutoScaleVmGroup.State.ENABLED)).thenReturn(true); Mockito.doReturn(virtualMachineId).when(autoScaleManagerImplSpy).createNewVM(asVmGroupMock); - Pair> startVm = Mockito.mock(Pair.class); - when(userVmMgr.startVirtualMachine(virtualMachineId, null, null, null)).thenReturn(startVm); when(asVmGroupMock.getLoadBalancerId()).thenReturn(loadBalancerId); when(lbVmMapDao.listByLoadBalancerId(loadBalancerId)).thenReturn(Arrays.asList(loadBalancerVMMapMock)); diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 5e10c4a38f23..7acd347d7fe8 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -41,6 +41,7 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.network.security.SecurityGroupVO; import com.cloud.offering.ServiceOffering; +import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -219,6 +220,12 @@ public class UserVmManagerImplTest { @Mock AccountVO account; + @Mock + VMTemplateVO vmTemplateVoMock; + + @Mock + ManagementService managementServiceMock; + @Mock private ServiceOfferingVO serviceOffering; @@ -1139,4 +1146,58 @@ public void testGetSecurityGroupIdList() { Mockito.verify(userVmManagerImpl).getSecurityGroupIdList(cmd); Mockito.verify(vnfTemplateManager).createSecurityGroupForVnfAppliance(any(), any(), any(), any(DeployVnfApplianceCmd.class)); } + + @Test + public void getCurrentVmPasswordOrDefineNewPasswordTestTemplateIsNotPasswordEnabledReturnPreDefinedString() { + String expected = "saved_password"; + + Mockito.doReturn(false).when(vmTemplateVoMock).isEnablePassword(); + + String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock); + + Assert.assertEquals(expected, result); + } + + @Test + public void getCurrentVmPasswordOrDefineNewPasswordTestVmHasPasswordReturnCurrentPassword() { + String expected = "current_password"; + + Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword(); + Mockito.doReturn(expected).when(userVmVoMock).getDetail("password"); + + String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock); + + Assert.assertEquals(expected, result); + } + + @Test + public void getCurrentVmPasswordOrDefineNewPasswordTestUserDefinedPasswordReturnNewPasswordAndSetVmPassword() { + String expected = "new_password"; + + Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword(); + Mockito.doReturn(null).when(userVmVoMock).getDetail("password"); + Mockito.doCallRealMethod().when(userVmVoMock).setPassword(Mockito.any()); + Mockito.doCallRealMethod().when(userVmVoMock).getPassword(); + + String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword(expected, userVmVoMock, vmTemplateVoMock); + + Assert.assertEquals(expected, result); + Assert.assertEquals(expected, userVmVoMock.getPassword()); + } + + @Test + public void getCurrentVmPasswordOrDefineNewPasswordTestUserDefinedPasswordReturnRandomPasswordAndSetVmPassword() { + String expected = "random_password"; + + Mockito.doReturn(true).when(vmTemplateVoMock).isEnablePassword(); + Mockito.doReturn(null).when(userVmVoMock).getDetail("password"); + Mockito.doReturn(expected).when(managementServiceMock).generateRandomPassword(); + Mockito.doCallRealMethod().when(userVmVoMock).setPassword(Mockito.any()); + Mockito.doCallRealMethod().when(userVmVoMock).getPassword(); + + String result = userVmManagerImpl.getCurrentVmPasswordOrDefineNewPassword("", userVmVoMock, vmTemplateVoMock); + + Assert.assertEquals(expected, result); + Assert.assertEquals(expected, userVmVoMock.getPassword()); + } } diff --git a/test/integration/component/test_project_resources.py b/test/integration/component/test_project_resources.py index f767af1788b9..16d21dfd7cba 100644 --- a/test/integration/component/test_project_resources.py +++ b/test/integration/component/test_project_resources.py @@ -551,66 +551,59 @@ def setUpClass(cls): cls.hypervisor = cls.testClient.getHypervisorInfo() if cls.hypervisor.lower() in ['lxc']: raise unittest.SkipTest("create template from volume is not supported on %s" % cls.hypervisor.lower()) + cls._cleanup = [] cls.template = get_template( - cls.api_client, - cls.zone.id, - cls.services["ostype"] - ) + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) cls.services["server"]["zoneid"] = cls.zone.id # Create Domains, Account etc cls.domain = Domain.create( - cls.api_client, - cls.services["domain"] - ) + cls.api_client, + cls.services["domain"] + ) + cls._cleanup.append(cls.domain) cls.account = Account.create( - cls.api_client, - cls.services["account"], - domainid=cls.domain.id - ) + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) cls.user = Account.create( - cls.api_client, - cls.services["account"], - domainid=cls.domain.id - ) + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls._cleanup.append(cls.user) # Create project as a domain admin cls.project = Project.create( - cls.api_client, - cls.services["project"], - account=cls.account.name, - domainid=cls.account.domainid - ) + cls.api_client, + cls.services["project"], + account=cls.account.name, + domainid=cls.account.domainid + ) + cls._cleanup.append(cls.project) cls.services["account"] = cls.account.name # Create Service offering and disk offerings etc cls.service_offering = ServiceOffering.create( - cls.api_client, - cls.services["service_offering"] - ) + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup.append(cls.service_offering) cls.userapiclient = cls.testClient.getUserApiClient( - UserName=cls.account.name, - DomainName=cls.domain.name - ) - - cls._cleanup = [ - cls.project, - cls.service_offering, - cls.account, - cls.user, - cls.domain - ] + UserName=cls.account.name, + DomainName=cls.domain.name + ) return @classmethod def tearDownClass(cls): - try: - #Cleanup resources used - cleanup_resources(cls.api_client, cls._cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return + super(TestTemplates, cls).tearDownClass() def setUp(self): self.apiclient = self.testClient.getApiClient() @@ -619,12 +612,7 @@ def setUp(self): return def tearDown(self): - try: - #Clean up, terminate the created instance, volumes and snapshots - cleanup_resources(self.apiclient, self.cleanup) - except Exception as e: - raise Exception("Warning: Exception during cleanup : %s" % e) - return + super(TestTemplates, self).tearDown() @attr(tags=["advanced", "basic", "sg", "eip", "advancedns"], required_hardware="false") def test_04_public_private_template_use_in_project(self): diff --git a/ui/docs/screenshot-dashboard.png b/ui/docs/screenshot-dashboard.png index 556cd7de420a..875bc2c5c134 100644 Binary files a/ui/docs/screenshot-dashboard.png and b/ui/docs/screenshot-dashboard.png differ diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 879c0771bb49..4cb53e92b6d9 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1302,6 +1302,7 @@ "label.move.to.bottom": "Move to bottom", "label.move.to.top": "Move to top", "label.move.up.row": "Move up one row", +"label.my.isos": "My ISOs", "label.my.templates": "My templates", "label.na": "N/A", "label.name": "Name", diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index f5ea23b3a654..d4c782ce06e9 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -369,6 +369,7 @@ export default { label: 'label.action.reset.password', message: 'message.action.instance.reset.password', dataView: true, + args: ['password'], show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled }, response: (result) => { return { diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index c4bcfe7eed58..5c78a4d0e68c 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -34,20 +34,20 @@ export default { permission: ['listNetworks'], resourceType: 'Network', columns: () => { - var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'account', 'domain', 'zonename'] + var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'domainpath', 'account', 'zonename'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domain', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } return fields }, - filters: ['all', 'account', 'domain', 'shared'], + filters: ['all', 'account', 'domainpath', 'shared'], searchFilters: ['keyword', 'zoneid', 'domainid', 'account', 'type', 'tags'], related: [{ name: 'vm', diff --git a/ui/src/views/compute/wizard/TemplateIsoSelection.vue b/ui/src/views/compute/wizard/TemplateIsoSelection.vue index 63c5a429e813..9393a7860de3 100644 --- a/ui/src/views/compute/wizard/TemplateIsoSelection.vue +++ b/ui/src/views/compute/wizard/TemplateIsoSelection.vue @@ -56,6 +56,10 @@ export default { name: 'TemplateIsoSelection', components: { TemplateIsoRadioGroup }, props: { + selected: { + type: String, + default: null + }, items: { type: Object, default: () => {} @@ -85,7 +89,7 @@ export default { name: 'label.community' }, { id: 'selfexecutable', - name: 'label.my.templates' + name: this.selected === 'isoid' ? 'label.my.isos' : 'label.my.templates' }, { id: 'sharedexecutable', name: 'label.sharedexecutable' diff --git a/ui/src/views/dashboard/UsageDashboard.vue b/ui/src/views/dashboard/UsageDashboard.vue index e7420b55c286..e9edd0cfb6e0 100644 --- a/ui/src/views/dashboard/UsageDashboard.vue +++ b/ui/src/views/dashboard/UsageDashboard.vue @@ -413,7 +413,7 @@ export default { (newValue, oldValue) => { if (newValue && newValue.id && (!oldValue || newValue.id !== oldValue.id)) { this.fetchData() - } else if (store.getters.userInfo.roletype !== 'Admin') { + } else if (store.getters.userInfo.roletype !== 'Admin' && !store.getters.logoutFlag) { this.fetchData() } } diff --git a/utils/src/main/java/com/cloud/utils/UriUtils.java b/utils/src/main/java/com/cloud/utils/UriUtils.java index dffc0106e8a8..a2bfa9eaa6fa 100644 --- a/utils/src/main/java/com/cloud/utils/UriUtils.java +++ b/utils/src/main/java/com/cloud/utils/UriUtils.java @@ -408,14 +408,14 @@ public static List getMetalinkUrls(String metalinkUrl) { return urls; } - public static final Set COMMPRESSION_FORMATS = ImmutableSet.of("zip", "bz2", "gz"); + public static final Set COMPRESSION_FORMATS = ImmutableSet.of("zip", "bz2", "gz"); public static final Set buildExtensionSet(boolean metalink, String... baseExtensions) { final ImmutableSet.Builder builder = ImmutableSet.builder(); for (String baseExtension : baseExtensions) { builder.add("." + baseExtension); - for (String format : COMMPRESSION_FORMATS) { + for (String format : COMPRESSION_FORMATS) { builder.add("." + baseExtension + "." + format); } } @@ -647,4 +647,8 @@ private static UriInfo getRbdUrlInfo(String url) { throw new CloudRuntimeException(url + " is not a valid uri for RBD"); } } + + public static boolean isUrlForCompressedFile(String url) { + return UriUtils.COMPRESSION_FORMATS.stream().anyMatch(f -> url.toLowerCase().endsWith(f)); + } } diff --git a/utils/src/main/java/com/cloud/utils/storage/QCOW2Utils.java b/utils/src/main/java/com/cloud/utils/storage/QCOW2Utils.java index 32a54722aaeb..4daf138bb8c8 100644 --- a/utils/src/main/java/com/cloud/utils/storage/QCOW2Utils.java +++ b/utils/src/main/java/com/cloud/utils/storage/QCOW2Utils.java @@ -32,6 +32,7 @@ import org.apache.log4j.Logger; import com.cloud.utils.NumbersUtil; +import com.cloud.utils.UriUtils; public final class QCOW2Utils { public static final Logger LOGGER = Logger.getLogger(QCOW2Utils.class.getName()); @@ -114,7 +115,7 @@ private static long getVirtualSizeFromInputStream(InputStream inputStream) throw public static long getVirtualSize(String urlStr) { try { URL url = new URL(urlStr); - return getVirtualSizeFromInputStream(url.openStream()); + return getVirtualSize(url.openStream(), UriUtils.isUrlForCompressedFile(urlStr)); } catch (MalformedURLException e) { LOGGER.warn("Failed to validate for qcow2, malformed URL: " + urlStr + ", error: " + e.getMessage()); throw new IllegalArgumentException("Invalid URL: " + urlStr); diff --git a/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java b/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java index 02878cf29606..2b37ce5fb72f 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/imagestore/ImageStoreUtil.java @@ -101,7 +101,7 @@ public static boolean isCorrectExtension(String path, String format) { public static boolean isCompressedExtension(String path) { final String lowerCasePath = path.toLowerCase(); - return UriUtils.COMMPRESSION_FORMATS + return UriUtils.COMPRESSION_FORMATS .stream() .map(extension -> "." + extension) .anyMatch(lowerCasePath::endsWith); diff --git a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java index 1a3ae17f584e..4ec1f9a9bd92 100644 --- a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java @@ -19,13 +19,13 @@ package com.cloud.utils; -import org.junit.Assert; -import org.junit.Test; - import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.junit.Assert; +import org.junit.Test; + public class UriUtilsTest { @Test public void encodeURIComponent() { @@ -265,4 +265,12 @@ public void testGetUriInfoIpv6() { testGetUriInfoInternal(url11, host); testGetUriInfoInternal(url12, host); } + + @Test + public void testIsUrlForCompressedFile() { + Assert.assertTrue(UriUtils.isUrlForCompressedFile("https://abc.com/xyz.bz2")); + Assert.assertTrue(UriUtils.isUrlForCompressedFile("http://abc.com/xyz.zip")); + Assert.assertTrue(UriUtils.isUrlForCompressedFile("https://abc.com/xyz.gz")); + Assert.assertFalse(UriUtils.isUrlForCompressedFile("http://abc.com/xyz.qcow2")); + } }