DEV Community

Cómo construí ora-tui: Un gestor en consola (TUI) en Go sin dependencias para Oracle RAC y Single Instance

Son las 3:15 AM de una guardia de producción. Te conectas por SSH a un nodo de Oracle RAC en Oracle Linux porque ha saltado una alerta crítica. En cuestión de segundos tienes que:

  1. Comprobar si los demonios del cluster (CRS, CSSD, EVMD, ASM) están ONLINE en todos los nodos (crsctl check cluster -all y crsctl status resource -t).
  2. Cambiar del usuario grid al usuario oracle para abrir sqlplus / as sysdba.
  3. Averiguar qué sesión está bloqueando una tabla crítica en gv$session (enq: TX - row lock contention) o qué tablespace acaba de lanzar un ORA-01654 porque llegó al 93% sin AUTOEXTEND.
  4. Si toca mantenimiento de sistema operativo en un nodo, reubicar todos los servicios RAC (srvctl relocate service), parar la instancia local y detener la pila CRS sin provocar ni un segundo de caída en las aplicaciones.

Después de repetir este ritual cientos de veces, decidí construir ora-tui: una herramienta interactiva de terminal (TUI) escrita en Go, empaquetada como un único binario estático (CGO_ENABLED=0) e instalable vía dnf install ora-tui.

En este artículo te cuento cómo funciona por dentro, cómo resolví los retos de arquitectura en servidores Oracle y cómo puedes probar su modo simulador (--mock) en tu propio portátil.


🖥️ El Diseño: Marcos Unicode (┌─┐ y ╔═╗) y Navegación con Flechas

Uno de los objetivos principales era que la herramienta se sintiera rápida y clara sobre cualquier sesión SSH, utilizando una jerarquía visual de marcos Unicode:

  • Borde Simple (┌ ─ ┐ │ └ ┘) para los paneles secundarios y tablas de datos.
  • Borde Doble (╔ ═ ╗ ║ ╚ ╝) para la cabecera principal, el panel que tiene el foco activo, las ventanas emergentes de mantenimiento y los diálogos de confirmación.
  • Barras de progreso por bloques ([█████████░] 93.0%) coloreadas según criticidad (verde $<80\%$, amarillo $80\text{–}90\%$, rojo $>90\%$).

Así se ve ora-tui en una sesión de terminal:

