| 38 | logger = Logger(name="orchestrator.py", level=logging.DEBUG) |
| 39 | |
| 40 | class Orchestrator: |
| 41 | def __init__(self): |
| 42 | """Initialise the orchestrator""" |
| 43 | self.shared_data = shared_data |
| 44 | self.actions = [] # List of actions to be executed |
| 45 | self.standalone_actions = [] # List of standalone actions to be executed |
| 46 | self.failed_scans_count = 0 # Count the number of failed scans |
| 47 | self.network_scanner = None |
| 48 | self.nmap_vuln_scanner = None |
| 49 | self.last_vuln_scan_time = datetime.min # Set the last vulnerability scan time to the minimum datetime value |
| 50 | |
| 51 | # Verify critical configuration attributes exist |
| 52 | self._verify_config_attributes() |
| 53 | |
| 54 | self.load_actions() # Load all actions from the actions file |
| 55 | actions_loaded = [action.__class__.__name__ for action in self.actions + self.standalone_actions] # Get the names of the loaded actions |
| 56 | logger.info(f"Actions loaded: {actions_loaded}") |
| 57 | |
| 58 | # CRITICAL: Pi Zero W2 resource management - limit concurrent actions |
| 59 | # Running too many actions simultaneously causes memory exhaustion and hangs |
| 60 | # REDUCED to 1 to prevent OOM kills during AI + scanning + display updates |
| 61 | self.semaphore = threading.Semaphore(1) # Max 1 concurrent action for Pi Zero W2 |
| 62 | |
| 63 | # No longer using ThreadPoolExecutor - direct threading is more reliable |
| 64 | # and avoids "cannot schedule new futures after interpreter shutdown" errors |
| 65 | |
| 66 | # Default timeout for action execution (in seconds) |
| 67 | self.action_timeout = getattr(self.shared_data, 'action_timeout', 300) # 5 minutes default |
| 68 | self.vuln_scan_timeout = getattr(self.shared_data, 'vuln_scan_timeout', 1800) # 30 minutes for vuln scans |
| 69 | |
| 70 | def _verify_config_attributes(self): |
| 71 | """Verify that all required configuration attributes exist on shared_data.""" |
| 72 | required_attrs = { |
| 73 | 'retry_success_actions': True, |
| 74 | 'retry_failed_actions': True, |
| 75 | 'success_retry_delay': 300, |
| 76 | 'failed_retry_delay': 180, |
| 77 | 'scan_vuln_running': True, |
| 78 | 'scan_vuln_no_ports': True, # Enable scanning hosts without discovered ports |
| 79 | 'enable_attacks': True, |
| 80 | 'scan_vuln_interval': 300, |
| 81 | 'scan_interval': 180, |
| 82 | 'action_timeout': 300, # 5 minutes default for actions |
| 83 | 'vuln_scan_timeout': 1800 # 30 minutes for vulnerability scans |
| 84 | } |
| 85 | |
| 86 | for attr, default_value in required_attrs.items(): |
| 87 | if not hasattr(self.shared_data, attr): |
| 88 | logger.warning(f"Missing config attribute '{attr}', setting default value: {default_value}") |
| 89 | setattr(self.shared_data, attr, default_value) |
| 90 | |
| 91 | def _should_retry(self, action_key, row, status_type='success', custom_delay_seconds=None): |
| 92 | """ |
| 93 | Check if an action should be retried based on its status and retry configuration. |
| 94 | |
| 95 | Args: |
| 96 | action_key: The action name/key to check |
| 97 | row: The data row containing action status |
no outgoing calls
no test coverage detected