diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java index 77e88a9466e..f8e210ac326 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java @@ -66,7 +66,7 @@ public interface TemplateDataStoreDao extends GenericDao listByTemplate(long templateId); - List listByTemplateNotBypassed(long templateId); + List listByTemplateNotBypassed(long templateId, Long... storeIds); TemplateDataStoreVO findByTemplateZoneReady(long templateId, Long zoneId); diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java index ea53825eab0..8aa411412b3 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java @@ -99,6 +99,7 @@ public class TemplateDataStoreDaoImpl extends GenericDaoBase listByTemplateNotBypassed(long templateId) { + public List listByTemplateNotBypassed(long templateId, Long... storeIds) { SearchCriteria sc = templateSearch.create(); sc.setParameters("template_id", templateId); sc.setParameters("download_state", Status.BYPASSED); sc.setParameters("destroyed", false); + sc.setParameters("storeids", storeIds); return search(sc, null); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 8411d251a4b..8bef78759e1 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -16,6 +16,55 @@ // under the License. package com.cloud.hypervisor.vmware.manager; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLDecoder; +import java.rmi.RemoteException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd; +import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl; +import org.apache.cloudstack.management.ManagementServerHost; +import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + import com.amazonaws.util.CollectionUtils; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -120,51 +169,6 @@ import com.google.common.base.Strings; import com.vmware.pbm.PbmProfile; import com.vmware.vim25.AboutInfo; import com.vmware.vim25.ManagedObjectReference; -import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; -import org.apache.cloudstack.api.command.admin.zone.ImportVsphereStoragePoliciesCmd; -import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; -import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePoliciesCmd; -import org.apache.cloudstack.api.command.admin.zone.ListVsphereStoragePolicyCompatiblePoolsCmd; -import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; -import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.Configurable; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl; -import org.apache.cloudstack.management.ManagementServerHost; -import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.rmi.RemoteException; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener, VmwareDatacenterService, Configurable { private static final Logger s_logger = Logger.getLogger(VmwareManagerImpl.class); @@ -268,6 +272,24 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw _storageMgr = new VmwareStorageManagerImpl(this); } + private boolean isSystemVmIsoCopyNeeded(File srcIso, File destIso) { + if (!destIso.exists()) { + return true; + } + boolean copyNeeded = false; + try { + String srcIsoMd5 = DigestUtils.md5Hex(new FileInputStream(srcIso)); + String destIsoMd5 = DigestUtils.md5Hex(new FileInputStream(destIso)); + copyNeeded = !StringUtils.equals(srcIsoMd5, destIsoMd5); + if (copyNeeded) { + s_logger.debug(String.format("MD5 checksum: %s for source ISO: %s is different from MD5 checksum: %s from destination ISO: %s", srcIsoMd5, srcIso.getAbsolutePath(), destIsoMd5, destIso.getAbsolutePath())); + } + } catch (IOException e) { + s_logger.debug(String.format("Unable to compare MD5 checksum for systemvm.iso at source: %s and destination: %s", srcIso.getAbsolutePath(), destIso.getAbsolutePath()), e); + } + return copyNeeded; + } + @Override public String getConfigComponentName() { return VmwareManagerImpl.class.getSimpleName(); @@ -706,7 +728,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw File srcIso = getSystemVMPatchIsoFile(); File destIso = new File(mountPoint + "/systemvm/" + getSystemVMIsoFileNameOnDatastore()); - if (!destIso.exists()) { + if (isSystemVmIsoCopyNeeded(srcIso, destIso)) { s_logger.info("Inject SSH key pairs before copying systemvm.iso into secondary storage"); _configServer.updateKeyPairs(); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index a98032c7fdd..9ed1f7d1579 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -380,8 +380,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { try { if (workerVm != null) { // detach volume and destroy worker vm - workerVm.detachAllDisks(); - workerVm.destroy(); + workerVm.detachAllDisksAndDestroy(); } } catch (Throwable e) { s_logger.warn("Failed to destroy worker VM: " + workerVMName); @@ -670,8 +669,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } vmMo.removeSnapshot(templateUniqueName, false); @@ -923,8 +921,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { clonedVm.detachAllDisks(); } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } } } @@ -980,8 +977,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } } } @@ -1037,8 +1033,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } if (workerVm != null) { //detach volume and destroy worker vm - workerVm.detachAllDisks(); - workerVm.destroy(); + workerVm.detachAllDisksAndDestroy(); } } } 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 6a8ff0d3ce1..0e93398b552 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 @@ -5981,9 +5981,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (recycle) { s_logger.info("Recycle pending worker VM: " + vmMo.getName()); + vmMo.cancelPendingTasks(); vmMo.powerOff(); - vmMo.detachAllDisks(); - vmMo.destroy(); + vmMo.detachAllDisksAndDestroy(); } } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index 131260a39c5..810f355662a 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -430,8 +430,7 @@ public class VmwareStorageProcessor implements StorageProcessor { virtualDeviceBackingInfo = backingInfo.getParent(); } - vmMo.detachAllDisks(); - vmMo.destroy(); + vmMo.detachAllDisksAndDestroy(); VmwareStorageLayoutHelper.moveVolumeToRootFolder(dcMo, backingFiles); @@ -821,8 +820,7 @@ public class VmwareStorageProcessor implements StorageProcessor { if (volume.getDeviceId().equals(0L)) { if (existingVm != null) { s_logger.info("Found existing VM " + vmName + " before cloning from template, destroying it"); - existingVm.detachAllDisks(); - existingVm.destroy(); + existingVm.detachAllDisksAndDestroy(); } s_logger.info("ROOT Volume from deploy-as-is template, cloning template"); cloneVMFromTemplate(hyperHost, template.getPath(), vmName, primaryStore.getUuid()); @@ -854,8 +852,7 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.info("Destroy dummy VM after volume creation"); if (vmMo != null) { s_logger.warn("Unable to destroy a null VM ManagedObjectReference"); - vmMo.detachAllDisks(); - vmMo.destroy(); + vmMo.detachAllDisksAndDestroy(); } } } else { @@ -930,8 +927,7 @@ public class VmwareStorageProcessor implements StorageProcessor { String vmdkFileBaseName = vmMo.getVmdkFileBaseNames().get(0); if (volume.getVolumeType() == Volume.Type.DATADISK) { s_logger.info("detach disks from volume-wrapper VM " + vmName); - vmMo.detachAllDisks(); - vmMo.destroy(); + vmMo.detachAllDisksAndDestroy(); } return vmdkFileBaseName; } @@ -968,11 +964,8 @@ public class VmwareStorageProcessor implements StorageProcessor { dsMo.moveDatastoreFile(vmwareLayoutFilePair[i], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[i], dcMo.getMor(), true); } - s_logger.info("detach disks from volume-wrapper VM " + vmdkName); - vmMo.detachAllDisks(); - - s_logger.info("destroy volume-wrapper VM " + vmdkName); - vmMo.destroy(); + s_logger.info("detach disks from volume-wrapper VM and destroy" + vmdkName); + vmMo.detachAllDisksAndDestroy(); String srcFile = dsMo.getDatastorePath(vmdkName, true); @@ -1144,8 +1137,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } if (workerVm != null) { //detach volume and destroy worker vm - workerVm.detachAllDisks(); - workerVm.destroy(); + workerVm.detachAllDisksAndDestroy(); } } } @@ -1282,8 +1274,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } } } @@ -1364,8 +1355,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } finally { try { if (volume.getVmName() == null && workerVmMo != null) { - workerVmMo.detachAllDisks(); - workerVmMo.destroy(); + workerVmMo.detachAllDisksAndDestroy(); } } catch (Throwable e) { s_logger.error("Failed to destroy worker VM created for detached volume"); @@ -1675,8 +1665,7 @@ public class VmwareStorageProcessor implements StorageProcessor { workerVM.exportVm(installFullPath, exportName, false, false); - workerVM.detachAllDisks(); - workerVM.destroy(); + workerVM.detachAllDisksAndDestroy(); } private String getTemplateVmdkName(String installFullPath, String exportName) { @@ -1853,8 +1842,7 @@ public class VmwareStorageProcessor implements StorageProcessor { return new Pair<>(diskDevice, disks); } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } } } @@ -2023,8 +2011,7 @@ public class VmwareStorageProcessor implements StorageProcessor { try { if (workerVm != null) { // detach volume and destroy worker vm - workerVm.detachAllDisks(); - workerVm.destroy(); + workerVm.detachAllDisksAndDestroy(); } } catch (Throwable e) { s_logger.warn("Failed to destroy worker VM: " + workerVMName); @@ -2566,8 +2553,7 @@ public class VmwareStorageProcessor implements StorageProcessor { } finally { s_logger.info("Destroy dummy VM after volume creation"); if (vmMo != null) { - vmMo.detachAllDisks(); - vmMo.destroy(); + vmMo.detachAllDisksAndDestroy(); } } } @@ -3779,8 +3765,7 @@ public class VmwareStorageProcessor implements StorageProcessor { return _storage.getSize(srcOVFFileName); } finally { if (clonedVm != null) { - clonedVm.detachAllDisks(); - clonedVm.destroy(); + clonedVm.detachAllDisksAndDestroy(); } } } diff --git a/plugins/hypervisors/xenserver/pom.xml b/plugins/hypervisors/xenserver/pom.xml index 98f0f3bc839..1d5e6bdc42e 100644 --- a/plugins/hypervisors/xenserver/pom.xml +++ b/plugins/hypervisors/xenserver/pom.xml @@ -48,5 +48,10 @@ xen-api ${cs.xapi.version} + + org.apache.maven + maven-artifact + 3.6.3 + diff --git a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java index c2a0969c9d3..bbf3686750d 100644 --- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java +++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscoverer.java @@ -31,7 +31,9 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; import org.apache.cloudstack.hypervisor.xenserver.XenserverConfigs; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; +import org.apache.maven.artifact.versioning.ComparableVersion; import org.apache.xmlrpc.XmlRpcException; import com.cloud.agent.AgentManager; @@ -122,6 +124,16 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L private String xenServerIsoName = TemplateManager.XS_TOOLS_ISO; private String xenServerIsoDisplayText = "XenServer Tools Installer ISO (xen-pv-drv-iso)"; + public final static String MIN_UEFI_SUPPORTED_VERSION = "8.2"; + + public static boolean isUefiSupported(String hostProductVersion) { + if (StringUtils.isEmpty(hostProductVersion)) { + return false; + } + ComparableVersion version = new ComparableVersion(hostProductVersion); + return version.compareTo(new ComparableVersion(MIN_UEFI_SUPPORTED_VERSION)) >= 0; + } + protected XcpServerDiscoverer() { } @@ -309,6 +321,9 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L details.put("username", username); params.put("username", username); details.put("password", password); + if (isUefiSupported(prodVersion)) { + details.put(com.cloud.host.Host.HOST_UEFI_ENABLE, Boolean.TRUE.toString()); + } params.put("password", password); params.put("zone", Long.toString(dcId)); params.put("guid", record.uuid); 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 8aaa25fed47..d22fb7f104c 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 @@ -16,6 +16,7 @@ // under the License. package com.cloud.hypervisor.xenserver.resource; +import static com.cloud.hypervisor.xenserver.discoverer.XcpServerDiscoverer.isUefiSupported; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.io.BufferedReader; @@ -51,6 +52,7 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageAnswer; import org.apache.cloudstack.diagnostics.CopyToSecondaryStorageCommand; import org.apache.cloudstack.diagnostics.DiagnosticsService; @@ -1438,6 +1440,11 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } catch (final Exception e) { throw new CloudRuntimeException("Unable to finalize VM MetaData: " + vmSpec); } + try { + setVmBootDetails(vm, conn, vmSpec.getBootType(), vmSpec.getBootMode()); + } catch (final XenAPIException | XmlRpcException e) { + throw new CloudRuntimeException(String.format("Unable to handle VM boot options: %s", vmSpec), e); + } return vm; } @@ -1784,6 +1791,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } details.put("product_brand", productBrand); details.put("product_version", _host.getProductVersion()); + if (isUefiSupported(_host.getProductVersion())) { + details.put(com.cloud.host.Host.HOST_UEFI_ENABLE, Boolean.TRUE.toString()); + } if (hr.softwareVersion.get("product_version_text_short") != null) { details.put("product_version_text_short", hr.softwareVersion.get("product_version_text_short")); cmd.setHypervisorVersion(hr.softwareVersion.get("product_version_text_short")); @@ -1942,6 +1952,20 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } + protected void setVmBootDetails(final VM vm, final Connection conn, String bootType, String bootMode) throws XenAPIException, XmlRpcException { + if (!ApiConstants.BootType.UEFI.toString().equals(bootType)) { + bootType = ApiConstants.BootType.BIOS.toString(); + } + Boolean isSecure = bootType.equals(ApiConstants.BootType.UEFI.toString()) && + ApiConstants.BootMode.SECURE.toString().equals(bootMode); + final Map bootParams = vm.getHVMBootParams(conn); + bootParams.replace("firmware", bootType.toLowerCase()); + vm.setHVMBootParams(conn, bootParams); + final Map platform = vm.getPlatform(conn); + platform.put("secureboot", isSecure.toString()); + vm.setPlatform(conn, platform); + } + /** * This method just creates a XenServer network following the tunnel network * naming convention diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscovererTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscovererTest.java index f8188afd682..26c2e6da625 100644 --- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscovererTest.java +++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/discoverer/XcpServerDiscovererTest.java @@ -17,6 +17,7 @@ package com.cloud.hypervisor.xenserver.discoverer; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -70,4 +71,25 @@ public class XcpServerDiscovererTest { inOrder.verify(vmTemplateVOMock).setDisplayText("XenServer Tools Installer ISO (xen-pv-drv-iso)"); inOrder.verify(vmTemplateDao).update(1L, vmTemplateVOMock); } + + @Test + public void uefiSupportedVersionTest() { + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("8.2")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("8.2.0")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("8.2.1")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("9")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("9.1")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("9.1.0")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("10")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("10.1")); + Assert.assertTrue(XcpServerDiscoverer.isUefiSupported("10.1.0")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported(null)); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("abc")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("0")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("7.4")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("8")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("8.1")); + Assert.assertFalse(XcpServerDiscoverer.isUefiSupported("8.1.0")); + } } diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 50748e1cc34..93269461e49 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -139,7 +139,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri EndPoint ep = epSelector.select(volume); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send DeleteCommand, check if host or ssvm is down?"; + String errMsg = "No remote endpoint to send CreateObjectCommand, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { @@ -281,7 +281,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri EndPoint ep = epSelector.select(srcData, destData); Answer answer = null; if (ep == null) { - String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; + String errMsg = "No remote endpoint to send CopyCommand, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index d733489f0fa..d0a7dfe275e 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -156,7 +156,9 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation detailsView, ResponseView view, TemplateJoinVO template) { - List templatesInStore = _templateStoreDao.listByTemplateNotBypassed(template.getId()); + List storesInZone = dataStoreDao.listStoresByZoneId(template.getDataCenterId()); + Long[] storeIds = storesInZone.stream().map(ImageStoreVO::getId).toArray(Long[]::new); + List templatesInStore = _templateStoreDao.listByTemplateNotBypassed(template.getId(), storeIds); List> downloadProgressDetails = new ArrayList(); HashMap downloadDetailInImageStores = null; for (TemplateDataStoreVO templateInStore : templatesInStore) { diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 7d2575d1509..c900197b9b3 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -1463,12 +1463,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic * If the volume is not in the primary storage, we do nothing here. */ protected void expungeVolumesInPrimaryStorageIfNeeded(VolumeVO volume) throws InterruptedException, ExecutionException { - VolumeInfo volOnPrimary = volFactory.getVolume(volume.getId(), DataStoreRole.Primary); - if (volOnPrimary != null) { - s_logger.info("Expunging volume " + volume.getId() + " from primary data store"); - AsyncCallFuture future = volService.expungeVolumeAsync(volOnPrimary); - future.get(); - } + expungeVolumesInPrimaryOrSecondary(volume, DataStoreRole.Primary); } /** @@ -1476,16 +1471,29 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic * If it is, we will execute an asynchronous call to delete it there. Then, we decrement the {@link ResourceType#secondary_storage} for the account that owns the volume. */ protected void expungeVolumesInSecondaryStorageIfNeeded(VolumeVO volume) throws InterruptedException, ExecutionException { - VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image); - if (volOnSecondary != null) { - s_logger.info("Expunging volume " + volume.getId() + " from secondary data store"); - AsyncCallFuture future2 = volService.expungeVolumeAsync(volOnSecondary); - future2.get(); - - _resourceLimitMgr.decrementResourceCount(volOnSecondary.getAccountId(), ResourceType.secondary_storage, volOnSecondary.getSize()); - } + expungeVolumesInPrimaryOrSecondary(volume, DataStoreRole.Image); } + private void expungeVolumesInPrimaryOrSecondary(VolumeVO volume, DataStoreRole role) throws InterruptedException, ExecutionException { + VolumeInfo volOnStorage = volFactory.getVolume(volume.getId(), role); + if (volOnStorage != null) { + s_logger.info("Expunging volume " + volume.getId() + " from " + role + " data store"); + AsyncCallFuture future = volService.expungeVolumeAsync(volOnStorage); + VolumeApiResult result = future.get(); + if (result.isFailed()) { + String msg = "Failed to expunge the volume " + volume + " in " + role + " data store"; + s_logger.warn(msg); + String details = ""; + if (result.getResult() != null && !result.getResult().isEmpty()) { + details = msg + " : " + result.getResult(); + } + throw new CloudRuntimeException(details); + } + if (DataStoreRole.Image.equals(role)) { + _resourceLimitMgr.decrementResourceCount(volOnStorage.getAccountId(), ResourceType.secondary_storage, volOnStorage.getSize()); + } + } + } /** * Clean volumes cache entries (if they exist). */ diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index cafd739d7ca..53e9452d0b9 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -742,7 +742,7 @@ class CsRemoteAccessVpn(CsDataBag): secret = CsFile(vpnsecretfilte) secret.empty() - secret.addeq("%s %%any : PSK \"%s\"" % (left, psk)) + secret.addeq("%s : PSK \"%s\"" % (left, psk)) secret.commit() xl2tpdconf = CsFile(xl2tpdconffile) @@ -1008,7 +1008,7 @@ class CsForwardingRules(CsDataBag): # Configure the hairpin snat self.fw.append(["nat", "front", "-A POSTROUTING -s %s -d %s -j SNAT -o %s --to-source %s" % - (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIp())]) + (self.getNetworkByIp(rule['internal_ip']), rule["internal_ip"], self.getDeviceByIp(rule["internal_ip"]), self.getGuestIpByIp(rule["internal_ip"]))]) class IpTablesExecutor: diff --git a/ui/src/components/view/InfoCard.vue b/ui/src/components/view/InfoCard.vue index 8c3083a1d2d..7419d03b5dc 100644 --- a/ui/src/components/view/InfoCard.vue +++ b/ui/src/components/view/InfoCard.vue @@ -26,7 +26,7 @@ @click="$message.success(`${$t('label.copied.clipboard')} : ${name}`)" v-clipboard:copy="name" > - + diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 670f3201d3e..be7ce574a22 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -477,10 +477,9 @@
+ v-if="vm.templateid && ['KVM', 'VMware', 'XenServer'].includes(hypervisor) && !template.deployasis"> @@ -1652,6 +1651,7 @@ export default { duration: 0 }) } + eventBus.$emit('vm-refresh-data') }, loadingMessage: `${title} ${this.$t('label.in.progress')}`, catchMessage: this.$t('error.fetching.async.job.result'), diff --git a/ui/src/views/compute/ScaleVM.vue b/ui/src/views/compute/ScaleVM.vue index 939c5e35f02..a7b6c18b19a 100644 --- a/ui/src/views/compute/ScaleVM.vue +++ b/ui/src/views/compute/ScaleVM.vue @@ -32,16 +32,18 @@ @handle-search-filter="($event) => fetchData($event)" /> @@ -120,14 +122,14 @@ export default { if (this.resource.state === 'Running') { return this.resource.cpunumber } - return 'serviceofferingdetails' in this.selectedOffering ? this.selectedOffering.serviceofferingdetails.mincpunumber * 1 : 1 + return this.selectedOffering?.serviceofferingdetails?.mincpunumber * 1 || 1 }, getMinMemory () { // We can only scale up while a VM is running if (this.resource.state === 'Running') { return this.resource.memory } - return 'serviceofferingdetails' in this.selectedOffering ? this.selectedOffering.serviceofferingdetails.minmemory * 1 : 32 + return this.selectedOffering?.serviceofferingdetails?.minmemory * 1 || 32 }, getMessage () { if (this.resource.hypervisor === 'VMware') { diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue b/ui/src/views/compute/wizard/ComputeSelection.vue index 31bfa7f5e34..5fe75d68178 100644 --- a/ui/src/views/compute/wizard/ComputeSelection.vue +++ b/ui/src/views/compute/wizard/ComputeSelection.vue @@ -25,7 +25,7 @@ :validate-status="errors.cpu.status" :help="errors.cpu.message"> - + - + tasks = _context.getVimClient().getDynamicProperty(taskmgr, "recentTask"); + + int vmTasks = 0, vmPendingTasks = 0; + for (ManagedObjectReference task : tasks) { + TaskInfo info = (TaskInfo) (_context.getVimClient().getDynamicProperty(task, "info")); + if (info.getEntityName().equals(vmName)) { + vmTasks++; + if (!(info.getState().equals(TaskInfoState.SUCCESS) || info.getState().equals(TaskInfoState.ERROR))) { + String taskName = StringUtils.isNotBlank(info.getName()) ? info.getName() : "Unknown"; + s_logger.debug(taskName + " task pending for the VM: " + vmName + ", cancelling it"); + vmPendingTasks++; + _context.getVimClient().cancelTask(task); + } + } + } + + s_logger.debug(vmPendingTasks + " pending tasks for the VM: " + vmName + " found, out of " + vmTasks + " recent VM tasks"); + } + public void tagAsWorkerVM() throws Exception { setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER, "true"); String workerTag = String.format("%d-%s", System.currentTimeMillis(), getContext().getStockObject("noderuninfo")); diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClient.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClient.java index 2395ccf5326..9d79e5466af 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClient.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClient.java @@ -42,6 +42,8 @@ import org.apache.cloudstack.utils.security.SecureSSLSocketFactory; import com.vmware.pbm.PbmPortType; import com.vmware.pbm.PbmService; import com.vmware.pbm.PbmServiceInstanceContent; + +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.w3c.dom.Element; @@ -779,4 +781,54 @@ public class VmwareClient { return vCenterSessionTimeout; } + public void cancelTask(ManagedObjectReference task) throws Exception { + TaskInfo info = (TaskInfo)(getDynamicProperty(task, "info")); + if (info == null) { + s_logger.warn("Unable to get the task info, so couldn't cancel the task"); + return; + } + + String taskName = StringUtils.isNotBlank(info.getName()) ? info.getName() : "Unknown"; + taskName += "(" + info.getKey() + ")"; + + String entityName = StringUtils.isNotBlank(info.getEntityName()) ? info.getEntityName() : ""; + + if (info.getState().equals(TaskInfoState.SUCCESS)) { + s_logger.debug(taskName + " task successfully completed for the entity " + entityName + ", can't cancel it"); + return; + } + + if (info.getState().equals(TaskInfoState.ERROR)) { + s_logger.debug(taskName + " task execution failed for the entity " + entityName + ", can't cancel it"); + return; + } + + s_logger.debug(taskName + " task pending for the entity " + entityName + ", trying to cancel"); + if (!info.isCancelable()) { + s_logger.warn(taskName + " task will continue to run on vCenter because it can't be cancelled"); + return; + } + + s_logger.debug("Cancelling task " + taskName + " of the entity " + entityName); + getService().cancelTask(task); + + // Since task cancellation is asynchronous, wait for the task to be cancelled + Object[] result = waitForValues(task, new String[] {"info.state", "info.error"}, new String[] {"state"}, + new Object[][] {new Object[] {TaskInfoState.SUCCESS, TaskInfoState.ERROR}}); + + if (result != null && result.length == 2) { //result for 2 properties: info.state, info.error + if (result[0].equals(TaskInfoState.SUCCESS)) { + s_logger.warn("Failed to cancel" + taskName + " task of the entity " + entityName + ", the task successfully completed"); + } + + if (result[1] instanceof LocalizedMethodFault) { + MethodFault fault = ((LocalizedMethodFault)result[1]).getFault(); + if (fault instanceof RequestCanceled) { + s_logger.debug(taskName + " task of the entity " + entityName + " was successfully cancelled"); + } + } else { + s_logger.warn("Couldn't cancel " + taskName + " task of the entity " + entityName + " due to " + ((LocalizedMethodFault)result[1]).getLocalizedMessage()); + } + } + } }