# frozen_string_literal: true # adn/tools/cli/ssh/ejecutor.rb — Abstracción para ejecución segura via SSH y Candados # ====================================================================================== require 'open3' require_relative '../../core/constants' module ADN class EjecutorRemoto CANDADOS_CMD = ["ruby", ADN::CANDADOS_PATH] def self.ejecutar(nodo_info, comando, options = {}) # Si es local, ejecutar directamente if nodo_info[:ip] == 'localhost' || nodo_info[:ip] == '127.0.0.1' puts " [LOCAL] > #{comando[0..50]}..." if options[:verbose] output = `#{comando} 2>&1` return { success: $?.success?, output: output } end clave = options[:clave] || (nodo_info[:host] == 'srv-dasu' ? 'srv-dasu:rmonla' : 'win_domain') usuario = options[:usuario] || (nodo_info[:so] == :windows ? "monlaricardo@utnlaroja.intranet" : "rmonla") # Construir comando SSH base como un array para evitar problemas de shell ssh_cmd = [ "sshpass", "-e", "ssh", "-p", (nodo_info[:puerto_ssh] || 22).to_s, "-o", "StrictHostKeyChecking=no", "#{usuario}@#{nodo_info[:ip]}", comando ] # Comando final encapsulado vía candados # Usamos el modo array de candados: run candados_full = CANDADOS_CMD + ["run", clave, "SSHPASS"] + ssh_cmd puts " [SSH] #{nodo_info[:nombre]} (#{nodo_info[:ip]}) > #{comando[0..50]}..." if options[:verbose] # Usar Open3 para capturar salida limpia stdout, stderr, status = Open3.capture3(*candados_full) { success: status.success?, output: stdout + stderr } end # Pipeline para streaming (mejorado) def self.stream_pipeline(src_array, dst_array) begin # Open3.pipeline acepta múltiples arrays de comando statuses = Open3.pipeline(src_array, dst_array) statuses.all?(&:success?) rescue => e puts "Error en pipeline: #{e.message}" false end end # Generador de comandos para candados (reutilizable) def self.comando_candados(clave, env_var, comando_array) CANDADOS_CMD + ["run", clave, env_var] + comando_array end end end