Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74cc660dd5 | |||
| 89eb1e7bae | |||
| 78e8e0886f | |||
| 08c6a089af | |||
| 06ffc605f7 | |||
| a3a3fe1d49 | |||
| 80a8b22a35 |
@@ -20,6 +20,18 @@ NB_URL="${NB_URL:-http://netbox.gt-tiso.ikeja.co.za}"
|
|||||||
# Tip: Prefer setting NB_TOKEN via environment. Leaving default blank avoids accidental leaks.
|
# Tip: Prefer setting NB_TOKEN via environment. Leaving default blank avoids accidental leaks.
|
||||||
NB_TOKEN="${NB_TOKEN:-}"
|
NB_TOKEN="${NB_TOKEN:-}"
|
||||||
|
|
||||||
|
# --- Optional performance knobs ---
|
||||||
|
# NB_PREREAD=1 enables extra GETs to print pre-change values (more load on NetBox).
|
||||||
|
NB_PREREAD="${NB_PREREAD:-0}"
|
||||||
|
USE_TAGS="${USE_TAGS:-yes}"
|
||||||
|
|
||||||
|
# Device ID cache (per worker process)
|
||||||
|
# Cache entries are considered fresh for 60 minutes. After that, they may be used up to an
|
||||||
|
# additional random 3-10 minutes ("stale window") before a refresh is attempted.
|
||||||
|
NB_IDCACHE_TTL_SECS="${NB_IDCACHE_TTL_SECS:-3600}"
|
||||||
|
NB_IDCACHE_STALE_MIN_SECS="${NB_IDCACHE_STALE_MIN_SECS:-180}"
|
||||||
|
NB_IDCACHE_STALE_MAX_SECS="${NB_IDCACHE_STALE_MAX_SECS:-600}"
|
||||||
|
|
||||||
log() { echo "[netbox-reporter] $*"; }
|
log() { echo "[netbox-reporter] $*"; }
|
||||||
warn(){ echo "[netbox-reporter][WARN] $*" >&2; }
|
warn(){ echo "[netbox-reporter][WARN] $*" >&2; }
|
||||||
err() { echo "[netbox-reporter][ERROR] $*" >&2; }
|
err() { echo "[netbox-reporter][ERROR] $*" >&2; }
|
||||||
@@ -33,6 +45,54 @@ fi
|
|||||||
# URL-encode for RabbitMQ HTTP API paths
|
# URL-encode for RabbitMQ HTTP API paths
|
||||||
urlenc() { printf '%s' "$1" | sed -e 's, ,%20,g' -e 's,/,%2F,g'; }
|
urlenc() { printf '%s' "$1" | sed -e 's, ,%20,g' -e 's,/,%2F,g'; }
|
||||||
|
|
||||||
|
# --- helpers ---
|
||||||
|
now_epoch() { date +%s; }
|
||||||
|
rand_between() {
|
||||||
|
local min="$1" max="$2"
|
||||||
|
# inclusive range
|
||||||
|
echo $(( min + (RANDOM % (max - min + 1)) ))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Device ID cache (associative arrays require bash 4+)
|
||||||
|
declare -A NB_DEV_ID_CACHE=()
|
||||||
|
declare -A NB_DEV_ID_CACHE_EXPIRES=()
|
||||||
|
declare -A NB_DEV_ID_CACHE_STALE_UNTIL=()
|
||||||
|
|
||||||
|
nb_get_device_id_cached() {
|
||||||
|
local name="$1"
|
||||||
|
local now exp stale id jitter
|
||||||
|
|
||||||
|
now="$(now_epoch)"
|
||||||
|
id="${NB_DEV_ID_CACHE[$name]:-}"
|
||||||
|
exp="${NB_DEV_ID_CACHE_EXPIRES[$name]:-0}"
|
||||||
|
stale="${NB_DEV_ID_CACHE_STALE_UNTIL[$name]:-0}"
|
||||||
|
|
||||||
|
# Fresh cache hit
|
||||||
|
if [[ -n "$id" && "$now" -lt "$exp" ]]; then
|
||||||
|
printf '%s' "$id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stale-but-usable cache hit (during jitter window): return cached ID, don't refresh yet
|
||||||
|
if [[ -n "$id" && "$now" -ge "$exp" && "$now" -lt "$stale" ]]; then
|
||||||
|
printf '%s' "$id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cache miss or refresh time: refresh via NetBox
|
||||||
|
id="$(nb_find_device_id "$name")" || true
|
||||||
|
if [[ -n "$id" ]]; then
|
||||||
|
NB_DEV_ID_CACHE["$name"]="$id"
|
||||||
|
NB_DEV_ID_CACHE_EXPIRES["$name"]=$(( now + NB_IDCACHE_TTL_SECS ))
|
||||||
|
jitter="$(rand_between "$NB_IDCACHE_STALE_MIN_SECS" "$NB_IDCACHE_STALE_MAX_SECS")"
|
||||||
|
NB_DEV_ID_CACHE_STALE_UNTIL["$name"]=$(( now + NB_IDCACHE_TTL_SECS + jitter ))
|
||||||
|
printf '%s' "$id"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# Simple RabbitMQ HTTP call
|
# Simple RabbitMQ HTTP call
|
||||||
rmq_api() {
|
rmq_api() {
|
||||||
local method="$1" path="$2" data="${3:-}"
|
local method="$1" path="$2" data="${3:-}"
|
||||||
@@ -51,13 +111,38 @@ nb_find_device_id() {
|
|||||||
resp="$(curl -sS \
|
resp="$(curl -sS \
|
||||||
-H "Authorization: Token $NB_TOKEN" \
|
-H "Authorization: Token $NB_TOKEN" \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
"$NB_URL/api/dcim/devices/?name=$(printf '%s' "$name" | jq -sRr @uri)&limit=1")" || return 1
|
"$NB_URL/api/dcim/devices/?name=$(printf '%s' "$name" | jq -sRr @uri)&limit=1&fields=id")" || return 1
|
||||||
jq -r '.results[0].id // empty' <<<"$resp"
|
jq -r '.results[0].id // empty' <<<"$resp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- READ a custom field value for a device (prints current value or empty if unset) ---
|
||||||
|
nb_read_custom_field() {
|
||||||
|
local dev_id="$1" key="$2"
|
||||||
|
local resp
|
||||||
|
resp="$(curl -sS \
|
||||||
|
-H "Authorization: Token $NB_TOKEN" \
|
||||||
|
-H "Accept: application/json" \
|
||||||
|
"$NB_URL/api/dcim/devices/$dev_id/?fields=custom_fields")" || return 1
|
||||||
|
jq -r --arg k "$key" '
|
||||||
|
(.custom_fields[$k] // empty)
|
||||||
|
| (if type=="object" or type=="array" then tojson else . end)
|
||||||
|
' <<<"$resp"
|
||||||
|
}
|
||||||
|
|
||||||
# PATCH NetBox custom fields (update_wo_restart)
|
# PATCH NetBox custom fields (update_wo_restart)
|
||||||
nb_patch_update_wo_restart() {
|
nb_patch_update_wo_restart() {
|
||||||
local dev_id="$1" update_progress="$2" updating_to="$3"
|
local dev_id="$1" update_progress="$2" updating_to="$3"
|
||||||
|
|
||||||
|
# Pre-read current values and print (optional; enable with NB_PREREAD=1)
|
||||||
|
local prev_p prev_u
|
||||||
|
if [[ "${NB_PREREAD}" == "1" ]]; then
|
||||||
|
prev_p="$(nb_read_custom_field "$dev_id" "update_progress" || true)"
|
||||||
|
prev_u="$(nb_read_custom_field "$dev_id" "updating_to" || true)"
|
||||||
|
echo ">>> Pre-change (id=$dev_id): update_progress='${prev_p:-<unset>}'; updating_to='${prev_u:-<unset>}'"
|
||||||
|
else
|
||||||
|
echo ">>> Pre-change (id=$dev_id): update_progress='<skipped>'; updating_to='<skipped>'"
|
||||||
|
fi
|
||||||
|
|
||||||
local body
|
local body
|
||||||
body="$(jq -n --arg p "$update_progress" --arg u "$updating_to" \
|
body="$(jq -n --arg p "$update_progress" --arg u "$updating_to" \
|
||||||
'{custom_fields: {update_progress: $p, updating_to: $u}}')"
|
'{custom_fields: {update_progress: $p, updating_to: $u}}')"
|
||||||
@@ -81,6 +166,16 @@ nb_patch_update_wo_restart() {
|
|||||||
# PATCH NetBox custom field: wifidebug (deploy_wifidebug)
|
# PATCH NetBox custom field: wifidebug (deploy_wifidebug)
|
||||||
nb_patch_wifidebug() {
|
nb_patch_wifidebug() {
|
||||||
local dev_id="$1" value="$2"
|
local dev_id="$1" value="$2"
|
||||||
|
|
||||||
|
# Pre-read current value and print (optional; enable with NB_PREREAD=1)
|
||||||
|
local prev
|
||||||
|
if [[ "${NB_PREREAD}" == "1" ]]; then
|
||||||
|
prev="$(nb_read_custom_field "$dev_id" "wifidebug" || true)"
|
||||||
|
echo ">>> Pre-change (id=$dev_id): wifidebug='${prev:-<unset>}'"
|
||||||
|
else
|
||||||
|
echo ">>> Pre-change (id=$dev_id): wifidebug='<skipped>'"
|
||||||
|
fi
|
||||||
|
|
||||||
local body
|
local body
|
||||||
body="$(jq -n --arg v "$value" '{custom_fields: {wifidebug: $v}}')"
|
body="$(jq -n --arg v "$value" '{custom_fields: {wifidebug: $v}}')"
|
||||||
|
|
||||||
@@ -150,6 +245,16 @@ nb_add_journal_raw() {
|
|||||||
# PATCH custom field: multiple_ssids = "yes" — used for task_notify path
|
# PATCH custom field: multiple_ssids = "yes" — used for task_notify path
|
||||||
nb_set_multiple_ssids_yes() {
|
nb_set_multiple_ssids_yes() {
|
||||||
local dev_id="$1"
|
local dev_id="$1"
|
||||||
|
|
||||||
|
# Pre-read current value and print (optional; enable with NB_PREREAD=1)
|
||||||
|
local prev
|
||||||
|
if [[ "${NB_PREREAD}" == "1" ]]; then
|
||||||
|
prev="$(nb_read_custom_field "$dev_id" "multiple_ssids" || true)"
|
||||||
|
echo ">>> Pre-change (id=$dev_id): multiple_ssids='${prev:-<unset>}'"
|
||||||
|
else
|
||||||
|
echo ">>> Pre-change (id=$dev_id): multiple_ssids='<skipped>'"
|
||||||
|
fi
|
||||||
|
|
||||||
local body='{"custom_fields":{"multiple_ssids":"yes"}}'
|
local body='{"custom_fields":{"multiple_ssids":"yes"}}'
|
||||||
|
|
||||||
local code
|
local code
|
||||||
@@ -172,6 +277,10 @@ nb_set_multiple_ssids_yes() {
|
|||||||
nb_add_tag_by_slug() {
|
nb_add_tag_by_slug() {
|
||||||
local dev_id="$1" tag_slug="$2"
|
local dev_id="$1" tag_slug="$2"
|
||||||
|
|
||||||
|
if [[ "$USE_TAGS" != "yes" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
# 1) Lookup tag by slug
|
# 1) Lookup tag by slug
|
||||||
local tag_resp tag_id tag_name
|
local tag_resp tag_id tag_name
|
||||||
tag_resp="$(curl -sS \
|
tag_resp="$(curl -sS \
|
||||||
@@ -208,7 +317,7 @@ nb_add_tag_by_slug() {
|
|||||||
dev_detail="$(curl -sS \
|
dev_detail="$(curl -sS \
|
||||||
-H "Authorization: Token $NB_TOKEN" \
|
-H "Authorization: Token $NB_TOKEN" \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
"$NB_URL/api/dcim/devices/$dev_id/")"
|
"$NB_URL/api/dcim/devices/$dev_id/?fields=tags")"
|
||||||
existing_objs_json="$(
|
existing_objs_json="$(
|
||||||
jq -c '
|
jq -c '
|
||||||
(.tags // []) as $t |
|
(.tags // []) as $t |
|
||||||
@@ -256,12 +365,16 @@ nb_add_tag_by_slug() {
|
|||||||
nb_remove_tag_by_slug() {
|
nb_remove_tag_by_slug() {
|
||||||
local dev_id="$1" tag_slug="$2"
|
local dev_id="$1" tag_slug="$2"
|
||||||
|
|
||||||
|
if [[ "$USE_TAGS" != "yes" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
# 1) Get current tags
|
# 1) Get current tags
|
||||||
local dev_detail existing_objs_json
|
local dev_detail existing_objs_json
|
||||||
dev_detail="$(curl -sS \
|
dev_detail="$(curl -sS \
|
||||||
-H "Authorization: Token $NB_TOKEN" \
|
-H "Authorization: Token $NB_TOKEN" \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
"$NB_URL/api/dcim/devices/$dev_id/")" || return 1
|
"$NB_URL/api/dcim/devices/$dev_id/?fields=tags")" || return 1
|
||||||
|
|
||||||
# Normalize tags to objects: [{name, slug?}]
|
# Normalize tags to objects: [{name, slug?}]
|
||||||
existing_objs_json="$(
|
existing_objs_json="$(
|
||||||
@@ -319,6 +432,16 @@ nb_remove_tag_by_slug() {
|
|||||||
# --- clear only custom_field update_progress (set to null) ---
|
# --- clear only custom_field update_progress (set to null) ---
|
||||||
nb_clear_update_progress() {
|
nb_clear_update_progress() {
|
||||||
local dev_id="$1"
|
local dev_id="$1"
|
||||||
|
|
||||||
|
# Pre-read current value and print (optional; enable with NB_PREREAD=1)
|
||||||
|
local prev
|
||||||
|
if [[ "${NB_PREREAD}" == "1" ]]; then
|
||||||
|
prev="$(nb_read_custom_field "$dev_id" "update_progress" || true)"
|
||||||
|
echo ">>> Pre-change (id=$dev_id): update_progress='${prev:-<unset>}'"
|
||||||
|
else
|
||||||
|
echo ">>> Pre-change (id=$dev_id): update_progress='<skipped>'"
|
||||||
|
fi
|
||||||
|
|
||||||
local body='{"custom_fields":{"update_progress":null}}'
|
local body='{"custom_fields":{"update_progress":null}}'
|
||||||
local code
|
local code
|
||||||
code="$(curl -sS -o /dev/null -w "%{http_code}" \
|
code="$(curl -sS -o /dev/null -w "%{http_code}" \
|
||||||
@@ -338,6 +461,16 @@ nb_clear_update_progress() {
|
|||||||
# --- NEW: generic single custom field patch ---
|
# --- NEW: generic single custom field patch ---
|
||||||
nb_patch_custom_field() {
|
nb_patch_custom_field() {
|
||||||
local dev_id="$1" key="$2" value="$3"
|
local dev_id="$1" key="$2" value="$3"
|
||||||
|
|
||||||
|
# Pre-read current value and print (optional; enable with NB_PREREAD=1)
|
||||||
|
local prev
|
||||||
|
if [[ "${NB_PREREAD}" == "1" ]]; then
|
||||||
|
prev="$(nb_read_custom_field "$dev_id" "$key" || true)"
|
||||||
|
echo ">>> Pre-change (id=$dev_id): ${key}='${prev:-<unset>}'"
|
||||||
|
else
|
||||||
|
echo ">>> Pre-change (id=$dev_id): ${key}='<skipped>'"
|
||||||
|
fi
|
||||||
|
|
||||||
# Build {"custom_fields": { "<key>": "<value>" }}
|
# Build {"custom_fields": { "<key>": "<value>" }}
|
||||||
local body
|
local body
|
||||||
body="$(jq -n --arg k "$key" --arg v "$value" '{custom_fields: {($k): $v}}')"
|
body="$(jq -n --arg k "$key" --arg v "$value" '{custom_fields: {($k): $v}}')"
|
||||||
@@ -378,7 +511,7 @@ handle_payload() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local dev_id
|
local dev_id
|
||||||
dev_id="$(nb_find_device_id "$device")"
|
dev_id="$(nb_get_device_id_cached "$device")"
|
||||||
if [[ -z "$dev_id" ]]; then
|
if [[ -z "$dev_id" ]]; then
|
||||||
err "Device '$device' not found in NetBox"
|
err "Device '$device' not found in NetBox"
|
||||||
return 0
|
return 0
|
||||||
@@ -420,6 +553,9 @@ handle_payload() {
|
|||||||
;;
|
;;
|
||||||
tag_add)
|
tag_add)
|
||||||
# add a tag to the device using task_result as slug
|
# add a tag to the device using task_result as slug
|
||||||
|
if [[ "$USE_TAGS" != "yes" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
if [[ -z "$task_result" ]]; then
|
if [[ -z "$task_result" ]]; then
|
||||||
warn "tag_add: task_result (tag slug) is empty; skipping."
|
warn "tag_add: task_result (tag slug) is empty; skipping."
|
||||||
return 0
|
return 0
|
||||||
@@ -431,15 +567,19 @@ handle_payload() {
|
|||||||
;;
|
;;
|
||||||
update_cleanup_success)
|
update_cleanup_success)
|
||||||
# cleanup after successful update
|
# cleanup after successful update
|
||||||
nb_remove_tag_by_slug "$dev_id" "update-in-progress" || true
|
if [[ "$USE_TAGS" == "yes" ]]; then
|
||||||
nb_remove_tag_by_slug "$dev_id" "update-auto-restarted" || true
|
nb_remove_tag_by_slug "$dev_id" "update-in-progress" || true
|
||||||
nb_add_tag_by_slug "$dev_id" "update-successful" || true
|
nb_remove_tag_by_slug "$dev_id" "update-auto-restarted" || true
|
||||||
|
nb_add_tag_by_slug "$dev_id" "update-successful" || true
|
||||||
|
fi
|
||||||
nb_clear_update_progress "$dev_id" || true
|
nb_clear_update_progress "$dev_id" || true
|
||||||
|
|
||||||
echo ">>> Device '$device' (id=$dev_id) cleanup done:"
|
echo ">>> Device '$device' (id=$dev_id) cleanup done:"
|
||||||
echo " - tag removed: update-in-progress"
|
if [[ "$USE_TAGS" == "yes" ]]; then
|
||||||
echo " - tag removed: update-auto-restarted"
|
echo " - tag removed: update-in-progress"
|
||||||
echo " - tag added: update-successful"
|
echo " - tag removed: update-auto-restarted"
|
||||||
|
echo " - tag added: update-successful"
|
||||||
|
fi
|
||||||
echo " - custom field cleared: update_progress"
|
echo " - custom field cleared: update_progress"
|
||||||
;;
|
;;
|
||||||
custom_field_set)
|
custom_field_set)
|
||||||
@@ -456,6 +596,9 @@ handle_payload() {
|
|||||||
;;
|
;;
|
||||||
tag_remove)
|
tag_remove)
|
||||||
# remove a tag from the device using task_result as slug
|
# remove a tag from the device using task_result as slug
|
||||||
|
if [[ "$USE_TAGS" != "yes" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
if [[ -z "$task_result" ]]; then
|
if [[ -z "$task_result" ]]; then
|
||||||
warn "tag_remove: task_result (tag slug) is empty; skipping."
|
warn "tag_remove: task_result (tag slug) is empty; skipping."
|
||||||
return 0
|
return 0
|
||||||
@@ -464,7 +607,7 @@ handle_payload() {
|
|||||||
echo ">>> Device '$device' (id=$dev_id) updated:"
|
echo ">>> Device '$device' (id=$dev_id) updated:"
|
||||||
echo " tag removed (slug) = '$task_result'"
|
echo " tag removed (slug) = '$task_result'"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
log "Ignoring unsupported task_name='$task' (no-op)"
|
log "Ignoring unsupported task_name='$task' (no-op)"
|
||||||
;;
|
;;
|
||||||
@@ -501,4 +644,4 @@ while :; do
|
|||||||
warn "Skipping non-JSON payload"
|
warn "Skipping non-JSON payload"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
Reference in New Issue
Block a user