Skip to content

Commit 2addfa1

Browse files
committed
test: fix tests failures caused by others in drs and host maintenance
1 parent d6abf0d commit 2addfa1

2 files changed

Lines changed: 63 additions & 15 deletions

File tree

test/integration/smoke/test_cluster_drs.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,29 @@ def migrateSvms(cls, cluster):
128128
responseS = cls.apiclient.listSystemVms(cmds)
129129
if isinstance(responseS, Iterable):
130130
for svm in responseS:
131-
if svm.hostid != cls.hosts[0].id:
131+
if svm.hostid != cls.hosts[0].id and svm.state == 'Running':
132132
systemVmIds.append(svm.id)
133133
cmdv = listRouters.listRoutersCmd()
134134
responseR = cls.apiclient.listRouters(cmdv)
135135
if isinstance(responseR, Iterable):
136136
for svm in responseR:
137-
if svm.hostid != cls.hosts[0].id:
137+
if svm.hostid != cls.hosts[0].id and svm.state == 'Running':
138138
systemVmIds.append(svm.id)
139139
numToMigrate = len(systemVmIds)
140140
cls.logger.debug(f'system vms and routers to migrate -- {numToMigrate}')
141141
cmdM = migrateSystemVm.migrateSystemVmCmd()
142142
cmdM.hostId=cls.hosts[0].id
143143
for id in systemVmIds:
144144
cmdM.virtualmachineid=id
145-
responseM = cls.apiclient.migrateSystemVm(cmdM)
146-
cls.logger.debug(f'migrated {responseM}')
145+
try:
146+
responseM = cls.apiclient.migrateSystemVm(cmdM)
147+
cls.logger.debug(f'migrated {responseM}')
148+
except Exception as e:
149+
# A system vm/router may have moved out of Running state (e.g. redundant
150+
# router failover, or an in-progress restart from a previous test's cleanup)
151+
# between the listSystemVms/listRouters call above and this migration attempt.
152+
# Don't fail the whole test class setup for one such VM.
153+
cls.logger.debug(f'Skipping migration of {id}, it may no longer be in Running state: {e}')
147154

148155

149156
@classmethod

test/integration/smoke/test_host_maintenance.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,40 @@ def get_ssh_client(self, ip, username, password, retries=10):
4545

4646
return ssh_client
4747

48-
def wait_until_host_is_in_state(self, hostid, resourcestate, interval=3, retries=20):
48+
def wait_until_host_is_in_state(self, hostid, resourcestate, interval=3, retries=20, abort_states=None):
49+
"""
50+
Wait until the host reaches resourcestate. If abort_states is given and the host lands
51+
in one of those states instead, skip the test immediately rather than waiting out the
52+
full timeout: ErrorInPrepareForMaintenance/ErrorInMaintenance while waiting for
53+
"Maintenance" usually means the hypervisor refused to migrate a VM off this host (most
54+
commonly because the hosts in the cluster have incompatible CPUs), which is an
55+
environment limitation, not something this test can exercise meaningfully.
56+
"""
4957
def check_resource_state():
5058
response = Host.list(
5159
self.apiclient,
5260
id=hostid
5361
)
5462
if isinstance(response, list):
55-
if response[0].resourcestate == resourcestate:
63+
current_state = response[0].resourcestate
64+
if current_state == resourcestate:
5665
self.logger.debug('Host with id %s is in resource state = %s' % (hostid, resourcestate))
5766
return True, None
58-
else:
59-
self.logger.debug("Waiting for host " + hostid +
60-
" to reach state " + resourcestate +
61-
", with current state " + response[0].resourcestate)
67+
if abort_states and current_state in abort_states:
68+
self.logger.debug('Host with id %s entered abort state = %s' % (hostid, current_state))
69+
return True, current_state
70+
self.logger.debug("Waiting for host " + hostid +
71+
" to reach state " + resourcestate +
72+
", with current state " + current_state)
6273
return False, None
6374

64-
done, _ = wait_until(interval, retries, check_resource_state)
75+
done, abort_state = wait_until(interval, retries, check_resource_state)
76+
if abort_state:
77+
raise unittest.SkipTest(
78+
"Host %s entered resource state %s while waiting to reach %s -- the hypervisor "
79+
"rejected the VM migration needed for this test (commonly caused by incompatible "
80+
"CPUs between hosts in the cluster). Skipping this migration-dependent test."
81+
% (hostid, abort_state, resourcestate))
6582
if not done:
6683
raise Exception("Failed to wait for host %s to be on resource state %s" % (hostid, resourcestate))
6784
return True
@@ -84,6 +101,20 @@ def cancel_host_maintenance(self, hostid):
84101
return res
85102

