Volver a la wiki

Servicio: Modal de configuración de dispositivo (frontend)

Descripción

Módulo JavaScript reutilizable (DeviceConfigModal) que proporciona un modal overlay compartido para editar configuración de DeviceProfile (dispositivos descubiertos automáticamente) desde cualquier página de monitorización.

Usado por Observatory, Wireless, UPS y Digital Signage.

Ubicación: static/js/services/DeviceConfigModal.js (236 LOC)

API pública

DeviceConfigModal.show(profileId, profileName, onSaved)

Abre el modal para un dispositivo.

Parámetros:

Efecto:

DeviceConfigModal.hide()

Cierra el modal sin guardar.

Efecto:

Estructura del modal

┌─────────────────────────────────────────┐
│ Configure — [Device Name]        [✕]    │
├─────────────────────────────────────────┤
│ ⚠ [Error si falla GET/PUT]              │
│                                         │
│ SNMP                                    │
│ Version: [v2c ▼ | v3]                  │
│ [Community field]  (si v2c)             │
│ [Username, Auth, Priv fields] (si v3)  │
│                                         │
│ SSH                                     │
│ [Username] [Password]                   │
│ [Hint: A password is stored...]        │
│ Credentials are encrypted (Fernet/AES) │
│                                         │
│ Groups                                  │
│ ☐ Group 1  ☐ Group 2  ☐ Group 3  ... │
│                                         │
├─────────────────────────────────────────┤
│                    [Cancel] [Save]      │
└─────────────────────────────────────────┘

Campos del formulario

SNMP

Version selector (v2c | v3):

Si v2c:

Si v3:

Hint secretos: Solo visible si has_snmp_v3_keys es true → “Keys are stored. Leave blank to keep them unchanged.”

SSH

Campos:

Hint: Solo visible si has_stored_ssh es true → “A password is stored. Leave blank to keep it unchanged.”

Nota: “Credentials are encrypted (Fernet/AES) before storage.” siempre visible.

Groups

Checkboxes: Cargados dinámicamente desde GET /api/racks/groups.

Fallback: Si falla carga de grupos, muestra “Could not load groups” o “No groups defined”.

Ciclo de vida

Inicialización (_ensureDOM())

Vinculación de eventos (_bindEvents())

Carga de configuración (_loadConfig())

  1. Fetch GET /api/network/auto-provision/profiles/{profile_id}/config.
  2. Pobla campos con valores:
    • snmp_version, snmp_community, snmp_v3_username, etc.
    • ssh_username (password siempre vacío).
    • Hints de secretos según flags has_snmp_v3_keys, has_stored_ssh.
  3. Llama _loadGroups(cfg.group_ids).
  4. Llama _toggleSnmpVersion() para alineación inicial.
  5. Si error: muestra en .dc-error y retorna.

Carga de grupos (_loadGroups(selectedIds))

  1. Fetch GET /api/racks/groups.
  2. Renderiza checkboxes dinámicos (.dc-group).
  3. Marca como “checked” los grupos ya asignados (selectedIds).
  4. Si error o lista vacía: muestra fallback.

Guardado (_save())

  1. Recolecta valores del formulario:
    const payload = {
        snmp_version: version,          // "v2c" | "v3"
        snmp_community: (si v2c),
        snmp_v3_username: (si v3),
        snmp_v3_auth_protocol: (si v3),
        snmp_v3_auth_key: (solo si editado),
        snmp_v3_priv_protocol: (si v3),
        snmp_v3_priv_key: (solo si editado),
        ssh_username: (trimmed),
        ssh_password: (solo si no vacío),
        group_ids: [ids seleccionados]
    };
  2. Semántica de secretos: Solo se envían si el usuario escribió algo nuevo (campos no vacíos).
  3. PUT PUT /api/network/auto-provision/profiles/{this._profileId}/config con payload.
  4. Desabilita botón “Save” durante request.
  5. Si éxito (200):
    • Toast: “Configuration saved” (success).
    • Llama callback onSaved() si existe.
    • Cierra modal.
  6. Si error:
    • Muestra mensaje en .dc-error.
    • Habilita botón.

Integración con páginas

Observatory

Archivo: static/js/pages/observatory.js

Método: Observatory.openDeviceConfig(profileId, name)

Decodificación de ID:

const PROFILE_ID_OFFSET = 1_000_000_000;
const endpoint = deviceId >= PROFILE_ID_OFFSET
    ? `/api/monitoring/profiles/${deviceId - PROFILE_ID_OFFSET}/enable-monitoring`
    : `/api/monitoring/devices/${deviceId}/enable-monitoring`;

ObservatoryTabs

Archivo: static/js/pages/observatory/ObservatoryTabs.js

Botón HTML (en pestaña de dispositivo):

${device.source === 'profile' ? `
    <button class="btn btn-xs btn-action"
            data-action="Observatory.openDeviceConfig"
            data-args='[${device.id - 1000000000}, "${device.name.replace(/"/g, '\\"')}"]'
            title="Edit credentials and groups">Config</button>
` : ''}

Nota: Decoding inline en data-args porque es una acción declarativa.

Wireless, UPS, Digital Signage

Patrón similar: Cada página añade botón y listener:

const cfgBtn = container.querySelector('.btn-device-config');
if (cfgBtn) {
    cfgBtn.addEventListener('click', () => {
        if (window.DeviceConfigModal) {
            window.DeviceConfigModal.show(
                parseInt(cfgBtn.dataset.profileId), 
                cfgBtn.dataset.profileName || '', 
                null  // sin callback
            );
        }
    });
}

Dependencias del servicio

Frontend:

Backend (consumidos por modal):

Seguridad en frontend

Styling

CSS inlining: Usa variables CSS :root para tema:

Clases Bootstrap reutilizadas:

Casos de uso

  1. Post-descubrimiento: Dispositivo descubierto pero sin credenciales SNMP/SSH → Opera abre Config, añade community/usuario/pass.
  2. Cambio de community: Operador cambia SNMP community en switch → Actualiza en Observatory sin ir a Auto-Provision.
  3. Asignación rápida: Nuevo UPS sin grupo → Clica Config, asigna a grupo de PDU, guarda.
  4. Auditoría: Todo cambio queda registrado en log de backend (log_action).

Notas técnicas

Véase también

Subir