Update 2025-12-19 20:23:59
This commit is contained in:
234
data/scripts/MAC_info.py
Normal file
234
data/scripts/MAC_info.py
Normal file
@@ -0,0 +1,234 @@
|
||||
#!/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} 초.")
|
||||
Reference in New Issue
Block a user