mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge branch '4.11'
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
commit
93e374599a
@ -19,6 +19,9 @@ package com.cloud.deploy;
|
|||||||
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||||
import com.cloud.vm.ReservationContext;
|
import com.cloud.vm.ReservationContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class DataCenterDeployment implements DeploymentPlan {
|
public class DataCenterDeployment implements DeploymentPlan {
|
||||||
long _dcId;
|
long _dcId;
|
||||||
Long _podId;
|
Long _podId;
|
||||||
@ -29,6 +32,7 @@ public class DataCenterDeployment implements DeploymentPlan {
|
|||||||
ExcludeList _avoids = null;
|
ExcludeList _avoids = null;
|
||||||
boolean _recreateDisks;
|
boolean _recreateDisks;
|
||||||
ReservationContext _context;
|
ReservationContext _context;
|
||||||
|
List<Long> preferredHostIds = new ArrayList<>();
|
||||||
|
|
||||||
public DataCenterDeployment(long dataCenterId) {
|
public DataCenterDeployment(long dataCenterId) {
|
||||||
this(dataCenterId, null, null, null, null, null);
|
this(dataCenterId, null, null, null, null, null);
|
||||||
@ -93,4 +97,14 @@ public class DataCenterDeployment implements DeploymentPlan {
|
|||||||
return _context;
|
return _context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPreferredHosts(List<Long> hostIds) {
|
||||||
|
this.preferredHostIds = new ArrayList<>(hostIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getPreferredHosts() {
|
||||||
|
return this.preferredHostIds;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ package com.cloud.deploy;
|
|||||||
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||||
import com.cloud.vm.ReservationContext;
|
import com.cloud.vm.ReservationContext;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public interface DeploymentPlan {
|
public interface DeploymentPlan {
|
||||||
@ -65,4 +67,8 @@ public interface DeploymentPlan {
|
|||||||
Long getPhysicalNetworkId();
|
Long getPhysicalNetworkId();
|
||||||
|
|
||||||
ReservationContext getReservationContext();
|
ReservationContext getReservationContext();
|
||||||
|
|
||||||
|
void setPreferredHosts(List<Long> hostIds);
|
||||||
|
|
||||||
|
List<Long> getPreferredHosts();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -438,6 +438,11 @@
|
|||||||
<artifactId>cloud-plugin-host-anti-affinity</artifactId>
|
<artifactId>cloud-plugin-host-anti-affinity</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloud-plugin-host-affinity</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.cloudstack</groupId>
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
<artifactId>cloud-plugin-api-solidfire-intg-test</artifactId>
|
<artifactId>cloud-plugin-api-solidfire-intg-test</artifactId>
|
||||||
|
|||||||
@ -248,7 +248,7 @@
|
|||||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
<property name="orderConfigKey" value="affinity.processors.order" />
|
<property name="orderConfigKey" value="affinity.processors.order" />
|
||||||
<property name="orderConfigDefault"
|
<property name="orderConfigDefault"
|
||||||
value="HostAntiAffinityProcessor,ExplicitDedicationProcessor" />
|
value="HostAntiAffinityProcessor,ExplicitDedicationProcessor,HostAffinityProcessor" />
|
||||||
<property name="excludeKey" value="affinity.processors.exclude" />
|
<property name="excludeKey" value="affinity.processors.exclude" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
|||||||
29
plugins/affinity-group-processors/host-affinity/pom.xml
Normal file
29
plugins/affinity-group-processors/host-affinity/pom.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<name>Apache CloudStack Plugin - Host Affinity Processor</name>
|
||||||
|
<artifactId>cloud-plugin-host-affinity</artifactId>
|
||||||
|
<parent>
|
||||||
|
<artifactId>cloudstack-plugins</artifactId>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<version>4.12.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package org.apache.cloudstack.affinity;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.cloud.vm.VMInstanceVO;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||||
|
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||||
|
|
||||||
|
import com.cloud.deploy.DeployDestination;
|
||||||
|
import com.cloud.deploy.DeploymentPlan;
|
||||||
|
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||||
|
import com.cloud.exception.AffinityConflictException;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.VirtualMachineProfile;
|
||||||
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
|
public class HostAffinityProcessor extends AffinityProcessorBase implements AffinityGroupProcessor {
|
||||||
|
|
||||||
|
private static final Logger s_logger = Logger.getLogger(HostAffinityProcessor.class);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected VMInstanceDao _vmInstanceDao;
|
||||||
|
@Inject
|
||||||
|
protected AffinityGroupDao _affinityGroupDao;
|
||||||
|
@Inject
|
||||||
|
protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid) throws AffinityConflictException {
|
||||||
|
VirtualMachine vm = vmProfile.getVirtualMachine();
|
||||||
|
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
|
||||||
|
if (CollectionUtils.isNotEmpty(vmGroupMappings)) {
|
||||||
|
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
|
||||||
|
processAffinityGroup(vmGroupMapping, plan, vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process Affinity Group for VM deployment
|
||||||
|
*/
|
||||||
|
protected void processAffinityGroup(AffinityGroupVMMapVO vmGroupMapping, DeploymentPlan plan, VirtualMachine vm) {
|
||||||
|
AffinityGroupVO group = _affinityGroupDao.findById(vmGroupMapping.getAffinityGroupId());
|
||||||
|
s_logger.debug("Processing affinity group " + group.getName() + " for VM Id: " + vm.getId());
|
||||||
|
|
||||||
|
List<Long> groupVMIds = _affinityGroupVMMapDao.listVmIdsByAffinityGroup(group.getId());
|
||||||
|
groupVMIds.remove(vm.getId());
|
||||||
|
|
||||||
|
List<Long> preferredHosts = getPreferredHostsFromGroupVMIds(groupVMIds);
|
||||||
|
plan.setPreferredHosts(preferredHosts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get host ids set from vm ids list
|
||||||
|
*/
|
||||||
|
protected Set<Long> getHostIdSet(List<Long> vmIds) {
|
||||||
|
Set<Long> hostIds = new HashSet<>();
|
||||||
|
for (Long groupVMId : vmIds) {
|
||||||
|
VMInstanceVO groupVM = _vmInstanceDao.findById(groupVMId);
|
||||||
|
hostIds.add(groupVM.getHostId());
|
||||||
|
}
|
||||||
|
return hostIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get preferred host ids list from the affinity group VMs
|
||||||
|
*/
|
||||||
|
protected List<Long> getPreferredHostsFromGroupVMIds(List<Long> vmIds) {
|
||||||
|
return new ArrayList<>(getHostIdSet(vmIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(VirtualMachineProfile vmProfile, DeployDestination plannedDestination) throws AffinityConflictException {
|
||||||
|
if (plannedDestination.getHost() == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
long plannedHostId = plannedDestination.getHost().getId();
|
||||||
|
VirtualMachine vm = vmProfile.getVirtualMachine();
|
||||||
|
List<AffinityGroupVMMapVO> vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType());
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(vmGroupMappings)) {
|
||||||
|
for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) {
|
||||||
|
if (!checkAffinityGroup(vmGroupMapping, vm, plannedHostId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Affinity Group
|
||||||
|
*/
|
||||||
|
protected boolean checkAffinityGroup(AffinityGroupVMMapVO vmGroupMapping, VirtualMachine vm, long plannedHostId) {
|
||||||
|
List<Long> groupVMIds = _affinityGroupVMMapDao.listVmIdsByAffinityGroup(vmGroupMapping.getAffinityGroupId());
|
||||||
|
groupVMIds.remove(vm.getId());
|
||||||
|
|
||||||
|
Set<Long> hostIds = getHostIdSet(groupVMIds);
|
||||||
|
return CollectionUtils.isEmpty(hostIds) || hostIds.contains(plannedHostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# 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.
|
||||||
|
name=host-affinity
|
||||||
|
parent=planner
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd"
|
||||||
|
>
|
||||||
|
|
||||||
|
<bean id="HostAffinityProcessor"
|
||||||
|
class="org.apache.cloudstack.affinity.HostAffinityProcessor">
|
||||||
|
<property name="name" value="HostAffinityProcessor" />
|
||||||
|
<property name="type" value="host affinity" />
|
||||||
|
</bean>
|
||||||
|
</beans>
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package org.apache.cloudstack.affinity;
|
||||||
|
|
||||||
|
import com.cloud.deploy.DeployDestination;
|
||||||
|
import com.cloud.deploy.DeploymentPlan;
|
||||||
|
import com.cloud.host.Host;
|
||||||
|
import com.cloud.vm.VMInstanceVO;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.VirtualMachineProfile;
|
||||||
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||||
|
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class HostAffinityProcessorTest {
|
||||||
|
|
||||||
|
private static final long AFFINITY_GROUP_ID = 2L;
|
||||||
|
private static final String AFFINITY_GROUP_NAME = "Host affinity group";
|
||||||
|
private static final Long VM_ID = 3L;
|
||||||
|
private static final Long GROUP_VM_1_ID = 1L;
|
||||||
|
private static final Long GROUP_VM_2_ID = 2L;
|
||||||
|
private static final Long HOST_ID = 1L;
|
||||||
|
private static final Long HOST_2_ID = 2L;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AffinityGroupDao affinityGroupDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AffinityGroupVMMapDao affinityGroupVMMapDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VMInstanceDao vmInstanceDao;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
@InjectMocks
|
||||||
|
HostAffinityProcessor processor = new HostAffinityProcessor();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DeploymentPlan plan;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VirtualMachine vm;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VMInstanceVO groupVM1;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VMInstanceVO groupVM2;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AffinityGroupVO affinityGroupVO;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
AffinityGroupVMMapVO mapVO;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DeployDestination dest;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
Host host;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
VirtualMachineProfile profile;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
when(groupVM1.getHostId()).thenReturn(HOST_ID);
|
||||||
|
when(groupVM2.getHostId()).thenReturn(HOST_ID);
|
||||||
|
when(vmInstanceDao.findById(GROUP_VM_1_ID)).thenReturn(groupVM1);
|
||||||
|
when(vmInstanceDao.findById(GROUP_VM_2_ID)).thenReturn(groupVM2);
|
||||||
|
|
||||||
|
when(affinityGroupVMMapDao.listVmIdsByAffinityGroup(AFFINITY_GROUP_ID)).thenReturn(new ArrayList<>(Arrays.asList(GROUP_VM_1_ID, GROUP_VM_2_ID, VM_ID)));
|
||||||
|
|
||||||
|
when(vm.getId()).thenReturn(VM_ID);
|
||||||
|
|
||||||
|
when(affinityGroupVO.getId()).thenReturn(AFFINITY_GROUP_ID);
|
||||||
|
when(affinityGroupVO.getName()).thenReturn(AFFINITY_GROUP_NAME);
|
||||||
|
when(mapVO.getAffinityGroupId()).thenReturn(AFFINITY_GROUP_ID);
|
||||||
|
|
||||||
|
when(affinityGroupDao.findById(AFFINITY_GROUP_ID)).thenReturn(affinityGroupVO);
|
||||||
|
|
||||||
|
when(dest.getHost()).thenReturn(host);
|
||||||
|
when(host.getId()).thenReturn(HOST_ID);
|
||||||
|
when(profile.getVirtualMachine()).thenReturn(vm);
|
||||||
|
when(affinityGroupVMMapDao.findByVmIdType(eq(VM_ID), any())).thenReturn(new ArrayList<>(Arrays.asList(mapVO)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessAffinityGroupMultipleVMs() {
|
||||||
|
processor.processAffinityGroup(mapVO, plan, vm);
|
||||||
|
verify(plan).setPreferredHosts(Arrays.asList(HOST_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessAffinityGroupEmptyGroup() {
|
||||||
|
when(affinityGroupVMMapDao.listVmIdsByAffinityGroup(AFFINITY_GROUP_ID)).thenReturn(new ArrayList<>());
|
||||||
|
processor.processAffinityGroup(mapVO, plan, vm);
|
||||||
|
verify(plan).setPreferredHosts(new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPreferredHostsFromGroupVMIdsMultipleVMs() {
|
||||||
|
List<Long> list = new ArrayList<>(Arrays.asList(GROUP_VM_1_ID, GROUP_VM_2_ID));
|
||||||
|
List<Long> preferredHosts = processor.getPreferredHostsFromGroupVMIds(list);
|
||||||
|
assertNotNull(preferredHosts);
|
||||||
|
assertEquals(1, preferredHosts.size());
|
||||||
|
assertEquals(HOST_ID, preferredHosts.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPreferredHostsFromGroupVMIdsEmptyVMsList() {
|
||||||
|
List<Long> list = new ArrayList<>();
|
||||||
|
List<Long> preferredHosts = processor.getPreferredHostsFromGroupVMIds(list);
|
||||||
|
assertNotNull(preferredHosts);
|
||||||
|
assertTrue(preferredHosts.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityGroup() {
|
||||||
|
assertTrue(processor.checkAffinityGroup(mapVO, vm, HOST_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityGroupWrongHostId() {
|
||||||
|
assertFalse(processor.checkAffinityGroup(mapVO, vm, HOST_2_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheck() {
|
||||||
|
assertTrue(processor.check(profile, dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckWrongHostId() {
|
||||||
|
when(host.getId()).thenReturn(HOST_2_ID);
|
||||||
|
assertFalse(processor.check(profile, dest));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,6 +45,7 @@ import javax.xml.transform.stream.StreamResult;
|
|||||||
|
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.libvirt.Connect;
|
import org.libvirt.Connect;
|
||||||
import org.libvirt.Domain;
|
import org.libvirt.Domain;
|
||||||
@ -332,9 +333,9 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||||||
if ("disk".equals(deviceChildNode.getNodeName())) {
|
if ("disk".equals(deviceChildNode.getNodeName())) {
|
||||||
Node diskNode = deviceChildNode;
|
Node diskNode = deviceChildNode;
|
||||||
|
|
||||||
String sourceFileDevText = getSourceFileDevText(diskNode);
|
String sourceText = getSourceText(diskNode);
|
||||||
|
|
||||||
String path = getPathFromSourceFileDevText(migrateStorage.keySet(), sourceFileDevText);
|
String path = getPathFromSourceText(migrateStorage.keySet(), sourceText);
|
||||||
|
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
MigrateCommand.MigrateDiskInfo migrateDiskInfo = migrateStorage.remove(path);
|
MigrateCommand.MigrateDiskInfo migrateDiskInfo = migrateStorage.remove(path);
|
||||||
@ -383,10 +384,10 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||||||
return getXml(doc);
|
return getXml(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPathFromSourceFileDevText(Set<String> paths, String sourceFileDevText) {
|
private String getPathFromSourceText(Set<String> paths, String sourceText) {
|
||||||
if (paths != null && sourceFileDevText != null) {
|
if (paths != null && !StringUtils.isBlank(sourceText)) {
|
||||||
for (String path : paths) {
|
for (String path : paths) {
|
||||||
if (sourceFileDevText.contains(path)) {
|
if (sourceText.contains(path)) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,7 +396,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSourceFileDevText(Node diskNode) {
|
private String getSourceText(Node diskNode) {
|
||||||
NodeList diskChildNodes = diskNode.getChildNodes();
|
NodeList diskChildNodes = diskNode.getChildNodes();
|
||||||
|
|
||||||
for (int i = 0; i < diskChildNodes.getLength(); i++) {
|
for (int i = 0; i < diskChildNodes.getLength(); i++) {
|
||||||
@ -415,6 +416,20 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo
|
|||||||
if (diskNodeAttribute != null) {
|
if (diskNodeAttribute != null) {
|
||||||
return diskNodeAttribute.getTextContent();
|
return diskNodeAttribute.getTextContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diskNodeAttribute = diskNodeAttributes.getNamedItem("protocol");
|
||||||
|
|
||||||
|
if (diskNodeAttribute != null) {
|
||||||
|
String textContent = diskNodeAttribute.getTextContent();
|
||||||
|
|
||||||
|
if ("rbd".equalsIgnoreCase(textContent)) {
|
||||||
|
diskNodeAttribute = diskNodeAttributes.getNamedItem("name");
|
||||||
|
|
||||||
|
if (diskNodeAttribute != null) {
|
||||||
|
return diskNodeAttribute.getTextContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@
|
|||||||
<module>api/discovery</module>
|
<module>api/discovery</module>
|
||||||
<module>acl/static-role-based</module>
|
<module>acl/static-role-based</module>
|
||||||
<module>acl/dynamic-role-based</module>
|
<module>acl/dynamic-role-based</module>
|
||||||
|
<module>affinity-group-processors/host-affinity</module>
|
||||||
<module>affinity-group-processors/host-anti-affinity</module>
|
<module>affinity-group-processors/host-anti-affinity</module>
|
||||||
<module>affinity-group-processors/explicit-dedication</module>
|
<module>affinity-group-processors/explicit-dedication</module>
|
||||||
<module>ca/root-ca</module>
|
<module>ca/root-ca</module>
|
||||||
|
|||||||
@ -33,6 +33,7 @@ import javax.naming.ConfigurationException;
|
|||||||
import com.cloud.utils.db.Filter;
|
import com.cloud.utils.db.Filter;
|
||||||
import com.cloud.utils.fsm.StateMachine2;
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
|
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
|
||||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||||
@ -321,7 +322,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
suitableHosts.add(host);
|
suitableHosts.add(host);
|
||||||
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
|
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
|
||||||
suitableHosts, suitableVolumeStoragePools, avoids,
|
suitableHosts, suitableVolumeStoragePools, avoids,
|
||||||
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes);
|
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes, plan.getPreferredHosts());
|
||||||
if (potentialResources != null) {
|
if (potentialResources != null) {
|
||||||
pod = _podDao.findById(host.getPodId());
|
pod = _podDao.findById(host.getPodId());
|
||||||
cluster = _clusterDao.findById(host.getClusterId());
|
cluster = _clusterDao.findById(host.getClusterId());
|
||||||
@ -461,7 +462,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
suitableHosts.add(host);
|
suitableHosts.add(host);
|
||||||
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
|
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
|
||||||
suitableHosts, suitableVolumeStoragePools, avoids,
|
suitableHosts, suitableVolumeStoragePools, avoids,
|
||||||
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes);
|
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes, plan.getPreferredHosts());
|
||||||
if (potentialResources != null) {
|
if (potentialResources != null) {
|
||||||
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
|
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
|
||||||
// remove the reused vol<->pool from
|
// remove the reused vol<->pool from
|
||||||
@ -1077,7 +1078,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
// choose the potential host and pool for the VM
|
// choose the potential host and pool for the VM
|
||||||
if (!suitableVolumeStoragePools.isEmpty()) {
|
if (!suitableVolumeStoragePools.isEmpty()) {
|
||||||
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(suitableHosts, suitableVolumeStoragePools, avoid,
|
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(suitableHosts, suitableVolumeStoragePools, avoid,
|
||||||
resourceUsageRequired, readyAndReusedVolumes);
|
resourceUsageRequired, readyAndReusedVolumes, plan.getPreferredHosts());
|
||||||
|
|
||||||
if (potentialResources != null) {
|
if (potentialResources != null) {
|
||||||
Host host = _hostDao.findById(potentialResources.first().getId());
|
Host host = _hostDao.findById(potentialResources.first().getId());
|
||||||
@ -1217,11 +1218,12 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Pair<Host, Map<Volume, StoragePool>> findPotentialDeploymentResources(List<Host> suitableHosts, Map<Volume, List<StoragePool>> suitableVolumeStoragePools,
|
protected Pair<Host, Map<Volume, StoragePool>> findPotentialDeploymentResources(List<Host> suitableHosts, Map<Volume, List<StoragePool>> suitableVolumeStoragePools,
|
||||||
ExcludeList avoid, DeploymentPlanner.PlannerResourceUsage resourceUsageRequired, List<Volume> readyAndReusedVolumes) {
|
ExcludeList avoid, DeploymentPlanner.PlannerResourceUsage resourceUsageRequired, List<Volume> readyAndReusedVolumes, List<Long> preferredHosts) {
|
||||||
s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM");
|
s_logger.debug("Trying to find a potenial host and associated storage pools from the suitable host/pool lists for this VM");
|
||||||
|
|
||||||
boolean hostCanAccessPool = false;
|
boolean hostCanAccessPool = false;
|
||||||
boolean haveEnoughSpace = false;
|
boolean haveEnoughSpace = false;
|
||||||
|
boolean hostAffinityCheck = false;
|
||||||
|
|
||||||
if (readyAndReusedVolumes == null) {
|
if (readyAndReusedVolumes == null) {
|
||||||
readyAndReusedVolumes = new ArrayList<Volume>();
|
readyAndReusedVolumes = new ArrayList<Volume>();
|
||||||
@ -1245,6 +1247,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
s_logger.debug("Checking if host: " + potentialHost.getId() + " can access any suitable storage pool for volume: " + vol.getVolumeType());
|
s_logger.debug("Checking if host: " + potentialHost.getId() + " can access any suitable storage pool for volume: " + vol.getVolumeType());
|
||||||
List<StoragePool> volumePoolList = suitableVolumeStoragePools.get(vol);
|
List<StoragePool> volumePoolList = suitableVolumeStoragePools.get(vol);
|
||||||
hostCanAccessPool = false;
|
hostCanAccessPool = false;
|
||||||
|
hostAffinityCheck = checkAffinity(potentialHost, preferredHosts);
|
||||||
for (StoragePool potentialSPool : volumePoolList) {
|
for (StoragePool potentialSPool : volumePoolList) {
|
||||||
if (hostCanAccessSPool(potentialHost, potentialSPool)) {
|
if (hostCanAccessSPool(potentialHost, potentialSPool)) {
|
||||||
hostCanAccessPool = true;
|
hostCanAccessPool = true;
|
||||||
@ -1273,8 +1276,12 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
s_logger.warn("insufficient capacity to allocate all volumes");
|
s_logger.warn("insufficient capacity to allocate all volumes");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!hostAffinityCheck) {
|
||||||
|
s_logger.debug("Host affinity check failed");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (hostCanAccessPool && haveEnoughSpace && checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired)) {
|
}
|
||||||
|
if (hostCanAccessPool && haveEnoughSpace && hostAffinityCheck && checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired)) {
|
||||||
s_logger.debug("Found a potential host " + "id: " + potentialHost.getId() + " name: " + potentialHost.getName() +
|
s_logger.debug("Found a potential host " + "id: " + potentialHost.getId() + " name: " + potentialHost.getName() +
|
||||||
" and associated storage pools for this VM");
|
" and associated storage pools for this VM");
|
||||||
return new Pair<Host, Map<Volume, StoragePool>>(potentialHost, storage);
|
return new Pair<Host, Map<Volume, StoragePool>>(potentialHost, storage);
|
||||||
@ -1286,6 +1293,20 @@ StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if:
|
||||||
|
* - Affinity is not enabled (preferred host is empty)
|
||||||
|
* - Affinity is enabled and potential host is on the preferred hosts list
|
||||||
|
*
|
||||||
|
* False if not
|
||||||
|
*/
|
||||||
|
@DB
|
||||||
|
public boolean checkAffinity(Host potentialHost, List<Long> preferredHosts) {
|
||||||
|
boolean hostAffinityEnabled = CollectionUtils.isNotEmpty(preferredHosts);
|
||||||
|
boolean hostAffinityMatches = hostAffinityEnabled && preferredHosts.contains(potentialHost.getId());
|
||||||
|
return !hostAffinityEnabled || hostAffinityMatches;
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean hostCanAccessSPool(Host host, StoragePool pool) {
|
protected boolean hostCanAccessSPool(Host host, StoragePool pool) {
|
||||||
boolean hostCanAccessSPool = false;
|
boolean hostCanAccessSPool = false;
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import java.util.Map;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
|
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
@ -325,7 +326,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
|
|||||||
Long zoneId = cmd.getZoneId();
|
Long zoneId = cmd.getZoneId();
|
||||||
// ignore passed zoneId if we are using region wide image store
|
// ignore passed zoneId if we are using region wide image store
|
||||||
List<ImageStoreVO> stores = _imgStoreDao.findRegionImageStores();
|
List<ImageStoreVO> stores = _imgStoreDao.findRegionImageStores();
|
||||||
if (!(stores != null && stores.size() > 0)) {
|
if (CollectionUtils.isEmpty(stores) && zoneId != null && zoneId > 0L) {
|
||||||
zoneList = new ArrayList<>();
|
zoneList = new ArrayList<>();
|
||||||
zoneList.add(zoneId);
|
zoneList.add(zoneId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,15 +16,19 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.vm;
|
package com.cloud.vm;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import com.cloud.host.Host;
|
||||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao;
|
import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
@ -136,9 +140,12 @@ public class DeploymentPlanningManagerImplTest {
|
|||||||
@Inject
|
@Inject
|
||||||
UserVmDetailsDao vmDetailsDao;
|
UserVmDetailsDao vmDetailsDao;
|
||||||
|
|
||||||
private static long domainId = 5L;
|
@Mock
|
||||||
|
Host host;
|
||||||
|
|
||||||
|
private static long domainId = 5L;
|
||||||
private static long dataCenterId = 1L;
|
private static long dataCenterId = 1L;
|
||||||
|
private static long hostId = 1l;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void setUp() throws ConfigurationException {
|
public static void setUp() throws ConfigurationException {
|
||||||
@ -172,6 +179,7 @@ public class DeploymentPlanningManagerImplTest {
|
|||||||
planners.add(_planner);
|
planners.add(_planner);
|
||||||
_dpm.setPlanners(planners);
|
_dpm.setPlanners(planners);
|
||||||
|
|
||||||
|
Mockito.when(host.getId()).thenReturn(hostId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -222,6 +230,26 @@ public class DeploymentPlanningManagerImplTest {
|
|||||||
assertNull("Planner cannot handle, destination should be null! ", dest);
|
assertNull("Planner cannot handle, destination should be null! ", dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityEmptyPreferredHosts() {
|
||||||
|
assertTrue(_dpm.checkAffinity(host, new ArrayList<>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityNullPreferredHosts() {
|
||||||
|
assertTrue(_dpm.checkAffinity(host, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityNotEmptyPreferredHostsContainingHost() {
|
||||||
|
assertTrue(_dpm.checkAffinity(host, Arrays.asList(3l, 4l, hostId, 2l)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCheckAffinityNotEmptyPreferredHostsNotContainingHost() {
|
||||||
|
assertFalse(_dpm.checkAffinity(host, Arrays.asList(3l, 4l, 2l)));
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan(basePackageClasses = {DeploymentPlanningManagerImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class,
|
@ComponentScan(basePackageClasses = {DeploymentPlanningManagerImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class,
|
||||||
type = FilterType.CUSTOM)}, useDefaultFilters = false)
|
type = FilterType.CUSTOM)}, useDefaultFilters = false)
|
||||||
|
|||||||
@ -21,8 +21,10 @@ from marvin.cloudstackTestCase import *
|
|||||||
from marvin.cloudstackAPI import *
|
from marvin.cloudstackAPI import *
|
||||||
from marvin.lib.utils import *
|
from marvin.lib.utils import *
|
||||||
from marvin.lib.base import *
|
from marvin.lib.base import *
|
||||||
from marvin.lib.common import *
|
from marvin.lib.common import (get_domain,
|
||||||
from marvin.sshClient import SshClient
|
get_zone,
|
||||||
|
get_template,
|
||||||
|
list_virtual_machines)
|
||||||
from nose.plugins.attrib import attr
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
class TestDeployVmWithAffinityGroup(cloudstackTestCase):
|
class TestDeployVmWithAffinityGroup(cloudstackTestCase):
|
||||||
@ -42,14 +44,14 @@ class TestDeployVmWithAffinityGroup(cloudstackTestCase):
|
|||||||
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
|
||||||
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
cls.hypervisor = cls.testClient.getHypervisorInfo()
|
||||||
|
|
||||||
cls.template = get_test_template(
|
cls.template = get_template(
|
||||||
cls.apiclient,
|
cls.apiclient,
|
||||||
cls.zone.id,
|
cls.zone.id,
|
||||||
cls.hypervisor
|
cls.hypervisor
|
||||||
)
|
)
|
||||||
|
|
||||||
if cls.template == FAILED:
|
if cls.template == FAILED:
|
||||||
assert False, "get_test_template() failed to return template"
|
assert False, "get_template() failed to return template"
|
||||||
|
|
||||||
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
|
||||||
|
|
||||||
@ -69,6 +71,16 @@ class TestDeployVmWithAffinityGroup(cloudstackTestCase):
|
|||||||
cls.ag = AffinityGroup.create(cls.apiclient, cls.services["virtual_machine"]["affinity"],
|
cls.ag = AffinityGroup.create(cls.apiclient, cls.services["virtual_machine"]["affinity"],
|
||||||
account=cls.account.name, domainid=cls.domain.id)
|
account=cls.account.name, domainid=cls.domain.id)
|
||||||
|
|
||||||
|
host_affinity = {
|
||||||
|
"name": "marvin-host-affinity",
|
||||||
|
"type": "host affinity",
|
||||||
|
}
|
||||||
|
cls.affinity = AffinityGroup.create(
|
||||||
|
cls.apiclient,
|
||||||
|
host_affinity,
|
||||||
|
account=cls.account.name,
|
||||||
|
domainid=cls.domain.id
|
||||||
|
)
|
||||||
cls._cleanup = [
|
cls._cleanup = [
|
||||||
cls.service_offering,
|
cls.service_offering,
|
||||||
cls.ag,
|
cls.ag,
|
||||||
@ -152,6 +164,81 @@ class TestDeployVmWithAffinityGroup(cloudstackTestCase):
|
|||||||
self.assertNotEqual(host_of_vm1, host_of_vm2,
|
self.assertNotEqual(host_of_vm1, host_of_vm2,
|
||||||
msg="Both VMs of affinity group %s are on the same host" % self.ag.name)
|
msg="Both VMs of affinity group %s are on the same host" % self.ag.name)
|
||||||
|
|
||||||
|
@attr(tags=["basic", "advanced", "multihost"], required_hardware="false")
|
||||||
|
def test_DeployVmAffinityGroup(self):
|
||||||
|
"""
|
||||||
|
test DeployVM in affinity groups
|
||||||
|
|
||||||
|
deploy VM1 and VM2 in the same host-affinity groups
|
||||||
|
Verify that the vms are deployed on the same host
|
||||||
|
"""
|
||||||
|
#deploy VM1 in affinity group created in setUp
|
||||||
|
vm1 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
affinitygroupnames=[self.affinity.name]
|
||||||
|
)
|
||||||
|
|
||||||
|
list_vm1 = list_virtual_machines(
|
||||||
|
self.apiclient,
|
||||||
|
id=vm1.id
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
isinstance(list_vm1, list),
|
||||||
|
True,
|
||||||
|
"Check list response returns a valid list"
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
len(list_vm1),
|
||||||
|
0,
|
||||||
|
"Check VM available in List Virtual Machines"
|
||||||
|
)
|
||||||
|
vm1_response = list_vm1[0]
|
||||||
|
self.assertEqual(
|
||||||
|
vm1_response.state,
|
||||||
|
'Running',
|
||||||
|
msg="VM is not in Running state"
|
||||||
|
)
|
||||||
|
host_of_vm1 = vm1_response.hostid
|
||||||
|
|
||||||
|
#deploy VM2 in affinity group created in setUp
|
||||||
|
vm2 = VirtualMachine.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["virtual_machine"],
|
||||||
|
templateid=self.template.id,
|
||||||
|
accountid=self.account.name,
|
||||||
|
domainid=self.account.domainid,
|
||||||
|
serviceofferingid=self.service_offering.id,
|
||||||
|
affinitygroupnames=[self.affinity.name]
|
||||||
|
)
|
||||||
|
list_vm2 = list_virtual_machines(
|
||||||
|
self.apiclient,
|
||||||
|
id=vm2.id
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
isinstance(list_vm2, list),
|
||||||
|
True,
|
||||||
|
"Check list response returns a valid list"
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
len(list_vm2),
|
||||||
|
0,
|
||||||
|
"Check VM available in List Virtual Machines"
|
||||||
|
)
|
||||||
|
vm2_response = list_vm2[0]
|
||||||
|
self.assertEqual(
|
||||||
|
vm2_response.state,
|
||||||
|
'Running',
|
||||||
|
msg="VM is not in Running state"
|
||||||
|
)
|
||||||
|
host_of_vm2 = vm2_response.hostid
|
||||||
|
|
||||||
|
self.assertEqual(host_of_vm1, host_of_vm2,
|
||||||
|
msg="Both VMs of affinity group %s are on different hosts" % self.affinity.name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user