CreaRack-SL

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:

  • profileId (int): ID del perfil (recibido del endpoint).
  • profileName (string): Nombre a mostrar en el título (“Configure — {name}”).
  • onSaved (function | null): Callback ejecutado tras guardar (ej. recargar lista de dispositivos).

Efecto:

  • Crea el DOM si no existe.
  • Carga configuración actual con GET /api/network/auto-provision/profiles/{profileId}/config.
  • Renderiza formulario con valores actuales.
  • Muestra modal (flex overlay).

DeviceConfigModal.hide()

Cierra el modal sin guardar.

Efecto:

  • Oculta overlay con display: none.

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):

  • Toggle dinámico: muestra/oculta campos según versión.
  • Controla campos .dc-snmp-v2c y .dc-snmp-v3.

Si v2c:

  • input.dc-snmp-community (placeholder: “public”)

Si v3:

  • input.dc-v3-username
  • select.dc-v3-auth-proto (— | SHA | MD5)
  • input.dc-v3-auth-key (password, placeholder vacío, hint si key almacenada)
  • select.dc-v3-priv-proto (— | AES | DES)
  • input.dc-v3-priv-key (password, placeholder vacío, hint si key almacenada)

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

SSH

Campos:

  • input.dc-ssh-username
  • input.dc-ssh-password (password type, placeholder vacío)

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.

  • Cada checkbox: input.dc-group[value="{id}"].
  • Selected si id in group_ids del perfil.

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

Ciclo de vida

Inicialización (_ensureDOM())

  • Detecta si overlay ya existe (this._overlay).
  • Si no: crea elemento <div id="device-config-modal"> con estructura HTML inline.
  • Aplica CSS variables (tema claro/oscuro automático vía :root).
  • Llama _bindEvents().

Vinculación de eventos (_bindEvents())

  • .modal-close-btn → hide().
  • .dc-cancel → hide().
  • Click en overlay (fuera modal) → hide() (backdrop dismiss).
  • .dc-snmp-version select → _toggleSnmpVersion().
  • .dc-save button → _save().

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)

  • Valida existencia de window.DeviceConfigModal.
  • Llama show(profileId, name, () => this.loadTargets()).
  • Callback recarga lista de targets/dispositivos.

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:

  • window.ApiService — Para GET/PUT a endpoints.
  • window.showAppToast() — Notificación de éxito (opcional).

Backend (consumidos por modal):

  • GET /api/network/auto-provision/profiles/{profile_id}/config
  • PUT /api/network/auto-provision/profiles/{profile_id}/config
  • GET /api/racks/groups

Seguridad en frontend

  • XSS prevention: Usa .textContent para mostrar datos, escape HTML en _esc().
  • No almacena secretos: Modal no guarda credenciales en localStorage ni sessionStorage.
  • Placeholder safety: Campos de password siempre vacíos (placeholder: “leave blank to keep”).
  • Validación de entrada: Backend valida; frontend solo recolecta.

Styling

CSS inlining: Usa variables CSS :root para tema:

  • --card-bg — Fondo de inputs.
  • --border-color — Bordes.
  • --text-primary — Texto principal.
  • --text-secondary — Texto secundario (hints).

Clases Bootstrap reutilizadas:

  • .modal-overlay, .modal-content, .modal-header, .modal-body, .modal-footer.
  • .btn, .btn-neutral, .btn-action, .btn-sm.

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

  • Modal como singleton: this._overlay es persistente durante la sesión (se reutiliza). DOM se crea una sola vez.
  • Callback flexible: onSaved puede ser null (ej. UPS, DS) o función (ej. Observatory → loadTargets()).
  • Exclude_unset en backend: Solo campos presentes en payload se actualizan; permite actualizaciones parciales.

Véase también

  • [[feature—monitoring—device-config-modal]]
  • [[entity—network—endpoint—profile-config]]
  • [[entity—network—model—device-profile]]
  • [[entity—monitoring—view—observatory]]
  • [[concept—ui—modal-patterns]]