1313import sys
1414
1515# compatible import for python 2 and 3
16- from .api_exceptions import APIException , APIClientException
16+ from .api_exceptions import APIException , APIClientException , TimeoutException
1717from .api_response import APIResponse
1818from cpapi .utils import get_massage_from_io_error , compatible_loads
1919
@@ -248,7 +248,7 @@ def login_as_root(self, domain=None, payload=None):
248248 except (WindowsError ) as err :
249249 raise APIClientException ("Could not login as root:\n " + str (type (err )) + " - " + str (err ))
250250
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 ):
252252 """
253253 performs a web-service API request to the management server
254254
@@ -260,9 +260,12 @@ def api_call(self, command, payload=None, sid=None, wait_for_task=True):
260260 and will not return until the task is completed.
261261 when wait_for_task=False, it is up to the user to call the "show-task" API and check
262262 the status of the command.
263+ :param timeout: Optional positive timeout (in seconds) before stop waiting for the task even if not completed.
263264 :return: APIResponse object
264265 :side-effects: updates the class's uid and server variables
265266 """
267+ timeout_start = time .time ()
268+
266269 self .check_fingerprint ()
267270 if payload is None :
268271 payload = {}
@@ -351,9 +354,9 @@ def api_call(self, command, payload=None, sid=None, wait_for_task=True):
351354 # If we want to wait for the task to end, wait for it
352355 if wait_for_task is True and res .success and command != "show-task" :
353356 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 ) )
355358 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 ) )
357360
358361 return res
359362
@@ -478,7 +481,7 @@ def get_server_fingerprint(self):
478481 conn .close ()
479482 return fingerprint_hash
480483
481- def __wait_for_task (self , task_id ):
484+ def __wait_for_task (self , task_id , timeout = - 1 ):
482485 """
483486 When the server needs to perform an API call that may take a long time (e.g. run-script, install-policy,
484487 publish), the server responds with a 'task-id'.
@@ -487,15 +490,22 @@ def __wait_for_task(self, task_id):
487490 The function will return when the task (and its sub-tasks) are no longer in-progress.
488491
489492 :param task_id: The task identifier.
493+ :param timeout: Optional positive timeout (in seconds) that will end the task even if not completed.
490494 :return: APIResponse object (response of show-task command).
491495 :raises APIException
492496 """
493497 task_complete = False
494498 task_result = None
499+ task_start = time .time ()
495500 in_progress = "in progress"
496501
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)
498503 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+
499509 # Check the status of the task
500510 task_result = self .api_call ("show-task" , {"task-id" : task_id , "details-level" : "full" }, self .sid , False )
501511
@@ -526,21 +536,22 @@ def __wait_for_task(self, task_id):
526536 self .check_tasks_status (task_result )
527537 return task_result
528538
529- def __wait_for_tasks (self , task_objects ):
539+ def __wait_for_tasks (self , task_objects , timeout = - 1 ):
530540 """
531541 The version of __wait_for_task function for the collection of tasks
532542
533543 :param task_objects: A list of task objects
534544 :return: APIResponse object (response of show-task command).
535545 """
546+ timeout_start = time .time ()
536547
537548 # A list of task ids to be retrieved
538549 tasks = []
539550 for task_obj in task_objects :
540551 # Retrieve the taskId and wait for the task to be completed
541552 task_id = task_obj ["task-id" ]
542553 tasks .append (task_id )
543- self .__wait_for_task (task_id )
554+ self .__wait_for_task (task_id , timeout = ( timeout - time . time () + timeout_start ) )
544555
545556 task_result = self .api_call ("show-task" , {"task-id" : tasks , "details-level" : "full" },
546557 self .sid , False )
@@ -557,7 +568,7 @@ def check_tasks_status(task_result):
557568 :return:
558569 """
559570 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 " :
561572 task_result .set_success_status (False )
562573 break
563574
0 commit comments