FR75 Enforce strict host tag checking (#421)

* Enforce strict host tag checking

* Add e2e tests

* Add more information to error log

* Fix e2e test

* Update global settings descrption

* fixup

* Fix e2e test teardown
This commit is contained in:
Vishesh 2024-06-25 14:38:59 +05:30 committed by GitHub
parent 8f88103a29
commit 8be18e587f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 773 additions and 50 deletions

View File

@ -126,6 +126,7 @@ jobs:
smoke/test_usage
smoke/test_usage_events
smoke/test_vm_deployment_planner
smoke/test_vm_strict_host_tags
smoke/test_vm_life_cycle
smoke/test_vm_lifecycle_unmanage_import
smoke/test_vm_snapshot_kvm

View File

@ -16,13 +16,13 @@
// under the License.
package com.cloud.host;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.Column;
@ -44,6 +44,7 @@ import javax.persistence.Transient;
import org.apache.cloudstack.util.HypervisorTypeConverter;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.VgpuTypesInfo;
@ -752,27 +753,48 @@ public class HostVO implements Host {
this.uuid = uuid;
}
public boolean checkHostServiceOfferingAndTemplateTags(ServiceOffering serviceOffering, VirtualMachineTemplate template) {
if (serviceOffering == null || template == null) {
return false;
}
private Set<String> getHostServiceOfferingAndTemplateStrictTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
if (StringUtils.isEmpty(serviceOffering.getHostTag()) && StringUtils.isEmpty(template.getTemplateTag())) {
return true;
return new HashSet<>();
}
if (getHostTags() == null) {
return false;
}
HashSet<String> hostTagsSet = new HashSet<>(getHostTags());
List<String> tags = new ArrayList<>();
List<String> hostTagsList = getHostTags();
HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
HashSet<String> tags = new HashSet<>();
if (StringUtils.isNotEmpty(serviceOffering.getHostTag())) {
tags.addAll(Arrays.asList(serviceOffering.getHostTag().split(",")));
}
if (StringUtils.isNotEmpty(template.getTemplateTag()) && !tags.contains(template.getTemplateTag())) {
if (StringUtils.isNotEmpty(template.getTemplateTag())) {
tags.add(template.getTemplateTag());
}
tags.removeIf(tag -> !strictHostTags.contains(tag));
tags.removeAll(hostTagsSet);
return tags;
}
public boolean checkHostServiceOfferingAndTemplateTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
if (serviceOffering == null || template == null) {
return false;
}
Set<String> tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags);
if (tags.isEmpty()) {
return true;
}
List<String> hostTagsList = getHostTags();
HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
return hostTagsSet.containsAll(tags);
}
public Set<String> getHostServiceOfferingAndTemplateMissingTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
Set<String> tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags);
if (tags.isEmpty()) {
return new HashSet<>();
}
List<String> hostTagsList = getHostTags();
HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
tags.removeAll(hostTagsSet);
return tags;
}
public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering) {
if (serviceOffering == null) {
return false;

View File

@ -1261,7 +1261,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
}
pstmt.close();
if(result.isEmpty()){
throw new CloudRuntimeException("No suitable host found for follow compute offering tags: " + computeOfferingTags);
throw new CloudRuntimeException("No suitable host found for the following compute offering tags: " + computeOfferingTags);
}
return result;
} catch (SQLException e) {
@ -1288,7 +1288,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
}
pstmt.close();
if(result.isEmpty()){
throw new CloudRuntimeException("No suitable host found for follow compute offering tags: " + computeOfferingTags);
throw new CloudRuntimeException("No suitable host found for the following compute offering tags: " + computeOfferingTags);
}
return result;
} catch (SQLException e) {

View File

@ -20,14 +20,18 @@ import com.cloud.offering.ServiceOffering;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.vm.VirtualMachine;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.Before;
import org.mockito.Mockito;
public class HostVOTest {
HostVO host;
@ -37,7 +41,7 @@ public class HostVOTest {
public void setUp() throws Exception {
host = new HostVO();
offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0,
false, "TestSO", false,VirtualMachine.Type.User,false);
false, "TestSO", false, VirtualMachine.Type.User, false);
}
@Test
@ -52,54 +56,74 @@ public class HostVOTest {
@Test
public void testRightTag() {
host.setHostTags(Arrays.asList("tag1","tag2"));
host.setHostTags(Arrays.asList("tag1", "tag2"));
offering.setHostTag("tag2,tag1");
assertTrue(host.checkHostServiceOfferingTags(offering));
}
@Test
public void testWrongTag() {
host.setHostTags(Arrays.asList("tag1","tag2"));
host.setHostTags(Arrays.asList("tag1", "tag2"));
offering.setHostTag("tag2,tag4");
assertFalse(host.checkHostServiceOfferingTags(offering));
}
@Test
public void testEitherNoSOOrTemplate() {
assertFalse(host.checkHostServiceOfferingAndTemplateTags(null, Mockito.mock(VirtualMachineTemplate.class)));
assertFalse(host.checkHostServiceOfferingAndTemplateTags(Mockito.mock(ServiceOffering.class), null));
assertFalse(host.checkHostServiceOfferingAndTemplateTags(null, Mockito.mock(VirtualMachineTemplate.class), null));
assertFalse(host.checkHostServiceOfferingAndTemplateTags(Mockito.mock(ServiceOffering.class), null, null));
}
@Test
public void testNoTagOfferingTemplate() {
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class)));
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Collections.emptySet()));
assertTrue(host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Collections.emptySet()).isEmpty());
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1", "tag2")));
assertTrue(host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1", "tag2")).isEmpty());
}
@Test
public void testRightTagOfferingTemplate() {
host.setHostTags(Arrays.asList("tag1", "tag2"));
offering.setHostTag("tag2,tag1");
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class)));
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1")));
Set<String> actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, Mockito.mock(VirtualMachineTemplate.class), Set.of("tag1"));
assertTrue(actualMissingTags.isEmpty());
host.setHostTags(Arrays.asList("tag1", "tag2", "tag3"));
offering.setHostTag("tag2,tag1");
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateTag()).thenReturn("tag3");
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template));
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag2", "tag3")));
actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag2", "tag3"));
assertTrue(actualMissingTags.isEmpty());
host.setHostTags(List.of("tag3"));
offering.setHostTag(null);
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template));
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag3")));
actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag3"));
assertTrue(actualMissingTags.isEmpty());
assertTrue(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag2", "tag1")));
actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag2", "tag1"));
assertTrue(actualMissingTags.isEmpty());
}
@Test
public void testWrongOfferingTag() {
host.setHostTags(Arrays.asList("tag1","tag2"));
host.setHostTags(Arrays.asList("tag1", "tag2"));
offering.setHostTag("tag2,tag4");
VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateTag()).thenReturn("tag1");
assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template));
assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4")));
Set<String> actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4"));
assertEquals(Set.of("tag4"), actualMissingTags);
offering.setHostTag("tag1,tag2");
template = Mockito.mock(VirtualMachineTemplate.class);
Mockito.when(template.getTemplateTag()).thenReturn("tag3");
assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template));
actualMissingTags = host.getHostServiceOfferingAndTemplateMissingTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4"));
assertFalse(host.checkHostServiceOfferingAndTemplateTags(offering, template, Set.of("tag1", "tag2", "tag3", "tag4")));
assertEquals(Set.of("tag3"), actualMissingTags);
}
}

