#!/usr/bin/env python3 """ 통합 시스템 제어 스크립트 (Unified System Control Script) 기존의 개별 제어 스크립트들을 하나로 통합하였습니다. 지원 기능: - power_on (06-PowerON.py) - power_off (07-PowerOFF.py) - log_clear (05-clrsel.py) - job_delete (08-job_delete_all.py) - tsr_collect (03-tsr_log.py) - tsr_save (04-tsr_save.py) """ import os import sys import argparse import subprocess import time import logging from concurrent.futures import ThreadPoolExecutor from pathlib import Path from dotenv import load_dotenv # ----------------------------------------------------------------------------- # Configuration # ----------------------------------------------------------------------------- logging.basicConfig( level=logging.INFO, format='%(asctime)s [INFO] %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) logger = logging.getLogger(__name__) # Load .env relative to script location or current directory load_dotenv() IDRAC_USER = os.getenv("IDRAC_USER", "root") IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin") OME_USER = os.getenv("OME_USER", "") OME_PASS = os.getenv("OME_PASS", "") # ----------------------------------------------------------------------------- # Command Definitions # ----------------------------------------------------------------------------- # Each action maps to a function that returns the racadm command list def cmd_power_on(ip): return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerup"] def cmd_power_off(ip): return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerdown"] def cmd_log_clear(ip): return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "clrsel"] def cmd_job_delete(ip): return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "jobqueue", "delete", "-i", "ALL"] def cmd_tsr_collect(ip): return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "techsupreport", "collect"] def cmd_tsr_save(ip): # Note: This command assumes a specific share path (legacy behavior) # Ideally this path should also be configurable via env share_path = "//10.10.3.15/share/" return [ "racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "techsupreport", "export", "-l", share_path, "-u", OME_USER, "-p", OME_PASS ] ACTION_MAP = { "power_on": cmd_power_on, "power_off": cmd_power_off, "log_clear": cmd_log_clear, "job_delete": cmd_job_delete, "tsr_collect": cmd_tsr_collect, "tsr_save": cmd_tsr_save, } # ----------------------------------------------------------------------------- # Execution Logic # ----------------------------------------------------------------------------- def execute_action(ip: str, action_name: str) -> None: """Run the racadm command for a single IP.""" try: cmd_func = ACTION_MAP.get(action_name) if not cmd_func: logger.error(f"[{ip}] Unknown action: {action_name}") return cmd = cmd_func(ip) logger.info(f"[{ip}] Executing {action_name}...") # Run command result = subprocess.run( cmd, capture_output=True, text=True, timeout=600 # Generous timeout for TSR operations ) if result.returncode == 0: logger.info(f"[{ip}] Success: {result.stdout.strip()}") else: logger.error(f"[{ip}] Failed: {result.stderr.strip()}") except Exception as e: logger.error(f"[{ip}] Exception: {e}") def main(): parser = argparse.ArgumentParser(description="Unified System Control Script") parser.add_argument("action", choices=ACTION_MAP.keys(), help="Action to perform") parser.add_argument("ip_file", type=Path, help="Path to the IP list file") # Optional: Allow passing IP directly via args instead of file (future extension) args = parser.parse_args() ip_file = args.ip_file action = args.action if not ip_file.is_file(): logger.error(f"IP file not found: {ip_file}") sys.exit(1) # Read IPs try: with ip_file.open("r", encoding="utf-8") as f: ips = [line.strip() for line in f if line.strip()] except Exception as e: logger.error(f"Failed to read IP file: {e}") sys.exit(1) if not ips: logger.warning("No IPs found in file.") sys.exit(0) logger.info(f"Starting '{action}' for {len(ips)} servers...") start_time = time.time() # Parallel Execution # Adjust max_workers as needed. 20 is a safe defaults for I/O bound network tasks. with ThreadPoolExecutor(max_workers=20) as executor: futures = [executor.submit(execute_action, ip, action) for ip in ips] for future in futures: future.result() # Wait for all to complete elapsed = time.time() - start_time logger.info(f"Completed '{action}' in {elapsed:.2f} seconds.") if __name__ == "__main__": main()