╔══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║                              ══ ORA-TUI :: ORACLE CLUSTER & DATABASE MANAGER ══                                      ║
╠══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ Modo: [ORACLE RAC] [MOCK DESIGN] │ Host: ora-rac01.corp.local │ Cluster: crs-prod-cluster   │ Ver: 19.24.0.0.0       ║
║ ORACLE_HOME: /u01/app/oracle/product/19.0.0/dbhome_1  │ GRID_HOME: /u01/app/19.0.0/grid                              ║
║ Estado: 2 Inst OPEN │ 4 PDBs │ 3 Sesiones (1 Lock TX) │ 1 Tablespace >90% │ 3 Inválidos │ 1 Alerta Crítica ORA-01654 ║
╠═════════════════════════════╦════════════════════════════════════════════════════════════════════════════════════════╣
║ MENU [Flechas ↑↓ + Enter]   ║ PANTALLA ACTIVA: VISTA INTEGRAL + VENTANA EMERGENTE (Borde Simple ┌─┐ y Doble ╔═╗)     ║
╠═════════════════════════════╬════════════════════════════════════════════════════════════════════════════════════════╣
║ ▸ 1. Cluster & DB Estado    ║ ┌─ NODOS DEL CLUSTER & INSTANCIAS (ORACLE RAC / SINGLE) ─────────────────────────────┐ ║
║   2. Usuarios Conectados    ║ │ NODO       ESTADO   CRS     CSSD    ASM            INST      MODO        FRA_USO   │ ║
║   3. Tablespaces & Files    ║ │ ora-rac01  ONLINE   ONLINE  ONLINE  ONLINE(+ASM1)  PRODCDB1  READ WRITE  [████░░░░]│ ║
║   4. Tablas & Objetos       ║ │ ora-rac02  ONLINE   ONLINE  ONLINE  ONLINE(+ASM2)  PRODCDB2  READ WRITE  [████░░░░]│ ║
║   5. ASM & Red Listeners    ║ └────────────────────────────────────────────────────────────────────────────────────┘ ║
║   6. RMAN, Flashback & DG   ║ ┌─ USUARIOS CONECTADOS (gv$session) ───────────┬─ TABLESPACES & BACKUPS RMAN ────────┐ ║
║   7. Alert Log & Errores    ║ │ SID,SER#  USUARIO    ESTADO   EVENTO / LOCK  │ RECURSO       ESTADO / USO %        │ ║
║   8. Centro Mantenimiento   ║ │ 142,48219 ERP_APP    ACTIVE   db file seq rd │ ERP_DATA_TBS  [█████████░] 93.0% !! │ ║
║   b. Cambiar Marcos ┌─┐/╔═╗ ║ │ 289,11042 CRM_BATCH  ACTIVE   enq: TX lock   │ SYSAUX        [████████░░] 84.4%    │ ║
║   t. Cambiar Topologia      ║ │ 512,9931  CRM_ADMIN  INACT    BLOCKER (289)  │ RMAN INCR L0  COMPLETED (498.4 GB)  │ ║
║   q. Salir (Quit)           ║ │ 604,33105 BI_REPORT  ACTIVE   ON CPU (512MB) │ GRP_RELEASE   GUARANTEED (18.4 GB)  │ ║
║                             ║ └──────────────────────────────────────────────┴─────────────────────────────────────┘ ║
║                             ║                                                                                        ║
║                             ║      ╔══════════════════════════════════════════════════════════════════════════╗      ║
║                             ║      ║      ══ VENTANA DE MANTENIMIENTO [Seleccione con Flechas ↑↓ + Enter] ══  ║      ║
║                             ║      ╠══════════════════════════════════════════════════════════════════════════╣      ║
║                             ║      ║ ▸ [1] Drenar / Aislar Nodo Cluster (Relocate Services + Stop CRS)        ║      ║
║                             ║      ║   [2] Parar / Arrancar Instancia o Base de Datos (srvctl / sqlplus)      ║      ║
║                             ║      ║   [3] Ampliar Tablespace Crítico (+Datafile 32G a ERP_DATA_TBS 93%)      ║      ║
║                             ║      ║   [4] Matar Sesión Bloqueante (KILL SESSION '512,9931,@2' IMMEDIATE)     ║      ║
║                             ║      ║   [5] Actualizar Estadísticas (DBMS_STATS) & Shrink Tablas Online        ║      ║
║                             ║      ║   [6] Recompilar Objetos Inválidos (UTL_RECOMP / COMPILE_SCHEMA)         ║      ║
║                             ║      ║   [7] Backup RMAN Archivelog + Crear Punto Restauración Garantizado      ║      ║
║                             ║      ║   [8] Analizar Errores ORA- en Alert Log & Purgar Trazas ADRCI           ║      ║
║                             ║      ║   [Esc] Cancelar / Cerrar Ventana                                        ║      ║
║                             ║      ╚══════════════════════════════════════════════════════════════════════════╝      ║
╠═════════════════════════════╩════════════════════════════════════════════════════════════════════════════════════════╣
║ ┌─ CONSOLA DE EJECUCION & AUDITORIA EN VIVO ───────────────────────────────────────────────────────────────────────┐ ║
║ │ [08:05:01] Auto-Discovery: Modo=ORACLE RAC │ Cluster=crs-prod-cluster │ Release=19.24.0.0.0                      │ ║
║ │ [08:05:02] ALERTA ORA-01654: Tablespace ERP_DATA_TBS al 93.0% │ Bloqueo TX detectado en SID 512 -> SID 289       │ ║
║ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
Enter fullscreen mode Exit fullscreen mode

