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


#!/usr/bin/env bash

set -euo pipefail

#
# Extract numeric leaf OIDs.
#
# Usage:
#
#   ./extract_mib_leaf_oids.sh
#       Load ALL available MIBs (-m ALL)
#       Output: leaf_oids.txt
#
#   ./extract_mib_leaf_oids.sh <mib-file>
#       Extract OBJECT-TYPE leaf OIDs from one MIB.
#       Output: leaf_oids.txt
#
#   ./extract_mib_leaf_oids.sh <mib-file> <output-file>
#       Extract OBJECT-TYPE leaf OIDs from one MIB
#       and write them to the specified file.
#
# Examples:
#
#   ./extract_mib_leaf_oids.sh
#
#   ./extract_mib_leaf_oids.sh \
#       /usr/share/snmp/mibs/aqnos.mib
#
#   ./extract_mib_leaf_oids.sh \
#       /usr/share/snmp/mibs/aqnos.mib \
#       aqnos_oids.txt
#

if [[ $# -gt 2 ]]; then
    echo "Usage: $0 [mib-file] [output-file]" >&2
    exit 1
fi

MIB_FILE="${1:-}"
OUTPUT_FILE="${2:-leaf_oids.txt}"

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

BASE_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"

TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT

NORMALIZED="$TMPDIR/normalized"

#
# ------------------------------------------------------------
# MODE 1:
#
# No MIB file specified.
#
# Load everything using:
#
#     -m ALL
#
# and use the complete MIB tree.
# ------------------------------------------------------------
#
if [[ -z "$MIB_FILE" ]]; then

    echo "No MIB file specified." >&2
    echo "Loading all available MIB modules (-m ALL)..." >&2

    RAW="$TMPDIR/all-tree"

    snmptranslate \
        -Pu \
        -Tz \
        -M "$BASE_MIBDIRS" \
        -m ALL \
        > "$RAW"

    #
    # -Tz produces lines similar to:
    #
    #   "sysDescr" "1.3.6.1.2.1.1.1"
    #
    # Extract only numeric OIDs.
    #
    awk -F'"' '
        NF >= 5 {
            oid = $4

            sub(/^\./, "", oid)

            if (oid ~ /^[0-9]+(\.[0-9]+)*$/)
                print oid
        }
    ' "$RAW" |
        LC_ALL=C sort -u > "$NORMALIZED"

#
# ------------------------------------------------------------
# MODE 2:
#
# A specific MIB file was supplied.
#
# Find OBJECT-TYPE declarations belonging specifically to that
# module and translate only those objects.
# ------------------------------------------------------------
#
else

    if [[ ! -f "$MIB_FILE" ]]; then
        echo "Error: MIB file does not exist:" >&2
        echo "  $MIB_FILE" >&2
        exit 1
    fi

    MIB_FILE="$(realpath "$MIB_FILE")"
    MIB_DIR="$(dirname "$MIB_FILE")"

    MIBDIRS="$MIB_DIR:$BASE_MIBDIRS"

    SYMBOLS="$TMPDIR/object-types"
    TRANSLATED="$TMPDIR/translated"

    #
    # Find module name:
    #
    #     AQNOS-MIB DEFINITIONS ::= BEGIN
    #
    MODULE_NAME="$(
        awk '
        {
            line = $0
            sub(/--.*/, "", line)

            n = split(line, fields, /[[:space:]]+/)
            previous = ""

            for (i = 1; i <= n; i++) {

                if (fields[i] == "DEFINITIONS" &&
                    previous != "") {

                    print previous
                    exit
                }

                if (fields[i] != "")
                    previous = fields[i]
            }
        }
        ' "$MIB_FILE"
    )"

    if [[ -z "$MODULE_NAME" ]]; then
        echo "Error: could not determine module name from:" >&2
        echo "  $MIB_FILE" >&2
        exit 1
    fi

    echo "MIB file: $MIB_FILE" >&2
    echo "Module:   $MODULE_NAME" >&2

    #
    # Find all OBJECT-TYPE declarations in this MIB.
    #
    # Handles:
    #
    #     someObject OBJECT-TYPE
    #
    # and:
    #
    #     someObject
    #         OBJECT-TYPE
    #
    awk '
    {
        line = $0

        sub(/--.*/, "", line)

        n = split(line, fields, /[[:space:]]+/)

        for (i = 1; i <= n; i++) {

            token = fields[i]

            if (token == "")
                continue

            if (token == "OBJECT-TYPE") {

                if (previous ~ /^[A-Za-z][A-Za-z0-9-]*$/)
                    print previous
            }

            previous = token
        }
    }
    ' "$MIB_FILE" |
        LC_ALL=C sort -u > "$SYMBOLS"

    OBJECT_COUNT="$(wc -l < "$SYMBOLS")"

    if [[ "$OBJECT_COUNT" -eq 0 ]]; then
        echo "Error: no OBJECT-TYPE declarations found." >&2
        exit 1
    fi

    echo "OBJECT-TYPE objects: $OBJECT_COUNT" >&2

    #
    # Translate each:
    #
    #     AQNOS-MIB::someObject
    #
    # into its numeric OID.
    #
    while IFS= read -r symbol; do

        snmptranslate \
            -Pu \
            -On \
            -M "$MIBDIRS" \
            -m ALL \
            "${MODULE_NAME}::${symbol}"

    done < "$SYMBOLS" > "$TRANSLATED"

    #
    # Normalize:
    #
    #     .1.3.6.1.4.1...
    #
    # ->
    #
    #     1.3.6.1.4.1...
    #
    sed 's/^\.//' "$TRANSLATED" |
        awk '
            /^[0-9]+(\.[0-9]+)*$/ {
                print
            }
        ' |
        LC_ALL=C sort -u > "$NORMALIZED"
fi


#
# ------------------------------------------------------------
# Remove all non-leaf OIDs.
#
# If an OID is an ancestor of another known OID, it isn't a
# leaf.
#
# Example:
#
#   1.3.6.1.4.1.100
#   1.3.6.1.4.1.100.1
#   1.3.6.1.4.1.100.2
#
# Result:
#
#   1.3.6.1.4.1.100.1
#   1.3.6.1.4.1.100.2
#
# ------------------------------------------------------------
#
awk '
{
    oid[++count] = $0
    exists[$0] = 1
}

END {

    #
    # Mark every known parent OID as non-leaf.
    #
    for (i = 1; i <= count; i++) {

        parent = oid[i]

        while (sub(/\.[^.]+$/, "", parent)) {

            if (parent in exists)
                nonleaf[parent] = 1
        }
    }

    #
    # Print only leaf OIDs.
    #
    for (i = 1; i <= count; i++) {

        if (!(oid[i] in nonleaf))
            print oid[i]
    }
}
' "$NORMALIZED" |
    LC_ALL=C sort -u > "$OUTPUT_FILE"


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

echo >&2
echo "Done." >&2
echo "Objects:   $TOTAL_COUNT" >&2
echo "Leaf OIDs: $LEAF_COUNT" >&2
echo "Output:    $OUTPUT_FILE" >&2