bvt: marvin test for the affinity groups feature

The test deploys two VMs in the simulator context and verifies that the
default host -antiaffinity processor placed the VMs on two distinct
hosts.

Signed-off-by: Prasanna Santhanam <tsp@apache.org>
This commit is contained in:
Prasanna Santhanam 2013-04-08 23:18:00 +05:30 committed by Prachi Damle
parent e52bf52238
commit 79812c253f
3 changed files with 203 additions and 128 deletions

View File

@ -1,4 +1,3 @@
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
@ -34,8 +33,8 @@
<!--
OSS deployment component configuration
-->
<bean id="databaseUpgradeChecker" class="com.cloud.upgrade.DatabaseUpgradeChecker" />
<bean id="configurationDaoImpl" class="com.cloud.configuration.dao.ConfigurationDaoImpl" />
<bean id="databaseUpgradeChecker" class="com.cloud.upgrade.DatabaseUpgradeChecker"/>
<bean id="configurationDaoImpl" class="com.cloud.configuration.dao.ConfigurationDaoImpl"/>
<!-- simulator components -->
<bean id="SimulatorSecondaryDiscoverer" class="com.cloud.resource.SimulatorSecondaryDiscoverer">
@ -122,9 +121,9 @@
<bean id="deploymentPlanners" class="com.cloud.utils.component.AdapterList">
<property name="Adapters">
<list>
<ref bean="FirstFitPlanner" />
<ref bean="UserDispersingPlanner" />
<ref bean="UserConcentratedPodPlanner" />
<ref bean="FirstFitPlanner"/>
<ref bean="UserDispersingPlanner"/>
<ref bean="UserConcentratedPodPlanner"/>
<!--
<ref bean="BareMetalPlanner" />
@ -215,6 +214,16 @@
</property>
</bean>
<bean id="GlobalLoadBalancingRulesServiceImpl" class ="org.apache.cloudstack.region.gslb.GlobalLoadBalancingRulesServiceImpl" />
<bean id="GlobalLoadBalancingRulesServiceImpl"
class="org.apache.cloudstack.region.gslb.GlobalLoadBalancingRulesServiceImpl"/>
<!--
AffinityGroup Processors
-->
<bean id="HostAntiAffinityProcessor" class="org.apache.cloudstack.affinity.HostAntiAffinityProcessor">
<property name="name" value="HostAntiAffinityProcessor"/>
<property name="type" value="host anti-affinity"/>
</bean>
</beans>

View File

