Files
p5g-marvis/README.md
2026-04-23 13:50:31 -05:00

326 lines
11 KiB
Markdown

# P5G Marvis — Full Setup Guide
This document describes the complete architecture and deployment procedure for the
**P5G Marvis** sidebar extension that injects custom pages into the Athonet NCM UI.
---
## Architecture Overview
```
Browser → Traefik (HTTPS :443)
├── /core/marvis/* → strip prefix → http://127.0.0.1:8100 (p5g-marvis FastAPI)
└── /core/radio/* → strip prefix → http://127.0.0.1:4000 (rm-ui, if present)
NCM UI (React SPA)
└── index-Cw8Irsq8.js (patched by patch-ncm.py)
├── Sidebar: P5G Marvis (Insights, Actions, Minis, AI) + P5G Radio
├── Router: /marvis/* and /radio/* → <iframe> loading the FastAPI pages
└── Perms: BW registry entries for /marvis and /radio (Tt = always allowed)
p5g-marvis FastAPI (port 8100)
├── GET /overview → app/ui/overview.html
├── GET /minis → app/ui/tasks.html
├── GET /actions → app/ui/actions.html
├── GET / → app/ui/index.html (catch-all SPA)
├── GET /api/network/nf-status → Prometheus metrics
├── GET /api/alerts → Alertmanager
└── GET /api/actions → log_analyzer service
```
---
## Local Files
All source files live at: `~/p5g-marvis/`
```
~/p5g-marvis/
├── patch-ncm.py # Injects sidebar/routes/perms into the NCM JS bundle
├── requirements.txt # fastapi==0.115.0, uvicorn[standard]==0.30.6, httpx==0.27.2
├── app/
│ ├── main.py # FastAPI app — route definitions and UI file serving
│ ├── config.py # Reads env vars (prometheus URL, alertmanager URL, AI config)
│ ├── ui/
│ │ ├── overview.html # P5G Marvis Insights page
│ │ ├── tasks.html # P5G Marvis Minis page (action tiles)
│ │ ├── actions.html # P5G Marvis Actions page
│ │ └── index.html # SPA catch-all
│ ├── routers/
│ │ ├── actions.py # /api/actions — log analysis
│ │ ├── alerts.py # /api/alerts — Alertmanager
│ │ ├── network.py # /api/network/nf-status — Prometheus
│ │ └── query.py # /api/query — PromQL passthrough
│ └── services/
│ ├── prometheus.py
│ ├── alertmanager.py
│ ├── log_analyzer.py
│ └── ai.py
```
---
## Deployed Hosts
| Host | IP | Status | P5G Radio |
|---|---|---|---|
| Primary | 172.27.0.159 | ✅ Deployed | ✅ (rm-ui on :4000) |
| Secondary | 192.168.86.150 | ✅ Deployed | ⚠️ No rm-ui container |
---
## Deploy / Re-deploy to a Host
### Prerequisites
- SSH key: `~/.ssh/5G-SSH-Key.pem`
- Target host must have Python 3.x and the Athonet NCM stack running
### Step 1 — Copy application files
```bash
TARGET=<IP> # e.g. 172.27.0.159 or 192.168.86.150
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET 'mkdir -p /opt/p5g-marvis/app/ui /opt/p5g-marvis/app/routers /opt/p5g-marvis/app/services /etc/athonet/traefik/ssl'
scp -i ~/.ssh/5G-SSH-Key.pem \
~/p5g-marvis/requirements.txt ~/p5g-marvis/patch-ncm.py \
root@$TARGET:/opt/p5g-marvis/
scp -i ~/.ssh/5G-SSH-Key.pem \
~/p5g-marvis/app/__init__.py ~/p5g-marvis/app/main.py ~/p5g-marvis/app/config.py \
root@$TARGET:/opt/p5g-marvis/app/
scp -i ~/.ssh/5G-SSH-Key.pem ~/p5g-marvis/app/ui/*.html \
root@$TARGET:/opt/p5g-marvis/app/ui/
scp -i ~/.ssh/5G-SSH-Key.pem ~/p5g-marvis/app/routers/*.py \
root@$TARGET:/opt/p5g-marvis/app/routers/
scp -i ~/.ssh/5G-SSH-Key.pem ~/p5g-marvis/app/services/*.py \
root@$TARGET:/opt/p5g-marvis/app/services/
```
### Step 2 — Install Python dependencies
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET '
python3 -m ensurepip --upgrade 2>/dev/null
python3 -m pip install -r /opt/p5g-marvis/requirements.txt --break-system-packages -q
'
```
### Step 3 — Create systemd service
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET 'cat > /etc/systemd/system/p5g-marvis.service << EOF
[Unit]
Description=P5G Marvis AI Network Assistant
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/p5g-marvis
Environment=MARVIS_PROMETHEUS_URL=http://127.0.0.1:9090
Environment=MARVIS_PROMETHEUS_PREFIX=/prometheus
Environment=MARVIS_ALERTMANAGER_URL=http://127.0.0.1:9093
Environment=MARVIS_AI_MODE=openai
Environment=MARVIS_OPENAI_BASE_URL=https://172.27.0.135:8001
Environment=MARVIS_OPENAI_MODEL=gemma-4-26B-A4B-it-UD-Q4_K_S.gguf
ExecStart=/usr/bin/python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8100
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable p5g-marvis
systemctl start p5g-marvis'
```
### Step 4 — Create Traefik routing config
Create `/etc/athonet/traefik/ssl/marvis.yml`:
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET 'cat > /etc/athonet/traefik/ssl/marvis.yml << EOF
http:
routers:
router-marvis-0:
rule: "PathPrefix(\`/core/marvis\`)"
service: service-marvis
entryPoints:
- websecure
tls: {}
priority: 25
middlewares:
- cors@http
- strip-path-marvis-0
# Add router-radio-0 here if rm-ui is present (port 4000)
middlewares:
strip-path-marvis-0:
stripPrefix:
prefixes:
- "/core/marvis"
services:
service-marvis:
loadBalancer:
servers:
- url: "http://127.0.0.1:8100"
passHostHeader: false
EOF'
```
### Step 5 — Add file provider to Traefik static config
Append to `/etc/athonet/traefik/traefik.yml` under the `providers:` section:
```yaml
file:
filename: "/etc/traefik/ssl/marvis.yml"
watch: true
```
> **Note**: The path inside the container is `/etc/traefik/ssl/marvis.yml` because the `ssl/` directory is bind-mounted. The host path is `/etc/athonet/traefik/ssl/marvis.yml`.
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET '
grep -q "file:" /etc/athonet/traefik/traefik.yml || (
echo " file:" >> /etc/athonet/traefik/traefik.yml
echo " filename: \"/etc/traefik/ssl/marvis.yml\"" >> /etc/athonet/traefik/traefik.yml
echo " watch: true" >> /etc/athonet/traefik/traefik.yml
)
docker restart traefik'
```
### Step 6 — Patch the NCM JS bundle
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET '
JS="/etc/athonet/ems-frontend/advanced/assets/index-Cw8Irsq8.js"
# Only create .bak if it does not already exist (preserve the clean original)
[ -f "$JS.bak" ] || cp "$JS" "$JS.bak"
cd /opt/p5g-marvis && python3 patch-ncm.py'
```
Expected output:
```
Applied: sidebar entry
Applied: marvis + radio routes with Or wrapper
Applied: /marvis permissions entry
Done — P5G Marvis: 10 occurrences, P5G Radio: 3 occurrences
```
If you see `Skipped:` instead of `Applied:`, the patch was already applied from a previous run — this is safe.
### Step 7 — Verify
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@$TARGET '
systemctl status p5g-marvis --no-pager | head -5
curl -sk http://127.0.0.1:8100/health'
```
Expected: `{"status":"ok"}`
---
## Update a UI Page
To update any UI page (e.g. after editing `tasks.html` locally):
```bash
/usr/bin/scp -i ~/.ssh/5G-SSH-Key.pem \
~/p5g-marvis/app/ui/tasks.html \
root@<IP>:/opt/p5g-marvis/app/ui/tasks.html
```
No service restart needed — FastAPI reads the file on each request.
---
## Re-run the JS Patch (e.g. after an NCM upgrade)
If NCM is upgraded and the JS bundle is replaced:
```bash
ssh -i ~/.ssh/5G-SSH-Key.pem root@<IP> '
JS="/etc/athonet/ems-frontend/advanced/assets/index-Cw8Irsq8.js"
# The new build will have a different filename — update patch-ncm.py JS= and BAK= lines
# Then create a fresh .bak and re-run
cp "$JS" "$JS.bak"
cd /opt/p5g-marvis && python3 patch-ncm.py'
```
> **Warning**: Check if the bundle filename changed after the upgrade. The filename is `index-<hash>.js`. Update the `JS` variable at the top of `patch-ncm.py` if it has changed.
---
## What the Patch Does (patch-ncm.py)
The script modifies the minified NCM React JS bundle in 3 places — it always reads from the `.bak` (clean original) ensuring it is safe to re-run:
1. **Sidebar nav entry** — appends P5G Marvis (with 4 sub-items) and P5G Radio items to the existing top-level navigation after the UPF entry.
2. **React Router routes** — inserts `/marvis` and `/radio` route objects between the existing UPF and Platform routes. Each renders an iframe pointing to `/core/marvis/<page>` or `/core/radio/`.
3. **Permissions registry** — registers all `/marvis/*` and `/radio` paths in the BW permissions map with `Tt` (always-allowed), preventing "Permissions for route not managed" errors.
---
## P5G Marvis Sidebar Structure
```
P5G Marvis
├── P5G Marvis Insights → /marvis/overview → iframes /core/marvis/overview
├── P5G Marvis Actions → /marvis/actions → iframes /core/marvis/actions
├── P5G Marvis Minis → /marvis/minis → iframes /core/marvis/minis (tasks.html)
└── P5G Marvis AI → /marvis/ai → iframes /core/marvis/ (index.html)
P5G Radio → /radio → iframes /core/radio/ (rm-ui :4000)
```
---
## Minis Page — Action Tiles (tasks.html)
### Diagnostics & Health
| Tile | Description |
|---|---|
| Ping All NFs | ICMP probes to all NFs via Prometheus |
| Refresh Alerts | Pull latest alerts from Alertmanager |
| Full NF Status Report | Query all 12 NF health metrics |
| Trace UE Data Path | Trace AMF→SMF→UPF path for a sample SUPI |
### Network Operations
| Tile | Description |
|---|---|
| Perform Emulated Data Session | Full attach + data session end-to-end test (non-disruptive) |
| Check Connected Devices | Query AMF/UPF state and report registration status |
| Generate Capacity Report | Device counts, bandwidth utilisation, peak hour trends |
| Clear All UE Sessions | Force-release all active sessions (**requires confirmation**) |
### Maintenance
| Tile | Description |
|---|---|
| Backup Configuration | Export configs for all NFs to timestamped archive |
| Reload Configuration | Reload from disk without restarting services |
| Purge Old Logs | Delete log files older than 7 days |
| Export Debug Bundle | Collect and compress NF logs, configs, metrics |
---
## Troubleshooting
| Problem | Check |
|---|---|
| Sidebar items not visible | Browser hard-refresh (Cmd+Shift+R). Confirm patch ran successfully. |
| Clicking sidebar shows blank page | Traefik routing — check `docker logs traefik`. `marvis.yml` must be present and file provider added to `traefik.yml`. |
| Service won't start | `journalctl -u p5g-marvis -n 50`. Usually a missing Python package. |
| Metrics not loading | Confirm Prometheus is on `127.0.0.1:9090` — check `MARVIS_PROMETHEUS_URL` env var in the service file. |
| patch-ncm.py: ERROR anchor not found | The NCM JS bundle was upgraded and the filename/content changed. Find new anchors in the `.bak` file and update `patch-ncm.py`. |
| After NCM upgrade patch not applied | Re-run Step 6 — but first check if the bundle filename changed. |