#!/usr/bin/env python3 """ Network configuration validator for ICS-SimLab. Checks for common network configuration issues that cause docker-compose failures: 1. Duplicate IPs within the same docker_network ("Address already in use") 2. docker_network not declared in ip_networks[] 3. IP address outside the declared subnet Usage: python3 -m tools.check_networking --config [--strict] Exit codes: 0: No issues found 1: Issues found (or --strict and warnings exist) 2: Configuration file error """ import argparse import json import sys from pathlib import Path from typing import List from models.ics_simlab_config_v2 import Config from tools.semantic_validation import validate_network_config, SemanticError def format_issues(errors: List[SemanticError]) -> str: """Format errors for human-readable output.""" lines = [] for err in errors: lines.append(f" - {err.entity}: {err.message}") return "\n".join(lines) def main() -> int: parser = argparse.ArgumentParser( description="Validate ICS-SimLab network configuration" ) parser.add_argument( "--config", required=True, help="Path to configuration.json" ) parser.add_argument( "--strict", action="store_true", help="Exit non-zero on any issue (not just errors)" ) parser.add_argument( "--json", action="store_true", help="Output in JSON format" ) args = parser.parse_args() config_path = Path(args.config) if not config_path.exists(): print(f"ERROR: Config file not found: {config_path}", file=sys.stderr) return 2 # Load and validate config try: raw_data = json.loads(config_path.read_text(encoding="utf-8")) config = Config.model_validate(raw_data) except json.JSONDecodeError as e: print(f"ERROR: Invalid JSON in {config_path}: {e}", file=sys.stderr) return 2 except Exception as e: print(f"ERROR: Config validation failed: {e}", file=sys.stderr) return 2 # Run network validation errors = validate_network_config(config) if args.json: output = { "config": str(config_path), "issues": [{"entity": e.entity, "message": e.message} for e in errors], "status": "error" if errors else "ok" } print(json.dumps(output, indent=2)) else: if errors: print(f"NETWORK VALIDATION ISSUES ({len(errors)}):") print(format_issues(errors)) print() print("FIX: Each device must have a unique IP within its docker_network.") print(" Check for copy-paste errors or IP assignment overlap.") else: print(f"OK: Network configuration valid ({config_path})") # Return appropriate exit code if errors: return 1 return 0 if __name__ == "__main__": sys.exit(main())