#!/usr/bin/env python3 from __future__ import annotations import re import subprocess from pathlib import Path from typing import Optional import logging # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s [INFO] root: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # ───────────────────────────────────────────────────────────── # 저장 위치: data/scripts 아래에 있다면 → data/idrac_info 생성 def resolve_output_dir() -> Path: here = Path(__file__).resolve().parent base = here.parent if here.name.lower() == "scripts" else here out = base / "idrac_info" out.mkdir(parents=True, exist_ok=True) return out # ───────────────────────────────────────────────────────────── # iDRAC 접속 계정 IDRAC_USER = "root" IDRAC_PASS = "calvin" def run(cmd: list[str]) -> str: """racadm 명령 실행 (stdout 수집)""" try: return subprocess.getoutput(" ".join(cmd)) except Exception: return "" def parse_single_value(pattern: str, text: str) -> Optional[str]: """단일 값 추출용""" m = re.search(pattern, text, flags=re.IGNORECASE) return m.group(1).strip() if m else None def find_mac_by_fqdd(fqdd: str, text: str) -> Optional[str]: """ swinventory 출력에서 FQDD = {fqdd} 라인을 찾고, 그 주변(±10줄) 내에서 MAC 주소를 추출. """ lines = text.splitlines() for i, line in enumerate(lines): if f"FQDD = {fqdd}" in line: for j in range(max(0, i - 10), min(i + 10, len(lines))): m = re.search(r"([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})", lines[j]) if m: return m.group(1) return None def extract_vendors(hwinventory: str) -> tuple[str, str]: """ [InstanceID: DIMM...] 또는 [InstanceID: Disk.Bay...] 블록 내 Manufacturer 추출 """ # Memory Vendors mem_vendors = re.findall( r"\[InstanceID:\s*DIMM[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)", hwinventory, flags=re.IGNORECASE ) mem_vendors = [v.strip() for v in mem_vendors if v.strip()] memory = ", ".join(sorted(set(mem_vendors))) if mem_vendors else "Not Found" # SSD Vendors ssd_vendors = re.findall( r"\[InstanceID:\s*Disk\.Bay[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)", hwinventory, flags=re.IGNORECASE ) ssd_vendors = [v.strip() for v in ssd_vendors if v.strip()] ssd = ", ".join(sorted(set(ssd_vendors))) if ssd_vendors else "Not Found" return memory, ssd def fetch_idrac_info_one(ip: str, output_dir: Path) -> None: """단일 서버 iDRAC 정보 수집""" logging.info(f"[+] Collecting iDRAC info from {ip} ...") getsysinfo = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo"]) hwinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "hwinventory"]) swinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "swinventory"]) # 서비스 태그 svc_tag = parse_single_value(r"SVC\s*Tag\s*=\s*(\S+)", getsysinfo) if not svc_tag: logging.error(f"[!] Failed to retrieve SVC Tag for IP: {ip}") return # iDRAC MAC idrac_mac = parse_single_value(r"MAC\s*Address\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo) # NIC.Integrated / Onboard MAC integrated_1 = parse_single_value(r"NIC\.Integrated\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo) integrated_2 = parse_single_value(r"NIC\.Integrated\.1-2-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo) onboard_1 = parse_single_value(r"NIC\.Embedded\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo) onboard_2 = parse_single_value(r"NIC\.Embedded\.2-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo) # 메모리 / SSD 제조사 memory, ssd = extract_vendors(hwinventory) # 결과 파일 저장 out_file = output_dir / f"{svc_tag}.txt" with out_file.open("w", encoding="utf-8", newline="\n") as f: f.write(f"{svc_tag}\n") f.write(f"{integrated_1 or ''}\n") f.write(f"{integrated_2 or ''}\n") f.write(f"{onboard_1 or ''}\n") f.write(f"{onboard_2 or ''}\n") f.write(f"{idrac_mac or ''}\n") f.write(f"{memory}\n") f.write(f"{ssd}\n") logging.info(f"[✔] {svc_tag} ({ip}) info saved.") def main(ip_file: str) -> None: ip_path = Path(ip_file) if not ip_path.is_file(): logging.error(f"[!] IP file {ip_file} does not exist.") return output_dir = resolve_output_dir() ips = [line.strip() for line in ip_path.read_text(encoding="utf-8").splitlines() if line.strip()] if not ips: logging.error("[!] No IP addresses found in the file.") return for ip in ips: try: fetch_idrac_info_one(ip, output_dir) except Exception as e: logging.error(f"[!] Error processing {ip}: {e}") try: ip_path.unlink(missing_ok=True) except Exception: pass logging.info("\n정보 수집 완료.") if __name__ == "__main__": import sys, time if len(sys.argv) != 2: logging.error("Usage: python script.py ") sys.exit(1) start = time.time() main(sys.argv[1]) end = time.time() elapsed = int(end - start) h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60 logging.info(f"수집 완료 시간: {h}시간 {m}분 {s}초")