View File

@ -31,6 +31,7 @@ import java.util.TreeSet;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.vm.UserVmManager;
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
@ -700,7 +701,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
VirtualMachineTemplate template = vmProfile.getTemplate();
if (offering.getHostTag() != null || template.getTemplateTag() != null) {
_hostDao.loadHostTags(host);
if (!host.checkHostServiceOfferingAndTemplateTags(offering, template)) {
if (!host.checkHostServiceOfferingAndTemplateTags(offering, template, UserVmManager.getStrictHostTags())) {
s_logger.debug("Service Offering host tag or template tag does not match the last host of this VM");
return false;
}

View File

@ -17,9 +17,12 @@
package com.cloud.vm;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.utils.StringUtils;
import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
import org.apache.cloudstack.framework.config.ConfigKey;
@ -38,6 +41,8 @@ import com.cloud.template.VirtualMachineTemplate;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
import static com.cloud.user.ResourceLimitService.ResourceLimitHostTags;
/**
*
*
@ -59,6 +64,22 @@ public interface UserVmManager extends UserVmService {
"Destroys the VM's root volume when the VM is destroyed.",
true, ConfigKey.Scope.Domain);
ConfigKey<String> StrictHostTags = new ConfigKey<>(
"Advanced",
String.class,
"vm.strict.host.tags",
"",
"A comma-separated list of tags which must match during operations like modifying the compute" +
"offering for an instance, and starting or live migrating an instance to a specific host.",
true);
ConfigKey<Boolean> EnforceStrictResourceLimitHostTagCheck = new ConfigKey<Boolean>(
"Advanced",
Boolean.class,
"vm.strict.resource.limit.host.tag.check",
"true",
"If set to true, tags specified in `resource.limit.host.tags` are also included in vm.strict.host.tags.",
true);
public static final String CKS_NODE = "cksnode";
/**
@ -123,6 +144,18 @@ public interface UserVmManager extends UserVmService {
void generateUsageEvent(VirtualMachine vm, boolean isDisplay, String eventType);
static Set<String> getStrictHostTags() {
String strictHostTags = StrictHostTags.value();
Set<String> strictHostTagsSet = new HashSet<>();
if (StringUtils.isNotEmpty(strictHostTags)) {
strictHostTagsSet.addAll(List.of(strictHostTags.split(",")));
}
if (EnforceStrictResourceLimitHostTagCheck.value() && StringUtils.isNotEmpty(ResourceLimitHostTags.value())) {
strictHostTagsSet.addAll(List.of(ResourceLimitHostTags.value().split(",")));
}
return strictHostTagsSet;
}
void persistDeviceBusInfo(UserVmVO paramUserVmVO, String paramString);
boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffering offering, VirtualMachineTemplate template, Long zoneId);

View File

@ -1883,16 +1883,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
upgradeStoppedVirtualMachine(vmId, newServiceOfferingId, customParameters);
return true;
}
if (State.Running.equals(vmInstance.getState())) {
ServiceOfferingVO newServiceOfferingVO = serviceOfferingDao.findById(newServiceOfferingId);
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId());
HostVO instanceHost = _hostDao.findById(vmInstance.getHostId());
_hostDao.loadHostTags(instanceHost);
if (!instanceHost.checkHostServiceOfferingAndTemplateTags(newServiceOfferingVO, template)) {
s_logger.error(String.format("Cannot upgrade VM [%s] as the new service offering [%s] does not have the required host tags %s.", vmInstance, newServiceOfferingVO,
instanceHost.getHostTags()));
Set<String> strictHostTags = UserVmManager.getStrictHostTags();
if (!instanceHost.checkHostServiceOfferingAndTemplateTags(newServiceOfferingVO, template, strictHostTags)) {
s_logger.error(String.format("Cannot upgrade VM [%s] as the new service offering [%s] does not have the required host tags [%s]. ",
vmInstance, newServiceOfferingVO,
instanceHost.getHostServiceOfferingAndTemplateMissingTags(newServiceOfferingVO, template, strictHostTags)));
return false;
}
}
@ -2001,6 +2002,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
// Check disable threshold for cluster is not crossed
HostVO host = _hostDao.findById(vmInstance.getHostId());
_hostDao.loadDetails(host);
if (_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)) {
throw new CloudRuntimeException(String.format("Unable to scale %s due to insufficient resources.", vmInstance.toString()));
}
@ -2013,12 +2015,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_resourceLimitMgr.updateVmResourceCountForServiceOfferingChange(caller.getAccountId(), vmInstance.isDisplay(),
(long) currentCpu, (long) newCpu, (long) currentMemory, (long) newMemory,
currentServiceOffering, newServiceOffering, template);
// #1 Check existing host has capacity
// #1 Check existing host has capacity & and the correct tags
if (!excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId()))) {
existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed)
&& _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, ByteScaleUtils.mebibytesToBytes(memoryDiff), false,
_capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU),
_capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false);
_capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false)
&& checkEnforceStrictHostTagCheck(vmInstance, host);
excludes.addHost(vmInstance.getHostId());
}
@ -5339,11 +5343,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId());
Pod destinationPod = getDestinationPod(podId, isRootAdmin);
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
Host destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost);
DataCenterDeployment plan = null;
boolean deployOnGivenHost = false;
if (destinationHost != null) {
s_logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM");
_hostDao.loadHostTags(destinationHost);
validateStrictHostTagCheck(vm, destinationHost);
final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId());
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false);
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
@ -5489,8 +5496,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return destinationCluster;
}
private Host getDestinationHost(Long hostId, boolean isRootAdmin, boolean isExplicitHost) {
Host destinationHost = null;
private HostVO getDestinationHost(Long hostId, boolean isRootAdmin, boolean isExplicitHost) {
HostVO destinationHost = null;
if (hostId != null) {
if (isExplicitHost && !isRootAdmin) {
throw new PermissionDeniedException(
@ -6524,6 +6531,32 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
protected boolean checkEnforceStrictHostTagCheck(VMInstanceVO vm, HostVO host) {
ServiceOffering serviceOffering = serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId());
VirtualMachineTemplate template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
return checkEnforceStrictHostTagCheck(host, serviceOffering, template);
}
private boolean checkEnforceStrictHostTagCheck(HostVO host, ServiceOffering serviceOffering, VirtualMachineTemplate template) {
Set<String> strictHostTags = UserVmManager.getStrictHostTags();
return host.checkHostServiceOfferingAndTemplateTags(serviceOffering, template, strictHostTags);
}
protected void validateStrictHostTagCheck(VMInstanceVO vm, HostVO host) {
ServiceOffering serviceOffering = serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId());
VirtualMachineTemplate template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId());
if (!checkEnforceStrictHostTagCheck(host, serviceOffering, template)) {
Set<String> missingTags = host.getHostServiceOfferingAndTemplateMissingTags(serviceOffering, template, UserVmManager.getStrictHostTags());
s_logger.error(String.format(
"Cannot deploy VM: %s to host : %s due to tag mismatch. host tags: %s," +
" strict host tags: %s serviceOffering tags: %s, template tags: %s, missing tags: %s",
vm, host, host.getHostTags(), UserVmManager.getStrictHostTags(), serviceOffering.getHostTag(), template.getTemplateTag(), missingTags));
throw new InvalidParameterValueException(String.format("Cannot deploy VM, destination host: %s " +
"is not compatible for the VM", host.getName()));
}
}
private DeployDestination checkVmMigrationDestination(VMInstanceVO vm, Host srcHost, Host destinationHost) throws VirtualMachineMigrationException {
if (destinationHost == null) {
return null;
@ -6549,6 +6582,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
throw new CloudRuntimeException("Cannot migrate VM, VM is DPDK enabled VM but destination host is not DPDK enabled");
}
HostVO destinationHostVO = _hostDao.findById(destinationHost.getId());
_hostDao.loadHostTags(destinationHostVO);
validateStrictHostTagCheck(vm, destinationHostVO);
checkHostsDedication(vm, srcHost.getId(), destinationHost.getId());
// call to core process
@ -6558,7 +6595,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
DeployDestination dest = new DeployDestination(dcVO, pod, cluster, destinationHost);
// check max guest vm limit for the destinationHost
HostVO destinationHostVO = _hostDao.findById(destinationHost.getId());
if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId()
@ -8201,7 +8237,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax,
VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties,
KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction};
KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction,
EnforceStrictResourceLimitHostTagCheck, StrictHostTags};
}
@Override

View File

@ -377,7 +377,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
return true;
}
hostDao.loadHostTags(host);
return host.checkHostServiceOfferingAndTemplateTags(serviceOffering, template);
return host.checkHostServiceOfferingAndTemplateTags(serviceOffering, template, UserVmManager.getStrictHostTags());
}
private boolean storagePoolSupportsDiskOffering(StoragePool pool, DiskOffering diskOffering) {

View File

@ -42,6 +42,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.cloud.host.HostVO;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
@ -1395,6 +1396,46 @@ public class UserVmManagerImplTest {
}
@Test
public void testValidateStrictHostTagCheckPass() {
ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
HostVO destinationHostVO = Mockito.mock(HostVO.class);
Mockito.when(_serviceOfferingDao.findByIdIncludingRemoved(1L)).thenReturn(serviceOffering);
Mockito.when(templateDao.findByIdIncludingRemoved(2L)).thenReturn(template);
Mockito.when(vm.getServiceOfferingId()).thenReturn(1L);
Mockito.when(vm.getTemplateId()).thenReturn(2L);
Mockito.when(destinationHostVO.checkHostServiceOfferingAndTemplateTags(Mockito.any(ServiceOffering.class), Mockito.any(VirtualMachineTemplate.class), Mockito.anySet())).thenReturn(true);
userVmManagerImpl.validateStrictHostTagCheck(vm, destinationHostVO);
Mockito.verify(
destinationHostVO, Mockito.times(1)
).checkHostServiceOfferingAndTemplateTags(Mockito.any(ServiceOffering.class), Mockito.any(VirtualMachineTemplate.class), Mockito.anySet());
}
@Test(expected = InvalidParameterValueException.class)
public void testValidateStrictHostTagCheckFail() {
ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
HostVO destinationHostVO = Mockito.mock(HostVO.class);
Mockito.when(_serviceOfferingDao.findByIdIncludingRemoved(1L)).thenReturn(serviceOffering);
Mockito.when(templateDao.findByIdIncludingRemoved(2L)).thenReturn(template);
Mockito.when(vm.getServiceOfferingId()).thenReturn(1L);
Mockito.when(vm.getTemplateId()).thenReturn(2L);
Mockito.when(destinationHostVO.checkHostServiceOfferingAndTemplateTags(Mockito.any(ServiceOffering.class), Mockito.any(VirtualMachineTemplate.class), Mockito.anySet())).thenReturn(false);
userVmManagerImpl.validateStrictHostTagCheck(vm, destinationHostVO);
}
public void testGetRootVolumeSizeForVmRestore() {
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
Mockito.when(template.getSize()).thenReturn(10L * GiB_TO_BYTES);

View File

@ -240,7 +240,7 @@ public class UnmanagedVMsManagerImplTest {
HostVO hostVO = Mockito.mock(HostVO.class);
when(hostVO.isInMaintenanceStates()).thenReturn(false);
hosts.add(hostVO);
when(hostVO.checkHostServiceOfferingAndTemplateTags(Mockito.any(), Mockito.any())).thenReturn(true);
when(hostVO.checkHostServiceOfferingAndTemplateTags(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true);
when(resourceManager.listHostsInClusterByStatus(Mockito.anyLong(), Mockito.any(Status.class))).thenReturn(hosts);
List<VMTemplateStoragePoolVO> templates = new ArrayList<>();
when(templatePoolDao.listAll()).thenReturn(templates);

View File

@ -0,0 +1,552 @@
# 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.
# Import Local Modules
from marvin.cloudstackAPI import (expungeVirtualMachine, updateConfiguration)
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import (Account, ServiceOffering, Template, Host, VirtualMachine)
from marvin.lib.common import (get_domain, get_zone)
from nose.plugins.attrib import attr
class TestVMDeploymentPlannerStrictTags(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestVMDeploymentPlannerStrictTags, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.hypervisor = testClient.getHypervisorInfo()
cls.services['mode'] = cls.zone.networktype
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
# Create an account, network, VM and IP addresses
cls.account = Account.create(cls.apiclient, cls.services["account"], domainid=cls.domain.id)
cls.service_offering_h1 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h1"])
cls.service_offering_h2 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h2"])
cls.template_t1 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t1")
cls.template_t1.download(cls.apiclient)
cls.template_t2 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t2")
cls.template_t2.download(cls.apiclient)
hosts = Host.list(cls.apiclient, zoneid=cls.zone.id, type='Routing')
cls.host_h1 = hosts[0] if len(hosts) >= 1 else None
cls._cleanup = [cls.account, cls.service_offering_h1, cls.service_offering_h2, cls.template_t1, cls.template_t2]
@classmethod
def tearDownClass(cls):
if cls.host_h1:
Host.update(cls.apiclient, id=cls.host_h1.id, hosttags="")
cls.updateConfiguration("vm.strict.host.tags", "")
cls.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
cls.updateConfiguration("resource.limit.host.tags", "")
super(TestVMDeploymentPlannerStrictTags, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
if self.host_h1:
Host.update(self.apiclient, id=self.host_h1.id, hosttags="h1,t1,v1")
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
self.cleanup = []
def tearDown(self):
self.cleanup_vm_for_template(self.template_t1.id)
self.cleanup_vm_for_template(self.template_t2.id)
super(TestVMDeploymentPlannerStrictTags, self).tearDown()
def cleanup_vm_for_template(self, templateid):
vm_list = VirtualMachine.list(self.apiclient, listall=True, templateid=templateid)
if type(vm_list) is list:
for vm in vm_list:
self.expunge_vm(vm)
def expunge_vm(self, vm):
try:
cmd = expungeVirtualMachine.expungeVirtualMachineCmd()
cmd.id = vm.id
self.apiclient.expungeVirtualMachine(cmd)
except Exception as e:
self.debug("Failed to expunge VM: %s" % e)
@classmethod
def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
cmd.name = name
cmd.value = value
self.apiclient.updateConfiguration(cmd)
def deploy_vm(self, destination_id, template_id, service_offering_id):
return VirtualMachine.create(self.apiclient, self.services["virtual_machine"], zoneid=self.zone.id,
templateid=template_id, serviceofferingid=service_offering_id,
hostid=destination_id)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_01_deploy_vm_on_specific_host_without_strict_tags(self):
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_02_deploy_vm_on_any_host_without_strict_tags(self):
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
vm = self.deploy_vm(None, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertIsNotNone(vm, "VM instance was not deployed")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_03_deploy_vm_on_specific_host_with_strict_tags_success(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "false")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_04_deploy_vm_on_any_host_with_strict_tags_success(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "false")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(None, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertIsNotNone(vm, "VM instance was not deployed")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
vm = self.deploy_vm(None, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_05_deploy_vm_on_specific_host_with_strict_tags_failure(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
try:
vm = self.deploy_vm(self.host_h1.id, self.template_t2.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.fail("VM should not be deployed")
except Exception as e:
self.assertTrue("Cannot deploy VM, destination host" in str(e))
try:
vm = self.deploy_vm(self.host_h1.id, self.template_t2.id, self.service_offering_h2.id)
self.cleanup.append(vm)
self.fail("VM should not be deployed")
except Exception as e:
self.assertTrue("Cannot deploy VM, destination host" in str(e))
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_06_deploy_vm_on_any_host_with_strict_tags_failure(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
try:
vm = self.deploy_vm(None, self.template_t2.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.fail("VM should not be deployed")
except Exception as e:
self.assertTrue("No suitable host found for the following compute offering tags: t2" in str(e))
class TestScaleVMStrictTags(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestScaleVMStrictTags, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.hypervisor = testClient.getHypervisorInfo()
cls.services['mode'] = cls.zone.networktype
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
# Create an account, network, VM and IP addresses
cls.account = Account.create(cls.apiclient, cls.services["account"], domainid=cls.domain.id)
cls.service_offering_h1 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h1"])
cls.service_offering_h2 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h2"])
cls.template_t1 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t1")
cls.template_t1.download(cls.apiclient)
cls.template_t2 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t2")
cls.template_t2.download(cls.apiclient)
hosts = Host.list(cls.apiclient, zoneid=cls.zone.id, type='Routing')
cls.host_h1 = hosts[0] if len(hosts) >= 1 else None
cls._cleanup = [cls.account, cls.service_offering_h1, cls.service_offering_h2, cls.template_t1, cls.template_t2]
@classmethod
def tearDownClass(cls):
if cls.host_h1:
Host.update(cls.apiclient, id=cls.host_h1.id, hosttags="")
cls.updateConfiguration("vm.strict.host.tags", "")
cls.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
cls.updateConfiguration("resource.limit.host.tags", "")
super(TestScaleVMStrictTags, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
if self.host_h1:
Host.update(self.apiclient, id=self.host_h1.id, hosttags="h1,t1,v1,h2,t2,v2")
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
self.cleanup = []
def tearDown(self):
self.cleanup_vm_for_template(self.template_t1.id)
self.cleanup_vm_for_template(self.template_t2.id)
super(TestScaleVMStrictTags, self).tearDown()
def cleanup_vm_for_template(self, templateid):
vm_list = VirtualMachine.list(self.apiclient, listall=True, templateid=templateid)
if type(vm_list) is list:
for vm in vm_list:
self.expunge_vm(vm)
def expunge_vm(self, vm):
try:
cmd = expungeVirtualMachine.expungeVirtualMachineCmd()
cmd.id = vm.id
self.apiclient.expungeVirtualMachine(cmd)
except Exception as e:
self.debug("Failed to expunge VM: %s" % e)
@classmethod
def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
cmd.name = name
cmd.value = value
self.apiclient.updateConfiguration(cmd)
def deploy_vm(self, destination_id, template_id, service_offering_id):
return VirtualMachine.create(self.apiclient, self.services["virtual_machine"], zoneid=self.zone.id,
templateid=template_id, serviceofferingid=service_offering_id,
hostid=destination_id)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_01_scale_vm_strict_tags_success(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
vm.stop(self.apiclient)
vm.scale(self.apiclient, serviceOfferingId=self.service_offering_h2.id)
vm.start(self.apiclient)
scaled_vm = VirtualMachine.list(self.apiclient, id=vm.id, listall=True)[0]
self.assertEqual(scaled_vm.serviceofferingid, self.service_offering_h2.id, "VM was not scaled")
self.assertEqual(self.host_h1.id, scaled_vm.hostid, "VM was not scaled")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_02_scale_vm_strict_tags_failure(self):
if self.host_h1:
Host.update(self.apiclient, id=self.host_h1.id, hosttags="h1,t1,v1")
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
try:
vm.stop(self.apiclient)
vm.scale(self.apiclient, serviceOfferingId=self.service_offering_h2.id)
vm.start(self.apiclient)
self.fail("VM should not be be able scale and start")
except Exception as e:
self.assertTrue("No suitable host found for the following compute offering tags: h2" in str(e))
class TestRestoreVMStrictTags(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestRestoreVMStrictTags, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.hypervisor = testClient.getHypervisorInfo()
cls.services['mode'] = cls.zone.networktype
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
# Create an account, network, VM and IP addresses
cls.account = Account.create(cls.apiclient, cls.services["account"], domainid=cls.domain.id)
cls.service_offering_h1 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h1"])
cls.service_offering_h2 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h2"])
cls.template_t1 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t1")
cls.template_t1.download(cls.apiclient)
cls.template_t2 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t2")
cls.template_t2.download(cls.apiclient)
hosts = Host.list(cls.apiclient, zoneid=cls.zone.id, type='Routing')
cls.host_h1 = hosts[0] if len(hosts) >= 1 else None
cls._cleanup = [cls.account, cls.service_offering_h1, cls.service_offering_h2, cls.template_t1, cls.template_t2]
@classmethod
def tearDownClass(cls):
if cls.host_h1:
Host.update(cls.apiclient, id=cls.host_h1.id, hosttags="")
cls.updateConfiguration("vm.strict.host.tags", "")
cls.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
cls.updateConfiguration("resource.limit.host.tags", "")
super(TestRestoreVMStrictTags, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
if self.host_h1:
Host.update(self.apiclient, id=self.host_h1.id, hosttags="h1,t1,v1")
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
self.cleanup = []
def tearDown(self):
self.cleanup_vm_for_template(self.template_t1.id)
self.cleanup_vm_for_template(self.template_t2.id)
super(TestRestoreVMStrictTags, self).tearDown()
def cleanup_vm_for_template(self, templateid):
vm_list = VirtualMachine.list(self.apiclient, listall=True, templateid=templateid)
if type(vm_list) is list:
for vm in vm_list:
self.expunge_vm(vm)
def expunge_vm(self, vm):
try:
cmd = expungeVirtualMachine.expungeVirtualMachineCmd()
cmd.id = vm.id
self.apiclient.expungeVirtualMachine(cmd)
except Exception as e:
self.debug("Failed to expunge VM: %s" % e)
@classmethod
def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
cmd.name = name
cmd.value = value
self.apiclient.updateConfiguration(cmd)
def deploy_vm(self, destination_id, template_id, service_offering_id):
return VirtualMachine.create(self.apiclient, self.services["virtual_machine"], zoneid=self.zone.id,
templateid=template_id, serviceofferingid=service_offering_id,
hostid=destination_id)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_01_restore_vm_strict_tags_success(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
vm.restore(self.apiclient, templateid=self.template_t2.id, expunge=True)
restored_vm = VirtualMachine.list(self.apiclient, id=vm.id, listall=True)[0]
self.assertEqual(restored_vm.templateid, self.template_t2.id, "VM was not scaled")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_02_restore_vm_strict_tags_failure(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
try:
vm.restore(self.apiclient, templateid=self.template_t2.id, expunge=True)
self.fail("VM should not be restored")
except Exception as e:
self.assertTrue("No suitable host found for the following compute offering tags: t2" in str(e))
class TestMigrateVMStrictTags(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestMigrateVMStrictTags, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.hypervisor = testClient.getHypervisorInfo()
cls.services['mode'] = cls.zone.networktype
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
hosts = Host.list(cls.apiclient, zoneid=cls.zone.id, type='Routing')
cls.host_h1 = hosts[0] if len(hosts) >= 1 else None
cls.host_h2 = None
if len(hosts) >= 2:
for host in hosts[1:]:
if host.clusterid == cls.host_h1.clusterid:
cls.host_h2 = host
break
if not cls.host_h2:
cls.skipTest("There are not enough hosts to run this test")
# Create an account, network, VM and IP addresses
cls.account = Account.create(cls.apiclient, cls.services["account"], domainid=cls.domain.id)
cls.service_offering_h1 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h1"])
cls.service_offering_h2 = ServiceOffering.create(cls.apiclient, cls.services["service_offering_h2"])
cls.template_t1 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t1")
cls.template_t1.download(cls.apiclient)
cls.template_t2 = Template.register(cls.apiclient, cls.services["test_templates"][cls.hypervisor.lower()],
zoneid=cls.zone.id, hypervisor=cls.hypervisor.lower(), templatetag="t2")
cls.template_t2.download(cls.apiclient)
cls._cleanup = [cls.account, cls.service_offering_h1, cls.service_offering_h2, cls.template_t1, cls.template_t2]
@classmethod
def tearDownClass(cls):
if cls.host_h1:
Host.update(cls.apiclient, id=cls.host_h1.id, hosttags="")
if cls.host_h2:
Host.update(cls.apiclient, id=cls.host_h2.id, hosttags="")
cls.updateConfiguration("vm.strict.host.tags", "")
cls.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
cls.updateConfiguration("resource.limit.host.tags", "")
super(TestMigrateVMStrictTags, cls).tearDownClass()
def setUp(self):
self.apiclient = self.testClient.getApiClient()
if self.host_h1:
Host.update(self.apiclient, id=self.host_h1.id, hosttags="h1,t1,v1")
self.updateConfiguration("vm.strict.host.tags", "")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "")
self.cleanup = []
def tearDown(self):
self.cleanup_vm_for_template(self.template_t1.id)
self.cleanup_vm_for_template(self.template_t2.id)
super(TestMigrateVMStrictTags, self).tearDown()
def cleanup_vm_for_template(self, templateid):
vm_list = VirtualMachine.list(self.apiclient, listall=True, templateid=templateid)
if type(vm_list) is list:
for vm in vm_list:
self.expunge_vm(vm)
def expunge_vm(self, vm):
try:
cmd = expungeVirtualMachine.expungeVirtualMachineCmd()
cmd.id = vm.id
self.apiclient.expungeVirtualMachine(cmd)
except Exception as e:
self.debug("Failed to expunge VM: %s" % e)
@classmethod
def updateConfiguration(self, name, value):
cmd = updateConfiguration.updateConfigurationCmd()
cmd.name = name
cmd.value = value
self.apiclient.updateConfiguration(cmd)
def deploy_vm(self, destination_id, template_id, service_offering_id):
return VirtualMachine.create(self.apiclient, self.services["virtual_machine"], zoneid=self.zone.id,
templateid=template_id, serviceofferingid=service_offering_id,
hostid=destination_id)
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_01_migrate_vm_strict_tags_success(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
Host.update(self.apiclient, id=self.host_h2.id, hosttags="h1,t1,v1")
vm.migrate(self.apiclient, self.host_h2.id)
migrated_vm = VirtualMachine.list(self.apiclient, id=vm.id, listall=True)[0]
self.assertEqual(migrated_vm.hostid, self.host_h2.id, "VM was not migratd")
@attr(tags=["advanced", "advancedns", "ssh", "smoke"], required_hardware="false")
def test_02_migrate_vm_strict_tags_failure(self):
self.updateConfiguration("vm.strict.host.tags", "v1,v2")
self.updateConfiguration("vm.strict.resource.limit.host.tag.check", "true")
self.updateConfiguration("resource.limit.host.tags", "h1,h2,t1,t2")
vm = self.deploy_vm(self.host_h1.id, self.template_t1.id, self.service_offering_h1.id)
self.cleanup.append(vm)
self.assertEqual(self.host_h1.id, vm.hostid, "VM instance was not deployed on target host ID")
Host.update(self.apiclient, id=self.host_h2.id, hosttags="h2,t2,v2")
try:
vm.migrate(self.apiclient, self.host_h2.id)
VirtualMachine.list(self.apiclient, id=vm.id, listall=True)[0]
self.fail("VM should not be migrated")
except Exception as e:
self.assertTrue("Cannot deploy VM, destination host:" in str(e))

View File

@ -177,9 +177,9 @@ test_data = {
"service_offering_h2": {
"name": "Tagged h2 Small Instance",
"displaytext": "Tagged h2 Small Instance",
"cpunumber": 1,
"cpuspeed": 100,
"memory": 256,
"cpunumber": 2,
"cpuspeed": 200,
"memory": 512,
"hosttags": "h2"
},
"disk_offering": {
@ -1034,7 +1034,18 @@ test_data = {
"requireshvm": "True",
"ispublic": "True",
"deployasis": "True"
}
},
"simulator": {
"name": "tiny-simulator",
"displaytext": "tiny simulator",
"format": "vhd",
"hypervisor": "simulator",
"ostype": "Other Linux (64-bit)",
"url": "http://dl.openvm.eu/cloudstack/macchinina/x86_64/macchinina.vhd.bz2",
"requireshvm": "True",
"ispublic": "True",
"isextractable": "True"
},
},
"test_ovf_templates": [
{

View File

@ -776,6 +776,8 @@ class VirtualMachine:
cmd.virtualmachineid = self.id
if templateid:
cmd.templateid = templateid
if expunge:
cmd.expunge = expunge
if diskofferingid:
cmd.diskofferingid = diskofferingid
if rootdisksize:
@ -788,7 +790,6 @@ class VirtualMachine:
'key': key,
'value': value
})
return apiclient.restoreVirtualMachine(cmd)
def get_ssh_client(