[Tools] ns8-bkps v6.0: refactorización modular de dtic-BKPs (YAML, clases, sin subdir-first)

This commit is contained in:
Ricardo Monla
2026-02-28 09:01:32 -03:00
parent 3555b788e7
commit 34d14c5645
9 changed files with 862 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/config.rb — Carga y guardado de config YAML
# ==========================================================
require 'yaml'
require 'fileutils'
module NS8BKPs
class Config
attr_reader :tareas, :comandos, :auto_avanzar
def initialize(path)
@path = path
reload!
end
def reload!
unless File.exist?(@path)
@tareas = []
@comandos = []
@auto_avanzar = true
return
end
data = YAML.safe_load(File.read(@path), permitted_classes: [Symbol], symbolize_names: true) || {}
@auto_avanzar = data[:auto_avanzar] != false
@tareas = (data[:tareas] || []).map { |t| simbolizar(t) }
@comandos = (data[:comandos] || []).map { |c| simbolizar(c) }
end
def find_tarea(id)
@tareas.find { |t| t[:id].to_s == id.to_s }
end
def save!
data = {
'auto_avanzar' => @auto_avanzar,
'tareas' => @tareas.map { |t| stringify(t) },
'comandos' => @comandos.map { |c| stringify(c) }
}
File.write(@path, YAML.dump(data))
FileUtils.chmod(0644, @path)
end
def add_tarea(tarea)
@tareas << simbolizar(tarea)
end
def remove_tarea(index)
@tareas.delete_at(index)
end
def add_comando(comando)
@comandos << simbolizar(comando)
end
def remove_comando(index)
@comandos.delete_at(index)
end
private
def simbolizar(hash)
hash.transform_keys(&:to_sym)
end
def stringify(hash)
hash.transform_keys(&:to_s).tap do |h|
h['tareas'] = h['tareas'].map(&:to_s) if h['tareas']
end
end
end
end
+75
View File
@@ -0,0 +1,75 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/logger.rb — Logger con colores y rotación
# ==========================================================
require 'logger'
require 'fileutils'
module NS8BKPs
# Colores ANSI
module C
RESET = "\e[0m"
BOLD = "\e[1m"
DIM = "\e[2m"
RED = "\e[91m"
GREEN = "\e[92m"
YELLOW = "\e[93m"
CYAN = "\e[96m"
end
# Iconos semánticos
OK = "#{C::GREEN}#{C::RESET}"
ERR = "#{C::RED}#{C::RESET}"
WARN = "#{C::YELLOW}#{C::RESET}"
INFO = "#{C::CYAN}#{C::RESET}"
class AppLogger
def initialize(log_dir)
FileUtils.mkdir_p(log_dir)
log_path = File.join(log_dir, 'ns8-bkps.log')
@file_logger = Logger.new(log_path, 'daily')
@file_logger.level = Logger::INFO
@file_logger.formatter = proc { |sev, dt, _, msg|
"[#{dt.strftime('%Y-%m-%d %H:%M:%S')}] [#{sev}] #{msg}\n"
}
end
def info(msg)
@file_logger.info(strip_ansi(msg))
puts "#{INFO} #{msg}"
end
def warn(msg)
@file_logger.warn(strip_ansi(msg))
puts "#{WARN} #{msg}"
end
def error(msg)
@file_logger.error(strip_ansi(msg))
puts "#{ERR} #{msg}"
end
def ok(msg)
@file_logger.info(strip_ansi(msg))
puts "#{OK} #{msg}"
end
def titulo(msg)
@file_logger.info(strip_ansi(msg))
puts "\n#{C::BOLD}#{C::CYAN}--- #{msg} ---#{C::RESET}"
end
def paso(msg)
@file_logger.info(strip_ansi(msg))
puts "#{C::CYAN}#{msg}#{C::RESET}"
end
private
def strip_ansi(s)
s.to_s.gsub(/\e\[[\d;]*[mK]/, '')
end
end
end
+174
View File
@@ -0,0 +1,174 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/menu.rb — Menú TUI interactivo + editor CRUD
# ==========================================================
module NS8BKPs
class Menu
def initialize(config, dispatcher, log)
@config = config
@dispatcher = dispatcher
@log = log
end
def ejecutar
loop do
system('clear') || system('cls')
mostrar_encabezado
opciones = construir_opciones
opciones.each { |op| puts op[:linea] }
puts "#{C::CYAN}────────────────────────#{C::RESET}"
print "\n#{C::YELLOW}Selecciona > #{C::RESET}"
$stdout.flush
entrada = $stdin.gets&.chomp&.strip&.upcase
next if entrada.nil? || entrada.empty?
op = opciones.find { |o| o[:id] == entrada }
unless op
@log.error("Opción no válida: #{entrada}")
sleep 1
next
end
case op[:tipo]
when :tarea
@dispatcher.ejecutar_tarea(op[:config])
when :comando
@dispatcher.ejecutar_comando(op[:config])
when :editor
editor_interactivo
@config.reload!
when :salir
puts "Saliendo..."
exit 0
end
unless [:editor, :salir].include?(op[:tipo]) || @config.auto_avanzar
print "\nPresiona Enter para continuar..."
$stdin.gets
end
end
end
private
def mostrar_encabezado
puts "#{C::BOLD}#{C::CYAN}╔══════════════════════════════════════════╗#{C::RESET}"
puts "#{C::BOLD}#{C::CYAN}║ ns8-bkps — Procesador de Backups v6.0 ║#{C::RESET}"
puts "#{C::BOLD}#{C::CYAN}╚══════════════════════════════════════════╝#{C::RESET}"
end
def construir_opciones
opciones = []
puts "\n#{C::BOLD}Tareas Individuales:#{C::RESET}"
@config.tareas.each_with_index do |t, i|
id = "T#{i + 1}"
opciones << { id: id, tipo: :tarea, config: t, linea: " #{C::YELLOW}#{id}.#{C::RESET} #{t[:texto]}" }
end
puts "\n#{C::BOLD}Comandos (Secuencias):#{C::RESET}"
@config.comandos.each_with_index do |c, i|
id = "C#{i + 1}"
opciones << { id: id, tipo: :comando, config: c, linea: " #{C::YELLOW}#{id}.#{C::RESET} #{c[:texto]}" }
end
puts "\n#{C::CYAN}────────────────────────#{C::RESET}"
opciones << { id: 'E', tipo: :editor, linea: " #{C::YELLOW}E.#{C::RESET} Editar tareas y comandos" }
opciones << { id: 'S', tipo: :salir, linea: " #{C::YELLOW}S.#{C::RESET} Salir" }
opciones.each { |op| puts op[:linea] if op[:tipo].is_a?(Symbol) && [:editor, :salir].include?(op[:tipo]) }
opciones
end
# ─── Editor CRUD ──────────────────────────────────────────────
def editor_interactivo
loop do
system('clear') || system('cls')
puts "#{C::BOLD}#{C::CYAN}--- Editor de Tareas y Comandos ---#{C::RESET}\n\n"
puts "#{C::BOLD}Tareas:#{C::RESET}"
@config.tareas.each_with_index do |t, i|
puts " #{C::YELLOW}T#{i+1}.#{C::RESET} #{t[:texto]} #{C::DIM}(#{t[:id]})#{C::RESET}"
end
puts "\n#{C::BOLD}Comandos:#{C::RESET}"
@config.comandos.each_with_index do |c, i|
puts " #{C::YELLOW}C#{i+1}.#{C::RESET} #{c[:texto]} #{C::DIM}(#{c[:id]}, tareas: #{c[:tareas]&.join(', ')})#{C::RESET}"
end
puts "\n#{C::CYAN}[Tx]=Editar, [NT]=Nueva Tarea, [BT]=Borrar Tarea, [G]=Guardar y Volver#{C::RESET}"
print "> "
$stdout.flush
op = $stdin.gets&.chomp&.strip&.downcase
return if op.nil?
case op
when /^t(\d+)$/
idx = $1.to_i - 1
editar_tarea(idx) if @config.tareas[idx]
when 'nt'
nueva_tarea
when 'bt'
print "Número a borrar (ej: 1): "
idx = $stdin.gets&.chomp.to_i - 1
@config.remove_tarea(idx) if idx >= 0 && @config.tareas[idx]
when 'g'
@config.save!
@log.ok("Configuración guardada.")
sleep 1
return
end
end
end
def editar_tarea(idx)
tarea = @config.tareas[idx]
puts "\n#{C::CYAN}Editando: #{tarea[:texto]}#{C::RESET}"
puts "#{C::DIM}(Dejar vacío = mantener valor actual)#{C::RESET}\n"
tarea.each do |k, v|
next if k == :id
tipo_hint = [true, false].include?(v) ? " (true/false)" : ""
print " #{C::YELLOW}#{k}#{C::RESET}#{tipo_hint} [#{v}]: "
$stdout.flush
nuevo = $stdin.gets&.chomp&.strip
next if nuevo.nil? || nuevo.empty?
tarea[k] = case v
when Symbol then nuevo.to_sym
when TrueClass, FalseClass then nuevo.downcase == 'true'
else nuevo
end
end
@log.ok("Tarea actualizada.")
end
def nueva_tarea
tarea = {
id: "tarea_#{Time.now.to_i}",
texto: "Nueva Tarea",
tipo: "rclone",
origen: "",
destino: "",
eliminar_origen: false,
sobrescribir: false
}
editar_tarea_hash(tarea)
@config.add_tarea(tarea)
end
def editar_tarea_hash(tarea)
tarea.each do |k, v|
print " #{C::YELLOW}#{k}#{C::RESET} [#{v}]: "
$stdout.flush
nuevo = $stdin.gets&.chomp&.strip
tarea[k] = nuevo unless nuevo.nil? || nuevo.empty?
end
end
end
end
+150
View File
@@ -0,0 +1,150 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/proc_proxmox.rb — Procesador de backups Proxmox
# ==========================================================
require 'tmpdir'
require 'shellwords'
require 'open3'
require 'fileutils'
module NS8BKPs
module Procesador
class Proxmox
def initialize(log)
@log = log
@pv = system('which pv > /dev/null 2>&1')
end
def ejecutar(tarea)
@log.titulo(tarea[:texto])
@log.info("#{tarea[:id]}: Origen remoto: #{tarea[:origen]}")
dir_tmp = montar(tarea[:origen], tarea[:id])
return false unless dir_tmp
procesados = 0
begin
logs = Dir.glob(File.join(dir_tmp, '*.log'))
if logs.empty?
@log.warn("#{tarea[:id]}: Sin archivos .log en origen.")
return true
end
@log.info("Encontrados #{logs.length} set(s) de backup Proxmox.")
logs.each do |ruta_log|
base = File.basename(ruta_log, '.log')
begin
fuentes = Dir.glob(File.join(dir_tmp, "#{base}*"))
nombre_vm = extraer_nombre_vm(fuentes, ruta_log, base, tarea[:id])
next unless nombre_vm
nombre_tar = "#{nombre_vm}_#{File.mtime(ruta_log).strftime('%Y%m%d_%H%M%S')}.tar.gz"
dir_dest = File.join(tarea[:destino], nombre_vm)
FileUtils.mkdir_p(dir_dest)
ruta_tar = File.join(dir_dest, nombre_tar)
if File.exist?(ruta_tar)
if tarea[:sobrescribir]
@log.warn("#{File.basename(ruta_tar)} existe. Sobrescribiendo.")
else
@log.warn("#{File.basename(ruta_tar)} existe. Saltando.")
next
end
end
@log.paso("Comprimiendo #{base} (VM: #{nombre_vm})")
comprimir(dir_tmp, fuentes, ruta_tar)
@log.ok("Compresión OK: #{File.basename(ruta_tar)}")
if tarea[:eliminar_origen]
fuentes.each { |f| FileUtils.rm_f(f) }
@log.info("Fuentes eliminadas de origen.")
end
procesados += 1
rescue => e
@log.error("#{tarea[:id]}: Error procesando '#{base}': #{e.message}")
FileUtils.rm_f(ruta_tar) if ruta_tar && File.exist?(ruta_tar)
end
end
@log.ok("#{procesados} sets Proxmox procesados.")
true
ensure
desmontar(dir_tmp, tarea[:id])
end
end
private
def extraer_nombre_vm(fuentes, ruta_log, base, tarea_id)
# 1. Intentar .notes
notes = fuentes.find { |f| f.end_with?('.notes') }
nombre = notes ? File.read(notes).strip : nil
# 2. Intentar desde el .log (INFO: VM Name: ...)
if nombre.nil? || nombre.empty?
File.foreach(ruta_log) do |line|
if line =~ /INFO: VM Name:\s*(.+)$/
nombre = $1.strip
break
end
end rescue nil
end
# 3. Fallback: ID de VM del nombre de archivo
if nombre.nil? || nombre.empty?
match = base.match(/vzdump-(?:qemu|lxc)-(\d+)-/)
nombre = match ? "VM-#{match[1]}" : nil
end
@log.warn("#{tarea_id}: Sin nombre de VM para '#{base}'. Saltando.") unless nombre
nombre
end
def comprimir(dir_origen, fuentes, ruta_salida)
nombres = fuentes.map { |f| File.basename(f) }
if @pv
cmd_tar = ['tar', '-cf', '-', '-C', dir_origen] + nombres
cmd_pv = ['pv', '-s', fuentes.sum { |f| File.size(f) rescue 0 }.to_s]
cmd_gz = ['gzip']
File.open(ruta_salida, 'wb') do |f|
Open3.pipeline(cmd_tar, cmd_pv, cmd_gz, out: f).each_with_index do |s, i|
raise "Falló #{%w[tar pv gzip][i]} (#{s.exitstatus})" unless s.success?
end
end
else
args = nombres.map { |f| Shellwords.escape(f) }.join(' ')
cmd = "tar -czf #{Shellwords.escape(ruta_salida)} -C #{Shellwords.escape(dir_origen)} #{args}"
system(cmd) or raise "tar falló (#{$?.exitstatus})"
end
end
def montar(remoto, tarea_id)
dir = Dir.mktmpdir("bkps_#{tarea_id}_")
@log.info("Montando #{remoto} en #{dir}")
pid = spawn("rclone mount #{Shellwords.escape(remoto)} #{dir} --daemon --vfs-cache-mode writes")
Process.detach(pid)
sleep 2
if Dir.exist?(dir) && !(Dir.entries(dir) - %w[. ..]).empty?
dir
else
@log.error("Error montando #{remoto}")
nil
end
end
def desmontar(dir, tarea_id)
return unless dir && Dir.exist?(dir)
@log.info("Desmontando #{dir}")
system("fusermount -u #{dir} 2>/dev/null || umount #{dir} 2>/dev/null")
FileUtils.remove_entry(dir) rescue nil
end
end
end
end
+85
View File
@@ -0,0 +1,85 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/proc_xen.rb — Procesador de backups XEN (.xva)
# ==========================================================
require 'shellwords'
require 'open3'
require 'fileutils'
module NS8BKPs
module Procesador
class Xen
def initialize(log)
@log = log
@pv = system('which pv > /dev/null 2>&1')
end
def ejecutar(tarea)
@log.titulo(tarea[:texto])
@log.info("#{tarea[:id]}: Origen: #{tarea[:origen]}")
archivos = Dir.glob(File.join(tarea[:origen], '*.xva'))
if archivos.empty?
@log.warn("#{tarea[:id]}: Sin archivos .xva en origen.")
return true
end
@log.info("Encontrados #{archivos.length} archivo(s) .xva.")
procesados = 0
archivos.each do |ruta_xva|
nombre_xva = File.basename(ruta_xva)
nombre_vm = nombre_xva.split('_').first
dir_dest = File.join(tarea[:destino], nombre_vm)
FileUtils.mkdir_p(dir_dest)
nombre_sin_ext = File.basename(nombre_xva, '.xva')
ruta_tar = File.join(dir_dest, "#{nombre_sin_ext}.tar.gz")
if File.exist?(ruta_tar)
if tarea[:sobrescribir]
@log.warn("#{File.basename(ruta_tar)} existe. Sobrescribiendo.")
else
@log.warn("#{File.basename(ruta_tar)} existe. Saltando.")
next
end
end
begin
@log.paso("Comprimiendo #{nombre_xva}")
if @pv
cmd_tar = ['tar', '-czf', '-', '-C', tarea[:origen], nombre_xva]
cmd_pv = ['pv', '-s', File.size(ruta_xva).to_s]
File.open(ruta_tar, 'wb') do |f|
Open3.pipeline(cmd_tar, cmd_pv, out: f).each_with_index do |s, i|
raise "Falló #{%w[tar pv][i]} (#{s.exitstatus})" unless s.success?
end
end
else
cmd = "tar -czf #{Shellwords.escape(ruta_tar)} -C #{Shellwords.escape(tarea[:origen])} #{Shellwords.escape(nombre_xva)}"
system(cmd) or raise "tar falló (#{$?.exitstatus})"
end
@log.ok("Compresión OK: #{File.basename(ruta_tar)}")
if tarea[:eliminar_origen]
FileUtils.rm_f(ruta_xva)
@log.info("XVA original eliminado.")
end
procesados += 1
rescue => e
@log.error("#{tarea[:id]}: Error comprimiendo #{nombre_xva}: #{e.message}")
FileUtils.rm_f(ruta_tar)
end
end
@log.ok("#{procesados} archivos .xva procesados.")
true
end
end
end
end
+56
View File
@@ -0,0 +1,56 @@
# frozen_string_literal: true
# ==========================================================
# ns8-bkps/lib/sync_rclone.rb — Sincronización rclone
# ==========================================================
#
# NOTA: Se eliminó la estrategia subdirectories-first que existía
# en dtic-BKPs. Con --delete-before rclone ya libera el espacio
# remoto antes de transferir, haciendo innecesario el loop por
# subdirectorios. El sync root único es suficiente y más eficiente.
require 'shellwords'
module NS8BKPs
module Procesador
class Rclone
RCLONE_OPTS = %w[
--delete-before
--retries 5
--retries-sleep 10s
--timeout 30m
--contimeout 10m
--tpslimit 10
--progress
-v
].join(' ').freeze
def initialize(log)
@log = log
end
def ejecutar(tarea)
@log.titulo(tarea[:texto])
origen = tarea[:origen]
destino = tarea[:destino]
@log.paso("Origen: #{origen}")
@log.paso("Destino: #{destino}")
cmd = "rclone sync #{Shellwords.escape(origen)} #{Shellwords.escape(destino)} #{RCLONE_OPTS}"
@log.info("#{tarea[:id]}: Ejecutando → #{cmd}")
exito = system(cmd)
codigo = $?.exitstatus
if exito
@log.ok("Sincronización rclone completada.")
else
@log.error("#{tarea[:id]}: Fallo en sincronización (código: #{codigo}).")
end
exito
end
end
end
end
+170
View File
@@ -0,0 +1,170 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# ==========================================================
# ns8-bkps — Procesador de Backups Automatizado v6.0
# ----------------------------------------------------------
# Refactorización de dtic-BKPs v5.5.1
# AUTOR: Ricardo MONLA (rmonla@)
# ==========================================================
#
# Uso:
# ruby ns8-bkps.rb # Menú interactivo (auto-screen)
# ruby ns8-bkps.rb T1 # Ejecutar tarea 1 directamente
# ruby ns8-bkps.rb C1 # Ejecutar comando 1 directamente
# ruby ns8-bkps.rb list # Listar tareas y comandos
# ruby ns8-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 NS8BKPs
# ─── 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 = "ns8bkps_#{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}ns8-bkps — Procesador de Backups v6.0#{C::RESET}
#{C::YELLOW}Uso:#{C::RESET}
ruby ns8-bkps.rb Menú interactivo
ruby ns8-bkps.rb T<N> Ejecutar tarea N
ruby ns8-bkps.rb C<N> Ejecutar comando N
ruby ns8-bkps.rb list Listar tareas/comandos
ruby ns8-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, 'ns8-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("===== ns8-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
+70
View File
@@ -0,0 +1,70 @@
# Configuración ns8-bkps — Tareas y Comandos de Backup
# Migrado desde dtic-BKPs_tasks.rb
auto_avanzar: true
tareas:
- id: proc_syncXen1
texto: "⚙️ Procesar archivos de BKPs de VMs: syncs-XEN01"
tipo: xva
origen: "/mnt/ns8Disco2/syncs-XEN01/"
destino: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/"
eliminar_origen: true
sobrescribir: false
- id: proc_syncPmox
texto: "⚙️ Procesar archivos de BKPs de VMs desde zfsDISCO1"
tipo: proxmox
origen: "pve_PMOX3:/mnt/pve/zfsDISCO1/dump/"
destino: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/"
eliminar_origen: false
sobrescribir: false
- id: sinc_pmox_ns8
texto: "📥 Descargar archivos de BKPs de VMs: zfsDISCO1"
tipo: rclone
origen: "pve_PMOX3:/mnt/pve/zfsDISCO1/dump/"
destino: "/mnt/ns8Disco2/syncs-PMOX/"
eliminar_origen: false
sobrescribir: false
- id: sync_BkpAntiguos_nube
texto: "☁️ Upload de BKPs a rmOneDrive: bkps-ANTIGUOS"
tipo: rclone
origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-ANTIGUOS/"
destino: "rmOneDrive:/dtic-BACKUPS/bkps-ANTIGUOS/"
eliminar_origen: false
sobrescribir: false
- id: sync_BkpsSERVIDORes_nube
texto: "☁️ Upload de bkps-SERVIDORes a rmOneDrive"
tipo: rclone
origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/"
destino: "rmOneDrive:/dtic-BACKUPS/bkps-SERVIDORes/"
eliminar_origen: false
sobrescribir: false
- id: sync_BkpUsers_nube
texto: "☁️ Upload de bkps-USERs a rmOneDrive"
tipo: rclone
origen: "/mnt/ns8Disco3/dtic-BACKUPS/bkps-USERs/"
destino: "rmOneDrive:/dtic-BACKUPS/bkps-USERs/"
eliminar_origen: false
sobrescribir: false
comandos:
- id: descargar_y_procesar_pmox
texto: "📥⚙️ Descargar y Procesar BKPs de PMOXs"
tareas: [sinc_pmox_ns8, proc_syncPmox]
- id: full_procesamiento
texto: "⚙️ Procesar TODOS los BKPs de NS8"
tareas: [proc_syncXen1, proc_syncPmox]
- id: full_upload
texto: "🚀 Subir a la Nube todos los BKPs"
tareas: [sync_BkpAntiguos_nube, sync_BkpsSERVIDORes_nube, sync_BkpUsers_nube]
- id: full_full
texto: "🚀 Procesar todo y Subir todo"
tareas: [proc_syncXen1, proc_syncPmox, sync_BkpsSERVIDORes_nube, sync_BkpAntiguos_nube, sync_BkpUsers_nube]