86103
def revert_host_state_on_failure(self, hostId):
104+
# updateHost(allocationstate=Enable) only has a transition defined from the
105+
# Disabled resource state. If a migration failed while putting the host into
106+
# maintenance, the host is left in one of the maintenance-related error states
107+
# (PrepareForMaintenance, ErrorInPrepareForMaintenance, Maintenance,
108+
# ErrorInMaintenance) instead, and only cancelHostMaintenance (AdminCancelMaintenance)
109+
# can move it back to Enabled from there. Recover via whichever API actually applies.
110+
host = Host.list(self.apiclient, id=hostId)[0]
111+
if host.resourcestate == "Enabled":
112+
return
113+
if host.resourcestate in ("PrepareForMaintenance", "ErrorInPrepareForMaintenance",
114+
"Maintenance", "ErrorInMaintenance"):
115+
self.cancel_host_maintenance(hostId)
116+
self.wait_until_host_is_in_state(hostId, "Enabled", 5, 60)
117+
return
87118
cmd = updateHost.updateHostCmd()
88119
cmd.id = hostId
89120
cmd.allocationstate = "Enable"
@@ -247,7 +278,9 @@ def hostPrepareAndCancelMaintenance(self, target_host_id, other_host_id):
247278
self.prepare_host_for_maintenance(target_host_id)
248279
migrations_finished = wait_until(5, 200, self.migrationsFinished, target_host_id)
249280

250-
self.wait_until_host_is_in_state(target_host_id, "Maintenance", 5, 200)
281+
self.wait_until_host_is_in_state(
282+
target_host_id, "Maintenance", 5, 200,
283+
abort_states=("ErrorInPrepareForMaintenance", "ErrorInMaintenance"))
251284

252285
vm_count_after_maintenance = self.noOfVMsOnHost(target_host_id)
253286

@@ -301,11 +334,15 @@ def test_01_cancel_host_maintenace_with_no_migration_jobs(self):
301334
else:
302335
raise unittest.SkipTest("VMs are still migrating so reverse migration /maintenace skipped")
303336

337+
except unittest.SkipTest:
338+
self.revert_host_state_on_failure(listHost[0].id)
339+
self.revert_host_state_on_failure(listHost[1].id)
340+
raise
304341
except Exception as e:
305342
self.revert_host_state_on_failure(listHost[0].id)
306343
self.revert_host_state_on_failure(listHost[1].id)
307344
self.logger.debug("Exception {}".format(e))
308-
self.fail("Host maintenance test failed {}".format(e[0]))
345+
self.fail("Host maintenance test failed {}".format(str(e)))
309346

310347

311348
@attr(
@@ -362,11 +399,15 @@ def test_02_cancel_host_maintenace_with_migration_jobs(self):
362399
else:
363400
raise unittest.SkipTest("VMs are still migrating so reverse migration /maintenace skipped")
364401

402+
except unittest.SkipTest:
403+
self.revert_host_state_on_failure(listHost[0].id)
404+
self.revert_host_state_on_failure(listHost[1].id)
405+
raise
365406
except Exception as e:
366407
self.revert_host_state_on_failure(listHost[0].id)
367408
self.revert_host_state_on_failure(listHost[1].id)
368409
self.logger.debug("Exception {}".format(e))
369-
self.fail("Host maintenance test failed {}".format(e[0]))
410+
self.fail("Host maintenance test failed {}".format(str(e)))
370411

371412
@attr(
372413
tags=[
@@ -437,7 +478,7 @@ def test_03_cancel_host_maintenace_with_migration_jobs_failure(self):
437478
self.revert_host_state_on_failure(listHost[1].id)
438479
Host.update(self.apiclient, id=target_host_id, hosttags="")
439480
self.logger.debug("Exception {}".format(e))
440-
self.fail("Host maintenance test failed {}".format(e[0]))
481+
self.fail("Host maintenance test failed {}".format(str(e)))
441482

442483

443484
class TestHostMaintenanceAgents(TestHostMaintenanceBase):

0 commit comments

Comments
 (0)