mirror of https://github.com/apache/cloudstack.git
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:
parent
8f88103a29
commit
8be18e587f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -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": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue