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 d25ffe3aa30e..ebb20720778c 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,7 @@ public class VirtualMachineTO { String vncAddr; Map params; String uuid; + String bootType; DiskTO[] disks; NicTO[] nics; @@ -380,4 +381,11 @@ public Pair> getOvfProperties() { public void setOvfProperties(Pair> ovfProperties) { this.ovfProperties = ovfProperties; } + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } } diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 1ecd48d74ce9..f3c517290efe 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 static String[] toStrings(Host.Type... types) { 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 977e27eb14f2..1abc76440a2d 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 static class Param { 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 84de8c9ebac8..fb7e336f1b05 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 fb44a8a11f48..a40cdf8e1c7b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -741,6 +741,8 @@ public class ApiConstants { public static final String EXITCODE = "exitcode"; public static final String TARGET_ID = "targetid"; public static final String VOLUME_IDS = "volumeids"; + public static final String BOOT_TYPE ="boottype"; + public static final String BOOT_MODE ="bootmode"; 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 71269df7d7a4..10882ed15817 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 @@ -111,6 +111,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 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," @@ -257,12 +263,30 @@ public Map getDetails() { } } } + if(getBootType() != null){ // export to get + if(getBootType().equalsIgnoreCase("UEFI")) { + customparameterMap.put("UEFI", getBootMode()); + } + } + if (rootdisksize != null && !customparameterMap.containsKey("rootdisksize")) { customparameterMap.put("rootdisksize", rootdisksize.toString()); } return customparameterMap; } + public String getBootType() { + if (bootType != null && !("bios".equalsIgnoreCase(bootType) || "uefi".equalsIgnoreCase(bootType))) { + throw new InvalidParameterValueException("Invalid Boot Type"); + } + return bootType; + } + + public String getBootMode() { + return bootMode; + } + + public Map getVmOVFProperties() { Map 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 3d53682d3b9e..57f74dd45aed 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 @@ -243,6 +243,11 @@ 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; + + // Default visibility to support accessing the details from unit tests Map getDetails() { return details; @@ -495,6 +500,14 @@ public void setDetails(Map details) { 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; } @@ -661,4 +674,8 @@ public String getHypervisorVersion() { 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 8a2f1a169d64..573f3c12e3fa 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 @@ -290,6 +290,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(); nics = new LinkedHashSet(); @@ -849,4 +857,13 @@ public void setDynamicallyScalable(Boolean dynamicallyScalable) { 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 c004514d084e..7b34077676c3 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 @@ void deploy(String reservationId, String caller, Map 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 1cc925b3ccf5..6fe7c34dc200 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1042,6 +1042,10 @@ public void orchestrateStart(final String vmUuid, final Map getParams() { public void setParams(Map params) { if (params != null) { rawParams = new HashMap(); - for (Map.Entry entry : params.entrySet()) { - rawParams.put(entry.getKey().getName(), JobSerializerHelper.toObjectSerializedString( - entry.getValue() instanceof Serializable ? (Serializable)entry.getValue() : entry.getValue().toString())); + if (params != null && !params.isEmpty()) { + for (Map.Entry entry : params.entrySet()) { + if (null != entry && null != entry.getValue() && null != entry.getKey()) { + rawParams.put(entry.getKey().getName(), JobSerializerHelper.toObjectSerializedString( + entry.getValue() instanceof Serializable ? (Serializable) entry.getValue() : entry.getValue().toString())); + } + } } } } 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 ef9c44ab26df..84521cc4e11d 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 @@ -148,6 +148,12 @@ public String reserveVirtualMachine(VMEntityVO vmEntityVO, DeploymentPlanner pla VMInstanceVO vm = _vmDao.findByUuid(vmEntityVO.getUuid()); VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm); vmProfile.setServiceOffering(_serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId())); + if (vmEntityVO.getDetails() != null) { + Map details = vmEntityVO.getDetails(); + if (details.containsKey(VirtualMachineProfile.Param.BootType.getName())) { + vmProfile.getParameters().put(VirtualMachineProfile.Param.BootType, details.get(VirtualMachineProfile.Param.BootType.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 598e61989d17..bd9578b3b19a 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,6 +20,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.HashMap; import javax.inject.Inject; @@ -269,4 +270,23 @@ public void disconnectFrom(NetworkEntity netowrk, short nicId) { } + @Override + public void setParamsToEntity(Map map) { + if (this.vmEntityVO != null) { + Map details = this.vmEntityVO.getDetails(); + + if (details == null) { + details = new HashMap(); + } + if (map != null && !map.isEmpty()) { + for (Map.Entry 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 dd45c0987adf..591f9443db6e 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 @@ -109,4 +109,6 @@ public interface HostDao extends GenericDao, StateDao listAllHostsUpByZoneAndHypervisor(long zoneId, HypervisorType hypervisorType); + + List listByHostCapability(Host.Type type, Long clusterId, Long podId, long dcId, String hostCapabilty); } 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 71f0aef39d67..25e4002b1ec7 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.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; @@ -1200,6 +1201,41 @@ public List listAllHostsUpByZoneAndHypervisor(long zoneId, HypervisorTyp .collect(Collectors.toList()); } + @Override + public List listByHostCapability(Type type, Long clusterId, Long podId, long dcId, String hostCapabilty) { + SearchBuilder hostCapabilitySearch = _detailsDao.createSearchBuilder(); + DetailVO tagEntity = hostCapabilitySearch.entity(); + hostCapabilitySearch.and("capability", tagEntity.getName(), SearchCriteria.Op.EQ); + hostCapabilitySearch.and("value", tagEntity.getValue(), SearchCriteria.Op.EQ); + + SearchBuilder 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 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); + + } + private ResultSet executeSqlGetResultsetForMethodFindHostInZoneToExecuteCommand(HypervisorType hypervisorType, long zoneId, TransactionLegacy tx, String sql) throws SQLException { PreparedStatement pstmt = tx.prepareAutoCloseStatement(sql); pstmt.setString(1, Objects.toString(hypervisorType)); 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 79cb1b4596d8..1a3aed0ab611 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.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; @@ -573,6 +574,10 @@ public DataStoreManager dataStoreManager() { return Mockito.mock(DataStoreManager.class); } + @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 b20f1a58b949..330d1ee23a89 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 @@ -217,6 +217,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; @@ -480,6 +481,10 @@ public StorageSubsystemCommandHandler getStorageHandler() { return storageHandler; } + public Properties getProperties() { + return _uefiProperties; + } + private static final class KeyValueInterpreter extends OutputInterpreter { private final Map map = new HashMap(); @@ -605,6 +610,11 @@ public boolean configure(final String name, final Map params) th 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); @@ -1089,6 +1099,28 @@ public boolean configure(final String name, final Map params) th 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 uefi.properties."); + } + + s_logger.info("uefi.properties 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")); + + } 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 params) { _diskActivityCheckEnabled = Boolean.parseBoolean((String)params.get("vm.diskactivity.checkenabled")); if (_diskActivityCheckEnabled) { @@ -2069,6 +2101,14 @@ public LibvirtVMDef createVMFromSpec(final VirtualMachineTO vmTO) { vm.setDomDescription(vmTO.getOs()); vm.setPlatformEmulator(vmTO.getPlatformEmulator()); + Map customParams = vmTO.getDetails(); + boolean isUefiEnabled = false; + String bootMode =null; + if (MapUtils.isNotEmpty(customParams) && customParams.containsKey("UEFI")) { + isUefiEnabled = true; + bootMode = customParams.get("UEFI"); + } + Map 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"); @@ -2088,11 +2128,38 @@ public LibvirtVMDef createVMFromSpec(final VirtualMachineTO vmTO) { } guest.setGuestArch(vmTO.getArch()); guest.setMachineType("pc"); + guest.setBootType(GuestDef.BootType.BIOS); + if (MapUtils.isNotEmpty(customParams) && customParams.containsKey("UEFI")) { + guest.setBootType(GuestDef.BootType.UEFI); + guest.setBootMode(GuestDef.BootMode.LEGACY); + if (StringUtils.isNotBlank(customParams.get("UEFI")) && "secure".equalsIgnoreCase(customParams.get("UEFI"))) { + 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 (_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_SECURE) != null && "secure".equalsIgnoreCase(bootMode)) + guest.setNvramTemplate(_uefiProperties.getProperty(GuestDef.GUEST_NVRAM_TEMPLATE_SECURE)); + 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(); @@ -2152,6 +2219,9 @@ So if getMinSpeed() returns null we fall back to getSpeed(). features.addFeatures("pae"); features.addFeatures("apic"); features.addFeatures("acpi"); + if(isUefiEnabled && isSecureMode(customParams.get("UEFI"))) { + features.addFeatures("smm"); + } //KVM hyperv enlightenment features based on OS Type enlightenWindowsVm(vmTO, features); @@ -3858,4 +3928,12 @@ public boolean isHostSecured() { } 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 dab1af510bff..f7cbfb2030b7 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,17 @@ public String toString() { } } + enum BootType { + UEFI, BIOS + } + + enum BootMode { + LEGACY, SECURE + } + private GuestType _type; + private BootType _boottype; + private BootMode _bootmode; private String _arch; private String _loader; private String _kernel; @@ -67,6 +77,14 @@ public String toString() { private String _uuid; private final List _bootdevs = new ArrayList(); 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 +94,10 @@ public GuestType getGuestType() { 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 +125,22 @@ public void setUuid(String uuid) { _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) { @@ -125,6 +163,24 @@ public String toString() { guestDef.append(" machine='" + _machine + "'"); } guestDef.append(">hvm\n"); + if (_loader != null) { + if (_bootmode == BootMode.LEGACY) { + guestDef.append("" + _loader + "\n"); + } else if (_bootmode == BootMode.SECURE) { + guestDef.append("" + _loader + "\n"); + } + } + if (_nvram != null) { + guestDef.append(""); + } else { + guestDef.append(">"); + } + + guestDef.append(_nvram); + guestDef.append(_uuid + ".fd"); + } if (!_bootdevs.isEmpty()) { for (BootOrder bo : _bootdevs) { guestDef.append("\n"); @@ -271,7 +327,11 @@ public String toString() { StringBuilder feaBuilder = new StringBuilder(); feaBuilder.append("\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(); 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 072ab9f6fed0..38922dc55297 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 @@ -228,6 +228,7 @@ public VirtualMachineTO implement(VirtualMachineProfile vm) { } } + 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/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 02f758ba844b..829cbe613feb 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 @@ -310,6 +310,7 @@ import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; import com.vmware.vim25.VirtualEthernetCardOpaqueNetworkBackingInfo; 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; @@ -1701,6 +1702,11 @@ protected StartAnswer execute(StartCommand cmd) { String dataDiskController = vmSpec.getDetails().get(VmDetailConstants.DATA_DISK_CONTROLLER); String rootDiskController = vmSpec.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER); DiskTO rootDiskTO = null; + String bootMode = "BIOS"; + 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)) { @@ -2259,6 +2265,16 @@ protected StartAnswer execute(StartCommand cmd) { } } + if (!bootMode.equalsIgnoreCase("BIOS")) { + vmConfigSpec.setFirmware("efi"); + if (bootMode.equalsIgnoreCase("UEFI_SECURE") ) { + VirtualMachineBootOptions bootOptions = new VirtualMachineBootOptions(); + bootOptions.setEfiSecureBootEnabled(true); + vmConfigSpec.setBootOptions(bootOptions); + } + } + + // // Configure VM // diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java index 79a9fb229724..20ec90dd3318 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java @@ -1374,6 +1374,14 @@ public VM createVmFromTemplate(final Connection conn, final VirtualMachineTO vmS s_logger.debug("HVM args are " + bootArgs); } + Map vmSpecDetails = vmSpec.getDetails(); + final Map bootParams = new HashMap(); + if (vmSpecDetails != null && vmSpecDetails.containsKey("UEFI")) { + bootParams.put("firmware", "uefi"); + } + vm.setHVMBootParams(conn, bootParams); + + if (!(guestOsTypeName.startsWith("Windows") || guestOsTypeName.startsWith("Citrix") || guestOsTypeName.startsWith("Other"))) { if (vmSpec.getBootloader() == BootloaderType.CD) { final DiskTO[] disks = vmSpec.getDisks(); @@ -1751,6 +1759,15 @@ protected void fillHostInfo(final Connection conn, final StartupRoutingCommand c details.put("private.network.device", _privateNetworkName); } + final Map hostParams = host.getLicenseParams(conn); + final String isGuefiRestricted = hostParams.get("restrict_guefi"); + if (isGuefiRestricted != null && !isGuefiRestricted.isEmpty()) { + details.put(com.cloud.host.Host.HOST_UEFI_ENABLE, new Boolean(isGuefiRestricted.equalsIgnoreCase("false")).toString()); + } else { + details.put(com.cloud.host.Host.HOST_UEFI_ENABLE, Boolean.FALSE.toString()); + } + + cmd.setHostDetails(details); cmd.setName(hr.nameLabel); cmd.setGuid(_host.getUuid()); 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 186c654fda38..aab9510c4f05 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.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 List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan pla 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(); @@ -123,11 +138,20 @@ public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan pla String hostTagOnOffering = offering.getHostTag(); String hostTagOnTemplate = template.getTemplateTag(); + String hostTagUefi = "UEFI"; boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false; boolean hasTemplateTag = hostTagOnTemplate != null ? true : false; List clusterHosts = new ArrayList(); + List hostsMatchingUefiTag = new ArrayList(); + 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 List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan pla } } + if (isVMDeployedWithUefi) { + clusterHosts.retainAll(hostsMatchingUefiTag); + } + // add all hosts that we are not considering to the avoid list List 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 4ccfce9edb48..ecada1ea795f 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 @@ -66,6 +66,7 @@ @Component public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation implements UserVmJoinDao { public static final Logger s_logger = Logger.getLogger(UserVmJoinDaoImpl.class); + private static final String UEFI = "UEFI"; @Inject private ConfigurationDao _configDao; @@ -317,6 +318,15 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us (UserVmManager.DisplayVMOVFProperties.value() && userVmDetailVO.getName().startsWith(ApiConstants.OVF_PROPERTIES))) { resourceDetails.put(userVmDetailVO.getName(), userVmDetailVO.getValue()); } + if (UEFI.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/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index 5760e24ce4d3..736a7ae238ae 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.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 List orderClusters(VirtualMachineProfile vmProfile, DeploymentPlan 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 @@ private void reorderClustersBasedOnImplicitTags(List clusterList, int requ List hostList = capacityDao.listHostsWithEnoughCapacity(requiredCpu, requiredRam, clusterId, Host.Type.Routing.toString()); if (!hostList.isEmpty() && implicitHostTags.length > 0) { uniqueTags = new Long(hostTagsDao.getDistinctImplicitHostTags(hostList, implicitHostTags).size()); - } + uniqueTags = uniqueTags + getHostsByCapability(hostList, Host.HOST_UEFI_ENABLE); + } UniqueTagsInClusterMap.put(clusterId, uniqueTags); } Collections.sort(clusterList, new Comparator() { @@ -218,6 +230,20 @@ public int compare(Long o1, Long o2) { }); } + private Long getHostsByCapability(List hostList, String hostCapability) { + int totalHostswithCapability = 0; + for (Long host : hostList) { //TODO: Fix this in single query instead of polling request for each Host + Map details = hostDetailsDao.findDetails(host); + if (details.containsKey(Host.HOST_UEFI_ENABLE)) { + if (details.get(Host.HOST_UEFI_ENABLE).equalsIgnoreCase("Yes")) { + totalHostswithCapability++; + } + + } + } + return totalHostswithCapability > 0 ? new Long(1) : new Long(0); + } + private List 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 b79097884890..765508b0e7e9 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -166,6 +166,11 @@ protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile vmProfile) { 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 (bootType != null && !bootType.isEmpty()) { + to.setBootType(bootType); + } + List 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 6c9bcace90fc..36b1ca2020d9 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 @@ private void setupAgentSecurity(final Connection sshConnection, final String age @Override public Map> find(long dcId, Long podId, Long clusterId, URI uri, String username, String password, List hostTags) throws DiscoveryException { + boolean isUefiSupported = false; ClusterVO cluster = _clusterDao.findById(clusterId); if (cluster == null || cluster.getHypervisorType() != getHypervisorType()) { @@ -256,6 +257,11 @@ private void setupAgentSecurity(final Connection sshConnection, final String age return null; } + if (SSHCmdHelper.sshExecuteCmd(sshConnection, "rpm -qa | grep -i ovmf", 3)) { + s_logger.debug("It's UEFI enabled KVM machine"); + isUefiSupported = true; + } + List netInfos = _networkMgr.getPhysicalNetworkInfo(dcId, getHypervisorType()); String kvmPrivateNic = null; String kvmPublicNic = null; @@ -338,6 +344,7 @@ private void setupAgentSecurity(final Connection sshConnection, final String age Map 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 6cb457471064..df0059b7623a 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -701,6 +701,8 @@ 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()); @@ -720,6 +722,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private ClusterDao _clusterDao; @Inject + private UserVmDetailsDao _UserVmDetailsDao; + @Inject private SecondaryStorageVmDao _secStorageVmDao; @Inject public EventDao _eventDao; @@ -1183,6 +1187,16 @@ public Ternary, Integer>, List, Map, Integer>, List, Map>(new Pair, + Integer>(new ArrayList(), new Integer(0)), new ArrayList(), new HashMap()); + } + } + 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 2503b33e3331..69e5545f7024 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3903,6 +3903,10 @@ public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCap _vmDao.persist(vm); for (String key : customParameters.keySet()) { + if(key.equalsIgnoreCase("uefi")) { + vm.setDetail(key,customParameters.get(key)); + continue; + } if( key.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || key.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || key.equalsIgnoreCase(VmDetailConstants.MEMORY)) { @@ -4213,13 +4217,24 @@ public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableExc Long podId = null; Long clusterId = null; Long hostId = cmd.getHostId(); + String bootType = cmd.getBootType(); + String bootMode = cmd.getBootMode(); + boolean isUEFIBootEnabled = false; + if( bootType != null && bootType.equalsIgnoreCase("UEFI")) { + isUEFIBootEnabled = true; + } Map 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()); + Map additonalParams = new HashMap(); + additonalParams.put(VirtualMachineProfile.Param.UefiFlag,isUEFIBootEnabled); + additonalParams.put(VirtualMachineProfile.Param.BootType,bootType); + additonalParams.put(VirtualMachineProfile.Param.BootMode,bootMode); + + return startVirtualMachine(vmId, podId, clusterId, hostId, diskOfferingMap, additonalParams, cmd.getDeploymentPlanner()); } private UserVm startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, Map diskOfferingMap, Map additonalParams, String deploymentPlannerToUse) @@ -4676,6 +4691,7 @@ public Pair> startVirtualMach } VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); + vmEntity.setParamsToEntity(additionalParams); DeploymentPlanner planner = null; if (deploymentPlannerToUse != null) { @@ -5029,6 +5045,17 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE throw new InvalidParameterValueException("Unable to use template " + templateId); } + String bootType = cmd.getBootType(); + if( bootType != null && bootType.equalsIgnoreCase("UEFI") && cmd.getHypervisor() == HypervisorType.XenServer) { + boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(template.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); + if (serviceOffering.getCpu() < 2) { + throw new InvalidParameterValueException("Please verify the Service Offering, UEFI enabled VMs on XenServer must have atleast 2 vCPUs"); + } + if (!isWindows) { + throw new InvalidParameterValueException("Please verify the template, in case of XenServer UEFI mode is allowed only for Windows guest operating systems"); + } + } + Long diskOfferingId = cmd.getDiskOfferingId(); DiskOffering diskOffering = null; if (diskOfferingId != null) { diff --git a/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java index 1d1ab89d8355..e73c0c6bd7dc 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.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) @@ -284,6 +285,12 @@ public HostTagsDao hostTagsDao() { return Mockito.mock(HostTagsDao.class); } + @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 85463de8c8bf..41deea2823f4 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.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; @@ -355,6 +358,9 @@ public HostTagsDao hostTagsDao() { return Mockito.mock(HostTagsDao.class); } + @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 7c8aec3dced9..eb0a0d3787d6 100644 --- a/ui/index.html +++ b/ui/index.html @@ -480,6 +480,31 @@

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/ui/l10n/en.js b/ui/l10n/en.js index 4ce59e08599b..612b06eaa3b3 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -868,6 +868,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", @@ -962,6 +963,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.kvm.traffic.label":"KVM traffic label", "label.label":"Label", diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index a6fdfbb3b952..9ee7e9b00b64 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -1366,6 +1366,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 86d49c9a54c8..48792b58fefc 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -2904,6 +2904,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 f6ef03ca9d3e..d14fba7f5e48 100755 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -17397,6 +17397,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 4fa7e3fc76c0..6f73e1570dbd 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -1393,6 +1393,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 = $('