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

156 lines
5.7 KiB
Python

import os
import re
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging
from dotenv import load_dotenv, find_dotenv
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# .env 자동 탐색 로드 (현재 파일 기준 상위 디렉터리까지 검색)
load_dotenv(find_dotenv())
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
def resolve_output_dir() -> Path:
"""
실행 위치와 무관하게 결과를 data/idrac_info 밑으로 저장.
- 스크립트가 data/scripts/ 에 있다면 → data/idrac_info
- 그 외 위치라도 → (스크립트 상위 폴더)/idrac_info
"""
here = Path(__file__).resolve().parent # .../data/scripts 또는 다른 폴더
# case 1: .../data/scripts → data/idrac_info
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent # data
# case 2: .../scripts (상위가 data가 아닐 때도 상위 폴더를 base로 사용)
elif here.name.lower() == "scripts":
base = here.parent
# case 3: 일반적인 경우: 현재 파일의 상위 폴더
else:
base = here.parent
out = base / "idrac_info"
out.mkdir(parents=True, exist_ok=True)
return out
def fetch_idrac_info(idrac_ip: str, output_dir: Path) -> None:
try:
# 서비스 태그 가져오기 (get 제외)
cmd_getsysinfo = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "getsysinfo"
]
getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
svc_tag_match = re.search(r"SVC Tag\s*=\s*(\S+)", getsysinfo)
svc_tag = svc_tag_match.group(1) if svc_tag_match else None
if not svc_tag:
logging.error(f"Failed to retrieve SVC Tag for IP: {idrac_ip}")
return
# InfiniBand.VndrConfigPage 목록 가져오기
cmd_list = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "get", "InfiniBand.VndrConfigPage"
]
output_list = subprocess.getoutput(" ".join(cmd_list))
# InfiniBand.VndrConfigPage.<숫자> 및 Key 값 추출
matches = re.findall(
r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
output_list
)
# 결과 저장 파일
output_file = output_dir / f"{svc_tag}.txt"
with output_file.open("w", encoding="utf-8", newline="\n") as f:
# 서비스 태그
f.write(f"{svc_tag}\n")
# --- 슬롯/GUID 수집 후 원하는 순서로 기록 ---
slot_to_guid: dict[str, str] = {}
slots_in_match_order: list[str] = []
# 각 페이지 상세 조회
for number, slot in matches:
cmd_detail = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "",
"get", f"InfiniBand.VndrConfigPage.{number}"
]
output_detail = subprocess.getoutput(" ".join(cmd_detail))
# PortGUID 추출
match_guid = re.search(r"PortGUID=(\S+)", output_detail)
port_guid = match_guid.group(1) if match_guid else "Not Found"
s = str(slot)
slot_to_guid[s] = port_guid
slots_in_match_order.append(s)
# 검색된 슬롯 개수에 따라 출력 순서 결정
total_slots = len(slots_in_match_order)
if total_slots == 4:
desired_order = ['38', '37', '32', '34']
elif total_slots == 10:
desired_order = ['38', '39', '37', '36', '32', '33', '34', '35', '31', '40']
else:
desired_order = slots_in_match_order
# 지정된 순서대로 파일에 기록 + GUID 요약 생성
hex_guid_list: list[str] = []
for s in desired_order:
guid = slot_to_guid.get(s, "Not Found")
f.write(f"Slot.{s}: {guid}\n")
if guid != "Not Found":
hex_guid_list.append(f"0x{guid.replace(':', '').upper()}")
if hex_guid_list:
f.write(f"GUID: {';'.join(hex_guid_list)}\n")
except Exception as e:
logging.error(f"Error processing IP {idrac_ip}: {e}")
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() # ← 여기서 OS 무관 저장 위치 확정 (data/idrac_info)
# print(f"[debug] output_dir = {output_dir}") # 필요 시 확인
with ip_path.open("r", encoding="utf-8") as file:
ip_addresses = [line.strip() for line in file if line.strip()]
# 스레드풀
with ThreadPoolExecutor(max_workers=100) as executor:
future_to_ip = {executor.submit(fetch_idrac_info, ip, output_dir): ip for ip in ip_addresses}
for future in as_completed(future_to_ip):
ip = future_to_ip[future]
try:
future.result()
logging.info(f"Completed: {ip}")
except Exception as e:
logging.error(f"Error processing {ip}: {e}")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
main(sys.argv[1])