13
13
import sys
14
14
15
15
# compatible import for python 2 and 3
16
- from .api_exceptions import APIException , APIClientException
16
+ from .api_exceptions import APIException , APIClientException , TimeoutException
17
17
from .api_response import APIResponse
18
18
from cpapi .utils import get_massage_from_io_error , compatible_loads
19
19
@@ -248,7 +248,7 @@ def login_as_root(self, domain=None, payload=None):
248
248
except (WindowsError ) as err :
249
249
raise APIClientException ("Could not login as root:\n " + str (type (err )) + " - " + str (err ))
250
250
251
- def api_call (self , command , payload = None , sid = None , wait_for_task = True ):
251
+ def api_call (self , command , payload = None , sid = None , wait_for_task = True , timeout = - 1 ):
252
252
"""
253
253
performs a web-service API request to the management server
254
254
@@ -260,9 +260,12 @@ def api_call(self, command, payload=None, sid=None, wait_for_task=True):
260
260
and will not return until the task is completed.
261
261
when wait_for_task=False, it is up to the user to call the "show-task" API and check
262
262
the status of the command.
263
+ :param timeout: Optional positive timeout (in seconds) before stop waiting for the task even if not completed.
263
264
:return: APIResponse object
264
265
:side-effects: updates the class's uid and server variables
265
266
"""
267
+ timeout_start = time .time ()
268
+
266
269
self .check_fingerprint ()
267
270
if payload is None :
268
271
payload = {}
@@ -351,9 +354,9 @@ def api_call(self, command, payload=None, sid=None, wait_for_task=True):
351
354
# If we want to wait for the task to end, wait for it
352
355
if wait_for_task is True and res .success and command != "show-task" :
353
356
if "task-id" in res .data :
354
- res = self .__wait_for_task (res .data ["task-id" ])
357
+ res = self .__wait_for_task (res .data ["task-id" ], timeout = ( timeout - time . time () + timeout_start ) )
355
358
elif "tasks" in res .data :
356
- res = self .__wait_for_tasks (res .data ["tasks" ])
359
+ res = self .__wait_for_tasks (res .data ["tasks" ], timeout = ( timeout - time . time () + timeout_start ) )
357
360
358
361
return res
359
362
@@ -478,7 +481,7 @@ def get_server_fingerprint(self):
478
481
conn .close ()
479
482
return fingerprint_hash
480
483
481
- def __wait_for_task (self , task_id ):
484
+ def __wait_for_task (self , task_id , timeout = - 1 ):
482
485
"""
483
486
When the server needs to perform an API call that may take a long time (e.g. run-script, install-policy,
484
487
publish), the server responds with a 'task-id'.
@@ -487,15 +490,22 @@ def __wait_for_task(self, task_id):
487
490
The function will return when the task (and its sub-tasks) are no longer in-progress.
488
491
489
492
:param task_id: The task identifier.
493
+ :param timeout: Optional positive timeout (in seconds) that will end the task even if not completed.
490
494
:return: APIResponse object (response of show-task command).
491
495
:raises APIException
492
496
"""
493
497
task_complete = False
494
498
task_result = None
499
+ task_start = time .time ()
495
500
in_progress = "in progress"
496
501
497
- # As long as there is a task in progress
502
+ # As long as there is a task in progress or the timeout isn't expired (and is positive)
498
503
while not task_complete :
504
+
505
+ # If timeout parameter was set and valid and timeout did expire, raise exception
506
+ if timeout >= 0 and time .time () - task_start > timeout :
507
+ raise TimeoutException ("Timeout reached when waiting for task to complete" )
508
+
499
509
# Check the status of the task
500
510
task_result = self .api_call ("show-task" , {"task-id" : task_id , "details-level" : "full" }, self .sid , False )
501
511
@@ -526,21 +536,22 @@ def __wait_for_task(self, task_id):
526
536
self .check_tasks_status (task_result )
527
537
return task_result
528
538
529
- def __wait_for_tasks (self , task_objects ):
539
+ def __wait_for_tasks (self , task_objects , timeout = - 1 ):
530
540
"""
531
541
The version of __wait_for_task function for the collection of tasks
532
542
533
543
:param task_objects: A list of task objects
534
544
:return: APIResponse object (response of show-task command).
535
545
"""
546
+ timeout_start = time .time ()
536
547
537
548
# A list of task ids to be retrieved
538
549
tasks = []
539
550
for task_obj in task_objects :
540
551
# Retrieve the taskId and wait for the task to be completed
541
552
task_id = task_obj ["task-id" ]
542
553
tasks .append (task_id )
543
- self .__wait_for_task (task_id )
554
+ self .__wait_for_task (task_id , timeout = ( timeout - time . time () + timeout_start ) )
544
555
545
556
task_result = self .api_call ("show-task" , {"task-id" : tasks , "details-level" : "full" },
546
557
self .sid , False )
@@ -557,7 +568,7 @@ def check_tasks_status(task_result):
557
568
:return:
558
569
"""
559
570
for task in task_result .data ["tasks" ]:
560
- if task ["status" ] == "failed" or task ["status" ] == "partially succeeded" :
571
+ if task ["status" ] == "failed" or task ["status" ] == "partially succeeded" or task [ "status" ] == "in progress " :
561
572
task_result .set_success_status (False )
562
573
break
563
574
0 commit comments