[A04.P005] DASUTEN: Pipeline refactorizado + Documentación + Informes

Refactorización del pipeline de backups DASUTEN:

1. Flujo simplificado (un solo destino):
   - Backup directo a X:\ (share srv-ns8)
   - Eliminado paso intermedio E:\BK_SQL\sysdasuten\
   - Reducido de 3 ubicaciones a 2 (X: → /var/tmp/)

2. Módulo común DasuExecutor (lib/dasu_executor.rb):
   - Centraliza ejecución PowerShell en dasu-sql4
   - Centraliza verificación de archivos en fenix
   - Elimina código repetido en 6 drones (-52% líneas)

3. Drones refactorizados:
   - dasuten_exportar_srvv-fenix.rb (FULL → X:)
   - dasuten_exportar_diferencial_srvv-fenix.rb (DIF → X:)
   - dasuten_transferir_srvv-fenix-srv-ns8.rb (simplificado)
   - dasuten_restaurar_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_restaurar_diferencial_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_download_drive-dasu-sql4.rb (usa DasuExecutor)
   - dasuten_verificar-integridad_dasu-sql4.rb (usa DasuExecutor)

4. Integración BKPs mantenida:
   - T8-T15: Tareas individuales
   - C7: dasuten_full (pipeline semanal)
   - C8: dasuten_diferencial (pipeline diario)

5. Documentación actualizada:
   - A04.P006_Backups-DASUTEN.md: flujo simplificado + módulo común
   - Lecciones aprendidas agregadas

6. Nuevos archivos:
   - Informes técnicos DASUTEN (docs/informes/2026-04_DASUTEN-migracion/)
   - A01.P011/P012: Documentación de drones y pipeline
   - proc_linux.rb, ops.rb: Utilitarios del ecosistema

7. Scripts legacy (para referencia):
   - orquestador_pipeline.rb, orquestador_pipeline_diferencial.rb
   - dasuten_upload_srv-ns8-drive.rb
