#!/usr/bin/env python3 from __future__ import annotations import re import subprocess from pathlib import Path from typing import Optional, List import logging # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s [INFO] root: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # ───────────────────────────────────────────────────────────── # 출력 디렉토리 결정 def resolve_output_dir() -> Path: here = Path(__file__).resolve().parent if here.name.lower() == "scripts" and here.parent.name.lower() == "data": base = here.parent else: base = here.parent 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 # ───────────────────────────────────────────────────────────── # NIC MAC 수집 (가변 포트 대응) def extract_nic_macs(text: str, nic_type: str) -> List[str]: """ nic_type: - 'Integrated' - 'Embedded' """ pattern = re.compile( rf"NIC\.{nic_type}\.\d+-\d+-\d+.*?([0-9A-Fa-f]{{2}}(?::[0-9A-Fa-f]{{2}}){{5}})", re.IGNORECASE ) return sorted(set(m.group(1) for m in pattern.finditer(text))) # ───────────────────────────────────────────────────────────── # hwinventory 블록 기반 Manufacturer 추출 (DIMM) def extract_memory_vendors(hwinventory: str) -> List[str]: vendors = set() blocks = re.split(r"\n\s*\n", hwinventory) for block in blocks: if "[InstanceID: DIMM." in block: m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE) if m: vendors.add(m.group(1).strip()) return sorted(vendors) # ───────────────────────────────────────────────────────────── # BOSS 디스크 Vendor 추출 def extract_boss_vendors(hwinventory: str) -> List[str]: vendors = set() blocks = re.split(r"\n\s*\n", hwinventory) for block in blocks: if "BOSS." in block: m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE) if m: vendors.add(m.group(1).strip()) return sorted(vendors) # ───────────────────────────────────────────────────────────── # 일반 Disk Vendor (BOSS 제외) DISK_PREFIXES = [ "Disk.Bay.", "Disk.M.2.", "Disk.Direct.", "Disk.Slot." ] def extract_disk_vendors_excluding_boss(hwinventory: str) -> List[str]: vendors = set() blocks = re.split(r"\n\s*\n", hwinventory) for block in blocks: if "BOSS." in block: continue for prefix in DISK_PREFIXES: if f"[InstanceID: {prefix}" in block: m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE) if m: vendors.add(m.group(1).strip()) return sorted(vendors) # ───────────────────────────────────────────────────────────── def fetch_idrac_info_one(ip: str, output_dir: Path) -> None: getsysinfo = run([ "racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo" ]) hwinventory = run([ "racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "hwinventory" ]) # ── Service Tag svc_tag = parse_single_value(r"SVC\s*Tag\s*=\s*(\S+)", getsysinfo) if not svc_tag: logging.error(f"[ERROR] SVC Tag 수집 실패: {ip}") return # ── iDRAC MAC idrac_mac = parse_single_value( r"MAC Address\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo ) # ── NIC MAC integrated_macs = extract_nic_macs(getsysinfo, "Integrated") embedded_macs = extract_nic_macs(getsysinfo, "Embedded") # ── Vendors memory_vendors = extract_memory_vendors(hwinventory) disk_vendors = extract_disk_vendors_excluding_boss(hwinventory) boss_vendors = extract_boss_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") for mac in integrated_macs: f.write(f"{mac}\n") for mac in embedded_macs: f.write(f"{mac}\n") f.write(f"{idrac_mac or ''}\n") # Memory Vendors for v in memory_vendors: f.write(f"{v}\n") # Disk Vendors (일반) for v in disk_vendors: f.write(f"{v}\n") # BOSS Vendors (있을 때만, 벤더명만) for v in boss_vendors: f.write(f"{v}\n") logging.info(f"[OK] {svc_tag} 수집 완료") # ───────────────────────────────────────────────────────────── def main(ip_file: str) -> None: ip_path = Path(ip_file) if not ip_path.is_file(): logging.error(f"[ERROR] IP 파일 없음: {ip_file}") 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("[ERROR] IP 목록이 비어 있습니다.") return for ip in ips: try: fetch_idrac_info_one(ip, output_dir) except Exception as e: logging.error(f"[ERROR] {ip} 처리 실패: {e}") # Bash 스크립트와 동일하게 입력 IP 파일 삭제 try: ip_path.unlink(missing_ok=True) except Exception: pass logging.info("정보 수집 완료.") # ───────────────────────────────────────────────────────────── if __name__ == "__main__": import sys import time if len(sys.argv) != 2: logging.error("Usage: python script.py ") sys.exit(1) start = time.time() main(sys.argv[1]) elapsed = int(time.time() - start) h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60 logging.info(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")