[ADN] Commit final DB-First - Actualización completa de documentación, herramientas y componentes
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# adn/tools/cli/proceso.rb — Subcomando para ejecución de procesos automatizados
|
||||
# ==============================================================================
|
||||
|
||||
require 'optparse'
|
||||
require 'open3'
|
||||
require 'date'
|
||||
|
||||
require_relative '../core/colores'
|
||||
require_relative '../core/nodos_info'
|
||||
require_relative '../core/ejecutor_remoto'
|
||||
require_relative '../db/core/bitacora_db'
|
||||
|
||||
module ADN
|
||||
class SubcomandoProceso
|
||||
def initialize(args, logger)
|
||||
@args = args
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def ejecutar
|
||||
if @args.empty? || @args[0] == 'help'
|
||||
mostrar_ayuda
|
||||
return
|
||||
end
|
||||
|
||||
proceso = @args.shift
|
||||
sub_args = @args
|
||||
|
||||
case proceso
|
||||
when 'nodos:migrar_backup'
|
||||
comando_migrar_backup(sub_args)
|
||||
else
|
||||
@logger.error("Proceso desconocido: #{proceso}")
|
||||
mostrar_ayuda
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mostrar_ayuda
|
||||
puts "#{Color::CYAN}⚙️ Procesos Automatizados ADN#{Color::RESET}"
|
||||
puts ""
|
||||
puts "#{Color::YELLOW}Uso:#{Color::RESET} ./adn/tools/run proceso <nombre_proceso> [argumentos]"
|
||||
puts ""
|
||||
puts "#{Color::BOLD}nodos:migrar_backup [OPC]#{Color::RESET} Migración de backups entre nodos"
|
||||
puts " --desde NODO Nodo origen"
|
||||
puts " --hacia NODO Nodo destino"
|
||||
puts " --ruta RUTA Ruta del archivo en el origen"
|
||||
puts " --ejecutar Ejecutar transferencia real"
|
||||
end
|
||||
|
||||
def comando_migrar_backup(args)
|
||||
opts = { desde: nil, hacia: nil, ruta: nil, simular: true }
|
||||
OptionParser.new do |o|
|
||||
o.on("--desde NODO") { |v| opts[:desde] = v }
|
||||
o.on("--hacia NODO") { |v| opts[:hacia] = v }
|
||||
o.on("--ruta RUTA") { |v| opts[:ruta] = v }
|
||||
o.on("--ejecutar") { opts[:simular] = false }
|
||||
end.parse!(args)
|
||||
|
||||
unless opts[:desde] && opts[:hacia] && opts[:ruta]
|
||||
puts "#{Color::RED}✗ Error: Faltan argumentos (--desde, --hacia, --ruta)#{Color::RESET}"
|
||||
return
|
||||
end
|
||||
|
||||
# 1. Resolver información de nodos
|
||||
info_src = NodosInfo.extraer_metadata(opts[:desde])
|
||||
info_dst = NodosInfo.extraer_metadata(opts[:hacia])
|
||||
|
||||
unless info_src && info_dst
|
||||
puts "#{Color::RED}✗ Error: No se pudo obtener información de #{opts[:desde]} o #{opts[:hacia]}#{Color::RESET}"
|
||||
return
|
||||
end
|
||||
|
||||
# Ajuste manual para srv-ns8 (detectar si estamos corriendo en él)
|
||||
info_src[:ip] = 'localhost' if opts[:desde] == 'srv-ns8'
|
||||
|
||||
puts "#{Color::CYAN}🚀 Preparando #{opts[:simular] ? 'SIMULACIÓN' : 'MIGRACIÓN'} de backup...#{Color::RESET}"
|
||||
puts " 📤 Origen : #{Color::BOLD}#{opts[:desde]}#{Color::RESET} (#{info_src[:ip]})"
|
||||
puts " 📥 Destino: #{Color::BOLD}#{opts[:hacia]}#{Color::RESET} (#{info_dst[:ip]})"
|
||||
|
||||
# 2. Registrar en Bitácora
|
||||
id_evento = registrar_inicio_bitacora(opts)
|
||||
|
||||
# 3. Ejecutar
|
||||
if opts[:simular]
|
||||
ejecutar_simulacion(id_evento, opts)
|
||||
else
|
||||
ejecutar_migracion_real(id_evento, opts, info_src, info_dst)
|
||||
end
|
||||
end
|
||||
|
||||
def ejecutar_migracion_real(id_evento, opts, info_src, info_dst)
|
||||
puts "#{Color::BOLD}🚀 Iniciando transferencia real...#{Color::RESET}"
|
||||
|
||||
desc = "PROCESO: Migración REAL de backup desde #{opts[:desde]} a #{opts[:hacia]}."
|
||||
|
||||
# A. Verificar Origen
|
||||
print " ▸ Verificando origen... "
|
||||
size = nil
|
||||
if info_src[:ip] == 'localhost'
|
||||
size = File.exist?(opts[:ruta]) ? File.size(opts[:ruta]) : nil
|
||||
else
|
||||
# Usar EjecutorRemoto para verificar el archivo
|
||||
res = EjecutorRemoto.ejecutar(info_src, "powershell -NoProfile -Command \"if(Test-Path '#{opts[:ruta]}'){$s=Get-Item '#{opts[:ruta]}'; $s.Length}else{exit 1}\"")
|
||||
size = res[:success] ? res[:output].to_i : nil
|
||||
end
|
||||
|
||||
if size
|
||||
mb = (size.to_f / 1024**2).round(2)
|
||||
puts "#{Color::GREEN}OK#{Color::RESET} (#{mb} MB)"
|
||||
desc += "\n- #{Time.now.strftime('%H:%M')} > Archivo localizado: #{mb} MB."
|
||||
else
|
||||
puts "#{Color::RED}FALLÓ#{Color::RESET} (No se encontró el archivo)"
|
||||
actualizar_bitacora(id_evento, { estado: '❌', descripcion: desc + "\n- ERROR: Archivo no encontrado." })
|
||||
return
|
||||
end
|
||||
|
||||
# B. Preparar Destino
|
||||
print " ▸ Preparando directorio en destino... "
|
||||
EjecutorRemoto.ejecutar(info_dst, "powershell -NoProfile -Command \"if(!(Test-Path C:\\temp)){New-Item -ItemType Directory -Path C:\\temp}\"")
|
||||
puts "#{Color::GREEN}OK#{Color::RESET}"
|
||||
|
||||
# C. Pipeline de Streaming
|
||||
filename = File.basename(opts[:ruta].gsub('\\', '/'))
|
||||
dst_path = "C:\\temp\\#{filename}"
|
||||
puts " 🚀 Streaming: #{opts[:desde]} -> #{opts[:hacia]} [#{dst_path}]"
|
||||
|
||||
# Definir comandos de stream adaptados
|
||||
if info_src[:ip] == 'localhost'
|
||||
src_cmd = ["cat", opts[:ruta]]
|
||||
else
|
||||
ps_src = "$s=[System.IO.File]::OpenRead('#{opts[:ruta].gsub('/', '\\\\')}'); $o=[Console]::OpenStandardOutput(); $b=New-Object byte[] 65536; while(($r=$s.Read($b,0,$b.Length)) -gt 0){ $o.Write($b,0,$r) }; $s.Close()"
|
||||
# Inyectar SSHPass vía EjecutorRemoto.comando_candados
|
||||
clave_src = (info_src[:host] == 'srv-dasu' ? 'srv-dasu:rmonla' : 'win_domain')
|
||||
ssh_user_src = "#{info_src[:usuario]}@#{info_src[:ip]}"
|
||||
src_cmd = EjecutorRemoto.comando_candados(clave_src, 'SSHPASS', ["sshpass", "-e", "ssh", "-p", info_src[:puerto_ssh].to_s, "-o", "StrictHostKeyChecking=no", ssh_user_src, "powershell -NoProfile -Command \"#{ps_src}\""])
|
||||
end
|
||||
|
||||
ps_dst = "$i=[Console]::OpenStandardInput(); $s=[System.IO.File]::Create('#{dst_path.gsub('/', '\\\\')}'); $b=New-Object byte[] 65536; while(($r=$i.Read($b,0,$b.Length)) -gt 0){$s.Write($b,0,$r)}; $s.Close()"
|
||||
clave_dst = (info_dst[:host] == 'srv-dasu' ? 'srv-dasu:rmonla' : 'admindasu')
|
||||
ssh_user_dst = "#{info_dst[:usuario]}@#{info_dst[:ip]}"
|
||||
dst_cmd = EjecutorRemoto.comando_candados(clave_dst, 'SSHPASS', ["sshpass", "-e", "ssh", "-p", info_dst[:puerto_ssh].to_s, "-o", "StrictHostKeyChecking=no", ssh_user_dst, "powershell -NoProfile -Command \"#{ps_dst}\""])
|
||||
|
||||
start_time = Time.now
|
||||
success = EjecutorRemoto.stream_pipeline(src_cmd, dst_cmd)
|
||||
end_time = Time.now
|
||||
|
||||
if success
|
||||
duracion = (end_time - start_time).round(0)
|
||||
puts "#{Color::GREEN}✅ Transferencia exitosa en #{duracion} segundos.#{Color::RESET}"
|
||||
actualizar_bitacora(id_evento, {
|
||||
estado: '✅',
|
||||
fin: Time.now.strftime('%H:%M'),
|
||||
descripcion: desc + "\n- #{Time.now.strftime('%H:%M')} > Transferencia completada en #{duracion}s."
|
||||
})
|
||||
else
|
||||
puts "#{Color::RED}✗ Falló la transferencia.#{Color::RESET}"
|
||||
actualizar_bitacora(id_evento, { estado: '❌', descripcion: desc + "\n- ERROR: Streaming fallido." })
|
||||
end
|
||||
end
|
||||
|
||||
# Auxiliares de Bitácora
|
||||
def registrar_inicio_bitacora(opts)
|
||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||||
nodo = db.find_nodo_by_nombre(opts[:hacia])
|
||||
bit = db.find_bitacora_by_fecha(Date.today.to_s) || db.create_bitacora(fecha: Date.today.to_s)
|
||||
db.create_entrada(
|
||||
bitacora_id: bit['id'],
|
||||
nodo_id: nodo ? nodo['id'] : nil,
|
||||
inicio: Time.now.strftime('%H:%M'),
|
||||
estado: '⏳',
|
||||
descripcion: "PROCESO: Migración de backup #{opts[:desde]} -> #{opts[:hacia]}."
|
||||
)['id']
|
||||
end
|
||||
end
|
||||
|
||||
def actualizar_bitacora(id, data)
|
||||
BitacorasDB::BitacoraDB.with_connection { |db| db.update_entrada(id, data) }
|
||||
end
|
||||
|
||||
def ejecutar_simulacion(id_evento, opts)
|
||||
puts " 🧪 Modo simulación activo."
|
||||
sleep(2)
|
||||
actualizar_bitacora(id_evento, { estado: '✅', fin: Time.now.strftime('%H:%M'), descripcion: "PROCESO: Simulación de migración completada." })
|
||||
puts " #{Color::GREEN}OK#{Color::RESET}"
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user