207 lines
12 KiB
Python
207 lines
12 KiB
Python
import subprocess
|
|
import sys
|
|
import re
|
|
import time
|
|
from pathlib import Path
|
|
import logging
|
|
|
|
# Configure logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s [INFO] root: %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# iDRAC 접속 설정
|
|
IDRAC_USER = "root"
|
|
IDRAC_PASS = "calvin"
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 저장 위치 결정
|
|
def resolve_output_dir() -> Path:
|
|
here = Path(__file__).resolve().parent
|
|
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
|
|
base = here.parent
|
|
elif here.name.lower() == "scripts":
|
|
base = here.parent
|
|
else:
|
|
base = here.parent
|
|
|
|
out = base / "idrac_info"
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
return out
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 유틸리티 함수
|
|
def run_racadm(ip, *args):
|
|
"""racadm 명령어를 실행하고 결과를 반환합니다."""
|
|
cmd = ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS] + list(args)
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=30)
|
|
return result.stdout
|
|
except Exception as e:
|
|
# 에러 발생 시 빈 문자열 반환 (스크립트 중단 방지)
|
|
return ""
|
|
|
|
def extract_value(text, pattern, delimiter='='):
|
|
"""텍스트에서 특정 키의 값을 추출하고 공백을 제거합니다."""
|
|
for line in text.splitlines():
|
|
if re.search(pattern, line, re.IGNORECASE):
|
|
if delimiter in line:
|
|
# '=' 기준으로 쪼갠 후 값만 가져와서 공백 제거
|
|
return line.split(delimiter, 1)[1].strip()
|
|
return "N/A"
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 메인 수집 함수
|
|
def fetch_idrac_info(ip, output_dir):
|
|
logging.info(f">>> [{ip}] 데이터 수집 시작...")
|
|
|
|
# 1. 원본 데이터 덩어리 가져오기 (API 호출 최소화)
|
|
hwinventory = run_racadm(ip, "hwinventory")
|
|
getsysinfo = run_racadm(ip, "getsysinfo")
|
|
sys_profile = run_racadm(ip, "get", "bios.SysProfileSettings")
|
|
proc_settings = run_racadm(ip, "get", "bios.ProcSettings")
|
|
mem_settings = run_racadm(ip, "get", "bios.MemSettings")
|
|
storage_ctrl = run_racadm(ip, "get", "STORAGE.Controller.1")
|
|
|
|
# 서비스 태그 확인 (파일명 결정용)
|
|
svc_tag = extract_value(getsysinfo, "SVC Tag")
|
|
if svc_tag == "N/A":
|
|
logging.error(f"!!! [{ip}] SVC Tag 추출 실패. 작업을 건너뜁니다.")
|
|
return
|
|
|
|
output_file = output_dir / f"{svc_tag}.txt"
|
|
|
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
# 헤더 작성
|
|
f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {svc_tag})\n\n")
|
|
|
|
# --- 1. Firmware Version 정보 ---
|
|
f.write("-" * 42 + " Firmware Version 정보 " + "-" * 42 + "\n")
|
|
f.write(f"1. SVC Tag : {svc_tag}\n")
|
|
f.write(f"2. Bios Firmware : {extract_value(getsysinfo, 'System BIOS Version')}\n")
|
|
f.write(f"3. iDRAC Firmware Version : {extract_value(getsysinfo, 'Firmware Version')}\n")
|
|
|
|
# NIC 펌웨어 (별도 호출 필요)
|
|
nic1 = run_racadm(ip, "get", "NIC.FrmwImgMenu.1")
|
|
nic5 = run_racadm(ip, "get", "NIC.FrmwImgMenu.5")
|
|
f.write(f"4. NIC Integrated Firmware Version : {extract_value(nic1, '#FamilyVersion')}\n")
|
|
f.write(f"5. OnBoard NIC Firmware Version : {extract_value(nic5, '#FamilyVersion')}\n")
|
|
f.write(f"6. Raid Controller Firmware Version : {extract_value(hwinventory, 'ControllerFirmwareVersion')}\n\n")
|
|
|
|
# --- 2. Bios 설정 정보 ---
|
|
f.write("-" * 45 + " Bios 설정 정보 " + "-" * 45 + "\n")
|
|
boot_mode = run_racadm(ip, "get", "bios.BiosBootSettings")
|
|
f.write(f"01. Bios Boot Mode : {extract_value(boot_mode, 'BootMode')}\n")
|
|
f.write(f"02. System Profile Settings - System Profile : {extract_value(sys_profile, 'SysProfile=')}\n")
|
|
f.write(f"03. System Profile Settings - CPU Power Management : {extract_value(sys_profile, 'ProcPwrPerf')}\n")
|
|
f.write(f"04. System Profile Settings - Memory Frequency : {extract_value(sys_profile, 'MemFrequency')}\n")
|
|
f.write(f"05. System Profile Settings - Turbo Boost : {extract_value(sys_profile, 'ProcTurboMode')}\n")
|
|
f.write(f"06. System Profile Settings - PCI ASPM L1 Link Power Management : {extract_value(sys_profile, 'PcieAspmL1')}\n")
|
|
f.write(f"07. System Profile Settings - C-States : {extract_value(sys_profile, 'ProcCStates')}\n")
|
|
f.write(f"08. System Profile Settings - Determinism Slider : {extract_value(sys_profile, 'DeterminismSlider')}\n")
|
|
f.write(f"08-2. System Profile Settings - Dynamic Link Width Management (DLWM) : {extract_value(sys_profile, 'DynamicLinkWidthManagement')}\n")
|
|
|
|
f.write(f"09. Processor Settings - Logical Processor : {extract_value(proc_settings, 'LogicalProc')}\n")
|
|
f.write(f"10. Processor Settings - Virtualization Technology : {extract_value(proc_settings, 'ProcVirtualization')}\n")
|
|
f.write(f"11. Processor Settings - NUMA Nodes Per Socket : {extract_value(proc_settings, 'NumaNodesPerSocket')}\n")
|
|
f.write(f"12. Processor Settings - x2APIC Mode : {extract_value(proc_settings, 'ProcX2Apic')}\n")
|
|
|
|
f.write(f"13. Memory Settings - Dram Refresh Delay : {extract_value(mem_settings, 'DramRefreshDelay')}\n")
|
|
f.write(f"14. Memory Settings - DIMM Self Healing (PPR) : {extract_value(mem_settings, 'PPROnUCE')}\n")
|
|
f.write(f"15. Memory Settings - Correctable Error Logging : {extract_value(mem_settings, 'CECriticalSEL')}\n")
|
|
|
|
thermal = run_racadm(ip, "get", "System.ThermalSettings")
|
|
f.write(f"16. System Settings - Thermal Profile Optimization : {extract_value(thermal, 'ThermalProfile')}\n")
|
|
|
|
integrated = run_racadm(ip, "get", "Bios.IntegratedDevices")
|
|
f.write(f"17. Integrated Devices Settings - SR-IOV Global Enable : {extract_value(integrated, 'SriovGlobalEnable')}\n")
|
|
|
|
misc = run_racadm(ip, "get", "bios.MiscSettings")
|
|
f.write(f"18. Miscellaneous Settings - F1/F2 Prompt on Error : {extract_value(misc, 'ErrPrompt')}\n\n")
|
|
|
|
# --- 3. iDRAC 설정 정보 ---
|
|
f.write("-" * 45 + " iDRAC 설정 정보 " + "-" * 45 + "\n")
|
|
f.write(f"01. iDRAC Settings - Timezone : {extract_value(run_racadm(ip, 'get', 'iDRAC.Time.Timezone'), 'Timezone')}\n")
|
|
f.write(f"02. iDRAC Settings - IPMI LAN Selection : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentNIC'), 'ActiveNIC')}\n")
|
|
f.write(f"03. iDRAC Settings - IPMI IP(IPv4) : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentIPv4'), 'DHCPEnable')}\n")
|
|
f.write(f"04. iDRAC Settings - IPMI IP(IPv6) : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentIPv6'), 'Enable=')}\n")
|
|
f.write(f"05. iDRAC Settings - Redfish Support : {extract_value(run_racadm(ip, 'get', 'iDRAC.Redfish.Enable'), 'Enable=')}\n")
|
|
f.write(f"06. iDRAC Settings - SSH Support : {extract_value(run_racadm(ip, 'get', 'iDRAC.SSH'), 'Enable=')}\n")
|
|
|
|
# AD 관련 설정 (AD Group 1, 2 포함)
|
|
f.write(f"07. iDRAC Settings - AD User Domain Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.USERDomain.1.Name'), 'Name')}\n")
|
|
f.write(f"08. iDRAC Settings - SC Server Address : {extract_value(run_racadm(ip, 'get', 'iDRAC.ActiveDirectory.DomainController1'), 'DomainController1')}\n")
|
|
f.write(f"09. iDRAC Settings - SE AD RoleGroup Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Name'), 'Name')}\n")
|
|
f.write(f"10. iDRAC Settings - SE AD RoleGroup Domain : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Domain'), 'Domain')}\n")
|
|
f.write(f"11. iDRAC Settings - SE AD RoleGroup Privilege : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Privilege'), 'Privilege')}\n")
|
|
f.write(f"12. iDRAC Settings - IDC AD RoleGroup Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Name'), 'Name')}\n")
|
|
f.write(f"13. iDRAC Settings - IDC AD RoleGroup Domain : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Domain'), 'Domain')}\n")
|
|
f.write(f"14. iDRAC Settings - IDC AD RoleGroup Privilege : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Privilege'), 'Privilege')}\n")
|
|
|
|
# Syslog 및 기타
|
|
f.write(f"15. iDRAC Settings - Remote Log (syslog) : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.SysLogEnable'), 'SysLogEnable')}\n")
|
|
f.write(f"16. iDRAC Settings - syslog server address 1 : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Server1'), 'Server1')}\n")
|
|
f.write(f"17. iDRAC Settings - syslog server address 2 : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Server2'), 'Server2')}\n")
|
|
f.write(f"18. iDRAC Settings - syslog server port : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Port'), 'Port')}\n")
|
|
f.write(f"19. iDRAC Settings - Remote KVM Nonsecure port : {extract_value(run_racadm(ip, 'get', 'iDRAC.VirtualConsole.Port'), 'Port')}\n\n")
|
|
|
|
# --- 4. Raid 설정 정보 (주석 해제 버전) ---
|
|
f.write("-" * 45 + " Raid 설정 정보 " + "-" * 45 + "\n")
|
|
f.write(f"01. RAID Settings - Raid ProductName : {extract_value(hwinventory, 'ProductName = PERC')}\n")
|
|
f.write(f"02. RAID Settings - Raid Types : {extract_value(hwinventory, 'RAIDTypes')}\n")
|
|
f.write(f"03. RAID Settings - StripeSize : {extract_value(hwinventory, 'StripeSize')}\n")
|
|
f.write(f"04. RAID Settings - ReadCachePolicy : {extract_value(hwinventory, 'ReadCachePolicy')}\n")
|
|
f.write(f"05. RAID Settings - WriteCachePolicy : {extract_value(hwinventory, 'WriteCachePolicy')}\n")
|
|
f.write(f"06. RAID Settings - CheckConsistencyRate : {extract_value(storage_ctrl, 'CheckConsistencyRate')}\n")
|
|
f.write(f"07. RAID Settings - PatrolReadMode : {extract_value(storage_ctrl, 'PatrolReadMode')}\n")
|
|
f.write(f"08. RAID Settings - period : 168h\n")
|
|
f.write(f"09. RAID Settings - Power Save : No\n")
|
|
f.write(f"10. RAID Settings - JBODMODE : Controller does not support JBOD\n")
|
|
f.write(f"11. RAID Settings - maxconcurrentpd : 240\n")
|
|
|
|
logging.info(f"✅ [{ip}] 저장 완료: {output_file.name}")
|
|
|
|
# ─────────────────────────────────────────────────────────────
|
|
# 실행 흐름 제어
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
logging.error(f"사용법: python {sys.argv[0]} <ip_file>")
|
|
sys.exit(1)
|
|
|
|
ip_file = Path(sys.argv[1])
|
|
if not ip_file.exists():
|
|
logging.error(f"오류: IP 파일 '{ip_file}'이 존재하지 않습니다.")
|
|
sys.exit(1)
|
|
|
|
output_dir = resolve_output_dir()
|
|
start_time = time.time()
|
|
|
|
# IP 목록 읽기
|
|
with open(ip_file, "r") as f:
|
|
ips = [line.strip() for line in f if line.strip()]
|
|
|
|
# 각 IP별로 수집 실행
|
|
for ip in ips:
|
|
try:
|
|
fetch_idrac_info(ip, output_dir)
|
|
except Exception as e:
|
|
logging.error(f"❌ [{ip}] 처리 중 예외 발생: {e}")
|
|
|
|
# 소요 시간 계산
|
|
end_time = time.time()
|
|
elapsed = end_time - start_time
|
|
hours, rem = divmod(elapsed, 3600)
|
|
minutes, seconds = divmod(rem, 60)
|
|
|
|
logging.info("="*50)
|
|
logging.info("정보 수집 완료.")
|
|
logging.info(f"총 소요 시간: {int(hours)}시간 {int(minutes)}분 {int(seconds)}초")
|
|
logging.info(f"저장 경로: {output_dir}")
|
|
logging.info("="*50)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|