update
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user