138 lines
5.2 KiB
Python
138 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
from .app import RunSummaryTUI, SecureCheckTUI
|
|
from .catalog import builtin_scenarios, task_catalog
|
|
from .config import build_paths, ensure_app_dirs
|
|
from .executor import ExecutionContext, execute_tasks
|
|
from .logging_utils import attach_run_handler, setup_logging
|
|
from .status import collect_status
|
|
from .storage import ScenarioStore
|
|
from .system_info import detect_system
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="SecureCheck - console semi-graphique pour contrôles sécurité Linux")
|
|
parser.add_argument("--dry-run", action="store_true", help="Simule les commandes sans modifier le système")
|
|
parser.add_argument("--run", action="store_true", help="Lance immédiatement les tâches passées via --tasks ou --scenario")
|
|
parser.add_argument("--tasks", help="Liste de tâches séparées par des virgules")
|
|
parser.add_argument("--scenario", help="Nom d'un scénario enregistré ou builtin")
|
|
parser.add_argument("--list-scenarios", action="store_true", help="Affiche les scénarios disponibles")
|
|
return parser.parse_args()
|
|
|
|
|
|
def resolve_task_selection(args: argparse.Namespace, store: ScenarioStore, available_task_keys: set[str]) -> list[str]:
|
|
if args.scenario:
|
|
scenario = store.get(args.scenario)
|
|
if not scenario:
|
|
raise SystemExit(f"Scénario inconnu: {args.scenario}")
|
|
return [key for key in scenario.task_keys if key in available_task_keys]
|
|
|
|
if args.tasks:
|
|
selected = [key.strip() for key in args.tasks.split(",") if key.strip()]
|
|
invalid = [key for key in selected if key not in available_task_keys]
|
|
if invalid:
|
|
raise SystemExit(f"Tâches inconnues: {', '.join(invalid)}")
|
|
return selected
|
|
|
|
return []
|
|
|
|
|
|
def print_led_dashboard(system) -> None:
|
|
print("")
|
|
print("=== Etat du système ===")
|
|
for item in collect_status(system):
|
|
led = "\033[32m●\033[0m" if item.ok else "\033[31m●\033[0m"
|
|
print(f"{led} [{item.category}] {item.label}: {item.detail}")
|
|
|
|
|
|
def print_summary(results, run_log_path, system) -> None:
|
|
ok_count = sum(1 for result in results if result.success)
|
|
ko_count = len(results) - ok_count
|
|
print("")
|
|
print("=== Résumé ===")
|
|
for result in results:
|
|
status = "OK" if result.success else "ECHEC"
|
|
suffix = f" | erreur: {result.error}" if result.error else ""
|
|
print(f"- {status} | {result.label} | {result.duration_seconds:.1f}s{suffix}")
|
|
for detail in result.details:
|
|
print(f" {detail}")
|
|
print_led_dashboard(system)
|
|
print(f"Logs d'exécution: {run_log_path}")
|
|
print(f"Total OK={ok_count} / ECHEC={ko_count}")
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
paths = build_paths()
|
|
ensure_app_dirs(paths)
|
|
logger = setup_logging(paths.app_log_file)
|
|
system = detect_system()
|
|
|
|
tasks = task_catalog()
|
|
task_by_key = {task.key: task for task in tasks}
|
|
store = ScenarioStore(paths.scenario_file, builtin_scenarios())
|
|
|
|
if args.list_scenarios:
|
|
for scenario in store.list_all():
|
|
print(f"{scenario.name}: {scenario.description}")
|
|
return 0
|
|
|
|
selected_keys = resolve_task_selection(args, store, set(task_by_key))
|
|
interactive_mode = not args.run
|
|
active_scenario_name = args.scenario
|
|
menu_message: str | None = None
|
|
|
|
while True:
|
|
if interactive_mode:
|
|
tui = SecureCheckTUI(
|
|
system,
|
|
tasks,
|
|
store,
|
|
status_provider=lambda: collect_status(system),
|
|
initial_selected=set(selected_keys) if selected_keys else None,
|
|
initial_scenario_name=active_scenario_name,
|
|
initial_message=menu_message,
|
|
)
|
|
selection = tui.run()
|
|
if selection is None:
|
|
return 0
|
|
selected_keys = selection.task_keys
|
|
active_scenario_name = selection.scenario_name
|
|
|
|
if not selected_keys:
|
|
if interactive_mode:
|
|
menu_message = "Aucune tâche sélectionnée."
|
|
continue
|
|
print("Aucune tâche sélectionnée.")
|
|
return 1
|
|
|
|
selected_tasks = [task_by_key[key] for key in selected_keys]
|
|
context = ExecutionContext(paths=paths, system=system, logger=logger, dry_run=args.dry_run)
|
|
|
|
run_log_path = paths.log_dir / f"run-{datetime.now().strftime('%Y%m%d-%H%M%S')}.log"
|
|
run_handler = attach_run_handler(logger, run_log_path)
|
|
try:
|
|
results = execute_tasks(context, selected_tasks)
|
|
finally:
|
|
logger.removeHandler(run_handler)
|
|
run_handler.close()
|
|
|
|
if interactive_mode:
|
|
status_items = collect_status(system)
|
|
RunSummaryTUI(results, status_items, str(run_log_path)).run()
|
|
ok_count = sum(1 for result in results if result.success)
|
|
ko_count = len(results) - ok_count
|
|
menu_message = f"Dernière exécution: {ok_count} OK / {ko_count} ECHEC. Sélection prête pour une nouvelle action."
|
|
continue
|
|
|
|
print_summary(results, run_log_path, system)
|
|
return 0 if all(result.success for result in results) else 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|