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 ") sys.exit(1) main(sys.argv[1])