cloudstack/test/integration/component/test_interpod_migration.py
Abhishek Kumar 3b6ce97097
infra: edge zones (#6840)
Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: dahn <daan@onecht.net>
2023-01-31 09:36:45 +01:00

465 lines
19 KiB
Python

# 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.
""" BVT tests for Virtual Machine migration across pods
"""
# Import Local Modules
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackAPI import (attachVolume,
detachVolume,
deleteVolume,
attachIso,
detachIso,
deleteIso,
startVirtualMachine,
stopVirtualMachine,
migrateVirtualMachineWithVolume)
from marvin.lib.utils import (cleanup_resources)
from marvin.lib.base import (Account,
Host,
Pod,
StoragePool,
ServiceOffering,
DiskOffering,
VirtualMachine,
Iso,
Volume)
from marvin.lib.common import (get_domain,
get_zone,
get_template)
from marvin.lib.decoratorGenerators import skipTestIf
from marvin.codes import FAILED, PASS
from nose.plugins.attrib import attr
# Import System modules
import time
_multiprocess_shared_ = True
class TestVMMigration(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestVMMigration, 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.services['mode'] = cls.zone.networktype
cls.cleanup = []
cls.hypervisorNotSupported = False
cls.hypervisor = cls.testClient.getHypervisorInfo()
if cls.hypervisor.lower() not in ['vmware']:
cls.hypervisorNotSupported = True
if cls.hypervisorNotSupported == False:
cls.pods = Pod.list(cls.apiclient, zoneid=cls.zone.id, listall=True)
if len(cls.pods) < 2:
assert False, "Not enough pods found: %d" % len(cls.pods)
cls.computeOfferingStorageTags = None
cls.diskOfferingStorageTags = None
for pod in cls.pods:
podStoragePools = StoragePool.list(
cls.apiclient,
scope='CLUSTER',
podid=pod.id)
if len(podStoragePools) < 1:
assert False, "Not enough CLUSTER scope storage pools found for pod: %s" % pod.id
taggedPool = []
for pool in podStoragePools:
if pool.tags != None and len(pool.tags) > 0:
taggedPool.append(pool)
if len(taggedPool) < 2:
assert False, "No CLUSTER scope, tagged storage pools found for pod: %s" % pod.id
if cls.computeOfferingStorageTags == None:
cls.computeOfferingStorageTags = taggedPool[0].tags
if cls.diskOfferingStorageTags == None:
cls.diskOfferingStorageTags = taggedPool[1].tags
template = get_template(
cls.apiclient,
cls.zone.id,
cls.services["ostype"])
if template == FAILED:
assert False, "get_template() failed to return template with description %s" % cls.services["ostype"]
# Set Zones and disk offerings
cls.services["small"]["zoneid"] = cls.zone.id
cls.services["small"]["template"] = template.id
cls.services["iso"]["zoneid"] = cls.zone.id
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
domainid=cls.domain.id)
cls.debug(cls.account.id)
compute_offering_service = cls.services["service_offerings"]["tiny"].copy()
compute_offering_service["tags"] = cls.computeOfferingStorageTags
cls.service_offering = ServiceOffering.create(
cls.apiclient,
compute_offering_service)
disk_offering_service = cls.services["disk_offering"].copy()
disk_offering_service["disksize"] = 1
cls.untagged_disk_offering = DiskOffering.create(
cls.apiclient,
disk_offering_service)
disk_offering_service["tags"] = cls.diskOfferingStorageTags
cls.tagged_disk_offering = DiskOffering.create(
cls.apiclient,
disk_offering_service)
cls.hostId = None
host = cls.getOldestHost(cls.pods[0].id, cls.pods[1].id)
if host != None:
cls.hostId = host.id
cls.cleanup = [
cls.service_offering,
cls.untagged_disk_offering,
cls.tagged_disk_offering,
cls.account
]
@classmethod
def tearDownClass(cls):
try:
cleanup_resources(cls.apiclient, cls.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.virtual_machine = None
if self.hypervisorNotSupported == False:
self.virtual_machine = VirtualMachine.create(
self.apiclient,
self.services["small"],
accountid=self.account.name,
domainid=self.account.domainid,
serviceofferingid=self.service_offering.id,
mode=self.services['mode'],
hostid=self.hostId
)
self.cleanup = []
def tearDown(self):
try:
if self.virtual_machine != None:
self.virtual_machine.delete(self.apiclient, expunge=True)
# Clean up, terminate the created accounts, domains etc
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
@classmethod
def getOldestHost(cls, pod1_id, pod2_id):
selectedHost = None
hosts = Host.list(cls.apiclient, type='Routing', podid=pod1_id)
morehosts = Host.list(cls.apiclient, type='Routing', podid=pod2_id)
if isinstance(morehosts, list) and len(morehosts)>0:
if isinstance(hosts, list) and len(hosts)>0:
hosts.extend(morehosts)
if isinstance(hosts, list) and len(hosts)>0:
selectedHost = hosts[0]
# Very basic way to get lowest version host
for host in hosts:
if int(host.hypervisorversion.replace(".", "")) < int(selectedHost.hypervisorversion.replace(".", "")):
selectedHost = host
return selectedHost
@skipTestIf("hypervisorNotSupported")
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_01_migrate_running_vm(self):
"""Test Running Virtual Machine Migration Without DATA disk or ISO
"""
# Validate the following:
# 1. Start VM if not running
# 2. Migrate VM to a different pod multiple times
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id)
if vmResponse.state != 'Running':
self.startVm(vmResponse.id)
migrationCount = 1
while migrationCount > 0:
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id, 'Running')
hostId = self.getDifferentPodHost(vmResponse.id, vmResponse.hostid).id
self.debug("#%d migration, current host ID: %s, new host ID: %s" % ((2-migrationCount), vmResponse.hostid, hostId))
self.migrateVmWithVolume(vmResponse.id, hostId)
migrationCount = migrationCount - 1
if migrationCount > 0:
time.sleep(self.services["sleep"])
return
@skipTestIf("hypervisorNotSupported")
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_02_migrate_running_vm_with_disk_and_iso(self):
"""Test Running Virtual Machine Migration With DATA disks or ISO
"""
# Validate the following:
# 1. Start VM if not running
# 2. Add disks and ISO to the VM
# 3. Migrate VM to a different pod multiple times
# 4. Remove disks and ISO from the VM
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id)
if vmResponse.state != 'Running':
self.startVm(vmResponse.id)
vol1 = self.addVolumeToVm(vmResponse.id, self.tagged_disk_offering)
vol2 = self.addVolumeToVm(vmResponse.id, self.untagged_disk_offering)
# self.addIsoToVm(vmResponse.id)
migrationCount = 1
while migrationCount > 0:
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id, 'Running')
hostId = self.getDifferentPodHost(vmResponse.id, vmResponse.hostid).id
self.debug("#%d migration, current host ID: %s, new host ID: %s" % ((2-migrationCount), vmResponse.hostid, hostId))
self.migrateVmWithVolume(vmResponse.id, hostId)
migrationCount = migrationCount - 1
if migrationCount > 0:
time.sleep(self.services["sleep"])
self.removeVolumeFromVm(vol1.id)
self.removeVolumeFromVm(vol2.id)
# self.removeIsoFromVm(vmResponse.id, vmResponse.isoid)
return
@skipTestIf("hypervisorNotSupported")
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_03_migrate_stopped_vm(self):
"""Test Stopped Virtual Machine Migration Without DATA disk or ISO
"""
# Validate the following:
# 1. Stop VM if not already stopped
# 2. Migrate VM to a different pod multiple times with volume to pool mapping
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id)
if vmResponse.state != 'Stopped':
self.stopVm(vmResponse.id)
migrationCount = 3
while migrationCount > 0:
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id, 'Stopped')
migrateTo = self.getDifferentPodVolumeStoragePoolMapping(vmResponse.id)
self.debug("#%d migration, mapping: %s" % ((4-migrationCount), migrateTo))
self.migrateVmWithVolume(vmResponse.id, None, migrateTo)
migrationCount = migrationCount - 1
if migrationCount > 0:
time.sleep(self.services["sleep"])
return
@skipTestIf("hypervisorNotSupported")
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
def test_04_migrate_stopped_vm_with_disk_and_iso(self):
"""Test Stopped Virtual Machine Migration With DATA disk or ISO
"""
# Validate the following:
# 1. Start VM if not running
# 2. Add disks and ISO to the VM
# 3. Stop the VM
# 4. Migrate VM to a different pod multiple times with volume to pool mapping
# 5. Start VM and remove disks and ISO from the VM
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id)
if vmResponse.state != 'Running':
self.startVm(vmResponse.id)
vol1 = self.addVolumeToVm(vmResponse.id, self.tagged_disk_offering)
vol2 = self.addVolumeToVm(vmResponse.id, self.untagged_disk_offering)
# self.addIsoToVm(vmResponse.id)
self.stopVm(vmResponse.id)
migrationCount = 3
while migrationCount > 0:
vmResponse = self.getVmVerifiedResponse(self.virtual_machine.id, 'Stopped')
migrateTo = self.getDifferentPodVolumeStoragePoolMapping(vmResponse.id)
self.debug("#%d migration, mapping: %s" % ((4-migrationCount), migrateTo))
self.migrateVmWithVolume(vmResponse.id, None, migrateTo)
migrationCount = migrationCount - 1
if migrationCount > 0:
time.sleep(self.services["sleep"])
self.removeVolumeFromVm(vol1.id)
self.removeVolumeFromVm(vol2.id)
# self.removeIsoFromVm(vmResponse.id, vmResponse.isoid)
return
def startVm(self, vm_id):
startVirtualMachineCmd = startVirtualMachine.startVirtualMachineCmd()
startVirtualMachineCmd.id = vm_id
self.apiclient.startVirtualMachine(startVirtualMachineCmd)
def stopVm(self, vm_id):
stopVirtualMachineCmd = stopVirtualMachine.stopVirtualMachineCmd()
stopVirtualMachineCmd.id = vm_id
self.apiclient.stopVirtualMachine(stopVirtualMachineCmd)
def addVolumeToVm(self, vm_id, disk_offering):
volume = Volume.create(
self.apiclient,
self.services["volume"],
zoneid=self.zone.id,
diskofferingid=disk_offering.id,
account=self.account.name,
domainid=self.account.domainid)
cmd = attachVolume.attachVolumeCmd()
cmd.id = volume.id
cmd.virtualmachineid = vm_id
attachedVolume = self.apiclient.attachVolume(cmd)
return attachedVolume
def removeVolumeFromVm(self, volume_id):
cmd = detachVolume.detachVolumeCmd()
cmd.id = volume_id
detachedVolume = self.apiclient.detachVolume(cmd)
cmd = deleteVolume.deleteVolumeCmd()
cmd.id = volume_id
self.apiclient.deleteVolume(cmd)
return
def addIsoToVm(self, vm_id):
iso = Iso.create(
self.apiclient,
self.services["iso"],
account=self.account.name,
domainid=self.account.domainid)
cmd = attachIso.attachIsoCmd()
cmd.id = iso.id
cmd.virtualmachineid = vm_id
attachedIso = self.apiclient.attachIso(cmd)
return
def removeIsoFromVm(self, vm_id, iso_id):
cmd = detachIso.detachIsoCmd()
cmd.virtualmachineid = vm_id
self.apiclient.detachIso(cmd)
cmd = deleteIso.deleteIsoCmd()
cmd.id = iso_id
self.apiclient.deleteIso(cmd)
return
def getVmVerifiedResponse(self, vm_id, state=None):
list_vm_response = VirtualMachine.list(
self.apiclient,
id=self.virtual_machine.id)
self.debug(
"Verify listVirtualMachines response for virtual machine: %s" \
% self.virtual_machine.id)
self.assertEqual(
isinstance(list_vm_response, list),
True,
"Check list response returns a valid list")
self.assertNotEqual(
len(list_vm_response),
0,
"Check VM available in List Virtual Machines")
vmResponse = list_vm_response[0]
if state != None:
self.assertEqual(
vmResponse.state,
state,
"VM not in state: %s" % state)
return vmResponse
def getDifferentPodHost(self, vm_id, host_id):
host = None
currentHost = Host.list(self.apiclient, id=host_id)
self.assertEqual(
isinstance(currentHost, list),
True,
"Check host list response returns a valid list")
self.assertNotEqual(
len(currentHost),
0,
"Check current host for VM ID: %s available in List Hosts" % vm_id)
currentHost = currentHost[0]
hosts = Host.listForMigration(self.apiclient, virtualmachineid=vm_id)
self.assertEqual(
isinstance(hosts, list),
True,
"Check host list response returns a valid list")
self.assertNotEqual(
len(hosts),
0,
"Hosts suitable for migration for VM ID: %s not found" % vm_id)
for hostForMigration in hosts:
if hostForMigration.podid != currentHost.podid:
host = hostForMigration
break
self.assertNotEqual(
host,
None,
"Host suitable for migration for VM ID: %s in a different pod not found" % vm_id)
return host
def getPodStoragePoolWithTags(self, pod_id, tags=None):
pool = None
storage_pools = StoragePool.list(
self.apiclient,
podid=pod_id,
listall=True)
if isinstance(storage_pools, list) and len(storage_pools) > 0:
if tags != None:
for storage_pool in storage_pools:
if storage_pool.tags == tags:
pool = storage_pool
break
else:
pool = storage_pool[0]
return pool
def getDifferentPodVolumeStoragePoolMapping(self, vm_id):
rootVolume = Volume.list(self.apiclient, virtualmachineid=vm_id, listall=True, type='ROOT')
self.assertEqual(
isinstance(rootVolume, list),
True,
"Check VM volumes list response returns a valid list")
self.assertNotEqual(
len(rootVolume),
0,
"Check VM ROOT volume available in List Volumes")
rootVolume = rootVolume[0]
volumeStoragePool = StoragePool.list(
self.apiclient,
id=rootVolume.storageid)
self.assertEqual(
isinstance(volumeStoragePool, list),
True,
"Check VM ROOT Volume storage list response returns a valid list")
self.assertNotEqual(
len(volumeStoragePool),
0,
"Check VM ROOT Volume storage available in List Storage Pools")
volumeStoragePool = volumeStoragePool[0]
podId = self.pods[0].id
if volumeStoragePool.podid == podId:
podId = self.pods[1].id
pool = self.getPodStoragePoolWithTags(podId, self.computeOfferingStorageTags)
self.assertNotEqual(
pool,
None,
"Target storage pool mapping for VM ID: %s failed" % vm_id)
migrateTo = { "volume": rootVolume.id, "pool": pool.id}
return [migrateTo]
def migrateVmWithVolume(self, vm_id, host_id, migrate_to=None):
migrateVirtualMachineWithVolumeCmd = migrateVirtualMachineWithVolume.migrateVirtualMachineWithVolumeCmd()
migrateVirtualMachineWithVolumeCmd.virtualmachineid = vm_id
if host_id != None:
migrateVirtualMachineWithVolumeCmd.hostid = host_id
if migrate_to != None:
migrateVirtualMachineWithVolumeCmd.migrateto = migrate_to
response = self.apiclient.migrateVirtualMachineWithVolume(migrateVirtualMachineWithVolumeCmd)
return response