From 2f4dcb62ead87a5810c73cba2ff71fba46626935 Mon Sep 17 00:00:00 2001 From: Ricardo Monla Date: Fri, 20 Feb 2026 22:52:05 -0300 Subject: [PATCH] =?UTF-8?q?[ADN=20/=20Bit=C3=A1cora]=20Evoluci=C3=B3n=20Pr?= =?UTF-8?q?ogresiva:=20Sistema=20de=20Iconograf=C3=ADa=20Dual=20(Estado=20?= =?UTF-8?q?+=20Sem=C3=A1ntica)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + adn/02_protocolo.md | 63 +++-- bitacoras/2026-02-20.md | 54 ++++ tools/dtic-BKPs/CHANGELOG.md | 39 +++ tools/dtic-BKPs/PROYECT_dtic-BKPs.md | 350 ++++++++++++++++++++++++++ tools/dtic-BKPs/README.md | 87 +++++++ tools/dtic-BKPs/app-run-sh | 13 + tools/dtic-BKPs/dtic-BKPs_app.rb | 274 ++++++++++++++++++++ tools/dtic-BKPs/dtic-BKPs_tasks.rb | 85 +++++++ tools/dtic-BKPs/fxs/config_handler.rb | 79 ++++++ tools/dtic-BKPs/fxs/proc_syncPmox.rb | 187 ++++++++++++++ tools/dtic-BKPs/fxs/proc_syncXEN.rb | 126 ++++++++++ tools/dtic-BKPs/fxs/sync_rclone.rb | 70 ++++++ tools/dtic-BKPs/fxs/ui_handlers.rb | 119 +++++++++ tools/dtic-BKPs/logs/.gitignore | 3 + tools/dtic-BKPs/logs/.gitkeep | 0 tools/dtic-BKPs/src/core/globals.rb | 57 +++++ tools/dtic-BKPs/src/install.sh | 74 ++++++ 18 files changed, 1666 insertions(+), 15 deletions(-) create mode 100644 bitacoras/2026-02-20.md create mode 100644 tools/dtic-BKPs/CHANGELOG.md create mode 100644 tools/dtic-BKPs/PROYECT_dtic-BKPs.md create mode 100644 tools/dtic-BKPs/README.md create mode 100755 tools/dtic-BKPs/app-run-sh create mode 100755 tools/dtic-BKPs/dtic-BKPs_app.rb create mode 100644 tools/dtic-BKPs/dtic-BKPs_tasks.rb create mode 100644 tools/dtic-BKPs/fxs/config_handler.rb create mode 100644 tools/dtic-BKPs/fxs/proc_syncPmox.rb create mode 100644 tools/dtic-BKPs/fxs/proc_syncXEN.rb create mode 100644 tools/dtic-BKPs/fxs/sync_rclone.rb create mode 100644 tools/dtic-BKPs/fxs/ui_handlers.rb create mode 100644 tools/dtic-BKPs/logs/.gitignore create mode 100644 tools/dtic-BKPs/logs/.gitkeep create mode 100644 tools/dtic-BKPs/src/core/globals.rb create mode 100755 tools/dtic-BKPs/src/install.sh diff --git a/.gitignore b/.gitignore index 6ad48659..c89f6241 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ secrets/ # Logs *.log logs/ +!tools/dtic-BKPs/logs/ # Temporales y caché tmp/ diff --git a/adn/02_protocolo.md b/adn/02_protocolo.md index 4feb277f..bcdda304 100644 --- a/adn/02_protocolo.md +++ b/adn/02_protocolo.md @@ -29,8 +29,8 @@ Este documento establece las reglas fundamentales para la interacción, document - **Secciones Obligatorias**: ### 4.1 Control de Gestión -- **Pendientes**: Tabla de tareas por hacer (ID | NODO | DETALLE). -- **En Proceso**: Tabla de tareas activas (ID | NODO | DETALLE). +- **Pendientes**: Tabla de tareas por hacer (ID **[Icono] - [Hora Ini] - [Nombre Tarea]** | NODO | DETALLE). +- **En Proceso**: Tabla de tareas activas (ID **[Icono] - [Hora Ini] - [Nombre Tarea]** | NODO | DETALLE). - **Resumen de Actividades**: - **Formato**: Tabla (NODO | RESUMEN INTEGRAL). - **Estilo**: **Narrativa Sintética e Integral**. SIN TÍTULOS NI ESTADOS en el texto. Solo la historia consolidada del día para ese nodo. @@ -38,12 +38,18 @@ Este documento establece las reglas fundamentales para la interacción, document ### 4.2 Actividades Detalladas - **Subdivisión**: Encabezados por Nodo (`### srv-ns8`). -- **Formato**: Tabla detallada (Tema | Detalle | Ini | Fin). -- **Estilo**: **Narrativa Generosa y Técnica**. Aquí se explican los "qué", "cómo" y "por qué". -- **Estados**: Uso de iconos (✅, 🧹, 🔧) para visualización rápida. +- **Formato**: Tabla detallada (Tema | Hitos). +- **Nomenclatura del Tema**: `[Estado] [Categoría] - [ID] - **[Nombre Tarea]**` (ej. `✅ 💾 - A09 - **dtic-BKPs C4**`). +- **Nomenclatura de Hitos**: `⏱️ **Cronología:**` seguido de viñetas con formato `- [Icono Semántico] **[Hora] hs**: [Descripción]` (ej. `- 🚀 **09:13 hs**: Reanudación del ciclo...`). +- **Estilo (Hitos)**: **Narrativa Generosa y Técnica**. Explicando los "qué" y "por qué". +- **Estados (Tema)**: Estrictamente delimitados para trazabilidad (✅ Cerrado, ⏳ En Proceso, ⚠️ Fallo). +- **Categoría (Tema)**: Uso de taxonomía semántica (🔧, 🧹, 📝, 🧪, etc) acoplada al Estado para dar catalogación de lectura rápida. +- **Semántica (Hitos)**: Uso libre de iconos descriptivos (🐛, 💾, 👁️, 🚀) integrados al nivel de la viñeta de tiempo para dar contexto visual detallado a cada evento. ### 4.3 Footer de Mantenimiento (IA) -- Al final de cada bitácora, incluir el bloque de comentarios HTML con las **Premisas de Trabajo** (Orden inverso, Nomenclatura, Flujo de Carga: Detalle -> Resumen). +- Al final de cada bitácora, incluir el bloque de comentarios HTML con las **Premisas de Trabajo**. +- **Evolución**: El footer debe ser idéntico al del día anterior o una versión evolucionada (con nuevas reglas). NUNCA una versión simplificada o diferente. +- **Registro**: Las nuevas reglas se acumulan para mantener la memoria del sistema. ## 5. Documentación de Nodos (Snapshots) - **Ubicación**: `nodos/.md` (Raíz del proyecto). @@ -61,18 +67,30 @@ Este documento establece las reglas fundamentales para la interacción, document 3. **Si no hay confirmación**: Revertir cambios inmediatamente y restaurar estado anterior. 4. **Si hay confirmación**: Hacer cambios persistentes. -## 7. Referencia de Estados (Bitácora Detallada) -Para optimizar espacio horizontal en tablas de detalle: +## 7. Referencia de Iconografía +Para optimizar el escaneo visual de las bitácoras, la columna de Tema implementa un Sistema Visual Dual (`[Estado] [Categoría]`): +### 1. Icono de Estado (Obligatorio) | Icono | Significado | Descripción | | :---: | :--- | :--- | -| ✅ | **Completado** | Tarea finalizada o problema solucionado exitosamente. | -| ⏳ | **En Proceso** | Tarea iniciada pero pendiente de cierre. | -| 🟢 | **Operativo** | Servicio funcionando correctamente tras intervención. | -| 🔧 | **Mantenimiento** | Acción técnica de configuración o ajuste. | -| 🧪 | **Verificado** | Validación técnica o prueba superada. | -| ⚠️ | **Alerta** | Problema detectado, riesgo o atención requerida. | -| 📍 | **Definido** | Hito establecido o prioridad fijada. | +| ✅ | **Completado** | Tarea general finalizada exitosamente. | +| ⏳ | **En Proceso** | Tarea en ejecución o pendiente de cierre. | +| ⚠️ | **Fallo / Alerta** | Tarea fallida, cancelada o riesgo detectado. | +| 📍 | **Definido** | Pendiente en la cola de planificación (Control de Gestión). | + +### 2. Iconos Semánticos (Categoría del Tema & Hitos Internos) +| Icono | Orientación | Ejemplos de Uso | +| :---: | :--- | :--- | +| 🟢 | **Operativo** | Servicio funcionando, confirmación de red, OK. | +| 🔧 | **Configuración** | Mantenimiento, ajuste de parámetros, cambios de código. | +| 🧪 | **Prueba** | Validación técnica, ensayos, simulaciones, logs. | +| 🐛 | **Bug / Fix** | Errores de software, excepciones, correcciones. | +| 🧹 | **Limpieza** | Refactorización, purga, reubicación de archivos. | +| 💾 | **Datos** | Backups, volcados, sincronización de discos, almacenamiento. | +| 🚀 | **Despliegue** | Lanzamiento masivo, ejecución grande, puesta en marcha. | +| 👁️ | **Análisis** | Revisión humana, planificación, auditoría, brainstorming. | +| 🧠 | **Estructura** | Conceptualización de arquitectura, metodologías, ideas críticas. | +| 📝 | **Documentación**| Actualización del ADN, manuales, reportes, logs manuales. | ## 8. Resguardo del Repositorio (Safeguard) - **Objetivo**: Asegurar la integridad y el historial de cambios del proyecto. @@ -80,3 +98,18 @@ Para optimizar espacio horizontal en tablas de detalle: - **Frecuencia**: Al finalizar una tarea significativa o conjunto de tareas relacionadas (hito). - **Alcance**: Todo cambio en `adn/`, `servicios/config`, `scripts/` y documentación `docs/`. - **Formato Mensaje**: `[Categoría] Descripción breve del cambio`. + +## 9. Evolución Progresiva (Mejora Continua) +- **Principio**: El ADN no es estático; evoluciona a partir de los descubrimientos y buenas prácticas (emergentes) surgidas durante la operación diaria. +- **Flujo de Vida de una Mejora**: + 1. **Incubación (Prueba de Concepto)**: Una técnica experimental (ej. notas ocultas HTML para instanciar a la IA, rediseños de interfaces) se prueba bajo fuego en la trinchera (bitácoras diarias, scripts, reportes). + 2. **Identificación**: Se reconoce el valor sistémico y repetible de la técnica. + 3. **Formalización**: + 4. UNIFICACIÓN: Una sola fila por Nodo en Resumen. + 5. ESTADO ACTIVO: Tareas iniciadas sin finalizar van a 'En Proceso'. + 6. IDENTIFICADORES: Usar el correlativo exacto del Tema (ej. `[✅ - 09:13 - **dtic-BKPs C4**](#...)`). + 7. ARMONÍA: Consultar y seguir estrictamente las premisas del ADN (`adn/02_protocolo.md`)., `adn/01_ontologia.md`, etc.). + 4. **Propagación**: A partir de su formalización, la regla es de cumplimiento obligatorio para el trabajo futuro y, donde sea viable, se refactoriza el material previo para mantener la Consistencia (ver Sección 3). +- **Ejemplos Fundacionales**: + 1. **Estructura Dinámica**: Las *Premisas de Trabajo (IA)* en el footer de las bitácoras nacieron como un experimento y evolucionaron orgánicamente hasta convertirse en un estándar estructural oficial (Sección 4.3). + 2. **Migración Tecnológica (Bash → Ruby)**: El salto de scripts procedimentales en Bash hacia aplicaciones estructuradas en Ruby (`dtic-BKPs`). La operación real demostró que Bash resultaba frágil e inescalable para secuencias complejas. Ruby fue probado, identificado como superior por su manejo de excepciones y adaptabilidad estructural, formalizado en la reescritura de las herramientas, y propagado como el nuevo estándar de facto para construir utilidades dentro del repositorio. diff --git a/bitacoras/2026-02-20.md b/bitacoras/2026-02-20.md new file mode 100644 index 00000000..78f4ffde --- /dev/null +++ b/bitacoras/2026-02-20.md @@ -0,0 +1,54 @@ +# Bitácora de Operaciones - 20/02/2026 + +## Control de Gestión + +### 🚩 Pendientes +| ID | NODO | DETALLE | +| :--- | :--- | :--- | + +### ⏳ En Proceso +| ID | NODO | DETALLE | +| :--- | :--- | :--- | +| [⏳ - A13 - **ADN / Mejora Continua (Hitos)**](#srv-ns8) | [srv-ns8](#srv-ns8) | Refactorización de la tabla de bitácoras transformando la columna de detalles en `Hitos` y replanteando la metadata de control (A00). Tarea en iteración activa por nuevas ideas estructurales. | + +### 📝 Resumen de Actividades +| NODO | RESUMEN INTEGRAL | +| :--- | :--- | +| [srv-ns8](#srv-ns8) | Inicio del procesamiento automatizado de copias de seguridad de la infraestructura y estructuración operativa del procesador de respaldos dtic-BKPs. | + +--- + +## 📂 Actividades Detalladas + +### srv-ns8 +| Tema | Hitos | +| :--- | :--- | +| ⏳ 👁️ - A14 - **ADN / Mejora Continua (Íconos)** | Evolución de la iconografía de la bitácora: Los iconos en la columna "Tema" ahora son estrictamente de estado (`✅` cerrado, `⏳` abierto), delegando los iconos semánticos descriptivos (`🔧`, `🧹`, `⚠️`, etc.) al nivel de la viñeta individual dentro de la cronología "Hitos".

