nid-snmp/snmp-walk.sh

181 lines
8.1 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────────────
# snmp-walk.sh — Live SNMP walk → parse → HTML viewer pipeline
#
# Usage:
# ./snmp-walk.sh # uses SNMP_TARGET from .env
# ./snmp-walk.sh 10.0.0.1 # override target IP
# ./snmp-walk.sh 10.0.0.1 full # override target + walk mode
#
# Reads configuration from .env (community, version, walk mode).
# Produces: walks/{IP}_{TIMESTAMP}_walk.txt
# walks/{IP}_{TIMESTAMP}_walk_monitoring.json
# walks/nid-viewer.html
# ──────────────────────────────────────────────────────────────────────
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
# ── Load .env ────────────────────────────────────────────────────────
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck source=/dev/null
source "$ENV_FILE"
set +a
else
echo "Warning: ${ENV_FILE} not found — using defaults" >&2
fi
# ── Resolve parameters (CLI overrides .env) ──────────────────────────
TARGET="${1:-${SNMP_TARGET:-}}"
WALK_MODE="${2:-${SNMP_WALK_MODE:-targeted}}"
VERSION="${SNMP_VERSION:-2c}"
if [[ -z "$TARGET" ]]; then
echo "Error: No target IP specified."
echo " Set SNMP_TARGET in .env or pass as argument: $0 <ip>"
exit 1
fi
# ── Validate prerequisites ───────────────────────────────────────────
for cmd in snmpwalk python3; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: '$cmd' not found. Please install it." >&2
exit 1
fi
done
# Prefer snmpbulkwalk (GETBULK PDUs — much faster) over snmpwalk (GETNEXT)
if command -v snmpbulkwalk &>/dev/null; then
WALK_CMD=snmpbulkwalk
BULK_ARGS=() # use default -Cr10; higher values truncate on some devices
else
WALK_CMD=snmpwalk
BULK_ARGS=()
fi
# ── Build snmpwalk auth flags ────────────────────────────────────────
SNMP_AUTH=()
if [[ "$VERSION" == "3" ]]; then
SNMP_AUTH+=(-v3)
SNMP_AUTH+=(-u "${SNMP_V3_USER:?SNMP_V3_USER required for v3}")
SNMP_AUTH+=(-l "${SNMP_V3_SEC_LEVEL:-authPriv}")
if [[ "${SNMP_V3_SEC_LEVEL:-authPriv}" != "noAuthNoPriv" ]]; then
SNMP_AUTH+=(-a "${SNMP_V3_AUTH_PROTO:-SHA}")
SNMP_AUTH+=(-A "${SNMP_V3_AUTH_PASS:?SNMP_V3_AUTH_PASS required}")
fi
if [[ "${SNMP_V3_SEC_LEVEL:-authPriv}" == "authPriv" ]]; then
SNMP_AUTH+=(-x "${SNMP_V3_PRIV_PROTO:-AES}")
SNMP_AUTH+=(-X "${SNMP_V3_PRIV_PASS:?SNMP_V3_PRIV_PASS required}")
fi
else
SNMP_AUTH+=(-v "${VERSION}" -c "${SNMP_COMMUNITY:-public}")
fi
# ── Define OID subtrees ──────────────────────────────────────────────
# Targeted: only what the viewer/parser needs
WALK_POLICIES="${SNMP_WALK_POLICIES:-true}"
TARGETED_OIDS=(
.1.3.6.1.2.1.1 # System (sysDescr, sysName, sysUpTime, …)
.1.3.6.1.2.1.2 # IF-MIB (interface table)
.1.3.6.1.2.1.4 # IP-MIB (addresses, routes)
.1.3.6.1.2.1.31 # IF-MIB extensions (ifName, ifAlias, 64-bit counters)
.1.3.6.1.2.1.55 # IPv6-MIB
.1.3.111.2.802.1.1.13 # LLDP-MIB (neighbors, stats)
.1.3.6.1.4.1.22420.1.1 # ACD-DESC-MIB (device identity, connectors, sensors)
.1.3.6.1.4.1.22420.2.1 # ACD-ALARM-MIB
.1.3.6.1.4.1.22420.2.2 # ACD-FILTER-MIB
.1.3.6.1.4.1.22420.2.4 # ACD-SFP-MIB (transceiver info/diag)
.1.3.6.1.4.1.22420.2.6 # ACD-REGULATOR-MIB
.1.3.6.1.4.1.22420.2.8 # ACD-SMAP-MIB (CoS profiles)
.1.3.6.1.4.1.22420.2.9 # ACD-PORT-MIB (port config/status)
)
if [[ "$WALK_POLICIES" == "true" ]]; then
TARGETED_OIDS+=(.1.3.6.1.4.1.22420.2.3) # ACD-POLICY-MIB (~73% of all OIDs)
fi
# ── Prepare output paths ─────────────────────────────────────────────
TIMESTAMP="$(date +%Y-%m-%d_%H-%M-%S)"
SAFE_IP="${TARGET//./-}"
WALKS_DIR="${SCRIPT_DIR}/walks"
WALK_FILE="${WALKS_DIR}/${SAFE_IP}_${TIMESTAMP}_walk.txt"
ERROR_FILE="${WALKS_DIR}/${SAFE_IP}_${TIMESTAMP}_errors.txt"
mkdir -p "$WALKS_DIR"
# ── Execute SNMP walk ─────────────────────────────────────────────────
echo "═══════════════════════════════════════════════════════"
echo " SNMP NID Viewer — Live Walk"
echo "═══════════════════════════════════════════════════════"
echo " Target: ${TARGET}"
echo " Version: SNMPv${VERSION}"
echo " Mode: ${WALK_MODE}"
echo " Walker: ${WALK_CMD}${BULK_ARGS:+ (max-rep=${BULK_ARGS[1]#-Cr})}"
echo " Output: ${WALK_FILE}"
echo "═══════════════════════════════════════════════════════"
echo ""
WALK_START="$(date +%s)"
if [[ "$WALK_MODE" == "full" ]]; then
echo "[1/3] Walking full OID tree (.1) ..."
"$WALK_CMD" -On -OQ "${BULK_ARGS[@]}" "${SNMP_AUTH[@]}" "$TARGET" .1 \
> "$WALK_FILE" 2> "$ERROR_FILE" || true
else
echo "[1/3] Walking ${#TARGETED_OIDS[@]} targeted subtrees ..."
: > "$WALK_FILE"
: > "$ERROR_FILE"
for oid in "${TARGETED_OIDS[@]}"; do
echo "${oid}"
"$WALK_CMD" -On -OQ "${BULK_ARGS[@]}" "${SNMP_AUTH[@]}" "$TARGET" "$oid" \
>> "$WALK_FILE" 2>> "$ERROR_FILE" || true
done
fi
WALK_END="$(date +%s)"
WALK_LINES="$(wc -l < "$WALK_FILE")"
WALK_SECS=$(( WALK_END - WALK_START ))
echo " ✓ Walk complete: ${WALK_LINES} lines in ${WALK_SECS}s"
# Remove empty error file
if [[ ! -s "$ERROR_FILE" ]]; then
rm -f "$ERROR_FILE"
fi
if [[ "$WALK_LINES" -eq 0 ]]; then
echo ""
echo "Error: Walk returned no data. Check:"
echo " - Device reachability (ping ${TARGET})"
echo " - SNMP community/credentials in .env"
echo " - Firewall rules (UDP 161)"
[[ -s "$ERROR_FILE" ]] && echo "" && cat "$ERROR_FILE"
exit 1
fi
# ── Parse walk → monitoring JSON ──────────────────────────────────────
echo ""
echo "[2/3] Parsing walk data ..."
python3 "${SCRIPT_DIR}/snmp-parse.py" "$WALK_FILE"
MONITORING_JSON="${WALK_FILE%.txt}_monitoring.json"
if [[ ! -f "$MONITORING_JSON" ]]; then
echo "Error: snmp-parse.py did not produce ${MONITORING_JSON}" >&2
exit 1
fi
echo " ✓ Monitoring JSON: ${MONITORING_JSON}"
# ── Build HTML viewer ─────────────────────────────────────────────────
echo ""
echo "[3/3] Building HTML viewer ..."
python3 "${SCRIPT_DIR}/build_nid_viewer.py" "$MONITORING_JSON"
echo " ✓ Viewer: ${WALKS_DIR}/nid-viewer.html"
# ── Summary ───────────────────────────────────────────────────────────
echo ""
echo "═══════════════════════════════════════════════════════"
echo " Done! Open walks/nid-viewer.html in a browser."
echo "═══════════════════════════════════════════════════════"