This commit is contained in:
Ricardo Monla
2026-04-14 20:06:06 -03:00
parent bc1f42db1f
commit 892019fa22
59 changed files with 5438 additions and 42 deletions
+172
View File
@@ -0,0 +1,172 @@
# frozen_string_literal: true
# ==========================================================
# adn/tools/bkps/lib/proc_linux.rb — Procesador Linux (apt)
#
# Fase 2: Soporta orquestación 1-dron-por-nodo
# listar(tarea) → lista nodos Linux pendientes
# ejecutar_uno(tarea, nodo) → actualiza UN solo nodo
# ejecutar(tarea) → actualiza TODOS (legacy)
# ==========================================================
require 'shellwords'
require 'net/ssh'
module ADN
module BKPs
module Procesador
class Linux
def initialize(log)
@log = log
end
# Scanner: devuelve lista de nodos Linux a actualizar
# Lee los archivos .md de nodos/ y filtra los Linux (Debian/Ubuntu)
def listar(tarea)
nodos_dir = File.join(ADN::NODOS_DIR || 'nodos', '')
unless Dir.exist?(nodos_dir)
@log.error("Directorio de nodos no existe: #{nodos_dir}")
return []
end
# Filtrar nodos Linux (excluye Windows y otros)
nodos_linux = Dir.glob(File.join(nodos_dir, '*.md'))
.select { |f| es_nodo_linux?(f) }
.map { |f| extraer_info_nodo(f) }
.compact
.reject { |n| n[:ip].nil? || n[:ip].empty? }
nodos_linux.map do |nodo|
{
archivo: nodo[:hostname], # Identificador para ejecutar_uno
vm: nodo[:hostname], # Nombre visible (nodo)
ruta: nodo[:ip], # IP del nodo
destino: nodo[:ip], # Mismo destino (es SSH)
tamaño: 0, # No aplicable para updates
pendiente: true, # Siempre verificar
metadata: nodo # Info adicional (usuario, etc.)
}
end
end
# Atómico: actualiza UN solo nodo Linux vía SSH
def ejecutar_uno(tarea, nombre_nodo)
ruta_md = File.join(ADN::NODOS_DIR || 'nodos', "#{nombre_nodo}.md")
nodo_info = File.exist?(ruta_md) ? extraer_info_nodo(ruta_md) : (tarea[:metadata] || {})
ip = nodo_info[:ip] || tarea[:origen]
usuario = nodo_info[:usuario] || tarea[:usuario] || 'root'
@log.info("🐧 Actualización Linux: #{nombre_nodo}")
@log.info(" SSH: #{usuario}@#{ip}")
begin
Net::SSH.start(ip, usuario, keys_only: true, timeout: 10) do |ssh|
# Variables de entorno para evitar interactividad
env_vars = "DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical"
apt_opts = "-o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
# Paso 1: apt update
@log.info(" [1/3] Ejecutando apt update...")
output_update = ssh.exec!("sudo #{env_vars} apt-get update 2>&1")
@log.info(" #{output_update.lines.last&.strip || 'OK'}")
# Paso 2: apt upgrade -y
@log.info(" [2/3] Ejecutando apt upgrade -y...")
output_upgrade = ssh.exec!("sudo #{env_vars} apt-get upgrade -y #{apt_opts} 2>&1")
@log.info(" #{output_upgrade.lines.last&.strip || 'OK'}")
# Paso 3: apt autoremove -y (limpieza)
@log.info(" [3/3] Ejecutando apt autoremove -y...")
output_autoremove = ssh.exec!("sudo #{env_vars} apt-get autoremove -y 2>&1")
@log.info(" #{output_autoremove.lines.last&.strip || 'OK'}")
@log.info("✔ Nodo #{nombre_nodo} actualizado exitosamente.")
true
end
rescue => e
@log.error("✗ Error actualizando #{nombre_nodo}: #{e.message}")
false
end
end
# Legacy: actualiza TODOS los nodos (compatibilidad)
def ejecutar(tarea)
@log.info("--- #{tarea[:texto]} ---")
@log.info("Origen: #{tarea[:origen]}")
nodos = listar(tarea)
if nodos.empty?
@log.info("Sin nodos Linux encontrados.")
return true
end
@log.info("Encontrados #{nodos.length} nodo(s) Linux.")
actualizados = 0
fallidos = 0
nodos.each do |nodo|
@log.info("\n" + "=" * 50)
if ejecutar_uno(tarea, nodo[:vm])
actualizados += 1
else
fallidos += 1
end
end
@log.info("\n" + "=" * 50)
@log.info("#{actualizados} nodo(s) actualizado(s), #{fallidos} fallido(s).")
fallidos == 0
end
private
# Verifica si el nodo es Linux (busca Debian, Ubuntu, Linux en el .md)
def es_nodo_linux?(archivo_md)
contenido = File.read(archivo_md)
# Excluir explícitamente Windows
return false if contenido.match?(/Windows\s+(Server\s+)?\d+/i)
return false if contenido.match?(/Windows\s+10/i)
return false if contenido.match?(/Windows\s+11/i)
# Incluir Linux/Debian/Ubuntu
return true if contenido.match?(/Debian/i)
return true if contenido.match?(/Ubuntu/i)
return true if contenido.match?(/Linux/i)
# Incluir si tiene IP LAN y es srvv-* o srv-* (convención de nombres)
hostname = File.basename(archivo_md, '.md')
return true if hostname.match?(/^srvv?-/)
false
end
# Extrae información del nodo desde el archivo .md
def extraer_info_nodo(archivo_md)
contenido = File.read(archivo_md)
hostname = File.basename(archivo_md, '.md')
# Extraer IP LAN
ip = contenido.match(/\*\*IP(?:\s+LAN)?\*\*:\s*`?(\d+\.\d+\.\d+\.\d+)`?/i)&.[](1) ||
contenido.match(/\|\s*IP(?:\s+LAN)?[^\|]*\|\s*`?(\d+\.\d+\.\d+\.\d+)`?\s*\|/i)&.[](1)
# Extraer usuario SSH (si está especificado)
usuario = contenido.match(/\*\*Usuario\s*SSH\*\*:\s*`?(\w+)`?/i)&.[](1) ||
contenido.match(/\*\*SSH\*\*:\s*`?ssh\s+(\w+)@/i)&.[](1) ||
'root'
# Extraer estado (para filtrar nodos offline)
estado = contenido.match(/\*\*Estado\*\*:\s*(🟢|🔴|⚫|🚧|🟡)/i)&.[](1)
{
hostname: hostname,
ip: ip,
usuario: usuario,
estado: estado || '🟢'
}
end
end
end
end
end
+2 -1
View File
@@ -17,5 +17,6 @@
"dasu-sql4:Administrador": "eyJpdiI6ImQzc09sRmo2NEZBbHZmN2IiLCJ0YWciOiJxK3Z3RU9uZG1QMXNUV1hJbEFmV3d3PT0iLCJkYXRhIjoicXd5dDI4bHBkeFNhRk1yTzluczlxdz09In0=", "dasu-sql4:Administrador": "eyJpdiI6ImQzc09sRmo2NEZBbHZmN2IiLCJ0YWciOiJxK3Z3RU9uZG1QMXNUV1hJbEFmV3d3PT0iLCJkYXRhIjoicXd5dDI4bHBkeFNhRk1yTzluczlxdz09In0=",
"dasu-sql4:sa": "eyJpdiI6Ik43aWR5d2RacFVFR3pyNysiLCJ0YWciOiJxeVAyTmxtYXloNTdFTVlvMFphQ2hRPT0iLCJkYXRhIjoiT0lrR1FGMXRVZ0czYU5PK1ZFcG8wUT09In0=", "dasu-sql4:sa": "eyJpdiI6Ik43aWR5d2RacFVFR3pyNysiLCJ0YWciOiJxeVAyTmxtYXloNTdFTVlvMFphQ2hRPT0iLCJkYXRhIjoiT0lrR1FGMXRVZ0czYU5PK1ZFcG8wUT09In0=",
"dasu-pcv:rustdesk": "eyJpdiI6ImRwRFRCYjNmc1htZUJMZ1ciLCJ0YWciOiJRYTdpcy9wRTdlTnpLemQ0Y1VqMTF3PT0iLCJkYXRhIjoiL3hEczU5OVRrRzAralFNPSJ9", "dasu-pcv:rustdesk": "eyJpdiI6ImRwRFRCYjNmc1htZUJMZ1ciLCJ0YWciOiJRYTdpcy9wRTdlTnpLemQ0Y1VqMTF3PT0iLCJkYXRhIjoiL3hEczU5OVRrRzAralFNPSJ9",
"dasu-pcv:adminpcv": "eyJpdiI6IlVUK3JOamxCeFhGUUlCMlUiLCJ0YWciOiI0MC81bE10Y0FJM1BqMTQ1NUIxWjhBPT0iLCJkYXRhIjoiUzZuOUIxaTVISmJpclNrREZYSTkwM1k9In0=" "dasu-pcv:adminpcv": "eyJpdiI6IlVUK3JOamxCeFhGUUlCMlUiLCJ0YWciOiI0MC81bE10Y0FJM1BqMTQ1NUIxWjhBPT0iLCJkYXRhIjoiUzZuOUIxaTVISmJpclNrREZYSTkwM1k9In0=",
"dasu-pc:UTNLR": "eyJpdiI6IjhLejRXUjNaTGU1RkRmaUgiLCJ0YWciOiJMRi9DdUhKWXg4VjJNL3MvajNqK1V3PT0iLCJkYXRhIjoicmJmdDZjZjNScXhFcVNzUC9ZSXc3SzA9In0="
} }
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/dasuten_backup_download.rb — Download desde Google Drive
# ==========================================================
# Flujo: Google Drive (drive_bkps-dasu) → dasu-sql4
# Uso: ruby adn/tools/cli/dasuten_backup_download.rb
# ==========================================================
require 'shellwords'
REMOTE_DRIVE = "rmonla-GDrive:drive_bkps-dasu"
LOCAL_BACKUP = "F:\\BACKUP\\sysdasuten_compressed_ADN.bak"
puts "[*] Iniciando descarga de backup desde Google Drive..."
begin
# Paso 1: Listar backups disponibles
puts "[1/4] Buscando backup más reciente en Drive..."
cmd_list = "rclone lsf '#{REMOTE_DRIVE}' --files-only --order '-modified' 2>/dev/null | grep sysdasuten | head -1"
backup_remoto = `#{cmd_list}`.strip
if backup_remoto.empty?
puts "[!] No se encontraron backups en #{REMOTE_DRIVE}"
exit 1
end
puts " Backup encontrado: #{backup_remoto}"
# Paso 2: Descargar backup
puts "[2/4] Descargando backup..."
puts " Origen: #{REMOTE_DRIVE}/#{backup_remoto}"
puts " Destino: #{LOCAL_BACKUP}"
puts " (Esto puede tomar 2-5 minutos)"
cmd_download = "rclone copy '#{REMOTE_DRIVE}/#{backup_remoto}' '#{LOCAL_BACKUP}' --progress -v"
start_download = Time.now
system(cmd_download)
if $?.success?
puts "[+] Descarga completada en #{(Time.now - start_download).round(2)} segundos."
else
puts "[!] Error en descarga (código: #{$?.exitstatus})"
exit 1
end
# Paso 3: Verificar archivo descargado
puts "[3/4] Verificando archivo descargado..."
cmd_verify = "powershell -Command \"(Get-Item '#{LOCAL_BACKUP}').Length\""
tamaño = `#{cmd_verify}`.strip.to_i
if tamaño > 0
puts "[+] Archivo verificado: #{(tamaño / 1024.0 / 1024.0).round(2)} MB"
else
puts "[!] Archivo descargado parece estar vacío"
exit 1
end
# Paso 4: Restaurar en SQL Server
puts "[4/4] Preparando para restauración..."
puts " Backup disponible en: #{LOCAL_BACKUP}"
puts ""
puts " Para restaurar, ejecutar:"
puts " sqlcmd -S localhost -Q \"RESTORE DATABASE sysdasuten FROM DISK = '#{LOCAL_BACKUP}' WITH REPLACE, MOVE 'sysdasuten' TO 'F:\\DATA\\sysdasuten.mdf', MOVE 'sysdasuten_log' TO 'F:\\LOG\\sysdasuten_log.ldf'\""
puts "\n✅ Descarga completada exitosamente."
rescue => e
puts "[!] Error fatal: #{e.message}"
exit 1
end
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/dasuten_backup_upload.rb — Upload a Google Drive
# ==========================================================
# Flujo: srv-ns8 (backup local) → Google Drive
# Uso: ruby adn/tools/cli/dasuten_backup_upload.rb
# ==========================================================
require 'time'
REMOTE_DRIVE = "rmonla-GDrive:drive_bkps-dasu"
BACKUP_LOCAL = "/var/tmp/sysdasuten_compressed_ADN.bak"
BACKUP_NAME = "sysdasuten_compressed_ADN_#{Time.now.strftime('%Y%m%d_%H%M%S')}.bak"
puts "[*] Iniciando upload de backup a Google Drive..."
# Verificar archivo local
unless File.exist?(BACKUP_LOCAL)
puts "[!] Backup no encontrado en #{BACKUP_LOCAL}"
puts " Primero generar backup en srvv-fenix y descargar a srv-ns8"
exit 1
end
tamaño_mb = (File.size(BACKUP_LOCAL) / 1024.0 / 1024.0).round(2)
puts " Archivo local: #{BACKUP_LOCAL}"
puts " Tamaño: #{tamaño_mb} MB"
puts " Destino: #{REMOTE_DRIVE}/#{BACKUP_NAME}"
begin
# Upload a Google Drive
puts "\n[1/2] Subiendo a Google Drive..."
puts " (Esto puede tomar 3-10 minutos dependiendo del ancho de banda)"
cmd_upload = "rclone copy '#{BACKUP_LOCAL}' '#{REMOTE_DRIVE}/#{BACKUP_NAME}' --progress -v"
start_upload = Time.now
system(cmd_upload)
if $?.success?
duracion = (Time.now - start_upload).round(2)
puts "[+] Upload completado en #{duracion} segundos."
else
puts "[!] Error en upload (código: #{$?.exitstatus})"
exit 1
end
# Verificar archivo en Drive
puts "\n[2/2] Verificando archivo en Drive..."
cmd_verify = "rclone size '#{REMOTE_DRIVE}/#{BACKUP_NAME}' 2>/dev/null"
output = `#{cmd_verify}`.strip
puts " Tamaño en Drive: #{output}"
puts "\n✅ Backup subido exitosamente a Google Drive:"
puts " #{REMOTE_DRIVE}/#{BACKUP_NAME}"
rescue => e
puts "[!] Error fatal: #{e.message}"
exit 1
end
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/dasuten_orquestador.rb — Orquestador Fase 8b
# ==========================================================
# Flujo completo: srvv-fenix → Google Drive → dasu-sql4
# Uso: ruby adn/tools/cli/dasuten_orquestador.rb [upload|download|restore]
# ==========================================================
require 'optparse'
require_relative 'dron/dispatcher'
PROJECT_ROOT = File.expand_path('../..', __dir__)
CLI_DIR = File.join(PROJECT_ROOT, 'adn', 'tools', 'cli')
ADN_RUN = File.join(PROJECT_ROOT, 'adn', 'tools', 'run')
opciones = {
paso: 'all',
dry_run: false,
nodo: nil
}
OptionParser.new do |opts|
opts.banner = "Uso: #{ADN_RUN} dasuten_orquestador [opciones]"
opts.on("--paso PASO", %w[upload download restore all], "Ejecutar solo este paso (upload|download|restore|all)") { |p| opciones[:paso] = p }
opts.on("--nodo NOMBRE", "Nodo para bitácora del dron") { |n| opciones[:nodo] = n }
opts.on("--dry-run", "Simulación sin ejecutar") { opciones[:dry_run] = true }
end.parse!(ARGV)
puts "\n#{'='*60}"
puts "🛸 ORQUESTADOR DASUTEN — Fase 8b (Refresco BD vía Google Drive)"
puts '='*60
puts " Paso seleccionado: #{opciones[:paso]}"
puts " Dry-run: #{opciones[:dry_run]}"
puts " Nodo: #{opciones[:nodo] || 'auto-detect'}"
puts '='*60
def lanzar_dron(cmd, nota, nodo: nil)
puts "\n🛸 Lanzando dron: #{nota}"
puts " Comando: #{cmd}"
args = ["lanzar", "--nota", nota]
args << "--nodo" << nodo if nodo
args << "--" << cmd
# Ejecutar dron directamente
require_relative 'dron/ejecutor'
resultado = Dron::Ejecutor.lanzar(
cmd: cmd,
nota: nota,
nodo: nodo,
timeout: 1800 # 30 minutos max
)
resultado
end
caso = opciones[:paso]
# ──────────────────────────────────────────────────────────────
# PASO 1: UPLOAD desde srvv-fenix a Google Drive
# ──────────────────────────────────────────────────────────────
if caso == 'all' || caso == 'upload'
puts "\n" + ""*60
puts "📤 PASO 1: Upload desde srvv-fenix → Google Drive"
puts ""*60
cmd_upload = "ruby #{CLI_DIR}/dasuten_backup_upload.rb"
if opciones[:dry_run]
puts "[DRY-RUN] Ejecutaría: #{cmd_upload}"
else
resultado = lanzar_dron(cmd_upload, "📤 Upload backup DASUTEN a Drive", nodo: 'srvv-fenix')
if resultado[:exit_code] != 0
puts "\n❌ Upload fallido. Deteniendo orquestación."
exit 1
end
end
end
# ──────────────────────────────────────────────────────────────
# PASO 2: DOWNLOAD desde Google Drive a dasu-sql4
# ──────────────────────────────────────────────────────────────
if caso == 'all' || caso == 'download'
puts "\n" + ""*60
puts "📥 PASO 2: Download desde Google Drive → dasu-sql4"
puts ""*60
cmd_download = "ruby #{CLI_DIR}/dasuten_backup_download.rb"
if opciones[:dry_run]
puts "[DRY-RUN] Ejecutaría: #{cmd_download}"
else
resultado = lanzar_dron(cmd_download, "📥 Download backup DASUTEN desde Drive", nodo: 'dasu-sql4')
if resultado[:exit_code] != 0
puts "\n❌ Download fallido. Deteniendo orquestación."
exit 1
end
end
end
# ──────────────────────────────────────────────────────────────
# PASO 3: RESTORE en dasu-sql4
# ──────────────────────────────────────────────────────────────
if caso == 'all' || caso == 'restore'
puts "\n" + ""*60
puts "💾 PASO 3: Restore + DBCC CHECKDB en dasu-sql4"
puts ""*60
cmd_restore = <<~SQL.gsub("\n", ' ').strip
powershell -Command "
$query = @'
RESTORE DATABASE sysdasuten FROM DISK = 'F:\\BACKUP\\sysdasuten_compressed_ADN.bak'
WITH REPLACE,
MOVE 'sysdasuten' TO 'F:\\DATA\\sysdasuten.mdf',
MOVE 'sysdasuten_log' TO 'F:\\LOG\\sysdasuten_log.ldf';
DBCC CHECKDB('sysdasuten');
'@
sqlcmd -S localhost -Q $query
"
SQL
if opciones[:dry_run]
puts "[DRY-RUN] Ejecutaría: #{cmd_restore}"
else
resultado = lanzar_dron(cmd_restore, "💾 Restore + CHECKDB DASUTEN", nodo: 'dasu-sql4')
if resultado[:exit_code] != 0
puts "\n❌ Restore fallido. Verificar logs."
exit 1
end
end
end
puts "\n" + ""*60
puts "✅ Orquestación completada exitosamente!"
puts ""*60
+12
View File
@@ -36,6 +36,8 @@ module Dron
SanadorComando.reintentar(args) SanadorComando.reintentar(args)
when 'estado', 'status' when 'estado', 'status'
VigilanteComando.estado(args) VigilanteComando.estado(args)
when 'ops', 'operaciones'
OpsComando.ejecutar(args)
when 'ayuda', 'help', '--help', '-h' when 'ayuda', 'help', '--help', '-h'
mostrar_ayuda mostrar_ayuda
else else
@@ -59,6 +61,7 @@ module Dron
lines << " #{Color::GREEN}flota#{Color::RESET} Dashboard compacto de la flota" lines << " #{Color::GREEN}flota#{Color::RESET} Dashboard compacto de la flota"
lines << " #{Color::GREEN}salud#{Color::RESET} Health check activo (detecta zombies)" lines << " #{Color::GREEN}salud#{Color::RESET} Health check activo (detecta zombies)"
lines << " #{Color::GREEN}estado <ID>#{Color::RESET} Estado detallado de un dron" lines << " #{Color::GREEN}estado <ID>#{Color::RESET} Estado detallado de un dron"
lines << " #{Color::GREEN}ops <acción>#{Color::RESET} Operaciones de infraestructura (update_os)"
lines << " #{Color::GREEN}sanear#{Color::RESET} Sanear flota (reintentar + limpiar)" lines << " #{Color::GREEN}sanear#{Color::RESET} Sanear flota (reintentar + limpiar)"
lines << " #{Color::GREEN}limpiar#{Color::RESET} Limpiar drones antiguos" lines << " #{Color::GREEN}limpiar#{Color::RESET} Limpiar drones antiguos"
lines << " #{Color::GREEN}reintentar#{Color::RESET} Re-intentar drones fallidos" lines << " #{Color::GREEN}reintentar#{Color::RESET} Re-intentar drones fallidos"
@@ -74,6 +77,8 @@ module Dron
lines << "\n#{Color::BOLD}Ejemplos:#{Color::RESET}" lines << "\n#{Color::BOLD}Ejemplos:#{Color::RESET}"
lines << " #{Color::DIM}$ ./adn/tools/run dron lanzar --nota 'Backup SQL' -- vzdump 103#{Color::RESET}" lines << " #{Color::DIM}$ ./adn/tools/run dron lanzar --nota 'Backup SQL' -- vzdump 103#{Color::RESET}"
lines << " #{Color::CYAN}Lanzar backup como dron#{Color::RESET}" lines << " #{Color::CYAN}Lanzar backup como dron#{Color::RESET}"
lines << " #{Color::DIM}$ ./adn/tools/run dron ops update_os --target linux#{Color::RESET}"
lines << " #{Color::CYAN}Actualizar OS de todos los nodos Linux#{Color::RESET}"
lines << " #{Color::DIM}$ ./adn/tools/run dron flota#{Color::RESET}" lines << " #{Color::DIM}$ ./adn/tools/run dron flota#{Color::RESET}"
lines << " #{Color::CYAN}Ver estado de la flota#{Color::RESET}" lines << " #{Color::CYAN}Ver estado de la flota#{Color::RESET}"
lines << " #{Color::DIM}$ ./adn/tools/run dron salud#{Color::RESET}" lines << " #{Color::DIM}$ ./adn/tools/run dron salud#{Color::RESET}"
@@ -279,4 +284,11 @@ module Dron
opciones opciones
end end
end end
class OpsComando
def self.ejecutar(args)
require_relative 'ops'
Dron::Ops.ejecutar(args)
end
end
end end
+293
View File
@@ -0,0 +1,293 @@
# frozen_string_literal: true
# ==========================================================
# Dron::Ops — Operaciones de Infraestructura via AtomicDrones
# ----------------------------------------------------------
# Responsabilidad: Orquestar tareas de mantenimiento de nodos
# (update_os, etc.) usando 1 dron ejecutor por nodo.
#
# Reutiliza:
# - NodosInfo (scanner de fichas .md)
# - Dron::Ejecutor (ejecución atómica con auto-bitácora)
#
# Uso:
# ./adn/tools/run dron ops update_os --target linux
# ./adn/tools/run dron ops update_os --nodo srvv-dns
# ./adn/tools/run dron ops update_os --target linux --dry-run
#
# Plan: A01.P011 — Drones de Operaciones de Infraestructura
# ==========================================================
require 'optparse'
require 'shellwords'
require_relative 'base'
require_relative 'ejecutor'
require_relative '../nodos/info'
require_relative '../../core/colores'
require_relative '../../core/constants'
module Dron
class Ops
# Comandos SSH para actualización desatendida
APT_ENV = 'DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical'
APT_OPTS = "-o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'"
APT_CMD = "sudo #{APT_ENV} apt-get update 2>&1 && " \
"sudo #{APT_ENV} apt-get upgrade -y #{APT_OPTS} 2>&1 && " \
"sudo #{APT_ENV} apt-get autoremove -y 2>&1"
class << self
# ─── Punto de entrada CLI ──────────────────────────────────
def ejecutar(args)
accion = args.shift
case accion
when 'update_os'
cmd_update_os(args)
when 'ayuda', 'help', '--help', '-h', nil
mostrar_ayuda
else
Base.logger.error("❌ Acción desconocida para ops: #{accion}")
mostrar_ayuda
exit 1
end
end
private
# ─── update_os ─────────────────────────────────────────────
def cmd_update_os(args)
opciones = parsear_opciones(args)
# Obtener nodos candidatos
nodos = escanear_nodos(opciones)
if nodos.empty?
puts "#{Color::YELLOW}⚠ Sin nodos Linux encontrados para actualizar.#{Color::RESET}"
return
end
mostrar_resumen(nodos, opciones)
return if opciones[:dry_run]
# Orquestar: 1 dron ejecutor por nodo
resultados = orquestar_nodos(nodos, opciones)
# Resumen final
mostrar_resultados(resultados, nodos.length)
exit 1 if resultados[:failed] > 0 && opciones[:on_error] == 'abort'
end
# ─── Scanner: reutiliza NodosInfo ──────────────────────────
def escanear_nodos(opciones)
nodos_dir = ADN::NODOS_DIR
unless Dir.exist?(nodos_dir)
Base.logger.error("Directorio de nodos no existe: #{nodos_dir}")
return []
end
candidatos = Dir.glob(File.join(nodos_dir, '*.md'))
.reject { |f| File.basename(f).start_with?('_') }
.map { |f| ADN::NodosInfo.extraer_metadata(File.basename(f, '.md')) }
.compact
# Filtrar por OS
if opciones[:target] == 'linux'
candidatos.select! { |n| n[:so] == :linux }
end
# Excluir nodos sin IP
candidatos.reject! { |n| n[:ip].nil? || n[:ip].to_s.strip.empty? }
# Excluir nodos offline
candidatos.reject! { |n| n[:estado]&.to_s&.include?('🔴') }
# Filtrar nodo específico
if opciones[:nodo]
candidatos.select! { |n| n[:nombre] == opciones[:nodo] }
end
# Filtrar por ring
if opciones[:ring]
candidatos.select! { |n| n[:ring] == opciones[:ring] }
end
candidatos
end
# ─── Orquestación: 1 dron por nodo ─────────────────────────
def orquestar_nodos(nodos, opciones)
resultados = { ok: 0, failed: 0, skipped: 0 }
abortado = false
nodos.each_with_index do |nodo, i|
break if abortado
puts " #{Color::BOLD}[#{i + 1}/#{nodos.length}]#{Color::RESET} " \
"#{Color::CYAN}#{nodo[:nombre]}#{Color::RESET} (#{nodo[:ip]})"
# Construir comando SSH para este nodo
usuario = nodo[:usuario] || 'root'
puerto = nodo[:puerto_ssh] || 22
ssh_cmd = construir_ssh_cmd(nodo, usuario, puerto)
# Lanzar dron ejecutor (auto-bitácora incluida)
nota = "🐧 Actualizar #{nodo[:nombre]}"
resultado = Dron::Ejecutor.lanzar(
cmd: ssh_cmd,
nota: nota,
timeout: opciones[:timeout],
nodo: nodo[:nombre]
)
if resultado[:exit_code] == 0
puts " #{Color::GREEN}✔ Completado#{Color::RESET} (#{resultado[:duration]}s)"
resultados[:ok] += 1
else
resultados[:failed] += 1
case opciones[:on_error]
when 'abort'
puts " #{Color::RED}✖ Falló — deteniendo orquestación#{Color::RESET}"
abortado = true
resultados[:skipped] = nodos.length - i - 1
when 'continue'
puts " #{Color::YELLOW}⚠ Falló — continuando con el siguiente#{Color::RESET}"
end
end
puts ""
end
resultados
end
# ─── Construcción de comando SSH ───────────────────────────
def construir_ssh_cmd(nodo, usuario, puerto)
ssh_opts = "-o StrictHostKeyChecking=no -o ConnectTimeout=10"
# Puerto no estándar
ssh_opts += " -p #{puerto}" if puerto != 22
# Opciones especiales (legacy servers como XenServer)
ssh_opts += " #{nodo[:ssh_opts]}" if nodo[:ssh_opts]
# Clave SSH específica
if nodo[:clave_ssh]
clave = nodo[:clave_ssh].gsub('~', ENV['HOME'] || '/root')
ssh_opts += " -i #{clave}"
end
remote_cmd = APT_CMD
# Determinar método de autenticación
if nodo[:clave_boveda]
# AUTH POR PASSWORD: Usar candados run + sshpass
# candados inyecta $PASS en el entorno, sshpass lo consume
adn_run = File.join(ADN::PROJECT_ROOT, 'adn/tools/run')
"#{adn_run} candados run #{nodo[:clave_boveda]} " \
"'sshpass -p $PASS ssh #{ssh_opts} #{usuario}@#{nodo[:ip]} \"#{remote_cmd}\"'"
else
# AUTH POR CLAVE SSH: Conexión directa
"ssh #{ssh_opts} #{usuario}@#{nodo[:ip]} '#{remote_cmd}'"
end
end
# ─── Presentación ─────────────────────────────────────────
def mostrar_resumen(nodos, opciones)
puts ""
puts "#{Color::BOLD}#{Color::CYAN}🐧 Dron Ops — Actualización de OS Linux#{Color::RESET}"
puts "" * 60
puts " Nodos detectados: #{nodos.length}"
puts " Target: #{opciones[:target]}"
puts " Ring: #{opciones[:ring] || 'todos'}" if opciones[:ring]
puts " On-error: #{opciones[:on_error]}"
puts " Timeout por nodo: #{opciones[:timeout]}s"
if opciones[:dry_run]
puts " Modo: #{Color::YELLOW}DRY-RUN (sin ejecutar)#{Color::RESET}"
end
puts ""
if opciones[:dry_run]
nodos.each_with_index do |nodo, i|
auth = nodo[:clave_boveda] ? "🔐 #{nodo[:clave_boveda]}" : "🔑 key"
ring_tag = nodo[:ring] ? " [#{nodo[:ring]}]" : ""
puts " #{Color::DIM}[#{i + 1}]#{Color::RESET} #{nodo[:nombre].ljust(18)} " \
"#{Color::DIM}#{nodo[:ip].ljust(16)} #{(nodo[:usuario] || 'root').ljust(8)} #{auth}#{ring_tag}#{Color::RESET}"
end
puts ""
puts "#{Color::YELLOW}[DRY-RUN] No se lanzaron drones.#{Color::RESET}"
end
end
def mostrar_resultados(resultados, total)
puts "#{Color::BOLD}🐧 Resumen de Operaciones#{Color::RESET}"
puts "" * 40
puts " ✅ Exitosos: #{resultados[:ok]}"
puts " ❌ Fallidos: #{resultados[:failed]}" if resultados[:failed] > 0
puts " ⏭️ Saltados: #{resultados[:skipped]}" if resultados[:skipped] > 0
puts "#{Color::BOLD}🐧 Operación #{resultados[:failed] > 0 ? 'con errores' : 'finalizada'}.#{Color::RESET}"
end
# ─── Parser de opciones ────────────────────────────────────
def parsear_opciones(args)
opciones = {
target: 'linux',
nodo: nil,
ring: nil,
on_error: 'continue',
timeout: 600,
dry_run: false
}
OptionParser.new do |opts|
opts.banner = "Uso: ./adn/tools/run dron ops update_os [opciones]"
opts.on("--target OS", "OS a actualizar (linux)") { |t| opciones[:target] = t }
opts.on("--nodo NOMBRE", "Actualizar solo este nodo") { |n| opciones[:nodo] = n }
opts.on("--ring RING", %w[test standard core], "Solo nodos de este ring (test|standard|core)") { |r| opciones[:ring] = r }
opts.on("--on-error MODE", %w[abort continue], "Qué hacer si un dron falla (abort|continue)") { |m| opciones[:on_error] = m }
opts.on("--timeout N", Integer, "Timeout por nodo en segundos (default: 600)") { |t| opciones[:timeout] = t }
opts.on("--dry-run", "Solo listar nodos sin ejecutar") { opciones[:dry_run] = true }
end.parse!(args)
opciones
end
def mostrar_ayuda
puts "#{Color::BOLD}🐧 Dron Ops — Operaciones de Infraestructura#{Color::RESET}"
puts "#{Color::DIM}#{'=' * 50}#{Color::RESET}"
puts ""
puts "#{Color::CYAN}Operaciones de mantenimiento sobre nodos vía AtomicDrones#{Color::RESET}"
puts ""
puts "Uso: #{Color::YELLOW}./adn/tools/run dron ops <acción> [opciones]#{Color::RESET}"
puts ""
puts "#{Color::BOLD}Acciones:#{Color::RESET}"
puts " #{Color::GREEN}update_os#{Color::RESET} Actualizar sistema operativo de nodos"
puts ""
puts "#{Color::BOLD}Opciones:#{Color::RESET}"
puts " #{Color::GREEN}--target OS#{Color::RESET} OS a actualizar: linux (default: linux)"
puts " #{Color::GREEN}--nodo NOMBRE#{Color::RESET} Actualizar solo un nodo específico"
puts " #{Color::GREEN}--on-error MODE#{Color::RESET} abort|continue (default: continue)"
puts " #{Color::GREEN}--timeout N#{Color::RESET} Timeout por nodo en segundos (default: 600)"
puts " #{Color::GREEN}--dry-run#{Color::RESET} Solo listar sin ejecutar"
puts ""
puts "#{Color::BOLD}Ejemplos:#{Color::RESET}"
puts " #{Color::DIM}$ ./adn/tools/run dron ops update_os --target linux --dry-run#{Color::RESET}"
puts " #{Color::CYAN}Listar nodos Linux sin actualizar#{Color::RESET}"
puts " #{Color::DIM}$ ./adn/tools/run dron ops update_os --nodo srvv-dns#{Color::RESET}"
puts " #{Color::CYAN}Actualizar solo srvv-dns#{Color::RESET}"
puts " #{Color::DIM}$ ./adn/tools/run dron ops update_os --on-error continue#{Color::RESET}"
puts " #{Color::CYAN}Actualizar toda la flota Linux continuando ante fallos#{Color::RESET}"
puts ""
end
end
end
end
@@ -0,0 +1,88 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/drones/dasuten_upload_srv-ns8-drive.rb
# ==========================================================
# Dron: Upload a Google Drive desde srv-ns8
# Flujo: /var/tmp/sysdasuten_compressed_ADN.bak → Google Drive
# Entrada: Backup ya descargado en /var/tmp/
# Salida: URL de Google Drive
# ==========================================================
require 'time'
require 'json'
require 'shellwords'
REMOTE_DRIVE = "rmonla-GDrive:drive_bkps-dasu"
# Leer ruta del backup desde el output de la transferencia
TRANSFER_OUTPUT = '/tmp/dron_transfer_output.json'
if File.exist?(TRANSFER_OUTPUT)
output = JSON.parse(File.read(TRANSFER_OUTPUT))
BACKUP_LOCAL = output['backup_local']
BACKUP_NAME = File.basename(BACKUP_LOCAL)
puts "[*] Dron: Upload a Google Drive (DIFERENCIAL)"
else
BACKUP_LOCAL = "/var/tmp/sysdasuten_compressed_ADN.bak"
BACKUP_NAME = "sysdasuten_compressed_ADN_#{Time.now.strftime('%Y%m%d_%H%M%S')}.bak"
puts "[*] Dron: Upload a Google Drive (COMPLETO)"
end
puts " Nodo: srv-ns8"
puts " Origen: #{BACKUP_LOCAL}"
puts " Destino: #{REMOTE_DRIVE}/#{BACKUP_NAME}"
# Verificar archivo local
unless File.exist?(BACKUP_LOCAL)
puts "[!] Backup no encontrado en #{BACKUP_LOCAL}"
puts " Ejecutar primero: dasuten_transferir_srvv-fenix-srv-ns8.rb"
exit 1
end
tamaño_mb = (File.size(BACKUP_LOCAL) / 1024.0 / 1024.0).round(2)
puts "[+] Archivo local: #{tamaño_mb} MB"
begin
# Upload a Google Drive
puts "\n[1/2] Subiendo a Google Drive..."
start = Time.now
cmd_upload = "rclone copy '#{BACKUP_LOCAL}' '#{REMOTE_DRIVE}/#{BACKUP_NAME}' --progress -v 2>&1"
system(cmd_upload)
if $?.success?
duracion = (Time.now - start).round(2)
puts "[+] Upload completado en #{duracion} segundos"
else
puts "[!] Error en upload (código: #{$?.exitstatus})"
exit 1
end
# Verificar en Drive
puts "\n[2/2] Verificando archivo en Drive..."
cmd_verify = "rclone size '#{REMOTE_DRIVE}/#{BACKUP_NAME}' 2>/dev/null"
output = `#{cmd_verify}`.strip
puts " Tamaño en Drive: #{output}"
# Output para orquestador
resultado = {
paso: 'upload_complete',
drive_ruta: "#{REMOTE_DRIVE}/#{BACKUP_NAME}",
drive_url: "https://drive.google.com/drive/folders/1Tgrn2QYyWf0v0eyY1bSGCLlhiqhtIA2n",
nodo: 'srv-ns8',
duracion: duracion,
timestamp: Time.now.iso8601,
siguiente_paso: 'download_drive'
}
puts "\n✅ Upload completado"
puts " Output: #{resultado.to_json}"
File.write('/tmp/dron_upload_output.json', resultado.to_json)
rescue => e
puts "[!] Error fatal: #{e.message}"
exit 1
end
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/drones/orquestador_pipeline.rb
# ==========================================================
# Orquestador de Pipeline DASUTEN — Intercomunicación vía JSON
# ==========================================================
# Flujo: exportar → transferir SMB → upload → download → restaurar → verificar
# Cada dron escribe su output en JSON y el orquestador lo lee
# para decidir el siguiente paso.
# ==========================================================
require 'optparse'
require 'json'
require 'time'
require 'shellwords'
CLI_DIR = File.expand_path(__dir__)
ADN_RUN = File.join(File.dirname(File.dirname(File.dirname(CLI_DIR))), 'tools', 'run')
# Configuración
opciones = {
paso_inicial: 'exportar',
dry_run: false,
timeout: 1800, # 30 min por dron
nodo_bitacora: 'dasuten-pipeline'
}
OptionParser.new do |opts|
opts.banner = "Uso: #{ADN_RUN} dron lanzar --nota 'Pipeline DASUTEN' -- ruby adn/tools/cli/drones/orquestador_pipeline.rb [opciones]"
opts.on("--paso PASO", %w[exportar transferir upload download restaurar verificar], "Comenzar desde este paso") { |p| opciones[:paso_inicial] = p }
opts.on("--dry-run", "Simulación sin ejecutar drones") { opciones[:dry_run] = true }
opts.on("--timeout N", Integer, "Timeout por dron en segundos") { |t| opciones[:timeout] = t }
end.parse!(ARGV)
# Estados del pipeline (6 drones atómicos)
ESTADOS = {
exportar: {
script: 'dasuten_exportar_srvv-fenix.rb',
nodo: 'srvv-fenix',
nota: '📤 Exportar backup DASUTEN',
output_file: '/tmp/dron_export_output.json',
siguiente: :transferir
},
transferir: {
script: 'dasuten_transferir_srvv-fenix-srv-ns8.rb',
nodo: 'srv-ns8',
nota: '🔄 Transferir SMB (fenix → ns8)',
output_file: '/tmp/dron_transfer_output.json',
siguiente: :upload
},
upload: {
script: 'dasuten_upload_srv-ns8-drive.rb',
nodo: 'srv-ns8',
nota: '☁️ Upload a Google Drive',
output_file: '/tmp/dron_upload_output.json',
siguiente: :download
},
download: {
script: 'dasuten_download_drive-dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '📥 Download desde Google Drive',
output_file: '/tmp/dron_download_output.json',
siguiente: :restaurar
},
restaurar: {
script: 'dasuten_restaurar_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '💾 Restaurar backup',
output_file: '/tmp/dron_restore_output.json',
siguiente: :verificar
},
verificar: {
script: 'dasuten_verificar-integridad_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '✅ Verificar integridad BD',
output_file: '/tmp/dron_verify_output.json',
siguiente: nil # Final
}
}.freeze
puts "\n" + "="*70
puts "🛸 ORQUESTADOR PIPELINE DASUTEN (Fase 8b — 6 drones atómicos)"
puts "="*70
puts " Paso inicial: #{opciones[:paso_inicial]}"
puts " Dry-run: #{opciones[:dry_run]}"
puts " Timeout: #{opciones[:timeout]}s por dron"
puts " Drones: #{ESTADOS.keys.join(' → ')}"
puts "="*70
# ──────────────────────────────────────────────────────────────
# Función para lanzar un dron y esperar su resultado
# ──────────────────────────────────────────────────────────────
def lanzar_dron(script, nodo, nota, timeout)
cmd = "ruby #{File.join(File.dirname(__FILE__), script)}"
puts "\n" + "-"*70
puts "🛸 Lanzando dron: #{nota}"
puts " Nodo: #{nodo}"
puts " Script: #{script}"
puts "-"*70
# Lanzar dron ejecutor
dron_cmd = "#{ADN_RUN} dron lanzar --nota #{Shellwords.escape(nota)} --nodo #{Shellwords.escape(nodo)} --timeout #{timeout} -- #{cmd}"
puts " Comando: #{dron_cmd}"
if opciones[:dry_run]
puts " [DRY-RUN] No ejecutado"
return true, {}
end
exito = system(dron_cmd)
codigo = $?.exitstatus
if exito
puts "\n✅ Dron completado exitosamente"
# Leer output del dron
output_file = ESTADOS[nodo.to_sym][:output_file]
if File.exist?(output_file)
resultado = JSON.parse(File.read(output_file))
puts " Output: #{resultado.inspect}"
return true, resultado
else
puts " [!] No se encontró archivo de output"
return true, {}
end
else
puts "\n❌ Dron fallido (código: #{codigo})"
return false, {}
end
end
# ──────────────────────────────────────────────────────────────
# Ejecutar pipeline
# ──────────────────────────────────────────────────────────────
paso_actual = opciones[:paso_inicial].to_sym
contexto = {} # Para pasar datos entre drones
duracioniones = []
begin
loop do
break unless ESTADOS[paso_actual]
config = ESTADOS[paso_actual]
# Inyectar contexto del paso anterior si es necesario
case paso_actual
when :upload
# El dron de upload ya sabe dónde está el archivo (hardcodeado en /var/tmp/)
when :download
# El dron de download usa file_id o test_file
when :restaurar
# El dron de restaurar usa la ruta hardcodeada F:\BACKUP\
end
# Lanzar dron
exito, resultado = lanzar_dron(
config[:script],
config[:nodo],
config[:nota],
opciones[:timeout]
)
unless exito
puts "\n❌ Pipeline fallido en paso: #{paso_actual}"
exit 1
end
# Guardar contexto para siguiente paso
contexto.merge!(resultado) if resultado
duraciones << resultado[:duracion] if resultado[:duracion]
# Decidir siguiente paso
if config[:siguiente]
puts "\n➡️ Pasando a: #{config[:siguiente]}"
paso_actual = config[:siguiente]
else
puts "\n✅ Pipeline completado exitosamente!"
break
end
end
# Resumen final
puts "\n" + "="*70
puts "📊 RESUMEN DEL PIPELINE"
puts "="*70
if contexto[:pipeline_completo] || contexto[:estado_integridad] == 'OK'
puts " Estado: ✅ COMPLETADO"
puts " Integridad BD: #{contexto[:estado_integridad] || 'N/A'}"
else
puts " Estado: ⏳ EN PROGRESO"
end
puts " Backup Drive: #{contexto[:drive_ruta] || contexto[:drive_origen] || 'N/A'}"
puts " Duración total: #{duraciones.sum.round(2)}s (#{duraciones.count} drones)"
puts "="*70
rescue => e
puts "\n❌ Error en orquestador: #{e.message}"
puts e.backtrace.first(5).join("\n")
exit 1
end
@@ -0,0 +1,206 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# adn/tools/cli/drones/orquestador_pipeline_diferencial.rb
# ==========================================================
# Orquestador de Pipeline DIFERENCIAL DASUTEN
# ==========================================================
# Flujo: exportar_dif → transferir → upload → download → restaurar_dif → verificar
# Para refrescos diarios (solo cambios desde último backup completo)
# ==========================================================
require 'optparse'
require 'json'
require 'time'
require 'shellwords'
CLI_DIR = File.expand_path(__dir__)
ADN_RUN = File.join(File.dirname(File.dirname(File.dirname(CLI_DIR))), 'tools', 'run')
# Configuración
opciones = {
paso_inicial: 'exportar_dif',
dry_run: false,
timeout: 900, # 15 min por dron (suficiente para diferencial)
nodo_bitacora: 'dasuten-pipeline-dif'
}
OptionParser.new do |opts|
opts.banner = "Uso: #{ADN_RUN} dron lanzar --nota 'Pipeline Diferencial DASUTEN' -- ruby adn/tools/cli/drones/orquestador_pipeline_diferencial.rb [opciones]"
opts.on("--paso PASO", %w[exportar_dif transferir upload download restaurar_dif verificar], "Comenzar desde este paso") { |p| opciones[:paso_inicial] = p }
opts.on("--dry-run", "Simulación sin ejecutar drones") { opciones[:dry_run] = true }
opts.on("--timeout N", Integer, "Timeout por dron en segundos") { |t| opciones[:timeout] = t }
end.parse!(ARGV)
# Estados del pipeline diferencial (6 drones atómicos)
ESTADOS = {
exportar_dif: {
script: 'dasuten_exportar_diferencial_srvv-fenix.rb',
nodo: 'srvv-fenix',
nota: '📤 Exportar backup DIFERENCIAL DASUTEN',
output_file: '/tmp/dron_export_dif_output.json',
siguiente: :transferir
},
transferir: {
script: 'dasuten_transferir_srvv-fenix-srv-ns8.rb',
nodo: 'srv-ns8',
nota: '🔄 Transferir SMB (fenix → ns8)',
output_file: '/tmp/dron_transfer_output.json',
siguiente: :upload
},
upload: {
script: 'dasuten_upload_srv-ns8-drive.rb',
nodo: 'srv-ns8',
nota: '☁️ Upload a Google Drive',
output_file: '/tmp/dron_upload_output.json',
siguiente: :download
},
download: {
script: 'dasuten_download_drive-dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '📥 Download desde Google Drive',
output_file: '/tmp/dron_download_output.json',
siguiente: :restaurar_dif
},
restaurar_dif: {
script: 'dasuten_restaurar_diferencial_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '💾 Restaurar backup DIFERENCIAL',
output_file: '/tmp/dron_restore_dif_output.json',
siguiente: :verificar
},
verificar: {
script: 'dasuten_verificar-integridad_dasu-sql4.rb',
nodo: 'dasu-sql4',
nota: '✅ Verificar integridad BD',
output_file: '/tmp/dron_verify_output.json',
siguiente: nil # Final
}
}.freeze
puts "\n" + "="*70
puts "🛸 ORQUESTADOR PIPELINE DIFERENCIAL DASUTEN (Refresco diario)"
puts "="*70
puts " Paso inicial: #{opciones[:paso_inicial]}"
puts " Dry-run: #{opciones[:dry_run]}"
puts " Timeout: #{opciones[:timeout]}s por dron"
puts " Drones: #{ESTADOS.keys.join(' → ')}"
puts "="*70
puts ""
puts " ️ Este pipeline usa backup DIFERENCIAL (solo cambios)"
puts " Esperado: ~5-10 min total (vs ~35 min del completo)"
puts "="*70
# ──────────────────────────────────────────────────────────────
# Función para lanzar un dron y esperar su resultado
# ──────────────────────────────────────────────────────────────
def lanzar_dron(script, nodo, nota, timeout, dry_run)
cmd = "ruby #{File.join(File.dirname(__FILE__), script)}"
puts "\n" + "-"*70
puts "🛸 Lanzando dron: #{nota}"
puts " Nodo: #{nodo}"
puts " Script: #{script}"
puts "-"*70
# Lanzar dron ejecutor
dron_cmd = "#{ADN_RUN} dron lanzar --nota #{Shellwords.escape(nota)} --nodo #{Shellwords.escape(nodo)} --timeout #{timeout} -- #{cmd}"
puts " Comando: #{dron_cmd}"
if dry_run
puts " [DRY-RUN] No ejecutado"
return true, {}
end
exito = system(dron_cmd)
codigo = $?.exitstatus
if exito
puts "\n✅ Dron completado exitosamente"
# Leer output del dron (buscar en todos los archivos posibles)
output_files = ['/tmp/dron_export_dif_output.json', '/tmp/dron_transfer_output.json',
'/tmp/dron_upload_output.json', '/tmp/dron_download_output.json',
'/tmp/dron_restore_dif_output.json', '/tmp/dron_verify_output.json']
output_file = output_files.find { |f| File.exist?(f) }
if output_file && File.exist?(output_file)
resultado = JSON.parse(File.read(output_file))
puts " Output: #{resultado.inspect}"
return true, resultado
else
puts " [!] No se encontró archivo de output"
return true, {}
end
else
puts "\n❌ Dron fallido (código: #{codigo})"
return false, {}
end
end
# ──────────────────────────────────────────────────────────────
# Ejecutar pipeline
# ──────────────────────────────────────────────────────────────
paso_actual = opciones[:paso_inicial].to_sym
contexto = {} # Para pasar datos entre drones
duraciones = []
begin
loop do
break unless ESTADOS[paso_actual]
config = ESTADOS[paso_actual]
# Lanzar dron
exito, resultado = lanzar_dron(
config[:script],
config[:nodo],
config[:nota],
opciones[:timeout],
opciones[:dry_run]
)
unless exito
puts "\n❌ Pipeline fallido en paso: #{paso_actual}"
exit 1
end
# Guardar contexto para siguiente paso
contexto.merge!(resultado) if resultado
duraciones << resultado[:duracion] if resultado[:duracion]
# Decidir siguiente paso
if config[:siguiente]
puts "\n➡️ Pasando a: #{config[:siguiente]}"
paso_actual = config[:siguiente]
else
puts "\n✅ Pipeline completado exitosamente!"
break
end
end
# Resumen final
puts "\n" + "="*70
puts "📊 RESUMEN DEL PIPELINE DIFERENCIAL"
puts "="*70
if contexto[:pipeline_completo] || contexto[:estado_integridad] == 'OK'
puts " Estado: ✅ COMPLETADO"
puts " Integridad BD: #{contexto[:estado_integridad] || 'N/A'}"
else
puts " Estado: ⏳ EN PROGRESO"
end
puts " Backup Drive: #{contexto[:drive_ruta] || contexto[:drive_origen] || 'N/A'}"
puts " Duración total: #{duraciones.sum.round(2)}s (#{duraciones.count} drones)"
puts " Tipo: #{contexto[:backup_tipo] || 'differential'}"
puts "="*70
rescue => e
puts "\n❌ Error en orquestador: #{e.message}"
puts e.backtrace.first(5).join("\n")
exit 1
end
+82 -1
View File
@@ -2,6 +2,12 @@
# adn/tools/cli/nodos/info.rb — Utilidades para extracción de metadatos de fichas de nodos # adn/tools/cli/nodos/info.rb — Utilidades para extracción de metadatos de fichas de nodos
# ====================================================================================== # ======================================================================================
#
# Fase 2.5: Soporte dual — Tabla canónica (META:BEGIN/META:END) + fallback regex
#
# Prioridad de lectura:
# 1. Tabla canónica entre <!-- META:BEGIN --> y <!-- META:END -->
# 2. Regex heurísticas (legacy, para fichas no migradas)
require_relative '../../core/constants' require_relative '../../core/constants'
@@ -12,6 +18,30 @@ module ADN
return nil unless File.exist?(ruta) return nil unless File.exist?(ruta)
contenido = File.read(ruta, encoding: 'UTF-8') contenido = File.read(ruta, encoding: 'UTF-8')
# ─── Intentar tabla canónica primero ──────────────────────
meta = extraer_meta_canonica(contenido)
if meta
return {
nombre: nodo_nombre,
ip: meta['IP'],
ip_lan: detectar_ip_lan(contenido),
host: meta['Host'],
so: detectar_so_desde_valor(meta['OS']),
vmid: meta['VMID'],
puerto_ssh: extraer_puerto_de_ssh(meta['SSH']),
usuario: extraer_usuario_de_ssh(meta['SSH']),
clave_boveda: meta['Bóveda'],
auth_type: meta['Auth']&.to_sym,
clave_ssh: meta['Clave SSH'],
ssh_opts: meta['SSH Opts'],
ring: meta['Ring'],
estado: meta['Estado'],
_fuente: :canonica
}
end
# ─── Fallback: Regex heurísticas (legacy) ─────────────────
{ {
nombre: nodo_nombre, nombre: nodo_nombre,
ip: detectar_ip(contenido), ip: detectar_ip(contenido),
@@ -24,7 +54,10 @@ module ADN
clave_boveda: detectar_clave_boveda(contenido), clave_boveda: detectar_clave_boveda(contenido),
auth_type: detectar_auth_type(contenido), auth_type: detectar_auth_type(contenido),
clave_ssh: detectar_clave_ssh(contenido), clave_ssh: detectar_clave_ssh(contenido),
ssh_opts: detectar_ssh_opts(contenido) ssh_opts: detectar_ssh_opts(contenido),
ring: nil,
estado: nil,
_fuente: :regex
} }
end end
@@ -35,6 +68,53 @@ module ADN
detectar_ip_lan(contenido) detectar_ip_lan(contenido)
end end
# ─── Parser de tabla canónica ──────────────────────────────
def self.extraer_meta_canonica(contenido)
match = contenido.match(/<!-- META:BEGIN -->(.*?)<!-- META:END -->/m)
return nil unless match
meta = {}
match[1].each_line do |linea|
# | **Campo** | `valor` | o | **Campo** | valor |
if linea =~ /\|\s*\*\*(.+?)\*\*\s*\|\s*`?([^`|]+?)`?\s*\|/
campo = $1.strip
valor = $2.strip
meta[campo] = valor unless valor.empty?
end
end
meta.empty? ? nil : meta
end
def self.extraer_usuario_de_ssh(ssh_str)
return nil unless ssh_str
# Formato: user@ip:port
if ssh_str =~ /^([a-zA-Z0-9_\-\.]+)@/
return $1
end
nil
end
def self.extraer_puerto_de_ssh(ssh_str)
return 22 unless ssh_str
# Formato: user@ip:port
if ssh_str =~ /:(\d+)$/
return $1.to_i
end
22
end
def self.detectar_so_desde_valor(os_str)
return :linux unless os_str
return :linux if os_str =~ /linux|ubuntu|debian|centos|rocky|proxmox|xen/i
return :windows if os_str =~ /windows|server|win/i
return :firmware if os_str =~ /firmware|hikvision|dvr|mikrotik|routeros/i
:linux
end
# ─── Regex heurísticas (legacy — para fichas sin META) ─────
def self.detectar_usuario(contenido) def self.detectar_usuario(contenido)
# **SSH**: user@ip:port o DOMAIN\user@ip:port # **SSH**: user@ip:port o DOMAIN\user@ip:port
if contenido =~ /\*\*SSH\*\*:\s*`?([^`\s(]+)/i if contenido =~ /\*\*SSH\*\*:\s*`?([^`\s(]+)/i
@@ -182,3 +262,4 @@ module ADN
end end
end end
end end
+16 -5
View File
@@ -21,7 +21,7 @@ require_relative 'nodos/info'
module ADN module ADN
class SubcomandoSSH class SubcomandoSSH
CANDADOS_PATH = File.join(ADN::PROJECT_ROOT, 'adn', 'tools', 'seguridad', 'candados.rb') CANDADOS_PATH = File.join(ADN::PROJECT_ROOT, 'adn', 'tools', 'candados', 'candados.rb')
def initialize(args, logger) def initialize(args, logger)
@args = args @args = args
@@ -261,17 +261,28 @@ module ADN
if proxy_config if proxy_config
@logger.info("Via ProxyJump #{config[:proxy]} (#{proxy_config[:ip]})") @logger.info("Via ProxyJump #{config[:proxy]} (#{proxy_config[:ip]})")
proxy_passphrase = obtener_secreto(proxy_config[:clave_boveda])
# ProxyCommand con sshpass: el proxy usa passphrase RSA, # Determinar comando ProxyCommand según auth_type del proxy
# el destino usa password. Todo se ejecuta localmente via bash. proxy_cmd = case proxy_config[:auth_type]
when :passphrase
proxy_passphrase = obtener_secreto(proxy_config[:clave_boveda])
"sshpass -P passphrase -p '#{proxy_passphrase}' ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto_ssh]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}"
when :key, :rsa
# Proxy usa clave SSH sin passphrase
"ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto_ssh]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}"
else
# Default: ssh sin passphrase
"ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto_ssh]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}"
end
# ProxyCommand con sshpass: el proxy usa clave/passphrase, el destino usa password
script = <<~BASH script = <<~BASH
export SSHPASS=#{password.shellescape} export SSHPASS=#{password.shellescape}
sshpass -e ssh \ sshpass -e ssh \
-o StrictHostKeyChecking=no \ -o StrictHostKeyChecking=no \
-o ConnectTimeout=30 \ -o ConnectTimeout=30 \
-o ServerAliveInterval=5 \ -o ServerAliveInterval=5 \
-o ProxyCommand="sshpass -P passphrase -p '#{proxy_passphrase}' ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto_ssh]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}" \ -o ProxyCommand="#{proxy_cmd}" \
-p #{config[:puerto_ssh]} \ -p #{config[:puerto_ssh]} \
'#{ssh_target}' \ '#{ssh_target}' \
"#{comando_remoto.gsub('"', '\\"')}" "#{comando_remoto.gsub('"', '\\"')}"
@@ -0,0 +1,368 @@
# A01.P011 — Drones de Operaciones: Actualización Automatizada de OS Linux
> **Estado**: 🟢 Fase 2 en progreso
> **Pertenece a**: [A01 — Ecosistema ADN](./A01_dtic-ADN.md)
> **Relacionado**: [A01.P009 — Evolución Dron ADN](./A01.P009_Evolucion-Dron-ADN.md)
> **Fecha**: 2026-04-13
> **Responsable**: Lic. Ricardo MONLA
> **Versión**: 3.0
---
## 📈 Progreso
```
Fase 1: ██████████ 100% Parche de Emergencia (hotfix en bkps.rb) ✅
Fase 2: ████████░░ 80% Desacoplamiento — `dron ops` + candados ✅
Fase 2.5: ████████░░ 80% Estandarización de Fichas de Nodos 🔧
Fase 3: ░░░░░░░░░░ 0% Robustez — Pre-flight, Smart Lock, Reboot Check
Fase 4: █████░░░░░ 50% Anillos de Despliegue (--ring habilitado) 🔧
Fase 5: ░░░░░░░░░░ 0% Observabilidad — Métricas por Nodo
```
---
## 📋 Resumen Ejecutivo
Este plan define la evolución de la capacidad de **Operaciones de Infraestructura** dentro del ecosistema ADN, comenzando por la actualización automatizada de sistemas operativos Linux (`apt update/upgrade`) mediante AtomicDrones.
**Problema raíz:** La funcionalidad de actualizar nodos Linux fue injertada como un parche rápido dentro de la herramienta de backups (`bkps.rb` / `proc_linux.rb`), violando el principio de responsabilidad única y generando acoplamiento semántico incorrecto. Actualizar un OS **no es un backup**.
**Visión:** Un nuevo subcomando `dron ops` (operaciones) que reutilice la infraestructura existente de ADN (`NodosInfo`, `dron/ejecutor.rb`, Bitácora Web) para orquestar tareas de mantenimiento de infraestructura de forma atómica, visible y desacoplada del sistema de backups.
---
## 🎯 Objetivo
Crear un sistema de **Drones de Operaciones** dentro del ecosistema ADN que permita:
1. Actualizar sistemas operativos Linux de forma automatizada, desatendida y atómica (1 dron = 1 nodo).
2. Registrar cada operación individualmente en la Bitácora Web con trazabilidad completa.
3. Reutilizar las herramientas ADN existentes (`NodosInfo`, `dron/ejecutor.rb`, `Dron::Base`) sin duplicar código.
4. Desacoplar completamente esta funcionalidad del módulo de backups (`bkps.rb`).
---
## 🔍 Hallazgos y Diagnóstico
### Auditoría del Estado Actual (2026-04-12)
| Hallazgo | Severidad | Detalle |
|----------|:---------:|---------|
| **Acoplamiento anómalo** | ✅ Resuelto | `proc_linux.rb` vivía en `bkps/lib/` — ahora existe `dron/ops.rb` independiente |
| **Duplicación de lógica de nodos** | ✅ Resuelto | `ops.rb` reutiliza `NodosInfo.extraer_metadata()` |
| **Orquestación vía bkps** | ✅ Resuelto | Comando nuevo: `dron ops update_os --target linux` |
| **Bloqueo interactivo** | ✅ Resuelto | Corregido con `DEBIAN_FRONTEND=noninteractive` |
| **Falta de candados** | 🟡 Media | Nodos con auth por password ahora usan `candados run` + `sshpass` |
| **Fichas no estandarizadas** | 🔴 Alta | El parser de `NodosInfo` falla en ~40% de fichas por formato libre |
### Hallazgo Crítico: Fichas de Nodos No Estandarizadas (2026-04-13)
> **Impacto:** El módulo `NodosInfo` detecta usuarios SSH incorrectos ("Requiere", "El", "Configurado", "ssh") porque las fichas `.md` de nodos no siguen un formato uniforme. Cada nodo fue documentado con estructura distinta.
| Nodo | Campo SSH en ficha | Usuario detectado | Correcto |
|------|-------------------|:-----------------:|:--------:|
| srvv-dns | `**SSH**: ssh rmonla@10.0.10.2` | `ssh` → corregido a `rmonla` | ✅ (con fallback) |
| srvv-docs | `**Acceso**: Requiere permisos docker` | `Requiere` → corregido a `root` | ⚠️ (fallback) |
| srvv-sitio0 | `**Acceso**: El usuario rmonla...` | `El` → corregido a `root` | ⚠️ (fallback) |
| srvv-nginx-rm | `\| **SSH** \| Puerto 7022, usuario root` | `ssh` → corregido a `root` | ⚠️ (fallback) |
| srvv-nginx-rm | `\| **Bóveda** \| srvv-nginx-rm:root` | No detectado | ❌ falta formato |
**Causa raíz:** Las fichas `.md` son prosa libre sin campos obligatorios estandarizados. El parser necesita ~15 regex heurísticas para cubrir variantes, y aun así falla.
### Código Duplicado Eliminado (Fase 2)
| Función | Antes (proc_linux.rb) | Ahora (ops.rb) |
|---------|:-------------------:|------------------------|
| Detectar si es Linux | `es_nodo_linux?()` 40 líneas | `NodosInfo.detectar_so() == :linux` |
| Extraer IP | `extraer_info_nodo()` regex | `NodosInfo.detectar_ip()` |
| Extraer usuario SSH | regex custom | `NodosInfo.detectar_usuario()` + sanitización |
| Auth por password | No soportado | `candados run` + `sshpass` via `clave_boveda` |
### Inventario de Flota Linux (13 nodos detectados)
| Nodo | IP | Rol | Host |
|------|-----|-----|------|
| srv-dasu | 10.0.10.X | Servidor físico | — |
| srv-pmox1 | 10.0.10.201 | Hipervisor Proxmox | — |
| srv-pmox2 | 10.0.10.202 | Hipervisor Proxmox | — |
| srv-pmox3 | 10.0.10.203 | Hipervisor Proxmox | — |
| srv-xen1 | 10.0.10.X | Hipervisor XenServer (legacy) | — |
| srvv-data | 10.0.10.X | VM — Datos | srv-pmoxN |
| srvv-dns | 10.0.10.2 | VM — DNS CoreDNS | srv-pmox1 |
| srvv-docs | 10.0.10.X | VM — Documentación | srv-pmoxN |
| srvv-dtic | 10.0.10.X | VM — Servicios DTIC | srv-pmoxN |
| srvv-koha | 10.0.10.X | VM — Biblioteca Koha | srv-pmoxN |
| srvv-sitio0 | 10.0.10.X | VM — Sitio principal | srv-pmoxN |
| srvv-sitio2 | 10.0.10.X | VM — Sitio secundario | srv-pmoxN |
| srvv-uptime | 10.0.10.X | VM — Monitoreo | srv-pmoxN |
---
## 🗺️ Fases de Implementación
### Fase 1: Parche de Emergencia (Hotfix en bkps.rb) — ✅ COMPLETA
**Objetivo:** Lograr que los drones de actualización Linux funcionen mínimamente, aunque acoplados al módulo de backups.
| ID | Tarea | Estado |
|:--:|-------|:------:|
| F1.T1 | Sustituir `apt` por `apt-get` con `DEBIAN_FRONTEND=noninteractive` | ✅ |
| F1.T2 | Agregar `--force-confdef --force-confold` a DPKG | ✅ |
| F1.T3 | Registrar tipo `linux` en `TIPOS` de `bkps.rb` | ✅ |
| F1.T4 | Corregir `ejecutar_uno()` para leer ficha `.md` del nodo individual | ✅ |
| F1.T5 | Instalar gema `net-ssh` en entorno de usuario | ✅ |
| F1.T6 | Corregir verbo en nota de Bitácora: `🐧 Actualizar` | ✅ |
**Criterio de éxito:**`./adn/tools/run bkps orquestar update_linux_nodes --on-error continue` ejecuta 13 drones individuales visibles en Bitácora Web.
```
Fase 1: ██████████ 100% ✅
```
---
### Fase 2: Desacoplamiento — Nuevo CLI `dron ops` — 🔧 EN PROGRESO
**Objetivo:** Extraer la lógica de operaciones de infraestructura fuera de `bkps.rb` y crear un subcomando propio dentro del sistema de Drones ADN.
**Principio:** Reutilizar, no reimplementar. El 80% del código necesario ya existe en ADN Core.
| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|:------:|
| F2.T1 | Crear `adn/tools/cli/dron/ops.rb` | Módulo de operaciones de infraestructura | ✅ |
| F2.T2 | Integrar `NodosInfo` como scanner | Filtrado por OS, estado, validación de usuario SSH | ✅ |
| F2.T3 | Conectar con `dron/ejecutor.rb` | Cada nodo = 1 dron ejecutor estándar (auto-bitácora) | ✅ |
| F2.T4 | Registrar en `dispatcher.rb` | Case `'ops'``OpsComando.ejecutar(args)` + ayuda CLI | ✅ |
| F2.T5 | Integrar `candados` para auth password | Nodos con `clave_boveda` usan `candados run` + `sshpass` | ✅ |
| F2.T6 | Deprecar `proc_linux.rb` de bkps | Marcar como legacy, redirigir a nuevo módulo | ⏳ |
| F2.T7 | Eliminar `update_linux_nodes` de `bkps.yml` | Limpieza de la configuración de backups | ⏳ |
**Arquitectura implementada:**
```
┌─────────────────────────────────────────────────────────────────┐
│ ./adn/tools/run dron ops update_os --target linux │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Dron::Ops (cli/dron/ops.rb) │
│ │
│ 1. Scanner: NodosInfo.extraer_metadata(nodo) para cada *.md │
│ → filtra por OS explícito (Debian/Ubuntu/Proxmox) │
│ → excluye Windows, firmware, cámaras │
│ → excluye nodos con estado 🔴 │
│ → sanitiza usuario SSH (lista negra + fallback user@IP) │
│ │
│ 2. Por cada nodo Linux: │
│ → Si tiene clave_boveda: │
│ candados run <clave> 'sshpass -p $PASS ssh ...' │
│ → Si no: │
│ ssh -i <key> user@ip 'apt-get update && upgrade' │
│ → Dron::Ejecutor.lanzar(cmd, nota) → Bitácora Web │
└─────────────────────────────────────────────────────────────────┘
```
**Uso actual (funcional):**
```bash
# Listar nodos Linux con tipo de auth (dry-run)
./adn/tools/run dron ops update_os --target linux --dry-run
# Ejecutar con continuación ante fallos
./adn/tools/run dron ops update_os --target linux --on-error continue
# Ejecutar solo un nodo específico
./adn/tools/run dron ops update_os --nodo srvv-dns
# Ayuda completa
./adn/tools/run dron ops --help
```
**Criterio de éxito:** ⚠️ Parcial. El comando funciona y los drones son visibles en Bitácora. Pendiente deprecar `proc_linux.rb` y limpiar `bkps.yml` una vez estandarizadas las fichas.
```
Fase 2: ████████░░ 80%
```
---
### Fase 2.5: Estandarización de Fichas de Nodos — 🔧 EN PROGRESO
**Objetivo:** Definir un formato canónico obligatorio para las fichas `nodos/*.md` que elimine la ambigüedad del parser `NodosInfo` y habilite operaciones confiables de toda herramienta ADN.
| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|:------:|
| F2.5.T1 | Definir formato canónico | Tabla `<!-- META:BEGIN -->` / `<!-- META:END -->` con campos fijos | ✅ |
| F2.5.T2 | Migrar fichas existentes | 32/32 fichas migradas (14 Linux + 2 otros + 16 Windows) | ✅ |
| F2.5.T3 | Actualizar `NodosInfo` | Parser dual: tabla canónica primero, regex fallback | ✅ |
| F2.5.T4 | Simplificar `ops.rb` | Eliminadas ~30 líneas de sanitización y regex de contenido | ✅ |
| F2.5.T5 | Validador de fichas | `./adn/tools/run nodos validar` → reporta fichas no conformes | ⏳ |
| F2.5.T6 | Actualizar `generar nodo` | Que la plantilla del generador use el formato canónico | ⏳ |
**Formato canónico implementado:**
```markdown
# Nodo: <hostname>
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `<hostname>` |
| **IP** | `<ip>` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Descripción breve |
| **SSH** | `user@ip:port` |
| **Auth** | `key` / `password` / `rsa_legacy` |
| **Bóveda** | `clave:candados` (solo si auth=password) |
| **Host** | `host-anfitrión` (solo VMs) |
| **VMID** | `123` (solo VMs) |
| **Ring** | `test` / `standard` / `core` |
<!-- META:END -->
```
**Resultados:**
- 32/32 fichas migradas con tabla canónica
- `NodosInfo` lee tabla canónica en 1 regex (vs ~15 antes)
- `ops.rb` simplificado: de ~65 líneas de scanner a ~20
- Nodos Linux detectados: 14 → 17 (se descubrieron `dtic-bitacoras`, `srv-ns8`, `srvv-sitio`)
- `--ring test|standard|core` habilitado en `dron ops` (Fase 4 parcial)
```
Fase 2.5: ████████░░ 80%
```
---
### Fase 3: Robustez — Pre-flight, Smart Lock, Reboot Check
**Objetivo:** Hacer que cada dron de actualización sea inteligente: que verifique condiciones previas, detecte errores conocidos y reporte el estado post-actualización.
| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|:------:|
| F3.T1 | Pre-flight: espacio en disco | Abortar si `/` o `/var` están al >95% | ⏳ |
| F3.T2 | Pre-flight: estado del nodo | Saltar nodos con `**Estado**: 🔴` | ⏳ |
| F3.T3 | Smart Lock detection | Detectar `Could not get lock /var/lib/dpkg/lock` y reportar `❌ dpkg bloqueado` | ⏳ |
| F3.T4 | Reboot Required check | Post-upgrade: `[ -f /var/run/reboot-required ]``⚠️ Reboot needed` | ⏳ |
| F3.T5 | Resumen de paquetes actualizados | Capturar cantidad de paquetes actualizados del output de `apt-get` | ⏳ |
**Flujo enriquecido por nodo:**
```
🐧 Dron update_os para srvv-dns
├── 🔍 Pre-flight checks
│ ├── Disco /: 61% ✔
│ ├── Disco /var: OK ✔
│ └── Estado: 🟢 ✔
├── [1/3] apt-get update ✔
├── [2/3] apt-get upgrade -y ✔ (4 paquetes)
├── [3/3] apt-get autoremove -y ✔
├── 🔍 Post-flight checks
│ └── reboot-required: NO ✔
└── ✅ srvv-dns actualizado (47s)
```
**Criterio de éxito:** Cero drones con errores silenciosos. Cada falencia conocida tiene su propio código de diagnóstico en la nota de Bitácora.
```
Fase 3: ░░░░░░░░░░ 0%
```
---
### Fase 4: Anillos de Despliegue (Rings)
**Objetivo:** No actualizar toda la flota de golpe. Dividir los nodos en anillos de criticidad para evitar caídas simultáneas de servicios dependientes.
| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|:------:|
| F4.T1 | Definir anillos en `nodos/*.md` | Nuevo campo `**Ring**: test|standard|core` | ⏳ |
| F4.T2 | Ring 0 — Test | Nodos de bajo impacto: srvv-uptime, srvv-sitio2 | ⏳ |
| F4.T3 | Ring 1 — Standard | VMs de servicios: srvv-dns, srvv-docs, srvv-koha | ⏳ |
| F4.T4 | Ring 2 — Core | Hipervisores y data: srv-pmox1/2/3, srvv-data | ⏳ |
| F4.T5 | Pausa entre anillos | Esperar confirmación o timeout entre rings | ⏳ |
**Uso propuesto:**
```bash
# Solo nodos de testing
./adn/tools/run dron ops update_os --ring test
# Despliegue completo con pausa entre anillos
./adn/tools/run dron ops update_os --rings all --pause 300
```
**Criterio de éxito:** Nunca se actualizan los 3 hipervisores Proxmox al mismo tiempo. Los nodos de servicio crítico se actualizan después de validar en el ring de test.
```
Fase 4: ░░░░░░░░░░ 0%
```
---
### Fase 5: Observabilidad — Métricas por Nodo
**Objetivo:** Historial de actualizaciones por nodo visible en la Bitácora Web.
| ID | Tarea | Descripción | Estado |
|:--:|-------|-------------|:------:|
| F5.T1 | Registro de versión de OS | Capturar `lsb_release -d` pre y post-update | ⏳ |
| F5.T2 | Historial de updates por nodo | Consulta: "¿Cuándo fue la última actualización de srvv-dns?" | ⏳ |
| F5.T3 | Alerta de nodos atrasados | `dron ops status` → nodos sin update en >30 días | ⏳ |
| F5.T4 | Dashboard de salud de flota OS | Vista consolidada en Bitácora Web | ⏳ |
**Criterio de éxito:** Con un solo comando se puede ver el estado de actualización de toda la flota Linux, detectando nodos atrasados.
```
Fase 5: ░░░░░░░░░░ 0%
```
---
## 📊 Conclusiones
### Lo que funciona hoy (Fases 1 + 2 + 2.5)
- **17 nodos Linux** detectados correctamente (incluidos 3 Proxmox, 1 XenServer, srv-ns8).
- **Desacoplamiento logrado:** `dron ops update_os` es subcomando propio del sistema de Drones ADN.
- **Candados integrado:** `srvv-nginx-rm` y futuros nodos con password usan la bóveda.
- **Fichas estandarizadas:** 32/32 con tabla `META:BEGIN` / `META:END`.
- **Rings habilitados:** `--ring test|standard|core` filtra la flota.
- **Dry-run informativo:** Muestra nodo, IP, usuario, auth type y ring.
### Desbloqueado por la estandarización
- **Parser simplificado:** `NodosInfo` lee tabla canónica con 1 regex (antes ~15).
- **Scanner limpio:** `ops.rb` pasó de ~65 líneas de workarounds a ~20 líneas claras.
- **Nodos descubiertos:** 3 nodos Linux que antes no se detectaban (`dtic-bitacoras`, `srv-ns8`, `srvv-sitio`).
- **Cero falsos positivos:** Cámaras IP, firmware y Windows correctamente excluidos.
### Métricas de referencia
| Métrica | Antes | Ahora |
|---------|:-----:|:-----:|
| Nodos Linux detectados | 14 | **17** |
| Nodos Windows (excluidos) | 15 | 14 |
| Total fichas en `nodos/` | 32 | 32 |
| Fichas con tabla canónica | 0 | **32** (100%) |
| Fichas con parseo regex (fallback) | 32 | **0** |
| Regex en NodosInfo (operativas) | ~15 | **1** (canónica) |
| Nodos con auth `key` | 13 | **16** |
| Nodos con auth `candados` | 0 | **1** (srvv-nginx-rm) |
| Líneas de scanner en `ops.rb` | ~65 | **~20** |
| Falsos positivos en dry-run | ~2 | **0** |
---
## 📎 Referencias
- [A01.P009 — Evolución Dron ADN](./A01.P009_Evolucion-Dron-ADN.md) — Plan maestro de AtomicDrones
- `adn/tools/cli/dron/ops.rb`**Módulo nuevo de operaciones** (Fase 2, activo)
- `adn/tools/cli/dron/dispatcher.rb` — Dispatcher con `ops` registrado
- `adn/tools/cli/dron/ejecutor.rb` — Ejecutor atómico con auto-bitácora
- `adn/tools/cli/nodos/info.rb` — Módulo `NodosInfo` (scanner de fichas)
- `adn/tools/candados/candados.rb` — Bóveda de credenciales
- `adn/tools/bkps/lib/proc_linux.rb` — Implementación legacy (Fase 1, a deprecar)
---
*Plan actualizado 2026-04-13 — Versión 3.0*
@@ -0,0 +1,640 @@
# A01.P012 — Pipeline de Drones para Backup y Restauración de DASUTEN
> **Estado**: 🟢 Pipeline implementado (2026-04-14)
> **Pertenece a**: [A01 — Ecosistema ADN](./A01_dtic-ADN.md)
> **Relacionado**: [A01.P009 — Evolución Dron ADN](./A01.P009_Evolucion-Dron-ADN.md)
> **Caso de Uso**: [A04.P005 — DASUTEN sin DC](../dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md)
> **Fecha**: 2026-04-14
> **Responsable**: Lic. Ricardo MONLA
> **Versión**: 2.0 — Con verificación de consistencia (DBCC CHECKDB) de BD
---
## 📈 Progreso
```
Fase 0: ██████████ 100% Diseño de Arquitectura de Pipeline ✅
Fase 1: ██████████ 100% Drones Atómicos Fragmentados ✅
Fase 1.5: ██████████ 100% Orquestador con Intercomunicación JSON ✅
Fase 1.7: ██████████ 100% Implementación Real — Pipeline DASUTEN ✅ (2026-04-14)
Fase 1.8: ██████████ 100% Verificación de Consistencia (DBCC CHECKDB) ✅
Fase 1.9: ██████████ 100% Pipeline Diferencial (Refresco diario) ✅
Fase 2: ░░░░░░░░░░ 0% Persistencia en Bitácora — Outputs en DB
Fase 3: ░░░░░░░░░░ 0% Reintentos Automáticos — Retry con backoff
Fase 4: ░░░░░░░░░░ 0% Condicionales — Skip si output indica "ya hecho"
```
---
## 📋 Resumen Ejecutivo
Este plan documenta el **Pipeline de Drones para Backup y Restauración de DASUTEN** — una implementación real donde múltiples drones atómicos se encadenan para formar el flujo de refresco de base de datos (Fase 8b), comunicándose entre sí mediante **archivos JSON** que actúan como medio de intercambio de contexto.
**Caso de uso:** Refresco de la base de datos `sysdasuten` desde `srvv-fenix` (producción) hasta `dasu-sql4` (nuevo servidor), pasando por Google Drive como intermediario.
**Problema resuelto:** Transferir 1.3 GB de backup entre servidores sin dependencia de Tailscale inestable, usando Google Drive como puente y verificando integridad con DBCC CHECKDB.
**Solución:** Un **orquestador** que:
1. Lanza 6 drones atómicos secuencialmente
2. Lee el output JSON de cada dron completado
3. Inyecta el contexto en el siguiente dron
4. Decide continuar o abortar según el resultado
### Convención de Nombres (desde 2026-04-14)
```
dasuten_<verbo>_<origen-destino>.rb
Ejemplos:
- dasuten_exportar_srvv-fenix.rb (acción en nodo específico)
- dasuten_transferir_srvv-fenix-srv-ns8.rb (transferencia entre nodos)
- dasuten_upload_srv-ns8-drive.rb (upload desde nodo a Drive)
```
---
## 🎯 Objetivo
Implementar un **Pipeline de Drones para el refresco de BD de DASUTEN** que permita:
1. **Fragmentar el flujo de backup** en 6 drones atómicos independientes (1 dron = 1 paso)
2. **Intercomunicar drones** mediante archivos JSON estandarizados (`/tmp/dron_*_output.json`)
3. **Orquestar el refresco completo** con un solo comando desde `srv-ns8`
4. **Registrar trazabilidad** de cada paso en la Bitácora Web
5. **Permitir re-ejecución** de pasos individuales sin re-correr todo el pipeline
6. **Verificar integridad** de la BD restaurada con `DBCC CHECKDB`
---
## 🏗️ Arquitectura del Pipeline
### Diagrama de Flujo (6 Drones Atómicos)
```
┌──────────────────────────────────────────────────────────────────────────┐
│ ORQUESTADOR (orquestador_pipeline.rb) │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Contexto Compartido (Hash Ruby) │ │
│ │ - backup_ruta: ruta del archivo .bak │ │
│ │ - drive_ruta: URL de Google Drive │ │
│ │ - estado_integridad: resultado de DBCC CHECKDB │ │
│ │ - duracion_total: suma de duraciones de drones │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
│ 1. Lanza dron
┌─────────────────┐
│ DRON: │ Nodo: srvv-fenix
│ exportar │ Tarea: BACKUP DATABASE WITH COMPRESSION
│ │ Output: /tmp/dron_export_output.json
└─────────────────┘
│ 2. Orquestador lee JSON, extrae backup_ruta
┌─────────────────┐
│ DRON: │ Nodo: srv-ns8
│ transferir │ Tarea: smbclient //fenix/BK_SQL → /var/tmp/
│ │ Output: /tmp/dron_transfer_output.json
└─────────────────┘
│ 3. Orquestador lee JSON, extrae backup_local
┌─────────────────┐
│ DRON: │ Nodo: srv-ns8
│ upload │ Tarea: rclone copy → Google Drive
│ │ Output: /tmp/dron_upload_output.json
└─────────────────┘
│ 4. Orquestador lee JSON, extrae drive_ruta
┌─────────────────┐
│ DRON: │ Nodo: dasu-sql4
│ download │ Tarea: Invoke-WebRequest ← Google Drive
│ │ Output: /tmp/dron_download_output.json
└─────────────────┘
│ 5. Orquestador lee JSON, extrae backup_local
┌─────────────────┐
│ DRON: │ Nodo: dasu-sql4
│ restaurar │ Tarea: RESTORE DATABASE (sin CHECKDB)
│ │ Output: /tmp/dron_restore_output.json
└─────────────────┘
│ 6. Orquestador lee JSON, decide verificar
┌─────────────────┐
│ DRON: │ Nodo: dasu-sql4
│ verificar │ Tarea: DBCC CHECKDB + validaciones
│ │ Output: /tmp/dron_verify_output.json
└─────────────────┘
✅ PIPELINE COMPLETADO (6 drones)
```
---
## 🧩 Drones Atómicos del Pipeline (6 drones)
Cada dron es **independiente**, **autocontenido** y **auto-bitácorado**.
### Dron 1: `dasuten_exportar_srvv-fenix.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `srvv-fenix` |
| **Tarea** | Generar backup comprimido en SQL Server |
| **Comando** | `BACKUP DATABASE sysdasuten TO DISK = ... WITH COMPRESSION` |
| **Output File** | `/tmp/dron_export_output.json` |
| **Output Clave** | `backup_ruta` |
**Output JSON:**
```json
{
"paso": "export_complete",
"backup_ruta": "E:\\BK_SQL\\sysdasuten_compressed_ADN.bak",
"nodo": "srvv-fenix",
"duracion": 45.2,
"timestamp": "2026-04-14T10:00:00-03:00",
"siguiente_paso": "transferir"
}
```
---
### Dron 2: `dasuten_transferir_srvv-fenix-srv-ns8.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `srv-ns8` |
| **Tarea** | Descargar backup desde srvv-fenix vía SMB |
| **Comando** | `smbclient //10.0.10.200/BK_SQL -c "get sysdasuten_compressed_ADN.bak"` |
| **Output File** | `/tmp/dron_transfer_output.json` |
| **Output Clave** | `backup_local` |
**Output JSON:**
```json
{
"paso": "transfer_complete",
"backup_local": "/var/tmp/sysdasuten_compressed_ADN.bak",
"nodo": "srv-ns8",
"duracion": 120.5,
"timestamp": "2026-04-14T10:02:00-03:00",
"siguiente_paso": "upload"
}
```
---
### Dron 3: `dasuten_upload_srv-ns8-drive.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `srv-ns8` |
| **Tarea** | Subir backup a Google Drive vía rclone |
| **Comando** | `rclone copy /var/tmp/... rmonla-GDrive:drive_bkps-dasu/...` |
| **Output File** | `/tmp/dron_upload_output.json` |
| **Output Clave** | `drive_ruta` |
**Output JSON:**
```json
{
"paso": "upload_complete",
"drive_ruta": "rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_100500.bak",
"drive_url": "https://drive.google.com/drive/folders/1Tgrn2QYyWf0v0eyY1bSGCLlhiqhtIA2n",
"nodo": "srv-ns8",
"duracion": 180.5,
"timestamp": "2026-04-14T10:05:00-03:00",
"siguiente_paso": "download"
}
```
---
### Dron 4: `dasuten_download_drive-dasu-sql4.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `dasu-sql4` |
| **Tarea** | Descargar backup desde Google Drive vía HTTP |
| **Comando** | `Invoke-WebRequest -Uri "https://drive.usercontent.google.com/download?id=..."` |
| **Output File** | `/tmp/dron_download_output.json` |
| **Output Clave** | `backup_local` |
**Output JSON:**
```json
{
"paso": "download_complete",
"backup_local": "F:\\BACKUP\\sysdasuten_compressed_ADN.bak",
"drive_origen": "rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_100500.bak",
"nodo": "dasu-sql4",
"duracion": 150.3,
"timestamp": "2026-04-14T10:08:00-03:00",
"siguiente_paso": "restaurar_backup"
}
```
---
### Dron 5: `dasuten_restaurar_dasu-sql4.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `dasu-sql4` |
| **Tarea** | Restaurar backup (solo RESTORE, sin CHECKDB) |
| **Comando** | `RESTORE DATABASE ... WITH REPLACE, MOVE ...` |
| **Output File** | `/tmp/dron_restore_output.json` |
| **Output Clave** | `estado_integridad=PENDING_CHECKDB` |
**Output JSON:**
```json
{
"paso": "restore_complete",
"backup_ruta": "F:\\BACKUP\\sysdasuten_compressed_ADN.bak",
"nodo": "dasu-sql4",
"duracion_restore": 0.23,
"estado_integridad": "PENDING_CHECKDB",
"timestamp": "2026-04-14T10:10:00-03:00",
"siguiente_paso": "verificar_integridad"
}
```
---
### Dron 6: `dasuten_verificar-integridad_dasu-sql4.rb`
| Campo | Valor |
|-------|-------|
| **Nodo** | `dasu-sql4` |
| **Tarea** | Verificar integridad con DBCC CHECKDB |
| **Comando** | `DBCC CHECKDB('sysdasuten') WITH NO_INFOMSGS, ALL_ERRORMSGS` |
| **Output File** | `/tmp/dron_verify_output.json` |
| **Output Clave** | `estado_integridad`, `pipeline_completo` |
**Output JSON:**
```json
{
"paso": "verify_complete",
"nodo": "dasu-sql4",
"duracion_checkdb": 248.56,
"estado_integridad": "OK",
"paginas_verificadas": 1186793,
"timestamp": "2026-04-14T10:14:00-03:00",
"pipeline_completo": true
}
```
---
## 🎼 Orquestador: `orquestador_pipeline.rb`
El orquestador es el **cerebro** del pipeline. No ejecuta tareas directamente, sino que:
1. **Lanza drones** secuencialmente vía `dron lanzar`
2. **Lee outputs JSON** de cada dron completado
3. **Inyecta contexto** en el siguiente dron (vía ENV o argumentos)
4. **Decide continuar** o abortar según exito/fracaso
5. **Registra estado** en la Bitácora Web
### Código del Orquestador (simplificado)
```ruby
#!/usr/bin/env ruby
# adn/tools/cli/drones/orquestador_pipeline.rb
ESTADOS = {
exportar: {
script: 'dasuten_exportar.rb',
nodo: 'srvv-fenix',
nota: '📤 Exportar backup DASUTEN',
output_file: '/tmp/dron_export_output.json',
siguiente: :upload
},
upload: {
script: 'dasuten_upload_drive.rb',
nodo: 'srv-ns8',
nota: '☁️ Upload a Google Drive',
output_file: '/tmp/dron_upload_output.json',
siguiente: :download
},
# ... más pasos
}.freeze
def lanzar_dron(script, nodo, nota, timeout)
cmd = "#{ADN_RUN} dron lanzar --nota #{nota} --nodo #{nodo} --timeout #{timeout} -- ruby #{script}"
exito = system(cmd)
if exito
# Leer output del dron
output_file = ESTADOS[nodo.to_sym][:output_file]
resultado = JSON.parse(File.read(output_file)) if File.exist?(output_file)
return true, resultado
else
return false, {}
end
end
# Ejecutar pipeline
paso_actual = :exportar
contexto = {}
loop do
break unless ESTADOS[paso_actual]
config = ESTADOS[paso_actual]
exito, resultado = lanzar_dron(config[:script], config[:nodo], config[:nota], timeout)
unless exito
puts "❌ Pipeline fallido en paso: #{paso_actual}"
exit 1
end
contexto.merge!(resultado)
if config[:siguiente]
paso_actual = config[:siguiente]
else
puts "✅ Pipeline completado!"
break
end
end
```
---
## 🚀 Uso del Pipeline
### Ejecutar Pipeline Completo
```bash
# Pipeline completo (todos los pasos encadenados)
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN 8b" -- \
ruby adn/tools/cli/drones/orquestador_pipeline.rb
```
### Ejecutar Drones Individualmente
```bash
# Paso 1: Exportar backup
./adn/tools/run dron lanzar --nota "Exportar backup" --nodo srvv-fenix -- \
ruby adn/tools/cli/drones/dasuten_exportar.rb
# Paso 2: Upload a Drive
./adn/tools/run dron lanzar --nota "Upload Drive" --nodo srv-ns8 -- \
ruby adn/tools/cli/drones/dasuten_upload_drive.rb
# Paso 3: Download desde Drive
./adn/tools/run dron lanzar --nota "Download Drive" --nodo dasu-sql4 -- \
ruby adn/tools/cli/drones/dasuten_download_drive.rb
# Paso 4: Restaurar backup
./adn/tools/run dron lanzar --nota "Restaurar backup" --nodo dasu-sql4 -- \
ruby adn/tools/cli/drones/dasuten_restaurar.rb
```
### Ejecutar desde un Paso Específico
```bash
# Comenzar desde upload (saltear export)
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN" -- \
ruby adn/tools/cli/drones/orquestador_pipeline.rb --paso upload
```
### Dry-Run (Simulación)
```bash
# Ver qué haría sin ejecutar
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN" -- \
ruby adn/tools/cli/drones/orquestador_pipeline.rb --dry-run
```
---
## 📊 Ventajas de esta Arquitectura
| Ventaja | Descripción |
|---------|-------------|
| **🧩 Atomicidad** | Cada dron hace UNA cosa. Fácil de testear, debuggear, reemplazar. |
| **🔗 Intercomunicación** | JSON como contrato entre drones. Lenguaje-agnóstico. |
| **🔄 Re-ejecución** | Si falla el paso 3, re-ejecutar solo paso 3 (no todo el pipeline). |
| **👁️ Observabilidad** | Cada dron se registra individualmente en Bitácora Web. |
| **🛡️ Tolerancia a Fallos** | Si un dron falla, el pipeline se detiene (fail-fast). |
| **📈 Escalabilidad** | Agregar nuevos pasos = agregar nuevo dron + registrar en ESTADOS. |
---
## 📁 Estructura de Archivos
```
adn/tools/cli/drones/
├── orquestador_pipeline.rb # Orquestador principal
├── dasuten_exportar.rb # Dron 1: Exportar backup
├── dasuten_upload_drive.rb # Dron 2: Upload a Drive
├── dasuten_download_drive.rb # Dron 3: Download desde Drive
└── dasuten_restaurar.rb # Dron 4: Restaurar backup
/tmp/
├── dron_export_output.json # Output del dron 1
├── dron_upload_output.json # Output del dron 2
├── dron_download_output.json # Output del dron 3
└── dron_restore_output.json # Output del dron 4
```
---
## 🔮 Futuras Mejoras (Fases Pendientes)
### Fase 2: Persistencia en Bitácora
Actualmente los outputs JSON son efímeros (`/tmp/`). La **Fase 2** persistirá los outputs en la Bitácora Web:
```ruby
# En dron_db.rb
CREATE TABLE drone_outputs (
id SERIAL PRIMARY KEY,
dron_id VARCHAR(50) REFERENCES drones(dron_id),
output JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- El orquestador puede leer outputs de la DB en lugar de archivos
output = DronDB.obtener_output(dron_id)
```
### Fase 3: Reintentos Automáticos
```ruby
# Orquestador con retry
def lanzar_dron_con_retry(script, nodo, nota, timeout, max_retries: 3)
max_retries.times do |intento|
exito, resultado = lanzar_dron(script, nodo, nota, timeout)
return true, resultado if exito
puts "⚠ Dron falló (intento #{intento + 1}/#{max_retries})"
sleep(2 ** intento) # Backoff exponencial
end
return false, {}
end
```
### Fase 4: Condicionales de Skip
```ruby
# Skip si ya está hecho
def deberia_ejecutar?(paso, contexto)
case paso
when :upload
# Skip si ya hay backup reciente en Drive
!backup_reciente_en_drive?(contexto[:backup_ruta])
when :restaurar
# Skip si BD ya está actualizada
!bd_actualizada?(contexto[:backup_local])
else
true
end
end
```
---
## 📝 Lecciones Aprendidas
### ✅ Lo que Funcionó
| Lección | Descripción |
|---------|-------------|
| **JSON como contrato** | Simple, legible, lenguaje-agnóstico. Funciona mejor que DB compartida. |
| **Outputs en /tmp** | Fácil de limpiar, no requiere permisos especiales. |
| **Orquestador stateless** | El estado está en los JSONs, no en el orquestador. |
| **Drones auto-bitácora** | Cada dron se registra individualmente en la Bitácora Web. |
| **Google Drive como intermediario** | Elimina dependencia de Tailscale inestable. Download HTTP directo desde dasu-sql4. |
| **Base64 encoding para PowerShell** | Evita problemas de escaping UTF-8 al transportar scripts sobre SSH. |
| **sshpass + ProxyCommand** | Patrón efectivo para acceder a VMs detrás de relay (dasu-sql4 detrás de srv-dasu). |
### 📊 Resultados del Pipeline DASUTEN (2026-04-14)
| Métrica | Valor |
|---------|-------|
| **Archivo transferido** | 1.3 GB (`sysdasuten_compressed_ADN.bak`) |
| **Tiempo total de pipeline** | ~35 minutos |
| **Upload a Drive** | 29 minutos (1736s) — limitado por ancho de banda |
| **Download HTTP** | ~5 minutos — con confirmación de virus manejada |
| **Restore SQL** | 0.23 segundos — MOVE instantáneo |
| **DBCC CHECKDB** | 248.56 segundos — **Integridad: OK** |
| **Throughput efectivo** | ~650 KB/s (limitado por subida a Drive) |
### ⚠️ Lo que Requiere Atención
| Lección | Descripción |
|---------|-------------|
| **Limpieza de archivos** | Los JSONs en `/tmp/` deben limpiarse post-pipeline. |
| **Timeouts por dron** | Cada dron debe tener timeout individual (no todos duran lo mismo). |
| **Manejo de errores** | El orquestador debe capturar stderr de cada dron para debugging. |
### 🔧 Desafíos Superados
| Desafío | Solución |
|---------|----------|
| **Virus confirmation page** | Parseo de HTML con regex PowerShell para extraer `id`, `uuid`, `confirm` |
| **dasu-sql4 sleep mode** | Acceso vía relay SSH desde srv-dasu con `sshpass -e` + ProxyCommand |
| **PowerShell escaping** | Base64 encoding del script completo + heredoc con `'PSCMD'` (sin interpolación) |
| **sqlcmd no disponible en PATH** | Usar ruta completa: `C:\Program Files\...\sqlcmd.exe` |
---
## 🔗 Referencias
- **Ámbito**: [A01_dtic-ADN.md](./A01_dtic-ADN.md)
- **Evolución Dron**: [A01.P009_Evolucion-Dron-ADN.md](./A01.P009_Evolucion-Dron-ADN.md)
- **Drones Ops**: [A01.P011_Drones-Operaciones-Infra.md](./A01.P011_Drones-Operaciones-Infra.md)
- **Caso de Uso**: [A04.P005_DASUTEN-sin-DC.md](../dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md) — Pipeline implementado para Fase 8b
---
## 📊 Estado del Pipeline DASUTEN (6 drones atómicos)
| Paso | Estado | Dron | Nodo | Output | Duración |
|------|--------|------|------|--------|----------|
| **8b.1** | ✅ Completado | `dasuten_exportar_srvv-fenix.rb` | srvv-fenix | `backup_ruta` | 44.8s |
| **8b.2** | ✅ Completado | `dasuten_transferir_srvv-fenix-srv-ns8.rb` | srv-ns8 | `backup_local` | ~2 min |
| **8b.3** | ✅ Completado | `dasuten_upload_srv-ns8-drive.rb` | srv-ns8 | `drive_ruta` | 1736s (29 min) |
| **8b.4** | ✅ Completado | `dasuten_download_drive-dasu-sql4.rb` | dasu-sql4 | `backup_local` | ~5 min |
| **8b.5** | ✅ Completado | `dasuten_restaurar_dasu-sql4.rb` | dasu-sql4 | `estado_integridad=PENDING` | 0.23s |
| **8b.6** | ✅ Completado | `dasuten_verificar-integridad_dasu-sql4.rb` | dasu-sql4 | `estado_integridad=OK` | 248.56s |
**Pipeline completado exitosamente el 2026-04-14 a las 17:21.**
### Resultados Finales
- **Backup comprimido:** 1.3 GB (`sysdasuten_compressed_ADN.bak`)
- **Google Drive URL:** `rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_*.bak`
- **Download HTTP directo:** Con confirmación de virus manejada (formulario HTML parsing)
- **Restore:** 0.23 segundos (MOVE a F:\DATA y F:\LOG)
- **DBCC CHECKDB:** 248.56 segundos — **Integridad: OK** (1,186,793 páginas verificadas, sin errores)
- **Duración total del pipeline:** ~35 minutos
---
## 🔄 Pipeline Diferencial (Refresco Diario)
Para refrescos diarios de la base de datos, se recomienda usar **backup diferencial** que solo transfiere los cambios desde el último backup completo.
### Ventajas del Pipeline Diferencial
| Métrica | Completo | Diferencial |
|---------|----------|-------------|
| **Tamaño backup** | 1.3 GB | ~50-100 MB* |
| **Tiempo export** | 45 seg | ~5-10 seg |
| **Tiempo upload** | 29 min | ~2-3 min |
| **Tiempo download** | ~5 min | ~30 seg |
| **Tiempo total** | ~35 min | ~5-10 min |
\* Depende de la cantidad de cambios diarios
### Drones del Pipeline Diferencial
| Paso | Dron | Nodo | Tarea |
|------|------|------|-------|
| **1** | `dasuten_exportar_diferencial_srvv-fenix.rb` | srvv-fenix | `BACKUP DATABASE ... WITH DIFFERENTIAL` |
| **2** | `dasuten_transferir_srvv-fenix-srv-ns8.rb` | srv-ns8 | SMB → `/var/tmp/` |
| **3** | `dasuten_upload_srv-ns8-drive.rb` | srv-ns8 | rclone → Google Drive |
| **4** | `dasuten_download_drive-dasu-sql4.rb` | dasu-sql4 | HTTP ← Google Drive |
| **5** | `dasuten_restaurar_diferencial_dasu-sql4.rb` | dasu-sql4 | `RESTORE ... WITH DIFFERENTIAL` + `RECOVERY` |
| **6** | `dasuten_verificar-integridad_dasu-sql4.rb` | dasu-sql4 | `DBCC CHECKDB` |
### Orquestador Diferencial
```bash
# Ejecutar pipeline diferencial completo
./adn/tools/run dron lanzar --nota "Refresco Diario DASUTEN" -- \
ruby adn/tools/cli/drones/orquestador_pipeline_diferencial.rb
```
### Estrategia Recomendada
| Día | Tipo de Backup | Duración Estimada |
|-----|----------------|-------------------|
| **Lunes** | Completo | ~35 min |
| **Martes** | Diferencial | ~5-10 min |
| **Miércoles** | Diferencial | ~5-10 min |
| **Jueves** | Diferencial | ~5-10 min |
| **Viernes** | Diferencial | ~5-10 min |
| **Sábado** | Diferencial | ~5-10 min |
| **Domingo** | — (sin cambios) | — |
### Consideraciones Importantes
1. **El backup diferencial se basa en el último backup completo**, no en el diferencial anterior
2. **Cada diferencial es acumulativo** — el martes tiene cambios desde lunes, el miércoles tiene cambios desde lunes (no desde martes)
3. **Restaurar diferencial requiere:**
- Último backup completo (ya restaurado en dasu-sql4)
- Backup diferencial más reciente
- `WITH NORECOVERY` para aplicar diferencial, luego `WITH RECOVERY` para poner BD online
---
*Documento creado: 2026-04-14*
*Última actualización: 2026-04-14*
*Versión: 2.1 — Con pipeline diferencial*
@@ -5,8 +5,8 @@
**Código:** A04.P005 **Código:** A04.P005
**Fecha:** 27 de marzo de 2026 **Fecha:** 27 de marzo de 2026
**Autor:** Sistema ADN **Autor:** Sistema ADN
**Versión:** 4.0 **Versión:** 5.3
**Estado:** 🚧 EN EJECUCIÓN (F5 completada — BD restaurada, pendiente validación con PC cliente) **Estado:** 🚧 EN EJECUCIÓN (F8b — Refresco final de BD pendiente)
**Dependencia:** A04.P001 (Red srv-dasu) **Dependencia:** A04.P001 (Red srv-dasu)
## 📊 Progreso General ## 📊 Progreso General
@@ -17,7 +17,11 @@
- **Fase 4: Nueva VM dasu-sql4** [██████████] 100% (12/12) ✅ - **Fase 4: Nueva VM dasu-sql4** [██████████] 100% (12/12) ✅
- **Fase 5: Migración BD** [██████████] 100% (5/5) ✅ - **Fase 5: Migración BD** [██████████] 100% (5/5) ✅
- **Fase 5b: Hardening** [██████████] 100% (4/4) ✅ - **Fase 5b: Hardening** [██████████] 100% (4/4) ✅
- **Fase 6: Validación** [█░░░░░░░░░] 9% (1/10) 🚧 - **Fase 6: Validación** [██████████] 100% (10/10)
- **Fase 7: PC Física** [██████████] 100% (9/9) ✅
- **Fase 8: Refresco BD** [██████████] 100% (4/4) ✅
- **Fase 8b: Refresco Final** [██████████] 100% (4/4) ✅ ¡COMPLETADA!
- **Fase 9: Cierre** [░░░░░░░░░░] 0% (0/4) ⏳
## 📋 Resumen Ejecutivo ## 📋 Resumen Ejecutivo
@@ -169,7 +173,7 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
- [x] **6.10:** **Decisión**: ¿Se puede prescindir del DC definitivamente? - [x] **6.10:** **Decisión**: ¿Se puede prescindir del DC definitivamente?
-**SÍ** → El esquema Workgroup es 100% viable. Se valida que el cliente DASUTEN funciona modificando el `Kermet.ini`. Con esta prueba exitosa en la máquina virtual cliente (Test-Bed `dasu-pcv`), procedemos oficialmente a desplegar este esquema en Producción Física. -**SÍ** → El esquema Workgroup es 100% viable. Se valida que el cliente DASUTEN funciona modificando el `Kermet.ini`. Con esta prueba exitosa en la máquina virtual cliente (Test-Bed `dasu-pcv`), procedemos oficialmente a desplegar este esquema en Producción Física.
### 🚀 **FASE 7: Migración a Producción (PC Física)** *(A iniciar 2026-04-11)* ### 🚧 **FASE 7: Migración a Producción (PC Física)** *(En curso 2026-04-13)*
> **Contexto:** Habiendo superado con éxito la reingeniería en el entorno controlado de la VM de prueba (`dasu-pcv`), el objetivo ahora es trasladar esta misma configuración (Workgroup + Configuración DB directa) a la PC física real operada por la usuaria de DASUTEN. > **Contexto:** Habiendo superado con éxito la reingeniería en el entorno controlado de la VM de prueba (`dasu-pcv`), el objetivo ahora es trasladar esta misma configuración (Workgroup + Configuración DB directa) a la PC física real operada por la usuaria de DASUTEN.
@@ -177,20 +181,61 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
> **Motivo:** Para que la resolución de nombres NetBIOS funcione de manera más armónica y el firewall perciba los nodos como "Red Privada" simétrica, todos los participantes deben pertenecer estrictamente al Group `DASUTEN`. > **Motivo:** Para que la resolución de nombres NetBIOS funcione de manera más armónica y el firewall perciba los nodos como "Red Privada" simétrica, todos los participantes deben pertenecer estrictamente al Group `DASUTEN`.
- [x] **PC Cliente Virtual (`dasu-pcv`):** Grupo `DASUTEN` asignado previamente. ✅ - [x] **PC Cliente Virtual (`dasu-pcv`):** Grupo `DASUTEN` asignado previamente. ✅
- [x] **Servidor SQL (`dasu-sql4`):** Modificar grupo de trabajo actual a `DASUTEN`. ✅ (Aplicado remotamente vía SSH) - [x] **Servidor SQL (`dasu-sql4`):** Modificar grupo de trabajo actual a `DASUTEN`. ✅ (Aplicado remotamente vía SSH)
- [ ] **PC Física (`dasu-pc`):** Se modificará a `DASUTEN` durante el relevamiento. - [x] **PC Física (`dasu-pc`):** Ya estaba en Workgroup `DASUTEN`. ✅ *(Confirmado 2026-04-13)*
#### 7.2 Relevamiento de la PC Física #### 7.2 Relevamiento y Despliegue en PC Física *(completado 2026-04-13)*
- [ ] **Acceso inicial:** Gestionar acceso por AnyDesk/TeamViewer a la PC física de la entidad. - [x] **Acceso inicial:** SSH habilitado (`UTNLR@100.119.233.11:7022`). Tailscale y AnyDesk activos. ✅
- [ ] **Validar estado actual:** Corroborar si aún sigue atada al dominio antiguo y si la usuaria está utilizando un perfil local o de dominio cacheado para evitar pérdida de archivos al mover a Workgroup. - [x] **Validar estado actual:** Win10 Pro, Workgroup `DASUTEN`, hostname `dasu-pc`, perfil local `aalmiron`. ✅
- [ ] **Configuración DASUTEN previo:** Backup preventivo de su directorio `C:\SysDasuten\Sistema\Kermet.ini`. - [x] **Despliegue SysDasuten:** `C:\SysDasuten` no existía previamente. Se copió el directorio completo desde `dasu-pcv` (montaje disco VM NTFS → tar.gz → HTTP LAN srv-dasu:8080 → PowerShell download → extracción). ✅
- [x] **Kermet.ini verificado:** `SERVER=dasu-sql4`, `UID=sa`, `DATABASE=sysdasuten`. ✅
- [x] **Acceso directo creado** en el escritorio de `aalmiron`. ✅ *(El .lnk generado por WScript.Shell no funcionó; se recreó manualmente).*
#### 7.2 Migración de Configuración en PC Física #### 7.3 Prueba Funcional con Usuaria *(parcial 2026-04-13)*
- [ ] **Ajuste Workgroup:** Desvincular del dominio obsoleto y agregar al grupo de trabajo `DASUTEN` sin afectar la información local. > **Usuaria:** Andrea Almirón (`aalmiron`)
- [ ] **Re-conexión al SQL (Nuevo esquema):** Inyectar el `Kermet.ini` modificado con `UID=sa` y **`SERVER=dasu-sql4`**. *(Nota: Se comprobó que usar el Hostname en lugar de la IP funciona perfectamente gracias a la resolución de nombres del grupo de trabajo compartido, otorgando resiliencia ante rotación del DHCP).*
- [ ] **Pruebas de Usuario Final:** Loguearse al sistema con credenciales productivas y validar.
#### 7.3 Destrucción Ecosistema Antiguo - [x] **Inicio rápido del sistema:** ✅ OK — Arranca sin errores.
- [ ] Proceder al apagado seguro de las VMs legacy que sostenían el Active Directory (VM 100, VM 101). - [x] **Acceso a ventana de bonos:** ✅ OK — Datos visibles y navegables.
- [x] **Impresión desde el sistema:** ✅ OK — **RESUELTO (2026-04-14)**. El symlink `C:\Sistema → C:\SysDasuten\Sistema` fue la solución definitiva. Las plantillas Word/Excel se localizan correctamente.
#### 7.4 Workaround Temporario *(resuelto 2026-04-13)*
> Mientras se resuelve el problema de impresión, se solicitó a la usuaria que continúe operando mediante el sistema anterior (vía acceso remoto a `dasu-pcv`).
- [x] **RustDesk reinstalado en dasu-pcv:** ❌ No resolvió inicialmente. Se actualizaron también las claves Kaspersky (antivirus vencido causaba cortes de red). Persistió el problema.
- [x] **Verificación cruzada desde srv-ns8:** ❌ Confirmado: RustDesk no conecta desde ningún nodo → problema con los servidores relay propios de RustDesk (no es local).
- [x] **Solución provisional: TeamViewer instalado** en ambas máquinas (dasu-pcv + dasu-pc). Interconexión configurada y verificada con éxito. ✅
- [x] **Usuaria informada:** Operando en sistema legacy vía TeamViewer mientras finaliza tareas urgentes. ✅
- [x] **HALLAZGO RustDesk (2026-04-13 ~16:00):** Se descubrió que RustDesk implementó una nueva política de validación contra su servidor central que antes no existía. Esta validación era la causa raíz de las fallas de conexión entre nodos. Se verificó reconectando exitosamente `srv-ns8 → dasu-pcv` tras aceptar la nueva validación. ✅
- ⚠️ **Acción pendiente:** Reconfigurar RustDesk en `dasu-pcv` (y `dasu-pc`) para que funcione con la nueva política, restaurando el acceso remoto directo sin depender de TeamViewer.
#### 7.5 Diagnóstico de Impresión — Ingeniería Inversa *(en curso 2026-04-13)*
> **Estrategia:** El sistema DASUTEN (Visual FoxPro) genera documentos de impresión mediante **OLE Automation** con Word/Excel, usando plantillas. Se usa `dasu-pcv` como entorno de diagnóstico para hacer ingeniería inversa.
##### 7.5.1 Ingeniería inversa — Resultados (2026-04-13 ~18:20)
- [x] **Plantillas localizadas:** `C:\SysDasuten\Sistema\Word\` (97 archivos `.doc` — formato Word 97-2003) + `C:\SysDasuten\Sistema\Excel\Recibo.xls`. ✅
- [x] **Kermet.ini verificado:** No contiene rutas a plantillas — solo config de BD. Las rutas se resuelven dentro del ejecutable VFP. ✅
- [x] **EXE VFP analizado:** `DasutenSQL.exe` compilado desde `c:\users\lis\fox\generalprueba\`. No hardcodea rutas a plantillas — las resuelve relativamente a `.\Word\` y `.\Excel\`. ✅
- [x] **Office en dasu-pcv:** Microsoft **Office 2010** (v14.0.6024.1000) — `C:\Program Files (x86)\Microsoft Office\Office14\`. ✅
- [x] **Office en dasu-pc:** Microsoft **Office 2016/365** (Office16) — `C:\Program Files\Microsoft Office\Office16\`. ✅
##### 🔴 CAUSA RAÍZ IDENTIFICADA (2026-04-13 ~18:28)
> **Ruta de plantillas incorrecta.** El archivo `leeme!!!!.txt` del sistema indica que las plantillas deben estar en **`C:\Sistema\Word\`** (ruta legacy hardcodeada en el ejecutable VFP). Sin embargo, en la configuración actual las plantillas están en `C:\SysDasuten\Sistema\Word\`. El directorio `C:\Sistema` **no existe** en ninguno de los dos equipos, por lo que el sistema no encuentra las plantillas al intentar imprimir.
>
> **La versión de Office (2010 vs 2016) es una diferencia pero NO la causa principal.**
##### 7.5.2 Resolución — Symlink *(parcial 2026-04-13, continúa 2026-04-14)*
- [x] **Symlink creado en dasu-pcv:** `mklink /D C:\Sistema C:\SysDasuten\Sistema` → enlace simbólico que redirige `C:\Sistema\` a `C:\SysDasuten\Sistema\`. ✅ Verificado: `C:\Sistema\Word\Consulta.doc` resuelve correctamente.
- [x] **OLE Word funcional en dasu-pcv (Session 0):** `New-Object -ComObject Word.Application` → Word 14.0 responde. ✅ Nota: `Documents.Open()` requiere sesión de escritorio interactiva (no funciona por SSH/Session 0).
##### 7.5.3 Pendiente — Validación interactiva en dasu-pcv *(próxima sesión)*
> ⚠️ **La prueba OLE por SSH (Session 0) no alcanza** — Word no puede abrir documentos sin escritorio activo. Se requiere sesión GUI.
- [x] **Paso 1:** Conectar a `dasu-pcv` vía RustDesk (nueva política) o TeamViewer (ya configurado). ✅
- [x] **Paso 2:** Abrir DASUTEN (`C:\SysDasuten\Sistema\DasutenSQL.exe`) desde el escritorio. ✅
- [x] **Paso 3:** Intentar imprimir (ej. bono de consulta) → symlink resolvió el error de plantillas. ✅
- [x] **Paso 4:** Replicado en `dasu-pc` (PC física). **Impresión funcional en producción.**
#### 7.6 Destrucción Ecosistema Antiguo *(pospuesto → Fase 9)*
> Movido a Fase 9 (Cierre del proyecto) tras validación completa.
### 🔄 **FASE 8: Refresco de BD de Producción Final** *(Orquestación)* ### 🔄 **FASE 8: Refresco de BD de Producción Final** *(Orquestación)*
@@ -205,6 +250,62 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
- [x] **Restauración Transaccional:** Ejecución de `RESTORE DATABASE ... WITH REPLACE` asegurando el forzado a Single User (ejecutado directo, el Dron arrojó error de parsing UTF8 por los mensajes en ISO-8859-1 de `sqlcmd`). ✅ - [x] **Restauración Transaccional:** Ejecución de `RESTORE DATABASE ... WITH REPLACE` asegurando el forzado a Single User (ejecutado directo, el Dron arrojó error de parsing UTF8 por los mensajes en ISO-8859-1 de `sqlcmd`). ✅
- [x] **Verificación Post-Restore:** Comprobación `DBCC CHECKDB` certificando la consistencia final de la DB. ✅ - [x] **Verificación Post-Restore:** Comprobación `DBCC CHECKDB` certificando la consistencia final de la DB. ✅
### 🚧 **FASE 8b: Refresco Final de BD** *(En curso 2026-04-14)*
> **Contexto:** Con la PC física operativa e impresión funcionando, se requiere un último refresco de la BD desde srvv-fenix para que dasu-sql4 tenga los datos más recientes (transacciones de los últimos días).
>
> **NUEVO ENFOQUE (2026-04-14):** Se reemplaza la cadena de transferencias SMB→SCP→SCP por **Google Drive como medio intermedio**, optimizando el flujo y eliminando dependencias de Tailscale inestable. Se implementa arquitectura de **Drones Atómicos Interconectados** con comunicación vía JSON.
#### Pipeline de transferencia (NUEVO — Google Drive)
```
srvv-fenix (BACKUP WITH COMPRESSION)
↓ SMB (rápido, LAN)
srv-ns8 (/var/tmp/)
↓ rclone (subida)
Google Drive (carpeta compartida drive_bkps-dasu)
↓ HTTP Direct Download (Invoke-WebRequest)
dasu-sql4 (F:\BACKUP\)
↓ RESTORE DATABASE WITH REPLACE
→ sysdasuten ONLINE
```
#### Drones Atómicos Implementados
| Dron | Nodo | Tarea | Output JSON |
|------|------|-------|-------------|
| `dasuten_exportar.rb` | srvv-fenix | BACKUP DATABASE WITH COMPRESSION | `backup_ruta` |
| `dasuten_upload_drive.rb` | srv-ns8 | rclone copy → Google Drive | `drive_ruta`, `drive_url` |
| `dasuten_download_drive.rb` | dasu-sql4 | Invoke-WebRequest ← Google Drive | `backup_local`, `duracion` |
| `dasuten_restaurar.rb` | dasu-sql4 | RESTORE + DBCC CHECKDB | `estado_integridad`, `pipeline_completo` |
#### Orquestador
- **Script:** `adn/tools/cli/drones/orquestador_pipeline.rb`
- **Función:** Lanza drones secuencialmente, lee outputs JSON, inyecta contexto al siguiente dron
- **Comando:** `./adn/tools/run dron lanzar --nota "Pipeline DASUTEN" -- ruby adn/tools/cli/drones/orquestador_pipeline.rb`
#### Estado Actual — Resultados Finales
| Paso | Estado | Duración | Resultado |
|------|--------|----------|-----------|
| **8b.1** | ✅ Completado | 44.8s | Backup generado en `srvv-fenix` |
| **8b.2** | ✅ Completado | 1736s (29 min) | Upload a Google Drive (1.4 GB) |
| **8b.3** | ✅ Completado | ~5 min | Download HTTP directo en `dasu-sql4` |
| **8b.4** | ✅ Completado | 248.79s (4 min) | Restore + DBCC CHECKDB — Integridad: **OK** |
**Pipeline completado exitosamente.** Base de datos `sysdasuten` ONLINE en `dasu-sql4` con datos actualizados al 2026-04-14.
#### Lecciones Técnicas — Google Drive Download
- **Virus confirmation page:** Google Drive muestra página de confirmación para archivos >100MB. Se maneja extrayendo parámetros `id`, `uuid`, `confirm` del HTML y construyendo URL confirmada: `https://drive.usercontent.google.com/download?id=...&confirm=...&uuid=...`
- **dasu-sql4 sleep mode:** La VM entra en suspensión cuando no se usa. Acceso recomendado vía relay SSH desde `srv-dasu` con `sshpass` + ProxyCommand.
- **Base64 encoding:** Scripts PowerShell se codifican en base64 para transporte sobre SSH, evitando problemas de escaping UTF-8.
### ⏳ **FASE 9: Cierre del Proyecto** *(pendiente — tras resolver impresión)*
> **Contexto:** Una vez que la impresión funcione en la PC física y el sistema esté 100% operativo, se procede al cierre formal del proyecto.
- [ ] **9.1:** Validación final completa con usuaria (todos los flujos funcionales incluyendo impresión). ✅/❌
- [ ] **9.2:** Reservar IPs fijas en router ISP para `dasu-sql4` y `dasu-pc` (evitar cambios por DHCP).
- [ ] **9.3:** Apagado seguro de VMs legacy: VM 100 (DC), VM 101 (SQL dominio). Documentar estado final.
- [ ] **9.4:** Actualizar informe ejecutivo (`docs/informes/2026-04_DASUTEN-migracion/01_informe_ejecutivo_DASUTEN.md`) a versión definitiva y elevar a autoridades.
--- ---
| **Red** | Privada 10.0.100.x (gw interno) | DHCP del ISP (gw del ISP) | | **Red** | Privada 10.0.100.x (gw interno) | DHCP del ISP (gw del ISP) |
| **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) | | **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) |
@@ -244,6 +345,8 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
- **Python `http.server` NO soporta descargas grandes (~1GB+):** `Invoke-WebRequest` y `Net.WebClient.DownloadFile()` en PowerShell fallan con "conexión terminada inesperadamente" al descargar archivos de ~1.3GB desde `python3 -m http.server`. El módulo es single-threaded y no maneja correctamente respuestas chunked/grandes. **Solución**: instalar OpenSSH Server en la VM destino Windows y transferir vía SCP por la LAN (gigabit), que es nativo y confiable para archivos grandes. - **Python `http.server` NO soporta descargas grandes (~1GB+):** `Invoke-WebRequest` y `Net.WebClient.DownloadFile()` en PowerShell fallan con "conexión terminada inesperadamente" al descargar archivos de ~1.3GB desde `python3 -m http.server`. El módulo es single-threaded y no maneja correctamente respuestas chunked/grandes. **Solución**: instalar OpenSSH Server en la VM destino Windows y transferir vía SCP por la LAN (gigabit), que es nativo y confiable para archivos grandes.
- **W-Zombi se bloquea en comandos largos:** Cuando el agente PS1 ejecuta un comando que tarda minutos (ej. `Add-WindowsCapability`), deja de pollear el relay. No se pierde — al terminar, reanuda automáticamente y recoge el siguiente comando en cola. No reiniciar el agente prematuramente. - **W-Zombi se bloquea en comandos largos:** Cuando el agente PS1 ejecuta un comando que tarda minutos (ej. `Add-WindowsCapability`), deja de pollear el relay. No se pierde — al terminar, reanuda automáticamente y recoge el siguiente comando en cola. No reiniciar el agente prematuramente.
- **Scripts wrapper evitan problemas de escape con candados:** Para comandos con comillas complejas (ej. `smbclient -U "user%$PASS"`), crear un script `.sh` local con `File.write`, asignar `chmod 0755`, y pasarlo como argumento a `candados run`. Esto evita el doble/triple escape de comillas. - **Scripts wrapper evitan problemas de escape con candados:** Para comandos con comillas complejas (ej. `smbclient -U "user%$PASS"`), crear un script `.sh` local con `File.write`, asignar `chmod 0755`, y pasarlo como argumento a `candados run`. Esto evita el doble/triple escape de comillas.
- **Google Drive como medio de transferencia (2026-04-14):** Para evitar cadenas largas de SCP/SMB con Tailscale inestable, se usa Google Drive como intermediario. **Ventaja:** Download HTTP directo desde dasu-sql4 sin depender de Tailscale. **Desafío:** Archivos >100MB requieren confirmación de virus — se resuelve parseando HTML y extrayendo parámetros `id`, `uuid`, `confirm`.
- **Arquitectura de Drones Atómicos Interconectados:** Cada paso del pipeline es un dron independiente que escribe su output en JSON (`/tmp/dron_*_output.json`). El orquestador lee estos JSONs para pasar contexto al siguiente dron. **Ventajas:** Re-ejecución de pasos individuales, observabilidad en Bitácora Web, acoplamiento mínimo.
## 🔗 Referencias ## 🔗 Referencias
@@ -259,3 +362,5 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
- **DHCP del ISP cambia IPs tras reboot:** `dasu-sql4` obtuvo `.11` en vez de `.26` tras el corte de luz. Verificar siempre la IP actual usando ARP + MAC antes de asumir que la IP anterior sigue vigente. - **DHCP del ISP cambia IPs tras reboot:** `dasu-sql4` obtuvo `.11` en vez de `.26` tras el corte de luz. Verificar siempre la IP actual usando ARP + MAC antes de asumir que la IP anterior sigue vigente.
- **W-Zombi no sobrevive reboots del agente:** El script PS1 debe re-lanzarse manualmente desde la consola Proxmox/noVNC tras un reinicio inesperado de la VM. Considerar crear una Scheduled Task. - **W-Zombi no sobrevive reboots del agente:** El script PS1 debe re-lanzarse manualmente desde la consola Proxmox/noVNC tras un reinicio inesperado de la VM. Considerar crear una Scheduled Task.
- **QEMU Guest Agent no inicia automáticamente:** Tras corte de luz, el servicio `QEMU-GA` quedó caído. Verificar que el servicio esté en `Automatic` startup. - **QEMU Guest Agent no inicia automáticamente:** Tras corte de luz, el servicio `QEMU-GA` quedó caído. Verificar que el servicio esté en `Automatic` startup.
- **DASUTEN hardcodea ruta `C:\Sistema`:** El ejecutable VFP busca plantillas en `C:\Sistema\Word\` y `C:\Sistema\Excel\`, pero la instalación moderna las coloca en `C:\SysDasuten\Sistema\`. **Solución:** `mklink /D C:\Sistema C:\SysDasuten\Sistema` — el symlink es transparente para el ejecutable y no requiere modificación alguna del binario.
- **OLE Automation Word es retrocompatible (Office 2010→2016):** `New-Object -ComObject Word.Application` funciona tanto con Office 14 (2010) como Office 16 (2016). La interfaz COM no cambió — no era necesario downgrade de Office.
@@ -0,0 +1,130 @@
# INFORME DE AVANCE
## Migración del Sistema DASUTEN a Infraestructura Autónoma
> **Área responsable:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Facultad Regional La Rioja — Universidad Tecnológica Nacional**
> **Fecha:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — DTIC, UTN-FRLR
> **Destinatarios:** [Completar]
> **Versión:** 0.3 (Borrador)
>
> *Este documento es de carácter ejecutivo y narrativo. El detalle técnico completo se encuentra en el documento complementario [02_informe_tecnico_DASUTEN.md](02_informe_tecnico_DASUTEN.md).*
---
## 1. Contexto y Punto de Partida
El sistema de gestión administrativa de **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) en la Facultad Regional La Rioja funcionaba desde hace años sobre la infraestructura informática compartida de la Facultad: servidores, red interna y servicios de dominio que atienden a toda la institución.
Si bien esta configuración fue funcional durante un tiempo, con el correr de los años fue manifestando limitaciones concretas:
- **Fragilidad operativa:** Cualquier incidencia en la red o los servidores de la Facultad —incluso ajena a DASUTEN— impactaba directamente en el funcionamiento del sistema.
- **Complejidad desproporcionada:** La arquitectura heredada requería múltiples servidores virtuales y un controlador de dominio, componentes pensados para entornos de mayor escala que resultaban sobredimensionados para las necesidades de una sola oficina.
- **Dificultad de mantenimiento:** Los respaldos de datos y las actualizaciones periódicas que envía Rectorado requerían coordinación con la infraestructura general de la Facultad, generando demoras y dependencias cruzadas.
- **Limitación de autonomía:** DASUTEN, si bien opera dentro de las instalaciones de la Facultad, responde funcionalmente a **Rectorado (Buenos Aires)**. La dependencia de la infraestructura local dificultaba una eventual gestión directa desde la sede central.
A esta situación se sumó que, al reubicarse la oficina en un espacio propio alejado de los servidores, las fallas de conectividad se agravaron por la cantidad de puntos intermedios en la red. Para dar continuidad al servicio, se gestionó una línea de internet propia y se implementó un esquema de trabajo indirecto donde las usuarias — Andrea Almirón y Romina Molina — accedían mediante escritorio remoto a una PC virtual para poder operar el sistema. Esto implicaba mantener cuatro componentes simultáneamente (servidor de dominio, servidor de base de datos, PC virtual y PC física), multiplicando los puntos de falla.
---
## 2. Objetivo del Proyecto
Diseñar e implementar una solución que permita al sistema DASUTEN operar de forma **completamente autónoma**, teniendo en cuenta:
- **Recursos limitados:** Aprovechar equipos ya existentes — un equipo de escritorio de prestaciones modestas y un servidor compacto reutilizado. **Sin adquisición de hardware nuevo.**
- **No interferir con la oficina:** Que DTIC pueda realizar respaldos, actualizaciones de Rectorado y soporte técnico **de forma remota y transparente**, sin afectar las tareas cotidianas.
- **Trabajo directo:** Que las usuarias operen el sistema directamente desde la PC de la oficina, sin depender de conexiones remotas intermedias.
- **Simplicidad:** Una arquitectura lo más simple posible, administrable y confiable en el tiempo.
---
## 3. Qué se logró
### 3.1 La simplificación
Se reemplazó la arquitectura compleja por un esquema directo y simple:
| Aspecto | Antes | Ahora |
|:---|:---:|:---:|
| Componentes necesarios | 4 | 2 |
| Forma de trabajo | Indirecta (vía escritorio remoto) | Directa (sistema en la PC) |
| Dependencia de red de Facultad | Sí | No |
| Puntos de falla | Múltiples | Mínimos |
| Requiere presencia física para soporte | Frecuentemente | No |
| Hardware nuevo adquirido | — | Ninguno |
Las usuarias ahora operan el sistema **directamente desde la PC de la oficina**. El servidor de base de datos funciona dentro de la misma oficina, en la red propia de DASUTEN, sin intermediarios.
### 3.2 El proceso: de testeo a producción
Si bien el proyecto llevó más tiempo del inicialmente estimado, esto responde a una decisión deliberada: se planificó una **etapa de testeo exhaustivo** antes de llevar el sistema a producción. Cada paso fue validado primero en un entorno de pruebas antes de aplicarse al entorno real, minimizando riesgos para la operación de la oficina.
**A la fecha, el sistema se encuentra a un paso del 100%**, restando únicamente un ajuste de compatibilidad en la impresión de documentos, para el cual ya se dispone de una solución provisoria.
### 3.3 Valor agregado: herramientas y metodología
Más allá del objetivo central, el trabajo realizado **generó herramientas y metodologías de valor** que benefician a la gestión de DTIC en su conjunto:
- **Dashboard web de seguimiento** del proyecto, con visibilidad en tiempo real del progreso.
- **Sistema de bitácoras** con registro detallado y auditable de cada intervención técnica.
- **Herramientas de automatización** (Ecosistema ADN) para respaldos, gestión remota y tareas repetitivas.
- **Metodología de trabajo con IA generativa**, altamente productiva y replicable en futuros proyectos.
---
## 4. Estado Actual
| Aspecto | Estado |
|:---|:---:|
| Sistema operativo en oficina | ✅ |
| Base de datos migrada y verificada | ✅ |
| Trabajo directo (sin escritorio remoto) | ✅ |
| Administración remota por DTIC | ✅ |
| Respaldos automatizados | ✅ |
| Impresión de documentos | ⚠️ En ajuste |
El sistema se encuentra **operativo**. La usuaria accede directamente al sistema desde la PC de la oficina. DTIC administra y respalda de forma remota sin interferir con la operación diaria.
Resta un ajuste en la impresión, con solución provisoria funcional.
---
## 5. Beneficios Alcanzados
- **Trabajo directo:** Las usuarias operan el sistema sin intermediarios.
- **Autonomía operativa:** Infraestructura propia, desvinculada de la Facultad, preparada para gestión desde Rectorado.
- **Optimización de recursos:** Sin inversión en hardware nuevo.
- **Soporte transparente:** DTIC gestiona remotamente sin afectar la oficina.
- **Mayor estabilidad:** De 4 componentes a 2, menos fallas posibles.
- **Herramientas reutilizables:** Activos aprovechables para otros proyectos de DTIC.
---
## 6. Próximos Pasos
1. Completar el ajuste de impresión para alcanzar el 100% de funcionalidad.
2. Estabilizar la configuración de red del servidor.
3. Formalizar los procedimientos de respaldo automatizado.
4. Evaluar el retiro definitivo de la infraestructura anterior.
---
## 7. Conclusión
La migración del sistema DASUTEN demuestra que es posible **lograr una mejora sustancial en estabilidad, autonomía y experiencia de trabajo** partiendo de recursos limitados y sin inversión adicional.
Se pasó de un esquema indirecto y complejo a uno directo y simple, donde el sistema funciona en la propia oficina. El proceso generó además herramientas y metodologías que quedan como activo del área para futuros proyectos.
DASUTEN opera hoy de forma independiente, administrable remotamente por DTIC y preparada para una gestión más ágil desde la Facultad Regional o desde Rectorado.
---
### Historial de versiones
| Versión | Fecha | Cambios |
|:---:|:---:|:---|
| 0.1 | 13/04/2026 | Borrador inicial |
| 0.2 | 13/04/2026 | Se amplía contexto de situación inicial |
| 0.3 | 13/04/2026 | Se retoma enfoque narrativo, se integra contexto como complemento, se agrega referencia al documento técnico |
*Fin del informe.*
@@ -0,0 +1,446 @@
# INFORME TÉCNICO — Migración del Sistema DASUTEN a Nuevo Servidor
> **Área:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Proyecto:** Migración de infraestructura DASUTEN — Modo Workgroup sin Domain Controller
> **Código interno:** A04.P005
> **Fecha del informe:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — Área DTIC, UTN-FRLR
> **Versión:** 0.2
>
> *Este documento contiene el detalle técnico completo de la migración. Para la síntesis ejecutiva, ver [01_informe_ejecutivo_DASUTEN.md](01_informe_ejecutivo_DASUTEN.md).*
---
## 1. Resumen Ejecutivo
Se informa el avance de la migración del sistema de gestión administrativa **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) desde la infraestructura compartida de la Facultad hacia un entorno independiente, autónomo y simplificado.
**Estado general del proyecto: 95% completado.**
El sistema DASUTEN se encuentra **operativo** en la nueva infraestructura desde el día 13 de abril de 2026, habiendo superado exitosamente las pruebas funcionales de acceso a datos. Resta únicamente resolver un problema de compatibilidad de impresión vinculado a las plantillas de Office utilizadas por el sistema.
### Logros principales
- ✅ Servidor de base de datos **completamente migrado** a infraestructura nueva.
- ✅ Base de datos de producción **restaurada y verificada** sin errores de integridad.
- ✅ Sistema DASUTEN **desplegado y operativo** en la PC física de la usuaria.
- ✅ Eliminación exitosa de la dependencia del **Active Directory (Domain Controller)**.
- ✅ Simplificación de la arquitectura de red — de 3 servidores virtuales a 1.
- ⚠️ Pendiente: resolución de problema de impresión por compatibilidad de plantillas Office.
---
## 2. Antecedentes y Motivación
### 2.1 Infraestructura original (en red de Facultad)
El sistema DASUTEN operaba sobre infraestructura compartida con la Facultad Regional La Rioja, dependiendo de:
- Un servidor SQL Server alojado en la red interna de la Facultad (`srvv-fenix`).
- Un Domain Controller Active Directory para la autenticación de usuarios.
- Conectividad de red interna de la Facultad con routing complejo.
Esta configuración generaba **múltiples puntos de falla**:
- Dependencia del dominio institucional para la autenticación.
- Complejidad de red con subnetting privado, NAT y gateway interno.
- Cualquier cambio en la infraestructura de la Facultad impactaba directamente en DASUTEN.
### 2.2 Esquema de trabajo provisorio (AnyDesk + PC Virtual)
Al reubicarse la oficina DASUTEN en un espacio propio alejado de los servidores, se agravaron las fallas de conectividad: la señal de red recorría múltiples puntos intermedios dentro del edificio, y la caída de cualquiera de ellos dejaba a la oficina sin sistema.
Para dar continuidad al servicio, se gestionó una **línea de internet propia** para la oficina y se implementó el siguiente esquema de trabajo:
```
PC Física (dasu-pc) Servidor srv-dasu (Proxmox VE)
Usuarias: Almirón / Molina ├── VM 100: dasu-srvv-dc (Domain Controller AD DS)
Acceso: AnyDesk ──────────────────► ├── VM 101: dasu-srvv-sql (SQL Server + BD sysdasuten)
(escritorio remoto) └── VM 102: dasu-pcv (PC Virtual ← trabajo aquí)
```
**Componentes requeridos:** 4 (servidor dominio + servidor BD + PC virtual + PC física con AnyDesk).
**Flujo de trabajo de las usuarias:** PC física → AnyDesk (escritorio remoto) → PC virtual → sistema DASUTEN.
Este esquema permitió:
- Dar continuidad al servicio independientemente de la red interna de la Facultad.
- Brindar asistencia técnica 24/7 por parte de DTIC vía acceso remoto.
Pero implicaba:
- Experiencia de trabajo **indirecta** para las usuarias (doble salto).
- Cuatro componentes simultáneos que debían estar operativos.
- Múltiples puntos de falla que dificultaban el soporte.
### 2.3 Objetivo del proyecto
**Migrar el sistema DASUTEN a una infraestructura independiente y simplificada**, eliminando la dependencia del Domain Controller (Active Directory), el esquema de escritorio remoto, y utilizando un esquema de red **Workgroup** directo, con el fin de:
1. Reducir la complejidad operativa (de 4 componentes a 2).
2. Permitir trabajo directo (sin AnyDesk intermedio).
3. Minimizar los puntos de falla.
4. Garantizar la autonomía operativa de DASUTEN respecto de la infraestructura de la Facultad.
5. Facilitar la eventual gestión desde Rectorado (Buenos Aires) si fuera necesario.
---
## 3. Infraestructura Implementada
### 3.1 Arquitectura de red
Se implementó una topología de **red plana simplificada**, donde todos los equipos obtienen direccionamiento IP directamente del router del ISP, eliminando subnetting privado y routing interno:
```
Internet
Router ISP (192.168.1.1 — Gateway)
├── srv-dasu (Proxmox VE) → 192.168.1.13
│ └── dasu-sql4 (VM 106) → 192.168.1.11 [SQL Server 2019]
└── dasu-pc (PC Física) → DHCP ISP [Estación de trabajo]
```
### 3.2 Componentes del nuevo entorno
| Componente | Descripción | Estado |
|:---|:---|:---:|
| **srv-dasu** | Servidor Proxmox VE 9.1 (Hipervisor) — CPU Intel, 15 GB RAM, 94 GB disco | ✅ Operativo |
| **dasu-sql4** (VM 106) | Windows Server 2022 — SQL Server 2019 Enterprise — 4 GB RAM, 2 vCPU, discos 60+120 GB | ✅ Operativo |
| **dasu-pc** | PC Física — Windows 10 Pro — AMD Ryzen 5, 8 GB RAM — Oficina DASUTEN | ✅ Operativo |
### 3.3 Comparativa: Antes vs. Después
| Aspecto | Enfoque Anterior (con DC + AnyDesk) | Enfoque Actual (Workgroup) |
|:---|:---|:---|
| **Componentes** | 4 (DC + SQL + PC virtual + PC física) | 2 (SQL + PC física) |
| **Forma de trabajo** | Indirecta (AnyDesk → PC virtual) | Directa (sistema en PC) |
| **Red** | Privada 10.0.100.x (gateway interno) | DHCP del ISP (red plana) |
| **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) |
| **Usuarios** | Active Directory | Usuarios locales Windows |
| **DNS** | AD DNS (10.0.100.10) | DNS del ISP (automático) |
| **Complejidad de red** | Alta (DC + DNS + dominio + routing) | Mínima (red plana ISP) |
| **Puntos de falla** | DC, SQL, red dominio, routing, AnyDesk | Solo SQL |
| **VMs necesarias** | 3 (DC + SQL + PC virtual) | 1 (solo SQL) |
---
## 4. Fases Ejecutadas y Progreso
### Resumen de progreso por fase
| Fase | Descripción | Estado | Progreso |
|:---:|:---|:---:|:---:|
| 1 | Preparación del entorno (srv-dasu) | ✅ Completada | 100% |
| 2 | Creación de VM SQL Server de prueba | ✅ Completada | 100% |
| 3 | Creación de VM PC Cliente de prueba | ✅ Completada | 100% |
| 4 | VM definitiva dasu-sql4 | ✅ Completada | 100% |
| 5 | Migración de base de datos | ✅ Completada | 100% |
| 5b | Hardening y persistencia de servicios | ✅ Completada | 100% |
| 6 | Validación con PC cliente virtual | ✅ Completada | 100% |
| 7 | Despliegue en PC física (producción) | 🚧 En curso | 75% |
| 8 | Refresco final de base de datos | ✅ Completada | 100% |
### 4.1 Fase 1-3: Preparación y pruebas de concepto (27-28 marzo)
Se preparó el hipervisor Proxmox, se reconfiguró el bridge de red para usar DHCP directo del ISP, y se crearon VMs de prueba para validar la viabilidad del enfoque sin Domain Controller.
**Hallazgo crítico:** Se detectó y resolvió que el bridge de red estaba configurado con la IP de una red privada anterior, lo cual habría impedido la conectividad de las nuevas VMs.
### 4.2 Fase 4: Servidor SQL definitivo (9-10 abril)
Se creó la VM definitiva `dasu-sql4` (VM 106) con una arquitectura optimizada:
- **Discos separados:** 60 GB para SO + 120 GB exclusivo para datos SQL (buena práctica).
- **SQL Server 2019 Enterprise** instalado con autenticación mixta.
- **Gestión remota** asegurada vía OpenSSH Server (puerto 7022) y Tailscale VPN.
### 4.3 Fase 5: Migración de Base de Datos (11 abril)
Se ejecutó la migración completa de la base de datos de producción:
1. **Exportación:** Backup comprimido (~1.33 GB) generado desde el servidor origen (`srvv-fenix`).
2. **Transferencia:** Archivo movido a través de la VPN institucional (Tailscale) en múltiples tramos seguros.
3. **Restauración:** Base restaurada exitosamente en 26 segundos (354 MB/s).
4. **Verificación:** `DBCC CHECKDB` ejecutado sin errores — integridad de datos confirmada al 100%.
### 4.4 Fase 5b: Hardening (11 abril)
Se validó que todos los servicios críticos (SSH, SQL Server) sobreviven reinicios del servidor sin intervención manual:
- OpenSSH Server: arranque automático ✅
- SQL Server: arranque automático ✅
- IP de red: mantenida tras reinicio ✅
### 4.5 Fase 6: Validación funcional en entorno virtual (11 abril)
Se restauró una VM cliente desde backup para simular el entorno real de la oficina DASUTEN:
- Se desvinculó la PC del dominio anterior y se unió al Workgroup `DASUTEN`.
- Se reconfiguró el archivo de conexión del sistema (`Kermet.ini`) para apuntar al nuevo servidor SQL.
- **Resultado:** El sistema DASUTEN conectó exitosamente, actualizó estructuras y descargó novedades.
**Conclusión técnica clave:** Se determinó mediante ingeniería inversa que el sistema DASUTEN (desarrollado en Visual FoxPro) **no tiene dependencia real del Active Directory** si se configura explícitamente la autenticación SQL en su archivo de configuración.
### 4.6 Fase 7: Despliegue en PC física de producción (13 abril — EN CURSO)
Se desplegó el sistema en la PC física de la oficina DASUTEN (`dasu-pc`):
- **Directorio del sistema** (`C:\SysDasuten`) transferido desde la VM de pruebas.
- **Configuración de conexión** apuntada al nuevo servidor `dasu-sql4`.
- **Acceso directo** creado en el escritorio de la usuaria (`aalmiron`).
- **Pruebas funcionales:**
- Inicio del sistema: ✅ OK
- Acceso a datos (ventana de bonos): ✅ OK
- **Impresión: ❌ FALLO** — Error al imprimir, posiblemente por incompatibilidad de las plantillas de Office (el sistema usa automatización OLE con Word/Excel).
### 4.7 Fase 8: Refresco final de base de datos (13 abril)
Se ejecutó un refresco automatizado de la base de datos para asegurar que el sistema arranca con los datos más actualizados del servidor de origen:
- Backup generado automáticamente desde el origen (44.8 segundos).
- Transferido de manera segura (SMB + SCP por VPN).
- Restaurado con `RESTORE DATABASE ... WITH REPLACE`.
- Integridad verificada con `DBCC CHECKDB` — sin errores.
---
## 5. Estado Actual
### 5.1 Funcionalidades operativas
| Funcionalidad | Estado |
|:---|:---:|
| Acceso al sistema DASUTEN | ✅ Operativo |
| Consulta de datos (bonos, registros) | ✅ Operativo |
| Conexión a base de datos SQL Server | ✅ Operativo |
| Acceso remoto para soporte (Tailscale/SSH) | ✅ Operativo |
| Impresión desde el sistema | ❌ Pendiente |
### 5.2 Problema pendiente: Impresión
**Descripción:** Al intentar imprimir desde el sistema DASUTEN en la PC física, se produce un error. El diagnóstico preliminar indica que la causa es una incompatibilidad entre las **plantillas de Office** requeridas por el sistema (diseñadas para Office 2003/2007) y la versión de Office actualmente instalada en la PC.
**Impacto:** La usuaria no puede generar documentos impresos desde el nuevo sistema.
**Workaround temporal:** Se habilitó acceso remoto (TeamViewer) desde la PC física hacia la VM de pruebas (`dasu-pcv`), donde la impresión funciona correctamente. La usuaria puede continuar operando mientras se resuelve el problema.
**Plan de resolución:** Verificar y ajustar la versión/configuración de Office en `dasu-pc` para compatibilizarla con las plantillas del sistema.
---
## 6. Beneficios Obtenidos
1. **Independencia operativa:** DASUTEN ya no depende de la infraestructura de red ni de los servidores de la Facultad.
2. **Simplificación:** Se pasó de 3 VMs + Domain Controller a solo 1 VM (SQL Server), reduciendo significativamente la complejidad.
3. **Reducción de puntos de falla:** De múltiples (DC, DNS institucional, routing complejo) a uno solo (SQL Server).
4. **Acceso remoto robusto:** Gestión técnica asegurada vía VPN (Tailscale) y SSH, sin necesidad de presencia física.
5. **Autonomía:** La infraestructura puede ser gestionada independientemente, facilitando una eventual transferencia a Rectorado.
6. **Preservación del entorno anterior:** Las VMs del enfoque con DC fueron apagadas y preservadas como respaldo, permitiendo un rollback si fuera necesario.
---
## 7. Cronograma Resumen
| Fecha | Hito |
|:---|:---|
| 27/03/2026 | Inicio — Preparación del entorno e inicio de pruebas de concepto |
| 28/03/2026 | VM SQL Server de prueba operativa |
| 09-10/04/2026 | Servidor SQL definitivo (dasu-sql4) desplegado y operativo |
| 11/04/2026 | Base de datos migrada, verificada y hardening completado |
| 11/04/2026 | Validación funcional exitosa en entorno virtual |
| 13/04/2026 | Despliegue en PC física de producción — sistema operativo con observación de impresión |
---
## 8. Próximos Pasos
| Prioridad | Acción | Plazo estimado |
|:---:|:---|:---|
| 🔴 Alta | Resolver problema de impresión (compatibilidad Office/plantillas) | 1-3 días hábiles |
| 🟡 Media | Reservar IPs fijas en router ISP (prevenir cambios por DHCP) | 1 semana |
| 🟢 Baja | Evaluar apagado definitivo de VMs legacy (DC, SQL antiguo) | Tras validación completa |
| 🟢 Baja | Documentar procedimiento de respaldo automatizado de la nueva BD | 2 semanas |
---
## 9. Conclusión
La migración del sistema DASUTEN a la nueva infraestructura se encuentra **prácticamente completada**, con el sistema operativo y los datos verificados. El enfoque sin Domain Controller demostró ser completamente viable, logrando una **reducción significativa de la complejidad** y de los puntos de falla.
El único aspecto pendiente es la resolución del problema de impresión, para el cual ya se dispone de un workaround funcional que permite a la usuaria continuar operando sin interrupción.
Se estima que el proyecto quedará **100% finalizado** dentro de los próximos días hábiles, una vez resuelta la compatibilidad de plantillas de Office.
---
# ANEXO TÉCNICO
## A. Inventario de Nodos
### A.1 Servidor Hipervisor: srv-dasu
| Campo | Valor |
|:---|:---|
| **Rol** | Hipervisor Proxmox VE 9.1 |
| **IP LAN** | 192.168.1.13 (DHCP ISP) |
| **IP VPN** | 100.112.46.104 (Tailscale) |
| **Bridge** | vmbr0 sobre enp33s0 |
| **RAM** | 15 GB total / ~4 GB libre post-VMs |
| **Disco** | 94 GB (68% uso, thin provisioned) |
| **Acceso remoto** | Tailscale + SSH |
### A.2 Servidor SQL: dasu-sql4 (VM 106)
| Campo | Valor |
|:---|:---|
| **Rol** | SQL Server 2019 Enterprise |
| **SO** | Windows Server 2022 (Español) |
| **IP LAN** | 192.168.1.11 (DHCP ISP) |
| **IP VPN** | 100.107.15.82 (Tailscale — inestable) |
| **Workgroup** | DASUTEN |
| **RAM** | 4 GB |
| **vCPU** | 2 |
| **Disco 1 (sata0)** | 60 GB — Sistema Operativo |
| **Disco 2 (sata1)** | 120 GB — Datos SQL (GPT/NTFS "SQLData") |
| **Estructura SQL** | F:\DATA, F:\LOG, F:\BACKUP, F:\TEMPDB |
| **Autenticación** | Mixta (SQL Auth + Windows) |
| **Puerto SQL** | TCP 1433 |
| **SSH** | Puerto 7022 (OpenSSH Server, arranque automático) |
| **Servicios auto-start** | MSSQLSERVER, sshd |
### A.3 PC Física Cliente: dasu-pc
| Campo | Valor |
|:---|:---|
| **Rol** | Estación de trabajo DASUTEN |
| **Patrimonio** | 32120 |
| **Ubicación** | Oficina DASUTEN (externa a Facultad) |
| **SO** | Windows 10 Pro |
| **Workgroup** | DASUTEN |
| **IP VPN** | 100.119.233.11 (Tailscale) |
| **SSH** | UTNLR@100.119.233.11:7022 |
| **CPU** | AMD Ryzen 5 4600G @ 4.30GHz (6 núcleos) |
| **RAM** | 8 GB DDR4-3200 |
| **Placa Base** | Asus Prime A320M-K |
| **Usuaria principal** | Andrea Almirón (`aalmiron`) |
| **Directorio DASUTEN** | C:\SysDasuten |
| **Config conexión** | C:\SysDasuten\Sistema\Kermet.ini |
### A.4 VM Cliente de Pruebas: dasu-pcv (VM 107) — Test-Bed
| Campo | Valor |
|:---|:---|
| **Rol** | Entorno de pruebas / fallback temporal |
| **SO** | Windows 10 |
| **Workgroup** | DASUTEN |
| **SSH** | Puerto 7022 |
| **Estado** | Operativa (usada como fallback vía TeamViewer) |
---
## B. Configuración del Sistema DASUTEN
### B.1 Archivo de conexión: Kermet.ini
Parámetros clave configurados para la operación sin Domain Controller:
```ini
SERVER=dasu-sql4
DATABASE=sysdasuten
UID=sa
PWD=[credencial almacenada en bóveda]
```
> **Nota técnica:** El campo `UID` debe estar explícitamente configurado con `sa` (u otra cuenta SQL Auth). Si se deja vacío, el sistema DASUTEN (Visual FoxPro) asume autenticación Windows integrada (SSPI/Kerberos), lo cual falla en entorno Workgroup sin Domain Controller.
### B.2 Directorio del sistema
```
C:\SysDasuten\
├── Sistema\ → Ejecutables VFP + Kermet.ini
├── Plantillas\ → Templates Word/Excel para impresión
└── [otros directorios de la aplicación]
```
---
## C. Base de Datos: sysdasuten
| Campo | Valor |
|:---|:---|
| **Nombre** | sysdasuten |
| **Motor** | SQL Server 2019 Enterprise |
| **Tamaño backup comprimido** | ~1.33 GB |
| **Nivel de compatibilidad** | 100 |
| **Recovery model** | FULL |
| **Integridad (DBCC CHECKDB)** | ✅ Sin errores |
| **Tiempo de restauración** | 26 segundos (354 MB/s) |
| **Origen de datos** | srvv-fenix (servidor legacy de Facultad) |
| **Último refresco** | 13/04/2026 |
### C.1 Ruta de transferencia del backup
```
srvv-fenix (Origen)
│ [BACKUP DATABASE ... WITH COMPRESSION]
│ → E:\BK_SQL\sysdasuten_compressed_ADN.bak (~1.33 GB)
srv-ns8 (Tránsito)
│ [SMB/smbget vía dominio]
│ → /var/tmp/
srv-dasu (Tránsito)
│ [SCP vía Tailscale]
dasu-sql4 (Destino)
│ [RESTORE DATABASE ... WITH REPLACE]
└ → F:\BACKUP\ → F:\DATA\ + F:\LOG\
```
---
## D. VMs Legacy Preservadas (Apagadas)
| VM ID | Nombre | Rol anterior | Estado |
|:---:|:---|:---|:---:|
| 100 | dasu-srvv-dc | Domain Controller AD DS | 🛑 Apagada |
| 101 | dasu-srvv-sql | SQL Server (dominio) | 🛑 Apagada |
| 102 | dasu-pcv | PC cliente (dominio) | 🛑 Apagada |
| 103 | dasu-sql2 | SQL Server prueba v1 | 🛑 Descartada |
| 104 | dasu-pcv2 | PC prueba v1 | 🛑 Descartada |
> Las VMs legacy (100-102) se mantienen apagadas como respaldo. Pueden reactivarse en caso de necesitar un rollback al enfoque con Domain Controller.
---
## E. Accesos Remotos Configurados
| Nodo | Método | Detalle |
|:---|:---|:---|
| srv-dasu | Tailscale SSH | 100.112.46.104 |
| dasu-sql4 | SSH (LAN) | 192.168.1.11:7022 |
| dasu-sql4 | Tailscale | 100.107.15.82 (inestable) |
| dasu-pc | Tailscale SSH | 100.119.233.11:7022 |
| dasu-pc | AnyDesk | Instalado (GUI) |
| dasu-pcv | TeamViewer | Activo (workaround temporal) |
---
## F. Cuenta de Gestión VPN
| Campo | Valor |
|:---|:---|
| **Proveedor** | Tailscale |
| **Cuenta** | pcdasu0@frlr.utn.edu.ar |
| **Nodos vinculados** | srv-dasu, dasu-sql4, dasu-pc, dasu-pcv |
---
### Historial de versiones
| Versión | Fecha | Cambios |
|:---:|:---:|:---|
| 0.1 | 13/04/2026 | Versión inicial (fusión de informe de avance + anexo técnico) |
| 0.2 | 13/04/2026 | Se agrega contexto de infraestructura anterior (esquema AnyDesk), versionado, referencia a doc ejecutivo |
*Fin del informe técnico.*
@@ -0,0 +1,23 @@
# 📁 Informe: Migración DASUTEN — Abril 2026
> Carpeta de recopilación para el informe de avance sobre la migración del sistema DASUTEN a infraestructura autónoma.
## Documentos
| # | Archivo | Audiencia | Descripción |
|:---:|:---|:---|:---|
| 01 | [01_informe_ejecutivo_DASUTEN.md](01_informe_ejecutivo_DASUTEN.md) | Autoridades | Narrativo, sin jerga técnica, visión de mejora |
| 02 | [02_informe_tecnico_DASUTEN.md](02_informe_tecnico_DASUTEN.md) | DTIC / Técnico | Detalle de fases, diagnósticos + anexo de nodos y configuraciones |
## Fuentes de datos
- Plan de ejecución: [`A04.P005_DASUTEN-sin-DC.md`](../../ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md)
- Manifiesto de ámbito: [`A04_dtic-DASUTEN.md`](../../ambito/dtic-DASUTEN/A04_dtic-DASUTEN.md)
- Fichas de nodos: [`nodos/dasu-pc.md`](../../../nodos/dasu-pc.md)
- Registros de bitácora del proyecto
## Estado
- **Fecha de generación:** 13/04/2026
- **Estado del proyecto:** ~95% completado (pendiente: ajuste de impresión)
- **Próxima actualización:** Al resolver impresión / cierre de proyecto
@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""Genera PDF del informe ejecutivo desde Markdown usando Chrome headless."""
import markdown
import subprocess
import sys
import os
SRC = "01_informe_ejecutivo_DASUTEN.md"
OUT = "01_informe_ejecutivo_DASUTEN.pdf"
TMP = "/tmp/_informe_tmp.html"
# Leer markdown
with open(SRC, "r", encoding="utf-8") as f:
md_text = f.read()
# Convertir a HTML
html_body = markdown.markdown(md_text, extensions=["tables", "fenced_code"])
# Envolver con estilos profesionales
html = f"""<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
@page {{
size: A4;
margin: 25mm 20mm 25mm 20mm;
}}
body {{
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #1a1a2e;
max-width: 100%;
}}
h1 {{
font-size: 20pt;
font-weight: 700;
color: #0f3460;
margin-top: 0;
border-bottom: 3px solid #0f3460;
padding-bottom: 8px;
}}
h2 {{
font-size: 15pt;
font-weight: 600;
color: #16213e;
margin-top: 28px;
border-bottom: 1.5px solid #e0e0e0;
padding-bottom: 5px;
page-break-after: avoid;
}}
h3 {{
font-size: 12pt;
font-weight: 600;
color: #1a1a2e;
margin-top: 20px;
page-break-after: avoid;
}}
p, li {{
text-align: justify;
orphans: 3;
widows: 3;
}}
blockquote {{
border-left: 4px solid #0f3460;
margin: 0 0 16px 0;
padding: 12px 16px;
background: #f8f9fa;
font-size: 10pt;
color: #333;
}}
blockquote p {{
margin: 4px 0;
text-align: left;
}}
table {{
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 10pt;
page-break-inside: avoid;
}}
th {{
background: #0f3460;
color: white;
font-weight: 600;
padding: 8px 12px;
text-align: left;
}}
td {{
padding: 7px 12px;
border-bottom: 1px solid #e0e0e0;
}}
tr:nth-child(even) {{
background: #f8f9fa;
}}
hr {{
border: none;
border-top: 1px solid #ccc;
margin: 24px 0;
}}
ul {{
padding-left: 20px;
}}
li {{
margin-bottom: 4px;
}}
em {{
color: #555;
}}
strong {{
color: #0f3460;
}}
code {{
background: #f0f0f0;
padding: 2px 5px;
border-radius: 3px;
font-size: 9.5pt;
}}
</style>
</head>
<body>
{html_body}
</body>
</html>"""
# Guardar HTML temporal
with open(TMP, "w", encoding="utf-8") as f:
f.write(html)
# Generar PDF con Chrome headless
result = subprocess.run([
"google-chrome",
"--headless",
"--disable-gpu",
"--no-sandbox",
"--disable-software-rasterizer",
f"--print-to-pdf={OUT}",
"--print-to-pdf-no-header",
TMP
], capture_output=True, text=True)
if os.path.exists(OUT):
size = os.path.getsize(OUT) / 1024
print(f"✅ PDF generado: {OUT} ({size:.0f} KB)")
else:
print(f"❌ Error al generar PDF")
print(result.stderr)
sys.exit(1)
# Limpiar
os.remove(TMP)
@@ -0,0 +1,136 @@
# INFORME DE AVANCE
## Migración del Sistema DASUTEN a Infraestructura Autónoma
> **Área responsable:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Facultad Regional La Rioja — Universidad Tecnológica Nacional**
> **Fecha:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — DTIC, UTN-FRLR
> **Destinatarios:** [Completar]
> **Versión:** 0.1 (Borrador inicial)
---
## 1. Contexto y Punto de Partida
El sistema de gestión administrativa de **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) en la Facultad Regional La Rioja funcionaba desde hace años sobre la infraestructura informática compartida de la Facultad: servidores, red interna y servicios de dominio que atienden a toda la institución.
Si bien esta configuración fue funcional durante un tiempo, con el correr de los años fue manifestando limitaciones concretas:
- **Fragilidad operativa:** Cualquier incidencia en la red o los servidores de la Facultad —incluso ajena a DASUTEN— impactaba directamente en el funcionamiento del sistema.
- **Complejidad desproporcionada:** La arquitectura heredada requería múltiples servidores virtuales y un controlador de dominio, componentes pensados para entornos de gran escala que resultaban sobredimensionados para las necesidades reales de una sola oficina.
- **Dificultad de mantenimiento:** Las tareas de mantenimiento, los respaldos de datos y las actualizaciones periódicas que envía Rectorado requerían coordinación con la infraestructura general de la Facultad, generando demoras y dependencias cruzadas.
- **Limitación de autonomía:** DASUTEN, si bien opera dentro de las instalaciones de la Facultad, responde funcionalmente a **Rectorado (Buenos Aires)**. La dependencia de la infraestructura local dificultaba una eventual gestión directa desde la sede central.
---
## 2. Objetivo del Proyecto
El objetivo fue diseñar e implementar una solución que permita al sistema DASUTEN operar de forma **completamente autónoma** respecto de la infraestructura de la Facultad, teniendo en cuenta las siguientes restricciones y premisas:
- **Recursos limitados:** Se parte de los equipos ya existentes en la oficina DASUTEN — un único equipo de escritorio de prestaciones modestas y un servidor compacto reutilizado. **No se requirió adquisición de hardware nuevo.**
- **No interferir con la operación diaria:** La solución debía permitir al equipo de DTIC realizar respaldos, aplicar actualizaciones de Rectorado y resolver problemas técnicos **de forma remota**, sin interrumpir ni afectar las tareas cotidianas de la oficina.
- **Administrabilidad a distancia:** Dado que la oficina DASUTEN se encuentra en una ubicación externa a la Facultad, todo el sistema debía ser gestionable remotamente por DTIC.
- **Simplicidad y sostenibilidad:** Con un solo equipo de modestas prestaciones como base, la arquitectura debía ser lo más simple posible para que resulte administrable y confiable en el tiempo.
---
## 3. Qué se hizo y cómo se llegó
### 3.1 La simplificación
El trabajo principal consistió en **desmontar la complejidad innecesaria** de la arquitectura anterior y reemplazarla por un esquema directo y simple:
| Aspecto | Antes | Ahora |
|:---|:---:|:---:|
| Servidores virtuales necesarios | 3 | 1 |
| Dependencia de red de Facultad | Sí | No |
| Puntos de falla | Múltiples | Mínimos |
| Requiere presencia física para soporte | Frecuentemente | No |
| Hardware nuevo adquirido | — | Ninguno |
En la práctica, se pasó de una arquitectura de tres servidores virtuales más un controlador de dominio compartido, a **un solo servidor virtual** que contiene la base de datos, conectado directamente a la PC de escritorio de la oficina. Todo opera dentro de la red propia de la oficina DASUTEN, sin depender del cableado ni de los servicios de la Facultad.
### 3.2 El proceso: de testeo a producción
Si bien el proyecto llevó más tiempo del inicialmente estimado, esto responde a una decisión deliberada: se planificó desde el inicio una **etapa de testeo exhaustivo** antes de llevar el sistema a producción. Cada paso fue validado primero en un entorno virtual de pruebas antes de aplicarse al entorno real, minimizando los riesgos para la operación de la oficina.
Este enfoque gradual permitió:
- Detectar y resolver problemas en un entorno controlado, sin afectar a la usuaria.
- Validar que el sistema funciona correctamente **sin la infraestructura de dominio** de la Facultad — algo que no estaba documentado y que requirió trabajo de ingeniería inversa sobre el software provisto por Rectorado.
- Migrar la base de datos de producción con verificación completa de integridad.
**A la fecha, el sistema se encuentra a un paso de estar implementado al 100%**, restando únicamente un ajuste de compatibilidad en la impresión de documentos, para el cual ya se dispone de una solución provisoria que permite continuar operando.
### 3.3 Valor agregado: herramientas y metodología
Un aspecto destacable del proceso es que, más allá del objetivo técnico central, el trabajo realizado durante estos meses **generó herramientas y metodologías de valor** que exceden al proyecto DASUTEN y benefician a la gestión de DTIC en su conjunto:
- **Dashboard web de seguimiento:** Se desarrolló un sitio web interno que muestra en tiempo real el avance del proyecto con sus hitos, permitiendo visibilidad y trazabilidad del progreso.
- **Sistema de bitácoras:** Se implementó un sistema de registro detallado de cada intervención técnica, creando un historial completo y auditable de los movimientos realizados sobre la infraestructura.
- **Herramientas de automatización (Ecosistema ADN):** Se construyó un conjunto de scripts y herramientas internas que optimizan las tareas de administración — desde la orquestación de respaldos automatizados hasta la gestión remota de los equipos, permitiendo que operaciones que antes requerían horas de trabajo manual se ejecuten de forma desatendida.
- **Metodología de trabajo con IA generativa:** Se incorporó la inteligencia artificial como herramienta de desarrollo operativo, tanto para la planificación y documentación del proyecto como para la creación de las herramientas mencionadas. Esta metodología de trabajo colaborativo entre el técnico y la IA demostró ser altamente productiva y replicable en futuros proyectos del área.
Estas herramientas fueron posibles gracias al aprendizaje acumulado durante el proceso de migración, y representan un activo que queda disponible para la gestión de otros proyectos de DTIC.
---
## 4. Estado Actual
| Aspecto | Estado |
|:---|:---:|
| Sistema DASUTEN operativo en oficina | ✅ |
| Base de datos migrada y verificada | ✅ |
| Acceso y consulta de datos | ✅ |
| Administración remota por DTIC | ✅ |
| Respaldos automatizados | ✅ |
| Impresión de documentos | ⚠️ En ajuste |
**El sistema se encuentra operativo.** La usuaria de la oficina DASUTEN puede acceder al sistema, consultar datos y realizar sus tareas habituales. El equipo de DTIC puede administrar, respaldar y actualizar el sistema de forma remota sin interferir con la operación de la oficina.
El único punto pendiente es un ajuste en la función de impresión, vinculado a la compatibilidad de unas plantillas de Office. Se dispuso una solución provisoria que permite a la usuaria continuar imprimiendo mientras se resuelve definitivamente.
---
## 5. Beneficios Alcanzados
### Autonomía operativa
DASUTEN opera ahora sobre infraestructura propia en su oficina, desvinculada de la Facultad. Esto facilita también una eventual gestión directa desde Rectorado.
### Optimización de recursos
Se logró la implementación completa **sin inversión en hardware nuevo**, aprovechando equipos existentes de prestaciones modestas. Un solo equipo compacto cumple ahora la función que antes requerían tres servidores virtuales.
### Soporte remoto transparente
DTIC puede realizar respaldos, actualizaciones y resolución de problemas **sin interrumpir el trabajo de la oficina** y sin necesidad de desplazarse físicamente.
### Mayor estabilidad
Al eliminar las dependencias cruzadas con la infraestructura de la Facultad, se reducen significativamente las fallas inesperadas.
### Herramientas reutilizables
El proceso generó herramientas de automatización, seguimiento y documentación que quedan disponibles para futuros proyectos de DTIC.
---
## 6. Próximos Pasos
1. **Completar el ajuste de impresión** para alcanzar el 100% de funcionalidad.
2. **Estabilizar la configuración de red** del servidor para garantizar continuidad a largo plazo.
3. **Formalizar los procedimientos de respaldo** automatizado para la operación de rutina.
4. **Evaluar el retiro definitivo** de la infraestructura anterior, una vez confirmada la estabilidad completa del nuevo entorno.
---
## 7. Conclusión
La migración del sistema DASUTEN a una infraestructura autónoma demuestra que es posible **lograr una mejora sustancial en estabilidad, autonomía y capacidad de gestión** partiendo de recursos limitados y sin inversión adicional en equipamiento.
El proyecto se encuentra en su tramo final, con el sistema operativo y la base de datos verificada. El proceso, aunque más extenso de lo inicialmente previsto, fue planificado como una transición gradual de testeo a producción que permitió minimizar riesgos y, como valor agregado, generar herramientas y metodologías que benefician al área en su conjunto.
DASUTEN opera hoy de forma independiente en su propia oficina, administrable remotamente por DTIC y preparada para una gestión más ágil, ya sea desde la Facultad Regional o desde Rectorado.
---
*Fin del informe.*
@@ -0,0 +1,132 @@
# INFORME DE AVANCE
## Migración del Sistema DASUTEN a Infraestructura Autónoma
> **Área responsable:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Facultad Regional La Rioja — Universidad Tecnológica Nacional**
> **Fecha:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — DTIC, UTN-FRLR
> **Destinatarios:** [Completar]
> **Versión:** 0.2 (Borrador)
### Historial de versiones
| Versión | Fecha | Cambios |
|:---:|:---:|:---|
| 0.1 | 13/04/2026 | Borrador inicial |
| 0.2 | 13/04/2026 | Se amplía contexto de situación inicial y esquema de trabajo previo |
---
## 1. Contexto y Punto de Partida
El sistema de gestión administrativa de **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) en la Facultad Regional La Rioja funcionaba desde hace años sobre la infraestructura informática compartida de la Facultad.
Cuando la oficina de DASUTEN fue reubicada en un espacio propio, alejado de los servidores de la Facultad, se sumó un problema de conectividad: la señal de red debía recorrer múltiples puntos intermedios dentro del edificio, y la caída de cualquiera de ellos dejaba a la oficina sin acceso al sistema.
### Solución provisoria que se venía aplicando
Para resolver esta situación, se gestionó internamente la baja de una **línea de internet propia** para la oficina DASUTEN, eliminando la dependencia del cableado interno de la Facultad. Sin embargo, esto no era suficiente por sí solo: el sistema seguía requiriendo acceso a los servidores internos de la Facultad para funcionar.
Se implementó entonces un esquema de trabajo remoto donde las usuarias — Andrea Almirón y Romina Molina — accedían desde la PC física de la oficina, a través de un software de escritorio remoto (AnyDesk), a una **PC virtual** alojada en los servidores. Recién desde esa PC virtual podían operar el sistema.
Este esquema, si bien permitió dar continuidad al servicio y brindó la posibilidad de asistencia técnica 24/7 por parte de DTIC, implicaba:
- Una experiencia de trabajo **indirecta** para las usuarias (PC física → escritorio remoto → PC virtual → sistema).
- La necesidad de mantener operativos **cuatro componentes** simultáneamente: un servidor de dominio, un servidor de base de datos, una PC virtual y la PC física con el software de acceso remoto.
- Múltiples puntos de falla que dificultaban el soporte.
---
## 2. Objetivo del Proyecto
Partiendo de esa situación, el objetivo fue diseñar una solución que permita al sistema DASUTEN operar de forma **directa y autónoma** en la propia oficina, con las siguientes premisas:
- **Recursos limitados:** Aprovechar los equipos ya existentes — un equipo de escritorio de prestaciones modestas y un servidor compacto reutilizado. **Sin adquisición de hardware nuevo.**
- **Trabajo directo:** Que las usuarias puedan operar el sistema directamente desde la PC de la oficina, sin depender de conexiones remotas intermedias.
- **No interferir con la oficina:** Que DTIC pueda realizar respaldos, aplicar actualizaciones de Rectorado y resolver problemas **de forma remota y transparente**.
- **Simplicidad:** Una arquitectura lo más simple posible, administrable y confiable en el tiempo.
---
## 3. Qué se logró
### 3.1 La simplificación
Se reemplazó la arquitectura compleja por un esquema directo y simple:
| Aspecto | Antes | Ahora |
|:---|:---:|:---:|
| Componentes necesarios | 4 (dominio + BD + PC virtual + PC física) | 2 (servidor BD + PC física) |
| Forma de trabajo | Indirecta (vía escritorio remoto) | Directa (sistema en la PC) |
| Dependencia de red de Facultad | Sí | No |
| Puntos de falla | Múltiples | Mínimos |
| Requiere presencia física para soporte | Frecuentemente | No |
| Hardware nuevo adquirido | — | Ninguno |
Las usuarias ahora operan el sistema **directamente desde la PC de la oficina**, sin necesidad de conexiones remotas intermedias. El servidor de base de datos funciona dentro de la misma oficina, en la red propia de DASUTEN.
### 3.2 El proceso: de testeo a producción
Si bien el proyecto llevó más tiempo del inicialmente estimado, esto responde a una decisión deliberada: se planificó una **etapa de testeo exhaustivo** antes de llevar el sistema a producción. Cada paso fue validado primero en un entorno virtual de pruebas antes de aplicarse al entorno real, minimizando los riesgos para la operación de la oficina.
**A la fecha, el sistema se encuentra a un paso del 100%**, restando únicamente un ajuste de compatibilidad en la impresión de documentos, para el cual ya se dispone de una solución provisoria.
### 3.3 Valor agregado: herramientas y metodología
Un aspecto destacable es que, más allá del objetivo central, el trabajo realizado **generó herramientas y metodologías de valor** que benefician a la gestión de DTIC en su conjunto:
- **Dashboard web de seguimiento** del proyecto, con visibilidad en tiempo real del progreso.
- **Sistema de bitácoras** con registro detallado de cada intervención técnica.
- **Herramientas de automatización** (Ecosistema ADN) para respaldos, gestión remota y tareas repetitivas.
- **Metodología de trabajo con IA generativa**, que demostró ser altamente productiva y replicable en futuros proyectos.
---
## 4. Estado Actual
| Aspecto | Estado |
|:---|:---:|
| Sistema operativo en oficina | ✅ |
| Base de datos migrada y verificada | ✅ |
| Trabajo directo (sin escritorio remoto) | ✅ |
| Administración remota por DTIC | ✅ |
| Respaldos automatizados | ✅ |
| Impresión de documentos | ⚠️ En ajuste |
El sistema se encuentra **operativo**. La usuaria de la oficina puede acceder directamente al sistema, consultar datos y realizar sus tareas habituales. DTIC administra, respalda y actualiza el sistema de forma remota sin interferir con la oficina.
Resta un ajuste en la impresión, con solución provisoria funcional mientras se resuelve.
---
## 5. Beneficios Alcanzados
- **Trabajo directo:** Las usuarias operan el sistema sin intermediarios, mejorando la experiencia y la productividad.
- **Autonomía operativa:** DASUTEN funciona sobre infraestructura propia, desvinculada de la Facultad y preparada para gestión directa desde Rectorado.
- **Optimización de recursos:** Implementación completa sin inversión en hardware nuevo.
- **Soporte transparente:** DTIC gestiona remotamente sin afectar la operación diaria.
- **Mayor estabilidad:** Al reducir de 4 componentes a 2, se minimizan las fallas.
- **Herramientas reutilizables:** El proceso generó activos aprovechables para otros proyectos de DTIC.
---
## 6. Próximos Pasos
1. Completar el ajuste de impresión para alcanzar el 100% de funcionalidad.
2. Estabilizar la configuración de red del servidor.
3. Formalizar los procedimientos de respaldo automatizado.
4. Evaluar el retiro definitivo de la infraestructura anterior.
---
## 7. Conclusión
La migración del sistema DASUTEN demuestra que es posible **lograr una mejora sustancial en estabilidad, autonomía y experiencia de trabajo** partiendo de recursos limitados y sin inversión adicional.
Se pasó de un esquema indirecto — donde las usuarias dependían de conexiones remotas a través de múltiples componentes para poder trabajar — a un esquema directo y simple donde el sistema funciona en la propia oficina.
El proyecto se encuentra en su tramo final, con el sistema operativo y la base de datos verificada. El proceso generó además herramientas y metodologías que quedan como activo del área para futuros proyectos.
---
*Fin del informe.*
@@ -0,0 +1,130 @@
# INFORME DE AVANCE
## Migración del Sistema DASUTEN a Infraestructura Autónoma
> **Área responsable:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Facultad Regional La Rioja — Universidad Tecnológica Nacional**
> **Fecha:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — DTIC, UTN-FRLR
> **Destinatarios:** [Completar]
> **Versión:** 0.3 (Borrador)
>
> *Este documento es de carácter ejecutivo y narrativo. El detalle técnico completo se encuentra en el documento complementario [02_informe_tecnico_DASUTEN.md](02_informe_tecnico_DASUTEN.md).*
---
## 1. Contexto y Punto de Partida
El sistema de gestión administrativa de **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) en la Facultad Regional La Rioja funcionaba desde hace años sobre la infraestructura informática compartida de la Facultad: servidores, red interna y servicios de dominio que atienden a toda la institución.
Si bien esta configuración fue funcional durante un tiempo, con el correr de los años fue manifestando limitaciones concretas:
- **Fragilidad operativa:** Cualquier incidencia en la red o los servidores de la Facultad —incluso ajena a DASUTEN— impactaba directamente en el funcionamiento del sistema.
- **Complejidad desproporcionada:** La arquitectura heredada requería múltiples servidores virtuales y un controlador de dominio, componentes pensados para entornos de mayor escala que resultaban sobredimensionados para las necesidades de una sola oficina.
- **Dificultad de mantenimiento:** Los respaldos de datos y las actualizaciones periódicas que envía Rectorado requerían coordinación con la infraestructura general de la Facultad, generando demoras y dependencias cruzadas.
- **Limitación de autonomía:** DASUTEN, si bien opera dentro de las instalaciones de la Facultad, responde funcionalmente a **Rectorado (Buenos Aires)**. La dependencia de la infraestructura local dificultaba una eventual gestión directa desde la sede central.
A esta situación se sumó que, al reubicarse la oficina en un espacio propio alejado de los servidores, las fallas de conectividad se agravaron por la cantidad de puntos intermedios en la red. Para dar continuidad al servicio, se gestionó una línea de internet propia y se implementó un esquema de trabajo indirecto donde las usuarias — Andrea Almirón y Romina Molina — accedían mediante escritorio remoto a una PC virtual para poder operar el sistema. Esto implicaba mantener cuatro componentes simultáneamente (servidor de dominio, servidor de base de datos, PC virtual y PC física), multiplicando los puntos de falla.
---
## 2. Objetivo del Proyecto
Diseñar e implementar una solución que permita al sistema DASUTEN operar de forma **completamente autónoma**, teniendo en cuenta:
- **Recursos limitados:** Aprovechar equipos ya existentes — un equipo de escritorio de prestaciones modestas y un servidor compacto reutilizado. **Sin adquisición de hardware nuevo.**
- **No interferir con la oficina:** Que DTIC pueda realizar respaldos, actualizaciones de Rectorado y soporte técnico **de forma remota y transparente**, sin afectar las tareas cotidianas.
- **Trabajo directo:** Que las usuarias operen el sistema directamente desde la PC de la oficina, sin depender de conexiones remotas intermedias.
- **Simplicidad:** Una arquitectura lo más simple posible, administrable y confiable en el tiempo.
---
## 3. Qué se logró
### 3.1 La simplificación
Se reemplazó la arquitectura compleja por un esquema directo y simple:
| Aspecto | Antes | Ahora |
|:---|:---:|:---:|
| Componentes necesarios | 4 | 2 |
| Forma de trabajo | Indirecta (vía escritorio remoto) | Directa (sistema en la PC) |
| Dependencia de red de Facultad | Sí | No |
| Puntos de falla | Múltiples | Mínimos |
| Requiere presencia física para soporte | Frecuentemente | No |
| Hardware nuevo adquirido | — | Ninguno |
Las usuarias ahora operan el sistema **directamente desde la PC de la oficina**. El servidor de base de datos funciona dentro de la misma oficina, en la red propia de DASUTEN, sin intermediarios.
### 3.2 El proceso: de testeo a producción
Si bien el proyecto llevó más tiempo del inicialmente estimado, esto responde a una decisión deliberada: se planificó una **etapa de testeo exhaustivo** antes de llevar el sistema a producción. Cada paso fue validado primero en un entorno de pruebas antes de aplicarse al entorno real, minimizando riesgos para la operación de la oficina.
**A la fecha, el sistema se encuentra a un paso del 100%**, restando únicamente un ajuste de compatibilidad en la impresión de documentos, para el cual ya se dispone de una solución provisoria.
### 3.3 Valor agregado: herramientas y metodología
Más allá del objetivo central, el trabajo realizado **generó herramientas y metodologías de valor** que benefician a la gestión de DTIC en su conjunto:
- **Dashboard web de seguimiento** del proyecto, con visibilidad en tiempo real del progreso.
- **Sistema de bitácoras** con registro detallado y auditable de cada intervención técnica.
- **Herramientas de automatización** (Ecosistema ADN) para respaldos, gestión remota y tareas repetitivas.
- **Metodología de trabajo con IA generativa**, altamente productiva y replicable en futuros proyectos.
---
## 4. Estado Actual
| Aspecto | Estado |
|:---|:---:|
| Sistema operativo en oficina | ✅ |
| Base de datos migrada y verificada | ✅ |
| Trabajo directo (sin escritorio remoto) | ✅ |
| Administración remota por DTIC | ✅ |
| Respaldos automatizados | ✅ |
| Impresión de documentos | ⚠️ En ajuste |
El sistema se encuentra **operativo**. La usuaria accede directamente al sistema desde la PC de la oficina. DTIC administra y respalda de forma remota sin interferir con la operación diaria.
Resta un ajuste en la impresión, con solución provisoria funcional.
---
## 5. Beneficios Alcanzados
- **Trabajo directo:** Las usuarias operan el sistema sin intermediarios.
- **Autonomía operativa:** Infraestructura propia, desvinculada de la Facultad, preparada para gestión desde Rectorado.
- **Optimización de recursos:** Sin inversión en hardware nuevo.
- **Soporte transparente:** DTIC gestiona remotamente sin afectar la oficina.
- **Mayor estabilidad:** De 4 componentes a 2, menos fallas posibles.
- **Herramientas reutilizables:** Activos aprovechables para otros proyectos de DTIC.
---
## 6. Próximos Pasos
1. Completar el ajuste de impresión para alcanzar el 100% de funcionalidad.
2. Estabilizar la configuración de red del servidor.
3. Formalizar los procedimientos de respaldo automatizado.
4. Evaluar el retiro definitivo de la infraestructura anterior.
---
## 7. Conclusión
La migración del sistema DASUTEN demuestra que es posible **lograr una mejora sustancial en estabilidad, autonomía y experiencia de trabajo** partiendo de recursos limitados y sin inversión adicional.
Se pasó de un esquema indirecto y complejo a uno directo y simple, donde el sistema funciona en la propia oficina. El proceso generó además herramientas y metodologías que quedan como activo del área para futuros proyectos.
DASUTEN opera hoy de forma independiente, administrable remotamente por DTIC y preparada para una gestión más ágil desde la Facultad Regional o desde Rectorado.
---
### Historial de versiones
| Versión | Fecha | Cambios |
|:---:|:---:|:---|
| 0.1 | 13/04/2026 | Borrador inicial |
| 0.2 | 13/04/2026 | Se amplía contexto de situación inicial |
| 0.3 | 13/04/2026 | Se retoma enfoque narrativo, se integra contexto como complemento, se agrega referencia al documento técnico |
*Fin del informe.*
@@ -0,0 +1,407 @@
# INFORME TÉCNICO — Migración del Sistema DASUTEN a Nuevo Servidor
> **Área:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Proyecto:** Migración de infraestructura DASUTEN — Modo Workgroup sin Domain Controller
> **Código interno:** A04.P005
> **Fecha del informe:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — Área DTIC, UTN-FRLR
---
## 1. Resumen Ejecutivo
Se informa el avance de la migración del sistema de gestión administrativa **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) desde la infraestructura compartida de la Facultad hacia un entorno independiente, autónomo y simplificado.
**Estado general del proyecto: 95% completado.**
El sistema DASUTEN se encuentra **operativo** en la nueva infraestructura desde el día 13 de abril de 2026, habiendo superado exitosamente las pruebas funcionales de acceso a datos. Resta únicamente resolver un problema de compatibilidad de impresión vinculado a las plantillas de Office utilizadas por el sistema.
### Logros principales
- ✅ Servidor de base de datos **completamente migrado** a infraestructura nueva.
- ✅ Base de datos de producción **restaurada y verificada** sin errores de integridad.
- ✅ Sistema DASUTEN **desplegado y operativo** en la PC física de la usuaria.
- ✅ Eliminación exitosa de la dependencia del **Active Directory (Domain Controller)**.
- ✅ Simplificación de la arquitectura de red — de 3 servidores virtuales a 1.
- ⚠️ Pendiente: resolución de problema de impresión por compatibilidad de plantillas Office.
---
## 2. Antecedentes y Motivación
### 2.1 Situación previa
El sistema DASUTEN operaba sobre infraestructura compartida con la Facultad Regional La Rioja, dependiendo de:
- Un servidor SQL Server alojado en la red interna de la Facultad (`srvv-fenix`).
- Un Domain Controller Active Directory para la autenticación de usuarios.
- Conectividad de red interna de la Facultad con routing complejo.
Esta configuración generaba **múltiples puntos de falla**:
- Dependencia del dominio institucional para la autenticación.
- Complejidad de red con subnetting privado, NAT y gateway interno.
- Cualquier cambio en la infraestructura de la Facultad impactaba directamente en DASUTEN.
### 2.2 Objetivo del proyecto
**Migrar el sistema DASUTEN a una infraestructura independiente y simplificada**, eliminando la dependencia del Domain Controller (Active Directory) y utilizando un esquema de red **Workgroup** directo, con el fin de:
1. Reducir la complejidad operativa.
2. Minimizar los puntos de falla.
3. Garantizar la autonomía operativa de DASUTEN respecto de la infraestructura de la Facultad.
4. Facilitar la eventual gestión desde Rectorado (Buenos Aires) si fuera necesario.
---
## 3. Infraestructura Implementada
### 3.1 Arquitectura de red
Se implementó una topología de **red plana simplificada**, donde todos los equipos obtienen direccionamiento IP directamente del router del ISP, eliminando subnetting privado y routing interno:
```
Internet
Router ISP (192.168.1.1 — Gateway)
├── srv-dasu (Proxmox VE) → 192.168.1.13
│ └── dasu-sql4 (VM 106) → 192.168.1.11 [SQL Server 2019]
└── dasu-pc (PC Física) → DHCP ISP [Estación de trabajo]
```
### 3.2 Componentes del nuevo entorno
| Componente | Descripción | Estado |
|:---|:---|:---:|
| **srv-dasu** | Servidor Proxmox VE 9.1 (Hipervisor) — CPU Intel, 15 GB RAM, 94 GB disco | ✅ Operativo |
| **dasu-sql4** (VM 106) | Windows Server 2022 — SQL Server 2019 Enterprise — 4 GB RAM, 2 vCPU, discos 60+120 GB | ✅ Operativo |
| **dasu-pc** | PC Física — Windows 10 Pro — AMD Ryzen 5, 8 GB RAM — Oficina DASUTEN | ✅ Operativo |
### 3.3 Comparativa: Antes vs. Después
| Aspecto | Enfoque Anterior (con DC) | Enfoque Actual (Workgroup) |
|:---|:---|:---|
| **Red** | Privada 10.0.100.x (gateway interno) | DHCP del ISP (red plana) |
| **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) |
| **Usuarios** | Active Directory | Usuarios locales Windows |
| **DNS** | AD DNS (10.0.100.10) | DNS del ISP (automático) |
| **Complejidad de red** | Alta (DC + DNS + dominio + routing) | Mínima (red plana ISP) |
| **Puntos de falla** | DC, SQL, red dominio, routing | Solo SQL |
| **VMs necesarias** | 3 (DC + SQL + PC) | 1 (solo SQL) |
---
## 4. Fases Ejecutadas y Progreso
### Resumen de progreso por fase
| Fase | Descripción | Estado | Progreso |
|:---:|:---|:---:|:---:|
| 1 | Preparación del entorno (srv-dasu) | ✅ Completada | 100% |
| 2 | Creación de VM SQL Server de prueba | ✅ Completada | 100% |
| 3 | Creación de VM PC Cliente de prueba | ✅ Completada | 100% |
| 4 | VM definitiva dasu-sql4 | ✅ Completada | 100% |
| 5 | Migración de base de datos | ✅ Completada | 100% |
| 5b | Hardening y persistencia de servicios | ✅ Completada | 100% |
| 6 | Validación con PC cliente virtual | ✅ Completada | 100% |
| 7 | Despliegue en PC física (producción) | 🚧 En curso | 75% |
| 8 | Refresco final de base de datos | ✅ Completada | 100% |
### 4.1 Fase 1-3: Preparación y pruebas de concepto (27-28 marzo)
Se preparó el hipervisor Proxmox, se reconfiguró el bridge de red para usar DHCP directo del ISP, y se crearon VMs de prueba para validar la viabilidad del enfoque sin Domain Controller.
**Hallazgo crítico:** Se detectó y resolvió que el bridge de red estaba configurado con la IP de una red privada anterior, lo cual habría impedido la conectividad de las nuevas VMs.
### 4.2 Fase 4: Servidor SQL definitivo (9-10 abril)
Se creó la VM definitiva `dasu-sql4` (VM 106) con una arquitectura optimizada:
- **Discos separados:** 60 GB para SO + 120 GB exclusivo para datos SQL (buena práctica).
- **SQL Server 2019 Enterprise** instalado con autenticación mixta.
- **Gestión remota** asegurada vía OpenSSH Server (puerto 7022) y Tailscale VPN.
### 4.3 Fase 5: Migración de Base de Datos (11 abril)
Se ejecutó la migración completa de la base de datos de producción:
1. **Exportación:** Backup comprimido (~1.33 GB) generado desde el servidor origen (`srvv-fenix`).
2. **Transferencia:** Archivo movido a través de la VPN institucional (Tailscale) en múltiples tramos seguros.
3. **Restauración:** Base restaurada exitosamente en 26 segundos (354 MB/s).
4. **Verificación:** `DBCC CHECKDB` ejecutado sin errores — integridad de datos confirmada al 100%.
### 4.4 Fase 5b: Hardening (11 abril)
Se validó que todos los servicios críticos (SSH, SQL Server) sobreviven reinicios del servidor sin intervención manual:
- OpenSSH Server: arranque automático ✅
- SQL Server: arranque automático ✅
- IP de red: mantenida tras reinicio ✅
### 4.5 Fase 6: Validación funcional en entorno virtual (11 abril)
Se restauró una VM cliente desde backup para simular el entorno real de la oficina DASUTEN:
- Se desvinculó la PC del dominio anterior y se unió al Workgroup `DASUTEN`.
- Se reconfiguró el archivo de conexión del sistema (`Kermet.ini`) para apuntar al nuevo servidor SQL.
- **Resultado:** El sistema DASUTEN conectó exitosamente, actualizó estructuras y descargó novedades.
**Conclusión técnica clave:** Se determinó mediante ingeniería inversa que el sistema DASUTEN (desarrollado en Visual FoxPro) **no tiene dependencia real del Active Directory** si se configura explícitamente la autenticación SQL en su archivo de configuración.
### 4.6 Fase 7: Despliegue en PC física de producción (13 abril — EN CURSO)
Se desplegó el sistema en la PC física de la oficina DASUTEN (`dasu-pc`):
- **Directorio del sistema** (`C:\SysDasuten`) transferido desde la VM de pruebas.
- **Configuración de conexión** apuntada al nuevo servidor `dasu-sql4`.
- **Acceso directo** creado en el escritorio de la usuaria (`aalmiron`).
- **Pruebas funcionales:**
- Inicio del sistema: ✅ OK
- Acceso a datos (ventana de bonos): ✅ OK
- **Impresión: ❌ FALLO** — Error al imprimir, posiblemente por incompatibilidad de las plantillas de Office (el sistema usa automatización OLE con Word/Excel).
### 4.7 Fase 8: Refresco final de base de datos (13 abril)
Se ejecutó un refresco automatizado de la base de datos para asegurar que el sistema arranca con los datos más actualizados del servidor de origen:
- Backup generado automáticamente desde el origen (44.8 segundos).
- Transferido de manera segura (SMB + SCP por VPN).
- Restaurado con `RESTORE DATABASE ... WITH REPLACE`.
- Integridad verificada con `DBCC CHECKDB` — sin errores.
---
## 5. Estado Actual
### 5.1 Funcionalidades operativas
| Funcionalidad | Estado |
|:---|:---:|
| Acceso al sistema DASUTEN | ✅ Operativo |
| Consulta de datos (bonos, registros) | ✅ Operativo |
| Conexión a base de datos SQL Server | ✅ Operativo |
| Acceso remoto para soporte (Tailscale/SSH) | ✅ Operativo |
| Impresión desde el sistema | ❌ Pendiente |
### 5.2 Problema pendiente: Impresión
**Descripción:** Al intentar imprimir desde el sistema DASUTEN en la PC física, se produce un error. El diagnóstico preliminar indica que la causa es una incompatibilidad entre las **plantillas de Office** requeridas por el sistema (diseñadas para Office 2003/2007) y la versión de Office actualmente instalada en la PC.
**Impacto:** La usuaria no puede generar documentos impresos desde el nuevo sistema.
**Workaround temporal:** Se habilitó acceso remoto (TeamViewer) desde la PC física hacia la VM de pruebas (`dasu-pcv`), donde la impresión funciona correctamente. La usuaria puede continuar operando mientras se resuelve el problema.
**Plan de resolución:** Verificar y ajustar la versión/configuración de Office en `dasu-pc` para compatibilizarla con las plantillas del sistema.
---
## 6. Beneficios Obtenidos
1. **Independencia operativa:** DASUTEN ya no depende de la infraestructura de red ni de los servidores de la Facultad.
2. **Simplificación:** Se pasó de 3 VMs + Domain Controller a solo 1 VM (SQL Server), reduciendo significativamente la complejidad.
3. **Reducción de puntos de falla:** De múltiples (DC, DNS institucional, routing complejo) a uno solo (SQL Server).
4. **Acceso remoto robusto:** Gestión técnica asegurada vía VPN (Tailscale) y SSH, sin necesidad de presencia física.
5. **Autonomía:** La infraestructura puede ser gestionada independientemente, facilitando una eventual transferencia a Rectorado.
6. **Preservación del entorno anterior:** Las VMs del enfoque con DC fueron apagadas y preservadas como respaldo, permitiendo un rollback si fuera necesario.
---
## 7. Cronograma Resumen
| Fecha | Hito |
|:---|:---|
| 27/03/2026 | Inicio — Preparación del entorno e inicio de pruebas de concepto |
| 28/03/2026 | VM SQL Server de prueba operativa |
| 09-10/04/2026 | Servidor SQL definitivo (dasu-sql4) desplegado y operativo |
| 11/04/2026 | Base de datos migrada, verificada y hardening completado |
| 11/04/2026 | Validación funcional exitosa en entorno virtual |
| 13/04/2026 | Despliegue en PC física de producción — sistema operativo con observación de impresión |
---
## 8. Próximos Pasos
| Prioridad | Acción | Plazo estimado |
|:---:|:---|:---|
| 🔴 Alta | Resolver problema de impresión (compatibilidad Office/plantillas) | 1-3 días hábiles |
| 🟡 Media | Reservar IPs fijas en router ISP (prevenir cambios por DHCP) | 1 semana |
| 🟢 Baja | Evaluar apagado definitivo de VMs legacy (DC, SQL antiguo) | Tras validación completa |
| 🟢 Baja | Documentar procedimiento de respaldo automatizado de la nueva BD | 2 semanas |
---
## 9. Conclusión
La migración del sistema DASUTEN a la nueva infraestructura se encuentra **prácticamente completada**, con el sistema operativo y los datos verificados. El enfoque sin Domain Controller demostró ser completamente viable, logrando una **reducción significativa de la complejidad** y de los puntos de falla.
El único aspecto pendiente es la resolución del problema de impresión, para el cual ya se dispone de un workaround funcional que permite a la usuaria continuar operando sin interrupción.
Se estima que el proyecto quedará **100% finalizado** dentro de los próximos días hábiles, una vez resuelta la compatibilidad de plantillas de Office.
---
# ANEXO TÉCNICO
## A. Inventario de Nodos
### A.1 Servidor Hipervisor: srv-dasu
| Campo | Valor |
|:---|:---|
| **Rol** | Hipervisor Proxmox VE 9.1 |
| **IP LAN** | 192.168.1.13 (DHCP ISP) |
| **IP VPN** | 100.112.46.104 (Tailscale) |
| **Bridge** | vmbr0 sobre enp33s0 |
| **RAM** | 15 GB total / ~4 GB libre post-VMs |
| **Disco** | 94 GB (68% uso, thin provisioned) |
| **Acceso remoto** | Tailscale + SSH |
### A.2 Servidor SQL: dasu-sql4 (VM 106)
| Campo | Valor |
|:---|:---|
| **Rol** | SQL Server 2019 Enterprise |
| **SO** | Windows Server 2022 (Español) |
| **IP LAN** | 192.168.1.11 (DHCP ISP) |
| **IP VPN** | 100.107.15.82 (Tailscale — inestable) |
| **Workgroup** | DASUTEN |
| **RAM** | 4 GB |
| **vCPU** | 2 |
| **Disco 1 (sata0)** | 60 GB — Sistema Operativo |
| **Disco 2 (sata1)** | 120 GB — Datos SQL (GPT/NTFS "SQLData") |
| **Estructura SQL** | F:\DATA, F:\LOG, F:\BACKUP, F:\TEMPDB |
| **Autenticación** | Mixta (SQL Auth + Windows) |
| **Puerto SQL** | TCP 1433 |
| **SSH** | Puerto 7022 (OpenSSH Server, arranque automático) |
| **Servicios auto-start** | MSSQLSERVER, sshd |
### A.3 PC Física Cliente: dasu-pc
| Campo | Valor |
|:---|:---|
| **Rol** | Estación de trabajo DASUTEN |
| **Patrimonio** | 32120 |
| **Ubicación** | Oficina DASUTEN (externa a Facultad) |
| **SO** | Windows 10 Pro |
| **Workgroup** | DASUTEN |
| **IP VPN** | 100.119.233.11 (Tailscale) |
| **SSH** | UTNLR@100.119.233.11:7022 |
| **CPU** | AMD Ryzen 5 4600G @ 4.30GHz (6 núcleos) |
| **RAM** | 8 GB DDR4-3200 |
| **Placa Base** | Asus Prime A320M-K |
| **Usuaria principal** | Andrea Almirón (`aalmiron`) |
| **Directorio DASUTEN** | C:\SysDasuten |
| **Config conexión** | C:\SysDasuten\Sistema\Kermet.ini |
### A.4 VM Cliente de Pruebas: dasu-pcv (VM 107) — Test-Bed
| Campo | Valor |
|:---|:---|
| **Rol** | Entorno de pruebas / fallback temporal |
| **SO** | Windows 10 |
| **Workgroup** | DASUTEN |
| **SSH** | Puerto 7022 |
| **Estado** | Operativa (usada como fallback vía TeamViewer) |
---
## B. Configuración del Sistema DASUTEN
### B.1 Archivo de conexión: Kermet.ini
Parámetros clave configurados para la operación sin Domain Controller:
```ini
SERVER=dasu-sql4
DATABASE=sysdasuten
UID=sa
PWD=[credencial almacenada en bóveda]
```
> **Nota técnica:** El campo `UID` debe estar explícitamente configurado con `sa` (u otra cuenta SQL Auth). Si se deja vacío, el sistema DASUTEN (Visual FoxPro) asume autenticación Windows integrada (SSPI/Kerberos), lo cual falla en entorno Workgroup sin Domain Controller.
### B.2 Directorio del sistema
```
C:\SysDasuten\
├── Sistema\ → Ejecutables VFP + Kermet.ini
├── Plantillas\ → Templates Word/Excel para impresión
└── [otros directorios de la aplicación]
```
---
## C. Base de Datos: sysdasuten
| Campo | Valor |
|:---|:---|
| **Nombre** | sysdasuten |
| **Motor** | SQL Server 2019 Enterprise |
| **Tamaño backup comprimido** | ~1.33 GB |
| **Nivel de compatibilidad** | 100 |
| **Recovery model** | FULL |
| **Integridad (DBCC CHECKDB)** | ✅ Sin errores |
| **Tiempo de restauración** | 26 segundos (354 MB/s) |
| **Origen de datos** | srvv-fenix (servidor legacy de Facultad) |
| **Último refresco** | 13/04/2026 |
### C.1 Ruta de transferencia del backup
```
srvv-fenix (Origen)
│ [BACKUP DATABASE ... WITH COMPRESSION]
│ → E:\BK_SQL\sysdasuten_compressed_ADN.bak (~1.33 GB)
srv-ns8 (Tránsito)
│ [SMB/smbget vía dominio]
│ → /var/tmp/
srv-dasu (Tránsito)
│ [SCP vía Tailscale]
dasu-sql4 (Destino)
│ [RESTORE DATABASE ... WITH REPLACE]
└ → F:\BACKUP\ → F:\DATA\ + F:\LOG\
```
---
## D. VMs Legacy Preservadas (Apagadas)
| VM ID | Nombre | Rol anterior | Estado |
|:---:|:---|:---|:---:|
| 100 | dasu-srvv-dc | Domain Controller AD DS | 🛑 Apagada |
| 101 | dasu-srvv-sql | SQL Server (dominio) | 🛑 Apagada |
| 102 | dasu-pcv | PC cliente (dominio) | 🛑 Apagada |
| 103 | dasu-sql2 | SQL Server prueba v1 | 🛑 Descartada |
| 104 | dasu-pcv2 | PC prueba v1 | 🛑 Descartada |
> Las VMs legacy (100-102) se mantienen apagadas como respaldo. Pueden reactivarse en caso de necesitar un rollback al enfoque con Domain Controller.
---
## E. Accesos Remotos Configurados
| Nodo | Método | Detalle |
|:---|:---|:---|
| srv-dasu | Tailscale SSH | 100.112.46.104 |
| dasu-sql4 | SSH (LAN) | 192.168.1.11:7022 |
| dasu-sql4 | Tailscale | 100.107.15.82 (inestable) |
| dasu-pc | Tailscale SSH | 100.119.233.11:7022 |
| dasu-pc | AnyDesk | Instalado (GUI) |
| dasu-pcv | TeamViewer | Activo (workaround temporal) |
---
## F. Cuenta de Gestión VPN
| Campo | Valor |
|:---|:---|
| **Proveedor** | Tailscale |
| **Cuenta** | pcdasu0@frlr.utn.edu.ar |
| **Nodos vinculados** | srv-dasu, dasu-sql4, dasu-pc, dasu-pcv |
---
*Fin del informe técnico.*
@@ -0,0 +1,407 @@
# INFORME TÉCNICO — Migración del Sistema DASUTEN a Nuevo Servidor
> **Área:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Proyecto:** Migración de infraestructura DASUTEN — Modo Workgroup sin Domain Controller
> **Código interno:** A04.P005
> **Fecha del informe:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — Área DTIC, UTN-FRLR
---
## 1. Resumen Ejecutivo
Se informa el avance de la migración del sistema de gestión administrativa **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) desde la infraestructura compartida de la Facultad hacia un entorno independiente, autónomo y simplificado.
**Estado general del proyecto: 95% completado.**
El sistema DASUTEN se encuentra **operativo** en la nueva infraestructura desde el día 13 de abril de 2026, habiendo superado exitosamente las pruebas funcionales de acceso a datos. Resta únicamente resolver un problema de compatibilidad de impresión vinculado a las plantillas de Office utilizadas por el sistema.
### Logros principales
- ✅ Servidor de base de datos **completamente migrado** a infraestructura nueva.
- ✅ Base de datos de producción **restaurada y verificada** sin errores de integridad.
- ✅ Sistema DASUTEN **desplegado y operativo** en la PC física de la usuaria.
- ✅ Eliminación exitosa de la dependencia del **Active Directory (Domain Controller)**.
- ✅ Simplificación de la arquitectura de red — de 3 servidores virtuales a 1.
- ⚠️ Pendiente: resolución de problema de impresión por compatibilidad de plantillas Office.
---
## 2. Antecedentes y Motivación
### 2.1 Situación previa
El sistema DASUTEN operaba sobre infraestructura compartida con la Facultad Regional La Rioja, dependiendo de:
- Un servidor SQL Server alojado en la red interna de la Facultad (`srvv-fenix`).
- Un Domain Controller Active Directory para la autenticación de usuarios.
- Conectividad de red interna de la Facultad con routing complejo.
Esta configuración generaba **múltiples puntos de falla**:
- Dependencia del dominio institucional para la autenticación.
- Complejidad de red con subnetting privado, NAT y gateway interno.
- Cualquier cambio en la infraestructura de la Facultad impactaba directamente en DASUTEN.
### 2.2 Objetivo del proyecto
**Migrar el sistema DASUTEN a una infraestructura independiente y simplificada**, eliminando la dependencia del Domain Controller (Active Directory) y utilizando un esquema de red **Workgroup** directo, con el fin de:
1. Reducir la complejidad operativa.
2. Minimizar los puntos de falla.
3. Garantizar la autonomía operativa de DASUTEN respecto de la infraestructura de la Facultad.
4. Facilitar la eventual gestión desde Rectorado (Buenos Aires) si fuera necesario.
---
## 3. Infraestructura Implementada
### 3.1 Arquitectura de red
Se implementó una topología de **red plana simplificada**, donde todos los equipos obtienen direccionamiento IP directamente del router del ISP, eliminando subnetting privado y routing interno:
```
Internet
Router ISP (192.168.1.1 — Gateway)
├── srv-dasu (Proxmox VE) → 192.168.1.13
│ └── dasu-sql4 (VM 106) → 192.168.1.11 [SQL Server 2019]
└── dasu-pc (PC Física) → DHCP ISP [Estación de trabajo]
```
### 3.2 Componentes del nuevo entorno
| Componente | Descripción | Estado |
|:---|:---|:---:|
| **srv-dasu** | Servidor Proxmox VE 9.1 (Hipervisor) — CPU Intel, 15 GB RAM, 94 GB disco | ✅ Operativo |
| **dasu-sql4** (VM 106) | Windows Server 2022 — SQL Server 2019 Enterprise — 4 GB RAM, 2 vCPU, discos 60+120 GB | ✅ Operativo |
| **dasu-pc** | PC Física — Windows 10 Pro — AMD Ryzen 5, 8 GB RAM — Oficina DASUTEN | ✅ Operativo |
### 3.3 Comparativa: Antes vs. Después
| Aspecto | Enfoque Anterior (con DC) | Enfoque Actual (Workgroup) |
|:---|:---|:---|
| **Red** | Privada 10.0.100.x (gateway interno) | DHCP del ISP (red plana) |
| **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) |
| **Usuarios** | Active Directory | Usuarios locales Windows |
| **DNS** | AD DNS (10.0.100.10) | DNS del ISP (automático) |
| **Complejidad de red** | Alta (DC + DNS + dominio + routing) | Mínima (red plana ISP) |
| **Puntos de falla** | DC, SQL, red dominio, routing | Solo SQL |
| **VMs necesarias** | 3 (DC + SQL + PC) | 1 (solo SQL) |
---
## 4. Fases Ejecutadas y Progreso
### Resumen de progreso por fase
| Fase | Descripción | Estado | Progreso |
|:---:|:---|:---:|:---:|
| 1 | Preparación del entorno (srv-dasu) | ✅ Completada | 100% |
| 2 | Creación de VM SQL Server de prueba | ✅ Completada | 100% |
| 3 | Creación de VM PC Cliente de prueba | ✅ Completada | 100% |
| 4 | VM definitiva dasu-sql4 | ✅ Completada | 100% |
| 5 | Migración de base de datos | ✅ Completada | 100% |
| 5b | Hardening y persistencia de servicios | ✅ Completada | 100% |
| 6 | Validación con PC cliente virtual | ✅ Completada | 100% |
| 7 | Despliegue en PC física (producción) | 🚧 En curso | 75% |
| 8 | Refresco final de base de datos | ✅ Completada | 100% |
### 4.1 Fase 1-3: Preparación y pruebas de concepto (27-28 marzo)
Se preparó el hipervisor Proxmox, se reconfiguró el bridge de red para usar DHCP directo del ISP, y se crearon VMs de prueba para validar la viabilidad del enfoque sin Domain Controller.
**Hallazgo crítico:** Se detectó y resolvió que el bridge de red estaba configurado con la IP de una red privada anterior, lo cual habría impedido la conectividad de las nuevas VMs.
### 4.2 Fase 4: Servidor SQL definitivo (9-10 abril)
Se creó la VM definitiva `dasu-sql4` (VM 106) con una arquitectura optimizada:
- **Discos separados:** 60 GB para SO + 120 GB exclusivo para datos SQL (buena práctica).
- **SQL Server 2019 Enterprise** instalado con autenticación mixta.
- **Gestión remota** asegurada vía OpenSSH Server (puerto 7022) y Tailscale VPN.
### 4.3 Fase 5: Migración de Base de Datos (11 abril)
Se ejecutó la migración completa de la base de datos de producción:
1. **Exportación:** Backup comprimido (~1.33 GB) generado desde el servidor origen (`srvv-fenix`).
2. **Transferencia:** Archivo movido a través de la VPN institucional (Tailscale) en múltiples tramos seguros.
3. **Restauración:** Base restaurada exitosamente en 26 segundos (354 MB/s).
4. **Verificación:** `DBCC CHECKDB` ejecutado sin errores — integridad de datos confirmada al 100%.
### 4.4 Fase 5b: Hardening (11 abril)
Se validó que todos los servicios críticos (SSH, SQL Server) sobreviven reinicios del servidor sin intervención manual:
- OpenSSH Server: arranque automático ✅
- SQL Server: arranque automático ✅
- IP de red: mantenida tras reinicio ✅
### 4.5 Fase 6: Validación funcional en entorno virtual (11 abril)
Se restauró una VM cliente desde backup para simular el entorno real de la oficina DASUTEN:
- Se desvinculó la PC del dominio anterior y se unió al Workgroup `DASUTEN`.
- Se reconfiguró el archivo de conexión del sistema (`Kermet.ini`) para apuntar al nuevo servidor SQL.
- **Resultado:** El sistema DASUTEN conectó exitosamente, actualizó estructuras y descargó novedades.
**Conclusión técnica clave:** Se determinó mediante ingeniería inversa que el sistema DASUTEN (desarrollado en Visual FoxPro) **no tiene dependencia real del Active Directory** si se configura explícitamente la autenticación SQL en su archivo de configuración.
### 4.6 Fase 7: Despliegue en PC física de producción (13 abril — EN CURSO)
Se desplegó el sistema en la PC física de la oficina DASUTEN (`dasu-pc`):
- **Directorio del sistema** (`C:\SysDasuten`) transferido desde la VM de pruebas.
- **Configuración de conexión** apuntada al nuevo servidor `dasu-sql4`.
- **Acceso directo** creado en el escritorio de la usuaria (`aalmiron`).
- **Pruebas funcionales:**
- Inicio del sistema: ✅ OK
- Acceso a datos (ventana de bonos): ✅ OK
- **Impresión: ❌ FALLO** — Error al imprimir, posiblemente por incompatibilidad de las plantillas de Office (el sistema usa automatización OLE con Word/Excel).
### 4.7 Fase 8: Refresco final de base de datos (13 abril)
Se ejecutó un refresco automatizado de la base de datos para asegurar que el sistema arranca con los datos más actualizados del servidor de origen:
- Backup generado automáticamente desde el origen (44.8 segundos).
- Transferido de manera segura (SMB + SCP por VPN).
- Restaurado con `RESTORE DATABASE ... WITH REPLACE`.
- Integridad verificada con `DBCC CHECKDB` — sin errores.
---
## 5. Estado Actual
### 5.1 Funcionalidades operativas
| Funcionalidad | Estado |
|:---|:---:|
| Acceso al sistema DASUTEN | ✅ Operativo |
| Consulta de datos (bonos, registros) | ✅ Operativo |
| Conexión a base de datos SQL Server | ✅ Operativo |
| Acceso remoto para soporte (Tailscale/SSH) | ✅ Operativo |
| Impresión desde el sistema | ❌ Pendiente |
### 5.2 Problema pendiente: Impresión
**Descripción:** Al intentar imprimir desde el sistema DASUTEN en la PC física, se produce un error. El diagnóstico preliminar indica que la causa es una incompatibilidad entre las **plantillas de Office** requeridas por el sistema (diseñadas para Office 2003/2007) y la versión de Office actualmente instalada en la PC.
**Impacto:** La usuaria no puede generar documentos impresos desde el nuevo sistema.
**Workaround temporal:** Se habilitó acceso remoto (TeamViewer) desde la PC física hacia la VM de pruebas (`dasu-pcv`), donde la impresión funciona correctamente. La usuaria puede continuar operando mientras se resuelve el problema.
**Plan de resolución:** Verificar y ajustar la versión/configuración de Office en `dasu-pc` para compatibilizarla con las plantillas del sistema.
---
## 6. Beneficios Obtenidos
1. **Independencia operativa:** DASUTEN ya no depende de la infraestructura de red ni de los servidores de la Facultad.
2. **Simplificación:** Se pasó de 3 VMs + Domain Controller a solo 1 VM (SQL Server), reduciendo significativamente la complejidad.
3. **Reducción de puntos de falla:** De múltiples (DC, DNS institucional, routing complejo) a uno solo (SQL Server).
4. **Acceso remoto robusto:** Gestión técnica asegurada vía VPN (Tailscale) y SSH, sin necesidad de presencia física.
5. **Autonomía:** La infraestructura puede ser gestionada independientemente, facilitando una eventual transferencia a Rectorado.
6. **Preservación del entorno anterior:** Las VMs del enfoque con DC fueron apagadas y preservadas como respaldo, permitiendo un rollback si fuera necesario.
---
## 7. Cronograma Resumen
| Fecha | Hito |
|:---|:---|
| 27/03/2026 | Inicio — Preparación del entorno e inicio de pruebas de concepto |
| 28/03/2026 | VM SQL Server de prueba operativa |
| 09-10/04/2026 | Servidor SQL definitivo (dasu-sql4) desplegado y operativo |
| 11/04/2026 | Base de datos migrada, verificada y hardening completado |
| 11/04/2026 | Validación funcional exitosa en entorno virtual |
| 13/04/2026 | Despliegue en PC física de producción — sistema operativo con observación de impresión |
---
## 8. Próximos Pasos
| Prioridad | Acción | Plazo estimado |
|:---:|:---|:---|
| 🔴 Alta | Resolver problema de impresión (compatibilidad Office/plantillas) | 1-3 días hábiles |
| 🟡 Media | Reservar IPs fijas en router ISP (prevenir cambios por DHCP) | 1 semana |
| 🟢 Baja | Evaluar apagado definitivo de VMs legacy (DC, SQL antiguo) | Tras validación completa |
| 🟢 Baja | Documentar procedimiento de respaldo automatizado de la nueva BD | 2 semanas |
---
## 9. Conclusión
La migración del sistema DASUTEN a la nueva infraestructura se encuentra **prácticamente completada**, con el sistema operativo y los datos verificados. El enfoque sin Domain Controller demostró ser completamente viable, logrando una **reducción significativa de la complejidad** y de los puntos de falla.
El único aspecto pendiente es la resolución del problema de impresión, para el cual ya se dispone de un workaround funcional que permite a la usuaria continuar operando sin interrupción.
Se estima que el proyecto quedará **100% finalizado** dentro de los próximos días hábiles, una vez resuelta la compatibilidad de plantillas de Office.
---
# ANEXO TÉCNICO
## A. Inventario de Nodos
### A.1 Servidor Hipervisor: srv-dasu
| Campo | Valor |
|:---|:---|
| **Rol** | Hipervisor Proxmox VE 9.1 |
| **IP LAN** | 192.168.1.13 (DHCP ISP) |
| **IP VPN** | 100.112.46.104 (Tailscale) |
| **Bridge** | vmbr0 sobre enp33s0 |
| **RAM** | 15 GB total / ~4 GB libre post-VMs |
| **Disco** | 94 GB (68% uso, thin provisioned) |
| **Acceso remoto** | Tailscale + SSH |
### A.2 Servidor SQL: dasu-sql4 (VM 106)
| Campo | Valor |
|:---|:---|
| **Rol** | SQL Server 2019 Enterprise |
| **SO** | Windows Server 2022 (Español) |
| **IP LAN** | 192.168.1.11 (DHCP ISP) |
| **IP VPN** | 100.107.15.82 (Tailscale — inestable) |
| **Workgroup** | DASUTEN |
| **RAM** | 4 GB |
| **vCPU** | 2 |
| **Disco 1 (sata0)** | 60 GB — Sistema Operativo |
| **Disco 2 (sata1)** | 120 GB — Datos SQL (GPT/NTFS "SQLData") |
| **Estructura SQL** | F:\DATA, F:\LOG, F:\BACKUP, F:\TEMPDB |
| **Autenticación** | Mixta (SQL Auth + Windows) |
| **Puerto SQL** | TCP 1433 |
| **SSH** | Puerto 7022 (OpenSSH Server, arranque automático) |
| **Servicios auto-start** | MSSQLSERVER, sshd |
### A.3 PC Física Cliente: dasu-pc
| Campo | Valor |
|:---|:---|
| **Rol** | Estación de trabajo DASUTEN |
| **Patrimonio** | 32120 |
| **Ubicación** | Oficina DASUTEN (externa a Facultad) |
| **SO** | Windows 10 Pro |
| **Workgroup** | DASUTEN |
| **IP VPN** | 100.119.233.11 (Tailscale) |
| **SSH** | UTNLR@100.119.233.11:7022 |
| **CPU** | AMD Ryzen 5 4600G @ 4.30GHz (6 núcleos) |
| **RAM** | 8 GB DDR4-3200 |
| **Placa Base** | Asus Prime A320M-K |
| **Usuaria principal** | Andrea Almirón (`aalmiron`) |
| **Directorio DASUTEN** | C:\SysDasuten |
| **Config conexión** | C:\SysDasuten\Sistema\Kermet.ini |
### A.4 VM Cliente de Pruebas: dasu-pcv (VM 107) — Test-Bed
| Campo | Valor |
|:---|:---|
| **Rol** | Entorno de pruebas / fallback temporal |
| **SO** | Windows 10 |
| **Workgroup** | DASUTEN |
| **SSH** | Puerto 7022 |
| **Estado** | Operativa (usada como fallback vía TeamViewer) |
---
## B. Configuración del Sistema DASUTEN
### B.1 Archivo de conexión: Kermet.ini
Parámetros clave configurados para la operación sin Domain Controller:
```ini
SERVER=dasu-sql4
DATABASE=sysdasuten
UID=sa
PWD=[credencial almacenada en bóveda]
```
> **Nota técnica:** El campo `UID` debe estar explícitamente configurado con `sa` (u otra cuenta SQL Auth). Si se deja vacío, el sistema DASUTEN (Visual FoxPro) asume autenticación Windows integrada (SSPI/Kerberos), lo cual falla en entorno Workgroup sin Domain Controller.
### B.2 Directorio del sistema
```
C:\SysDasuten\
├── Sistema\ → Ejecutables VFP + Kermet.ini
├── Plantillas\ → Templates Word/Excel para impresión
└── [otros directorios de la aplicación]
```
---
## C. Base de Datos: sysdasuten
| Campo | Valor |
|:---|:---|
| **Nombre** | sysdasuten |
| **Motor** | SQL Server 2019 Enterprise |
| **Tamaño backup comprimido** | ~1.33 GB |
| **Nivel de compatibilidad** | 100 |
| **Recovery model** | FULL |
| **Integridad (DBCC CHECKDB)** | ✅ Sin errores |
| **Tiempo de restauración** | 26 segundos (354 MB/s) |
| **Origen de datos** | srvv-fenix (servidor legacy de Facultad) |
| **Último refresco** | 13/04/2026 |
### C.1 Ruta de transferencia del backup
```
srvv-fenix (Origen)
│ [BACKUP DATABASE ... WITH COMPRESSION]
│ → E:\BK_SQL\sysdasuten_compressed_ADN.bak (~1.33 GB)
srv-ns8 (Tránsito)
│ [SMB/smbget vía dominio]
│ → /var/tmp/
srv-dasu (Tránsito)
│ [SCP vía Tailscale]
dasu-sql4 (Destino)
│ [RESTORE DATABASE ... WITH REPLACE]
└ → F:\BACKUP\ → F:\DATA\ + F:\LOG\
```
---
## D. VMs Legacy Preservadas (Apagadas)
| VM ID | Nombre | Rol anterior | Estado |
|:---:|:---|:---|:---:|
| 100 | dasu-srvv-dc | Domain Controller AD DS | 🛑 Apagada |
| 101 | dasu-srvv-sql | SQL Server (dominio) | 🛑 Apagada |
| 102 | dasu-pcv | PC cliente (dominio) | 🛑 Apagada |
| 103 | dasu-sql2 | SQL Server prueba v1 | 🛑 Descartada |
| 104 | dasu-pcv2 | PC prueba v1 | 🛑 Descartada |
> Las VMs legacy (100-102) se mantienen apagadas como respaldo. Pueden reactivarse en caso de necesitar un rollback al enfoque con Domain Controller.
---
## E. Accesos Remotos Configurados
| Nodo | Método | Detalle |
|:---|:---|:---|
| srv-dasu | Tailscale SSH | 100.112.46.104 |
| dasu-sql4 | SSH (LAN) | 192.168.1.11:7022 |
| dasu-sql4 | Tailscale | 100.107.15.82 (inestable) |
| dasu-pc | Tailscale SSH | 100.119.233.11:7022 |
| dasu-pc | AnyDesk | Instalado (GUI) |
| dasu-pcv | TeamViewer | Activo (workaround temporal) |
---
## F. Cuenta de Gestión VPN
| Campo | Valor |
|:---|:---|
| **Proveedor** | Tailscale |
| **Cuenta** | pcdasu0@frlr.utn.edu.ar |
| **Nodos vinculados** | srv-dasu, dasu-sql4, dasu-pc, dasu-pcv |
---
*Fin del informe técnico.*
@@ -0,0 +1,446 @@
# INFORME TÉCNICO — Migración del Sistema DASUTEN a Nuevo Servidor
> **Área:** Departamento de Tecnología de la Información y Comunicaciones (DTIC)
> **Proyecto:** Migración de infraestructura DASUTEN — Modo Workgroup sin Domain Controller
> **Código interno:** A04.P005
> **Fecha del informe:** 13 de abril de 2026
> **Elaborado por:** Ricardo Monla — Área DTIC, UTN-FRLR
> **Versión:** 0.2
>
> *Este documento contiene el detalle técnico completo de la migración. Para la síntesis ejecutiva, ver [01_informe_ejecutivo_DASUTEN.md](01_informe_ejecutivo_DASUTEN.md).*
---
## 1. Resumen Ejecutivo
Se informa el avance de la migración del sistema de gestión administrativa **DASUTEN** (Departamento de Acción Social Universitaria Tecnológica Nacional) desde la infraestructura compartida de la Facultad hacia un entorno independiente, autónomo y simplificado.
**Estado general del proyecto: 95% completado.**
El sistema DASUTEN se encuentra **operativo** en la nueva infraestructura desde el día 13 de abril de 2026, habiendo superado exitosamente las pruebas funcionales de acceso a datos. Resta únicamente resolver un problema de compatibilidad de impresión vinculado a las plantillas de Office utilizadas por el sistema.
### Logros principales
- ✅ Servidor de base de datos **completamente migrado** a infraestructura nueva.
- ✅ Base de datos de producción **restaurada y verificada** sin errores de integridad.
- ✅ Sistema DASUTEN **desplegado y operativo** en la PC física de la usuaria.
- ✅ Eliminación exitosa de la dependencia del **Active Directory (Domain Controller)**.
- ✅ Simplificación de la arquitectura de red — de 3 servidores virtuales a 1.
- ⚠️ Pendiente: resolución de problema de impresión por compatibilidad de plantillas Office.
---
## 2. Antecedentes y Motivación
### 2.1 Infraestructura original (en red de Facultad)
El sistema DASUTEN operaba sobre infraestructura compartida con la Facultad Regional La Rioja, dependiendo de:
- Un servidor SQL Server alojado en la red interna de la Facultad (`srvv-fenix`).
- Un Domain Controller Active Directory para la autenticación de usuarios.
- Conectividad de red interna de la Facultad con routing complejo.
Esta configuración generaba **múltiples puntos de falla**:
- Dependencia del dominio institucional para la autenticación.
- Complejidad de red con subnetting privado, NAT y gateway interno.
- Cualquier cambio en la infraestructura de la Facultad impactaba directamente en DASUTEN.
### 2.2 Esquema de trabajo provisorio (AnyDesk + PC Virtual)
Al reubicarse la oficina DASUTEN en un espacio propio alejado de los servidores, se agravaron las fallas de conectividad: la señal de red recorría múltiples puntos intermedios dentro del edificio, y la caída de cualquiera de ellos dejaba a la oficina sin sistema.
Para dar continuidad al servicio, se gestionó una **línea de internet propia** para la oficina y se implementó el siguiente esquema de trabajo:
```
PC Física (dasu-pc) Servidor srv-dasu (Proxmox VE)
Usuarias: Almirón / Molina ├── VM 100: dasu-srvv-dc (Domain Controller AD DS)
Acceso: AnyDesk ──────────────────► ├── VM 101: dasu-srvv-sql (SQL Server + BD sysdasuten)
(escritorio remoto) └── VM 102: dasu-pcv (PC Virtual ← trabajo aquí)
```
**Componentes requeridos:** 4 (servidor dominio + servidor BD + PC virtual + PC física con AnyDesk).
**Flujo de trabajo de las usuarias:** PC física → AnyDesk (escritorio remoto) → PC virtual → sistema DASUTEN.
Este esquema permitió:
- Dar continuidad al servicio independientemente de la red interna de la Facultad.
- Brindar asistencia técnica 24/7 por parte de DTIC vía acceso remoto.
Pero implicaba:
- Experiencia de trabajo **indirecta** para las usuarias (doble salto).
- Cuatro componentes simultáneos que debían estar operativos.
- Múltiples puntos de falla que dificultaban el soporte.
### 2.3 Objetivo del proyecto
**Migrar el sistema DASUTEN a una infraestructura independiente y simplificada**, eliminando la dependencia del Domain Controller (Active Directory), el esquema de escritorio remoto, y utilizando un esquema de red **Workgroup** directo, con el fin de:
1. Reducir la complejidad operativa (de 4 componentes a 2).
2. Permitir trabajo directo (sin AnyDesk intermedio).
3. Minimizar los puntos de falla.
4. Garantizar la autonomía operativa de DASUTEN respecto de la infraestructura de la Facultad.
5. Facilitar la eventual gestión desde Rectorado (Buenos Aires) si fuera necesario.
---
## 3. Infraestructura Implementada
### 3.1 Arquitectura de red
Se implementó una topología de **red plana simplificada**, donde todos los equipos obtienen direccionamiento IP directamente del router del ISP, eliminando subnetting privado y routing interno:
```
Internet
Router ISP (192.168.1.1 — Gateway)
├── srv-dasu (Proxmox VE) → 192.168.1.13
│ └── dasu-sql4 (VM 106) → 192.168.1.11 [SQL Server 2019]
└── dasu-pc (PC Física) → DHCP ISP [Estación de trabajo]
```
### 3.2 Componentes del nuevo entorno
| Componente | Descripción | Estado |
|:---|:---|:---:|
| **srv-dasu** | Servidor Proxmox VE 9.1 (Hipervisor) — CPU Intel, 15 GB RAM, 94 GB disco | ✅ Operativo |
| **dasu-sql4** (VM 106) | Windows Server 2022 — SQL Server 2019 Enterprise — 4 GB RAM, 2 vCPU, discos 60+120 GB | ✅ Operativo |
| **dasu-pc** | PC Física — Windows 10 Pro — AMD Ryzen 5, 8 GB RAM — Oficina DASUTEN | ✅ Operativo |
### 3.3 Comparativa: Antes vs. Después
| Aspecto | Enfoque Anterior (con DC + AnyDesk) | Enfoque Actual (Workgroup) |
|:---|:---|:---|
| **Componentes** | 4 (DC + SQL + PC virtual + PC física) | 2 (SQL + PC física) |
| **Forma de trabajo** | Indirecta (AnyDesk → PC virtual) | Directa (sistema en PC) |
| **Red** | Privada 10.0.100.x (gateway interno) | DHCP del ISP (red plana) |
| **Autenticación SQL** | Windows Integrated (Kerberos) | SQL Auth (mixta) |
| **Usuarios** | Active Directory | Usuarios locales Windows |
| **DNS** | AD DNS (10.0.100.10) | DNS del ISP (automático) |
| **Complejidad de red** | Alta (DC + DNS + dominio + routing) | Mínima (red plana ISP) |
| **Puntos de falla** | DC, SQL, red dominio, routing, AnyDesk | Solo SQL |
| **VMs necesarias** | 3 (DC + SQL + PC virtual) | 1 (solo SQL) |
---
## 4. Fases Ejecutadas y Progreso
### Resumen de progreso por fase
| Fase | Descripción | Estado | Progreso |
|:---:|:---|:---:|:---:|
| 1 | Preparación del entorno (srv-dasu) | ✅ Completada | 100% |
| 2 | Creación de VM SQL Server de prueba | ✅ Completada | 100% |
| 3 | Creación de VM PC Cliente de prueba | ✅ Completada | 100% |
| 4 | VM definitiva dasu-sql4 | ✅ Completada | 100% |
| 5 | Migración de base de datos | ✅ Completada | 100% |
| 5b | Hardening y persistencia de servicios | ✅ Completada | 100% |
| 6 | Validación con PC cliente virtual | ✅ Completada | 100% |
| 7 | Despliegue en PC física (producción) | 🚧 En curso | 75% |
| 8 | Refresco final de base de datos | ✅ Completada | 100% |
### 4.1 Fase 1-3: Preparación y pruebas de concepto (27-28 marzo)
Se preparó el hipervisor Proxmox, se reconfiguró el bridge de red para usar DHCP directo del ISP, y se crearon VMs de prueba para validar la viabilidad del enfoque sin Domain Controller.
**Hallazgo crítico:** Se detectó y resolvió que el bridge de red estaba configurado con la IP de una red privada anterior, lo cual habría impedido la conectividad de las nuevas VMs.
### 4.2 Fase 4: Servidor SQL definitivo (9-10 abril)
Se creó la VM definitiva `dasu-sql4` (VM 106) con una arquitectura optimizada:
- **Discos separados:** 60 GB para SO + 120 GB exclusivo para datos SQL (buena práctica).
- **SQL Server 2019 Enterprise** instalado con autenticación mixta.
- **Gestión remota** asegurada vía OpenSSH Server (puerto 7022) y Tailscale VPN.
### 4.3 Fase 5: Migración de Base de Datos (11 abril)
Se ejecutó la migración completa de la base de datos de producción:
1. **Exportación:** Backup comprimido (~1.33 GB) generado desde el servidor origen (`srvv-fenix`).
2. **Transferencia:** Archivo movido a través de la VPN institucional (Tailscale) en múltiples tramos seguros.
3. **Restauración:** Base restaurada exitosamente en 26 segundos (354 MB/s).
4. **Verificación:** `DBCC CHECKDB` ejecutado sin errores — integridad de datos confirmada al 100%.
### 4.4 Fase 5b: Hardening (11 abril)
Se validó que todos los servicios críticos (SSH, SQL Server) sobreviven reinicios del servidor sin intervención manual:
- OpenSSH Server: arranque automático ✅
- SQL Server: arranque automático ✅
- IP de red: mantenida tras reinicio ✅
### 4.5 Fase 6: Validación funcional en entorno virtual (11 abril)
Se restauró una VM cliente desde backup para simular el entorno real de la oficina DASUTEN:
- Se desvinculó la PC del dominio anterior y se unió al Workgroup `DASUTEN`.
- Se reconfiguró el archivo de conexión del sistema (`Kermet.ini`) para apuntar al nuevo servidor SQL.
- **Resultado:** El sistema DASUTEN conectó exitosamente, actualizó estructuras y descargó novedades.
**Conclusión técnica clave:** Se determinó mediante ingeniería inversa que el sistema DASUTEN (desarrollado en Visual FoxPro) **no tiene dependencia real del Active Directory** si se configura explícitamente la autenticación SQL en su archivo de configuración.
### 4.6 Fase 7: Despliegue en PC física de producción (13 abril — EN CURSO)
Se desplegó el sistema en la PC física de la oficina DASUTEN (`dasu-pc`):
- **Directorio del sistema** (`C:\SysDasuten`) transferido desde la VM de pruebas.
- **Configuración de conexión** apuntada al nuevo servidor `dasu-sql4`.
- **Acceso directo** creado en el escritorio de la usuaria (`aalmiron`).
- **Pruebas funcionales:**
- Inicio del sistema: ✅ OK
- Acceso a datos (ventana de bonos): ✅ OK
- **Impresión: ❌ FALLO** — Error al imprimir, posiblemente por incompatibilidad de las plantillas de Office (el sistema usa automatización OLE con Word/Excel).
### 4.7 Fase 8: Refresco final de base de datos (13 abril)
Se ejecutó un refresco automatizado de la base de datos para asegurar que el sistema arranca con los datos más actualizados del servidor de origen:
- Backup generado automáticamente desde el origen (44.8 segundos).
- Transferido de manera segura (SMB + SCP por VPN).
- Restaurado con `RESTORE DATABASE ... WITH REPLACE`.
- Integridad verificada con `DBCC CHECKDB` — sin errores.
---
## 5. Estado Actual
### 5.1 Funcionalidades operativas
| Funcionalidad | Estado |
|:---|:---:|
| Acceso al sistema DASUTEN | ✅ Operativo |
| Consulta de datos (bonos, registros) | ✅ Operativo |
| Conexión a base de datos SQL Server | ✅ Operativo |
| Acceso remoto para soporte (Tailscale/SSH) | ✅ Operativo |
| Impresión desde el sistema | ❌ Pendiente |
### 5.2 Problema pendiente: Impresión
**Descripción:** Al intentar imprimir desde el sistema DASUTEN en la PC física, se produce un error. El diagnóstico preliminar indica que la causa es una incompatibilidad entre las **plantillas de Office** requeridas por el sistema (diseñadas para Office 2003/2007) y la versión de Office actualmente instalada en la PC.
**Impacto:** La usuaria no puede generar documentos impresos desde el nuevo sistema.
**Workaround temporal:** Se habilitó acceso remoto (TeamViewer) desde la PC física hacia la VM de pruebas (`dasu-pcv`), donde la impresión funciona correctamente. La usuaria puede continuar operando mientras se resuelve el problema.
**Plan de resolución:** Verificar y ajustar la versión/configuración de Office en `dasu-pc` para compatibilizarla con las plantillas del sistema.
---
## 6. Beneficios Obtenidos
1. **Independencia operativa:** DASUTEN ya no depende de la infraestructura de red ni de los servidores de la Facultad.
2. **Simplificación:** Se pasó de 3 VMs + Domain Controller a solo 1 VM (SQL Server), reduciendo significativamente la complejidad.
3. **Reducción de puntos de falla:** De múltiples (DC, DNS institucional, routing complejo) a uno solo (SQL Server).
4. **Acceso remoto robusto:** Gestión técnica asegurada vía VPN (Tailscale) y SSH, sin necesidad de presencia física.
5. **Autonomía:** La infraestructura puede ser gestionada independientemente, facilitando una eventual transferencia a Rectorado.
6. **Preservación del entorno anterior:** Las VMs del enfoque con DC fueron apagadas y preservadas como respaldo, permitiendo un rollback si fuera necesario.
---
## 7. Cronograma Resumen
| Fecha | Hito |
|:---|:---|
| 27/03/2026 | Inicio — Preparación del entorno e inicio de pruebas de concepto |
| 28/03/2026 | VM SQL Server de prueba operativa |
| 09-10/04/2026 | Servidor SQL definitivo (dasu-sql4) desplegado y operativo |
| 11/04/2026 | Base de datos migrada, verificada y hardening completado |
| 11/04/2026 | Validación funcional exitosa en entorno virtual |
| 13/04/2026 | Despliegue en PC física de producción — sistema operativo con observación de impresión |
---
## 8. Próximos Pasos
| Prioridad | Acción | Plazo estimado |
|:---:|:---|:---|
| 🔴 Alta | Resolver problema de impresión (compatibilidad Office/plantillas) | 1-3 días hábiles |
| 🟡 Media | Reservar IPs fijas en router ISP (prevenir cambios por DHCP) | 1 semana |
| 🟢 Baja | Evaluar apagado definitivo de VMs legacy (DC, SQL antiguo) | Tras validación completa |
| 🟢 Baja | Documentar procedimiento de respaldo automatizado de la nueva BD | 2 semanas |
---
## 9. Conclusión
La migración del sistema DASUTEN a la nueva infraestructura se encuentra **prácticamente completada**, con el sistema operativo y los datos verificados. El enfoque sin Domain Controller demostró ser completamente viable, logrando una **reducción significativa de la complejidad** y de los puntos de falla.
El único aspecto pendiente es la resolución del problema de impresión, para el cual ya se dispone de un workaround funcional que permite a la usuaria continuar operando sin interrupción.
Se estima que el proyecto quedará **100% finalizado** dentro de los próximos días hábiles, una vez resuelta la compatibilidad de plantillas de Office.
---
# ANEXO TÉCNICO
## A. Inventario de Nodos
### A.1 Servidor Hipervisor: srv-dasu
| Campo | Valor |
|:---|:---|
| **Rol** | Hipervisor Proxmox VE 9.1 |
| **IP LAN** | 192.168.1.13 (DHCP ISP) |
| **IP VPN** | 100.112.46.104 (Tailscale) |
| **Bridge** | vmbr0 sobre enp33s0 |
| **RAM** | 15 GB total / ~4 GB libre post-VMs |
| **Disco** | 94 GB (68% uso, thin provisioned) |
| **Acceso remoto** | Tailscale + SSH |
### A.2 Servidor SQL: dasu-sql4 (VM 106)
| Campo | Valor |
|:---|:---|
| **Rol** | SQL Server 2019 Enterprise |
| **SO** | Windows Server 2022 (Español) |
| **IP LAN** | 192.168.1.11 (DHCP ISP) |
| **IP VPN** | 100.107.15.82 (Tailscale — inestable) |
| **Workgroup** | DASUTEN |
| **RAM** | 4 GB |
| **vCPU** | 2 |
| **Disco 1 (sata0)** | 60 GB — Sistema Operativo |
| **Disco 2 (sata1)** | 120 GB — Datos SQL (GPT/NTFS "SQLData") |
| **Estructura SQL** | F:\DATA, F:\LOG, F:\BACKUP, F:\TEMPDB |
| **Autenticación** | Mixta (SQL Auth + Windows) |
| **Puerto SQL** | TCP 1433 |
| **SSH** | Puerto 7022 (OpenSSH Server, arranque automático) |
| **Servicios auto-start** | MSSQLSERVER, sshd |
### A.3 PC Física Cliente: dasu-pc
| Campo | Valor |
|:---|:---|
| **Rol** | Estación de trabajo DASUTEN |
| **Patrimonio** | 32120 |
| **Ubicación** | Oficina DASUTEN (externa a Facultad) |
| **SO** | Windows 10 Pro |
| **Workgroup** | DASUTEN |
| **IP VPN** | 100.119.233.11 (Tailscale) |
| **SSH** | UTNLR@100.119.233.11:7022 |
| **CPU** | AMD Ryzen 5 4600G @ 4.30GHz (6 núcleos) |
| **RAM** | 8 GB DDR4-3200 |
| **Placa Base** | Asus Prime A320M-K |
| **Usuaria principal** | Andrea Almirón (`aalmiron`) |
| **Directorio DASUTEN** | C:\SysDasuten |
| **Config conexión** | C:\SysDasuten\Sistema\Kermet.ini |
### A.4 VM Cliente de Pruebas: dasu-pcv (VM 107) — Test-Bed
| Campo | Valor |
|:---|:---|
| **Rol** | Entorno de pruebas / fallback temporal |
| **SO** | Windows 10 |
| **Workgroup** | DASUTEN |
| **SSH** | Puerto 7022 |
| **Estado** | Operativa (usada como fallback vía TeamViewer) |
---
## B. Configuración del Sistema DASUTEN
### B.1 Archivo de conexión: Kermet.ini
Parámetros clave configurados para la operación sin Domain Controller:
```ini
SERVER=dasu-sql4
DATABASE=sysdasuten
UID=sa
PWD=[credencial almacenada en bóveda]
```
> **Nota técnica:** El campo `UID` debe estar explícitamente configurado con `sa` (u otra cuenta SQL Auth). Si se deja vacío, el sistema DASUTEN (Visual FoxPro) asume autenticación Windows integrada (SSPI/Kerberos), lo cual falla en entorno Workgroup sin Domain Controller.
### B.2 Directorio del sistema
```
C:\SysDasuten\
├── Sistema\ → Ejecutables VFP + Kermet.ini
├── Plantillas\ → Templates Word/Excel para impresión
└── [otros directorios de la aplicación]
```
---
## C. Base de Datos: sysdasuten
| Campo | Valor |
|:---|:---|
| **Nombre** | sysdasuten |
| **Motor** | SQL Server 2019 Enterprise |
| **Tamaño backup comprimido** | ~1.33 GB |
| **Nivel de compatibilidad** | 100 |
| **Recovery model** | FULL |
| **Integridad (DBCC CHECKDB)** | ✅ Sin errores |
| **Tiempo de restauración** | 26 segundos (354 MB/s) |
| **Origen de datos** | srvv-fenix (servidor legacy de Facultad) |
| **Último refresco** | 13/04/2026 |
### C.1 Ruta de transferencia del backup
```
srvv-fenix (Origen)
│ [BACKUP DATABASE ... WITH COMPRESSION]
│ → E:\BK_SQL\sysdasuten_compressed_ADN.bak (~1.33 GB)
srv-ns8 (Tránsito)
│ [SMB/smbget vía dominio]
│ → /var/tmp/
srv-dasu (Tránsito)
│ [SCP vía Tailscale]
dasu-sql4 (Destino)
│ [RESTORE DATABASE ... WITH REPLACE]
└ → F:\BACKUP\ → F:\DATA\ + F:\LOG\
```
---
## D. VMs Legacy Preservadas (Apagadas)
| VM ID | Nombre | Rol anterior | Estado |
|:---:|:---|:---|:---:|
| 100 | dasu-srvv-dc | Domain Controller AD DS | 🛑 Apagada |
| 101 | dasu-srvv-sql | SQL Server (dominio) | 🛑 Apagada |
| 102 | dasu-pcv | PC cliente (dominio) | 🛑 Apagada |
| 103 | dasu-sql2 | SQL Server prueba v1 | 🛑 Descartada |
| 104 | dasu-pcv2 | PC prueba v1 | 🛑 Descartada |
> Las VMs legacy (100-102) se mantienen apagadas como respaldo. Pueden reactivarse en caso de necesitar un rollback al enfoque con Domain Controller.
---
## E. Accesos Remotos Configurados
| Nodo | Método | Detalle |
|:---|:---|:---|
| srv-dasu | Tailscale SSH | 100.112.46.104 |
| dasu-sql4 | SSH (LAN) | 192.168.1.11:7022 |
| dasu-sql4 | Tailscale | 100.107.15.82 (inestable) |
| dasu-pc | Tailscale SSH | 100.119.233.11:7022 |
| dasu-pc | AnyDesk | Instalado (GUI) |
| dasu-pcv | TeamViewer | Activo (workaround temporal) |
---
## F. Cuenta de Gestión VPN
| Campo | Valor |
|:---|:---|
| **Proveedor** | Tailscale |
| **Cuenta** | pcdasu0@frlr.utn.edu.ar |
| **Nodos vinculados** | srv-dasu, dasu-sql4, dasu-pc, dasu-pcv |
---
### Historial de versiones
| Versión | Fecha | Cambios |
|:---:|:---:|:---|
| 0.1 | 13/04/2026 | Versión inicial (fusión de informe de avance + anexo técnico) |
| 0.2 | 13/04/2026 | Se agrega contexto de infraestructura anterior (esquema AnyDesk), versionado, referencia a doc ejecutivo |
*Fin del informe técnico.*
+12
View File
@@ -1,5 +1,17 @@
# Nodo: cam-hikvision-24 (Detectado) # Nodo: cam-hikvision-24 (Detectado)
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `cam-hikvision-24` |
| **IP** | `10.0.10.24` |
| **OS** | Firmware Hikvision |
| **Estado** | 🟢 Online |
| **Rol** | Cámara IP / DVR |
| **Ring** | — |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `cam-hikvision-24` (Asignado provisionalmente) - **Hostname**: `cam-hikvision-24` (Asignado provisionalmente)
- **IP**: `10.0.10.24` - **IP**: `10.0.10.24`
+17 -2
View File
@@ -1,5 +1,20 @@
# Nodo: dasu-pc # Nodo: dasu-pc
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-pc` |
| **IP** | `100.119.233.11` |
| **OS** | Windows 10 |
| **Estado** | 🟢 Online |
| **Rol** | Estación de Trabajo DASUTEN |
| **SSH** | `UTNLR@100.119.233.11:7022` |
| **Auth** | `rsa_legacy` |
| **Ring** | `—` |
<!-- META:END -->
> Acceso vía Tailscale (Passphrase RSA)
## Información General ## Información General
- **Hostname**: `dasu-pc` (Prev: `dasu-pc-0`, `pc-dasu0`) - **Hostname**: `dasu-pc` (Prev: `dasu-pc-0`, `pc-dasu0`)
- **Patrimonio**: `32120` - **Patrimonio**: `32120`
@@ -8,8 +23,8 @@
- **Rol**: Estación de Trabajo Remota (Usuarios: Andrea/Romina) - **Rol**: Estación de Trabajo Remota (Usuarios: Andrea/Romina)
- **Sistema Operativo**: Windows 10 - **Sistema Operativo**: Windows 10
- **Dominio**: `dasuten.utnlr` (Unido 18/03/2026 - P2601.07.01) - **Dominio**: `dasuten.utnlr` (Unido 18/03/2026 - P2601.07.01)
- **Conectividad**: Tailscale (`100.100.145.51`) - ✅ ONLINE (Unattended Mode) - **Conectividad**: Tailscale (`100.119.233.11`) - ✅ ONLINE (Unattended Mode) *(Actualizado 2026-04-13)*
- **SSH**: `UTNLR@100.100.145.51:7022` (Plan A, Passphrase RSA, Bóveda: `rsa`) - **SSH**: `UTNLR@100.119.233.11:7022` (Plan A, Passphrase RSA, Bóveda: `rsa`)
- **Acceso Respaldo (Plan B)**: W-ZOMBI v2.0 C2 (Vía Powershell: `iwr 100.111.195.4:8000/zombi.ps1 -useb | iex`) - **Acceso Respaldo (Plan B)**: W-ZOMBI v2.0 C2 (Vía Powershell: `iwr 100.111.195.4:8000/zombi.ps1 -useb | iex`)
## Credenciales de Gestión (Tailscale Admin) ## Credenciales de Gestión (Tailscale Admin)
+33 -16
View File
@@ -1,11 +1,27 @@
# Nodo: dasu-pcv # Nodo: dasu-pcv
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-pcv` |
| **IP** | `10.0.100.7` |
| **OS** | Windows 10 LTSC |
| **Estado** | 🟢 Online |
| **Rol** | PC cliente DASUTEN (Test-Bed) |
| **SSH** | `rmonla@10.0.100.7:7022` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `107` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-pcv` (pendiente renombrar, original: `pcv-dasu0`) - **Hostname**: `dasu-pcv` (Renombrado desde `pcv-dasu0`)
- **Rol**: PC cliente DASUTEN — Modo Workgroup (pendiente desvincular dominio) - **Rol**: PC cliente DASUTEN — Modo Workgroup (Test-Bed validado)
- **OS**: Windows 10 LTSC (Español, con GUI) - **OS**: Windows 10 LTSC (Español, con GUI)
- **Estado**: 🚧 Restaurando desde backup vzdump - **Estado**: ✅ Validado (Fase 6 completada)
- **Última Actualización**: 2026-04-11 - **Última Actualización**: 2026-04-13
- **Padre**: srv-dasu - **Padre**: srv-dasu
## Virtualización ## Virtualización
@@ -21,27 +37,28 @@
- **Origen**: Restaurado desde `images:backup/vzdump-qemu-102-2026_03_16-00_40_03.vma.zst` (13.47 GB, con wrapper zstd `--no-check` por checksum corrupto) - **Origen**: Restaurado desde `images:backup/vzdump-qemu-102-2026_03_16-00_40_03.vma.zst` (13.47 GB, con wrapper zstd `--no-check` por checksum corrupto)
## Red ## Red
- **IP LAN**: 192.168.1.20 (DHCP ISP) - **IP LAN**: 192.168.1.20 (DHCP ISP — puede variar)
- **Tailscale**: `100.103.81.108`*(Actualizado 2026-04-13)*
- **Gateway**: 192.168.1.1 - **Gateway**: 192.168.1.1
- **DNS**: ISP (automático) - **DNS**: ISP (automático)
- **Dominio actual**: DASUTEN (Workgroup) - **Dominio actual**: DASUTEN (Workgroup)
- **Dominio anterior**: UTNLARIOJA (Desvinculado) - **Dominio anterior**: UTNLARIOJA (Desvinculado)
## Gestión ## Gestión
- **Admin Local**: `adminpcv` ($lldgUTNlarioja00) - **Admin Local**: `adminpcv` (Bóveda: `dasu-pcv:adminpcv`)
- **Acceso Remoto**: RustDesk ID 296893471 (Password permanente: `$DASU123utn`) - **SSH**: `adminpcv@100.103.81.108:7022`
- **Acceso Remoto**: RustDesk ID 296893471 (Bóveda: `dasu-pcv:rustdesk`)
- **Gestor Backup**: Proxmox Console noVNC - **Gestor Backup**: Proxmox Console noVNC
## Software ## Software
- **Launcher DASUTEN**: Instalado (configuración legacy apuntando al antiguo SQL con dominio) - **Launcher DASUTEN**: Instalado y validado ✅
- **Connection string actual**: Pendiente reconfigurar → `192.168.1.11:1433` con auth SQL (sa) - **Kermet.ini**: Configurado con `SERVER=dasu-sql4`, `UID=sa`, SQL Auth ✅
## Pendiente ## Tareas completadas
- [ ] Desvincular del dominio → WORKGROUP - [x] Desvincular del dominio → WORKGROUP `DASUTEN`
- [ ] Renombrar hostname a `dasu-pcv` - [x] Renombrar hostname a `dasu-pcv`
- [ ] Reconfigurar connection string DASUTEN → `192.168.1.11:1433` - [x] Reconfigurar Kermet.ini → `SERVER=dasu-sql4` con SQL Auth ✅
- [ ] Verificar IP asignada por DHCP - [x] Test funcional contra dasu-sql4 ✅
- [ ] Test funcional contra dasu-sql4
## Historial ## Historial
- 2026-03-16: Backup vzdump original de VM 102 (pcv-dasu0) - 2026-03-16: Backup vzdump original de VM 102 (pcv-dasu0)
+16
View File
@@ -1,5 +1,21 @@
# Nodo: dasu-pcv2 # Nodo: dasu-pcv2
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-pcv2` |
| **IP** | `10.0.100.8` |
| **OS** | Windows 10 LTSC |
| **Estado** | 🚧 En configuración |
| **Rol** | Estación de Trabajo DASUTEN (Fase 3) |
| **SSH** | `rmonla@10.0.100.8:7022` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `104` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-pcv2` - **Hostname**: `dasu-pcv2`
- **Rol**: Estación de Trabajo Remota (Fase 3 DASUTEN) - **Rol**: Estación de Trabajo Remota (Fase 3 DASUTEN)
+16
View File
@@ -1,5 +1,21 @@
# Nodo: dasu-sql2 # Nodo: dasu-sql2
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-sql2` |
| **IP** | `10.0.100.3` |
| **OS** | Windows Server 2022 Core |
| **Estado** | 🟢 Online |
| **Rol** | SQL Server 2019 (DASUTEN) |
| **SSH** | `rmonla@10.0.100.3:7022` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `103` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-sql2` - **Hostname**: `dasu-sql2`
- **Rol**: SQL Server 2019 (DASUTEN) — Modo Workgroup - **Rol**: SQL Server 2019 (DASUTEN) — Modo Workgroup
+16
View File
@@ -1,5 +1,21 @@
# Nodo: dasu-sql3 # Nodo: dasu-sql3
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-sql3` |
| **IP** | `10.0.100.5` |
| **OS** | Windows Server 2022 |
| **Estado** | 🚧 Instalando |
| **Rol** | SQL Server 2019 (DASUTEN) |
| **SSH** | `rmonla@10.0.100.5:7022` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `105` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-sql3` - **Hostname**: `dasu-sql3`
- **Rol**: SQL Server 2019 (DASUTEN) — Modo Workgroup - **Rol**: SQL Server 2019 (DASUTEN) — Modo Workgroup
+35 -2
View File
@@ -1,5 +1,22 @@
# Nodo: dasu-sql4 # Nodo: dasu-sql4
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-sql4` |
| **IP** | `192.168.1.11` |
| **OS** | Windows Server 2022 Standard |
| **Estado** | 🟢 Online |
| **Rol** | SQL Server 2019 Enterprise (DASUTEN) |
| **SSH** | `Administrador@192.168.1.11:7022` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `106` |
| **Ring** | `—` |
| **Bóveda** | `dasu-sql4:Administrador` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-sql4` - **Hostname**: `dasu-sql4`
- **Rol**: SQL Server 2019 Enterprise (DASUTEN) — Modo Workgroup - **Rol**: SQL Server 2019 Enterprise (DASUTEN) — Modo Workgroup
@@ -25,11 +42,27 @@
- **Autostart**: sí (`onboot: 1`) - **Autostart**: sí (`onboot: 1`)
## Red ## Red
- **IP LAN**: 192.168.1.11 (DHCP ISP — cambió de .26 tras reboot por corte de luz) - **IP LAN**: 192.168.1.11 (DHCP ISP)
- **IP Tailscale**: 100.107.15.82 (⚠️ inestable, usar relay srv-dasu) - **IP Tailscale**: ⚠️ No disponible (offline/inestable)
- **Gateway**: 192.168.1.1 - **Gateway**: 192.168.1.1
- **DNS**: ISP (automático) - **DNS**: ISP (automático)
## ⚠️ Acceso Remoto — Importante
dasu-sql4 entra en **modo suspensión** cuando no se usa, lo que inhabilita el acceso SSH directo y Tailscale.
**Método recomendado para operaciones:**
```bash
# Desde srv-dasu, acceder vía IP LAN con PowerShell remoto o SSH
ssh srv-dasu "ssh -p 7022 rmonla@192.168.1.11 'powershell -Command \"...\"'"
# O usar herramientas ADN que ya manejan el relay automáticamente
./adn/tools/run dron lanzar --nota "Operación" --nodo dasu-sql4 -- ruby script.rb
```
**Historial de IPs:**
- `10.0.100.6` — ❌ Obsoleta (ya no se usa)
- `192.168.1.11` — ✅ Actual (DHCP ISP)
## Software Instalado ## Software Instalado
- **VirtIO Drivers**: ✅ (virtio-win-0.1.285) - **VirtIO Drivers**: ✅ (virtio-win-0.1.285)
- **Tailscale**: ✅ (cuenta `pcdasu0@frlr.utn.edu.ar`) - **Tailscale**: ✅ (cuenta `pcdasu0@frlr.utn.edu.ar`)
+17
View File
@@ -1,5 +1,22 @@
# Nodo: dasu-srvv-dc # Nodo: dasu-srvv-dc
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-srvv-dc` |
| **IP** | `10.0.100.1` |
| **OS** | Windows Server Core |
| **Estado** | 🟢 Online |
| **Rol** | Controlador de Dominio AD DS / DNS |
| **SSH** | `admindasu@10.0.100.1:7022` |
| **Auth** | `password` |
| **Bóveda** | `dasu-dc:admindasu` |
| **Host** | `srv-dasu` |
| **VMID** | `100` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `dasu-srvv-dc.dasuten.utnlr` - **Hostname**: `dasu-srvv-dc.dasuten.utnlr`
- **Nombre anterior**: `dc-dasuten` (renombrado 16/03/2026) - **Nombre anterior**: `dc-dasuten` (renombrado 16/03/2026)
+16
View File
@@ -1,5 +1,21 @@
# dasu-srvv-sql (SQL Server 2019) # dasu-srvv-sql (SQL Server 2019)
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dasu-srvv-sql` |
| **IP** | `100.107.24.124` |
| **OS** | Windows Server 2022 Core |
| **Estado** | 🟢 Online |
| **Rol** | Servidor SQL Server 2019 |
| **SSH** | `root@100.107.24.124:22` |
| **Auth** | `password` |
| **Host** | `srv-dasu` |
| **VMID** | `102` |
| **Ring** | — |
<!-- META:END -->
- **Nombre**: `dasu-srvv-sql` - **Nombre**: `dasu-srvv-sql`
- **Identity**: `Windows 2022 Core` - **Identity**: `Windows 2022 Core`
- **Rol**: `Servidor de Base de Datos SQL` - **Rol**: `Servidor de Base de Datos SQL`
+16
View File
@@ -1,5 +1,21 @@
# Ficha de Nodo: dtic-bitacoras # Ficha de Nodo: dtic-bitacoras
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `dtic-bitacoras` |
| **IP** | `10.0.10.4` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟡 Pendiente |
| **Rol** | Sistema de Bitácoras DTIC |
| **SSH** | `root@10.0.10.4:22` |
| **Auth** | `key` |
| **Host** | `srvv-dtic` |
| **Ring** | `standard` |
<!-- META:END -->
> Servicio Docker en srvv-dtic
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `dtic-bitacoras` | | **Hostname** | `dtic-bitacoras` |
+16
View File
@@ -1,5 +1,21 @@
# Nodo: pcv-dasu1 # Nodo: pcv-dasu1
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `pcv-dasu1` |
| **IP** | `100.100.145.51` |
| **OS** | Windows 10 |
| **Estado** | 🟢 Online |
| **Rol** | Estación de Trabajo DASU |
| **SSH** | `rmonla@100.100.145.51:7022` |
| **Auth** | `password` |
| **Host** | `srv-pmox2` |
| **VMID** | `108` |
| **Ring** | `—` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `pcv-dasu1` (Prev: `pcvDASU2`) - **Hostname**: `pcv-dasu1` (Prev: `pcvDASU2`)
- **Ubicación**: Interna (Facultad / srv-pmox2) - **Ubicación**: Interna (Facultad / srv-pmox2)
+14
View File
@@ -1,5 +1,19 @@
# Ficha de Nodo: pcv-dasu2 # Ficha de Nodo: pcv-dasu2
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `pcv-dasu2` |
| **IP** | `10.0.10.202` |
| **OS** | Windows 10 |
| **Estado** | 🟢 Online |
| **Rol** | Sistema DASU / Atención al Usuario |
| **Host** | `srv-pmox2` |
| **VMID** | `109` |
| **Ring** | `—` |
<!-- META:END -->
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `pcv-dasu2` | | **Hostname** | `pcv-dasu2` |
+14
View File
@@ -1,5 +1,19 @@
# Nodo: pcv-serviio # Nodo: pcv-serviio
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `pcv-serviio` |
| **IP** | `DHCP` |
| **OS** | Windows 10 |
| **Estado** | 🟢 Online |
| **Rol** | Media Server |
| **Host** | `srv-pmox2` |
| **VMID** | `110` |
| **Ring** | `—` |
<!-- META:END -->
> Ficha de nodo | Referencia: [`adn/01_ontologia.md`](../adn/01_ontologia.md) > Ficha de nodo | Referencia: [`adn/01_ontologia.md`](../adn/01_ontologia.md)
## Identificación ## Identificación
+15
View File
@@ -1,5 +1,20 @@
# Nodo: srv-dasu # Nodo: srv-dasu
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-dasu` |
| **IP** | `100.112.46.104` |
| **OS** | Proxmox VE 8 / Debian 12 |
| **Estado** | 🟢 Online |
| **Rol** | Hypervisor Proxmox VE (Standalone — DASUTEN) |
| **SSH** | `root@100.112.46.104:22` |
| **Auth** | `key` |
| **Ring** | `core` |
<!-- META:END -->
> Acceso vía Tailscale DNS (`ssh srv-dasu`)
## Identidad ## Identidad
- **Hostname**: `srv-dasu.utnlarioja` - **Hostname**: `srv-dasu.utnlarioja`
- **Rol Inicial**: Hypervisor Proxmox VE (Standalone - DASUTEN) - **Rol Inicial**: Hypervisor Proxmox VE (Standalone - DASUTEN)
+14
View File
@@ -1,5 +1,19 @@
# Ficha de Nodo: srv-ns8 (Host Principal) # Ficha de Nodo: srv-ns8 (Host Principal)
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-ns8` |
| **IP** | `10.0.10.8` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Workstation / Docker Host / Hypervisor |
| **SSH** | `rmonla@10.0.10.8:22` |
| **Auth** | `key` |
| **Ring** | `core` |
<!-- META:END -->
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `srv-ns8` | | **Hostname** | `srv-ns8` |
+14
View File
@@ -1,5 +1,19 @@
# Nodo: srv-pmox1 # Nodo: srv-pmox1
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-pmox1` |
| **IP** | `10.0.10.201` |
| **OS** | Proxmox VE 8.2 |
| **Estado** | 🟢 Online |
| **Rol** | Nodo de Virtualización (Proxmox VE) |
| **SSH** | `root@10.0.10.201:22` |
| **Auth** | `key` |
| **Ring** | `core` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srv-pmox1` - **Hostname**: `srv-pmox1`
- **IP**: `10.0.10.201` - **IP**: `10.0.10.201`
+14
View File
@@ -1,5 +1,19 @@
# Nodo: srv-pmox2 # Nodo: srv-pmox2
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-pmox2` |
| **IP** | `10.0.10.202` |
| **OS** | Proxmox VE 8 |
| **Estado** | 🟢 Online |
| **Rol** | Nodo de Virtualización (Proxmox VE) |
| **SSH** | `root@10.0.10.202:22` |
| **Auth** | `key` |
| **Ring** | `core` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srv-pmox2` - **Hostname**: `srv-pmox2`
- **IP**: `10.0.10.202` - **IP**: `10.0.10.202`
+14
View File
@@ -1,5 +1,19 @@
# Nodo: srv-pmox3 # Nodo: srv-pmox3
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-pmox3` |
| **IP** | `10.0.10.203` |
| **OS** | Proxmox VE 8 |
| **Estado** | 🟢 Online |
| **Rol** | Nodo de Virtualización (Proxmox VE) |
| **SSH** | `root@10.0.10.203:22` |
| **Auth** | `key` |
| **Ring** | `core` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srv-pmox3` - **Hostname**: `srv-pmox3`
- **IP**: `10.0.10.203` - **IP**: `10.0.10.203`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srv-xen1 # Nodo: srv-xen1
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srv-xen1` |
| **IP** | `10.0.10.23` |
| **OS** | XenServer 7.0 (Linux) |
| **Estado** | 🟢 Online |
| **Rol** | Hypervisor XenServer (Legacy) |
| **SSH** | `rmonla@10.0.10.23:22` |
| **Auth** | `rsa_legacy` |
| **Clave SSH** | `~/.ssh/id_rsa_xen` |
| **SSH Opts** | `-o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostkeyAlgorithms=+ssh-rsa` |
| **Ring** | `core` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srv-xen1` - **Hostname**: `srv-xen1`
- **IP**: `10.0.10.23` - **IP**: `10.0.10.23`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-data # Nodo: srvv-data
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-data` |
| **IP** | `10.0.10.13` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Base de Datos / Almacenamiento |
| **SSH** | `root@10.0.10.13:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox3` |
| **VMID** | `107` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-data` - **Hostname**: `srvv-data`
- **IP**: `10.0.10.13` - **IP**: `10.0.10.13`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-dns # Nodo: srvv-dns
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-dns` |
| **IP** | `10.0.10.2` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Servidor DNS Primario (CoreDNS) |
| **SSH** | `rmonla@10.0.10.2:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox1` |
| **VMID** | `112` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-dns` - **Hostname**: `srvv-dns`
- **IP**: `10.0.10.2` - **IP**: `10.0.10.2`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-docs # Nodo: srvv-docs
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-docs` |
| **IP** | `10.0.10.14` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Docker Host / Documentación |
| **SSH** | `root@10.0.10.14:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox1` |
| **VMID** | `104` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-docs` - **Hostname**: `srvv-docs`
- **IP**: `10.0.10.14` - **IP**: `10.0.10.14`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-dtic # Nodo: srvv-dtic
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-dtic` |
| **IP** | `10.0.10.4` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Docker Host / Servicios Internos |
| **SSH** | `root@10.0.10.4:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox1` |
| **VMID** | `103` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-dtic` - **Hostname**: `srvv-dtic`
- **IP**: `10.0.10.4` - **IP**: `10.0.10.4`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-fenix # Nodo: srvv-fenix
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-fenix` |
| **IP** | `10.0.10.111` |
| **OS** | Windows Server 2019 |
| **Estado** | 🟢 Online |
| **Rol** | Sistema Académico Legacy (DASUTEN) |
| **SSH** | `rmonla@10.0.10.111:7022` |
| **Auth** | `password` |
| **Host** | `srv-pmox3` |
| **VMID** | `111` |
| **Ring** | `—` |
<!-- META:END -->
## Identidad ## Identidad
- **Hostname**: `srvv-fenix.utnlarioja` - **Hostname**: `srvv-fenix.utnlarioja`
- **Rol Inicial**: Sistema Académico Legacy (DASUTEN / Facultad) - **Rol Inicial**: Sistema Académico Legacy (DASUTEN / Facultad)
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-koha # Nodo: srvv-koha
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-koha` |
| **IP** | `10.0.10.130` |
| **OS** | Debian 11 (Bullseye) |
| **Estado** | 🟢 Online |
| **Rol** | Biblioteca Koha |
| **SSH** | `root@10.0.10.130:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox2` |
| **VMID** | `105` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-koha` - **Hostname**: `srvv-koha`
- **IP**: `10.0.10.130` - **IP**: `10.0.10.130`
+16
View File
@@ -1,5 +1,21 @@
# Ficha de Nodo: srvv-maurik # Ficha de Nodo: srvv-maurik
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-maurik` |
| **IP** | `10.0.10.10` |
| **OS** | Windows Server 2019 Standard |
| **Estado** | 🟢 Online |
| **Rol** | Controlador de Dominio (utnlarioja.intranet) |
| **SSH** | `monlaricardo@10.0.10.10:7022` |
| **Auth** | `password` |
| **Bóveda** | `utnlarioja.intranet:monlaricardo` |
| **Host** | `srv-xen1` |
| **Ring** | `—` |
<!-- META:END -->
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `srvv-maurik` | | **Hostname** | `srvv-maurik` |
+17
View File
@@ -1,5 +1,22 @@
# Ficha de Nodo: srvv-nginx-rm # Ficha de Nodo: srvv-nginx-rm
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-nginx-rm` |
| **IP** | `10.0.10.117` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Servidor Web NGINX (Público) |
| **SSH** | `root@10.0.10.117:7022` |
| **Auth** | `password` |
| **Bóveda** | `srvv-nginx-rm:root` |
| **Host** | `srv-pmox3` |
| **VMID** | `116` |
| **Ring** | `standard` |
<!-- META:END -->
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `srvv-nginx-rm` | | **Hostname** | `srvv-nginx-rm` |
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-sitio # Nodo: srvv-sitio
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-sitio` |
| **IP** | `10.0.10.18` |
| **OS** | Ubuntu Linux |
| **Estado** | 🟢 Online |
| **Rol** | Servidor Web / Docker Host |
| **SSH** | `root@10.0.10.18:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox1` |
| **VMID** | `101` |
| **Ring** | `standard` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-sitio` - **Hostname**: `srvv-sitio`
- **IP Privada**: `10.0.10.18` - **IP Privada**: `10.0.10.18`
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-sitio0 # Nodo: srvv-sitio0
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-sitio0` |
| **IP** | `10.0.10.119` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Servidor Web / Docker Host |
| **SSH** | `root@10.0.10.119:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox1` |
| **VMID** | `102` |
| **Ring** | `test` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-SCERO` - **Hostname**: `srvv-SCERO`
- **IP**: `10.0.10.119` - **IP**: `10.0.10.119`
+17
View File
@@ -1,5 +1,22 @@
# Nodo: srvv-sitio2 # Nodo: srvv-sitio2
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-sitio2` |
| **IP** | `10.0.10.19` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Servidor Web Secundario (LXC) |
| **SSH** | `rmonla@10.0.10.19:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox3` |
| **VMID** | `113` |
| **Ring** | `test` |
<!-- META:END -->
> Root deshabilitado — usar rmonla con sudo
## Información General ## Información General
- **Hostname**: `srvv-sitio2` - **Hostname**: `srvv-sitio2`
- **IP**: `10.0.10.19` - **IP**: `10.0.10.19`
+15
View File
@@ -1,5 +1,20 @@
# Ficha de Nodo: srvv-sysacadweb # Ficha de Nodo: srvv-sysacadweb
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-sysacadweb` |
| **IP** | `10.0.10.12` |
| **OS** | Windows Web Server 2008 R2 |
| **Estado** | 🟢 Online |
| **Rol** | Servidor Web SysACAD |
| **SSH** | `monlaricardo@10.0.10.12:7022` |
| **Auth** | `password` |
| **Host** | `srv-xen1` |
| **Ring** | `—` |
<!-- META:END -->
| Atributo | Valor | | Atributo | Valor |
| :--- | :--- | | :--- | :--- |
| **Hostname** | `srvv-sysacadweb` | | **Hostname** | `srvv-sysacadweb` |
+16
View File
@@ -1,5 +1,21 @@
# Nodo: srvv-uptime # Nodo: srvv-uptime
<!-- META:BEGIN -->
| Campo | Valor |
|:------|:------|
| **Hostname** | `srvv-uptime` |
| **IP** | `10.0.10.9` |
| **OS** | Debian 12 (Bookworm) |
| **Estado** | 🟢 Online |
| **Rol** | Monitorización (Uptime Kuma) |
| **SSH** | `root@10.0.10.9:22` |
| **Auth** | `key` |
| **Host** | `srv-pmox3` |
| **VMID** | `106` |
| **Ring** | `test` |
<!-- META:END -->
## Información General ## Información General
- **Hostname**: `srvv-uptime` - **Hostname**: `srvv-uptime`
- **IP**: `10.0.10.9` - **IP**: `10.0.10.9`