#!/usr/bin/env python3 """ SNMP Walk Parser - Combined Nested JSON + Flat CSV + Basic Analysis Reads a snmpwalk output file (net-snmp style with -On -OQ format) Creates: - nested JSON tree - flat CSV with oid + value columns - shows basic statistics and example filters """ import json import re import sys from pathlib import Path import pandas as pd # ──────────────────────────────────────────────── # CONFIG - change these as needed # ──────────────────────────────────────────────── INPUT_FILE = Path("~/snmp-walks/10-13-60-102_2026-02-27_11-23-07_walk.txt").expanduser() # Where to save results (same folder as input by default) OUTPUT_JSON = INPUT_FILE.with_suffix('.tree.json') OUTPUT_CSV = INPUT_FILE.with_suffix('.flat.csv') # ──────────────────────────────────────────────── # Helper functions # ──────────────────────────────────────────────── def parse_line(line: str) -> tuple[str, str] | None: """Split oid and value, clean value quotes""" line = line.strip() if not line or 'No more variables left' in line: return None # Matches: .1.3.6.1.... "value with \"escaped\" quotes" or .1.3.6.1.... 12345 match = re.match(r'^(\.\d+(?:\.\d+)*)\s+(.*)$', line) if not match: return None oid, value = match.groups() value = value.strip() # Remove surrounding quotes if present (simple case) if value.startswith('"') and value.endswith('"'): value = value[1:-1] # Optional: you could add more cleaning here (unescape \", etc.) return oid, value def build_nested_tree(flat_data: dict[str, str]) -> dict: """Convert flat oid → value into nested dictionary""" tree = {} for oid, value in flat_data.items(): parts = oid.lstrip('.').split('.') current = tree for part in parts[:-1]: current = current.setdefault(part, {}) current[parts[-1]] = value return tree def main(): if not INPUT_FILE.is_file(): print(f"File not found: {INPUT_FILE}", file=sys.stderr) sys.exit(1) print(f"Reading: {INPUT_FILE}") print(f"Output: {OUTPUT_JSON}") print(f" {OUTPUT_CSV}\n") # ── Parse file ─────────────────────────────────────── flat_data = {} # oid → value skipped = 0 with INPUT_FILE.open(encoding='utf-8', errors='replace') as f: for line in f: result = parse_line(line) if result: oid, value = result flat_data[oid] = value else: skipped += 1 print(f"Parsed {len(flat_data):,} OIDs (skipped {skipped:,} lines)\n") # ── 1. Nested JSON tree ────────────────────────────── tree = build_nested_tree(flat_data) with OUTPUT_JSON.open('w', encoding='utf-8') as f: json.dump(tree, f, indent=2, ensure_ascii=False) print(f"→ Nested tree saved: {OUTPUT_JSON}") # ── 2. Flat DataFrame + CSV ────────────────────────── df = pd.DataFrame.from_dict(flat_data, orient='index', columns=['value']) df.index.name = 'oid' df = df.reset_index() df.to_csv(OUTPUT_CSV, index=False, encoding='utf-8') print(f"→ Flat table saved: {OUTPUT_CSV}") # ── Quick console summary & examples ───────────────── print("\nQuick summary:") print(f"Total entries: {len(df):,}") print(f"Unique OID prefixes (top 8):") df['prefix'] = df['oid'].str.replace(r'\.\d+$', '', regex=True) print(df['prefix'].value_counts().head(8)) print("\nExample: System group (.1.3.6.1.2.1.1)") system = df[df['oid'].str.startswith('.1.3.6.1.2.1.1')] if not system.empty: print(system[['oid', 'value']].to_string(index=False)) else: print("→ No system group entries found") print("\nExample: Interface descriptions (.1.3.6.1.2.1.2.2.1.2)") ifaces = df[df['oid'].str.contains(r'\.1\.3\.6\.1\.2\.1\.2\.2\.1\.2\b')] if not ifaces.empty: print(ifaces[['oid', 'value']].to_string(index=False)) else: print("→ No interface descriptions found") print("\nDone. Open the .json or .csv file in VS Code / Excel / pandas.") if __name__ == '__main__': main()