⏱️ **Cronología:**
- 👁️ **22:33 hs**: Conceptualización de la separación visual estado/semántica.
- ✍️ **22:45 hs**: Refactorización de todos los registros del día para adoptar la norma de iconos semánticos en eventos. | +| ⏳ 📝 - A13 - **ADN / Mejora Continua (Hitos)** | Refactorización de la tabla de bitácoras transformando la columna de detalles en `Hitos` con viñetas de marca de tiempo, eliminando las columnas `Ini/Fin` obsoletas, y adoptando identificadores estáticos alfanuméricos (`A00`) dentro del Tema. Tarea devuelta a estado activo por nuevas ideas del usuario en proceso.

⏱️ **Cronología:**
- 🏗️ **22:06 hs**: Inicio de refactorización estructural.
- 🚀 **22:15 hs**: Despliegue preliminar en formato `A00` de las actividades del día.
- 💡 **22:28 hs**: Tarea reabierta a la espera de nuevas dimensiones de mejora. | +| ✅ 📝 - A12 - **ADN / Mejora Continua (Formato)** | Rediseño estructural de la tabla de Actividades Detalladas combinando [Estado] - [Hora] - [Nombre Tarea] en la columna unificada `Tema`. Formalizado como estándar en el ADN.

⏱️ **Cronología:**
- 🧠 **21:45 hs**: Conceptualización aprobada.
- 📝 **21:55 hs**: Modificación de `02_protocolo.md` (Sección 4.2 y Regla 6). | +| ✅ 📝 - A11 - **ADN / Mejora Continua** | Ampliada oficial la Sección 9 del Protocolo catalogando la migración de scripts operativos de Bash hacia procesadores robustos en Ruby (`dtic-BKPs`) como segundo Ejemplo Fundacional histórico de Evolución Tecnológica sistémica.

⏱️ **Cronología:**
- ✍️ **14:14 hs**: Inicio de redacción.
- 💾 **14:26 hs**: Fin y guardado de la nueva sección empírica. | +| ✅ 📝 - A10 - **ADN / Mejora Continua** | Inyectado de manera formal el marco metodológico "Evolución Progresiva" (Sección 9) dentro de `02_protocolo.md`. Esto define el ciclo de vida (Incubación → Identificación → Formalización → Propagación) para madurar prácticas sistémicas emergentes.

⏱️ **Cronología:**
- ✍️ **13:35 hs**: Inicio.
- 💾 **13:40 hs**: Fin. | +| ✅ 💾 - A09 - **dtic-BKPs C4** | Ejecución del ciclo masivo de backups nocturno automáticos en modo C4 finalizó con éxito tras ~8.5Hrs de carga total. Se confirma absoluta resiliencia de conexión.

⏱️ **Cronología:**
- 🚀 **09:13 hs**: Reanudación del ciclo para atacar la carga masiva y uso efectivo del `--delete-before` para purgar One-Drive.
- 🟢 **09:39 hs**: Fase Proxmox saneada. Montaje y volcado de las VMs de `zfsDISCO1` superado (evasión del bug de passphrase SSH).
- ☁️ **12:54 hs**: Inicio de sincronización bruta a OneDrive del nodo ultrapesado `srv-SysACADWeb` despues limpiar espacio.
- 👁️ **14:29 hs**: Control interactivo de avance: Flujo de red constante, sin cuellos de botella ni errores Code 7.
- 🏁 **17:35 hs**: Finalización exitosa global y cierre del script `dtic-BKPs`. | +| ✅ 🔧 - A08 - **dtic-BKPs / Proxmox** | Resolución del error de montaje `zfsDISCO1`. Se reconfiguró el perfil rclone `pve_PMOX3` para inyectar correctamente la autenticación SSH liberada por agente en memoria vaciando el parámetro manual de archivo de llave e indicando `--key-use-agent`.

⏱️ **Cronología:**
- 🐛 **08:55 hs**: Inicio de diagnóstico del agente SSH.
- 🔧 **09:05 hs**: Fin de reconfiguración del remoto de Rclone. | +| ✅ 🔧 - A07 - **dtic-BKPs / script** | Corrección del launcher `app-run-sh` estableciendo comportamiento daemonizado realístico (`-d -m`) sobre la sesión GNU screen.

⏱️ **Cronología:**
- 🐛 **08:55 hs**: Inicio de diagnóstico del bug visual de la terminal interactiva.
- 🔧 **09:05 hs**: Corrección a daemon inyectada. | +| ✅ 🔧 - A06 - **.gitignore** | Excepción (`!tools/dtic-BKPs/logs/`) configurada en la raíz para permitir el versionado del subdirectorio de logs.

⏱️ **Cronología:**
- 🔧 **08:28 hs**: Realizado. | +| ✅ 🔧 - A05 - **dtic-BKPs / rclone** | Refactorización del script `sync_rclone.rb`. Se inyectaron parámetros de resiliencia de conexión (`--retries 5`, `--timeout 30m`) y se forzó la purga previa de archivos (`--delete-before`) para optimizar el uso de almacenamiento remoto.

⏱️ **Cronología:**
- 🏗️ **08:10 hs**: Inicio de refactorización Ruby.
- 💾 **08:15 hs**: Fin. | +| ✅ 🧪 - A04 - **Análisis dtic-BKPs** | Diagnóstico del fallo de sincronización nocturna. Causas: (1) `pve_PMOX3` - Error montando disco remoto `zfsDISCO1`. (2) `srv-SysACADWeb` - Error rclone (Code 7) por desconexión de red o límite de cuota en nube.

⏱️ **Cronología:**
- 👁️ **07:42 hs**: Inicio de revisión de logs de madrugada.
- ⚠️ **07:55 hs**: Fin de diagnóstico y raíces establecidas. | +| ✅ 🧹 - A03 - **Estructura** | Reubicación de la herramienta de respaldos abandonando `scripts/_app_dtic-BKPs` para establecerse definitivamente en `tools/dtic-BKPs`.

⏱️ **Cronología:**
- 🧹 **07:36 hs**: Inicio de reubicación.
- 💾 **07:45 hs**: Fin. | +| ✅ 🔧 - A02 - **dtic-BKPs** | Estructuración y revisión operativa del procesador de respaldos automatizado (v5.5.1) en Ruby, integrando los scripts de ejecución via screen.

⏱️ **Cronología:**
- 🏗️ **07:00 hs**: Inicio de revisión operativa.
- 🧪 **07:28 hs**: Fin. | +| ⚠️ ⚠️ - A01 - **Fallo de Procesamiento** | La ejecución del ciclo automatizado de respaldos (`dtic-BKPs`) falló debido a problemas de conectividad con almacenamiento (Proxmox 3) y error de sincronización hacia OneDrive (cód 7).

