175 lines
6.0 KiB
Python
175 lines
6.0 KiB
Python
#!/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)
|
|
|
|
# NIC Slot별 MAC
|
|
NIC_Slot_2_1 = find_mac_by_fqdd("NIC.Slot.2-1-1", swinventory)
|
|
NIC_Slot_2_2 = find_mac_by_fqdd("NIC.Slot.2-2-1", swinventory)
|
|
NIC_Slot_3_1 = find_mac_by_fqdd("NIC.Slot.3-1-1", swinventory)
|
|
NIC_Slot_3_2 = find_mac_by_fqdd("NIC.Slot.3-2-1", swinventory)
|
|
|
|
# 메모리 / 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"{NIC_Slot_2_1 or ''}\n")
|
|
f.write(f"{NIC_Slot_2_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 <ip_file>")
|
|
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}초")
|