@ -16,142 +16,181 @@
# specific language governing permissions and limitations
# under the License.
import marvin
from marvin.cloudstackTestCase import *
from marvin.remoteSSHClient import remoteSSHClient
import hashlib
import random
from marvin.cloudstackAPI import *
from marvin.integration.lib.utils import *
from marvin.integration.lib.base import *
from marvin.integration.lib.common import *
from marvin import remoteSSHClient
from nose.plugins.attrib import attr
class Services:
"""Test Account Services
"""
def __init__(self):
self.services = {
"domain": {
"name": "Domain",
},
"account": {
"email": "test@test.com",
"firstname": "Test",
"lastname": "User",
"username": "test",
# Random characters are appended for unique
# username
"password": "password",
},
"service_offering": {
"name": "Tiny Instance",
"displaytext": "Tiny Instance",
"cpunumber": 1,
"cpuspeed": 100,
# in MHz
"memory": 64,
# In MBs
},
"ostype": 'CentOS 5.3 (64-bit)',
"mode": 'advanced',
"affinity": {
"name": "webvms",
"type": "host anti-affinity",
}
}
class TestDeployVmWithAffinityGroup(cloudstackTestCase):
"""
This test deploys a virtual machine into a user account
using the small service offering and builtin template
"""
def setUp(self):
"""
CloudStack internally saves its passwords in md5 form and that is how we
specify it in the API. Python's hashlib library helps us to quickly hash
strings as follows
"""
mdf = hashlib.md5()
mdf.update('password')
mdf_pass = mdf.hexdigest()
self.apiClient = self.testClient.getApiClient() #Get ourselves an API client
@classmethod
def setUpClass(cls):
cls.api_client = super(TestDeployVmWithAffinityGroup, cls).getClsTestClient().getApiClient()
cls.services = Services().services
# Get Zone, Domain and templates
cls.domain = get_domain(cls.api_client, cls.services)
cls.zone = get_zone(cls.api_client, cls.services)
cls.disk_offering = DiskOffering.create(
cls.api_client,
cls.services["disk_offering"]
)
cls.template = get_template(
cls.api_client,
cls.zone.id,
cls.services["ostype"]
)
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
cls.services["volume"]["zoneid"] = cls.zone.id
self.acct = createAccount.createAccountCmd() #The createAccount command
self.acct.accounttype = 0 #We need a regular user. admins have accounttype=1
self.acct.firstname = 'test'
self.acct.lastname = 'user' #What's up doc?
self.acct.password = mdf_pass #The md5 hashed password string
self.acct.username = 'testuser'
self.acct.email = 'testuser@xyz.com'
self.acct.account = 'testacct'
self.acct.domainid = 1 #The default ROOT domain
self.acctResponse = self.apiClient.createAccount(self.acct)
# And upon successful creation we'll log a helpful message in our logs
# using the default debug logger of the test framework
self.debug("successfully created account: %s, user: %s, id: \
%s"%(self.acctResponse.account.account, \
self.acctResponse.account.username, \
self.acctResponse.account.id))
cls.services["template"] = cls.template.id
cls.services["zoneid"] = cls.zone.id
cls.account = Account.create(
cls.api_client,
cls.services["account"],
domainid=cls.domain.id
)
self.zone = listZones.listZonesCmd()
self.zone.uuid = self.apiClient.listZones(self.zone)[0].id
cls.services["account"] = cls.account.account.name
self.service_offering = listServiceOfferings.listServiceOfferingsCmd()
self.service_offering.uuid = self.apiClient.listServiceOfferings(self.service_offering)[0].id
cls.service_offering = ServiceOffering.create(
cls.api_client,
cls.services["service_offering"]
)
self.template = listTemplates.listTemplatesCmd()
self.template.templatefilter = 'featured'
self.template.name = 'CentOS'
self.template.uuid = self.apiClient.listTemplates(self.template)[0].id
cls.ag = AffinityGroup.create(cls.api_client, cls.services["affinity"], domainid=cls.domain.id)
def test_DeployVm(self):
cls._cleanup = [
cls.service_offering,
cls.disk_offering,
cls.account,
]
return
@attr(tags=["simulator", "basic", "advanced"])
def test_DeployVmAntiAffinityGroup(self):
"""
Let's start by defining the attributes of our VM that we will be
deploying on CloudStack. We will be assuming a single zone is available
and is configured and all templates are Ready
The hardcoded values are used only for brevity.
First create the host anti-affinity group for this account
Deploys a couple of VMs in the same affinity group and verifies they are not on the same host
"""
createAGCmd = createAffinityGroup.createAffinityGroupCmd()
createAGCmd.name = 'webvms1'
createAGCmd.type = 'host anti-affinity'
createAGCmd.account = self.acct.account
createAGCmd.domainid = self.acct.domainid
#deploy VM1 in affinity group created in setUp
vm1 = VirtualMachine.create(
self.api_client,
self.services["virtual_machine"],
templateid=self.template.id,
accountid=self.account.account.name,
domainid=self.account.account.domainid,
serviceofferingid=self.service_offering.id,
affinitygroupnames=self.ag.name,
mode=self.services["mode"]
)
createAGResponse = self.apiClient.createAffinityGroup(createAGCmd)
self.debug("AffinityGroup %s was created in the job %s"%(createAGResponse.id, createAGResponse.jobid))
list_vm1 = list_virtual_machines(
self.api_client,
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.api_client,
self.services["virtual_machine"],
templateid=self.template.id,
accountid=self.account.account.name,
domainid=self.account.account.domainid,
serviceofferingid=self.service_offering.id,
affinitygroupnames=self.ag.name,
mode=self.services["mode"]
)
list_vm2 = list_virtual_machines(
self.api_client,
id=self.vm1.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.assertNotEqual(host_of_vm1, host_of_vm2,
msg="Both VMs of affinity group %s are on the same host" % self.ag.name)
deployVmCmd = deployVirtualMachine.deployVirtualMachineCmd()
deployVmCmd.zoneid = self.zone.uuid
deployVmCmd.templateid = self.template.uuid #CentOS 5.6 builtin
deployVmCmd.serviceofferingid = self.service_offering.uuid
deployVmCmd.account = self.acct.account
deployVmCmd.domainid = self.acct.domainid
deployVmCmd.affinitygroupnames=[]
deployVmCmd.affinitygroupnames.append(str(createAGResponse.name))
deployVmResponse = self.apiClient.deployVirtualMachine(deployVmCmd)
self.debug("VM %s was deployed in the job %s"%(deployVmResponse.id, deployVmResponse.jobid))
# At this point our VM is expected to be Running. Let's find out what
# listVirtualMachines tells us about VMs in this account
listVmCmd = listVirtualMachines.listVirtualMachinesCmd()
listVmCmd.id = deployVmResponse.id
listVmResponse = self.apiClient.listVirtualMachines(listVmCmd)
self.assertNotEqual(len(listVmResponse), 0, "Check if the list API \
returns a non-empty response")
vm = listVmResponse[0]
self.assertEqual(vm.state, "Running", "Check if VM has reached Running state in CS")
VM1hostid = vm.hostid
#Deploy another VM in same affinity group
deployVm2Cmd = deployVirtualMachine.deployVirtualMachineCmd()
deployVm2Cmd.zoneid = self.zone.uuid
deployVm2Cmd.templateid = self.template.uuid #CentOS 5.6 builtin
deployVm2Cmd.serviceofferingid = self.service_offering.uuid
deployVm2Cmd.account = self.acct.account
deployVm2Cmd.domainid = self.acct.domainid
deployVm2Cmd.affinitygroupnames=[]
deployVm2Cmd.affinitygroupnames.append(str(createAGResponse.name))
deployVm2Response = self.apiClient.deployVirtualMachine(deployVm2Cmd)
self.debug("VM2 %s was deployed in the job %s"%(deployVm2Response.id, deployVm2Response.jobid))
# At this point our VM is expected to be Running. Let's find out what
# listVirtualMachines tells us about VMs in this account
listVm2Cmd = listVirtualMachines.listVirtualMachinesCmd()
listVm2Cmd.id = deployVm2Response.id
listVm2Response = self.apiClient.listVirtualMachines(listVm2Cmd)
self.assertNotEqual(len(listVm2Response), 0, "Check if the list API \
returns a non-empty response")
vm2 = listVm2Response[0]
self.assertEqual(vm2.state, "Running", "Check if VM has reached Running state in CS")
VM2hostid = vm2.hostid
self.assertNotEqual(VM1hostid, VM2hostid, "The hosts of the 2 VM's in the host anti-affinity group are not different, test failed")
def tearDown(self):
"""
And finally let us cleanup the resources we created by deleting the
account. All good unittests are atomic and rerunnable this way
"""
deleteAcct = deleteAccount.deleteAccountCmd()
deleteAcct.id = self.acctResponse.account.id
self.apiClient.deleteAccount(deleteAcct)
self.testClient.close()
@classmethod
def tearDown(cls):
try:
cls.api_client = super(TestDeployVmWithAffinityGroup, cls).getClsTestClient().getApiClient()
#Clean up, terminate the created templates
cleanup_resources(cls.api_client, cls.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)

View File

@ -223,7 +223,7 @@ class VirtualMachine:
def create(cls, apiclient, services, templateid=None, accountid=None,
domainid=None, zoneid=None, networkids=None, serviceofferingid=None,
securitygroupids=None, projectid=None, startvm=None,
diskofferingid=None, hostid=None, mode='basic'):
diskofferingid=None, affinitygroupname=None, hostid=None, mode='basic'):
"""Create the instance"""
cmd = deployVirtualMachine.deployVirtualMachineCmd()
@ -268,6 +268,9 @@ class VirtualMachine:
if "userdata" in services:
cmd.userdata = base64.b64encode(services["userdata"])
if "affinitygroupnames" in services:
cmd.affinitygroupnames = services["affinitygroupnames"]
if projectid:
cmd.projectid = projectid
@ -2424,3 +2427,27 @@ class VPC:
cmd = listVPCs.listVPCsCmd()
[setattr(cmd, k, v) for k, v in kwargs.items()]
return(apiclient.listVPCs(cmd))
class AffinityGroup:
def __init__(self, items):
self.__dict__.update(items)
@classmethod
def create(cls, apiclient, services, account=None, domainid=None):
agCmd = createAffinityGroup.createAffinityGroupCmd()
agCmd.name = services['name']
agCmd.displayText = services['displaytext'] if 'displaytext' in services else services['name']
agCmd.type = services['type']
agCmd.account = services['account'] if 'account' in services else account
agCmd.domainid = services['domainid'] if 'domainid' in services else domainid
def update(self):
pass
def delete(self):
pass
@classmethod
def list(cls):
pass