diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da263142f097..d562f18f5c73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -187,7 +187,8 @@ jobs: component/test_vpc_offerings component/test_vpc_routers component/test_vpn_users", - "component/test_vpc_network_lbrules" ] + "component/test_vpc_network_lbrules", + "component/test_router_resources"] steps: - uses: actions/checkout@v3 diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 41b2c8135cf0..249748632921 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -23,6 +23,7 @@ import com.cloud.configuration.ResourceLimit; import com.cloud.domain.Domain; import com.cloud.exception.ResourceAllocationException; +import com.cloud.offering.ServiceOffering; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.user.ResourceReservation; @@ -210,4 +211,10 @@ public interface ResourceLimitService { */ ResourceReservation getReservation(Account account, Boolean displayResource, ResourceType type, Long delta) throws ResourceAllocationException; + /** + * Returns the service offering by the given configuration. + * + * @return the service offering found or null if not found + */ + ServiceOffering getServiceOfferingByConfig(); } diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index ada94cd057c1..aba330f237ce 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; @@ -83,10 +84,21 @@ public interface VirtualMachineManager extends Manager { ConfigKey AllowExposeDomainInMetadata = new ConfigKey<>("Advanced", Boolean.class, "metadata.allow.expose.domain", "false", "If set to true, it allows the VM's domain to be seen in metadata.", true, ConfigKey.Scope.Domain); + ConfigKey ResourceCountRouters = new ConfigKey<>("Advanced", Boolean.class, "resource.count.routers", + "false","Count the CPU and memory resource count of virtual routers towards domain resource calculation", + true, ConfigKey.Scope.Domain); + + ConfigKey ResourceCountRoutersType = new ConfigKey<>("Advanced", String.class, "resource.count.routers.type", "all", + "Possible values are all and delta. If value is all then entire VR cpu and ram are counted else diff " + + "between current VR offering and default VR offering is considered", true, ConfigKey.Scope.Domain); + interface Topics { String VM_POWER_STATE = "vm.powerstate"; } + static final String COUNT_ALL_VR_RESOURCES = "all"; + static final String COUNT_DELTA_VR_RESOURCES = "delta"; + /** * Allocates a new virtual machine instance in the CloudStack DB. This * orchestrates the creation of all virtual resources needed in CloudStack @@ -97,8 +109,7 @@ interface Topics { * define this VM but it must be unique for all of CloudStack. * @param template The template this VM is based on. * @param serviceOffering The service offering that specifies the offering this VM should provide. - * @param defaultNetwork The default network for the VM. - * @param rootDiskOffering For created VMs not based on templates, root disk offering specifies the root disk. + * @param rootDiskOfferingInfo For created VMs not based on templates, root disk offering specifies the root disk. * @param dataDiskOfferings Data disks to attach to the VM. * @param auxiliaryNetworks additional networks to attach the VMs to. * @param plan How to deploy the VM. @@ -173,9 +184,9 @@ void advanceReboot(String vmUuid, Map param void checkIfCanUpgrade(VirtualMachine vmInstance, ServiceOffering newServiceOffering); /** - * @param vmId - * @param serviceOfferingId - * @return + * @param vmId the vm id + * @param newServiceOffering the new service offering + * @return true if the vm db was upgraded, false otherwise */ boolean upgradeVmDb(long vmId, ServiceOffering newServiceOffering, ServiceOffering currentServiceOffering); @@ -219,9 +230,8 @@ NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile request NicTO toNicTO(NicProfile nic, HypervisorType hypervisorType); /** - * @param profile - * @param hvGuru - * @return + * @param profile the vm profile + * @return the vmTO */ VirtualMachineTO toVmTO(VirtualMachineProfile profile); @@ -285,4 +295,7 @@ static String getHypervisorHostname(String name) { HashMap> getVmNetworkStatistics(long hostId, String hostName, Map vmMap); + void incrementVrResourceCount(ServiceOffering offering, Account owner, boolean isDeployOrDestroy) throws CloudRuntimeException; + + void decrementVrResourceCount(ServiceOffering offering, Account owner, boolean isDeployOrDestroy) throws CloudRuntimeException; } 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 1b3d914b27a7..ba9f25c40e04 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -27,7 +27,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -47,6 +46,7 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -1095,7 +1095,13 @@ public void orchestrateStart(final String vmUuid, final Map para } } + /** + * Counts VR resources for the domain if global setting is true. + * If the value is "all", counts all VR resources, otherwise get the difference between + * current VR offering and default VR offering. + * + * @param offering VR service offering + * @param defaultRouterOffering default VR service offering + * @param owner account + * @return a Pair of CPU and RAM + */ + public Pair resolveCpuAndMemoryCount(ServiceOffering offering, ServiceOffering defaultRouterOffering, Account owner) { + Integer cpuCount = 0; + Integer memoryCount = 0; + if (COUNT_ALL_VR_RESOURCES.equalsIgnoreCase(ResourceCountRoutersType.valueIn(owner.getDomainId()))) { + cpuCount = offering.getCpu(); + memoryCount = offering.getRamSize(); + } else if (COUNT_DELTA_VR_RESOURCES.equalsIgnoreCase(ResourceCountRoutersType.valueIn(owner.getDomainId()))) { + // Default offering value can be greater than current offering value + if (offering.getCpu() >= defaultRouterOffering.getCpu()) { + cpuCount = offering.getCpu() - defaultRouterOffering.getCpu(); + } + if (offering.getRamSize() >= defaultRouterOffering.getRamSize()) { + memoryCount = offering.getRamSize() - defaultRouterOffering.getRamSize(); + } + } + + return Pair.of(cpuCount.longValue(), memoryCount.longValue()); + } + + private void validateResourceCount(Pair cpuMemoryCount, Account owner) { + final Long cpuCount = cpuMemoryCount.first(); + final Long memoryCount = cpuMemoryCount.second(); + try { + if (cpuCount > 0) { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, cpuCount); + } + if (memoryCount > 0) { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, memoryCount); + } + } catch (ResourceAllocationException ex) { + throw new CloudRuntimeException(String.format("Unable to deploy/start routers due to %s.", ex.getMessage())); + } + } + + /** + * Checks if resource count can be allocated to the account/domain. + * + * @param cpuMemoryCount a Pair of cpu and ram + * @param owner the account + */ + public void calculateResourceCount(Pair cpuMemoryCount, Account owner, boolean isIncrement) { + validateResourceCount(cpuMemoryCount, owner); + final Long cpuCount = cpuMemoryCount.first(); + final Long memoryCount = cpuMemoryCount.second(); + + // Increment the resource count + if (s_logger.isDebugEnabled()) { + if (isIncrement) { + s_logger.debug(String.format("Incrementing the CPU count with value %s and RAM value with %s.", cpuCount, memoryCount)); + } else { + s_logger.debug(String.format("Decrementing CPU resource count with value %s and memory resource with value %s.",cpuCount, memoryCount)); + } + } + + if(isIncrement) { + _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.cpu, cpuCount); + _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.memory, memoryCount); + } else { + _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.cpu, cpuCount); + _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.memory, memoryCount); + } + } + + /** + * Increments the VR resource count. + * If the global setting resource.count.router is true then the VR + * resource count will be considered as well. + * If the global setting resource.count.router.type is "all" then + * the total VR resource count will be considered, otherwise the difference between + * the current VR service offering and the default offering will + * be considered. + * During router deployment/destroy, we increment the resource + * count only if resource.count.running.vms is false, otherwise + * we increment it during VR start/stop. Same applies for + * decrementing resource count. + * + * @param offering VR service offering + * @param owner account + * @param isDeployOrDestroy true if router is being deployed/destroyed + */ + @Override + public void incrementVrResourceCount(ServiceOffering offering, Account owner, boolean isDeployOrDestroy) { + if (isDeployOrDestroy == Boolean.TRUE.equals(ResourceCountRunningVMsonly.value())) { + return; + } // + + final ServiceOffering defaultRouterOffering = _resourceLimitMgr.getServiceOfferingByConfig(); + final Pair cpuMemoryCount = resolveCpuAndMemoryCount(offering, defaultRouterOffering, owner); + calculateResourceCount(cpuMemoryCount, owner, true); + } + + /** + * Decrements the VR resource count. + * + * @param offering the service offering of the VR + * @param owner the account of the VR + * @param isDeployOrDestroy true if the VR is being deployed or destroyed, false if the VR is being started or stopped + */ + @Override + public void decrementVrResourceCount(ServiceOffering offering, Account owner, boolean isDeployOrDestroy) { + if (isDeployOrDestroy == Boolean.TRUE.equals(ResourceCountRunningVMsonly.value())) { + return; + } + + final ServiceOffering defaultRouterOffering = _resourceLimitMgr.getServiceOfferingByConfig(); + final Pair cpuMemoryCount = resolveCpuAndMemoryCount(offering, defaultRouterOffering, owner); + calculateResourceCount(cpuMemoryCount, owner, false); + } + private void resetVmNicsDeviceId(Long vmId) { final List nics = _nicsDao.listByVmId(vmId); - Collections.sort(nics, new Comparator() { - @Override - public int compare(NicVO nic1, NicVO nic2) { - Long nicDevId1 = Long.valueOf(nic1.getDeviceId()); - Long nicDevId2 = Long.valueOf(nic2.getDeviceId()); - return nicDevId1.compareTo(nicDevId2); - } + nics.sort((nic1, nic2) -> { + Long nicDevId1 = (long) nic1.getDeviceId(); + Long nicDevId2 = (long) nic2.getDeviceId(); + return nicDevId1.compareTo(nicDevId2); }); int deviceId = 0; for (final NicVO nic : nics) { @@ -2151,10 +2276,13 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl boolean result = stateTransitTo(vm, Event.OperationSucceeded, null); if (result) { + ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { - ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); - resourceCountDecrement(vm.getAccountId(),new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountDecrement(vm.getAccountId(),Long.valueOf(offering.getCpu()), Long.valueOf(offering.getRamSize())); } + + final Account owner = _entityMgr.findById(Account.class, vm.getAccountId()); + updateVrCountResourceBy(vm.type, vm.getDomainId(), offering, owner, true); } else { throw new CloudRuntimeException("unable to stop " + vm); } @@ -2165,6 +2293,16 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl } } + private void updateVrCountResourceBy(VirtualMachine.Type type, long domainId, ServiceOffering offering, Account owner, boolean decrement) { + if (VirtualMachine.Type.DomainRouter.equals(type) && Boolean.TRUE.equals(ResourceCountRouters.valueIn(domainId))) { + if (decrement) { + decrementVrResourceCount(offering, owner, true); + } else { + incrementVrResourceCount(offering, owner, true); + } + } + } + private void setStateMachine() { _stateMachine = VirtualMachine.State.getStateMachine(); } @@ -3536,13 +3674,10 @@ protected VirtualMachineTO getVmTO(Long vmId) { final VMInstanceVO vm = _vmDao.findById(vmId); final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); final List nics = _nicsDao.listByVmId(profile.getId()); - Collections.sort(nics, new Comparator() { - @Override - public int compare(NicVO nic1, NicVO nic2) { - Long nicId1 = Long.valueOf(nic1.getDeviceId()); - Long nicId2 = Long.valueOf(nic2.getDeviceId()); - return nicId1.compareTo(nicId2); - } + nics.sort((nic1, nic2) -> { + Long nicDevId1 = (long) nic1.getDeviceId(); + Long nicDevId2 = (long) nic2.getDeviceId(); + return nicDevId1.compareTo(nicDevId2); }); for (final NicVO nic : nics) { @@ -4672,7 +4807,7 @@ public ConfigKey[] getConfigKeys() { VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool, HaVmRestartHostUp, ResourceCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel, SystemVmRootDiskSize, - AllowExposeDomainInMetadata + AllowExposeDomainInMetadata, ResourceCountRouters, ResourceCountRoutersType }; } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index 742bb3dda897..0071adb05a2b 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -17,6 +17,7 @@ package com.cloud.vm; +import static com.cloud.vm.VirtualMachineManager.ResourceCountRunningVMsonly; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -33,6 +34,10 @@ import java.util.List; import java.util.Map; +import com.cloud.user.Account; +import com.cloud.user.ResourceLimitService; +import com.cloud.utils.Pair; +import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -46,7 +51,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Command; @@ -83,7 +88,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDao; @RunWith(MockitoJUnitRunner.class) public class VirtualMachineManagerImplTest { @@ -94,8 +98,6 @@ public class VirtualMachineManagerImplTest { @Mock private AgentManager agentManagerMock; @Mock - private VMInstanceDao vmInstanceDaoMock; - @Mock private ServiceOfferingDao serviceOfferingDaoMock; @Mock private VolumeDao volumeDaoMock; @@ -103,17 +105,15 @@ public class VirtualMachineManagerImplTest { private PrimaryDataStoreDao storagePoolDaoMock; @Mock private VMInstanceVO vmInstanceMock; - private long vmInstanceVoMockId = 1L; - + private final long vmInstanceVoMockId = 1L; @Mock private ServiceOfferingVO serviceOfferingMock; - @Mock private DiskOfferingVO diskOfferingMock; - private long hostMockId = 1L; - private long clusterMockId = 2L; - private long zoneMockId = 3L; + private final long hostMockId = 1L; + private final long clusterMockId = 2L; + private final long zoneMockId = 3L; @Mock private HostVO hostMock; @Mock @@ -123,12 +123,11 @@ public class VirtualMachineManagerImplTest { private VirtualMachineProfile virtualMachineProfileMock; @Mock private StoragePoolVO storagePoolVoMock; - private long storagePoolVoMockId = 11L; - private long storagePoolVoMockClusterId = 234L; + private final long storagePoolVoMockId = 11L; @Mock private VolumeVO volumeVoMock; - private long volumeMockId = 1111L; + private final long volumeMockId = 1111L; @Mock private StoragePoolHostDao storagePoolHostDaoMock; @@ -151,6 +150,10 @@ public class VirtualMachineManagerImplTest { private UserVmDao userVmDaoMock; @Mock private UserVmVO userVmMock; + @Mock + private VMInstanceDao vmDaoMock; + @Mock + private ResourceLimitService resourceLimitServiceMock; @Before public void setup() { @@ -167,6 +170,8 @@ public void setup() { when(userVmJoinDaoMock.searchByIds(any())).thenReturn(new ArrayList<>()); when(userVmDaoMock.findById(any())).thenReturn(userVmMock); + when(vmDaoMock.findById(any())).thenReturn(vmInstanceMock); + when(resourceLimitServiceMock.getServiceOfferingByConfig()).thenReturn(serviceOfferingMock); Mockito.doReturn(vmInstanceVoMockId).when(virtualMachineProfileMock).getId(); @@ -211,7 +216,6 @@ public void testaddHostIpToCertDetailsIfConfigAllowsWhenConfigFalse() { ConfigKey testConfig = mock(ConfigKey.class); Long dataCenterId = 5L; - String hostIp = "1.1.1.1"; String routerIp = "2.2.2.2"; Map ipAddresses = new HashMap<>(); ipAddresses.put(NetworkElementCommand.ROUTER_IP, routerIp); @@ -228,7 +232,7 @@ public void testaddHostIpToCertDetailsIfConfigAllowsWhenConfigFalse() { @Test(expected = CloudRuntimeException.class) public void testScaleVM3() throws Exception { DeploymentPlanner.ExcludeList excludeHostList = new DeploymentPlanner.ExcludeList(); - virtualMachineManagerImpl.findHostAndMigrate(vmInstanceMock.getUuid(), 2l, null, excludeHostList); + virtualMachineManagerImpl.findHostAndMigrate(vmInstanceMock.getUuid(), 2L, null, excludeHostList); } @Test @@ -277,9 +281,9 @@ public void testSendStopWithNullAnswer() throws Exception { @Test public void testExeceuteInSequence() { - assertTrue(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.XenServer) == false); - assertTrue(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.KVM) == false); - assertTrue(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.Ovm3) == VirtualMachineManager.ExecuteInSequence.value()); + assertFalse(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.XenServer)); + assertFalse(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.KVM)); + assertEquals(virtualMachineManagerImpl.getExecuteInSequence(HypervisorType.Ovm3), VirtualMachineManager.ExecuteInSequence.value()); } private void overrideDefaultConfigValue(final ConfigKey configKey, final String value) throws IllegalAccessException, NoSuchFieldException { @@ -305,10 +309,10 @@ public void testExeceuteInSequenceVmware() throws IllegalAccessException, NoSuch } @Test - public void testCheckIfCanUpgrade() throws Exception { + public void testCheckIfCanUpgrade() { when(vmInstanceMock.getState()).thenReturn(State.Stopped); when(serviceOfferingMock.isDynamic()).thenReturn(true); - when(vmInstanceMock.getServiceOfferingId()).thenReturn(1l); + when(vmInstanceMock.getServiceOfferingId()).thenReturn(1L); ServiceOfferingVO mockCurrentServiceOffering = mock(ServiceOfferingVO.class); DiskOfferingVO mockCurrentDiskOffering = mock(DiskOfferingVO.class); @@ -753,7 +757,7 @@ private void prepareAndTestIsRootVolumeOnLocalStorage(ScopeType scope, boolean e mockedVolumes.add(volumeVoMock); Mockito.doReturn(mockedVolumes).when(volumeDaoMock).findByInstanceAndType(Mockito.anyLong(), Mockito.any()); - boolean result = virtualMachineManagerImpl.isRootVolumeOnLocalStorage(0l); + boolean result = virtualMachineManagerImpl.isRootVolumeOnLocalStorage(0L); assertEquals(expected, result); } @@ -838,4 +842,56 @@ public void checkIfTemplateNeededForCreatingVmVolumesTemplateAvailable() { Mockito.when(templateZoneDao.findByZoneTemplate(dcId, templateId)).thenReturn(Mockito.mock(VMTemplateZoneVO.class)); virtualMachineManagerImpl.checkIfTemplateNeededForCreatingVmVolumes(vm); } + + @Test + public void shouldIncrementVrResourceCountReturnWhenConfigResourceCountRunningVMsonlyIsEnabled() throws NoSuchFieldException, IllegalAccessException { + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + Account owner = Mockito.mock(Account.class); + boolean isDeployOrDestroy = true; + overrideDefaultConfigValue(ResourceCountRunningVMsonly, "true"); + + virtualMachineManagerImpl.incrementVrResourceCount(offering, owner, isDeployOrDestroy); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(0)).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(0)).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + } + + @Test + public void shouldIncrementVrResourceCountContinueWhenConfigResourceCountRunningVMsonlyIsDisabled() throws NoSuchFieldException, IllegalAccessException { + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + Account owner = Mockito.mock(Account.class); + boolean isDeployOrDestroy = true; + overrideDefaultConfigValue(ResourceCountRunningVMsonly, "false"); + Mockito.doReturn(Pair.of("", "")).when(virtualMachineManagerImpl).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.doNothing().when(virtualMachineManagerImpl).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + + virtualMachineManagerImpl.incrementVrResourceCount(offering, owner, isDeployOrDestroy); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(1)).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(1)).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + } + + @Test + public void shouldDecrementVrResourceCountReturnWhenConfigResourceCountRunningVMsonlyIsEnabled() throws NoSuchFieldException, IllegalAccessException { + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + Account owner = Mockito.mock(Account.class); + boolean isDeployOrDestroy = true; + overrideDefaultConfigValue(ResourceCountRunningVMsonly, "true"); + + virtualMachineManagerImpl.decrementVrResourceCount(offering, owner, isDeployOrDestroy); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(0)).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(0)).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + } + + @Test + public void shouldDecrementVrResourceCountContinueWhenConfigResourceCountRunningVMsonlyIsDisabled() throws NoSuchFieldException, IllegalAccessException { + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + Account owner = Mockito.mock(Account.class); + boolean isDeployOrDestroy = true; + overrideDefaultConfigValue(ResourceCountRunningVMsonly, "false"); + Mockito.doReturn(Pair.of("", "")).when(virtualMachineManagerImpl).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.doNothing().when(virtualMachineManagerImpl).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + + virtualMachineManagerImpl.incrementVrResourceCount(offering, owner, isDeployOrDestroy); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(1)).resolveCpuAndMemoryCount(any(), any(), any()); + Mockito.verify(virtualMachineManagerImpl, Mockito.times(1)).calculateResourceCount(any(), any(), Mockito.anyBoolean()); + } } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 9255f9275e94..2482f6ce60d9 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -46,6 +46,7 @@ import javax.naming.ConfigurationException; +import com.cloud.vm.VirtualMachineManager; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -293,6 +294,7 @@ import com.googlecode.ipv6.IPv6Address; import com.googlecode.ipv6.IPv6Network; +import static org.apache.commons.lang3.StringUtils.isBlank; public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable { public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class); @@ -458,6 +460,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati protected Set configValuesForValidation; private Set weightBasedParametersForValidation; private Set overprovisioningFactorsForValidation; + private Set resourceCountRoutersTypeValues; public static final String VM_USERDATA_MAX_LENGTH_STRING = "vm.userdata.max.length"; public static final ConfigKey SystemVMUseLocalStorage = new ConfigKey(Boolean.class, "system.vm.use.local.storage", "Advanced", "false", @@ -517,6 +520,7 @@ public boolean configure(final String name, final Map params) th populateConfigValuesForValidationSet(); weightBasedParametersForValidation(); overProvisioningFactorsForValidation(); + populateConfigValuesForRouterResourceCountValidation(); initMessageBusListener(); return true; } @@ -580,6 +584,12 @@ private void overProvisioningFactorsForValidation() { overprovisioningFactorsForValidation.add(CapacityManager.StorageOverprovisioningFactor.key()); } + private void populateConfigValuesForRouterResourceCountValidation() { + resourceCountRoutersTypeValues = new HashSet<>(); + resourceCountRoutersTypeValues.add(VirtualMachineManager.COUNT_ALL_VR_RESOURCES); + resourceCountRoutersTypeValues.add(VirtualMachineManager.COUNT_DELTA_VR_RESOURCES); + } + private void initMessageBusListener() { messageBus.subscribe(EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, new MessageSubscriber() { @Override @@ -644,12 +654,12 @@ public boolean start() { if (localCidrs != null && localCidrs.length > 0) { s_logger.warn("Management network CIDR is not configured originally. Set it default to " + localCidrs[0]); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), "Management network CIDR is not configured originally. Set it default to " + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, 0L, "Management network CIDR is not configured originally. Set it default to " + localCidrs[0], ""); _configDao.update(Config.ManagementNetwork.key(), Config.ManagementNetwork.getCategory(), localCidrs[0]); } else { s_logger.warn("Management network CIDR is not properly configured and we are not able to find a default setting"); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, 0L, "Management network CIDR is not properly configured and we are not able to find a default setting", ""); } } @@ -1176,6 +1186,8 @@ private String validateConfigurationValue(final String name, String value, final return errMsg; } + isValueForUpdateCfgCmdDeltaOrAll(name, value); + if (value == null) { if (type.equals(Boolean.class)) { return "Please enter either 'true' or 'false'."; @@ -1282,6 +1294,15 @@ private String validateConfigurationValue(final String name, String value, final return String.format("Invalid value for configuration [%s].", name); } + private void isValueForUpdateCfgCmdDeltaOrAll(String name, String value) { + if (VirtualMachineManager.ResourceCountRoutersType.key().equalsIgnoreCase(name) + && (!resourceCountRoutersTypeValues.contains(value) || isBlank(value))) { + final String msg = "Possible values are: delta or all."; + s_logger.error(msg); + throw new InvalidParameterValueException(msg); + } + } + /** * A valid value should be an integer between min and max (the values from the range). */ @@ -4928,7 +4949,7 @@ public VlanVO doInTransaction(final TransactionStatus status) { ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), usageHidden, ip.getClass().getName(), ip.getUuid()); } // increment resource count for dedicated public ip's - _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, Long.valueOf(ips.size())); } else if (domain != null && !forSystemVms) { // This VLAN is domain-wide, so create a DomainVlanMapVO entry final DomainVlanMapVO domainVlanMapVO = new DomainVlanMapVO(domain.getId(), vlan.getId()); @@ -5279,7 +5300,7 @@ public boolean deleteVlanAndPublicIpRange(final long userId, final long vlanDbId } finally { _vlanDao.releaseFromLockTable(vlanDbId); if (resourceCountToBeDecrement > 0) { //Making sure to decrement the count of only success operations above. For any reaason if disassociation fails then this number will vary from original range length. - _resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, new Long(resourceCountToBeDecrement)); + _resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, Long.valueOf(resourceCountToBeDecrement)); } } } else { // !isAccountSpecific @@ -5433,7 +5454,7 @@ public Vlan dedicatePublicIpRange(final DedicatePublicIpRangeCmd cmd) throws Res // increment resource count for dedicated public ip's if (vlanOwner != null) { - _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, Long.valueOf(ips.size())); } return vlan; @@ -5525,7 +5546,7 @@ public boolean releasePublicIpRange(final long vlanDbId, final long userId, fina } } // decrement resource count for dedicated public ip's - _resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, new Long(ips.size())); + _resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), ResourceType.public_ip, Long.valueOf(ips.size())); success = true; } else if (isDomainSpecific && _domainVlanMapDao.remove(domainVlan.get(0).getId())) { s_logger.debug("Remove the vlan from domain_vlan_map successfully."); @@ -5694,7 +5715,7 @@ public void checkPodCidrSubnets(final long dcId, final Long podIdToBeSkipped, fi final List newCidrPair = new ArrayList(); newCidrPair.add(0, getCidrAddress(cidr)); newCidrPair.add(1, (long)getCidrSize(cidr)); - currentPodCidrSubnets.put(new Long(-1), newCidrPair); + currentPodCidrSubnets.put(Long.valueOf(-1), newCidrPair); final DataCenterVO dcVo = _zoneDao.findById(dcId); final String guestNetworkCidr = dcVo.getGuestNetworkCidr(); @@ -5794,7 +5815,7 @@ private boolean validPod(final String podName, final long zoneId) { } private String getPodName(final long podId) { - return _podDao.findById(new Long(podId)).getName(); + return _podDao.findById(Long.valueOf(podId)).getName(); } private boolean validZone(final String zoneName) { @@ -5806,7 +5827,7 @@ private boolean validZone(final long zoneId) { } private String getZoneName(final long zoneId) { - final DataCenterVO zone = _zoneDao.findById(new Long(zoneId)); + final DataCenterVO zone = _zoneDao.findById(Long.valueOf(zoneId)); if (zone != null) { return zone.getName(); } else { diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java index 934336066ccf..950fddcd56c0 100644 --- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java @@ -264,6 +264,10 @@ public VirtualRouter destroyRouter(final long routerId, final Account caller, fi _accountMgr.checkAccess(caller, null, true, router); + final Account owner = _accountMgr.getAccount(router.getAccountId()); + ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(router.getServiceOfferingId()); + decrementVrResourceCount(owner, routerOffering); + _itMgr.expunge(router.getUuid()); _routerHealthCheckResultDao.expungeHealthChecks(router.getId()); _routerDao.remove(router.getId()); @@ -492,7 +496,7 @@ protected String retrieveTemplateName(final HypervisorType hType, final long dat @Override public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean startRouter) - throws InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, ResourceUnavailableException { + throws InsufficientCapacityException, ResourceUnavailableException { final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId()); final Account owner = routerDeploymentDefinition.getOwner(); @@ -502,6 +506,8 @@ public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploy // failed both times, throw the exception up final List hypervisors = getHypervisors(routerDeploymentDefinition); + incrementVrResourceCount(owner, routerOffering); + int allocateRetry = 0; int startRetry = 0; DomainRouterVO router = null; @@ -551,6 +557,10 @@ public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploy s_logger.debug("Failed to allocate the VR with hypervisor type " + hType + ", retrying one more time"); continue; } else { + // If VR can't be deployed then decrement the resource count + if (VirtualMachineManager.ResourceCountRouters.valueIn(owner.getDomainId())) { + _itMgr.decrementVrResourceCount(routerOffering, owner,true); + } throw ex; } } finally { @@ -566,8 +576,8 @@ public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploy s_logger.debug("Failed to start the VR " + router + " with hypervisor type " + hType + ", " + "destroying it and recreating one more time"); // destroy the router destroyRouter(router.getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM); - continue; } else { + decrementVrResourceCount(owner, routerOffering); throw ex; } } finally { @@ -582,6 +592,32 @@ public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploy return router; } + /** + * If VR can't be deployed then decrement the resource count + * + * @param owner the owner of the VR + * @param routerOffering the service offering + */ + public void decrementVrResourceCount(Account owner, ServiceOfferingVO routerOffering) { + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRouters.valueIn(owner.getDomainId()))) { + _itMgr.decrementVrResourceCount(routerOffering, owner,true); + } + } // shouldDecrementIfTheDomainExists + + /** + * Increment the resource count with router offering. + * If router can't be deployed or started, decrement the resources. + * If resource.count.running.vms is false, increment resource count. + * + * @param owner the owner of the VR + * @param routerOffering the service offering + */ + public void incrementVrResourceCount(Account owner, ServiceOfferingVO routerOffering) { + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRouters.valueIn(owner.getDomainId()))) { + _itMgr.incrementVrResourceCount(routerOffering, owner, true); + } + } + protected void filterSupportedHypervisors(final List hypervisors) { // For non vpc we keep them all assuming all types in the list are // supported diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 9efeed39ac07..57b91eba8071 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -29,6 +29,12 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.offering.ServiceOffering; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -86,6 +92,7 @@ import com.cloud.user.AccountVO; import com.cloud.user.ResourceLimitService; import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; @@ -113,6 +120,7 @@ @Component public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLimitService, Configurable { public static final Logger s_logger = Logger.getLogger(ResourceLimitManagerImpl.class); + public static final String OFFERINGS_NAME = "offerings"; @Inject private AccountManager _accountMgr; @@ -163,15 +171,18 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Inject private VlanDao _vlanDao; + @Inject + private ServiceOfferingDao serviceOfferingDao; + protected GenericSearchBuilder templateSizeSearch; protected GenericSearchBuilder snapshotSizeSearch; protected SearchBuilder ResourceCountSearch; ScheduledExecutorService _rcExecutor; long _resourceCountCheckInterval = 0; - Map accountResourceLimitMap = new EnumMap(ResourceType.class); - Map domainResourceLimitMap = new EnumMap(ResourceType.class); - Map projectResourceLimitMap = new EnumMap(ResourceType.class); + Map accountResourceLimitMap = new EnumMap<>(ResourceType.class); + Map domainResourceLimitMap = new EnumMap<>(ResourceType.class); + Map projectResourceLimitMap = new EnumMap<>(ResourceType.class); @Override public boolean start() { @@ -270,7 +281,7 @@ public void incrementResourceCount(long accountId, ResourceType type, Long... de return; } - long numToIncrement = (delta.length == 0) ? 1 : delta[0].longValue(); + long numToIncrement = (delta.length == 0) ? 1 : delta[0]; if (!updateResourceCountForAccount(accountId, type, true, numToIncrement)) { // we should fail the operation (resource creation) when failed to update the resource count @@ -285,7 +296,7 @@ public void decrementResourceCount(long accountId, ResourceType type, Long... de s_logger.trace("Not decrementing resource count for system accounts, returning"); return; } - long numToDecrement = (delta.length == 0) ? 1 : delta[0].longValue(); + long numToDecrement = (delta.length == 0) ? 1 : delta[0]; if (!updateResourceCountForAccount(accountId, type, false, numToDecrement)) { _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, "Failed to decrement resource count of type " + type + " for account id=" + accountId, @@ -307,10 +318,10 @@ public long findCorrectResourceLimitForAccount(Account account, ResourceType typ // Check if limit is configured for account if (limit != null) { - max = limit.getMax().longValue(); + max = limit.getMax(); } else { // If the account has an no limit set, then return global default account limits - Long value = null; + Long value; if (account.getType() == Account.Type.PROJECT) { value = projectResourceLimitMap.get(type); } else { @@ -348,10 +359,10 @@ public long findCorrectResourceLimitForAccount(long accountId, Long limit, Resou // Check if limit is configured for account if (limit != null) { - max = limit.longValue(); + max = limit; } else { // If the account has an no limit set, then return global default account limits - Long value = null; + Long value; if (account.getType() == Account.Type.PROJECT) { value = projectResourceLimitMap.get(type); } else { @@ -383,7 +394,7 @@ public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type) ResourceLimitVO limit = _resourceLimitDao.findByOwnerIdAndType(domain.getId(), ResourceOwnerType.Domain, type); if (limit != null) { - max = limit.getMax().longValue(); + max = limit.getMax(); } else { // check domain hierarchy Long domainId = domain.getParent(); @@ -397,9 +408,9 @@ public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type) } if (limit != null) { - max = limit.getMax().longValue(); + max = limit.getMax(); } else { - Long value = null; + Long value; value = domainResourceLimitMap.get(type); if (value != null) { if (value < 0) { // return unlimit if value is set to negative @@ -418,7 +429,7 @@ public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type) private void checkDomainResourceLimit(final Account account, final Project project, final ResourceType type, long numResources) throws ResourceAllocationException { // check all domains in the account's domain hierarchy - Long domainId = null; + Long domainId; if (project != null) { domainId = project.getDomainId(); } else { @@ -472,8 +483,7 @@ private void checkAccountResourceLimit(final Account account, final Project proj // Check account limits long accountResourceLimit = findCorrectResourceLimitForAccount(account, type); long currentResourceCount = _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type); - long currentResourceReservation = reservationDao.getAccountReservation(account.getId(), type); - long requestedResourceCount = currentResourceCount + currentResourceReservation + numResources; + long requestedResourceCount = currentResourceCount + numResources; String convertedAccountResourceLimit = String.valueOf(accountResourceLimit); String convertedCurrentResourceCount = String.valueOf(currentResourceCount); @@ -522,14 +532,14 @@ private List lockDomainRows(long domainId, final ResourceType t @Override public long findDefaultResourceLimitForDomain(ResourceType resourceType) { - Long resourceLimit = null; + Long resourceLimit; resourceLimit = domainResourceLimitMap.get(resourceType); if (resourceLimit != null && (resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage)) { if (! Long.valueOf(Resource.RESOURCE_UNLIMITED).equals(resourceLimit)) { resourceLimit = resourceLimit * ResourceType.bytesToGiB; } } else { - resourceLimit = Long.valueOf(Resource.RESOURCE_UNLIMITED); + resourceLimit = (long) Resource.RESOURCE_UNLIMITED; } return resourceLimit; } @@ -566,8 +576,8 @@ public void doInTransactionWithoutResult(TransactionStatus status) throws Resour @Override public List searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, Long startIndex, Long pageSizeVal) { Account caller = CallContext.current().getCallingAccount(); - List limits = new ArrayList(); - boolean isAccount = true; + List limits = new ArrayList<>(); + boolean isAccount; if (!_accountMgr.isAdmin(caller.getId())) { accountId = caller.getId(); @@ -662,8 +672,8 @@ public List searchForLimits(Long id, Long accountId, Long domai // see if any limits are missing from the table, and if yes - get it from the config table and add ResourceType[] resourceTypes = ResourceCount.ResourceType.values(); if (foundLimits.size() != resourceTypes.length) { - List accountLimitStr = new ArrayList(); - List domainLimitStr = new ArrayList(); + List accountLimitStr = new ArrayList<>(); + List domainLimitStr = new ArrayList<>(); for (ResourceLimitVO foundLimit : foundLimits) { if (foundLimit.getAccountId() != null) { accountLimitStr.add(foundLimit.getType().toString()); @@ -702,7 +712,7 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege Account caller = CallContext.current().getCallingAccount(); if (max == null) { - max = new Long(Resource.RESOURCE_UNLIMITED); + max = Long.valueOf(Resource.RESOURCE_UNLIMITED); } else if (max.longValue() < Resource.RESOURCE_UNLIMITED) { throw new InvalidParameterValueException("Please specify either '-1' for an infinite limit, or a limit that is at least '0'."); } @@ -711,7 +721,7 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege ResourceType resourceType = null; if (typeId != null) { for (ResourceType type : Resource.ResourceType.values()) { - if (type.getOrdinal() == typeId.intValue()) { + if (type.getOrdinal() == typeId) { resourceType = type; } } @@ -742,7 +752,7 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege throw new InvalidParameterValueException("Only " + Resource.RESOURCE_UNLIMITED + " limit is supported for Root Admin accounts"); } - if ((caller.getAccountId() == accountId.longValue()) && (_accountMgr.isDomainAdmin(caller.getId()) || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN)) { + if ((caller.getAccountId() == accountId) && (_accountMgr.isDomainAdmin(caller.getId()) || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN)) { // If the admin is trying to update their own account, disallow. throw new PermissionDeniedException("Unable to update resource limit for their own account " + accountId + ", permission denied"); } @@ -760,12 +770,12 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege _accountMgr.checkAccess(caller, domain); - if (Domain.ROOT_DOMAIN == domainId.longValue()) { + if (Domain.ROOT_DOMAIN == domainId) { // no one can add limits on ROOT domain, disallow... throw new PermissionDeniedException("Cannot update resource limit for ROOT domain " + domainId + ", permission denied"); } - if ((caller.getDomainId() == domainId.longValue()) && caller.getType() == Account.Type.DOMAIN_ADMIN || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN) { + if ((caller.getDomainId() == domainId) && caller.getType() == Account.Type.DOMAIN_ADMIN || caller.getType() == Account.Type.RESOURCE_DOMAIN_ADMIN) { // if the admin is trying to update their own domain, disallow... throw new PermissionDeniedException("Unable to update resource limit for domain " + domainId + ", permission denied"); } @@ -773,7 +783,7 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege if (parentDomainId != null) { DomainVO parentDomain = _domainDao.findById(parentDomainId); long parentMaximum = findCorrectResourceLimitForDomain(parentDomain, resourceType); - if ((parentMaximum >= 0) && (max.longValue() > parentMaximum)) { + if ((parentMaximum >= 0) && (max > parentMaximum)) { throw new InvalidParameterValueException("Domain " + domain.getName() + "(id: " + parentDomain.getId() + ") has maximum allowed resource limit " + parentMaximum + " for " + resourceType + ", please specify a value less that or equal to " + parentMaximum); } @@ -797,17 +807,17 @@ public ResourceLimitVO updateResourceLimit(Long accountId, Long domainId, Intege } @Override - public List recalculateResourceCount(Long accountId, Long domainId, Integer typeId) throws InvalidParameterValueException, CloudRuntimeException, PermissionDeniedException { + public List recalculateResourceCount(Long accountId, Long domainId, Integer typeId) throws CloudRuntimeException { Account callerAccount = CallContext.current().getCallingAccount(); long count = 0; - List counts = new ArrayList(); - List resourceTypes = new ArrayList(); + List counts = new ArrayList<>(); + List resourceTypes = new ArrayList<>(); ResourceType resourceType = null; if (typeId != null) { for (ResourceType type : Resource.ResourceType.values()) { - if (type.getOrdinal() == typeId.intValue()) { + if (type.getOrdinal() == typeId) { resourceType = type; } } @@ -989,7 +999,8 @@ public void doInTransactionWithoutResult(TransactionStatus status) { } public long countCpusForAccount(long accountId) { - long cputotal = 0; + long cputotal = 0L; + // user vms SearchBuilder userVmSearch = _userVmJoinDao.createSearchBuilder(); userVmSearch.and("accountId", userVmSearch.entity().getAccountId(), Op.EQ); @@ -1000,20 +1011,74 @@ public long countCpusForAccount(long accountId) { SearchCriteria sc1 = userVmSearch.create(); sc1.setParameters("accountId", accountId); - if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) - sc1.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging, State.Stopped}); - else - sc1.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + sc1.setParameters("state", State.Destroyed, State.Error, State.Expunging, State.Stopped); + } else { + sc1.setParameters("state", State.Destroyed, State.Error, State.Expunging); + } sc1.setParameters("displayVm", 1); List userVms = _userVmJoinDao.search(sc1,null); for (UserVmJoinVO vm : userVms) { - cputotal += Long.valueOf(vm.getCpu()); + cputotal += vm.getCpu(); + } + + final Pair totalByAccountId = calculateCpuTotalByAccountId(accountId); + if (totalByAccountId.first()) { + return totalByAccountId.second(); + } else { + return cputotal; } - return cputotal; + } + + private Pair calculateCpuTotalByAccountId(long accountId) { + long cputotal = 0; + boolean hasCpus = false; + final Account owner = _accountDao.findById(accountId); + final DomainVO domain = _domainDao.findById(owner.getDomainId()); + final ServiceOffering defaultRouterOffering = getServiceOfferingByConfig(); + + GenericSearchBuilder cpuSearch = serviceOfferingDao.createSearchBuilder(SumCount.class); + cpuSearch.select("sum", Func.SUM, cpuSearch.entity().getCpu()); + cpuSearch.select("count", Func.COUNT, (Object[])null); + SearchBuilder join1 = _vmDao.createSearchBuilder(); + join1.and("accountId", join1.entity().getAccountId(), Op.EQ); + join1.and("type", join1.entity().getType(), Op.IN); + join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + join1.and("displayVm", join1.entity().isDisplayVm(), Op.EQ); + cpuSearch.join(OFFERINGS_NAME, join1, cpuSearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); + cpuSearch.done(); + + SearchCriteria sc = cpuSearch.create(); + sc.setJoinParameters(OFFERINGS_NAME, "accountId", accountId); + sc.setJoinParameters(OFFERINGS_NAME, "type", VirtualMachine.Type.DomainRouter); // domain routers + + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRunningVMsonly.value())) { + sc.setJoinParameters(OFFERINGS_NAME, "state", State.Destroyed, State.Error, State.Expunging, State.Stopped); + } else { + sc.setJoinParameters(OFFERINGS_NAME, "state", State.Destroyed, State.Error, State.Expunging); + } + sc.setJoinParameters(OFFERINGS_NAME, "displayVm", 1); + List cpus = serviceOfferingDao.customSearch(sc, null); + if (cpus != null) { + hasCpus = true; + // Calculate the VR CPU resource count depending on the global setting + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRouters.valueIn(domain.getId()))) { + cputotal += cpus.get(0).sum; + + // Get the diff of current router offering with default offering + if (VirtualMachineManager.ResourceCountRoutersType.valueIn(domain.getId()) + .equalsIgnoreCase(VirtualMachineManager.COUNT_DELTA_VR_RESOURCES)) { + cputotal = cputotal - cpus.get(0).count * defaultRouterOffering.getCpu(); + } + } + } + + return Pair.of(hasCpus, cputotal); } public long calculateMemoryForAccount(long accountId) { - long ramtotal = 0; + long ramtotal = 0L; + // user vms SearchBuilder userVmSearch = _userVmJoinDao.createSearchBuilder(); userVmSearch.and("accountId", userVmSearch.entity().getAccountId(), Op.EQ); @@ -1024,16 +1089,68 @@ public long calculateMemoryForAccount(long accountId) { SearchCriteria sc1 = userVmSearch.create(); sc1.setParameters("accountId", accountId); - if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) - sc1.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging, State.Stopped}); - else - sc1.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRunningVMsonly.value())) { + sc1.setParameters("state", State.Destroyed, State.Error, State.Expunging, State.Stopped); + } else { + sc1.setParameters("state", State.Destroyed, State.Error, State.Expunging); + } sc1.setParameters("displayVm", 1); List userVms = _userVmJoinDao.search(sc1,null); for (UserVmJoinVO vm : userVms) { - ramtotal += Long.valueOf(vm.getRamSize()); + ramtotal += vm.getRamSize(); + } + + final Pair totalByAccountId = calculateMemoryTotalByAccountId(accountId); + if (Boolean.TRUE.equals(totalByAccountId.first())) { + return totalByAccountId.second(); + } else { + return ramtotal; } - return ramtotal; + } + + public Pair calculateMemoryTotalByAccountId(long accountId) { + long ramtotal = 0; + boolean hasMemory = false; + final Account owner = _accountDao.findById(accountId); + final DomainVO domain = _domainDao.findById(owner.getDomainId()); + final ServiceOffering defaultRouterOffering = getServiceOfferingByConfig(); + + GenericSearchBuilder memorySearch = serviceOfferingDao.createSearchBuilder(SumCount.class); + memorySearch.select("sum", Func.SUM, memorySearch.entity().getRamSize()); + memorySearch.select("count", Func.COUNT, null); + SearchBuilder join1 = _vmDao.createSearchBuilder(); + join1.and("accountId", join1.entity().getAccountId(), Op.EQ); + join1.and("type", join1.entity().getType(), Op.IN); + join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + join1.and("displayVm", join1.entity().isDisplayVm(), Op.EQ); + memorySearch.join(OFFERINGS_NAME, join1, memorySearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); + memorySearch.done(); + + SearchCriteria sc = memorySearch.create(); + sc.setJoinParameters(OFFERINGS_NAME, "accountId", accountId); + sc.setJoinParameters(OFFERINGS_NAME, "type", VirtualMachine.Type.DomainRouter); // domain routers + sc.setJoinParameters(OFFERINGS_NAME, "displayVm", 1); + + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRunningVMsonly.value())) { + sc.setJoinParameters(OFFERINGS_NAME, "state", State.Destroyed, State.Error, State.Expunging, State.Stopped); + } else { + sc.setJoinParameters(OFFERINGS_NAME, "state", State.Destroyed, State.Error, State.Expunging); + } + sc.setJoinParameters(OFFERINGS_NAME, "displayVm", 1); + List memory = serviceOfferingDao.customSearch(sc, null); + if (memory != null) { + // Calculate VR memory count depending on global setting + if (Boolean.TRUE.equals(VirtualMachineManager.ResourceCountRouters.valueIn(domain.getId()))) { + ramtotal += memory.get(0).sum; + if (VirtualMachineManager.ResourceCountRoutersType.valueIn(domain.getId()) + .equalsIgnoreCase(VirtualMachineManager.COUNT_DELTA_VR_RESOURCES)) { + ramtotal = ramtotal - memory.get(0).count * defaultRouterOffering.getRamSize(); + } + } + + hasMemory = true; + } + return Pair.of(hasMemory, ramtotal); } public long calculateSecondaryStorageForAccount(long accountId) { @@ -1068,7 +1185,7 @@ private long calculatePublicIpForAccount(long accountId) { List dedicatedVlans = _vlanDao.listDedicatedVlans(accountId); for (VlanVO dedicatedVlan : dedicatedVlans) { List ips = _ipAddressDao.listByVlanId(dedicatedVlan.getId()); - dedicatedCount += new Long(ips.size()); + dedicatedCount += Long.valueOf(ips.size()); } allocatedCount = _ipAddressDao.countAllocatedIPsForAccount(accountId); if (dedicatedCount > allocatedCount) { @@ -1114,6 +1231,26 @@ public void decrementResourceCount(long accountId, ResourceType type, Boolean di } } + /** + * Returns the service offering by the given configuration. + * + * @return the service offering found or null if not found + */ + public ServiceOffering getServiceOfferingByConfig() { + ServiceOffering defaultRouterOffering = null; + final String globalRouterOffering = VirtualNetworkApplianceManager.VirtualRouterServiceOffering.value(); + + if (globalRouterOffering != null) { + defaultRouterOffering = serviceOfferingDao.findByUuid(globalRouterOffering); + } + + if (defaultRouterOffering == null) { + defaultRouterOffering = serviceOfferingDao.findByName(ServiceOffering.routerDefaultOffUniqueName); + } + + return defaultRouterOffering; + } + @Override public void changeResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) { diff --git a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java index 4267b71d24fa..27f1397e88f1 100644 --- a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java +++ b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java @@ -26,12 +26,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.user.Account; +import com.cloud.vm.VirtualMachineManager; +import org.apache.cloudstack.framework.config.ConfigKey; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.Mockito; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -40,7 +44,9 @@ import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; +import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; @RunWith(MockitoJUnitRunner.class) public class NetworkHelperImplTest { @@ -53,9 +59,12 @@ public class NetworkHelperImplTest { @InjectMocks protected NetworkHelperImpl nwHelper = new NetworkHelperImpl(); + @Mock + protected VirtualMachineManager _itMgr; + @Test(expected=ResourceUnavailableException.class) public void testSendCommandsToRouterWrongRouterVersion() - throws AgentUnavailableException, OperationTimedoutException, ResourceUnavailableException { + throws OperationTimedoutException, ResourceUnavailableException { // Prepare NetworkHelperImpl nwHelperUT = spy(this.nwHelper); VirtualRouter vr = mock(VirtualRouter.class); @@ -70,7 +79,7 @@ public void testSendCommandsToRouterWrongRouterVersion() @Test public void testSendCommandsToRouter() - throws AgentUnavailableException, OperationTimedoutException, ResourceUnavailableException { + throws OperationTimedoutException, ResourceUnavailableException { // Prepare NetworkHelperImpl nwHelperUT = spy(this.nwHelper); VirtualRouter vr = mock(VirtualRouter.class); @@ -108,7 +117,7 @@ public void testSendCommandsToRouter() */ @Test public void testSendCommandsToRouterWithTrueResult() - throws AgentUnavailableException, OperationTimedoutException, ResourceUnavailableException { + throws OperationTimedoutException, ResourceUnavailableException { // Prepare NetworkHelperImpl nwHelperUT = spy(this.nwHelper); VirtualRouter vr = mock(VirtualRouter.class); @@ -146,7 +155,7 @@ public void testSendCommandsToRouterWithTrueResult() */ @Test public void testSendCommandsToRouterWithNoAnswers() - throws AgentUnavailableException, OperationTimedoutException, ResourceUnavailableException { + throws OperationTimedoutException, ResourceUnavailableException { // Prepare NetworkHelperImpl nwHelperUT = spy(this.nwHelper); VirtualRouter vr = mock(VirtualRouter.class); @@ -170,4 +179,53 @@ public void testSendCommandsToRouterWithNoAnswers() assertFalse(result); } + @Test + public void shouldIncrementIfTheDomainExists() throws NoSuchFieldException, IllegalAccessException { + Account owner = Mockito.mock(Account.class); + ServiceOfferingVO offering = mock(ServiceOfferingVO.class); + when(owner.getDomainId()).thenReturn(1L); + + overrideDefaultConfigValue(VirtualMachineManager.ResourceCountRouters, "true"); + nwHelper.incrementVrResourceCount(owner, offering); + verify(_itMgr, times(1)).incrementVrResourceCount(offering, owner, true); + } + + @Test + public void shouldDoNothingIfTheDomainDoNotExistsForIncrementVrResourceCount() throws NoSuchFieldException, IllegalAccessException { + Account owner = Mockito.mock(Account.class); + ServiceOfferingVO offering = mock(ServiceOfferingVO.class); + when(owner.getDomainId()).thenReturn(999L); + + overrideDefaultConfigValue(VirtualMachineManager.ResourceCountRouters, "false"); + nwHelper.incrementVrResourceCount(owner, offering); + verify(_itMgr, times(0)).incrementVrResourceCount(offering, owner, true); + } + + @Test + public void shouldDecrementIfTheDomainExists() throws NoSuchFieldException, IllegalAccessException { + Account owner = Mockito.mock(Account.class); + ServiceOfferingVO offering = mock(ServiceOfferingVO.class); + when(owner.getDomainId()).thenReturn(1L); + + overrideDefaultConfigValue(VirtualMachineManager.ResourceCountRouters, "true"); + nwHelper.decrementVrResourceCount(owner, offering); + verify(_itMgr, times(1)).decrementVrResourceCount(offering, owner, true); + } + + @Test + public void shouldDoNothingIfTheDomainDoNotExistsForDecrementVrResourceCount() throws NoSuchFieldException, IllegalAccessException { + Account owner = Mockito.mock(Account.class); + ServiceOfferingVO offering = mock(ServiceOfferingVO.class); + when(owner.getDomainId()).thenReturn(999L); + + overrideDefaultConfigValue(VirtualMachineManager.ResourceCountRouters, "false"); + nwHelper.decrementVrResourceCount(owner, offering); + verify(_itMgr, times(0)).decrementVrResourceCount(offering, owner, true); + } + + private void overrideDefaultConfigValue(final ConfigKey configKey, final String value) throws IllegalAccessException, NoSuchFieldException { + final Field f = ConfigKey.class.getDeclaredField("_defaultValue"); + f.setAccessible(true); + f.set(configKey, value); + } } diff --git a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java index 1d1da5331d92..fde106823b15 100644 --- a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -21,6 +21,7 @@ import javax.naming.ConfigurationException; +import com.cloud.offering.ServiceOffering; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.user.ResourceReservation; import org.springframework.stereotype.Component; @@ -178,6 +179,11 @@ public ResourceReservation getReservation(Account account, Boolean displayResour throw new CloudRuntimeException("no reservation implemented for mock resource management."); } + @Override + public ServiceOffering getServiceOfferingByConfig() { + return null; + } + /* (non-Javadoc) * @see com.cloud.utils.component.Manager#configure(java.lang.String, java.util.Map) */ @@ -213,5 +219,4 @@ public String getName() { // TODO Auto-generated method stub return null; } - } diff --git a/test/integration/component/test_router_resources.py b/test/integration/component/test_router_resources.py new file mode 100644 index 000000000000..35d906767fd8 --- /dev/null +++ b/test/integration/component/test_router_resources.py @@ -0,0 +1,826 @@ +# 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. + +""" +Test case for router resources +""" + +# Import local modules + +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (scaleSystemVm, + stopRouter, + startRouter, + restartNetwork, + updateConfiguration) +from marvin.lib.utils import (cleanup_resources) +from marvin.lib.base import (NetworkOffering, + ServiceOffering, + VirtualMachine, + Account, + Domain, + Network, + Router, + destroyRouter, + Zone, + updateResourceCount) +from marvin.lib.common import (get_zone, + get_template, + get_domain, + list_virtual_machines, + list_networks, + list_configurations, + list_routers, + list_service_offering) + +import logging + +class TestRouterResources(cloudstackTestCase): + + @classmethod + def setupClass(cls): + cls.testClient = super( + TestRouterResources, cls + ).getClsTestClient() + cls.apiclient = cls.testClient.getApiClient() + + cls.services = cls.testClient.getParsedTestDataConfig() + zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) + cls.zone = Zone(zone.__dict__) + cls.template = get_template(cls.apiclient, cls.zone.id) + cls._cleanup = [] + + cls.logger = logging.getLogger("TestRouterResources") + cls.stream_handler = logging.StreamHandler() + cls.logger.setLevel(logging.DEBUG) + cls.logger.addHandler(cls.stream_handler) + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.apiclient) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"] + ) + + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["big"] + ) + + cls._cleanup.append(cls.service_offering) + + # Create new domain1 + cls.domain1 = Domain.create( + cls.apiclient, + services=cls.services["acl"]["domain1"], + parentdomainid=cls.domain.id + ) + + cls._cleanup.append(cls.domain1) + + # Create account1 + cls.account1 = Account.create( + cls.apiclient, + cls.services["acl"]["accountD1"], + domainid=cls.domain1.id + ) + + cls._cleanup.append(cls.account1) + + # Create Network Offering with all the services + cls.network_offering = NetworkOffering.create( + cls.apiclient, + cls.services["isolated_network_offering"] + ) + + cls._cleanup.append(cls.network_offering) + + # Enable Network offering + cls.network_offering.update(cls.apiclient, state='Enabled') + + cls.network = Network.create( + cls.apiclient, + cls.services["isolated_network"], + accountid=cls.account1.name, + domainid=cls.account1.domainid, + networkofferingid=cls.network_offering.id, + zoneid=cls.zone.id + ) + + cls._cleanup.append(cls.network) + + virtualmachine = VirtualMachine.create( + cls.apiclient, + services=cls.services["virtual_machine_userdata"], + accountid=cls.account1.name, + domainid=cls.account1.domainid, + serviceofferingid=cls.service_offering.id, + networkids=cls.network.id, + templateid=cls.template.id, + zoneid=cls.zone.id + ) + + cls._cleanup.append(virtualmachine) + + vms = list_virtual_machines( + cls.apiclient, + account=cls.account1.name, + domainid=cls.account1.domainid, + id=virtualmachine.id + ) + vm = vms[0] + + # get vm cpu and memory values + cls.vm_cpu_count = vm.cpunumber + cls.vm_mem_count = vm.memory + + routers = list_routers( + cls.apiclient, + account=cls.account1.name, + domainid=cls.account1.domainid + ) + + router = routers[0] + + router_so_id = router.serviceofferingid + + list_service_response = list_service_offering( + cls.apiclient, + id=router_so_id, + issystem="true", + systemvmtype="domainrouter", + listall="true" + ) + + # Get default router service offering cpu and memory values + cls.default_vr_cpu = list_service_response[0].cpunumber + cls.default_vr_ram = list_service_response[0].memory + + # Disable Network offering + cls.network_offering.update(cls.apiclient, state='Disabled') + + @classmethod + def tearDownClass(cls): + super(TestRouterResources, cls).tearDownClass() + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + return + + def tearDown(self): + super(TestRouterResources, self).tearDown() + + def get_resource_amount(self, resource_type): + """ + Function to update resource count of a domain + for the corresponding resource_type passed as parameter + :param resource_type: + :return: resource count + """ + cmd = updateResourceCount.updateResourceCountCmd() + cmd.account = self.account1.name + cmd.domainid = self.domain1.id + cmd.resourcetype = resource_type + response = self.apiclient.updateResourceCount(cmd) + amount = response[0].resourcecount + return amount + + def update_configuration(self, name, value, domainid): + """ + Function to update the global setting value for the domain + :param name: + :param value: + :param domainid: + :return: + """ + updateConfigurationCmd = updateConfiguration.updateConfigurationCmd() + updateConfigurationCmd.name = name + updateConfigurationCmd.value = value + updateConfigurationCmd.domainid = domainid + return self.apiclient.updateConfiguration(updateConfigurationCmd) + + def get_vr_service_offering(self): + """ + Function to get Virtual Router service offering + :return: + """ + routers = list_routers( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid + ) + + router = routers[0] + + router_so_id = router.serviceofferingid + + list_service_response = list_service_offering( + self.apiclient, + id=router_so_id, + issystem="true", + listall="true" + ) + + return list_service_response + + def stop_router(self, routerid): + cmd = stopRouter.stopRouterCmd() + cmd.id = routerid + cmd.forced = "true" + self.apiclient.stopRouter(cmd) + + def destroy_router(self, routerid): + self.stop_router(routerid) + cmd = destroyRouter.destroyRouterCmd() + cmd.id = routerid + self.apiclient.destroyRouter(cmd) + + def restart_network(self): + cmd = restartNetwork.restartNetworkCmd() + cmd.id = self.network.id + cmd.cleanup = True + self.apiclient.restartNetwork(cmd) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_01_count_vm_resources(self): + """ + Test case to just count running vm resources with global setting set to false + + # Steps + 1. Vm is already created + 2. Get the resource count of the running vm + 3. Update the resource count for the domain/account + 4. Make sure that these two values matches + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + + self.info("Initial resource count of domain/account are") + self.info("cpu is %s and ram is %s" % (cores, ram)) + + self.assertEqual( + self.vm_cpu_count, + cores, + "VM CPU count doesnt not match" + ) + + self.assertEqual( + self.vm_mem_count, + ram, + "VM memory count does not match" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_02_count_vm_resources_with_all_vr_resource(self): + """ + Test vm resource count along with vr resource count when global + setting value is set to "all" + + # Steps + 1. Get the vm resource count from test case 1 + 2. Get the default service offering of VR + 3. Extract cpu and ram size from it + 4. Set the global setting "resource.count.routers" to true + 5. Set the value of "resource.count.routers.type" to "all" + 6. Update the resource count of domain/account + 7. Make sure that the cpu and ram count is equal to (vm + vr) + """ + + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 2 + list_service_offering_response = self.get_vr_service_offering() + + # Step 3 + vr_cpu = list_service_offering_response[0].cpunumber + vr_ram = list_service_offering_response[0].memory + + # Step 4 + self.update_configuration("resource.count.routers", + "true", self.domain1.id) + + # Step 5 + self.update_configuration("resource.count.routers.type", + "all", self.domain1.id) + + # Step 6 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count of domain/account are") + self.info("cpu is %s and ram is %s" % (cores, ram)) + + # Step 7 + self.assertEqual( + cores, + self.vm_cpu_count + vr_cpu, + "Total resource count for cpu does not match VM + VR cpu count" + ) + + self.assertEqual( + ram, + self.vm_mem_count + vr_ram, + "Total resource count for memory does not match VM + VR memory" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_03_count_vm_resources_with_delta_vr_resource(self): + """ + Test vm resource count along with vr resource count when global setting + value is set to "delta" + + # Steps + 1. Get the current service offering of VR + 2. Extract cpu and ram size from it + 3. Set the global setting "resource.count.routers" to true + 4. Set the value of "resource.count.routers.type" to "delta" + 5. Update the resource count of domain/account + 6. Make sure that the cpu and ram count is equal to + VM + (current router offering - default router offering) + """ + + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 and 2 + list_service_offering_response = self.get_vr_service_offering() + new_vr_cpu = list_service_offering_response[0].cpunumber + new_vr_ram = list_service_offering_response[0].memory + + # Step 3 + self.update_configuration("resource.count.routers", + "true", self.domain1.id) + + # Step 4 + self.update_configuration("resource.count.routers.type", + "delta", self.domain1.id) + + # Step 5 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores, ram)) + + # Step 6 + self.assertEqual( + cores, + self.vm_cpu_count + (new_vr_cpu - self.default_vr_cpu), + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + (new_vr_ram - self.default_vr_ram), + "Total resource count of memory does not match delta for vr memory" + ) + + def test_04_count_vm_resource_with_new_vr_offering_all(self): + """ + Test to count vm resources along with new vr service offering with + global setting set to "all" + + Steps + 1. Create a new router service offering with 2 cores and 2Gb Ram + 2. Stop the router + 3. Update the service offering of the router with new offering + 4. Start the router + 5. Set the global setting value to "all" + 6. Update the resource count of domain/account + 7. Get the new cpu/ram count of VR + 8. Make sure that the resource count is equal to VM + new VR service offering + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + offering_data = { + 'displaytext': 'TestOffering', + 'cpuspeed': 1000, + 'cpunumber': 2, + 'name': 'TestOffering', + 'memory': 2048, + 'issystem': 'true', + 'systemvmtype': 'domainrouter' + } + network_offering = self.new_network_offering = ServiceOffering.create( + self.apiclient, + offering_data, + domainid=self.domain1.id + ) + self.cleanup.append(network_offering) + + routers = list_routers( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid + ) + + router = routers[0] + + # Step 2 + # Stop the router + self.stop_router(router.id) + + # Step 3 + scale_systemvm_cmd = scaleSystemVm.scaleSystemVmCmd() + scale_systemvm_cmd.id=router.id + scale_systemvm_cmd.serviceofferingid = self.new_network_offering.id + self.apiclient.scaleSystemVm(scale_systemvm_cmd) + + # Step 4 + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + # Step 5 + self.update_configuration("resource.count.routers.type", + "all", self.domain1.id) + + # Step 6 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores,ram)) + + # Step 7 + list_service_offering_response = self.get_vr_service_offering() + updated_vr_cpu = list_service_offering_response[0].cpunumber + updated_vr_ram = list_service_offering_response[0].memory + + # Step 8 + self.assertEqual( + cores, + self.vm_cpu_count + updated_vr_cpu, + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + updated_vr_ram, + "Total resource count of memory does not match delta for vr memory" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_05_count_vm_resource_with_delta_vr_count(self): + """ + Test to count vm resources along with new vr service offering with + global setting set to "delta" + + Steps + 1. Set the global setting "resource.count.routers.type" value to "delta" + 2. Update the resource count of domain/account + 3. Get the new cpu/ram count of VR + 4. Make sure that the resource count is equal to + VM + (new VR service offering - default router offering) + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + self.update_configuration("resource.count.routers.type", + "delta", self.domain1.id) + + # Step 2 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores, ram)) + + # Step 3 + list_service_offering_response = self.get_vr_service_offering() + updated_vr_cpu = list_service_offering_response[0].cpunumber + updated_vr_ram = list_service_offering_response[0].memory + + # Step 4 + self.assertEqual( + cores, + self.vm_cpu_count + (updated_vr_cpu - self.default_vr_cpu), + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + (updated_vr_ram - self.default_vr_ram), + "Total resource count of memory does not match delta for vr memory" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_06_count_just_vm_resource(self): + """ + Test to count vm resources when global setting "resource.count.routers" + is set to false + + Steps + 1. Set the global setting "resource.count.routers" value to "false" + 2. Update the resource count of domain/account + 3. Make sure that the resource count is equal to VM resource count + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + # set resource.count.routers to false + self.update_configuration("resource.count.routers", + "false", self.domain1.id) + + # Step 2 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores,ram)) + + # Step 3 + self.assertEqual( + cores, + self.vm_cpu_count, + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count, + "Total resource count of memory does not match delta for vr memory" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_07_count_vm_resources_with_stopped_router(self): + """ + Test to count vm resources when global setting "resource.count.routers" + is set to true and VR is stopped + + Steps + 1. Set the global setting "resource.count.routers" value to "true" + 2. Stop the VR + 3. Update the resource count of domain/account + 4. Get the value of global setting "resource.count.running.vms.only + 5. If the above value is true then resource count should be equal to + resource count of VM else its resource count of VM + VR + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + # set resource.count.routers to true + self.update_configuration("resource.count.routers", + "true", self.domain1.id) + self.update_configuration("resource.count.routers.type", + "all", self.domain1.id) + + # Step 2 + routers = list_routers( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid + ) + + router = routers[0] + + cmd = stopRouter.stopRouterCmd() + cmd.id = router.id + cmd.forced = True + self.apiclient.stopRouter(cmd) + + # Step 3 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores,ram)) + + # Step 4 + resource_count_running_vms = list_configurations( + self.apiclient, + name='resource.count.running.vms.only' + ) + + running_vms_only = resource_count_running_vms[0].value + + new_cpu_count = 0 + new_ram_size = 0 + + if running_vms_only == 'true': + new_cpu_count = self.vm_cpu_count + new_ram_size = self.vm_mem_count + else: + list_service_offering_response = self.get_vr_service_offering() + updated_vr_cpu = list_service_offering_response[0].cpunumber + updated_vr_ram = list_service_offering_response[0].memory + new_cpu_count = self.vm_cpu_count + updated_vr_cpu + new_ram_size = self.vm_mem_count + updated_vr_ram + + # Step 5 + self.assertEqual( + cores, + new_cpu_count, + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + new_ram_size, + "Total resource count of memory does not match delta for vr memory" + ) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_08_count_resources_restarting_network(self): + """ + Test to count vm resources when global setting "resource.count.routers" + is set to true and network is restarted with cleanup + + Steps + 1. Restart the network with cleanup option so that VR uses default offering + 2. Update the resource count of domain/account + 3. Make sure that the resource count is equal to VM + default VR service offering + 4. Create a new router service offering with 2 cores and 2Gb Ram + 5. Stop the router + 6. Update the service offering of the router with new offering + 7. Start the router + 8. Update the resource count of domain/account + 9. Make sure that the resource count is equal to VM + new VR service offering + 10. Set the global setting "router.service.offering" with new VR service offering + 11. Restart network with cleanup option + 12. Resource count should be equal to VM + (new VR offering) + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + self.restart_network() + + # Step 2 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + + list_service_offering_response = self.get_vr_service_offering() + default_vr_cpu = list_service_offering_response[0].cpunumber + default_vr_ram = list_service_offering_response[0].memory + + # Step 3 + self.assertEqual( + cores, + self.vm_cpu_count + default_vr_cpu, + "Total resource count of cpu does not match vm + default vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + default_vr_ram, + "Total resource count of memory does not match vm + default vr ram" + ) + + # Step 4 + offering_data = { + 'displaytext': 'TestOffering2', + 'cpuspeed': 1000, + 'cpunumber': 2, + 'name': 'TestOffering2', + 'memory': 2048, + 'issystem': 'true', + 'systemvmtype': 'domainrouter' + } + + network_offering = ServiceOffering.create( + self.apiclient, + offering_data, + domainid=self.domain1.id + ) + self.cleanup.append(network_offering) + + # Step 5 + routers = list_routers( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid + ) + + router = routers[0] + + self.stop_router(router.id) + + # Step 6 + scale_systemvm_cmd = scaleSystemVm.scaleSystemVmCmd() + scale_systemvm_cmd.id=router.id + scale_systemvm_cmd.serviceofferingid = network_offering.id + self.apiclient.scaleSystemVm(scale_systemvm_cmd) + + # Step 7 + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + # Step 8 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + + list_service_offering_response = self.get_vr_service_offering() + updated_vr_cpu = list_service_offering_response[0].cpunumber + updated_vr_ram = list_service_offering_response[0].memory + + # Step 9 + self.assertEqual( + cores, + self.vm_cpu_count + updated_vr_cpu, + "Total resource count of cpu does not match vm + new vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + updated_vr_ram, + "Total resource count of memory does not match vm + new vr ram" + ) + + # Step 10 + self.update_configuration("router.service.offering", list_service_offering_response[0].id, None) + + # Step 11 + self.restart_network() + + # Step 12 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + + self.assertEqual( + cores, + self.vm_cpu_count + updated_vr_cpu, + "Total resource count of cpu does not match vm + new vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count + updated_vr_ram, + "Total resource count of memory does not match vm + new vr ram" + ) + + self.update_configuration("router.service.offering", "", None) + + @attr(tags=["advanced", "basic", "sg"], required_hardware="false") + def test_09_count_vm_resources_with_destroyed_router(self): + """ + Test to count vm resources when global setting "resource.count.routers" + is set to true and VR is destroyed + + Steps + 1. Set the global setting "resource.count.routers" value to "true" + 2. Destroy the VR + 3. Update the resource count of domain/account + 4.The new resource count should be equal to VM resource count + """ + CPU_RESOURCE_ID = 8 + RAM_RESOURCE_ID = 9 + + # Step 1 + # set resource.count.routers to true + self.update_configuration("resource.count.routers", + "true", self.domain1.id) + self.update_configuration("resource.count.routers.type", + "all", self.domain1.id) + + # Step 2 + routers = list_routers( + self.apiclient, + account=self.account1.name, + domainid=self.account1.domainid + ) + + router = routers[0] + + self.destroy_router(router.id) + + # Step 3 + cores = int(self.get_resource_amount(CPU_RESOURCE_ID)) + ram = int(self.get_resource_amount(RAM_RESOURCE_ID)) + self.info("New resource count for domain/account are") + self.info("cpu is %s and ram is %s" % (cores,ram)) + + # Step 4 + self.assertEqual( + cores, + self.vm_cpu_count, + "Total resource count of cpu does not match delta for vr cpu" + ) + + self.assertEqual( + ram, + self.vm_mem_count, + "Total resource count of memory does not match delta for vr memory" + ) diff --git a/utils/src/main/java/com/cloud/utils/Pair.java b/utils/src/main/java/com/cloud/utils/Pair.java index 73d35625fa7c..0b530e954006 100644 --- a/utils/src/main/java/com/cloud/utils/Pair.java +++ b/utils/src/main/java/com/cloud/utils/Pair.java @@ -35,6 +35,10 @@ public Pair(T t, U u) { this.u = u; } + public static Pair of(T t, U u) { + return new Pair<>(t, u); + } + public T first() { return t; } @@ -61,8 +65,7 @@ public void set(T t, U u) { @Override // Note: This means any two pairs with null for both values will match each // other but what can I do? This is due to stupid type erasure. - public - int hashCode() { + public int hashCode() { return (t != null ? t.hashCode() : 0) | (u != null ? u.hashCode() : 0); } diff --git a/utils/src/test/java/com/cloud/utils/PairTest.java b/utils/src/test/java/com/cloud/utils/PairTest.java new file mode 100644 index 000000000000..f55dec4fd968 --- /dev/null +++ b/utils/src/test/java/com/cloud/utils/PairTest.java @@ -0,0 +1,87 @@ +package com.cloud.utils; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class PairTest { + + @Test + public void shouldReturnAValidInstanceFromPairWithStrings() { + final String expetedFirst = "A"; + final String expetedSecond = "B"; + final Pair actual = Pair.of("A", "B"); + + assertNotNull("the pair is null",actual); + assertEquals(expetedFirst, actual.first()); + assertEquals(expetedSecond, actual.second()); + } + + @Test + public void shouldReturnAValidInstanceFromPairWithIntegers() { + final Integer expetedFirst = 1; + final Integer expetedSecond = 2; + final Pair actual = Pair.of(1, 2); + + assertNotNull("the pair is null",actual); + assertEquals(expetedFirst, actual.first()); + assertEquals(expetedSecond, actual.second()); + } + + @Test + public void shouldReturnAValidInstanceFromPairWithLongs() { + final Long expetedFirst = 1L; + final Long expetedSecond = 2L; + final Pair actual = Pair.of(1L, 2L); + + assertNotNull("the pair is null",actual); + assertEquals(expetedFirst, actual.first()); + assertEquals(expetedSecond, actual.second()); + } + + @Test + public void shouldReturnAValidInstanceFromPairWithDiferentsTypes() { + final String expetedFirst = "A"; + final Long expetedSecond = 2L; + final Pair actual = Pair.of("A", 2L); + + assertNotNull("the pair is null",actual); + assertEquals(expetedFirst, actual.first()); + assertEquals(expetedSecond, actual.second()); + } + + @Test + public void shouldReturnAValidInstanceFromPairWithNulls() { + final Pair actual = Pair.of(null,null); + + assertNotNull("the pair is null",actual); + assertNull(actual.first()); + assertNull(actual.second()); + } + + @Test + public void testToString_whenTAndUAreNull() { + final Pair p = new Pair<>(null, null); + final String expected = "P[null:null]"; + final String actual = p.toString(); + assertEquals(expected, actual); + } + + @Test + public void testToString_whenTNotNullAndUIsNull() { + final Pair p = new Pair<>("apple", null); + final String expected = "P[apple:null]"; + final String actual = p.toString(); + assertEquals(expected, actual); + } + + @Test + public void testToString_whenTIsNullAndUNotNull() { + final Pair p = new Pair<>(null, "orange"); + final String expected = "P[null:orange]"; + final String actual = p.toString(); + assertEquals(expected, actual); + } +}