diff --git a/adn/tools/bkps/lib/proc_proxmox.rb b/adn/tools/bkps/lib/proc_proxmox.rb index 502a11a0..3d3c358f 100644 --- a/adn/tools/bkps/lib/proc_proxmox.rb +++ b/adn/tools/bkps/lib/proc_proxmox.rb @@ -3,6 +3,11 @@ # ========================================================== # adn/tools/bkps/lib/proc_proxmox.rb — Procesador Proxmox # Migrado desde dtic-BKPs v6.0 → ADN::BKPs +# +# Fase 2: Soporta orquestación 1-dron-por-VM +# listar(tarea) → lista sets de backup pendientes +# ejecutar_uno(tarea, arch) → procesa UN solo set (.log) +# ejecutar(tarea) → procesa TODOS (legacy) # ========================================================== require 'tmpdir' @@ -19,6 +24,62 @@ module ADN @pv = system('which pv > /dev/null 2>&1') end + # Scanner: devuelve lista de sets de backup pendientes + def listar(tarea) + dir_tmp = montar(tarea[:origen], tarea[:id]) + return [] unless dir_tmp + + begin + logs = Dir.glob(File.join(dir_tmp, '*.log')) + logs.map do |ruta_log| + base = File.basename(ruta_log, '.log') + fuentes = Dir.glob(File.join(dir_tmp, "#{base}*")) + nombre_vm = extraer_nombre_vm(fuentes, ruta_log, base, tarea[:id]) + + nombre_tar = nombre_vm ? "#{nombre_vm}_#{File.mtime(ruta_log).strftime('%Y%m%d_%H%M%S')}.tar.gz" : nil + dir_dest = nombre_vm ? File.join(tarea[:destino], nombre_vm) : nil + ruta_tar = dir_dest ? File.join(dir_dest, nombre_tar) : nil + + tamaño = fuentes.sum { |f| File.size(f) rescue 0 } + + { + archivo: "#{base}.log", + ruta: ruta_log, + vm: nombre_vm || "VM-desconocida", + destino: ruta_tar, + tamaño: tamaño, + pendiente: ruta_tar ? (!File.exist?(ruta_tar) || tarea[:sobrescribir]) : false + } + end + ensure + desmontar(dir_tmp, tarea[:id]) + end + end + + # Atómico: procesa UN solo set de backup (.log base name) + def ejecutar_uno(tarea, nombre_archivo) + base = File.basename(nombre_archivo, '.log') + dir_tmp = montar(tarea[:origen], tarea[:id]) + return false unless dir_tmp + + begin + ruta_log = File.join(dir_tmp, "#{base}.log") + unless File.exist?(ruta_log) + @log.error("Set de backup no encontrado: #{base}.log") + return false + end + + fuentes = Dir.glob(File.join(dir_tmp, "#{base}*")) + nombre_vm = extraer_nombre_vm(fuentes, ruta_log, base, tarea[:id]) + return false unless nombre_vm + + procesar_set(tarea, dir_tmp, ruta_log, base, fuentes, nombre_vm) + ensure + desmontar(dir_tmp, tarea[:id]) + end + end + + # Legacy: procesa TODOS los sets (compatibilidad) def ejecutar(tarea) @log.info("--- #{tarea[:texto]} ---") @log.info("#{tarea[:id]}: Origen remoto: #{tarea[:origen]}") @@ -39,39 +100,11 @@ module ADN 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 + 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.info("#{File.basename(ruta_tar)} existe. Sobrescribiendo.") - else - @log.info("#{File.basename(ruta_tar)} existe. Saltando.") - next - end - end - - @log.info("Comprimiendo #{base} (VM: #{nombre_vm})") - comprimir(dir_tmp, fuentes, ruta_tar) - @log.info("✔ 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 + procesados += 1 if procesar_set(tarea, dir_tmp, ruta_log, base, fuentes, nombre_vm) end @log.info("✔ #{procesados} sets Proxmox procesados.") @@ -83,6 +116,37 @@ module ADN private + def procesar_set(tarea, dir_tmp, ruta_log, base, fuentes, 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.info("#{File.basename(ruta_tar)} existe. Sobrescribiendo.") + else + @log.info("#{File.basename(ruta_tar)} existe. Saltando.") + return true + end + end + + @log.info("Comprimiendo #{base} (VM: #{nombre_vm})") + comprimir(dir_tmp, fuentes, ruta_tar) + @log.info("✔ 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 + + true + rescue => e + @log.error("#{tarea[:id]}: Error procesando '#{base}': #{e.message}") + FileUtils.rm_f(ruta_tar) if ruta_tar && File.exist?(ruta_tar) + false + end + def extraer_nombre_vm(fuentes, ruta_log, base, tarea_id) notes = fuentes.find { |f| f.end_with?('.notes') } nombre = notes ? File.read(notes).strip : nil @@ -148,3 +212,4 @@ module ADN end end end +