Initial project upload

This commit is contained in:
Kim.KANGHEE
2026-05-05 17:14:11 +09:00
commit 122b73d254
282 changed files with 72135 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
import os
import subprocess
import time
from dotenv import load_dotenv
from concurrent.futures import ThreadPoolExecutor
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# .env 파일 로드
load_dotenv()
# 사용자 이름 및 비밀번호 설정
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
# IP 파일 유효성 검사
def validate_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
raise FileNotFoundError(f"IP 파일 {ip_file_path} 이(가) 존재하지 않습니다.")
return ip_file_path
# 정보 저장 디렉터리 설정
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 정보를 가져오는 함수 정의
def fetch_idrac_info(ip_address):
try:
# 모든 hwinventory 저장
hwinventory = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} hwinventory")
# 모든 샷시 정보 저장
getsysinfo = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} getsysinfo")
# 모든 SysProfileSettings 저장
SysProfileSettings = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get bios.SysProfileSettings")
# ProcessorSettings 저장
ProcessorSettings = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get bios.ProcSettings")
# Memory Settings 저장
MemorySettings = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get bios.MemSettings")
# Raid Settings 저장
STORAGEController = subprocess.getoutput(f"racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get STORAGE.Controller.1")
# 서비스 태그 가져오기
SVC_TAG = get_value(getsysinfo, "SVC Tag")
if not SVC_TAG:
raise ValueError(f"IP {ip_address} 에 대한 SVC Tag 가져오기 실패")
# 출력 파일 작성
output_file = os.path.join(OUTPUT_DIR, f"{SVC_TAG}.txt")
with open(output_file, 'a', encoding='utf-8') as f:
f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {SVC_TAG})\n\n")
f.write("------------------------------------------Firware Version 정보------------------------------------------\n")
f.write(f"1. SVC Tag : {SVC_TAG}\n")
f.write(f"2. Bios Firmware : {get_value(getsysinfo, 'System BIOS Version')}\n")
f.write(f"3. iDRAC Firmware Version : {get_value(getsysinfo, 'Firmware Version')}\n")
f.write(f"4. NIC Integrated Firmware Version : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get NIC.FrmwImgMenu.1'), '#FamilyVersion')}\n")
f.write(f"5. OnBoard NIC Firmware Version : Not Found\n")
f.write(f"6. Raid Controller Firmware Version : {get_value(hwinventory, 'ControllerFirmwareVersion')}\n\n")
f.write("---------------------------------------------Bios 설정 정보----------------------------------------------\n")
f.write(f"01. Bios Boot Mode : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get bios.BiosBootSettings'), 'BootMode')}\n")
f.write(f"02. System Profile Settings - System Profile : {get_value(SysProfileSettings, 'SysProfile=')}\n")
f.write(f"03. System Profile Settings - CPU Power Management : {get_value(SysProfileSettings, 'EnergyPerformanceBias')}\n")
f.write(f"04. System Profile Settings - Memory Frequency : {get_value(SysProfileSettings, 'MemFrequency')}\n")
f.write(f"05. System Profile Settings - Turbo Boost : {get_value(SysProfileSettings, 'ProcTurboMode')}\n")
f.write(f"06. System Profile Settings - C1E : {get_value(SysProfileSettings, 'ProcC1E')}\n")
f.write(f"07. System Profile Settings - C-States : {get_value(SysProfileSettings, 'ProcCStates')}\n")
f.write(f"08. System Profile Settings - Monitor/Mwait : {get_value(SysProfileSettings, 'MonitorMwait')}\n")
f.write(f"09. Processor Settings - Logical Processor : {get_value(ProcessorSettings, 'LogicalProc')}\n")
f.write(f"10. Processor Settings - Virtualization Technology : {get_value(ProcessorSettings, 'ProcVirtualization')}\n")
f.write(f"11. Processor Settings - LLC Prefetch : {get_value(ProcessorSettings, 'LlcPrefetch')}\n")
f.write(f"12. Processor Settings - x2APIC Mode : {get_value(ProcessorSettings, 'ProcX2Apic')}\n")
f.write(f"13. Memory Settings - Node Interleaving : {get_value(MemorySettings, 'NodeInterleave')}\n")
f.write(f"14. Memory Settings - DIMM Self Healing (Post Package Repair) on Uncorrectable Memory Error : {get_value(MemorySettings, 'PPROnUCE')}\n")
f.write(f"15. Memory Settings - Correctable Error Logging : {get_value(MemorySettings, 'CECriticalSEL')}\n")
f.write(f"16. System Settings - Thermal Profile Optimization : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get System.ThermalSettings'), 'ThermalProfile')}\n")
f.write(f"17. Integrated Devices Settings - SR-IOV Global Enable : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get Bios.IntegratedDevices'), 'SriovGlobalEnable')}\n")
f.write(f"18. Miscellaneous Settings - F1/F2 Prompt on Error : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get bios.MiscSettings'), 'ErrPrompt')}\n\n")
f.write("---------------------------------------------iDRAC 설정 정보----------------------------------------------\n")
f.write(f"01. iDRAC Settings - Timezone : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.Time.Timezone'), 'Timezone')}\n")
f.write(f"02. iDRAC Settings - IPMI LAN Selection : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.CurrentNIC'), 'ActiveNIC')}\n")
f.write(f"03. iDRAC Settings - IPMI IP(IPv4) : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.CurrentIPv4'), 'DHCPEnable')}\n")
f.write(f"04. iDRAC Settings - IPMI IP(IPv6) : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.CurrentIPv6'), 'Enable')}\n")
f.write(f"05. iDRAC Settings - Redfish Support : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.Redfish.Enable'), 'Enable')}\n")
f.write(f"06. iDRAC Settings - SSH Support : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.SSH'), 'Enable')}\n")
f.write(f"07. iDRAC Settings - AD User Domain Name : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.USERDomain.1.Name'), 'Name')}\n")
f.write(f"08. iDRAC Settings - SC Server Address : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ActiveDirectory.DomainController1'), 'DomainController1')}\n")
f.write(f"09. iDRAC Settings - SE AD RoleGroup Name : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.1.Name'), 'Name')}\n")
f.write(f"10. iDRAC Settings - SE AD RoleGroup Domain : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.1.Domain'), 'Domain')}\n")
f.write(f"11. iDRAC Settings - SE AD RoleGroup Privilege : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.1.Privilege'), 'Privilege')}\n")
f.write(f"12. iDRAC Settings - IDC AD RoleGroup Name : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.2.Name'), 'Name')}\n")
f.write(f"13. iDRAC Settings - IDC AD RoleGroup Domain : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.2.Domain'), 'Domain')}\n")
f.write(f"14. iDRAC Settings - IDC AD RoleGroup Privilege : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.ADGroup.2.Privilege'), 'Privilege')}\n")
f.write(f"15. iDRAC Settings - Remote Log (syslog) : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.SysLog.SysLogEnable'), 'SysLogEnable')}\n")
f.write(f"16. iDRAC Settings - syslog server address 1 : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.SysLog.Server1'), 'Server1')}\n")
f.write(f"17. iDRAC Settings - syslog server address 2 : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.SysLog.Server2'), 'Server2')}\n")
f.write(f"18. iDRAC Settings - syslog server port : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.SysLog.Port'), 'Port')}\n")
f.write(f"19. iDRAC Settings - Remote KVM Nonsecure port : {get_value(subprocess.getoutput(f'racadm -r {ip_address} -u {IDRAC_USER} -p {IDRAC_PASS} get iDRAC.VirtualConsole.Port'), 'Port')}\n\n")
f.write("---------------------------------------------Raid 설정 정보----------------------------------------------\n")
f.write(f"01. RAID Settings - Raid ProductName : {get_value(hwinventory, 'ProductName = PERC')}\n")
f.write(f"02. RAID Settings - Raid Types : No-Raid mode\n")
logging.info(f"IP {ip_address} 에 대한 정보를 {output_file} 에 저장했습니다.")
except Exception as e:
logging.error(f"오류 발생: {e}")
# 명령 결과에서 원하는 값 가져오기
def get_value(output, key):
for line in output.splitlines():
if key.lower() in line.lower():
return line.split('=')[1].strip()
return None
# 시작 시간 기록
start_time = time.time()
# IP 목록 파일을 읽어 병렬로 작업 수행
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error(f"Usage: {sys.argv[0]} <ip_file>")
sys.exit(1)
ip_file_path = validate_ip_file(sys.argv[1])
with open(ip_file_path, 'r') as ip_file:
ip_addresses = ip_file.read().splitlines()
# 병렬 처리를 위해 ThreadPoolExecutor 사용
max_workers = 100 # 작업 풀 크기 설정
with ThreadPoolExecutor(max_workers=max_workers) as executor:
executor.map(fetch_idrac_info, ip_addresses)
# 종료 시간 기록
end_time = time.time()
# 소요 시간 계산
elapsed_time = end_time - start_time
elapsed_hours = int(elapsed_time // 3600)
elapsed_minutes = int((elapsed_time % 3600) // 60)
elapsed_seconds = int(elapsed_time % 60)
logging.info("정보 수집 완료.")
logging.info(f"수집 완료 시간: {elapsed_hours} 시간, {elapsed_minutes} 분, {elapsed_seconds} 초.")
+129
View File
@@ -0,0 +1,129 @@
import sys
import os
import shutil
import tempfile
import time
import logging
import subprocess
from pathlib import Path
# ─────────────────────────────────────────────
# 설정 (필요하면 .env 등에서 읽어와도 됨)
IDRAC_USER = os.getenv("IDRAC_USER", "root")
IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin")
RACADM = os.getenv("RACADM_PATH", "racadm") # PATH에 있으면 'racadm'
# 로깅
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
def read_ip_list(ip_file: Path):
ips = []
for line in ip_file.read_text(encoding="utf-8").splitlines():
s = line.strip()
if s:
ips.append(s)
return ips
def preflight(ip: str) -> tuple[bool, str]:
"""접속/인증/권한 간단 점검: getsysinfo 호출."""
cmd = [RACADM, "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo"]
try:
p = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", shell=False, timeout=30)
if p.returncode != 0:
return False, p.stderr.strip() or p.stdout.strip()
return True, ""
except Exception as e:
return False, str(e)
def safe_xml_path(src: Path) -> Path:
"""
racadm이 경로의 공백/한글을 싫어하는 문제 회피:
임시 폴더에 ASCII 파일명으로 복사해서 사용.
"""
tmp_dir = Path(tempfile.gettempdir()) / "idrac_xml"
tmp_dir.mkdir(parents=True, exist_ok=True)
# ASCII, 공백 제거 파일명
dst_name = "config_" + str(int(time.time())) + ".xml"
dst = tmp_dir / dst_name
shutil.copy2(src, dst)
return dst
def apply_xml(ip: str, xml_path: Path) -> tuple[bool, str]:
"""
racadm XML 적용. 벤더/세대에 따라
'config -f' 또는 'set -t xml -f' 를 씁니다.
일반적으로 최신 iDRAC은 config -f 가 잘 동작합니다.
"""
# 1) 공백/한글 제거된 임시 경로 준비
safe_path = safe_xml_path(xml_path)
# 2) 명령 조립(리스트 인자, shell=False)
# 필요 시 아래 둘 중 하나만 사용하세요.
cmd = [RACADM, "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "set", "-t", "xml", "-f", str(safe_path)]
# 대안:
# cmd = [RACADM, "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "set", "-t", "xml", "-f", str(safe_path)]
logging.info(f"실행 명령(리스트) → {' '.join(cmd)}")
try:
p = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", shell=False, timeout=180)
stdout = (p.stdout or "").strip()
stderr = (p.stderr or "").strip()
if p.returncode != 0:
msg = f"racadm 실패 (rc={p.returncode})\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}"
return False, msg
# 일부 버전은 성공해도 stdout만 출력하고 rc=0
return True, stdout or "성공"
except Exception as e:
return False, f"예외: {e}"
finally:
# 임시 XML 제거(필요 시 보관하려면 주석처리)
try:
safe_path.unlink(missing_ok=True)
except Exception:
pass
def main():
if len(sys.argv) != 3:
logging.error("Usage: python 02-set_config.py <ip_file> <xml_file>")
sys.exit(1)
ip_file = Path(sys.argv[1])
xml_file = Path(sys.argv[2])
if not ip_file.is_file():
logging.error("IP 파일을 찾을 수 없습니다: %s", ip_file)
sys.exit(2)
if not xml_file.is_file():
logging.error("XML 파일을 찾을 수 없습니다: %s", xml_file)
sys.exit(3)
ips = read_ip_list(ip_file)
if not ips:
logging.error("IP 목록이 비어있습니다.")
sys.exit(4)
start = time.time()
for ip in ips:
logging.info("%s에 XML 파일 '%s' 설정 적용 중...", ip, xml_file)
ok, why = preflight(ip)
if not ok:
logging.error("%s 사전 점검(getsysinfo) 실패: %s", ip, why)
continue
ok, msg = apply_xml(ip, xml_file)
if ok:
logging.info("%s 설정 성공: %s", ip, msg)
else:
logging.error("%s 설정 실패\n%s", ip, msg)
el = int(time.time() - start)
logging.info("전체 설정 소요 시간: %d 시간, %d 분, %d 초.", el // 3600, (el % 3600) // 60, el % 60)
if __name__ == "__main__":
main()
+105
View File
@@ -0,0 +1,105 @@
import os
import subprocess
import time
import logging
from dotenv import load_dotenv
from concurrent.futures import ThreadPoolExecutor
# .env 파일 로드
load_dotenv()
# 로깅 설정
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
# 사용자 이름 및 비밀번호 설정
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
# IP 파일 유효성 검사 함수
def validate_ip_file(ip_file_path):
if not os.path.isfile(ip_file_path):
raise FileNotFoundError(f"IP 파일 '{ip_file_path}' 이(가) 존재하지 않습니다.")
return ip_file_path
# 정보 저장 디렉터리 설정
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 정보를 가져오는 함수
def fetch_idrac_info(idrac_ip):
try:
logger.info(f"{idrac_ip}에 대한 샷시 정보 가져오는 중...")
getsysinfo_command = [
"racadm", "-r", idrac_ip,
"-u", IDRAC_USER,
"-p", IDRAC_PASS,
"getsysinfo"
]
getsysinfo_result = subprocess.run(getsysinfo_command, capture_output=True, text=True)
getsysinfo = getsysinfo_result.stdout
svc_tag = ""
for line in getsysinfo.splitlines():
if "SVC Tag" in line:
svc_tag = line.split('=')[1].strip()
break
if not svc_tag:
logger.error(f"IP: {idrac_ip}에 대한 SVC Tag 가져오기 실패")
return
logger.info(f"{idrac_ip}에 대한 서버 Log 가져오는 중...")
viewerlog_command = [
"racadm", "-r", idrac_ip,
"-u", IDRAC_USER,
"-p", IDRAC_PASS,
"getsel"
]
viewerlog_result = subprocess.run(viewerlog_command, capture_output=True, text=True)
viewerlog = viewerlog_result.stdout
output_file = os.path.join(OUTPUT_DIR, f"{svc_tag}.txt")
with open(output_file, 'w', encoding='utf-8') as f:
f.write(f"Dell Server Log Viewer (SVC Tag: {svc_tag})\n\n")
f.write("------------------------------------------Log Viewer------------------------------------------\n")
f.write(f"1. SVC Tag : {svc_tag}\n")
f.write(f"{viewerlog}\n")
logger.info(f"{idrac_ip}에 대한 로그 정보가 {output_file}에 저장되었습니다.")
except Exception as e:
logger.error(f"설정 중 오류 발생: {e}")
# 소요 시간 계산 함수
def calculate_elapsed_time(start_time):
elapsed_time = time.time() - start_time
hours, remainder = divmod(elapsed_time, 3600)
minutes, seconds = divmod(remainder, 60)
logger.info(f"전체 작업 소요 시간: {int(hours)} 시간, {int(minutes)} 분, {int(seconds)} 초.")
# 메인 함수
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logger.error(f"Usage: {sys.argv[0]} <ip_file>")
sys.exit(1)
ip_file_path = validate_ip_file(sys.argv[1])
with open(ip_file_path, 'r') as ip_file:
ip_addresses = [line.strip() for line in ip_file if line.strip()]
start_time = time.time()
# 병렬 처리를 위해 ThreadPoolExecutor 사용
max_workers = 100 # 병렬 작업 수 설정
with ThreadPoolExecutor(max_workers=max_workers) as executor:
executor.map(fetch_idrac_info, ip_addresses)
calculate_elapsed_time(start_time)
+206
View File
@@ -0,0 +1,206 @@
import subprocess
import sys
import re
import time
from pathlib import Path
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# iDRAC 접속 설정
IDRAC_USER = "root"
IDRAC_PASS = "calvin"
# ─────────────────────────────────────────────────────────────
# 저장 위치 결정
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent
elif here.name.lower() == "scripts":
base = here.parent
else:
base = here.parent
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
# ─────────────────────────────────────────────────────────────
# 유틸리티 함수
def run_racadm(ip, *args):
"""racadm 명령어를 실행하고 결과를 반환합니다."""
cmd = ["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS] + list(args)
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True, timeout=30)
return result.stdout
except Exception as e:
# 에러 발생 시 빈 문자열 반환 (스크립트 중단 방지)
return ""
def extract_value(text, pattern, delimiter='='):
"""텍스트에서 특정 키의 값을 추출하고 공백을 제거합니다."""
for line in text.splitlines():
if re.search(pattern, line, re.IGNORECASE):
if delimiter in line:
# '=' 기준으로 쪼갠 후 값만 가져와서 공백 제거
return line.split(delimiter, 1)[1].strip()
return "N/A"
# ─────────────────────────────────────────────────────────────
# 메인 수집 함수
def fetch_idrac_info(ip, output_dir):
logging.info(f">>> [{ip}] 데이터 수집 시작...")
# 1. 원본 데이터 덩어리 가져오기 (API 호출 최소화)
hwinventory = run_racadm(ip, "hwinventory")
getsysinfo = run_racadm(ip, "getsysinfo")
sys_profile = run_racadm(ip, "get", "bios.SysProfileSettings")
proc_settings = run_racadm(ip, "get", "bios.ProcSettings")
mem_settings = run_racadm(ip, "get", "bios.MemSettings")
storage_ctrl = run_racadm(ip, "get", "STORAGE.Controller.1")
# 서비스 태그 확인 (파일명 결정용)
svc_tag = extract_value(getsysinfo, "SVC Tag")
if svc_tag == "N/A":
logging.error(f"!!! [{ip}] SVC Tag 추출 실패. 작업을 건너뜁니다.")
return
output_file = output_dir / f"{svc_tag}.txt"
with open(output_file, "w", encoding="utf-8") as f:
# 헤더 작성
f.write(f"Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: {svc_tag})\n\n")
# --- 1. Firmware Version 정보 ---
f.write("-" * 42 + " Firmware Version 정보 " + "-" * 42 + "\n")
f.write(f"1. SVC Tag : {svc_tag}\n")
f.write(f"2. Bios Firmware : {extract_value(getsysinfo, 'System BIOS Version')}\n")
f.write(f"3. iDRAC Firmware Version : {extract_value(getsysinfo, 'Firmware Version')}\n")
# NIC 펌웨어 (별도 호출 필요)
nic1 = run_racadm(ip, "get", "NIC.FrmwImgMenu.1")
nic5 = run_racadm(ip, "get", "NIC.FrmwImgMenu.5")
f.write(f"4. NIC Integrated Firmware Version : {extract_value(nic1, '#FamilyVersion')}\n")
f.write(f"5. OnBoard NIC Firmware Version : {extract_value(nic5, '#FamilyVersion')}\n")
f.write(f"6. Raid Controller Firmware Version : {extract_value(hwinventory, 'ControllerFirmwareVersion')}\n\n")
# --- 2. Bios 설정 정보 ---
f.write("-" * 45 + " Bios 설정 정보 " + "-" * 45 + "\n")
boot_mode = run_racadm(ip, "get", "bios.BiosBootSettings")
f.write(f"01. Bios Boot Mode : {extract_value(boot_mode, 'BootMode')}\n")
f.write(f"02. System Profile Settings - System Profile : {extract_value(sys_profile, 'SysProfile=')}\n")
f.write(f"03. System Profile Settings - CPU Power Management : {extract_value(sys_profile, 'ProcPwrPerf')}\n")
f.write(f"04. System Profile Settings - Memory Frequency : {extract_value(sys_profile, 'MemFrequency')}\n")
f.write(f"05. System Profile Settings - Turbo Boost : {extract_value(sys_profile, 'ProcTurboMode')}\n")
f.write(f"06. System Profile Settings - PCI ASPM L1 Link Power Management : {extract_value(sys_profile, 'PcieAspmL1')}\n")
f.write(f"07. System Profile Settings - C-States : {extract_value(sys_profile, 'ProcCStates')}\n")
f.write(f"08. System Profile Settings - Determinism Slider : {extract_value(sys_profile, 'DeterminismSlider')}\n")
f.write(f"08-2. System Profile Settings - Dynamic Link Width Management (DLWM) : {extract_value(sys_profile, 'DynamicLinkWidthManagement')}\n")
f.write(f"09. Processor Settings - Logical Processor : {extract_value(proc_settings, 'LogicalProc')}\n")
f.write(f"10. Processor Settings - Virtualization Technology : {extract_value(proc_settings, 'ProcVirtualization')}\n")
f.write(f"11. Processor Settings - NUMA Nodes Per Socket : {extract_value(proc_settings, 'NumaNodesPerSocket')}\n")
f.write(f"12. Processor Settings - x2APIC Mode : {extract_value(proc_settings, 'ProcX2Apic')}\n")
f.write(f"13. Memory Settings - Dram Refresh Delay : {extract_value(mem_settings, 'DramRefreshDelay')}\n")
f.write(f"14. Memory Settings - DIMM Self Healing (PPR) : {extract_value(mem_settings, 'PPROnUCE')}\n")
f.write(f"15. Memory Settings - Correctable Error Logging : {extract_value(mem_settings, 'CECriticalSEL')}\n")
thermal = run_racadm(ip, "get", "System.ThermalSettings")
f.write(f"16. System Settings - Thermal Profile Optimization : {extract_value(thermal, 'ThermalProfile')}\n")
integrated = run_racadm(ip, "get", "Bios.IntegratedDevices")
f.write(f"17. Integrated Devices Settings - SR-IOV Global Enable : {extract_value(integrated, 'SriovGlobalEnable')}\n")
misc = run_racadm(ip, "get", "bios.MiscSettings")
f.write(f"18. Miscellaneous Settings - F1/F2 Prompt on Error : {extract_value(misc, 'ErrPrompt')}\n\n")
# --- 3. iDRAC 설정 정보 ---
f.write("-" * 45 + " iDRAC 설정 정보 " + "-" * 45 + "\n")
f.write(f"01. iDRAC Settings - Timezone : {extract_value(run_racadm(ip, 'get', 'iDRAC.Time.Timezone'), 'Timezone')}\n")
f.write(f"02. iDRAC Settings - IPMI LAN Selection : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentNIC'), 'ActiveNIC')}\n")
f.write(f"03. iDRAC Settings - IPMI IP(IPv4) : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentIPv4'), 'DHCPEnable')}\n")
f.write(f"04. iDRAC Settings - IPMI IP(IPv6) : {extract_value(run_racadm(ip, 'get', 'iDRAC.CurrentIPv6'), 'Enable=')}\n")
f.write(f"05. iDRAC Settings - Redfish Support : {extract_value(run_racadm(ip, 'get', 'iDRAC.Redfish.Enable'), 'Enable=')}\n")
f.write(f"06. iDRAC Settings - SSH Support : {extract_value(run_racadm(ip, 'get', 'iDRAC.SSH'), 'Enable=')}\n")
# AD 관련 설정 (AD Group 1, 2 포함)
f.write(f"07. iDRAC Settings - AD User Domain Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.USERDomain.1.Name'), 'Name')}\n")
f.write(f"08. iDRAC Settings - SC Server Address : {extract_value(run_racadm(ip, 'get', 'iDRAC.ActiveDirectory.DomainController1'), 'DomainController1')}\n")
f.write(f"09. iDRAC Settings - SE AD RoleGroup Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Name'), 'Name')}\n")
f.write(f"10. iDRAC Settings - SE AD RoleGroup Domain : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Domain'), 'Domain')}\n")
f.write(f"11. iDRAC Settings - SE AD RoleGroup Privilege : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.1.Privilege'), 'Privilege')}\n")
f.write(f"12. iDRAC Settings - IDC AD RoleGroup Name : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Name'), 'Name')}\n")
f.write(f"13. iDRAC Settings - IDC AD RoleGroup Domain : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Domain'), 'Domain')}\n")
f.write(f"14. iDRAC Settings - IDC AD RoleGroup Privilege : {extract_value(run_racadm(ip, 'get', 'iDRAC.ADGroup.2.Privilege'), 'Privilege')}\n")
# Syslog 및 기타
f.write(f"15. iDRAC Settings - Remote Log (syslog) : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.SysLogEnable'), 'SysLogEnable')}\n")
f.write(f"16. iDRAC Settings - syslog server address 1 : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Server1'), 'Server1')}\n")
f.write(f"17. iDRAC Settings - syslog server address 2 : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Server2'), 'Server2')}\n")
f.write(f"18. iDRAC Settings - syslog server port : {extract_value(run_racadm(ip, 'get', 'iDRAC.SysLog.Port'), 'Port')}\n")
f.write(f"19. iDRAC Settings - Remote KVM Nonsecure port : {extract_value(run_racadm(ip, 'get', 'iDRAC.VirtualConsole.Port'), 'Port')}\n\n")
# --- 4. Raid 설정 정보 (주석 해제 버전) ---
f.write("-" * 45 + " Raid 설정 정보 " + "-" * 45 + "\n")
f.write(f"01. RAID Settings - Raid ProductName : {extract_value(hwinventory, 'ProductName = PERC')}\n")
f.write(f"02. RAID Settings - Raid Types : {extract_value(hwinventory, 'RAIDTypes')}\n")
f.write(f"03. RAID Settings - StripeSize : {extract_value(hwinventory, 'StripeSize')}\n")
f.write(f"04. RAID Settings - ReadCachePolicy : {extract_value(hwinventory, 'ReadCachePolicy')}\n")
f.write(f"05. RAID Settings - WriteCachePolicy : {extract_value(hwinventory, 'WriteCachePolicy')}\n")
f.write(f"06. RAID Settings - CheckConsistencyRate : {extract_value(storage_ctrl, 'CheckConsistencyRate')}\n")
f.write(f"07. RAID Settings - PatrolReadMode : {extract_value(storage_ctrl, 'PatrolReadMode')}\n")
f.write(f"08. RAID Settings - period : 168h\n")
f.write(f"09. RAID Settings - Power Save : No\n")
f.write(f"10. RAID Settings - JBODMODE : Controller does not support JBOD\n")
f.write(f"11. RAID Settings - maxconcurrentpd : 240\n")
logging.info(f"✅ [{ip}] 저장 완료: {output_file.name}")
# ─────────────────────────────────────────────────────────────
# 실행 흐름 제어
def main():
if len(sys.argv) < 2:
logging.error(f"사용법: python {sys.argv[0]} <ip_file>")
sys.exit(1)
ip_file = Path(sys.argv[1])
if not ip_file.exists():
logging.error(f"오류: IP 파일 '{ip_file}'이 존재하지 않습니다.")
sys.exit(1)
output_dir = resolve_output_dir()
start_time = time.time()
# IP 목록 읽기
with open(ip_file, "r") as f:
ips = [line.strip() for line in f if line.strip()]
# 각 IP별로 수집 실행
for ip in ips:
try:
fetch_idrac_info(ip, output_dir)
except Exception as e:
logging.error(f"❌ [{ip}] 처리 중 예외 발생: {e}")
# 소요 시간 계산
end_time = time.time()
elapsed = end_time - start_time
hours, rem = divmod(elapsed, 3600)
minutes, seconds = divmod(rem, 60)
logging.info("="*50)
logging.info("정보 수집 완료.")
logging.info(f"총 소요 시간: {int(hours)}시간 {int(minutes)}{int(seconds)}")
logging.info(f"저장 경로: {output_dir}")
logging.info("="*50)
if __name__ == "__main__":
main()
+167
View File
@@ -0,0 +1,167 @@
import os
import re
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging
from dotenv import load_dotenv, find_dotenv
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# .env 자동 탐색 로드 (현재 파일 기준 상위 디렉터리까지 검색)
load_dotenv(find_dotenv())
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
def resolve_output_dir() -> Path:
"""
실행 위치와 무관하게 결과를 data/idrac_info 밑으로 저장.
- 스크립트가 data/scripts/ 에 있다면 → data/idrac_info
- 그 외 위치라도 → (스크립트 상위 폴더)/idrac_info
"""
here = Path(__file__).resolve().parent # .../data/scripts 또는 다른 폴더
# case 1: .../data/scripts → data/idrac_info
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent # data
# case 2: .../scripts (상위가 data가 아닐 때도 상위 폴더를 base로 사용)
elif here.name.lower() == "scripts":
base = here.parent
# case 3: 일반적인 경우: 현재 파일의 상위 폴더
else:
base = here.parent
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
def parse_gpu_serials_from_hwinventory(hwinv_text: str) -> dict:
"""
iDRAC hwinventory 전체 텍스트에서 GPU(Video.Slot.*) 블록을 찾아
{FQDD(or InstanceID): SerialNumber} 딕셔너리로 반환.
블록은 빈 줄로 구분되어 있다고 가정.
"""
results = {}
# 빈 줄 기준 블록 분할(여러 개의 개행을 하나의 경계로)
blocks = re.split(r"\n\s*\n", hwinv_text, flags=re.MULTILINE)
for block in blocks:
# GPU(Video) 블록만 처리
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:
# 안전장치: Description에 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
def fetch_idrac_info(idrac_ip: str, output_dir: Path) -> None:
try:
# 서비스 태그 가져오기 (get 제외)
cmd_getsysinfo = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "getsysinfo"
]
getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
svc_tag_match = re.search(r"SVC Tag\s*=\s*(\S+)", getsysinfo)
svc_tag = svc_tag_match.group(1) if svc_tag_match else None
if not svc_tag:
logging.error(f"Failed to retrieve SVC Tag for IP: {idrac_ip}")
return
# 전체 하드웨어 인벤토리
cmd_hwinv = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "hwinventory"
]
hwinv_text = subprocess.getoutput(" ".join(cmd_hwinv))
gpu_map = parse_gpu_serials_from_hwinventory(hwinv_text)
# 결과 저장 파일 (SVC Tag 기준)
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 GPU(Video) inventory found or SerialNumber not present.\n")
return
# 정렬: Video.Slot.<num>-<idx> 의 <num>, <idx> 기준
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)
for fqdd in sorted(gpu_map.keys(), key=slot_key):
serial = gpu_map[fqdd]
f.write(f"{fqdd}: {serial}\n")
# 추가 요약: 시리얼만 세미콜론으로 모아서 한 줄로
serials_only = [gpu_map[k] for k in sorted(gpu_map.keys(), key=slot_key) if gpu_map[k] != "Not Found"]
if serials_only:
f.write(f"SERIALS: {';'.join(serials_only)}\n")
except Exception as e:
logging.error(f"Error processing IP {idrac_ip}: {e}")
def main(ip_file: str) -> None:
ip_path = Path(ip_file)
if not ip_path.is_file():
logging.error(f"IP file {ip_file} does not exist.")
return
output_dir = resolve_output_dir()
with ip_path.open("r", encoding="utf-8") as file:
ip_addresses = [line.strip() for line in file if line.strip()]
# 스레드풀 (동시 100개까지)
with ThreadPoolExecutor(max_workers=100) as executor:
future_to_ip = {executor.submit(fetch_idrac_info, ip, output_dir): ip for ip in ip_addresses}
for future in as_completed(future_to_ip):
ip = future_to_ip[future]
try:
future.result()
logging.info(f"✅ Completed: {ip}")
except Exception as e:
logging.error(f"❌ Error processing {ip}: {e}")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error("Usage: python GPU_Serial_v1.py <ip_file>")
sys.exit(1)
main(sys.argv[1])
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""
레거시 호환성 래퍼 - Intel_Server_info.py
실제 로직은 unified/collect_server_info.py의 'intel_server' 프로파일 사용
"""
import sys
from pathlib import Path
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
from unified.collect_server_info import main as unified_main
if __name__ == "__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()
+174
View File
@@ -0,0 +1,174 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
import subprocess
from pathlib import Path
from typing import Optional
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# 저장 위치: data/scripts 아래에 있다면 → data/idrac_info 생성
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
base = here.parent if here.name.lower() == "scripts" else here
out = base / "temp" / "staging"
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
def find_mac_by_fqdd(fqdd: str, text: str) -> Optional[str]:
"""
swinventory 출력에서 FQDD = {fqdd} 라인을 찾고,
그 주변(±10줄) 내에서 MAC 주소를 추출.
"""
lines = text.splitlines()
for i, line in enumerate(lines):
if f"FQDD = {fqdd}" in line:
for j in range(max(0, i - 10), min(i + 10, len(lines))):
m = re.search(r"([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})", lines[j])
if m:
return m.group(1)
return None
def extract_vendors(hwinventory: str) -> tuple[str, str]:
"""
[InstanceID: DIMM...] 또는 [InstanceID: Disk.Bay...] 블록 내 Manufacturer 추출
"""
# Memory Vendors
mem_vendors = re.findall(
r"\[InstanceID:\s*DIMM[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)",
hwinventory,
flags=re.IGNORECASE
)
mem_vendors = [v.strip() for v in mem_vendors if v.strip()]
memory = ", ".join(sorted(set(mem_vendors))) if mem_vendors else "Not Found"
# SSD Vendors
ssd_vendors = re.findall(
r"\[InstanceID:\s*Disk\.Bay[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)",
hwinventory,
flags=re.IGNORECASE
)
ssd_vendors = [v.strip() for v in ssd_vendors if v.strip()]
ssd = ", ".join(sorted(set(ssd_vendors))) if ssd_vendors else "Not Found"
return memory, ssd
def fetch_idrac_info_one(ip: str, output_dir: Path) -> None:
"""단일 서버 iDRAC 정보 수집"""
logging.info(f"[+] Collecting iDRAC info from {ip} ...")
getsysinfo = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo"])
hwinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "hwinventory"])
swinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "swinventory"])
# 서비스 태그
svc_tag = parse_single_value(r"SVC\s*Tag\s*=\s*(\S+)", getsysinfo)
if not svc_tag:
logging.error(f"[!] Failed to retrieve SVC Tag for IP: {ip}")
return
# iDRAC MAC
idrac_mac = parse_single_value(r"MAC\s*Address\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
# NIC.Integrated / Onboard MAC
integrated_1 = parse_single_value(r"NIC\.Integrated\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
integrated_2 = parse_single_value(r"NIC\.Integrated\.1-2-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
onboard_1 = parse_single_value(r"NIC\.Embedded\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
onboard_2 = parse_single_value(r"NIC\.Embedded\.2-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
# NIC Slot별 MAC
NIC_Slot_2_1 = find_mac_by_fqdd("NIC.Slot.2-1-1", swinventory)
NIC_Slot_2_2 = find_mac_by_fqdd("NIC.Slot.2-2-1", swinventory)
NIC_Slot_3_1 = find_mac_by_fqdd("NIC.Slot.3-1-1", swinventory)
NIC_Slot_3_2 = find_mac_by_fqdd("NIC.Slot.3-2-1", swinventory)
# 메모리 / SSD 제조사
memory, ssd = extract_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")
f.write(f"{integrated_1 or ''}\n")
f.write(f"{integrated_2 or ''}\n")
f.write(f"{onboard_1 or ''}\n")
f.write(f"{onboard_2 or ''}\n")
f.write(f"{NIC_Slot_2_1 or ''}\n")
f.write(f"{NIC_Slot_2_2 or ''}\n")
f.write(f"{idrac_mac or ''}\n")
f.write(f"{memory}\n")
f.write(f"{ssd}\n")
logging.info(f"[✔] {svc_tag} ({ip}) info saved.")
def main(ip_file: str) -> None:
ip_path = Path(ip_file)
if not ip_path.is_file():
logging.error(f"[!] IP file {ip_file} does not exist.")
return
output_dir = resolve_output_dir()
ips = [line.strip() for line in ip_path.read_text(encoding="utf-8").splitlines() if line.strip()]
if not ips:
logging.error("[!] No IP addresses found in the file.")
return
for ip in ips:
try:
fetch_idrac_info_one(ip, output_dir)
except Exception as e:
logging.error(f"[!] Error processing {ip}: {e}")
try:
ip_path.unlink(missing_ok=True)
except Exception:
pass
logging.info("\n정보 수집 완료.")
if __name__ == "__main__":
import sys, time
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
start = time.time()
main(sys.argv[1])
end = time.time()
elapsed = int(end - start)
h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
logging.info(f"수집 완료 시간: {h}시간 {m}{s}")
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
#local set1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.MemSettings.CorrEccSmi Disabled)
#local set2=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.ProcSettings.ProcVirtualization Disabled)
#local set3=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.BiosBootSettings.SetBootOrderEn NIC.PxeDevice.1-1,NIC.PxeDevice.2-1)
#local set4=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.NetworkSettings.PxeDev2EnDis Enabled)
#local set5=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.PxeDev2Settings.PxeDev2Interface NIC.Integrated.1-2-1)
local set6=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.IntegratedDevices.EmbNic1Nic2 Enabled)
#local set7=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.SysProfileSettings.SysProfile Custom)
#local set8=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.SysProfileSettings.DynamicLinkWidthManagement Unforced)
#local set9=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.MiscSettings.ErrPrompt Disabled)
local set10=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS jobqueue create BIOS.Setup.1-1 -r forced)
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
#local set1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.MemSettings.CorrEccSmi Disabled)
#local set2=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.ProcSettings.ProcVirtualization Disabled)
#local set3=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.BiosBootSettings.SetBootOrderEn NIC.PxeDevice.1-1,NIC.PxeDevice.2-1)
#local set4=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.NetworkSettings.PxeDev2EnDis Enabled)
#local set5=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.PxeDev2Settings.PxeDev2Interface NIC.Integrated.1-2-1)
local set6=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.IntegratedDevices.EmbNic1Nic2 DisabledOs)
#local set7=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.SysProfileSettings.SysProfile Custom)
#local set8=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.SysProfileSettings.DynamicLinkWidthManagement Unforced)
#local set9=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set BIOS.MiscSettings.ErrPrompt Disabled)
local set10=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS jobqueue create BIOS.Setup.1-1 -r forced)
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+97
View File
@@ -0,0 +1,97 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
#local swinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS swinventory)
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
#서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
#iDRAC MAC 확인
local idrac_mac=$(echo "$getsysinfo" | grep -i "MAC Address = " | awk -F '=' '{print $2}' | tr -d '[:space:]')
#NIC.Integrated MAC 확인
local Integrated_1=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_2=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-2-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#추가 NIC MAC 확인
#local NIC_Slot_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#local NIC_Slot_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#OnBoard MAC 확인
local Onboard_1=$(echo "$getsysinfo" | grep -P "NIC.Embedded.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Onboard_2=$(echo "$getsysinfo" | grep -P "NIC.Embedded.2-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#파트 벤더 확인
local memory=$(echo "$hwinventory" | grep -A5 "DIMM" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
local nvme_m2=$(echo "$hwinventory" | grep -A3 "Disk.Direct" | grep "Manufacturer" | awk -F '= ' '{print $2}' | sort | uniq)
local ssd=$(echo "$hwinventory" | grep -A3 "Disk.Bay" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
# SVC Tag 확인
echo "$SVC_TAG" >> "$OUTPUT_FILE"
echo "$Integrated_1" >> "$OUTPUT_FILE"
echo "$Integrated_2" >> "$OUTPUT_FILE"
echo "$Onboard_1" >> "$OUTPUT_FILE"
echo "$Onboard_2" >> "$OUTPUT_FILE"
echo "$idrac_mac" >> "$OUTPUT_FILE"
echo "$memory" >> "$OUTPUT_FILE"
echo "$ssd" >> "$OUTPUT_FILE"
#임시파일 제거
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
레거시 호환성 래퍼 - MAC_info.py
실제 로직은 unified/collect_mac.py의 'default' 프로파일 사용
"""
import sys
from pathlib import Path
# unified 스크립트 경로 추가
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
# unified 스크립트 임포트 및 실행
from unified.collect_mac import main as unified_main
if __name__ == "__main__":
# 기존 인터페이스 유지: python MAC_info.py <ip_file>
# 내부적으로 unified 스크립트 호출 (default 프로파일 사용)
if len(sys.argv) < 2:
print("Usage: python MAC_info.py <ip_file>")
sys.exit(1)
# 프로파일 이름 추가
sys.argv.append("default")
unified_main()
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""
레거시 호환성 래퍼 - PortGUID.py
실제 로직은 unified/collect_guid.py의 'default' 프로파일 사용
"""
import sys
from pathlib import Path
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
from unified.collect_guid import main as unified_main
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python PortGUID.py <ip_file>")
sys.exit(1)
# default 프로파일 사용
sys.argv.append("default")
unified_main()
+168
View File
@@ -0,0 +1,168 @@
import os
import re
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
import logging
from dotenv import load_dotenv, find_dotenv
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# .env 자동 탐색 로드 (현재 파일 기준 상위 디렉터리까지 검색)
load_dotenv(find_dotenv())
IDRAC_USER = os.getenv("IDRAC_USER")
IDRAC_PASS = os.getenv("IDRAC_PASS")
def resolve_output_dir() -> Path:
"""
실행 위치와 무관하게 결과를 data/idrac_info 밑으로 저장.
- 스크립트가 data/scripts/ 에 있다면 → data/idrac_info
- 그 외 위치라도 → (스크립트 상위 폴더)/idrac_info
"""
here = Path(__file__).resolve().parent # .../data/scripts 또는 다른 폴더
# case 1: .../data/scripts → data/idrac_info
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent # data
# case 2: .../scripts (상위가 data가 아닐 때도 상위 폴더를 base로 사용)
elif here.name.lower() == "scripts":
base = here.parent
# case 3: 일반적인 경우: 현재 파일의 상위 폴더
else:
base = here.parent
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
def fetch_idrac_info(idrac_ip: str, output_dir: Path) -> None:
try:
# 서비스 태그 가져오기 (get 제외)
cmd_getsysinfo = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "getsysinfo"
]
getsysinfo = subprocess.getoutput(" ".join(cmd_getsysinfo))
svc_tag_match = re.search(r"SVC Tag\s*=\s*(\S+)", getsysinfo)
svc_tag = svc_tag_match.group(1) if svc_tag_match else None
if not svc_tag:
logging.error(f"Failed to retrieve SVC Tag for IP: {idrac_ip}")
return
# InfiniBand.VndrConfigPage 목록 가져오기
cmd_list = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "", "get", "InfiniBand.VndrConfigPage"
]
output_list = subprocess.getoutput(" ".join(cmd_list))
# InfiniBand.VndrConfigPage.<숫자> 및 Key 값 추출
matches = re.findall(
r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
output_list
)
# 결과 저장 파일
output_file = output_dir / f"{svc_tag}.txt"
with output_file.open("w", encoding="utf-8", newline="\n") as f:
# 서비스 태그
f.write(f"{svc_tag}\n")
# --- 슬롯/GUID 수집 후 원하는 순서로 기록 ---
slot_to_guid: dict[str, str] = {}
slots_in_match_order: list[str] = []
# 각 페이지 상세 조회
for number, slot in matches:
cmd_detail = [
"racadm", "-r", idrac_ip, "-u", IDRAC_USER or "", "-p", IDRAC_PASS or "",
"get", f"InfiniBand.VndrConfigPage.{number}"
]
output_detail = subprocess.getoutput(" ".join(cmd_detail))
# PortGUID 추출
match_guid = re.search(r"PortGUID=(\S+)", output_detail)
port_guid = match_guid.group(1) if match_guid else "Not Found"
s = str(slot)
slot_to_guid[s] = port_guid
slots_in_match_order.append(s)
# 검색된 슬롯 개수에 따라 출력 순서 결정
# 환경변수에서 슬롯 우선순위 읽기 (GUIDtxtT0Execl.py와 동일)
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:
# 기본 우선순위 (슬롯 개수에 따라)
total_slots = len(slots_in_match_order)
if total_slots == 4:
desired_order = ['38', '37', '32', '34']
elif total_slots == 10:
desired_order = ['38', '39', '37', '36', '32', '33', '34', '35', '31', '40']
else:
desired_order = slots_in_match_order
logging.info(f"기본 슬롯 우선순위 사용 (슬롯 {total_slots}개): {desired_order}")
# 지정된 순서대로 파일에 기록 + GUID 요약 생성
hex_guid_list: list[str] = []
for s in desired_order:
guid = slot_to_guid.get(s, "Not Found")
f.write(f"Slot.{s}: {guid}\n")
if guid != "Not Found":
hex_guid_list.append(f"0x{guid.replace(':', '').upper()}")
if hex_guid_list:
# GUID 순서: 사용자 지정 순서대로 세미콜론으로 연결
f.write(f"GUID: {';'.join(hex_guid_list)}\n")
logging.info(f"GUID 합치기 완료: {len(hex_guid_list)}개 슬롯")
except Exception as e:
logging.error(f"Error processing IP {idrac_ip}: {e}")
def main(ip_file: str) -> None:
ip_path = Path(ip_file)
if not ip_path.is_file():
logging.error(f"IP file {ip_file} does not exist.")
return
output_dir = resolve_output_dir() # ← 여기서 OS 무관 저장 위치 확정 (data/idrac_info)
# print(f"[debug] output_dir = {output_dir}") # 필요 시 확인
with ip_path.open("r", encoding="utf-8") as file:
ip_addresses = [line.strip() for line in file if line.strip()]
# 스레드풀
with ThreadPoolExecutor(max_workers=100) as executor:
future_to_ip = {executor.submit(fetch_idrac_info, ip, output_dir): ip for ip in ip_addresses}
for future in as_completed(future_to_ip):
ip = future_to_ip[future]
try:
future.result()
logging.info(f"Completed: {ip}")
except Exception as e:
logging.error(f"Error processing {ip}: {e}")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
main(sys.argv[1])
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
import subprocess
from pathlib import Path
from typing import Optional
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# 저장 위치: data/scripts 아래에 있다면 → data/idrac_info 생성
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
base = here.parent if here.name.lower() == "scripts" else here
out = base / "temp" / "staging"
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
def find_mac_by_fqdd(fqdd: str, text: str) -> Optional[str]:
"""
swinventory 출력에서 FQDD = {fqdd} 라인을 찾고,
그 주변(±10줄) 내에서 MAC 주소를 추출.
"""
lines = text.splitlines()
for i, line in enumerate(lines):
if f"FQDD = {fqdd}" in line:
for j in range(max(0, i - 10), min(i + 10, len(lines))):
m = re.search(r"([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})", lines[j])
if m:
return m.group(1)
return None
def extract_vendors(hwinventory: str) -> tuple[str, str]:
"""
[InstanceID: DIMM...] 또는 [InstanceID: Disk.Bay...] 블록 내 Manufacturer 추출
"""
# Memory Vendors
mem_vendors = re.findall(
r"\[InstanceID:\s*DIMM[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)",
hwinventory,
flags=re.IGNORECASE
)
mem_vendors = [v.strip() for v in mem_vendors if v.strip()]
memory = ", ".join(sorted(set(mem_vendors))) if mem_vendors else "Not Found"
# SSD Vendors
ssd_vendors = re.findall(
r"\[InstanceID:\s*Disk\.Bay[^\]]*\][^\[]*?Manufacturer\s*=\s*([^\n\r]+)",
hwinventory,
flags=re.IGNORECASE
)
ssd_vendors = [v.strip() for v in ssd_vendors if v.strip()]
ssd = ", ".join(sorted(set(ssd_vendors))) if ssd_vendors else "Not Found"
return memory, ssd
def fetch_idrac_info_one(ip: str, output_dir: Path) -> None:
"""단일 서버 iDRAC 정보 수집"""
logging.info(f"[+] Collecting iDRAC info from {ip} ...")
getsysinfo = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "getsysinfo"])
hwinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "hwinventory"])
swinventory = run(["racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS, "swinventory"])
# 서비스 태그
svc_tag = parse_single_value(r"SVC\s*Tag\s*=\s*(\S+)", getsysinfo)
if not svc_tag:
logging.error(f"[!] Failed to retrieve SVC Tag for IP: {ip}")
return
# iDRAC MAC
idrac_mac = parse_single_value(r"MAC\s*Address\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
# NIC.Integrated / Onboard MAC
integrated_1 = parse_single_value(r"NIC\.Integrated\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
integrated_2 = parse_single_value(r"NIC\.Integrated\.1-2-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
onboard_1 = parse_single_value(r"NIC\.Embedded\.1-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
onboard_2 = parse_single_value(r"NIC\.Embedded\.2-1-1\s+Ethernet\s*=\s*([0-9A-Fa-f:]{17})", getsysinfo)
# 메모리 / SSD 제조사
memory, ssd = extract_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")
f.write(f"{integrated_1 or ''}\n")
f.write(f"{integrated_2 or ''}\n")
f.write(f"{onboard_1 or ''}\n")
f.write(f"{onboard_2 or ''}\n")
f.write(f"{idrac_mac or ''}\n")
f.write(f"{memory}\n")
f.write(f"{ssd}\n")
logging.info(f"[✔] {svc_tag} ({ip}) info saved.")
def main(ip_file: str) -> None:
ip_path = Path(ip_file)
if not ip_path.is_file():
logging.error(f"[!] IP file {ip_file} does not exist.")
return
output_dir = resolve_output_dir()
ips = [line.strip() for line in ip_path.read_text(encoding="utf-8").splitlines() if line.strip()]
if not ips:
logging.error("[!] No IP addresses found in the file.")
return
for ip in ips:
try:
fetch_idrac_info_one(ip, output_dir)
except Exception as e:
logging.error(f"[!] Error processing {ip}: {e}")
try:
ip_path.unlink(missing_ok=True)
except Exception:
pass
logging.info("\n정보 수집 완료.")
if __name__ == "__main__":
import sys, time
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
start = time.time()
main(sys.argv[1])
end = time.time()
elapsed = int(end - start)
h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
logging.info(f"수집 완료 시간: {h}시간 {m}{s}")
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
local set=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS systemerase cryptographicerasepd)
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+188
View File
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
import re
import subprocess
from pathlib import Path
from typing import Optional
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [INFO] root: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# ─────────────────────────────────────────────────────────────
# 저장 위치 결정
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent
elif here.name.lower() == "scripts":
base = here.parent
else:
base = here.parent
out = base / "temp" / "staging"
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
# ─────────────────────────────────────────────────────────────
# hwinventory 블록 기반 Manufacturer 추출 (핵심 수정 부분)
def extract_manufacturers(hwinventory: str, instance_prefix: str) -> list[str]:
"""
instance_prefix:
- 'DIMM.' → 메모리
- 'Disk.Bay.' → 디스크
"""
vendors = []
blocks = re.split(r"\n\s*\n", hwinventory)
for block in blocks:
if f"[InstanceID: {instance_prefix}" in block:
m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE)
if m:
vendors.append(m.group(1).strip())
return 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
)
# ── Integrated NIC
integrated_1 = parse_single_value(
r"NIC\.Integrated\.1-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
getsysinfo
)
integrated_2 = parse_single_value(
r"NIC\.Integrated\.1-2-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
getsysinfo
)
# ── Embedded NIC
onboard_1 = parse_single_value(
r"NIC\.Embedded\.1-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
getsysinfo
)
onboard_2 = parse_single_value(
r"NIC\.Embedded\.2-1-1.*?([0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5})",
getsysinfo
)
# ─────────────────────────────────────────────
# Memory / Disk Vendor (수정 완료)
mem_vendors = sorted(set(extract_manufacturers(hwinventory, "DIMM.")))
ssd_vendors = sorted(set(extract_manufacturers(hwinventory, "Disk.Bay.")))
memory = "\n".join(mem_vendors)
ssd = "\n".join(ssd_vendors)
# ── 결과 파일 저장
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")
f.write(f"{integrated_1 or ''}\n")
f.write(f"{integrated_2 or ''}\n")
f.write(f"{onboard_1 or ''}\n")
f.write(f"{onboard_2 or ''}\n")
f.write(f"{idrac_mac or ''}\n")
f.write(f"{memory}\n")
f.write(f"{ssd}\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 스크립트 동작 유지
try:
ip_path.unlink(missing_ok=True)
except Exception:
pass
logging.info("정보 수집 완료.")
if __name__ == "__main__":
import sys
import time
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
start = time.time()
main(sys.argv[1])
elapsed = int(time.time() - start)
h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
logging.info(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")
+271
View File
@@ -0,0 +1,271 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
import re
import time
import subprocess
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'
)
# ─────────────────────────────────────────────────────────────
# 저장 위치: 이 파일이 data/scripts/ 아래 있으면 → data/idrac_info
# 그 외여도 이 파일의 상위 폴더에 data/idrac_info 생성
def resolve_output_dir() -> Path:
here = Path(__file__).resolve().parent
# 통상 data/scripts/에 둘 경우 data/idrac_info 로 저장
if here.name.lower() == "scripts" and here.parent.name.lower() == "data":
base = here.parent # data
elif (here / "data").is_dir():
base = here / "data"
else:
base = here # 그래도 현재 기준으로 생성
out = base / "temp" / "staging"
out.mkdir(parents=True, exist_ok=True)
return out
# ─────────────────────────────────────────────────────────────
# 사용자 이름 및 비밀번호 (Bash 스크립트와 동일)
IDRAC_USER = "root"
IDRAC_PASS = "calvin"
def sh(cmd: list[str] | str) -> str:
"""racadm 호출을 간단히 실행 (stdout만 수집)"""
if isinstance(cmd, list):
cmd = " ".join(cmd)
try:
return subprocess.getoutput(cmd)
except Exception:
return ""
def grep_value_line(text: str, key: str, after_eq_strip=True) -> str | None:
"""
Bash에서 `grep -i "<key>" | awk -F '=' '{print $2}' | tr -d '[:space:]'`
에 상응하는 간단 추출기.
"""
# 대소문자 구분 없이 key를 포함하는 라인을 찾음
pat = re.compile(re.escape(key), re.IGNORECASE)
for line in text.splitlines():
if pat.search(line):
if after_eq_strip and "=" in line:
return line.split("=", 1)[1].strip().replace(" ", "")
return line.strip()
return None
def parse_after_eq(text: str, key_regex: str, strip_spaces=True) -> str | None:
"""
정규표현식으로 키를 찾아 '=' 뒤 값을 추출.
ex) key_regex=r"System BIOS Version"
"""
for line in text.splitlines():
# [Key=...] 형식의 줄은 건너뛰기
if line.strip().startswith('[') and line.strip().endswith(']'):
continue
if re.search(key_regex, line, flags=re.IGNORECASE):
if "=" in line:
val = line.split("=", 1)[1]
return val.strip().replace(" ", "") if strip_spaces else val.strip()
return None
def fetch_idrac_info(ip: str, output_dir: Path) -> None:
# 원 스크립트 호출 순서 및 동일 항목 수집
hwinventory = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "hwinventory"])
getsysinfo = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "getsysinfo"])
sys_profile = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.SysProfileSettings"])
proc_settings = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.ProcSettings"])
mem_settings = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.MemSettings"])
storage_ctrl = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "STORAGE.Controller.1"])
svc_tag = parse_after_eq(getsysinfo, r"SVC\s*Tag")
bios_fw = parse_after_eq(getsysinfo, r"System\s*BIOS\s*Version")
idrac_fw = parse_after_eq(getsysinfo, r"Firmware\s*Version")
intel_nic_fw = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "NIC.FrmwImgMenu.1"]),
r"#FamilyVersion")
onboard_nic_fw = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "NIC.FrmwImgMenu.5"]),
r"#FamilyVersion")
raid_fw = parse_after_eq(hwinventory, r"ControllerFirmwareVersion")
bios_boot_mode = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.BiosBootSettings"]),
r"BootMode")
# SysProfileSettings 세부
sysprof_1 = parse_after_eq(sys_profile, r"SysProfile")
sysprof_2 = parse_after_eq(sys_profile, r"EnergyPerformanceBias")
sysprof_3 = parse_after_eq(sys_profile, r"MemFrequency")
sysprof_4 = parse_after_eq(sys_profile, r"ProcTurboMode")
sysprof_5 = parse_after_eq(sys_profile, r"ProcC1E")
sysprof_6 = parse_after_eq(sys_profile, r"ProcCStates")
sysprof_7 = parse_after_eq(sys_profile, r"MonitorMwait")
# ProcessorSettings
proc_1 = parse_after_eq(proc_settings, r"LogicalProc")
proc_2 = parse_after_eq(proc_settings, r"ProcVirtualization")
proc_3 = parse_after_eq(proc_settings, r"LlcPrefetch")
proc_4 = parse_after_eq(proc_settings, r"ProcX2Apic")
# MemorySettings
mem_1 = parse_after_eq(mem_settings, r"NodeInterleave")
mem_2 = parse_after_eq(mem_settings, r"PPROnUCE")
mem_3 = parse_after_eq(mem_settings, r"CECriticalSEL")
# System.ThermalSettings
system_thermal = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "System.ThermalSettings"])
system_1 = parse_after_eq(system_thermal, r"ThermalProfile", strip_spaces=False)
# Bios.IntegratedDevices
integ_devs = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "Bios.IntegratedDevices"])
integ_1 = parse_after_eq(integ_devs, r"SriovGlobalEnable")
# bios.MiscSettings
misc = sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "bios.MiscSettings"])
misc_1 = parse_after_eq(misc, r"ErrPrompt")
# iDRAC.* 다양한 설정
tz = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.Time.Timezone"]),
r"Timezone")
active_nic = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentNIC"]),
r"ActiveNIC")
ipv4_dhcp = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentIPv4"]),
r"DHCPEnable")
ipv6_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.CurrentIPv6"]),
r"Enable")
redfish_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.Redfish.Enable"]),
r"Enable")
ssh_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SSH"]),
r"Enable")
ad_user_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.USERDomain.1.Name"]),
r"Name")
ad_dc1 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ActiveDirectory.DomainController1"]),
r"DomainController1")
ad_grp1_name = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Name"]),
r"Name")
ad_grp1_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Domain"]),
r"Domain")
ad_grp1_priv = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.1.Privilege"]),
r"Privilege")
ad_grp2_name = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Name"]),
r"Name")
ad_grp2_domain = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Domain"]),
r"Domain")
ad_grp2_priv = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.ADGroup.2.Privilege"]),
r"Privilege")
syslog_enable = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.SysLogEnable"]),
r"SysLogEnable")
syslog_s1 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Server1"]),
r"Server1")
syslog_s2 = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Server2"]),
r"Server2")
syslog_port = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.SysLog.Port"]),
r"Port")
kvm_port = parse_after_eq(sh(["racadm", f"-r{ip}", f"-u{IDRAC_USER}", f"-p{IDRAC_PASS}", "get", "iDRAC.VirtualConsole.Port"]),
r"Port")
if not svc_tag:
logging.error(f"Failed to retrieve SVC Tag for IP: {ip}")
return
out_file = output_dir / f"{svc_tag}.txt"
with out_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")
f.write("------------------------------------------Firware Version 정보------------------------------------------\n")
f.write(f"1. SVC Tag : {svc_tag}\n")
f.write(f"2. Bios Firmware : {bios_fw or ''}\n")
f.write(f"3. iDRAC Firmware Version : {idrac_fw or ''}\n")
f.write(f"4. NIC Integrated Firmware Version : {intel_nic_fw or ''}\n")
f.write(f"5. OnBoard NIC Firmware Version : {onboard_nic_fw or ''}\n")
f.write(f"6. Raid Controller Firmware Version : {raid_fw or ''}\n\n")
f.write("---------------------------------------------Bios 설정 정보----------------------------------------------\n")
f.write(f"01. Bios Boot Mode : {bios_boot_mode or ''}\n")
f.write(f"02. System Profile Settings - System Profile : {sysprof_1 or ''}\n")
f.write(f"03. System Profile Settings - CPU Power Management : {sysprof_2 or ''}\n")
f.write(f"04. System Profile Settings - Memory Frequency : {sysprof_3 or ''}\n")
f.write(f"05. System Profile Settings - Turbo Boost : {sysprof_4 or ''}\n")
f.write(f"06. System Profile Settings - C1E : {sysprof_5 or ''}\n")
f.write(f"07. System Profile Settings - C-States : {sysprof_6 or ''}\n")
f.write(f"08. System Profile Settings - Monitor/Mwait : {sysprof_7 or ''}\n")
f.write(f"09. Processor Settings - Logical Processor : {proc_1 or ''}\n")
f.write(f"10. Processor Settings - Virtualization Technology : {proc_2 or ''}\n")
f.write(f"11. Processor Settings - LLC Prefetch : {proc_3 or ''}\n")
f.write(f"12. Processor Settings - x2APIC Mode : {proc_4 or ''}\n")
f.write(f"13. Memory Settings - Node Interleaving : {mem_1 or ''}\n")
f.write(f"14. Memory Settings - DIMM Self Healing (Post Package Repair) on Uncorrectable Memory Error : {mem_2 or ''}\n")
f.write(f"15. Memory Settings - Correctable Error Logging : {mem_3 or ''}\n")
f.write(f"16. System Settings - Thermal Profile Optimization : {system_1 or ''}\n")
f.write(f"17. Integrated Devices Settings - SR-IOV Global Enable : {integ_1 or ''}\n")
f.write(f"18. Miscellaneous Settings - F1/F2 Prompt on Error : {misc_1 or ''}\n\n")
f.write("---------------------------------------------iDRAC 설정 정보----------------------------------------------\n")
f.write(f"01. iDRAC Settings - Timezone : {tz or ''}\n")
f.write(f"02. iDRAC Settings - IPMI LAN Selection : {active_nic or ''}\n")
f.write(f"03. iDRAC Settings - IPMI IP(IPv4) : {ipv4_dhcp or ''}\n")
f.write(f"04. iDRAC Settings - IPMI IP(IPv6) : {ipv6_enable or ''}\n")
f.write(f"05. iDRAC Settings - Redfish Support : {redfish_enable or ''}\n")
f.write(f"06. iDRAC Settings - SSH Support : {ssh_enable or ''}\n")
f.write(f"07. iDRAC Settings - AD User Domain Name : {ad_user_domain or ''}\n")
f.write(f"08. iDRAC Settings - SC Server Address : {ad_dc1 or ''}\n")
f.write(f"09. iDRAC Settings - SE AD RoleGroup Name : {ad_grp1_name or ''}\n")
f.write(f"10. iDRAC Settings - SE AD RoleGroup Dome인 : {ad_grp1_domain or ''}\n")
f.write(f"11. iDRAC Settings - SE AD RoleGroup Privilege : {ad_grp1_priv or ''}\n")
f.write(f"12. iDRAC Settings - IDC AD RoleGroup Name : {ad_grp2_name or ''}\n")
f.write(f"13. iDRAC Settings - IDC AD RoleGroup Domain : {ad_grp2_domain or ''}\n")
f.write(f"14. iDRAC Settings - IDC AD RoleGroup Privilege : {ad_grp2_priv or ''}\n")
f.write(f"15. iDRAC Settings - Remote Log (syslog) : {syslog_enable or ''}\n")
f.write(f"16. iDRAC Settings - syslog server address 1 : {syslog_s1 or ''}\n")
f.write(f"17. iDRAC Settings - syslog server address 2 : {syslog_s2 or ''}\n")
f.write(f"18. iDRAC Settings - syslog server port : {syslog_port or ''}\n")
f.write(f"19. iDRAC Settings - Remote KVM Nonsecure port : {kvm_port or ''}\n")
def main(ip_file: str) -> None:
ip_path = Path(ip_file)
if not ip_path.is_file():
logging.error(f"Usage: python script.py <ip_file>\nIP file {ip_file} does not exist.")
return
output_dir = resolve_output_dir()
# 원 Bash는 cat $IP_FILE → 파일 전체를 하나의 IP처럼 사용(=실질적으로 "한 줄 IP")
# 여기서는 첫 번째 비어있지 않은 줄만 사용해 동일하게 동작시킵니다.
lines = [ln.strip() for ln in ip_path.read_text(encoding="utf-8", errors="ignore").splitlines()]
ip = next((ln for ln in lines if ln), None)
if not ip:
logging.error("No IP address found in the file.")
return
start = time.time()
fetch_idrac_info(ip, output_dir)
end = time.time()
# 입력 파일 삭제 (원 Bash의 rm -f $IP_FILE와 동일)
try:
ip_path.unlink(missing_ok=True)
except Exception:
pass
elapsed = int(end - start)
h, m, s = elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60
logging.info("정보 수집 완료.")
logging.info(f"수집 완료 시간: {h} 시간, {m} 분, {s} 초.")
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
logging.error("Usage: python script.py <ip_file>")
sys.exit(1)
main(sys.argv[1])
+98
View File
@@ -0,0 +1,98 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
#local swinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS swinventory)
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
#서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
#iDRAC MAC 확인
local idrac_mac=$(echo "$getsysinfo" | grep -i "MAC Address = " | awk -F '=' '{print $2}' | tr -d '[:space:]')
#NIC.Integrated MAC 확인
local Integrated_1=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_2=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-2-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#추가 NIC MAC 확인
#local NIC_Slot_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#local NIC_Slot_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#OnBoard MAC 확인
local Onboard_1=$(echo "$getsysinfo" | grep -P "NIC.Embedded.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Onboard_2=$(echo "$getsysinfo" | grep -P "NIC.Embedded.2-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#파트 벤더 확인
local memory=$(echo "$hwinventory" | grep -A5 "DIMM" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
local nvme_m2=$(echo "$hwinventory" | grep -A3 "Disk.Direct" | grep "Manufacturer" | awk -F '= ' '{print $2}' | sort | uniq)
local ssd=$(echo "$hwinventory" | grep -A3 "Disk.Bay" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
# SVC Tag 확인
echo "$SVC_TAG" >> "$OUTPUT_FILE"
echo "$Integrated_1" >> "$OUTPUT_FILE"
echo "$Integrated_2" >> "$OUTPUT_FILE"
echo "$Onboard_1" >> "$OUTPUT_FILE"
echo "$Onboard_2" >> "$OUTPUT_FILE"
echo "$idrac_mac" >> "$OUTPUT_FILE"
echo "$memory" >> "$OUTPUT_FILE"
echo "$nvme_m2" >> "$OUTPUT_FILE"
echo "$ssd" >> "$OUTPUT_FILE"
#임시파일 제거
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+314
View File
@@ -0,0 +1,314 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
# 모든 샷시 정보 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
# 모든 SysProfileSettings 저장
local SysProfileSettings=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get bios.SysProfileSettings)
# ProcessorSettings 저장
local ProcessorSettings=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get bios.ProcSettings)
# Memory Settings 저장
local MemorySettings=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get bios.MemSettings)
# Raid Settings 저장
local STORAGEController=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get STORAGE.Controller.1)
# 서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Bios Firmware Version 확인
local Bios_firmware=$(echo "$getsysinfo" | grep -i "System BIOS Version" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Firmware Version 확인
local iDRAC_firmware=$(echo "$getsysinfo" | grep -i "Firmware Version" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Intel NIC Firmware Version 확인
local Intel_NIC_firmware=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get NIC.FrmwImgMenu.1 | grep -i "#FamilyVersion" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# OnBoard NIC Firmware Version 확인
local Onboard_NIC_firmware=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get NIC.FrmwImgMenu.5 | grep -i "#FamilyVersion" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# R/C Firmware Version 확인
local Raid_firmware=$(echo "$hwinventory" | grep -i "ControllerFirmwareVersion" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Bios 설정 Boot Mode 확인
local Bios_BootMode=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get bios.BiosBootSettings | grep -i "BootMode" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Bios SysProfileSettings 설정 정보 확인
local SysProFileSettings_info1=$(echo "$SysProfileSettings" | grep -i "SysProfile=" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info2=$(echo "$SysProfileSettings" | grep -i "EnergyPerformanceBias" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info3=$(echo "$SysProfileSettings" | grep -i "MemFrequency" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info4=$(echo "$SysProfileSettings" | grep -i "ProcTurboMode" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info5=$(echo "$SysProfileSettings" | grep -i "ProcC1E" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info6=$(echo "$SysProfileSettings" | grep -i "ProcCStates" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local SysProFileSettings_info7=$(echo "$SysProfileSettings" | grep -i "MonitorMwait" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Processor Settings - Logical Processor
local ProcessorSettings_info1=$(echo "$ProcessorSettings" | grep -i "LogicalProc" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local ProcessorSettings_info2=$(echo "$ProcessorSettings" | grep -i "ProcVirtualization" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local ProcessorSettings_info3=$(echo "$ProcessorSettings" | grep -i "LlcPrefetch" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local ProcessorSettings_info4=$(echo "$ProcessorSettings" | grep -i "ProcX2Apic" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Memory Settings - Node Interleaving
local MemorySettings_info1=$(echo "$MemorySettings" | grep -i "NodeInterleave" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local MemorySettings_info2=$(echo "$MemorySettings" | grep -i "PPROnUCE" | awk -F '=' '{print $2}' | tr -d '[:space:]')
local MemorySettings_info3=$(echo "$MemorySettings" | grep -i "CECriticalSEL" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# System Settings - Thermal Profile Optimization
local SystemSettings_info1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get System.ThermalSettings | grep -i "ThermalProfile" | awk -F '=' '{print $2}')
# Integrated Devices Settings - SR-IOV Global Enable
local IntegratedDevicesSettings_info1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get Bios.IntegratedDevices | grep -i "SriovGlobalEnable" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# Miscellaneous Settings - F1/F2 Prompt on Error
local IMiscellaneousSettings_info1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get bios.MiscSettings | grep -i "ErrPrompt" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - Timezone
local iDRAC_Settings_info1=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.Time.Timezone | grep -i "Timezone" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IPMI LAN Selection
local iDRAC_Settings_info2=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.CurrentNIC | grep -i "ActiveNIC" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IPMI IP(IPv4)
local iDRAC_Settings_info3=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.CurrentIPv4 | grep -i "DHCPEnable" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IPMI IP(IPv6)
local iDRAC_Settings_info4=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.CurrentIPv6 | grep -i "Enable=" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - Redfish Support
local iDRAC_Settings_info5=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.Redfish.Enable | grep -i "Enable=" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - SSH Support
local iDRAC_Settings_info6=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.SSH | grep -i "Enable=" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - AD User Domain Name
local iDRAC_Settings_info7=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.USERDomain.1.Name | grep -i "Name" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - SC Server Address
local iDRAC_Settings_info8=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ActiveDirectory.DomainController1 | grep -i "DomainController1" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - SE AD RoleGroup Name
local iDRAC_Settings_info9=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.1.Name | grep -i "Name" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - SE AD RoleGroup Dome인
local iDRAC_Settings_info10=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.1.Domain | grep -i "Domain" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - SE AD RoleGroup Privilege
local iDRAC_Settings_info11=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.1.Privilege | grep -i "Privilege" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IDC AD RoleGroup name
local iDRAC_Settings_info12=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.2.Name | grep -i "Name" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IDC AD RoleGroup Dome인
local iDRAC_Settings_info13=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.2.Domain | grep -i "Domain" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - IDC AD RoleGroup Privilege
local iDRAC_Settings_info14=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.ADGroup.2.Privilege | grep -i "Privilege" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - Remote Log (syslog)
local iDRAC_Settings_info15=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.SysLog.SysLogEnable | grep -i "SysLogEnable" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - syslog server address 1
local iDRAC_Settings_info16=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.SysLog.Server1 | grep -i "Server1" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - syslog server address 2
local iDRAC_Settings_info17=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.SysLog.Server2 | grep -i "Server2" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - syslog server port
local iDRAC_Settings_info18=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.SysLog.Port | grep -i "Port" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# iDRAC Settings - VirtualConsole Port
local iDRAC_Settings_info19=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS get iDRAC.VirtualConsole.Port | grep -i "Port" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - ProductName
local RAID_info0=$(echo "$hwinventory" | grep -i "ProductName = PERC" | awk -F '=' '{print $2}')
# RAID Settings - ProductName
local RAID_info7=$(echo "$hwinventory" | grep -i "ProductName = BOSS" | awk -F '=' '{print $2}')
# RAID Settings - RAIDType
local RAID_info1=$(echo "$hwinventory" | grep -i "RAIDTypes" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - StripeSize
local RAID_info2=$(echo "$hwinventory" | grep -i "StripeSize" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - ReadCachePolicy
local RAID_info3=$(echo "$hwinventory" | grep -i "ReadCachePolicy" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - WriteCachePolicy
local RAID_info4=$(echo "$hwinventory" | grep -i "WriteCachePolicy" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - CheckConsistencyMode
local RAID_info5=$(echo "$STORAGEController" | grep -i "CheckConsistencyMode" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# RAID Settings - PatrolReadRate
local RAID_info6=$(echo "$STORAGEController" | grep -i "PatrolReadRate" | awk -F '=' '{print $2}' | tr -d '[:space:]')
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
echo "Dell EMC Server Bios,iDRAC,R/C Setting (SVC Tag: $SVC_TAG)" | tee -a "$OUTPUT_FILE"
echo -e "\n" >> "$OUTPUT_FILE"
echo "------------------------------------------Firware Version 정보------------------------------------------" >> "$OUTPUT_FILE"
# SVC Tag 확인
echo "1. SVC Tag : $SVC_TAG" >> "$OUTPUT_FILE"
# Bios Firmware Version 확인
echo "2. Bios Firmware : $Bios_firmware" >> "$OUTPUT_FILE"
# iDRAC Firmware Version 확인
echo "3. iDRAC Firmware Version : $iDRAC_firmware" >> "$OUTPUT_FILE"
# Intel NIC Firmware Version 확인
echo "4. NIC Integrated Firmware Version : $Intel_NIC_firmware" >> "$OUTPUT_FILE"
# OnBoard NIC Firmware Version 확인
echo "5. OnBoard NIC Firmware Version : $Onboard_NIC_firmware" >> "$OUTPUT_FILE"
# Raid Controller Firmware Version 확인
echo "6. Raid Controller Firmware Version : $Raid_firmware" >> "$OUTPUT_FILE"
echo -e "\n" >> "$OUTPUT_FILE"
echo "---------------------------------------------Bios 설정 정보----------------------------------------------" >> "$OUTPUT_FILE"
# bios Boot Mode 확인
echo "01. Bios Boot Mode : $Bios_BootMode" >> "$OUTPUT_FILE"
# SysProfileSettings - System Profile
echo "02. System Profile Settings - System Profile : $SysProFileSettings_info1" >> "$OUTPUT_FILE"
# SysProfileSettings - CPU Power Management
echo "03. System Profile Settings - CPU Power Management : $SysProFileSettings_info2" >> "$OUTPUT_FILE"
# SysProfileSettings - Memory Frequency
echo "04. System Profile Settings - Memory Frequency : $SysProFileSettings_info3" >> "$OUTPUT_FILE"
# SysProfileSettings - Turbo Boost
echo "05. System Profile Settings - Turbo Boost : $SysProFileSettings_info4" >> "$OUTPUT_FILE"
# SysProfileSettings - C1E
echo "06. System Profile Settings - C1E : $SysProFileSettings_info5" >> "$OUTPUT_FILE"
# SysProfileSettings - C-States
echo "07. System Profile Settings - C-States : $SysProFileSettings_info6" >> "$OUTPUT_FILE"
# SysProfileSettings - C-States
echo "08. System Profile Settings - Monitor/Mwait : $SysProFileSettings_info7" >> "$OUTPUT_FILE"
# Processor Settings - Logical Processor
echo "09. Processor Settings - Logical Processor : $ProcessorSettings_info1" >> "$OUTPUT_FILE"
# Processor Settings - Virtualization Technology
echo "10. Processor Settings - Virtualization Technology : $ProcessorSettings_info2" >> "$OUTPUT_FILE"
# Processor Settings - LLC Prefetch
echo "11. Processor Settings - LLC Prefetch : $ProcessorSettings_info3" >> "$OUTPUT_FILE"
# Processor Settings - x2APIC Mode
echo "12. Processor Settings - x2APIC Mode : $ProcessorSettings_info4" >> "$OUTPUT_FILE"
# Memory Settings - Node Interleaving
echo "13. Memory Settings - Node Interleaving : $MemorySettings_info1" >> "$OUTPUT_FILE"
# Memory Settings - DIMM Self Healing (Post Package Repair) on Uncorrectable Memory Error
echo "14. Memory Settings - DIMM Self Healing (Post Package Repair) on Uncorrectable Memory Error : $MemorySettings_info2" >> "$OUTPUT_FILE"
# Memory Settings - Correctable Error Logging
echo "15. Memory Settings - Correctable Error Logging : $MemorySettings_info3" >> "$OUTPUT_FILE"
# System Settings - Thermal Profile Optimization
echo "16. System Settings - Thermal Profile Optimization : $SystemSettings_info1" >> "$OUTPUT_FILE"
# Integrated Devices Settings - SR-IOV Global Enable
echo "17. Integrated Devices Settings - SR-IOV Global Enable : $IntegratedDevicesSettings_info1" >> "$OUTPUT_FILE"
# Miscellaneous Settings - F1/F2 Prompt on Error
echo "18. Miscellaneous Settings - F1/F2 Prompt on Error : $IMiscellaneousSettings_info1" >> "$OUTPUT_FILE"
echo -e "\n" >> "$OUTPUT_FILE"
echo "---------------------------------------------iDRAC 설정 정보----------------------------------------------" >> "$OUTPUT_FILE"
# iDRAC Settings - Timezone
echo "01. iDRAC Settings - Timezone : $iDRAC_Settings_info1" >> "$OUTPUT_FILE"
# iDRAC Settings - IPMI LAN Selection
echo "02. iDRAC Settings - IPMI LAN Selection : $iDRAC_Settings_info2" >> "$OUTPUT_FILE"
# iDRAC Settings - IPMI IP(IPv4)
echo "03. iDRAC Settings - IPMI IP(IPv4) : $iDRAC_Settings_info3" >> "$OUTPUT_FILE"
# iDRAC Settings - IPMI IP(IPv6)
echo "04. iDRAC Settings - IPMI IP(IPv6) : $iDRAC_Settings_info4" >> "$OUTPUT_FILE"
# iDRAC Settings - Redfish Support
echo "05. iDRAC Settings - Redfish Support : $iDRAC_Settings_info5" >> "$OUTPUT_FILE"
# iDRAC Settings - SSH Support
echo "06. iDRAC Settings - SSH Support : $iDRAC_Settings_info6" >> "$OUTPUT_FILE"
# iDRAC Settings - AD User Domain Name
echo "07. iDRAC Settings - AD User Domain Name : $iDRAC_Settings_info7" >> "$OUTPUT_FILE"
# iDRAC Settings - SC Server Address
echo "08. iDRAC Settings - SC Server Address : $iDRAC_Settings_info8" >> "$OUTPUT_FILE"
# iDRAC Settings - SE AD RoleGroup Name
echo "09. iDRAC Settings - SE AD RoleGroup Name : $iDRAC_Settings_info9" >> "$OUTPUT_FILE"
# iDRAC Settings - SE AD RoleGroup Dome인
echo "10. iDRAC Settings - SE AD RoleGroup Dome인 : $iDRAC_Settings_info10" >> "$OUTPUT_FILE"
# iDRAC Settings - SE AD RoleGroup Privilege
echo "11. iDRAC Settings - SE AD RoleGroup Privilege : $iDRAC_Settings_info11" >> "$OUTPUT_FILE"
# iDRAC Settings - SE IDC RoleGroup Name
echo "12. iDRAC Settings - IDC AD RoleGroup Name : $iDRAC_Settings_info12" >> "$OUTPUT_FILE"
# iDRAC Settings - SE IDC RoleGroup Dome인
echo "13. iDRAC Settings - IDC AD RoleGroup Domain : $iDRAC_Settings_info13" >> "$OUTPUT_FILE"
# iDRAC Settings - SE IDC RoleGroup Dome인
echo "14. iDRAC Settings - IDC AD RoleGroup Privilege : $iDRAC_Settings_info14" >> "$OUTPUT_FILE"
# iDRAC Settings - Remote Log (syslog)
echo "15. iDRAC Settings - Remote Log (syslog) : $iDRAC_Settings_info15" >> "$OUTPUT_FILE"
# iDRAC Settings - Remote Log (syslog)
echo "16. iDRAC Settings - syslog server address 1 : $iDRAC_Settings_info16" >> "$OUTPUT_FILE"
# iDRAC Settings - Remote Log (syslog)
echo "17. iDRAC Settings - syslog server address 2 : $iDRAC_Settings_info17" >> "$OUTPUT_FILE"
# iDRAC Settings - syslog server port
echo "18. iDRAC Settings - syslog server port : $iDRAC_Settings_info18" >> "$OUTPUT_FILE"
# iDRAC Settings - Remote KVM Nonsecure port
echo "19. iDRAC Settings - Remote KVM Nonsecure port : $iDRAC_Settings_info19" >> "$OUTPUT_FILE"
echo -e "\n" >> "$OUTPUT_FILE"
echo "---------------------------------------------Raid 설정 정보----------------------------------------------" >> "$OUTPUT_FILE"
# RAID Settings - Raid Types
echo "01. RAID Settings - Raid ProductName : $RAID_info7, $RAID_info0" >> "$OUTPUT_FILE"
# RAID Settings - Raid Types
echo "02. RAID Settings - Raid Typest(NVMe) : $RAID_info1" >> "$OUTPUT_FILE"
# RAID Settings - StripeSize
echo "03. RAID Settings - StripeSize(NVMe) : $RAID_info2" >> "$OUTPUT_FILE"
# RAID Settings - ReadCachePolicy
echo "04. RAID Settings - ReadCachePolicy(NVMe) : $RAID_info3" >> "$OUTPUT_FILE"
# RAID Settings - ReadCachePolicy
echo "05. RAID Settings - WriteCachePolicy(NVMe) : $RAID_info4" >> "$OUTPUT_FILE"
# RAID Settings - CheckConsistencyMode
echo "06. RAID Settings - CheckConsistencyMode : $RAID_info5" >> "$OUTPUT_FILE"
# RAID Settings - PatrolReadMode
echo "07. RAID Settings - PatrolReadRate : $RAID_info6" >> "$OUTPUT_FILE"
# RAID Settings - period
echo "08. RAID Settings - period : 168h" >> "$OUTPUT_FILE"
# RAID Settings - Power Save
echo "09. RAID Settings - Power Save : No" >> "$OUTPUT_FILE"
# RAID Settings - JBODMODE
echo "10. RAID Settings - JBODMODE : Controller does not support JBOD" >> "$OUTPUT_FILE"
# RAID Settings - maxconcurrentpd
echo "11. RAID Settings - maxconcurrentpd : 240" >> "$OUTPUT_FILE"
# 임시 파일 삭제
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+98
View File
@@ -0,0 +1,98 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
#local swinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS swinventory)
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
#서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
#iDRAC MAC 확인
local idrac_mac=$(echo "$getsysinfo" | grep -i "MAC Address = " | awk -F '=' '{print $2}' | tr -d '[:space:]')
#NIC.Integrated MAC 확인
local Integrated_1=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_2=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-2-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#추가 NIC MAC 확인
#local NIC_Slot_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#local NIC_Slot_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.1-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#OnBoard MAC 확인
local Onboard_1=$(echo "$getsysinfo" | grep -P "NIC.Embedded.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Onboard_2=$(echo "$getsysinfo" | grep -P "NIC.Embedded.2-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#파트 벤더 확인
local memory=$(echo "$hwinventory" | grep -A5 "DIMM" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
local nvme_m2=$(echo "$hwinventory" | grep -A3 "Disk.Direct" | grep "Manufacturer" | awk -F '= ' '{print $2}' | sort | uniq)
local ssd=$(echo "$hwinventory" | grep -A3 "Disk.Bay" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
# SVC Tag 확인
echo "$SVC_TAG" >> "$OUTPUT_FILE"
echo "$Integrated_1" >> "$OUTPUT_FILE"
echo "$Integrated_2" >> "$OUTPUT_FILE"
echo "$Onboard_1" >> "$OUTPUT_FILE"
echo "$Onboard_2" >> "$OUTPUT_FILE"
echo "$idrac_mac" >> "$OUTPUT_FILE"
echo "$memory" >> "$OUTPUT_FILE"
echo "$nvme_m2" >> "$OUTPUT_FILE"
echo "$ssd" >> "$OUTPUT_FILE"
#임시파일 제거
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+120
View File
@@ -0,0 +1,120 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
local swinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS swinventory)
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
#서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
#iDRAC MAC 확인
local idrac_mac=$(echo "$getsysinfo" | grep -i "MAC Address = " | awk -F '=' '{print $2}' | tr -d '[:space:]')
#NIC.Integrated MAC 확인
local Integrated_1=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_2=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-2-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_3=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-3-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_4=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-4-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#NIC.Slot.31 MAC 확인
local NICslot31_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.31-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local NICslot31_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.31-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#NIC.Slot.40 MAC 확인
local NICslot40_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.40-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local NICslot40_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.40-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#추가 NIC MAC 확인
local InfiniBand_Slot_32=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.32-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_34=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.34-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_37=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.37-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_38=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.38-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#OnBoard MAC 확인
local Onboard_1=$(echo "$getsysinfo" | grep -P "NIC.Embedded.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Onboard_2=$(echo "$getsysinfo" | grep -P "NIC.Embedded.2-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#파트 벤더 확인
local memory=$(echo "$hwinventory" | grep -A5 "DIMM" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
local ssd=$(echo "$hwinventory" | grep -A3 "Disk.Bay" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq)
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
# 정보저장
echo "$SVC_TAG" >> "$OUTPUT_FILE"
echo "$Integrated_1" >> "$OUTPUT_FILE"
echo "$Integrated_2" >> "$OUTPUT_FILE"
echo "$Integrated_3" >> "$OUTPUT_FILE"
echo "$Integrated_4" >> "$OUTPUT_FILE"
echo "$Onboard_1" >> "$OUTPUT_FILE"
echo "$Onboard_2" >> "$OUTPUT_FILE"
echo "$NICslot31_1" >> "$OUTPUT_FILE"
echo "$NICslot31_2" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_38" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_37" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_32" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_34" >> "$OUTPUT_FILE"
echo "$NICslot40_1" >> "$OUTPUT_FILE"
echo "$NICslot40_2" >> "$OUTPUT_FILE"
echo "$idrac_mac" >> "$OUTPUT_FILE"
echo "$memory" >> "$OUTPUT_FILE"
echo "$ssd" >> "$OUTPUT_FILE"
#임시파일 제거
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
레거시 호환성 래퍼 - XE9680_H200_IB_10EA_MAC_info.py
실제 로직은 unified/collect_mac.py의 'xe9680_h200_10ea' 프로파일 사용
"""
import sys
from pathlib import Path
script_dir = Path(__file__).parent
sys.path.insert(0, str(script_dir))
from unified.collect_mac import main as unified_main
if __name__ == "__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()
@@ -0,0 +1,123 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# 모든 hwinventory 저장
local getsysinfo=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS getsysinfo)
local swinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS swinventory)
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS hwinventory)
#서비스 태그 가져오기
local SVC_TAG=$(echo "$getsysinfo" | grep -i "SVC Tag" | awk -F '=' '{print $2}' | tr -d '[:space:]')
#iDRAC MAC 확인
local idrac_mac=$(echo "$getsysinfo" | grep -i "MAC Address = " | awk -F '=' '{print $2}' | tr -d '[:space:]')
#NIC.Integrated MAC 확인
local Integrated_1=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_2=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-2-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_3=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-3-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Integrated_4=$(echo "$getsysinfo" | grep -i "NIC.Integrated.1-4-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#NIC.Slot.31 MAC 확인
#local NICslot31_1=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.31-1-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#local NICslot31_2=$(echo "$swinventory" | awk '/FQDD = NIC.Slot.31-2-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#추가 NIC MAC 확인
local InfiniBand_Slot_31=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.31-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_32=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.32-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_33=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.33-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_34=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.34-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_35=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.35-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_36=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.36-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_37=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.37-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_38=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.38-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_39=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.39-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local InfiniBand_Slot_40=$(echo "$swinventory" | awk '/FQDD = InfiniBand.Slot.40-1/ {print x; next} {x=$0}' | tail -1 | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#OnBoard MAC 확인
local Onboard_1=$(echo "$getsysinfo" | grep -P "NIC.Embedded.1-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
local Onboard_2=$(echo "$getsysinfo" | grep -P "NIC.Embedded.2-1-1" | grep -o -E "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
#파트 벤더 확인
local memory=$(echo "$hwinventory" | grep -A5 "DIMM" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq | cut -c1)
local ssd=$(echo "$hwinventory" | grep -A3 "Disk.Bay" | grep "Manufacturer" | awk -F '=' '{print $2}' | awk '{$1=$1};1' | sort | uniq)
# 서비스 태그가 존재하는지 확인
if [ -z "$SVC_TAG" ]; then
echo "Failed to retrieve SVC Tag for IP: $IDRAC_IP"
return
fi
local OUTPUT_FILE="$OUTPUT_DIR/$SVC_TAG.txt"
# 정보저장
echo "$SVC_TAG" >> "$OUTPUT_FILE"
echo "$Integrated_1" >> "$OUTPUT_FILE"
echo "$Integrated_2" >> "$OUTPUT_FILE"
echo "$Integrated_3" >> "$OUTPUT_FILE"
echo "$Integrated_4" >> "$OUTPUT_FILE"
echo "$Onboard_1" >> "$OUTPUT_FILE"
echo "$Onboard_2" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_38" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_39" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_37" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_36" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_32" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_33" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_34" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_35" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_31" >> "$OUTPUT_FILE"
echo "$InfiniBand_Slot_40" >> "$OUTPUT_FILE"
echo "$idrac_mac" >> "$OUTPUT_FILE"
echo "$memory" >> "$OUTPUT_FILE"
echo "$ssd" >> "$OUTPUT_FILE"
#임시파일 제거
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "정보 수집 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+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()
+250
View File
@@ -0,0 +1,250 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import concurrent.futures as futures
import os
import re
import subprocess
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'
)
# ===== 설정: 기본 계정/비밀번호 (환경변수로 덮어쓰기 가능) =====
IDRAC_USER = os.getenv("IDRAC_USER", "root")
IDRAC_PASS = os.getenv("IDRAC_PASS", "calvin")
# 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}")
# --- 유틸: 명령 실행 ---
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} 초.")
if __name__ == "__main__":
main()
+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()
+297
View File
@@ -0,0 +1,297 @@
"""
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, instance_must_contain: str = None) -> List[str]:
"""
컴포넌트 벤더 추출
Args:
hwinventory: hwinventory 명령 출력
component_prefix: 컴포넌트 접두사 ("DIMM", "Disk.Bay", "BOSS" 등)
instance_must_contain: InstanceID/FQDD에 반드시 포함돼야 할 문자열 (예: "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
matched_id = None
# Check InstanceID header: [InstanceID: DIMM.Socket.A1]
if f"[InstanceID: {component_prefix}" in block:
is_target_component = True
m_inst = re.search(r"\[InstanceID:\s*([^\]]+)\]", block)
if m_inst:
matched_id = m_inst.group(1).strip()
# 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
matched_id = m_fqdd.group(1).strip()
if not is_target_component:
continue
# instance_must_contain 필터 적용
if instance_must_contain and matched_id and instance_must_contain not in matched_id:
continue
m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE)
if m:
vendors.add(m.group(1).strip())
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
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set storage.Controller.1.PatrolReadRate 9 )
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS jobqueue create RAID.Slot.2-1 -r pwrcycle)
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
# 사용자 이름 및 비밀번호 설정
IDRAC_USER="root"
IDRAC_PASS="calvin"
# IP 주소 파일 경로 인자 받기
if [ -z "$1" ]; then
echo "Usage: $0 <ip_file>"
exit 1
fi
IP_FILE=$1
if [ ! -f "$IP_FILE" ]; then
echo "IP file $IP_FILE does not exist."
exit 1
fi
# 정보 저장 디렉터리 설정
OUTPUT_DIR="$(dirname "$0")/../temp/staging"
mkdir -p $OUTPUT_DIR
# iDRAC 정보를 가져오는 함수 정의
fetch_idrac_info() {
local IDRAC_IP=$(cat $IP_FILE)
# DellEMC Server
local hwinventory=$(racadm -r $IDRAC_IP -u $IDRAC_USER -p $IDRAC_PASS set iDRAC.VirtualConsole.Port 25513)
rm -f $IP_FILE
}
export -f fetch_idrac_info
export IDRAC_USER
export IDRAC_PASS
export OUTPUT_DIR
# 시작 시간 기록
START_TIME=$(date +%s)
# IP 목록 파일을 읽어 병렬로 작업 수행
fetch_idrac_info
# 종료 시간 기록
END_TIME=$(date +%s)
# 소요 시간 계산
ELAPSED_TIME=$(($END_TIME - $START_TIME))
ELAPSED_HOURS=$(($ELAPSED_TIME / 3600))
ELAPSED_MINUTES=$((($ELAPSED_TIME % 3600) / 60))
ELAPSED_SECONDS=$(($ELAPSED_TIME % 60))
echo "설정 완료."
echo "수집 완료 시간: $ELAPSED_HOURS 시간, $ELAPSED_MINUTES 분, $ELAPSED_SECONDS 초."
+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()
+191
View File
@@ -0,0 +1,191 @@
#!/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 = []
boss_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 제외)
regular_vendors = set()
for prefix in ["Disk.Bay.", "Disk.M.2.", "Disk.Slot."]:
regular_vendors.update(parser.extract_vendors(hwinventory, prefix))
disk_vendors = sorted(regular_vendors)
# BOSS 디스크 벤더 별도 수집 (Disk.Direct. 중 BOSS 포함 항목만)
boss_disk_vendors = parser.extract_vendors(hwinventory, "Disk.Direct.", instance_must_contain="BOSS")
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")
for vendor in boss_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()
+239
View File
@@ -0,0 +1,239 @@
#!/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
import re
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import Optional
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")
TSR_SHARE_URL = os.getenv("TSR_SHARE_URL", "//10.10.3.15/share/")
OME_USER = os.getenv("OME_USER", "")
OME_PASS = os.getenv("OME_PASS", "")
TSR_JOB_TIMEOUT = int(os.getenv("TSR_JOB_TIMEOUT", "900"))
TSR_JOB_POLL_INTERVAL = int(os.getenv("TSR_JOB_POLL_INTERVAL", "10"))
TSR_SAVE_WORKERS = int(os.getenv("TSR_SAVE_WORKERS", "30"))
# -----------------------------------------------------------------------------
# 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):
return [
"racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS,
"techsupreport", "export", "-l", TSR_SHARE_URL, "-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 redact_cmd(cmd):
redacted = []
hide_next = False
for part in cmd:
if hide_next:
redacted.append("***")
hide_next = False
continue
redacted.append(part)
if part == "-p":
hide_next = True
return redacted
def extract_job_id(output: str) -> Optional[str]:
match = re.search(r"Job ID\s*=\s*(JID_\d+)", output or "")
return match.group(1) if match else None
def wait_for_job(ip: str, job_id: str) -> bool:
deadline = time.time() + TSR_JOB_TIMEOUT
view_cmd = [
"racadm", "-r", ip, "-u", IDRAC_USER, "-p", IDRAC_PASS,
"jobqueue", "view", "-i", job_id
]
while time.time() < deadline:
result = subprocess.run(
view_cmd,
capture_output=True,
text=True,
timeout=120
)
output = "\n".join(part for part in [result.stdout.strip(), result.stderr.strip()] if part)
logger.info(f"[{ip}] Job {job_id} status:\n{output}")
if "Status=Completed" in output:
return True
if "Status=Failed" in output:
return False
time.sleep(TSR_JOB_POLL_INTERVAL)
logger.error(f"[{ip}] Job {job_id} status check timed out after {TSR_JOB_TIMEOUT} seconds.")
return False
def execute_action(ip: str, action_name: str) -> bool:
"""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 False
cmd = cmd_func(ip)
logger.info(f"[{ip}] Executing {action_name}...")
if action_name == "tsr_save":
logger.info(f"[{ip}] Command: {redact_cmd(cmd)}")
# Run command
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=600 # Generous timeout for TSR operations
)
output = "\n".join(part for part in [result.stdout.strip(), result.stderr.strip()] if part)
if result.returncode == 0:
logger.info(f"[{ip}] Success: {output}")
if action_name == "tsr_save":
job_id = extract_job_id(output)
if not job_id:
logger.error(f"[{ip}] TSR Save did not return a Job ID.")
return False
return wait_for_job(ip, job_id)
return True
else:
logger.error(f"[{ip}] Failed: {output}")
return False
except Exception as e:
logger.error(f"[{ip}] Exception: {e}")
return False
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()
success_count = 0
failed_count = 0
if action == "tsr_save":
max_workers = max(1, min(TSR_SAVE_WORKERS, len(ips)))
logger.info(f"TSR Save parallel workers={max_workers}")
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(execute_action, ip, action) for ip in ips]
for future in futures:
if future.result():
success_count += 1
else:
failed_count += 1
else:
# 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:
if future.result():
success_count += 1
else:
failed_count += 1
elapsed = time.time() - start_time
logger.info(
f"Completed '{action}' in {elapsed:.2f} seconds. "
f"success={success_count}, failed={failed_count}"
)
if failed_count:
sys.exit(1)
if __name__ == "__main__":
main()