[#705] LibreNMS→GLPI network-layer drift report (phase 1 of CMDB cross-feed)
ci / audit (push) Successful in 37s
ci / audit (push) Successful in 37s
librenms-glpi-drift.sh: LibreNMS discovery vs GLPI NetworkEquipment, report-only. First run: 11 devices discovered, 0 in GLPI. Location data needs normalization (sysLocation jokes) before location sync. Phase 2 = LLDP links → GLPI connections. https://projects.knownelement.com/issues/705
This commit is contained in:
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# librenms-glpi-drift.sh — network-layer CMDB drift report [#705 x #343 x #461]
|
||||
#
|
||||
# Compares LibreNMS-discovered network devices (the network discovery truth)
|
||||
# against GLPI NetworkEquipment CIs (the CMDB). Report-only; apply-mode is a
|
||||
# separate approved change.
|
||||
#
|
||||
# Env: librenms.env (LIBRENMS_URL/LIBRENMS_API_TOKEN), mglpi.env (MGLPI_*).
|
||||
# Exit: 0 no drift | 3 drift found | 1 error.
|
||||
set -uo pipefail
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
[ -f "$HOME/.creds/librenms.env" ] && . "$HOME/.creds/librenms.env"
|
||||
# shellcheck disable=SC1091
|
||||
[ -f "$HOME/.creds/mglpi.env" ] && . "$HOME/.creds/mglpi.env"
|
||||
: "${LIBRENMS_URL:?}"; : "${LIBRENMS_API_TOKEN:?}"
|
||||
: "${MGLPI_URL:?}"; : "${MGLPI_APP_TOKEN:?}"; : "${MGLPI_USER_TOKEN:?}"
|
||||
|
||||
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
# 1. LibreNMS devices
|
||||
curl -sS --max-time 30 -H "X-Auth-Token: ${LIBRENMS_API_TOKEN}" \
|
||||
"${LIBRENMS_URL}/api/v0/devices" > "$TMP/lnms.json"
|
||||
jq -r '.devices[] | [.hostname, (.sysName // ""), (.location // ""), (.hardware // ""), (.os // "")] | @tsv' \
|
||||
"$TMP/lnms.json" > "$TMP/lnms.tsv"
|
||||
[ -s "$TMP/lnms.tsv" ] || { echo "FAIL: LibreNMS returned no devices" >&2; exit 1; }
|
||||
|
||||
# 2. GLPI NetworkEquipment CIs
|
||||
S=$(curl -sS --max-time 30 -H "Content-Type: application/json" \
|
||||
-H "App-Token: ${MGLPI_APP_TOKEN}" -H "Authorization: user_token ${MGLPI_USER_TOKEN}" \
|
||||
"${MGLPI_URL}/initSession" | jq -r '.session_token // empty')
|
||||
[ -n "$S" ] || { echo "FAIL: GLPI session init" >&2; exit 1; }
|
||||
curl -sS --max-time 60 -H "App-Token: ${MGLPI_APP_TOKEN}" -H "Session-Token: $S" \
|
||||
"${MGLPI_URL}/NetworkEquipment?range=0-499&is_deleted=0" > "$TMP/glpi.json"
|
||||
curl -sS --max-time 30 -X DELETE -H "App-Token: ${MGLPI_APP_TOKEN}" \
|
||||
-H "Session-Token: $S" "${MGLPI_URL}/killSession" >/dev/null
|
||||
jq -r '.[] | select(type == "object") | (.name // "") | ascii_downcase' "$TMP/glpi.json" \
|
||||
| sort -u > "$TMP/glpi.names"
|
||||
|
||||
# 3. diff by hostname (short name match: glpi uses short names, lnms uses FQDN)
|
||||
cut -f1 "$TMP/lnms.tsv" | sed 's/\..*//' | tr '[:upper:]' '[:lower:]' | sort -u > "$TMP/lnms.names"
|
||||
comm -23 "$TMP/lnms.names" "$TMP/glpi.names" > "$TMP/missing_in_glpi"
|
||||
comm -13 "$TMP/lnms.names" "$TMP/glpi.names" > "$TMP/ghost_in_glpi"
|
||||
miss=$(grep -c . "$TMP/missing_in_glpi" || true)
|
||||
ghost=$(grep -c . "$TMP/ghost_in_glpi" || true)
|
||||
|
||||
{
|
||||
echo "# Network-layer CMDB drift report $(date -Is) (LibreNMS discovery vs GLPI NetworkEquipment)"
|
||||
echo "LibreNMS devices: $(grep -c . "$TMP/lnms.names") | GLPI NetworkEquipment CIs: $(grep -c . "$TMP/glpi.names")"
|
||||
echo
|
||||
echo "## discovered by LibreNMS, MISSING in GLPI ($miss)"
|
||||
while IFS=$'\t' read -r fqdn _sys loc hw os; do
|
||||
short=${fqdn%%.*}
|
||||
grep -qx "$short" "$TMP/missing_in_glpi" && echo "$short (lnms: $fqdn, loc=$loc, hw=$hw, os=$os)"
|
||||
done < "$TMP/lnms.tsv"
|
||||
echo
|
||||
echo "## in GLPI as NetworkEquipment, NOT in LibreNMS ($ghost)"
|
||||
cat "$TMP/ghost_in_glpi"
|
||||
echo
|
||||
echo "# phase 2 (queued): LLDP neighbor links → GLPI port connections"
|
||||
} | tee "${OUT:-/dev/null}"
|
||||
|
||||
[ "$miss" -eq 0 ] && [ "$ghost" -eq 0 ] && exit 0 || exit 3
|
||||
Reference in New Issue
Block a user