Update to debug paths
This commit is contained in:
@@ -50,6 +50,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import ssl
|
import ssl
|
||||||
@@ -154,34 +155,53 @@ class Summary:
|
|||||||
|
|
||||||
_CANDIDATES: dict[str, list[str]] = {
|
_CANDIDATES: dict[str, list[str]] = {
|
||||||
"smb_shares": [
|
"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/SMB/sharing.smb.query.json",
|
||||||
"ixdiagnose/plugins/Sharing/sharing.smb.query.json",
|
"ixdiagnose/plugins/Sharing/sharing.smb.query.json",
|
||||||
"ixdiagnose/SMB/sharing.smb.query.json",
|
"ixdiagnose/SMB/sharing.smb.query.json",
|
||||||
|
# CORE / freenas-debug
|
||||||
"freenas-debug/sharing/smb.json",
|
"freenas-debug/sharing/smb.json",
|
||||||
"sharing/smb.json",
|
"sharing/smb.json",
|
||||||
"middleware/sharing.smb.query.json",
|
"middleware/sharing.smb.query.json",
|
||||||
],
|
],
|
||||||
"nfs_shares": [
|
"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/NFS/sharing.nfs.query.json",
|
||||||
"ixdiagnose/plugins/Sharing/sharing.nfs.query.json",
|
"ixdiagnose/plugins/Sharing/sharing.nfs.query.json",
|
||||||
"ixdiagnose/NFS/sharing.nfs.query.json",
|
"ixdiagnose/NFS/sharing.nfs.query.json",
|
||||||
|
# CORE / freenas-debug
|
||||||
"freenas-debug/sharing/nfs.json",
|
"freenas-debug/sharing/nfs.json",
|
||||||
"sharing/nfs.json",
|
"sharing/nfs.json",
|
||||||
"middleware/sharing.nfs.query.json",
|
"middleware/sharing.nfs.query.json",
|
||||||
],
|
],
|
||||||
"smb_config": [
|
"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/plugins/SMB/smb.config.json",
|
||||||
"ixdiagnose/SMB/smb.config.json",
|
"ixdiagnose/SMB/smb.config.json",
|
||||||
|
# CORE / freenas-debug
|
||||||
"freenas-debug/SMB/smb_config.json",
|
"freenas-debug/SMB/smb_config.json",
|
||||||
"middleware/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
|
# Keyword fragments for heuristic fallback scan
|
||||||
_KEYWORDS: dict[str, list[str]] = {
|
_KEYWORDS: dict[str, list[str]] = {
|
||||||
"smb_shares": ["sharing.smb", "smb_share", "sharing/smb"],
|
"smb_shares": ["sharing.smb", "smb_share", "sharing/smb", "smb_info"],
|
||||||
"nfs_shares": ["sharing.nfs", "nfs_share", "sharing/nfs"],
|
"nfs_shares": ["sharing.nfs", "nfs_share", "sharing/nfs", "nfs_config"],
|
||||||
"smb_config": ["smb.config", "smb_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
|
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(
|
def _find_data(
|
||||||
tf: tarfile.TarFile,
|
tf: tarfile.TarFile,
|
||||||
members: dict[str, tarfile.TarInfo],
|
members: dict[str, tarfile.TarInfo],
|
||||||
@@ -222,7 +256,8 @@ def _find_data(
|
|||||||
info = member
|
info = member
|
||||||
break
|
break
|
||||||
if info is not None:
|
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:
|
if result is not None:
|
||||||
log.info(" %-12s → %s", data_type, info.name)
|
log.info(" %-12s → %s", data_type, info.name)
|
||||||
return result
|
return result
|
||||||
@@ -234,7 +269,8 @@ def _find_data(
|
|||||||
if not path.lower().endswith(".json"):
|
if not path.lower().endswith(".json"):
|
||||||
continue
|
continue
|
||||||
if any(kw in path.lower() for kw in keywords):
|
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:
|
if result is not None:
|
||||||
log.info(" %-12s → %s (heuristic)", data_type, path)
|
log.info(" %-12s → %s (heuristic)", data_type, path)
|
||||||
return result
|
return result
|
||||||
@@ -242,6 +278,36 @@ def _find_data(
|
|||||||
return None
|
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]:
|
def parse_archive(tar_path: str) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Extract SMB shares, NFS shares, and SMB config from the debug archive.
|
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:
|
try:
|
||||||
with tarfile.open(tar_path, "r:*") as tf:
|
with _open_source_tar(tar_path) as tf:
|
||||||
members = _members_map(tf)
|
members = _members_map(tf)
|
||||||
log.info(" Archive contains %d total entries.", len(members))
|
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")
|
print(f"\nJSON files in archive: {tar_path}\n")
|
||||||
try:
|
try:
|
||||||
with tarfile.open(tar_path, "r:*") as tf:
|
with _open_source_tar(tar_path) as tf:
|
||||||
json_members = sorted(
|
json_members = sorted(
|
||||||
(m for m in tf.getmembers() if m.name.endswith(".json")),
|
(m for m in tf.getmembers() if m.name.endswith(".json")),
|
||||||
key=lambda m: m.name,
|
key=lambda m: m.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user