Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix NPE issues during host rolling maintenance, due to host tags and custom constrained/unconstrained service offering #9844

Open
wants to merge 2 commits into
base: 4.19
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.log4j.Logger;

import com.cloud.agent.AgentManager;
Expand Down Expand Up @@ -65,12 +66,16 @@
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;

public class RollingMaintenanceManagerImpl extends ManagerBase implements RollingMaintenanceManager {
Expand All @@ -86,6 +91,8 @@
@Inject
private VMInstanceDao vmInstanceDao;
@Inject
protected UserVmDetailsDao userVmDetailsDao;
@Inject
private ServiceOfferingDao serviceOfferingDao;
@Inject
private ClusterDetailsDao clusterDetailsDao;
Expand Down Expand Up @@ -621,10 +628,19 @@
int successfullyCheckedVmMigrations = 0;
for (VMInstanceVO runningVM : vmsRunning) {
boolean canMigrateVm = false;
Ternary<Integer, Integer, Integer> cpuSpeedAndRamSize = getComputeResourcesCpuSpeedAndRamSize(runningVM);
Integer cpu = cpuSpeedAndRamSize.first();
Integer speed = cpuSpeedAndRamSize.second();
Integer ramSize = cpuSpeedAndRamSize.third();

Check warning on line 634 in server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java#L631-L634

Added lines #L631 - L634 were not covered by tests
if (ObjectUtils.anyNull(cpu, speed,ramSize)) {
s_logger.warn(String.format("Cannot fetch compute resources for the VM %s, skipping it from the capacity check", runningVM));
continue;

Check warning on line 637 in server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java#L636-L637

Added lines #L636 - L637 were not covered by tests
}

ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(runningVM.getServiceOfferingId());
for (Host hostInCluster : hostsInCluster) {
if (!checkHostTags(hostTags, hostTagsDao.getHostTags(hostInCluster.getId()), serviceOffering.getHostTag())) {
s_logger.debug(String.format("Host tags mismatch between %s and %s Skipping it from the capacity check", host, hostInCluster));
s_logger.warn(String.format("Host tags mismatch between %s and %s, skipping it from the capacity check", host, hostInCluster));

Check warning on line 643 in server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java#L643

Added line #L643 was not covered by tests
continue;
}
DeployDestination deployDestination = new DeployDestination(null, null, null, host);
Expand All @@ -634,13 +650,13 @@
affinityChecks = affinityChecks && affinityProcessor.check(vmProfile, deployDestination);
}
if (!affinityChecks) {
s_logger.debug(String.format("Affinity check failed between %s and %s Skipping it from the capacity check", host, hostInCluster));
s_logger.warn(String.format("Affinity check failed between %s and %s, skipping it from the capacity check", host, hostInCluster));

Check warning on line 653 in server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java#L653

Added line #L653 was not covered by tests
continue;
}
boolean maxGuestLimit = capacityManager.checkIfHostReachMaxGuestLimit(host);
boolean hostHasCPUCapacity = capacityManager.checkIfHostHasCpuCapability(hostInCluster.getId(), serviceOffering.getCpu(), serviceOffering.getSpeed());
int cpuRequested = serviceOffering.getCpu() * serviceOffering.getSpeed();
long ramRequested = serviceOffering.getRamSize() * 1024L * 1024L;
boolean hostHasCPUCapacity = capacityManager.checkIfHostHasCpuCapability(hostInCluster.getId(), cpu, speed);
int cpuRequested = cpu * speed;
long ramRequested = ramSize * 1024L * 1024L;

Check warning on line 659 in server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/com/cloud/resource/RollingMaintenanceManagerImpl.java#L657-L659

Added lines #L657 - L659 were not covered by tests
ClusterDetailsVO clusterDetailsCpuOvercommit = clusterDetailsDao.findDetail(cluster.getId(), "cpuOvercommitRatio");
ClusterDetailsVO clusterDetailsRamOvercommmt = clusterDetailsDao.findDetail(cluster.getId(), "memoryOvercommitRatio");
Float cpuOvercommitRatio = Float.parseFloat(clusterDetailsCpuOvercommit.getValue());
Expand All @@ -666,11 +682,36 @@
return new Pair<>(true, "OK");
}

protected Ternary<Integer, Integer, Integer> getComputeResourcesCpuSpeedAndRamSize(VMInstanceVO runningVM) {
ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(runningVM.getServiceOfferingId());
Integer cpu = serviceOffering.getCpu();
Integer speed = serviceOffering.getSpeed();
Integer ramSize = serviceOffering.getRamSize();
if (serviceOffering.isDynamic()) {
List<UserVmDetailVO> vmDetails = userVmDetailsDao.listDetails(runningVM.getId());
if (CollectionUtils.isNotEmpty(vmDetails)) {
for (UserVmDetailVO vmDetail : vmDetails) {
if (vmDetail.getName() != null && vmDetail.getValue() != null) {
if (cpu == null && VmDetailConstants.CPU_NUMBER.equals(vmDetail.getName())) {
cpu = Integer.valueOf(vmDetail.getValue());
} else if (speed == null && VmDetailConstants.CPU_SPEED.equals(vmDetail.getName())) {
speed = Integer.valueOf(vmDetail.getValue());
} else if (ramSize == null && VmDetailConstants.MEMORY.equals(vmDetail.getName())) {
ramSize = Integer.valueOf(vmDetail.getValue());
}
}
}
}
}

return new Ternary<>(cpu, speed, ramSize);
}

/**
* Check hosts tags
*/
private boolean checkHostTags(List<HostTagVO> hostTags, List<HostTagVO> hostInClusterTags, String offeringTag) {
if (CollectionUtils.isEmpty(hostTags) && CollectionUtils.isEmpty(hostInClusterTags)) {
if ((CollectionUtils.isEmpty(hostTags) && CollectionUtils.isEmpty(hostInClusterTags)) || StringUtils.isBlank(offeringTag)) {
return true;
} else if ((CollectionUtils.isNotEmpty(hostTags) && CollectionUtils.isEmpty(hostInClusterTags)) ||
(CollectionUtils.isEmpty(hostTags) && CollectionUtils.isNotEmpty(hostInClusterTags))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.org.Cluster;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.UserVmDetailVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VmDetailConstants;
import com.cloud.vm.dao.UserVmDetailsDao;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -53,6 +61,12 @@ public class RollingMaintenanceManagerImplTest {
HostVO host4;
@Mock
Cluster cluster;
@Mock
VMInstanceVO vm;
@Mock
ServiceOfferingDao serviceOfferingDao;
@Mock
UserVmDetailsDao userVmDetailsDao;

@Spy
@InjectMocks
Expand Down Expand Up @@ -164,4 +178,50 @@ public void testPerformStateChecksForce() {

Assert.assertEquals(1, hosts.size());
}

@Test
public void testGetComputeResourcesCpuSpeedAndRamSize_ForNormalOffering() {
ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
Mockito.when(serviceOffering.isDynamic()).thenReturn(false);
Mockito.when(serviceOffering.getCpu()).thenReturn(1);
Mockito.when(serviceOffering.getSpeed()).thenReturn(500);
Mockito.when(serviceOffering.getRamSize()).thenReturn(512);

Mockito.when(vm.getServiceOfferingId()).thenReturn(1L);
Mockito.when(serviceOfferingDao.findById(1L)).thenReturn(serviceOffering);

Ternary<Integer, Integer, Integer> cpuSpeedAndRamSize = manager.getComputeResourcesCpuSpeedAndRamSize(vm);

Assert.assertEquals(1, cpuSpeedAndRamSize.first().intValue());
Assert.assertEquals(500, cpuSpeedAndRamSize.second().intValue());
Assert.assertEquals(512, cpuSpeedAndRamSize.third().intValue());
}

@Test
public void testGetComputeResourcesCpuSpeedAndRamSize_ForCustomOffering() {
ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
Mockito.when(serviceOffering.isDynamic()).thenReturn(true);
Mockito.when(serviceOffering.getCpu()).thenReturn(null);
Mockito.when(serviceOffering.getSpeed()).thenReturn(null);
Mockito.when(serviceOffering.getRamSize()).thenReturn(null);

List<UserVmDetailVO> vmDetails = new ArrayList<>();
UserVmDetailVO cpuDetail = new UserVmDetailVO(1L, VmDetailConstants.CPU_NUMBER, "2", false);
vmDetails.add(cpuDetail);
UserVmDetailVO speedDetail = new UserVmDetailVO(1L, VmDetailConstants.CPU_SPEED, "1000", false);
vmDetails.add(speedDetail);
UserVmDetailVO ramSizeDetail = new UserVmDetailVO(1L, VmDetailConstants.MEMORY, "1024", false);
vmDetails.add(ramSizeDetail);

Mockito.when(vm.getId()).thenReturn(1L);
Mockito.when(vm.getServiceOfferingId()).thenReturn(1L);
Mockito.when(serviceOfferingDao.findById(1L)).thenReturn(serviceOffering);
Mockito.when(userVmDetailsDao.listDetails(1L)).thenReturn(vmDetails);

Ternary<Integer, Integer, Integer> cpuSpeedAndRamSize = manager.getComputeResourcesCpuSpeedAndRamSize(vm);

Assert.assertEquals(2, cpuSpeedAndRamSize.first().intValue());
Assert.assertEquals(1000, cpuSpeedAndRamSize.second().intValue());
Assert.assertEquals(1024, cpuSpeedAndRamSize.third().intValue());
}
}
Loading