Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .devcontainer/scripts/load-devices.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
if [ -n "${CSV_PATH:-}" ]; then
: # user provided CSV_PATH
else
# Portable mktemp fallback: try GNU coreutils first, then busybox-style
if mktemp --version >/dev/null 2>&1; then
CSV_PATH="$(mktemp --tmpdir netalertx-devices-XXXXXX.csv 2>/dev/null || mktemp /tmp/netalertx-devices-XXXXXX.csv)"
else
CSV_PATH="$(mktemp -t netalertx-devices.XXXXXX 2>/dev/null || mktemp /tmp/netalertx-devices-XXXXXX.csv)"
fi
fi
DEVICE_COUNT="${DEVICE_COUNT:-255}"
SEED="${SEED:-20211}"
NETWORK_CIDR="${NETWORK_CIDR:-192.168.50.0/22}"
DB_DIR="${NETALERTX_DB:-/data/db}"
DB_FILE="${DB_DIR%/}/app.db"

# Ensure we are inside the devcontainer
"${SCRIPT_DIR}/isDevContainer.sh" >/dev/null

if [ ! -f "${DB_FILE}" ]; then
echo "[load-devices] Database not found at ${DB_FILE}. Is the devcontainer initialized?" >&2
exit 1
fi

if ! command -v sqlite3 >/dev/null 2>&1; then
echo "[load-devices] sqlite3 is required but not installed." >&2
exit 1
fi
if ! command -v python3 >/dev/null 2>&1; then
echo "[load-devices] python3 is required but not installed." >&2
exit 1
fi
if ! command -v curl >/dev/null 2>&1; then
echo "[load-devices] curl is required but not installed." >&2
exit 1
fi

# Generate synthetic device inventory CSV
python3 "${REPO_ROOT}/scripts/generate-device-inventory.py" \
--output "${CSV_PATH}" \
--devices "${DEVICE_COUNT}" \
--seed "${SEED}" \
--network "${NETWORK_CIDR}" >/dev/null

echo "[load-devices] CSV generated at ${CSV_PATH} (devices=${DEVICE_COUNT}, seed=${SEED})"

API_TOKEN="$(sqlite3 "${DB_FILE}" "SELECT setValue FROM Settings WHERE setKey='API_TOKEN';")"
GRAPHQL_PORT="$(sqlite3 "${DB_FILE}" "SELECT setValue FROM Settings WHERE setKey='GRAPHQL_PORT';")"

if [ -z "${API_TOKEN}" ] || [ -z "${GRAPHQL_PORT}" ]; then
echo "[load-devices] Failed to read API_TOKEN or GRAPHQL_PORT from ${DB_FILE}" >&2
exit 1
fi

IMPORT_URL="http://localhost:${GRAPHQL_PORT}/devices/import"

HTTP_CODE=$(curl -sS -o /tmp/load-devices-response.json -w "%{http_code}" \
-X POST "${IMPORT_URL}" \
-H "Authorization: Bearer ${API_TOKEN}" \
-F "file=@${CSV_PATH}")

if [ "${HTTP_CODE}" != "200" ]; then
echo "[load-devices] Import failed with HTTP ${HTTP_CODE}. Response:" >&2
cat /tmp/load-devices-response.json >&2
exit 1
fi

# Fetch totals for a quick sanity check
TOTALS=$(curl -sS -H "Authorization: Bearer ${API_TOKEN}" "http://localhost:${GRAPHQL_PORT}/devices/totals" || true)

echo "[load-devices] Import succeeded (HTTP ${HTTP_CODE})."
echo "[load-devices] Devices totals: ${TOTALS}"
echo "[load-devices] Done. CSV kept at ${CSV_PATH}"
27 changes: 25 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"showReuseMessage": false,
"group": "POSIX Tasks"
},

"problemMatcher": [],
"group": {
"kind": "build",
Expand Down Expand Up @@ -59,6 +58,31 @@
"color": "terminal.ansiRed"
}
},
{
"label": "[Dev Container] Load Sample Devices",
"type": "shell",
"command": "./isDevContainer.sh || exit 1; ./load-devices.sh",
"detail": "Generates a synthetic device inventory and imports it into the devcontainer database via /devices/import.",
"options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts",
"env": {
"CSV_PATH": "/tmp/netalertx-devices.csv"
}
},
"presentation": {
"echo": true,
"reveal": "always",
"panel": "shared",
"showReuseMessage": false,
"clear": false,
"group": "Devcontainer"
},
"problemMatcher": [],
"icon": {
"id": "cloud-upload",
"color": "terminal.ansiYellow"
}
},
{
"label": "[Dev Container] Re-Run Startup Script",
"type": "shell",
Expand All @@ -73,7 +97,6 @@
"panel": "shared",
"showReuseMessage": false
},

"problemMatcher": [],
"icon": {
"id": "beaker",
Expand Down
30 changes: 28 additions & 2 deletions scripts/generate-device-inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ def build_row(
now: dt.datetime,
) -> dict[str, str]:
comments = "Synthetic device generated for testing."
first_seen = random_time(now)
last_seen = random_time(now)
t1 = random_time(now)
t2 = random_time(now)
first_seen, last_seen = (t1, t2) if t1 <= t2 else (t2, t1)
fqdn = f"{name.lower().replace(' ', '-')}.{site}" if name else ""

# Minimal fields set; missing ones default to empty string for CSV compatibility.
Expand Down Expand Up @@ -215,6 +216,7 @@ def generate_rows(args: argparse.Namespace, header: list[str]) -> list[dict[str,

rows: list[dict[str, str]] = []

# Include one Internet root device that anchors the tree; it does not consume an IP.
required_devices = 1 + args.switches + args.aps + args.devices
if required_devices > len(ip_pool):
raise ValueError(
Expand All @@ -227,6 +229,30 @@ def take_ip() -> str:
ip_pool.remove(choice)
return choice

# Root "Internet" device (no parent, no IP) so the topology has a defined root.
root_row = build_row(
name="Internet",
dev_type="Gateway",
vendor="NetAlertX",
mac="Internet",
parent_mac="",
ip="",
header=header,
owner=args.owner,
site=args.site,
ssid=args.ssid,
now=now,
)
root_row["devComments"] = "Synthetic root device representing the Internet."
root_row["devParentRelType"] = "Root"
root_row["devStaticIP"] = "0"
root_row["devScan"] = "0"
root_row["devAlertEvents"] = "0"
root_row["devAlertDown"] = "0"
root_row["devLogEvents"] = "0"
root_row["devPresentLastScan"] = "0"
rows.append(root_row)

router_mac = random_mac(macs)
router_ip = take_ip()
rows.append(
Expand Down