⚙️ Decisiones Clave de Arquitectura

1. ¿Por qué CGO_ENABLED=0 y cero librerías de cliente C?

La mayoría de los drivers de Oracle requieren CGO y enlazar dinámicamente contra libclntsh.so (Oracle Instant Client). En entornos empresariales con RHEL / Oracle Linux 7, 8 y 9 y distintas versiones de glibc, distribuir binarios con dependencias dinámicas es una fuente constante de problemas.

En ora-tui, el binario se compila 100% estático (CGO_ENABLED=0 GOOS=linux GOARCH=amd64) y se comunica directamente con las herramientas nativas que ya residen en $ORACLE_HOME/bin y $GRID_HOME/bin (sqlplus -s / as sysdba, srvctl, crsctl, olsnodes, lsnrctl, rman, adrci).

2. Auto-Descubrimiento sin Archivos de Configuración

Nada más ejecutarse, ora-tui descubre qué tipo de instalación Oracle tiene delante sin pedir un solo archivo .yaml o .env:

  1. Topología del Servidor: Lee /etc/oracle/ocr.loc y /etc/oracle/olr.loc:
    • local_only=false $\rightarrow$ ORACLE RAC
    • local_only=true $\rightarrow$ ORACLE RESTART (Standalone Grid)
    • No existe $\rightarrow$ SINGLE INSTANCE
  2. Homes e Instancias Vivas en /proc: Además de leer /etc/oratab e inventory.xml, escanea /proc/[0-9]*/cmdline para localizar procesos ora_pmon_<SID>, db_pmon_<SID> (Oracle 21c/23ai) y asm_pmon_+ASM*. Así detecta bases de datos levantadas aunque nadie las haya registrado en /etc/oratab.
func ExtractSIDFromPMON(cmdline string) (sid string, isASM bool) {
    fields := strings.Fields(cmdline)
    if len(fields) == 0 {
        return "", false
    }
    proc := filepath.Base(fields[0])
    switch {
    case strings.HasPrefix(proc, "ora_pmon_"):
        return strings.TrimPrefix(proc, "ora_pmon_"), false
    case strings.HasPrefix(proc, "db_pmon_"):
        return strings.TrimPrefix(proc, "db_pmon_"), false
    case strings.HasPrefix(proc, "asm_pmon_"):
        return strings.TrimPrefix(proc, "asm_pmon_"), true
    }
    return "", false
}
Enter fullscreen mode Exit fullscreen mode

3. Cambio Dinámico de Usuario UNIX (root $\rightarrow$ oracle / grid)

En un cluster RAC con separación de roles, grid es dueño de $GRID_HOME y oracle es dueño de $ORACLE_HOME. Ejecutar sqlplus o srvctl como root falla o ensucia permisos.

ora-tui lee el uid y gid propietarios de cada directorio con os.Stat y lanza cada subproceso con las credenciales exactas de ese usuario mediante syscall.Credential, reservando root únicamente para crsctl start/stop crs:

