[ADN / Bitácora] Evolución Progresiva: Sistema de Iconografía Dual (Estado + Semántica)

This commit is contained in:
Ricardo Monla
2026-02-20 22:52:05 -03:00
parent ca715aee69
commit 2f4dcb62ea
18 changed files with 1666 additions and 15 deletions
+1
View File
@@ -16,6 +16,7 @@ secrets/
# Logs
*.log
logs/
!tools/dtic-BKPs/logs/
# Temporales y caché
tmp/
+48 -15
View File
@@ -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/<hostname>.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.
+54
View File
@@ -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".<br><br>⏱️ **Cronología:**<br>- 👁️ **22:33 hs**: Conceptualización de la separación visual estado/semántica.<br>- ✍️ **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.<br><br>⏱️ **Cronología:**<br>- 🏗️ **22:06 hs**: Inicio de refactorización estructural.<br>- 🚀 **22:15 hs**: Despliegue preliminar en formato `A00` de las actividades del día.<br>- 💡 **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.<br><br>⏱️ **Cronología:**<br>- 🧠 **21:45 hs**: Conceptualización aprobada.<br>- 📝 **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.<br><br>⏱️ **Cronología:**<br>- ✍️ **14:14 hs**: Inicio de redacción.<br>- 💾 **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.<br><br>⏱️ **Cronología:**<br>- ✍️ **13:35 hs**: Inicio.<br>- 💾 **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.<br><br>⏱️ **Cronología:**<br>- 🚀 **09:13 hs**: Reanudación del ciclo para atacar la carga masiva y uso efectivo del `--delete-before` para purgar One-Drive.<br>- 🟢 **09:39 hs**: Fase Proxmox saneada. Montaje y volcado de las VMs de `zfsDISCO1` superado (evasión del bug de passphrase SSH).<br>- ☁️ **12:54 hs**: Inicio de sincronización bruta a OneDrive del nodo ultrapesado `srv-SysACADWeb` despues limpiar espacio.<br>- 👁️ **14:29 hs**: Control interactivo de avance: Flujo de red constante, sin cuellos de botella ni errores Code 7.<br>- 🏁 **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`.<br><br>⏱️ **Cronología:**<br>- 🐛 **08:55 hs**: Inicio de diagnóstico del agente SSH.<br>- 🔧 **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.<br><br>⏱️ **Cronología:**<br>- 🐛 **08:55 hs**: Inicio de diagnóstico del bug visual de la terminal interactiva.<br>- 🔧 **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.<br><br>⏱️ **Cronología:**<br>- 🔧 **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.<br><br>⏱️ **Cronología:**<br>- 🏗️ **08:10 hs**: Inicio de refactorización Ruby.<br>- 💾 **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.<br><br>⏱️ **Cronología:**<br>- 👁️ **07:42 hs**: Inicio de revisión de logs de madrugada.<br>- ⚠️ **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`.<br><br>⏱️ **Cronología:**<br>- 🧹 **07:36 hs**: Inicio de reubicación.<br>- 💾 **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.<br><br>⏱️ **Cronología:**<br>- 🏗️ **07:00 hs**: Inicio de revisión operativa.<br>- 🧪 **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).<br><br>⏱️ **Cronología:**<br>- ⚠️ **00:12 hs**: Inicio de falla registrada.<br>- 💥 **05:36 hs**: Caída final del ciclo. |
---
<!-- 🤖 PREMISAS DE TRABAJO (IA) -->
<!--
1. ORDEN INVERSO: Lo más nuevo SIEMPRE arriba (Tabla Resumen, Secciones de Nodo y Filas de Detalle).
2. NARRATIVA:
- Resumen (Arriba): Narrativa integral y sintética, SIN TÍTULOS NI ESTADOS en el texto.
- Detalle (Abajo): Generoso, explicando el "qué" y el "valor" técnico.
3. ESTRUCTURA: Control -> Resumen -> Actividades Detalladas (Subdivididas).
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 como ID (ej. `[✅ - A01 - **Nombre Tarea**](#...)`).
7. ICONOGRAFÍA: Tema = [Estado] [Categoría] (ej. ✅ 📝, ⏳ 👁️). Hitos = Emojis semánticos en cada viñeta.
8. ARMONÍA: Consultar y seguir estrictamente las premisas del ADN (`adn/02_protocolo.md`).
-->
+39
View File
@@ -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
+350
View File
@@ -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.*
+87
View File
@@ -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.
+13
View File
@@ -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"
+274
View File
@@ -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
+85
View File
@@ -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],
},
]
+79
View File
@@ -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
+187
View File
@@ -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
+126
View File
@@ -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
+70
View File
@@ -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
+119
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
*
!.gitignore
!.gitkeep
View File
+57
View File
@@ -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
+74
View File
@@ -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