Class: HostMachine

Inherits:
Object
  • Object
show all
Defined in:
lib/drbman/host_machine.rb

Overview

HostMachine is used to interface with a host machine

Notes

A host machine may be either another machine or the localhost. By supporting localhost, it is likely that the process will be on a different core than the current processes.

Once HostMachine opens an ssh connection, it does not close the connection until a disconnect() is invoked.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host_string, logger, choices) ⇒ HostMachine

Returns a new instance of HostMachine.

Parameters:

  • host_string (String)

    describes the host to connect to. The format is “user{:password@}machine:port”

  • logger (Logger)

    the logger to use

  • choices (UserChoices, Hash)

Options Hash (choices):

  • :keys (Array<String>) — default: ['~/.ssh/id_dsa', '~/.ssh/id_rsa']

    array of ssh key file names.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/drbman/host_machine.rb', line 24

def initialize(host_string, logger, choices)
  @logger = logger
  @choices = choices
  @machine = 'localhost'
  @user = ENV['USER']
  @port = 9000
  keys = choices[:keys] || ["~/.ssh/id_dsa", "~/.ssh/id_rsa"]
  @password = {:keys => keys.collect{|name| name.gsub('~', ENV['HOME'])}.select{|name| File.exist?(name)}}
  case host_string
  when /^(\S+)\:(\S+)\@(\S+)\:(\d+)$/
    @user = $1
    @password = {:password => $2}
    @machine = $3
    @port = $4.to_i
  when /^(\S+)\:(\S+)\@(\S+)$/
    @user = $1
    @password = {:password => $2}
    @machine = $3
  when /^(\S+)\@(\S+)\:(\d+)$/
    @user = $1
    @machine = $2
    @port = $3.to_i
  when /^(\S+)\@(\S+)$/
    @user = $1
    @machine = $2
  when /^(\S+)\:(\d+)$/
    @machine = $1
    @port = $2.to_i
  when /^(\S+)$/
    @machine = $1
  end
  @name = "#{user}@#{machine}:#{port}"
  @ssh = nil
  @logger.debug { self.pretty_inspect }
end

Class Attribute Details

.connection_mutexObject

Returns the value of attribute connection_mutex.



15
16
17
# File 'lib/drbman/host_machine.rb', line 15

def connection_mutex
  @connection_mutex
end

Instance Attribute Details

#controllerObject

Returns the value of attribute controller.



11
12
13
# File 'lib/drbman/host_machine.rb', line 11

def controller
  @controller
end

#dirObject

Returns the value of attribute dir.



11
12
13
# File 'lib/drbman/host_machine.rb', line 11

def dir
  @dir
end

#machineObject (readonly)

Returns the value of attribute machine.



12
13
14
# File 'lib/drbman/host_machine.rb', line 12

def machine
  @machine
end

#nameObject (readonly)

Returns the value of attribute name.



12
13
14
# File 'lib/drbman/host_machine.rb', line 12

def name
  @name
end

#portObject (readonly)

Returns the value of attribute port.



12
13
14
# File 'lib/drbman/host_machine.rb', line 12

def port
  @port
end

#userObject (readonly)

Returns the value of attribute user.



12
13
14
# File 'lib/drbman/host_machine.rb', line 12

def user
  @user
end

#uuidObject

Returns the value of attribute uuid.



11
12
13
# File 'lib/drbman/host_machine.rb', line 11

def uuid
  @uuid
end

Instance Method Details

#alive?Boolean

Returns:

  • (Boolean)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/drbman/host_machine.rb', line 60

def alive?
  result = true
  begin
    session do |host|
      response = host.sh('uptime')
      @logger.debug { "#{host.name} #{response}" }
      raise Exception.new('no response from uptime') if response.strip.empty?
    end
  rescue Exception => e
    @logger.warn { "#{@name} is not responding: #{e}" }
    result = false
  end
  result
end

#download(remote_src, local_dest) ⇒ Object

download a directory structure from the host machine.

Parameters:

  • remote_src (String)

    the source directory on the host machine

  • local_dest (String)

    the destination directory on the local machine

Raises:

  • (Exception)

    if the files are not copied



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/drbman/host_machine.rb', line 124

def download(remote_src, local_dest)
  result = nil
  unless @ssh.nil?
    begin
      @ssh.scp.download!(local_src, remote_dest, :recursive => true) do |ch, name, sent, total|
        @logger.debug { "#{name}: #{sent}/#{total}" }
      end
      @ssh.loop
    rescue Exception => e
      # only raise the exception if the files differ
      raise e unless same_files?(local_dest, remote_src)
    end
  end
end

#session {|HostMachine| ... } ⇒ Object

Connect to the host, execute the given block, then disconnect from the host host_machine = HostMachine.new(‘localhost’, @logger) host_machine.session do |host|

host.upload(local_dir, "#{host.dir}/#{File.basename(local_dir)}")
@logger.debug { host.sh("ls -lR #{host.dir}") }

end

Yields:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/drbman/host_machine.rb', line 83

def session(&block)
  begin
    # this is ugly but apparently net-ssh can fail public_key authentication
    # when ran in parallel.
    HostMachine.connection_mutex.synchronize do
      connect
    end
    yield self
  rescue Net::SSH::AuthenticationFailed => e
    @logger.error { "Authentication Failed for #{e}" }
    raise e
  ensure
    disconnect
  end
end

#sh(command, opts = {}) ⇒ String?

run a command on the host machine Note that the environment on the host machine is the default environment instead of the user’s environment. So by default we try to source ~/.profile and ~/.bashrc

Parameters:

  • command (String)

    the command to run

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :source (Array<String>) — default: ['~/.profile', '~/.bashrc']

    array of files to source.

Returns:

  • (String, nil)

    the output from running the command



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/drbman/host_machine.rb', line 147

def sh(command, opts={})
  unless @ssh.nil?
    opts[:source] = ['~/.profile', '~/.bashrc'] if opts[:source].blank?
    result = nil
    commands = pre_commands(opts[:source])
    commands << command
    command_line = commands.join(' && ')
    @logger.debug { "sh: \"#{command_line}\""}
    result = @ssh.exec!(command_line)
    @logger.debug { "=> #{result}" }
  end
  result
end

#upload(local_src, remote_dest) ⇒ Object

upload a directory structure to the host machine.

Parameters:

  • local_src (String)

    the source directory on the local machine

  • remote_dest (String)

    the destination directory on the host machine

Raises:

  • (Exception)

    if the files are not copied



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/drbman/host_machine.rb', line 103

def upload(local_src, remote_dest)
  @logger.debug { "upload(\"#{local_src}\", \"#{remote_dest}\")" }
  result = nil
  unless @ssh.nil?
    begin
      @ssh.scp.upload!(local_src, remote_dest, :recursive => true) do |ch, name, sent, total|
        @logger.debug { "#{name}: #{sent}/#{total}" }
      end
      @ssh.loop
    rescue Exception => e
      # only raise the exception if the files differ
      @logger.debug { e }
      raise e unless same_files?(local_src, remote_dest)
    end
  end
end