Files
inventory/seed/software-catalog.sh
2026-09-03 21:39:15 -05:00

170 lines
6.6 KiB
Bash

#!/bin/bash
#
# software-catalog.sh — seed the GLPI software catalog [#705]
#
# Usage: software-catalog.sh [--dry-run]
# Needs: ~/.creds/glpi.env (GLPI_URL, GLPI_APP_TOKEN, GLPI_USER_TOKEN)
# ~/.creds/wazuh.env optional (WAZUH_API_PASS) — for live agent installs
#
# Creates Software assets + SoftwareVersions and links installations to
# Computer CIs. Idempotent at all three levels (software, version, install).
# Versions are recorded ONLY where verified; unknowns say so — GLPI Agent
# auto-inventory (proposed) is the durable fix for version drift.
#
set -euo pipefail
DRY=0
[ "${1:-}" = "--dry-run" ] && DRY=1
CREDS="${HOME}/.creds/glpi.env"
# shellcheck disable=SC1090
. "$CREDS"
: "${GLPI_URL:?}" "${GLPI_APP_TOKEN:?}" "${GLPI_USER_TOKEN:?}"
API="$GLPI_URL/apirest.php"
api() {
local method="$1" path="$2" body="${3:-}"
if [ -n "$body" ]; then
curl -sS --max-time 30 -X "$method" -H "App-Token: $GLPI_APP_TOKEN" \
-H "Session-Token: $SESSION" -H "Content-Type: application/json" \
-d "$body" "$API/$path"
else
curl -sS --max-time 30 -X "$method" -H "App-Token: $GLPI_APP_TOKEN" \
-H "Session-Token: $SESSION" "$API/$path"
fi
}
SESSION=$(curl -sS --max-time 20 -H "Content-Type: application/json" \
-H "App-Token: $GLPI_APP_TOKEN" \
-H "Authorization: user_token $GLPI_USER_TOKEN" \
"$API/initSession" | jq -re '.session_token')
echo "— session ok"
# software_id <name> — existing id or empty
sw_id() {
api GET "Software?searchText%5Bname%5D=$(printf %s "$1" | sed 's/ /%20/g')" \
| jq -r --arg n "$1" '[.[] | select(.name==$n)][0].id // empty'
}
# version_id <swid> <version> — GLPI ignores softwares_id as a search param,
# so pull the full list and filter client-side (parent AND name)
ver_id() {
api GET "SoftwareVersion?range=0-999" \
| jq -r --arg s "$1" --arg v "$2" \
'[.[] | select((.softwares_id|tostring)==$s and .name==$v)][0].id // empty'
}
# computer_id <ci-name>
pc_id() {
api GET "Computer?searchText%5Bname%5D=$(printf %s "$1" | sed 's/ /%20/g')" \
| jq -r --arg n "$1" '[.[] | select(.name==$n)][0].id // empty'
}
# install_exists <verid> <pcid>
install_exists() {
api GET "Item_SoftwareVersion?range=0-4999" \
| jq -e --arg v "$1" --arg p "$2" \
'[.[] | select(.softwareversions_id==($v|tonumber) and .items_id==($p|tonumber) and .itemtype=="Computer")] | length > 0' \
>/dev/null 2>&1
}
# ensure <swname> <version> <targets-comma-list|-> — '-' = catalog only
ensure() {
local sw="$1" ver="$2" targets="$3" sid vid cid
sid=$(sw_id "$sw")
if [ -z "$sid" ]; then
if [ "$DRY" = 1 ]; then echo " DRY: software '$sw'"; sid=0; else
sid=$(api POST Software "{\"input\":{\"name\":\"$sw\",\"manufacturers_id\":0,\"comment\":\"seeded by software-catalog.sh (#705)\"}}" \
| jq -r '.id // empty')
echo " + software '$sw' (id=$sid)"
fi
else
echo " = software '$sw' (id=$sid)"
fi
[ "$sid" = 0 ] && [ "$DRY" = 1 ] && return 0
vid=$(ver_id "$sid" "$ver")
if [ -z "$vid" ]; then
if [ "$DRY" = 1 ]; then echo " DRY: version '$ver'"; else
vid=$(api POST SoftwareVersion "{\"input\":{\"softwares_id\":\"$sid\",\"name\":\"$ver\",\"state\":0}}" \
| jq -r '.id // empty')
echo " + version '$ver' (id=$vid)"
fi
else
echo " = version '$ver' (id=$vid)"
fi
[ -z "$vid" ] && return 0
[ "$targets" = "-" ] && return 0
local IFS=','
for t in $targets; do
cid=$(pc_id "$t")
if [ -z "$cid" ]; then echo " ! no CI named '$t' (skipped)"; continue; fi
if install_exists "$vid" "$cid"; then echo " = installed on $t"; continue; fi
if [ "$DRY" = 1 ]; then echo " DRY: install on $t"; else
api POST Item_SoftwareVersion \
"{\"input\":{\"itemtype\":\"Computer\",\"items_id\":\"$cid\",\"softwareversions_id\":\"$vid\"}}" \
| jq -re 'if type=="array" or .id then empty else . end' >/dev/null 2>&1 \
&& echo " ! install POST failed on $t" || echo " + installed on $t"
fi
done
}
echo "— catalog rows"
# name | version | targets (comma list, '-' = catalog only)
while IFS='|' read -r sw ver targets; do
[ -z "$sw" ] && continue
ensure "$(echo "$sw" | xargs)" "$(echo "$ver" | xargs)" "$(echo "$targets" | xargs)"
done <<EOF
Proxmox VE|9.2.5|pfv-tsys1,pfv-tsys3,pfv-tsys4,pfv-tsys5,pfv-tsys6,pfv-tsys7,pfv-tsys9
Wazuh Manager|4.14.7|tsys-siem
Wazuh Indexer|4.14.7|tsys-siem
Wazuh Dashboard|4.14.7|tsys-siem
Wazuh Agent|4.14.7-1|-
Docker Engine|unknown (GLPI Agent pending)|pfv-netinfra-01,pfv-netinfra-02,tsys-librenms,tsys-cloudron
Pi-hole FTL (v6)|unknown (GLPI Agent pending)|pfv-netinfra-01,pfv-netinfra-02
Technitium DNS Server|unknown (GLPI Agent pending)|pfv-netinfra-01,pfv-netinfra-02
ISC dhcpd|unknown (GLPI Agent pending)|pfv-netinfra-01,pfv-netinfra-02
nginx|unknown (GLPI Agent pending)|tsys-ca,tsys-librenms
LibreNMS|unknown (GLPI Agent pending)|tsys-librenms
NetDisco|unknown (GLPI Agent pending)|tsys-librenms
Oxidized|unknown (GLPI Agent pending)|tsys-librenms
SmokePing|unknown (GLPI Agent pending)|tsys-librenms
unpoller|unknown (GLPI Agent pending)|tsys-librenms
NUT (Network UPS Tools)|unknown (GLPI Agent pending)|-
Proxmox Backup Server|4.x (dev, VM 5104)|-
k3s|unknown (k8s lane)|-
Uptime Kuma|unknown (Cloudron app)|tsys-cloudron
GLPI|unknown (Cloudron app)|tsys-cloudron
Vaultwarden|unknown (Cloudron app)|tsys-cloudron
EOF
# Wazuh Agent installs: from the LIVE manager (ground truth), CI matched by name
if [ -r "${HOME}/.creds/wazuh.env" ] && [ "${SKIP_AGENTS:-0}" != 1 ]; then
# shellcheck disable=SC1090
. "${HOME}/.creds/wazuh.env"
echo "— wazuh agent installs (live manager)"
sid=$(sw_id "Wazuh Agent")
[ -z "$sid" ] && { echo " ! Wazuh Agent software missing"; exit 0; }
vid=$(ver_id "$sid" "4.14.7-1")
[ -z "$vid" ] && { echo " ! version 4.14.7-1 missing"; exit 0; }
TOKEN=$(curl -sS --max-time 20 -u "wazuh:${WAZUH_API_PASS}" -k \
-X POST "https://tsys-wazuh.knel.net:55000/security/user/authenticate?raw=true")
curl -sS --max-time 30 -k -H "Authorization: Bearer $TOKEN" \
"https://tsys-wazuh.knel.net:55000/agents?status=active&limit=500" \
| jq -r '.data.affected_items[].name' > /tmp/wz-agents.txt
while IFS= read -r a; do
[ "$a" = "000" ] && continue
cid=$(pc_id "$a")
if [ -z "$cid" ]; then echo " ! agent '$a' has no CI (skipped)"; continue; fi
if install_exists "$vid" "$cid"; then echo " = $a"; continue; fi
if [ "$DRY" = 1 ]; then echo " DRY: install on $a"; else
api POST Item_SoftwareVersion \
"{\"input\":{\"itemtype\":\"Computer\",\"items_id\":\"$cid\",\"softwareversions_id\":\"$vid\"}}" >/dev/null \
&& echo " + $a"
fi
done < /tmp/wz-agents.txt
fi
echo "— done"