Загрузка данных


#!/usr/bin/env bash

set -Eeuo pipefail

# Usage:
#   ./extract-leaf-oids.sh [output-file]
#
# Example:
#   ./extract-leaf-oids.sh module_name.oid
#
# Output format:
#   "objectName" "1.3.6.1.4.1...."

OUTPUT_FILE="${1:-leaf-oids.oid}"

DEFAULT_MIBDIRS="$HOME/.snmp/mibs\
:/usr/share/snmp/mibs\
:/usr/share/snmp/mibs/iana\
:/usr/share/snmp/mibs/ietf\
:/usr/share/mibs/site\
:/usr/share/mibs/iana\
:/usr/share/mibs/ietf\
:/usr/share/mibs/netsnmp\
:$PWD"

# You can override this by exporting MIBDIRS before running the script.
MIB_PATHS="${MIBDIRS:-$DEFAULT_MIBDIRS}"

TEMP_DIR="$(mktemp -d)"
RAW_FILE="$TEMP_DIR/all-oids.raw"
PARSED_FILE="$TEMP_DIR/all-oids.tsv"
LEAF_FILE="$TEMP_DIR/leaf-oids.tsv"
ERROR_FILE="${OUTPUT_FILE}.errors"

cleanup() {
    rm -rf "$TEMP_DIR"
}

trap cleanup EXIT

if ! command -v snmptranslate >/dev/null 2>&1; then
    echo "Error: snmptranslate is not installed or is not in PATH." >&2
    exit 1
fi

echo "Loading all MIB modules..." >&2

if ! snmptranslate \
    -Pu \
    -Tz \
    -M "$MIB_PATHS" \
    -m ALL \
    >"$RAW_FILE" 2>"$ERROR_FILE"
then
    echo "Error: snmptranslate failed." >&2
    echo "See: $ERROR_FILE" >&2
    exit 1
fi

# Convert:
#   "sysDescr"    "1.3.6.1.2.1.1.1"
#
# Into:
#   sysDescr<TAB>1.3.6.1.2.1.1.1
awk -F'"' '
    NF >= 5 &&
    $2 != "" &&
    $4 ~ /^\.?[0-9]+(\.[0-9]+)*$/ {
        oid = $4
        sub(/^\./, "", oid)
        print $2 "\t" oid
    }
' "$RAW_FILE" >"$PARSED_FILE"

if [[ ! -s "$PARSED_FILE" ]]; then
    echo "Error: no valid OIDs were parsed from snmptranslate output." >&2
    echo "See: $ERROR_FILE" >&2
    exit 1
fi

# An OID is not a leaf when it is a prefix/ancestor of another OID.
#
# Example:
#   1.3.6             non-leaf
#   1.3.6.1           non-leaf
#   1.3.6.1.2         leaf, provided no longer OID starts with 1.3.6.1.2.
awk -F'\t' '
    BEGIN {
        OFS = "\t"
    }

    {
        name = $1
        oid  = $2

        # Save each unique name/OID pair.
        pair = name SUBSEP oid

        if (!(pair in pair_seen)) {
            pair_seen[pair] = 1
            count++
            names[count] = name
            oids[count] = oid
        }

        oid_exists[oid] = 1
    }

    END {
        # Mark every ancestor of every OID as a non-leaf.
        for (oid in oid_exists) {
            component_count = split(oid, components, ".")

            prefix = components[1]

            for (i = 2; i < component_count; i++) {
                prefix = prefix "." components[i]
                non_leaf[prefix] = 1
            }
        }

        # Print only OIDs that were never marked as ancestors.
        for (i = 1; i <= count; i++) {
            if (!(oids[i] in non_leaf)) {
                print names[i], oids[i]
            }
        }
    }
' "$PARSED_FILE" >"$LEAF_FILE"

# Sort by numeric OID and restore the snmptranslate -Tz-like format.
LC_ALL=C sort \
    -t$'\t' \
    -k2,2V \
    -k1,1 \
    "$LEAF_FILE" |
awk -F'\t' '{
    printf "\"%s\" \"%s\"\n", $1, $2
}' >"$OUTPUT_FILE"

TOTAL_COUNT="$(wc -l <"$PARSED_FILE")"
LEAF_COUNT="$(wc -l <"$OUTPUT_FILE")"

echo "Finished." >&2
echo "Parsed objects: $TOTAL_COUNT" >&2
echo "Leaf objects:   $LEAF_COUNT" >&2
echo "Output:         $OUTPUT_FILE" >&2

if [[ -s "$ERROR_FILE" ]]; then
    echo "MIB parser warnings: $ERROR_FILE" >&2
else
    rm -f "$ERROR_FILE"
fi