From de095ba70d2a2261ffb2db7f3b8f29544939028e Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 15 Dec 2023 16:25:28 +0530 Subject: [PATCH 001/212] server: fix url check for storages without a valid url (#8353) Fixes #8352 Some managed storages may not need a valid URL to be passed. We can skip check and extraction of host or path from url of such storages. --- .../com/cloud/storage/StorageManagerImpl.java | 103 ++++++++++++------ .../cloud/storage/StorageManagerImplTest.java | 49 +++++++++ 2 files changed, 116 insertions(+), 36 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 4bc471c5084..04889d0b2a8 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -18,10 +18,12 @@ package com.cloud.storage; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; +import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.net.URISyntaxException; +import java.net.URLDecoder; import java.net.UnknownHostException; import java.nio.file.Files; import java.sql.PreparedStatement; @@ -132,8 +134,9 @@ import org.apache.cloudstack.storage.object.ObjectStore; import org.apache.cloudstack.storage.object.ObjectStoreEntity; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -254,8 +257,6 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.VMInstanceDao; import com.google.common.collect.Sets; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; @Component @@ -684,12 +685,21 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return true; } - private DataStore createLocalStorage(Map poolInfos) throws ConnectionException{ + protected String getValidatedPareForLocalStorage(Object obj, String paramName) { + String result = obj == null ? null : obj.toString(); + if (StringUtils.isEmpty(result)) { + throw new InvalidParameterValueException(String.format("Invalid %s provided", paramName)); + } + return result; + } + + protected DataStore createLocalStorage(Map poolInfos) throws ConnectionException{ Object existingUuid = poolInfos.get("uuid"); if( existingUuid == null ){ poolInfos.put("uuid", UUID.randomUUID().toString()); } - String hostAddress = poolInfos.get("host").toString(); + String hostAddress = getValidatedPareForLocalStorage(poolInfos.get("host"), "host"); + String hostPath = getValidatedPareForLocalStorage(poolInfos.get("hostPath"), "path"); Host host = _hostDao.findByName(hostAddress); if( host == null ) { @@ -708,8 +718,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C StoragePoolInfo pInfo = new StoragePoolInfo(poolInfos.get("uuid").toString(), host.getPrivateIpAddress(), - poolInfos.get("hostPath").toString(), - poolInfos.get("hostPath").toString(), + hostPath, + hostPath, StoragePoolType.Filesystem, capacityBytes, 0, @@ -809,6 +819,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C public PrimaryDataStoreInfo createPool(CreateStoragePoolCmd cmd) throws ResourceInUseException, IllegalArgumentException, UnknownHostException, ResourceUnavailableException { String providerName = cmd.getStorageProviderName(); Map uriParams = extractUriParamsAsMap(cmd.getUrl()); + boolean isFileScheme = "file".equals(uriParams.get("scheme")); DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(providerName); if (storeProvider == null) { @@ -822,7 +833,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C Long podId = cmd.getPodId(); Long zoneId = cmd.getZoneId(); - ScopeType scopeType = uriParams.get("scheme").toString().equals("file") ? ScopeType.HOST : ScopeType.CLUSTER; + ScopeType scopeType = ScopeType.CLUSTER; + if (isFileScheme) { + scopeType = ScopeType.HOST; + } String scope = cmd.getScope(); if (scope != null) { try { @@ -889,12 +903,14 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C params.put("managed", cmd.isManaged()); params.put("capacityBytes", cmd.getCapacityBytes()); params.put("capacityIops", cmd.getCapacityIops()); - params.putAll(uriParams); + if (MapUtils.isNotEmpty(uriParams)) { + params.putAll(uriParams); + } DataStoreLifeCycle lifeCycle = storeProvider.getDataStoreLifeCycle(); DataStore store = null; try { - if (params.get("scheme").toString().equals("file")) { + if (isFileScheme) { store = createLocalStorage(params); } else { store = lifeCycle.initialize(params); @@ -923,42 +939,55 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return (PrimaryDataStoreInfo)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Primary); } - private Map extractUriParamsAsMap(String url){ + protected Map extractUriParamsAsMap(String url) { Map uriParams = new HashMap<>(); - UriUtils.UriInfo uriInfo = UriUtils.getUriInfo(url); + UriUtils.UriInfo uriInfo; + try { + uriInfo = UriUtils.getUriInfo(url); + } catch (CloudRuntimeException cre) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("URI validation for url: %s failed, returning empty uri params", url)); + } + return uriParams; + } String scheme = uriInfo.getScheme(); String storageHost = uriInfo.getStorageHost(); String storagePath = uriInfo.getStoragePath(); - try { - if (scheme == null) { - throw new InvalidParameterValueException("scheme is null " + url + ", add nfs:// (or cifs://) as a prefix"); - } else if (scheme.equalsIgnoreCase("nfs")) { - if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) { - throw new InvalidParameterValueException("host or path is null, should be nfs://hostname/path"); - } - } else if (scheme.equalsIgnoreCase("cifs")) { - // Don't validate against a URI encoded URI. + if (scheme == null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Scheme for url: %s is not found, returning empty uri params", url)); + } + return uriParams; + } + boolean isHostOrPathBlank = StringUtils.isAnyBlank(storagePath, storageHost); + if (scheme.equalsIgnoreCase("nfs")) { + if (isHostOrPathBlank) { + throw new InvalidParameterValueException("host or path is null, should be nfs://hostname/path"); + } + } else if (scheme.equalsIgnoreCase("cifs")) { + // Don't validate against a URI encoded URI. + try { URI cifsUri = new URI(url); String warnMsg = UriUtils.getCifsUriParametersProblems(cifsUri); if (warnMsg != null) { throw new InvalidParameterValueException(warnMsg); } - } else if (scheme.equalsIgnoreCase("sharedMountPoint")) { - if (storagePath == null) { - throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path"); - } - } else if (scheme.equalsIgnoreCase("rbd")) { - if (storagePath == null) { - throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool"); - } - } else if (scheme.equalsIgnoreCase("gluster")) { - if (storageHost == null || storagePath == null || storageHost.trim().isEmpty() || storagePath.trim().isEmpty()) { - throw new InvalidParameterValueException("host or path is null, should be gluster://hostname/volume"); - } + } catch (URISyntaxException e) { + throw new InvalidParameterValueException(url + " is not a valid uri"); + } + } else if (scheme.equalsIgnoreCase("sharedMountPoint")) { + if (storagePath == null) { + throw new InvalidParameterValueException("host or path is null, should be sharedmountpoint://localhost/path"); + } + } else if (scheme.equalsIgnoreCase("rbd")) { + if (storagePath == null) { + throw new InvalidParameterValueException("host or path is null, should be rbd://hostname/pool"); + } + } else if (scheme.equalsIgnoreCase("gluster")) { + if (isHostOrPathBlank) { + throw new InvalidParameterValueException("host or path is null, should be gluster://hostname/volume"); } - } catch (URISyntaxException e) { - throw new InvalidParameterValueException(url + " is not a valid uri"); } String hostPath = null; @@ -975,7 +1004,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C uriParams.put("host", storageHost); uriParams.put("hostPath", hostPath); uriParams.put("userInfo", uriInfo.getUserInfo()); - uriParams.put("port", uriInfo.getPort() + ""); + if (uriInfo.getPort() > 0) { + uriParams.put("port", uriInfo.getPort() + ""); + } return uriParams; } diff --git a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java index 478547bff1e..ba5f2baf932 100644 --- a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java @@ -17,12 +17,15 @@ package com.cloud.storage; import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.exception.ConnectionException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; import com.cloud.storage.dao.VolumeDao; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.commons.collections.MapUtils; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -33,7 +36,9 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @RunWith(MockitoJUnitRunner.class) public class StorageManagerImplTest { @@ -157,4 +162,48 @@ public class StorageManagerImplTest { } + @Test + public void testExtractUriParamsAsMapWithSolidFireUrl() { + String sfUrl = "MVIP=1.2.3.4;SVIP=6.7.8.9;clusterAdminUsername=admin;" + + "clusterAdminPassword=password;clusterDefaultMinIops=1000;" + + "clusterDefaultMaxIops=2000;clusterDefaultBurstIopsPercentOfMaxIops=2"; + Map uriParams = storageManagerImpl.extractUriParamsAsMap(sfUrl); + Assert.assertTrue(MapUtils.isEmpty(uriParams)); + } + + @Test + public void testExtractUriParamsAsMapWithNFSUrl() { + String scheme = "nfs"; + String host = "HOST"; + String path = "/PATH"; + String sfUrl = String.format("%s://%s%s", scheme, host, path); + Map uriParams = storageManagerImpl.extractUriParamsAsMap(sfUrl); + Assert.assertTrue(MapUtils.isNotEmpty(uriParams)); + Assert.assertEquals(scheme, uriParams.get("scheme")); + Assert.assertEquals(host, uriParams.get("host")); + Assert.assertEquals(path, uriParams.get("hostPath")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateLocalStorageHostFailure() { + Map test = new HashMap<>(); + test.put("host", null); + try { + storageManagerImpl.createLocalStorage(test); + } catch (ConnectionException e) { + throw new RuntimeException(e); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateLocalStoragePathFailure() { + Map test = new HashMap<>(); + test.put("host", "HOST"); + test.put("hostPath", ""); + try { + storageManagerImpl.createLocalStorage(test); + } catch (ConnectionException e) { + throw new RuntimeException(e); + } + } } From dda672503f250233ad827d714333bd62f6bfe172 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 15 Dec 2023 21:43:32 +1000 Subject: [PATCH 002/212] Remove unneeded duplicate words (#8358) This PR removes some unneeded duplicate words. --- .../engine/orchestration/NetworkOrchestrator.java | 2 +- .../api/commands/ConfigureSimulatorHAProviderState.java | 4 ++-- .../cloud/hypervisor/vmware/resource/VmwareResource.java | 2 +- .../com/cloud/storage/resource/VmwareStorageProcessor.java | 2 +- test/integration/component/test_escalations_instances.py | 6 +++--- usage/src/main/java/com/cloud/usage/UsageManagerImpl.java | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 30fd35e5f37..c6396dbdede 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -3248,7 +3248,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // get updated state for the network network = _networksDao.findById(networkId); if (network.getState() != Network.State.Allocated && network.getState() != Network.State.Setup && !forced) { - s_logger.debug("Network is not not in the correct state to be destroyed: " + network.getState()); + s_logger.debug("Network is not in the correct state to be destroyed: " + network.getState()); return false; } diff --git a/plugins/hypervisors/simulator/src/main/java/com/cloud/api/commands/ConfigureSimulatorHAProviderState.java b/plugins/hypervisors/simulator/src/main/java/com/cloud/api/commands/ConfigureSimulatorHAProviderState.java index 1d68a184a5a..ef6f2db3391 100644 --- a/plugins/hypervisors/simulator/src/main/java/com/cloud/api/commands/ConfigureSimulatorHAProviderState.java +++ b/plugins/hypervisors/simulator/src/main/java/com/cloud/api/commands/ConfigureSimulatorHAProviderState.java @@ -69,12 +69,12 @@ public final class ConfigureSimulatorHAProviderState extends BaseCmd { private Boolean activity; @Parameter(name = ApiConstants.RECOVER, type = CommandType.BOOLEAN, - description = "Set true is haprovider for simulator host should be be recoverable", + description = "Set true is haprovider for simulator host should be recoverable", required = true) private Boolean recovery; @Parameter(name = ApiConstants.FENCE, type = CommandType.BOOLEAN, - description = "Set true is haprovider for simulator host should be be fence-able", + description = "Set true is haprovider for simulator host should be fence-able", required = true) private Boolean fenceable; 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 6c457273bc3..a01ddcc81ce 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 @@ -3542,7 +3542,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes if (diskInfo == null) { diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(infoInChain.getDiskDeviceBusName()); if (diskInfo != null) { - s_logger.info("Found existing disk from from chain device bus information: " + infoInChain.getDiskDeviceBusName()); + s_logger.info("Found existing disk from chain device bus information: " + infoInChain.getDiskDeviceBusName()); return diskInfo; } } 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 2cf1663e677..be37c16a0ca 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 @@ -2209,7 +2209,7 @@ public class VmwareStorageProcessor implements StorageProcessor { if (diskInfo == null) { diskInfo = diskInfoBuilder.getDiskInfoByDeviceBusName(infoInChain.getDiskDeviceBusName()); if (diskInfo != null) { - s_logger.info("Found existing disk from from chain device bus information: " + infoInChain.getDiskDeviceBusName()); + s_logger.info("Found existing disk from chain device bus information: " + infoInChain.getDiskDeviceBusName()); return diskInfo; } } diff --git a/test/integration/component/test_escalations_instances.py b/test/integration/component/test_escalations_instances.py index 0004cce0b35..89c4f4ce2a6 100644 --- a/test/integration/component/test_escalations_instances.py +++ b/test/integration/component/test_escalations_instances.py @@ -1244,7 +1244,7 @@ class TestListInstances(cloudstackTestCase): status[0], "Listing VM's by name and zone failed" ) - # Verifying Verifying that the size of the list is 1 + # Verifying that the size of the list is 1 self.assertEqual( 1, len(list_vms), @@ -1404,7 +1404,7 @@ class TestListInstances(cloudstackTestCase): status[0], "Listing VM's by name and zone failed" ) - # Verifying Verifying that the size of the list is 1 + # Verifying that the size of the list is 1 self.assertEqual( 1, len(list_vms), @@ -1474,7 +1474,7 @@ class TestListInstances(cloudstackTestCase): status[0], "Listing VM's by name, account and zone failed" ) - # Verifying Verifying that the size of the list is 1 + # Verifying that the size of the list is 1 self.assertEqual( 1, len(list_vms), diff --git a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java index 37906496059..c027b7074fb 100644 --- a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java +++ b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java @@ -1899,7 +1899,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna if (usageVpnUsers.size() > 0) { s_logger.debug(String.format("We do not need to create the usage VPN user [%s] assigned to account [%s] because it already exists.", userId, accountId)); } else { - s_logger.debug(String.format("Creating VPN user user [%s] assigned to account [%s] domain [%s], zone [%s], and created at [%s]", userId, accountId, domainId, zoneId, + s_logger.debug(String.format("Creating VPN user [%s] assigned to account [%s] domain [%s], zone [%s], and created at [%s]", userId, accountId, domainId, zoneId, event.getCreateDate())); UsageVPNUserVO vpnUser = new UsageVPNUserVO(zoneId, accountId, domainId, userId, event.getResourceName(), event.getCreateDate(), null); _usageVPNUserDao.persist(vpnUser); From 127fd9d2f06ebb2938b482e95914a32ef788e57e Mon Sep 17 00:00:00 2001 From: sato03 Date: Fri, 15 Dec 2023 09:12:24 -0300 Subject: [PATCH 003/212] UI: Project column in Default View (#8287) The Default View has a projects toggle button that allows users to see which resources belong to projects, but there is nothing to indicate which project they belong to. For this reason, a project column was added if the projects button is enabled, indicating the name of the project to which the resource belongs. --------- Co-authored-by: Henrique Sato --- ui/src/components/view/ListView.vue | 4 +++ ui/src/config/section/compute.js | 44 +++++++++++++++++++++++++---- ui/src/config/section/event.js | 11 +++++++- ui/src/config/section/image.js | 6 ++++ ui/src/config/section/network.js | 41 ++++++++++++++++++++++++--- ui/src/config/section/storage.js | 11 ++++++-- ui/src/views/AutogenView.vue | 3 ++ 7 files changed, 108 insertions(+), 12 deletions(-) diff --git a/ui/src/components/view/ListView.vue b/ui/src/components/view/ListView.vue index 53c6efb321d..a61f1930f71 100644 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@ -330,6 +330,10 @@ {{ text }} {{ text }} + diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index b56c8eeead9..4cb9ed8e2ba 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -61,16 +61,18 @@ export default { if (store.getters.metrics) { fields.push(...metricsFields) } - if (store.getters.userInfo.roletype === 'Admin') { fields.splice(2, 0, 'instancename') - fields.push('account') fields.push('hostname') + fields.push('account') } else if (store.getters.userInfo.roletype === 'DomainAdmin') { fields.push('account') } else { fields.push('serviceofferingname') } + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('zonename') return fields }, @@ -464,8 +466,13 @@ export default { columns: () => { const fields = ['displayname', 'state', 'name', 'type', 'current', 'parentName', 'created'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { - fields.push('domain') fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('domain') + } else if (store.getters.listAllProjects) { + fields.push('project') } return fields }, @@ -536,6 +543,9 @@ export default { if (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)) { fields.push('account') } + if (store.listAllProjects) { + fields.push('project') + } if (store.apis.scaleKubernetesCluster.params.filter(x => x.name === 'autoscalingenabled').length > 0) { fields.splice(2, 0, 'autoscalingenabled') } @@ -633,7 +643,13 @@ export default { docHelp: 'adminguide/autoscale_without_netscaler.html', resourceType: 'AutoScaleVmGroup', permission: ['listAutoScaleVmGroups'], - columns: ['name', 'state', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'account'], + columns: (store) => { + var fields = ['name', 'state', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'account'] + if (store.listAllProjects) { + fields.push('project') + } + return fields + }, details: ['name', 'id', 'account', 'domain', 'associatednetworkname', 'associatednetworkid', 'lbruleid', 'lbprovider', 'publicip', 'publicipid', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'interval', 'state', 'created'], related: [{ name: 'vm', @@ -737,7 +753,15 @@ export default { docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group', resourceType: 'VMInstanceGroup', permission: ['listInstanceGroups'], - columns: ['name', 'account', 'domain'], + + columns: (store) => { + var fields = ['name', 'account'] + if (store.listAllProjects) { + fields.push('project') + } + fields.push('domain') + return fields + }, details: ['name', 'id', 'account', 'domain', 'created'], related: [{ name: 'vm', @@ -791,7 +815,12 @@ export default { var fields = ['name', 'fingerprint'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('domain') + } else if (store.getters.listAllProjects) { + fields.push('project') } return fields }, @@ -941,7 +970,12 @@ export default { var fields = ['name', 'type', 'description'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('domain') + } else if (store.getters.listAllProjects) { + fields.push('project') } return fields }, diff --git a/ui/src/config/section/event.js b/ui/src/config/section/event.js index 5f4b27b88ed..5ab87e86964 100644 --- a/ui/src/config/section/event.js +++ b/ui/src/config/section/event.js @@ -15,13 +15,22 @@ // specific language governing permissions and limitations // under the License. +import store from '@/store' + export default { name: 'event', title: 'label.events', icon: 'ScheduleOutlined', docHelp: 'adminguide/events.html', permission: ['listEvents'], - columns: ['level', 'type', 'state', 'description', 'resource', 'username', 'account', 'domain', 'created'], + columns: () => { + var fields = ['level', 'type', 'state', 'description', 'resource', 'username', 'account'] + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push(...['domain', 'created']) + return fields + }, details: ['username', 'id', 'description', 'resourcetype', 'resourceid', 'state', 'level', 'type', 'account', 'domain', 'created'], searchFilters: ['level', 'domainid', 'account', 'keyword', 'resourcetype'], related: [{ diff --git a/ui/src/config/section/image.js b/ui/src/config/section/image.js index 792e47f021f..7a5d52d1b89 100644 --- a/ui/src/config/section/image.js +++ b/ui/src/config/section/image.js @@ -47,6 +47,9 @@ export default { fields.push('size') fields.push('account') } + if (store.getters.listAllProjects) { + fields.push('project') + } if (['Admin'].includes(store.getters.userInfo.roletype)) { fields.push('templatetype') fields.push('order') @@ -220,6 +223,9 @@ export default { fields.push('size') fields.push('account') } + if (store.getters.listAllProjects) { + fields.push('project') + } if (['Admin'].includes(store.getters.userInfo.roletype)) { fields.push('order') } diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 7d7313ffb25..e1a0e69b57e 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -34,10 +34,16 @@ export default { permission: ['listNetworks'], resourceType: 'Network', columns: () => { - var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'domainpath', 'account', 'zonename'] + var fields = ['name', 'state', 'type', 'vpcname', 'cidr', 'ip6cidr', 'broadcasturi', 'domainpath'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } + if (store.getters.listAllProjects) { + fields.push('project') + } else { + fields.push('account') + } + fields.push('zonename') return fields }, details: () => { @@ -197,7 +203,14 @@ export default { docHelp: 'adminguide/networking_and_traffic.html#configuring-a-virtual-private-cloud', permission: ['listVPCs'], resourceType: 'Vpc', - columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'domain', 'zonename'], + columns: () => { + var fields = ['name', 'state', 'displaytext', 'cidr', 'account'] + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push(...['domain', 'zonename']) + return fields + }, details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'], related: [{ @@ -334,10 +347,16 @@ export default { if (store.getters.userInfo.roletype === 'Admin') { fields.splice(2, 0, 'instancename') fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('domain') fields.push('hostname') } else if (store.getters.userInfo.roletype === 'DomainAdmin') { fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } } else { fields.push('serviceofferingname') } @@ -730,7 +749,14 @@ export default { docHelp: 'adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts', permission: ['listPublicIpAddresses'], resourceType: 'PublicIpAddress', - columns: ['ipaddress', 'state', 'associatednetworkname', 'vpcname', 'virtualmachinename', 'allocated', 'account', 'domain', 'zonename'], + columns: () => { + var fields = ['ipaddress', 'state', 'associatednetworkname', 'vpcname', 'virtualmachinename', 'allocated', 'account'] + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push(...['domain', 'zonename']) + return fields + }, details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'], filters: ['allocated', 'reserved', 'free'], component: shallowRef(() => import('@/views/network/PublicIpResource.vue')), @@ -1120,7 +1146,14 @@ export default { title: 'label.vpncustomergatewayid', icon: 'lock-outlined', permission: ['listVpnCustomerGateways'], - columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account', 'domain'], + columns: () => { + var fields = ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account'] + if (store.getters.listAllProjects) { + fields.push('project') + } + fields.push('domain') + return fields + }, details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'account', 'domain'], searchFilters: ['keyword', 'domainid', 'account'], resourceType: 'VPNCustomerGateway', diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 0fbb930e750..a096067b135 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -49,13 +49,15 @@ export default { if (store.getters.metrics) { fields.push(...metricsFields) } - if (store.getters.userInfo.roletype === 'Admin') { - fields.push('account') fields.push('storage') + fields.push('account') } else if (store.getters.userInfo.roletype === 'DomainAdmin') { fields.push('account') } + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('zonename') return fields @@ -320,7 +322,12 @@ export default { var fields = ['name', 'state', 'volumename', 'intervaltype', 'physicalsize', 'created'] if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) { fields.push('account') + if (store.getters.listAllProjects) { + fields.push('project') + } fields.push('domain') + } else if (store.getters.listAllProjects) { + fields.push('project') } fields.push('zonename') return fields diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 6c9c13dbf77..bf1c42d4c05 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -879,6 +879,9 @@ export default { this.$store.getters.customColumns[this.$store.getters.userInfo.id][this.$route.path] = this.selectedColumns } else { this.selectedColumns = this.$store.getters.customColumns[this.$store.getters.userInfo.id][this.$route.path] || this.selectedColumns + if (this.$store.getters.listAllProjects && !this.projectView) { + this.selectedColumns.push('project') + } this.updateSelectedColumns() } } From af872224d6a4cfff16486f09ab522e5dfeb1c9dc Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Sat, 16 Dec 2023 16:18:20 +0530 Subject: [PATCH 004/212] =?UTF-8?q?README:=20that=20time=20of=20the=20year?= =?UTF-8?q?!=20=F0=9F=8E=84=20(#8365)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rohit Yadav --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e193913612f..7e91acb3348 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Apache CloudStack [![Build Status](https://github.com/apache/cloudstack/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/build.yml) [![UI Build](https://github.com/apache/cloudstack/actions/workflows/ui.yml/badge.svg)](https://github.com/apache/cloudstack/actions/workflows/ui.yml) [![License Check](https://github.com/apache/cloudstack/actions/workflows/rat.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/rat.yml) [![Simulator CI](https://github.com/apache/cloudstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/ci.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=apache_cloudstack&metric=alert_status)](https://sonarcloud.io/dashboard?id=apache_cloudstack) [![codecov](https://codecov.io/gh/apache/cloudstack/branch/main/graph/badge.svg)](https://codecov.io/gh/apache/cloudstack) -[![Apache CloudStack](tools/logo/apache_cloudstack.png)](https://cloudstack.apache.org/) +[![Apache CloudStack](tools/logo/acsxmas.jpg)](https://cloudstack.apache.org/) Apache CloudStack is open source software designed to deploy and manage large networks of virtual machines, as a highly available, highly scalable From 16d45f731d7ae7d49287d33f21c2eb7ae141707e Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Mon, 18 Dec 2023 03:36:31 -0300 Subject: [PATCH 005/212] Save the account which deliberately removed a public IP from quarantine (#8339) When a public IP gets removed from quarantine, the removal reason gets saved to the database; however, it may also be useful for operators to know who removed the public IP from quarantine. For that reason, this PR extends the public IP quarantine feature so that the account that deliberately removed an IP from quarantine also gets saved to the database. --- .../com/cloud/network/PublicIpQuarantine.java | 2 + .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/response/IpQuarantineResponse.java | 12 ++++ .../network/vo/PublicIpQuarantineVO.java | 12 ++++ .../upgrade/dao/Upgrade41810to41900.java | 4 ++ .../META-INF/db/schema-41810to41900.sql | 3 + .../java/com/cloud/api/ApiResponseHelper.java | 4 ++ .../cloud/network/IpAddressManagerImpl.java | 2 + .../com/cloud/api/ApiResponseHelperTest.java | 56 +++++++++++++++++++ .../cloud/network/IpAddressManagerTest.java | 27 +++++++++ 10 files changed, 123 insertions(+) diff --git a/api/src/main/java/com/cloud/network/PublicIpQuarantine.java b/api/src/main/java/com/cloud/network/PublicIpQuarantine.java index d1ec98afe46..625a1bbbe50 100644 --- a/api/src/main/java/com/cloud/network/PublicIpQuarantine.java +++ b/api/src/main/java/com/cloud/network/PublicIpQuarantine.java @@ -30,6 +30,8 @@ public interface PublicIpQuarantine extends InternalIdentity, Identity { String getRemovalReason(); + Long getRemoverAccountId(); + Date getRemoved(); Date getCreated(); 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 d7767721667..3ae0f319189 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -803,6 +803,7 @@ public class ApiConstants { public static final String IPSEC_PSK = "ipsecpsk"; public static final String GUEST_IP = "guestip"; public static final String REMOVED = "removed"; + public static final String REMOVER_ACCOUNT_ID = "removeraccountid"; public static final String REMOVAL_REASON = "removalreason"; public static final String COMPLETED = "completed"; public static final String IKE_VERSION = "ikeversion"; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IpQuarantineResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IpQuarantineResponse.java index 55720296315..c93f259382c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IpQuarantineResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IpQuarantineResponse.java @@ -60,6 +60,10 @@ public class IpQuarantineResponse extends BaseResponse { @Param(description = "The reason for removing the IP from quarantine prematurely.") private String removalReason; + @SerializedName(ApiConstants.REMOVER_ACCOUNT_ID) + @Param(description = "ID of the account that removed the IP from quarantine.") + private String removerAccountId; + public IpQuarantineResponse() { super("quarantinedips"); } @@ -127,4 +131,12 @@ public class IpQuarantineResponse extends BaseResponse { public void setRemovalReason(String removalReason) { this.removalReason = removalReason; } + + public String getRemoverAccountId() { + return removerAccountId; + } + + public void setRemoverAccountId(String removerAccountId) { + this.removerAccountId = removerAccountId; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vo/PublicIpQuarantineVO.java b/engine/schema/src/main/java/com/cloud/network/vo/PublicIpQuarantineVO.java index 56d167a0060..89e02610bd2 100644 --- a/engine/schema/src/main/java/com/cloud/network/vo/PublicIpQuarantineVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vo/PublicIpQuarantineVO.java @@ -63,6 +63,9 @@ public class PublicIpQuarantineVO implements PublicIpQuarantine { @Column(name = "removal_reason") private String removalReason = null; + @Column(name = "remover_account_id") + private Long removerAccountId = null; + public PublicIpQuarantineVO() { } @@ -98,6 +101,11 @@ public class PublicIpQuarantineVO implements PublicIpQuarantine { return removalReason; } + @Override + public Long getRemoverAccountId() { + return this.removerAccountId; + } + @Override public String getUuid() { return uuid; @@ -111,6 +119,10 @@ public class PublicIpQuarantineVO implements PublicIpQuarantine { this.removalReason = removalReason; } + public void setRemoverAccountId(Long removerAccountId) { + this.removerAccountId = removerAccountId; + } + @Override public Date getRemoved() { return removed; diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java index bdfe58cbf89..13e30c0f6e2 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade41810to41900.java @@ -77,6 +77,7 @@ public class Upgrade41810to41900 implements DbUpgrade, DbUpgradeSystemVmTemplate decryptConfigurationValuesFromAccountAndDomainScopesNotInSecureHiddenCategories(conn); migrateBackupDates(conn); addIndexes(conn); + addRemoverAccountIdForeignKeyToQuarantinedIps(conn); } @Override @@ -262,4 +263,7 @@ public class Upgrade41810to41900 implements DbUpgrade, DbUpgradeSystemVmTemplate DbUpgradeUtils.addIndexIfNeeded(conn, "event", "resource_type", "resource_id"); } + private void addRemoverAccountIdForeignKeyToQuarantinedIps(Connection conn) { + DbUpgradeUtils.addForeignKey(conn, "quarantined_ips", "remover_account_id", "account", "id"); + } } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql index 27170fcac14..308d48a311c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql @@ -314,3 +314,6 @@ CREATE TABLE `cloud_usage`.`bucket_statistics` ( `size` bigint unsigned COMMENT 'total size of bucket objects', PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add remover account ID to quarantined IPs table. +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.quarantined_ips', 'remover_account_id', 'bigint(20) unsigned DEFAULT NULL COMMENT "ID of the account that removed the IP from quarantine, foreign key to `account` table"'); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 5424a36cdd8..e2b72f6175c 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -5200,6 +5200,10 @@ public class ApiResponseHelper implements ResponseGenerator { quarantinedIpsResponse.setRemoved(quarantinedIp.getRemoved()); quarantinedIpsResponse.setEndDate(quarantinedIp.getEndDate()); quarantinedIpsResponse.setRemovalReason(quarantinedIp.getRemovalReason()); + if (quarantinedIp.getRemoverAccountId() != null) { + Account removerAccount = _accountMgr.getAccount(quarantinedIp.getRemoverAccountId()); + quarantinedIpsResponse.setRemoverAccountId(removerAccount.getUuid()); + } quarantinedIpsResponse.setResponseName("quarantinedip"); return quarantinedIpsResponse; diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 75ea572491e..b5908935350 100644 --- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java @@ -2471,9 +2471,11 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage PublicIpQuarantineVO publicIpQuarantineVO = publicIpQuarantineDao.findById(quarantineProcessId); Ip ipAddress = _ipAddressDao.findById(publicIpQuarantineVO.getPublicIpAddressId()).getAddress(); Date removedDate = new Date(); + Long removerAccountId = CallContext.current().getCallingAccountId(); publicIpQuarantineVO.setRemoved(removedDate); publicIpQuarantineVO.setRemovalReason(removalReason); + publicIpQuarantineVO.setRemoverAccountId(removerAccountId); s_logger.debug(String.format("Removing public IP Address [%s] from quarantine by updating the removed date to [%s].", ipAddress, removedDate)); publicIpQuarantineDao.persist(publicIpQuarantineVO); diff --git a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java index f681669c789..1bea3ac78f3 100644 --- a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java +++ b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java @@ -17,10 +17,12 @@ package com.cloud.api; import com.cloud.domain.DomainVO; +import com.cloud.network.PublicIpQuarantine; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmGroupVO; import com.cloud.network.as.AutoScaleVmProfileVO; import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao; +import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkServiceMapDao; @@ -41,6 +43,7 @@ import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; +import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.UnmanagedInstanceResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; @@ -97,6 +100,9 @@ public class ApiResponseHelperTest { @Mock UserDataDao userDataDaoMock; + @Mock + IPAddressDao ipAddressDaoMock; + @Spy @InjectMocks ApiResponseHelper apiResponseHelper = new ApiResponseHelper(); @@ -396,4 +402,54 @@ public class ApiResponseHelperTest { Assert.assertEquals(1, response.getDisks().size()); Assert.assertEquals(1, response.getNics().size()); } + + @Test + public void createQuarantinedIpsResponseTestReturnsObject() { + String quarantinedIpUuid = "quarantined_ip_uuid"; + Long previousOwnerId = 300L; + String previousOwnerUuid = "previous_owner_uuid"; + String previousOwnerName = "previous_owner_name"; + Long removerAccountId = 400L; + String removerAccountUuid = "remover_account_uuid"; + Long publicIpAddressId = 500L; + String publicIpAddress = "1.2.3.4"; + Date created = new Date(599L); + Date removed = new Date(600L); + Date endDate = new Date(601L); + String removalReason = "removalReason"; + + PublicIpQuarantine quarantinedIpMock = Mockito.mock(PublicIpQuarantine.class); + IPAddressVO ipAddressVoMock = Mockito.mock(IPAddressVO.class); + Account previousOwner = Mockito.mock(Account.class); + Account removerAccount = Mockito.mock(Account.class); + + Mockito.when(quarantinedIpMock.getUuid()).thenReturn(quarantinedIpUuid); + Mockito.when(quarantinedIpMock.getPreviousOwnerId()).thenReturn(previousOwnerId); + Mockito.when(quarantinedIpMock.getPublicIpAddressId()).thenReturn(publicIpAddressId); + Mockito.doReturn(ipAddressVoMock).when(ipAddressDaoMock).findById(publicIpAddressId); + Mockito.when(ipAddressVoMock.getAddress()).thenReturn(new Ip(publicIpAddress)); + Mockito.doReturn(previousOwner).when(accountManagerMock).getAccount(previousOwnerId); + Mockito.when(previousOwner.getUuid()).thenReturn(previousOwnerUuid); + Mockito.when(previousOwner.getName()).thenReturn(previousOwnerName); + Mockito.when(quarantinedIpMock.getCreated()).thenReturn(created); + Mockito.when(quarantinedIpMock.getRemoved()).thenReturn(removed); + Mockito.when(quarantinedIpMock.getEndDate()).thenReturn(endDate); + Mockito.when(quarantinedIpMock.getRemovalReason()).thenReturn(removalReason); + Mockito.when(quarantinedIpMock.getRemoverAccountId()).thenReturn(removerAccountId); + Mockito.when(removerAccount.getUuid()).thenReturn(removerAccountUuid); + Mockito.doReturn(removerAccount).when(accountManagerMock).getAccount(removerAccountId); + + IpQuarantineResponse result = apiResponseHelper.createQuarantinedIpsResponse(quarantinedIpMock); + + Assert.assertEquals(quarantinedIpUuid, result.getId()); + Assert.assertEquals(publicIpAddress, result.getPublicIpAddress()); + Assert.assertEquals(previousOwnerUuid, result.getPreviousOwnerId()); + Assert.assertEquals(previousOwnerName, result.getPreviousOwnerName()); + Assert.assertEquals(created, result.getCreated()); + Assert.assertEquals(removed, result.getRemoved()); + Assert.assertEquals(endDate, result.getEndDate()); + Assert.assertEquals(removalReason, result.getRemovalReason()); + Assert.assertEquals(removerAccountUuid, result.getRemoverAccountId()); + Assert.assertEquals("quarantinedip", result.getResponseName()); + } } diff --git a/server/src/test/java/com/cloud/network/IpAddressManagerTest.java b/server/src/test/java/com/cloud/network/IpAddressManagerTest.java index 935fb4e8c3b..5b8399ad000 100644 --- a/server/src/test/java/com/cloud/network/IpAddressManagerTest.java +++ b/server/src/test/java/com/cloud/network/IpAddressManagerTest.java @@ -36,12 +36,14 @@ import com.cloud.network.dao.PublicIpQuarantineDao; import com.cloud.network.vo.PublicIpQuarantineVO; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import org.apache.cloudstack.context.CallContext; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; @@ -397,6 +399,31 @@ public class IpAddressManagerTest { Assert.assertFalse(result); } + @Test + public void removePublicIpAddressFromQuarantineTestPersistsObject() { + Long quarantineProcessId = 100L; + Long publicAddressId = 200L; + Long callingAccountId = 300L; + String removalReason = "removalReason"; + + try (MockedStatic callContextMockedStatic = Mockito.mockStatic(CallContext.class)) { + Mockito.doReturn(publicIpQuarantineVOMock).when(publicIpQuarantineDaoMock).findById(quarantineProcessId); + Mockito.when(publicIpQuarantineVOMock.getPublicIpAddressId()).thenReturn(publicAddressId); + Mockito.doReturn(ipAddressVO).when(ipAddressDao).findById(publicAddressId); + + CallContext callContextMock = Mockito.mock(CallContext.class); + Mockito.when(callContextMock.getCallingAccountId()).thenReturn(callingAccountId); + callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock); + + ipAddressManager.removePublicIpAddressFromQuarantine(quarantineProcessId, removalReason); + + Mockito.verify(publicIpQuarantineVOMock).setRemoved(Mockito.any()); + Mockito.verify(publicIpQuarantineVOMock).setRemovalReason(removalReason); + Mockito.verify(publicIpQuarantineVOMock).setRemoverAccountId(callingAccountId); + Mockito.verify(publicIpQuarantineDaoMock).persist(publicIpQuarantineVOMock); + } + } + @Test public void updateSourceNatIpAddress() throws Exception { IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class); From 33e2a4dd6635798f98d4726406ed1af4c00a4cc5 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 18 Dec 2023 07:38:51 +0100 Subject: [PATCH 006/212] VPC: update default network offering for vpc tier to conserve_mode=1 (#8309) This PR updates the conserve mode of default vpc tier offering to conserve_mode=1 so we can create both port forwarding and load balancing rules on a public IP in vpc tiers. This fixes #8313 --- .../main/resources/META-INF/db/schema-41810to41900.sql | 2 ++ .../java/com/cloud/network/vpc/VpcManagerImpl.java | 4 ++-- test/integration/component/test_vpc_network.py | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql index 308d48a311c..15307353c3f 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41810to41900.sql @@ -21,6 +21,8 @@ ALTER TABLE `cloud`.`mshost` MODIFY COLUMN `state` varchar(25); +UPDATE `cloud`.`network_offerings` SET conserve_mode=1 WHERE name='DefaultIsolatedNetworkOfferingForVpcNetworks'; + -- Invalidate existing console_session records UPDATE `cloud`.`console_session` SET removed=now(); -- Modify acquired column in console_session to datetime type diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 1f99d164625..341a3b81b42 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -1809,9 +1809,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis * ("No redunant router support when network belnogs to VPC"); } */ - // 4) Conserve mode should be off + // 4) Conserve mode should be off in older versions if (guestNtwkOff.isConserveMode()) { - throw new InvalidParameterValueException("Only networks with conserve mode Off can belong to VPC"); + s_logger.info("Creating a network with conserve mode in VPC"); } // 5) If Netscaler is LB provider make sure it is in dedicated mode diff --git a/test/integration/component/test_vpc_network.py b/test/integration/component/test_vpc_network.py index 9a313e23094..db436bbd398 100644 --- a/test/integration/component/test_vpc_network.py +++ b/test/integration/component/test_vpc_network.py @@ -1001,23 +1001,23 @@ class TestVPCNetwork(cloudstackTestCase): # 1. Create a network offering with guest type=Isolated that has all # supported Services(Vpn,dhcpdns,UserData, SourceNat,Static NAT,LB # and PF,LB,NetworkAcl ) provided by VPCVR and conserve mode is ON - # 2. Create offering fails since Conserve mode ON isn't allowed within - # VPC + # 2. Create offering should succeed since Conserve mode ON is allowed within + # VPC since https://github.com/apache/cloudstack/pull/8309 # 3. Repeat test for offering which has Netscaler as external LB # provider """ self.debug("Creating network offering with conserve mode = ON") - with self.assertRaises(Exception): + try: nw = NetworkOffering.create( self.apiclient, self.services[value], conservemode=True ) self.cleanup.append(nw) - self.debug( - "Network creation failed as VPC support nw with conserve mode OFF") + except Exception as e: + self.warn("Network creation failed in VPC with conserve mode ON") return From ab70108f1573d6deaa2a28e29c64122d3d059237 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 20 Dec 2023 04:27:27 +0100 Subject: [PATCH 007/212] CKS: create Security Groups for CKS clusters of each account (#8316) This PR fixes #7684 The security groups contain the same rules for port 22 and 6443, no need to recreate for each CKS cluster. --- .../cluster/KubernetesClusterManagerImpl.java | 46 +++++++++++-------- .../KubernetesClusterActionWorker.java | 1 + .../security/SecurityGroupManagerImpl.java | 5 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 41ad7981e5d..19c66c1ba0b 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -121,9 +121,9 @@ import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.router.NetworkHelper; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.SecurityGroupService; -import com.cloud.network.security.SecurityGroupVO; import com.cloud.network.security.SecurityRule; import com.cloud.network.vpc.NetworkACL; import com.cloud.offering.NetworkOffering; @@ -1068,22 +1068,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logAndThrow(Level.ERROR, String.format("Creating Kubernetes cluster failed due to error while finding suitable deployment plan for cluster in zone : %s", zone.getName())); } - SecurityGroupVO securityGroupVO = null; + SecurityGroup securityGroup = null; if (zone.isSecurityGroupEnabled()) { - securityGroupVO = securityGroupManager.createSecurityGroup(KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME.concat(Long.toHexString(System.currentTimeMillis())), "Security group for CKS nodes", owner.getDomainId(), owner.getId(), owner.getAccountName()); - if (securityGroupVO == null) { - throw new CloudRuntimeException(String.format("Failed to create security group: %s", KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME)); - } - List cidrList = new ArrayList<>(); - cidrList.add(NetUtils.ALL_IP4_CIDRS); - securityGroupService.authorizeSecurityGroupRule(securityGroupVO.getId(), NetUtils.TCP_PROTO, - KubernetesClusterActionWorker.CLUSTER_NODES_DEFAULT_SSH_PORT_SG, KubernetesClusterActionWorker.CLUSTER_NODES_DEFAULT_SSH_PORT_SG, - null, null, cidrList, null, SecurityRule.SecurityRuleType.IngressRule); - securityGroupService.authorizeSecurityGroupRule(securityGroupVO.getId(), NetUtils.TCP_PROTO, - KubernetesClusterActionWorker.CLUSTER_API_PORT, KubernetesClusterActionWorker.CLUSTER_API_PORT, - null, null, cidrList, null, SecurityRule.SecurityRuleType.IngressRule); - securityGroupService.authorizeSecurityGroupRule(securityGroupVO.getId(), NetUtils.ALL_PROTO, - null, null, null, null, cidrList, null, SecurityRule.SecurityRuleType.EgressRule); + securityGroup = getOrCreateSecurityGroupForAccount(owner); } final Network defaultNetwork = getKubernetesClusterNetworkIfMissing(cmd.getName(), zone, owner, (int)controlNodeCount, (int)clusterSize, cmd.getExternalLoadBalancerIpAddress(), cmd.getNetworkId()); @@ -1091,7 +1078,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne final long cores = serviceOffering.getCpu() * (controlNodeCount + clusterSize); final long memory = serviceOffering.getRamSize() * (controlNodeCount + clusterSize); - SecurityGroupVO finalSecurityGroupVO = securityGroupVO; + final SecurityGroup finalSecurityGroup = securityGroup; final KubernetesClusterVO cluster = Transaction.execute(new TransactionCallback() { @Override public KubernetesClusterVO doInTransaction(TransactionStatus status) { @@ -1099,7 +1086,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne serviceOffering.getId(), finalTemplate.getId(), defaultNetwork.getId(), owner.getDomainId(), owner.getAccountId(), controlNodeCount, clusterSize, KubernetesCluster.State.Created, cmd.getSSHKeyPairName(), cores, memory, cmd.getNodeRootDiskSize(), ""); if (zone.isSecurityGroupEnabled()) { - newCluster.setSecurityGroupId(finalSecurityGroupVO.getId()); + newCluster.setSecurityGroupId(finalSecurityGroup.getId()); } kubernetesClusterDao.persist(newCluster); return newCluster; @@ -1114,6 +1101,29 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne return cluster; } + private SecurityGroup getOrCreateSecurityGroupForAccount(Account owner) { + String securityGroupName = String.format("%s-%s", KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME, owner.getUuid()); + String securityGroupDesc = String.format("%s and account %s", KubernetesClusterActionWorker.CKS_SECURITY_GROUP_DESCRIPTION, owner.getName()); + SecurityGroup securityGroup = securityGroupManager.getSecurityGroup(securityGroupName, owner.getId()); + if (securityGroup == null) { + securityGroup = securityGroupManager.createSecurityGroup(securityGroupName, securityGroupDesc, owner.getDomainId(), owner.getId(), owner.getAccountName()); + if (securityGroup == null) { + throw new CloudRuntimeException(String.format("Failed to create security group: %s", KubernetesClusterActionWorker.CKS_CLUSTER_SECURITY_GROUP_NAME)); + } + List cidrList = new ArrayList<>(); + cidrList.add(NetUtils.ALL_IP4_CIDRS); + securityGroupService.authorizeSecurityGroupRule(securityGroup.getId(), NetUtils.TCP_PROTO, + KubernetesClusterActionWorker.CLUSTER_NODES_DEFAULT_SSH_PORT_SG, KubernetesClusterActionWorker.CLUSTER_NODES_DEFAULT_SSH_PORT_SG, + null, null, cidrList, null, SecurityRule.SecurityRuleType.IngressRule); + securityGroupService.authorizeSecurityGroupRule(securityGroup.getId(), NetUtils.TCP_PROTO, + KubernetesClusterActionWorker.CLUSTER_API_PORT, KubernetesClusterActionWorker.CLUSTER_API_PORT, + null, null, cidrList, null, SecurityRule.SecurityRuleType.IngressRule); + securityGroupService.authorizeSecurityGroupRule(securityGroup.getId(), NetUtils.ALL_PROTO, + null, null, null, null, cidrList, null, SecurityRule.SecurityRuleType.EgressRule); + } + return securityGroup; + } + /** * Start operation can be performed at two different life stages of Kubernetes cluster. First when a freshly created cluster * in which case there are no resources provisioned for the Kubernetes cluster. So during start all the resources diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index 0417161c3f5..6d44565d823 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -106,6 +106,7 @@ public class KubernetesClusterActionWorker { public static final int CLUSTER_NODES_DEFAULT_SSH_PORT_SG = DEFAULT_SSH_PORT; public static final String CKS_CLUSTER_SECURITY_GROUP_NAME = "CKSSecurityGroup"; + public static final String CKS_SECURITY_GROUP_DESCRIPTION = "Security group for CKS nodes"; protected static final Logger LOGGER = Logger.getLogger(KubernetesClusterActionWorker.class); diff --git a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java index b423ce78fa8..551a6ed69c0 100644 --- a/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/main/java/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -63,7 +63,6 @@ import com.cloud.agent.api.SecurityGroupRulesCmd; import com.cloud.agent.api.SecurityGroupRulesCmd.IpPortAndProto; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; -import com.cloud.api.query.dao.SecurityGroupJoinDao; import com.cloud.configuration.Config; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; @@ -131,8 +130,6 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro @Inject SecurityGroupDao _securityGroupDao; @Inject - SecurityGroupJoinDao _securityGroupJoinDao; - @Inject SecurityGroupRuleDao _securityGroupRuleDao; @Inject SecurityGroupVMMapDao _securityGroupVMMapDao; @@ -1405,7 +1402,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro } @Override - public SecurityGroupVO getDefaultSecurityGroup(long accountId) { + public SecurityGroup getDefaultSecurityGroup(long accountId) { return _securityGroupDao.findByAccountAndName(accountId, DEFAULT_GROUP_NAME); } From 7c06d289d283c8184ec9ae39fde72de0395541b7 Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 20 Dec 2023 11:17:41 +0530 Subject: [PATCH 008/212] Fixup test_image_store_object_migration.py (#8378) This PR fixes failure seem in #7344 (comment) --- test/integration/smoke/test_image_store_object_migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/smoke/test_image_store_object_migration.py b/test/integration/smoke/test_image_store_object_migration.py index de504aec810..fcd10d70e2e 100644 --- a/test/integration/smoke/test_image_store_object_migration.py +++ b/test/integration/smoke/test_image_store_object_migration.py @@ -148,7 +148,7 @@ class TestImageStoreObjectMigration(cloudstackTestCase): storeObjects = originalSecondaryStore.listObjects(self.apiclient, path="template/tmpl/" + str(account_id) + "/" + str(template_id)) - self.assertEqual(len(storeObjects), 2, "Check template is uploaded on secondary storage") + self.assertGreaterEqual(len(storeObjects), 2, "Check template is uploaded on secondary storage") # Migrate template to another secondary storage secondaryStores = ImageStore.list(self.apiclient, zoneid=self.zone.id) @@ -173,7 +173,7 @@ class TestImageStoreObjectMigration(cloudstackTestCase): storeObjects = destSecondaryStore.listObjects(self.apiclient, path="template/tmpl/" + str(account_id) + "/" + str(template_id)) - self.assertEqual(len(storeObjects), 2, "Check template is uploaded on destination secondary storage") + self.assertGreaterEqual(len(storeObjects), 2, "Check template is uploaded on destination secondary storage") def registerTemplate(self, cmd): temp = self.apiclient.registerTemplate(cmd)[0] From 64ecd00eb70531d177031b50e5c15f73668df6e8 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 20 Dec 2023 11:52:08 +0530 Subject: [PATCH 009/212] test: fix test_host_ping.py to restore original host state (#8380) Failures seen in #7344 were debugged and it was seen since one of the host is in Alert state. VM deployment fails with affinity group. Signed-off-by: Abhishek Kumar --- test/integration/smoke/test_host_ping.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/integration/smoke/test_host_ping.py b/test/integration/smoke/test_host_ping.py index 9de77f9b771..6414553543a 100644 --- a/test/integration/smoke/test_host_ping.py +++ b/test/integration/smoke/test_host_ping.py @@ -40,9 +40,14 @@ class TestHostPing(cloudstackTestCase): self.services = self.testClient.getParsedTestDataConfig() self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests()) self.pod = get_pod(self.apiclient, self.zone.id) + self.original_host_state_map = {} self.cleanup = [] def tearDown(self): + for host_id in self.original_host_state_map: + state = self.original_host_state_map[host_id] + sql_query = "UPDATE host SET status = '" + state + "' WHERE uuid = '" + host_id + "'" + self.dbConnection.execute(sql_query) super(TestHostPing, self).tearDown() def checkHostStateInCloudstack(self, state, host_id): @@ -92,6 +97,7 @@ class TestHostPing(cloudstackTestCase): self.logger.debug('Hypervisor = {}'.format(host.id)) hostToTest = listHost[0] + self.original_host_state_map[hostToTest.id] = hostToTest.state sql_query = "UPDATE host SET status = 'Alert' WHERE uuid = '" + hostToTest.id + "'" self.dbConnection.execute(sql_query) From 1411da1a22bc6aa26634f3038475e3d5fbbcd6bb Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 20 Dec 2023 11:54:39 +0530 Subject: [PATCH 010/212] Add e2e tests for listing resources (#8281) This PR adds e2e tests for listing resources --- .github/workflows/ci.yml | 15 +- setup/dev/advdualzone.cfg | 60 +- .../test_affinity_groups_projects.py | 2 +- test/integration/smoke/test_list_accounts.py | 379 +++++++++++ .../smoke/test_list_disk_offerings.py | 319 +++++++++ test/integration/smoke/test_list_domains.py | 216 ++++++ test/integration/smoke/test_list_hosts.py | 372 +++++++++++ .../smoke/test_list_service_offerings.py | 559 ++++++++++++++++ .../smoke/test_list_storage_pools.py | 396 +++++++++++ test/integration/smoke/test_list_volumes.py | 618 ++++++++++++++++++ test/integration/smoke/test_metrics_api.py | 1 - .../smoke/test_secondary_storage.py | 2 +- test/integration/smoke/test_templates.py | 16 +- 13 files changed, 2910 insertions(+), 45 deletions(-) create mode 100644 test/integration/smoke/test_list_accounts.py create mode 100644 test/integration/smoke/test_list_disk_offerings.py create mode 100644 test/integration/smoke/test_list_domains.py create mode 100644 test/integration/smoke/test_list_hosts.py create mode 100644 test/integration/smoke/test_list_service_offerings.py create mode 100644 test/integration/smoke/test_list_storage_pools.py create mode 100644 test/integration/smoke/test_list_volumes.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6edc7bdb20..dd96fcfce4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,8 +194,15 @@ jobs: component/test_vpc_network component/test_vpc_offerings component/test_vpc_routers - component/test_vpn_users", - "component/test_vpc_network_lbrules" ] + component/test_vpn_users + component/test_vpc_network_lbrules", + "smoke/test_list_accounts + smoke/test_list_disk_offerings + smoke/test_list_domains + smoke/test_list_hosts + smoke/test_list_service_offerings + smoke/test_list_storage_pools + smoke/test_list_volumes"] steps: - uses: actions/checkout@v4 @@ -278,7 +285,7 @@ jobs: while ! nc -vzw 5 localhost 8096 2>&1 > /dev/null; do grep Exception /tmp/jetty-log; sleep 10; done set -e echo -e "\nStarting Advanced Zone DataCenter deployment" - python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advanced.cfg 2>&1 || true + python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advdualzone.cfg 2>&1 || true - name: Run Integration Tests with Simulator run: | @@ -291,7 +298,7 @@ jobs: TESTS=($(echo $TESTS | tr -d '\n' | tr -s ' ')) for suite in "${TESTS[@]}" ; do echo -e "Currently running test: $suite\n" - time nosetests-3.4 --with-xunit --xunit-file=integration-test-results/$suite.xml --with-marvin --marvin-config=setup/dev/advanced.cfg test/integration/$suite.py -s -a tags=advanced,required_hardware=false --zone=Sandbox-simulator --hypervisor=simulator || true ; + time nosetests-3.4 --with-xunit --xunit-file=integration-test-results/$suite.xml --with-marvin --marvin-config=setup/dev/advdualzone.cfg test/integration/$suite.py -s -a tags=advanced,required_hardware=false --zone=zim1 --hypervisor=simulator || true ; done echo -e "Stopping Simulator, integration tests run completed\n" diff --git a/setup/dev/advdualzone.cfg b/setup/dev/advdualzone.cfg index b11675d712a..97a98402348 100644 --- a/setup/dev/advdualzone.cfg +++ b/setup/dev/advdualzone.cfg @@ -18,12 +18,12 @@ "zones": [ { "name": "zim1", - "guestcidraddress": "10.100.1.0/24", - "dns1": "10.147.100.6", + "guestcidraddress": "10.1.1.0/24", + "dns1": "10.147.28.6", "physical_networks": [ { "broadcastdomainrange": "Zone", - "vlan": "1100-1200", + "vlan": "100-200", "name": "z1-pnet", "traffictypes": [ { @@ -63,19 +63,19 @@ }, "ipranges": [ { - "startip": "192.168.100.2", - "endip": "192.168.100.200", + "startip": "192.168.2.2", + "endip": "192.168.2.200", "netmask": "255.255.255.0", "vlan": "50", - "gateway": "192.168.100.1" + "gateway": "192.168.2.1" } ], "networktype": "Advanced", "pods": [ { - "endip": "172.16.100.200", + "endip": "172.16.15.200", "name": "Z1P1", - "startip": "172.16.100.2", + "startip": "172.16.15.2", "netmask": "255.255.255.0", "clusters": [ { @@ -96,11 +96,11 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.100.6:/export/home/sandbox/z1p1", + "url": "nfs://10.147.28.6:/export/home/sandbox/z1p1", "name": "Z1PS1" }, { - "url": "nfs://10.147.100.6:/export/home/sandbox/z1p2", + "url": "nfs://10.147.28.6:/export/home/sandbox/z1p2", "name": "Z1PS2" } ] @@ -123,35 +123,35 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.100.6:/export/home/sandbox/z1p3", + "url": "nfs://10.147.28.6:/export/home/sandbox/z1p3", "name": "Z1PS3" }, { - "url": "nfs://10.147.100.6:/export/home/sandbox/z1p4", + "url": "nfs://10.147.28.6:/export/home/sandbox/z1p4", "name": "Z1PS4" } ] } ], - "gateway": "172.16.100.1" + "gateway": "172.16.15.1" } ], - "internaldns1": "10.147.100.6", + "internaldns1": "10.147.28.6", "secondaryStorages": [ { - "url": "nfs://10.147.100.6:/export/home/sandbox/z1secondary", + "url": "nfs://10.147.28.6:/export/home/sandbox/z1secondary", "provider" : "NFS" } ] }, { "name": "zim2", - "guestcidraddress": "10.200.1.0/24", - "dns1": "10.147.200.6", + "guestcidraddress": "10.1.2.0/24", + "dns1": "10.147.29.6", "physical_networks": [ { "broadcastdomainrange": "Zone", - "vlan": "2100-2200", + "vlan": "300-400", "name": "z2-pnet", "traffictypes": [ { @@ -191,19 +191,19 @@ }, "ipranges": [ { - "startip": "192.168.200.2", - "endip": "192.168.200.200", + "startip": "192.168.3.2", + "endip": "192.168.3.200", "netmask": "255.255.255.0", - "vlan": "50", - "gateway": "192.168.200.1" + "vlan": "51", + "gateway": "192.168.3.1" } ], "networktype": "Advanced", "pods": [ { - "endip": "172.16.200.200", + "endip": "172.16.16.200", "name": "Z2P1", - "startip": "172.16.200.2", + "startip": "172.16.16.2", "netmask": "255.255.255.0", "clusters": [ { @@ -224,11 +224,11 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.200.6:/export/home/sandbox/z2p1", + "url": "nfs://10.147.29.6:/export/home/sandbox/z2p1", "name": "Z2PS1" }, { - "url": "nfs://10.147.200.6:/export/home/sandbox/z2p2", + "url": "nfs://10.147.29.6:/export/home/sandbox/z2p2", "name": "Z2PS2" } ] @@ -251,20 +251,20 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.200.6:/export/home/sandbox/z2p3", + "url": "nfs://10.147.29.6:/export/home/sandbox/z2p3", "name": "Z2PS3" }, { - "url": "nfs://10.147.200.6:/export/home/sandbox/z2p4", + "url": "nfs://10.147.29.6:/export/home/sandbox/z2p4", "name": "Z2PS4" } ] } ], - "gateway": "172.16.200.1" + "gateway": "172.16.16.1" } ], - "internaldns1": "10.147.200.6", + "internaldns1": "10.147.29.6", "secondaryStorages": [ { "url": "nfs://10.147.200.6:/export/home/sandbox/z2secondary", diff --git a/test/integration/component/test_affinity_groups_projects.py b/test/integration/component/test_affinity_groups_projects.py index 1c0b4c2bdd8..07811e79fde 100644 --- a/test/integration/component/test_affinity_groups_projects.py +++ b/test/integration/component/test_affinity_groups_projects.py @@ -1065,7 +1065,7 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): """ test DeployVM in anti-affinity groups with more vms than hosts. """ - hosts = list_hosts(self.api_client, type="routing") + hosts = list_hosts(self.api_client, type="routing", zoneid=self.zone.id) aff_grp = self.create_aff_grp(self.account_api_client) vms = [] for host in hosts: diff --git a/test/integration/smoke/test_list_accounts.py b/test/integration/smoke/test_list_accounts.py new file mode 100644 index 00000000000..1cce3cef170 --- /dev/null +++ b/test/integration/smoke/test_list_accounts.py @@ -0,0 +1,379 @@ +# 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. +""" Tests for API listing of accounts with different filters +""" + +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.base import (Account, + Domain) +from marvin.lib.common import (get_domain, list_accounts) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListAccounts(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListAccounts, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.domain = get_domain(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls._cleanup = [] + cls.accounts = list_accounts(cls.apiclient, listall=True) + + cls.child_domain_1 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + cls._cleanup.append(cls.child_domain_1) + + cls.services["account"]["username"] = "child_account_admin" + cls.child_account_admin = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.child_domain_1.id + ) + cls._cleanup.append(cls.child_account_admin) + + cls.services["username"] = "child_account_user" + cls.child_account_user = Account.create( + cls.apiclient, + cls.services["account"], + admin=0, + domainid=cls.child_domain_1.id + ) + cls.child_account_user.disable(cls.apiclient) + cls._cleanup.append(cls.child_account_user) + + cls.child_domain_2 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + cls._cleanup.append(cls.child_domain_2) + + @classmethod + def tearDownClass(cls): + super(TestListAccounts, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_accounts_accounttype_filter(self): + """Test listing accounts with accounttype filter + """ + list_account_response = Account.list( + self.apiclient, + accounttype=0, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_user.name, + "Check for list response return valid data" + ) + self.assertEqual( + list_account_response[0].accounttype, + 0, + "Check for list response return valid data" + ) + + list_account_response = Account.list( + self.apiclient, + accounttype=2, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_admin.name, + "Check for list response return valid data" + ) + self.assertEqual( + list_account_response[0].accounttype, + 2, + "Check for list response return valid data" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_accounts_domainid_filter(self): + """Test listing accounts with domainid filter + """ + list_account_response = Account.list( + self.apiclient, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 2, + "List Account response has incorrect length" + ) + self.assertEqual( + self.child_domain_1.id, + list_account_response[0].domainid, + "Check for list response return valid data" + ) + self.assertEqual( + self.child_domain_1.id, + list_account_response[1].domainid, + "Check for list response return valid data" + ) + + list_account_response = Account.list( + self.apiclient, + domainid=self.child_domain_2.id + ) + self.assertIsNone(list_account_response, "Check for list response return valid data") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_accounts_id_filter(self): + """Test listing accounts with id filter + """ + list_account_response = Account.list( + self.apiclient, + id=self.child_account_user.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_user.name, + "Expected account name and actual account name should be same" + ) + + list_account_response = Account.list( + self.apiclient, + id=self.child_account_admin.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_admin.name, + "Expected account name and actual account name should be same" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_accounts_name_filter(self): + """Test listing accounts with name filter + """ + list_account_response = Account.list( + self.apiclient, + name=self.child_account_user.name, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_user.name, + "Expected account name and actual account name should be same" + ) + + list_account_response = Account.list( + self.apiclient, + name=self.child_account_admin.name, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_admin.name, + "Expected account name and actual account name should be same" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_accounts_state_filter(self): + """Test listing accounts with state filter + """ + list_account_response = Account.list( + self.apiclient, + state="enabled", + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_admin.name, + "Expected account name and actual account name should be same" + ) + + list_account_response = Account.list( + self.apiclient, + state="disabled", + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + len(list_account_response), + 1, + "List Account response has incorrect length" + ) + self.assertEqual( + list_account_response[0].name, + self.child_account_user.name, + "Expected account name and actual account name should be same" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_accounts_keyword_filter(self): + """Test listing accounts with keyword filter + """ + list_account_response = Account.list( + self.apiclient, + keyword=self.child_account_user.name, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + self.child_account_user.name, + list_account_response[0].name, + "Expected account name and actual account name should be same" + ) + + list_account_response = Account.list( + self.apiclient, + keyword=self.child_account_admin.name, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + self.child_account_admin.name, + list_account_response[0].name, + "Expected account name and actual account name should be same" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_accounts_with_listall_filters(self): + """Test listing accounts with listall filters + """ + list_account_response = Account.list( + self.apiclient, + listall=False + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + 1, + len(list_account_response), + "List Account response has incorrect length" + ) + + list_account_response = Account.list( + self.apiclient, + listall=True + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + 2, + len(list_account_response) - len(self.accounts), + "List Account response has incorrect length" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_list_accounts_with_no_filters(self): + """Test listing accounts with no filters + """ + list_account_response = Account.list( + self.apiclient + ) + self.assertTrue( + isinstance(list_account_response, list), + "List Account response is not a valid list" + ) + self.assertEqual( + 1, + len(list_account_response), + "List Account response has incorrect length" + ) diff --git a/test/integration/smoke/test_list_disk_offerings.py b/test/integration/smoke/test_list_disk_offerings.py new file mode 100644 index 00000000000..6319ea338ad --- /dev/null +++ b/test/integration/smoke/test_list_disk_offerings.py @@ -0,0 +1,319 @@ +# 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. +""" Tests for API listing of disk offerings with different filters +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED +from marvin.lib.base import (Account, + Domain, + Volume, + ServiceOffering, + DiskOffering, + VirtualMachine) +from marvin.lib.common import (get_domain, list_accounts, + list_zones, list_clusters, list_hosts) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListDiskOfferings(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListDiskOfferings, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.hypervisor = testClient.getHypervisorInfo() + cls.domain = get_domain(cls.apiclient) + cls.zones = list_zones(cls.apiclient) + cls.zone = cls.zones[0] + cls.clusters = list_clusters(cls.apiclient) + cls.cluster = cls.clusters[0] + cls.hosts = list_hosts(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls._cleanup = [] + cls.disk_offerings = DiskOffering.list(cls.apiclient, listall=True) + + cls.disk_offering = DiskOffering.create(cls.apiclient, + cls.services["disk_offering"], + domainid=cls.domain.id) + cls._cleanup.append(cls.disk_offering) + + cls.child_domain_1 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + cls._cleanup.append(cls.child_domain_1) + + cls.account_1 = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account_1) + + cls.domainadmin_api_client = testClient.getUserApiClient( + UserName=cls.account_1.user[0].username, + DomainName=cls.domain.name, + type=2 + ) + + cls.disk_offering_child_domain = DiskOffering.create(cls.apiclient, + cls.services["disk_offering"], + domainid=cls.child_domain_1.id, + zoneid=cls.zone.id, + encrypt=True) + cls._cleanup.append(cls.disk_offering_child_domain) + + @classmethod + def tearDownClass(cls): + super(TestListDiskOfferings, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_disk_offerings_id_filter(self): + """ Test list disk offerings with id filter + """ + # List all disk offerings + disk_offerings = DiskOffering.list(self.apiclient, id=self.disk_offering.id) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + # Verify the id of the disk offering returned is the same as the one requested + self.assertEqual( + disk_offerings[0].id, + self.disk_offering.id, + "List disk offerings should return the disk offering requested" + ) + + disk_offerings = DiskOffering.list(self.apiclient, id=-1) + self.assertIsNone(disk_offerings, "List disk offerings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_disk_offerings_name_filter(self): + """ Test list disk offerings with name filter + """ + disk_offerings = DiskOffering.list(self.apiclient, name=self.services["disk_offering"]["name"]) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings), + 2, + "List disk offerings response has incorrect length" + ) + # Verify the name of the disk offering returned is the same as the one requested + self.assertEqual( + disk_offerings[0].name, + self.services["disk_offering"]["name"], + "List disk offerings should return the disk offering requested" + ) + self.assertEqual( + disk_offerings[1].name, + self.services["disk_offering"]["name"], + "List disk offerings should return the disk offering requested" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_disk_offerings_zoneid_filter(self): + """ Test list disk offerings with zoneid filter + """ + disk_offerings_zone_1 = DiskOffering.list(self.apiclient, zoneid=self.zone.id) + self.assertTrue( + isinstance(disk_offerings_zone_1, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings_zone_1) - len(self.disk_offerings), + 2, + "List disk offerings response has incorrect length" + ) + + for disk_offering in disk_offerings_zone_1: + self.assertTrue( + disk_offering.zoneid is None or disk_offering.zoneid == self.zone.id, + "List disk offerings should return the disk offering requested" + ) + + if len(self.zones) > 1: + disk_offerings_zone_2 = DiskOffering.list(self.apiclient, zoneid=self.zones[1].id) + self.assertTrue( + isinstance(disk_offerings_zone_2, list), + "List disk offerings response is not a valid list" + ) + for disk_offering in disk_offerings_zone_2: + self.assertTrue( + disk_offering.zoneid is None or disk_offering.zoneid == self.zones[1].id, + "List disk offerings should return the disk offering requested" + ) + + self.assertEqual(len(disk_offerings_zone_1) - len(disk_offerings_zone_2), 1) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_disk_offerings_domainid_filter(self): + """ Test list disk offerings with domainid filter + """ + disk_offerings = DiskOffering.list(self.apiclient, domainid=self.domain.id) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + self.assertEqual( + disk_offerings[0].domainid, + self.domain.id, + "List disk offerings should return the disk offering requested" + ) + + disk_offerings = DiskOffering.list(self.apiclient, domainid=self.child_domain_1.id) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + self.assertEqual( + disk_offerings[0].domainid, + self.child_domain_1.id, + "List disk offerings should return the disk offering requested" + ) + + disk_offerings = DiskOffering.list(self.apiclient, domainid=-1) + self.assertIsNone(disk_offerings, "List disk offerings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_disk_offerings_encrypted_filter(self): + """ Test list disk offerings with encrypted filter + """ + disk_offerings = DiskOffering.list(self.apiclient, encrypt=True) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + + self.assertEqual( + len(disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + self.assertTrue( + disk_offerings[0].encrypt, + "List disk offerings should return the disk offering requested" + ) + + disk_offerings = DiskOffering.list(self.apiclient, encrypt=False) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings) - len(self.disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + for disk_offering in disk_offerings: + self.assertFalse( + disk_offering.encrypt, + "List disk offerings should return the disk offering requested" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_disk_offerings_keyword_filter(self): + """ Test list disk offerings with keyword filter + """ + disk_offerings = DiskOffering.list(self.apiclient, keyword=self.disk_offering.name) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings), + 2, + "List disk offerings response has incorrect length" + ) + self.assertEqual( + disk_offerings[0].name, + self.disk_offering.name, + "List disk offerings should return the disk offering requested" + ) + self.assertEqual( + disk_offerings[1].name, + self.disk_offering.name, + "List disk offerings should return the disk offering requested" + ) + + disk_offerings = DiskOffering.list(self.apiclient, keyword="random") + self.assertIsNone(disk_offerings, "List disk offerings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_disk_offering_isrecursive_filter(self): + """ Test list disk offerings with isrecursive parameter + """ + disk_offerings = DiskOffering.list(self.domainadmin_api_client, isrecursive=True) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings) - len(self.disk_offerings), + 2, + "List disk offerings response has incorrect length" + ) + + disk_offerings = DiskOffering.list(self.domainadmin_api_client, isrecursive=False) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings) - len(self.disk_offerings), + 1, + "List disk offerings response has incorrect length" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_list_disk_offering_no_filter(self): + """ Test list disk offerings with no filters + """ + disk_offerings = DiskOffering.list(self.apiclient) + self.assertTrue( + isinstance(disk_offerings, list), + "List disk offerings response is not a valid list" + ) + self.assertEqual( + len(disk_offerings) - len(self.disk_offerings), + 2, + "List disk offerings response has incorrect length" + ) diff --git a/test/integration/smoke/test_list_domains.py b/test/integration/smoke/test_list_domains.py new file mode 100644 index 00000000000..546ffbbf1e3 --- /dev/null +++ b/test/integration/smoke/test_list_domains.py @@ -0,0 +1,216 @@ +# 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. +""" Tests for API listing of domains with different filters +""" + +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.lib.base import (Account, + Domain) +from marvin.lib.common import (get_domain, list_accounts) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListDomains(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListDomains, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.domain = get_domain(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls._cleanup = [] + + cls.child_domain_1 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + cls._cleanup.append(cls.child_domain_1) + + cls.child_account_1 = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.child_domain_1.id + ) + cls._cleanup.append(cls.child_account_1) + + cls.child_account_apiclient = testClient.getUserApiClient(cls.child_account_1.user[0]['username'], cls.child_domain_1.name, type=2) + + cls.child_domain_2 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.child_domain_1.id + ) + cls._cleanup.append(cls.child_domain_2) + + @classmethod + def tearDownClass(cls): + super(TestListDomains, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_domains_id_filter(self): + """ Test list domains with id filter + """ + # List all domains + domains = Domain.list(self.apiclient, id=self.domain.id) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + self.assertEqual( + domains[0].id, + self.domain.id, + "Check if list domains returns valid domain" + ) + + # List all domains with a non-existent id + with self.assertRaises(Exception): + Domain.list(self.apiclient, id=-1) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_domains_name_filter(self): + """ Test list domains with name filter + """ + # List all domains + domains = Domain.list(self.apiclient, name=self.domain.name) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + self.assertEqual( + domains[0].name, + self.domain.name, + "Check if list domains returns valid domain" + ) + + domains = Domain.list(self.apiclient, name="non-existent-domain") + self.assertIsNone(domains, "List Domain response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_domains_listall_filter(self): + """ Test list domains with listall parameter + """ + # List all domains + domains = Domain.list(self.child_account_apiclient, listall=True) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 2, + "List Domain response has incorrect length" + ) + + domains = Domain.list(self.child_account_apiclient, listall=False) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + self.assertEqual( + domains[0].id, + self.child_domain_1.id, + "Check if list domains returns valid domain" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_domains_level_filter(self): + """ Test list domains with level filter + """ + # List all domains + domains = Domain.list(self.apiclient, level=0) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + self.assertEqual( + domains[0].id, + self.domain.id, + "Check if list domains returns valid domain" + ) + + domains = Domain.list(self.apiclient, level=1) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + + domains = Domain.list(self.apiclient, level=2) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 1, + "List Domain response has incorrect length" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_domains_no_filter(self): + """ Test list domains with no filter + """ + # List all domains + domains = Domain.list(self.apiclient) + self.assertEqual( + isinstance(domains, list), + True, + "List Domain response is not a valid list" + ) + self.assertEqual( + len(domains), + 3, + "List Domain response has incorrect length" + ) diff --git a/test/integration/smoke/test_list_hosts.py b/test/integration/smoke/test_list_hosts.py new file mode 100644 index 00000000000..7bae216d51d --- /dev/null +++ b/test/integration/smoke/test_list_hosts.py @@ -0,0 +1,372 @@ +# 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. +""" Tests for API listing of hosts with different filters +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED +from marvin.lib.base import (Configurations, Host) +from marvin.lib.common import (get_domain, list_accounts, + list_zones, list_clusters) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListHosts(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListHosts, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.hypervisor = testClient.getHypervisorInfo() + cls.zones = list_zones(cls.apiclient) + cls.zone = cls.zones[0] + cls.clusters = list_clusters(cls.apiclient) + cls.cluster = cls.clusters[0] + cls.hosts = Host.list(cls.apiclient) + + + @classmethod + def tearDownClass(cls): + super(TestListHosts, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_hosts_no_filter(self): + """Test list hosts with no filter""" + hosts = Host.list(self.apiclient) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_hosts_clusterid_filter(self): + """Test list hosts with clusterid filter""" + hosts = Host.list(self.apiclient, clusterid=self.cluster.id) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + for host in hosts: + self.assertEqual( + host.clusterid, + self.cluster.id, + "Host should be in the cluster %s" % self.cluster.id + ) + with self.assertRaises(Exception): + hosts = Host.list(self.apiclient, clusterid="invalidclusterid") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_hosts_hahost_filter(self): + """Test list hosts with hahost filter""" + configs = Configurations.list( + self.apiclient, + name='ha.tag' + ) + if isinstance(configs, list) and configs[0].value != "" and configs[0].value is not None: + hosts = Host.list(self.apiclient, hahost=True) + if hosts is not None: + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + for host in hosts: + self.assertEqual( + host.hahost, + True, + "Host should be a HA host" + ) + + hosts = Host.list(self.apiclient, hahost=False) + if hosts is not None: + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + for host in hosts: + self.assertTrue( + host.hahost is None or host.hahost is False, + "Host should not be a HA host" + ) + else: + self.debug("HA is not enabled in the setup") + hosts = Host.list(self.apiclient, hahost="invalidvalue") + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + self.assertEqual( + len(hosts), + len(self.hosts), + "Length of host response should be equal to the length of hosts" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_hosts_hypervisor_filter(self): + """Test list hosts with hypervisor filter""" + hosts = Host.list(self.apiclient, hypervisor=self.hypervisor) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should greater than 0" + ) + for host in hosts: + self.assertEqual( + host.hypervisor.lower(), + self.hypervisor.lower(), + "Host should be a %s hypervisor" % self.hypervisor + ) + + hosts = Host.list(self.apiclient, hypervisor="invalidhypervisor") + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertEqual( + len(hosts), + len(self.hosts), + "Length of host response should be equal to the length of hosts" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_hosts_id_filter(self): + """Test list hosts with id filter""" + hosts = Host.list(self.apiclient, id=self.hosts[0].id) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertEqual( + len(hosts), + 1, + "Length of host response should be 1" + ) + self.assertEqual( + hosts[0].id, + self.hosts[0].id, + "Host id should match with the host id in the list" + ) + + with self.assertRaises(Exception): + hosts = Host.list(self.apiclient, id="invalidid") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_hosts_keyword_filter(self): + """Test list hosts with keyword filter""" + hosts = Host.list(self.apiclient, keyword=self.hosts[0].name) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertIn( + host.name, + self.hosts[0].name, + "Host name should match with the host name in the list" + ) + + hosts = Host.list(self.apiclient, keyword="invalidkeyword") + self.assertIsNone( + hosts, + "Host response should be None" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_hosts_name_filter(self): + """Test list hosts with name filter""" + hosts = Host.list(self.apiclient, name=self.hosts[0].name) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertIn( + host.name, + self.hosts[0].name, + "Host name should match with the host name in the list" + ) + + hosts = Host.list(self.apiclient, name="invalidname") + self.assertIsNone( + hosts, + "Host response should be None" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_list_hosts_podid_filter(self): + """Test list hosts with podid filter""" + hosts = Host.list(self.apiclient, podid=self.hosts[0].podid) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertEqual( + host.podid, + self.hosts[0].podid, + "Host podid should match with the host podid in the list" + ) + with self.assertRaises(Exception): + hosts = Host.list(self.apiclient, podid="invalidpodid") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_09_list_hosts_resourcestate_filter(self): + """Test list hosts with resourcestate filter""" + hosts = Host.list(self.apiclient, resourcestate=self.hosts[0].resourcestate) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertEqual( + host.resourcestate, + self.hosts[0].resourcestate, + "Host resourcestate should match with the host resourcestate in the list" + ) + + hosts = Host.list(self.apiclient, resourcestate="invalidresourcestate") + self.assertIsNone( + hosts, + "Host response should be None" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_10_list_hosts_state_filter(self): + """Test list hosts with state filter""" + hosts = Host.list(self.apiclient, state=self.hosts[0].state) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertEqual( + host.state, + self.hosts[0].state, + "Host state should match with the host state in the list" + ) + + hosts = Host.list(self.apiclient, state="invalidstate") + self.assertIsNone( + hosts, + "Host response should be None" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_11_list_hosts_type_filter(self): + """Test list hosts with type filter""" + hosts = Host.list(self.apiclient, type=self.hosts[0].type) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertEqual( + host.type, + self.hosts[0].type, + "Host type should match with the host type in the list" + ) + + hosts = Host.list(self.apiclient, type="invalidtype") + self.assertIsNone( + hosts, + "Host response should be None" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_12_list_hosts_zoneid_filter(self): + """Test list hosts with zoneid filter""" + hosts = Host.list(self.apiclient, zoneid=self.zone.id) + self.assertTrue( + isinstance(hosts, list), + "Host response type should be a valid list" + ) + self.assertGreater( + len(hosts), + 0, + "Length of host response should be greater than 0" + ) + for host in hosts: + self.assertEqual( + host.zoneid, + self.zone.id, + "Host zoneid should match with the host zoneid in the list" + ) + + with self.assertRaises(Exception): + hosts = Host.list(self.apiclient, zoneid="invalidzoneid") diff --git a/test/integration/smoke/test_list_service_offerings.py b/test/integration/smoke/test_list_service_offerings.py new file mode 100644 index 00000000000..319675419dd --- /dev/null +++ b/test/integration/smoke/test_list_service_offerings.py @@ -0,0 +1,559 @@ +# 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. +""" Tests for API listing of service offerings with different filters +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED +from marvin.lib.base import (Account, + Domain, + Volume, + ServiceOffering, + DiskOffering, + VirtualMachine) +from marvin.lib.common import (get_domain, list_accounts, + list_zones, list_clusters, list_hosts) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListServiceOfferings(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListServiceOfferings, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.hypervisor = testClient.getHypervisorInfo() + cls.domain = get_domain(cls.apiclient) + cls.zones = list_zones(cls.apiclient) + cls.zone = cls.zones[0] + cls.clusters = list_clusters(cls.apiclient) + cls.cluster = cls.clusters[0] + cls.hosts = list_hosts(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls._cleanup = [] + cls.service_offerings = ServiceOffering.list(cls.apiclient) + cls.system_service_offerings = ServiceOffering.list(cls.apiclient, issystem=True) + + cls.child_domain_1 = Domain.create( + cls.apiclient, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + cls._cleanup.append(cls.child_domain_1) + + cls.account_1 = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account_1) + + cls.domainadmin_api_client = testClient.getUserApiClient( + UserName=cls.account_1.user[0].username, + DomainName=cls.domain.name, + type=2 + ) + + cls.system_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["tiny"], + issystem=True, + name="custom_system_offering", + systemvmtype="domainrouter" + ) + cls._cleanup.append(cls.system_offering) + + cls.service_offering_1 = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["small"], + cpunumber=2, + cpuspeed=2000, + domainid=cls.child_domain_1.id, + encryptroot=True, + name="custom_offering_1", + zoneid=cls.zone.id + ) + cls._cleanup.append(cls.service_offering_1) + + + @classmethod + def tearDownClass(cls): + super(TestListServiceOfferings, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_service_offerings_cpunumber_filter(self): + """Test list service offerings with cpunumber filter + """ + # List all service offerings with cpunumber 1 + service_offerings = ServiceOffering.list( + self.apiclient, + cpunumber=1 + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings) - len(self.service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertGreaterEqual( + service_offering.cpunumber, + 1, + "List ServiceOfferings response has incorrect cpunumber" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + cpunumber=99999 + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_service_offerings_cpuspeed_filter(self): + """Test list service offerings with cpuspeed filter + """ + # List all service offerings with cpuspeed 1000 + service_offerings = ServiceOffering.list( + self.apiclient, + cpuspeed=1000 + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertGreaterEqual( + service_offering.cpuspeed, + 1000, + "List ServiceOfferings response has incorrect cpuspeed" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + cpuspeed=99999 + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_service_offerings_memory_filter(self): + """Test list service offerings with memory filter + """ + # List all service offerings with memory 256 + service_offerings = ServiceOffering.list( + self.apiclient, + memory=256 + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertGreaterEqual( + service_offering.memory, + 256, + "List ServiceOfferings response has incorrect memory" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + memory=99999 + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_service_offerings_domainid_filter(self): + """Test list service offerings with domainid filter + """ + # List all service offerings with domainid + service_offerings = ServiceOffering.list( + self.apiclient, + domainid=self.domain.id + ) + self.assertIsNone( + service_offerings, + "List ServiceOfferings response is not None" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + domainid=self.child_domain_1.id + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertEqual( + service_offering.domainid, + self.child_domain_1.id, + "List ServiceOfferings response has incorrect domainid" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_service_offerings_encryptroot_filter(self): + """Test list service offerings with encryptroot filter + """ + # List all service offerings with encryptroot True + service_offerings = ServiceOffering.list( + self.apiclient, + encryptroot=True + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertTrue( + service_offering.encryptroot, + "List ServiceOfferings response has incorrect encryptroot" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + encryptroot=False + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertFalse( + service_offering.encryptroot, + "List ServiceOfferings response has incorrect encryptroot" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_service_offerings_id_filter(self): + """Test list service offerings with id filter + """ + # List all service offerings with id + service_offerings = ServiceOffering.list( + self.apiclient, + id=self.system_offering.id + ) + self.assertIsNone( + service_offerings, + "List ServiceOfferings response is not None" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + id=self.service_offering_1.id + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + self.assertEqual( + service_offerings[0].id, + self.service_offering_1.id, + "List ServiceOfferings response has incorrect id" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + id=-1 + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_service_offerings_isrecursive_filter(self): + """Test list service offerings with isrecursive filter + """ + # List all service offerings with listall True + service_offerings = ServiceOffering.list( + self.domainadmin_api_client, + isrecursive=True + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + len(self.service_offerings) + 1, + "List ServiceOfferings response is empty" + ) + + # List all service offerings with isrecursive False + service_offerings = ServiceOffering.list( + self.domainadmin_api_client, + isrecursive=False + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + len(self.service_offerings), + "List ServiceOfferings response is empty" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_list_service_offerings_issystem_filter(self): + """Test list service offerings with issystem filter + """ + # List all service offerings with issystem True + service_offerings = ServiceOffering.list( + self.apiclient, + issystem=True + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + len(self.system_service_offerings) + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertTrue( + service_offering.issystem, + "List ServiceOfferings response has incorrect issystem" + ) + + # List all service offerings with issystem False + service_offerings = ServiceOffering.list( + self.apiclient, + issystem=False + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + len(self.service_offerings) + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertFalse( + service_offering.issystem, + "List ServiceOfferings response has incorrect issystem" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_09_list_service_offerings_keyword_filter(self): + """Test list service offerings with keyword filter + """ + # List all service offerings with keyword + service_offerings = ServiceOffering.list( + self.apiclient, + keyword=self.system_offering.name + ) + self.assertIsNone( + service_offerings, + "List ServiceOfferings response is not None" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + keyword=self.service_offering_1.name + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + self.assertEqual( + service_offerings[0].name, + self.service_offering_1.name, + "List ServiceOfferings response has incorrect name" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + keyword="invalid" + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_10_list_service_offerings_name_filter(self): + """Test list service offerings with name filter + """ + # List all service offerings with name + service_offerings = ServiceOffering.list( + self.apiclient, + name=self.system_offering.name + ) + self.assertIsNone( + service_offerings, + "List ServiceOfferings response is not None" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + name=self.system_offering.name, + issystem=True + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + self.assertEqual( + service_offerings[0].name, + self.system_offering.name, + "List ServiceOfferings response has incorrect name" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + name=self.service_offering_1.name + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + self.assertEqual( + service_offerings[0].name, + self.service_offering_1.name, + "List ServiceOfferings response has incorrect name" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + name="invalid" + ) + self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_11_list_service_offerings_systemvmtype_filter(self): + """Test list service offerings with systemvmtype filter + """ + # List all service offerings with systemvmtype domainrouter + service_offerings = ServiceOffering.list( + self.apiclient, + systemvmtype="domainrouter" + ) + self.assertIsNone( + service_offerings, + "List ServiceOfferings response is not None" + ) + + service_offerings = ServiceOffering.list( + self.apiclient, + systemvmtype="domainrouter", + issystem=True + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertGreaterEqual( + len(service_offerings), + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertEqual( + service_offering.systemvmtype, + "domainrouter", + "List ServiceOfferings response has incorrect systemvmtype" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_12_list_service_offerings_zoneid_filter(self): + """Test list service offerings with zoneid filter + """ + service_offerings = ServiceOffering.list( + self.apiclient, + zoneid=self.zone.id + ) + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + len(self.service_offerings) + 1, + "List ServiceOfferings response is empty" + ) + for service_offering in service_offerings: + self.assertTrue( + service_offering.zoneid is None or service_offering.zoneid == self.zone.id, + "List ServiceOfferings response has incorrect zoneid" + ) + + if len(self.zones) > 1: + service_offerings = ServiceOffering.list( + self.apiclient, + zoneid=self.zones[1].id + ) + if service_offerings is not None: + self.assertTrue( + isinstance(service_offerings, list), + "List ServiceOfferings response is not a valid list" + ) + self.assertEqual( + len(service_offerings), + len(self.service_offerings), + "List ServiceOfferings response is empty" + ) diff --git a/test/integration/smoke/test_list_storage_pools.py b/test/integration/smoke/test_list_storage_pools.py new file mode 100644 index 00000000000..c2c075da65a --- /dev/null +++ b/test/integration/smoke/test_list_storage_pools.py @@ -0,0 +1,396 @@ +# 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. +""" Tests for API listing of storage pools with different filters +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED +from marvin.lib.base import (StoragePool) +from marvin.lib.common import (get_domain, list_accounts, + list_zones, list_clusters, list_hosts) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListStoragePools(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListStoragePools, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.hypervisor = testClient.getHypervisorInfo() + cls.domain = get_domain(cls.apiclient) + cls.zones = list_zones(cls.apiclient) + cls.zone = cls.zones[0] + cls.clusters = list_clusters(cls.apiclient) + cls.cluster = cls.clusters[0] + cls.hosts = list_hosts(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls.storage_pools = StoragePool.list(cls.apiclient) + + + @classmethod + def tearDownClass(cls): + super(TestListStoragePools, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_storage_pools_clusterid_filter(self): + """ Test list storage pools by clusterid filter + """ + storage_pools = StoragePool.list( + self.apiclient, + clusterid=self.cluster.id + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.clusterid, + self.cluster.id, + "Cluster id should be equal to the cluster id passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + clusterid="-1" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid cluster id is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_storage_pools_id_filter(self): + """ Test list storage pools by id filter + """ + valid_id = self.storage_pools[0].id + storage_pools = StoragePool.list( + self.apiclient, + id=valid_id + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertEqual( + len(storage_pools), + 1, + "Length of storage pools should be equal to 1" + ) + self.assertEqual( + storage_pools[0].id, + valid_id, + "Cluster id should be equal to the cluster id passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + id="-1" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid cluster id is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_storage_pools_ipaddress_filter(self): + """ Test list storage pools by ipaddress filter + """ + valid_ipaddress = self.storage_pools[0].ipaddress + storage_pools = StoragePool.list( + self.apiclient, + ipaddress=valid_ipaddress + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.ipaddress, + valid_ipaddress, + "IP address should be equal to the ip address passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + ipaddress="1.1.1.1" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid ip address is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_storage_pools_keyword_filter(self): + """ Test list storage pools by keyword filter + """ + valid_keyword = self.storage_pools[0].name + storage_pools = StoragePool.list( + self.apiclient, + keyword=valid_keyword + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertIn( + valid_keyword, + storage_pool.name, + "Keyword should be present in the storage pool name" + ) + + storage_pools = StoragePool.list( + self.apiclient, + keyword="invalid" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid keyword is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_storage_pools_name_filter(self): + """ Test list storage pools by name filter + """ + valid_name = self.storage_pools[0].name + storage_pools = StoragePool.list( + self.apiclient, + name=valid_name + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.name, + valid_name, + "Name should be equal to the name passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + name="invalid" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid name is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_storage_pools_path_filter(self): + """ Test list storage pools by path filter + """ + valid_path = self.storage_pools[0].path + storage_pools = StoragePool.list( + self.apiclient, + path=valid_path + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.path, + valid_path, + "Path should be equal to the path passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + path="invalid" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid path is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_storage_pools_podid_filter(self): + """ Test list storage pools by podid filter + """ + storage_pools = StoragePool.list( + self.apiclient, + podid=self.cluster.podid + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.podid, + self.cluster.podid, + "Pod id should be equal to the pod id passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + podid="-1" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid pod id is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_list_storage_pools_scope_filter(self): + """ Test list storage pools by scope filter + """ + valid_scope = self.storage_pools[0].scope + storage_pools = StoragePool.list( + self.apiclient, + scope=valid_scope + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.scope, + valid_scope, + "Scope should be equal to the scope passed in the filter" + ) + with self.assertRaises(Exception): + storage_pools = StoragePool.list( + self.apiclient, + scope="invalid" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_09_list_storage_pools_status_filter(self): + """ Test list storage pools by status filter + """ + valid_status = self.storage_pools[0].status + storage_pools = StoragePool.list( + self.apiclient, + status=valid_status + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.status, + valid_status, + "State should be equal to the status passed in the filter" + ) + with self.assertRaises(Exception): + storage_pools = StoragePool.list( + self.apiclient, + status="invalid" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_10_list_storage_pools_zoneid_filter(self): + """ Test list storage pools by zoneid filter + """ + storage_pools = StoragePool.list( + self.apiclient, + zoneid=self.zone.id + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.zoneid, + self.zone.id, + "Zone id should be equal to the zone id passed in the filter" + ) + + storage_pools = StoragePool.list( + self.apiclient, + zoneid="-1" + ) + self.assertIsNone( + storage_pools, + "Response should be empty when invalid zone id is passed" + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_11_list_storage_pools_no_filter(self): + """ Test list storage pools with no filter + """ + storage_pools = StoragePool.list( + self.apiclient + ) + self.assertTrue( + isinstance(storage_pools, list), + "Storage pool response type should be a list" + ) + self.assertGreater( + len(storage_pools), + 0, + "Length of storage pools should greater than 0" + ) diff --git a/test/integration/smoke/test_list_volumes.py b/test/integration/smoke/test_list_volumes.py new file mode 100644 index 00000000000..b08e9cf3b38 --- /dev/null +++ b/test/integration/smoke/test_list_volumes.py @@ -0,0 +1,618 @@ +# 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. +""" Tests for API listing of volumes with different filters +""" +# Import Local Modules +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.codes import FAILED +from marvin.lib.base import (Account, + Domain, + Volume, + ServiceOffering, + Tag, + DiskOffering, + VirtualMachine) +from marvin.lib.common import (get_domain, list_accounts, + list_zones, list_clusters, list_hosts, get_suitable_test_template) +# Import System modules +from nose.plugins.attrib import attr + +_multiprocess_shared_ = True + + +class TestListVolumes(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestListVolumes, cls).getClsTestClient() + cls.apiclient = testClient.getApiClient() + cls.services = testClient.getParsedTestDataConfig() + cls.hypervisor = testClient.getHypervisorInfo() + cls.domain = get_domain(cls.apiclient) + cls.zones = list_zones(cls.apiclient) + cls.zone = cls.zones[0] + cls.clusters = list_clusters(cls.apiclient) + cls.cluster = cls.clusters[0] + cls.hosts = list_hosts(cls.apiclient) + cls.account = list_accounts(cls.apiclient, name="admin")[0] + cls._cleanup = [] + + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["tiny"] + ) + cls._cleanup.append(cls.service_offering) + + template = get_suitable_test_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"], + cls.hypervisor + ) + if template == FAILED: + assert False, "get_test_template() failed to return template" + + cls.services["template"]["ostypeid"] = template.ostypeid + cls.services["template_2"]["ostypeid"] = template.ostypeid + cls.services["ostypeid"] = template.ostypeid + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["mode"] = cls.zone.networktype + + cls.disk_offering = DiskOffering.create(cls.apiclient, + cls.services["disk_offering"]) + cls._cleanup.append(cls.disk_offering) + + # Get already existing volumes in the env for assertions + cls.volumes = Volume.list(cls.apiclient, zoneid=cls.zone.id) or [] + + # Create VM + cls.virtual_machine = VirtualMachine.create( + cls.apiclient, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + clusterid=cls.cluster.id, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + cls.child_domain = Domain.create( + cls.apiclient, + cls.services["domain"]) + cls._cleanup.append(cls.child_domain) + + cls.child_account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.child_domain.id) + cls._cleanup.append(cls.child_account) + + cls.vol_1 = Volume.create(cls.apiclient, + cls.services["volume"], + zoneid=cls.zone.id, + account=cls.account.name, + domainid=cls.account.domainid, + diskofferingid=cls.disk_offering.id) + cls._cleanup.append(cls.vol_1) + + cls.vol_1 = cls.virtual_machine.attach_volume( + cls.apiclient, + cls.vol_1 + ) + cls._cleanup.append(cls.virtual_machine) + + Tag.create(cls.apiclient, cls.vol_1.id, "Volume", {"abc": "xyz"}) + + cls.vol_2 = Volume.create(cls.apiclient, + cls.services["volume"], + zoneid=cls.zone.id, + account=cls.account.name, + domainid=cls.account.domainid, + diskofferingid=cls.disk_offering.id) + + cls._cleanup.append(cls.vol_2) + + cls.vol_3 = Volume.create(cls.apiclient, + cls.services["volume"], + zoneid=cls.zone.id, + account=cls.child_account.name, + domainid=cls.child_account.domainid, + diskofferingid=cls.disk_offering.id) + cls._cleanup.append(cls.vol_3) + + @classmethod + def tearDownClass(cls): + super(TestListVolumes, cls).tearDownClass() + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_01_list_volumes_account_domain_filter(self): + """Test listing Volumes with account & domain filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 3, + "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) + ) + + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + account=self.child_account.name, + domainid=self.child_account.domainid + ) + + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 Volume, received %s" % len(list_volume_response) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_02_list_volumes_diskofferingid_filter(self): + """Test listing Volumes with diskofferingid filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_03_list_volumes_id_filter(self): + """Test listing Volumes with id filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + id=self.vol_1.id + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 Volume, received %s" % len(list_volume_response) + ) + self.assertEqual( + list_volume_response[0].id, + self.vol_1.id, + "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_04_list_volumes_ids_filter(self): + """Test listing Volumes with ids filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + ids=[self.vol_1.id, self.vol_2.id, self.vol_3.id] + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], + "ListVolumes response Volume 1 not in list") + self.assertIn(list_volume_response[1].id, [self.vol_1.id, self.vol_2.id], + "ListVolumes response Volume 2 not in list") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_05_list_volumes_isrecursive(self): + """Test listing Volumes with isrecursive filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + isrecursive=True, + domainid=self.account.domainid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response) - len(self.volumes), + 4, + "ListVolumes response expected 4 Volumes, received %s" % len(list_volume_response) + ) + + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + isrecursive=False, + domainid=self.account.domainid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response) - len(self.volumes), + 3, + "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_06_list_volumes_keyword_filter(self): + """Test listing Volumes with keyword filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + keyword=self.services["volume"]["diskname"] + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + self.assertIn( + list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], + "ListVolumes response Volume 1 not in list") + self.assertIn(list_volume_response[1].id, [self.vol_1.id, self.vol_2.id], + "ListVolumes response Volume 2 not in list") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_07_list_volumes_listall(self): + """Test listing Volumes with listall filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response) - len(self.volumes), + 4, + "ListVolumes response expected 4 Volumes, received %s" % len(list_volume_response) + ) + + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + listall=False + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response) - len(self.volumes), + 3, + "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_08_listsystemvms(self): + list_volumes_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + listsystemvms=True + ) + self.assertEqual( + isinstance(list_volumes_response, list), + True, + "List Volume response is not a valid list" + ) + self.assertGreater( + len(list_volumes_response), + 3, + "ListVolumes response expected more than 3 Volumes, received %s" % len(list_volumes_response) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_09_list_volumes_name_filter(self): + """Test listing Volumes with name filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + name=self.vol_1.name + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) + ) + self.assertEqual( + list_volume_response[0].id, + self.vol_1.id, + "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) + ) + self.assertEqual( + list_volume_response[0].name, + self.vol_1.name, + "ListVolumes response expected Volume with name %s, received %s" % ( + self.vol_1.name, list_volume_response[0].name) + ) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_10_list_volumes_podid_filter(self): + """Test listing Volumes with podid filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + podid=self.vol_1.podid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertGreater( + len(list_volume_response), + 1, + "ListVolumes response expected more than 1 Volume, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_11_list_volumes_state_filter(self): + """Test listing Volumes with state filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + state="Ready" + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) + + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + state="Allocated" + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) + ) + self.assertEqual(self.vol_2.id, list_volume_response[0].id, + "ListVolumes response expected Volume with id %s" % self.vol_3.id) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_12_list_volumes_storageid_filter(self): + """Test listing Volumes with storageid filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + storageid=self.vol_1.storageid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertGreaterEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 or more Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_13_list_volumes_type_filter(self): + """Test listing Volumes with type filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + type="DATADISK" + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) + + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + type="ROOT" + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) + ) + self.assertNotIn(list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], + "ListVolumes response expected ROOT Volume") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_14_list_volumes_virtualmachineid_filter(self): + """Test listing Volumes with virtualmachineid filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id, + virtualmachineid=self.vol_1.virtualmachineid + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 2, + "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_15_list_volumes_zoneid_filter(self): + """Test listing Volumes with zoneid filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zones[0].id + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 3, + "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) + ) + + if len(self.zones) > 1: + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zones[1].id + ) + self.assertIsNone(list_volume_response, "List Volume response is not None") + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_16_list_volumes_tags_filter(self): + """Test listing Volumes with tags filter + """ + list_volume_response = Volume.list( + self.apiclient, + tags=[{"key": "abc", "value": "xyz"}] + ) + + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertEqual( + len(list_volume_response), + 1, + "ListVolumes response expected 1 or more Volumes, received %s" % len(list_volume_response) + ) + self.assertEqual( + list_volume_response[0].id, + self.vol_1.id, + "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) + ) + self.assertEqual( + list_volume_response[0].tags[0]["key"], + "abc", + "ListVolumes response expected Volume with tag key abc, received %s" % list_volume_response[0].tags[0]["key"] + ) + self.assertEqual( + list_volume_response[0].tags[0]["value"], + "xyz", + "ListVolumes response expected Volume with tag value xyz, received %s" % list_volume_response[0].tags[0]["value"] + ) + + list_volume_response = Volume.list( + self.apiclient, + tags=[{"key": "abc", "value": "xyz1"}] + ) + self.assertIsNone(list_volume_response, "List Volume response is not None") + with self.assertRaises(Exception): + list_volume_response = Volume.list( + self.apiclient, + tags=[{"key": None, "value": None}] + ) + + + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") + def test_17_list_volumes_no_filter(self): + """Test listing Volumes with no filter + """ + list_volume_response = Volume.list( + self.apiclient, + zoneid=self.zone.id + ) + self.assertTrue( + isinstance(list_volume_response, list), + "List Volume response is not a valid list" + ) + self.assertGreaterEqual( + len(list_volume_response), + 3, + "ListVolumes response expected 3 or more Volumes, received %s" % len(list_volume_response) + ) + self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], + "ListVolumes response expected Volume with id %s" % self.vol_1.id) diff --git a/test/integration/smoke/test_metrics_api.py b/test/integration/smoke/test_metrics_api.py index d5ad559fad0..1042ad997fc 100644 --- a/test/integration/smoke/test_metrics_api.py +++ b/test/integration/smoke/test_metrics_api.py @@ -289,7 +289,6 @@ class TestMetrics(cloudstackTestCase): self.assertTrue(hasattr(li, 'hosts')) self.assertEqual(li.hosts, len(list_hosts(self.apiclient, - zoneid=self.zone.id, type='Routing'))) self.assertTrue(hasattr(li, 'imagestores')) diff --git a/test/integration/smoke/test_secondary_storage.py b/test/integration/smoke/test_secondary_storage.py index 5b339ae67b9..4b26950ea64 100644 --- a/test/integration/smoke/test_secondary_storage.py +++ b/test/integration/smoke/test_secondary_storage.py @@ -340,7 +340,7 @@ class TestSecStorageServices(cloudstackTestCase): # 1. Try complete migration from a storage with more (or equal) free space - migration should be refused storages = self.list_secondary_storages(self.apiclient) - if (len(storages)) < 2: + if (len(storages)) < 2 or (storages[0]['zoneid'] != storages[1]['zoneid']): self.skipTest( "This test requires more than one secondary storage") diff --git a/test/integration/smoke/test_templates.py b/test/integration/smoke/test_templates.py index 66008b1a8f3..2696db8f96b 100644 --- a/test/integration/smoke/test_templates.py +++ b/test/integration/smoke/test_templates.py @@ -1024,17 +1024,17 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): cls.services["disk_offering"] ) cls._cleanup.append(cls.disk_offering) - template = get_template( + cls.template = get_template( cls.apiclient, cls.zone.id, cls.services["ostype"] ) - if template == FAILED: + if cls.template == FAILED: assert False, "get_template() failed to return template with description %s" % cls.services["ostype"] - cls.services["template"]["ostypeid"] = template.ostypeid - cls.services["template_2"]["ostypeid"] = template.ostypeid - cls.services["ostypeid"] = template.ostypeid + cls.services["template"]["ostypeid"] = cls.template.ostypeid + cls.services["template_2"]["ostypeid"] = cls.template.ostypeid + cls.services["ostypeid"] = cls.template.ostypeid cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["volume"]["diskoffering"] = cls.disk_offering.id @@ -1055,7 +1055,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): cls.virtual_machine = VirtualMachine.create( cls.apiclient, cls.services["virtual_machine"], - templateid=template.id, + templateid=cls.template.id, accountid=cls.account.name, domainid=cls.account.domainid, serviceofferingid=cls.service_offering.id, @@ -1104,7 +1104,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(tags=["advanced", "advancedns"], required_hardware="false") + @attr(tags=["advanced", "advancedns"], required_hardware="true") def test_09_copy_delete_template(self): cmd = listZones.listZonesCmd() zones = self.apiclient.listZones(cmd) @@ -1156,7 +1156,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): list_template_response = Template.list( self.apiclient, - templatefilter=self.services["template"]["templatefilter"], + templatefilter=self.services["templatefilter"], id=self.template.id, zoneid=self.destZone.id ) From 969e094419d66fab925d1185c3c2551bf22ad407 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Thu, 21 Dec 2023 13:06:32 +0530 Subject: [PATCH 011/212] server: improve stats collector logs to state what the collector does (#8387) This simply improves the log statement that prints debug statements during beginning of a stats collector run for hosts or VMs. Signed-off-by: Rohit Yadav --- .../main/java/com/cloud/server/StatsCollector.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/cloud/server/StatsCollector.java b/server/src/main/java/com/cloud/server/StatsCollector.java index 91410198e2f..19820093f3a 100644 --- a/server/src/main/java/com/cloud/server/StatsCollector.java +++ b/server/src/main/java/com/cloud/server/StatsCollector.java @@ -644,13 +644,12 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc @Override protected void runInContext() { try { - LOGGER.debug("HostStatsCollector is running..."); - SearchCriteria sc = createSearchCriteriaForHostTypeRoutingStateUpAndNotInMaintenance(); - - Map metrics = new HashMap<>(); List hosts = _hostDao.search(sc, null); + LOGGER.debug(String.format("HostStatsCollector is running to process %d UP hosts", hosts.size())); + + Map metrics = new HashMap<>(); for (HostVO host : hosts) { HostStatsEntry hostStatsEntry = (HostStatsEntry) _resourceMgr.getHostStatistics(host.getId()); if (hostStatsEntry != null) { @@ -1192,13 +1191,12 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc @Override protected void runInContext() { try { - LOGGER.trace("VmStatsCollector is running..."); - SearchCriteria sc = createSearchCriteriaForHostTypeRoutingStateUpAndNotInMaintenance(); List hosts = _hostDao.search(sc, null); - Map metrics = new HashMap<>(); + LOGGER.debug(String.format("VmStatsCollector is running to process VMs across %d UP hosts", hosts.size())); + Map metrics = new HashMap<>(); for (HostVO host : hosts) { Date timestamp = new Date(); Map vmMap = getVmMapForStatsForHost(host); From 9d3a7be4dd9f68acab05ba28f89e1467622205b8 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 21 Dec 2023 09:47:57 +0100 Subject: [PATCH 012/212] server: fix debug message when expunge a vm (#8374) This PR fixes the debug message when expunge a vm --- .../java/com/cloud/vm/VirtualMachineManagerImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 4c8883476a2..a49609fc374 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -602,7 +602,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { if (vm == null || vm.getRemoved() != null) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find vm or vm is destroyed: " + vm); + s_logger.debug("Unable to find vm or vm is expunged: " + vm); } return; } @@ -612,17 +612,17 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac try { if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); - throw new CloudRuntimeException("Unable to destroy " + vm); + s_logger.debug("Unable to expunge the vm because it is not in the correct state: " + vm); + throw new CloudRuntimeException("Unable to expunge " + vm); } } catch (final NoTransitionException e) { - s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); - throw new CloudRuntimeException("Unable to destroy " + vm, e); + s_logger.debug("Unable to expunge the vm because it is not in the correct state: " + vm); + throw new CloudRuntimeException("Unable to expunge " + vm, e); } if (s_logger.isDebugEnabled()) { - s_logger.debug("Destroying vm " + vm); + s_logger.debug("Expunging vm " + vm); } final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); From d83d994929828ab63f23047dfe819c4c2d637d8e Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 21 Dec 2023 20:54:43 +0530 Subject: [PATCH 013/212] test: additional check to ensure hosts are left in up state (#8383) With this change, a fix is added for failures seen with test_08_migrate_vm or other migration-related tests because the target host is in `Connecting` state, #8356 (comment) #8374 (comment) and more --- test/integration/smoke/test_vm_life_cycle.py | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/integration/smoke/test_vm_life_cycle.py b/test/integration/smoke/test_vm_life_cycle.py index a9a554e19ad..6c824c1fe7d 100644 --- a/test/integration/smoke/test_vm_life_cycle.py +++ b/test/integration/smoke/test_vm_life_cycle.py @@ -1011,8 +1011,37 @@ class TestSecuredVmMigration(cloudstackTestCase): @classmethod def tearDownClass(cls): + if cls.hypervisor.lower() in ["kvm"]: + cls.ensure_all_hosts_are_up() super(TestSecuredVmMigration, cls).tearDownClass() + @classmethod + def ensure_all_hosts_are_up(cls): + hosts = Host.list( + cls.apiclient, + zoneid=cls.zone.id, + type='Routing', + hypervisor='KVM' + ) + for host in hosts: + if host.state != "Up": + SshClient(host.ipaddress, port=22, user=cls.hostConfig["username"], passwd=cls.hostConfig["password"]) \ + .execute("service cloudstack-agent stop ; \ + sleep 10 ; \ + service cloudstack-agent start") + interval = 5 + retries = 10 + while retries > -1: + time.sleep(interval) + restarted_host = Host.list( + cls.apiclient, + hostid=host.id, + type='Routing' + )[0] + if restarted_host.state == "Up": + break + retries = retries - 1 + def setUp(self): self.apiclient = self.testClient.getApiClient() self.dbclient = self.testClient.getDbConnection() From ab808995ffe76a2a308d91bbc761693538cfa0dd Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 21 Dec 2023 20:56:40 +0530 Subject: [PATCH 014/212] Revert "Add e2e tests for listing resources (#8281)" (#8396) This reverts commit 1411da1a22bc6aa26634f3038475e3d5fbbcd6bb. --- .github/workflows/ci.yml | 15 +- setup/dev/advdualzone.cfg | 60 +- .../test_affinity_groups_projects.py | 2 +- test/integration/smoke/test_list_accounts.py | 379 ----------- .../smoke/test_list_disk_offerings.py | 319 --------- test/integration/smoke/test_list_domains.py | 216 ------ test/integration/smoke/test_list_hosts.py | 372 ----------- .../smoke/test_list_service_offerings.py | 559 ---------------- .../smoke/test_list_storage_pools.py | 396 ----------- test/integration/smoke/test_list_volumes.py | 618 ------------------ test/integration/smoke/test_metrics_api.py | 1 + .../smoke/test_secondary_storage.py | 2 +- test/integration/smoke/test_templates.py | 16 +- 13 files changed, 45 insertions(+), 2910 deletions(-) delete mode 100644 test/integration/smoke/test_list_accounts.py delete mode 100644 test/integration/smoke/test_list_disk_offerings.py delete mode 100644 test/integration/smoke/test_list_domains.py delete mode 100644 test/integration/smoke/test_list_hosts.py delete mode 100644 test/integration/smoke/test_list_service_offerings.py delete mode 100644 test/integration/smoke/test_list_storage_pools.py delete mode 100644 test/integration/smoke/test_list_volumes.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd96fcfce4b..c6edc7bdb20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,15 +194,8 @@ jobs: component/test_vpc_network component/test_vpc_offerings component/test_vpc_routers - component/test_vpn_users - component/test_vpc_network_lbrules", - "smoke/test_list_accounts - smoke/test_list_disk_offerings - smoke/test_list_domains - smoke/test_list_hosts - smoke/test_list_service_offerings - smoke/test_list_storage_pools - smoke/test_list_volumes"] + component/test_vpn_users", + "component/test_vpc_network_lbrules" ] steps: - uses: actions/checkout@v4 @@ -285,7 +278,7 @@ jobs: while ! nc -vzw 5 localhost 8096 2>&1 > /dev/null; do grep Exception /tmp/jetty-log; sleep 10; done set -e echo -e "\nStarting Advanced Zone DataCenter deployment" - python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advdualzone.cfg 2>&1 || true + python3 tools/marvin/marvin/deployDataCenter.py -i setup/dev/advanced.cfg 2>&1 || true - name: Run Integration Tests with Simulator run: | @@ -298,7 +291,7 @@ jobs: TESTS=($(echo $TESTS | tr -d '\n' | tr -s ' ')) for suite in "${TESTS[@]}" ; do echo -e "Currently running test: $suite\n" - time nosetests-3.4 --with-xunit --xunit-file=integration-test-results/$suite.xml --with-marvin --marvin-config=setup/dev/advdualzone.cfg test/integration/$suite.py -s -a tags=advanced,required_hardware=false --zone=zim1 --hypervisor=simulator || true ; + time nosetests-3.4 --with-xunit --xunit-file=integration-test-results/$suite.xml --with-marvin --marvin-config=setup/dev/advanced.cfg test/integration/$suite.py -s -a tags=advanced,required_hardware=false --zone=Sandbox-simulator --hypervisor=simulator || true ; done echo -e "Stopping Simulator, integration tests run completed\n" diff --git a/setup/dev/advdualzone.cfg b/setup/dev/advdualzone.cfg index 97a98402348..b11675d712a 100644 --- a/setup/dev/advdualzone.cfg +++ b/setup/dev/advdualzone.cfg @@ -18,12 +18,12 @@ "zones": [ { "name": "zim1", - "guestcidraddress": "10.1.1.0/24", - "dns1": "10.147.28.6", + "guestcidraddress": "10.100.1.0/24", + "dns1": "10.147.100.6", "physical_networks": [ { "broadcastdomainrange": "Zone", - "vlan": "100-200", + "vlan": "1100-1200", "name": "z1-pnet", "traffictypes": [ { @@ -63,19 +63,19 @@ }, "ipranges": [ { - "startip": "192.168.2.2", - "endip": "192.168.2.200", + "startip": "192.168.100.2", + "endip": "192.168.100.200", "netmask": "255.255.255.0", "vlan": "50", - "gateway": "192.168.2.1" + "gateway": "192.168.100.1" } ], "networktype": "Advanced", "pods": [ { - "endip": "172.16.15.200", + "endip": "172.16.100.200", "name": "Z1P1", - "startip": "172.16.15.2", + "startip": "172.16.100.2", "netmask": "255.255.255.0", "clusters": [ { @@ -96,11 +96,11 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.28.6:/export/home/sandbox/z1p1", + "url": "nfs://10.147.100.6:/export/home/sandbox/z1p1", "name": "Z1PS1" }, { - "url": "nfs://10.147.28.6:/export/home/sandbox/z1p2", + "url": "nfs://10.147.100.6:/export/home/sandbox/z1p2", "name": "Z1PS2" } ] @@ -123,35 +123,35 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.28.6:/export/home/sandbox/z1p3", + "url": "nfs://10.147.100.6:/export/home/sandbox/z1p3", "name": "Z1PS3" }, { - "url": "nfs://10.147.28.6:/export/home/sandbox/z1p4", + "url": "nfs://10.147.100.6:/export/home/sandbox/z1p4", "name": "Z1PS4" } ] } ], - "gateway": "172.16.15.1" + "gateway": "172.16.100.1" } ], - "internaldns1": "10.147.28.6", + "internaldns1": "10.147.100.6", "secondaryStorages": [ { - "url": "nfs://10.147.28.6:/export/home/sandbox/z1secondary", + "url": "nfs://10.147.100.6:/export/home/sandbox/z1secondary", "provider" : "NFS" } ] }, { "name": "zim2", - "guestcidraddress": "10.1.2.0/24", - "dns1": "10.147.29.6", + "guestcidraddress": "10.200.1.0/24", + "dns1": "10.147.200.6", "physical_networks": [ { "broadcastdomainrange": "Zone", - "vlan": "300-400", + "vlan": "2100-2200", "name": "z2-pnet", "traffictypes": [ { @@ -191,19 +191,19 @@ }, "ipranges": [ { - "startip": "192.168.3.2", - "endip": "192.168.3.200", + "startip": "192.168.200.2", + "endip": "192.168.200.200", "netmask": "255.255.255.0", - "vlan": "51", - "gateway": "192.168.3.1" + "vlan": "50", + "gateway": "192.168.200.1" } ], "networktype": "Advanced", "pods": [ { - "endip": "172.16.16.200", + "endip": "172.16.200.200", "name": "Z2P1", - "startip": "172.16.16.2", + "startip": "172.16.200.2", "netmask": "255.255.255.0", "clusters": [ { @@ -224,11 +224,11 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.29.6:/export/home/sandbox/z2p1", + "url": "nfs://10.147.200.6:/export/home/sandbox/z2p1", "name": "Z2PS1" }, { - "url": "nfs://10.147.29.6:/export/home/sandbox/z2p2", + "url": "nfs://10.147.200.6:/export/home/sandbox/z2p2", "name": "Z2PS2" } ] @@ -251,20 +251,20 @@ "clustertype": "CloudManaged", "primaryStorages": [ { - "url": "nfs://10.147.29.6:/export/home/sandbox/z2p3", + "url": "nfs://10.147.200.6:/export/home/sandbox/z2p3", "name": "Z2PS3" }, { - "url": "nfs://10.147.29.6:/export/home/sandbox/z2p4", + "url": "nfs://10.147.200.6:/export/home/sandbox/z2p4", "name": "Z2PS4" } ] } ], - "gateway": "172.16.16.1" + "gateway": "172.16.200.1" } ], - "internaldns1": "10.147.29.6", + "internaldns1": "10.147.200.6", "secondaryStorages": [ { "url": "nfs://10.147.200.6:/export/home/sandbox/z2secondary", diff --git a/test/integration/component/test_affinity_groups_projects.py b/test/integration/component/test_affinity_groups_projects.py index 07811e79fde..1c0b4c2bdd8 100644 --- a/test/integration/component/test_affinity_groups_projects.py +++ b/test/integration/component/test_affinity_groups_projects.py @@ -1065,7 +1065,7 @@ class TestDeployVMAffinityGroups(cloudstackTestCase): """ test DeployVM in anti-affinity groups with more vms than hosts. """ - hosts = list_hosts(self.api_client, type="routing", zoneid=self.zone.id) + hosts = list_hosts(self.api_client, type="routing") aff_grp = self.create_aff_grp(self.account_api_client) vms = [] for host in hosts: diff --git a/test/integration/smoke/test_list_accounts.py b/test/integration/smoke/test_list_accounts.py deleted file mode 100644 index 1cce3cef170..00000000000 --- a/test/integration/smoke/test_list_accounts.py +++ /dev/null @@ -1,379 +0,0 @@ -# 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. -""" Tests for API listing of accounts with different filters -""" - -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.lib.base import (Account, - Domain) -from marvin.lib.common import (get_domain, list_accounts) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListAccounts(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListAccounts, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.domain = get_domain(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls._cleanup = [] - cls.accounts = list_accounts(cls.apiclient, listall=True) - - cls.child_domain_1 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.domain.id - ) - cls._cleanup.append(cls.child_domain_1) - - cls.services["account"]["username"] = "child_account_admin" - cls.child_account_admin = Account.create( - cls.apiclient, - cls.services["account"], - admin=True, - domainid=cls.child_domain_1.id - ) - cls._cleanup.append(cls.child_account_admin) - - cls.services["username"] = "child_account_user" - cls.child_account_user = Account.create( - cls.apiclient, - cls.services["account"], - admin=0, - domainid=cls.child_domain_1.id - ) - cls.child_account_user.disable(cls.apiclient) - cls._cleanup.append(cls.child_account_user) - - cls.child_domain_2 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.domain.id - ) - cls._cleanup.append(cls.child_domain_2) - - @classmethod - def tearDownClass(cls): - super(TestListAccounts, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_accounts_accounttype_filter(self): - """Test listing accounts with accounttype filter - """ - list_account_response = Account.list( - self.apiclient, - accounttype=0, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_user.name, - "Check for list response return valid data" - ) - self.assertEqual( - list_account_response[0].accounttype, - 0, - "Check for list response return valid data" - ) - - list_account_response = Account.list( - self.apiclient, - accounttype=2, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_admin.name, - "Check for list response return valid data" - ) - self.assertEqual( - list_account_response[0].accounttype, - 2, - "Check for list response return valid data" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_accounts_domainid_filter(self): - """Test listing accounts with domainid filter - """ - list_account_response = Account.list( - self.apiclient, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 2, - "List Account response has incorrect length" - ) - self.assertEqual( - self.child_domain_1.id, - list_account_response[0].domainid, - "Check for list response return valid data" - ) - self.assertEqual( - self.child_domain_1.id, - list_account_response[1].domainid, - "Check for list response return valid data" - ) - - list_account_response = Account.list( - self.apiclient, - domainid=self.child_domain_2.id - ) - self.assertIsNone(list_account_response, "Check for list response return valid data") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_accounts_id_filter(self): - """Test listing accounts with id filter - """ - list_account_response = Account.list( - self.apiclient, - id=self.child_account_user.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_user.name, - "Expected account name and actual account name should be same" - ) - - list_account_response = Account.list( - self.apiclient, - id=self.child_account_admin.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_admin.name, - "Expected account name and actual account name should be same" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_accounts_name_filter(self): - """Test listing accounts with name filter - """ - list_account_response = Account.list( - self.apiclient, - name=self.child_account_user.name, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_user.name, - "Expected account name and actual account name should be same" - ) - - list_account_response = Account.list( - self.apiclient, - name=self.child_account_admin.name, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_admin.name, - "Expected account name and actual account name should be same" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_accounts_state_filter(self): - """Test listing accounts with state filter - """ - list_account_response = Account.list( - self.apiclient, - state="enabled", - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_admin.name, - "Expected account name and actual account name should be same" - ) - - list_account_response = Account.list( - self.apiclient, - state="disabled", - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - len(list_account_response), - 1, - "List Account response has incorrect length" - ) - self.assertEqual( - list_account_response[0].name, - self.child_account_user.name, - "Expected account name and actual account name should be same" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_accounts_keyword_filter(self): - """Test listing accounts with keyword filter - """ - list_account_response = Account.list( - self.apiclient, - keyword=self.child_account_user.name, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - self.child_account_user.name, - list_account_response[0].name, - "Expected account name and actual account name should be same" - ) - - list_account_response = Account.list( - self.apiclient, - keyword=self.child_account_admin.name, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - self.child_account_admin.name, - list_account_response[0].name, - "Expected account name and actual account name should be same" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_accounts_with_listall_filters(self): - """Test listing accounts with listall filters - """ - list_account_response = Account.list( - self.apiclient, - listall=False - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - 1, - len(list_account_response), - "List Account response has incorrect length" - ) - - list_account_response = Account.list( - self.apiclient, - listall=True - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - 2, - len(list_account_response) - len(self.accounts), - "List Account response has incorrect length" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_list_accounts_with_no_filters(self): - """Test listing accounts with no filters - """ - list_account_response = Account.list( - self.apiclient - ) - self.assertTrue( - isinstance(list_account_response, list), - "List Account response is not a valid list" - ) - self.assertEqual( - 1, - len(list_account_response), - "List Account response has incorrect length" - ) diff --git a/test/integration/smoke/test_list_disk_offerings.py b/test/integration/smoke/test_list_disk_offerings.py deleted file mode 100644 index 6319ea338ad..00000000000 --- a/test/integration/smoke/test_list_disk_offerings.py +++ /dev/null @@ -1,319 +0,0 @@ -# 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. -""" Tests for API listing of disk offerings with different filters -""" -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.codes import FAILED -from marvin.lib.base import (Account, - Domain, - Volume, - ServiceOffering, - DiskOffering, - VirtualMachine) -from marvin.lib.common import (get_domain, list_accounts, - list_zones, list_clusters, list_hosts) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListDiskOfferings(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListDiskOfferings, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.hypervisor = testClient.getHypervisorInfo() - cls.domain = get_domain(cls.apiclient) - cls.zones = list_zones(cls.apiclient) - cls.zone = cls.zones[0] - cls.clusters = list_clusters(cls.apiclient) - cls.cluster = cls.clusters[0] - cls.hosts = list_hosts(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls._cleanup = [] - cls.disk_offerings = DiskOffering.list(cls.apiclient, listall=True) - - cls.disk_offering = DiskOffering.create(cls.apiclient, - cls.services["disk_offering"], - domainid=cls.domain.id) - cls._cleanup.append(cls.disk_offering) - - cls.child_domain_1 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.domain.id - ) - cls._cleanup.append(cls.child_domain_1) - - cls.account_1 = Account.create( - cls.apiclient, - cls.services["account"], - admin=True, - domainid=cls.domain.id - ) - cls._cleanup.append(cls.account_1) - - cls.domainadmin_api_client = testClient.getUserApiClient( - UserName=cls.account_1.user[0].username, - DomainName=cls.domain.name, - type=2 - ) - - cls.disk_offering_child_domain = DiskOffering.create(cls.apiclient, - cls.services["disk_offering"], - domainid=cls.child_domain_1.id, - zoneid=cls.zone.id, - encrypt=True) - cls._cleanup.append(cls.disk_offering_child_domain) - - @classmethod - def tearDownClass(cls): - super(TestListDiskOfferings, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_disk_offerings_id_filter(self): - """ Test list disk offerings with id filter - """ - # List all disk offerings - disk_offerings = DiskOffering.list(self.apiclient, id=self.disk_offering.id) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - # Verify the id of the disk offering returned is the same as the one requested - self.assertEqual( - disk_offerings[0].id, - self.disk_offering.id, - "List disk offerings should return the disk offering requested" - ) - - disk_offerings = DiskOffering.list(self.apiclient, id=-1) - self.assertIsNone(disk_offerings, "List disk offerings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_disk_offerings_name_filter(self): - """ Test list disk offerings with name filter - """ - disk_offerings = DiskOffering.list(self.apiclient, name=self.services["disk_offering"]["name"]) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings), - 2, - "List disk offerings response has incorrect length" - ) - # Verify the name of the disk offering returned is the same as the one requested - self.assertEqual( - disk_offerings[0].name, - self.services["disk_offering"]["name"], - "List disk offerings should return the disk offering requested" - ) - self.assertEqual( - disk_offerings[1].name, - self.services["disk_offering"]["name"], - "List disk offerings should return the disk offering requested" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_disk_offerings_zoneid_filter(self): - """ Test list disk offerings with zoneid filter - """ - disk_offerings_zone_1 = DiskOffering.list(self.apiclient, zoneid=self.zone.id) - self.assertTrue( - isinstance(disk_offerings_zone_1, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings_zone_1) - len(self.disk_offerings), - 2, - "List disk offerings response has incorrect length" - ) - - for disk_offering in disk_offerings_zone_1: - self.assertTrue( - disk_offering.zoneid is None or disk_offering.zoneid == self.zone.id, - "List disk offerings should return the disk offering requested" - ) - - if len(self.zones) > 1: - disk_offerings_zone_2 = DiskOffering.list(self.apiclient, zoneid=self.zones[1].id) - self.assertTrue( - isinstance(disk_offerings_zone_2, list), - "List disk offerings response is not a valid list" - ) - for disk_offering in disk_offerings_zone_2: - self.assertTrue( - disk_offering.zoneid is None or disk_offering.zoneid == self.zones[1].id, - "List disk offerings should return the disk offering requested" - ) - - self.assertEqual(len(disk_offerings_zone_1) - len(disk_offerings_zone_2), 1) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_disk_offerings_domainid_filter(self): - """ Test list disk offerings with domainid filter - """ - disk_offerings = DiskOffering.list(self.apiclient, domainid=self.domain.id) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - self.assertEqual( - disk_offerings[0].domainid, - self.domain.id, - "List disk offerings should return the disk offering requested" - ) - - disk_offerings = DiskOffering.list(self.apiclient, domainid=self.child_domain_1.id) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - self.assertEqual( - disk_offerings[0].domainid, - self.child_domain_1.id, - "List disk offerings should return the disk offering requested" - ) - - disk_offerings = DiskOffering.list(self.apiclient, domainid=-1) - self.assertIsNone(disk_offerings, "List disk offerings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_disk_offerings_encrypted_filter(self): - """ Test list disk offerings with encrypted filter - """ - disk_offerings = DiskOffering.list(self.apiclient, encrypt=True) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - - self.assertEqual( - len(disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - self.assertTrue( - disk_offerings[0].encrypt, - "List disk offerings should return the disk offering requested" - ) - - disk_offerings = DiskOffering.list(self.apiclient, encrypt=False) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings) - len(self.disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - for disk_offering in disk_offerings: - self.assertFalse( - disk_offering.encrypt, - "List disk offerings should return the disk offering requested" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_disk_offerings_keyword_filter(self): - """ Test list disk offerings with keyword filter - """ - disk_offerings = DiskOffering.list(self.apiclient, keyword=self.disk_offering.name) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings), - 2, - "List disk offerings response has incorrect length" - ) - self.assertEqual( - disk_offerings[0].name, - self.disk_offering.name, - "List disk offerings should return the disk offering requested" - ) - self.assertEqual( - disk_offerings[1].name, - self.disk_offering.name, - "List disk offerings should return the disk offering requested" - ) - - disk_offerings = DiskOffering.list(self.apiclient, keyword="random") - self.assertIsNone(disk_offerings, "List disk offerings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_disk_offering_isrecursive_filter(self): - """ Test list disk offerings with isrecursive parameter - """ - disk_offerings = DiskOffering.list(self.domainadmin_api_client, isrecursive=True) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings) - len(self.disk_offerings), - 2, - "List disk offerings response has incorrect length" - ) - - disk_offerings = DiskOffering.list(self.domainadmin_api_client, isrecursive=False) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings) - len(self.disk_offerings), - 1, - "List disk offerings response has incorrect length" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_list_disk_offering_no_filter(self): - """ Test list disk offerings with no filters - """ - disk_offerings = DiskOffering.list(self.apiclient) - self.assertTrue( - isinstance(disk_offerings, list), - "List disk offerings response is not a valid list" - ) - self.assertEqual( - len(disk_offerings) - len(self.disk_offerings), - 2, - "List disk offerings response has incorrect length" - ) diff --git a/test/integration/smoke/test_list_domains.py b/test/integration/smoke/test_list_domains.py deleted file mode 100644 index 546ffbbf1e3..00000000000 --- a/test/integration/smoke/test_list_domains.py +++ /dev/null @@ -1,216 +0,0 @@ -# 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. -""" Tests for API listing of domains with different filters -""" - -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.lib.base import (Account, - Domain) -from marvin.lib.common import (get_domain, list_accounts) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListDomains(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListDomains, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.domain = get_domain(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls._cleanup = [] - - cls.child_domain_1 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.domain.id - ) - cls._cleanup.append(cls.child_domain_1) - - cls.child_account_1 = Account.create( - cls.apiclient, - cls.services["account"], - admin=True, - domainid=cls.child_domain_1.id - ) - cls._cleanup.append(cls.child_account_1) - - cls.child_account_apiclient = testClient.getUserApiClient(cls.child_account_1.user[0]['username'], cls.child_domain_1.name, type=2) - - cls.child_domain_2 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.child_domain_1.id - ) - cls._cleanup.append(cls.child_domain_2) - - @classmethod - def tearDownClass(cls): - super(TestListDomains, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_domains_id_filter(self): - """ Test list domains with id filter - """ - # List all domains - domains = Domain.list(self.apiclient, id=self.domain.id) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - self.assertEqual( - domains[0].id, - self.domain.id, - "Check if list domains returns valid domain" - ) - - # List all domains with a non-existent id - with self.assertRaises(Exception): - Domain.list(self.apiclient, id=-1) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_domains_name_filter(self): - """ Test list domains with name filter - """ - # List all domains - domains = Domain.list(self.apiclient, name=self.domain.name) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - self.assertEqual( - domains[0].name, - self.domain.name, - "Check if list domains returns valid domain" - ) - - domains = Domain.list(self.apiclient, name="non-existent-domain") - self.assertIsNone(domains, "List Domain response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_domains_listall_filter(self): - """ Test list domains with listall parameter - """ - # List all domains - domains = Domain.list(self.child_account_apiclient, listall=True) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 2, - "List Domain response has incorrect length" - ) - - domains = Domain.list(self.child_account_apiclient, listall=False) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - self.assertEqual( - domains[0].id, - self.child_domain_1.id, - "Check if list domains returns valid domain" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_domains_level_filter(self): - """ Test list domains with level filter - """ - # List all domains - domains = Domain.list(self.apiclient, level=0) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - self.assertEqual( - domains[0].id, - self.domain.id, - "Check if list domains returns valid domain" - ) - - domains = Domain.list(self.apiclient, level=1) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - - domains = Domain.list(self.apiclient, level=2) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 1, - "List Domain response has incorrect length" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_domains_no_filter(self): - """ Test list domains with no filter - """ - # List all domains - domains = Domain.list(self.apiclient) - self.assertEqual( - isinstance(domains, list), - True, - "List Domain response is not a valid list" - ) - self.assertEqual( - len(domains), - 3, - "List Domain response has incorrect length" - ) diff --git a/test/integration/smoke/test_list_hosts.py b/test/integration/smoke/test_list_hosts.py deleted file mode 100644 index 7bae216d51d..00000000000 --- a/test/integration/smoke/test_list_hosts.py +++ /dev/null @@ -1,372 +0,0 @@ -# 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. -""" Tests for API listing of hosts with different filters -""" -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.codes import FAILED -from marvin.lib.base import (Configurations, Host) -from marvin.lib.common import (get_domain, list_accounts, - list_zones, list_clusters) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListHosts(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListHosts, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.hypervisor = testClient.getHypervisorInfo() - cls.zones = list_zones(cls.apiclient) - cls.zone = cls.zones[0] - cls.clusters = list_clusters(cls.apiclient) - cls.cluster = cls.clusters[0] - cls.hosts = Host.list(cls.apiclient) - - - @classmethod - def tearDownClass(cls): - super(TestListHosts, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_hosts_no_filter(self): - """Test list hosts with no filter""" - hosts = Host.list(self.apiclient) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_hosts_clusterid_filter(self): - """Test list hosts with clusterid filter""" - hosts = Host.list(self.apiclient, clusterid=self.cluster.id) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - for host in hosts: - self.assertEqual( - host.clusterid, - self.cluster.id, - "Host should be in the cluster %s" % self.cluster.id - ) - with self.assertRaises(Exception): - hosts = Host.list(self.apiclient, clusterid="invalidclusterid") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_hosts_hahost_filter(self): - """Test list hosts with hahost filter""" - configs = Configurations.list( - self.apiclient, - name='ha.tag' - ) - if isinstance(configs, list) and configs[0].value != "" and configs[0].value is not None: - hosts = Host.list(self.apiclient, hahost=True) - if hosts is not None: - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - for host in hosts: - self.assertEqual( - host.hahost, - True, - "Host should be a HA host" - ) - - hosts = Host.list(self.apiclient, hahost=False) - if hosts is not None: - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - for host in hosts: - self.assertTrue( - host.hahost is None or host.hahost is False, - "Host should not be a HA host" - ) - else: - self.debug("HA is not enabled in the setup") - hosts = Host.list(self.apiclient, hahost="invalidvalue") - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - self.assertEqual( - len(hosts), - len(self.hosts), - "Length of host response should be equal to the length of hosts" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_hosts_hypervisor_filter(self): - """Test list hosts with hypervisor filter""" - hosts = Host.list(self.apiclient, hypervisor=self.hypervisor) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should greater than 0" - ) - for host in hosts: - self.assertEqual( - host.hypervisor.lower(), - self.hypervisor.lower(), - "Host should be a %s hypervisor" % self.hypervisor - ) - - hosts = Host.list(self.apiclient, hypervisor="invalidhypervisor") - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertEqual( - len(hosts), - len(self.hosts), - "Length of host response should be equal to the length of hosts" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_hosts_id_filter(self): - """Test list hosts with id filter""" - hosts = Host.list(self.apiclient, id=self.hosts[0].id) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertEqual( - len(hosts), - 1, - "Length of host response should be 1" - ) - self.assertEqual( - hosts[0].id, - self.hosts[0].id, - "Host id should match with the host id in the list" - ) - - with self.assertRaises(Exception): - hosts = Host.list(self.apiclient, id="invalidid") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_hosts_keyword_filter(self): - """Test list hosts with keyword filter""" - hosts = Host.list(self.apiclient, keyword=self.hosts[0].name) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertIn( - host.name, - self.hosts[0].name, - "Host name should match with the host name in the list" - ) - - hosts = Host.list(self.apiclient, keyword="invalidkeyword") - self.assertIsNone( - hosts, - "Host response should be None" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_hosts_name_filter(self): - """Test list hosts with name filter""" - hosts = Host.list(self.apiclient, name=self.hosts[0].name) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertIn( - host.name, - self.hosts[0].name, - "Host name should match with the host name in the list" - ) - - hosts = Host.list(self.apiclient, name="invalidname") - self.assertIsNone( - hosts, - "Host response should be None" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_list_hosts_podid_filter(self): - """Test list hosts with podid filter""" - hosts = Host.list(self.apiclient, podid=self.hosts[0].podid) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertEqual( - host.podid, - self.hosts[0].podid, - "Host podid should match with the host podid in the list" - ) - with self.assertRaises(Exception): - hosts = Host.list(self.apiclient, podid="invalidpodid") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_09_list_hosts_resourcestate_filter(self): - """Test list hosts with resourcestate filter""" - hosts = Host.list(self.apiclient, resourcestate=self.hosts[0].resourcestate) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertEqual( - host.resourcestate, - self.hosts[0].resourcestate, - "Host resourcestate should match with the host resourcestate in the list" - ) - - hosts = Host.list(self.apiclient, resourcestate="invalidresourcestate") - self.assertIsNone( - hosts, - "Host response should be None" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_10_list_hosts_state_filter(self): - """Test list hosts with state filter""" - hosts = Host.list(self.apiclient, state=self.hosts[0].state) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertEqual( - host.state, - self.hosts[0].state, - "Host state should match with the host state in the list" - ) - - hosts = Host.list(self.apiclient, state="invalidstate") - self.assertIsNone( - hosts, - "Host response should be None" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_11_list_hosts_type_filter(self): - """Test list hosts with type filter""" - hosts = Host.list(self.apiclient, type=self.hosts[0].type) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertEqual( - host.type, - self.hosts[0].type, - "Host type should match with the host type in the list" - ) - - hosts = Host.list(self.apiclient, type="invalidtype") - self.assertIsNone( - hosts, - "Host response should be None" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_12_list_hosts_zoneid_filter(self): - """Test list hosts with zoneid filter""" - hosts = Host.list(self.apiclient, zoneid=self.zone.id) - self.assertTrue( - isinstance(hosts, list), - "Host response type should be a valid list" - ) - self.assertGreater( - len(hosts), - 0, - "Length of host response should be greater than 0" - ) - for host in hosts: - self.assertEqual( - host.zoneid, - self.zone.id, - "Host zoneid should match with the host zoneid in the list" - ) - - with self.assertRaises(Exception): - hosts = Host.list(self.apiclient, zoneid="invalidzoneid") diff --git a/test/integration/smoke/test_list_service_offerings.py b/test/integration/smoke/test_list_service_offerings.py deleted file mode 100644 index 319675419dd..00000000000 --- a/test/integration/smoke/test_list_service_offerings.py +++ /dev/null @@ -1,559 +0,0 @@ -# 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. -""" Tests for API listing of service offerings with different filters -""" -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.codes import FAILED -from marvin.lib.base import (Account, - Domain, - Volume, - ServiceOffering, - DiskOffering, - VirtualMachine) -from marvin.lib.common import (get_domain, list_accounts, - list_zones, list_clusters, list_hosts) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListServiceOfferings(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListServiceOfferings, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.hypervisor = testClient.getHypervisorInfo() - cls.domain = get_domain(cls.apiclient) - cls.zones = list_zones(cls.apiclient) - cls.zone = cls.zones[0] - cls.clusters = list_clusters(cls.apiclient) - cls.cluster = cls.clusters[0] - cls.hosts = list_hosts(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls._cleanup = [] - cls.service_offerings = ServiceOffering.list(cls.apiclient) - cls.system_service_offerings = ServiceOffering.list(cls.apiclient, issystem=True) - - cls.child_domain_1 = Domain.create( - cls.apiclient, - cls.services["domain"], - parentdomainid=cls.domain.id - ) - cls._cleanup.append(cls.child_domain_1) - - cls.account_1 = Account.create( - cls.apiclient, - cls.services["account"], - admin=True, - domainid=cls.domain.id - ) - cls._cleanup.append(cls.account_1) - - cls.domainadmin_api_client = testClient.getUserApiClient( - UserName=cls.account_1.user[0].username, - DomainName=cls.domain.name, - type=2 - ) - - cls.system_offering = ServiceOffering.create( - cls.apiclient, - cls.services["service_offerings"]["tiny"], - issystem=True, - name="custom_system_offering", - systemvmtype="domainrouter" - ) - cls._cleanup.append(cls.system_offering) - - cls.service_offering_1 = ServiceOffering.create( - cls.apiclient, - cls.services["service_offerings"]["small"], - cpunumber=2, - cpuspeed=2000, - domainid=cls.child_domain_1.id, - encryptroot=True, - name="custom_offering_1", - zoneid=cls.zone.id - ) - cls._cleanup.append(cls.service_offering_1) - - - @classmethod - def tearDownClass(cls): - super(TestListServiceOfferings, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_service_offerings_cpunumber_filter(self): - """Test list service offerings with cpunumber filter - """ - # List all service offerings with cpunumber 1 - service_offerings = ServiceOffering.list( - self.apiclient, - cpunumber=1 - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings) - len(self.service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertGreaterEqual( - service_offering.cpunumber, - 1, - "List ServiceOfferings response has incorrect cpunumber" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - cpunumber=99999 - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_service_offerings_cpuspeed_filter(self): - """Test list service offerings with cpuspeed filter - """ - # List all service offerings with cpuspeed 1000 - service_offerings = ServiceOffering.list( - self.apiclient, - cpuspeed=1000 - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertGreaterEqual( - service_offering.cpuspeed, - 1000, - "List ServiceOfferings response has incorrect cpuspeed" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - cpuspeed=99999 - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_service_offerings_memory_filter(self): - """Test list service offerings with memory filter - """ - # List all service offerings with memory 256 - service_offerings = ServiceOffering.list( - self.apiclient, - memory=256 - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertGreaterEqual( - service_offering.memory, - 256, - "List ServiceOfferings response has incorrect memory" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - memory=99999 - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_service_offerings_domainid_filter(self): - """Test list service offerings with domainid filter - """ - # List all service offerings with domainid - service_offerings = ServiceOffering.list( - self.apiclient, - domainid=self.domain.id - ) - self.assertIsNone( - service_offerings, - "List ServiceOfferings response is not None" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - domainid=self.child_domain_1.id - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertEqual( - service_offering.domainid, - self.child_domain_1.id, - "List ServiceOfferings response has incorrect domainid" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_service_offerings_encryptroot_filter(self): - """Test list service offerings with encryptroot filter - """ - # List all service offerings with encryptroot True - service_offerings = ServiceOffering.list( - self.apiclient, - encryptroot=True - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertTrue( - service_offering.encryptroot, - "List ServiceOfferings response has incorrect encryptroot" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - encryptroot=False - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertFalse( - service_offering.encryptroot, - "List ServiceOfferings response has incorrect encryptroot" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_service_offerings_id_filter(self): - """Test list service offerings with id filter - """ - # List all service offerings with id - service_offerings = ServiceOffering.list( - self.apiclient, - id=self.system_offering.id - ) - self.assertIsNone( - service_offerings, - "List ServiceOfferings response is not None" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - id=self.service_offering_1.id - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - self.assertEqual( - service_offerings[0].id, - self.service_offering_1.id, - "List ServiceOfferings response has incorrect id" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - id=-1 - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_service_offerings_isrecursive_filter(self): - """Test list service offerings with isrecursive filter - """ - # List all service offerings with listall True - service_offerings = ServiceOffering.list( - self.domainadmin_api_client, - isrecursive=True - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - len(self.service_offerings) + 1, - "List ServiceOfferings response is empty" - ) - - # List all service offerings with isrecursive False - service_offerings = ServiceOffering.list( - self.domainadmin_api_client, - isrecursive=False - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - len(self.service_offerings), - "List ServiceOfferings response is empty" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_list_service_offerings_issystem_filter(self): - """Test list service offerings with issystem filter - """ - # List all service offerings with issystem True - service_offerings = ServiceOffering.list( - self.apiclient, - issystem=True - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - len(self.system_service_offerings) + 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertTrue( - service_offering.issystem, - "List ServiceOfferings response has incorrect issystem" - ) - - # List all service offerings with issystem False - service_offerings = ServiceOffering.list( - self.apiclient, - issystem=False - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - len(self.service_offerings) + 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertFalse( - service_offering.issystem, - "List ServiceOfferings response has incorrect issystem" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_09_list_service_offerings_keyword_filter(self): - """Test list service offerings with keyword filter - """ - # List all service offerings with keyword - service_offerings = ServiceOffering.list( - self.apiclient, - keyword=self.system_offering.name - ) - self.assertIsNone( - service_offerings, - "List ServiceOfferings response is not None" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - keyword=self.service_offering_1.name - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - self.assertEqual( - service_offerings[0].name, - self.service_offering_1.name, - "List ServiceOfferings response has incorrect name" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - keyword="invalid" - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_10_list_service_offerings_name_filter(self): - """Test list service offerings with name filter - """ - # List all service offerings with name - service_offerings = ServiceOffering.list( - self.apiclient, - name=self.system_offering.name - ) - self.assertIsNone( - service_offerings, - "List ServiceOfferings response is not None" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - name=self.system_offering.name, - issystem=True - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - self.assertEqual( - service_offerings[0].name, - self.system_offering.name, - "List ServiceOfferings response has incorrect name" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - name=self.service_offering_1.name - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - self.assertEqual( - service_offerings[0].name, - self.service_offering_1.name, - "List ServiceOfferings response has incorrect name" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - name="invalid" - ) - self.assertIsNone(service_offerings, "List ServiceOfferings response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_11_list_service_offerings_systemvmtype_filter(self): - """Test list service offerings with systemvmtype filter - """ - # List all service offerings with systemvmtype domainrouter - service_offerings = ServiceOffering.list( - self.apiclient, - systemvmtype="domainrouter" - ) - self.assertIsNone( - service_offerings, - "List ServiceOfferings response is not None" - ) - - service_offerings = ServiceOffering.list( - self.apiclient, - systemvmtype="domainrouter", - issystem=True - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertGreaterEqual( - len(service_offerings), - 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertEqual( - service_offering.systemvmtype, - "domainrouter", - "List ServiceOfferings response has incorrect systemvmtype" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_12_list_service_offerings_zoneid_filter(self): - """Test list service offerings with zoneid filter - """ - service_offerings = ServiceOffering.list( - self.apiclient, - zoneid=self.zone.id - ) - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - len(self.service_offerings) + 1, - "List ServiceOfferings response is empty" - ) - for service_offering in service_offerings: - self.assertTrue( - service_offering.zoneid is None or service_offering.zoneid == self.zone.id, - "List ServiceOfferings response has incorrect zoneid" - ) - - if len(self.zones) > 1: - service_offerings = ServiceOffering.list( - self.apiclient, - zoneid=self.zones[1].id - ) - if service_offerings is not None: - self.assertTrue( - isinstance(service_offerings, list), - "List ServiceOfferings response is not a valid list" - ) - self.assertEqual( - len(service_offerings), - len(self.service_offerings), - "List ServiceOfferings response is empty" - ) diff --git a/test/integration/smoke/test_list_storage_pools.py b/test/integration/smoke/test_list_storage_pools.py deleted file mode 100644 index c2c075da65a..00000000000 --- a/test/integration/smoke/test_list_storage_pools.py +++ /dev/null @@ -1,396 +0,0 @@ -# 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. -""" Tests for API listing of storage pools with different filters -""" -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.codes import FAILED -from marvin.lib.base import (StoragePool) -from marvin.lib.common import (get_domain, list_accounts, - list_zones, list_clusters, list_hosts) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListStoragePools(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListStoragePools, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.hypervisor = testClient.getHypervisorInfo() - cls.domain = get_domain(cls.apiclient) - cls.zones = list_zones(cls.apiclient) - cls.zone = cls.zones[0] - cls.clusters = list_clusters(cls.apiclient) - cls.cluster = cls.clusters[0] - cls.hosts = list_hosts(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls.storage_pools = StoragePool.list(cls.apiclient) - - - @classmethod - def tearDownClass(cls): - super(TestListStoragePools, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_storage_pools_clusterid_filter(self): - """ Test list storage pools by clusterid filter - """ - storage_pools = StoragePool.list( - self.apiclient, - clusterid=self.cluster.id - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.clusterid, - self.cluster.id, - "Cluster id should be equal to the cluster id passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - clusterid="-1" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid cluster id is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_storage_pools_id_filter(self): - """ Test list storage pools by id filter - """ - valid_id = self.storage_pools[0].id - storage_pools = StoragePool.list( - self.apiclient, - id=valid_id - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertEqual( - len(storage_pools), - 1, - "Length of storage pools should be equal to 1" - ) - self.assertEqual( - storage_pools[0].id, - valid_id, - "Cluster id should be equal to the cluster id passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - id="-1" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid cluster id is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_storage_pools_ipaddress_filter(self): - """ Test list storage pools by ipaddress filter - """ - valid_ipaddress = self.storage_pools[0].ipaddress - storage_pools = StoragePool.list( - self.apiclient, - ipaddress=valid_ipaddress - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.ipaddress, - valid_ipaddress, - "IP address should be equal to the ip address passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - ipaddress="1.1.1.1" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid ip address is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_storage_pools_keyword_filter(self): - """ Test list storage pools by keyword filter - """ - valid_keyword = self.storage_pools[0].name - storage_pools = StoragePool.list( - self.apiclient, - keyword=valid_keyword - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertIn( - valid_keyword, - storage_pool.name, - "Keyword should be present in the storage pool name" - ) - - storage_pools = StoragePool.list( - self.apiclient, - keyword="invalid" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid keyword is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_storage_pools_name_filter(self): - """ Test list storage pools by name filter - """ - valid_name = self.storage_pools[0].name - storage_pools = StoragePool.list( - self.apiclient, - name=valid_name - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.name, - valid_name, - "Name should be equal to the name passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - name="invalid" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid name is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_storage_pools_path_filter(self): - """ Test list storage pools by path filter - """ - valid_path = self.storage_pools[0].path - storage_pools = StoragePool.list( - self.apiclient, - path=valid_path - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.path, - valid_path, - "Path should be equal to the path passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - path="invalid" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid path is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_storage_pools_podid_filter(self): - """ Test list storage pools by podid filter - """ - storage_pools = StoragePool.list( - self.apiclient, - podid=self.cluster.podid - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.podid, - self.cluster.podid, - "Pod id should be equal to the pod id passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - podid="-1" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid pod id is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_list_storage_pools_scope_filter(self): - """ Test list storage pools by scope filter - """ - valid_scope = self.storage_pools[0].scope - storage_pools = StoragePool.list( - self.apiclient, - scope=valid_scope - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.scope, - valid_scope, - "Scope should be equal to the scope passed in the filter" - ) - with self.assertRaises(Exception): - storage_pools = StoragePool.list( - self.apiclient, - scope="invalid" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_09_list_storage_pools_status_filter(self): - """ Test list storage pools by status filter - """ - valid_status = self.storage_pools[0].status - storage_pools = StoragePool.list( - self.apiclient, - status=valid_status - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.status, - valid_status, - "State should be equal to the status passed in the filter" - ) - with self.assertRaises(Exception): - storage_pools = StoragePool.list( - self.apiclient, - status="invalid" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_10_list_storage_pools_zoneid_filter(self): - """ Test list storage pools by zoneid filter - """ - storage_pools = StoragePool.list( - self.apiclient, - zoneid=self.zone.id - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) - for storage_pool in storage_pools: - self.assertEqual( - storage_pool.zoneid, - self.zone.id, - "Zone id should be equal to the zone id passed in the filter" - ) - - storage_pools = StoragePool.list( - self.apiclient, - zoneid="-1" - ) - self.assertIsNone( - storage_pools, - "Response should be empty when invalid zone id is passed" - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_11_list_storage_pools_no_filter(self): - """ Test list storage pools with no filter - """ - storage_pools = StoragePool.list( - self.apiclient - ) - self.assertTrue( - isinstance(storage_pools, list), - "Storage pool response type should be a list" - ) - self.assertGreater( - len(storage_pools), - 0, - "Length of storage pools should greater than 0" - ) diff --git a/test/integration/smoke/test_list_volumes.py b/test/integration/smoke/test_list_volumes.py deleted file mode 100644 index b08e9cf3b38..00000000000 --- a/test/integration/smoke/test_list_volumes.py +++ /dev/null @@ -1,618 +0,0 @@ -# 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. -""" Tests for API listing of volumes with different filters -""" -# Import Local Modules -from marvin.cloudstackTestCase import cloudstackTestCase -from marvin.codes import FAILED -from marvin.lib.base import (Account, - Domain, - Volume, - ServiceOffering, - Tag, - DiskOffering, - VirtualMachine) -from marvin.lib.common import (get_domain, list_accounts, - list_zones, list_clusters, list_hosts, get_suitable_test_template) -# Import System modules -from nose.plugins.attrib import attr - -_multiprocess_shared_ = True - - -class TestListVolumes(cloudstackTestCase): - - @classmethod - def setUpClass(cls): - testClient = super(TestListVolumes, cls).getClsTestClient() - cls.apiclient = testClient.getApiClient() - cls.services = testClient.getParsedTestDataConfig() - cls.hypervisor = testClient.getHypervisorInfo() - cls.domain = get_domain(cls.apiclient) - cls.zones = list_zones(cls.apiclient) - cls.zone = cls.zones[0] - cls.clusters = list_clusters(cls.apiclient) - cls.cluster = cls.clusters[0] - cls.hosts = list_hosts(cls.apiclient) - cls.account = list_accounts(cls.apiclient, name="admin")[0] - cls._cleanup = [] - - cls.service_offering = ServiceOffering.create( - cls.apiclient, - cls.services["service_offerings"]["tiny"] - ) - cls._cleanup.append(cls.service_offering) - - template = get_suitable_test_template( - cls.apiclient, - cls.zone.id, - cls.services["ostype"], - cls.hypervisor - ) - if template == FAILED: - assert False, "get_test_template() failed to return template" - - cls.services["template"]["ostypeid"] = template.ostypeid - cls.services["template_2"]["ostypeid"] = template.ostypeid - cls.services["ostypeid"] = template.ostypeid - cls.services["virtual_machine"]["zoneid"] = cls.zone.id - cls.services["mode"] = cls.zone.networktype - - cls.disk_offering = DiskOffering.create(cls.apiclient, - cls.services["disk_offering"]) - cls._cleanup.append(cls.disk_offering) - - # Get already existing volumes in the env for assertions - cls.volumes = Volume.list(cls.apiclient, zoneid=cls.zone.id) or [] - - # Create VM - cls.virtual_machine = VirtualMachine.create( - cls.apiclient, - cls.services["virtual_machine"], - templateid=template.id, - accountid=cls.account.name, - domainid=cls.account.domainid, - clusterid=cls.cluster.id, - serviceofferingid=cls.service_offering.id, - mode=cls.services["mode"] - ) - - cls.child_domain = Domain.create( - cls.apiclient, - cls.services["domain"]) - cls._cleanup.append(cls.child_domain) - - cls.child_account = Account.create( - cls.apiclient, - cls.services["account"], - admin=True, - domainid=cls.child_domain.id) - cls._cleanup.append(cls.child_account) - - cls.vol_1 = Volume.create(cls.apiclient, - cls.services["volume"], - zoneid=cls.zone.id, - account=cls.account.name, - domainid=cls.account.domainid, - diskofferingid=cls.disk_offering.id) - cls._cleanup.append(cls.vol_1) - - cls.vol_1 = cls.virtual_machine.attach_volume( - cls.apiclient, - cls.vol_1 - ) - cls._cleanup.append(cls.virtual_machine) - - Tag.create(cls.apiclient, cls.vol_1.id, "Volume", {"abc": "xyz"}) - - cls.vol_2 = Volume.create(cls.apiclient, - cls.services["volume"], - zoneid=cls.zone.id, - account=cls.account.name, - domainid=cls.account.domainid, - diskofferingid=cls.disk_offering.id) - - cls._cleanup.append(cls.vol_2) - - cls.vol_3 = Volume.create(cls.apiclient, - cls.services["volume"], - zoneid=cls.zone.id, - account=cls.child_account.name, - domainid=cls.child_account.domainid, - diskofferingid=cls.disk_offering.id) - cls._cleanup.append(cls.vol_3) - - @classmethod - def tearDownClass(cls): - super(TestListVolumes, cls).tearDownClass() - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_01_list_volumes_account_domain_filter(self): - """Test listing Volumes with account & domain filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - account=self.account.name, - domainid=self.account.domainid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 3, - "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) - ) - - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - account=self.child_account.name, - domainid=self.child_account.domainid - ) - - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 Volume, received %s" % len(list_volume_response) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_02_list_volumes_diskofferingid_filter(self): - """Test listing Volumes with diskofferingid filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - account=self.account.name, - domainid=self.account.domainid, - diskofferingid=self.disk_offering.id - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_03_list_volumes_id_filter(self): - """Test listing Volumes with id filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - id=self.vol_1.id - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 Volume, received %s" % len(list_volume_response) - ) - self.assertEqual( - list_volume_response[0].id, - self.vol_1.id, - "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_04_list_volumes_ids_filter(self): - """Test listing Volumes with ids filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - ids=[self.vol_1.id, self.vol_2.id, self.vol_3.id] - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], - "ListVolumes response Volume 1 not in list") - self.assertIn(list_volume_response[1].id, [self.vol_1.id, self.vol_2.id], - "ListVolumes response Volume 2 not in list") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_05_list_volumes_isrecursive(self): - """Test listing Volumes with isrecursive filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - isrecursive=True, - domainid=self.account.domainid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response) - len(self.volumes), - 4, - "ListVolumes response expected 4 Volumes, received %s" % len(list_volume_response) - ) - - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - isrecursive=False, - domainid=self.account.domainid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response) - len(self.volumes), - 3, - "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_06_list_volumes_keyword_filter(self): - """Test listing Volumes with keyword filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - keyword=self.services["volume"]["diskname"] - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - self.assertIn( - list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], - "ListVolumes response Volume 1 not in list") - self.assertIn(list_volume_response[1].id, [self.vol_1.id, self.vol_2.id], - "ListVolumes response Volume 2 not in list") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_07_list_volumes_listall(self): - """Test listing Volumes with listall filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - listall=True - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response) - len(self.volumes), - 4, - "ListVolumes response expected 4 Volumes, received %s" % len(list_volume_response) - ) - - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - listall=False - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response) - len(self.volumes), - 3, - "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_08_listsystemvms(self): - list_volumes_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - listsystemvms=True - ) - self.assertEqual( - isinstance(list_volumes_response, list), - True, - "List Volume response is not a valid list" - ) - self.assertGreater( - len(list_volumes_response), - 3, - "ListVolumes response expected more than 3 Volumes, received %s" % len(list_volumes_response) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_09_list_volumes_name_filter(self): - """Test listing Volumes with name filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - name=self.vol_1.name - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) - ) - self.assertEqual( - list_volume_response[0].id, - self.vol_1.id, - "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) - ) - self.assertEqual( - list_volume_response[0].name, - self.vol_1.name, - "ListVolumes response expected Volume with name %s, received %s" % ( - self.vol_1.name, list_volume_response[0].name) - ) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_10_list_volumes_podid_filter(self): - """Test listing Volumes with podid filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - podid=self.vol_1.podid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertGreater( - len(list_volume_response), - 1, - "ListVolumes response expected more than 1 Volume, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_11_list_volumes_state_filter(self): - """Test listing Volumes with state filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - state="Ready" - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) - - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - state="Allocated" - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) - ) - self.assertEqual(self.vol_2.id, list_volume_response[0].id, - "ListVolumes response expected Volume with id %s" % self.vol_3.id) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_12_list_volumes_storageid_filter(self): - """Test listing Volumes with storageid filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - storageid=self.vol_1.storageid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertGreaterEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 or more Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_13_list_volumes_type_filter(self): - """Test listing Volumes with type filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - type="DATADISK" - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) - - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - type="ROOT" - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 Volumes, received %s" % len(list_volume_response) - ) - self.assertNotIn(list_volume_response[0].id, [self.vol_1.id, self.vol_2.id], - "ListVolumes response expected ROOT Volume") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_14_list_volumes_virtualmachineid_filter(self): - """Test listing Volumes with virtualmachineid filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id, - virtualmachineid=self.vol_1.virtualmachineid - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 2, - "ListVolumes response expected 2 Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_15_list_volumes_zoneid_filter(self): - """Test listing Volumes with zoneid filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zones[0].id - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 3, - "ListVolumes response expected 3 Volumes, received %s" % len(list_volume_response) - ) - - if len(self.zones) > 1: - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zones[1].id - ) - self.assertIsNone(list_volume_response, "List Volume response is not None") - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_16_list_volumes_tags_filter(self): - """Test listing Volumes with tags filter - """ - list_volume_response = Volume.list( - self.apiclient, - tags=[{"key": "abc", "value": "xyz"}] - ) - - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertEqual( - len(list_volume_response), - 1, - "ListVolumes response expected 1 or more Volumes, received %s" % len(list_volume_response) - ) - self.assertEqual( - list_volume_response[0].id, - self.vol_1.id, - "ListVolumes response expected Volume with id %s, received %s" % (self.vol_1.id, list_volume_response[0].id) - ) - self.assertEqual( - list_volume_response[0].tags[0]["key"], - "abc", - "ListVolumes response expected Volume with tag key abc, received %s" % list_volume_response[0].tags[0]["key"] - ) - self.assertEqual( - list_volume_response[0].tags[0]["value"], - "xyz", - "ListVolumes response expected Volume with tag value xyz, received %s" % list_volume_response[0].tags[0]["value"] - ) - - list_volume_response = Volume.list( - self.apiclient, - tags=[{"key": "abc", "value": "xyz1"}] - ) - self.assertIsNone(list_volume_response, "List Volume response is not None") - with self.assertRaises(Exception): - list_volume_response = Volume.list( - self.apiclient, - tags=[{"key": None, "value": None}] - ) - - - @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") - def test_17_list_volumes_no_filter(self): - """Test listing Volumes with no filter - """ - list_volume_response = Volume.list( - self.apiclient, - zoneid=self.zone.id - ) - self.assertTrue( - isinstance(list_volume_response, list), - "List Volume response is not a valid list" - ) - self.assertGreaterEqual( - len(list_volume_response), - 3, - "ListVolumes response expected 3 or more Volumes, received %s" % len(list_volume_response) - ) - self.assertIn(self.vol_1.id, [volume.id for volume in list_volume_response], - "ListVolumes response expected Volume with id %s" % self.vol_1.id) diff --git a/test/integration/smoke/test_metrics_api.py b/test/integration/smoke/test_metrics_api.py index 1042ad997fc..d5ad559fad0 100644 --- a/test/integration/smoke/test_metrics_api.py +++ b/test/integration/smoke/test_metrics_api.py @@ -289,6 +289,7 @@ class TestMetrics(cloudstackTestCase): self.assertTrue(hasattr(li, 'hosts')) self.assertEqual(li.hosts, len(list_hosts(self.apiclient, + zoneid=self.zone.id, type='Routing'))) self.assertTrue(hasattr(li, 'imagestores')) diff --git a/test/integration/smoke/test_secondary_storage.py b/test/integration/smoke/test_secondary_storage.py index 4b26950ea64..5b339ae67b9 100644 --- a/test/integration/smoke/test_secondary_storage.py +++ b/test/integration/smoke/test_secondary_storage.py @@ -340,7 +340,7 @@ class TestSecStorageServices(cloudstackTestCase): # 1. Try complete migration from a storage with more (or equal) free space - migration should be refused storages = self.list_secondary_storages(self.apiclient) - if (len(storages)) < 2 or (storages[0]['zoneid'] != storages[1]['zoneid']): + if (len(storages)) < 2: self.skipTest( "This test requires more than one secondary storage") diff --git a/test/integration/smoke/test_templates.py b/test/integration/smoke/test_templates.py index 2696db8f96b..66008b1a8f3 100644 --- a/test/integration/smoke/test_templates.py +++ b/test/integration/smoke/test_templates.py @@ -1024,17 +1024,17 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): cls.services["disk_offering"] ) cls._cleanup.append(cls.disk_offering) - cls.template = get_template( + template = get_template( cls.apiclient, cls.zone.id, cls.services["ostype"] ) - if cls.template == FAILED: + if template == FAILED: assert False, "get_template() failed to return template with description %s" % cls.services["ostype"] - cls.services["template"]["ostypeid"] = cls.template.ostypeid - cls.services["template_2"]["ostypeid"] = cls.template.ostypeid - cls.services["ostypeid"] = cls.template.ostypeid + cls.services["template"]["ostypeid"] = template.ostypeid + cls.services["template_2"]["ostypeid"] = template.ostypeid + cls.services["ostypeid"] = template.ostypeid cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["volume"]["diskoffering"] = cls.disk_offering.id @@ -1055,7 +1055,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): cls.virtual_machine = VirtualMachine.create( cls.apiclient, cls.services["virtual_machine"], - templateid=cls.template.id, + templateid=template.id, accountid=cls.account.name, domainid=cls.account.domainid, serviceofferingid=cls.service_offering.id, @@ -1104,7 +1104,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(tags=["advanced", "advancedns"], required_hardware="true") + @attr(tags=["advanced", "advancedns"], required_hardware="false") def test_09_copy_delete_template(self): cmd = listZones.listZonesCmd() zones = self.apiclient.listZones(cmd) @@ -1156,7 +1156,7 @@ class TestCopyAndDeleteTemplatesAcrossZones(cloudstackTestCase): list_template_response = Template.list( self.apiclient, - templatefilter=self.services["templatefilter"], + templatefilter=self.services["template"]["templatefilter"], id=self.template.id, zoneid=self.destZone.id ) From 08749d8354f202fe665fd0b884f68f779711f37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Jandre?= <48719461+JoaoJandre@users.noreply.github.com> Date: Fri, 22 Dec 2023 07:13:39 -0300 Subject: [PATCH 015/212] server: skip password policies check on empty password (#8370) This PR changes the password.policy.regex default value to empty. With an empty value for the configuration, it is skipped during the password policy check, only when the configuration is set to something different than a blank string, the regex will get checked. This way, when creating a user on org.apache.cloudstack.ldap.LdapAuthenticator#authenticate() we won't get an error by default, as an empty value for the password is passed. --- .../main/java/com/cloud/user/PasswordPolicyImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java b/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java index 40eec5674cd..1082f3cc0d5 100644 --- a/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java +++ b/server/src/main/java/com/cloud/user/PasswordPolicyImpl.java @@ -27,6 +27,12 @@ public class PasswordPolicyImpl implements PasswordPolicy, Configurable { private Logger logger = Logger.getLogger(PasswordPolicyImpl.class); public void verifyIfPasswordCompliesWithPasswordPolicies(String password, String username, Long domainId) { + if (StringUtils.isEmpty(password)) { + logger.warn(String.format("User [%s] has an empty password, skipping password policy checks. " + + "If this is not a LDAP user, there is something wrong.", username)); + return; + } + int numberOfSpecialCharactersInPassword = 0; int numberOfUppercaseLettersInPassword = 0; int numberOfLowercaseLettersInPassword = 0; @@ -188,12 +194,12 @@ public class PasswordPolicyImpl implements PasswordPolicy, Configurable { logger.trace(String.format("Validating if the new password for user [%s] matches regex [%s] defined in the configuration [%s].", username, passwordPolicyRegex, PasswordPolicyRegex.key())); - if (passwordPolicyRegex == null){ - logger.trace(String.format("Regex is null; therefore, we will not validate if the new password matches with regex for user [%s].", username)); + if (StringUtils.isEmpty(passwordPolicyRegex)) { + logger.trace(String.format("Regex is empty; therefore, we will not validate if the new password matches with regex for user [%s].", username)); return; } - if (!password.matches(passwordPolicyRegex)){ + if (!password.matches(passwordPolicyRegex)) { logger.error(String.format("User [%s] informed a new password that does not match with regex [%s]. Refusing the user's new password.", username, passwordPolicyRegex)); throw new InvalidParameterValueException("User password does not match with password policy regex."); } From 9d4f0715d434c8ee1e064f8681093133241e34e2 Mon Sep 17 00:00:00 2001 From: dahn Date: Wed, 27 Dec 2023 13:56:17 +0100 Subject: [PATCH 016/212] change of the guard (#8408) --- .asf.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.asf.yaml b/.asf.yaml index 1772623f1b1..8ce43df00b2 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -56,10 +56,9 @@ github: - alexandremattioli - vishesh92 - GaOrtiga - - BryanMLima - SadiJr - - JoaoJandre - winterhazel + - rp- protected_branches: ~ From 2eaed80435113f24ba7b66397d7a41079bd2b68b Mon Sep 17 00:00:00 2001 From: Vishesh Date: Thu, 4 Jan 2024 17:26:07 +0530 Subject: [PATCH 017/212] ui: fix ssl check in image store browser (#8430) This PR fixes the ssl in image store browser. --- ui/src/components/view/ObjectStoreBrowser.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/components/view/ObjectStoreBrowser.vue b/ui/src/components/view/ObjectStoreBrowser.vue index f2e68c8b954..531846a9da5 100644 --- a/ui/src/components/view/ObjectStoreBrowser.vue +++ b/ui/src/components/view/ObjectStoreBrowser.vue @@ -449,7 +449,7 @@ export default { initMinioClient () { if (!this.client) { const url = /https?:\/\/([^/]+)\/?/.exec(this.resource.url.split(this.resource.name)[0])[1] - const isHttps = /^https/.test(url) + const isHttps = /^https/.test(this.resource.url) this.client = new Minio.Client({ endPoint: url.split(':')[0], port: url.split(':').length > 1 ? parseInt(url.split(':')[1]) : isHttps ? 443 : 80, From 687610dd5f5c41c99ac9e7161481396a3c2305ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Jandre?= <48719461+JoaoJandre@users.noreply.github.com> Date: Thu, 4 Jan 2024 08:57:24 -0300 Subject: [PATCH 018/212] Fix bootstrap exceptions (#8397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During management server initialization, some EmptyStackException and BeansException exceptions are always thrown; These exceptions do not affect ACS, however, they can cause confusion when observing the management server startup logs with DEBUG level on. The causes of the exceptions have been addressed. Co-authored-by: João Jandre --- .../model/impl/DefaultModuleDefinitionSet.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java index 83d2feaa845..6c03c3ce9e1 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java @@ -101,9 +101,13 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { log.debug(String.format("Trying to obtain module [%s] context.", moduleDefinitionName)); ApplicationContext context = getApplicationContext(moduleDefinitionName); try { - Runnable runnable = context.getBean("moduleStartup", Runnable.class); - log.info(String.format("Starting module [%s].", moduleDefinitionName)); - runnable.run(); + if (context.containsBean("moduleStartup")) { + Runnable runnable = context.getBean("moduleStartup", Runnable.class); + log.info(String.format("Starting module [%s].", moduleDefinitionName)); + runnable.run(); + } else { + log.debug(String.format("Could not get module [%s] context bean.", moduleDefinitionName)); + } } catch (BeansException e) { log.warn(String.format("Failed to start module [%s] due to: [%s].", moduleDefinitionName, e.getMessage())); if (log.isDebugEnabled()) { @@ -126,6 +130,10 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { public void with(ModuleDefinition def, Stack parents) { try { String moduleDefinitionName = def.getName(); + if (parents.isEmpty()) { + log.debug(String.format("Could not find module [%s] context as they have no parents.", moduleDefinitionName)); + return; + } log.debug(String.format("Trying to obtain module [%s] context.", moduleDefinitionName)); ApplicationContext parent = getApplicationContext(parents.peek().getName()); log.debug(String.format("Trying to load module [%s] context.", moduleDefinitionName)); From 66ae96b5eb6ae6cdeb3f78fa08ea530461054c88 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 4 Jan 2024 17:33:55 +0530 Subject: [PATCH 019/212] ui: fix layout for action button for template form (#8434) Fixes layout for the action buttons on register template form Signed-off-by: Abhishek Kumar --- ui/src/views/image/RegisterOrUploadTemplate.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/src/views/image/RegisterOrUploadTemplate.vue b/ui/src/views/image/RegisterOrUploadTemplate.vue index 400d096596c..999a1b8d120 100644 --- a/ui/src/views/image/RegisterOrUploadTemplate.vue +++ b/ui/src/views/image/RegisterOrUploadTemplate.vue @@ -429,8 +429,10 @@ - {{ $t('label.cancel') }} - {{ $t('label.ok') }} +
+ {{ $t('label.cancel') }} + {{ $t('label.ok') }} +
From 746bae740eaa4a1cb2c130fa87c3ee05411228b1 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 4 Jan 2024 17:59:18 +0530 Subject: [PATCH 020/212] ui: fix default domainid for add account (#8435) Fixes setting domainid param default value which was being set to 0. Signed-off-by: Abhishek Kumar --- ui/src/views/iam/AddAccount.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/src/views/iam/AddAccount.vue b/ui/src/views/iam/AddAccount.vue index bfab8df6648..232dbea1a9b 100644 --- a/ui/src/views/iam/AddAccount.vue +++ b/ui/src/views/iam/AddAccount.vue @@ -303,7 +303,6 @@ export default { } else { this.loadMore(apiToCall, page + 1, sema) } - this.form.domainid = 0 }) }, fetchRoles () { From 3f9dd4dc07ff40a4e0216014715e4fe37fc46d28 Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Fri, 5 Jan 2024 04:27:13 -0300 Subject: [PATCH 021/212] Fix VMware VM ingestion template selection and default template failure (#8429) This PR fixes the template selection regression for VMware Ingestion in the UI on 4.19.0 RC1 and adds back the default template selection for VMware Fixes: #8428 #8432 --- .../org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java | 4 +++- ui/src/views/tools/ImportUnmanagedInstance.vue | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index b7190f4ff21..789e0bf06aa 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1111,7 +1111,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDisk.getController()); - allDetails.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDisk.getCapacity() / Resource.ResourceType.bytesToGiB)); + if (cluster.getHypervisorType() != Hypervisor.HypervisorType.VMware) { + allDetails.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDisk.getCapacity() / Resource.ResourceType.bytesToGiB)); + } try { checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed); diff --git a/ui/src/views/tools/ImportUnmanagedInstance.vue b/ui/src/views/tools/ImportUnmanagedInstance.vue index 6433e68da57..a03ef3866ec 100644 --- a/ui/src/views/tools/ImportUnmanagedInstance.vue +++ b/ui/src/views/tools/ImportUnmanagedInstance.vue @@ -111,7 +111,7 @@ - + @@ -120,7 +120,7 @@ :value="templateType" @change="changeTemplateType"> - + {{ $t('label.template.temporary.import') }} @@ -667,7 +667,7 @@ export default { nic.broadcasturi = 'pvlan://' + nic.vlanid + '-i' + nic.isolatedpvlan } } - if (this.cluster.hypervisortype === 'VMWare') { + if (this.cluster.hypervisortype === 'VMware') { nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan', networkname: 'network' }) } else { nic.meta = this.getMeta(nic, { macaddress: 'mac', vlanid: 'vlan' }) @@ -849,7 +849,7 @@ export default { this.nicsNetworksMapping = data }, defaultTemplateType () { - if (this.cluster.hypervisortype === 'VMWare') { + if (this.cluster.hypervisortype === 'VMware') { return 'auto' } return 'custom' From f023fc53c0acc68716a46cbce66ffc67dde2461d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 8 Jan 2024 17:51:04 +0530 Subject: [PATCH 022/212] engine-schema: fix finding guestos mapping with parent version (#8426) GuestOS mappings are retrieved from the parent hypervisor version when a minor, patch hypervisor version doesn't exist. Fixes #8412 Signed-off-by: Abhishek Kumar --- .../storage/dao/GuestOSHypervisorDaoImpl.java | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java index e03e3f7ce64..69f4d4a3ceb 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSHypervisorDaoImpl.java @@ -20,9 +20,12 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.apache.cloudstack.utils.CloudStackVersion; import org.apache.commons.collections.CollectionUtils; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.utils.db.Filter; import com.cloud.utils.db.QueryBuilder; @@ -32,6 +35,7 @@ import com.cloud.utils.db.SearchCriteria; @Component public class GuestOSHypervisorDaoImpl extends GenericDaoBase implements GuestOSHypervisorDao { + private static final Logger s_logger = Logger.getLogger(GuestOSHypervisorDaoImpl.class); protected final SearchBuilder guestOsSearch; protected final SearchBuilder mappingSearch; @@ -80,6 +84,29 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase listByGuestOsId(long guestOsId) { SearchCriteria sc = guestOsSearch.create(); @@ -87,8 +114,7 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase sc = mappingSearch.create(); String version = "default"; if (!(hypervisorVersion == null || hypervisorVersion.isEmpty())) { @@ -100,6 +126,12 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase sc = userDefinedMappingSearch.create(); @@ -123,8 +155,7 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase sc = guestOsNameSearch.create(); String version = "default"; if (!(hypervisorVersion == null || hypervisorVersion.isEmpty())) { @@ -138,6 +169,12 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase sc = guestOsNameSearch.create(); From 5c32a0edbaa5a6bbcb527e7a4f986bed9a48c114 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 8 Jan 2024 18:02:11 +0530 Subject: [PATCH 023/212] ui: prevent scheduling readyforshutdown job when api inaccessible (#8448) Non-admin account may not have access to the readyForShutdown API therefore UI should not schedule the job. Signed-off-by: Abhishek Kumar --- ui/src/components/page/GlobalLayout.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/src/components/page/GlobalLayout.vue b/ui/src/components/page/GlobalLayout.vue index 85ca9f2c8c1..6dd5c530fa5 100644 --- a/ui/src/components/page/GlobalLayout.vue +++ b/ui/src/components/page/GlobalLayout.vue @@ -199,8 +199,10 @@ export default { created () { this.menus = this.mainMenu.find((item) => item.path === '/').children this.collapsed = !this.sidebarOpened - const readyForShutdownPollingJob = setInterval(this.checkShutdown, 5000) - this.$store.commit('SET_READY_FOR_SHUTDOWN_POLLING_JOB', readyForShutdownPollingJob) + if ('readyForShutdown' in this.$store.getters.apis) { + const readyForShutdownPollingJob = setInterval(this.checkShutdown, 5000) + this.$store.commit('SET_READY_FOR_SHUTDOWN_POLLING_JOB', readyForShutdownPollingJob) + } }, mounted () { const layoutMode = this.$config.theme['@layout-mode'] || 'light' From 514d2c2a26c6713899bef601d8ba6f295c7ad78b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 9 Jan 2024 14:43:04 +0530 Subject: [PATCH 024/212] schema,engine-schema: explicit VMware 8.0.0.2, 8.0.0.3 support, logs (#8444) Fixes #8412 Add support for 8.0.0.2 explicitly to prevent falling over to the parent version Adds log when hypervisor capabilities fail over to the parent version --------- Signed-off-by: Abhishek Kumar --- .../hypervisor/dao/HypervisorCapabilitiesDaoImpl.java | 10 ++++++++-- .../main/resources/META-INF/db/schema-41810to41900.sql | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java index e8272825213..a4ec0a66360 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java @@ -75,11 +75,17 @@ public class HypervisorCapabilitiesDaoImpl extends GenericDaoBase Date: Tue, 9 Jan 2024 09:08:11 -0300 Subject: [PATCH 025/212] Fix KVM import unmanaged instance (#8433) This PR fixes KVM manage/unmanage functionality on 4.19.0 RC1 - was introduced on #7976 but the latest merge commits on the PR removed the execution for KVM --- .../vm/UnmanagedVMsManagerImpl.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 789e0bf06aa..ebd11f11957 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1322,7 +1322,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { ActionEventUtils.onStartedActionEvent(userId, owner.getId(), EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, true, 0); - //TODO: Placeholder for integration with KVM ingestion and KVM extend unmanage/manage VMs if (cmd instanceof ImportVmCmd) { ImportVmCmd importVmCmd = (ImportVmCmd) cmd; if (StringUtils.isBlank(importVmCmd.getImportSource())) { @@ -1338,8 +1337,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { details, importVmCmd, forced); } } else { - if (cluster.getHypervisorType() == Hypervisor.HypervisorType.VMware) { - userVm = importUnmanagedInstanceFromVmwareToVmware(zone, cluster, hosts, additionalNameFilters, + if (List.of(Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM).contains(cluster.getHypervisorType())) { + userVm = importUnmanagedInstanceFromHypervisor(zone, cluster, hosts, additionalNameFilters, template, instanceName, displayName, hostName, caller, owner, userId, serviceOffering, dataDiskOfferingMap, nicNetworkMap, nicIpAddressMap, @@ -1458,13 +1457,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } - private UserVm importUnmanagedInstanceFromVmwareToVmware(DataCenter zone, Cluster cluster, - List hosts, List additionalNameFilters, - VMTemplateVO template, String instanceName, String displayName, - String hostName, Account caller, Account owner, long userId, - ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, - Map nicNetworkMap, Map nicIpAddressMap, - Map details, Boolean migrateAllowed, List managedVms, boolean forced) { + private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cluster, + List hosts, List additionalNameFilters, + VMTemplateVO template, String instanceName, String displayName, + String hostName, Account caller, Account owner, long userId, + ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, + Map nicNetworkMap, Map nicIpAddressMap, + Map details, Boolean migrateAllowed, List managedVms, boolean forced) { UserVm userVm = null; for (HostVO host : hosts) { HashMap unmanagedInstances = getUnmanagedInstancesForHost(host, instanceName, managedVms); From 2b28a664fe0d04b2eb81937db8a85cd4c2c6e69c Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 9 Jan 2024 17:38:44 +0530 Subject: [PATCH 026/212] Updated jetty maxFormContentSize value to 1048576 bytes (default is 200000 bytes), to support user data upto 1048576 bytes (#8420) This PR enables support for user data content upto 1048576 bytes - updates jetty maxFormContentSize value to 1048576 bytes (default is 200000 bytes). CloudStack can support max user data content to 1048576 bytes (the size can be configurable through vm.userdata.max.length setting, max 1048576), but it's limited due to the default Jetty max content size, which is 200000 bytes. Configuration Reference from jetty doc: https://eclipse.dev/jetty/documentation/jetty-9/index.html#configuring-specific-webapp-deployment (check with maxFormContentSize here) --- client/conf/server.properties.in | 3 +++ .../org/apache/cloudstack/ServerDaemon.java | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/client/conf/server.properties.in b/client/conf/server.properties.in index 42d98a6eb29..9fb777f1e21 100644 --- a/client/conf/server.properties.in +++ b/client/conf/server.properties.in @@ -29,6 +29,9 @@ http.port=8080 # Max inactivity time in minutes for the session session.timeout=30 +# Max allowed API request payload / content size in bytes +request.content.size=1048576 + # Options to configure and enable HTTPS on management server # # For management server to pickup these configuration settings, the configured diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java index 63cdc45b8dc..763c274c7f5 100644 --- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java @@ -26,10 +26,10 @@ import java.lang.management.ManagementFactory; import java.net.URL; import java.util.Properties; -import com.cloud.utils.Pair; -import com.cloud.utils.server.ServerProperties; import org.apache.commons.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.HttpConfiguration; @@ -40,6 +40,7 @@ import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.MovedContextHandler; import org.eclipse.jetty.server.handler.RequestLogHandler; @@ -50,10 +51,10 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.webapp.WebAppContext; -import org.apache.log4j.Logger; +import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; -import org.apache.commons.lang3.StringUtils; +import com.cloud.utils.server.ServerProperties; /*** * The ServerDaemon class implements the embedded server, it can be started either @@ -79,6 +80,8 @@ public class ServerDaemon implements Daemon { private static final String KEYSTORE_PASSWORD = "https.keystore.password"; private static final String WEBAPP_DIR = "webapp.dir"; private static final String ACCESS_LOG = "access.log"; + private static final String REQUEST_CONTENT_SIZE_KEY = "request.content.size"; + private static final int DEFAULT_REQUEST_CONTENT_SIZE = 1048576; //////////////////////////////////////////////////////// /////////////// Server Configuration /////////////////// @@ -90,6 +93,7 @@ public class ServerDaemon implements Daemon { private int httpPort = 8080; private int httpsPort = 8443; private int sessionTimeout = 30; + private int maxFormContentSize = DEFAULT_REQUEST_CONTENT_SIZE; private boolean httpsEnable = false; private String accessLogFile = "access.log"; private String bindInterface = null; @@ -136,6 +140,7 @@ public class ServerDaemon implements Daemon { setWebAppLocation(properties.getProperty(WEBAPP_DIR)); setAccessLogFile(properties.getProperty(ACCESS_LOG, "access.log")); setSessionTimeout(Integer.valueOf(properties.getProperty(SESSION_TIMEOUT, "30"))); + setMaxFormContentSize(Integer.valueOf(properties.getProperty(REQUEST_CONTENT_SIZE_KEY, String.valueOf(DEFAULT_REQUEST_CONTENT_SIZE)))); } catch (final IOException e) { LOG.warn("Failed to read configuration from server.properties file", e); } finally { @@ -186,6 +191,7 @@ public class ServerDaemon implements Daemon { // Extra config options server.setStopAtShutdown(true); + server.setAttribute(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, maxFormContentSize); // HTTPS Connector createHttpsConnector(httpConfig); @@ -257,6 +263,7 @@ public class ServerDaemon implements Daemon { final WebAppContext webApp = new WebAppContext(); webApp.setContextPath(contextPath); webApp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); + webApp.setMaxFormContentSize(maxFormContentSize); // GZIP handler final GzipHandler gzipHandler = new GzipHandler(); @@ -355,4 +362,8 @@ public class ServerDaemon implements Daemon { public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } + + public void setMaxFormContentSize(int maxFormContentSize) { + this.maxFormContentSize = maxFormContentSize; + } } From d6ac91f2df0903737028f882f121edc8fe19b215 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 9 Jan 2024 17:44:11 +0530 Subject: [PATCH 027/212] minio: fix store user creation (#8425) To prevent errors during multi-user access, use account UUID to create/access user on the provider side. Also, update the existing secret key for a user that already exists. --- .../driver/MinIOObjectStoreDriverImpl.java | 100 ++++++++++++------ .../MinIOObjectStoreDriverImplTest.java | 76 +++++++++---- 2 files changed, 123 insertions(+), 53 deletions(-) diff --git a/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java b/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java index 15df5df56dc..b85383a65e8 100644 --- a/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java +++ b/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java @@ -18,16 +18,38 @@ */ package org.apache.cloudstack.storage.datastore.driver; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao; +import org.apache.cloudstack.storage.datastore.db.ObjectStoreDetailsDao; +import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO; +import org.apache.cloudstack.storage.object.BaseObjectStoreDriverImpl; +import org.apache.cloudstack.storage.object.Bucket; +import org.apache.cloudstack.storage.object.BucketObject; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Logger; + import com.amazonaws.services.s3.model.AccessControlList; import com.amazonaws.services.s3.model.BucketPolicy; import com.cloud.agent.api.to.DataStoreTO; -import org.apache.cloudstack.storage.object.Bucket; import com.cloud.storage.BucketVO; import com.cloud.storage.dao.BucketDao; import com.cloud.user.Account; import com.cloud.user.AccountDetailsDao; import com.cloud.user.dao.AccountDao; import com.cloud.utils.exception.CloudRuntimeException; + import io.minio.BucketExistsArgs; import io.minio.DeleteBucketEncryptionArgs; import io.minio.MakeBucketArgs; @@ -42,26 +64,10 @@ import io.minio.admin.UserInfo; import io.minio.admin.messages.DataUsageInfo; import io.minio.messages.SseConfiguration; import io.minio.messages.VersioningConfiguration; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao; -import org.apache.cloudstack.storage.datastore.db.ObjectStoreDetailsDao; -import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO; -import org.apache.cloudstack.storage.object.BaseObjectStoreDriverImpl; -import org.apache.cloudstack.storage.object.BucketObject; -import org.apache.commons.codec.binary.Base64; -import org.apache.log4j.Logger; - -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.inject.Inject; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { private static final Logger s_logger = Logger.getLogger(MinIOObjectStoreDriverImpl.class); + protected static final String ACS_PREFIX = "acs"; @Inject AccountDao _accountDao; @@ -81,14 +87,18 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { private static final String ACCESS_KEY = "accesskey"; private static final String SECRET_KEY = "secretkey"; - private static final String MINIO_ACCESS_KEY = "minio-accesskey"; - private static final String MINIO_SECRET_KEY = "minio-secretkey"; + protected static final String MINIO_ACCESS_KEY = "minio-accesskey"; + protected static final String MINIO_SECRET_KEY = "minio-secretkey"; @Override public DataStoreTO getStoreTO(DataStore store) { return null; } + protected String getUserOrAccessKeyForAccount(Account account) { + return String.format("%s-%s", ACS_PREFIX, account.getUuid()); + } + @Override public Bucket createBucket(Bucket bucket, boolean objectLock) { //ToDo Client pool mgmt @@ -135,8 +145,8 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { " \"Version\": \"2012-10-17\"\n" + " }"; MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId); - String policyName = "acs-"+account.getAccountName()+"-policy"; - String userName = "acs-"+account.getAccountName(); + String policyName = getUserOrAccessKeyForAccount(account) + "-policy"; + String userName = getUserOrAccessKeyForAccount(account); try { minioAdminClient.addCannedPolicy(policyName, policy); minioAdminClient.setPolicy(userName, false, policyName); @@ -250,22 +260,53 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { } + protected void updateAccountCredentials(final long accountId, final String accessKey, final String secretKey, final boolean checkIfNotPresent) { + Map details = _accountDetailsDao.findDetails(accountId); + boolean updateNeeded = false; + if (!checkIfNotPresent || StringUtils.isBlank(details.get(MINIO_ACCESS_KEY))) { + details.put(MINIO_ACCESS_KEY, accessKey); + updateNeeded = true; + } + if (StringUtils.isAllBlank(secretKey, details.get(MINIO_SECRET_KEY))) { + s_logger.error(String.format("Failed to retrieve secret key for MinIO user: %s from store and account details", accessKey)); + } + if (StringUtils.isNotBlank(secretKey) && (!checkIfNotPresent || StringUtils.isBlank(details.get(MINIO_SECRET_KEY)))) { + details.put(MINIO_SECRET_KEY, secretKey); + updateNeeded = true; + } + if (!updateNeeded) { + return; + } + _accountDetailsDao.persist(accountId, details); + } + @Override public boolean createUser(long accountId, long storeId) { Account account = _accountDao.findById(accountId); MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId); - String accessKey = "acs-"+account.getAccountName(); + String accessKey = getUserOrAccessKeyForAccount(account); // Check user exists try { UserInfo userInfo = minioAdminClient.getUserInfo(accessKey); if(userInfo != null) { - s_logger.debug("User already exists in MinIO store: "+accessKey); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Skipping user creation as the user already exists in MinIO store: %s", accessKey)); + } + updateAccountCredentials(accountId, accessKey, userInfo.secretKey(), true); return true; } - } catch (Exception e) { - s_logger.debug("User does not exist. Creating user: "+accessKey); + } catch (NoSuchAlgorithmException | IOException | InvalidKeyException e) { + s_logger.error(String.format("Error encountered while retrieving user: %s for existing MinIO store user check", accessKey), e); + return false; + } catch (RuntimeException e) { // MinIO lib may throw RuntimeException with code: XMinioAdminNoSuchUser + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Ignoring error encountered while retrieving user: %s for existing MinIO store user check", accessKey)); + } + s_logger.trace("Exception during MinIO user check", e); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("MinIO store user does not exist. Creating user: %s", accessKey)); } - KeyGenerator generator = null; try { generator = KeyGenerator.getInstance("HmacSHA1"); @@ -280,10 +321,7 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { throw new CloudRuntimeException(e); } // Store user credentials - Map details = new HashMap<>(); - details.put(MINIO_ACCESS_KEY, accessKey); - details.put(MINIO_SECRET_KEY, secretKey); - _accountDetailsDao.persist(accountId, details); + updateAccountCredentials(accountId, accessKey, secretKey, false); return true; } diff --git a/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java b/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java index 3041a5b232b..ac88a0ded39 100644 --- a/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java +++ b/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java @@ -16,16 +16,23 @@ // under the License. package org.apache.cloudstack.storage.datastore.driver; -import com.cloud.storage.BucketVO; -import com.cloud.storage.dao.BucketDao; -import com.cloud.user.AccountDetailVO; -import com.cloud.user.AccountDetailsDao; -import com.cloud.user.AccountVO; -import com.cloud.user.dao.AccountDao; -import io.minio.BucketExistsArgs; -import io.minio.MinioClient; -import io.minio.RemoveBucketArgs; -import io.minio.admin.MinioAdminClient; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao; import org.apache.cloudstack.storage.datastore.db.ObjectStoreDetailsDao; import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO; @@ -34,22 +41,24 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; -import java.util.ArrayList; +import com.cloud.storage.BucketVO; +import com.cloud.storage.dao.BucketDao; +import com.cloud.user.AccountDetailVO; +import com.cloud.user.AccountDetailsDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import io.minio.BucketExistsArgs; +import io.minio.MinioClient; +import io.minio.RemoveBucketArgs; +import io.minio.admin.MinioAdminClient; +import io.minio.admin.UserInfo; @RunWith(MockitoJUnitRunner.class) public class MinIOObjectStoreDriverImplTest { @@ -97,7 +106,7 @@ public class MinIOObjectStoreDriverImplTest { doReturn(minioClient).when(minioObjectStoreDriverImpl).getMinIOClient(anyLong()); doReturn(minioAdminClient).when(minioObjectStoreDriverImpl).getMinIOAdminClient(anyLong()); when(bucketDao.listByObjectStoreIdAndAccountId(anyLong(), anyLong())).thenReturn(new ArrayList()); - when(account.getAccountName()).thenReturn("admin"); + when(account.getUuid()).thenReturn(UUID.randomUUID().toString()); when(accountDao.findById(anyLong())).thenReturn(account); when(accountDetailsDao.findDetail(anyLong(),anyString())). thenReturn(new AccountDetailVO(1L, "abc","def")); @@ -119,4 +128,27 @@ public class MinIOObjectStoreDriverImplTest { verify(minioClient, times(1)).bucketExists(any()); verify(minioClient, times(1)).removeBucket(any()); } + + @Test + public void testCreateUserExisting() throws Exception { + String uuid = "uuid"; + String accessKey = MinIOObjectStoreDriverImpl.ACS_PREFIX + "-" + uuid; + String secretKey = "secret"; + + doReturn(minioAdminClient).when(minioObjectStoreDriverImpl).getMinIOAdminClient(anyLong()); + when(accountDao.findById(anyLong())).thenReturn(account); + when(account.getUuid()).thenReturn(uuid); + UserInfo info = mock(UserInfo.class); + when(info.secretKey()).thenReturn(secretKey); + when(minioAdminClient.getUserInfo(accessKey)).thenReturn(info); + final Map persistedMap = new HashMap<>(); + Mockito.doAnswer((Answer) invocation -> { + persistedMap.putAll((Map)invocation.getArguments()[1]); + return null; + }).when(accountDetailsDao).persist(Mockito.anyLong(), Mockito.anyMap()); + boolean result = minioObjectStoreDriverImpl.createUser(1L, 1L); + assertTrue(result); + assertEquals(accessKey, persistedMap.get(MinIOObjectStoreDriverImpl.MINIO_ACCESS_KEY)); + assertEquals(secretKey, persistedMap.get(MinIOObjectStoreDriverImpl.MINIO_SECRET_KEY)); + } } From 76aff0f422a476f1de720999d309f8f07756cd50 Mon Sep 17 00:00:00 2001 From: sato03 Date: Wed, 10 Jan 2024 03:35:46 -0300 Subject: [PATCH 028/212] Add reconnect button to hosts on alert (#8468) Currently, if a host is on alert, the option to reconnect it is not presented in the UI. This PR addresses this issue by adding a reconnect button to the host if it is in an alert state. --- ui/src/config/section/infra/hosts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/config/section/infra/hosts.js b/ui/src/config/section/infra/hosts.js index cb0de90b26c..62fd6ea18dd 100644 --- a/ui/src/config/section/infra/hosts.js +++ b/ui/src/config/section/infra/hosts.js @@ -94,7 +94,7 @@ export default { label: 'label.action.force.reconnect', message: 'message.confirm.action.force.reconnect', dataView: true, - show: (record) => { return ['Disconnected', 'Up'].includes(record.state) } + show: (record) => { return ['Disconnected', 'Up', 'Alert'].includes(record.state) } }, { api: 'updateHost', From 32f379270c165b77e02f93c118ef352734af39a4 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 10 Jan 2024 12:06:12 +0530 Subject: [PATCH 029/212] Register managed user data using POST call from UI (to support user data content > 4096 bytes) (#8487) This PR allows to register managed user data using POST call from UI (to support user data content > 4096 bytes). Partially fixes #8415 --- ui/src/views/compute/RegisterUserData.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/views/compute/RegisterUserData.vue b/ui/src/views/compute/RegisterUserData.vue index a1322a12832..990e59ff277 100644 --- a/ui/src/views/compute/RegisterUserData.vue +++ b/ui/src/views/compute/RegisterUserData.vue @@ -211,7 +211,7 @@ export default { params.params = userdataparams } - api('registerUserData', params).then(json => { + api('registerUserData', {}, 'POST', params).then(json => { this.$message.success(this.$t('message.success.register.user.data') + ' ' + values.name) }).catch(error => { this.$notifyError(error) From c569fe91191cc07d639bb89268b435a01ce4f065 Mon Sep 17 00:00:00 2001 From: slavkap <51903378+slavkap@users.noreply.github.com> Date: Wed, 10 Jan 2024 09:42:07 +0200 Subject: [PATCH 030/212] Fix KVM import and list unmanaged VMs (#8445) VM import fixes 1 - Fix of VM insert for VMs with StorPool volumes 2 - Fix of list/insert unmanaged VMs with RBD volumes --- ...rtGetUnmanagedInstancesCommandWrapper.java | 31 +++++++++---------- .../vm/UnmanagedVMsManagerImpl.java | 2 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java index a2d84063d74..9a4498b12fd 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetUnmanagedInstancesCommandWrapper.java @@ -27,20 +27,17 @@ import com.cloud.resource.ResourceWrapper; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -import org.apache.cloudstack.utils.qemu.QemuImg; -import org.apache.cloudstack.utils.qemu.QemuImgException; -import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.cloudstack.vm.UnmanagedInstanceTO; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.libvirt.Connect; import org.libvirt.Domain; +import org.libvirt.DomainBlockInfo; import org.libvirt.LibvirtException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; @ResourceWrapper(handles=GetUnmanagedInstancesCommand.class) public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWrapper { @@ -132,7 +129,7 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra instance.setPowerState(getPowerState(libvirtComputingResource.getVmState(conn,domain.getName()))); instance.setMemory((int) LibvirtComputingResource.getDomainMemory(domain) / 1024); instance.setNics(getUnmanagedInstanceNics(parser.getInterfaces())); - instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource)); + instance.setDisks(getUnmanagedInstanceDisks(parser.getDisks(),libvirtComputingResource, conn, domain.getName())); instance.setVncPassword(parser.getVncPasswd() + "aaaaaaaaaaaaaa"); // Suffix back extra characters for DB compatibility return instance; @@ -170,7 +167,7 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra return nics; } - private List getUnmanagedInstanceDisks(List disksInfo, LibvirtComputingResource libvirtComputingResource){ + private List getUnmanagedInstanceDisks(List disksInfo, LibvirtComputingResource libvirtComputingResource, Connect conn, String domainName) { final ArrayList disks = new ArrayList<>(disksInfo.size()); int counter = 0; for (LibvirtVMDef.DiskDef diskDef : disksInfo) { @@ -180,14 +177,11 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra final UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk(); Long size = null; - String imagePath = null; try { - QemuImgFile file = new QemuImgFile(diskDef.getSourcePath()); - QemuImg qemu = new QemuImg(0); - Map info = qemu.info(file); - size = Long.parseLong(info.getOrDefault("virtual_size", "0")); - imagePath = info.getOrDefault("image", null); - } catch (QemuImgException | LibvirtException e) { + Domain dm = conn.domainLookupByName(domainName); + DomainBlockInfo blockInfo = dm.blockInfo(diskDef.getDiskLabel()); + size = blockInfo.getCapacity(); + } catch (LibvirtException e) { throw new RuntimeException(e); } @@ -203,14 +197,19 @@ public final class LibvirtGetUnmanagedInstancesCommandWrapper extends CommandWra disk.setDatastoreHost(sourceHostPath.first()); disk.setDatastorePath(sourceHostPath.second()); } else { - disk.setDatastorePath(diskDef.getSourcePath()); + int pathEnd = diskDef.getSourcePath().lastIndexOf("/"); + if (pathEnd >= 0) { + disk.setDatastorePath(diskDef.getSourcePath().substring(0, pathEnd)); + } else { + disk.setDatastorePath(diskDef.getSourcePath()); + } disk.setDatastoreHost(diskDef.getSourceHost()); } disk.setDatastoreType(diskDef.getDiskType().toString()); disk.setDatastorePort(diskDef.getSourceHostPort()); - disk.setImagePath(imagePath); - disk.setDatastoreName(imagePath.substring(imagePath.lastIndexOf("/"))); + disk.setImagePath(diskDef.getSourcePath()); + disk.setDatastoreName(disk.getDatastorePath()); disks.add(disk); } return disks; diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index ebd11f11957..559b6c7af06 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -551,7 +551,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List pools = primaryDataStoreDao.listPoolsByCluster(cluster.getId()); pools.addAll(primaryDataStoreDao.listByDataCenterId(zone.getId())); for (StoragePool pool : pools) { - if (pool.getPath().endsWith(dsName)) { + if (StringUtils.contains(pool.getPath(), dsPath)) { storagePool = pool; break; } From b8d3e342be2134cfe61a654ce2826ad0c1bc9b3e Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Wed, 10 Jan 2024 04:51:00 -0300 Subject: [PATCH 031/212] Fix KVM import unmanaged instances on basic zone (#8465) This PR fixes import unmanaged instances on KVM basic zones, on top of #8433 Fixes: #8439: point 1 --- .../service/NetworkOrchestrationService.java | 3 +- .../orchestration/NetworkOrchestrator.java | 46 +++++++--- .../NetworkOrchestratorTest.java | 88 +++++++++++++++++++ .../com/cloud/network/dao/IPAddressDao.java | 2 + .../cloud/network/dao/IPAddressDaoImpl.java | 9 ++ .../java/com/cloud/vm/UserVmManagerImpl.java | 2 +- .../vm/UnmanagedVMsManagerImpl.java | 5 +- .../com/cloud/vpc/MockNetworkManagerImpl.java | 3 +- .../vm/UnmanagedVMsManagerImplTest.java | 2 +- 9 files changed, 144 insertions(+), 16 deletions(-) diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 6d7c540613b..c691b8b0942 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -20,6 +20,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.ConfigKey.Scope; @@ -338,7 +339,7 @@ public interface NetworkOrchestrationService { */ void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile); - Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException; + Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter datacenter, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException; void unmanageNics(VirtualMachineProfile vm); } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index c6396dbdede..12271f5f105 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -4564,18 +4564,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @DB @Override - public Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final boolean forced) + public Pair importNic(final String macAddress, int deviceId, final Network network, final Boolean isDefaultNic, final VirtualMachine vm, final Network.IpAddresses ipAddresses, final DataCenter dataCenter, final boolean forced) throws ConcurrentOperationException, InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { s_logger.debug("Allocating nic for vm " + vm.getUuid() + " in network " + network + " during import"); String guestIp = null; + IPAddressVO freeIpAddress = null; if (ipAddresses != null && StringUtils.isNotEmpty(ipAddresses.getIp4Address())) { if (ipAddresses.getIp4Address().equals("auto")) { ipAddresses.setIp4Address(null); } - if (network.getGuestType() != GuestType.L2) { - guestIp = _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address()); - } else { - guestIp = null; + freeIpAddress = getGuestIpForNicImport(network, dataCenter, ipAddresses); + if (freeIpAddress != null && freeIpAddress.getAddress() != null) { + guestIp = freeIpAddress.getAddress().addr(); } if (guestIp == null && network.getGuestType() != GuestType.L2 && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) { throw new InsufficientVirtualNetworkCapacityException("Unable to acquire Guest IP address for network " + network, DataCenter.class, @@ -4583,6 +4583,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } final String finalGuestIp = guestIp; + final IPAddressVO freeIp = freeIpAddress; final NicVO vo = Transaction.execute(new TransactionCallback() { @Override public NicVO doInTransaction(TransactionStatus status) { @@ -4594,12 +4595,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType()); vo.setMacAddress(macAddressToPersist); vo.setAddressFormat(Networks.AddressFormat.Ip4); - if (NetUtils.isValidIp4(finalGuestIp) && StringUtils.isNotEmpty(network.getGateway())) { + Pair pair = getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, freeIp); + String gateway = pair.first(); + String netmask = pair.second(); + if (NetUtils.isValidIp4(finalGuestIp) && StringUtils.isNotEmpty(gateway)) { vo.setIPv4Address(finalGuestIp); - vo.setIPv4Gateway(network.getGateway()); - if (StringUtils.isNotEmpty(network.getCidr())) { - vo.setIPv4Netmask(NetUtils.cidr2Netmask(network.getCidr())); - } + vo.setIPv4Gateway(gateway); + vo.setIPv4Netmask(netmask); } vo.setBroadcastUri(network.getBroadcastUri()); vo.setMode(network.getMode()); @@ -4636,6 +4638,30 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return new Pair(vmNic, Integer.valueOf(deviceId)); } + protected IPAddressVO getGuestIpForNicImport(Network network, DataCenter dataCenter, Network.IpAddresses ipAddresses) { + if (network.getGuestType() == GuestType.L2) { + return null; + } + if (dataCenter.getNetworkType() == NetworkType.Advanced) { + String guestIpAddress = _ipAddrMgr.acquireGuestIpAddress(network, ipAddresses.getIp4Address()); + return _ipAddressDao.findByIp(guestIpAddress); + } + return _ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(network.getId(), dataCenter.getId(), IpAddress.State.Free); + } + + /** + * Obtain the gateway and netmask for a VM NIC to import + * If the VM to import is on a Basic Zone, then obtain the information from the vlan table instead of the network + */ + protected Pair getNetworkGatewayAndNetmaskForNicImport(Network network, DataCenter dataCenter, IPAddressVO freeIp) { + VlanVO vlan = dataCenter.getNetworkType() == NetworkType.Basic && freeIp != null ? + _vlanDao.findById(freeIp.getVlanId()) : null; + String gateway = vlan != null ? vlan.getVlanGateway() : network.getGateway(); + String netmask = vlan != null ? vlan.getVlanNetmask() : + (StringUtils.isNotEmpty(network.getCidr()) ? NetUtils.cidr2Netmask(network.getCidr()) : null); + return new Pair<>(gateway, netmask); + } + private String generateNewMacAddressIfForced(Network network, String macAddress, boolean forced) { if (!forced) { throw new CloudRuntimeException("NIC with MAC address = " + macAddress + " exists on network with ID = " + network.getId() + diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index 0aed5a2c259..3d2c96b083d 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -29,6 +29,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; +import com.cloud.network.IpAddressManager; +import com.cloud.utils.Pair; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; @@ -125,6 +128,7 @@ public class NetworkOrchestratorTest extends TestCase { testOrchastrator.routerNetworkDao = mock(RouterNetworkDao.class); testOrchastrator._vpcMgr = mock(VpcManager.class); testOrchastrator.routerJoinDao = mock(DomainRouterJoinDao.class); + testOrchastrator._ipAddrMgr = mock(IpAddressManager.class); DhcpServiceProvider provider = mock(DhcpServiceProvider.class); Map capabilities = new HashMap(); @@ -708,4 +712,88 @@ public class NetworkOrchestratorTest extends TestCase { Assert.assertEquals(ip6Dns[0], profile.getIPv6Dns1()); Assert.assertEquals(ip6Dns[1], profile.getIPv6Dns2()); } + + @Test + public void testGetNetworkGatewayAndNetmaskForNicImportAdvancedZone() { + Network network = Mockito.mock(Network.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class); + + String networkGateway = "10.1.1.1"; + String networkNetmask = "255.255.255.0"; + String networkCidr = "10.1.1.0/24"; + Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + Mockito.when(network.getGateway()).thenReturn(networkGateway); + Mockito.when(network.getCidr()).thenReturn(networkCidr); + Pair pair = testOrchastrator.getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, ipAddressVO); + Assert.assertNotNull(pair); + Assert.assertEquals(networkGateway, pair.first()); + Assert.assertEquals(networkNetmask, pair.second()); + } + + @Test + public void testGetNetworkGatewayAndNetmaskForNicImportBasicZone() { + Network network = Mockito.mock(Network.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class); + + String defaultNetworkGateway = "10.1.1.1"; + String defaultNetworkNetmask = "255.255.255.0"; + VlanVO vlan = Mockito.mock(VlanVO.class); + Mockito.when(vlan.getVlanGateway()).thenReturn(defaultNetworkGateway); + Mockito.when(vlan.getVlanNetmask()).thenReturn(defaultNetworkNetmask); + Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic); + Mockito.when(ipAddressVO.getVlanId()).thenReturn(1L); + Mockito.when(testOrchastrator._vlanDao.findById(1L)).thenReturn(vlan); + Pair pair = testOrchastrator.getNetworkGatewayAndNetmaskForNicImport(network, dataCenter, ipAddressVO); + Assert.assertNotNull(pair); + Assert.assertEquals(defaultNetworkGateway, pair.first()); + Assert.assertEquals(defaultNetworkNetmask, pair.second()); + } + + @Test + public void testGetGuestIpForNicImportL2Network() { + Network network = Mockito.mock(Network.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + Mockito.when(network.getGuestType()).thenReturn(GuestType.L2); + Assert.assertNull(testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses)); + } + + @Test + public void testGetGuestIpForNicImportAdvancedZone() { + Network network = Mockito.mock(Network.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated); + Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + String ipAddress = "10.1.10.10"; + IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class); + Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress); + Mockito.when(testOrchastrator._ipAddrMgr.acquireGuestIpAddress(network, ipAddress)).thenReturn(ipAddress); + Ip ip = mock(Ip.class); + Mockito.when(ipAddressVO.getAddress()).thenReturn(ip); + Mockito.when(testOrchastrator._ipAddressDao.findByIp(ipAddress)).thenReturn(ipAddressVO); + IPAddressVO guestIp = testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses); + Assert.assertEquals(ip, guestIp.getAddress()); + } + + @Test + public void testGetGuestIpForNicImportBasicZone() { + Network network = Mockito.mock(Network.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated); + Mockito.when(dataCenter.getNetworkType()).thenReturn(DataCenter.NetworkType.Basic); + long networkId = 1L; + long dataCenterId = 1L; + IPAddressVO ipAddressVO = Mockito.mock(IPAddressVO.class); + Ip ip = mock(Ip.class); + Mockito.when(ipAddressVO.getAddress()).thenReturn(ip); + Mockito.when(network.getId()).thenReturn(networkId); + Mockito.when(dataCenter.getId()).thenReturn(dataCenterId); + Mockito.when(testOrchastrator._ipAddressDao.findBySourceNetworkIdAndDatacenterIdAndState(networkId, dataCenterId, State.Free)).thenReturn(ipAddressVO); + IPAddressVO guestIp = testOrchastrator.getGuestIpForNicImport(network, dataCenter, ipAddresses); + Assert.assertEquals(ip, guestIp.getAddress()); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java index f75dc8a6661..b1b1e1cf757 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDao.java @@ -103,4 +103,6 @@ public interface IPAddressDao extends GenericDao { List listByNetworkId(long networkId); void buildQuarantineSearchCriteria(SearchCriteria sc); + + IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetworkId, long dataCenterId, State state); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java index 9ffc4c9159c..d1427522715 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java @@ -554,4 +554,13 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen sc.setParametersIfNotNull("quarantinedPublicIpsIdsNIN", quarantinedIpsIdsAllowedToUser); } + + @Override + public IPAddressVO findBySourceNetworkIdAndDatacenterIdAndState(long sourceNetworkId, long dataCenterId, State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("sourcenetwork", sourceNetworkId); + sc.setParameters("dataCenterId", dataCenterId); + sc.setParameters("state", State.Free); + return findOneBy(sc); + } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 121ca95e365..b7bd528e6e9 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4581,7 +4581,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Host host, Host lastHost, VirtualMachine.PowerState powerState) { if (isImport) { vm.setDataCenterId(zone.getId()); - if (hypervisorType == HypervisorType.VMware) { + if (List.of(HypervisorType.VMware, HypervisorType.KVM).contains(hypervisorType)) { vm.setHostId(host.getId()); } if (lastHost != null) { diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 559b6c7af06..85f2119b4ca 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -855,7 +855,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } private NicProfile importNic(UnmanagedInstanceTO.Nic nic, VirtualMachine vm, Network network, Network.IpAddresses ipAddresses, int deviceId, boolean isDefaultNic, boolean forced) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { - Pair result = networkOrchestrationService.importNic(nic.getMacAddress(), deviceId, network, isDefaultNic, vm, ipAddresses, forced); + DataCenterVO dataCenterVO = dataCenterDao.findById(network.getDataCenterId()); + Pair result = networkOrchestrationService.importNic(nic.getMacAddress(), deviceId, network, isDefaultNic, vm, ipAddresses, dataCenterVO, forced); if (result == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("NIC ID: %s import failed", nic.getNicId())); } @@ -2371,7 +2372,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { cleanupFailedImportVM(userVm); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); } - networkOrchestrationService.importNic(macAddress,0,network, true, userVm, requestedIpPair, true); + networkOrchestrationService.importNic(macAddress,0,network, true, userVm, requestedIpPair, zone, true); publishVMUsageUpdateResourceCount(userVm, serviceOffering); return userVm; } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 7535841974e..04556c49ab2 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -24,6 +24,7 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.DataCenter; import com.cloud.network.PublicIpQuarantine; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; @@ -1030,7 +1031,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } @Override - public Pair importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses, boolean forced) { + public Pair importNic(String macAddress, int deviceId, Network network, Boolean isDefaultNic, VirtualMachine vm, IpAddresses ipAddresses, DataCenter dataCenter, boolean forced) { return null; } diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index e7831998353..229e59b1a7a 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -367,7 +367,7 @@ public class UnmanagedVMsManagerImplTest { NicProfile profile = Mockito.mock(NicProfile.class); Integer deviceId = 100; Pair pair = new Pair(profile, deviceId); - when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), Mockito.anyBoolean())).thenReturn(pair); + when(networkOrchestrationService.importNic(nullable(String.class), nullable(Integer.class), nullable(Network.class), nullable(Boolean.class), nullable(VirtualMachine.class), nullable(Network.IpAddresses.class), nullable(DataCenter.class), Mockito.anyBoolean())).thenReturn(pair); when(volumeDao.findByInstance(Mockito.anyLong())).thenReturn(volumes); List userVmResponses = new ArrayList<>(); UserVmResponse userVmResponse = new UserVmResponse(); From 4f40eae1c406834f2539317b5a8f33031e70bd5c Mon Sep 17 00:00:00 2001 From: Vishesh Date: Wed, 10 Jan 2024 17:52:46 +0530 Subject: [PATCH 032/212] DRS: Use free metrics insteado of used for computation (#8458) This PR makes changes to use cluster's free metrics instead of used while computing imbalance for the cluster. This allows DRS to run for clusters where hosts doesn't have the same amount of metrics. --- .../cluster/ClusterDrsAlgorithm.java | 36 +++++++++---------- .../cloudstack/cluster/BalancedTest.java | 20 +++++------ .../cloudstack/cluster/CondensedTest.java | 20 +++++------ .../cluster/ClusterDrsServiceImpl.java | 12 +++---- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java index ad5edc9224b..889c49298ec 100644 --- a/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java +++ b/api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java @@ -65,18 +65,18 @@ public interface ClusterDrsAlgorithm extends Adapter { * the service offering for the virtual machine * @param destHost * the destination host for the virtual machine - * @param hostCpuUsedMap - * a map of host IDs to the amount of CPU used on each host - * @param hostMemoryUsedMap - * a map of host IDs to the amount of memory used on each host + * @param hostCpuFreeMap + * a map of host IDs to the amount of CPU free on each host + * @param hostMemoryFreeMap + * a map of host IDs to the amount of memory free on each host * @param requiresStorageMotion * whether storage motion is required for the virtual machine * * @return a ternary containing improvement, cost, benefit */ Ternary getMetrics(long clusterId, VirtualMachine vm, ServiceOffering serviceOffering, - Host destHost, Map hostCpuUsedMap, - Map hostMemoryUsedMap, Boolean requiresStorageMotion); + Host destHost, Map hostCpuFreeMap, + Map hostMemoryFreeMap, Boolean requiresStorageMotion); /** * Calculates the imbalance of the cluster after a virtual machine migration. @@ -87,30 +87,30 @@ public interface ClusterDrsAlgorithm extends Adapter { * the virtual machine being migrated * @param destHost * the destination host for the virtual machine - * @param hostCpuUsedMap - * a map of host IDs to the amount of CPU used on each host - * @param hostMemoryUsedMap - * a map of host IDs to the amount of memory used on each host + * @param hostCpuFreeMap + * a map of host IDs to the amount of CPU free on each host + * @param hostMemoryFreeMap + * a map of host IDs to the amount of memory free on each host * * @return a pair containing the CPU and memory imbalance of the cluster after the migration */ default Pair getImbalancePostMigration(ServiceOffering serviceOffering, VirtualMachine vm, - Host destHost, Map hostCpuUsedMap, - Map hostMemoryUsedMap) { + Host destHost, Map hostCpuFreeMap, + Map hostMemoryFreeMap) { List postCpuList = new ArrayList<>(); List postMemoryList = new ArrayList<>(); final int vmCpu = serviceOffering.getCpu() * serviceOffering.getSpeed(); final long vmRam = serviceOffering.getRamSize() * 1024L * 1024L; - for (Long hostId : hostCpuUsedMap.keySet()) { - long cpu = hostCpuUsedMap.get(hostId); - long memory = hostMemoryUsedMap.get(hostId); + for (Long hostId : hostCpuFreeMap.keySet()) { + long cpu = hostCpuFreeMap.get(hostId); + long memory = hostMemoryFreeMap.get(hostId); if (hostId == destHost.getId()) { - postCpuList.add(cpu + vmCpu); - postMemoryList.add(memory + vmRam); - } else if (hostId.equals(vm.getHostId())) { postCpuList.add(cpu - vmCpu); postMemoryList.add(memory - vmRam); + } else if (hostId.equals(vm.getHostId())) { + postCpuList.add(cpu + vmCpu); + postMemoryList.add(memory + vmRam); } else { postCpuList.add(cpu); postMemoryList.add(memory); diff --git a/plugins/drs/cluster/balanced/src/test/java/org/apache/cloudstack/cluster/BalancedTest.java b/plugins/drs/cluster/balanced/src/test/java/org/apache/cloudstack/cluster/BalancedTest.java index 4b84049e49a..0da807e65c3 100644 --- a/plugins/drs/cluster/balanced/src/test/java/org/apache/cloudstack/cluster/BalancedTest.java +++ b/plugins/drs/cluster/balanced/src/test/java/org/apache/cloudstack/cluster/BalancedTest.java @@ -68,7 +68,7 @@ public class BalancedTest { List cpuList, memoryList; - Map hostCpuUsedMap, hostMemoryUsedMap; + Map hostCpuFreeMap, hostMemoryFreeMap; @Mock @@ -105,13 +105,13 @@ public class BalancedTest { cpuList = Arrays.asList(1L, 2L); memoryList = Arrays.asList(512L, 2048L); - hostCpuUsedMap = new HashMap<>(); - hostCpuUsedMap.put(1L, 1000L); - hostCpuUsedMap.put(2L, 2000L); + hostCpuFreeMap = new HashMap<>(); + hostCpuFreeMap.put(1L, 2000L); + hostCpuFreeMap.put(2L, 1000L); - hostMemoryUsedMap = new HashMap<>(); - hostMemoryUsedMap.put(1L, 512L * 1024L * 1024L); - hostMemoryUsedMap.put(2L, 2048L * 1024L * 1024L); + hostMemoryFreeMap = new HashMap<>(); + hostMemoryFreeMap.put(1L, 2048L * 1024L * 1024L); + hostMemoryFreeMap.put(2L, 512L * 1024L * 1024L); } private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, @@ -191,7 +191,7 @@ public class BalancedTest { public void getMetricsWithCpu() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "cpu"); Ternary result = balanced.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(0.0, result.first(), 0.01); assertEquals(0.0, result.second(), 0.0); assertEquals(1.0, result.third(), 0.0); @@ -205,7 +205,7 @@ public class BalancedTest { public void getMetricsWithMemory() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "memory"); Ternary result = balanced.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(0.4, result.first(), 0.01); assertEquals(0, result.second(), 0.0); assertEquals(1, result.third(), 0.0); @@ -219,7 +219,7 @@ public class BalancedTest { public void getMetricsWithDefault() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "both"); Ternary result = balanced.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(0.4, result.first(), 0.01); assertEquals(0, result.second(), 0.0); assertEquals(1, result.third(), 0.0); diff --git a/plugins/drs/cluster/condensed/src/test/java/org/apache/cloudstack/cluster/CondensedTest.java b/plugins/drs/cluster/condensed/src/test/java/org/apache/cloudstack/cluster/CondensedTest.java index d8cf581768a..0ba2b66379a 100644 --- a/plugins/drs/cluster/condensed/src/test/java/org/apache/cloudstack/cluster/CondensedTest.java +++ b/plugins/drs/cluster/condensed/src/test/java/org/apache/cloudstack/cluster/CondensedTest.java @@ -66,7 +66,7 @@ public class CondensedTest { List cpuList, memoryList; - Map hostCpuUsedMap, hostMemoryUsedMap; + Map hostCpuFreeMap, hostMemoryFreeMap; private AutoCloseable closeable; @@ -98,13 +98,13 @@ public class CondensedTest { cpuList = Arrays.asList(1L, 2L); memoryList = Arrays.asList(512L, 2048L); - hostCpuUsedMap = new HashMap<>(); - hostCpuUsedMap.put(1L, 1000L); - hostCpuUsedMap.put(2L, 2000L); + hostCpuFreeMap = new HashMap<>(); + hostCpuFreeMap.put(1L, 2000L); + hostCpuFreeMap.put(2L, 1000L); - hostMemoryUsedMap = new HashMap<>(); - hostMemoryUsedMap.put(1L, 512L * 1024L * 1024L); - hostMemoryUsedMap.put(2L, 2048L * 1024L * 1024L); + hostMemoryFreeMap = new HashMap<>(); + hostMemoryFreeMap.put(1L, 2048L * 1024L * 1024L); + hostMemoryFreeMap.put(2L, 512L * 1024L * 1024L); } private void overrideDefaultConfigValue(final ConfigKey configKey, @@ -185,7 +185,7 @@ public class CondensedTest { public void getMetricsWithCpu() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "cpu"); Ternary result = condensed.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(0.0, result.first(), 0.0); assertEquals(0, result.second(), 0.0); assertEquals(1, result.third(), 0.0); @@ -199,7 +199,7 @@ public class CondensedTest { public void getMetricsWithMemory() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "memory"); Ternary result = condensed.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(-0.4, result.first(), 0.01); assertEquals(0, result.second(), 0.0); assertEquals(1, result.third(), 0.0); @@ -213,7 +213,7 @@ public class CondensedTest { public void getMetricsWithDefault() throws NoSuchFieldException, IllegalAccessException { overrideDefaultConfigValue(ClusterDrsMetric, "_defaultValue", "both"); Ternary result = condensed.getMetrics(clusterId, vm3, serviceOffering, destHost, - hostCpuUsedMap, hostMemoryUsedMap, false); + hostCpuFreeMap, hostMemoryFreeMap, false); assertEquals(-0.4, result.first(), 0.0001); assertEquals(0, result.second(), 0.0); assertEquals(1, result.third(), 0.0); diff --git a/server/src/main/java/org/apache/cloudstack/cluster/ClusterDrsServiceImpl.java b/server/src/main/java/org/apache/cloudstack/cluster/ClusterDrsServiceImpl.java index 3ebb97ae4f0..f949233e8e8 100644 --- a/server/src/main/java/org/apache/cloudstack/cluster/ClusterDrsServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/cluster/ClusterDrsServiceImpl.java @@ -354,9 +354,9 @@ public class ClusterDrsServiceImpl extends ManagerBase implements ClusterDrsServ hostList.stream().map(HostVO::getId).toArray(Long[]::new)); Map hostCpuMap = hostJoinList.stream().collect(Collectors.toMap(HostJoinVO::getId, - hostJoin -> hostJoin.getCpuUsedCapacity() + hostJoin.getCpuReservedCapacity())); + hostJoin -> hostJoin.getCpus() * hostJoin.getSpeed() - hostJoin.getCpuReservedCapacity() - hostJoin.getCpuUsedCapacity())); Map hostMemoryMap = hostJoinList.stream().collect(Collectors.toMap(HostJoinVO::getId, - hostJoin -> hostJoin.getMemUsedCapacity() + hostJoin.getMemReservedCapacity())); + hostJoin -> hostJoin.getTotalMemory() - hostJoin.getMemUsedCapacity() - hostJoin.getMemReservedCapacity())); Map vmIdServiceOfferingMap = new HashMap<>(); @@ -387,10 +387,10 @@ public class ClusterDrsServiceImpl extends ManagerBase implements ClusterDrsServ long vmCpu = (long) serviceOffering.getCpu() * serviceOffering.getSpeed(); long vmMemory = serviceOffering.getRamSize() * 1024L * 1024L; - hostCpuMap.put(vm.getHostId(), hostCpuMap.get(vm.getHostId()) - vmCpu); - hostCpuMap.put(destHost.getId(), hostCpuMap.get(destHost.getId()) + vmCpu); - hostMemoryMap.put(vm.getHostId(), hostMemoryMap.get(vm.getHostId()) - vmMemory); - hostMemoryMap.put(destHost.getId(), hostMemoryMap.get(destHost.getId()) + vmMemory); + hostCpuMap.put(vm.getHostId(), hostCpuMap.get(vm.getHostId()) + vmCpu); + hostCpuMap.put(destHost.getId(), hostCpuMap.get(destHost.getId()) - vmCpu); + hostMemoryMap.put(vm.getHostId(), hostMemoryMap.get(vm.getHostId()) + vmMemory); + hostMemoryMap.put(destHost.getId(), hostMemoryMap.get(destHost.getId()) - vmMemory); vm.setHostId(destHost.getId()); iteration++; } From e87ce0c7230260c3f6d6a61e09eed7257de9cbcc Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 10 Jan 2024 18:13:32 +0530 Subject: [PATCH 033/212] Fix reorder/list pools when cluster details are not set, while deploying vm / attaching volume (#8373) This PR fixes reorder/list pools when cluster details are not set, while deploying vm / attaching volume. Problem: Attach volume to a VM fails, on infra with zone-wide pools & vm.allocation.algorithm=userdispersing as the cluster details are not set (passed as null) while reordering / listing pools by volumes. Solution: Ignore cluster details when not set, while reordering / listing pools by volumes. --- .../com/cloud/storage/dao/VolumeDaoImpl.java | 38 +++++-- .../cloud/storage/dao/VolumeDaoImplTest.java | 105 ++++++++++++++++++ .../AbstractStoragePoolAllocatorTest.java | 52 +++++++-- 3 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index bf556622463..a773a9502ce 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -83,8 +83,9 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected static final String SELECT_HYPERTYPE_FROM_ZONE_VOLUME = "SELECT s.hypervisor from volumes v, storage_pool s where v.pool_id = s.id and v.id = ?"; protected static final String SELECT_POOLSCOPE = "SELECT s.scope from storage_pool s, volumes v where s.id = v.pool_id and v.id = ?"; - private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? " - + " AND pool.pod_id = ? AND pool.cluster_id = ? " + " GROUP BY pool.id ORDER BY 2 ASC "; + private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1 = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? "; + private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2 = " GROUP BY pool.id ORDER BY 2 ASC "; + private static final String ORDER_ZONE_WIDE_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? " + " AND pool.scope = 'ZONE' AND pool.status='Up' " + " GROUP BY pool.id ORDER BY 2 ASC "; @@ -612,14 +613,27 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol public List listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; - List result = new ArrayList(); + List result = new ArrayList<>(); + StringBuilder sql = new StringBuilder(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1); try { - String sql = ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT; - pstmt = txn.prepareAutoCloseStatement(sql); - pstmt.setLong(1, accountId); - pstmt.setLong(2, dcId); - pstmt.setLong(3, podId); - pstmt.setLong(4, clusterId); + List resourceIdList = new ArrayList<>(); + resourceIdList.add(accountId); + resourceIdList.add(dcId); + + if (podId != null) { + sql.append(" AND pool.pod_id = ?"); + resourceIdList.add(podId); + } + if (clusterId != null) { + sql.append(" AND pool.cluster_id = ?"); + resourceIdList.add(clusterId); + } + sql.append(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2); + + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmt.setLong(i + 1, resourceIdList.get(i)); + } ResultSet rs = pstmt.executeQuery(); while (rs.next()) { @@ -627,9 +641,11 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol } return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e); + s_logger.debug("DB Exception on: " + sql.toString(), e); + throw new CloudRuntimeException(e); } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT, e); + s_logger.debug("Caught: " + sql.toString(), e); + throw new CloudRuntimeException(e); } } diff --git a/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java b/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java new file mode 100644 index 00000000000..7968ee4a375 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VolumeDaoImplTest.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.dao; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.utils.db.TransactionLegacy; + +@RunWith(MockitoJUnitRunner.class) +public class VolumeDaoImplTest { + @Mock + private PreparedStatement preparedStatementMock; + + @Mock + private TransactionLegacy transactionMock; + + private static MockedStatic mockedTransactionLegacy; + + private final VolumeDaoImpl volumeDao = new VolumeDaoImpl(); + + @BeforeClass + public static void init() { + mockedTransactionLegacy = Mockito.mockStatic(TransactionLegacy.class); + } + + @AfterClass + public static void close() { + mockedTransactionLegacy.close(); + } + + @Test + public void testListPoolIdsByVolumeCount_with_cluster_details() throws SQLException { + final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER = + "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? AND pool.pod_id = ? AND pool.cluster_id = ? GROUP BY pool.id ORDER BY 2 ASC "; + final long dcId = 1, accountId = 1; + final Long podId = 1L, clusterId = 1L; + + when(TransactionLegacy.currentTxn()).thenReturn(transactionMock); + when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER))).thenReturn(preparedStatementMock); + ResultSet rs = Mockito.mock(ResultSet.class); + when(preparedStatementMock.executeQuery()).thenReturn(rs, rs); + + volumeDao.listPoolIdsByVolumeCount(dcId, podId, clusterId, accountId); + + verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITH_CLUSTER); + verify(preparedStatementMock, times(1)).setLong(1, accountId); + verify(preparedStatementMock, times(1)).setLong(2, dcId); + verify(preparedStatementMock, times(1)).setLong(3, podId); + verify(preparedStatementMock, times(1)).setLong(4, clusterId); + verify(preparedStatementMock, times(4)).setLong(anyInt(), anyLong()); + verify(preparedStatementMock, times(1)).executeQuery(); + } + + @Test + public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLException { + final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER = + "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? GROUP BY pool.id ORDER BY 2 ASC "; + final long dcId = 1, accountId = 1; + + when(TransactionLegacy.currentTxn()).thenReturn(transactionMock); + when(transactionMock.prepareAutoCloseStatement(startsWith(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER))).thenReturn(preparedStatementMock); + ResultSet rs = Mockito.mock(ResultSet.class); + when(preparedStatementMock.executeQuery()).thenReturn(rs, rs); + + volumeDao.listPoolIdsByVolumeCount(dcId, null, null, accountId); + + verify(transactionMock, times(1)).prepareAutoCloseStatement(ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_QUERY_WITHOUT_CLUSTER); + verify(preparedStatementMock, times(1)).setLong(1, accountId); + verify(preparedStatementMock, times(1)).setLong(2, dcId); + verify(preparedStatementMock, times(2)).setLong(anyInt(), anyLong()); + verify(preparedStatementMock, times(1)).executeQuery(); + } +} diff --git a/engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java b/engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java index 58b6fc99a59..466ae7db9bc 100644 --- a/engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java +++ b/engine/storage/src/test/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocatorTest.java @@ -17,13 +17,13 @@ package org.apache.cloudstack.storage.allocator; -import com.cloud.deploy.DeploymentPlan; -import com.cloud.deploy.DeploymentPlanner; -import com.cloud.storage.Storage; -import com.cloud.storage.StoragePool; -import com.cloud.user.Account; -import com.cloud.vm.DiskProfile; -import com.cloud.vm.VirtualMachineProfile; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.junit.After; import org.junit.Assert; @@ -34,10 +34,14 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.storage.Storage; +import com.cloud.storage.StoragePool; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.vm.DiskProfile; +import com.cloud.vm.VirtualMachineProfile; @RunWith(MockitoJUnitRunner.class) public class AbstractStoragePoolAllocatorTest { @@ -51,6 +55,9 @@ public class AbstractStoragePoolAllocatorTest { Account account; private List pools; + @Mock + VolumeDao volumeDao; + @Before public void setUp() { pools = new ArrayList<>(); @@ -83,6 +90,29 @@ public class AbstractStoragePoolAllocatorTest { Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools); } + @Test + public void reorderStoragePoolsBasedOnAlgorithm_userdispersing_reorder_check() { + allocator.allocationAlgorithm = "userdispersing"; + allocator.volumeDao = volumeDao; + + when(plan.getDataCenterId()).thenReturn(1l); + when(plan.getPodId()).thenReturn(1l); + when(plan.getClusterId()).thenReturn(1l); + when(account.getAccountId()).thenReturn(1l); + List poolIds = new ArrayList<>(); + poolIds.add(1l); + poolIds.add(9l); + when(volumeDao.listPoolIdsByVolumeCount(1l,1l,1l,1l)).thenReturn(poolIds); + + List reorderedPools = allocator.reorderStoragePoolsBasedOnAlgorithm(pools, plan, account); + Assert.assertEquals(poolIds.size(),reorderedPools.size()); + + Mockito.verify(allocator, Mockito.times(0)).reorderPoolsByCapacity(plan, pools); + Mockito.verify(allocator, Mockito.times(1)).reorderPoolsByNumberOfVolumes(plan, pools, account); + Mockito.verify(allocator, Mockito.times(0)).reorderRandomPools(pools); + Mockito.verify(volumeDao, Mockito.times(1)).listPoolIdsByVolumeCount(1l,1l,1l,1l); + } + @Test public void reorderStoragePoolsBasedOnAlgorithm_firstfitleastconsumed() { allocator.allocationAlgorithm = "firstfitleastconsumed"; From c43b7c04f4cf4e51020f229ab027898449e67bf4 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 11 Jan 2024 11:58:56 +0530 Subject: [PATCH 034/212] ui: fix labels when migrating instances from vmware (#8490) Fixes #8474 Renames labels when importing from VMware --- ui/public/locales/en.json | 1 + ui/src/views/tools/ManageInstances.vue | 9 +++++++-- ui/src/views/tools/SelectVmwareVcenter.vue | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 7de4c709225..443b086f580 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -2932,6 +2932,7 @@ "message.installwizard.tooltip.tungsten.provider.vrouterport": "Tungsten provider vrouter port is required", "message.instances.managed": "Instances controlled by CloudStack.", "message.instances.unmanaged": "Instances not controlled by CloudStack.", +"message.instances.migrate.vmware": "Instances that can be migrated from VMware.", "message.interloadbalance.not.return.elementid": "error: listInternalLoadBalancerElements API doesn't return internal LB element ID.", "message.ip.address.changes.effect.after.vm.restart": "IP address changes takes effect only after Instance restart.", "message.ip.v6.prefix.delete": "IPv6 prefix deleted", diff --git a/ui/src/views/tools/ManageInstances.vue b/ui/src/views/tools/ManageInstances.vue index fc14f684e72..c7913cabbe4 100644 --- a/ui/src/views/tools/ManageInstances.vue +++ b/ui/src/views/tools/ManageInstances.vue @@ -238,6 +238,7 @@ @@ -322,8 +323,8 @@ +