chore: initial import from KNEL/PFVCluster@041d311 [#769]

Split per O&M lane work order. Full history: KNEL/PFVCluster.
https://projects.knownelement.com/issues/769#note-4152
This commit is contained in:
2026-09-03 21:39:15 -05:00
commit 5d02177e63
20 changed files with 1468 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/bash
#
# from-inventory.sh — convert the Discourse #307 system inventory (raw
# markdown of its wiki post) into the CMDB seed CSV [#705]
#
# Input: raw markdown of https://community.turnsys.com/t/307 (post 385),
# fetched e.g. via the discourse-cli raw-API escape hatch
# Output: cmdb/seed/systems.csv (normalized CI rows; v0 schema)
#
# Re-run whenever #307 changes; the CSV is the GLPI import candidate, the
# markdown topic stays the human-readable inventory.
#
set -euo pipefail
if [ $# -ne 1 ] || [ ! -r "$1" ]; then
echo "usage: $0 <inv307-raw.md >systems.csv" >&2
exit 2
fi
echo 'category,ci_name,host_node,vmid,specs,ts_ip,extra_ip,tuned,access,extra,status,source_section'
awk -F'|' '
function trim(s) { gsub(/^[ \t]+|[ \t]+$/, "", s); return s }
function strip(s) { gsub(/"/, "", s); return trim(s) }
function csv(s) { gsub(/"/, "", s); return "\"" trim(s)"\"" }
# canon(s) — apply the section-7 VM-name -> Tailscale/DNS-name map so
# ci_name always uses the canonical fleet name [#705]
function canon(s) { s = strip(s); return (s in map) ? map[s] : s }
/^## / {
sec = substr($0, 4)
sub(/\(.*/, "", sec)
gsub(/^[ \t]+|[ \t]+$/, "", sec)
next
}
/^\|/ {
# pass 1 (same file read twice): capture only the section-7 rename map
if (FNR == NR) {
if (sec ~ /^7\./) {
for (i = 1; i <= NF; i++) f[i] = trim($i)
if (f[2] == "" || f[2] ~ /^-+$/ || f[2] == "VM Name") next
key = f[2]; sub(/[ (].*/, "", key)
map[key] = trim(f[3])
}
next
}
if (sec ~ /^7\./) next
if (sec !~ /^[1-6]\./) next
for (i = 1; i <= NF; i++) f[i] = trim($i)
if (f[2] == "" || f[2] ~ /^-+$/) next
if (f[2] == "Host" || f[2] == "VMID" || f[2] == "Name" || f[2] == "DNS Name") next
status = (index($0, "RETIRED") > 0) ? "retired" : "active"
if (sec ~ /^1\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"hypervisor", csv(canon(f[2])), "", "",
csv(f[3] "; CPU " f[4] "; RAM " f[5]), csv(f[6]), csv(f[8]),
csv(f[9]), csv(f[10]), csv("mgmt " f[7]), status, csv("307 s1")
else if (sec ~ /^2\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"vm-prod", csv(canon(f[3])), csv(f[4]), csv(f[2]),
csv("CPU " f[5] "; RAM " f[6] "; TS? " f[7]), csv(f[8]), "",
csv(f[10]), csv(f[9]), "", status, csv("307 s2")
else if (sec ~ /^3\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"vm-sectestbed", csv(canon(f[3])), "pfv-tsys5", csv(f[2]), "", csv(f[5]),
"", csv(f[7]), csv(f[6]), "", status, csv("307 s3")
else if (sec ~ /^4\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"vm-preprod", csv(canon(f[3])), "pfv-tsys5", csv(f[2]), "", csv(f[5]),
"", csv(f[7]), csv(f[6]), "", status, csv("307 s4")
else if (sec ~ /^5\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"physical-sbc", csv(canon(f[2])), "", "", "", csv(f[3]), "",
"", csv(f[5]), csv("dns " f[4]), status, csv("307 s5")
else if (sec ~ /^6\./)
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"stale-dns-record", csv(f[2]), "", "", "", "", "",
"", "", "", "retired", csv("307 s6: " f[4])
}
' "$1" "$1"
+169
View File
@@ -0,0 +1,169 @@
#!/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"
+76
View File
@@ -0,0 +1,76 @@
category,ci_name,host_node,vmid,specs,ts_ip,extra_ip,tuned,access,extra,status,source_section
hypervisor,"pfv-tsys1",,,"OptiPlex 9020; CPU 8c; RAM 31Gi","100.121.189.98","10.100.100.1","virtual-host","root","mgmt 192.168.3.11",active,"307 s1"
hypervisor,"pfv-tsys3",,,"Precision 7510; CPU 8c; RAM 31Gi","100.66.186.10","10.100.100.3","virtual-host","root","mgmt 192.168.2.5",active,"307 s1"
hypervisor,"pfv-tsys4",,,"Precision T1700; CPU 8c; RAM 15Gi","100.70.77.93","10.100.100.4","virtual-host","root","mgmt 192.168.3.251",active,"307 s1"
hypervisor,"pfv-tsys5",,,"Precision T7500; CPU 8c; RAM 94Gi","100.87.135.12","10.100.100.5","virtual-host","root","mgmt 192.168.3.250",active,"307 s1"
hypervisor,"pfv-tsys6",,,"PowerEdge R610; CPU 16c; RAM 125Gi","100.73.35.111","(bond)","virtual-host","root","mgmt 192.168.3.169",active,"307 s1"
hypervisor,"pfv-tsys7",,,"PowerEdge R620; CPU 24c; RAM 188Gi","100.110.146.27","10.100.100.7","virtual-host","root","mgmt 192.168.0.250",active,"307 s1"
hypervisor,"pfv-tsys9",,,"OptiPlex 7080; CPU 12c; RAM 23Gi","100.101.158.76","10.100.100.9","virtual-host","root","mgmt 192.168.3.58",active,"307 s1"
vm-prod,"pfv-bms","tsys1","100","CPU 4c; RAM 6GB; TS? Yes","100.67.108.125",,"-","SSH 22222 (LAN: pfv-bms-lan.knel.net)",,active,"307 s2"
vm-prod,"tsys-ca","tsys1","101","CPU 2c; RAM 4GB; TS? No (DNS: 100.102.96.24)","-",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-cnode1","tsys1","102","CPU 2c; RAM 4GB; TS? Yes","100.125.134.53",,"network-latency","localuser",,active,"307 s2"
vm-prod,"pfv-netinfra-01","tsys1","103","CPU 2c; RAM 4GB; TS? Yes","100.70.181.72",,"network-latency","localuser",,active,"307 s2"
vm-prod,"tsys-librenms","tsys1","104","CPU 2c; RAM 4GB; TS? No (DNS: 100.86.204.77)","-",,"-","NO",,active,"307 s2"
vm-prod,"tsys-proxmox-datacenter","tsys1","105","CPU 2c; RAM 2GB; TS? No (DNS: 100.125.183.68)","-",,"-","NO",,active,"307 s2"
vm-prod,"tsys-ucs-01","tsys1","108","CPU 2c; RAM 6GB; TS? No (DNS: 100.109.13.110)","-",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-wnode-tsys3","tsys3","313","CPU 8c; RAM 28GB; TS? Yes","100.126.9.112",,"-","localuser",,active,"307 s2"
vm-prod,"pfv-k8s-wnode-tsys5","tsys5","500","CPU 2c; RAM 12GB; TS? Yes","100.122.252.116",,"-","localuser",,retired,"307 s2"
vm-prod,"devbox-cloudron","tsys5","501","CPU 2c; RAM 4GB; TS? Yes","100.119.72.25",,"-","NO",,active,"307 s2"
vm-prod,"hfnoc-uisp-preprod","tsys5","515","CPU 2c; RAM 2GB; TS? No","-",,"-","NO",,active,"307 s2"
vm-prod,"ultix-streaming","tsys5","5111","CPU 4c; RAM 25GB; TS? Yes","100.101.187.119",,"-","NO (do-not-reboot)",,active,"307 s2"
vm-prod,"ultix-offstage","tsys5","5112","CPU 4c; RAM 22GB; TS? Yes","100.70.119.59",,"-","localuser (do-not-reboot)",,retired,"307 s2"
vm-prod,"tsys-awx","tsys6","600","CPU 2c; RAM 12GB; TS? No (DNS: 100.91.39.53)","-",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-wnode-tsys6","tsys6","601","CPU 2c; RAM 98GB; TS? Yes","100.83.49.75",,"-","localuser",,active,"307 s2"
vm-prod,"pfv-rr-middleware-02","tsys6","602","CPU 2c; RAM 6GB; TS? Yes","100.93.47.4",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-cnode3","tsys6","603","CPU 4c; RAM 4GB; TS? Yes","100.106.222.18",,"network-latency","localuser",,active,"307 s2"
vm-prod,"tsys-proxmox-mailgw-01","tsys6","604","CPU 2c; RAM 4GB; TS? No (DNS: 100.68.129.71)","-",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-wnode-tsys7","tsys7","701","CPU 2c; RAM 98GB; TS? Yes","100.119.240.11",,"-","localuser",,active,"307 s2"
vm-prod,"hfnoc-uisp","tsys7","702","CPU 2c; RAM 8GB; TS? Yes","100.94.188.89",,"-","NO",,active,"307 s2"
vm-prod,"pfv-rr-middleware-01","tsys7","703","CPU 2c; RAM 6GB; TS? Yes","100.106.54.59",,"-","NO",,active,"307 s2"
vm-prod,"pfv-k8s-cnode2","tsys7","705","CPU 4c; RAM 4GB; TS? Yes","100.109.34.72",,"network-latency","localuser",,active,"307 s2"
vm-prod,"kali-rd","tsys7","706","CPU 2c; RAM 6GB; TS? Yes","100.105.136.23",,"-","NO",,active,"307 s2"
vm-prod,"tsys-siem","tsys7","707","CPU 2c; RAM 8GB; TS? No (DNS: 100.72.35.113)","-",,"-","NO",,active,"307 s2"
vm-prod,"kali-tsys","tsys7","708","CPU 4c; RAM 6GB; TS? Yes","100.82.30.115",,"-","NO",,active,"307 s2"
vm-prod,"tsys-voip","tsys7","709","CPU 4c; RAM 4GB; TS? No (DNS: 100.83.126.67)","-",,"-","NO",,active,"307 s2"
vm-prod,"tsys-umbrel","tsys7","710","CPU 2c; RAM 26GB; TS? No (DNS: 100.66.182.14)","-",,"-","NO",,active,"307 s2"
vm-prod,"tsys-proxmox-mailgw-02","tsys7","711","CPU 2c; RAM 4GB; TS? No (DNS: 100.126.29.88)","-",,"-","NO",,active,"307 s2"
vm-prod,"tsys-ucs-02","tsys9","902","CPU 2c; RAM 4GB; TS? No (DNS: 100.68.10.17)","-",,"-","NO",,active,"307 s2"
vm-prod,"pfv-netinfra-02","tsys9","904","CPU 2c; RAM 4GB; TS? Yes","100.71.171.20",,"network-latency","localuser",,active,"307 s2"
vm-prod,"pfv-k8s-wnode-tsys9","tsys9","905","CPU 4c; RAM 14GB; TS? Yes","100.95.201.66",,"-","localuser",,active,"307 s2"
vm-sectestbed,"sectestbed-sandbox",pfv-tsys5,"5000",,"100.64.20.60",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-siem",pfv-tsys5,"5101",,"100.108.121.18",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-proxmox-pve",pfv-tsys5,"5102",,"100.80.72.71",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-proxmox-datacenter",pfv-tsys5,"5103",,"100.94.1.34",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-proxmox-pbs",pfv-tsys5,"5104",,"100.127.238.29",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-awx",pfv-tsys5,"5105",,"100.64.56.24",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-k8s-cnode",pfv-tsys5,"5106",,"100.68.155.111",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-k8s-wnode",pfv-tsys5,"5107",,"100.107.110.89",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-librenms",pfv-tsys5,"5108",,"100.92.94.87",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-netinfra",pfv-tsys5,"5109",,"100.100.220.117",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-cloudron",pfv-tsys5,"51011",,"100.97.140.105",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-hfnoc-uisp",pfv-tsys5,"51012",,"100.101.168.80",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-rancherplatform",pfv-tsys5,"51013",,"100.88.171.10",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-proxmox-mailgw",pfv-tsys5,"51014",,"100.117.24.21",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-ca",pfv-tsys5,"51015",,"100.113.245.124",,"-","NO",,active,"307 s3"
vm-sectestbed,"sectestbed-voip",pfv-tsys5,"51016",,"100.86.176.105",,"-","NO",,active,"307 s3"
vm-preprod,"preprod-awx",pfv-tsys5,"53100",,"100.77.216.36",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-siem",pfv-tsys5,"53101",,"100.98.162.14",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-rancherplatform",pfv-tsys5,"53102",,"100.126.231.121",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-proxmox-mailgw",pfv-tsys5,"53103",,"100.114.9.49",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-ca",pfv-tsys5,"53104",,"100.94.119.5",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-proxmox-datacenter",pfv-tsys5,"53105",,"100.101.250.10",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-librenms",pfv-tsys5,"53106",,"100.79.52.34",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-voip",pfv-tsys5,"53107",,"100.109.99.109",,"-","NO",,active,"307 s4"
vm-preprod,"preprod-cloudron",pfv-tsys5,"53108",,"100.95.69.89",,"-","NO",,active,"307 s4"
physical-sbc,"pfvsvrpi",,,,"100.91.151.113",,,"Raspberry Pi","dns pfvsvrpi.knel.net",active,"307 s5"
physical-sbc,"pfv-jetson-nano-1",,,,"100.82.230.119",,,"NVIDIA Jetson","dns pfv-jetson-nano-1.knel.net",active,"307 s5"
physical-sbc,"subopi-dev-3",,,,"100.64.231.65",,,"SBC (Pi?)","dns subopi-dev-3.knel.net",active,"307 s5"
physical-sbc,"subopi-dev-4",,,,"-",,,"SBC","dns subopi-dev-4.knel.net (100.65.224.85)",active,"307 s5"
physical-sbc,"subopi3",,,,"-",,,"SBC","dns subopi3.knel.net (100.93.17.77)",active,"307 s5"
physical-sbc,"stlpc-artroom",,,,"100.120.77.113",,,"Linux PC","dns stlpc-artroom.knel.net",active,"307 s5"
physical-sbc,"stlpc-garage",,,,"100.72.192.22",,,"Linux PC","dns stlpc-garage.knel.net",active,"307 s5"
physical-sbc,"stlp-3dscanner",,,,"100.125.14.37",,,"OFFLINE 184d","dns stlp-3dscanner.knel.net",active,"307 s5"
physical-sbc,"tailscale-router",,,,"-",,,"**RETIRED 2026-09-02** — subnet-router role replaced by pfv-netinfra-01/02; host removed from tailnet; no DNS records remain","dns ~~tailscale-router.knel.net (100.103.48.57)~~",retired,"307 s5"
physical-sbc,"netbird",,,,"100.123.45.23",,,"NetBird VPN","dns -",active,"307 s5"
physical-sbc,"pfv-proxmox-backup-server",,,,"100.114.81.107",,,"PBS","dns pfv-proxmox-backup-server.knel.net",active,"307 s5"
physical-sbc,"stlpc-bizoffice",,,,"100.96.130.53",,,"WINDOWS (not Linux)","dns stlpc-bizoffice.knel.net",active,"307 s5"
1 category ci_name host_node vmid specs ts_ip extra_ip tuned access extra status source_section
2 hypervisor pfv-tsys1 OptiPlex 9020; CPU 8c; RAM 31Gi 100.121.189.98 10.100.100.1 virtual-host root mgmt 192.168.3.11 active 307 s1
3 hypervisor pfv-tsys3 Precision 7510; CPU 8c; RAM 31Gi 100.66.186.10 10.100.100.3 virtual-host root mgmt 192.168.2.5 active 307 s1
4 hypervisor pfv-tsys4 Precision T1700; CPU 8c; RAM 15Gi 100.70.77.93 10.100.100.4 virtual-host root mgmt 192.168.3.251 active 307 s1
5 hypervisor pfv-tsys5 Precision T7500; CPU 8c; RAM 94Gi 100.87.135.12 10.100.100.5 virtual-host root mgmt 192.168.3.250 active 307 s1
6 hypervisor pfv-tsys6 PowerEdge R610; CPU 16c; RAM 125Gi 100.73.35.111 (bond) virtual-host root mgmt 192.168.3.169 active 307 s1
7 hypervisor pfv-tsys7 PowerEdge R620; CPU 24c; RAM 188Gi 100.110.146.27 10.100.100.7 virtual-host root mgmt 192.168.0.250 active 307 s1
8 hypervisor pfv-tsys9 OptiPlex 7080; CPU 12c; RAM 23Gi 100.101.158.76 10.100.100.9 virtual-host root mgmt 192.168.3.58 active 307 s1
9 vm-prod pfv-bms tsys1 100 CPU 4c; RAM 6GB; TS? Yes 100.67.108.125 - SSH 22222 (LAN: pfv-bms-lan.knel.net) active 307 s2
10 vm-prod tsys-ca tsys1 101 CPU 2c; RAM 4GB; TS? No (DNS: 100.102.96.24) - - NO active 307 s2
11 vm-prod pfv-k8s-cnode1 tsys1 102 CPU 2c; RAM 4GB; TS? Yes 100.125.134.53 network-latency localuser active 307 s2
12 vm-prod pfv-netinfra-01 tsys1 103 CPU 2c; RAM 4GB; TS? Yes 100.70.181.72 network-latency localuser active 307 s2
13 vm-prod tsys-librenms tsys1 104 CPU 2c; RAM 4GB; TS? No (DNS: 100.86.204.77) - - NO active 307 s2
14 vm-prod tsys-proxmox-datacenter tsys1 105 CPU 2c; RAM 2GB; TS? No (DNS: 100.125.183.68) - - NO active 307 s2
15 vm-prod tsys-ucs-01 tsys1 108 CPU 2c; RAM 6GB; TS? No (DNS: 100.109.13.110) - - NO active 307 s2
16 vm-prod pfv-k8s-wnode-tsys3 tsys3 313 CPU 8c; RAM 28GB; TS? Yes 100.126.9.112 - localuser active 307 s2
17 vm-prod pfv-k8s-wnode-tsys5 tsys5 500 CPU 2c; RAM 12GB; TS? Yes 100.122.252.116 - localuser retired 307 s2
18 vm-prod devbox-cloudron tsys5 501 CPU 2c; RAM 4GB; TS? Yes 100.119.72.25 - NO active 307 s2
19 vm-prod hfnoc-uisp-preprod tsys5 515 CPU 2c; RAM 2GB; TS? No - - NO active 307 s2
20 vm-prod ultix-streaming tsys5 5111 CPU 4c; RAM 25GB; TS? Yes 100.101.187.119 - NO (do-not-reboot) active 307 s2
21 vm-prod ultix-offstage tsys5 5112 CPU 4c; RAM 22GB; TS? Yes 100.70.119.59 - localuser (do-not-reboot) retired 307 s2
22 vm-prod tsys-awx tsys6 600 CPU 2c; RAM 12GB; TS? No (DNS: 100.91.39.53) - - NO active 307 s2
23 vm-prod pfv-k8s-wnode-tsys6 tsys6 601 CPU 2c; RAM 98GB; TS? Yes 100.83.49.75 - localuser active 307 s2
24 vm-prod pfv-rr-middleware-02 tsys6 602 CPU 2c; RAM 6GB; TS? Yes 100.93.47.4 - NO active 307 s2
25 vm-prod pfv-k8s-cnode3 tsys6 603 CPU 4c; RAM 4GB; TS? Yes 100.106.222.18 network-latency localuser active 307 s2
26 vm-prod tsys-proxmox-mailgw-01 tsys6 604 CPU 2c; RAM 4GB; TS? No (DNS: 100.68.129.71) - - NO active 307 s2
27 vm-prod pfv-k8s-wnode-tsys7 tsys7 701 CPU 2c; RAM 98GB; TS? Yes 100.119.240.11 - localuser active 307 s2
28 vm-prod hfnoc-uisp tsys7 702 CPU 2c; RAM 8GB; TS? Yes 100.94.188.89 - NO active 307 s2
29 vm-prod pfv-rr-middleware-01 tsys7 703 CPU 2c; RAM 6GB; TS? Yes 100.106.54.59 - NO active 307 s2
30 vm-prod pfv-k8s-cnode2 tsys7 705 CPU 4c; RAM 4GB; TS? Yes 100.109.34.72 network-latency localuser active 307 s2
31 vm-prod kali-rd tsys7 706 CPU 2c; RAM 6GB; TS? Yes 100.105.136.23 - NO active 307 s2
32 vm-prod tsys-siem tsys7 707 CPU 2c; RAM 8GB; TS? No (DNS: 100.72.35.113) - - NO active 307 s2
33 vm-prod kali-tsys tsys7 708 CPU 4c; RAM 6GB; TS? Yes 100.82.30.115 - NO active 307 s2
34 vm-prod tsys-voip tsys7 709 CPU 4c; RAM 4GB; TS? No (DNS: 100.83.126.67) - - NO active 307 s2
35 vm-prod tsys-umbrel tsys7 710 CPU 2c; RAM 26GB; TS? No (DNS: 100.66.182.14) - - NO active 307 s2
36 vm-prod tsys-proxmox-mailgw-02 tsys7 711 CPU 2c; RAM 4GB; TS? No (DNS: 100.126.29.88) - - NO active 307 s2
37 vm-prod tsys-ucs-02 tsys9 902 CPU 2c; RAM 4GB; TS? No (DNS: 100.68.10.17) - - NO active 307 s2
38 vm-prod pfv-netinfra-02 tsys9 904 CPU 2c; RAM 4GB; TS? Yes 100.71.171.20 network-latency localuser active 307 s2
39 vm-prod pfv-k8s-wnode-tsys9 tsys9 905 CPU 4c; RAM 14GB; TS? Yes 100.95.201.66 - localuser active 307 s2
40 vm-sectestbed sectestbed-sandbox pfv-tsys5 5000 100.64.20.60 - NO active 307 s3
41 vm-sectestbed sectestbed-siem pfv-tsys5 5101 100.108.121.18 - NO active 307 s3
42 vm-sectestbed sectestbed-proxmox-pve pfv-tsys5 5102 100.80.72.71 - NO active 307 s3
43 vm-sectestbed sectestbed-proxmox-datacenter pfv-tsys5 5103 100.94.1.34 - NO active 307 s3
44 vm-sectestbed sectestbed-proxmox-pbs pfv-tsys5 5104 100.127.238.29 - NO active 307 s3
45 vm-sectestbed sectestbed-awx pfv-tsys5 5105 100.64.56.24 - NO active 307 s3
46 vm-sectestbed sectestbed-k8s-cnode pfv-tsys5 5106 100.68.155.111 - NO active 307 s3
47 vm-sectestbed sectestbed-k8s-wnode pfv-tsys5 5107 100.107.110.89 - NO active 307 s3
48 vm-sectestbed sectestbed-librenms pfv-tsys5 5108 100.92.94.87 - NO active 307 s3
49 vm-sectestbed sectestbed-netinfra pfv-tsys5 5109 100.100.220.117 - NO active 307 s3
50 vm-sectestbed sectestbed-cloudron pfv-tsys5 51011 100.97.140.105 - NO active 307 s3
51 vm-sectestbed sectestbed-hfnoc-uisp pfv-tsys5 51012 100.101.168.80 - NO active 307 s3
52 vm-sectestbed sectestbed-rancherplatform pfv-tsys5 51013 100.88.171.10 - NO active 307 s3
53 vm-sectestbed sectestbed-proxmox-mailgw pfv-tsys5 51014 100.117.24.21 - NO active 307 s3
54 vm-sectestbed sectestbed-ca pfv-tsys5 51015 100.113.245.124 - NO active 307 s3
55 vm-sectestbed sectestbed-voip pfv-tsys5 51016 100.86.176.105 - NO active 307 s3
56 vm-preprod preprod-awx pfv-tsys5 53100 100.77.216.36 - NO active 307 s4
57 vm-preprod preprod-siem pfv-tsys5 53101 100.98.162.14 - NO active 307 s4
58 vm-preprod preprod-rancherplatform pfv-tsys5 53102 100.126.231.121 - NO active 307 s4
59 vm-preprod preprod-proxmox-mailgw pfv-tsys5 53103 100.114.9.49 - NO active 307 s4
60 vm-preprod preprod-ca pfv-tsys5 53104 100.94.119.5 - NO active 307 s4
61 vm-preprod preprod-proxmox-datacenter pfv-tsys5 53105 100.101.250.10 - NO active 307 s4
62 vm-preprod preprod-librenms pfv-tsys5 53106 100.79.52.34 - NO active 307 s4
63 vm-preprod preprod-voip pfv-tsys5 53107 100.109.99.109 - NO active 307 s4
64 vm-preprod preprod-cloudron pfv-tsys5 53108 100.95.69.89 - NO active 307 s4
65 physical-sbc pfvsvrpi 100.91.151.113 Raspberry Pi dns pfvsvrpi.knel.net active 307 s5
66 physical-sbc pfv-jetson-nano-1 100.82.230.119 NVIDIA Jetson dns pfv-jetson-nano-1.knel.net active 307 s5
67 physical-sbc subopi-dev-3 100.64.231.65 SBC (Pi?) dns subopi-dev-3.knel.net active 307 s5
68 physical-sbc subopi-dev-4 - SBC dns subopi-dev-4.knel.net (100.65.224.85) active 307 s5
69 physical-sbc subopi3 - SBC dns subopi3.knel.net (100.93.17.77) active 307 s5
70 physical-sbc stlpc-artroom 100.120.77.113 Linux PC dns stlpc-artroom.knel.net active 307 s5
71 physical-sbc stlpc-garage 100.72.192.22 Linux PC dns stlpc-garage.knel.net active 307 s5
72 physical-sbc stlp-3dscanner 100.125.14.37 OFFLINE 184d dns stlp-3dscanner.knel.net active 307 s5
73 physical-sbc tailscale-router - **RETIRED 2026-09-02** — subnet-router role replaced by pfv-netinfra-01/02; host removed from tailnet; no DNS records remain dns ~~tailscale-router.knel.net (100.103.48.57)~~ retired 307 s5
74 physical-sbc netbird 100.123.45.23 NetBird VPN dns - active 307 s5
75 physical-sbc pfv-proxmox-backup-server 100.114.81.107 PBS dns pfv-proxmox-backup-server.knel.net active 307 s5
76 physical-sbc stlpc-bizoffice 100.96.130.53 WINDOWS (not Linux) dns stlpc-bizoffice.knel.net active 307 s5
+104
View File
@@ -0,0 +1,104 @@
#!/bin/bash
#
# to-glpi.sh — seed GLPI from the CMDB CSV + create the scoped agent user [#705]
#
# Prereqs: ~/.creds/glpi.env with GLPI_URL, GLPI_APP_TOKEN, and a VALID
# GLPI_USER_TOKEN (glpi admin). Run cmdb/seed/from-inventory.sh first
# to produce systems.csv.
#
# Usage:
# to-glpi.sh [--dry-run] [systems.csv]
#
# What it does:
# 1. initSession as the admin user token.
# 2. Creates local user 'cmdb' (Read-Only profile, random password +
# random api_token) — the scoped agent identity per Charles's ruling.
# Writes ~/.creds/glpi-agent.env (0600). Skipped if the user exists.
# 3. Imports systems.csv rows as GLPI Computers (batch; skips names that
# already exist). comment carries category/specs/ts_ip provenance.
#
set -euo pipefail
DRY=0
[ "${1:-}" = "--dry-run" ] && { DRY=1; shift; }
CSV="${1:-$(dirname "$0")/systems.csv}"
[ -r "$CSV" ] || { echo "no CSV at $CSV" >&2; exit 2; }
CREDS="${HOME}/.creds/glpi.env"
# shellcheck disable=SC1090
. "$CREDS"
: "${GLPI_URL:?}" "${GLPI_APP_TOKEN:?}" "${GLPI_USER_TOKEN:?}"
API="$GLPI_URL/apirest.php"
hdr() { printf 'Content-Type: application/json\nApp-Token: %s\nSession-Token: %s\n' "$GLPI_APP_TOKEN" "$SESSION"; }
echo "— initSession"
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 (${#SESSION} chars)"
# --- 2. scoped agent user -------------------------------------------------
if curl -sS -H "$(hdr)" "$API/User?searchText%5Bname%5D=cmdb" | jq -e 'if type=="object" then (.totalcount > 0) else (length > 0) end' >/dev/null; then
echo "— user 'cmdb' already exists (skipping create)"
else
AGENT_PASS=$(head -c 24 /dev/urandom | base64 | tr -d '/+=')
AGENT_TOKEN=$(head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n')
if [ "$DRY" = "1" ]; then
echo "— DRY: would create user cmdb"
else
UID_JSON=$(curl -sS -X POST -H "$(hdr)" -d '{"input":{"name":"cmdb","realname":"CMDB agent (core-IT)","password":"'"$AGENT_PASS"'","api_token":"'"$AGENT_TOKEN"'"}}' "$API/User")
NEWUID=$(echo "$UID_JSON" | jq -r '.id // .users_id // empty')
echo " created users_id=$NEWUID"
PROF_ID=$(curl -sS -H "$(hdr)" "$API/Profile?range=0-50" | jq -r '.[] | select(.name=="Read-Only") | .id' | head -1)
[ -n "$PROF_ID" ] && curl -sS -X POST -H "$(hdr)" \
-d '{"input":{"users_id":"'"$NEWUID"'","profiles_id":"'"$PROF_ID"'","entities_id":0,"is_recursive":1}}' \
"$API/Profile_User" > /dev/null && echo " profile Read-Only ($PROF_ID) @ root entity"
umask 077
printf 'GLPI_URL=%s\nGLPI_APP_TOKEN=%s\nGLPI_USER_TOKEN=%s\n' \
"$GLPI_URL" "$GLPI_APP_TOKEN" "$AGENT_TOKEN" > "${HOME}/.creds/glpi-agent.env"
echo " agent creds written to ~/.creds/glpi-agent.env (0600)"
fi
fi
# --- 3. seed import -------------------------------------------------------
echo "— existing Computers"
EXIST=$(curl -sS -H "$(hdr)" "$API/Computer?range=0-999&is_deleted=0" | jq -r '[.[].name] | join("\n")')
# CSV -> JSON objects (pure jq; header names become keys)
jq -Rn '
def strip: gsub("^\"";"") | gsub("\"$";"");
(input | split(",") | map(strip)) as $keys |
[ inputs | split(",") | map(strip) as $v |
reduce range(0; ($keys | length)) as $i
({}; . + {($keys[$i]): ($v[$i] // "")}) ]
' "$CSV" > /tmp/glpi-rows.json
# GLPI Computer input objects; skip names already present
jq -c --arg existing "$EXIST" '
[ .[] | select(.ci_name != "") | select(.ci_name as $n | ($existing | split("\n")) | index($n) | not)]
' /tmp/glpi-rows.json > /tmp/glpi-batch.json
# Map to GLPI Computer fields
jq -c '[ .[] | {name: .ci_name,
comment: ((.category // "") + " | " + (.specs // "") + " | ts=" + (.ts_ip // "") +
" | " + (.extra // "") + " | src=" + (.source_section // "") +
" | status=" + (.status // ""))} ]' /tmp/glpi-batch.json > /tmp/glpi-input.json
mv /tmp/glpi-input.json /tmp/glpi-batch.json
TOTAL=$(jq 'length' /tmp/glpi-batch.json)
skipped=$(( $(wc -l < "$CSV") - 1 - TOTAL ))
echo "— import: $TOTAL to create, $skipped already present"
if [ "$DRY" = "1" ]; then echo "— DRY: no changes made"; exit 0; fi
# Batch-create in chunks of 25 (API limits)
SPLIT=25; i=0
while [ "$i" -lt "$TOTAL" ]; do
jq ".[$i:$((i+SPLIT))]" /tmp/glpi-batch.json > /tmp/glpi-chunk.json
curl -sS -X POST -H "$(hdr)" \
-d "$(jq -c '{input: .}' /tmp/glpi-chunk.json)" "$API/Computer" \
| jq -r 'if type=="array" then " +\(length) created" else " ERR: \(tostring)" end'
i=$((i+SPLIT))
done
echo "done — verify count via GET /Computer?range=0-200"