ManageIQ/linux_admin

View on GitHub
lib/linux_admin/ssh.rb

Summary

Maintainability
A
3 hrs
Test Coverage
F
50%
module LinuxAdmin
  class SSH
    attr_reader :ip
    attr_reader :username
    attr_reader :private_key
    attr_reader :agent

    def initialize(ip, username, private_key = nil, password = nil)
      @ip = ip
      @private_key = private_key
      @username = username
      @password = password
    end

    def perform_commands(commands = [], agent_socket = nil, stdin = nil)
      require 'net/ssh'
      if block_given?
        execute_commands(commands, agent_socket, stdin, &Proc.new)
      else
        execute_commands(commands, agent_socket, stdin)
      end
    end

    private

    def execute_commands(commands, agent_socket, stdin)
      result = nil
      args = {:verify_host_key => false, :number_of_password_prompts => 0}
      if agent_socket
        args.merge!(:forward_agent              => true,
                    :agent_socket_factory       => -> { UNIXSocket.open(agent_socket) })
      elsif @private_key
        args[:key_data] = [@private_key]
      elsif @password
        args[:password] = @password
      end
      Net::SSH.start(@ip, @username, args) do |ssh|
        if block_given?
          result = yield ssh
        else
          commands.each do |cmd|
            result = ssh_exec!(ssh, cmd, stdin)
            result[:last_command] = cmd
            break if result[:exit_status] != 0
          end
        end
      end
      result
    end

    def ssh_exec!(ssh, command, stdin)
      stdout_data = ''
      stderr_data = ''
      exit_status = nil
      exit_signal = nil

      ssh.open_channel do |channel|
        channel.request_pty unless stdin
        channel.exec(command) do |_, success|
          channel.send_data(stdin) if stdin
          channel.eof!
          raise StandardError, "Command \"#{command}\" was unable to execute" unless success
          channel.on_data do |_, data|
            stdout_data << data
          end
          channel.on_extended_data do |_, _, data|
            stderr_data << data
          end
          channel.on_request('exit-status') do |_, data|
            exit_status = data.read_long
          end

          channel.on_request('exit-signal') do |_, data|
            exit_signal = data.read_long
          end
        end
      end
      ssh.loop
      {
        :stdout      => stdout_data,
        :stderr      => stderr_data,
        :exit_status => exit_status,
        :exit_signal => exit_signal
      }
    end
  end
end