Files
iDRAC_Info/data/scripts/core/parsers.py
T
unknown 40150cd66d update
2026-03-21 07:26:38 +09:00

292 lines
10 KiB
Python

"""
racadm 출력 파싱 유틸리티
"""
import re
from typing import List, Optional, Dict, Tuple
class InfoParser:
"""racadm 명령 출력 파싱 유틸리티"""
# Word boundary to ensure we don't match partial GUIDs (which are longer)
MAC_PATTERN = re.compile(r"\b([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}\b")
@staticmethod
def extract_service_tag(sysinfo: str) -> Optional[str]:
"""
서비스 태그 추출
Args:
sysinfo: getsysinfo 명령 출력
Returns:
서비스 태그 또는 None
"""
m = re.search(r"SVC\s*Tag\s*=\s*(\S+)", sysinfo, re.IGNORECASE)
return m.group(1).strip() if m else None
@staticmethod
def extract_nic_macs(sysinfo: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
"""
NIC MAC 주소 추출
Args:
sysinfo: getsysinfo 명령 출력
nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Embedded.1-1-1"])
Returns:
{pattern: mac_address} 딕셔너리
"""
results = {}
for pattern in nic_patterns:
for line in sysinfo.splitlines():
if pattern in line:
mac = InfoParser.MAC_PATTERN.search(line)
results[pattern] = mac.group(0) if mac else None
break
else:
results[pattern] = None
return results
@staticmethod
def extract_nic_macs_from_hwinventory(hwinventory: str, nic_patterns: List[str]) -> Dict[str, Optional[str]]:
"""
hwinventory에서 NIC MAC 주소 추출
Args:
hwinventory: hwinventory 명령 출력
nic_patterns: NIC 패턴 리스트 (예: ["NIC.Integrated.1-1-1", "NIC.Slot.1-1-1"])
Returns:
{pattern: mac_address} 딕셔너리
"""
results = {pattern: None for pattern in nic_patterns}
# hwinventory를 블록 단위로 분리 (빈 줄로 구분)
blocks = re.split(r"\n\s*\n", hwinventory)
for block in blocks:
# 블록 내에서 FQDD 또는 InstanceID 확인
fqdd = None
# FQDD = NIC.Integrated.1-1-1 또는 [InstanceID: NIC.Integrated.1-1-1] 형식 찾기
m_fqdd = re.search(r"FQDD\s*=\s*(\S+)", block, re.IGNORECASE)
if m_fqdd:
fqdd = m_fqdd.group(1).strip()
# FQDD 키워드가 없을 경우를 대비해 InstanceID 헤더도 확인
elif "[InstanceID:" in block:
m_inst = re.search(r"InstanceID:\s*([^\]]+)", block)
if m_inst:
fqdd = m_inst.group(1).strip()
if not fqdd:
continue
# 요청된 패턴 중 하나라도 포함하는지 확인 (정확한 매칭을 위해 수정 가능하나, 보통 포함 관계면 충분)
# 여기서는 패턴이 FQDD와 정확히 일치하거나 FQDD가 패턴을 포함하는 경우를 찾음
matched_pattern = None
for pattern in nic_patterns:
# 대소문자 무시 비교
if pattern.lower() == fqdd.lower():
matched_pattern = pattern
break
# 정확히 일치하는 패턴을 못 찾았다면 다음 블록으로
if not matched_pattern:
continue
# MAC 주소 찾기
# Ethernet Address = AA:BB:CC:DD:EE:FF
# CurrentMACAddress = AA:BB:CC:DD:EE:FF
# MAC Address = AA:BB:CC:DD:EE:FF
mac_match = InfoParser.MAC_PATTERN.search(block)
if mac_match:
results[matched_pattern] = mac_match.group(0)
return results
@staticmethod
def extract_idrac_mac(sysinfo: str) -> Optional[str]:
"""
iDRAC MAC 주소 추출
Args:
sysinfo: getsysinfo 명령 출력
Returns:
iDRAC MAC 주소 또는 None
"""
for line in sysinfo.splitlines():
if "MAC Address" in line:
mac = InfoParser.MAC_PATTERN.search(line)
if mac:
return mac.group(0)
return None
@staticmethod
def extract_vendors(hwinventory: str, component_prefix: str) -> List[str]:
"""
컴포넌트 벤더 추출
Args:
hwinventory: hwinventory 명령 출력
component_prefix: 컴포넌트 접두사 ("DIMM", "Disk.Bay", "BOSS" 등)
Returns:
벤더 리스트 (중복 제거, 정렬됨)
"""
vendors = set()
blocks = re.split(r"\n\s*\n", hwinventory)
for block in blocks:
# Check if this block is for the target component using InstanceID or FQDD
is_target_component = False
# Check InstanceID header: [InstanceID: DIMM.Socket.A1]
if f"[InstanceID: {component_prefix}" in block:
is_target_component = True
# Also check FQDD line if InstanceID header check fails (fallback)
elif not is_target_component:
m_fqdd = re.search(r"^FQDD\s*=\s*(\S+)", block, re.MULTILINE)
if m_fqdd and component_prefix in m_fqdd.group(1):
is_target_component = True
if is_target_component:
m = re.search(r"Manufacturer\s*=\s*(.+)", block, re.IGNORECASE)
if m:
vendor = m.group(1).strip()
# Filter out "Dell Inc." if user considers it wrong/noise for these components?
# The user specifically complained about "Dell Inc." being WRONG.
# However, if the DIMM *is* Dell, we should report it.
# But the likely cause of "Dell Inc." appearing when it shouldn't is the System block match.
# With this stricter check, the System block (InstanceID: System.Embedded.1) will NOT match "DIMM".
vendors.add(vendor)
return sorted(vendors)
@staticmethod
def extract_infiniband_page_map(pages_output: str) -> List[Tuple[str, str]]:
"""
InfiniBand 페이지 번호와 슬롯 번호 매핑 추출
Args:
pages_output: get InfiniBand.VndrConfigPage 출력
Returns:
[(page_number, slot_number), ...] 리스트
"""
# InfiniBand.VndrConfigPage.1 [Key=InfiniBand.Slot.38-1#VndrConfigPage]
matches = re.findall(
r"InfiniBand\.VndrConfigPage\.(\d+)\s+\[Key=InfiniBand\.Slot\.(\d+)-\d+#VndrConfigPage]",
pages_output
)
return matches
@staticmethod
def extract_port_guid(page_detail: str) -> Optional[str]:
"""
InfiniBand 페이지 상세에서 PortGUID 추출
Args:
page_detail: get InfiniBand.VndrConfigPage.{N} 출력
Returns:
PortGUID 값 또는 None
"""
m = re.search(r"PortGUID=(\S+)", page_detail)
return m.group(1) if m else None
@staticmethod
def extract_value_by_pattern(text: str, pattern: str) -> Optional[str]:
"""
패턴으로 값 추출 (= 뒤의 값)
Args:
text: 검색할 텍스트
pattern: 검색 패턴
Returns:
추출된 값 또는 None
"""
for line in text.splitlines():
if pattern.lower() in line.lower():
if '=' in line:
return line.split('=', 1)[1].strip()
return None
@staticmethod
def extract_infiniband_macs_from_swinventory(swinventory: str, target_slots: List[int]) -> Dict[int, str]:
"""
swinventory에서 InfiniBand 슬롯의 MAC 주소 추출
(FQDD 라인 바로 윗줄에 MAC 주소가 있다는 가정 - 레거시 스크립트 로직)
Args:
swinventory: swinventory 명령 출력
target_slots: 추출할 슬롯 번호 리스트
Returns:
{slot: mac_address} 딕셔너리
"""
results = {}
prev_line = ""
# 슬롯별 패턴 미리 생성 (예: "FQDD = InfiniBand.Slot.31-1")
slot_patterns = {slot: f"FQDD = InfiniBand.Slot.{slot}-1" for slot in target_slots}
for line in swinventory.splitlines():
for slot, pattern in slot_patterns.items():
if pattern in line:
# 이전 라인에서 MAC 찾기
mac = InfoParser.MAC_PATTERN.search(prev_line)
if mac:
results[slot] = mac.group(0)
prev_line = line
return results
@staticmethod
def extract_gpu_serials(hwinventory: str) -> Dict[str, str]:
"""
iDRAC hwinventory 전체 텍스트에서 GPU(Video.Slot.*) 블록을 찾아
{FQDD(or InstanceID): SerialNumber} 딕셔너리로 반환.
Args:
hwinventory: hwinventory 명령 출력
Returns:
{fqdd: serial} 딕셔너리
"""
results = {}
# 빈 줄 기준 블록 분할(여러 개의 개행을 하나의 경계로)
blocks = re.split(r"\n\s*\n", hwinventory, flags=re.MULTILINE)
for block in blocks:
# GPU(Video) 블록만 처리
# Video.Slot.X 또는 유사 패턴
if not re.search(r"\[?InstanceID:\s*Video\.Slot\.", block):
continue
# FQDD 우선, 없으면 [InstanceID: ...]에서 추출
fqdd = None
m_fqdd = re.search(r"^FQDD\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
if m_fqdd:
fqdd = m_fqdd.group(1).strip()
else:
m_hdr = re.search(r"\[InstanceID:\s*(Video\.Slot\.[^\]\r\n]+)\]", block)
if m_hdr:
fqdd = m_hdr.group(1).strip()
if not fqdd:
continue
# SerialNumber 추출
m_sn = re.search(r"^SerialNumber\s*=\s*([^\r\n]+)", block, flags=re.MULTILINE)
serial = m_sn.group(1).strip() if m_sn else "Not Found"
results[fqdd] = serial
return results