[#389] GVM target import: gvm-script (gmp.send_command, lxml parse) + IANA port-list injection; feed jq rewrite (record-bound, no IFS collapse)
ci / audit (push) Successful in 24s
ci / audit (push) Successful in 24s
63/63 GLPI targets imported into gvmd (0 failures). Admin creds in ~/.creds/gvm-test.env. Import is idempotent (deletes glpi-* first). https://projects.knownelement.com/issues/389
This commit is contained in:
+10
-19
@@ -56,34 +56,25 @@ else
|
|||||||
fetch_inventory > "$INVENTORY_JSON" || exit 1
|
fetch_inventory > "$INVENTORY_JSON" || exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Normalize: one record per line with parsed IPs
|
case "$FORMAT" in
|
||||||
NORMALIZED=$(jq -r '
|
csv)
|
||||||
|
printf 'glpi_id,name,ts_ip,mgmt_ip,serial,last_inventory,agent_fed\n'
|
||||||
|
jq -r '
|
||||||
.[]
|
.[]
|
||||||
| select( ($ONLY_DYNAMIC == 0 or .is_dynamic == 1) )
|
| select( ($ONLY_DYNAMIC == 0 or .is_dynamic == 1) )
|
||||||
| ( .comment // "" ) as $c
|
| ( .comment // "" ) as $c
|
||||||
| ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts
|
| ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts
|
||||||
| ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt
|
| ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt
|
||||||
| select( $INCLUDE_NO_IP == 1 or $ts != "" or $mgmt != "" )
|
| select( $INCLUDE_NO_IP == 1 or $ts != "" or $mgmt != "" )
|
||||||
| [ (.id|tostring), .name, $ts, $mgmt, (.serial // "-"),
|
| [ (.id|tostring), .name, $ts, $mgmt, (.serial // ""),
|
||||||
(.last_inventory_update // "-"), ((.is_dynamic // 0)|tostring) ]
|
(.last_inventory_update // ""), ((.is_dynamic // 0)|tostring) ]
|
||||||
| @tsv' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON")
|
| @csv' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON"
|
||||||
|
|
||||||
[ -n "$NORMALIZED" ] || { echo "no scan targets parsed from GLPI" >&2; exit 2; }
|
|
||||||
|
|
||||||
case "$FORMAT" in
|
|
||||||
csv)
|
|
||||||
printf 'glpi_id,name,ts_ip,mgmt_ip,serial,last_inventory,agent_fed\n'
|
|
||||||
printf '%s\n' "$NORMALIZED"
|
|
||||||
;;
|
;;
|
||||||
gmp)
|
gmp)
|
||||||
printf '<!-- GMP create_target fragments generated from GLPI CMDB %s. Review hosts before importing. -->\n' "$(date -Is)"
|
printf '<!-- GMP create_target fragments generated from GLPI CMDB %s. Review hosts before importing. -->\n' "$(date -Is)"
|
||||||
printf '%s\n' "$NORMALIZED" | while IFS="$(printf '\t')" read -r id name ts mgmt _serial inv dyn; do
|
# jq emits the complete element per record (host list built in jq — avoids
|
||||||
hosts="$ts"
|
# bash read() IFS-collapse on empty TSV fields; record bound via . as $rec)
|
||||||
if [ -n "$mgmt" ]; then hosts="${hosts:+$hosts,}$mgmt"; fi
|
jq -r ' .[] | . as $rec | ( $rec.comment // "" ) as $c | ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts | ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt | [ $ts, $mgmt ] | map(select(length > 0)) | join(",") as $hosts | select( $hosts != "" ) | $rec.name as $n | "<create_target><name>glpi-\($rec.id)-\($n | gsub("[^a-zA-Z0-9.-]"; "-"))</name><hosts>\($hosts)</hosts><comment>GLPI #\($rec.id) agent=\($rec.is_dynamic // 0)</comment></create_target>"' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON"
|
||||||
[ -n "$hosts" ] || continue
|
|
||||||
printf '<create_target><name>glpi-%s-%s</name><hosts>%s</hosts><comment>GLPI #%s inv=%s agent=%s</comment><alive_tests>Scan Config Default</alive_tests></create_target>\n' \
|
|
||||||
"$id" "$(printf '%s' "$name" | tr -c '[:alnum:].-' '-')" "$hosts" "$id" "$inv" "$dyn"
|
|
||||||
done
|
|
||||||
;;
|
;;
|
||||||
*) echo "bad format: $FORMAT" >&2; exit 1 ;;
|
*) echo "bad format: $FORMAT" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import re
|
||||||
|
from lxml import etree
|
||||||
|
data = open('/tmp/targets.gmp').read()
|
||||||
|
cmds = re.findall(r'<create_target>.*?</create_target>', data, re.S)
|
||||||
|
|
||||||
|
pl = etree.fromstring(gmp.send_command('<get_port_lists filter="rows=-1"/>').encode())
|
||||||
|
plist_id = None
|
||||||
|
for p in pl.findall('.//port_list'):
|
||||||
|
if 'All IANA assigned TCP' in (p.findtext('name') or ''):
|
||||||
|
plist_id = p.get('id')
|
||||||
|
break
|
||||||
|
if not plist_id:
|
||||||
|
raise SystemExit('no IANA TCP port list found')
|
||||||
|
|
||||||
|
# clean previous glpi-* targets (idempotent re-import)
|
||||||
|
existing = etree.fromstring(gmp.send_command('<get_targets filter="rows=-1"/>').encode())
|
||||||
|
for t in existing.findall('.//target'):
|
||||||
|
if (t.findtext('name') or '').startswith('glpi-'):
|
||||||
|
gmp.send_command(f'<delete_target target_id="{t.get("id")}"/>')
|
||||||
|
|
||||||
|
ok = fail = 0
|
||||||
|
for c in cmds:
|
||||||
|
frag = etree.fromstring(c.encode())
|
||||||
|
el = etree.SubElement(frag, 'port_list')
|
||||||
|
el.set('id', plist_id)
|
||||||
|
root = etree.fromstring(gmp.send_command(etree.tostring(frag).decode()).encode())
|
||||||
|
st = root.get('status')
|
||||||
|
if st == '201':
|
||||||
|
ok += 1
|
||||||
|
else:
|
||||||
|
fail += 1
|
||||||
|
if fail <= 3:
|
||||||
|
resp = root.findtext('.//response')
|
||||||
|
print('FAIL', st, (resp or etree.tostring(root).decode())[:180])
|
||||||
|
print(f'imported={ok} failed={fail}')
|
||||||
Reference in New Issue
Block a user