diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java index 429de5774a4..2f0cc736af3 100644 --- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java +++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java @@ -27,7 +27,7 @@ public class Hypervisor { static Map hypervisorTypeMap; static Map supportedImageFormatMap; - public static enum HypervisorType { + public enum HypervisorType { None, //for storage hosts XenServer, KVM, @@ -40,6 +40,7 @@ public class Hypervisor { Ovm, Ovm3, LXC, + Custom, Any; /*If you don't care about the hypervisor type*/ @@ -57,6 +58,7 @@ public class Hypervisor { hypervisorTypeMap.put("lxc", HypervisorType.LXC); hypervisorTypeMap.put("any", HypervisorType.Any); hypervisorTypeMap.put("ovm3", HypervisorType.Ovm3); + hypervisorTypeMap.put("custom", HypervisorType.Custom); supportedImageFormatMap = new HashMap<>(); supportedImageFormatMap.put(HypervisorType.XenServer, ImageFormat.VHD); @@ -68,7 +70,19 @@ public class Hypervisor { public static HypervisorType getType(String hypervisor) { return hypervisor == null ? HypervisorType.None : - hypervisorTypeMap.getOrDefault(hypervisor.toLowerCase(Locale.ROOT), HypervisorType.None); + (hypervisor.toLowerCase(Locale.ROOT).equalsIgnoreCase( + HypervisorGuru.HypervisorCustomDisplayName.value()) ? Custom : + hypervisorTypeMap.getOrDefault(hypervisor.toLowerCase(Locale.ROOT), HypervisorType.None)); + } + + /** + * Returns the display name of a hypervisor type in case the custom hypervisor is used, + * using the 'hypervisor.custom.display.name' setting. Otherwise, returns hypervisor name + */ + public String getHypervisorDisplayName() { + return !Hypervisor.HypervisorType.Custom.equals(this) ? + this.toString() : + HypervisorGuru.HypervisorCustomDisplayName.value(); } /** diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index c7dc0bba109..2dfa707b57b 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.Command; import com.cloud.agent.api.to.NicTO; @@ -35,6 +36,10 @@ import com.cloud.vm.VirtualMachineProfile; public interface HypervisorGuru extends Adapter { + ConfigKey HypervisorCustomDisplayName = new ConfigKey<>(String.class, + "hypervisor.custom.display.name", ConfigKey.CATEGORY_ADVANCED, "Custom", + "Display name for custom hypervisor", true, ConfigKey.Scope.Global, null); + HypervisorType getHypervisorType(); /** diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 1a5e851d884..5709e3ed900 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import com.cloud.hypervisor.HypervisorGuru; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -342,9 +343,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Parameter zoneids cannot combine all zones (-1) option with other zones"); - if (isDirectDownload() && !getHypervisor().equalsIgnoreCase(Hypervisor.HypervisorType.KVM.toString())) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, - "Parameter directdownload is only allowed for KVM templates"); + String customHypervisor = HypervisorGuru.HypervisorCustomDisplayName.value(); + if (isDirectDownload() && !(getHypervisor().equalsIgnoreCase(Hypervisor.HypervisorType.KVM.toString()) + || getHypervisor().equalsIgnoreCase(customHypervisor))) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Parameter directdownload " + + "is only allowed for KVM or %s templates", customHypervisor)); } if (!isDeployAsIs() && osTypeId == null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java index 4ed0cdd8d74..41a0fdc4567 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostForMigrationResponse.java @@ -24,7 +24,6 @@ import org.apache.cloudstack.api.EntityReference; import com.cloud.host.Host; import com.cloud.host.Status; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @@ -84,7 +83,7 @@ public class HostForMigrationResponse extends BaseResponse { @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the host hypervisor") - private HypervisorType hypervisor; + private String hypervisor; @SerializedName("cpunumber") @Param(description = "the CPU number of the host") @@ -295,7 +294,7 @@ public class HostForMigrationResponse extends BaseResponse { this.version = version; } - public void setHypervisor(HypervisorType hypervisor) { + public void setHypervisor(String hypervisor) { this.hypervisor = hypervisor; } 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 5d809cf1553..e1f1e5ee241 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 @@ -29,7 +29,6 @@ import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement; import com.cloud.host.Host; import com.cloud.host.Status; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @@ -89,7 +88,7 @@ public class HostResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the host hypervisor") - private HypervisorType hypervisor; + private String hypervisor; @SerializedName("cpusockets") @Param(description = "the number of CPU sockets on the host") @@ -335,7 +334,7 @@ public class HostResponse extends BaseResponseWithAnnotations { this.version = version; } - public void setHypervisor(HypervisorType hypervisor) { + public void setHypervisor(String hypervisor) { this.hypervisor = hypervisor; } @@ -602,7 +601,7 @@ public class HostResponse extends BaseResponseWithAnnotations { return version; } - public HypervisorType getHypervisor() { + public String getHypervisor() { return hypervisor; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java index b5e5624bbe5..c19397e0c83 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HypervisorCapabilitiesResponse.java @@ -20,7 +20,6 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @@ -37,7 +36,7 @@ public class HypervisorCapabilitiesResponse extends BaseResponse { @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the hypervisor type") - private HypervisorType hypervisor; + private String hypervisor; @SerializedName(ApiConstants.MAX_GUESTS_LIMIT) @Param(description = "the maximum number of guest vms recommended for this hypervisor") @@ -83,11 +82,11 @@ public class HypervisorCapabilitiesResponse extends BaseResponse { this.hypervisorVersion = hypervisorVersion; } - public HypervisorType getHypervisor() { + public String getHypervisor() { return hypervisor; } - public void setHypervisor(HypervisorType hypervisor) { + public void setHypervisor(String hypervisor) { this.hypervisor = hypervisor; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java index 37670cf6224..9b553ed0744 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VMSnapshotResponse.java @@ -25,7 +25,6 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; -import com.cloud.hypervisor.Hypervisor; import com.cloud.serializer.Param; import com.cloud.vm.snapshot.VMSnapshot; import com.google.gson.annotations.SerializedName; @@ -111,7 +110,7 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the type of hypervisor on which snapshot is stored") - private Hypervisor.HypervisorType hypervisor; + private String hypervisor; public VMSnapshotResponse() { tags = new LinkedHashSet(); @@ -266,11 +265,11 @@ public class VMSnapshotResponse extends BaseResponseWithTagInformation implement this.tags = tags; } - public Hypervisor.HypervisorType getHypervisor() { + public String getHypervisor() { return hypervisor; } - public void setHypervisor(Hypervisor.HypervisorType hypervisor) { + public void setHypervisor(String hypervisor) { this.hypervisor = hypervisor; } } diff --git a/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java b/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java index 087649b887a..e900d28a864 100644 --- a/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSHypervisorVO.java @@ -26,6 +26,7 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.db.GenericDao; @Entity @@ -72,7 +73,7 @@ public class GuestOSHypervisorVO implements GuestOSHypervisor { @Override public String getHypervisorType() { - return hypervisorType; + return Hypervisor.HypervisorType.getType(hypervisorType).toString(); } @Override diff --git a/engine/storage/integration-test/src/test/resources/component.xml b/engine/storage/integration-test/src/test/resources/component.xml index aee37145114..d384d546665 100644 --- a/engine/storage/integration-test/src/test/resources/component.xml +++ b/engine/storage/integration-test/src/test/resources/component.xml @@ -121,6 +121,11 @@ + + + + diff --git a/plugins/network-elements/dns-notifier/src/main/resources/components-example.xml b/plugins/network-elements/dns-notifier/src/main/resources/components-example.xml index c53c0b14ef1..76a6cad9b4a 100755 --- a/plugins/network-elements/dns-notifier/src/main/resources/components-example.xml +++ b/plugins/network-elements/dns-notifier/src/main/resources/components-example.xml @@ -111,6 +111,7 @@ under the License. + diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 162b1f38add..16377e13b03 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -1283,6 +1283,9 @@ public class ApiDBUtils { // If this check is not passed, the hypervisor type will remain OVM. type = HypervisorType.KVM; break; + } else if (pool.getHypervisor() == HypervisorType.Custom) { + type = HypervisorType.Custom; + break; } } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 568625c856b..8cd80dd9a52 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -38,6 +38,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -735,7 +736,7 @@ public class ApiResponseHelper implements ResponseGenerator { if (vm != null) { vmSnapshotResponse.setVirtualMachineId(vm.getUuid()); vmSnapshotResponse.setVirtualMachineName(StringUtils.isEmpty(vm.getDisplayName()) ? vm.getHostName() : vm.getDisplayName()); - vmSnapshotResponse.setHypervisor(vm.getHypervisorType()); + vmSnapshotResponse.setHypervisor(vm.getHypervisorType().getHypervisorDisplayName()); DataCenterVO datacenter = ApiDBUtils.findZoneById(vm.getDataCenterId()); if (datacenter != null) { vmSnapshotResponse.setZoneId(datacenter.getUuid()); @@ -1437,7 +1438,7 @@ public class ApiResponseHelper implements ResponseGenerator { clusterResponse.setZoneId(dc.getUuid()); clusterResponse.setZoneName(dc.getName()); } - clusterResponse.setHypervisorType(cluster.getHypervisorType().toString()); + clusterResponse.setHypervisorType(cluster.getHypervisorType().getHypervisorDisplayName()); clusterResponse.setClusterType(cluster.getClusterType().toString()); clusterResponse.setAllocationState(cluster.getAllocationState().toString()); clusterResponse.setManagedState(cluster.getManagedState().toString()); @@ -1633,7 +1634,7 @@ public class ApiResponseHelper implements ResponseGenerator { vmResponse.setTemplateName(template.getName()); } vmResponse.setCreated(vm.getCreated()); - vmResponse.setHypervisor(vm.getHypervisorType().toString()); + vmResponse.setHypervisor(vm.getHypervisorType().getHypervisorDisplayName()); if (vm.getHostId() != null) { Host host = ApiDBUtils.findHostById(vm.getHostId()); @@ -2792,7 +2793,7 @@ public class ApiResponseHelper implements ResponseGenerator { public HypervisorCapabilitiesResponse createHypervisorCapabilitiesResponse(HypervisorCapabilities hpvCapabilities) { HypervisorCapabilitiesResponse hpvCapabilitiesResponse = new HypervisorCapabilitiesResponse(); hpvCapabilitiesResponse.setId(hpvCapabilities.getUuid()); - hpvCapabilitiesResponse.setHypervisor(hpvCapabilities.getHypervisorType()); + hpvCapabilitiesResponse.setHypervisor(hpvCapabilities.getHypervisorType().getHypervisorDisplayName()); hpvCapabilitiesResponse.setHypervisorVersion(hpvCapabilities.getHypervisorVersion()); hpvCapabilitiesResponse.setIsSecurityGroupEnabled(hpvCapabilities.isSecurityGroupEnabled()); hpvCapabilitiesResponse.setMaxGuestsLimit(hpvCapabilities.getMaxGuestsLimit()); @@ -3690,7 +3691,7 @@ public class ApiResponseHelper implements ResponseGenerator { public GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor guestOSHypervisor) { GuestOsMappingResponse response = new GuestOsMappingResponse(); response.setId(guestOSHypervisor.getUuid()); - response.setHypervisor(guestOSHypervisor.getHypervisorType()); + response.setHypervisor(Hypervisor.HypervisorType.getType(guestOSHypervisor.getHypervisorType()).getHypervisorDisplayName()); response.setHypervisorVersion(guestOSHypervisor.getHypervisorVersion()); response.setOsNameForHypervisor((guestOSHypervisor.getGuestOsName())); response.setIsUserDefined(Boolean.valueOf(guestOSHypervisor.getIsUserDefined()).toString()); @@ -4940,7 +4941,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setId(certificate.getUuid()); response.setAlias(certificate.getAlias()); handleCertificateResponse(certificate.getCertificate(), response); - response.setHypervisor(certificate.getHypervisorType().name()); + response.setHypervisor(certificate.getHypervisorType().getHypervisorDisplayName()); response.setObjectName("directdownloadcertificate"); return response; } diff --git a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index 83a89622bd2..e3011bc4d66 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -126,7 +126,7 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase implements hostResponse.setCpuNumber(host.getCpus()); hostResponse.setZoneId(host.getZoneUuid()); hostResponse.setDisconnectedOn(host.getDisconnectedOn()); - hostResponse.setHypervisor(host.getHypervisorType()); + if (host.getHypervisorType() != null) { + String hypervisorType = host.getHypervisorType().getHypervisorDisplayName(); + hostResponse.setHypervisor(hypervisorType); + } hostResponse.setHostType(host.getType()); hostResponse.setLastPinged(new Date(host.getLastPinged())); Long mshostId = host.getManagementServerId(); @@ -239,7 +242,8 @@ public class HostJoinDaoImpl extends GenericDaoBase implements hostResponse.setUefiCapabilty(new Boolean(false)); } } - if (details.contains(HostDetails.all) && host.getHypervisorType() == Hypervisor.HypervisorType.KVM) { + if (details.contains(HostDetails.all) && (host.getHypervisorType() == Hypervisor.HypervisorType.KVM || + host.getHypervisorType() == Hypervisor.HypervisorType.Custom)) { //only kvm has the requirement to return host details try { hostResponse.setDetails(hostDetails); @@ -303,7 +307,7 @@ public class HostJoinDaoImpl extends GenericDaoBase implements hostResponse.setCpuNumber(host.getCpus()); hostResponse.setZoneId(host.getZoneUuid()); hostResponse.setDisconnectedOn(host.getDisconnectedOn()); - hostResponse.setHypervisor(host.getHypervisorType()); + hostResponse.setHypervisor(host.getHypervisorType().getHypervisorDisplayName()); hostResponse.setHostType(host.getType()); hostResponse.setLastPinged(new Date(host.getLastPinged())); hostResponse.setManagementServerId(host.getManagementServerId()); diff --git a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java index 527cc949ed1..de469d21a11 100644 --- a/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/StoragePoolJoinDaoImpl.java @@ -110,7 +110,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase[] getConfigKeys() { - return new ConfigKey[] {VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor, VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor }; + return new ConfigKey[] {VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor, + VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor, + HypervisorCustomDisplayName + }; } } diff --git a/server/src/main/java/com/cloud/hypervisor/discoverer/CustomServerDiscoverer.java b/server/src/main/java/com/cloud/hypervisor/discoverer/CustomServerDiscoverer.java new file mode 100644 index 00000000000..534947f092e --- /dev/null +++ b/server/src/main/java/com/cloud/hypervisor/discoverer/CustomServerDiscoverer.java @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.hypervisor.discoverer; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.kvm.discoverer.LibvirtServerDiscoverer; + +public class CustomServerDiscoverer extends LibvirtServerDiscoverer { + @Override + public Hypervisor.HypervisorType getHypervisorType() { + return Hypervisor.HypervisorType.Custom; + } + + @Override + protected String getPatchPath() { + return "scripts/vm/hypervisor/kvm/"; + } +} diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 150aed65a36..d1697646c39 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -42,6 +42,7 @@ import com.cloud.exception.StorageUnavailableException; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; +import com.cloud.hypervisor.HypervisorGuru; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -652,7 +653,9 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, } } - return discoverHostsFull(dcId, podId, clusterId, clusterName, url, username, password, cmd.getHypervisor(), hostTags, cmd.getFullUrlParams(), false); + String hypervisorType = cmd.getHypervisor().equalsIgnoreCase(HypervisorGuru.HypervisorCustomDisplayName.value()) ? + "Custom" : cmd.getHypervisor(); + return discoverHostsFull(dcId, podId, clusterId, clusterName, url, username, password, hypervisorType, hostTags, cmd.getFullUrlParams(), false); } @Override diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index c16dc4eb2f4..5791f4bf789 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -1264,7 +1264,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } if (hypervisorType != null) { - sc.setParameters("hypervisorType", hypervisorType); + String hypervisorStr = (String) hypervisorType; + String hypervisorSearch = HypervisorType.getType(hypervisorStr).toString(); + sc.setParameters("hypervisorType", hypervisorSearch); } if (clusterType != null) { @@ -4470,7 +4472,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } else { final List clustersForZone = _clusterDao.listByZoneId(zoneId); for (final ClusterVO cluster : clustersForZone) { - result.add(cluster.getHypervisorType().toString()); + result.add(cluster.getHypervisorType().getHypervisorDisplayName()); } } diff --git a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java index f5c19ecff1d..3f45505bcc5 100644 --- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java @@ -148,20 +148,21 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { } /** - * Validate on random running KVM host that URL is reachable + * Validate on random running host that URL is reachable * @param url url */ - private Long performDirectDownloadUrlValidation(final String format, final String url, final List zoneIds) { + private Long performDirectDownloadUrlValidation(final String format, final Hypervisor.HypervisorType hypervisor, + final String url, final List zoneIds) { HostVO host = null; if (zoneIds != null && !zoneIds.isEmpty()) { for (Long zoneId : zoneIds) { - host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, zoneId); + host = resourceManager.findOneRandomRunningHostByHypervisor(hypervisor, zoneId); if (host != null) { break; } } } else { - host = resourceManager.findOneRandomRunningHostByHypervisor(Hypervisor.HypervisorType.KVM, null); + host = resourceManager.findOneRandomRunningHostByHypervisor(hypervisor, null); } if (host == null) { @@ -198,7 +199,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { zoneIds = new ArrayList<>(); zoneIds.add(cmd.getZoneId()); } - Long templateSize = performDirectDownloadUrlValidation(ImageFormat.ISO.getFileExtension(), url, zoneIds); + Long templateSize = performDirectDownloadUrlValidation(ImageFormat.ISO.getFileExtension(), + Hypervisor.HypervisorType.KVM, url, zoneIds); profile.setSize(templateSize); } profile.setUrl(url); @@ -221,9 +223,11 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { TemplateProfile profile = super.prepare(cmd); String url = profile.getUrl(); UriUtils.validateUrl(cmd.getFormat(), url, cmd.isDirectDownload()); + Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.getType(cmd.getHypervisor()); if (cmd.isDirectDownload()) { DigestHelper.validateChecksumString(cmd.getChecksum()); - Long templateSize = performDirectDownloadUrlValidation(cmd.getFormat(), url, cmd.getZoneIds()); + Long templateSize = performDirectDownloadUrlValidation(cmd.getFormat(), + hypervisor, url, cmd.getZoneIds()); profile.setSize(templateSize); } profile.setUrl(url); diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 1a360c88edb..790d9163bf0 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -309,8 +309,12 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, if (type == HypervisorType.BareMetal) { adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.BareMetal.getName()); } else { - // see HypervisorTemplateAdapter - adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.Hypervisor.getName()); + // Get template adapter according to hypervisor + adapter = AdapterBase.getAdapterByName(_adapters, type.name()); + // Otherwise, default to generic hypervisor template adapter + if (adapter == null) { + adapter = AdapterBase.getAdapterByName(_adapters, TemplateAdapterType.Hypervisor.getName()); + } } if (adapter == null) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 1dd6d6541e9..3ddaf6b58d3 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -650,6 +650,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir HypervisorType.Simulator )); + protected static final List ROOT_DISK_SIZE_OVERRIDE_SUPPORTING_HYPERVISORS = Arrays.asList( + HypervisorType.KVM, + HypervisorType.XenServer, + HypervisorType.VMware, + HypervisorType.Simulator, + HypervisorType.Custom + ); + private static final List HYPERVISORS_THAT_CAN_DO_STORAGE_MIGRATION_ON_NON_USER_VMS = Arrays.asList(HypervisorType.KVM, HypervisorType.VMware); @Override @@ -4338,7 +4346,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * @throws InvalidParameterValueException if the hypervisor does not support rootdisksize override */ protected void verifyIfHypervisorSupportsRootdiskSizeOverride(HypervisorType hypervisorType) { - if (!(hypervisorType == HypervisorType.KVM || hypervisorType == HypervisorType.XenServer || hypervisorType == HypervisorType.VMware || hypervisorType == HypervisorType.Simulator)) { + if (!ROOT_DISK_SIZE_OVERRIDE_SUPPORTING_HYPERVISORS.contains(hypervisorType)) { throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support rootdisksize override"); } } @@ -5004,6 +5012,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Answer startAnswer = cmds.getAnswer(StartAnswer.class); String returnedIp = null; String originalIp = null; + String originalVncPassword = profile.getVirtualMachine().getVncPassword(); + String returnedVncPassword = null; if (startAnswer != null) { StartAnswer startAns = (StartAnswer)startAnswer; VirtualMachineTO vmTO = startAns.getVirtualMachine(); @@ -5012,6 +5022,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir returnedIp = nicTO.getIp(); } } + returnedVncPassword = vmTO.getVncPassword(); } List nics = _nicDao.listByVmId(vm.getId()); @@ -5063,6 +5074,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } + updateVncPasswordIfItHasChanged(originalVncPassword, returnedVncPassword, profile); + // get system ip and create static nat rule for the vm try { _rulesMgr.getSystemIpAndEnableStaticNatForVm(profile.getVirtualMachine(), false); @@ -5097,6 +5110,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return true; } + protected void updateVncPasswordIfItHasChanged(String originalVncPassword, String returnedVncPassword, VirtualMachineProfile profile) { + if (returnedVncPassword != null && !originalVncPassword.equals(returnedVncPassword)) { + UserVmVO userVm = _vmDao.findById(profile.getId()); + userVm.setVncPassword(returnedVncPassword); + _vmDao.update(userVm.getId(), userVm); + } + } + @Override public void finalizeExpunge(VirtualMachine vm) { } diff --git a/server/src/main/resources/META-INF/cloudstack/server-discoverer/spring-server-discoverer-context.xml b/server/src/main/resources/META-INF/cloudstack/server-discoverer/spring-server-discoverer-context.xml index 3a7e0ffb04a..98abb08ae11 100644 --- a/server/src/main/resources/META-INF/cloudstack/server-discoverer/spring-server-discoverer-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/server-discoverer/spring-server-discoverer-context.xml @@ -38,6 +38,11 @@ + + + + diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index ef001906d98..9c591690bbb 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -221,6 +221,9 @@ public class UserVmManagerImplTest { @Mock UserDataManager userDataManager; + @Mock + VirtualMachineProfile virtualMachineProfile; + private static final long vmId = 1l; private static final long zoneId = 2L; private static final long accountId = 3L; @@ -248,6 +251,8 @@ public class UserVmManagerImplTest { customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, "123"); lenient().doNothing().when(resourceLimitMgr).incrementResourceCount(anyLong(), any(Resource.ResourceType.class)); lenient().doNothing().when(resourceLimitMgr).decrementResourceCount(anyLong(), any(Resource.ResourceType.class), anyLong()); + + Mockito.when(virtualMachineProfile.getId()).thenReturn(vmId); } @After @@ -561,13 +566,10 @@ public class UserVmManagerImplTest { public void verifyIfHypervisorSupportRootdiskSizeOverrideTest() { Hypervisor.HypervisorType[] hypervisorTypeArray = Hypervisor.HypervisorType.values(); int exceptionCounter = 0; - int expectedExceptionCounter = hypervisorTypeArray.length - 4; + int expectedExceptionCounter = hypervisorTypeArray.length - 5; for(int i = 0; i < hypervisorTypeArray.length; i++) { - if (Hypervisor.HypervisorType.KVM == hypervisorTypeArray[i] - || Hypervisor.HypervisorType.XenServer == hypervisorTypeArray[i] - || Hypervisor.HypervisorType.VMware == hypervisorTypeArray[i] - || Hypervisor.HypervisorType.Simulator == hypervisorTypeArray[i]) { + if (UserVmManagerImpl.ROOT_DISK_SIZE_OVERRIDE_SUPPORTING_HYPERVISORS.contains(hypervisorTypeArray[i])) { userVmManagerImpl.verifyIfHypervisorSupportsRootdiskSizeOverride(hypervisorTypeArray[i]); } else { try { @@ -1041,4 +1043,21 @@ public class UserVmManagerImplTest { Pair pair = mockObjectsForChooseVmMigrationDestinationUsingVolumePoolMapTest(false, destinationHost); Assert.assertEquals(destinationHost, userVmManagerImpl.chooseVmMigrationDestinationUsingVolumePoolMap(pair.first(), pair.second(), null)); } + + @Test + public void testUpdateVncPasswordIfItHasChanged() { + String vncPassword = "12345678"; + userVmManagerImpl.updateVncPasswordIfItHasChanged(vncPassword, vncPassword, virtualMachineProfile); + Mockito.verify(userVmDao, Mockito.never()).update(vmId, userVmVoMock); + } + + @Test + public void testUpdateVncPasswordIfItHasChangedNewPassword() { + String vncPassword = "12345678"; + String newPassword = "87654321"; + Mockito.when(userVmVoMock.getId()).thenReturn(vmId); + userVmManagerImpl.updateVncPasswordIfItHasChanged(vncPassword, newPassword, virtualMachineProfile); + Mockito.verify(userVmDao).findById(vmId); + Mockito.verify(userVmDao).update(vmId, userVmVoMock); + } } diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js index 60f30e3f3e0..22a9afc79cd 100644 --- a/ui/src/config/section/infra/hosts.js +++ b/ui/src/config/section/infra/hosts.js @@ -81,7 +81,9 @@ export default { label: 'label.action.secure.host', message: 'message.action.secure.host', dataView: true, - show: (record) => { return record.hypervisor === 'KVM' }, + show: (record) => { + return record.hypervisor === 'KVM' || record.hypervisor === store.getters.customHypervisorName + }, args: ['hostid'], mapping: { hostid: { diff --git a/ui/src/store/getters.js b/ui/src/store/getters.js index d795e3318c1..e6f02028ab1 100644 --- a/ui/src/store/getters.js +++ b/ui/src/store/getters.js @@ -48,7 +48,8 @@ const getters = { twoFaEnabled: state => state.user.twoFaEnabled, twoFaProvider: state => state.user.twoFaProvider, twoFaIssuer: state => state.user.twoFaIssuer, - loginFlag: state => state.user.loginFlag + loginFlag: state => state.user.loginFlag, + customHypervisorName: state => state.user.customHypervisorName } export default getters diff --git a/ui/src/store/modules/user.js b/ui/src/store/modules/user.js index 051380231a2..91861a87a41 100644 --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@ -64,7 +64,8 @@ const user = { shutdownTriggered: false, twoFaEnabled: false, twoFaProvider: '', - twoFaIssuer: '' + twoFaIssuer: '', + customHypervisorName: 'Custom' }, mutations: { @@ -151,6 +152,9 @@ const user = { }, SET_LOGIN_FLAG: (state, flag) => { state.loginFlag = flag + }, + SET_CUSTOM_HYPERVISOR_NAME (state, name) { + state.customHypervisorName = name } }, @@ -298,6 +302,15 @@ const user = { commit('SET_CLOUDIAN', cloudian) }).catch(ignored => { }) + + api('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { + if (json.listconfigurationsresponse.configuration !== null) { + const config = json.listconfigurationsresponse.configuration[0] + commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) + } + }).catch(error => { + reject(error) + }) }) }, @@ -391,6 +404,15 @@ const user = { }).catch(error => { reject(error) }) + + api('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { + if (json.listconfigurationsresponse.configuration !== null) { + const config = json.listconfigurationsresponse.configuration[0] + commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) + } + }).catch(error => { + reject(error) + }) }) }, UpdateConfiguration ({ commit }) { @@ -411,6 +433,9 @@ const user = { }, SetLoginFlag ({ commit }, loggedIn) { commit('SET_LOGIN_FLAG', loggedIn) + }, + SetCustomHypervisorName ({ commit }, name) { + commit('SET_CUSTOM_HYPERVISOR_NAME', name) } } } diff --git a/ui/src/views/image/RegisterOrUploadTemplate.vue b/ui/src/views/image/RegisterOrUploadTemplate.vue index 4ed33afce9a..06ca207a0ab 100644 --- a/ui/src/views/image/RegisterOrUploadTemplate.vue +++ b/ui/src/views/image/RegisterOrUploadTemplate.vue @@ -154,7 +154,7 @@ - + @@ -398,6 +398,7 @@ export default { userdatapolicylist: {}, defaultOsId: null, hyperKVMShow: false, + hyperCustomShow: false, hyperXenServerShow: false, hyperVMWShow: false, selectedFormat: '', @@ -408,7 +409,8 @@ export default { allowed: false, allowDirectDownload: false, uploadParams: null, - currentForm: ['plus-outlined', 'PlusOutlined'].includes(this.action.currentAction.icon) ? 'Create' : 'Upload' + currentForm: ['plus-outlined', 'PlusOutlined'].includes(this.action.currentAction.icon) ? 'Create' : 'Upload', + customHypervisorName: 'Custom' } }, beforeCreate () { @@ -456,6 +458,7 @@ export default { }) }, fetchData () { + this.fetchCustomHypervisorName() this.fetchZone() this.fetchOsTypes() this.fetchUserData() @@ -516,6 +519,23 @@ export default { }) }) }, + fetchCustomHypervisorName () { + const params = { + name: 'hypervisor.custom.display.name' + } + this.loading = true + api('listConfigurations', params).then(json => { + if (json.listconfigurationsresponse.configuration !== null) { + const config = json.listconfigurationsresponse.configuration[0] + if (config && config.name === params.name) { + this.customHypervisorName = config.value + store.dispatch('SetCustomHypervisorName', this.customHypervisorName) + } + } + }).finally(() => { + this.loading = false + }) + }, fetchZone () { const params = {} let listZones = [] @@ -781,6 +801,13 @@ export default { description: 'TAR' }) break + case this.customHypervisorName: + this.hyperCustomShow = true + format.push({ + id: 'RAW', + description: 'RAW' + }) + break default: break } @@ -839,6 +866,7 @@ export default { this.hyperXenServerShow = false this.hyperVMWShow = false this.hyperKVMShow = false + this.hyperCustomShow = false this.deployasis = false this.allowDirectDownload = false this.selectedFormat = null diff --git a/ui/src/views/infra/zone/ZoneWizardAddResources.vue b/ui/src/views/infra/zone/ZoneWizardAddResources.vue index 4c919af94ed..24ddd264486 100644 --- a/ui/src/views/infra/zone/ZoneWizardAddResources.vue +++ b/ui/src/views/infra/zone/ZoneWizardAddResources.vue @@ -108,6 +108,7 @@ import { nextTick } from 'vue' import { api } from '@/api' import { mixinDevice } from '@/utils/mixin.js' import StaticInputsForm from '@views/infra/zone/StaticInputsForm' +import store from '@/store' export default { components: { @@ -283,7 +284,7 @@ export default { placeHolder: 'message.error.host.name', required: true, display: { - hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator'] + hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator', store.getters.customHypervisorName] } }, { @@ -292,7 +293,7 @@ export default { placeHolder: 'message.error.host.username', required: true, display: { - hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator'] + hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator', store.getters.customHypervisorName] } }, { @@ -329,7 +330,7 @@ export default { required: true, password: true, display: { - hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator'], + hypervisor: ['VMware', 'BareMetal', 'Ovm', 'Hyperv', 'KVM', 'XenServer', 'LXC', 'Simulator', store.getters.customHypervisorName], authmethod: 'password' } },