Migración dtic-DASUTEN a estándar A04: - A04_dtic-DASUTEN.md: Manifiesto creado - A04.P001 a A04.P005: Planes renombrados de P2601.* - _hist/_hist_P2601_dasuten.md: Legacy archivado Migración dtic-BKPs a estándar A03: - A03_dtic-BKPs.md: Manifiesto creado - A03.P001, A03.P002: Planes migrados - docs/ambito/dtic-BKPs/: Nueva estructura Herramientas ADN nuevas: - adn/tools/bkps/: BKPs migrado a tools ADN - adn/tools/cli/dron.rb: Vigía de tareas largas - adn/tools/cli/copiloto.rb: Asistente CLI - adn/tools/cli/novato.rb: CLI de aprendizaje Limpieza: - dtic-BKPs/ → _hist/dtic-BKPs/ (legacy archivado) - adn/tools/cli/backup.rb → _hist/ (migrado a bkps.rb) Otros: - nodos/pcv-dasu1.md, srvv-data.md, srvv-nginx-rm.md: Actualizados - adn/tools/core/conciliador.rb: Mejoras - adn/tools/w-zombi/data/cmd.json: Actualizado Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
171 lines
5.5 KiB
Ruby
Executable File
171 lines
5.5 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
||
# frozen_string_literal: true
|
||
|
||
# ==========================================================
|
||
# dtic-BKPs — Procesador de Backups Automatizado v6.0
|
||
# ----------------------------------------------------------
|
||
# Refactorización de dtic-BKPs v5.5.1
|
||
# AUTOR: Ricardo MONLA (rmonla@)
|
||
# ==========================================================
|
||
#
|
||
# Uso:
|
||
# ruby dtic-BKPs.rb # Menú interactivo (auto-screen)
|
||
# ruby dtic-BKPs.rb T1 # Ejecutar tarea 1 directamente
|
||
# ruby dtic-BKPs.rb C1 # Ejecutar comando 1 directamente
|
||
# ruby dtic-BKPs.rb list # Listar tareas y comandos
|
||
# ruby dtic-BKPs.rb --help # Ayuda
|
||
|
||
require 'shellwords'
|
||
|
||
# ─── Rutas ───────────────────────────────────────────────────
|
||
APP_DIR = File.dirname(File.expand_path(__FILE__))
|
||
LIB_DIR = File.join(APP_DIR, 'lib')
|
||
|
||
require_relative 'lib/logger'
|
||
require_relative 'lib/config'
|
||
require_relative 'lib/proc_proxmox'
|
||
require_relative 'lib/proc_xen'
|
||
require_relative 'lib/sync_rclone'
|
||
require_relative 'lib/menu'
|
||
|
||
include DTICBKPs
|
||
|
||
# ─── Dispatcher ──────────────────────────────────────────────
|
||
class Dispatcher
|
||
TIPOS = {
|
||
'proxmox' => -> (log) { Procesador::Proxmox.new(log) },
|
||
'xva' => -> (log) { Procesador::Xen.new(log) },
|
||
'rclone' => -> (log) { Procesador::Rclone.new(log) },
|
||
}.freeze
|
||
|
||
def initialize(config, log)
|
||
@config = config
|
||
@log = log
|
||
end
|
||
|
||
def ejecutar_tarea(tarea)
|
||
tipo = tarea[:tipo].to_s
|
||
factory = TIPOS[tipo]
|
||
unless factory
|
||
@log.error("Tipo desconocido: #{tipo}")
|
||
return
|
||
end
|
||
procesador = factory.call(@log)
|
||
procesador.ejecutar(tarea)
|
||
end
|
||
|
||
def ejecutar_comando(comando)
|
||
@log.titulo(comando[:texto])
|
||
tareas_ids = comando[:tareas] || []
|
||
tareas_ids.each_with_index do |id, i|
|
||
tarea = @config.find_tarea(id)
|
||
if tarea
|
||
@log.paso("Paso #{i + 1}/#{tareas_ids.length}: #{tarea[:texto]}")
|
||
ejecutar_tarea(tarea)
|
||
else
|
||
@log.error("Tarea '#{id}' no encontrada en comando '#{comando[:id]}'.")
|
||
end
|
||
end
|
||
@log.ok("Comando '#{comando[:texto]}' completado.")
|
||
end
|
||
end
|
||
|
||
# ─── Auto-Screen Wrapper ─────────────────────────────────────
|
||
def auto_screen!
|
||
return if ENV['TERM']&.start_with?('screen') || ENV['STY']
|
||
puts "\n#{C::CYAN}ℹ Relanzando en sesión screen para permitir desatención...#{C::RESET}"
|
||
|
||
cmd = ([$PROGRAM_NAME] + ARGV).map { |a| Shellwords.escape(a) }.join(' ')
|
||
nombre = "dticbkps_#{Time.now.strftime('%H%M%S')}"
|
||
|
||
puts "Para reconectarte: #{C::YELLOW}screen -r #{nombre}#{C::RESET}"
|
||
sleep 1
|
||
|
||
system('screen', '-dmS', nombre, 'bash', '-c', "#{cmd}; echo ''; read -p 'Enter para cerrar...'")
|
||
exit 0
|
||
end
|
||
|
||
# ─── Ctrl+C Handler ──────────────────────────────────────────
|
||
Signal.trap('INT') do
|
||
puts "\n#{C::RED}Ctrl+C — Saliendo...#{C::RESET}"
|
||
exit 130
|
||
end
|
||
|
||
# ─── Ayuda ───────────────────────────────────────────────────
|
||
def mostrar_ayuda(config)
|
||
puts <<~HELP
|
||
#{C::BOLD}#{C::CYAN}dtic-BKPs — Procesador de Backups v6.0#{C::RESET}
|
||
|
||
#{C::YELLOW}Uso:#{C::RESET}
|
||
ruby dtic-BKPs.rb Menú interactivo
|
||
ruby dtic-BKPs.rb T<N> Ejecutar tarea N
|
||
ruby dtic-BKPs.rb C<N> Ejecutar comando N
|
||
ruby dtic-BKPs.rb list Listar tareas/comandos
|
||
ruby dtic-BKPs.rb --help Esta ayuda
|
||
|
||
#{C::YELLOW}Tareas configuradas:#{C::RESET}
|
||
HELP
|
||
|
||
config.tareas.each_with_index do |t, i|
|
||
puts " #{C::GREEN}T#{i+1}#{C::RESET} #{t[:texto]} #{C::DIM}(#{t[:tipo]})#{C::RESET}"
|
||
end
|
||
|
||
puts "\n #{C::YELLOW}Comandos configurados:#{C::RESET}"
|
||
config.comandos.each_with_index do |c, i|
|
||
puts " #{C::GREEN}C#{i+1}#{C::RESET} #{c[:texto]}"
|
||
end
|
||
end
|
||
|
||
def cmd_list(config)
|
||
puts "#{C::BOLD}Tareas:#{C::RESET}"
|
||
config.tareas.each_with_index do |t, i|
|
||
puts " T#{i+1} #{t[:id]} #{t[:texto]} [#{t[:tipo]}] #{t[:origen]} → #{t[:destino]}"
|
||
end
|
||
puts "\n#{C::BOLD}Comandos:#{C::RESET}"
|
||
config.comandos.each_with_index do |c, i|
|
||
puts " C#{i+1} #{c[:id]} #{c[:texto]} → #{c[:tareas]&.join(', ')}"
|
||
end
|
||
end
|
||
|
||
# ─── Main ────────────────────────────────────────────────────
|
||
|
||
config_path = File.join(APP_DIR, 'dtic-BKPs.yml')
|
||
log = AppLogger.new(File.join(APP_DIR, 'logs'))
|
||
config = Config.new(config_path)
|
||
dispatcher = Dispatcher.new(config, log)
|
||
|
||
arg = ARGV[0]&.strip
|
||
|
||
case arg
|
||
when nil
|
||
# Menú interactivo con auto-screen
|
||
auto_screen!
|
||
log.info("===== dtic-BKPs v6.0 iniciado (Screen) =====")
|
||
Menu.new(config, dispatcher, log).ejecutar
|
||
|
||
when '--help', '-h', 'help'
|
||
mostrar_ayuda(config)
|
||
|
||
when 'list'
|
||
cmd_list(config)
|
||
|
||
when /^[TC]\d+$/i
|
||
# Ejecución directa desde CLI
|
||
tipo = arg[0].upcase
|
||
idx = arg[1..].to_i - 1
|
||
|
||
if tipo == 'T' && config.tareas[idx]
|
||
dispatcher.ejecutar_tarea(config.tareas[idx])
|
||
elsif tipo == 'C' && config.comandos[idx]
|
||
dispatcher.ejecutar_comando(config.comandos[idx])
|
||
else
|
||
puts "#{ERR} Opción no válida: #{arg}"
|
||
exit 1
|
||
end
|
||
|
||
else
|
||
puts "#{ERR} Comando desconocido: #{arg}"
|
||
mostrar_ayuda(config)
|
||
exit 1
|
||
end
|