@@ -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
443484class TestHostMaintenanceAgents (TestHostMaintenanceBase ):
0 commit comments