⏱️ **Cronología:**
- ⚠️ **00:12 hs**: Inicio de falla registrada.
- 💥 **05:36 hs**: Caída final del ciclo. | + +--- + + diff --git a/tools/dtic-BKPs/CHANGELOG.md b/tools/dtic-BKPs/CHANGELOG.md new file mode 100644 index 00000000..a2351174 --- /dev/null +++ b/tools/dtic-BKPs/CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [5.5.1] - 2025-11-14 + +### Fixed + +- Fixed naming inconsistencies across the application, ensuring all references to the app name are correct. + +## [5.5.0] - 2025-11-14 + +### Added + +- Automatic execution via command-line parameters + +## [5.4.1] - 2025-11-14 + +### Fixed + +- Mejora en el manejo de sesiones de screen en app-run.sh para adjuntar automáticamente a la sesión al ejecutar el script. + +## [5.4] - 2025-11-14 + +### Changed + +- Modified synchronization process to execute in subdirectories before root. + +## [5.3] - 2025-11-13 + +### Changed + +- Reorganization of prompts files +- Documentation updates \ No newline at end of file diff --git a/tools/dtic-BKPs/PROYECT_dtic-BKPs.md b/tools/dtic-BKPs/PROYECT_dtic-BKPs.md new file mode 100644 index 00000000..1310aed9 --- /dev/null +++ b/tools/dtic-BKPs/PROYECT_dtic-BKPs.md @@ -0,0 +1,350 @@ +# Documentación del Proyecto dtic-BKPs + +## Resumen del Proyecto + +**Nombre del Proyecto:** dtic-BKPs +**Versión:** v5.5.1 +**Autor:** Lic. Ricardo MONLA (rmonla@) +**Organización:** Dirección de TIC, Facultad Regional La Rioja, Universidad Tecnológica Nacional +**Licencia:** MIT License + +### Propósito y Descripción + +dtic-BKPs es un procesador automatizado de respaldos escrito en Ruby, diseñado para manejar el procesamiento, compresión y sincronización de respaldos de máquinas virtuales desde diversas plataformas de virtualización. El sistema proporciona un menú interactivo basado en terminal para gestionar operaciones de respaldo en entornos XenServer/XCP-ng y Proxmox VE. + +La aplicación automatiza el flujo de trabajo de descarga de respaldos desde servidores remotos, procesándolos (compresión, organización) y subiéndolos a almacenamiento en la nube u otros destinos utilizando rclone para sincronización universal de archivos. + +### Características Principales + +- **Interfaz de Terminal Interactiva:** Sistema basado en menús para ejecutar tareas de respaldo +- **Soporte Multiplataforma:** Procesamiento nativo para XenServer/XCP-ng (archivos .xva) y respaldos de Proxmox VE +- **Sincronización Universal:** Integración con rclone para sincronizar a cualquier destino local o remoto +- **Arquitectura Modular:** Estructura de código organizada con procesadores separados para diferentes tipos de respaldo +- **Tareas Configurables:** Definiciones de tareas editables a través de la interfaz de la aplicación +- **Logging Integral:** Logging detallado con retroalimentación visual e indicadores de progreso +- **Secuencias de Comandos:** Soporte para flujos de trabajo de respaldo de múltiples pasos + +## Estructura del Proyecto + +### Diseño de Directorios + +``` +dtic-BKPs/ +├── _vStable/ # Directorio de versión estable +│ ├── dtic-BKPs_app.rb # Script principal de la aplicación +│ ├── dtic-BKPs_tasks.rb # Archivo de configuración de tareas +│ ├── runDticBKPs.sh # Script de ejecución +│ ├── README.md # Documentación del proyecto +│ ├── LICENSE # Licencia MIT +│ ├── logs/ # Directorio de archivos de log +│ │ └── dtic-BKPs.log # Log principal de la aplicación +│ ├── fxs/ # Módulos de funciones +│ │ ├── config_handler.rb # Gestión de configuración +│ │ ├── ui_handlers.rb # Manejadores de interfaz de usuario +│ │ ├── proc_syncPmox.rb # Procesador de respaldos Proxmox +│ │ ├── proc_syncXEN.rb # Procesador de respaldos Xen +│ │ └── sync_rclone.rb # Sincronización rclone +│ └── src/ # Código fuente y documentación +│ ├── install.sh # Script de instalación +│ ├── project__v5.5.0_*.md # Documentación del proyecto +│ └── core/ # Módulos principales +│ └── globals.rb # Constantes globales y utilidades +├── _vStable/ # Versiones estables anteriores +└── _basurero/ # Directorio de archivos +``` + +### Componentes Principales + +#### Aplicación Principal (`dtic-BKPs_app.rb`) +- Punto de entrada del procesador de respaldos +- Sistema de menú interactivo +- Motor de ejecución de tareas +- Carga y gestión de configuración + +#### Configuración de Tareas (`dtic-BKPs_tasks.rb`) +- Define tareas individuales de respaldo +- Contiene secuencias de comandos (flujos de trabajo de múltiples pasos) +- Parámetros configurables para cada tipo de tarea + +#### Módulos de Funciones (`fxs/`) +- **config_handler.rb:** Carga y guarda configuraciones de tareas +- **ui_handlers.rb:** Gestiona interfaz de usuario e interacciones de menú +- **proc_syncPmox.rb:** Procesa conjuntos de respaldos de Proxmox VE +- **proc_syncXEN.rb:** Procesa archivos .xva de XenServer/XCP-ng +- **sync_rclone.rb:** Maneja operaciones de sincronización rclone + +## Stack Tecnológico + +### Tecnologías Principales + +| Componente | Tecnología | Versión | Propósito | +|------------|------------|---------|-----------| +| **Lenguaje de Programación** | Ruby | 2.7+ | Lógica principal de la aplicación | +| **Sincronización** | rclone | Última | Transferencia universal de archivos | +| **Compresión** | tar/gzip | Sistema | Compresión de respaldos | +| **Monitoreo de Progreso** | pv | Opcional | Visualización de progreso | +| **Logging** | Ruby Logger | Integrado | Logging de la aplicación | + +### Dependencias + +#### Dependencias Requeridas +- **Ruby 2.7+:** Entorno de ejecución principal +- **rclone:** Herramienta universal de sincronización de archivos +- **tar y gzip:** Utilidades de compresión (generalmente preinstaladas en Linux) + +#### Dependencias Opcionales +- **pv (Pipe Viewer):** Proporciona barras de progreso durante operaciones de compresión + +#### Requisitos del Sistema +- **Sistema Operativo:** Linux (Debian/Ubuntu recomendado) +- **Permisos:** Acceso a directorios de origen y destino de respaldos +- **Red:** Conectividad a orígenes remotos de respaldos (si aplica) +- **Almacenamiento:** Espacio suficiente para procesamiento de respaldos y archivos temporales + +## Funcionalidades + +### Tipos de Procesamiento de Respaldos + +#### 1. Procesamiento XenServer/XCP-ng (`:xva`) +- **Entrada:** Archivos de respaldo de máquinas virtuales .xva +- **Proceso:** Comprime archivos .xva individuales en archivos .tar.gz +- **Organización:** Agrupa respaldos por nombre de máquina virtual +- **Salida:** Archivos comprimidos en estructura de directorios de destino + +#### 2. Procesamiento Proxmox VE (`:proxmox_log_notes`) +- **Entrada:** Conjuntos de respaldos de Proxmox (archivos .log, .notes y datos) +- **Proceso:** Lee archivo .notes para nombre de MV, comprime conjunto completo de respaldo +- **Organización:** Crea archivos con timestamp por máquina virtual +- **Salida:** Archivos .tar.gz consolidados con nomenclatura apropiada + +#### 3. Sincronización Universal (`:rclone_sync`) +- **Entrada:** Cualquier directorio local o remoto +- **Proceso:** Utiliza rclone para sincronizar archivos entre ubicaciones +- **Destinos:** Rutas locales, almacenamiento en nube, servidores remotos +- **Características:** Sincronización incremental, reporte de progreso, manejo de errores + +### Gestión de Tareas + +#### Tareas Individuales +Cada tarea define: +- **ID Único:** Identificador simbólico para la tarea +- **Texto de Visualización:** Descripción amigable para el usuario +- **Tipo de Proceso:** `:xva`, `:proxmox_log_notes`, o `:rclone_sync` +- **Ruta de Origen:** Directorio origen (local o remoto rclone) +- **Ruta de Destino:** Directorio destino (local o remoto rclone) +- **Opciones:** Eliminar origen después del procesamiento, sobrescribir destino + +#### Secuencias de Comandos +Flujos de trabajo de múltiples pasos que ejecutan múltiples tareas en orden: +- **Descargar y Procesar:** Sincronizar desde remoto, luego procesar respaldos +- **Ciclo Completo de Respaldo:** Descargar → Procesar → Subir a nube +- **Operaciones Masivas:** Procesar múltiples orígenes de respaldos + +### Interfaz de Usuario + +#### Menú Principal +- **Tareas Individuales:** Ejecutar operaciones de respaldo únicas +- **Secuencias de Comandos:** Ejecutar flujos de trabajo de respaldo de múltiples pasos +- **Editor de Tareas:** Modificar tareas existentes o crear nuevas +- **Salir:** Cierre limpio de la aplicación + +#### Modo Editor de Tareas +- **Editar Tareas:** Modificar parámetros de tareas existentes +- **Crear Tareas:** Agregar nuevas operaciones de respaldo +- **Eliminar Tareas:** Remover tareas no utilizadas +- **Guardar Cambios:** Persistir modificaciones en archivo de configuración + +### Sistema de Configuración + +#### Archivo de Configuración de Tareas +- **Formato:** Array Ruby de hashes de tareas +- **Ubicación:** `dtic-BKPs_tasks.rb` +- **Editable:** A través de interfaz de aplicación o manualmente +- **Auto-generado:** Comentarios y formato preservados + +#### Configuración de Runtime +- **Avance Automático:** Opción para proceder automáticamente a través de tareas +- **Nivel de Logging:** Verbose configurable +- **Salida en Color:** Retroalimentación visual con colores ANSI + +## Instalación y Configuración + +### Proceso de Instalación + +1. **Descarga:** Obtener todos los archivos del proyecto +2. **Permisos:** Hacer ejecutable el script de instalación + ```bash + chmod +x install.sh + ``` +3. **Ejecutar Instalador:** Ejecutar el script de instalación + ```bash + ./install.sh + ``` +4. **Verificación de Dependencias:** El script verifica instalación de Ruby y rclone +5. **Configuración:** Genera configuración inicial de tareas + +### Configuración de Tareas + +#### Ejemplos de Configuración de Tareas + +**Procesamiento de Respaldo Xen:** +```ruby +{ + id: :proc_syncXen1, + menu_texto: "Procesar respaldos de MV Xen01", + tipo_proceso: :xva, + origen: "/mnt/respaldos/xen01/", + destino: "/mnt/procesados/xen01/", + eliminar_origen: true, + sobrescribir_destino: false, +} +``` + +**Procesamiento de Respaldo Proxmox:** +```ruby +{ + id: :proc_syncPmox, + menu_texto: "Procesar respaldos Proxmox", + tipo_proceso: :proxmox_log_notes, + origen: "servidor_pve:/var/lib/vz/dump/", + destino: "/mnt/procesados/proxmox/", + eliminar_origen: false, + sobrescribir_destino: false, +} +``` + +**Subida a Nube:** +```ruby +{ + id: :subir_nube, + menu_texto: "Subir a almacenamiento en nube", + tipo_proceso: :rclone_sync, + origen: "/mnt/procesados/", + destino: "proveedor_nube:/respaldos/", + eliminar_origen: false, + sobrescribir_destino: false, +} +``` + +## Guía de Uso + +### Operación Básica + +1. **Iniciar Aplicación:** + ```bash + ./dtic-BKPs_app.rb + ``` + +2. **Seleccionar Operación:** + - Ingresar número de tarea para ejecución individual + - Ingresar número de comando para ejecución de secuencia + - Presionar 'E' para modo de edición de tareas + +3. **Monitorear Progreso:** + - Indicadores visuales de progreso + - Actualizaciones de estado en tiempo real + - Logging detallado a archivo + +### Características Avanzadas + +#### Secuencias de Comandos +Ejecutar flujos de trabajo complejos de respaldo: +- **C1:** Descargar y procesar respaldos Proxmox +- **C2:** Ciclo completo (descargar → procesar → subir) +- **C3:** Sincronización masiva a nube + +#### Personalización de Tareas +Modificar tareas a través del editor: +- Cambiar rutas de origen/destino +- Ajustar opciones de procesamiento +- Agregar nuevos orígenes de respaldo +- Crear secuencias de comandos personalizadas + +## Logging y Monitoreo + +### Archivos de Log +- **Log Principal:** `logs/dtic-BKPs.log` +- **Formato:** Timestamp, severidad, mensaje +- **Rotación:** Rotación diaria con limpieza automática + +### Niveles de Log +- **INFO:** Operaciones normales y completación +- **WARN:** Problemas no críticos (archivos faltantes, etc.) +- **ERROR:** Fallos críticos que requieren atención + +### Características de Monitoreo +- **Estadísticas de Ejecución:** Seguimiento de tareas completadas y rendimiento +- **Indicadores de Progreso:** Retroalimentación visual durante operaciones largas +- **Reportes de Error:** Mensajes de error detallados con contexto +- **Rastro de Auditoría:** Registro completo de todas las operaciones + +## Consideraciones de Seguridad + +### Control de Acceso +- **Permisos de Archivos:** Acceso apropiado a directorios de respaldo +- **Acceso Remoto:** Configuraciones remotas rclone seguras +- **Gestión de Credenciales:** Almacenamiento seguro de credenciales de acceso + +### Protección de Datos +- **Encriptación:** Usar almacenamiento remoto encriptado cuando sea posible +- **Integridad de Respaldos:** Verificación de completitud de respaldos +- **Logging de Acceso:** Rastro de auditoría de operaciones de respaldo + +## Mantenimiento y Solución de Problemas + +### Mantenimiento Regular +- **Rotación de Logs:** Monitorear tamaños de archivos de log +- **Respaldo de Configuración:** Preservar configuraciones de tareas +- **Actualizaciones de Dependencias:** Mantener rclone y Ruby actualizados + +### Problemas Comunes +- **Dependencias Faltantes:** Ejecutar script de instalación para verificar +- **Errores de Permisos:** Verificar permisos de acceso a directorios +- **Problemas de Red:** Verificar conectividad a orígenes remotos +- **Errores de Configuración:** Validar definiciones de tareas + +### Optimización de Rendimiento +- **Procesamiento Paralelo:** Considerar múltiples instancias para diferentes orígenes +- **Planificación de Almacenamiento:** Asegurar espacio adecuado para procesamiento +- **Ancho de Banda de Red:** Monitorear velocidades de transferencia para operaciones remotas + +## Desarrollo y Extensión + +### Arquitectura +- **Diseño Modular:** Procesadores separados para diferentes tipos de respaldo +- **Configuración Dirigida:** Definiciones de tareas en archivos externos +- **Extensible:** Fácil adición de nuevos tipos de procesador + +### Agregar Nuevos Procesadores +1. Crear nuevo módulo procesador en `fxs/` +2. Agregar mapeo de procesador en aplicación principal +3. Definir parámetros de configuración de tareas +4. Actualizar documentación y ejemplos + +### Estándares de Código +- **Estilo Ruby:** Seguir convenciones y mejores prácticas de Ruby +- **Documentación:** Comentarios y documentación comprehensivos +- **Manejo de Errores:** Manejo robusto de errores y logging +- **Testing:** Testing manual de nuevas características + +## Soporte y Recursos + +### Documentación +- **README.md:** Instalación y uso básico +- **Archivos del Proyecto:** Documentación técnica detallada +- **Ejemplos de Configuración:** Definiciones de tareas de ejemplo + +### Comunidad y Soporte +- **Autor:** Lic. Ricardo MONLA (rmonla@) +- **Organización:** Dirección de TIC, Universidad Tecnológica Nacional +- **Licencia:** MIT License (código abierto) + +### Historial de Versiones +- **v4.4:** Versión modular inicial con soporte Xen y Proxmox +- **v5.2:** Sistema de configuración mejorado y editor de tareas +- **v5.5.0:** Automatic execution via command-line parameters +- **v5.5.1 (2025-11-14):** Fixed naming inconsistencies across the application to ensure correct app name references. + +--- + +*Esta documentación proporciona una visión comprehensiva del procesador automatizado de respaldos dtic-BKPs. Para detalles específicos de implementación, referirse al código fuente y archivos de configuración.* \ No newline at end of file diff --git a/tools/dtic-BKPs/README.md b/tools/dtic-BKPs/README.md new file mode 100644 index 00000000..7d83c23f --- /dev/null +++ b/tools/dtic-BKPs/README.md @@ -0,0 +1,87 @@ +# dtic-BKPs - Procesador de Backups Automatizado v5.5.1 + +`dtic-BKPs` es un script de terminal interactivo escrito en Ruby, diseñado para automatizar el procesamiento, la compresión y la sincronización de copias de seguridad. Esta versión introduce una estructura de archivos modular, una configuración simplificada y la capacidad de editar tareas directamente desde la aplicación. + +## ✨ Características Principales + +* **Menú Interactivo y Editable:** Navega, ejecuta, y ahora también **crea, edita y elimina tareas** sin salir de la aplicación. +* **Configuración Simplificada:** La definición de tareas se ha estandarizado con claves `origen` y `destino`, independientemente de si la ruta es local o remota (`rclone`). +* **Estructura Modular:** El código está organizado en módulos para la aplicación principal, configuración, instalación, logging y procesadores específicos. +* **Soporte Multiplataforma:** Procesamiento nativo para backups de **XenServer/XCP-ng** (`.xva`) y **Proxmox VE**. +* **Sincronización Universal con `rclone`:** Sincroniza archivos entre cualquier combinación de rutas locales y remotas. +* **Feedback Visual y Logging:** Mantiene la interfaz de usuario clara con colores, íconos, barras de progreso y un registro detallado en `dtic-BKPs.log`. + +## 📂 Estructura del Proyecto + +El proyecto tiene una estructura modular para facilitar su mantenimiento. Los archivos principales son: + +* `install.sh`: Script principal de instalación que prepara el entorno. +* `dtic-BKPs_app.rb`: El script que ejecuta la aplicación y su menú interactivo. +* `dtic-BKPs_install.rb`: Script en Ruby para verificar dependencias y generar la configuración inicial. +* `dtic-BKPs_conf.rb`: Aquí se guardan las tareas y preferencias. **Este archivo es modificable desde la propia aplicación**. +* `core/globals.rb`: Define constantes, colores y el logger global. +* `fxs/`: Contiene los módulos con las funciones principales (UI, manejo de configuración y procesadores). +* `LICENSE`: La licencia del proyecto (MIT). + +## 🚀 Instalación y Puesta en Marcha (Debian/Ubuntu) + +La instalación está diseñada para ser lo más simple posible. + +1. **Clona o descarga todos los archivos** del proyecto en un mismo directorio. + +2. **Otorga permisos de ejecución** al script de instalación principal: + ```bash + chmod +x install.sh + ``` + +3. **Ejecuta el instalador:** + ```bash + ./install.sh + ``` + +El script verificará si tienes `Ruby` instalado y te guiará para instalarlo si es necesario. Luego, lanzará el instalador de la aplicación (`dtic-BKPs_install.rb`), que verificará el resto de las dependencias (`rclone`, `pv`, etc.). + +## 🔧 Configuración de Tareas + +Puedes configurar las tareas de dos maneras: + +1. **Editando `dtic-BKPs_conf.rb` manualmente** (método tradicional). +2. **Usando el editor integrado** en la aplicación (opción `E` en el menú principal). + +### Formato de Tarea Simplificado + +| Clave | Descripción | Ejemplo | +| ---------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------- | +| `id` | Símbolo único para la tarea. | `:xen_procesar` | +| `menu_texto` | Descripción que se muestra en el menú. | `"Procesar backups Xen01 (.xva)"` | +| `tipo_proceso` | `:xva`, `:proxmox_log_notes` o `:rclone_sync`. | `:rclone_sync` | +| `origen` | Ruta de origen (local o remota `rclone`). | `"miRemoto:/var/lib/vz/dump/"` | +| `destino` | Ruta de destino (local o remota `rclone`). | `"/media/backups/pmox1_sync/"` | +| `eliminar_origen` | `true` o `false`. Borra archivos originales tras el procesamiento. (No aplica a `rclone`) | `true` | +| `sobrescribir_destino` | `true` o `false`. Sobrescribe archivos en el destino si ya existen. | `false` | + +## ▶️ Uso de la Aplicación + +Ejecuta `./dtic-BKPs_app.rb` para iniciar el menú. Las opciones son: + +* `[Enter]` / `[Número]`: Ejecuta la tarea seleccionada. +* `[E]`: Entra al modo de edición de tareas. +* `[S]`: Sale de la aplicación. + +### Modo Edición + +Dentro del modo de edición, puedes: +* **Editar** una tarea existente (`[Número]`). +* Añadir una **Nueva** tarea (`[N]`). +* **Borrar** una tarea (`[B]`). +* **Guardar** los cambios en `dtic-BKPs_conf.rb` y volver al menú principal (`[G]`). + +## ✍️ Autor + +* **Lic. Ricardo MONLA (rmonla@)** + +* **Dirección de TIC, Facultad Regional La Rioja, Universidad Tecnológica Nacional** + +## 📜 Licencia + +Este proyecto está bajo la Licencia MIT. Consulta el archivo `LICENSE` para más detalles. \ No newline at end of file diff --git a/tools/dtic-BKPs/app-run-sh b/tools/dtic-BKPs/app-run-sh new file mode 100755 index 00000000..0e9a8d9c --- /dev/null +++ b/tools/dtic-BKPs/app-run-sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Logging functionality for dtic-BKPs execution +LOG_FILE="./logs/runDtic-BKPs.log" +TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') + +echo "[$TIMESTAMP] Starting dtic-BKPs application via screen" >> "$LOG_FILE" + +# Execute the Ruby application in screen with relative path and start detached +screen -S dtic-BKPs_session -d -m ./dtic-BKPs_app.rb "$1" + +# Log completion (note: this will execute immediately as screen detaches) +echo "[$TIMESTAMP] dtic-BKPs screen session launched" >> "$LOG_FILE" \ No newline at end of file diff --git a/tools/dtic-BKPs/dtic-BKPs_app.rb b/tools/dtic-BKPs/dtic-BKPs_app.rb new file mode 100755 index 00000000..33cc2508 --- /dev/null +++ b/tools/dtic-BKPs/dtic-BKPs_app.rb @@ -0,0 +1,274 @@ +#!/usr/bin/ruby +# frozen_string_literal: true + +# ========================================================== +# dtic-BKPs - Automatic Backup Processor (Unified Script) +# ---------------------------------------------------------- +# APP: dtic-BKPs +# VERSION: v5.5.1 +# AUTHOR: Ricardo MONLA ( rmonla@ ) +# LICENSE: MIT License +# ========================================================== + +# --- REQUIRES GLOBALES --- +require 'fileutils' +require 'open3' +require 'time' +require 'io/console' +require 'shellwords' +require 'logger' + +# --- CARGA INICIAL DE MÓDULOS --- +# (Se asume que los otros archivos están en el mismo directorio o accesibles) +# require_relative 'core/globals' +require_relative 'fxs/config_handler' +require_relative 'fxs/ui_handlers' +require_relative 'fxs/proc_syncPmox' +require_relative 'fxs/sync_rclone' +require_relative 'fxs/proc_syncXEN' + +# ========================================================== +# CONSTANTES GLOBALES +# ========================================================== + +# --- CONSTANTES GLOBALES DEL SCRIPT --- +APP_NOM_BASE = 'dtic-BKPs' +APP_NOM = "#{APP_NOM_BASE}_app.rb" +APP_VER = 'v5.5.1' # Actualizado +APP_AUTOR = "Ricardo MONLA ( rmonla@ )" +APP_DIR_SCRIPT = File.dirname(File.expand_path(__FILE__)) +APP_CFG_NOM_ARCHIVO = "#{APP_NOM_BASE}_tasks.rb" +APP_LOG_NOM_ARCHIVO = "logs/#{APP_NOM_BASE}.log" +APP_CFG_RUTA_ARCHIVO = File.join(APP_DIR_SCRIPT, APP_CFG_NOM_ARCHIVO) +APP_LOG_RUTA_ARCHIVO = File.join(APP_DIR_SCRIPT, APP_LOG_NOM_ARCHIVO) + +# --- COLORES E ICONOS GLOBALES --- +RESET = "\e[0m"; NEGRITA = "\e[1m"; SUBRAYADO="\e[4m"; INTERMITENTE = "\e[5m" +CIAN = "\e[96m"; AMARILLO = "\e[93m"; ROJO = "\e[91m"; VERDE = "\e[92m" +ICONO_OK = "#{VERDE}✔#{RESET}"; ICONO_ERROR = "#{ROJO}✖#{RESET}"; ICONO_WARN = "#{AMARILLO}⚠#{RESET}"; ICONO_INFO = "#{CIAN}ℹ#{RESET}" + +# --- CONFIGURACIÓN GLOBAL --- + +# Bloque para configurar el logger directamente aquí +require 'logger' # Asegúrate de que Logger esté cargado +$logger = Logger.new(APP_LOG_RUTA_ARCHIVO, 'daily') +$logger.level = Logger::INFO # Puedes ajustar el nivel de log aquí +$logger.formatter = proc do |severity, datetime, progname, msg| + "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}] [#{severity}] #{msg}\n" +end + +$estadisticas_ejecucion = { tareas_completadas: {} } + +# --- FUNCIÓN DE LOGGING GLOBAL --- +def log(nivel, mensaje) + # Usa esta expresión regular más robusta para eliminar códigos ANSI + msg_l = mensaje.gsub(/\e\[(?:\d{1,3}(?:;\d{1,3})*)?[m|K]/, '') + case nivel + when :info then $logger.info(msg_l) + when :error then $logger.error(msg_l) + when :warn then $logger.warn(msg_l) + else $logger.debug(msg_l) + end +end + +# Función auxiliar para verificar si un comando existe +def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each { |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + } + end + nil +end + +# ========================================================== +# (Bucle Principal) +# ========================================================== + +# Mapea los tipos de proceso a sus funciones correspondientes. +PROCESSOR_MAP = { + xva: ->(task_cfg) { procesar_backups_xva(task_cfg) }, + rclone_sync: ->(task_cfg) { sincronizar_rclone(task_cfg) }, + proxmox_log_notes: ->(task_cfg) { procesar_backups_tipo_proxmox(task_cfg) } +} + +def ejecutar_tarea_individual(tarea_cfg) + processor = PROCESSOR_MAP[tarea_cfg[:tipo_proceso]] + if processor + processor.call(tarea_cfg) + else + puts "#{ICONO_ERROR} Tipo de proceso desconocido: #{tarea_cfg[:tipo_proceso]}" + log :error, "Tipo de proceso desconocido para tarea #{tarea_cfg[:id]}: #{tarea_cfg[:tipo_proceso]}" + end +end + +def ejecutar_comando(comando_cfg, todas_las_tareas) + system 'clear' or system 'cls' + puts "\n#{NEGRITA}#{CIAN}--- Ejecutando Comando: #{comando_cfg[:menu_texto]} ---#{RESET}" + log :info, "Iniciando ejecución del comando: #{comando_cfg[:id]}" + + comando_cfg[:tareas].each_with_index do |id_tarea, index| + tarea_actual = todas_las_tareas.find { |t| t[:id] == id_tarea } + if tarea_actual + puts "\n#{AMARILLO}--- Paso #{index + 1}/#{comando_cfg[:tareas].length}: #{tarea_actual[:menu_texto]} ---#{RESET}" + log :info, "Ejecutando tarea '#{id_tarea}' dentro del comando '#{comando_cfg[:id]}'." + ejecutar_tarea_individual(tarea_actual) + # Aquí puedes añadir lógica para pausar o continuar automáticamente + # if !AUTO_AVANZAR_TAREA_MENU # O una configuración específica para comandos + # puts "\nPresiona Enter para continuar con la siguiente tarea del comando..." + # $stdin.gets + # end + else + puts "#{ICONO_ERROR} Tarea con ID '#{id_tarea}' no encontrada para el comando '#{comando_cfg[:id]}'. Saltando." + log :error, "Tarea con ID '#{id_tarea}' no encontrada para el comando '#{comando_cfg[:id]}'." + end + end + puts "\n#{ICONO_OK} Comando '#{comando_cfg[:menu_texto]}' completado." + log :info, "Comando '#{comando_cfg[:id]}' completado." + sleep 2 +end + +def bucle_principal(tareas_config, comandos_config, auto_avanzar) + + loop do + opciones_menu = [] + + # Añadir tareas individuales al menú + tareas_config.each_with_index do |tarea_cfg, index| + opciones_menu << { + id: "T#{index + 1}", + texto: tarea_cfg[:menu_texto], + tipo: :tarea, + config: tarea_cfg + } + end + + # Añadir comandos al menú + comandos_config.each_with_index do |comando_cfg, index| + opciones_menu << { + id: "C#{index + 1}", + texto: comando_cfg[:menu_texto], + tipo: :comando, + config: comando_cfg + } + end + + opciones_menu << { id: 'E', texto: "Editar tareas y comandos programados", tipo: :especial } + opciones_menu << { id: 'S', texto: "Salir del procesador", tipo: :especial } + + system 'clear' or system 'cls' + mostrar_encabezado_app + puts "\n#{CIAN}--- Menú Principal ---#{RESET}" + + puts "\n#{NEGRITA}Tareas Individuales: #{RESET}" + opciones_menu.select { |op| op[:tipo] == :tarea }.each do |op| + puts "#{AMARILLO}#{op[:id]}.#{RESET} #{op[:texto]}" + end + + puts "\n#{NEGRITA}Comandos (Secuencias de Tareas): #{RESET}" + opciones_menu.select { |op| op[:tipo] == :comando }.each do |op| + puts "#{AMARILLO}#{op[:id]}.#{RESET} #{op[:texto]}" + end + + puts "\n#{CIAN}--------------------#{RESET}" + # Opciones especiales al final + opciones_menu.select { |op| op[:tipo] == :especial }.each do |op| + puts "#{AMARILLO}#{op[:id]}.#{RESET} #{op[:texto]}" + end + puts "#{CIAN}--------------------#{RESET}" + + opcion_usuario = leer_entrada_simple("Selecciona una opción > ").strip.upcase + opcion_encontrada = opciones_menu.find { |op| op[:id].to_s == opcion_usuario } + + if opcion_encontrada + case opcion_encontrada[:tipo] + when :tarea + ejecutar_tarea_individual(opcion_encontrada[:config]) + when :comando + ejecutar_comando(opcion_encontrada[:config], tareas_config) + when :especial + case opcion_encontrada[:id] + when 'E' + modificar_tareas_y_comandos_menu(tareas_config, comandos_config, auto_avanzar) + # Recargar configuración después de editar + tareas_config, comandos_config, auto_avanzar = cargar_configuracion + when 'S' + puts "Saliendo..."; exit 0 + end + end + + # Pausar si no es una opción de edición/salir + unless ['E', 'S'].include?(opcion_encontrada[:id].to_s) || auto_avanzar + puts "\nPresiona Enter para continuar..." + $stdin.gets + end + else + puts "#{ICONO_ERROR} Opción no válida." + sleep 1 + end + end +end + +# --- Inicio del Script --- +Signal.trap("INT") { puts "\n#{ROJO}Ctrl+C detectado. Saliendo...#{RESET}"; log :warn, "Script interrumpido (Ctrl+C)."; exit 130; } +log :info, "===== Inicio script #{APP_NOM} #{APP_VER} =====" + +tareas_config, comandos_config, auto_avanzar = cargar_configuracion + +opciones_menu = [] + +# Añadir tareas individuales al menú +tareas_config.each_with_index do |tarea_cfg, index| + opciones_menu << { + id: "T#{index + 1}", + texto: tarea_cfg[:menu_texto], + tipo: :tarea, + config: tarea_cfg + } +end + +# Añadir comandos al menú +comandos_config.each_with_index do |comando_cfg, index| + opciones_menu << { + id: "C#{index + 1}", + texto: comando_cfg[:menu_texto], + tipo: :comando, + config: comando_cfg + } +end + +opciones_menu << { id: 'E', texto: "Editar tareas y comandos programados", tipo: :especial } +opciones_menu << { id: 'S', texto: "Salir del procesador", tipo: :especial } + +if ARGV[0] + opcion_usuario = ARGV[0].strip.upcase + opcion_encontrada = opciones_menu.find { |op| op[:id].to_s == opcion_usuario } + + if opcion_encontrada + case opcion_encontrada[:tipo] + when :tarea + ejecutar_tarea_individual(opcion_encontrada[:config]) + when :comando + ejecutar_comando(opcion_encontrada[:config], tareas_config) + when :especial + case opcion_encontrada[:id] + when 'E' + modificar_tareas_y_comandos_menu(tareas_config, comandos_config, auto_avanzar) + # Recargar configuración después de editar + tareas_config, comandos_config, auto_avanzar = cargar_configuracion + when 'S' + puts "Saliendo..."; exit 0 + end + end + log :info, "Ejecutado automáticamente desde línea de comandos: #{opcion_usuario}" + exit 0 + else + puts "#{ICONO_ERROR} Opción no válida: #{opcion_usuario}" + log :error, "Opción no válida desde línea de comandos: #{opcion_usuario}" + exit 1 + end +else + bucle_principal(tareas_config, comandos_config, auto_avanzar) +end \ No newline at end of file diff --git a/tools/dtic-BKPs/dtic-BKPs_tasks.rb b/tools/dtic-BKPs/dtic-BKPs_tasks.rb new file mode 100644 index 00000000..544a7cc5 --- /dev/null +++ b/tools/dtic-BKPs/dtic-BKPs_tasks.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true +# ========================================================== +# Archivo de Configuración para dtic-BKPs +# Generado automáticamente el 2025-09-25 11:53:34 +# ========================================================== +AUTO_AVANZAR_TAREA_MENU = true +TAREAS_CONFIG = [ + { + id: :proc_syncXen1, + menu_texto: "⚙️ Procesar archivos de BKPs de VMs: syncs-XEN01", + tipo_proceso: :xva, + origen: "/mnt/ns8Disco2/syncs-XEN01/", + destino: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/", + eliminar_origen: true, + sobrescribir_destino: false, + }, + { + id: :proc_syncPmox, + menu_texto: "⚙️ Procesar archivos de BKPs de VMs desde zfsDISCO1", + tipo_proceso: :proxmox_log_notes, + origen: "pve_PMOX3:/mnt/pve/zfsDISCO1/dump/", + destino: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/", + eliminar_origen: false, + sobrescribir_destino: false, + }, + { + id: :sinc_pmox_ns8, + menu_texto: "📥 Descargar archivos de BKPs de VMs: zfsDISCO1", + tipo_proceso: :rclone_sync, + origen: "pve_PMOX3:/mnt/pve/zfsDISCO1/dump/", + destino: "/mnt/ns8Disco2/syncs-PMOX/", + eliminar_origen: false, + sobrescribir_destino: false, + }, + { + id: :sync_BkpAntiguos_nube, + menu_texto: "☁️ Upload de BKPs a rmOneDrive: bkps-ANTIGUOS", + tipo_proceso: :rclone_sync, + origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-ANTIGUOS/", + destino: "rmOneDrive:/dtic-BACKUPS/bkps-ANTIGUOS/", + eliminar_origen: false, + sobrescribir_destino: false, + }, + { + id: :sync_BkpsSERVIDORes_nube, + menu_texto: "☁️ Upload de bkps-SERVIDORes a rmOneDrive", + tipo_proceso: :rclone_sync, + origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/", + destino: "rmOneDrive:/dtic-BACKUPS/bkps-SERVIDORes/", + eliminar_origen: false, + sobrescribir_destino: false, + }, + { + id: :sync_BkpUsers_nube, + menu_texto: "☁️ Upload de bkps-USERs a rmOneDrive", + tipo_proceso: :rclone_sync, + origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-USERs/", + destino: "rmOneDrive:/dtic-BACKUPS/bkps-USERs/", + eliminar_origen: false, + sobrescribir_destino: false, + } + +] +COMANDOS_CONFIG = [ + { + id: :descargar_y_procesar_pmox, + menu_texto: "📥⚙️ Descargar y Procesar BKPs de PMOXs", + tareas: [:sinc_pmox_ns8, :proc_syncPmox], + }, + { + id: :full_procesamiento, + menu_texto: "⚙️ Procesar TODOS los BKPs de NS8", + tareas: [:proc_syncXen1, :proc_syncPmox], + }, + { + id: :full_upload, + menu_texto: "🚀 Subir a la Nube todos los BKPs", + tareas: [:sync_BkpAntiguos_nube, :sync_BkpsSERVIDORes_nube, :sync_BkpUsers_nube, :sync_BkpXen01_nube], + }, + { + id: :full_full, + menu_texto: "🚀 Procesar todo y Subir todo.", + tareas: [:proc_syncXen1, :proc_syncPmox, :sync_BkpsSERVIDORes_nube, :sync_BkpAntiguos_nube, :sync_BkpUsers_nube], + }, +] diff --git a/tools/dtic-BKPs/fxs/config_handler.rb b/tools/dtic-BKPs/fxs/config_handler.rb new file mode 100644 index 00000000..d747952a --- /dev/null +++ b/tools/dtic-BKPs/fxs/config_handler.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# ========================================================== +# FILE: fxs/config_handler.rb +# ========================================================== + +def cargar_configuracion + unless File.exist?(APP_CFG_RUTA_ARCHIVO) + puts "#{ICONO_WARN} Archivo de configuración de tareas no encontrado: #{APP_CFG_RUTA_ARCHIVO}." + puts "#{ICONO_INFO} Usando configuración interna. Los cambios se guardarán en un nuevo archivo." + # Si el archivo no existe, devuelve valores predeterminados (vacíos o un ejemplo) + return [], [], true # Retorna tareas vacías, comandos vacíos y auto_avanzar en true + end + + begin + # Carga el archivo de configuración. Esto definirá TAREAS_CONFIG y COMANDOS_CONFIG + load APP_CFG_RUTA_ARCHIVO + + unless defined?(TAREAS_CONFIG) && TAREAS_CONFIG.is_a?(Array) + puts "#{ICONO_ERROR} TAREAS_CONFIG no está definida correctamente en '#{APP_CFG_RUTA_ARCHIVO}'." + exit 1 + end + + unless defined?(COMANDOS_CONFIG) && COMANDOS_CONFIG.is_a?(Array) + puts "#{ICONO_ERROR} COMANDOS_CONFIG no está definida correctamente en '#{APP_CFG_RUTA_ARCHIVO}'." + exit 1 + end + + auto_avanzar = defined?(AUTO_AVANZAR_TAREA_MENU) ? AUTO_AVANZAR_TAREA_MENU : true + return TAREAS_CONFIG, COMANDOS_CONFIG, auto_avanzar + rescue LoadError, SyntaxError => e + puts "#{ICONO_ERROR} Error cargando configuración desde '#{APP_CFG_RUTA_ARCHIVO}':\n#{e.message}" + exit 1 + end +end + +def guardar_configuracion(tareas, comandos, auto_avanzar) + begin + File.open(APP_CFG_RUTA_ARCHIVO, 'w') do |f| + f.puts "# frozen_string_literal: true\n" + f.puts "# ==========================================================" + f.puts "# Archivo de Configuración para #{APP_NOM_BASE}" + f.puts "# Generado automáticamente el #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" + f.puts "# ==========================================================\n" + f.puts "AUTO_AVANZAR_TAREA_MENU = #{auto_avanzar}\n" + + f.puts "TAREAS_CONFIG = [" + tareas.each do |tarea| + f.puts " {" + tarea.each { |key, value| f.puts " #{key}: #{value.inspect}," } + f.puts " }," + end + f.puts "]\n" + + f.puts "COMANDOS_CONFIG = [" + comandos.each do |comando| + f.puts " {" + comando.each do |key, value| + if key == :tareas + f.puts " #{key}: [#{value.map(&:inspect).join(', ')}]," + else + f.puts " #{key}: #{value.inspect}," + end + end + f.puts " }," + end + f.puts "]" + end + puts "\n#{ICONO_OK} Configuración de tareas y comandos guardada en '#{APP_CFG_NOM_ARCHIVO}'." + log :info, "Configuración de tareas y comandos actualizada y guardada por el usuario." + sleep 1.5 + return true + rescue StandardError => e + puts "\n#{ICONO_ERROR} No se pudo guardar la configuración: #{e.message}" + log :error, "Fallo al guardar configuración de tareas y comandos: #{e.message}" + sleep 2 + return false + end +end \ No newline at end of file diff --git a/tools/dtic-BKPs/fxs/proc_syncPmox.rb b/tools/dtic-BKPs/fxs/proc_syncPmox.rb new file mode 100644 index 00000000..16c83956 --- /dev/null +++ b/tools/dtic-BKPs/fxs/proc_syncPmox.rb @@ -0,0 +1,187 @@ +# frozen_string_literal: true + +# ========================================================== +# dtic-BKPs - Automatic Backup Processor +# ---------------------------------------------------------- +# APP: dtic-BKPs +# FILE: fxs/proc_syncPmox.rb +# VERSION: v5.5.1 +# AUTHOR: Ricardo MONLA (rmonla@) +# LICENSE: MIT License +# ========================================================== + +require 'tmpdir' +require 'shellwords' +require 'open3' +require 'fileutils' + +# Carga las constantes, logger y helpers globales +# require_relative '../core/globals' + +# --- Helpers para montaje rclone --- +def montar_rclone(origen_remote, tarea_id) + dir_tmp = Dir.mktmpdir("bkps_#{tarea_id}_") + log :info, "#{tarea_id}: Montando origen remoto #{origen_remote} en #{dir_tmp}" + + # Ejecutar en background + pid = spawn("rclone mount #{Shellwords.escape(origen_remote)} #{dir_tmp} --daemon --vfs-cache-mode writes") + Process.detach(pid) + + # Esperar un poco a que monte + sleep 2 + + if Dir.exist?(dir_tmp) && !(Dir.entries(dir_tmp) - %w[. ..]).empty? + return dir_tmp + else + msg = "#{tarea_id}: Error montando remote #{origen_remote}" + log :error, msg + puts "#{ICONO_ERROR} #{msg}" + sleep 5 + return nil + end +end + +def desmontar_rclone(dir_tmp, tarea_id) + return unless dir_tmp && Dir.exist?(dir_tmp) + log :info, "#{tarea_id}: Desmontando #{dir_tmp}" + system("fusermount -u #{dir_tmp} 2>/dev/null || umount #{dir_tmp} 2>/dev/null") + FileUtils.remove_entry(dir_tmp) if Dir.exist?(dir_tmp) +end + +# --- Función Principal del Procesador Proxmox --- +def procesar_backups_tipo_proxmox(tarea_cfg) + system 'clear' or system 'cls' + + # 1. --- Extraer configuración e inicializar variables --- + origen_remote = tarea_cfg[:origen] + dir_destino_base = tarea_cfg[:destino] + eliminar_original = tarea_cfg[:eliminar_origen] + sobrescribir_destino = tarea_cfg[:sobrescribir_destino] + backups_procesados_llamada = 0 + + puts "\n#{NEGRITA}#{CIAN}--- #{tarea_cfg[:menu_texto]} ---#{RESET}" + log :info, "#{tarea_cfg[:id]}: Iniciado. Origen remoto: #{origen_remote}, Eliminar: #{eliminar_original}, Sobrescribir: #{sobrescribir_destino}" + + # 2. --- Montar origen remoto --- + dir_origen = montar_rclone(origen_remote, tarea_cfg[:id]) + return unless dir_origen + + begin + @pv_disponible ||= !`which pv`.to_s.strip.empty? + unless @pv_disponible + if @pv_notificado.nil? + msg = "#{tarea_cfg[:id]}: Comando 'pv' no encontrado. Compresión sin barra de progreso." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + @pv_notificado = true + end + end + + # 3. --- Buscar archivos .log --- + archivos_log = Dir.glob(File.join(dir_origen, '*.log')) + + if archivos_log.empty? + msg = "#{tarea_cfg[:id]}: No se encontraron archivos .log en '#{dir_origen}'." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + sleep 5 + $estadisticas_ejecucion[:tareas_completadas][tarea_cfg[:id]] = { archivos: 0, estado: "OK (Sin archivos)" } + return + end + + puts "#{ICONO_INFO} Encontrados #{archivos_log.length} set(s) de backup Proxmox a procesar." + + # 4. --- Iterar y procesar cada set --- + archivos_log.each do |ruta_log| + nombre_base_log = File.basename(ruta_log, '.log') + begin + archivos_fuente_del_backup = Dir.glob(File.join(dir_origen, "#{nombre_base_log}*")) + + ruta_notes = archivos_fuente_del_backup.find { |f| f.end_with?('.notes') } + unless ruta_notes + log :warn, "#{tarea_cfg[:id]}: No se encontró el archivo .notes para '#{nombre_base_log}'. Saltando." + next + end + + nombre_vm = File.read(ruta_notes).strip + if nombre_vm.empty? + log :warn, "#{tarea_cfg[:id]}: El archivo .notes para '#{nombre_base_log}' está vacío. Saltando." + next + end + + nombre_final_tar = "#{nombre_vm}_#{File.mtime(ruta_log).strftime('%Y%m%d_%H%M%S')}.tar.gz" + dir_destino_vm = File.join(dir_destino_base, nombre_vm) + FileUtils.mkdir_p(dir_destino_vm) + ruta_salida_tar = File.join(dir_destino_vm, nombre_final_tar) + + if File.exist?(ruta_salida_tar) + if sobrescribir_destino + msg = "#{tarea_cfg[:id]}: Destino '#{File.basename(ruta_salida_tar)}' existe. Se sobrescribirá." + log :info, msg + puts "#{ICONO_WARN} #{msg}" + else + msg = "#{tarea_cfg[:id]}: Destino '#{File.basename(ruta_salida_tar)}' existe. No se sobrescribe. Saltando." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + next + end + end + + # --- Realizar compresión --- + puts "#{CIAN}→ Comprimiendo set #{NEGRITA}#{nombre_base_log}#{RESET} (VM: #{nombre_vm}) a #{File.basename(ruta_salida_tar)}..." + log :info, "#{tarea_cfg[:id]}: Comprimiendo Proxmox set '#{nombre_vm}' (base: #{nombre_base_log})" + + nombres_para_tar = archivos_fuente_del_backup.map { |f| File.basename(f) } + + if @pv_disponible + cmd_tar = ['tar', '-cf', '-', '-C', dir_origen] + nombres_para_tar + cmd_pv = ['pv', '-s', archivos_fuente_del_backup.sum { |f| File.size(f) rescue 0 }.to_s] + cmd_gzip = ['gzip'] + + File.open(ruta_salida_tar, 'wb') do |f_out| + Open3.pipeline(cmd_tar, cmd_pv, cmd_gzip, out: f_out).each_with_index do |status, i| + unless status.success? + cmd_name = ['tar', 'pv', 'gzip'][i] + raise "Error en compresión (falló #{cmd_name} con código: #{status.exitstatus})" + end + end + end + else + archivos_escapados = nombres_para_tar.map { |f| Shellwords.escape(f) }.join(' ') + cmd_tar_directo = "tar -czf #{Shellwords.escape(ruta_salida_tar)} -C #{Shellwords.escape(dir_origen)} #{archivos_escapados}" + system(cmd_tar_directo) + raise "Error en compresión (falló tar con código: #{$?.exitstatus})" unless $?.success? + end + + # --- Finalizar --- + log :info, "#{tarea_cfg[:id]}: Compresión OK → #{ruta_salida_tar}" + puts "\n#{ICONO_OK} Compresión OK: #{File.basename(ruta_salida_tar)}" + + if eliminar_original + log :info, "#{tarea_cfg[:id]}: Eliminando fuentes Proxmox '#{nombre_base_log}'..." + archivos_fuente_del_backup.each { |f| FileUtils.rm_f(f) } + end + + backups_procesados_llamada += 1 + + rescue StandardError => e + err_msg = "#{tarea_cfg[:id]}: Error procesando set '#{nombre_base_log}': #{e.message}" + log :error, err_msg + puts "\n#{ICONO_ERROR} #{err_msg}" + sleep 5 + FileUtils.rm_f(ruta_salida_tar) if ruta_salida_tar + next + end + end + + # 5. --- Reportar estadísticas finales --- + $estadisticas_ejecucion[:tareas_completadas][tarea_cfg[:id]] = { archivos: backups_procesados_llamada, estado: "OK" } + log :info, "#{tarea_cfg[:id]}: #{backups_procesados_llamada} sets Proxmox procesados." + puts "#{ICONO_OK} Tarea '#{tarea_cfg[:menu_texto]}' completada. Procesados: #{backups_procesados_llamada}." + sleep 1.5 + + ensure + # Siempre desmontar aunque haya error + desmontar_rclone(dir_origen, tarea_cfg[:id]) + end +end diff --git a/tools/dtic-BKPs/fxs/proc_syncXEN.rb b/tools/dtic-BKPs/fxs/proc_syncXEN.rb new file mode 100644 index 00000000..d03589fd --- /dev/null +++ b/tools/dtic-BKPs/fxs/proc_syncXEN.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +# ========================================================== +# dtic-BKPs - Automatic Backup Processor +# ---------------------------------------------------------- +# APP: dtic-BKPs +# FILE: fxs/proc_syncXEN.rb +# VERSION: v5.5.1 +# AUTHOR: Ricardo MONLA (rmonla@) +# LICENSE: MIT License +# ========================================================== + +# --- Función Principal del Procesador XVA --- +def procesar_backups_xva(tarea_cfg) + system 'clear' or system 'cls' + # 1. --- Extraer configuración y inicializar variables --- + dir_origen = tarea_cfg[:origen] + dir_destino_base = tarea_cfg[:destino] + eliminar_original = tarea_cfg[:eliminar_origen] + sobrescribir_destino = tarea_cfg[:sobrescribir_destino] + archivos_procesados_llamada = 0 + + puts "\n#{NEGRITA}#{CIAN}--- #{tarea_cfg[:menu_texto]} ---#{RESET}" + log :info, "#{tarea_cfg[:id]}: Iniciado desde #{dir_origen}. Eliminar: #{eliminar_original}, Sobrescribir: #{sobrescribir_destino}" + + @pv_disponible ||= !`which pv`.to_s.strip.empty? + unless @pv_disponible + if @pv_notificado.nil? + msg = "#{tarea_cfg[:id]}: Comando 'pv' no encontrado. Compresión sin barra de progreso." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + @pv_notificado = true + end + end + + # 2. --- Buscar archivos a procesar --- + archivos_disponibles = Dir.glob(File.join(dir_origen, '*.xva')) + + if archivos_disponibles.empty? + msg = "#{tarea_cfg[:id]}: No hay archivos .xva en '#{dir_origen}'." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + $estadisticas_ejecucion[:tareas_completadas][tarea_cfg[:id]] = { archivos: 0, estado: "OK (Sin archivos)" } + return + end + + puts "#{ICONO_INFO} Encontrados #{archivos_disponibles.length} archivo(s) .xva a procesar." + + # 3. --- Iterar y procesar cada archivo --- + archivos_disponibles.each do |ruta_completa_xva_origen| + nombre_base_xva = File.basename(ruta_completa_xva_origen) + nombre_vm = nombre_base_xva.split('_').first + dir_dest_especifico_vm = File.join(dir_destino_base, nombre_vm) + + begin + FileUtils.mkdir_p(dir_dest_especifico_vm) + rescue StandardError => e + log :error, "#{tarea_cfg[:id]}: Error creando dir '#{dir_dest_especifico_vm}': #{e.message}" + next + end + + nombre_sin_ext = File.basename(nombre_base_xva, '.xva') + ruta_salida_comp = File.join(dir_dest_especifico_vm, "#{nombre_sin_ext}.tar.gz") + + if File.exist?(ruta_salida_comp) + if sobrescribir_destino + msg = "#{tarea_cfg[:id]}: Destino '#{File.basename(ruta_salida_comp)}' existe. Se sobrescribirá." + log :info, msg + puts "#{ICONO_WARN} #{msg}" + else + msg = "#{tarea_cfg[:id]}: Destino '#{File.basename(ruta_salida_comp)}' existe. No se sobrescribe. Saltando." + log :warn, msg + puts "#{ICONO_WARN} #{msg}" + next + end + end + + # 4. --- Realizar la compresión --- + puts "#{CIAN}→ Comprimiendo #{NEGRITA}#{nombre_base_xva}#{RESET} a #{File.basename(ruta_salida_comp)}..." + log :info, "#{tarea_cfg[:id]}: Comprimiendo XVA #{nombre_base_xva} → #{File.basename(ruta_salida_comp)}" + + begin + if @pv_disponible + cmd_tar = ['tar', '-czf', '-', '-C', dir_origen, nombre_base_xva] + cmd_pv = ['pv', '-s', File.size(ruta_completa_xva_origen).to_s] + + File.open(ruta_salida_comp, 'wb') do |f_out| + Open3.pipeline(cmd_tar, cmd_pv, out: f_out).each_with_index do |status, i| + unless status.success? + cmd_name = i == 0 ? 'tar' : 'pv' + raise "Error en compresión (falló #{cmd_name} con código: #{status.exitstatus})" + end + end + end + else + cmd_tar_directo = "tar -czf #{Shellwords.escape(ruta_salida_comp)} -C #{Shellwords.escape(dir_origen)} #{Shellwords.escape(nombre_base_xva)}" + system(cmd_tar_directo) + raise "Error en compresión (falló tar con código: #{$?.exitstatus})" unless $?.success? + end + + # 5. --- Finalizar y limpiar --- + log :info, "#{tarea_cfg[:id]}: Compresión OK → #{ruta_salida_comp}" + puts "\n#{ICONO_OK} Compresión OK: #{File.basename(ruta_salida_comp)}" + + if eliminar_original + log :info, "#{tarea_cfg[:id]}: Eliminando XVA original #{nombre_base_xva}" + FileUtils.rm_f(ruta_completa_xva_origen) + end + + archivos_procesados_llamada += 1 + + rescue StandardError => e + err_msg = "#{tarea_cfg[:id]}: Error al comprimir #{nombre_base_xva}: #{e.message}" + log :error, err_msg + puts "\n#{ICONO_ERROR} #{err_msg}" + FileUtils.rm_f(ruta_salida_comp) + next + end + end + + # 6. --- Reportar estadísticas finales --- + $estadisticas_ejecucion[:tareas_completadas][tarea_cfg[:id]] = { archivos: archivos_procesados_llamada, estado: "OK" } + log :info, "#{tarea_cfg[:id]}: #{archivos_procesados_llamada} archivos .xva procesados" + puts "#{ICONO_OK} Tarea '#{tarea_cfg[:menu_texto]}' completada. Procesados: #{archivos_procesados_llamada}." + sleep 1.5 +end diff --git a/tools/dtic-BKPs/fxs/sync_rclone.rb b/tools/dtic-BKPs/fxs/sync_rclone.rb new file mode 100644 index 00000000..6b3604cd --- /dev/null +++ b/tools/dtic-BKPs/fxs/sync_rclone.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +# ========================================================== +# dtic-BKPs - Automatic Backup Processor +# ---------------------------------------------------------- +# APP: dtic-BKPs +# FILE: fxs/sync_rclone.rb +# VERSION: v5.5.1 +# AUTHOR: Ricardo MONLA (rmonla@) +# LICENSE: MIT License +# ========================================================== + +# Carga las constantes y helpers globales +# require_relative '../core/globals' + +def sincronizar_rclone(tarea_cfg) + system 'clear' or system 'cls' + origen = tarea_cfg[:origen] + destino = tarea_cfg[:destino] + + puts "\n#{NEGRITA}#{CIAN}--- #{tarea_cfg[:menu_texto]} ---#{RESET}" + log :info, "#{tarea_cfg[:id]}: Iniciado rclone sync. Origen: #{origen}, Destino: #{destino}" + + # Get all subdirectories recursively + subdirs = Dir.glob("#{origen}/**/*").select { |f| File.directory?(f) }.map { |d| d.sub("#{origen}/", '') } + + all_exito = true + + # Opciones base de rclone: + # --delete-before: Borra origen del remoto ANTES de transferir, liberando espacio clave para respaldos grandes (Code 7 limits) + # --retries 5 etc: Blindaje de conexión para archivos gigantescos hacia la nube. + rclone_ops = "--delete-before --retries 5 --retries-sleep 10s --timeout 30m --contimeout 10m --tpslimit 10" + + # Execute rclone for each subdirectory + subdirs.each do |subdir| + comando_sub = "rclone sync #{Shellwords.escape(File.join(origen, subdir))} #{Shellwords.escape(File.join(destino, subdir))} --progress -v #{rclone_ops}" + log :info, "#{tarea_cfg[:id]}: Ejecutando subdir #{subdir} → #{comando_sub}" + puts "#{CIAN}→ Ejecutando rclone sync subdir (#{subdir})#{RESET}" + + exito_sub = system(comando_sub) + if !exito_sub + log :error, "#{tarea_cfg[:id]}: Fallo en sincronización subdir #{subdir}" + puts "#{ICONO_ERROR} Fallo sincronización rclone subdir (#{subdir})." + end + all_exito &= exito_sub + end + + # Execute rclone for the root directory + comando_rclone = "rclone sync #{Shellwords.escape(origen)} #{Shellwords.escape(destino)} --progress -v #{rclone_ops}" + log :info, "#{tarea_cfg[:id]}: Ejecutando root → #{comando_rclone}" + puts "#{CIAN}→ Ejecutando rclone sync root (Origen: #{origen} → Destino: #{destino})#{RESET}" + + exito = system(comando_rclone) + all_exito &= exito + codigo_salida = $?.exitstatus + estado_final = all_exito ? "Éxito" : "Fallo (cód: #{codigo_salida})" + + $estadisticas_ejecucion[:tareas_completadas][tarea_cfg[:id]] = { estado: estado_final } + + if all_exito + log :info, "#{tarea_cfg[:id]}: Sincronización OK" + puts "#{ICONO_OK} Sincronización rclone OK." + else + log :error, "#{tarea_cfg[:id]}: Fallo en sincronización (#{estado_final})" + puts "#{ICONO_ERROR} Fallo sincronización rclone (#{estado_final})." + end + + sleep 1.5 + all_exito +end \ No newline at end of file diff --git a/tools/dtic-BKPs/fxs/ui_handlers.rb b/tools/dtic-BKPs/fxs/ui_handlers.rb new file mode 100644 index 00000000..6e60d93f --- /dev/null +++ b/tools/dtic-BKPs/fxs/ui_handlers.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +# ========================================================== +# FILE: fxs/ui_handlers.rb +# ========================================================== + +def leer_entrada_simple(prompt) + print prompt; $stdout.flush; $stdin.gets.chomp +end + +def mostrar_encabezado_app + puts "#{NEGRITA}#{CIAN}--- dtic-BKPs - Procesador de Backups #{APP_VER} ---#{RESET}" +end + +def editar_item_config(item, type_name) + system 'clear' or system 'cls' + puts "#{NEGRITA}#{CIAN}--- Editando #{type_name}: #{item[:id]} ---#{RESET}" + puts "Deja el campo en blanco para mantener el valor actual." + + item.each do |key, value| + next if key == :id # ID no se edita directamente + + if key == :tareas && type_name == "Comando" + current_tasks = value.map(&:to_s).join(', ') + nuevo_valor_str = leer_entrada_simple("#{AMARILLO}#{key}#{RESET} (IDs de tareas separados por coma) [Actual: #{current_tasks}] > ").strip + unless nuevo_valor_str.empty? + item[key] = nuevo_valor_str.split(',').map(&:strip).map(&:to_sym).uniq + end + else + tipo = value.is_a?(TrueClass) || value.is_a?(FalseClass) ? "(true/false)" : "" + nuevo_valor = leer_entrada_simple("#{AMARILLO}#{key}#{RESET} #{tipo} [Actual: #{value.inspect}] > ").strip + unless nuevo_valor.empty? + if value.is_a?(Symbol) + item[key] = nuevo_valor.to_sym + elsif tipo == "(true/false)" + item[key] = nuevo_valor.downcase == 'true' + else + item[key] = nuevo_valor + end + end + end + end + puts "#{ICONO_OK} #{type_name} actualizada." + sleep 1 +end + +def modificar_tareas_y_comandos_menu(tareas_actuales, comandos_actuales, auto_avanzar_actual) + loop do + system 'clear' or system 'cls' + puts "#{NEGRITA}#{CIAN}--- Editor de Tareas y Comandos ---#{RESET}" + + puts "\n#{NEGRITA}Tareas Individuales: #{RESET}" + tareas_actuales.each_with_index do |tarea, index| + puts "#{AMARILLO}T#{index + 1}.#{RESET} #{tarea[:menu_texto]} (#{AMARILLO}id: :#{tarea[:id]}#{RESET})" + end + + puts "\n#{NEGRITA}Comandos: #{RESET}" + comandos_actuales.each_with_index do |comando, index| + puts "#{AMARILLO}C#{index + 1}.#{RESET} #{comando[:menu_texto]} (#{AMARILLO}id: :#{comando[:id]}#{RESET}, tareas: #{comando[:tareas].join(', ')}#{RESET})" + end + + puts "#{CIAN}-------------------------#{RESET}" + prompt = "[Tx]=Editar Tarea, [Cx]=Editar Comando, [NT]=Nueva Tarea, [NC]=Nuevo Comando, [BT]=Borrar Tarea, [BC]=Borrar Comando, [G]=Guardar y Volver > " + opcion = leer_entrada_simple(prompt).downcase + + case opcion + when /^t(\d+)$/ + index = $1.to_i - 1 + editar_item_config(tareas_actuales[index], "Tarea") if tareas_actuales[index] + when /^c(\d+)$/ + index = $1.to_i - 1 + editar_item_config(comandos_actuales[index], "Comando") if comandos_actuales[index] + when 'nt' + # Generar un ID único para la nueva tarea + new_id = "nueva_tarea_#{Time.now.to_i}".to_sym + nueva_tarea = { + id: new_id, + menu_texto: "Nueva Tarea (#{new_id})", + tipo_proceso: :rclone_sync, + origen: "", + destino: "", + eliminar_origen: false, + sobrescribir_destino: false + } + editar_item_config(nueva_tarea, "Nueva Tarea") + tareas_actuales << nueva_tarea + when 'nc' + # Generar un ID único para el nuevo comando + new_id = "nuevo_comando_#{Time.now.to_i}".to_sym + nuevo_comando = { + id: new_id, + menu_texto: "Nuevo Comando (#{new_id})", + tareas: [] # Se pueden añadir tareas después + } + editar_item_config(nuevo_comando, "Nuevo Comando") + comandos_actuales << nuevo_comando + when 'bt' + index_b = leer_entrada_simple("Número de tarea a borrar (Ej: T1) > ").downcase.gsub('t', '').to_i - 1 + if tareas_actuales[index_b] + puts "#{ICONO_WARN} Borrando tarea '#{tareas_actuales[index_b][:menu_texto]}'." + tareas_actuales.delete_at(index_b) + sleep 1 + end + when 'bc' + index_b = leer_entrada_simple("Número de comando a borrar (Ej: C1) > ").downcase.gsub('c', '').to_i - 1 + if comandos_actuales[index_b] + puts "#{ICONO_WARN} Borrando comando '#{comandos_actuales[index_b][:menu_texto]}'." + comandos_actuales.delete_at(index_b) + sleep 1 + end + when 'g' + guardar_configuracion(tareas_actuales, comandos_actuales, auto_avanzar_actual) + return + else + puts "#{ICONO_ERROR} Opción no válida." + sleep 1 + end + end +end \ No newline at end of file diff --git a/tools/dtic-BKPs/logs/.gitignore b/tools/dtic-BKPs/logs/.gitignore new file mode 100644 index 00000000..bf27f311 --- /dev/null +++ b/tools/dtic-BKPs/logs/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!.gitkeep diff --git a/tools/dtic-BKPs/logs/.gitkeep b/tools/dtic-BKPs/logs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tools/dtic-BKPs/src/core/globals.rb b/tools/dtic-BKPs/src/core/globals.rb new file mode 100644 index 00000000..0a137096 --- /dev/null +++ b/tools/dtic-BKPs/src/core/globals.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# ========================================================== +# FILE: core/globals.rb +# ========================================================== + +# --- CONSTANTES GLOBALES DEL SCRIPT --- +APP_NOM_BASE = 'dtic-BKPs' +APP_NOM = "#{APP_NOM_BASE}_app.rb" +APP_VER = 'v5.5.1' # Actualizado +APP_AUTOR = "Ricardo MONLA ( rmonla@ )" +APP_DIR_SCRIPT = File.dirname(File.expand_path(__FILE__)) +APP_CFG_NOM_ARCHIVO = "#{APP_NOM_BASE}_tasks.rb" +APP_LOG_NOM_ARCHIVO = "../logs/#{APP_NOM_BASE}.log" +APP_CFG_RUTA_ARCHIVO = File.join(APP_DIR_SCRIPT, APP_CFG_NOM_ARCHIVO) +APP_LOG_RUTA_ARCHIVO = File.join(APP_DIR_SCRIPT, APP_LOG_NOM_ARCHIVO) + +# --- COLORES E ICONOS GLOBALES --- +RESET = "\e[0m"; NEGRITA = "\e[1m"; SUBRAYADO="\e[4m"; INTERMITENTE = "\e[5m" +CIAN = "\e[96m"; AMARILLO = "\e[93m"; ROJO = "\e[91m"; VERDE = "\e[92m" +ICONO_OK = "#{VERDE}✔#{RESET}"; ICONO_ERROR = "#{ROJO}✖#{RESET}"; ICONO_WARN = "#{AMARILLO}⚠#{RESET}"; ICONO_INFO = "#{CIAN}ℹ#{RESET}" + +# --- CONFIGURACIÓN GLOBAL --- + +# Bloque para configurar el logger directamente aquí +require 'logger' # Asegúrate de que Logger esté cargado +$logger = Logger.new(APP_LOG_RUTA_ARCHIVO, 'daily') +$logger.level = Logger::INFO # Puedes ajustar el nivel de log aquí +$logger.formatter = proc do |severity, datetime, progname, msg| + "[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}] [#{severity}] #{msg}\n" +end + +$estadisticas_ejecucion = { tareas_completadas: {} } + +# --- FUNCIÓN DE LOGGING GLOBAL --- +def log(nivel, mensaje) + # Usa esta expresión regular más robusta para eliminar códigos ANSI + msg_l = mensaje.gsub(/\e\[(?:\d{1,3}(?:;\d{1,3})*)?[m|K]/, '') + case nivel + when :info then $logger.info(msg_l) + when :error then $logger.error(msg_l) + when :warn then $logger.warn(msg_l) + else $logger.debug(msg_l) + end +end + +# Función auxiliar para verificar si un comando existe +def which(cmd) + exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : [''] + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + exts.each { |ext| + exe = File.join(path, "#{cmd}#{ext}") + return exe if File.executable?(exe) && !File.directory?(exe) + } + end + nil +end \ No newline at end of file diff --git a/tools/dtic-BKPs/src/install.sh b/tools/dtic-BKPs/src/install.sh new file mode 100755 index 00000000..80ef49ee --- /dev/null +++ b/tools/dtic-BKPs/src/install.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# ========================================================== +# dtic-BKPs - Automatic Backup Processor +# ---------------------------------------------------------- +# APP: dtic-BKPs +# FILE: install.sh +# VERSION: v5.5.0 (of the main app) +# AUTHOR: Ricardo MONLA (rmonla@) +# LICENSE: MIT License +# ========================================================== + +# --- Colores para la salida --- +CIAN='\033[0;96m' +AMARILLO='\033[0;93m' +VERDE='\033[0;92m' +ROJO='\033[0;91m' +NEGRITA='\033[1m' +NC='\033[0m' # Sin color + +# --- Iconos --- +ICONO_OK="${VERDE}✔${NC}" +ICONO_ERROR="${ROJO}✖${NC}" +ICONO_INFO="${CIAN}ℹ${NC}" +ICONO_WARN="${AMARILLO}⚠${NC}" + +echo -e "${NEGRITA}${CIAN}--- Instalador de dtic-BKPs ---${NC}" + +# 1. VERIFICAR SI RUBY ESTÁ INSTALADO +echo -e "\n${ICONO_INFO} Verificando si Ruby está instalado..." + +if command -v ruby > /dev/null 2>&1 +then + # Ruby ya está instalado + RUBY_VERSION=$(ruby -v) + echo -e "${ICONO_OK} Ruby ya está instalado: ${AMARILLO}${RUBY_VERSION}${NC}" +else + # Ruby no está instalado + echo -e "${ICONO_WARN} Ruby no se encuentra en el sistema." + + # Manera compatible con 'sh' para pedir entrada + echo -n -e "${AMARILLO}¿Deseas instalar Ruby (a través de 'sudo apt install ruby-full')? (S/N): ${NC}" + read respuesta + + # Usamos 'case' en lugar de 'if [[ ... =~ ... ]]' para máxima compatibilidad + case "$respuesta" in + [Ss] | [Ss][Ii]) # Acepta S, s, Si, si + echo -e "\n${ICONO_INFO} Se necesita contraseña de administrador para instalar paquetes." + echo -e "${ICONO_INFO} Actualizando lista de paquetes (sudo apt-get update)..." + sudo apt-get update + + echo -e "${ICONO_INFO} Instalando ruby-full..." + sudo apt-get install -y ruby-full + + if ! command -v ruby > /dev/null 2>&1 + then + echo -e "\n${ICONO_ERROR} La instalación de Ruby falló. Por favor, instálalo manualmente y vuelve a ejecutar este script." + exit 1 + fi + echo -e "\n${ICONO_OK} Ruby instalado correctamente." + ;; + *) + echo -e "\n${ICONO_ERROR} Instalación cancelada. Ruby es necesario para continuar." + exit 1 + ;; + esac +fi + +# 2. EJECUTAR EL SCRIPT DE INSTALACIÓN DE RUBY +echo -e "\n${ICONO_INFO} Lanzando el instalador de la aplicación (dtic-BKPs_install.rb)..." +echo -e "${CIAN}--------------------------------------------------${NC}" + +ruby ./dtic-BKPs_install.rb + +exit 0 \ No newline at end of file