🐝 Fase 2: Orquestación Proxmox 1-dron-por-VM
- proc_proxmox.rb: listar() + ejecutar_uno() atómico - Misma interfaz que proc_xen.rb para orquestador genérico
This commit is contained in:
@@ -3,6 +3,11 @@
|
|||||||
# ==========================================================
|
# ==========================================================
|
||||||
# adn/tools/bkps/lib/proc_proxmox.rb — Procesador Proxmox
|
# adn/tools/bkps/lib/proc_proxmox.rb — Procesador Proxmox
|
||||||
# Migrado desde dtic-BKPs v6.0 → ADN::BKPs
|
# 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'
|
require 'tmpdir'
|
||||||
@@ -19,6 +24,62 @@ module ADN
|
|||||||
@pv = system('which pv > /dev/null 2>&1')
|
@pv = system('which pv > /dev/null 2>&1')
|
||||||
end
|
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)
|
def ejecutar(tarea)
|
||||||
@log.info("--- #{tarea[:texto]} ---")
|
@log.info("--- #{tarea[:texto]} ---")
|
||||||
@log.info("#{tarea[:id]}: Origen remoto: #{tarea[:origen]}")
|
@log.info("#{tarea[:id]}: Origen remoto: #{tarea[:origen]}")
|
||||||
@@ -39,39 +100,11 @@ module ADN
|
|||||||
|
|
||||||
logs.each do |ruta_log|
|
logs.each do |ruta_log|
|
||||||
base = File.basename(ruta_log, '.log')
|
base = File.basename(ruta_log, '.log')
|
||||||
begin
|
fuentes = Dir.glob(File.join(dir_tmp, "#{base}*"))
|
||||||
fuentes = Dir.glob(File.join(dir_tmp, "#{base}*"))
|
nombre_vm = extraer_nombre_vm(fuentes, ruta_log, base, tarea[:id])
|
||||||
nombre_vm = extraer_nombre_vm(fuentes, ruta_log, base, tarea[:id])
|
next unless nombre_vm
|
||||||
next unless nombre_vm
|
|
||||||
|
|
||||||
nombre_tar = "#{nombre_vm}_#{File.mtime(ruta_log).strftime('%Y%m%d_%H%M%S')}.tar.gz"
|
procesados += 1 if procesar_set(tarea, dir_tmp, ruta_log, base, fuentes, nombre_vm)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@log.info("✔ #{procesados} sets Proxmox procesados.")
|
@log.info("✔ #{procesados} sets Proxmox procesados.")
|
||||||
@@ -83,6 +116,37 @@ module ADN
|
|||||||
|
|
||||||
private
|
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)
|
def extraer_nombre_vm(fuentes, ruta_log, base, tarea_id)
|
||||||
notes = fuentes.find { |f| f.end_with?('.notes') }
|
notes = fuentes.find { |f| f.end_with?('.notes') }
|
||||||
nombre = notes ? File.read(notes).strip : nil
|
nombre = notes ? File.read(notes).strip : nil
|
||||||
@@ -148,3 +212,4 @@ module ADN
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user