Files
iDRAC_Info/data/scripts/MAC_info.py
2025-12-19 20:23:59 +09:00

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} 초.")