ics-simlab-config-gen-claude/tools/check_networking.py

102 lines
2.9 KiB
Python

#!/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 <path> [--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())