README afirmaba que la app no puede correr como servicio de Windows y sugería NSSM, contradiciendo a install.ps1 desde que existe. LEEME.txt solo documentaba el camino manual para Windows, mientras que para Linux ya traía el instalador. BUILD.md gana -UpdateInPlace, las tres garantías al reemplazar el binario y el remedio del token filtrado por UAC. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
491 lines
18 KiB
Markdown
491 lines
18 KiB
Markdown
# CloudRestoreAS
|
||
|
||
**Aplicación de Escritorio para Restauración Automática de Bases de Datos SQL Server**
|
||
|
||
CloudRestoreAS es una aplicación Windows de escritorio desarrollada en Python 3.11+ con interfaz gráfica PySide6/Qt que funciona como daemon de usuario para automatizar la restauración de bases de datos SQL Server a partir de respaldos ZIP (incluyendo ZIP multipart).
|
||
|
||
## 🎯 Características Principales
|
||
|
||
- **Vigilancia Automática**: Monitorea carpetas por nuevos archivos ZIP con detección de estabilidad
|
||
- **ZIP Multipart**: Soporte completo para archivos .zip.001, .zip.002, etc.
|
||
- **Extracción con 7-Zip**: Integración nativa con 7-Zip para descompresión
|
||
- **Restauración SQL Server**: Conexión directa vía pyodbc con soporte Windows Auth y SQL Auth
|
||
- **Motor Concurrente**: Workers configurables para extracción y restauración
|
||
- **Mapeo de Nodos**: Sistema flexible de mapeo nombre_archivo -> base_datos
|
||
- **Interfaz Gráfica Completa**: Dashboard, gestión de jobs, configuración, logs
|
||
- **System Tray**: Minimización a bandeja del sistema para operación 24/7
|
||
- **Métricas Persistentes**: Base de datos SQLite con auditoría completa
|
||
- **Logging Rotativo**: Logs automáticos con rotación por tamaño
|
||
- **Tolerancia a Fallos**: Reintentos, timeouts, y manejo de errores robusto
|
||
- **Modo Dry Run**: Prueba el pipeline sin ejecutar RESTORE real
|
||
|
||
## 📋 Requisitos
|
||
|
||
### Sistema Operativo
|
||
- Windows 10/11
|
||
- Windows Server 2019/2022 o superior
|
||
- Linux x86-64 (servidor o escritorio). En servidor **headless** (sin pantalla) corre
|
||
automáticamente en modo Qt `offscreen`. El binario Linux es **autocontenido**: no
|
||
requiere instalar Python, 7-Zip, driver ODBC ni librerías Qt en el destino.
|
||
|
||
### Software Requerido
|
||
|
||
1. **Python 3.11+**
|
||
- Descargar desde: https://www.python.org/downloads/
|
||
- Asegurarse de agregar Python al PATH durante la instalación
|
||
|
||
2. **7-Zip**
|
||
- Descargar desde: https://www.7-zip.org/
|
||
- Instalar en ubicación estándar (C:\Program Files\7-Zip)
|
||
- La aplicación puede auto-detectar la instalación
|
||
|
||
3. **ODBC Driver 17 for SQL Server** (o superior)
|
||
- Descargar desde: https://docs.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server
|
||
- Requerido para la conexión con SQL Server
|
||
|
||
4. **SQL Server** (destino de restauraciones)
|
||
- SQL Server 2016 o superior recomendado
|
||
- Permisos necesarios:
|
||
- `CREATE DATABASE`
|
||
- `ALTER DATABASE`
|
||
- `RESTORE DATABASE`
|
||
- Acceso de lectura/escritura en carpetas de datos
|
||
|
||
## 🚀 Instalación
|
||
|
||
### Opción 1: Ejecución desde Código Fuente
|
||
|
||
1. **Clonar o descargar el proyecto**
|
||
```powershell
|
||
cd C:\Aduanasoft\CloudRestoreAs
|
||
```
|
||
|
||
2. **Crear entorno virtual (recomendado)**
|
||
```powershell
|
||
python -m venv venv
|
||
.\venv\Scripts\Activate.ps1
|
||
```
|
||
|
||
3. **Instalar dependencias**
|
||
```powershell
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
4. **Ejecutar la aplicación**
|
||
```powershell
|
||
python runner.py
|
||
```
|
||
|
||
### Opción 2: Ejecutable portable (recomendado para producción)
|
||
|
||
**Todo de una vez (desde WSL)** — genera Windows + Linux + los paquetes de release:
|
||
```bash
|
||
./build-all.sh # ambos + dist/release/*.tar.gz y *.zip
|
||
./build-all.sh --linux-only # solo Linux (Docker)
|
||
./build-all.sh --windows-only # solo Windows (PowerShell + build.ps1)
|
||
./build-all.sh --clean # rebuild desde cero
|
||
```
|
||
Requiere Docker (para Linux) y, para Windows, `powershell.exe` accesible desde WSL con Python 3.11+ instalado en Windows.
|
||
|
||
**Solo Windows** — generar: `.\build.ps1` → `dist\CloudRestoreAS.exe`
|
||
**Solo Linux** — generar: `./build.sh` (o `packaging/scripts/docker-build-linux.sh` para el entorno controlado) → `dist/CloudRestoreAS`
|
||
|
||
> 📦 Referencia completa de compilación, empaquetado, instalación (`install.sh`/systemd),
|
||
> flags del binario y verificación: **[BUILD.md](BUILD.md)**.
|
||
|
||
> El build de Linux embebe dentro del binario, además de la app: las libs de sistema
|
||
> de Qt (cluster xcb/X11 + EGL), el driver **ODBC 18** con sus dependencias
|
||
> (unixODBC, Kerberos/GSSAPI, OpenSSL) y el **7-Zip** (`7zz`). Por eso el destino no
|
||
> necesita `apt install` de nada. (El build se hace en un entorno con esas libs; ver
|
||
> `packaging/scripts/download-bundled-deps.sh` y `docker-build-linux.sh`.)
|
||
|
||
**Desplegar en cada equipo** (sin Python, sin instalador, sin dependencias de sistema):
|
||
1. Copiar **solo** el ejecutable a una carpeta.
|
||
2. Ejecutarlo (doble clic o `./CloudRestoreAS`).
|
||
3. Se crean automáticamente: `config/`, `Entrada/`, `Procesados/`, `Fallados/`, `Temp/`.
|
||
4. Editar `config/.env` (URL, token e instancia del panel).
|
||
5. Reiniciar la aplicación.
|
||
|
||
**Despliegue en servidor Linux (headless / SQL Server sobre Linux):**
|
||
```bash
|
||
sudo ./install.sh --service # servicio systemd 24/7 (offscreen, motor auto-inicio)
|
||
# o manual:
|
||
QT_QPA_PLATFORM=offscreen ./CloudRestoreAS --start-engine --headless
|
||
```
|
||
`install.sh` **no instala ni descarga nada** del sistema: solo coloca el binario, hace
|
||
el bootstrap de `config/` y registra el arranque (systemd headless o autostart de
|
||
escritorio con `--desktop`). El `data_folder` del destino viene del PANEL; con SQL
|
||
Server sobre Linux serán rutas POSIX (p. ej. `/var/opt/mssql/data`) y el `RESTORE`
|
||
las adapta automáticamente.
|
||
|
||
Ver `packaging/LEEME.txt` para instrucciones resumidas.
|
||
|
||
## ⚙️ Configuración Inicial
|
||
|
||
### 1. Primera Ejecución
|
||
|
||
Al ejecutar la aplicación por primera vez:
|
||
|
||
1. Ve al tab **Configuración**
|
||
2. Configura las siguientes carpetas:
|
||
|
||
- **Carpeta de Entrada**: Donde se monitoreará por nuevos archivos ZIP
|
||
- **Carpeta Procesados**: Donde se moverán los ZIP exitosos
|
||
- **Carpeta Fallados**: Donde se moverán los ZIP que fallen
|
||
- **Carpeta Extracción**: Temporal para extraer archivos (se limpia automáticamente)
|
||
- **Carpeta Data SQL**: Donde SQL Server almacena los archivos .mdf/.ldf
|
||
|
||
3. Configura **7-Zip**:
|
||
- Haz clic en "🔍 Detectar" para auto-detectar
|
||
- O selecciona manualmente `7z.exe`
|
||
|
||
4. Configura **SQL Server**:
|
||
- Nombre del servidor (ej: `localhost`, `.\SQLEXPRESS`, o IP)
|
||
- Tipo de autenticación:
|
||
- **Windows Auth**: Recomendado (usa credenciales de Windows)
|
||
- **SQL Auth**: Requiere usuario y contraseña SQL Server
|
||
- Prueba la conexión con "🔌 Probar Conexión"
|
||
|
||
5. **Guarda la configuración** (💾 Guardar Configuración)
|
||
|
||
### 2. Configurar Nodos
|
||
|
||
Los nodos son el mapeo entre nombre de archivo ZIP y base de datos destino.
|
||
|
||
1. Ve al tab **Nodos**
|
||
2. Haz clic en **➕ Nuevo Nodo**
|
||
3. Configura:
|
||
- **Nombre del Nodo**: Nombre exacto del archivo ZIP CON EXTENSIÓN (ej: `BACKUP_SISTEMA.ZIP`)
|
||
- Se compara en MAYÚSCULAS y case-insensitive
|
||
- **Base de Datos**: Nombre de la base de datos destino en SQL Server (ej: `Sistema_DB`)
|
||
- **Activo**: Marca si el nodo está activo
|
||
- **Notas**: Información opcional
|
||
|
||
**Ejemplo de configuración de nodo:**
|
||
```
|
||
Nombre del Nodo: BACKUP_VENTAS.ZIP
|
||
Base de Datos: Ventas_Prod
|
||
Activo: ✓
|
||
Notas: Base de datos de ventas - restauración diaria
|
||
```
|
||
|
||
### 3. Configuración Avanzada
|
||
|
||
**Concurrencia:**
|
||
- **Workers de Extracción**: Número de extracciones simultáneas (default: 1)
|
||
- **Workers de Restore**: Número de restauraciones simultáneas (default: 1)
|
||
- ⚠️ Recomendado mantener en 1 para no saturar SQL Server
|
||
|
||
**Estabilidad de Archivos:**
|
||
- **Verificar Estabilidad**: Activa/desactiva verificación antes de procesar
|
||
- **Intervalo de Verificación**: Tiempo entre checks (default: 5s)
|
||
- **Duración Estable**: Tiempo que el archivo debe estar sin cambios (default: 10s)
|
||
- **Usar Marcador .ready**: Si se activa, requiere archivo `.ready` junto al ZIP
|
||
|
||
**Timeouts:**
|
||
- **Extracción**: Timeout máximo para 7-Zip (default: 30 min)
|
||
- **Restauración**: Timeout máximo para RESTORE (default: 60 min)
|
||
|
||
**Características:**
|
||
- **Modo Dry Run**: Simula todo sin ejecutar RESTORE real (para pruebas)
|
||
- **Escaneo Automático**: Auto-escanea la carpeta de entrada
|
||
- **Intervalo de Escaneo**: Frecuencia de escaneo (default: 30s)
|
||
|
||
## 🎮 Uso
|
||
|
||
### Iniciar el Motor
|
||
|
||
1. Menú **Motor** → **▶️ Iniciar**
|
||
2. O presiona el botón de inicio en el Dashboard
|
||
3. La aplicación comenzará a monitorear la carpeta de entrada
|
||
|
||
### Procesamiento Automático
|
||
|
||
1. Copia un archivo ZIP a la **Carpeta de Entrada**
|
||
2. La aplicación detectará el archivo cuando esté estable
|
||
3. Procesamiento automático:
|
||
- ✅ Verificar estabilidad del archivo
|
||
- ✅ Identificar nodo y base de datos destino
|
||
- ✅ Extraer .bak del ZIP (soporta multipart)
|
||
- ✅ Obtener estructura lógica del backup (FILELISTONLY)
|
||
- ✅ Restaurar base de datos con REPLACE
|
||
- ✅ Limpiar archivos temporales
|
||
- ✅ Mover ZIP a carpeta Procesados (con subcarpeta por fecha)
|
||
|
||
### Procesamiento Manual
|
||
|
||
1. Menú **Motor** → **🔍 Escanear Ahora**
|
||
2. Fuerza un escaneo inmediato de la carpeta de entrada
|
||
|
||
### Monitoreo
|
||
|
||
**Dashboard:**
|
||
- Contadores en tiempo real (Total, En Cola, Ejecutando, Completados, Fallados)
|
||
- Tiempos promedio de ejecución
|
||
- Estado del motor (Corriendo/Pausado/Detenido)
|
||
- Últimos 10 jobs
|
||
|
||
**Jobs:**
|
||
- Lista completa de todos los jobs procesados
|
||
- Filtros por estado, nombre, fecha
|
||
- Vista detallada de cada job:
|
||
- Información general
|
||
- Pasos ejecutados con tiempos
|
||
- Errores y mensajes
|
||
|
||
**Logs:**
|
||
- Visualización de eventos del sistema
|
||
- Filtros por nivel (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||
- Acceso rápido a carpeta de logs en disco
|
||
|
||
### Pausar/Continuar
|
||
|
||
- Menú **Motor** → **⏸️ Pausar**: Detiene el escaneo pero permite terminar jobs en curso
|
||
- Menú **Motor** → **▶️ Continuar**: Reanuda el monitoreo
|
||
|
||
### Minimizar a Tray
|
||
|
||
- Cerrar la ventana la minimiza a la bandeja del sistema
|
||
- Doble clic en el icono del tray para mostrar/ocultar
|
||
- Clic derecho en el icono para acceder al menú:
|
||
- Mostrar/Ocultar
|
||
- Pausar/Continuar
|
||
- Salir
|
||
|
||
## 📊 Archivos Multipart
|
||
|
||
La aplicación soporta automáticamente archivos ZIP multipart:
|
||
|
||
```
|
||
BACKUP_SISTEMA.ZIP.001
|
||
BACKUP_SISTEMA.ZIP.002
|
||
BACKUP_SISTEMA.ZIP.003
|
||
```
|
||
|
||
**Comportamiento:**
|
||
- Solo coloca el archivo `.001` en la carpeta de entrada
|
||
- Los demás archivos (`.002`, `.003`, etc.) deben estar en la misma carpeta
|
||
- 7-Zip detectará automáticamente todas las partes
|
||
- Todos los archivos se moverán juntos a Procesados/Fallados
|
||
|
||
## 🔐 Seguridad
|
||
|
||
### Contraseñas SQL
|
||
|
||
Las contraseñas de SQL Server se cifran usando **DPAPI (Data Protection API)** de Windows:
|
||
|
||
- El cifrado está vinculado al usuario y máquina actuales
|
||
- Las contraseñas no son portables entre usuarios/máquinas
|
||
- Si cambias de usuario, debes reconfigurar las credenciales
|
||
|
||
### Permisos Requeridos
|
||
|
||
**En SQL Server:**
|
||
```sql
|
||
-- Crear login (si usa SQL Auth)
|
||
CREATE LOGIN [CloudRestoreUser] WITH PASSWORD = 'P@ssw0rd';
|
||
|
||
-- Otorgar permisos
|
||
GRANT CREATE DATABASE TO [CloudRestoreUser];
|
||
ALTER SERVER ROLE [dbcreator] ADD MEMBER [CloudRestoreUser];
|
||
```
|
||
|
||
**En Windows:**
|
||
- Permiso de lectura/escritura en carpetas configuradas
|
||
- Permiso de ejecución de 7-Zip
|
||
|
||
## 🗂️ Estructura de Base de Datos
|
||
|
||
La aplicación usa SQLite local (`data/app.db`) con las siguientes tablas:
|
||
|
||
### jobs
|
||
Registro de todos los jobs procesados.
|
||
|
||
### job_steps
|
||
Pasos individuales de cada job con tiempos y códigos de salida.
|
||
|
||
### nodes
|
||
Mapeos de nodo a base de datos.
|
||
|
||
### events
|
||
Log de eventos del sistema.
|
||
|
||
### config
|
||
Configuración persistente de la aplicación.
|
||
|
||
## 🐛 Solución de Problemas
|
||
|
||
### El motor no inicia
|
||
|
||
**Síntomas:** Al hacer clic en Iniciar, el motor se detiene inmediatamente.
|
||
|
||
**Solución:**
|
||
1. Verifica que todas las carpetas estén configuradas y existan
|
||
2. Verifica que 7-Zip esté instalado y la ruta sea correcta
|
||
3. Revisa los logs para errores específicos
|
||
|
||
### No se detectan archivos ZIP
|
||
|
||
**Síntomas:** Copias un ZIP a la carpeta de entrada pero no se procesa.
|
||
|
||
**Solución:**
|
||
1. Asegúrate de que el motor esté corriendo (no pausado)
|
||
2. Verifica que el archivo sea `.zip` o `.zip.001`
|
||
3. Espera el tiempo de estabilidad configurado (default 10s)
|
||
4. Prueba con **Escanear Ahora**
|
||
5. Revisa si hay errores de permisos en la carpeta
|
||
|
||
### Error "NODE_NOT_MAPPED"
|
||
|
||
**Síntomas:** El job falla con "No existe mapeo activo para nodo..."
|
||
|
||
**Solución:**
|
||
1. Ve al tab **Nodos**
|
||
2. Verifica que existe un nodo con el nombre EXACTO del archivo (con extensión)
|
||
3. Ejemplo: Si el archivo es `backup.zip`, el nodo debe ser `BACKUP.ZIP`
|
||
4. Verifica que el nodo esté marcado como **Activo**
|
||
|
||
### Error de conexión SQL Server
|
||
|
||
**Síntomas:** "Error conectando a SQL Server..."
|
||
|
||
**Solución:**
|
||
1. Verifica que SQL Server esté corriendo
|
||
2. Prueba la conexión en el tab Configuración (🔌 Probar Conexión)
|
||
3. Si usas SQL Auth, verifica usuario y contraseña
|
||
4. Si usas Windows Auth, verifica que el usuario de Windows tenga permisos
|
||
5. Verifica que ODBC Driver 17 esté instalado:
|
||
```powershell
|
||
Get-OdbcDriver | Where-Object {$_.Name -like "*SQL Server*"}
|
||
```
|
||
|
||
### Extracción falla con código de salida 2
|
||
|
||
**Síntomas:** El step "extract" falla con exit_code=2
|
||
|
||
**Solución:**
|
||
1. Verifica que el archivo ZIP no esté corrupto
|
||
2. Para multipart, asegúrate de que TODAS las partes estén presentes
|
||
3. Verifica que haya espacio suficiente en disco para la extracción
|
||
4. Revisa los logs de 7-Zip en el step para más detalles
|
||
|
||
### RESTORE falla
|
||
|
||
**Síntomas:** El step "restore" falla
|
||
|
||
**Solución:**
|
||
1. Verifica que el usuario tenga permisos `CREATE DATABASE` y `ALTER DATABASE`
|
||
2. Verifica que la carpeta Data SQL exista y tenga permisos de escritura
|
||
3. Si la base de datos ya existe, el RESTORE la sobrescribirá (REPLACE)
|
||
4. Revisa el error específico en el detalle del job
|
||
5. Activa **Modo Dry Run** para ver el SQL generado sin ejecutarlo
|
||
|
||
## 📁 Estructura del Proyecto
|
||
|
||
```
|
||
CloudRestoreAs/
|
||
├── app/
|
||
│ ├── db/ # Capa de acceso a datos
|
||
│ │ ├── database.py # Gestor de SQLite
|
||
│ │ ├── job_repository.py
|
||
│ │ ├── node_repository.py
|
||
│ │ └── ...
|
||
│ ├── engine/ # Motor de procesamiento
|
||
│ │ ├── engine.py # Motor principal
|
||
│ │ ├── file_watcher.py # Vigilancia de archivos
|
||
│ │ └── restore_worker.py
|
||
│ ├── extract/ # Extracción con 7-Zip
|
||
│ │ └── seven_zip.py
|
||
│ ├── sql/ # Operaciones SQL Server
|
||
│ │ └── sql_manager.py
|
||
│ ├── ui/ # Interfaz gráfica
|
||
│ │ ├── main_window.py
|
||
│ │ ├── dashboard_tab.py
|
||
│ │ ├── jobs_tab.py
|
||
│ │ ├── nodes_tab.py
|
||
│ │ ├── config_tab.py
|
||
│ │ └── logs_tab.py
|
||
│ ├── utils/ # Utilidades
|
||
│ │ ├── logger.py
|
||
│ │ └── crypto.py
|
||
│ └── constants.py
|
||
├── data/ # Base de datos SQLite
|
||
│ └── app.db
|
||
├── logs/ # Archivos de log
|
||
│ └── app.log
|
||
├── runner.py # Punto de entrada
|
||
├── requirements.txt # Dependencias
|
||
├── build.ps1 # Script de build
|
||
└── README.md # Este archivo
|
||
```
|
||
|
||
## 🔧 Desarrollo
|
||
|
||
### Ejecutar en Modo Debug
|
||
|
||
```powershell
|
||
python runner.py
|
||
```
|
||
|
||
Los logs se escriben en:
|
||
- Consola (stdout)
|
||
- Archivo `logs/app.log` (con rotación automática)
|
||
|
||
### Ejecutar Tests
|
||
|
||
_(Opcional: agregar tests con pytest)_
|
||
|
||
```powershell
|
||
pip install pytest
|
||
pytest tests/
|
||
```
|
||
|
||
## 📝 Notas Importantes
|
||
|
||
1. **Backups Antes de Restaurar**: Aunque la aplicación usa `REPLACE`, asegúrate de tener backups de las bases de datos existentes.
|
||
|
||
2. **Carpeta Data SQL**: La carpeta configurada debe ser accesible por SQL Server. Generalmente es:
|
||
- SQL Server 2019: `C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA`
|
||
- SQL Server 2022: `C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\DATA`
|
||
|
||
3. **Nombres de Nodos**: Los nombres de nodos son case-insensitive pero deben incluir la extensión completa.
|
||
|
||
4. **Operación 24/7**: La aplicación está diseñada para correr continuamente. Minimízala a tray y déjala corriendo.
|
||
|
||
5. **Logs Rotativos**: Los logs se rotan automáticamente cada 10MB. Se mantienen 10 archivos de respaldo.
|
||
|
||
## ❓ FAQ
|
||
|
||
**P: ¿Puedo procesar múltiples archivos simultáneamente?**
|
||
R: Sí, configura más workers en Configuración → Concurrencia. Sin embargo, recomendamos mantener restore_workers=1 para no saturar SQL Server.
|
||
|
||
**P: ¿Qué pasa si la aplicación se cierra mientras procesa un job?**
|
||
R: Al reiniciar, los jobs en estado "extracting/restoring" se marcan como "failed_restart". Puedes configurar políticas de reintento (actualmente el código base lo soporta pero no está en la UI).
|
||
|
||
**P: ¿Puedo usar esto para SQL Server en otra máquina?**
|
||
R: Sí, especifica el nombre o IP del servidor remoto en Configuración → SQL Server. Asegúrate de que:
|
||
- El puerto SQL Server esté abierto (default 1433)
|
||
- Las credenciales tengan permisos remotos
|
||
- La carpeta Data SQL sea accesible remotamente (o usa UNC paths)
|
||
|
||
**P: ¿Soporta bases de datos con múltiples archivos de datos?**
|
||
R: Sí, la aplicación usa RESTORE FILELISTONLY para detectar todos los archivos lógicos (MDF, LDF, y archivos adicionales). Solo mantiene el archivo principal de datos (.mdf) y de log (.ldf).
|
||
|
||
**P: ¿Puedo ejecutar esto como Windows Service?**
|
||
R: Sí, con `install.ps1 -Service`: registra una tarea programada ONSTART que corre como SYSTEM, sin sesión iniciada y sin UI. **No** se usa NSSM ni ningún envoltorio descargado — el servidor destino no instala ni baja nada, y una tarea programada ya viene en el SO. Requiere PowerShell como Administrador. Ver [BUILD.md](BUILD.md) §6.
|
||
|
||
## 📄 Licencia
|
||
|
||
© 2026 Aduanasoft. Todos los derechos reservados.
|
||
|
||
## 🆘 Soporte
|
||
|
||
Para soporte o reportar problemas:
|
||
- Revisa primero la sección **Solución de Problemas**
|
||
- Revisa los logs en el tab **Logs** o en la carpeta `logs/`
|
||
- Contacta al equipo de desarrollo
|
||
|
||
---
|
||
|
||
**CloudRestoreAS v1.0.0** - Restauración Automática de Bases de Datos SQL Server
|