This is an automated email from the ASF dual-hosted git repository. andrijapanic pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push: new d4b537e UEFI Implementation: Enabled UEFI Support for Guest VM's on Hypervisor KVM,VMware. enabled boot modes [Legacy,Secure] support for UEFI boot with known caveats. (#3638) d4b537e is described below commit d4b537efa7ed27ef88b735a22493808b61529186 Author: pavanaravapalli <pavankuma...@accelerite.com> AuthorDate: Sat Mar 14 01:26:26 2020 +0530 UEFI Implementation: Enabled UEFI Support for Guest VM's on Hypervisor KVM,VMware. enabled boot modes [Legacy,Secure] support for UEFI boot with known caveats. (#3638) Co-authored-by: Pavan Kumar Aravapalli <pavan_aravapa...@accelerite.com> Co-authored-by: dahn <daan.hoogl...@shapeblue.com> --- .../com/cloud/agent/api/to/VirtualMachineTO.java | 13 +++ api/src/main/java/com/cloud/host/Host.java | 1 + .../java/com/cloud/vm/VirtualMachineProfile.java | 3 + .../main/java/com/cloud/vm/VmDetailConstants.java | 1 + .../org/apache/cloudstack/api/ApiConstants.java | 21 ++++ .../api/command/user/vm/DeployVMCmd.java | 48 ++++++++ .../cloudstack/api/response/HostResponse.java | 16 +++ .../cloudstack/api/response/UserVmResponse.java | 17 +++ .../cloud/entity/api/VirtualMachineEntity.java | 6 + .../com/cloud/vm/VirtualMachineManagerImpl.java | 4 + .../cloud/entity/api/VMEntityManagerImpl.java | 10 ++ .../cloud/entity/api/VirtualMachineEntityImpl.java | 20 ++++ .../src/main/java/com/cloud/host/dao/HostDao.java | 2 + .../main/java/com/cloud/host/dao/HostDaoImpl.java | 34 ++++++ .../implicitplanner/ImplicitPlannerTest.java | 5 + .../kvm/resource/LibvirtComputingResource.java | 113 +++++++++++++++++- .../hypervisor/kvm/resource/LibvirtVMDef.java | 129 ++++++++++++++++++++- .../java/com/cloud/hypervisor/guru/VMwareGuru.java | 1 + .../hypervisor/vmware/VmwareServerDiscoverer.java | 5 + .../hypervisor/vmware/resource/VmwareResource.java | 19 +++ .../manager/allocator/impl/FirstFitAllocator.java | 28 +++++ .../com/cloud/api/query/dao/UserVmJoinDaoImpl.java | 9 ++ .../deploy/DeploymentPlanningManagerImpl.java | 11 ++ .../java/com/cloud/deploy/FirstFitPlanner.java | 29 ++++- .../com/cloud/hypervisor/HypervisorGuruBase.java | 10 +- .../kvm/discoverer/LibvirtServerDiscoverer.java | 7 ++ .../com/cloud/server/ManagementServerImpl.java | 14 +++ .../main/java/com/cloud/vm/UserVmManagerImpl.java | 16 ++- .../vm/DeploymentPlanningManagerImplTest.java | 7 ++ .../java/com/cloud/vm/FirstFitPlannerTest.java | 6 + ui/index.html | 25 ++++ ui/l10n/en.js | 3 + ui/scripts/instanceWizard.js | 12 ++ ui/scripts/instances.js | 6 + ui/scripts/system.js | 4 + ui/scripts/ui-custom/instanceWizard.js | 44 +++++++ .../com/cloud/hypervisor/vmware/mo/HostMO.java | 13 +++ .../cloud/hypervisor/vmware/util/VmwareHelper.java | 1 + 38 files changed, 700 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java index d25ffe3..40f30df 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -63,6 +63,8 @@ public class VirtualMachineTO { String vncAddr; Map<String, String> params; String uuid; + String bootType; + String bootMode; DiskTO[] disks; NicTO[] nics; @@ -380,4 +382,15 @@ public class VirtualMachineTO { public void setOvfProperties(Pair<String, List<OVFPropertyTO>> ovfProperties) { this.ovfProperties = ovfProperties; } + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } + + public String getBootMode() { return bootMode; } + + public void setBootMode(String bootMode) { this.bootMode = bootMode; } } diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 1ecd48d..e5a3889 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -52,6 +52,7 @@ public interface Host extends StateObject<Status>, Identity, Partition, HAResour return strs; } } + public static final String HOST_UEFI_ENABLE = "host.uefi.enable"; /** * @return name of the machine. diff --git a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java index 977e27e..1abc764 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachineProfile.java @@ -60,6 +60,9 @@ public interface VirtualMachineProfile { public static final Param PxeSeverType = new Param("PxeSeverType"); public static final Param HaTag = new Param("HaTag"); public static final Param HaOperation = new Param("HaOperation"); + public static final Param UefiFlag = new Param("UefiFlag"); + public static final Param BootMode = new Param("BootMode"); + public static final Param BootType = new Param("BootType"); private String name; diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 7c40a7b..3812aa2 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -20,6 +20,7 @@ public interface VmDetailConstants { String KEYBOARD = "keyboard"; String CPU_CORE_PER_SOCKET = "cpu.corespersocket"; String ROOT_DISK_SIZE = "rootdisksize"; + String BOOT_MODE = "boot.mode"; // VMware specific String NIC_ADAPTER = "nicAdapter"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 0482364..2a201db 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -804,6 +804,27 @@ public class ApiConstants { public static final String NODE_ROOT_DISK_SIZE = "noderootdisksize"; public static final String SUPPORTS_HA = "supportsha"; + public static final String BOOT_TYPE ="boottype"; + public static final String BOOT_MODE ="bootmode"; + + public enum BootType { + UEFI, BIOS; + + @Override + public String toString() { + return this.name(); + } + } + + public enum BootMode { + LEGACY, SECURE; + + @Override + public String toString() { + return this.name(); + } + } + public enum HostDetails { all, capacity, events, stats, min; } 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 60b9f31..db315cc 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 @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.vm; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -26,6 +27,8 @@ import java.util.Map; import javax.annotation.Nonnull; +import com.cloud.utils.StringUtils; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ACL; @@ -110,6 +113,12 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") private List<Long> networkIds; + @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]") + private String bootType; + + @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy By default for BIOS") + private String bootMode; + //DataDisk information @ACL @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," @@ -244,6 +253,22 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG return domainId; } + private ApiConstants.BootType getBootType() { + + if (StringUtils.isNotBlank(bootType)) { + try { + String type = bootType.trim().toUpperCase(); + return ApiConstants.BootType.valueOf(type); + } catch (IllegalArgumentException e) { + String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() + + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); + s_logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + public Map<String, String> getDetails() { Map<String, String> customparameterMap = new HashMap<String, String>(); if (details != null && details.size() != 0) { @@ -256,12 +281,35 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG } } } + if(getBootType() != null){ // export to get + if(getBootType() == ApiConstants.BootType.UEFI) { + customparameterMap.put(getBootType().toString(), getBootMode().toString()); + } + } + if (rootdisksize != null && !customparameterMap.containsKey("rootdisksize")) { customparameterMap.put("rootdisksize", rootdisksize.toString()); } return customparameterMap; } + + public ApiConstants.BootMode getBootMode() { + if (StringUtils.isNotBlank(bootMode)) { + try { + String mode = bootMode.trim().toUpperCase(); + return ApiConstants.BootMode.valueOf(mode); + } catch (IllegalArgumentException e) { + String errMesg = "Invalid bootMode " + bootMode + "Specified for vm " + getName() + + " Valid values are: "+ Arrays.toString(ApiConstants.BootMode.values()); + s_logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + + public Map<String, String> getVmOVFProperties() { Map<String, String> map = new HashMap<>(); if (MapUtils.isNotEmpty(vmOvfProperties)) { 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 cd70af1..7b47f8a 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 @@ -244,6 +244,10 @@ public class HostResponse extends BaseResponse { @Param(description = "the admin that annotated this host", since = "4.11") private String username; + @SerializedName("ueficapability") + @Param(description = "true if the host has capability to support UEFI boot") + private Boolean uefiCapabilty; + @Override public String getObjectId() { return this.getId(); @@ -499,6 +503,14 @@ public class HostResponse extends BaseResponse { detailsCopy.remove("username"); detailsCopy.remove("password"); + if(detailsCopy.containsKey(Host.HOST_UEFI_ENABLE)) { + this.setUefiCapabilty(Boolean.parseBoolean((String) detailsCopy.get(Host.HOST_UEFI_ENABLE))); + detailsCopy.remove(Host.HOST_UEFI_ENABLE); + } else { + this.setUefiCapabilty(new Boolean(false)); // in case of existing host which is not scanned for UEFI capability + } + + this.details = detailsCopy; } @@ -668,4 +680,8 @@ public class HostResponse extends BaseResponse { public Boolean getHaHost() { return haHost; } + + public void setUefiCapabilty(Boolean hostCapability) { + this.uefiCapabilty = hostCapability; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index ca3ca87..b8b0189 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -298,6 +298,14 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "OS type id of the vm", since = "4.4") private String osTypeId; + @SerializedName(ApiConstants.BOOT_MODE) + @Param(description = "Guest vm Boot Mode") + private String bootMode; + + @SerializedName(ApiConstants.BOOT_TYPE) + @Param(description = "Guest vm Boot Type") + private String bootType; + public UserVmResponse() { securityGroupList = new LinkedHashSet<SecurityGroupResponse>(); nics = new LinkedHashSet<NicResponse>(); @@ -873,4 +881,13 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co public String getOsTypeId() { return osTypeId; } + + public String getBootType() { return bootType; } + + public void setBootType(String bootType) { this.bootType = bootType; } + + public String getBootMode() { return bootMode; } + + public void setBootMode(String bootMode) { this.bootMode = bootMode; } + } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntity.java b/engine/api/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntity.java index c004514..7b34077 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntity.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntity.java @@ -171,4 +171,10 @@ public interface VirtualMachineEntity extends CloudStackEntity { * @param netowrk network to disconnect from */ void disconnectFrom(NetworkEntity netowrk, short nicId); + + /** + * passing additional params of deployment associated with the virtual machine + */ + void setParamsToEntity(Map<VirtualMachineProfile.Param, Object> params); + } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 7765611..8817efb 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1064,6 +1064,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, owner, params); + s_logger.info(" Uefi params " + "UefiFlag: " + params.get(VirtualMachineProfile.Param.UefiFlag) + + " Boot Type: " + params.get(VirtualMachineProfile.Param.BootType) + + " Boot Mode: " + params.get(VirtualMachineProfile.Param.BootMode) + ); DeployDestination dest = null; try { dest = _dpMgr.planDeployment(vmProfile, plan, avoids, planner); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index ef9c44a..567675a 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMEntityDao; import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -148,6 +149,15 @@ public class VMEntityManagerImpl implements VMEntityManager { VMInstanceVO vm = _vmDao.findByUuid(vmEntityVO.getUuid()); VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm); vmProfile.setServiceOffering(_serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId())); + if (MapUtils.isNotEmpty(vmEntityVO.getDetails()) && + vmEntityVO.getDetails().containsKey(VirtualMachineProfile.Param.UefiFlag.getName()) && + "yes".equalsIgnoreCase(vmEntityVO.getDetails().get(VirtualMachineProfile.Param.UefiFlag.getName()))) + { + Map<String, String> details = vmEntityVO.getDetails(); + vmProfile.getParameters().put(VirtualMachineProfile.Param.BootType, details.get(VirtualMachineProfile.Param.BootType.getName())); + vmProfile.getParameters().put(VirtualMachineProfile.Param.BootMode, details.get(VirtualMachineProfile.Param.BootMode.getName())); + vmProfile.getParameters().put(VirtualMachineProfile.Param.UefiFlag, details.get(VirtualMachineProfile.Param.UefiFlag.getName())); + } DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodIdToDeployIn(), null, null, null, null); if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) { plan = diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java index 598e619..3e5f910 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java @@ -20,9 +20,11 @@ import java.lang.reflect.Method; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.HashMap; import javax.inject.Inject; +import org.apache.commons.collections.MapUtils; import org.springframework.stereotype.Component; import org.apache.cloudstack.engine.cloud.entity.api.db.VMEntityVO; @@ -269,4 +271,22 @@ public class VirtualMachineEntityImpl implements VirtualMachineEntity { } + @Override + public void setParamsToEntity(Map<VirtualMachineProfile.Param, Object> map) { + if (MapUtils.isNotEmpty(map)) { + if (this.vmEntityVO != null) { + Map<String, String> details = this.vmEntityVO.getDetails(); + if (details == null) { + details = new HashMap<String, String>(); + } + for (Map.Entry<VirtualMachineProfile.Param, Object> entry : map.entrySet()) { + if (null != entry && null != entry.getValue() && null != entry.getKey()) { + details.put(entry.getKey().getName(), entry.getValue().toString()); + } + } + this.vmEntityVO.setDetails(details); + } + } + + } } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java index 34b8963..ced19ce 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -112,5 +112,7 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat List<HostVO> listAllHostsUpByZoneAndHypervisor(long zoneId, HypervisorType hypervisorType); + List<HostVO> listByHostCapability(Host.Type type, Long clusterId, Long podId, long dcId, String hostCapabilty); + List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType); } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index 20d817c..ec4573f 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -43,6 +43,7 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.host.Host; +import com.cloud.host.DetailVO; import com.cloud.host.Host.Type; import com.cloud.host.HostTagVO; import com.cloud.host.HostVO; @@ -1222,6 +1223,39 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao } @Override + public List<HostVO> listByHostCapability(Type type, Long clusterId, Long podId, long dcId, String hostCapabilty) { + SearchBuilder<DetailVO> hostCapabilitySearch = _detailsDao.createSearchBuilder(); + DetailVO tagEntity = hostCapabilitySearch.entity(); + hostCapabilitySearch.and("capability", tagEntity.getName(), SearchCriteria.Op.EQ); + hostCapabilitySearch.and("value", tagEntity.getValue(), SearchCriteria.Op.EQ); + + SearchBuilder<HostVO> hostSearch = createSearchBuilder(); + HostVO entity = hostSearch.entity(); + hostSearch.and("type", entity.getType(), SearchCriteria.Op.EQ); + hostSearch.and("pod", entity.getPodId(), SearchCriteria.Op.EQ); + hostSearch.and("dc", entity.getDataCenterId(), SearchCriteria.Op.EQ); + hostSearch.and("cluster", entity.getClusterId(), SearchCriteria.Op.EQ); + hostSearch.and("status", entity.getStatus(), SearchCriteria.Op.EQ); + hostSearch.and("resourceState", entity.getResourceState(), SearchCriteria.Op.EQ); + hostSearch.join("hostCapabilitySearch", hostCapabilitySearch, entity.getId(), tagEntity.getHostId(), JoinBuilder.JoinType.INNER); + + SearchCriteria<HostVO> sc = hostSearch.create(); + sc.setJoinParameters("hostCapabilitySearch", "value", Boolean.toString(true)); + sc.setJoinParameters("hostCapabilitySearch", "capability", hostCapabilty); + sc.setParameters("type", type.toString()); + if (podId != null) { + sc.setParameters("pod", podId); + } + if (clusterId != null) { + sc.setParameters("cluster", clusterId); + } + sc.setParameters("dc", dcId); + sc.setParameters("status", Status.Up.toString()); + sc.setParameters("resourceState", ResourceState.Enabled.toString()); + + return listBy(sc); + } + public List<HostVO> listByClusterAndHypervisorType(long clusterId, HypervisorType hypervisorType) { SearchCriteria<HostVO> sc = ClusterHypervisorSearch.create(); sc.setParameters("clusterId", clusterId); diff --git a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java index 79cb1b4..1a3aed0 100644 --- a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java +++ b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -73,6 +73,7 @@ import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.host.dao.HostTagsDao; import com.cloud.resource.ResourceManager; import com.cloud.service.ServiceOfferingVO; @@ -574,6 +575,10 @@ public class ImplicitPlannerTest { } @Bean + public HostDetailsDao hostDetailsDao() { return Mockito.mock(HostDetailsDao.class); } + + + @Bean public ClusterDetailsDao clusterDetailsDao() { return Mockito.mock(ClusterDetailsDao.class); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 1be6785..fd9075e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -224,6 +224,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private String _dcId; private String _pod; private String _clusterId; + private final Properties _uefiProperties = new Properties(); private long _hvVersion; private Duration _timeout; @@ -497,7 +498,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public StorageSubsystemCommandHandler getStorageHandler() { return storageHandler; } - private static final class KeyValueInterpreter extends OutputInterpreter { private final Map<String, String> map = new HashMap<String, String>(); @@ -628,6 +628,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (!success) { return false; } + try { + loadUefiProperties(); + } catch (FileNotFoundException e) { + s_logger.error("uefi properties file not found due to: " + e.getLocalizedMessage()); + } _storage = new JavaStorageLayer(); _storage.configure("StorageLayer", params); @@ -1150,6 +1155,31 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return true; } + private void loadUefiProperties() throws FileNotFoundException { + + if (_uefiProperties != null && _uefiProperties.getProperty("guest.loader.legacy") != null) { + return; + } + final File file = PropertiesUtil.findConfigFile("uefi.properties"); + if (file == null) { + throw new FileNotFoundException("Unable to find file uefi.properties."); + } + + s_logger.info("uefi.properties file found at " + file.getAbsolutePath()); + try { + PropertiesUtil.loadFromFile(_uefiProperties, file); + s_logger.info("guest.nvram.template.legacy = " + _uefiProperties.getProperty("guest.nvram.template.legacy")); + s_logger.info("guest.loader.legacy = " + _uefiProperties.getProperty("guest.loader.legacy")); + s_logger.info("guest.nvram.template.secure = " + _uefiProperties.getProperty("guest.nvram.template.secure")); + s_logger.info("guest.loader.secure =" + _uefiProperties.getProperty("guest.loader.secure")); + s_logger.info("guest.nvram.path = " + _uefiProperties.getProperty("guest.nvram.path")); + } catch (final FileNotFoundException ex) { + throw new CloudRuntimeException("Cannot find the file: " + file.getAbsolutePath(), ex); + } catch (final IOException ex) { + throw new CloudRuntimeException("IOException in reading " + file.getAbsolutePath(), ex); + } + } + protected void configureDiskActivityChecks(final Map<String, Object> params) { _diskActivityCheckEnabled = Boolean.parseBoolean((String)params.get("vm.diskactivity.checkenabled")); if (_diskActivityCheckEnabled) { @@ -2106,6 +2136,18 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv vm.setDomDescription(vmTO.getOs()); vm.setPlatformEmulator(vmTO.getPlatformEmulator()); + Map<String, String> customParams = vmTO.getDetails(); + boolean isUefiEnabled = false; + boolean isSecureBoot = false; + String bootMode =null; + if (MapUtils.isNotEmpty(customParams) && customParams.containsKey(GuestDef.BootType.UEFI.toString())) { + isUefiEnabled = true; + bootMode = customParams.get(GuestDef.BootType.UEFI.toString()); + if (StringUtils.isNotBlank(bootMode) && "secure".equalsIgnoreCase(bootMode)) { + isSecureBoot = true; + } + } + Map<String, String> extraConfig = vmTO.getExtraConfig(); if (dpdkSupport && (!extraConfig.containsKey(DpdkHelper.DPDK_NUMA) || !extraConfig.containsKey(DpdkHelper.DPDK_HUGE_PAGES))) { s_logger.info("DPDK is enabled but it needs extra configurations for CPU NUMA and Huge Pages for VM deployment"); @@ -2125,11 +2167,44 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } guest.setGuestArch(_guestCpuArch != null ? _guestCpuArch : vmTO.getArch()); guest.setMachineType(_guestCpuArch != null && _guestCpuArch.equals("aarch64") ? "virt" : "pc"); + guest.setBootType(GuestDef.BootType.BIOS); + if (MapUtils.isNotEmpty(customParams) && customParams.containsKey(GuestDef.BootType.UEFI.toString())) { + guest.setBootType(GuestDef.BootType.UEFI); + guest.setBootMode(GuestDef.BootMode.LEGACY); + if (StringUtils.isNotBlank(customParams.get(GuestDef.BootType.UEFI.toString())) && "secure".equalsIgnoreCase(customParams.get(GuestDef.BootType.UEFI.toString()))) { + guest.setMachineType("q35"); + guest.setBootMode(GuestDef.BootMode.SECURE); // setting to secure mode + } + } guest.setUuid(uuid); guest.setBootOrder(GuestDef.BootOrder.CDROM); guest.setBootOrder(GuestDef.BootOrder.HARDISK); - vm.addComp(guest); + if (isUefiEnabled) { + if (_uefiProperties.getProperty(GuestDef.GUEST_LOADER_SECURE) != null && "secure".equalsIgnoreCase(bootMode)) { + guest.setLoader(_uefiProperties.getProperty(GuestDef.GUEST_LOADER_SECURE)); + } + + if (_uefiProperties.getProperty(GuestDef.GUEST_LOADER_LEGACY) != null && "legacy".equalsIgnoreCase(bootMode)) { + guest.setLoader(_uefiProperties.getProperty(GuestDef.GUEST_LOADER_LEGACY)); + } + + if (_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_PATH) != null) { + guest.setNvram(_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_PATH)); + } + + if (isSecureBoot) { + if (_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_SECURE) != null && "secure".equalsIgnoreCase(bootMode)) { + guest.setNvramTemplate(_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_SECURE)); + } + } else { + if (_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_LEGACY) != null) { + guest.setNvramTemplate(_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_LEGACY)); + } + } + } + + vm.addComp(guest); final GuestResourceDef grd = new GuestResourceDef(); @@ -2189,6 +2264,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv features.addFeatures("pae"); features.addFeatures("apic"); features.addFeatures("acpi"); + if (isUefiEnabled && isSecureMode(customParams.get(GuestDef.BootType.UEFI.toString()))) { + features.addFeatures("smm"); + } //KVM hyperv enlightenment features based on OS Type enlightenWindowsVm(vmTO, features); @@ -2329,7 +2407,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } public void createVbd(final Connect conn, final VirtualMachineTO vmSpec, final String vmName, final LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException { + final Map<String, String> details = vmSpec.getDetails(); final List<DiskTO> disks = Arrays.asList(vmSpec.getDisks()); + boolean isSecureBoot = false; + boolean isWindowsTemplate = false; Collections.sort(disks, new Comparator<DiskTO>() { @Override public int compare(final DiskTO arg0, final DiskTO arg1) { @@ -2337,6 +2418,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } }); + if (MapUtils.isNotEmpty(details) && details.containsKey(GuestDef.BootType.UEFI.toString())) { + isSecureBoot = isSecureMode(details.get(GuestDef.BootType.UEFI.toString())); + } + if (vmSpec.getOs().toLowerCase().contains("window")) { + isWindowsTemplate =true; + } for (final DiskTO volume : disks) { KVMPhysicalDisk physicalDisk = null; KVMStoragePool pool = null; @@ -2397,8 +2484,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv int devId = volume.getDiskSeq().intValue(); if (volume.getType() == Volume.Type.ISO) { if (volPath == null) { - /* Add iso as placeholder */ - disk.defISODisk(null, devId); + if (isSecureBoot) { + disk.defISODisk(null, devId,isSecureBoot,isWindowsTemplate); + } else { + /* Add iso as placeholder */ + disk.defISODisk(null, devId); + } } else { disk.defISODisk(volPath, devId); } @@ -2436,7 +2527,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (volume.getType() == Volume.Type.DATADISK) { disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData, DiskDef.DiskFmtType.QCOW2); } else { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.DiskFmtType.QCOW2); + if (isSecureBoot) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, DiskDef.DiskFmtType.QCOW2, isWindowsTemplate); + } else { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, diskBusType, DiskDef.DiskFmtType.QCOW2); + } } } @@ -4052,4 +4147,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } return true; } + + public boolean isSecureMode(String bootMode) { + if (StringUtils.isNotBlank(bootMode) && "secure".equalsIgnoreCase(bootMode)) { + return true; + } + + return false; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index bfbb4d5..e2d506c 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -57,7 +57,39 @@ public class LibvirtVMDef { } } + enum BootType { + UEFI("UEFI"), BIOS("BIOS"); + + String _type; + + BootType(String type) { + _type = type; + } + + @Override + public String toString() { + return _type; + } + } + + enum BootMode { + LEGACY("LEGACY"), SECURE("SECURE"); + + String _mode; + + BootMode(String mode) { + _mode = mode; + } + + @Override + public String toString() { + return _mode; + } + } + private GuestType _type; + private BootType _boottype; + private BootMode _bootmode; private String _arch; private String _loader; private String _kernel; @@ -67,6 +99,14 @@ public class LibvirtVMDef { private String _uuid; private final List<BootOrder> _bootdevs = new ArrayList<BootOrder>(); private String _machine; + private String _nvram; + private String _nvramTemplate; + + public static final String GUEST_LOADER_SECURE = "guest.loader.secure"; + public static final String GUEST_LOADER_LEGACY = "guest.loader.legacy"; + public static final String GUEST_NVRAM_PATH = "guest.nvram.path"; + public static final String GUEST_NVRAM_TEMPLATE_SECURE = "guest.nvram.template.secure"; + public static final String GUEST_NVRAM_TEMPLATE_LEGACY = "guest.nvram.template.legacy"; public void setGuestType(GuestType type) { _type = type; @@ -76,6 +116,10 @@ public class LibvirtVMDef { return _type; } + public void setNvram(String nvram) { _nvram = nvram; } + + public void setNvramTemplate(String nvramTemplate) { _nvramTemplate = nvramTemplate; } + public void setGuestArch(String arch) { _arch = arch; } @@ -103,6 +147,22 @@ public class LibvirtVMDef { _uuid = uuid; } + public BootType getBootType() { + return _boottype; + } + + public void setBootType(BootType boottype) { + this._boottype = boottype; + } + + public BootMode getBootMode() { + return _bootmode; + } + + public void setBootMode(BootMode bootmode) { + this._bootmode = bootmode; + } + @Override public String toString() { if (_type == GuestType.KVM) { @@ -128,6 +188,24 @@ public class LibvirtVMDef { if (_arch != null && _arch.equals("aarch64")) { guestDef.append("<loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader>\n"); } + if (_loader != null) { + if (_bootmode == BootMode.LEGACY) { + guestDef.append("<loader readonly='yes' secure='no' type='pflash'>" + _loader + "</loader>\n"); + } else if (_bootmode == BootMode.SECURE) { + guestDef.append("<loader readonly='yes' secure='yes' type='pflash'>" + _loader + "</loader>\n"); + } + } + if (_nvram != null) { + guestDef.append("<nvram "); + if (_nvramTemplate != null) { + guestDef.append("template='" + _nvramTemplate + "'>"); + } else { + guestDef.append(">"); + } + + guestDef.append(_nvram); + guestDef.append(_uuid + ".fd</nvram>"); + } if (!_bootdevs.isEmpty()) { for (BootOrder bo : _bootdevs) { guestDef.append("<boot dev='" + bo + "'/>\n"); @@ -276,7 +354,11 @@ public class LibvirtVMDef { StringBuilder feaBuilder = new StringBuilder(); feaBuilder.append("<features>\n"); for (String feature : _features) { - feaBuilder.append("<" + feature + "/>\n"); + if (feature.equalsIgnoreCase("smm")) { + feaBuilder.append("<" + feature + " state=\'on\' " + "/>\n"); + } else { + feaBuilder.append("<" + feature + "/>\n"); + } } if (hyperVEnlightenmentFeatureDef != null) { String hpervF = hyperVEnlightenmentFeatureDef.toString(); @@ -508,7 +590,7 @@ public class LibvirtVMDef { } public enum DiskBus { - IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML("uml"), FDC("fdc"); + IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML("uml"), FDC("fdc"), SATA("sata"); String _bus; DiskBus(String bus) { @@ -633,13 +715,17 @@ public class LibvirtVMDef { return "sd" + getDevLabelSuffix(devId); } else if (bus == DiskBus.VIRTIO) { return "vd" + getDevLabelSuffix(devId); + } else if (bus == DiskBus.SATA){ + if (!forIso) { + return "sda"; + } } if (forIso) { devId --; } else if(devId >= 2) { devId += 2; } - return "hd" + getDevLabelSuffix(devId); + return (DiskBus.SATA == bus) ? "sdb" : "hd" + getDevLabelSuffix(devId); } @@ -671,6 +757,23 @@ public class LibvirtVMDef { } + public void defFileBasedDisk(String filePath, int devId, DiskFmtType diskFmtType,boolean isWindowsOS) { + + _diskType = DiskType.FILE; + _deviceType = DeviceType.DISK; + _diskCacheMode = DiskCacheMode.NONE; + _sourcePath = filePath; + _diskFmtType = diskFmtType; + + if (isWindowsOS) { + _diskLabel = getDevLabel(devId, DiskBus.SATA, false); // Windows Secure VM + _bus = DiskBus.SATA; + } else { + _diskLabel = getDevLabel(devId, DiskBus.VIRTIO, false); // Linux Secure VM + _bus = DiskBus.VIRTIO; + } + } + public void defISODisk(String volPath) { _diskType = DiskType.FILE; _deviceType = DeviceType.CDROM; @@ -695,6 +798,26 @@ public class LibvirtVMDef { } } + public void defISODisk(String volPath, Integer devId,boolean isSecure, boolean isWindowOs) { + if (!isSecure) { + defISODisk(volPath, devId); + } else { + _diskType = DiskType.FILE; + _deviceType = DeviceType.CDROM; + _sourcePath = volPath; + if (isWindowOs) { + _diskLabel = getDevLabel(devId, DiskBus.SATA, true); + _bus = DiskBus.SATA; + } else { + _diskLabel = getDevLabel(devId, DiskBus.SCSI, true); + _bus = DiskBus.SCSI; + } + _diskFmtType = DiskFmtType.RAW; + _diskCacheMode = DiskCacheMode.NONE; + + } + } + public void defBlockBasedDisk(String diskName, int devId, DiskBus bus) { _diskType = DiskType.BLOCK; _deviceType = DeviceType.DISK; diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index 39c1e66..4e8acad 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -294,6 +294,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co } } + details.put(VmDetailConstants.BOOT_MODE, to.getBootType()); String diskDeviceType = details.get(VmDetailConstants.ROOT_DISK_CONTROLLER); if (userVm) { if (diskDeviceType == null) { diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java index 4b2f830..1102f9d 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareServerDiscoverer.java @@ -45,6 +45,7 @@ import com.cloud.exception.DiscoveredWithErrorException; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceInUseException; +import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -367,6 +368,10 @@ public class VmwareServerDiscoverer extends DiscovererBase implements Discoverer details.put("url", hostMo.getHostName()); details.put("username", username); details.put("password", password); + boolean uefiLegacySupported = hostMo.isUefiLegacySupported(); + if (uefiLegacySupported) { + details.put(Host.HOST_UEFI_ENABLE, "true"); + } String guid = morHost.getType() + ":" + morHost.getValue() + "@" + url.getHost(); details.put("guid", guid); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 7624f95..5a1459d 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -318,6 +318,7 @@ import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; import com.vmware.vim25.VirtualEthernetCardOpaqueNetworkBackingInfo; import com.vmware.vim25.VirtualIDEController; import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualMachineBootOptions; import com.vmware.vim25.VirtualMachineFileInfo; import com.vmware.vim25.VirtualMachineFileLayoutEx; import com.vmware.vim25.VirtualMachineFileLayoutExFileInfo; @@ -1723,6 +1724,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa String dataDiskController = vmSpec.getDetails().get(VmDetailConstants.DATA_DISK_CONTROLLER); String rootDiskController = vmSpec.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER); DiskTO rootDiskTO = null; + String bootMode = ApiConstants.BootType.BIOS.toString(); + if (vmSpec.getDetails().containsKey(VmDetailConstants.BOOT_MODE)) { + bootMode = vmSpec.getDetails().get(VmDetailConstants.BOOT_MODE); + } + // If root disk controller is scsi, then data disk controller would also be scsi instead of using 'osdefault' // This helps avoid mix of different scsi subtype controllers in instance. if (DiskControllerType.osdefault == DiskControllerType.getType(dataDiskController) && DiskControllerType.lsilogic == DiskControllerType.getType(rootDiskController)) { @@ -2280,6 +2286,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + if (!bootMode.equalsIgnoreCase(ApiConstants.BootType.BIOS.toString())) { + vmConfigSpec.setFirmware("efi"); + if (vmSpec.getDetails().containsKey(ApiConstants.BootType.UEFI.toString()) && "secure".equalsIgnoreCase(vmSpec.getDetails().get(ApiConstants.BootType.UEFI.toString()))) { + VirtualMachineBootOptions bootOptions = new VirtualMachineBootOptions(); + bootOptions.setEfiSecureBootEnabled(true); + vmConfigSpec.setBootOptions(bootOptions); + } + } + + // // Configure VM // @@ -2772,6 +2788,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa private static void configCustomExtraOption(List<OptionValue> extraOptions, VirtualMachineTO vmSpec) { // we no longer to validation anymore for (Map.Entry<String, String> entry : vmSpec.getDetails().entrySet()) { + if (entry.getKey().equalsIgnoreCase(VmDetailConstants.BOOT_MODE)) { + continue; + } OptionValue newVal = new OptionValue(); newVal.setKey(entry.getKey()); newVal.setValue(entry.getValue()); diff --git a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java index 186c654..aab9510 100644 --- a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java +++ b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java @@ -61,6 +61,9 @@ import com.cloud.utils.component.AdapterBase; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.dao.UserVmDetailsDao; + /** * An allocator that tries to find a fit on a computing host. This allocator does not care whether or not the host supports routing. @@ -92,6 +95,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { CapacityManager _capacityMgr; @Inject CapacityDao _capacityDao; + @Inject + UserVmDetailsDao _userVmDetailsDao; boolean _checkHvm = true; protected String _allocationAlgorithm = "random"; @@ -112,6 +117,16 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate(); Account account = vmProfile.getOwner(); + boolean isVMDeployedWithUefi = false; + UserVmDetailVO userVmDetailVO = _userVmDetailsDao.findDetail(vmProfile.getId(), "UEFI"); + if(userVmDetailVO != null){ + if ("secure".equalsIgnoreCase(userVmDetailVO.getValue()) || "legacy".equalsIgnoreCase(userVmDetailVO.getValue())) { + isVMDeployedWithUefi = true; + } + } + s_logger.info(" Guest VM is requested with Cusotm[UEFI] Boot Type "+ isVMDeployedWithUefi); + + if (type == Host.Type.Storage) { // FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not return new ArrayList<Host>(); @@ -123,11 +138,20 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { String hostTagOnOffering = offering.getHostTag(); String hostTagOnTemplate = template.getTemplateTag(); + String hostTagUefi = "UEFI"; boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false; boolean hasTemplateTag = hostTagOnTemplate != null ? true : false; List<HostVO> clusterHosts = new ArrayList<HostVO>(); + List<HostVO> hostsMatchingUefiTag = new ArrayList<HostVO>(); + if(isVMDeployedWithUefi){ + hostsMatchingUefiTag = _hostDao.listByHostCapability(type, clusterId, podId, dcId, Host.HOST_UEFI_ENABLE); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Hosts with tag '" + hostTagUefi + "' are:" + hostsMatchingUefiTag); + } + } + String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); if (haVmTag != null) { @@ -175,6 +199,10 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } } + if (isVMDeployedWithUefi) { + clusterHosts.retainAll(hostsMatchingUefiTag); + } + // add all hosts that we are not considering to the avoid list List<HostVO> allhostsInCluster = _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, null); allhostsInCluster.removeAll(clusterHosts); diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index a612829..21c6786 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -321,6 +321,15 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo (UserVmManager.DisplayVMOVFProperties.value() && userVmDetailVO.getName().startsWith(ApiConstants.OVF_PROPERTIES))) { resourceDetails.put(userVmDetailVO.getName(), userVmDetailVO.getValue()); } + if ((ApiConstants.BootType.UEFI.toString()).equalsIgnoreCase(userVmDetailVO.getName())) { + userVmResponse.setBootType("Uefi"); + userVmResponse.setBootMode(userVmDetailVO.getValue().toLowerCase()); + + } + } + if (vmDetails.size() == 0) { + userVmResponse.setBootType("Bios"); + userVmResponse.setBootMode("legacy"); } // Remove blacklisted settings if user is not admin if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 1315cdf..0c1aab3 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -30,10 +30,12 @@ import java.util.TreeSet; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.StringUtils; import com.cloud.utils.db.Filter; import com.cloud.utils.fsm.StateMachine2; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.log4j.Logger; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -271,6 +273,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> { } String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); + String uefiFlag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag); if (plan.getHostId() != null && haVmTag == null) { Long hostIdSpecified = plan.getHostId(); @@ -278,6 +281,14 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> { s_logger.debug("DeploymentPlan has host_id specified, choosing this host and making no checks on this host: " + hostIdSpecified); } HostVO host = _hostDao.findById(hostIdSpecified); + if (host != null && StringUtils.isNotBlank(uefiFlag) && "yes".equalsIgnoreCase(uefiFlag)) { + _hostDao.loadDetails(host); + if (MapUtils.isNotEmpty(host.getDetails()) && host.getDetails().containsKey(Host.HOST_UEFI_ENABLE) && "false".equalsIgnoreCase(host.getDetails().get(Host.HOST_UEFI_ENABLE))) { + s_logger.debug("Cannot deploy to specified host as host does n't support uefi vm deployment, returning."); + return null; + + } + } if (host == null) { s_logger.debug("The specified host cannot be found"); } else if (avoids.shouldAvoid(host)) { diff --git a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index 5760e24..88f6b95 100644 --- a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java @@ -68,12 +68,15 @@ import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.host.dao.HostDetailsDao; public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPlanner, Configurable, DeploymentPlanner { private static final Logger s_logger = Logger.getLogger(FirstFitPlanner.class); @Inject protected HostDao hostDao; @Inject + protected HostDetailsDao hostDetailsDao; + @Inject protected DataCenterDao dcDao; @Inject protected HostPodDao podDao; @@ -187,8 +190,16 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla if (clusterList != null && !clusterList.isEmpty()) { ServiceOffering offering = vmProfile.getServiceOffering(); + boolean nonUefiVMDeploy =false; + if (vmProfile.getParameters().containsKey(VirtualMachineProfile.Param.BootType)) { + if (vmProfile.getParameters().get(VirtualMachineProfile.Param.BootType).toString().equalsIgnoreCase("BIOS")) { + nonUefiVMDeploy = true; + + } + + } // In case of non-GPU VMs, protect GPU enabled Hosts and prefer VM deployment on non-GPU Hosts. - if ((serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString()) == null) && !(hostGpuGroupsDao.listHostIds().isEmpty())) { + if (((serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString()) == null) && !(hostGpuGroupsDao.listHostIds().isEmpty())) || nonUefiVMDeploy) { int requiredCpu = offering.getCpu() * offering.getSpeed(); long requiredRam = offering.getRamSize() * 1024L * 1024L; reorderClustersBasedOnImplicitTags(clusterList, requiredCpu, requiredRam); @@ -205,7 +216,8 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla List<Long> 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<Long>() { @@ -218,6 +230,19 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla }); } + private Long getHostsByCapability(List<Long> hostList, String hostCapability) { + for (Long host : hostList) { //TODO: Fix this in single query instead of polling request for each Host + Map<String, String> details = hostDetailsDao.findDetails(host); + if (details.containsKey(Host.HOST_UEFI_ENABLE)) { + if (details.get(Host.HOST_UEFI_ENABLE).equalsIgnoreCase("Yes")) { + return new Long(1); + } + + } + } + return new Long(0); + } + private List<Long> scanPodsForDestination(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) { ServiceOffering offering = vmProfile.getServiceOffering(); diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 9e55506..0c3263a 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -22,8 +22,6 @@ import java.util.UUID; import javax.inject.Inject; -import com.cloud.network.dao.NetworkDetailVO; -import com.cloud.network.dao.NetworkDetailsDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -37,6 +35,8 @@ import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.gpu.GPU; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDetailVO; +import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkVO; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; @@ -47,6 +47,7 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.StoragePool; import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.component.AdapterBase; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; @@ -171,6 +172,11 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis offering.getRamSize() * 1024l * 1024l, null, null, vm.isHaEnabled(), vm.limitCpuUse(), vm.getVncPassword()); to.setBootArgs(vmProfile.getBootArgs()); + String bootType = (String)vmProfile.getParameter(new VirtualMachineProfile.Param("BootType")); + if (StringUtils.isNotBlank(bootType)) { + to.setBootType(bootType); + } + List<NicProfile> nicProfiles = vmProfile.getNics(); NicTO[] nics = new NicTO[nicProfiles.size()]; int i = 0; diff --git a/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java b/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java index e232d6a..904a488 100644 --- a/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java +++ b/server/src/main/java/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java @@ -202,6 +202,7 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements @Override public Map<? extends ServerResource, Map<String, String>> find(long dcId, Long podId, Long clusterId, URI uri, String username, String password, List<String> hostTags) throws DiscoveryException { + boolean isUefiSupported = false; ClusterVO cluster = _clusterDao.findById(clusterId); if (cluster == null || cluster.getHypervisorType() != getHypervisorType()) { @@ -256,6 +257,11 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements return null; } + if (SSHCmdHelper.sshExecuteCmd(sshConnection, "rpm -qa | grep -i ovmf", 3)) { + s_logger.debug("It's UEFI enabled KVM machine"); + isUefiSupported = true; + } + List<PhysicalNetworkSetupInfo> netInfos = _networkMgr.getPhysicalNetworkInfo(dcId, getHypervisorType()); String kvmPrivateNic = null; String kvmPublicNic = null; @@ -338,6 +344,7 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements Map<String, String> hostDetails = connectedHost.getDetails(); hostDetails.put("password", password); hostDetails.put("username", username); + hostDetails.put(Host.HOST_UEFI_ENABLE, isUefiSupported == true ? Boolean.toString(true) : Boolean.toString(false)); _hostDao.saveDetails(connectedHost); return resources; } catch (DiscoveredWithErrorException e) { diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index ff29f1d..a3e9bb6 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -709,6 +709,8 @@ import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.dao.UserVmDetailsDao; public class ManagementServerImpl extends ManagerBase implements ManagementServer, Configurable { public static final Logger s_logger = Logger.getLogger(ManagementServerImpl.class.getName()); @@ -728,6 +730,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private ClusterDao _clusterDao; @Inject + private UserVmDetailsDao _UserVmDetailsDao; + @Inject private SecondaryStorageVmDao _secStorageVmDao; @Inject public EventDao _eventDao; @@ -1191,6 +1195,16 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw ex; } + UserVmDetailVO userVmDetailVO = _UserVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString()); + if (userVmDetailVO != null) { + s_logger.info(" Live Migration of UEFI enabled VM : " + vm.getInstanceName() + " is not supported"); + if ("legacy".equalsIgnoreCase(userVmDetailVO.getValue()) || "secure".equalsIgnoreCase(userVmDetailVO.getValue())) { + // Return empty list. + return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(new Pair<List<? extends Host>, + Integer>(new ArrayList<HostVO>(), new Integer(0)), new ArrayList<Host>(), new HashMap<Host, Boolean>()); + } + } + if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { s_logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName() + " is not supported"); // Return empty list. diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 3f28f12..b88e250 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3963,6 +3963,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } else { vm.setDetail(key, customParameters.get(key)); } + + if (key.equalsIgnoreCase(ApiConstants.BootType.UEFI.toString())) { + vm.setDetail(key, customParameters.get(key)); + continue; + } } vm.setDetail(VmDetailConstants.DEPLOY_VM, "true"); @@ -4282,13 +4287,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Long podId = null; Long clusterId = null; Long hostId = cmd.getHostId(); + Map<VirtualMachineProfile.Param, Object> additonalParams = null; Map<Long, DiskOffering> diskOfferingMap = cmd.getDataDiskTemplateToDiskOfferingMap(); if (cmd instanceof DeployVMCmdByAdmin) { DeployVMCmdByAdmin adminCmd = (DeployVMCmdByAdmin)cmd; podId = adminCmd.getPodId(); clusterId = adminCmd.getClusterId(); } - return startVirtualMachine(vmId, podId, clusterId, hostId, diskOfferingMap, null, cmd.getDeploymentPlanner()); + if (MapUtils.isNotEmpty(cmd.getDetails()) && cmd.getDetails().containsKey(ApiConstants.BootType.UEFI.toString())) { + additonalParams = new HashMap<VirtualMachineProfile.Param, Object>(); + Map<String, String> map = cmd.getDetails(); + additonalParams.put(VirtualMachineProfile.Param.UefiFlag, "Yes"); + additonalParams.put(VirtualMachineProfile.Param.BootType, ApiConstants.BootType.UEFI.toString()); + additonalParams.put(VirtualMachineProfile.Param.BootMode, map.get(ApiConstants.BootType.UEFI.toString())); + } + return startVirtualMachine(vmId, podId, clusterId, hostId, diskOfferingMap, additonalParams, cmd.getDeploymentPlanner()); } private UserVm startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, Map<Long, DiskOffering> diskOfferingMap, Map<VirtualMachineProfile.Param, Object> additonalParams, String deploymentPlannerToUse) @@ -4747,6 +4760,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); } } + vmEntity.setParamsToEntity(additionalParams); String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); diff --git a/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java index 1d1ab89..e73c0c6 100644 --- a/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -102,6 +102,7 @@ import com.cloud.utils.component.ComponentContext; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.host.dao.HostDetailsDao; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class) @@ -285,6 +286,12 @@ public class DeploymentPlanningManagerImplTest { } @Bean + public HostDetailsDao hostDetailsDao() { + return Mockito.mock(HostDetailsDao.class); + } + + + @Bean public ClusterDetailsDao clusterDetailsDao() { return Mockito.mock(ClusterDetailsDao.class); } diff --git a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java index 85463de..41deea2 100644 --- a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java +++ b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java @@ -95,6 +95,7 @@ import com.cloud.utils.component.ComponentContext; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.host.dao.HostDetailsDao; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class) @@ -109,6 +110,8 @@ public class FirstFitPlannerTest { @Inject UserVmDao vmDao; @Inject + HostDetailsDao hostDetailsDao; + @Inject UserVmDetailsDao vmDetailsDao; @Inject ConfigurationDao configDao; @@ -356,6 +359,9 @@ public class FirstFitPlannerTest { } @Bean + public HostDetailsDao hostDetailsDao() { return Mockito.mock(HostDetailsDao.class); } + + @Bean public HostGpuGroupsDao hostGpuGroupsDao() { return Mockito.mock(HostGpuGroupsDao.class); } diff --git a/ui/index.html b/ui/index.html index 7d24838..ef7a461 100644 --- a/ui/index.html +++ b/ui/index.html @@ -480,6 +480,31 @@ </div> </div> + <!-- UEFI Boot --> + <div class="select" odd> + <div class="name"> + <span><translate key="label.vm.boottype" /></span> + </div> + <div class="value"> + <select name="customboot" id="customboot"> + <option value="BIOS">BIOS</option> + <option value="UEFI">UEFI</option> + </select> + </div> + </div> + + <div class="select field hide-if-unselected"> + <div class="name"> + <span><translate key="label.vm.bootmode" /></span> + </div> + <div class="value"> + <select name="bootmode" id="bootmode"> + <option value="LEGACY">LEGACY</option> + </select> + </div> + </div> + + <!-- Zone --> <div class="select"> <div class="name"> diff --git a/ui/l10n/en.js b/ui/l10n/en.js index d01b622..cb967be 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -893,6 +893,7 @@ var dictionary = { "label.host.name":"Host Name", "label.host.tag":"Host Tag", "label.host.tags":"Host Tags", +"label.host.ueficapability":"UEFI Supported", "label.hosts":"Hosts", "label.hourly":"Hourly", "label.hvm":"HVM", @@ -990,6 +991,8 @@ var dictionary = { "label.keep.colon":"Keep:", "label.key":"Key", "label.keyboard.language":"Keyboard language", +"label.vm.boottype":"Boot Type", +"label.vm.bootmode":"Boot Mode", "label.keyboard.type":"Keyboard type", "label.kubernetes.cluster":"Kubernetes cluster", "label.kubernetes.cluster.details":"Kubernetes cluster details", diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index 04cceaa..cd95913 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -1399,6 +1399,18 @@ keyboard : keyboard }); } + var boottype = args.data.customboot; + if (boottype != null && boottype.length > 0) { + $.extend(deployVmData, { + boottype : boottype + }); + } + var bootmode = args.data.bootmode; + if (bootmode != null && bootmode.length > 0) { + $.extend(deployVmData, { + bootmode : bootmode + }); + } if (g_hostid != null) { $.extend(deployVmData, { diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index bb32d3a..58cb86c 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -3322,6 +3322,12 @@ }, id: { label: 'label.id' + }, + boottype: { + label: 'label.vm.boottype' + }, + bootmode: { + label: 'label.vm.bootmode' } }], diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 1e29500..29f428a 100755 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -18024,6 +18024,10 @@ }); } }, + ueficapability: { + label:'label.host.ueficapability', + converter: cloudStack.converters.toBooleanText + }, hahost: { label: 'label.ha.enabled', converter: cloudStack.converters.toBooleanText diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 2450ed1..4aefa97 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -1392,6 +1392,50 @@ $(this).closest('div.select').hide(); } } + + var uefi = function(bootType){ + var $bootmode = $step.find('select[name=bootmode]'); + + if(bootType.toLowerCase() == 'uefi' ){ + $bootmode.html(''); + var $option = $('<option>'); + var id = 'LEGACY'; + var description = 'LEGACY'; + + $option.attr('value', id); + $option.html(description); + $option.appendTo($bootmode); + + var $option2 = $('<option>'); + var id2 = 'SECURE'; + var description2 = 'SECURE'; + + $option2.attr('value', id2); + $option2.html(description2); + $option2.appendTo($bootmode); + + } + + if(bootType.toLowerCase() == 'bios' ){ + $bootmode.html(''); + + var $option = $('<option>'); + var id = 'LEGACY'; + var description = 'LEGACY'; + + $option.attr('value', id); + $option.html(description); + $option.appendTo($bootmode); + } + + } + + var $uefiselect = $step.find('select[name=customboot]'); + $uefiselect.unbind('change'); + $uefiselect.change(function(){ + uefi($uefiselect.val()); + }); + }); } }; diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java index 5aab3a2..7877db9 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -1196,4 +1196,17 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { } return morNetwork; } + + public String getProductVersion() throws Exception { + return getHostAboutInfo().getVersion(); + } + + public boolean isUefiLegacySupported() throws Exception { + String hostVersion = getProductVersion(); + if (hostVersion.compareTo(VmwareHelper.MIN_VERSION_UEFI_LEGACY) >= 0) { + return true; + } + return false; + } + } diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java index dd65775..3d209fb 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java @@ -92,6 +92,7 @@ public class VmwareHelper { public static final int MAX_IDE_CONTROLLER_COUNT = 2; public static final int MAX_ALLOWED_DEVICES_IDE_CONTROLLER = 2; public static final int MAX_ALLOWED_DEVICES_SCSI_CONTROLLER = 15; + public static final String MIN_VERSION_UEFI_LEGACY = "5.5"; public static boolean isReservedScsiDeviceNumber(int deviceNumber) { return deviceNumber == 7;