2121from wait_for import wait_for
2222
2323from robottelo .config import settings
24- from robottelo .constants import ANY_CONTEXT
24+ from robottelo .constants import ANY_CONTEXT , DEFAULT_LOC
2525from robottelo .utils .datafactory import (
2626 gen_string ,
2727 valid_hostgroups_list_short ,
@@ -98,7 +98,6 @@ def test_positive_hostgroups_full_nested_names(
9898
9999@pytest .mark .rhel_ver_list ([settings .content_host .default_rhel_version ])
100100def test_positive_run_default_job_template (
101- session ,
102101 target_sat ,
103102 rex_contenthost ,
104103 module_org ,
@@ -129,7 +128,15 @@ def test_positive_run_default_job_template(
129128
130129 with target_sat .ui_session () as session :
131130 session .organization .select (module_org .name )
132- assert session .host .search (hostname )[0 ]['Name' ] == hostname
131+ session .location .select (loc_name = DEFAULT_LOC )
132+ # Search with retry to handle potential context/indexing delays
133+ result = wait_for (
134+ lambda : session .all_hosts .search (f"name = { hostname } " ),
135+ timeout = 30 ,
136+ delay = 2 ,
137+ fail_condition = [],
138+ ).out
139+ assert result [0 ]['Name' ] == hostname
133140 command = 'ls'
134141 session .jobinvocation .run (
135142 {
@@ -153,7 +160,7 @@ def test_positive_run_default_job_template(
153160
154161
155162@pytest .mark .rhel_ver_list ([settings .content_host .default_rhel_version ])
156- def test_rex_through_host_details (session , target_sat , rex_contenthost , module_org ):
163+ def test_rex_through_host_details (target_sat , rex_contenthost , module_org ):
157164 """Run remote execution using the new host details page
158165
159166 :id: ee625595-4995-43b2-9e6d-633c9b33ff93
@@ -185,7 +192,17 @@ def test_rex_through_host_details(session, target_sat, rex_contenthost, module_o
185192 )
186193 task_status = target_sat .api .ForemanTask (id = task_result [0 ].id ).poll ()
187194 assert task_status ['result' ] == 'success'
188- recent_jobs = session .host_new .get_details (hostname , "overview.recent_jobs" )['overview' ]
195+
196+ # Wait for recent jobs table to load with job data
197+ recent_jobs = wait_for (
198+ lambda : session .host_new .get_details (hostname , "overview.recent_jobs" )['overview' ],
199+ timeout = 30 ,
200+ delay = 2 ,
201+ fail_condition = lambda result : (
202+ not result .get ('recent_jobs' , {}).get ('finished' , {}).get ('table' )
203+ ),
204+ ).out
205+
189206 assert recent_jobs ['recent_jobs' ]['finished' ]['table' ][0 ]['column0' ] == "Run ls"
190207 assert recent_jobs ['recent_jobs' ]['finished' ]['table' ][0 ]['column2' ] == "succeeded"
191208
@@ -195,7 +212,7 @@ def test_rex_through_host_details(session, target_sat, rex_contenthost, module_o
195212 'ui_user' , [{'admin' : True }, {'admin' : False }], indirect = True , ids = ['adminuser' , 'nonadminuser' ]
196213)
197214def test_positive_run_custom_job_template (
198- session , module_org , default_location , target_sat , ui_user , rex_contenthost
215+ module_org , default_location , target_sat , ui_user , rex_contenthost
199216):
200217 """Run a job template on a host
201218
@@ -226,7 +243,15 @@ def test_positive_run_custom_job_template(
226243 job_template_name = gen_string ('alpha' )
227244 with target_sat .ui_session () as session :
228245 session .organization .select (module_org .name )
229- assert session .host .search (hostname )[0 ]['Name' ] == hostname
246+ session .location .select (default_location .name )
247+ # Search with retry to handle potential context/indexing delays
248+ result = wait_for (
249+ lambda : session .all_hosts .search (f"name = { hostname } " ),
250+ timeout = 30 ,
251+ delay = 2 ,
252+ fail_condition = [],
253+ ).out
254+ assert result [0 ]['Name' ] == hostname
230255 session .jobtemplate .create (
231256 {
232257 'template.name' : job_template_name ,
@@ -255,9 +280,7 @@ def test_positive_run_custom_job_template(
255280
256281@pytest .mark .upgrade
257282@pytest .mark .rhel_ver_list ([settings .content_host .default_rhel_version ])
258- def test_positive_run_job_template_multiple_hosts (
259- session , module_org , target_sat , rex_contenthosts
260- ):
283+ def test_positive_run_job_template_multiple_hosts (module_org , target_sat , rex_contenthosts ):
261284 """Run a job template against multiple hosts
262285
263286 :id: c4439ec0-bb80-47f6-bc31-fa7193bfbeeb
@@ -268,7 +291,7 @@ def test_positive_run_job_template_multiple_hosts(
268291
269292 1. Set remote_execution_connect_by_ip on hosts to true
270293 2. Navigate to the hosts page and select at least two hosts
271- 3. Click the "Select Action "
294+ 3. Click the "Schedule a job "
272295 4. Select the job and appropriate template
273296 5. Run the job
274297
@@ -282,9 +305,15 @@ def test_positive_run_job_template_multiple_hosts(
282305 with target_sat .ui_session () as session :
283306 session .organization .select (module_org .name )
284307 for host in host_names :
285- assert session .host .search (host )[0 ]['Name' ] == host
286- session .host .reset_search ()
287- job_status = session .host .schedule_remote_job (
308+ # Search with retry to handle potential context/indexing delays
309+ result = wait_for (
310+ lambda h = host : session .all_hosts .search (f"name = { h } " ),
311+ timeout = 30 ,
312+ delay = 2 ,
313+ fail_condition = [],
314+ ).out
315+ assert result [0 ]['Name' ] == host
316+ job_status = session .host_new .schedule_remote_job (
288317 host_names ,
289318 {
290319 'category_and_template.job_category' : 'Commands' ,
@@ -298,7 +327,7 @@ def test_positive_run_job_template_multiple_hosts(
298327
299328
300329@pytest .mark .rhel_ver_list ([settings .content_host .default_rhel_version ])
301- def test_positive_run_scheduled_job_template_by_ip (session , module_org , rex_contenthost ):
330+ def test_positive_run_scheduled_job_template_by_ip (module_org , target_sat , rex_contenthost ):
302331 """Schedule a job to be ran against a host by ip
303332
304333 :id: 4387bed9-969d-45fb-80c2-b0905bb7f1bd
@@ -323,13 +352,20 @@ def test_positive_run_scheduled_job_template_by_ip(session, module_org, rex_cont
323352 """
324353 job_time = 6 * 60
325354 hostname = rex_contenthost .hostname
326- with session :
355+ with target_sat . ui_session () as session :
327356 session .organization .select (module_org .name )
328- session .location .select ('Default Location' )
329- assert session .host .search (hostname )[0 ]['Name' ] == hostname
357+ session .location .select (loc_name = DEFAULT_LOC )
358+ # Search with retry to handle potential context/indexing delays
359+ result = wait_for (
360+ lambda : session .all_hosts .search (f"name = { hostname } " ),
361+ timeout = 30 ,
362+ delay = 2 ,
363+ fail_condition = [],
364+ ).out
365+ assert result [0 ]['Name' ] == hostname
330366 plan_time = session .browser .get_client_datetime () + datetime .timedelta (seconds = job_time )
331367 command_to_run = 'sleep 10'
332- job_status = session .host .schedule_remote_job (
368+ job_status = session .host_new .schedule_remote_job (
333369 [hostname ],
334370 {
335371 'category_and_template.job_category' : 'Commands' ,
@@ -341,52 +377,52 @@ def test_positive_run_scheduled_job_template_by_ip(session, module_org, rex_cont
341377 },
342378 wait_for_results = False ,
343379 )
344- # Note that to create this host scheduled job we spent some time from that plan_time, as it
345- # was calculated before creating the job
380+ # Track state transitions through polling
346381 job_left_time = (plan_time - session .browser .get_client_datetime ()).total_seconds ()
347- # assert that we have time left to wait, otherwise we have to use more job time,
348- # the job_time must be significantly greater than job creation time.
349- assert job_left_time > 0
382+ assert job_left_time > 0 , "Job scheduled time already passed during creation"
383+
384+ # Initial state: should be waiting to start
350385 assert job_status ['hosts' ][0 ]['Name' ] == hostname
351386 assert job_status ['hosts' ][0 ]['Status' ] in ('Awaiting start' , 'N/A' )
352- # sleep 3/4 of the left time
353- time .sleep (job_left_time * 3 / 4 )
354- job_status = session .jobinvocation .read (f'Run { command_to_run } ' , hostname , 'hosts' )
355- assert job_status ['hosts' ][0 ]['Name' ] == hostname
356- assert job_status ['hosts' ][0 ]['Status' ] in (
357- 'Awaiting start' ,
358- 'N/A' ,
359- 'Succeeded' ,
360- )
361- # recalculate the job left time to be more accurate
362- job_left_time = (plan_time - session .browser .get_client_datetime ()).total_seconds ()
363- # the last read time should not take more than 1/4 of the last left time
364- assert job_left_time > 0
365- wait_for (
366- lambda : (
367- session .jobinvocation .read (f'Run { command_to_run } ' , hostname , 'hosts' )['hosts' ][0 ][
368- 'Status'
369- ]
370- == 'Pending'
371- ),
372- timeout = (job_left_time + 30 ),
373- delay = 1 ,
374- )
375- # wait the job to change status to "Succeeded"
376- wait_for (
377- lambda : (
378- session .jobinvocation .read (f'Run { command_to_run } ' , hostname , 'hosts' )['hosts' ][0 ][
379- 'Status'
380- ]
381- == 'Succeeded'
382- ),
383- timeout = 30 ,
384- delay = 1 ,
385- )
387+
388+ # Poll for state transitions and record them
389+ observed_states = [job_status ['hosts' ][0 ]['Status' ]]
390+ poll_timeout = job_left_time + 60 # Extra buffer for job execution
391+ poll_start = time .time ()
392+
393+ while time .time () - poll_start < poll_timeout :
394+ job_status = session .jobinvocation .read (f'Run { command_to_run } ' , hostname , 'hosts' )
395+ current_status = job_status ['hosts' ][0 ]['Status' ]
396+
397+ # Record state changes
398+ if current_status != observed_states [- 1 ]:
399+ observed_states .append (current_status )
400+
401+ # Exit when job completes
402+ if current_status in ('Succeeded' , 'Failed' , 'Cancelled' ):
403+ break
404+
405+ time .sleep (2 )
406+
407+ # Read final complete status
386408 job_status = session .jobinvocation .read (f'Run { command_to_run } ' , hostname )
409+ final_status = job_status ['hosts' ][0 ]['Status' ]
410+ if final_status != observed_states [- 1 ]:
411+ observed_states .append (final_status )
412+
413+ # Verify state progression
414+ # Expected: Awaiting start/N/A -> Running/Pending -> Succeeded
415+ assert 'Succeeded' in observed_states , f"Job never succeeded. States: { observed_states } "
416+
417+ has_pre_run_state = any (s in ('Awaiting start' , 'N/A' ) for s in observed_states [:- 1 ])
418+ has_running_state = any (s in ('Pending' , 'Running' ) for s in observed_states )
419+ assert has_pre_run_state , f"Missing pre-run state. States: { observed_states } "
420+ assert has_running_state or len (observed_states ) == 2 , (
421+ f"Job should go through running state or transition directly. States: { observed_states } "
422+ )
423+
387424 assert job_status ['overall_status' ]['is_success' ]
388425 assert job_status ['hosts' ][0 ]['Name' ] == hostname
389- assert job_status ['hosts' ][0 ]['Status' ] == 'Succeeded'
390426
391427
392428@pytest .mark .rhel_ver_list ([settings .content_host .default_rhel_version ])
0 commit comments