Panel de pared para el estado de instancias SQL Server monitorizadas por SolarWinds DPA. Saca las métricas de rendimiento de la REST interna de DPA —la API pública no expone ni una— y las pinta en tres vistas: detalle para el PC, Modo TV para la pantalla de sala y una rotación desatendida. Solo lectura, sin dependencias más allá de la librería estándar de Python.
Wall dashboard for SQL Server instances monitored by SolarWinds DPA. Pulls performance metrics from DPA's internal REST layer — the public API exposes none — and renders them in three views: a detailed desktop view, a TV mode for a wall screen, and an unattended slideshow. Read-only, no dependencies beyond the Python standard library.
- Sin dependencias ni base de datos propia. Un solo
python3 dpa.py. DPA ya guarda el histórico, así que/api/historicoes una pasarela con caché: las gráficas de 24 h salen llenas desde el primer minuto. - Solo lectura. El colector inicia sesión y consulta. Nunca escribe en DPA ni en las instancias monitorizadas.
- Toda la instalación en
dpa.json. Conexión, credenciales, colores, orden de rotación y pie de página se declaran en un fichero. El código no conoce ninguna instancia concreta. - Semáforo con los umbrales de DPA, no inventados aquí: se leen los
alarmLevelde las nueve categorías (CPU, Memory, Disk, Sessions, Waits, TempDB, Backups, Network, License Compliance). - Detalle de bloqueos, con las columnas crudas de
sysprocesses: quién bloquea, quién espera, desde qué programa y cuánto lleva. - Availability Groups: clúster, salud, réplica primaria, rol, número de réplicas y modo de conmutación.
- Tres vistas: normal, Modo TV y presentación en rotación automática.
- Aviso de caída del propio panel: si el navegador pierde el servicio, se superpone un cartel con cronómetro. Una pantalla congelada no se confunde con una sala en verde.
DPA expone dos interfaces HTTP y hacen cosas distintas:
API pública (/iwc/api) |
REST interna (/iwc/rest) |
|
|---|---|---|
| Autenticación | token (refresh → access) | cookie de sesión web |
| Documentada | sí (Swagger en /iwc/api-doc) |
no |
| Contenido | administración: registrar instancias, monitores, alertas, licencias | datos de rendimiento |
La API pública no expone ni una métrica de rendimiento. Todo lo que pinta este panel viene de la REST interna, a la que el colector accede iniciando sesión con usuario y contraseña, igual que un navegador. Si la sesión caduca, DPA devuelve HTML en vez de JSON; el cliente lo detecta, vuelve a loguearse y reintenta una sola vez.
| Endpoint | Para qué |
|---|---|
GET /iwc/rest/databases |
inventario: id, nombre, tipo, estado del monitor, AG |
GET /iwc/rest/databases/{id}/super-tab |
nivel de Trends, Tuning, salud del AG y Recursos |
GET /iwc/rest/databases/{id}/metrics/categories |
alarmLevel por categoría (el semáforo de DPA) |
GET /iwc/rest/databases/{id}/metrics/data |
series {time, value} de las métricas |
GET /iwc/rest/ag-status/summary |
Availability Group completo |
El detalle de los bloqueos no existe en ninguna API JSON. La pantalla Current de DPA lo saca de una página HTML normal:
GET /iwc/realTimeTab.iwc?db_id=<id>&action=4&cs=1
Devuelve la tabla de la pestaña Blocking con las columnas de sysprocesses,
y el colector la parsea con html.parser.
Es la parte frágil del proyecto: si SolarWinds cambia esa página en una
actualización, deja de parsear. Por eso está acotada — solo se pide cuando DPA
ya ha dicho que hay sesiones bloqueadas, y va en su propio try: si falla, se
registra en el log, la tarjeta sigue viva y simplemente no aparece la lista.
| Python | 3.9 o superior (solo librería estándar) |
| SO | Cualquiera para ejecutarlo; el despliegue automático asume Ubuntu con systemd |
| DPA | SolarWinds Database Performance Analyzer accesible por HTTP/HTTPS |
| Cuenta | Un usuario de DPA de solo lectura, dedicado a este panel |
| Red | Acceso del servidor al puerto de DPA (8124/tcp con HTTPS, 8123/tcp con HTTP) |
git clone https://github.com/mr7security/sqlserver-dpa-dashboard.git
cd sqlserver-dpa-dashboard
cp dpa.json.example dpa.json # edita url, usuario y password
python3 dpa.pyAbre http://localhost:9000. Sin un DPA real detrás el panel arranca igualmente
y muestra el aviso de sin conexión: sirve para ver la interfaz.
git clone https://github.com/mr7security/sqlserver-dpa-dashboard.git ~/sqlserver-dpa-dashboard
cd ~/sqlserver-dpa-dashboard
sudo bash deploy.sh
sudo nano /opt/sqlserver-dpa-dashboard/dpa.json
sudo systemctl restart sqlserver-dpa-dashboardInstala en /opt/sqlserver-dpa-dashboard, crea el usuario de servicio dpadash
(sin shell y sin home) y deja dpa.json en modo 640. El servicio corre sin
privilegios, así que el puerto tiene que ser ≥ 1024.
Actualizar es git pull && sudo bash deploy.sh. No pisa dpa.json.
Todo vive en dpa.json (copia de dpa.json.example):
{
"url": "https://192.0.2.10:8124",
"usuario": "CAMBIAME",
"password": "CAMBIAME",
"verificar_ssl": false,
"puerto": 9000,
"intervalo_segundos": 30,
"listar_sesiones_activas": true,
"ui": {
"titulo": "Estado de bases de datos",
"pie": "DEPARTAMENTO IT",
"colores_servidor": { "DB-01": "#38bdf8", "DB-02": "#818cf8" },
"alias_tv": { "SRV-05 via CLUSTER-LARGO": "DB-05" },
"orden_presentacion": ["DB-01", "DB-02", "DB-03"]
}
}Los colores se eligen fuera de la gama del semáforo (verde/ámbar/rojo) para
que nunca se confundan con una alarma. Las instancias que no aparezcan en
orden_presentacion se añaden al final por orden alfabético: registrar una
nueva en DPA no obliga a tocar nada aquí.
| Vista | Para qué | Qué muestra |
|---|---|---|
| Normal | mirar desde el PC | una instancia por fila, a todo el ancho: métricas y medidores, detalle y AG, gráficas y disco. Debajo, bloqueos y sesiones activas |
| Modo TV | pantalla de sala | todas las instancias a la vez, solo sesiones e indicadores, con un panel de bloqueos abajo |
| Presentación | rotación desatendida | alterna la vista general con cada instancia a pantalla completa, cada 15 s (30 s si está en crítico) |
Controles en presentación: flechas para moverse, espacio para pausar, Esc para
salir, y clic en cualquier nombre de la tira superior para saltar ahí.
| Nivel | Cuándo |
|---|---|
caido |
monitor de DPA parado, o sin respuesta |
critico |
alguna categoría en CRITICAL, Tuning/AG en CRITICAL, o ≥ 5 sesiones bloqueadas |
aviso |
alguna categoría en WARNING, o 1-4 sesiones bloqueadas |
ok |
resto |
| Ruta | Devuelve |
|---|---|
/ |
el dashboard |
/api/estado |
instantánea completa en JSON |
/api/historico?db=<id>&metrica=<nombre>&horas=<n> |
serie histórica |
/api/config |
solo el bloque ui: nunca expone conexión ni credenciales |
/logo |
logotipo opcional, si dejas un logo.* junto a dpa.py |
Solo se aceptan métricas de la lista blanca del colector.
journalctl -u sqlserver-dpa-dashboard -f
sudo -u dpadash python3 /opt/sqlserver-dpa-dashboard/dpa.py --once | sed -n '/^{/,$p'--once mezcla los logs con el JSON, de ahí el sed.
sqlserver-dpa-dashboard/
├── dpa.py colector, API y servidor HTTP (stdlib)
├── dpa.html interfaz completa, autocontenida
├── dpa.json.example plantilla de configuración
├── deploy.sh instalación/actualización en Ubuntu
├── sqlserver-dpa-dashboard.service unidad systemd endurecida
├── ruff.toml reglas de lint
├── .github/workflows/ci.yml lint, compilación y control de secretos
├── LICENSE
└── README.md
Ficheros que no se versionan: dpa.json y el logo.* corporativo.
dpa.jsoncontiene usuario y contraseña de DPA. Está en.gitignore,deploy.shlo deja en modo640y el CI falla si aparece en el repositorio.- Usa un usuario de DPA dedicado y de solo lectura, nunca
dpaADMIN. - Prefiere HTTPS (8124). Con HTTP (8123) la contraseña viaja en claro por la red.
- El servicio corre como usuario sin privilegios (
dpadash), conProtectSystem=strict,NoNewPrivilegesyRestrictSUIDSGID. - El panel no tiene autenticación: publícalo solo en la VLAN de gestión, o detrás de un proxy inverso que la añada.
/api/configexpone únicamente el bloque de presentación. Los nombres de instancia salen en/api/estado— un motivo más para no exponer el puerto.
Las incidencias y las pull request son bienvenidas. Antes de abrir una PR:
pip install ruff
ruff check dpa.py
python -m py_compile dpa.pyLos commits siguen Conventional Commits:
feat:, fix:, docs:, refactor:, chore:.
MIT — ver LICENSE.