11import logging
22import os
33import threading
4+ import time
45from queue import Queue
56
67import connexion
8+ from sdx_datamodel .connection_sm import ConnectionStateMachine
9+ from sdx_datamodel .constants import MongoCollections
710from sdx_pce .topology .temanager import TEManager
811
912from sdx_controller import encoder
13+ from sdx_controller .handlers .connection_handler import (
14+ ConnectionHandler ,
15+ connection_state_machine ,
16+ )
1017from sdx_controller .messaging .rpc_queue_consumer import RpcConsumer
1118from sdx_controller .utils .db_utils import DbUtils
1219
1320logger = logging .getLogger (__name__ )
1421logging .getLogger ("pika" ).setLevel (logging .WARNING )
1522LOG_FILE = os .environ .get ("LOG_FILE" )
1623LOG_LEVEL = os .getenv ("LOG_LEVEL" , "DEBUG" )
24+ OXP_RESPONSE_TIMEOUT = int (os .getenv ("OXP_RESPONSE_TIMEOUT" , 60 ))
25+ PROVISIONING_MONITOR_INTERVAL = int (os .getenv ("PROVISIONING_MONITOR_INTERVAL" , 5 ))
1726
1827
1928def create_rpc_thread (app ):
@@ -32,6 +41,82 @@ def create_rpc_thread(app):
3241 rpc_thread .start ()
3342
3443
44+ def create_provisioning_timeout_thread (app ):
45+ """
46+ Start a background monitor for connections stuck in
47+ UNDER_PROVISIONING longer than the configured timeout.
48+ """
49+ if OXP_RESPONSE_TIMEOUT <= 0 :
50+ logger .info ("[ProvisioningTimeout] Disabled." )
51+ app .provisioning_timeout_thread = None
52+ return
53+
54+ connection_handler = ConnectionHandler (app .db_instance )
55+
56+ def monitor_loop ():
57+ logger .info (
58+ f"[ProvisioningTimeout] Started monitoring with timeout={ OXP_RESPONSE_TIMEOUT } s interval={ PROVISIONING_MONITOR_INTERVAL } s."
59+ )
60+ while True :
61+ try :
62+ now = time .time ()
63+ connections = app .db_instance .get_all_entries_in_collection (
64+ MongoCollections .CONNECTIONS
65+ )
66+ for connection_entry in connections :
67+ service_id = next (iter (connection_entry ), None )
68+ connection = (
69+ connection_entry .get (service_id ) if service_id else None
70+ )
71+ if not isinstance (connection , dict ):
72+ continue
73+ if connection .get ("status" ) != str (
74+ ConnectionStateMachine .State .UNDER_PROVISIONING
75+ ):
76+ continue
77+
78+ started_at = connection .get ("provisioning_started_at" )
79+ if not isinstance (started_at , (int , float )):
80+ continue
81+ if connection .get ("provisioning_timeout_handled" ):
82+ continue
83+ if now - started_at < OXP_RESPONSE_TIMEOUT :
84+ continue
85+
86+ logger .warning (
87+ f"[ProvisioningTimeout] Connection { service_id } timed out after { int (now - started_at )} s waiting for OXP responses."
88+ )
89+
90+ connection ["provisioning_timeout_handled" ] = True
91+ connection ["partial_cleanup_requested" ] = True
92+ connection ["timeout_reason" ] = (
93+ f"OXP response timeout after { OXP_RESPONSE_TIMEOUT } seconds"
94+ )
95+ connection , _ = connection_state_machine (
96+ connection , ConnectionStateMachine .State .DOWN
97+ )
98+ app .db_instance .add_key_value_pair_to_db (
99+ MongoCollections .CONNECTIONS , service_id , connection
100+ )
101+ cleanup_status , cleanup_code = (
102+ connection_handler .cleanup_partial_connection (
103+ app .te_manager , service_id , connection
104+ )
105+ )
106+ logger .info (
107+ f"[ProvisioningTimeout] Cleanup result for { service_id } : { cleanup_status } , code={ cleanup_code } "
108+ )
109+ except Exception as e :
110+ logger .exception (
111+ f"[ProvisioningTimeout] Error while monitoring connections: { e } "
112+ )
113+ time .sleep (PROVISIONING_MONITOR_INTERVAL )
114+
115+ provisioning_thread = threading .Thread (target = monitor_loop , daemon = True )
116+ provisioning_thread .start ()
117+ app .provisioning_timeout_thread = provisioning_thread
118+
119+
35120def create_app (run_listener : bool = True ):
36121 """
37122 Create a connexion app.
@@ -74,6 +159,8 @@ def create_app(run_listener: bool = True):
74159 # pass this around.
75160 app .app .te_manager = app .te_manager
76161
162+ create_provisioning_timeout_thread (app )
163+
77164 if run_listener :
78165 create_rpc_thread (app )
79166 else :
0 commit comments