Update to debug paths
This commit is contained in:
@@ -50,6 +50,7 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import ssl
|
||||
@@ -154,34 +155,53 @@ class Summary:
|
||||
|
||||
_CANDIDATES: dict[str, list[str]] = {
|
||||
"smb_shares": [
|
||||
# SCALE 24.04+ – combined plugin file; shares are under "sharing_smb_query"
|
||||
"ixdiagnose/plugins/smb/smb_info.json",
|
||||
# Older SCALE layouts
|
||||
"ixdiagnose/plugins/SMB/sharing.smb.query.json",
|
||||
"ixdiagnose/plugins/Sharing/sharing.smb.query.json",
|
||||
"ixdiagnose/SMB/sharing.smb.query.json",
|
||||
# CORE / freenas-debug
|
||||
"freenas-debug/sharing/smb.json",
|
||||
"sharing/smb.json",
|
||||
"middleware/sharing.smb.query.json",
|
||||
],
|
||||
"nfs_shares": [
|
||||
# SCALE 24.04+ – combined plugin file; shares are under "sharing_nfs_query"
|
||||
"ixdiagnose/plugins/nfs/nfs_config.json",
|
||||
# Older SCALE layouts
|
||||
"ixdiagnose/plugins/NFS/sharing.nfs.query.json",
|
||||
"ixdiagnose/plugins/Sharing/sharing.nfs.query.json",
|
||||
"ixdiagnose/NFS/sharing.nfs.query.json",
|
||||
# CORE / freenas-debug
|
||||
"freenas-debug/sharing/nfs.json",
|
||||
"sharing/nfs.json",
|
||||
"middleware/sharing.nfs.query.json",
|
||||
],
|
||||
"smb_config": [
|
||||
# SCALE 24.04+ – combined plugin file; config is under "smb_config"
|
||||
"ixdiagnose/plugins/smb/smb_info.json",
|
||||
# Older SCALE layouts
|
||||
"ixdiagnose/plugins/SMB/smb.config.json",
|
||||
"ixdiagnose/SMB/smb.config.json",
|
||||
# CORE / freenas-debug
|
||||
"freenas-debug/SMB/smb_config.json",
|
||||
"middleware/smb.config.json",
|
||||
],
|
||||
}
|
||||
|
||||
# When a candidate file bundles multiple datasets, pull out the right sub-key.
|
||||
_KEY_WITHIN_FILE: dict[str, str] = {
|
||||
"smb_shares": "sharing_smb_query",
|
||||
"nfs_shares": "sharing_nfs_query",
|
||||
"smb_config": "smb_config",
|
||||
}
|
||||
|
||||
# Keyword fragments for heuristic fallback scan
|
||||
_KEYWORDS: dict[str, list[str]] = {
|
||||
"smb_shares": ["sharing.smb", "smb_share", "sharing/smb"],
|
||||
"nfs_shares": ["sharing.nfs", "nfs_share", "sharing/nfs"],
|
||||
"smb_config": ["smb.config", "smb_config"],
|
||||
"smb_shares": ["sharing.smb", "smb_share", "sharing/smb", "smb_info"],
|
||||
"nfs_shares": ["sharing.nfs", "nfs_share", "sharing/nfs", "nfs_config"],
|
||||
"smb_config": ["smb.config", "smb_config", "smb_info"],
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +223,20 @@ def _read_json(tf: tarfile.TarFile, info: tarfile.TarInfo) -> Optional[Any]:
|
||||
return None
|
||||
|
||||
|
||||
def _extract_subkey(raw: Any, data_type: str) -> Optional[Any]:
|
||||
"""
|
||||
When a JSON file bundles multiple datasets, pull out the sub-key that
|
||||
corresponds to data_type (e.g. "sharing_smb_query" from smb_info.json).
|
||||
Falls back to the raw value when no sub-key mapping exists.
|
||||
"""
|
||||
if not isinstance(raw, dict):
|
||||
return raw
|
||||
key = _KEY_WITHIN_FILE.get(data_type)
|
||||
if key and key in raw:
|
||||
return raw[key]
|
||||
return raw
|
||||
|
||||
|
||||
def _find_data(
|
||||
tf: tarfile.TarFile,
|
||||
members: dict[str, tarfile.TarInfo],
|
||||
@@ -222,7 +256,8 @@ def _find_data(
|
||||
info = member
|
||||
break
|
||||
if info is not None:
|
||||
result = _read_json(tf, info)
|
||||
raw = _read_json(tf, info)
|
||||
result = _extract_subkey(raw, data_type)
|
||||
if result is not None:
|
||||
log.info(" %-12s → %s", data_type, info.name)
|
||||
return result
|
||||
@@ -234,7 +269,8 @@ def _find_data(
|
||||
if not path.lower().endswith(".json"):
|
||||
continue
|
||||
if any(kw in path.lower() for kw in keywords):
|
||||
result = _read_json(tf, members[path])
|
||||
raw = _read_json(tf, members[path])
|
||||
result = _extract_subkey(raw, data_type)
|
||||
if result is not None:
|
||||
log.info(" %-12s → %s (heuristic)", data_type, path)
|
||||
return result
|
||||
@@ -242,6 +278,36 @@ def _find_data(
|
||||
return None
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _open_source_tar(tar_path: str):
|
||||
"""
|
||||
Open the archive that actually contains the ixdiagnose data.
|
||||
|
||||
TrueNAS HA debug bundles (25.04+) wrap each node's ixdiagnose snapshot
|
||||
in a separate .txz inside the outer .tgz. We prefer the member whose
|
||||
name includes '_active'; if none is labelled that way we fall back to the
|
||||
first .txz found. Single-node (non-HA) bundles are used directly.
|
||||
"""
|
||||
with tarfile.open(tar_path, "r:*") as outer:
|
||||
txz_members = [
|
||||
m for m in outer.getmembers()
|
||||
if m.name.lower().endswith(".txz") and m.isfile()
|
||||
]
|
||||
if not txz_members:
|
||||
yield outer
|
||||
return
|
||||
|
||||
# HA bundle – pick the active node's inner archive
|
||||
active = next(
|
||||
(m for m in txz_members if "_active" in m.name.lower()),
|
||||
txz_members[0],
|
||||
)
|
||||
log.info(" HA bundle detected; reading inner archive: %s", active.name)
|
||||
fh = outer.extractfile(active)
|
||||
with tarfile.open(fileobj=fh, mode="r:*") as inner:
|
||||
yield inner
|
||||
|
||||
|
||||
def parse_archive(tar_path: str) -> dict[str, Any]:
|
||||
"""
|
||||
Extract SMB shares, NFS shares, and SMB config from the debug archive.
|
||||
@@ -255,7 +321,7 @@ def parse_archive(tar_path: str) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
try:
|
||||
with tarfile.open(tar_path, "r:*") as tf:
|
||||
with _open_source_tar(tar_path) as tf:
|
||||
members = _members_map(tf)
|
||||
log.info(" Archive contains %d total entries.", len(members))
|
||||
|
||||
@@ -297,7 +363,7 @@ def list_archive_and_exit(tar_path: str) -> None:
|
||||
"""
|
||||
print(f"\nJSON files in archive: {tar_path}\n")
|
||||
try:
|
||||
with tarfile.open(tar_path, "r:*") as tf:
|
||||
with _open_source_tar(tar_path) as tf:
|
||||
json_members = sorted(
|
||||
(m for m in tf.getmembers() if m.name.endswith(".json")),
|
||||
key=lambda m: m.name,
|
||||
|
||||
Reference in New Issue
Block a user