if !asRoot && targetOwner != nil && targetOwner.UID != 0 && currentUID == 0 {
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Credential: &syscall.Credential{
            Uid: targetOwner.UID,
            Gid: targetOwner.GID,
        },
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Marcos Simples (┌─┐) y Dobles (╔═╗) en tview

La librería rivo/tview permite definir globalmente los runas de dibujo de cajas, distinguiendo entre ventanas normales y ventanas con foco activo. Configurando tview.Borders, conseguimos que cualquier tabla o menú emergente resalte automáticamente con borde doble cuando el usuario lo selecciona con las flechas:

// Borde simple (┌ ─ ┐ │ └ ┘) para contenedores y tablas estándar
tview.Borders.Horizontal = '─'
tview.Borders.Vertical = '│'
tview.Borders.TopLeft = '┌'
tview.Borders.TopRight = '┐'
tview.Borders.BottomLeft = '└'
tview.Borders.BottomRight = '┘'

// Borde doble (╔ ═ ╗ ║ ╚ ╝) para la ventana con foco y modales emergentes
tview.Borders.HorizontalFocus = '═'
tview.Borders.VerticalFocus = '║'
tview.Borders.TopLeftFocus = '╔'
tview.Borders.TopRightFocus = '╗'
tview.Borders.BottomLeftFocus = '╚'
tview.Borders.BottomRightFocus = '╝'
Enter fullscreen mode Exit fullscreen mode

Además, pulsando la tecla b en cualquier momento, puedes alternar en caliente entre el modo mixto (┌─┐ + ╔═╗), todo borde doble (╔═╗), todo borde simple (┌─┐) o ASCII clásico de 7 bits (+ - = |) si entras desde una consola serie antigua.


🛠️ Los 8 Módulos de Diagnóstico y Mantenimiento Guiado

En lugar de memorizar comandos, te mueves con las flechas del teclado (↑/↓) por las 8 vistas y pulsas Enter sobre cualquier fila para abrir su menú contextual de mantenimiento:

  1. Cluster & DB Estado: Salud de demonios (CRS, CSSD, EVMD, ASM), VIPs, instancias, PDBs y Drenado Seguro de Nodo (Graceful Node Drain) en 4 pasos:
    • Paso 1: Pre-chequeo de nodos failover ONLINE.
    • Paso 2: Reubicación en caliente de servicios (srvctl relocate service).
    • Paso 3: Parada limpia de la instancia local (srvctl stop instance -stopoption IMMEDIATE).
    • Paso 4: Parada opcional de la pila de clusterware (crsctl stop crs).
  2. Usuarios Conectados (gv$session): Detección instantánea de bloqueos TX/TM, SQL_ID, evento de espera y texto SQL actual, con opción para ejecutar KILL SESSION IMMEDIATE o DISCONNECT SESSION POST_TRANSACTION.
  3. Tablespaces & Datafiles: Barras visuales de llenado y asistente para añadir datafiles en ASM (+DATA), activar AUTOEXTEND ON o hacer COALESCE.
  4. Tablas, Segmentos & Objetos Inválidos: Identificación de tablas con estadísticas STALE o alta fragmentación para lanzar DBMS_STATS.GATHER_TABLE_STATS, SHRINK SPACE CASCADE, REBUILD ONLINE de índices o recompilar objetos inválidos con UTL_RECOMP.
  5. ASM & Red Listeners: Monitorización de diskgroups (v$asm_diskgroup) y control de listeners locales y SCAN (lsnrctl start/stop/reload).
  6. RMAN, Flashback & Data Guard: Estado de backups RMAN, creación de Guaranteed Restore Points (GRP) antes de un cambio crítico y verificación de lag en réplicas Data Guard Standby.
  7. Alert Log & Errores ORA-: Lectura directa sobre v$diag_alert_ext con salto automático al módulo que resuelve el error y purgado de trazas con adrci.
  8. Centro de Mantenimiento: Catálogo guiado de operaciones DBA con confirmación de seguridad y consola de auditoría en tiempo real.

🚀 Pruébalo en 30 Segundos (Modo Simulación --mock)

No necesitas tener un cluster Oracle RAC instalado en tu máquina para probar la interfaz. ora-tui incluye un modo simulador (--mock) con datos realistas de un cluster RAC en producción (incluyendo sesiones bloqueadas, un tablespace al 93% y alertas ORA-01654):

git clone https://github.com/mario/ora-tui.git
cd ora-tui

# Compilar y lanzar el simulador interactivo en modo Oracle RAC:
make mock

# O ver la captura estática directamente en tu terminal:
make preview
Enter fullscreen mode Exit fullscreen mode

¿Qué otra tarea repetitiva de administración Oracle te gustaría automatizar desde un menú de consola? ¡Te leo en los comentarios!

Top comments (0)