# frozen_string_literal: true # ========================================================== # adn/tools/cli/drones/lib/dasu_executor.rb # ========================================================== # Módulo helper para ejecutar PowerShell en dasu-sql4 vía srv-dasu # Centraliza: SSH, sshpass, base64 encoding, lectura de JSON # ========================================================== require 'base64' require 'tempfile' module DasuExecutor # Configuración del target dasu-sql4 TARGET_IP = '192.168.1.11' TARGET_USER = 'Administrador' TARGET_PASS = 'UTNlarioja00DASU' TARGET_PORT = '7022' # Path donde se guardan los JSON output en dasu-sql4 DEFAULT_JSON_PATH = 'C:\temp\output.json' module_function # Ejecuta un script PowerShell en dasu-sql4 vía srv-dasu # # @param ps_script [String] Script PowerShell a ejecutar # @param output_path [String] Path donde PowerShell guardará el JSON (default: C:\temp\output.json) # @return [Hash] JSON parseado del output # # Ejemplo: # result = DasuExecutor.execute_ps_on_dasu(<<~PS) # @{ status = "ok" } | ConvertTo-Json | Out-File C:\temp\out.json # Get-Content C:\temp\out.json -Raw # PS def execute_ps_on_dasu(ps_script, output_path: DEFAULT_JSON_PATH) ps_encoded = Base64.strict_encode64(ps_script) bash_script = <<~BASHHEREDOC #!/bin/bash export SSHPASS="#{TARGET_PASS}" sshpass -e ssh -o StrictHostKeyChecking=no -p #{TARGET_PORT} #{TARGET_USER}@#{TARGET_IP} \ "powershell -Command \\\"Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{ps_encoded}')))\\\"" BASHHEREDOC execute_bash_temporary(bash_script) end # Ejecuta un script PowerShell y lee el JSON resultante # Asume que el script escribe un JSON en output_path # # @param ps_script [String] Script PowerShell a ejecutar # @param output_path [String] Path del JSON en dasu-sql4 # @return [Hash] JSON parseado def execute_ps_with_json_output(ps_script, output_path: DEFAULT_JSON_PATH) # Ejecutar el script principal execute_ps_on_dasu(ps_script, output_path: output_path) # Leer el JSON resultante read_json_from_dasu(output_path) end # Lee un JSON desde dasu-sql4 # # @param output_path [String] Path del JSON en dasu-sql4 (ej: C:\temp\output.json) # @return [Hash] JSON parseado def read_json_from_dasu(output_path) json_ps = "Get-Content #{output_path} -Raw" json_encoded = Base64.strict_encode64(json_ps) json_script = <<~BASHHEREDOC.strip #!/bin/bash export SSHPASS="#{TARGET_PASS}" sshpass -e ssh -o StrictHostKeyChecking=no -p #{TARGET_PORT} #{TARGET_USER}@#{TARGET_IP} \ "powershell -Command \\\"Invoke-Expression ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('#{json_encoded}')))\\\"" BASHHEREDOC resultado = execute_bash_temporary(json_script) resultado = resultado.strip # Extraer JSON del output (puede tener ruido) json_match = resultado.match(/\{.*\}/m) resultado = json_match ? json_match[0] : resultado require 'json' JSON.parse(resultado) end # Ejecuta un script bash temporal y lo limpia después # # @param bash_script [String] Script bash a ejecutar # @param remote_base [String] Directorio remoto base (default: /tmp) # @return [String] Output del comando def execute_bash_temporary(bash_script, remote_base: '/tmp') bash_file = Tempfile.new(['dasu', '.sh']) bash_file.write(bash_script) bash_file.chmod(0755) bash_file.close remote_path = "#{remote_base}/dron_#{Time.now.to_i}.sh" # Copiar script a srv-dasu scp_cmd = "scp -o StrictHostKeyChecking=no #{bash_file.path} srv-dasu:#{remote_path}" unless system(scp_cmd) bash_file.unlink raise "Error al copiar script a srv-dasu" end # Ejecutar script ssh_cmd = "ssh srv-dasu 'bash #{remote_path}'" output_buffer = "" IO.popen(ssh_cmd, err: [:child, :out]) do |io| io.each_line do |line| safe_line = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') print safe_line output_buffer += safe_line end end output = output_buffer # Limpieza bash_file.unlink system("ssh srv-dasu 'rm -f #{remote_path}' 2>/dev/null") output end # Helper para ejecutar PowerShell en fenix vía xp_cmdshell # Útil para verificar archivos en shares mapeados # # @param client [TinyTds::Client] Conexión SQL Server # @param ps_script [String] Script PowerShell a ejecutar # @return [Array] Resultado de la ejecución def execute_ps_on_fenix(client, ps_script) escaped = ps_script.gsub('"', '\"') client.execute("EXEC xp_cmdshell 'powershell -Command \"#{escaped}\"'") end # Verifica si un archivo existe en un path Windows/fenix # # @param client [TinyTds::Client] Conexión SQL Server # @param path [String] Path del archivo (ej: X:\backup.bak) # @return [Boolean] true si existe def file_exists_on_fenix?(client, path) result = client.execute("EXEC master.sys.xp_fileexist '#{path}'") result.to_a.any? { |f| f['File Exists'] == 1 } end # Obtiene información de tamaño de un archivo en fenix # # @param client [TinyTds::Client] Conexión SQL Server # @param path [String] Path del archivo # @return [Hash] Con :exists, :size_bytes, :size_mb def get_file_info_on_fenix(client, path) ps_script = <<~PSCMD.strip if (Test-Path "#{path}") { $item = Get-Item "#{path}" Write-Host "EXISTS:1" Write-Host "SIZE:$($item.Length)" } else { Write-Host "EXISTS:0" } PSCMD result = execute_ps_on_fenix(client, ps_script) output = result.map { |r| r['output'] }.compact.join("\n") exists = output.include?('EXISTS:1') size = output.match(/SIZE:(\d+)/) ? $1.to_i : 0 { exists: exists, size_bytes: size, size_mb: (size / 1024.0 / 1024.0).round(2) } end end