#!/usr/bin/env bash TMP=$(mktemp -d); TMP_NE=$TMP/ne.json; TMP_PT=$TMP/port.json; TMP_CONN=$TMP/conn.json; TMP_PAIRS=$TMP/pairs.tsv; trap 'rm -rf $TMP' EXIT # populate-netequip-from-librenms.sh — phase 2a of the GLPI x LibreNMS cross-feed [#808] # # Creates NetworkEquipment CIs in GLPI from LibreNMS discovery (skips hosts # that already exist as Computers), then wires LLDP links as GLPI # NetworkPort + NetworkPort_NetworkPort connections. Change 19 documents # this operation (C2 ruling: agent CMDB writes approved). # # Inputs: # /tmp/lldp_devices.tsv fqdnhardwareos (network devices only) # /tmp/lldp_links.tsv fqdnAportAfqdnBportB # Env: mglpi.env (Super-Admin session; Hotliner CANNOT write CIs). # Exit: 0 ok | 1 error. set -uo pipefail # shellcheck disable=SC1091 [ -f "$HOME/.creds/mglpi.env" ] && . "$HOME/.creds/mglpi.env" : "${MGLPI_URL:?}"; : "${MGLPI_APP_TOKEN:?}"; : "${MGLPI_USER_TOKEN:?}" S=$(curl -sk --max-time 20 -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; } api() { # method path payload-file -> body local method=$1 path=$2 body=${3:-} if [ -n "$body" ]; then curl -sk --max-time 20 -X "$method" -H "App-Token: ${MGLPI_APP_TOKEN}" \ -H "Session-Token: $S" -H "Content-Type: application/json" \ -d "@$body" "${MGLPI_URL}/$path" else curl -sk --max-time 20 -X "$method" -H "App-Token: ${MGLPI_APP_TOKEN}" \ -H "Session-Token: $S" "${MGLPI_URL}/$path" fi } # --- 1. NetworkEquipment CIs (dedupe by short hostname) --- ne_created=0; ne_exists=0 declare -A NE_ID while IFS=$'\t' read -r fqdn hw os; do [ -n "$fqdn" ] || continue short=${fqdn%%.*} # exists already? (by name) found=$(api GET "search/NetworkEquipment?criteria%5B0%5D%5Bfield%5D=1&criteria%5B0%5D%5Bsearchtype%5D=contains&criteria%5B0%5D%5Bvalue%5D=${short}&forcedisplay%5B%5D=2" \ | jq -r '.data[0]["2"] // empty') if [ -n "$found" ]; then NE_ID[$short]=$found; ne_exists=$((ne_exists+1)); continue fi jq -n --arg n "$short" --arg c "LibreNMS-discovered: hw=$hw os=$os (CR19/#808)" \ '{input: {name: $n, comment: $c}}' > "$TMP_NE" 2>/dev/null || { printf '{"input": {"name": "%s", "comment": "LibreNMS-discovered hw=%s os=%s (CR19/#808)"}}' "$short" "$hw" "$os" > "$TMP_NE" } r=$(api POST NetworkEquipment "$TMP_NE") id=$(echo "$r" | jq -r '.id // empty') if [ -n "$id" ]; then NE_ID[$short]=$id; ne_created=$((ne_created+1)); else echo "ERR create NetEquipment $short: $(echo "$r" | head -c 120)" >&2; fi done < /tmp/lldp_devices.tsv echo "netequip: created=$ne_created existed=$ne_exists" # --- 2. ports + connections from LLDP links --- port_created=0; conn_created=0; conn_dup=0 declare -A PORT_ID ensure_port() { # fqdn short ifname -> echoes port id local fqdn=$1 ifname=$2 short=${1%%.*} key r pid key="$short|$ifname" [ -n "${PORT_ID[$key]:-}" ] && { echo "${PORT_ID[$key]}"; return; } local ne=${NE_ID[$short]:-} if [ -z "$ne" ]; then echo ""; return; fi printf '{"input": {"itemtype": "NetworkEquipment", "items_id": %s, "name": "%s", "ifnum": "%s", "logical_number": 1}}' \ "$ne" "$ifname" "$ifname" > "$TMP_PT" r=$(api POST NetworkPort "$TMP_PT") pid=$(echo "$r" | jq -r '.id // empty') if [ -n "$pid" ]; then PORT_ID[$key]=$pid; port_created=$((port_created+1)); fi echo "$pid" } # dedupe undirected pairs awk -F'\t' '{ if (($1 FS $3) < ($3 FS $1)) print $1"\t"$2"\t"$3"\t"$4; else print $3"\t"$4"\t"$1"\t"$2 }' \ /tmp/lldp_links.tsv | sort -u > "$TMP_PAIRS" while IFS=$'\t' read -r fa pa fb pb; do [ -n "$fa" ] || continue pa=${pa:-unknown}; pb=${pb:-unknown} ida=$(ensure_port "$fa" "$pa"); idb=$(ensure_port "$fb" "$pb") [ -n "$ida" ] && [ -n "$idb" ] || { echo "SKIP link $fa/$pa <-> $fb/$pb (missing CI/port)" >&2; continue; } lo=$(( ida < idb ? ida : idb )); hi=$(( ida < idb ? idb : ida )) if api GET "search/NetworkPort_NetworkPort?criteria%5B0%5D%5Bfield%5D=13&criteria%5B0%5D%5Bsearchtype%5D=equals&criteria%5B0%5D%5Bvalue%5D=$lo" \ | grep -q "\"$hi\""; then conn_dup=$((conn_dup+1)); continue; fi printf '{"input": {"networkports_id_1": %s, "networkports_id_2": %s}}' "$lo" "$hi" > "$TMP_CONN" r=$(api POST NetworkPort_NetworkPort "$TMP_CONN") st=$(echo "$r" | jq -r 'if type=="array" then .[0].id else .id end // empty' 2>/dev/null) if [ -n "$st" ]; then conn_created=$((conn_created+1)); else conn_dup=$((conn_dup+1)); fi done < "$TMP_PAIRS" echo "ports: created=$port_created | connections: created=$conn_created dup/skip=$conn_dup" curl -sk --max-time 15 -X DELETE -H "App-Token: ${MGLPI_APP_TOKEN}" -H "Session-Token: $S" "${MGLPI_URL}/killSession" -o /dev/null exit 0