235 lines
7.3 KiB
Python
235 lines
7.3 KiB
Python
#!/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 <ip_file>")
|
|
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} 초.")
|