380 lines
12 KiB
Markdown
380 lines
12 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.
|
|
|
|
---
|
|
|
|
## Deployment Model
|
|
|
|
The target environment for this project is a host where services are started by
|
|
`systemd`, including Docker-backed services. Marvis is intended to run the same
|
|
way:
|
|
|
|
- the FastAPI app runs in a container with host networking on `127.0.0.1:8100`
|
|
- Traefik exposes it at `/core/marvis/*`
|
|
- `patch-ncm.py` injects the sidebar entries and iframe routes into the NCM UI
|
|
- the injected entries should only be added for services that are actually
|
|
reachable on the host when the patch is applied
|
|
|
|
For a containerised deployment without Compose, this repo includes an example
|
|
unit file at `config/p5g-marvis.service`.
|
|
|
|
### Build the image
|
|
|
|
```bash
|
|
docker build -t p5g-marvis:latest .
|
|
```
|
|
|
|
### Install the systemd unit
|
|
|
|
```bash
|
|
cp config/p5g-marvis.service /usr/lib/systemd/system/p5g-marvis.service
|
|
mkdir -p /etc/p5g-marvis
|
|
cp config/marvis.env.example /etc/p5g-marvis/marvis.env
|
|
systemctl daemon-reload
|
|
systemctl enable --now p5g-marvis
|
|
```
|
|
|
|
Marvis will then be reachable on:
|
|
|
|
```text
|
|
http://127.0.0.1:8100
|
|
http://127.0.0.1:8100/api/docs
|
|
```
|
|
|
|
Edit `/etc/p5g-marvis/marvis.env` per host instead of hardcoding addresses in the
|
|
unit file. In particular:
|
|
|
|
- set `MARVIS_PROMETHEUS_URL` and `MARVIS_ALERTMANAGER_URL` to the correct host
|
|
endpoints for that appliance
|
|
- keep the defaults if Prometheus and Alertmanager are already available on host
|
|
loopback from the appliance network namespace
|
|
|
|
---
|
|
|
|
## 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'
|
|
```
|
|
|
|
`patch-ncm.py` injects only the services it detects as reachable on the host:
|
|
|
|
- `P5G_MARVIS_ENABLED=true|false` overrides Marvis detection
|
|
- `P5G_RADIO_ENABLED=true|false` overrides Radio detection
|
|
- by default it probes `127.0.0.1:8100` for Marvis and `127.0.0.1:4000` for Radio
|
|
|
|
Expected output:
|
|
```
|
|
Applied: sidebar entry
|
|
Applied: injected iframe routes
|
|
Applied: 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. |
|