This commit is contained in:
unknown
2026-03-21 07:26:38 +09:00
parent 166d94fca4
commit 40150cd66d
406 changed files with 34179 additions and 55658 deletions
+2 -2
View File
@@ -2,5 +2,5 @@
#IDRAC_PASS=tksoWkd12#
IDRAC_USER=root
IDRAC_PASS=calvin
OME_USER=OME
OME_PASS=epF!@34
OME_USER=khsky84
OME_PASS=Zes9ro12#$
+3 -1
View File
@@ -26,7 +26,9 @@ def validate_ip_file(ip_file_path):
return ip_file_path
# 정보 저장 디렉터리 설정
OUTPUT_DIR = "idrac_info"
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# data/scripts/ -> data/temp/staging
OUTPUT_DIR = os.path.join(BASE_DIR, "..", "temp", "staging")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# iDRAC 정보를 가져오는 함수 정의
-60
View File
@@ -1,60 +0,0 @@
import os
import subprocess
import time
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 환경 변수 로드
load_dotenv() # .env 파일에서 환경 변수 로드
# 사용자 이름 및 비밀번호 설정
IDRAC_USER, IDRAC_PASS = os.getenv("IDRAC_USER"), os.getenv("IDRAC_PASS")
# IP 주소 파일 로드 함수
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
logging.error(f"IP file {ip_file_path} does not exist.")
sys.exit(1)
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# iDRAC 정보를 가져오는 함수 정의
def fetch_idrac_info(idrac_ip):
logging.info(f"Collecting TSR report for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
["racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "techsupreport", "collect"],
capture_output=True,
text=True
)
if result.returncode == 0:
logging.info(f"Successfully collected TSR report for {idrac_ip}")
else:
logging.error(f"Failed to collect TSR report for {idrac_ip}: {result.stderr.strip()}")
except Exception as e:
logging.error(f"Exception occurred for {idrac_ip}: {e}")
# 메인 함수
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
start_time = time.time()
# 병렬로 iDRAC 정보를 가져오는 작업 수행 (최대 10개 동시 처리)
with Pool() as pool:
pool.map(fetch_idrac_info, ip_list)
elapsed_time = time.time() - start_time
logging.info(f"설정 완료. 수집 완료 시간: {int(elapsed_time // 3600)} 시간, {int((elapsed_time % 3600) // 60)} 분, {int(elapsed_time % 60)} 초.")
-63
View File
@@ -1,63 +0,0 @@
import os
import subprocess
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 환경 변수 로드
load_dotenv() # .env 파일에서 환경 변수 로드
# 사용자 이름 및 비밀번호 설정
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
OME_USER = os.getenv("OME_USER")
OME_PASS = os.getenv("OME_PASS")
# IP 주소 파일 로드 및 유효성 검사
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
logging.error(f"IP file {ip_file_path} does not exist.")
sys.exit(1)
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# iDRAC 정보를 가져오는 함수
def fetch_idrac_info(idrac_ip):
logging.info(f"Collecting TSR report for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
[
"racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS,
"techsupreport", "export", "-l", "//10.10.3.251/share/", "-u", OME_USER, "-p", OME_PASS
],
capture_output=True,
text=True
)
if result.returncode == 0:
logging.info(f"Successfully collected TSR report for {idrac_ip}")
else:
logging.error(f"Failed to collect TSR report for {idrac_ip}: {result.stderr.strip()}")
except Exception as e:
logging.error(f"Exception occurred for {idrac_ip}: {e}")
# 메인 함수
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
# 병렬로 iDRAC 정보를 가져오는 작업 수행
with Pool() as pool:
pool.map(fetch_idrac_info, ip_list)
logging.info("설정 완료.")
-51
View File
@@ -1,51 +0,0 @@
import os
import subprocess
import time
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
# Load environment variables
load_dotenv() # Load variables from .env file
# Credentials
IDRAC_USER, IDRAC_PASS = os.getenv("IDRAC_USER"), os.getenv("IDRAC_PASS")
# Load IP addresses from file
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
sys.exit(f"IP file {ip_file_path} does not exist.")
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# Clear SEL log for given iDRAC IP
def clear_idrac_sel_log(idrac_ip):
print(f"Clearing SEL log for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
["racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "clrsel"],
capture_output=True,
text=True
)
print(
f"Successfully cleared SEL log for {idrac_ip}"
if result.returncode == 0
else f"Failed to clear SEL log for {idrac_ip}: {result.stderr.strip()}"
)
except Exception as e:
print(f"Exception occurred for {idrac_ip}: {e}")
# Main function
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
start_time = time.time()
# Execute in parallel using Pool (max 10 simultaneous processes)
with Pool(processes=10) as pool:
pool.map(clear_idrac_sel_log, ip_list)
elapsed_time = time.time() - start_time
print(f"Log clear 완료. 완료 시간: {int(elapsed_time // 3600)} 시간, {int((elapsed_time % 3600) // 60)} 분, {int(elapsed_time % 60)} 초.")
-60
View File
@@ -1,60 +0,0 @@
import os
import subprocess
import time
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Load environment variables
load_dotenv() # Load variables from .env file
# Credentials
IDRAC_USER, IDRAC_PASS = os.getenv("IDRAC_USER"), os.getenv("IDRAC_PASS")
# Load IP addresses from file
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
logging.error(f"IP file {ip_file_path} does not exist.")
sys.exit(1)
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# Power on the server for given iDRAC IP
def poweron_idrac_server(idrac_ip):
logging.info(f"Powering on server for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
["racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerup"],
capture_output=True,
text=True
)
if result.returncode == 0:
logging.info(f"Successfully powered on server for {idrac_ip}")
else:
logging.error(f"Failed to power on server for {idrac_ip}: {result.stderr.strip()}")
except Exception as e:
logging.error(f"Exception occurred for {idrac_ip}: {e}")
# Main function
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
start_time = time.time()
# Execute in parallel using Pool (max 10 simultaneous processes)
with Pool() as pool:
pool.map(poweron_idrac_server, ip_list)
elapsed_time = time.time() - start_time
logging.info(f"Server Power On 완료. 완료 시간: {int(elapsed_time // 3600)} 시간, {int((elapsed_time % 3600) // 60)} 분, {int(elapsed_time % 60)} 초.")
-60
View File
@@ -1,60 +0,0 @@
import os
import subprocess
import time
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Load environment variables
load_dotenv() # Load variables from .env file
# Credentials
IDRAC_USER, IDRAC_PASS = os.getenv("IDRAC_USER"), os.getenv("IDRAC_PASS")
# Load IP addresses from file
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
logging.error(f"IP file {ip_file_path} does not exist.")
sys.exit(1)
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# Power off the server for given iDRAC IP
def poweroff_idrac_server(idrac_ip):
logging.info(f"Powering off server for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
["racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerdown"],
capture_output=True,
text=True
)
if result.returncode == 0:
logging.info(f"Successfully powered off server for {idrac_ip}")
else:
logging.error(f"Failed to power off server for {idrac_ip}: {result.stderr.strip()}")
except Exception as e:
logging.error(f"Exception occurred for {idrac_ip}: {e}")
# Main function
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
start_time = time.time()
# Execute in parallel using Pool (max 10 simultaneous processes)
with Pool() as pool:
pool.map(poweroff_idrac_server, ip_list)
elapsed_time = time.time() - start_time
logging.info(f"Server Power Off 완료. 완료 시간: {int(elapsed_time // 3600)} 시간, {int((elapsed_time % 3600) // 60)} 분, {int(elapsed_time % 60)} 초.")
-60
View File
@@ -1,60 +0,0 @@
import os
import subprocess
import time
from dotenv import load_dotenv
import sys
from multiprocessing import Pool
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# Load environment variables
load_dotenv() # Load variables from .env file
# Credentials
IDRAC_USER, IDRAC_PASS = os.getenv("IDRAC_USER"), os.getenv("IDRAC_PASS")
# Load IP addresses from file
def load_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
logging.error(f"IP file {ip_file_path} does not exist.")
sys.exit(1)
with open(ip_file_path, "r") as file:
return [line.strip() for line in file if line.strip()]
# Delete all jobs for given iDRAC IP
def delete_all_jobs(idrac_ip):
logging.info(f"Deleting all jobs for iDRAC IP: {idrac_ip}")
try:
result = subprocess.run(
["racadm", "-r", idrac_ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "jobqueue", "delete", "-i", "ALL"],
capture_output=True,
text=True
)
if result.returncode == 0:
logging.info(f"Successfully deleted all jobs for {idrac_ip}")
else:
logging.error(f"Failed to delete jobs for {idrac_ip}: {result.stderr.strip()}")
except Exception as e:
logging.error(f"Exception occurred for {idrac_ip}: {e}")
# Main function
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit("Usage: python script.py <ip_file>")
ip_list = load_ip_file(sys.argv[1])
start_time = time.time()
# Execute in parallel using Pool (max 10 simultaneous processes)
with Pool() as pool:
pool.map(delete_all_jobs, ip_list)
elapsed_time = time.time() - start_time
logging.info(f"Job delete 완료. 완료 시간: {int(elapsed_time // 3600)} 시간, {int((elapsed_time % 3600) // 60)} 분, {int(elapsed_time % 60)} 초.")
+2 -1
View File
@@ -27,7 +27,8 @@ def validate_ip_file(ip_file_path):
return ip_file_path
# 정보 저장 디렉터리 설정
OUTPUT_DIR = "idrac_info"
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
OUTPUT_DIR = os.path.join(BASE_DIR, "..", "temp", "staging")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# iDRAC 정보를 가져오는 함수
+1 -1
View File
@@ -28,7 +28,7 @@ def resolve_output_dir() -> Path:
else:
base = here.parent
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -39,7 +39,7 @@ def resolve_output_dir() -> Path:
else:
base = here.parent
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+16 -187
View File
@@ -1,192 +1,21 @@
import subprocess
import os
import re
import time
#!/usr/bin/env python3
"""
레거시 호환성 래퍼 - Intel_Server_info.py
실제 로직은 unified/collect_server_info.py의 'intel_server' 프로파일 사용
"""
import sys
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'
)
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
# --- [설정] iDRAC 접속 정보 ---
IDRAC_USER = "root"
IDRAC_PASS = "calvin"
def resolve_output_dir() -> Path:
"""
사용자가 지정한 로직에 따라 저장 위치를 결정합니다.
스크립트 위치가 /data/scripts/ 일 경우 /data/idrac_info/ 에 저장합니다.
"""
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: str, command: str) -> str:
"""racadm 명령어를 실행하고 결과를 문자열로 반환합니다."""
full_cmd = f"racadm -r {ip} -u {IDRAC_USER} -p {IDRAC_PASS} {command}"
try:
# 쉘 명령 실행 (stderr도 포함하여 수집)
result = subprocess.check_output(full_cmd, shell=True, stderr=subprocess.STDOUT, universal_newlines=True)
return result
except subprocess.CalledProcessError as e:
# 통신 실패 시 빈 문자열 반환
return ""
def get_val(text: str, pattern: str) -> str:
"""텍스트 내에서 특정 키워드를 찾아 해당 줄의 설정값(= 이후의 값)을 추출합니다."""
for line in text.splitlines():
if re.search(pattern, line, re.IGNORECASE):
parts = line.split('=')
if len(parts) >= 2:
return parts[1].strip()
return "N/A"
def fetch_idrac_info(ip: str, output_dir: Path):
logging.info(f">>> {ip} 정보 수집 중...")
# 1. 원시 데이터 벌크 수집 (네트워크 오버헤드 감소)
getsysinfo = run_racadm(ip, "getsysinfo")
hwinventory = run_racadm(ip, "hwinventory")
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 = get_val(getsysinfo, "SVC Tag")
if svc_tag == "N/A":
logging.error(f"[경고] {ip} 접속 실패 혹은 SVC Tag 확인 불가. 건너뜁니다.")
return
report_path = output_dir / f"{svc_tag}.txt"
with open(report_path, "w", encoding="utf-8") as f:
# 헤더 기록
f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {svc_tag})\n\n")
# --- Section 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 : {get_val(getsysinfo, 'System BIOS Version')}\n")
f.write(f"3. iDRAC Firmware Version : {get_val(getsysinfo, 'Firmware Version')}\n")
nic1 = run_racadm(ip, "get NIC.FrmwImgMenu.1")
f.write(f"4. NIC Integrated Firmware Version : {get_val(nic1, '#FamilyVersion')}\n")
nic5 = run_racadm(ip, "get NIC.FrmwImgMenu.5")
f.write(f"5. OnBoard NIC Firmware Version : {get_val(nic5, '#FamilyVersion')}\n")
f.write(f"6. Raid Controller Firmware Version : {get_val(hwinventory, 'ControllerFirmwareVersion')}\n\n")
# --- Section 2: Bios 설정 정보 ---
f.write("-" * 45 + " Bios 설정 정보 " + "-" * 46 + "\n")
boot_settings = run_racadm(ip, "get bios.BiosBootSettings")
f.write(f"01. Bios Boot Mode : {get_val(boot_settings, 'BootMode')}\n")
f.write(f"02. System Profile : {get_val(sys_profile, 'SysProfile=')}\n")
f.write(f"03. CPU Power Management : {get_val(sys_profile, 'EnergyPerformanceBias')}\n")
f.write(f"04. Memory Frequency : {get_val(sys_profile, 'MemFrequency')}\n")
f.write(f"05. Turbo Boost : {get_val(sys_profile, 'ProcTurboMode')}\n")
f.write(f"06. C1E : {get_val(sys_profile, 'ProcC1E')}\n")
f.write(f"07. C-States : {get_val(sys_profile, 'ProcCStates')}\n")
f.write(f"08. Monitor/Mwait : {get_val(sys_profile, 'MonitorMwait')}\n")
f.write(f"09. Logical Processor : {get_val(proc_settings, 'LogicalProc')}\n")
f.write(f"10. Virtualization Technology : {get_val(proc_settings, 'ProcVirtualization')}\n")
f.write(f"11. LLC Prefetch : {get_val(proc_settings, 'LlcPrefetch')}\n")
f.write(f"12. x2APIC Mode : {get_val(proc_settings, 'ProcX2Apic')}\n")
f.write(f"13. Node Interleaving : {get_val(mem_settings, 'NodeInterleave')}\n")
f.write(f"14. DIMM Self Healing : {get_val(mem_settings, 'PPROnUCE')}\n")
f.write(f"15. Correctable Error Logging : {get_val(mem_settings, 'CECriticalSEL')}\n")
thermal = run_racadm(ip, "get System.ThermalSettings")
f.write(f"16. Thermal Profile Optimization : {get_val(thermal, 'ThermalProfile')}\n")
sriov = run_racadm(ip, "get Bios.IntegratedDevices")
f.write(f"17. SR-IOV Global Enable : {get_val(sriov, 'SriovGlobalEnable')}\n")
misc = run_racadm(ip, "get bios.MiscSettings")
f.write(f"18. F1/F2 Prompt on Error : {get_val(misc, 'ErrPrompt')}\n\n")
# --- Section 3: iDRAC 설정 정보 ---
f.write("-" * 45 + " iDRAC 설정 정보 " + "-" * 45 + "\n")
f.write(f"01. Timezone : {get_val(run_racadm(ip, 'get iDRAC.Time.Timezone'), 'Timezone')}\n")
f.write(f"02. IPMI LAN Selection : {get_val(run_racadm(ip, 'get iDRAC.CurrentNIC'), 'ActiveNIC')}\n")
f.write(f"03. IPMI IP(IPv4) DHCP : {get_val(run_racadm(ip, 'get iDRAC.CurrentIPv4'), 'DHCPEnable')}\n")
f.write(f"04. IPMI IP(IPv6) Enable : {get_val(run_racadm(ip, 'get iDRAC.CurrentIPv6'), 'Enable=')}\n")
f.write(f"05. Redfish Support : {get_val(run_racadm(ip, 'get iDRAC.Redfish.Enable'), 'Enable=')}\n")
f.write(f"06. SSH Support : {get_val(run_racadm(ip, 'get iDRAC.SSH'), 'Enable=')}\n")
f.write(f"07. AD User Domain Name : {get_val(run_racadm(ip, 'get iDRAC.USERDomain.1.Name'), 'Name')}\n")
f.write(f"08. SC Server Address : {get_val(run_racadm(ip, 'get iDRAC.ActiveDirectory.DomainController1'), 'DomainController1')}\n")
# Syslog 관련
f.write(f"15. Remote Log (syslog) : {get_val(run_racadm(ip, 'get iDRAC.SysLog.SysLogEnable'), 'SysLogEnable')}\n")
f.write(f"16. syslog server 1 : {get_val(run_racadm(ip, 'get iDRAC.SysLog.Server1'), 'Server1')}\n")
f.write(f"17. syslog server 2 : {get_val(run_racadm(ip, 'get iDRAC.SysLog.Server2'), 'Server2')}\n")
f.write(f"18. syslog server port : {get_val(run_racadm(ip, 'get iDRAC.SysLog.Port'), 'Port')}\n")
f.write(f"19. VirtualConsole Port : {get_val(run_racadm(ip, 'get iDRAC.VirtualConsole.Port'), 'Port')}\n\n")
# --- Section 4: Raid 설정 정보 ---
f.write("-" * 45 + " Raid 설정 정보 " + "-" * 46 + "\n")
f.write(f"01. Raid ProductName : {get_val(hwinventory, 'ProductName = BOSS')}, {get_val(hwinventory, 'ProductName = PERC')}\n")
f.write(f"02. RAID Types : {get_val(hwinventory, 'RAIDTypes')}\n")
f.write(f"03. StripeSize : {get_val(hwinventory, 'StripeSize')}\n")
f.write(f"04. ReadCachePolicy : {get_val(hwinventory, 'ReadCachePolicy')}\n")
f.write(f"05. WriteCachePolicy : {get_val(hwinventory, 'WriteCachePolicy')}\n")
f.write(f"06. CheckConsistencyMode : {get_val(storage_ctrl, 'CheckConsistencyMode')}\n")
f.write(f"07. PatrolReadRate : {get_val(storage_ctrl, 'PatrolReadRate')}\n")
f.write(f"08. period : 168h\n")
f.write(f"09. Power Save : No\n")
f.write(f"10. JBODMODE : Controller does not support JBOD\n")
f.write(f"11. maxconcurrentpd : 240\n")
logging.info(f" ㄴ 완료: {report_path.name}")
def main():
import sys
if len(sys.argv) < 2:
logging.error("Usage: python script.py <ip_list_file>")
return
ip_file = sys.argv[1]
if not os.path.exists(ip_file):
logging.error(f"파일을 찾을 수 없습니다: {ip_file}")
return
# 저장 위치 결정
output_dir = resolve_output_dir()
logging.info(f"[*] 결과 저장 폴더: {output_dir}")
# 시간 측정 시작
start_time = time.time()
# IP 파일 읽기
with open(ip_file, "r") as f:
ips = [line.strip() for line in f if line.strip()]
# 순차적 정보 수집
for ip in ips:
fetch_idrac_info(ip, output_dir)
# 소요 시간 계산
elapsed = time.time() - start_time
hours = int(elapsed // 3600)
minutes = int((elapsed % 3600) // 60)
seconds = int(elapsed % 60)
logging.info("=" * 50)
logging.info(f"정보 수집 완료.")
logging.info(f"소요 시간: {hours}시간 {minutes}{seconds}")
logging.info("=" * 50)
from unified.collect_server_info import main as unified_main
if __name__ == "__main__":
main()
if len(sys.argv) < 2:
print("Usage: python Intel_Server_info.py <ip_file>")
sys.exit(1)
# intel_server 프로파일 사용
sys.argv.append("intel_server")
unified_main()
+1 -1
View File
@@ -19,7 +19,7 @@ logging.basicConfig(
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
base = here.parent if here.name.lower() == "scripts" else here
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+19 -227
View File
@@ -1,234 +1,26 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
import subprocess
# -*- coding: utf-8 -*-
"""
레거시 호환성 래퍼 - MAC_info.py
실제 로직은 unified/collect_mac.py의 'default' 프로파일 사용
"""
import sys
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'
)
# unified 스크립트 경로 추가
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
# ─────────────────────────────────────────────────────────────
# 출력 디렉토리 결정
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
# unified 스크립트 임포트 및 실행
from unified.collect_mac import main as unified_main
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>")
# 기존 인터페이스 유지: python MAC_info.py <ip_file>
# 내부적으로 unified 스크립트 호출 (default 프로파일 사용)
if len(sys.argv) < 2:
print("Usage: python MAC_info.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} 초.")
# 프로파일 이름 추가
sys.argv.append("default")
unified_main()
+16 -130
View File
@@ -1,135 +1,21 @@
import os
import re
import subprocess
from dotenv import load_dotenv
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging
#!/usr/bin/env python3
"""
레거시 호환성 래퍼 - PortGUID.py
실제 로직은 unified/collect_guid.py의 'default' 프로파일 사용
"""
import sys
from pathlib import Path
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
# .env 파일에서 사용자 이름 및 비밀번호 설정
load_dotenv()
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
def fetch_idrac_info(idrac_ip, output_dir):
try:
# 서비스 태그 가져오기 (get 제외)
cmd_getsysinfo = f"racadm -r {idrac_ip} -u {IDRAC_USER} -p {IDRAC_PASS} getsysinfo"
getsysinfo = subprocess.getoutput(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 = f"racadm -r {idrac_ip} -u {IDRAC_USER} -p {IDRAC_PASS} get InfiniBand.VndrConfigPage"
output_list = subprocess.getoutput(cmd_list)
# InfiniBand.VndrConfigPage.<숫자> 및 Key 값 추출
matches = re.findall(r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]", output_list)
# 결과를 저장할 파일 생성
output_file = os.path.join(output_dir, f"{svc_tag}.txt")
os.makedirs(output_dir, exist_ok=True)
with open(output_file, "w") as f:
# 서비스 태그 저장
f.write(f"{svc_tag}\n")
# 슬롯별 GUID를 딕셔너리로 수집
slot_to_guid = {}
slots_found = []
# 각 InfiniBand.VndrConfigPage.<숫자> 처리
for number, slot in matches:
cmd_detail = f"racadm -r {idrac_ip} -u {IDRAC_USER} -p {IDRAC_PASS} get InfiniBand.VndrConfigPage.{number}"
output_detail = subprocess.getoutput(cmd_detail)
# PortGUID 값 추출
match_guid = re.search(r"PortGUID=(\S+)", output_detail)
port_guid = match_guid.group(1) if match_guid else "Not Found"
slot_to_guid[slot] = port_guid
slots_found.append(slot)
# 환경변수에서 슬롯 우선순위 읽기
slot_priority_str = os.getenv("GUID_SLOT_PRIORITY", "")
if slot_priority_str:
# 사용자 지정 슬롯 우선순위 사용
desired_order = [s.strip() for s in slot_priority_str.split(",") if s.strip()]
logging.info(f"사용자 지정 슬롯 우선순위 사용: {desired_order}")
else:
# 기본: 발견된 순서대로
desired_order = slots_found
logging.info(f"기본 순서 사용 (발견된 순서): {desired_order}")
# 지정된 순서대로 파일에 기록 + GUID 합치기
hex_guid_list = []
for slot in desired_order:
port_guid = slot_to_guid.get(slot, "Not Found")
# Slot.<숫자>: <PortGUID> 형식으로 저장
f.write(f"Slot.{slot}: {port_guid}\n")
# PortGUID를 0x 형식으로 변환하여 리스트에 추가
if port_guid != "Not Found":
hex_guid_list.append(f"0x{port_guid.replace(':', '').upper()}")
# 모든 PortGUID를 "GUID: 0x<GUID1>;0x<GUID2>" 형식으로 저장
if hex_guid_list:
f.write(f"GUID: {';'.join(hex_guid_list)}\n")
logging.info(f"GUID 합치기 완료: {len(hex_guid_list)}개 슬롯")
logging.info(f"✅ Completed: {idrac_ip}")
except Exception as e:
logging.error(f"Error processing IP {idrac_ip}: {e}")
def main(ip_file):
if not os.path.isfile(ip_file):
logging.error(f"IP file {ip_file} does not exist.")
return
# Output directory resolution
# Try to find 'data/idrac_info' relative to this script
script_dir = os.path.dirname(os.path.abspath(__file__))
# Assuming script is in data/scripts, parent is data
data_dir = os.path.dirname(script_dir)
output_dir = os.path.join(data_dir, "idrac_info")
# Fallback to current directory if structure is different
if not os.path.basename(data_dir) == "data":
output_dir = os.path.join(os.getcwd(), "idrac_info")
os.makedirs(output_dir, exist_ok=True)
logging.info(f"Output directory set to: {output_dir}")
with open(ip_file, "r") as file:
ip_addresses = [line.strip() for line in file.readlines()]
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):
try:
future.result()
except Exception as e:
logging.error(f"Error processing task: {e}")
from unified.collect_guid import main as unified_main
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
if len(sys.argv) < 2:
print("Usage: python PortGUID.py <ip_file>")
sys.exit(1)
ip_file = sys.argv[1]
main(ip_file)
# default 프로파일 사용
sys.argv.append("default")
unified_main()
+1 -1
View File
@@ -39,7 +39,7 @@ def resolve_output_dir() -> Path:
else:
base = here.parent
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -19,7 +19,7 @@ logging.basicConfig(
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
base = here.parent if here.name.lower() == "scripts" else here
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -26,7 +26,7 @@ def resolve_output_dir() -> Path:
else:
base = here.parent
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -27,7 +27,7 @@ def resolve_output_dir() -> Path:
base = here / "data"
else:
base = here # 그래도 현재 기준으로 생성
out = base / "idrac_info"
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+14 -236
View File
@@ -1,244 +1,22 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import concurrent.futures as futures
import os
import re
import subprocess
"""
레거시 호환성 래퍼 - XE9680_H200_IB_10EA_MAC_info.py
실제 로직은 unified/collect_mac.py의 'xe9680_h200_10ea' 프로파일 사용
"""
import sys
import time
from pathlib import Path
from typing import Dict, Optional, Tuple, List
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
# ===== 설정: 기본 계정/비밀번호 (환경변수로 덮어쓰기 가능) =====
IDRAC_USER = os.getenv("IDRAC_USER", "root")
IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin")
BASE_DIR = Path(__file__).resolve().parent.parent
OUTPUT_DIR = BASE_DIR / "idrac_info"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
MAC_RE = re.compile(r"([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
# --- 유틸: 명령 실행 ---
def run(cmd: List[str]) -> Tuple[int, str]:
try:
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
return p.returncode, p.stdout or ""
except Exception as e:
return 1, f"__EXEC_ERROR__ {e}"
# --- 파서: getsysinfo/swinventory/hwinventory 에서 필요한 값 추출 ---
def extract_first_mac(text: str) -> Optional[str]:
m = MAC_RE.search(text)
return m.group(0) if m else None
def parse_svc_tag(getsysinfo: str) -> Optional[str]:
# 예: "SVC Tag = ABCDEF2"
m = re.search(r"SVC\s*Tag\s*=\s*([^\s]+)", getsysinfo)
return m.group(1).strip() if m else None
def parse_specific_mac_in_getsysinfo(getsysinfo: str, key: str) -> Optional[str]:
"""
key 예시:
- "NIC.Integrated.1-1-1"
- "NIC.Embedded.1-1-1"
해당 라인에서 MAC 패턴 추출
"""
for line in getsysinfo.splitlines():
if key in line:
mac = extract_first_mac(line)
if mac:
return mac
return None
def parse_idrac_mac(getsysinfo: str) -> Optional[str]:
"""
원본 스크립트는 "MAC Address = " 를 grep 했습니다.
해당 라인(들) 중 첫 MAC을 사용합니다.
"""
lines = [ln for ln in getsysinfo.splitlines() if "MAC Address" in ln]
for ln in lines:
mac = extract_first_mac(ln)
if mac:
return mac
return None
def parse_infiniband_slot_macs_from_swinventory(swinventory: str, slots: List[int]) -> Dict[int, Optional[str]]:
"""
bash의 awk 트릭(해당 FQDD의 이전 줄에서 MAC 추출)을 그대로 재현:
- swinventory 라인을 순회하면서 직전 라인을 prev로 저장
- line이 'FQDD = InfiniBand.Slot.<N>-1' 에 매치되면 prev에서 MAC 추출
"""
want = {s: None for s in slots}
prev = ""
for line in swinventory.splitlines():
m = re.search(r"FQDD\s*=\s*InfiniBand\.Slot\.(\d+)-1", line)
if m:
slot = int(m.group(1))
if slot in want and want[slot] is None:
want[slot] = extract_first_mac(prev) # 이전 줄에서 MAC
prev = line
return want
def parse_memory_vendor_initials(hwinventory: str) -> str:
"""
원본: grep -A5 "DIMM" | grep "Manufacturer" | ... | sort | uniq | cut -c1
- 간단화: DIMM 근처 5줄 윈도우에서 Manufacturer 라인 모으기
- 제조사 첫 글자만 모아 중복 제거, 정렬 후 이어붙임
"""
lines = hwinventory.splitlines()
idxs = [i for i, ln in enumerate(lines) if "DIMM" in ln]
vendors: List[str] = []
for i in idxs:
window = lines[i : i + 6] # 본문 포함 6줄(= -A5)
for ln in window:
if "Manufacturer" in ln and "=" in ln:
v = ln.split("=", 1)[1].strip()
if v:
vendors.append(v)
initials = sorted({v[0] for v in vendors if v})
return "".join(initials)
def parse_ssd_manufacturers(hwinventory: str) -> str:
"""
원본: grep -A3 "Disk.Bay" | grep "Manufacturer" | uniq
- "Disk.Bay" 라인부터 3줄 윈도우 내 Manufacturer 라인 추출, 고유값 join
"""
lines = hwinventory.splitlines()
vendors: List[str] = []
for i, ln in enumerate(lines):
if "Disk.Bay" in ln:
window = lines[i : i + 4]
for w in window:
if "Manufacturer" in w and "=" in w:
v = w.split("=", 1)[1].strip()
if v:
vendors.append(v)
uniq = sorted({v for v in vendors})
return ", ".join(uniq)
# --- 단일 IP 처리 ---
def fetch_idrac_info_for_ip(ip: str) -> Tuple[str, bool, str]:
"""
returns: (ip, success, message)
success=True면 파일 저장 완료
"""
ip = ip.strip()
if not ip:
return ip, False, "빈 라인"
# racadm 호출
base = ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS]
rc1, getsysinfo = run(base + ["getsysinfo"])
rc2, swinventory = run(base + ["swinventory"])
rc3, hwinventory = run(base + ["hwinventory"])
if rc1 != 0 and rc2 != 0 and rc3 != 0:
return ip, False, f"모든 racadm 호출 실패:\n{getsysinfo or ''}\n{swinventory or ''}\n{hwinventory or ''}"
svc_tag = parse_svc_tag(getsysinfo) if getsysinfo else None
if not svc_tag:
return ip, False, "서비스 태그(SVC Tag) 추출 실패"
# 개별 필드 파싱
integrated_1 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Integrated.1-1-1") if getsysinfo else None
integrated_2 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Integrated.1-2-1") if getsysinfo else None
integrated_3 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Integrated.1-3-1") if getsysinfo else None
integrated_4 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Integrated.1-4-1") if getsysinfo else None
onboard_1 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Embedded.1-1-1") if getsysinfo else None
onboard_2 = parse_specific_mac_in_getsysinfo(getsysinfo, "NIC.Embedded.2-1-1") if getsysinfo else None
idrac_mac = parse_idrac_mac(getsysinfo) if getsysinfo else None
# InfiniBand.Slot.<N>-1 MAC (이전 라인에서 MAC 추출) — 출력 순서 유지
desired_slots = [38, 39, 37, 36, 32, 33, 34, 35, 31, 40]
slot_macs = parse_infiniband_slot_macs_from_swinventory(swinventory, desired_slots) if swinventory else {s: None for s in desired_slots}
memory_initials = parse_memory_vendor_initials(hwinventory) if hwinventory else ""
ssd_vendors = parse_ssd_manufacturers(hwinventory) if hwinventory else ""
# 저장
out_path = OUTPUT_DIR / f"{svc_tag}.txt"
with out_path.open("w", encoding="utf-8") as f:
# 원본 스크립트와 동일한 출력 순서
f.write(f"{svc_tag}\n")
f.write(f"{integrated_1 or ''}\n")
f.write(f"{integrated_2 or ''}\n")
f.write(f"{integrated_3 or ''}\n")
f.write(f"{integrated_4 or ''}\n")
f.write(f"{onboard_1 or ''}\n")
f.write(f"{onboard_2 or ''}\n")
# 슬롯 고정 순서
for sl in desired_slots:
f.write(f"{slot_macs.get(sl) or ''}\n")
f.write(f"{idrac_mac or ''}\n")
f.write(f"{memory_initials}\n")
f.write(f"{ssd_vendors}\n")
return ip, True, f"저장: {out_path}"
# --- 메인 ---
def main():
parser = argparse.ArgumentParser(description="iDRAC 정보 수집 (Python 포팅)")
parser.add_argument("ip_file", help="IP 주소 목록 파일 (줄 단위)")
parser.add_argument("--workers", type=int, default=20, help="병렬 스레드 수 (기본 20)")
args = parser.parse_args()
ip_file = Path(args.ip_file)
if not ip_file.exists():
logging.error(f"IP 파일이 존재하지 않습니다: {ip_file}")
sys.exit(1)
with ip_file.open("r", encoding="utf-8") as f:
ips = [ln.strip() for ln in f if ln.strip()]
if not ips:
logging.error("IP 목록이 비어 있습니다.")
sys.exit(1)
logging.info(f"[시작] 총 {len(ips)}대, workers={args.workers}, IDRAC_USER={IDRAC_USER}")
t0 = time.time()
ok = 0
fail = 0
# 병렬 수집
with futures.ThreadPoolExecutor(max_workers=args.workers) as ex:
futs = {ex.submit(fetch_idrac_info_for_ip, ip): ip for ip in ips}
for fut in futures.as_completed(futs):
ip = futs[fut]
try:
_ip, success, msg = fut.result()
prefix = "[OK] " if success else "[FAIL] "
if success:
logging.info(prefix + ip + " - " + msg)
else:
logging.error(prefix + ip + " - " + msg)
ok += int(success)
fail += int(not success)
except Exception as e:
logging.error(f"[EXC] {ip} - {e}")
fail += 1
dt = int(time.time() - t0)
h, r = divmod(dt, 3600)
m, s = divmod(r, 60)
logging.info("\n정보 수집 완료.")
logging.info(f"성공 {ok}대 / 실패 {fail}")
logging.info(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")
from unified.collect_mac import main as unified_main
if __name__ == "__main__":
main()
if len(sys.argv) < 2:
print("Usage: python XE9680_H200_IB_10EA_MAC_info.py <ip_file>")
sys.exit(1)
# xe9680_h200_10ea 프로파일 사용
sys.argv.append("xe9680_h200_10ea")
unified_main()
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+210
View File
@@ -0,0 +1,210 @@
import os
import re
import sys
import yaml
import argparse
import subprocess
import logging
import fnmatch
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
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")
# 경로 설정
BASE_DIR = Path(__file__).resolve().parent
PROFILES_FILE = BASE_DIR / "profiles" / "gpu_profiles.yaml"
OUTPUT_DIR = BASE_DIR.parent / "temp" / "staging"
def load_profile(profile_name="default"):
"""Load specific profile from gpu_profiles.yaml"""
if not PROFILES_FILE.exists():
logging.warning(f"Profile file not found at {PROFILES_FILE}. Using defaults.")
return {}
try:
with open(PROFILES_FILE, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
profiles = data.get('profiles', {})
return profiles.get(profile_name, {})
except Exception as e:
logging.error(f"Error loading profile: {e}")
return {}
def resolve_output_dir() -> Path:
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
return OUTPUT_DIR
def parse_gpu_serials_from_hwinventory(hwinv_text: str, target_slots=None) -> dict:
"""
Parse GPU serials and filter based on target_slots patterns.
target_slots: list of glob patterns (e.g. ["Video.Slot.*"])
"""
results = {}
blocks = re.split(r"\n\s*\n", hwinv_text, flags=re.MULTILINE)
for block in blocks:
# Check if it looks like a Video/GPU block
if not re.search(r"\[?InstanceID:\s*Video\.Slot\.", block):
continue
# Extract FQDD
fqdd = None
m_fqdd = re.search(r"^FQDD\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
if m_fqdd:
fqdd = m_fqdd.group(1).strip()
else:
m_hdr = re.search(r"\[InstanceID:\s*(Video\.Slot\.[^\]\r\n]+)\]", block)
if m_hdr:
fqdd = m_hdr.group(1).strip()
if not fqdd:
continue
# Filtering logic
if target_slots:
matched = False
for pattern in target_slots:
# fnmatch supports simple unix shell style wildcards (*, ?, [], etc)
if fnmatch.fnmatch(fqdd, pattern):
matched = True
break
if not matched:
continue
# Extract SerialNumber
m_sn = re.search(r"^SerialNumber\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
serial = m_sn.group(1).strip() if m_sn else "Not Found"
results[fqdd] = serial
return results
def get_slot_index(fqdd: str):
"""
Extract logic for sorting.
Format usually: Video.Slot.X-Y -> returns (X, Y) tuple
"""
m = re.search(r"Video\.Slot\.(\d+)-(\d+)", fqdd)
if m:
return (int(m.group(1)), int(m.group(2)))
m_simple = re.search(r"Video\.Slot\.(\d+)", fqdd)
if m_simple:
return (int(m_simple.group(1)), 0)
return (float('inf'), float('inf'))
def fetch_idrac_info(idrac_ip: str, output_dir: Path, gpu_settings: dict) -> None:
try:
# 1. Get SVC Tag
cmd_getsysinfo = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "getsysinfo"
]
# execute without shell=True for security (if possible), but subprocess.getoutput uses shell
# Here we use subprocess.run for better control if needed, but sticking to logic similar to original for now
# Ideally: result = subprocess.run(cmd_getsysinfo, capture_output=True, text=True)
# But let's stick to getoutput wrapper logic or use run compatible way
# Using subprocess.run is safer and modern
res_sys = subprocess.run(cmd_getsysinfo, capture_output=True, text=True, encoding='utf-8', errors='ignore')
getsysinfo = res_sys.stdout
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
# 2. Get Hardware Inventory
cmd_hwinv = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "hwinventory"
]
res_inv = subprocess.run(cmd_hwinv, capture_output=True, text=True, encoding='utf-8', errors='ignore')
hwinv_text = res_inv.stdout
# 3. Parse and Filter
target_slots = gpu_settings.get('target_slots', ["Video.Slot.*"])
gpu_map = parse_gpu_serials_from_hwinventory(hwinv_text, target_slots)
# 4. Sort
sort_order = gpu_settings.get('sort_order', 'slot_index')
if sort_order == 'slot_index':
sorted_keys = sorted(gpu_map.keys(), key=lambda k: (get_slot_index(k), k))
elif sort_order == 'serial':
sorted_keys = sorted(gpu_map.keys(), key=lambda k: gpu_map[k])
else:
sorted_keys = sorted(gpu_map.keys())
# 5. Write Output (Text format)
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")
if not gpu_map:
f.write("No matching GPU inventory found.\n")
return
for fqdd in sorted_keys:
serial = gpu_map[fqdd]
f.write(f"{fqdd}: {serial}\n")
# Serials only line
serials_only = [gpu_map[k] for k in sorted_keys if gpu_map[k] != "Not Found"]
if serials_only:
f.write(f"SERIALS: {';'.join(serials_only)}\n")
logging.info(f"Saved {output_file}")
except Exception as e:
logging.error(f"Error processing IP {idrac_ip}: {e}")
def main():
parser = argparse.ArgumentParser(description="Collect GPU Serial Info")
parser.add_argument("ip_file", help="File containing list of IP addresses")
parser.add_argument("--profile", default="default", help="Profile name from gpu_profiles.yaml")
args = parser.parse_args()
ip_path = Path(args.ip_file)
if not ip_path.is_file():
logging.error(f"IP file {args.ip_file} does not exist.")
return
# Load Profile
profile = load_profile(args.profile)
logging.info(f"Loaded profile: {args.profile}")
gpu_settings = profile.get('gpu_settings', {})
output_dir = resolve_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=50) as executor:
future_to_ip = {
executor.submit(fetch_idrac_info, ip, output_dir, gpu_settings): ip
for ip in ip_addresses
}
for future in as_completed(future_to_ip):
ip = future_to_ip[future]
try:
future.result()
except Exception as e:
logging.error(f"Error on {ip}: {e}")
if __name__ == "__main__":
main()
+8 -1
View File
@@ -23,7 +23,14 @@ logging.basicConfig(
IDRAC_USER = os.getenv("IDRAC_USER", "root")
IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin")
OUTPUT_DIR = Path("idrac_info")
# Resolve output dir relative to script location or CWD
BASE_DIR = Path(__file__).resolve().parent
if BASE_DIR.name == "scripts":
DATA_DIR = BASE_DIR.parent
else:
DATA_DIR = Path("data").resolve()
OUTPUT_DIR = DATA_DIR / "temp" / "staging"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
MAC_RE = re.compile(r"([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
+4
View File
@@ -0,0 +1,4 @@
"""
공통 라이브러리 패키지
"""
__version__ = "1.0.0"
+129
View File
@@ -0,0 +1,129 @@
"""
iDRAC 클라이언트 - racadm 명령 실행 및 결과 캐싱
"""
from typing import Optional, Dict, Tuple
import subprocess
import os
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
class IDRACClient:
"""
racadm 명령 실행 및 결과 캐싱을 담당하는 클라이언트
"""
def __init__(self, ip: str, user: str = None, password: str = None):
"""
Args:
ip: iDRAC IP 주소
user: iDRAC 사용자명 (기본값: 환경변수 IDRAC_USER 또는 'root')
password: iDRAC 비밀번호 (기본값: 환경변수 IDRAC_PASS 또는 'calvin')
"""
self.ip = ip
self.user = user or os.getenv("IDRAC_USER", "root")
self.password = password or os.getenv("IDRAC_PASS", "calvin")
self._cache: Dict[str, str] = {} # 중복 호출 방지 캐시
def run_command(self, command: str) -> Tuple[int, str]:
"""
racadm 명령 실행
Args:
command: 실행할 racadm 명령 (예: "getsysinfo", "get bios.BiosBootSettings")
Returns:
(return_code, output) 튜플
"""
cmd = f"racadm -r {self.ip} -u {self.user} -p {self.password} {command}"
try:
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
check=False,
timeout=30 # 30초 타임아웃
)
return result.returncode, result.stdout
except subprocess.TimeoutExpired:
logging.error(f"[{self.ip}] 명령 타임아웃: {command}")
return 1, ""
except Exception as e:
logging.error(f"[{self.ip}] 명령 실행 오류: {e}")
return 1, f"Error: {e}"
def get_sysinfo(self) -> str:
"""
getsysinfo 명령 실행 (캐싱)
Returns:
getsysinfo 출력 결과
"""
if "sysinfo" not in self._cache:
_, output = self.run_command("getsysinfo")
self._cache["sysinfo"] = output
return self._cache["sysinfo"]
def get_hwinventory(self) -> str:
"""
hwinventory 명령 실행 (캐싱)
Returns:
hwinventory 출력 결과
"""
if "hwinventory" not in self._cache:
_, output = self.run_command("hwinventory")
self._cache["hwinventory"] = output
return self._cache["hwinventory"]
def get_swinventory(self) -> str:
"""
swinventory 명령 실행 (캐싱)
Returns:
swinventory 출력 결과
"""
if "swinventory" not in self._cache:
_, output = self.run_command("swinventory")
self._cache["swinventory"] = output
return self._cache["swinventory"]
def get_infiniband_pages(self) -> str:
"""
InfiniBand 설정 페이지 목록 조회 (InfiniBand.VndrConfigPage)
Returns:
racadm get InfiniBand.VndrConfigPage 출력
"""
if "infiniband_pages" not in self._cache:
_, output = self.run_command("get InfiniBand.VndrConfigPage")
self._cache["infiniband_pages"] = output
return self._cache["infiniband_pages"]
def get_infiniband_page_detail(self, page_number: str) -> str:
"""
특정 InfiniBand 설정 페이지 상세 조회
Args:
page_number: 페이지 번호 (예: "1")
Returns:
racadm get InfiniBand.VndrConfigPage.{page_number} 출력
"""
cache_key = f"infiniband_page_{page_number}"
if cache_key not in self._cache:
_, output = self.run_command(f"get InfiniBand.VndrConfigPage.{page_number}")
self._cache[cache_key] = output
return self._cache[cache_key]
def clear_cache(self):
"""캐시 초기화"""
self._cache.clear()
+291
View File
@@ -0,0 +1,291 @@
"""
racadm 출력 파싱 유틸리티
"""
import re
from typing import List, Optional, Dict, Tuple
class InfoParser:
"""racadm 명령 출력 파싱 유틸리티"""
# Word boundary to ensure we don't match partial GUIDs (which are longer)
MAC_PATTERN = re.compile(r"\b([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}\b")
@staticmethod
def extract_service_tag(sysinfo: str) -> Optional[str]:
"""
서비스 태그 추출
Args:
sysinfo: getsysinfo 명령 출력
Returns:
서비스 태그 또는 None
"""
m = re.search(r"SVC\s*Tag\s*=\s*(\S+)", sysinfo, re.IGNORECASE)
return m.group(1).strip() if m else None
@staticmethod
def extract_nic_macs(sysinfo: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
"""
NIC MAC 주소 추출
Args:
sysinfo: getsysinfo 명령 출력
nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Embedded.1-1-1"])
Returns:
{pattern: mac_address} 딕셔너리
"""
results = {}
for pattern in nic_patterns:
for line in sysinfo.splitlines():
if pattern in line:
mac = InfoParser.MAC_PATTERN.search(line)
results[pattern] = mac.group(0) if mac else None
break
else:
results[pattern] = None
return results
@staticmethod
def extract_nic_macs_from_hwinventory(hwinventory: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
"""
hwinventory에서 NIC MAC 주소 추출
Args:
hwinventory: hwinventory 명령 출력
nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Slot.1-1-1"])
Returns:
{pattern: mac_address} 딕셔너리
"""
results = {pattern: None for pattern in nic_patterns}
# hwinventory를 블록 단위로 분리 (빈 줄로 구분)
blocks = re.split(r"\n\s*\n", hwinventory)
for block in blocks:
# 블록 내에서 FQDD 또는 InstanceID 확인
fqdd = None
# FQDD = NIC.Integrated.1-1-1 또는 [InstanceID: NIC.Integrated.1-1-1] 형식 찾기
m_fqdd = re.search(r"FQDD\s*=\s*(\S+)", block, re.IGNORECASE)
if m_fqdd:
fqdd = m_fqdd.group(1).strip()
# FQDD 키워드가 없을 경우를 대비해 InstanceID 헤더도 확인
elif "[InstanceID:" in block:
m_inst = re.search(r"InstanceID:\s*([^\]]+)", block)
if m_inst:
fqdd = m_inst.group(1).strip()
if not fqdd:
continue
# 요청된 패턴 중 하나라도 포함하는지 확인 (정확한 매칭을 위해 수정 가능하나, 보통 포함 관계면 충분)
# 여기서는 패턴이 FQDD와 정확히 일치하거나 FQDD가 패턴을 포함하는 경우를 찾음
matched_pattern = None
for pattern in nic_patterns:
# 대소문자 무시 비교
if pattern.lower() == fqdd.lower():
matched_pattern = pattern
break
# 정확히 일치하는 패턴을 못 찾았다면 다음 블록으로
if not matched_pattern:
continue
# MAC 주소 찾기
# Ethernet Address = AA:BB:CC:DD:EE:FF
# CurrentMACAddress = AA:BB:CC:DD:EE:FF
# MAC Address = AA:BB:CC:DD:EE:FF
mac_match = InfoParser.MAC_PATTERN.search(block)
if mac_match:
results[matched_pattern] = mac_match.group(0)
return results
@staticmethod
def extract_idrac_mac(sysinfo: str) -> Optional[str]:
"""
iDRAC MAC 주소 추출
Args:
sysinfo: getsysinfo 명령 출력
Returns:
iDRAC MAC 주소 또는 None
"""
for line in sysinfo.splitlines():
if "MAC Address" in line:
mac = InfoParser.MAC_PATTERN.search(line)
if mac:
return mac.group(0)
return None
@staticmethod
def extract_vendors(hwinventory: str, component_prefix: str) -> List[str]:
"""
컴포넌트 벤더 추출
Args:
hwinventory: hwinventory 명령 출력
component_prefix: 컴포넌트 접두사 ("DIMM", "Disk.Bay", "BOSS" 등)
Returns:
벤더 리스트 (중복 제거, 정렬됨)
"""
vendors = set()
blocks = re.split(r"\n\s*\n", hwinventory)
for block in blocks:
# Check if this block is for the target component using InstanceID or FQDD
is_target_component = False
# Check InstanceID header: [InstanceID: DIMM.Socket.A1]
if f"[InstanceID: {component_prefix}" in block:
is_target_component = True
# Also check FQDD line if InstanceID header check fails (fallback)
elif not is_target_component:
m_fqdd = re.search(r"^FQDD\s*=\s*(\S+)", block, re.MULTILINE)
if m_fqdd and component_prefix in m_fqdd.group(1):
is_target_component = True
if is_target_component:
m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE)
if m:
vendor = m.group(1).strip()
# Filter out "Dell Inc." if user considers it wrong/noise for these components?
# The user specifically complained about "Dell Inc." being WRONG.
# However, if the DIMM *is* Dell, we should report it.
# But the likely cause of "Dell Inc." appearing when it shouldn't is the System block match.
# With this stricter check, the System block (InstanceID: System.Embedded.1) will NOT match "DIMM".
vendors.add(vendor)
return sorted(vendors)
@staticmethod
def extract_infiniband_page_map(pages_output: str) -> List[Tuple[str, str]]:
"""
InfiniBand 페이지 번호와 슬롯 번호 매핑 추출
Args:
pages_output: get InfiniBand.VndrConfigPage 출력
Returns:
[(page_number, slot_number), ...] 리스트
"""
# InfiniBand.VndrConfigPage.1 [Key=InfiniBand.Slot.38-1#VndrConfigPage]
matches = re.findall(
r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
pages_output
)
return matches
@staticmethod
def extract_port_guid(page_detail: str) -> Optional[str]:
"""
InfiniBand 페이지 상세에서 PortGUID 추출
Args:
page_detail: get InfiniBand.VndrConfigPage.{N} 출력
Returns:
PortGUID 값 또는 None
"""
m = re.search(r"PortGUID=(\S+)", page_detail)
return m.group(1) if m else None
@staticmethod
def extract_value_by_pattern(text: str, pattern: str) -> Optional[str]:
"""
패턴으로 값 추출 (= 뒤의 값)
Args:
text: 검색할 텍스트
pattern: 검색 패턴
Returns:
추출된 값 또는 None
"""
for line in text.splitlines():
if pattern.lower() in line.lower():
if '=' in line:
return line.split('=', 1)[1].strip()
return None
@staticmethod
def extract_infiniband_macs_from_swinventory(swinventory: str, target_slots: List[int]) -> Dict[int, str]:
"""
swinventory에서 InfiniBand 슬롯의 MAC 주소 추출
(FQDD 라인 바로 윗줄에 MAC 주소가 있다는 가정 - 레거시 스크립트 로직)
Args:
swinventory: swinventory 명령 출력
target_slots: 추출할 슬롯 번호 리스트
Returns:
{slot: mac_address} 딕셔너리
"""
results = {}
prev_line = ""
# 슬롯별 패턴 미리 생성 (예: "FQDD = InfiniBand.Slot.31-1")
slot_patterns = {slot: f"FQDD = InfiniBand.Slot.{slot}-1" for slot in target_slots}
for line in swinventory.splitlines():
for slot, pattern in slot_patterns.items():
if pattern in line:
# 이전 라인에서 MAC 찾기
mac = InfoParser.MAC_PATTERN.search(prev_line)
if mac:
results[slot] = mac.group(0)
prev_line = line
return results
@staticmethod
def extract_gpu_serials(hwinventory: str) -> Dict[str, str]:
"""
iDRAC hwinventory 전체 텍스트에서 GPU(Video.Slot.*) 블록을 찾아
{FQDD(or InstanceID): SerialNumber} 딕셔너리로 반환.
Args:
hwinventory: hwinventory 명령 출력
Returns:
{fqdd: serial} 딕셔너리
"""
results = {}
# 빈 줄 기준 블록 분할(여러 개의 개행을 하나의 경계로)
blocks = re.split(r"\n\s*\n", hwinventory, flags=re.MULTILINE)
for block in blocks:
# GPU(Video) 블록만 처리
# Video.Slot.X 또는 유사 패턴
if not re.search(r"\[?InstanceID:\s*Video\.Slot\.", block):
continue
# FQDD 우선, 없으면 [InstanceID: ...]에서 추출
fqdd = None
m_fqdd = re.search(r"^FQDD\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
if m_fqdd:
fqdd = m_fqdd.group(1).strip()
else:
m_hdr = re.search(r"\[InstanceID:\s*(Video\.Slot\.[^\]\r\n]+)\]", block)
if m_hdr:
fqdd = m_hdr.group(1).strip()
if not fqdd:
continue
# SerialNumber 추출
m_sn = re.search(r"^SerialNumber\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
serial = m_sn.group(1).strip() if m_sn else "Not Found"
results[fqdd] = serial
return results
+22
View File
@@ -0,0 +1,22 @@
# GPU 서버 정보 수집 프로파일 (GUID + GPU Serial)
profiles:
default:
description: "기본 GPU 시리얼 수집 설정"
# GUID 관련 설정 (제거됨)
# slot_priority: [38, 39, 37, 36, 32, 33, 34, 35, 31, 40]
# GPU Serial 관련 설정
gpu_settings:
target_slots: ["Video.Slot.*"]
sort_order: "slot_index" # slot_index, serial, etc.
output_format: "combined"
h100_cluster:
description: "H100 클러스터용 설정"
slot_priority: [1, 2, 3, 4]
gpu_settings:
target_slots: ["Video.Slot.0-1", "Video.Slot.1-1"]
sort_order: "slot_index"
output_format: "combined"
+11
View File
@@ -0,0 +1,11 @@
# GUID 수집 프로파일 정의
profiles:
default:
description: "기본 GUID 수집"
slot_priority: [38, 39, 37, 36, 32, 33, 34, 35, 31, 40]
output_format: "combined" # combined 또는 individual
custom_priority:
description: "사용자 정의 슬롯 우선순위"
slot_priority: [] # 런타임에 환경변수 GUID_SLOT_PRIORITY에서 읽음
output_format: "combined"
+107
View File
@@ -0,0 +1,107 @@
profiles:
16G_R760XD2:
description: 기본 MAC 수집 (Integrated + Embedded)
exclude_boss: true
include_disk_vendor: true
include_idrac_mac: true
include_memory_vendor: true
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Slot.3-1-1
- NIC.Slot.3-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
16G_T8/T8A:
description: 새 프로파일
extends: default
16G_TYPE4,6:
description: 기본 MAC 수집 (Integrated + Embedded)
exclude_boss: true
include_disk_vendor: true
include_idrac_mac: true
include_memory_vendor: true
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
16G_TYPE8,8A:
description: 기본 MAC 수집 (Integrated + Embedded)
exclude_boss: true
include_disk_vendor: true
include_idrac_mac: true
include_memory_vendor: true
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
TY11_60ea:
description: 기본 MAC 수집 (Integrated + Embedded)
exclude_boss: true
include_disk_vendor: true
include_idrac_mac: true
include_memory_vendor: true
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Slot.1-1-1
- NIC.Slot.1-2-1
- NIC.Slot.2-1-1
- NIC.Slot.2-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
default:
description: 기본 MAC 수집 (Integrated + Embedded)
exclude_boss: true
include_disk_vendor: true
include_idrac_mac: true
include_memory_vendor: true
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
jp_54ea:
description: JP 54EA 서버
extends: default
jp_type11a:
description: JP_TYPE11A
extends: default
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
- NIC.Slot.1-1-1
- NIC.Slot.1-2-1
- NIC.Slot.2-1-1
- NIC.Slot.2-2-1
sp_18ea:
description: SP 18EA 서버
extends: default
type11:
description: TYPE11 서버
extends: default
xe9680_h200_10ea:
description: XE9680 H200 10EA InfiniBand
extends: default
infiniband_slots:
- 38
- 39
- 37
- 36
- 32
- 33
- 34
- 35
- 31
- 40
nic_patterns:
- NIC.Integrated.1-1-1
- NIC.Integrated.1-2-1
- NIC.Integrated.1-3-1
- NIC.Integrated.1-4-1
- NIC.Embedded.1-1-1
- NIC.Embedded.2-1-1
@@ -0,0 +1,676 @@
profiles:
JYH_AMD:
bios_settings:
- command: get bios.BiosBootSettings
name: BIOS Boot Mode
pattern: BootMode
- command: get bios.SysProfileSettings
name: System Profile
pattern: SysProfile=
- command: get bios.SysProfileSettings
name: Memory Frequency
pattern: MemFrequency
- command: get bios.SysProfileSettings
name: Turbo Boost
pattern: ProcTurboMode
- command: get bios.SysProfileSettings
name: C-States
pattern: ProcCStates
- command: get bios.ProcSettings
name: Logical Processor
pattern: LogicalProc
- command: get bios.ProcSettings
name: Virtualization Technology
pattern: ProcVirtualization
- command: get bios.MemSettings
name: DramRefreshDelay
pattern: DramRefreshDelay
- command: get bios.ProcSettings
name: x2APIC Mode
pattern: ProcX2Apic
- command: get bios.MemSettings
name: DIMM Self Healing
pattern: PPROnUCE
- command: get bios.MemSettings
name: Correctable Error Logging
pattern: CECriticalSEL
- command: get System.ThermalSettings
name: Thermal Profile Optimization
pattern: ThermalProfile
- command: get Bios.IntegratedDevices
name: SR-IOV Global Enable
pattern: SriovGlobalEnable
- command: get bios.MiscSettings
name: F1/F2 Prompt on Error
pattern: ErrPrompt
- command: get iDRAC.ASRConfig
name: ASR Config
pattern: Enable=
- command: get Bios.IntegratedDevices.OsWatchdogTimer
name: Os watchdof timer
pattern: OswatchdogTimer=
description: 기본 서버 정보 수집 (BIOS, iDRAC, RAID)
extends: minimal
firmware:
- command: get NIC.FrmwImgMenu.1
name: NIC(XE810) Integrated Firmware
pattern: '#FamilyVersion'
- command: get NIC.FrmwImgMenu.3
name: NIC(BCM5720) Integrated Firmware
pattern: '#FamilyVersion'
- command: hwinventory
name: RAID Controller Firmware
pattern: ControllerFirmwareVersion
idrac_settings:
- command: get iDRAC.Time.Timezone
name: Timezone
pattern: Timezone
- command: get iDRAC.CurrentNIC
name: IPMI LAN Selection
pattern: ActiveNIC
- command: get iDRAC.CurrentIPv4
name: IPMI IPv4 DHCP
pattern: DHCPEnable
- command: get iDRAC.CurrentIPv6
name: IPMI IPv6 Enable
pattern: Enable=
- command: get iDRAC.Redfish.Enable
name: Redfish Support
pattern: Enable=
- command: get iDRAC.SSH
name: SSH Support
pattern: Enable=
- command: get iDRAC.USERDomain.1.Name
name: AD User Domain Name
pattern: Name
- command: get iDRAC.ActiveDirectory.DomainController1
name: SC Server Address
pattern: DomainController1
- command: get iDRAC.SysLog.SysLogEnable
name: Remote Log (syslog)
pattern: SysLogEnable
- command: get iDRAC.SysLog.Server1
name: Syslog Server 1
pattern: Server1
- command: get iDRAC.SysLog.Server2
name: Syslog Server 2
pattern: Server2
- command: get iDRAC.SysLog.Port
name: Syslog Port
pattern: Port
- command: get iDRAC.VirtualConsole.Port
name: VirtualConsole Port
pattern: Port
raid_settings:
- command: hwinventory
name: BOSS ProductName
pattern: ProductName = BOSS
- command: hwinventory
name: RAID Types
pattern: RAIDTypes
- command: hwinventory
name: StripeSize
pattern: StripeSize
- command: hwinventory
name: ReadCachePolicy
pattern: ReadCachePolicy
- command: hwinventory
name: WriteCachePolicy
pattern: WriteCachePolicy
- command: get STORAGE.Controller.1
name: CheckConsistencyMode
pattern: CheckConsistencyMode
R760XD2_Server_info:
bios_settings:
- command: get bios.BiosBootSettings
name: BIOS Boot Mode
pattern: BootMode
- command: get bios.SysProfileSettings
name: System Profile
pattern: SysProfile=
- command: get bios.SysProfileSettings
name: CPU Power Management
pattern: EnergyPerformanceBias
- command: get bios.SysProfileSettings
name: Memory Frequency
pattern: MemFrequency
- command: get bios.SysProfileSettings
name: Turbo Boost
pattern: ProcTurboMode
- command: get bios.SysProfileSettings
name: C1E
pattern: ProcC1E
- command: get bios.SysProfileSettings
name: C-States
pattern: ProcCStates
- command: get bios.SysProfileSettings
name: Monitor/Mwait
pattern: MonitorMwait
- command: get bios.ProcSettings
name: Logical Processor
pattern: LogicalProc
- command: get bios.ProcSettings
name: Virtualization Technology
pattern: ProcVirtualization
- command: get bios.ProcSettings
name: LLC Prefetch
pattern: LlcPrefetch
- command: get bios.ProcSettings
name: x2APIC Mode
pattern: ProcX2Apic
- command: get bios.MemSettings
name: Node Interleaving
pattern: NodeInterleave
- command: get bios.MemSettings
name: DIMM Self Healing
pattern: PPROnUCE
- command: get bios.MemSettings
name: Correctable Error Logging
pattern: CECriticalSEL
- command: get System.ThermalSettings
name: Thermal Profile Optimization
pattern: ThermalProfile
- command: get Bios.IntegratedDevices
name: SR-IOV Global Enable
pattern: SriovGlobalEnable
- command: get bios.MiscSettings
name: F1/F2 Prompt on Error
pattern: ErrPrompt
- command: get iDRAC.ASRConfig
name: ASR Config
pattern: Enable=
- command: get Bios.IntegratedDevices.OsWatchdogTimer
name: Os watchdof timer
pattern: OswatchdogTimer=
description: 기본 서버 정보 수집 (BIOS, iDRAC, RAID)
extends: minimal
firmware:
- command: get NIC.FrmwImgMenu.1
name: NIC Integrated Firmware
pattern: '#FamilyVersion'
- command: hwinventory
name: RAID Controller Firmware
pattern: ControllerFirmwareVersion
- command: hwinventory
name: BOSS Controller Firmware
pattern: ControllerFirmwareVersion = 2
idrac_settings:
- command: get iDRAC.Time.Timezone
name: Timezone
pattern: Timezone
- command: get iDRAC.CurrentNIC
name: IPMI LAN Selection
pattern: ActiveNIC
- command: get iDRAC.CurrentIPv4
name: IPMI IPv4 DHCP
pattern: DHCPEnable
- command: get iDRAC.CurrentIPv6
name: IPMI IPv6 Enable
pattern: Enable=
- command: get iDRAC.Redfish.Enable
name: Redfish Support
pattern: Enable=
- command: get iDRAC.SSH
name: SSH Support
pattern: Enable=
- command: get iDRAC.USERDomain.1.Name
name: AD User Domain Name
pattern: Name
- command: get iDRAC.ActiveDirectory.DomainController1
name: SC Server Address
pattern: DomainController1
- command: get iDRAC.SysLog.SysLogEnable
name: Remote Log (syslog)
pattern: SysLogEnable
- command: get iDRAC.SysLog.Server1
name: Syslog Server 1
pattern: Server1
- command: get iDRAC.SysLog.Server2
name: Syslog Server 2
pattern: Server2
- command: get iDRAC.SysLog.Port
name: Syslog Port
pattern: Port
- command: get iDRAC.VirtualConsole.Port
name: VirtualConsole Port
pattern: Port
raid_settings:
- command: hwinventory
name: RAID ProductName
pattern: ProductName = PERC
- command: hwinventory
name: BOSS ProductName
pattern: ProductName = BOSS
- command: hwinventory
name: RAID Types
pattern: RAIDTypes
- command: hwinventory
name: StripeSize
pattern: StripeSize
- command: hwinventory
name: ReadCachePolicy
pattern: ReadCachePolicy
- command: hwinventory
name: WriteCachePolicy
pattern: WriteCachePolicy
- command: get STORAGE.Controller.1
name: CheckConsistencyMode
pattern: CheckConsistencyMode
- command: get STORAGE.Controller.1
name: PatrolReadRate
pattern: PatrolReadRate
amd_server:
bios_settings:
- command: get bios.ProcSettings
name: AMD SMT Mode
pattern: SmtMode
- command: get bios.ProcSettings
name: AMD IOMMU
pattern: Iommu
description: AMD 서버 전용 프로파일
extends: default
default:
bios_settings:
- command: get bios.BiosBootSettings
name: BIOS Boot Mode
pattern: BootMode
- command: get bios.SysProfileSettings
name: System Profile
pattern: SysProfile=
- command: get bios.SysProfileSettings
name: CPU Power Management
pattern: EnergyPerformanceBias
- command: get bios.SysProfileSettings
name: Memory Frequency
pattern: MemFrequency
- command: get bios.SysProfileSettings
name: Turbo Boost
pattern: ProcTurboMode
- command: get bios.SysProfileSettings
name: C1E
pattern: ProcC1E
- command: get bios.SysProfileSettings
name: C-States
pattern: ProcCStates
- command: get bios.SysProfileSettings
name: Monitor/Mwait
pattern: MonitorMwait
- command: get bios.ProcSettings
name: Logical Processor
pattern: LogicalProc
- command: get bios.ProcSettings
name: Virtualization Technology
pattern: ProcVirtualization
- command: get bios.ProcSettings
name: LLC Prefetch
pattern: LlcPrefetch
- command: get bios.ProcSettings
name: x2APIC Mode
pattern: ProcX2Apic
- command: get bios.MemSettings
name: Node Interleaving
pattern: NodeInterleave
- command: get bios.MemSettings
name: DIMM Self Healing
pattern: PPROnUCE
- command: get bios.MemSettings
name: Correctable Error Logging
pattern: CECriticalSEL
- command: get System.ThermalSettings
name: Thermal Profile Optimization
pattern: ThermalProfile
- command: get Bios.IntegratedDevices
name: SR-IOV Global Enable
pattern: SriovGlobalEnable
- command: get bios.MiscSettings
name: F1/F2 Prompt on Error
pattern: ErrPrompt
description: 기본 서버 정보 수집 (BIOS, iDRAC, RAID)
extends: minimal
firmware:
- command: get NIC.FrmwImgMenu.1
name: NIC Integrated Firmware
pattern: '#FamilyVersion'
- command: get NIC.FrmwImgMenu.5
name: OnBoard NIC Firmware
pattern: '#FamilyVersion'
- command: hwinventory
name: RAID Controller Firmware
pattern: ControllerFirmwareVersion
idrac_settings:
- command: get iDRAC.Time.Timezone
name: Timezone
pattern: Timezone
- command: get iDRAC.CurrentNIC
name: IPMI LAN Selection
pattern: ActiveNIC
- command: get iDRAC.CurrentIPv4
name: IPMI IPv4 DHCP
pattern: DHCPEnable
- command: get iDRAC.CurrentIPv6
name: IPMI IPv6 Enable
pattern: Enable=
- command: get iDRAC.Redfish.Enable
name: Redfish Support
pattern: Enable=
- command: get iDRAC.SSH
name: SSH Support
pattern: Enable=
- command: get iDRAC.USERDomain.1.Name
name: AD User Domain Name
pattern: Name
- command: get iDRAC.ActiveDirectory.DomainController1
name: SC Server Address
pattern: DomainController1
- command: get iDRAC.SysLog.SysLogEnable
name: Remote Log (syslog)
pattern: SysLogEnable
- command: get iDRAC.SysLog.Server1
name: Syslog Server 1
pattern: Server1
- command: get iDRAC.SysLog.Server2
name: Syslog Server 2
pattern: Server2
- command: get iDRAC.SysLog.Port
name: Syslog Port
pattern: Port
- command: get iDRAC.VirtualConsole.Port
name: VirtualConsole Port
pattern: Port
raid_settings:
- command: hwinventory
name: RAID ProductName
pattern: ProductName = PERC
- command: hwinventory
name: RAID Types
pattern: RAIDTypes
- command: hwinventory
name: StripeSize
pattern: StripeSize
- command: hwinventory
name: ReadCachePolicy
pattern: ReadCachePolicy
- command: hwinventory
name: WriteCachePolicy
pattern: WriteCachePolicy
- command: get STORAGE.Controller.1
name: CheckConsistencyMode
pattern: CheckConsistencyMode
- command: get STORAGE.Controller.1
name: PatrolReadRate
pattern: PatrolReadRate
intel_server:
description: Intel 서버 전용 프로파일
extends: default
minimal:
description: 최소 정보만 수집 (펌웨어 버전만)
firmware:
- command: getsysinfo
name: SVC Tag
pattern: SVC Tag
- command: getsysinfo
name: BIOS Firmware
pattern: System BIOS Version
- command: getsysinfo
name: iDRAC Firmware
pattern: Firmware Version
문병철매니저님_AMD_T8A:
bios_settings:
- command: get bios.BiosBootSettings
name: BIOS Boot Mode
pattern: BootMode
- command: get bios.SysProfileSettings
name: System Profile
pattern: SysProfile=
- command: get bios.SysProfileSettings
name: Memory Frequency
pattern: MemFrequency
- command: get bios.SysProfileSettings
name: Turbo Boost
pattern: ProcTurboMode
- command: get bios.SysProfileSettings
name: C-States
pattern: ProcCStates
- command: get bios.ProcSettings
name: Logical Processor
pattern: LogicalProc
- command: get bios.ProcSettings
name: Virtualization Technology
pattern: ProcVirtualization
- command: get bios.MemSettings
name: DramRefreshDelay
pattern: DramRefreshDelay
- command: get bios.ProcSettings
name: x2APIC Mode
pattern: ProcX2Apic
- command: get bios.MemSettings
name: DIMM Self Healing
pattern: PPROnUCE
- command: get bios.MemSettings
name: Correctable Error Logging
pattern: CECriticalSEL
- command: get System.ThermalSettings
name: Thermal Profile Optimization
pattern: ThermalProfile
- command: get Bios.IntegratedDevices
name: SR-IOV Global Enable
pattern: SriovGlobalEnable
- command: get bios.MiscSettings
name: F1/F2 Prompt on Error
pattern: ErrPrompt
- command: get iDRAC.ASRConfig
name: ASR Config
pattern: Enable=
- command: get Bios.IntegratedDevices.OsWatchdogTimer
name: Os watchdof timer
pattern: OswatchdogTimer=
description: 기본 서버 정보 수집 (BIOS, iDRAC, RAID)
extends: minimal
firmware:
- command: get NIC.FrmwImgMenu.1
name: NIC(XE810) Integrated Firmware
pattern: '#FamilyVersion'
- command: get NIC.FrmwImgMenu.3
name: NIC(BCM5720) Integrated Firmware
pattern: '#FamilyVersion'
- command: hwinventory
name: RAID Controller Firmware
pattern: ControllerFirmwareVersion
idrac_settings:
- command: get iDRAC.Time.Timezone
name: Timezone
pattern: Timezone
- command: get iDRAC.CurrentNIC
name: IPMI LAN Selection
pattern: ActiveNIC
- command: get iDRAC.CurrentIPv4
name: IPMI IPv4 DHCP
pattern: DHCPEnable
- command: get iDRAC.CurrentIPv6
name: IPMI IPv6 Enable
pattern: Enable=
- command: get iDRAC.Redfish.Enable
name: Redfish Support
pattern: Enable=
- command: get iDRAC.SSH
name: SSH Support
pattern: Enable=
- command: get iDRAC.USERDomain.1.Name
name: AD User Domain Name
pattern: Name
- command: get iDRAC.ActiveDirectory.DomainController1
name: SC Server Address
pattern: DomainController1
- command: get iDRAC.SysLog.SysLogEnable
name: Remote Log (syslog)
pattern: SysLogEnable
- command: get iDRAC.SysLog.Server1
name: Syslog Server 1
pattern: Server1
- command: get iDRAC.SysLog.Server2
name: Syslog Server 2
pattern: Server2
- command: get iDRAC.SysLog.Port
name: Syslog Port
pattern: Port
- command: get iDRAC.VirtualConsole.Port
name: VirtualConsole Port
pattern: Port
raid_settings:
- command: hwinventory
name: BOSS ProductName
pattern: ProductName = BOSS
- command: hwinventory
name: RAID Types
pattern: RAIDTypes
- command: hwinventory
name: StripeSize
pattern: StripeSize
- command: hwinventory
name: ReadCachePolicy
pattern: ReadCachePolicy
- command: hwinventory
name: WriteCachePolicy
pattern: WriteCachePolicy
- command: get STORAGE.Controller.1
name: CheckConsistencyMode
pattern: CheckConsistencyMode
문병철매니저님_INTEL_SERVER:
bios_settings:
- command: get bios.BiosBootSettings
name: BIOS Boot Mode
pattern: BootMode
- command: get bios.SysProfileSettings
name: System Profile
pattern: SysProfile=
- command: get bios.SysProfileSettings
name: CPU Power Management
pattern: EnergyPerformanceBias
- command: get bios.SysProfileSettings
name: Memory Frequency
pattern: MemFrequency
- command: get bios.SysProfileSettings
name: Turbo Boost
pattern: ProcTurboMode
- command: get bios.SysProfileSettings
name: C1E
pattern: ProcC1E
- command: get bios.SysProfileSettings
name: C-States
pattern: ProcCStates
- command: get bios.SysProfileSettings
name: Monitor/Mwait
pattern: MonitorMwait
- command: get bios.ProcSettings
name: Logical Processor
pattern: LogicalProc
- command: get bios.ProcSettings
name: Virtualization Technology
pattern: ProcVirtualization
- command: get bios.ProcSettings
name: LLC Prefetch
pattern: LlcPrefetch
- command: get bios.ProcSettings
name: x2APIC Mode
pattern: ProcX2Apic
- command: get bios.MemSettings
name: Node Interleaving
pattern: NodeInterleave
- command: get bios.MemSettings
name: DIMM Self Healing
pattern: PPROnUCE
- command: get bios.MemSettings
name: Correctable Error Logging
pattern: CECriticalSEL
- command: get System.ThermalSettings
name: Thermal Profile Optimization
pattern: ThermalProfile
- command: get Bios.IntegratedDevices
name: SR-IOV Global Enable
pattern: SriovGlobalEnable
- command: get bios.MiscSettings
name: F1/F2 Prompt on Error
pattern: ErrPrompt
- command: get iDRAC.ASRConfig
name: ASR Config
pattern: Enable=
- command: get Bios.IntegratedDevices.OsWatchdogTimer
name: Os watchdof timer
pattern: OswatchdogTimer=
description: 기본 서버 정보 수집 (BIOS, iDRAC, RAID)
extends: minimal
firmware:
- command: get NIC.FrmwImgMenu.1
name: NIC Integrated Firmware
pattern: '#FamilyVersion'
- command: hwinventory
name: RAID Controller Firmware
pattern: ControllerFirmwareVersion
- command: hwinventory
name: BOSS Controller Firmware
pattern: ControllerFirmwareVersion = 2
idrac_settings:
- command: get iDRAC.Time.Timezone
name: Timezone
pattern: Timezone
- command: get iDRAC.CurrentNIC
name: IPMI LAN Selection
pattern: ActiveNIC
- command: get iDRAC.CurrentIPv4
name: IPMI IPv4 DHCP
pattern: DHCPEnable
- command: get iDRAC.CurrentIPv6
name: IPMI IPv6 Enable
pattern: Enable=
- command: get iDRAC.Redfish.Enable
name: Redfish Support
pattern: Enable=
- command: get iDRAC.SSH
name: SSH Support
pattern: Enable=
- command: get iDRAC.USERDomain.1.Name
name: AD User Domain Name
pattern: Name
- command: get iDRAC.ActiveDirectory.DomainController1
name: SC Server Address
pattern: DomainController1
- command: get iDRAC.SysLog.SysLogEnable
name: Remote Log (syslog)
pattern: SysLogEnable
- command: get iDRAC.SysLog.Server1
name: Syslog Server 1
pattern: Server1
- command: get iDRAC.SysLog.Server2
name: Syslog Server 2
pattern: Server2
- command: get iDRAC.SysLog.Port
name: Syslog Port
pattern: Port
- command: get iDRAC.VirtualConsole.Port
name: VirtualConsole Port
pattern: Port
raid_settings:
- command: hwinventory
name: RAID ProductName
pattern: ProductName = PERC
- command: hwinventory
name: BOSS ProductName
pattern: ProductName = BOSS
- command: hwinventory
name: RAID Types
pattern: RAIDTypes
- command: hwinventory
name: StripeSize
pattern: StripeSize
- command: hwinventory
name: ReadCachePolicy
pattern: ReadCachePolicy
- command: hwinventory
name: WriteCachePolicy
pattern: WriteCachePolicy
- command: get STORAGE.Controller.1
name: CheckConsistencyMode
pattern: CheckConsistencyMode
- command: get STORAGE.Controller.1
name: PatrolReadRate
pattern: PatrolReadRate
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+1 -1
View File
@@ -18,7 +18,7 @@ if [ ! -f "$IP_FILE" ]; then
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="idrac_info"
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""
통합 GPU 시리얼 수집 스크립트
hwinventory에서 Video.Slot 정보 수집
"""
import sys
import os
import re
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.idrac_client import IDRACClient
from core.parsers import InfoParser
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def collect_gpu_for_ip(ip: str, output_dir: Path) -> tuple:
"""단일 IP에 대한 GPU Serial 정보 수집"""
try:
logging.info(f"[{ip}] GPU 정보 수집 시작")
client = IDRACClient(ip)
# 1. Get Service Tag
sysinfo = client.get_sysinfo()
svc_tag = InfoParser.extract_service_tag(sysinfo)
if not svc_tag:
return ip, False, "서비스 태그 추출 실패"
# 2. Get Hardware Inventory
hwinventory = client.get_hwinventory()
# 3. Parse GPU Serials
gpu_map = InfoParser.extract_gpu_serials(hwinventory)
# 4. Write File
output_file = output_dir / f"{svc_tag}.txt"
# 정렬 로직 (Legacy GPU_Serial_v1.py 유지)
def slot_key(k: str):
m = re.search(r"Video\.Slot\.(\d+)-(\d+)", k)
if m:
return (int(m.group(1)), int(m.group(2)))
return (1_000_000, 1_000_000, k)
with output_file.open('w', encoding='utf-8', newline='\n') as f:
f.write(f"{svc_tag}\n")
if not gpu_map:
f.write("No GPU(Video) inventory found or SerialNumber not present.\n")
return ip, True, f"저장: {output_file.name} (GPU 없음)"
# Write individual slots
sorted_keys = sorted(gpu_map.keys(), key=slot_key)
for fqdd in sorted_keys:
serial = gpu_map[fqdd]
f.write(f"{fqdd}: {serial}\n")
# Write summary line
serials_only = [gpu_map[k] for k in sorted_keys if gpu_map[k] != "Not Found"]
if serials_only:
f.write(f"SERIALS: {';'.join(serials_only)}\n")
return ip, True, f"저장: {output_file.name} ({len(serials_only)}개 발견)"
except Exception as e:
logging.error(f"[{ip}] 오류: {e}")
return ip, False, f"오류: {e}"
def main():
if len(sys.argv) < 2:
print("Usage: python collect_gpu.py <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)
# 출력 디렉토리
# 출력 디렉토리 (스크립트 위치 기준)
script_dir = Path(__file__).resolve().parent
data_root = script_dir.parent.parent
output_dir = data_root / "temp" / "staging"
output_dir.mkdir(parents=True, exist_ok=True)
# IP 목록 읽기
ips = [line.strip() for line in ip_file.read_text(encoding='utf-8').splitlines() if line.strip()]
if not ips:
logging.error("IP 목록이 비어 있습니다.")
sys.exit(1)
logging.info(f"{len(ips)}대 GPU 정보 수집 시작")
# 병렬 처리
ok, fail = 0, 0
with ThreadPoolExecutor(max_workers=20) as executor:
futures = {executor.submit(collect_gpu_for_ip, ip, output_dir): ip for ip in ips}
for future in futures:
ip, success, msg = future.result()
if success:
logging.info(f"[OK] {ip} - {msg}")
ok += 1
else:
logging.error(f"[FAIL] {ip} - {msg}")
fail += 1
logging.info(f"완료: 성공 {ok}대 / 실패 {fail}")
if __name__ == "__main__":
main()
+216
View File
@@ -0,0 +1,216 @@
#!/usr/bin/env python3
"""
통합 GUID 수집 스크립트
프로파일 기반으로 InfiniBand GUID 수집
"""
import sys
import yaml
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.idrac_client import IDRACClient
from core.parsers import InfoParser
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def load_profile(profile_name: str) -> dict:
"""프로파일 로드"""
profile_file = Path(__file__).parent.parent / "profiles" / "guid_profiles.yaml"
if not profile_file.exists():
logging.error(f"프로파일 파일 없음: {profile_file}")
sys.exit(1)
with open(profile_file, 'r', encoding='utf-8') as f:
profiles = yaml.safe_load(f)['profiles']
profile = profiles.get(profile_name, profiles['default'])
# 환경변수에서 슬롯 우선순위 읽기 (custom_priority 프로파일용)
if not profile.get('slot_priority'):
env_priority = os.getenv("GUID_SLOT_PRIORITY", "")
if env_priority:
profile['slot_priority'] = [int(s.strip()) for s in env_priority.split(",") if s.strip()]
else:
profile['slot_priority'] = profiles['default']['slot_priority']
return profile
def collect_guid_for_ip(ip: str, profile: dict, output_dir: Path) -> tuple:
"""단일 IP에 대한 GUID 정보 수집 (PortGUID_v1.py 로직 기반)"""
try:
logging.info(f"[{ip}] DEBUG: Running Unified GUID Script v2.2 (PortGUID_v1 Logic)")
# IDRAC credentials from env or default
idrac_user = os.getenv("IDRAC_USER", "root")
idrac_pass = os.getenv("IDRAC_PASS", "calvin")
# 1. Get Service Tag (using raw subprocess like v1)
cmd_getsysinfo = [
"racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass, "getsysinfo"
]
# v1 uses subprocess.getoutput which runs with shell=True/standard shell behavior.
# Here we replicate it or use subprocess.run with capture_output.
# PortGUID_v1 uses: getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
# Using subprocess.run for better control but mimicking the command structure
import subprocess
import re
try:
# mimic v1: Use the exact same command string style
proc_info = subprocess.run(cmd_getsysinfo, capture_output=True, text=True, timeout=60)
getsysinfo = proc_info.stdout
except Exception as e:
return ip, False, f"SVC Tag 수집 실패: {e}"
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:
return ip, False, "서비스 태그 추출 실패 (Regex miss)"
# 2. Get InfiniBand Page List
cmd_list = [
"racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass, "get", "InfiniBand.VndrConfigPage"
]
proc_list = subprocess.run(cmd_list, capture_output=True, text=True, timeout=60)
output_list = proc_list.stdout
# 3. Parse Page Map
matches = re.findall(
r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
output_list
)
slot_to_guid = {}
# 4. Fetch Detail for each page
for number, slot in matches:
cmd_detail = [
"racadm", "-r", ip, "-u", idrac_user, "-p", idrac_pass,
"get", f"InfiniBand.VndrConfigPage.{number}"
]
proc_detail = subprocess.run(cmd_detail, capture_output=True, text=True, timeout=60)
output_detail = proc_detail.stdout
match_guid = re.search(r"PortGUID=(\S+)", output_detail)
port_guid = match_guid.group(1) if match_guid else None
if port_guid:
slot_to_guid[int(slot)] = port_guid
# 5. Determine Priority (Preference: Profile > Env > Default(v1 logic))
# Logic from PortGUID_v1.py:
# if total_slots == 4: order = ...
# elif total_slots == 10: order = ...
# We will use the profile's logic if available, but fallback to v1 logic if profile is 'default' and matches slot counts
desired_order = profile.get('slot_priority', [])
# If profile didn't specify (or empty), use v1 adaptive logic
if not desired_order:
total_slots = len(matches)
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 = [int(slot) for _, slot in matches] # Just collection order
logging.info(f"[{ip}] DEBUG: Using Adaptive Priority (Count={total_slots}): {desired_order}")
else:
logging.info(f"[{ip}] DEBUG: Using Profile Priority: {desired_order}")
# 6. Write File
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")
hex_guid_list = []
# Write in desired order
for slot in desired_order:
guid = slot_to_guid.get(slot)
if guid:
f.write(f"Slot.{slot}: {guid}\n")
# Format
fmt_guid = guid.replace(':', '').upper()
if not fmt_guid.startswith("0X"):
fmt_guid = "0x" + fmt_guid
hex_guid_list.append(fmt_guid)
# Remaining slots (if any weren't in priority list)?
# v1 doesn't write them if not in priority list. We will stick to v1 behavior.
if profile.get('output_format', 'combined') == 'combined' and hex_guid_list:
f.write(f"GUID: {';'.join(hex_guid_list)}\n")
return ip, True, f"저장: {output_file.name} ({len(hex_guid_list)}개 GUID/v1_Logic)"
except Exception as e:
logging.error(f"[{ip}] 오류: {e}")
return ip, False, f"오류: {e}"
def main():
if len(sys.argv) < 2:
print("Usage: python collect_guid.py <ip_file> [profile_name]")
print("Available profiles: default, custom_priority")
sys.exit(1)
ip_file = Path(sys.argv[1])
profile_name = sys.argv[2] if len(sys.argv) > 2 else "default"
if not ip_file.exists():
logging.error(f"IP 파일 없음: {ip_file}")
sys.exit(1)
# 프로파일 로드
profile = load_profile(profile_name)
logging.info(f"프로파일 사용: {profile_name} - {profile.get('description', '')}")
logging.info(f"슬롯 우선순위: {profile['slot_priority']}")
# 출력 디렉토리
# 출력 디렉토리 (스크립트 위치 기준)
script_dir = Path(__file__).resolve().parent
data_root = script_dir.parent.parent
output_dir = data_root / "temp" / "staging"
output_dir.mkdir(parents=True, exist_ok=True)
# IP 목록 읽기
ips = [line.strip() for line in ip_file.read_text(encoding='utf-8').splitlines() if line.strip()]
if not ips:
logging.error("IP 목록이 비어 있습니다.")
sys.exit(1)
logging.info(f"{len(ips)}대 처리 시작")
# 병렬 처리
ok, fail = 0, 0
with ThreadPoolExecutor(max_workers=20) as executor:
futures = {executor.submit(collect_guid_for_ip, ip, profile, output_dir): ip for ip in ips}
for future in futures:
ip, success, msg = future.result()
if success:
logging.info(f"[OK] {ip} - {msg}")
ok += 1
else:
logging.error(f"[FAIL] {ip} - {msg}")
fail += 1
logging.info(f"완료: 성공 {ok}대 / 실패 {fail}")
if __name__ == "__main__":
main()
+186
View File
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""
통합 MAC 수집 스크립트
프로파일 기반으로 다양한 서버 타입 지원
"""
import sys
import yaml
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging
# core 모듈 임포트
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.idrac_client import IDRACClient
from core.parsers import InfoParser
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def load_profile(profile_name: str) -> dict:
"""프로파일 로드 및 상속 처리"""
profile_file = Path(__file__).parent.parent / "profiles" / "mac_profiles.yaml"
if not profile_file.exists():
logging.error(f"프로파일 파일 없음: {profile_file}")
sys.exit(1)
with open(profile_file, 'r', encoding='utf-8') as f:
profiles = yaml.safe_load(f)['profiles']
profile = profiles.get(profile_name)
if not profile:
logging.error(f"프로파일 '{profile_name}' 없음. 사용 가능: {list(profiles.keys())}")
sys.exit(1)
# extends 처리
if 'extends' in profile:
base = profiles[profile['extends']].copy()
base.update(profile)
profile = base
return profile
def collect_mac_for_ip(ip: str, profile: dict, output_dir: Path) -> tuple:
"""단일 IP에 대한 MAC 정보 수집"""
try:
client = IDRACClient(ip)
parser = InfoParser()
# 데이터 수집
sysinfo = client.get_sysinfo()
hwinventory = client.get_hwinventory()
# 서비스 태그
svc_tag = parser.extract_service_tag(sysinfo)
if not svc_tag:
return ip, False, "서비스 태그 추출 실패"
# NIC MAC 주소
# NIC MAC 주소
# sysinfo보다 hwinventory가 더 상세한 정보(Slot NIC 등)를 포함하므로 변경
nic_macs = parser.extract_nic_macs_from_hwinventory(hwinventory, profile['nic_patterns'])
# iDRAC MAC
idrac_mac = None
if profile.get('include_idrac_mac'):
idrac_mac = parser.extract_idrac_mac(sysinfo)
# InfiniBand (선택적)
ib_guids = {}
if 'infiniband_slots' in profile:
# Legacy logic: Fetch from swinventory (checking previous line of FQDD)
swinventory = client.get_swinventory()
ib_guids = parser.extract_infiniband_macs_from_swinventory(swinventory, profile['infiniband_slots'])
# 벤더 정보
memory_vendors = []
disk_vendors = []
if profile.get('include_memory_vendor'):
memory_vendors = parser.extract_vendors(hwinventory, "DIMM")
if profile.get('include_disk_vendor'):
if profile.get('exclude_boss'):
# BOSS 제외한 디스크 벤더
all_vendors = set()
for prefix in ["Disk.Bay.", "Disk.M.2.", "Disk.Direct.", "Disk.Slot."]:
all_vendors.update(parser.extract_vendors(hwinventory, prefix))
disk_vendors = sorted(all_vendors)
else:
disk_vendors = parser.extract_vendors(hwinventory, "Disk.")
# 파일 저장
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")
# NIC MAC (순서 유지)
for pattern in profile['nic_patterns']:
f.write(f"{nic_macs.get(pattern) or ''}\n")
# InfiniBand (있을 경우)
if ib_guids:
for slot in profile['infiniband_slots']:
f.write(f"{ib_guids.get(slot) or ''}\n")
# iDRAC MAC
if idrac_mac:
f.write(f"{idrac_mac}\n")
# 벤더 정보
for vendor in memory_vendors:
f.write(f"{vendor}\n")
for vendor in disk_vendors:
f.write(f"{vendor}\n")
return ip, True, f"저장: {output_file.name}"
except Exception as e:
logging.error(f"[{ip}] 오류: {e}")
return ip, False, f"오류: {e}"
def main():
if len(sys.argv) < 2:
print("Usage: python collect_mac.py <ip_file> [profile_name]")
print("Available profiles: default, xe9680_h200_10ea, type11, jp_54ea, sp_18ea")
sys.exit(1)
ip_file = Path(sys.argv[1])
profile_name = sys.argv[2] if len(sys.argv) > 2 else "default"
if not ip_file.exists():
logging.error(f"IP 파일 없음: {ip_file}")
sys.exit(1)
# 프로파일 로드
profile = load_profile(profile_name)
logging.info(f"프로파일 사용: {profile_name} - {profile.get('description', '')}")
# 출력 디렉토리 (스크립트 위치 기준)
# script: data/scripts/unified/collect_mac.py -> data_root: data/
script_dir = Path(__file__).resolve().parent
data_root = script_dir.parent.parent
output_dir = data_root / "temp" / "staging"
output_dir.mkdir(parents=True, exist_ok=True)
# IP 목록 읽기
ips = [line.strip() for line in ip_file.read_text(encoding='utf-8').splitlines() if line.strip()]
if not ips:
logging.error("IP 목록이 비어 있습니다.")
sys.exit(1)
logging.info(f"{len(ips)}대 처리 시작")
# 병렬 처리
ok, fail = 0, 0
with ThreadPoolExecutor(max_workers=20) as executor:
futures = {executor.submit(collect_mac_for_ip, ip, profile, output_dir): ip for ip in ips}
for future in futures:
ip, success, msg = future.result()
if success:
logging.info(f"[OK] {ip} - {msg}")
ok += 1
else:
logging.error(f"[FAIL] {ip} - {msg}")
fail += 1
logging.info(f"완료: 성공 {ok}대 / 실패 {fail}")
# IP 파일 삭제 (기존 스크립트와 동일)
try:
ip_file.unlink(missing_ok=True)
except Exception:
pass
if __name__ == "__main__":
main()
+192
View File
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
"""
통합 서버 정보 수집 스크립트
프로파일 기반으로 BIOS/iDRAC/RAID 설정 수집
"""
import sys
import yaml
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
import logging
sys.path.insert(0, str(Path(__file__).parent.parent))
from core.idrac_client import IDRACClient
from core.parsers import InfoParser
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def load_profile(profile_name: str) -> dict:
"""프로파일 로드 및 상속 처리"""
profile_file = Path(__file__).parent.parent / "profiles" / "server_info_profiles.yaml"
if not profile_file.exists():
logging.error(f"프로파일 파일 없음: {profile_file}")
sys.exit(1)
with open(profile_file, 'r', encoding='utf-8') as f:
profiles = yaml.safe_load(f)['profiles']
profile = profiles.get(profile_name)
if not profile:
logging.error(f"프로파일 '{profile_name}' 없음. 사용 가능: {list(profiles.keys())}")
sys.exit(1)
# extends 처리
if 'extends' in profile:
base = profiles[profile['extends']].copy()
# 리스트 병합
for key in ['firmware', 'bios_settings', 'idrac_settings', 'raid_settings', 'custom_items']:
if key in profile:
base_items = base.get(key, [])
profile_items = profile[key]
# 중복 제거하면서 병합
base[key] = base_items + profile_items
base.update({k: v for k, v in profile.items() if k not in ['firmware', 'bios_settings', 'idrac_settings', 'raid_settings', 'custom_items']})
profile = base
return profile
def collect_server_info_for_ip(ip: str, profile: dict, output_dir: Path) -> tuple:
"""단일 IP에 대한 서버 정보 수집"""
try:
client = IDRACClient(ip)
parser = InfoParser()
# 서비스 태그 추출
sysinfo = client.get_sysinfo()
svc_tag = parser.extract_service_tag(sysinfo)
if not svc_tag:
return ip, False, "서비스 태그 추출 실패"
# 결과 파일 생성
output_file = output_dir / f"{svc_tag}.txt"
with output_file.open('w', encoding='utf-8', newline='\n') as f:
f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {svc_tag})\n\n")
# 펌웨어 버전 정보
if 'firmware' in profile:
f.write("-" * 42 + " Firmware Version 정보 " + "-" * 42 + "\n")
for idx, item in enumerate(profile['firmware'], 1):
value = _get_value_from_command(client, item['command'], item['pattern'])
f.write(f"{idx}. {item['name']} : {value}\n")
f.write("\n")
# BIOS 설정 정보
if 'bios_settings' in profile:
f.write("-" * 45 + " Bios 설정 정보 " + "-" * 46 + "\n")
for idx, item in enumerate(profile['bios_settings'], 1):
value = _get_value_from_command(client, item['command'], item['pattern'])
f.write(f"{idx:02d}. {item['name']} : {value}\n")
f.write("\n")
# iDRAC 설정 정보
if 'idrac_settings' in profile:
f.write("-" * 45 + " iDRAC 설정 정보 " + "-" * 45 + "\n")
for idx, item in enumerate(profile['idrac_settings'], 1):
value = _get_value_from_command(client, item['command'], item['pattern'])
f.write(f"{idx:02d}. {item['name']} : {value}\n")
f.write("\n")
# RAID 설정 정보
if 'raid_settings' in profile:
f.write("-" * 45 + " Raid 설정 정보 " + "-" * 46 + "\n")
for idx, item in enumerate(profile['raid_settings'], 1):
value = _get_value_from_command(client, item['command'], item['pattern'])
f.write(f"{idx:02d}. {item['name']} : {value}\n")
f.write("\n")
# 커스텀 항목 (있을 경우)
if 'custom_items' in profile and profile['custom_items']:
f.write("-" * 45 + " 커스텀 항목 " + "-" * 46 + "\n")
for idx, item in enumerate(profile['custom_items'], 1):
value = _get_value_from_command(client, item['command'], item['pattern'])
f.write(f"{idx:02d}. {item['name']} : {value}\n")
f.write("\n")
return ip, True, f"저장: {output_file.name}"
except Exception as e:
logging.error(f"[{ip}] 오류: {e}")
return ip, False, f"오류: {e}"
def _get_value_from_command(client: IDRACClient, command: str, pattern: str) -> str:
"""racadm 명령 실행 및 값 추출"""
# 캐싱된 데이터 사용
if command == "getsysinfo":
output = client.get_sysinfo()
elif command == "hwinventory":
output = client.get_hwinventory()
else:
_, output = client.run_command(command)
# 패턴 매칭
value = InfoParser.extract_value_by_pattern(output, pattern)
return value if value else "N/A"
def main():
if len(sys.argv) < 2:
print("Usage: python collect_server_info.py <ip_file> [profile_name]")
print("Available profiles: default, intel_server, amd_server, minimal")
sys.exit(1)
ip_file = Path(sys.argv[1])
profile_name = sys.argv[2] if len(sys.argv) > 2 else "default"
if not ip_file.exists():
logging.error(f"IP 파일 없음: {ip_file}")
sys.exit(1)
# 프로파일 로드
profile = load_profile(profile_name)
logging.info(f"프로파일 사용: {profile_name} - {profile.get('description', '')}")
# 출력 디렉토리 (data/temp/staging)
script_dir = Path(__file__).resolve().parent
# unified 디렉토리에 있으므로, data 디렉토리는 3단계 상위 (unified -> scripts -> data)
data_dir = script_dir.parent.parent
# 만약 구조가 다를 경우를 대비한 안전장치 (scripts가 아닐 경우 등)
if data_dir.name != "data":
# fallback: 현재 작업 디렉토리 기준 data 찾기 시도
data_dir = Path("data").resolve()
output_dir = data_dir / "temp" / "staging"
output_dir.mkdir(parents=True, exist_ok=True)
# IP 목록 읽기
ips = [line.strip() for line in ip_file.read_text(encoding='utf-8').splitlines() if line.strip()]
if not ips:
logging.error("IP 목록이 비어 있습니다.")
sys.exit(1)
logging.info(f"{len(ips)}대 처리 시작")
# 병렬 처리 (서버 정보 수집은 racadm 호출이 많아 worker 수 줄임)
ok, fail = 0, 0
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(collect_server_info_for_ip, ip, profile, output_dir): ip for ip in ips}
for future in futures:
ip, success, msg = future.result()
if success:
logging.info(f"[OK] {ip} - {msg}")
ok += 1
else:
logging.error(f"[FAIL] {ip} - {msg}")
fail += 1
logging.info(f"완료: 성공 {ok}대 / 실패 {fail}")
if __name__ == "__main__":
main()
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
통합 시스템 제어 스크립트 (Unified System Control Script)
기존의 개별 제어 스크립트들을 하나로 통합하였습니다.
지원 기능:
- power_on (06-PowerON.py)
- power_off (07-PowerOFF.py)
- log_clear (05-clrsel.py)
- job_delete (08-job_delete_all.py)
- tsr_collect (03-tsr_log.py)
- tsr_save (04-tsr_save.py)
"""
import os
import sys
import argparse
import subprocess
import time
import logging
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from dotenv import load_dotenv
# -----------------------------------------------------------------------------
# Configuration
# -----------------------------------------------------------------------------
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
# Load .env relative to script location or current directory
load_dotenv()
IDRAC_USER = os.getenv("IDRAC_USER", "root")
IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin")
OME_USER = os.getenv("OME_USER", "")
OME_PASS = os.getenv("OME_PASS", "")
# -----------------------------------------------------------------------------
# Command Definitions
# -----------------------------------------------------------------------------
# Each action maps to a function that returns the racadm command list
def cmd_power_on(ip):
return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerup"]
def cmd_power_off(ip):
return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "serveraction", "powerdown"]
def cmd_log_clear(ip):
return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "clrsel"]
def cmd_job_delete(ip):
return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "jobqueue", "delete", "-i", "ALL"]
def cmd_tsr_collect(ip):
return ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "techsupreport", "collect"]
def cmd_tsr_save(ip):
# Note: This command assumes a specific share path (legacy behavior)
# Ideally this path should also be configurable via env
share_path = "//10.10.3.15/share/"
return [
"racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS,
"techsupreport", "export", "-l", share_path, "-u", OME_USER, "-p", OME_PASS
]
ACTION_MAP = {
"power_on": cmd_power_on,
"power_off": cmd_power_off,
"log_clear": cmd_log_clear,
"job_delete": cmd_job_delete,
"tsr_collect": cmd_tsr_collect,
"tsr_save": cmd_tsr_save,
}
# -----------------------------------------------------------------------------
# Execution Logic
# -----------------------------------------------------------------------------
def execute_action(ip: str, action_name: str) -> None:
"""Run the racadm command for a single IP."""
try:
cmd_func = ACTION_MAP.get(action_name)
if not cmd_func:
logger.error(f"[{ip}] Unknown action: {action_name}")
return
cmd = cmd_func(ip)
logger.info(f"[{ip}] Executing {action_name}...")
# Run command
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=600 # Generous timeout for TSR operations
)
if result.returncode == 0:
logger.info(f"[{ip}] Success: {result.stdout.strip()}")
else:
logger.error(f"[{ip}] Failed: {result.stderr.strip()}")
except Exception as e:
logger.error(f"[{ip}] Exception: {e}")
def main():
parser = argparse.ArgumentParser(description="Unified System Control Script")
parser.add_argument("action", choices=ACTION_MAP.keys(), help="Action to perform")
parser.add_argument("ip_file", type=Path, help="Path to the IP list file")
# Optional: Allow passing IP directly via args instead of file (future extension)
args = parser.parse_args()
ip_file = args.ip_file
action = args.action
if not ip_file.is_file():
logger.error(f"IP file not found: {ip_file}")
sys.exit(1)
# Read IPs
try:
with ip_file.open("r", encoding="utf-8") as f:
ips = [line.strip() for line in f if line.strip()]
except Exception as e:
logger.error(f"Failed to read IP file: {e}")
sys.exit(1)
if not ips:
logger.warning("No IPs found in file.")
sys.exit(0)
logger.info(f"Starting '{action}' for {len(ips)} servers...")
start_time = time.time()
# Parallel Execution
# Adjust max_workers as needed. 20 is a safe defaults for I/O bound network tasks.
with ThreadPoolExecutor(max_workers=20) as executor:
futures = [executor.submit(execute_action, ip, action) for ip in ips]
for future in futures:
future.result() # Wait for all to complete
elapsed = time.time() - start_time
logger.info(f"Completed '{action}' in {elapsed:.2f} seconds.")
if __name__ == "__main__":
main()