diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java index 46f8f5e0429..c8fcf5f3df5 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java @@ -28,6 +28,12 @@ import com.cloud.vm.VirtualMachineProfile; /** */ public interface StoragePoolAllocator extends Adapter { + /** + * Overloaded method calls allocateToPool with bypassStorageTypeCheck = false + * and returns a list of pools suitable. + **/ + List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo); + /** * Determines which storage pools are suitable for the guest virtual machine * and returns a list of pools suitable. @@ -45,10 +51,12 @@ public interface StoragePoolAllocator extends Adapter { * @param ExcludeList * avoid * @param int returnUpTo (use -1 to return all possible pools) + * @param boolean bypassStorageTypeCheck allows bypassing useLocalStorage check for provided DiskProfile when true * @return List List of storage pools that are suitable for the * VM **/ - List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo); + List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck); + static int RETURN_UPTO_ALL = -1; diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java index 6caef20b2d7..a3bebfa332f 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -376,6 +376,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem tmpltTypeHyperSearch2.and("templateType", tmpltTypeHyperSearch2.entity().getTemplateType(), SearchCriteria.Op.EQ); tmpltTypeHyperSearch2.and("hypervisorType", tmpltTypeHyperSearch2.entity().getHypervisorType(), SearchCriteria.Op.EQ); tmpltTypeHyperSearch2.and("templateName", tmpltTypeHyperSearch2.entity().getName(), SearchCriteria.Op.EQ); + tmpltTypeHyperSearch2.and("state", tmpltTypeHyperSearch2.entity().getState(), SearchCriteria.Op.EQ); tmpltTypeSearch = createSearchBuilder(); tmpltTypeSearch.and("state", tmpltTypeSearch.entity().getState(), SearchCriteria.Op.EQ); @@ -897,6 +898,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem SearchCriteria sc = tmpltTypeHyperSearch2.create(); sc.setParameters("templateType", TemplateType.ROUTING); sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); if (templateName != null) { sc.setParameters("templateName", templateName); } @@ -911,6 +913,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem sc = tmpltTypeHyperSearch2.create(); sc.setParameters("templateType", TemplateType.SYSTEM); sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); if (templateName != null) { sc.setParameters("templateName", templateName); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index 59fe3f14d39..3375c6ff207 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -122,6 +122,8 @@ public interface PrimaryDataStoreDao extends GenericDao { List listLocalStoragePoolByPath(long datacenterId, String path); + List findPoolsInClusters(List clusterIds); + void deletePoolTags(long poolId); List listChildStoragePoolsInDatastoreCluster(long poolId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index 7830df57204..dfe1a6994d5 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -56,6 +56,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private final SearchBuilder DeleteLvmSearch; private final SearchBuilder DcLocalStorageSearch; private final GenericSearchBuilder StatusCountSearch; + private final SearchBuilder ClustersSearch; @Inject private StoragePoolDetailsDao _detailsDao; @@ -133,6 +134,10 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase DcLocalStorageSearch.and("scope", DcLocalStorageSearch.entity().getScope(), SearchCriteria.Op.EQ); DcLocalStorageSearch.done(); + ClustersSearch = createSearchBuilder(); + ClustersSearch.and("clusterIds", ClustersSearch.entity().getClusterId(), Op.IN); + ClustersSearch.and("status", ClustersSearch.entity().getStatus(), Op.EQ); + } @Override @@ -568,4 +573,12 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase sc.addAnd("removed", SearchCriteria.Op.NULL); return getCount(sc); } + + @Override + public List findPoolsInClusters(List clusterIds) { + SearchCriteria sc = ClustersSearch.create(); + sc.setParameters("clusterIds", clusterIds.toArray()); + sc.setParameters("status", StoragePoolStatus.Up); + return listBy(sc); + } } diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 2a1c2574431..7de40e924de 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -26,9 +26,6 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.exception.StorageUnavailableException; -import com.cloud.storage.StoragePoolStatus; - import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -42,10 +39,12 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.StorageUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.StorageUtil; import com.cloud.storage.Volume; import com.cloud.storage.dao.VolumeDao; @@ -87,11 +86,16 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return false; } - protected abstract List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo); + protected abstract List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck); @Override public List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { - List pools = select(dskCh, vmProfile, plan, avoid, returnUpTo); + return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, false); + } + + @Override + public List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { + List pools = select(dskCh, vmProfile, plan, avoid, returnUpTo, bypassStorageTypeCheck); return reorderPools(pools, vmProfile, plan); } diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java index 12884d5d62e..9967a2caff9 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java @@ -45,9 +45,13 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat DiskOfferingDao _diskOfferingDao; @Override - protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { s_logger.debug("ClusterScopeStoragePoolAllocator looking for storage pool"); + if (!bypassStorageTypeCheck && dskCh.useLocalStorage()) { + return null; + } + List suitablePools = new ArrayList(); long dcId = plan.getDataCenterId(); diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/GarbageCollectingStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/GarbageCollectingStoragePoolAllocator.java index b3a1c0d4b10..f02a89811b0 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/GarbageCollectingStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/GarbageCollectingStoragePoolAllocator.java @@ -47,7 +47,7 @@ public class GarbageCollectingStoragePoolAllocator extends AbstractStoragePoolAl boolean _storagePoolCleanupEnabled; @Override - public List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + public List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { s_logger.debug("GarbageCollectingStoragePoolAllocator looking for storage pool"); if (!_storagePoolCleanupEnabled) { s_logger.debug("Storage pool cleanup is not enabled, so GarbageCollectingStoragePoolAllocator is being skipped."); @@ -68,7 +68,7 @@ public class GarbageCollectingStoragePoolAllocator extends AbstractStoragePoolAl ExcludeList myAvoids = new ExcludeList(avoid.getDataCentersToAvoid(), avoid.getPodsToAvoid(), avoid.getClustersToAvoid(), avoid.getHostsToAvoid(), avoid.getPoolsToAvoid()); - return allocator.allocateToPool(dskCh, vmProfile, plan, myAvoids, returnUpTo); + return allocator.allocateToPool(dskCh, vmProfile, plan, myAvoids, returnUpTo, bypassStorageTypeCheck); } @Override diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java index 390272ea697..6fc4adaad0e 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/LocalStoragePoolAllocator.java @@ -60,10 +60,10 @@ public class LocalStoragePoolAllocator extends AbstractStoragePoolAllocator { ConfigurationDao _configDao; @Override - protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { s_logger.debug("LocalStoragePoolAllocator trying to find storage pool to fit the vm"); - if (!dskCh.useLocalStorage()) { + if (!bypassStorageTypeCheck && !dskCh.useLocalStorage()) { return null; } diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/UseLocalForRootAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/UseLocalForRootAllocator.java index e552a1bf2ca..4b150b26dc4 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/UseLocalForRootAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/UseLocalForRootAllocator.java @@ -39,12 +39,17 @@ public class UseLocalForRootAllocator extends LocalStoragePoolAllocator implemen @Override public List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + return allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, false); + } + + @Override + public List allocateToPool(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { DataCenterVO dc = _dcDao.findById(plan.getDataCenterId()); if (!dc.isLocalStorageEnabled()) { return null; } - return super.allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo); + return super.allocateToPool(dskCh, vmProfile, plan, avoid, returnUpTo, bypassStorageTypeCheck); } @Override diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java index 225f781489c..34955f104e0 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java @@ -49,9 +49,13 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator { private CapacityDao capacityDao; @Override - protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + protected List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { LOGGER.debug("ZoneWideStoragePoolAllocator to find storage pool"); + if (!bypassStorageTypeCheck && dskCh.useLocalStorage()) { + return null; + } + if (LOGGER.isTraceEnabled()) { // Log the pools details that are ignored because they are in disabled state List disabledPools = storagePoolDao.findDisabledPoolsByScope(plan.getDataCenterId(), null, null, ScopeType.ZONE); diff --git a/packaging/README.md b/packaging/README.md index 9c54ea61fc7..634b1cf98a2 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -6,9 +6,9 @@ These scripts are also used by the CloudStack team to build packages for the off # Requirements The RPM and DEB packages have dependencies on versions of specific libraries. Due to these dependencies the following distributions and their versions are supported by the packages. -* CentOS / RHEL: 7 +* CentOS / RHEL: 7 and 8 * Debian 7 (Wheezy) and 8 (Jessy) (untested!) -* Ubuntu: 16.04 (Xenial) and 18.04 (Bionic) +* Ubuntu: 16.04 (Xenial), 18.04 (Bionic) and 20.04 (Focal) # Building Using the scripts in the *packaging* directory the RPM and DEB packages can be build. diff --git a/plugins/ca/root-ca/src/test/java/org/apache/cloudstack/ca/provider/RootCAProviderTest.java b/plugins/ca/root-ca/src/test/java/org/apache/cloudstack/ca/provider/RootCAProviderTest.java index 04aba36c8be..f9fd531c4c2 100644 --- a/plugins/ca/root-ca/src/test/java/org/apache/cloudstack/ca/provider/RootCAProviderTest.java +++ b/plugins/ca/root-ca/src/test/java/org/apache/cloudstack/ca/provider/RootCAProviderTest.java @@ -41,7 +41,10 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.Mockito; + @RunWith(MockitoJUnitRunner.class) public class RootCAProviderTest { @@ -57,12 +60,6 @@ public class RootCAProviderTest { f.set(provider, o); } - private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException { - Field f = ConfigKey.class.getDeclaredField(name); - f.setAccessible(true); - f.set(configKey, o); - } - @Before public void setUp() throws Exception { caKeyPair = CertUtils.generateRandomKeyPair(1024); @@ -133,14 +130,16 @@ public class RootCAProviderTest { @Test public void testCreateSSLEngineWithoutAuthStrictness() throws Exception { - overrideDefaultConfigValue(RootCAProvider.rootCAAuthStrictness, "_defaultValue", "false"); + provider.rootCAAuthStrictness = Mockito.mock(ConfigKey.class); + Mockito.when(provider.rootCAAuthStrictness.value()).thenReturn(Boolean.FALSE); final SSLEngine e = provider.createSSLEngine(SSLUtils.getSSLContext(), "/1.2.3.4:5678", null); Assert.assertFalse(e.getNeedClientAuth()); } @Test public void testCreateSSLEngineWithAuthStrictness() throws Exception { - overrideDefaultConfigValue(RootCAProvider.rootCAAuthStrictness, "_defaultValue", "true"); + provider.rootCAAuthStrictness = Mockito.mock(ConfigKey.class); + Mockito.when(provider.rootCAAuthStrictness.value()).thenReturn(Boolean.TRUE); final SSLEngine e = provider.createSSLEngine(SSLUtils.getSSLContext(), "/1.2.3.4:5678", null); Assert.assertTrue(e.getNeedClientAuth()); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteVMSnapshotCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteVMSnapshotCommandWrapper.java index a0faa37ac12..5b55db24f4d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteVMSnapshotCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteVMSnapshotCommandWrapper.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; import org.libvirt.Connect; import org.libvirt.Domain; -import org.libvirt.DomainInfo.DomainState; +import org.libvirt.DomainInfo; import org.libvirt.DomainSnapshot; import org.libvirt.LibvirtException; @@ -52,18 +52,33 @@ public final class LibvirtDeleteVMSnapshotCommandWrapper extends CommandWrapper< final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr(); Domain dm = null; DomainSnapshot snapshot = null; + DomainInfo.DomainState oldState = null; + boolean tryingResume = false; + Connect conn = null; try { final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper(); - Connect conn = libvirtUtilitiesHelper.getConnection(); + conn = libvirtUtilitiesHelper.getConnection(); dm = libvirtComputingResource.getDomain(conn, vmName); snapshot = dm.snapshotLookupByName(cmd.getTarget().getSnapshotName()); - s_logger.debug("Suspending domain " + vmName); - dm.suspend(); // suspend the vm to avoid image corruption + oldState = dm.getInfo().state; + if (oldState == DomainInfo.DomainState.VIR_DOMAIN_RUNNING) { + s_logger.debug("Suspending domain " + vmName); + dm.suspend(); // suspend the vm to avoid image corruption + } snapshot.delete(0); // only remove this snapshot, not children + if (oldState == DomainInfo.DomainState.VIR_DOMAIN_RUNNING) { + // Resume the VM + tryingResume = true; + dm = libvirtComputingResource.getDomain(conn, vmName); + if (dm.getInfo().state == DomainInfo.DomainState.VIR_DOMAIN_PAUSED) { + dm.resume(); + } + } + return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); } catch (LibvirtException e) { String msg = " Delete VM snapshot failed due to " + e.toString(); @@ -97,21 +112,26 @@ public final class LibvirtDeleteVMSnapshotCommandWrapper extends CommandWrapper< } else if (snapshot == null) { s_logger.debug("Can not find vm snapshot " + cmd.getTarget().getSnapshotName() + " on vm: " + vmName + ", return true"); return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); + } else if (tryingResume) { + s_logger.error("Failed to resume vm after delete snapshot " + cmd.getTarget().getSnapshotName() + " on vm: " + vmName + " return true : " + e); + return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); } s_logger.warn(msg, e); return new DeleteVMSnapshotAnswer(cmd, false, msg); } finally { if (dm != null) { + // Make sure if the VM is paused, then resume it, in case we got an exception during our delete() and didn't have the chance before try { - if (dm.getInfo().state == DomainState.VIR_DOMAIN_PAUSED) { + dm = libvirtComputingResource.getDomain(conn, vmName); + if (oldState == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && dm.getInfo().state == DomainInfo.DomainState.VIR_DOMAIN_PAUSED) { s_logger.debug("Resuming domain " + vmName); dm.resume(); } dm.free(); - } catch (LibvirtException l) { - s_logger.trace("Ignoring libvirt error.", l); - }; + } catch (LibvirtException e) { + s_logger.error("Failed to resume vm after delete snapshot " + cmd.getTarget().getSnapshotName() + " on vm: " + vmName + " return true : " + e); + } } } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index d1d0f0c262f..09dc8b1f23e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -1057,7 +1057,7 @@ public class KVMStorageProcessor implements StorageProcessor { } } } catch (final Exception ex) { - s_logger.debug("Failed to delete snapshots on primary", ex); + s_logger.error("Failed to delete snapshots on primary", ex); } } diff --git a/plugins/storage-allocators/random/src/main/java/org/apache/cloudstack/storage/allocator/RandomStoragePoolAllocator.java b/plugins/storage-allocators/random/src/main/java/org/apache/cloudstack/storage/allocator/RandomStoragePoolAllocator.java index 6b912fb74aa..eed623a9ed0 100644 --- a/plugins/storage-allocators/random/src/main/java/org/apache/cloudstack/storage/allocator/RandomStoragePoolAllocator.java +++ b/plugins/storage-allocators/random/src/main/java/org/apache/cloudstack/storage/allocator/RandomStoragePoolAllocator.java @@ -35,7 +35,7 @@ public class RandomStoragePoolAllocator extends AbstractStoragePoolAllocator { private static final Logger s_logger = Logger.getLogger(RandomStoragePoolAllocator.class); @Override - public List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { + public List select(DiskProfile dskCh, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo, boolean bypassStorageTypeCheck) { List suitablePools = new ArrayList(); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 7c0cba346e9..3ef1c20980a 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.UUID; @@ -575,6 +576,8 @@ import com.cloud.alert.AlertManager; import com.cloud.alert.AlertVO; import com.cloud.alert.dao.AlertDao; import com.cloud.api.ApiDBUtils; +import com.cloud.api.query.dao.StoragePoolJoinDao; +import com.cloud.api.query.vo.StoragePoolJoinVO; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDao; @@ -662,6 +665,7 @@ import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.Volume; import com.cloud.storage.VolumeApiServiceImpl; import com.cloud.storage.VolumeVO; @@ -793,6 +797,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private PrimaryDataStoreDao _poolDao; @Inject + private StoragePoolJoinDao _poolJoinDao; + @Inject private NetworkDao _networkDao; @Inject private StorageManager _storageMgr; @@ -1149,7 +1155,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return new Pair, Integer>(result.first(), result.second()); } - private HypervisorType getHypervisorType(VMInstanceVO vm, StoragePool srcVolumePool, VirtualMachineProfile profile) { + private HypervisorType getHypervisorType(VMInstanceVO vm, StoragePool srcVolumePool) { HypervisorType type = null; if (vm == null) { StoragePoolVO poolVo = _poolDao.findById(srcVolumePool.getId()); @@ -1159,18 +1165,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe ClusterVO cluster = _clusterDao.findById(clusterId); type = cluster.getHypervisorType(); } - } else if (ScopeType.ZONE.equals(poolVo.getScope())) { - Long zoneId = poolVo.getDataCenterId(); - if (zoneId != null) { - DataCenterVO dc = _dcDao.findById(zoneId); - } } if (null == type) { type = srcVolumePool.getHypervisor(); } } else { - type = profile.getHypervisorType(); + type = vm.getHypervisorType(); } return type; } @@ -1508,11 +1509,17 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } StoragePool srcVolumePool = _poolDao.findById(volume.getPoolId()); - allPools = getAllStoragePoolsCompatibleWithVolumeSourceStoragePool(srcVolumePool); + HypervisorType hypervisorType = getHypervisorType(vm, srcVolumePool); + Pair> hostClusterPair = getVolumeVmHostClusters(srcVolumePool, vm, hypervisorType); + Host vmHost = hostClusterPair.first(); + List clusters = hostClusterPair.second(); + allPools = getAllStoragePoolCompatibleWithVolumeSourceStoragePool(srcVolumePool, hypervisorType, clusters); + allPools.remove(srcVolumePool); if (vm != null) { - suitablePools = findAllSuitableStoragePoolsForVm(volume, vm, srcVolumePool); + suitablePools = findAllSuitableStoragePoolsForVm(volume, vm, vmHost, srcVolumePool, + CollectionUtils.isNotEmpty(clusters) ? clusters.get(0) : null, hypervisorType); } else { - suitablePools = allPools; + suitablePools = findAllSuitableStoragePoolsForDetachedVolume(volume, allPools); } List avoidPools = new ArrayList<>(); if (srcVolumePool.getParent() != 0L) { @@ -1539,6 +1546,30 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } + private Pair> getVolumeVmHostClusters(StoragePool srcVolumePool, VirtualMachine vm, HypervisorType hypervisorType) { + Host host = null; + List clusters = new ArrayList<>(); + Long clusterId = srcVolumePool.getClusterId(); + if (vm != null) { + Long hostId = vm.getHostId(); + if (hostId == null) { + hostId = vm.getLastHostId(); + } + if (hostId != null) { + host = _hostDao.findById(hostId); + } + } + if (clusterId == null && host != null) { + clusterId = host.getClusterId(); + } + if (clusterId != null && vm != null) { + clusters.add(_clusterDao.findById(clusterId)); + } else { + clusters.addAll(_clusterDao.listByDcHyType(srcVolumePool.getDataCenterId(), hypervisorType.toString())); + } + return new Pair<>(host, clusters); + } + /** * This method looks for all storage pools that are compatible with the given volume. *
    @@ -1546,15 +1577,18 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe *
  • We also all storage available filtering by data center, pod and cluster as the current storage pool used by the given volume.
  • *
*/ - private List getAllStoragePoolsCompatibleWithVolumeSourceStoragePool(StoragePool srcVolumePool) { + private List getAllStoragePoolCompatibleWithVolumeSourceStoragePool(StoragePool srcVolumePool, HypervisorType hypervisorType, List clusters) { List storagePools = new ArrayList<>(); - // Storage pool with Zone Scope holds valid DataCenter Id only, Pod Id and Cluster Id are null - // Storage pool with Cluster/Host Scope holds valid DataCenter Id, Pod Id and Cluster Id - // Below methods call returns all the compatible pools with scope : ZONE, CLUSTER, HOST (as they are listed with Scope: null here) - List compatibleStoragePools = _poolDao.listBy(srcVolumePool.getDataCenterId(), srcVolumePool.getPodId(), srcVolumePool.getClusterId(), null); - if (CollectionUtils.isNotEmpty(compatibleStoragePools)) { - compatibleStoragePools.remove(srcVolumePool); - storagePools.addAll(compatibleStoragePools); + List zoneWideStoragePools = _poolDao.findZoneWideStoragePoolsByHypervisor(srcVolumePool.getDataCenterId(), hypervisorType); + if (CollectionUtils.isNotEmpty(zoneWideStoragePools)) { + storagePools.addAll(zoneWideStoragePools); + } + if (CollectionUtils.isNotEmpty(clusters)) { + List clusterIds = clusters.stream().map(Cluster::getId).collect(Collectors.toList()); + List clusterAndLocalStoragePools = _poolDao.findPoolsInClusters(clusterIds); + if (CollectionUtils.isNotEmpty(clusterAndLocalStoragePools)) { + storagePools.addAll(clusterAndLocalStoragePools); + } } return storagePools; @@ -1567,35 +1601,33 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe * * Side note: the idea behind this method is to provide power for administrators of manually overriding deployments defined by CloudStack. */ - private List findAllSuitableStoragePoolsForVm(final VolumeVO volume, VMInstanceVO vm, StoragePool srcVolumePool) { + private List findAllSuitableStoragePoolsForVm(final VolumeVO volume, VMInstanceVO vm, Host vmHost, StoragePool srcVolumePool, Cluster srcCluster, HypervisorType hypervisorType) { List suitablePools = new ArrayList<>(); - - HostVO host = _hostDao.findById(vm.getHostId()); - if (host == null) { - host = _hostDao.findById(vm.getLastHostId()); - } - ExcludeList avoid = new ExcludeList(); avoid.addPool(srcVolumePool.getId()); - - DataCenterDeployment plan = new DataCenterDeployment(volume.getDataCenterId(), srcVolumePool.getPodId(), srcVolumePool.getClusterId(), null, null, null); + Long clusterId = null; + Long podId = null; + if (srcCluster != null) { + clusterId = srcCluster.getId(); + podId = srcCluster.getPodId(); + } + DataCenterDeployment plan = new DataCenterDeployment(volume.getDataCenterId(), podId, clusterId, + null, null, null, null); VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); // OfflineVmwareMigration: vm might be null here; deal! - HypervisorType type = getHypervisorType(vm, srcVolumePool, profile); DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); - //This is an override mechanism so we can list the possible local storage pools that a volume in a shared pool might be able to be migrated to - DiskProfile diskProfile = new DiskProfile(volume, diskOffering, type); - diskProfile.setUseLocalStorage(true); + DiskProfile diskProfile = new DiskProfile(volume, diskOffering, hypervisorType); for (StoragePoolAllocator allocator : _storagePoolAllocators) { - List pools = allocator.allocateToPool(diskProfile, profile, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL); + List pools = allocator.allocateToPool(diskProfile, profile, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL, true); if (CollectionUtils.isEmpty(pools)) { continue; } for (StoragePool pool : pools) { - boolean isLocalPoolSameHostAsSourcePool = pool.isLocal() && StringUtils.equals(host.getPrivateIpAddress(), pool.getHostAddress()); - if (isLocalPoolSameHostAsSourcePool || pool.isShared()) { + boolean isLocalPoolSameHostAsVmHost = pool.isLocal() && + (vmHost == null || StringUtils.equals(vmHost.getPrivateIpAddress(), pool.getHostAddress())); + if (isLocalPoolSameHostAsVmHost || pool.isShared()) { suitablePools.add(pool); } } @@ -1603,6 +1635,29 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return suitablePools; } + private List findAllSuitableStoragePoolsForDetachedVolume(Volume volume, List allPools) { + List suitablePools = new ArrayList<>(); + if (CollectionUtils.isEmpty(allPools)) { + return suitablePools; + } + DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + List tags = new ArrayList<>(); + String[] tagsArray = diskOffering.getTagsArray(); + if (tagsArray != null && tagsArray.length > 0) { + tags = Arrays.asList(tagsArray); + } + Long[] poolIds = allPools.stream().map(StoragePool::getId).toArray(Long[]::new); + List pools = _poolJoinDao.searchByIds(poolIds); + for (StoragePoolJoinVO storagePool : pools) { + if (StoragePoolStatus.Up.equals(storagePool.getStatus()) && + (CollectionUtils.isEmpty(tags) || tags.contains(storagePool.getTag()))) { + Optional match = allPools.stream().filter(x -> x.getId() == storagePool.getId()).findFirst(); + match.ifPresent(suitablePools::add); + } + } + return suitablePools; + } + private Pair, Integer> searchForServers(final Long startIndex, final Long pageSize, final Object name, final Object type, final Object state, final Object zone, final Object pod, final Object cluster, final Object id, final Object keyword, final Object resourceState, final Object haHosts, final Object hypervisorType, final Object hypervisorVersion, final Object... excludes) { diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 29b57e2f3f5..e3fc40f9b31 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -53,6 +53,10 @@ export default { name: 'virtual.routers', component: () => import('@/views/network/RoutersTab.vue'), show: (record) => { return (record.type === 'Isolated' || record.type === 'Shared') && 'listRouters' in store.getters.apis } + }, { + name: 'guest.ip.range', + component: () => import('@/views/network/GuestIpRanges.vue'), + show: (record) => { return 'listVlanIpRanges' in store.getters.apis && (record.type === 'Shared' || (record.service && record.service.filter(x => x.name === 'SourceNat').count === 0)) } }], actions: [ { diff --git a/ui/src/core/lazy_lib/components_use.js b/ui/src/core/lazy_lib/components_use.js index b99af3c1874..e9b7c83e5d5 100644 --- a/ui/src/core/lazy_lib/components_use.js +++ b/ui/src/core/lazy_lib/components_use.js @@ -63,7 +63,9 @@ import { Pagination, Comment, Tree, - Calendar + Calendar, + Slider, + AutoComplete } from 'ant-design-vue' Vue.use(ConfigProvider) @@ -110,6 +112,8 @@ Vue.use(Pagination) Vue.use(Comment) Vue.use(Tree) Vue.use(Calendar) +Vue.use(Slider) +Vue.use(AutoComplete) Vue.prototype.$confirm = Modal.confirm Vue.prototype.$message = message diff --git a/ui/src/views/infra/ClusterAdd.vue b/ui/src/views/infra/ClusterAdd.vue index a0665fe92d3..f4c03fe96a0 100644 --- a/ui/src/views/infra/ClusterAdd.vue +++ b/ui/src/views/infra/ClusterAdd.vue @@ -216,11 +216,35 @@ export default { if (this.hypervisor === 'VMware') { this.clustertype = 'ExternalManaged' + if ((this.host === null || this.host.length === 0) && + (this.dataCenter === null || this.dataCenter.length === 0)) { + api('listVmwareDcs', { + zoneid: this.zoneId + }).then(response => { + var vmwaredcs = response.listvmwaredcsresponse.VMwareDC + if (vmwaredcs !== null) { + this.host = vmwaredcs[0].vcenter + this.dataCenter = vmwaredcs[0].name + } + this.addCluster() + }).catch(error => { + this.$notification.error({ + message: `${this.$t('label.error')} ${error.response.status}`, + description: error.response.data.listvmwaredcsresponse.errortext, + duration: 0 + }) + }) + return + } + } + this.addCluster() + }, + addCluster () { + if (this.hypervisor === 'VMware') { const clusternameVal = this.clustername this.url = `http://${this.host}/${this.dataCenter}/${clusternameVal}` this.clustername = `${this.host}/${this.dataCenter}/${clusternameVal}` } - this.loading = true this.parentToggleLoading() api('addCluster', {}, 'POST', { diff --git a/ui/src/views/infra/network/IpRangesTabGuest.vue b/ui/src/views/infra/network/IpRangesTabGuest.vue index f70d7a574f7..49d63f7cbf1 100644 --- a/ui/src/views/infra/network/IpRangesTabGuest.vue +++ b/ui/src/views/infra/network/IpRangesTabGuest.vue @@ -34,6 +34,11 @@ :rowKey="record => record.id" :pagination="false" > + + +
+
+ + + + {{ $t('label.gateway') }} + + + + + + + + + {{ $t('label.netmask') }} + + + + + + + + + + + {{ $t('label.startipv4') }} + + + + + + + + + + + {{ $t('label.endipv4') }} + + + + + + + + + + + {{ $t('label.ip6cidr') }} + + + + + + + + + {{ $t('label.ip6gateway') }} + + + + + + + + + + + {{ $t('label.startipv6') }} + + + + + + + + + + + {{ $t('label.endipv6') }} + + + + + + + + +
+ + {{ this.$t('label.cancel') }} + + + {{ this.$t('label.ok') }} + +
+
+
+
+
+ + + + + diff --git a/ui/src/views/network/GuestIpRanges.vue b/ui/src/views/network/GuestIpRanges.vue new file mode 100644 index 00000000000..a2e364af7a6 --- /dev/null +++ b/ui/src/views/network/GuestIpRanges.vue @@ -0,0 +1,196 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + +