Osmclient migration to Python3 (feature 8031)
[osm/osmclient.git] / osmclient / common / wait.py
index 9610856..a85808c 100644 (file)
@@ -30,9 +30,7 @@ TIMEOUT_SDNC_OPERATION = TIMEOUT_GENERIC_OPERATION
 TIMEOUT_VIM_OPERATION = TIMEOUT_GENERIC_OPERATION
 TIMEOUT_WIM_OPERATION = TIMEOUT_GENERIC_OPERATION
 TIMEOUT_NS_OPERATION = 3600
-
 POLLING_TIME_INTERVAL = 1
-
 MAX_DELETE_ATTEMPTS = 3
 
 def _show_detailed_status(old_detailed_status, new_detailed_status):
@@ -44,11 +42,11 @@ def _show_detailed_status(old_detailed_status, new_detailed_status):
 
 def _get_finished_states(entity):
     # Note that the member name is either:
-    # 'operationState' (NS and NSI)
-    # '_admin.'operationalState' (other)
+    # 'operationState' (NS, NSI)
+    # '_admin.'operationalState' (VIM, WIM, SDN)
     # For NS and NSI, 'operationState' may be one of:
     # PROCESSING, COMPLETED,PARTIALLY_COMPLETED, FAILED_TEMP,FAILED,ROLLING_BACK,ROLLED_BACK
-    # For other entities, '_admin.operationalState' may be one of:
+    # For VIM, WIM, SDN: '_admin.operationalState' may be one of:
     # operationalState: ENABLED, DISABLED, ERROR, PROCESSING
     if entity == 'NS' or entity == 'NSI':
         return ['COMPLETED', 'PARTIALLY_COMPLETED', 'FAILED_TEMP', 'FAILED']
@@ -66,7 +64,7 @@ def _get_operational_state(resp, entity):
         return resp.get('_admin', {}).get('operationalState')
 
 def _op_has_finished(resp, entity):
-    # _op_has_finished() returns:
+    # This function returns:
     # 0 on success (operation has finished)
     # 1 on pending (operation has not finished)
     # -1 on error (bad response)
@@ -87,8 +85,24 @@ def _get_detailed_status(resp, entity, detailed_status_deleted):
         # For NS and NSI, 'detailed-status' is a JSON "root" member:
         return resp.get('detailed-status')
     else:
-        # For other entities, 'detailed-status' a leaf node to '_admin':
-        return resp.get('_admin', {}).get('detailed-status')
+        # For VIM, WIM, SDN, 'detailed-status' is either:
+        # - a leaf node to '_admin' (operations NOT supported)
+        # - a leaf node of the Nth element in the list '_admin.operations[]' (operations supported by LCM and NBI)
+        # https://osm.etsi.org/gerrit/#/c/7767 : LCM support for operations
+        # https://osm.etsi.org/gerrit/#/c/7734 : NBI support for current_operation
+        ops = resp.get('_admin', {}).get('operations')
+        op_index = resp.get('_admin', {}).get('current_operation')
+        if ops and op_index:
+            # Operations are supported, verify operation index
+            if isinstance(op_index, (int)) or op_index.isdigit():
+                op_index = int(op_index)
+                if op_index > 0 and op_index < len(ops) and ops[op_index] and ops[op_index]["detailed-status"]:
+                    return ops[op_index]["detailed-status"]
+            # operation index is either non-numeric or out-of-range
+            return 'Unexpected error when getting detailed-status!'
+        else:
+            # Operations are NOT supported
+            return resp.get('_admin', {}).get('detailed-status')
 
 def _has_delete_error(resp, entity, deleteFlag, delete_attempts_left):
     if deleteFlag and delete_attempts_left:
@@ -113,20 +127,25 @@ def wait_for_status(entity_label, entity_id, timeout, apiUrlStatus, http_cmd, de
     detailed_status_deleted = None
     time_to_return = False
     delete_attempts_left = MAX_DELETE_ATTEMPTS
+    wait_for_404 = False
     try:
         while True:
             http_code, resp_unicode = http_cmd('{}/{}'.format(apiUrlStatus, entity_id))
             resp = ''
             if resp_unicode:
                 resp = json.loads(resp_unicode)
-            # print 'HTTP CODE: {}'.format(http_code)
-            # print 'RESP: {}'.format(resp)
-            # print 'URL: {}/{}'.format(apiUrlStatus, entity_id)
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            # print('URL: {}/{}'.format(apiUrlStatus, entity_id))
             if deleteFlag and http_code == 404:
                 # In case of deletion, '404 Not Found' means successfully deleted
                 # Display 'detailed-status: Deleted' and return
                 time_to_return = True
                 detailed_status_deleted = 'Deleted'
+            elif deleteFlag and http_code in (200, 201, 202, 204):
+                # In case of deletion and HTTP Status = 20* OK, deletion may be PROCESSING or COMPLETED
+                # If this is the case, we should keep on polling until 404 (deleted) is returned.
+                wait_for_404 = True
             elif http_code not in (200, 201, 202, 204):
                 raise ClientException(str(resp))
             if not time_to_return:
@@ -148,12 +167,14 @@ def wait_for_status(entity_label, entity_id, timeout, apiUrlStatus, http_cmd, de
                     else:
                         # Operation has finished, either with success or error
                         if deleteFlag:
-                            if delete_attempts_left < MAX_DELETE_ATTEMPTS:
-                                time_to_return = True
                             delete_attempts_left -= 1
+                            if not wait_for_404 and delete_attempts_left < MAX_DELETE_ATTEMPTS:
+                                time_to_return = True
                         else:
                             time_to_return = True
             new_detailed_status = _get_detailed_status(resp, entity_label, detailed_status_deleted)
+            # print('DETAILED-STATUS: {}'.format(new_detailed_status))
+            # print('DELETE-ATTEMPTS-LEFT: {}'.format(delete_attempts_left))
             if not new_detailed_status:
                 new_detailed_status = 'In progress'
             # TODO: Change LCM to provide detailed-status more up to date