101 lines
2.6 KiB
Ruby
Executable File
101 lines
2.6 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# [PROC-01] - Mantenimiento de Backups Proxmox (Ruby Edition)
|
|
# Lógica: Granular por VMID, Retención 6 días, Destino zfsDISCO1
|
|
|
|
require 'date'
|
|
require 'fileutils'
|
|
|
|
STORAGE_NAME = "zfsDISCO1"
|
|
# NOTA: En Proxmox, la ruta suele ser /mnt/pve/zfsDISCO1 o similar.
|
|
# Ajustamos a la ruta montada del storage.
|
|
BASE_PATH = "/mnt/pve/#{STORAGE_NAME}/dump"
|
|
RETENTION_DAYS = 6
|
|
VM_PATTERN = /^srv[v]?-/
|
|
|
|
def log(message, type = "INFO")
|
|
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
|
puts "[#{timestamp}] [#{type}] #{message}"
|
|
end
|
|
|
|
def get_vmid_list
|
|
# Obtener IDs de VMs (QEMU) y Contenedores (LXC)
|
|
ids = []
|
|
|
|
# QEMU VMs
|
|
`qm list`.each_line.with_index do |line, idx|
|
|
next if idx == 0 # Cabecera
|
|
parts = line.split
|
|
vmid = parts[0]
|
|
name = parts[1]
|
|
ids << vmid if name =~ VM_PATTERN
|
|
end
|
|
|
|
# LXC Containers
|
|
`pct list`.each_line.with_index do |line, idx|
|
|
next if idx == 0
|
|
parts = line.split
|
|
vmid = parts[0]
|
|
name = parts[2] # El nombre suele ser la 3er columna en pct list
|
|
ids << vmid if name =~ VM_PATTERN
|
|
end
|
|
|
|
ids.uniq
|
|
end
|
|
|
|
def prune_old_backups(vmid)
|
|
log("Iniciando fase de limpieza para VMID: #{vmid}...")
|
|
return unless Dir.exist?(BASE_PATH)
|
|
|
|
# Patrón de archivos de Proxmox: vzdump-[qemu|lxc]-<VMID>-<TIMESTAMP>.*
|
|
# Buscamos archivos de este VMID específico
|
|
files = Dir.glob("#{BASE_PATH}/vzdump-*-#{vmid}-*")
|
|
|
|
files.each do |file|
|
|
mtime = File.mtime(file)
|
|
age_days = (Date.today - mtime.to_date).to_i
|
|
|
|
if age_days > RETENTION_DAYS
|
|
log("Eliminando backup antiguo: #{File.basename(file)} (Antigüedad: #{age_days} días)", "WARN")
|
|
begin
|
|
FileUtils.rm(file)
|
|
rescue => e
|
|
log("Error al eliminar #{file}: #{e.message}", "ERROR")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def run_backup(vmid)
|
|
log("Iniciando backup para VMID: #{vmid} hacia #{STORAGE_NAME}...")
|
|
# Usamos --mode stop para asegurar reinicio automático y consistencia
|
|
cmd = "vzdump #{vmid} --storage #{STORAGE_NAME} --mode stop --compress zstd"
|
|
|
|
log("Ejecutando: #{cmd}")
|
|
success = system(cmd)
|
|
|
|
if success
|
|
log("Backup completado exitosamente para VMID: #{vmid}", "SUCCESS")
|
|
else
|
|
log("FALLO en el backup para VMID: #{vmid}", "ERROR")
|
|
end
|
|
end
|
|
|
|
# --- EJECUCIÓN PRINCIPAL ---
|
|
log("=== INICIO DE CICLO DE MANTENIMIENTO DE BACKUPS ===")
|
|
|
|
unless Dir.exist?(BASE_PATH)
|
|
log("Error: No se encuentra la ruta del storage #{BASE_PATH}. ¿Está montado?", "ERROR")
|
|
exit 1
|
|
end
|
|
|
|
vmids = get_vmid_list
|
|
log("Nodos detectados para procesar: #{vmids.join(', ')}")
|
|
|
|
vmids.each do |vmid|
|
|
prune_old_backups(vmid)
|
|
run_backup(vmid)
|
|
puts "-" * 40
|
|
end
|
|
|
|
log("=== FIN DE CICLO DE MANTENIMIENTO ===")
|