royw-drbman 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,9 @@
1
1
  *.sw?
2
2
  .DS_Store
3
+ .yardoc
3
4
  coverage
4
5
  rdoc
6
+ doc
5
7
  pkg
8
+ tmp
6
9
  *~
data/Rakefile CHANGED
@@ -52,3 +52,9 @@ Rake::RDocTask.new do |rdoc|
52
52
  rdoc.rdoc_files.include('lib/**/*.rb')
53
53
  end
54
54
 
55
+ require 'yard'
56
+ YARD::Rake::YardocTask.new do |t|
57
+ t.files = ['lib/**/*.rb'] # optional
58
+ # t.options = ['--any', '--extra', '--opts'] # optional
59
+ end
60
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{drbman}
5
- s.version = "0.0.1"
5
+ s.version = "0.0.2"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Roy Wright"]
9
- s.date = %q{2009-07-14}
9
+ s.date = %q{2009-07-16}
10
10
  s.default_executable = %q{drbman}
11
11
  s.email = %q{roy@wright.org}
12
12
  s.executables = ["drbman"]
@@ -33,6 +33,7 @@ Gem::Specification.new do |s|
33
33
  "examples/primes/lib/primes/sieve_of_eratosthenes.rb",
34
34
  "examples/primes/spec/primes_spec.rb",
35
35
  "examples/primes/spec/spec_helper.rb",
36
+ "examples/primes/stress_test.rb",
36
37
  "lib/drbman.rb",
37
38
  "lib/drbman/cli.rb",
38
39
  "lib/drbman/drb_pool.rb",
@@ -58,7 +59,8 @@ Gem::Specification.new do |s|
58
59
  "examples/primes/lib/primes/sieve_of_eratosthenes.rb",
59
60
  "examples/primes/lib/primes.rb",
60
61
  "examples/primes/spec/primes_spec.rb",
61
- "examples/primes/spec/spec_helper.rb"
62
+ "examples/primes/spec/spec_helper.rb",
63
+ "examples/primes/stress_test.rb"
62
64
  ]
63
65
 
64
66
  if s.respond_to? :specification_version then
@@ -1,34 +1,44 @@
1
1
  require 'drb'
2
- require 'log4r'
3
2
 
3
+ # A helper object for calculating primes using the Sieve of Eratosthenes
4
+ #
5
+ # == Usage
6
+ # ruby prime_helper.rb foo.example.com 1234
7
+ # will run the service as: druby://foo.example.com:1234
8
+ #
9
+ # ruby prime_helper.rb foo.example.com
10
+ # will run the service as: druby://foo.example.com:9000
11
+ #
12
+ # ruby prime_helper.rb
13
+ # will run the service as: druby://localhost:9000
14
+ #
4
15
  class PrimeHelper
5
16
  attr_accessor :name
6
- def non_primes(ip, n)
17
+
18
+ # Find the multiples of the give prime number that are less than the
19
+ # given maximum.
20
+ # @example
21
+ # multiples_of(5,20) => [10, 15]
22
+ # @param [Integer] prime the prime number to find the multiples of
23
+ # @param [Integer] maximum the maximum integer
24
+ # @return [Array<Integer>] the array of the prime multiples
25
+ def multiples_of(prime, maximum)
7
26
  a = []
8
- 2.upto((n - 1) / ip) { |i| a << (i * ip) }
27
+ 2.upto((maximum - 1) / prime) { |i| a << (i * prime) }
9
28
  a
10
29
  end
30
+
31
+ # Stop the DRb service
11
32
  def stop_service
12
33
  DRb.stop_service
13
34
  end
14
35
  end
15
36
 
16
- logger = Log4r::Logger.new('prime_helper')
17
- logger.outputters = Log4r::FileOutputter.new(:console, :filename => File.join(File.dirname(__FILE__), '../prime_helper.log'))
18
- Log4r::Outputter[:console].formatter = Log4r::PatternFormatter.new(:pattern => "%m")
19
- logger.level = Log4r::INFO
20
-
21
37
  machine = 'localhost'
22
38
  machine = ARGV[0] unless ARGV.length < 1
23
39
  port = 9000
24
40
  port = ARGV[1] unless ARGV.length < 2
25
- service = "druby://#{machine}:#{port}"
26
- logger.info { "ARGV => #{ARGV.inspect}" }
27
- logger.info { "machine => #{machine}" }
28
- logger.info { "port => #{port}" }
29
- logger.info { "drb service => #{service}" }
30
41
  server = PrimeHelper.new
31
- server.name = service
32
- DRb.start_service(service, server)
42
+ server.name = "druby://#{machine}:#{port}"
43
+ DRb.start_service(server.name, server)
33
44
  DRb.thread.join
34
- logger.info { "finished" }
@@ -55,6 +55,12 @@ class CLI < UserChoices::Command
55
55
  builder.add_choice(:debug, :type => :boolean, :default => false) do |command_line|
56
56
  command_line.uses_switch('-v', '--verbose', 'Display debug messages')
57
57
  end
58
+ builder.add_choice(:leave, :type => :boolean, :default => false) do |command_line|
59
+ command_line.uses_switch('-l', '--leave', 'Leave files on host machines')
60
+ end
61
+ builder.add_choice(:ssh_debug, :type => :boolean, :default => false) do |command_line|
62
+ command_line.uses_switch('-s', '--ssh-debug', 'Display the ssh debug messages')
63
+ end
58
64
  builder.add_choice(:hosts, :type => [:string], :default => []) do |command_line|
59
65
  command_line.uses_option('-H', '--hosts "HOST,HOST"', 'Comma separated host machines, ex: "machine1{,machine2{,...}}"')
60
66
  end
@@ -1,29 +1,50 @@
1
- # Command design pattern
1
+ # The Sieve of Eratosthenes prime number finder
2
+ # Note, uses the Command design pattern
2
3
  class SieveOfEratosthenes
3
4
  attr_reader :primes_elapse_time
4
- def initialize(n, choices, logger)
5
- @n = n.to_i
5
+
6
+ # Use the Sieve of Eratosthenes to find prime numbers
7
+ #
8
+ # @param [Integer] maximum find all primes lower than this maximum value
9
+ # @option choices [Array<String>] :dirs array of local directories to copy to the host machines. REQUIRED
10
+ # @option choices [String] :run the name of the file to run on the host machine. REQUIRED
11
+ # This file should start the drb server. Note, this file will be daemonized before running.
12
+ # @option choices [Array<String>] :hosts array of host machine descriptions "{user{:password}@}machine{:port}"
13
+ # This defaults to ['localhost']
14
+ # @option choices [Integer] :port default port number used to assign to hosts without a port number.
15
+ # The port number is incremented for each host. This defaults to 9000
16
+ # @option choices [Array<String>] :gems array of gem names to verify are installed on the host machine.
17
+ # Note, 'daemons' is always added to this array.
18
+ # @param [Logger] logger the logger to use
19
+ def initialize(maximum, choices, logger)
20
+ @maximum = maximum.to_i
6
21
  @choices = choices
7
22
  @logger = logger
8
23
 
9
24
  # we need at least one host that has a drb server running
10
25
  @choices[:hosts] = ['localhost'] if @choices[:hosts].blank?
11
26
 
27
+ # specify the directories to copy to the host machine
28
+ @choices[:dirs] = [File.join(File.dirname(__FILE__), '../drb_server')]
29
+
12
30
  # set the file to be ran that contains the drb server
13
31
  @choices[:run] = 'drb_server/prime_helper.rb' if @choices[:run].blank?
32
+
33
+ # specify gems required by the drb server object
34
+ # each host will be checked to make sure these gems are installed
14
35
  @choices[:gems] = ['log4r']
15
36
 
16
- # specify the directories to copy to the host machine
17
- @choices[:dirs] = [File.join(File.dirname(__FILE__), '../drb_server')]
18
37
  end
19
38
 
39
+ # Calculate the primes
40
+ # @return [Array<Integer] the primes in an Array
20
41
  def execute
21
42
  result = []
22
43
  @logger.debug { @choices.pretty_inspect }
23
44
 
24
45
  Drbman.new(@logger, @choices) do |drbman|
25
46
  @primes_elapse_time = elapse do
26
- result = primes(@n, drbman)
47
+ result = primes(@maximum, drbman)
27
48
  end
28
49
  end
29
50
  result
@@ -31,12 +52,12 @@ class SieveOfEratosthenes
31
52
 
32
53
  private
33
54
 
34
- def primes(n, drbman)
55
+ def primes(maximum, drbman)
35
56
  indices = []
36
- if n > 2
37
- composites = calc_composites(n, drbman)
57
+ if maximum > 2
58
+ composites = calc_composites(maximum, drbman)
38
59
  flat_comps = composites.flatten.uniq
39
- indices = calc_indices(flat_comps, n)
60
+ indices = calc_indices(flat_comps, maximum)
40
61
  end
41
62
  indices
42
63
  end
@@ -45,21 +66,19 @@ class SieveOfEratosthenes
45
66
  # sqr_primes = [2,3]
46
67
  # composites = [[2*2, 2*3, 2*4,...,2*9], [3*2, 3*3, 3*4,...,3*6]]
47
68
  # returns Array
48
- def calc_composites(n, drbman)
49
- sqr_primes = primes(Math.sqrt(n).to_i, drbman)
69
+ def calc_composites(maximum, drbman)
70
+ sqr_primes = primes(Math.sqrt(maximum).to_i, drbman)
50
71
  composites = []
51
72
  threads = []
52
73
  mutex = Mutex.new
53
74
  sqr_primes.each do |ip|
54
75
  # parallelize via threads
55
76
  # then use the drb object within the thread
56
- threads << Thread.new(ip, n) do |value, max|
57
- # @logger.debug { "thread(#{ip}, #{n})" }
77
+ threads << Thread.new(ip, maximum) do |prime, max|
58
78
  drbman.get_object do |prime_helper|
59
- # @logger.debug { "prime_helper.name => #{prime_helper.name}" }
60
- non_primes = prime_helper.non_primes(value, max)
79
+ prime_multiples = prime_helper.multiples_of(prime, max)
61
80
  mutex.synchronize do
62
- composites << non_primes
81
+ composites << prime_multiples
63
82
  end
64
83
  end
65
84
  end
@@ -68,9 +87,9 @@ class SieveOfEratosthenes
68
87
  composites
69
88
  end
70
89
 
71
- def calc_indices(flat_comps, n)
90
+ def calc_indices(flat_comps, maximum)
72
91
  indices = []
73
- flags = Array.new(n, true)
92
+ flags = Array.new(maximum, true)
74
93
  flat_comps.each {|i| flags[i] = false}
75
94
  flags.each_index {|i| indices << i if flags[i] }
76
95
  indices.shift(2)
@@ -1,6 +1,8 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'primes'
3
3
 
4
+ # Note, these assume you have an ssh public key set up for
5
+ # this box (i.e: it does an "ssh localhost").
4
6
 
5
7
  describe('SieveOfEratosthenes') do
6
8
  before(:each) do
@@ -14,11 +16,20 @@ describe('SieveOfEratosthenes') do
14
16
  sieve = Primes.new(@logger, @choices)
15
17
  sieve.execute.should == [2,3,5,7,11,13,17,19]
16
18
  end
17
- it 'should take a while' do
18
- sieve = SieveOfEratosthenes.new(10000000, @choices, @logger)
19
- primes = sieve.execute
20
- puts "#{primes.length} primes found"
21
- primes.length.should == 664579
19
+
20
+ it 'should find 303 primes below 2000 with single host' do
21
+ @choices[:max_integer] = 2000
22
+ @choices[:hosts] = ['localhost']
23
+ sieve = Primes.new(@logger, @choices)
24
+ sieve.execute.length.should == 303
25
+ end
26
+
27
+ it 'should find 303 primes below 2000 with two hosts' do
28
+ @choices[:max_integer] = 2000
29
+ @choices[:hosts] = ['localhost', 'localhost']
30
+ sieve = Primes.new(@logger, @choices)
31
+ sieve.execute.length.should == 303
22
32
  end
33
+
23
34
  end
24
35
 
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ good = 0
4
+ bad = 0
5
+ for cnt in 1..200
6
+ if `bin/primes 2000 -H localhost,localhost` =~ /303\sprimes\sfound/
7
+ good += 1
8
+ putc '.'
9
+ else
10
+ bad += 1
11
+ putc 'x'
12
+ end
13
+ puts " #{cnt}" if (cnt % 50) == 0
14
+ STDOUT.flush
15
+ end
16
+ puts
17
+ puts "good: #{good}"
18
+ puts "bad: #{bad}"
@@ -55,6 +55,12 @@ class CLI < UserChoices::Command
55
55
  builder.add_choice(:run, :type => :string, :default => nil) do |command_line|
56
56
  command_line.uses_option('-r', '--run "COMMAND"', "The ruby file that starts the drb server")
57
57
  end
58
+ builder.add_choice(:leave, :type => :boolean, :default => false) do |command_line|
59
+ command_line.uses_switch('-l', '--leave', 'Leave files on host machines')
60
+ end
61
+ builder.add_choice(:ssh_debug, :type => :boolean, :default => false) do |command_line|
62
+ command_line.uses_switch('-s', '--ssh-debug', 'Display the ssh debug messages')
63
+ end
58
64
  builder.add_choice(:hosts, :type => [:string], :default => []) do |command_line|
59
65
  command_line.uses_option('-H', '--hosts "HOST,HOST"', 'Comma separated account URLs, ex: "{user{:pass}@}machine1{,{user{:pass}@}machine2}"')
60
66
  end
@@ -100,9 +100,15 @@ class DrbPool
100
100
 
101
101
  # Adds an in_use attribute
102
102
  module InUse
103
+ # set the in_use flag
104
+ # @param [Boolean] flag
105
+ # @return [Boolean] the new state of the in_use flag
103
106
  def in_use=(flag)
104
107
  @in_use = (flag ? true : false)
105
108
  end
109
+
110
+ # get the in_use flag
111
+ # @return [Boolean] the state of the in_use flag
106
112
  def in_use?
107
113
  @in_use
108
114
  end
@@ -15,22 +15,22 @@
15
15
  class Drbman
16
16
  # @param [Logger] logger the logger
17
17
  # @param [UserChoices,Hash] choices
18
- # @option choices [Array<String>] :dirs array of local directories to copy to the host machines. REQUIRED
19
- # @option choices [String] :run the name of the file to run on the host machine. REQUIRED
18
+ # @option choices [Array<String>] :dirs array of local directories to copy to the host machines (REQUIRED).
19
+ # @option choices [String] :run the name of the file to run on the host machine (REQUIRED).
20
20
  # This file should start the drb server. Note, this file will be daemonized before running.
21
- # @option choices [Array<String>] :hosts array of host machine descriptions "{user{:password}@}machine{:port}"
22
- # This defaults to ['localhost']
23
- # @option choices [Integer] :port default port number used to assign to hosts without a port number.
24
- # The port number is incremented for each host. This defaults to 9000
25
- # @option choices [Array<String>] :gems array of gem names to verify are installed on the host machine.
26
- # Note, 'daemons' is always added to this array.
21
+ # @option choices [Array<String>] :hosts (['localhost']) array of host machine descriptions "{user{:password}@}machine{:port}".
22
+ # @option choices [Integer] :port (9000) default port number used to assign to hosts without a port number,
23
+ # the port number is incremented for each host.
24
+ # @option choices [Array<String>] :gems ([]) array of gem names to verify are installed on the host machine,
25
+ # note, 'daemons' is always added to this array.
26
+ # @option choices [Array<String>] :keys (['~/.ssh/id_dsa', '~/.ssh/id_rsa']) array of ssh key file names.
27
27
  # @yield [Drbman]
28
28
  # @example Usage
29
- # Drbman.new(logger, choices) do |drbman|
30
- # drbman.get_object do |obj|
31
- # obj.do_something
29
+ # Drbman.new(logger, choices) do |drbman|
30
+ # drbman.get_object do |obj|
31
+ # obj.do_something
32
+ # end
32
33
  # end
33
- # end
34
34
  def initialize(logger, choices, &block)
35
35
  @logger = logger
36
36
  @user_choices = choices
@@ -38,20 +38,20 @@ class Drbman
38
38
  # @hosts[machine_description] = HostMachine instance
39
39
  @hosts = {}
40
40
 
41
- choices[:port] ||= 9000
42
- choices[:hosts] ||= ['localhost']
43
- choices[:gems] ||= []
44
- choices[:gems] = (choices[:gems] + ['daemons']).uniq.compact
41
+ @user_choices[:port] ||= 9000
42
+ @user_choices[:hosts] ||= ['localhost']
43
+ @user_choices[:gems] ||= []
44
+ @user_choices[:gems] = (@user_choices[:gems] + ['daemons']).uniq.compact
45
45
 
46
- raise ArgumentError.new('Missing choices[:run]') if choices[:run].blank?
47
- raise ArgumentError.new('Missing choices[:hosts]') if choices[:hosts].blank?
48
- raise ArgumentError.new('Missing choices[:dirs]') if choices[:dirs].blank?
46
+ raise ArgumentError.new('Missing choices[:run]') if @user_choices[:run].blank?
47
+ raise ArgumentError.new('Missing choices[:hosts]') if @user_choices[:hosts].blank?
48
+ raise ArgumentError.new('Missing choices[:dirs]') if @user_choices[:dirs].blank?
49
49
 
50
50
  # populate the @hosts hash. key => host machine description, value => HostMachine instance
51
- port = choices[:port]
51
+ port = @user_choices[:port]
52
52
  @user_choices[:hosts].each do |host|
53
53
  host = "#{host}:#{port}" unless host =~ /\:\d+\s*$/
54
- @hosts[host] = HostMachine.new(host, @logger)
54
+ @hosts[host] = HostMachine.new(host, @logger, @user_choices)
55
55
  port += 1
56
56
  end
57
57
 
@@ -87,12 +87,7 @@ class Drbman
87
87
  @hosts.each do |name, machine|
88
88
  threads << Thread.new(machine) do |host_machine|
89
89
  host_machine.session do |host|
90
- # begin
91
- startup(host)
92
- # rescue Exception => e
93
- # @logger.error { e }
94
- # @logger.debug { e.backtrace.join("\n") }
95
- # end
90
+ startup(host)
96
91
  end
97
92
  end
98
93
  end
@@ -122,7 +117,7 @@ class Drbman
122
117
  # @param [HostMachine] host the host machine
123
118
  # @raise [Exception] when a component is not installed on the host
124
119
  def startup(host)
125
- @logger.info { "Setting up: #{host.name}" }
120
+ @logger.debug { "Setting up: #{host.name}" }
126
121
  check_gems(host)
127
122
  create_directory(host)
128
123
  upload_dirs(host)
@@ -133,7 +128,7 @@ class Drbman
133
128
  # Stop the drb server on the given host then remove the drb files from the host
134
129
  # @param [HostMachine] host the host machine
135
130
  def cleanup(host)
136
- @logger.info { "Cleaning up: #{host.name}" }
131
+ @logger.debug { "Cleaning up: #{host.name}" }
137
132
  stop_drb_server(host)
138
133
  cleanup_files(host)
139
134
  end
@@ -144,7 +139,7 @@ class Drbman
144
139
  def cleanup_files(host)
145
140
  unless host.dir.blank? || (host.dir =~ /[\*\?]/)
146
141
  @logger.debug { "#{host.name}: rm -rf #{host.dir}"}
147
- host.sh("rm -rf #{host.dir}")
142
+ host.sh("rm -rf #{host.dir}") unless @user_choices[:leave]
148
143
  end
149
144
  end
150
145
 
@@ -201,7 +196,7 @@ class Drbman
201
196
  def check_gems(host)
202
197
  missing_gems = []
203
198
  @user_choices[:gems].each do |gem_name|
204
- case host.sh("gem list -l -i #{gem_name}")
199
+ case str = host.sh("gem list -l -i #{gem_name}")
205
200
  when /false/i
206
201
  missing_gems << gem_name
207
202
  when /command not found/
@@ -218,11 +213,13 @@ class Drbman
218
213
  # Note, requires 'uuidgen' in the path on the host machine
219
214
  # @todo maybe generate a random uuid locally instead
220
215
  # @param [HostMachine] host the host machine
216
+ # @return [String] the created directory path
221
217
  def create_directory(host)
222
218
  host.uuid = UUIDTools::UUID.random_create
223
219
  host.dir = "~/.drbman/#{host.uuid}".strip
224
220
  host.sh("mkdir -p #{host.dir}")
225
221
  @logger.debug { "host directory: #{host.dir}" }
222
+ host.dir
226
223
  end
227
224
 
228
225
  end
@@ -11,15 +11,24 @@ class HostMachine
11
11
  attr_accessor :uuid, :dir, :controller
12
12
  attr_reader :name, :machine, :user, :port
13
13
 
14
+ class << self
15
+ attr_accessor :connection_mutex
16
+ end
17
+ @connection_mutex = Mutex.new
18
+
14
19
  # @param [String] host_string describes the host to connect to.
15
20
  # The format is "{user{:password}@}machine{:port}"
16
21
  # @param [Logger] logger the logger to use
17
- def initialize(host_string, logger)
22
+ # @param [UserChoices,Hash] choices
23
+ # @option choices [Array<String>] :keys (['~/.ssh/id_dsa', '~/.ssh/id_rsa']) array of ssh key file names.
24
+ def initialize(host_string, logger, choices)
18
25
  @logger = logger
26
+ @choices = choices
19
27
  @machine = 'localhost'
20
28
  @user = ENV['USER']
21
29
  @port = 9000
22
- @password = {:keys => ['~/.ssh/id_dsa']}
30
+ keys = choices[:keys] || ["~/.ssh/id_dsa", "~/.ssh/id_rsa"]
31
+ @password = {:keys => keys.collect{|name| name.gsub('~', ENV['HOME'])}.select{|name| File.exist?(name)}}
23
32
  case host_string
24
33
  when /^(\S+)\:(\S+)\@(\S+)\:(\d+)$/
25
34
  @user = $1
@@ -57,9 +66,20 @@ class HostMachine
57
66
  # @logger.debug { host.sh("ls -lR #{host.dir}") }
58
67
  # end
59
68
  def session(&block)
60
- connect
61
- yield self
62
- disconnect
69
+ begin
70
+ # this is ugly but apparently net-ssh can fail public_key authentication
71
+ # when ran in parallel.
72
+ HostMachine.connection_mutex.synchronize do
73
+ connect
74
+ end
75
+ yield self
76
+ rescue Exception => e
77
+ @logger.error { e }
78
+ @logger.error { e.backtrace.join("\n") }
79
+ raise e
80
+ ensure
81
+ disconnect
82
+ end
63
83
  end
64
84
 
65
85
  # upload a directory structure to the host machine.
@@ -68,7 +88,6 @@ class HostMachine
68
88
  # @raise [Exception] if the files are not copied
69
89
  def upload(local_src, remote_dest)
70
90
  @logger.debug { "upload(\"#{local_src}\", \"#{remote_dest}\")" }
71
- connect
72
91
  result = nil
73
92
  unless @ssh.nil?
74
93
  begin
@@ -82,13 +101,12 @@ class HostMachine
82
101
  end
83
102
  end
84
103
  end
85
-
104
+
86
105
  # download a directory structure from the host machine.
87
106
  # @param [String] remote_src the source directory on the host machine
88
107
  # @param [String] local_dest the destination directory on the local machine
89
108
  # @raise [Exception] if the files are not copied
90
109
  def download(remote_src, local_dest)
91
- connect
92
110
  result = nil
93
111
  unless @ssh.nil?
94
112
  begin
@@ -109,84 +127,56 @@ class HostMachine
109
127
  #
110
128
  # @param [String] command the command to run
111
129
  # @param [Hash] opts
112
- # @options opts [Array<String>] :source array of files to source. defaults to ['~/.profile', '~/.bashrc']
130
+ # @option opts [Array<String>] :source (['~/.profile', '~/.bashrc']) array of files to source.
131
+ # @return [String, nil] the output from running the command
113
132
  def sh(command, opts={})
114
- @logger.debug { "sh \"#{command}\""}
115
- # if opts[:source].blank?
116
- # opts[:source] = ['~/.profile', '~/.bashrc']
117
- # end
118
- connect
119
- result = nil
120
133
  unless @ssh.nil?
121
- if @pre_commands.nil?
122
- @pre_commands = []
123
- opts[:source] ||= []
124
- opts[:source].each do |name|
125
- ls_out = @ssh.exec!("ls #{name}")
126
- @pre_commands << "source #{name}" if ls_out =~ /^\s*\S+\/#{File.basename(name)}\s*$/
127
- end
128
- end
129
- commands = @pre_commands.clone
134
+ opts[:source] = ['~/.profile', '~/.bashrc'] if opts[:source].blank?
135
+ result = nil
136
+ commands = pre_commands(opts[:source])
130
137
  commands << command
131
138
  command_line = commands.join(' && ')
139
+ @logger.debug { "sh: \"#{command_line}\""}
132
140
  result = @ssh.exec!(command_line)
141
+ @logger.debug { "=> #{result}" }
133
142
  end
134
143
  result
135
144
  end
136
145
 
137
- # run a command as the superuser on the host machine
138
- # Note that the environment on the host machine is the default environment instead
139
- # of the user's environment. So by default we try to source ~/.profile and ~/.bashrc
140
- #
141
- # @param [String] command the command to run
142
- # @param [Hash] opts
143
- # @options opts [Array<String>] :source array of files to source. defaults to ['~/.profile', '~/.bashrc']
144
- def sudo(command, opts={})
145
- @logger.debug { "sudo \"#{command}\""}
146
- # if opts[:source].blank?
147
- # opts[:source] = ['~/.profile', '~/.bashrc']
148
- # end
149
- connect
150
- result = nil
151
- unless @ssh.nil?
152
- buf = []
153
- @ssh.open_channel do |channel|
154
- if @pre_commands.nil?
155
- @pre_commands = []
156
- opts[:source] ||= []
157
- opts[:source].each do |name|
158
- ls_out = @ssh.exec!("ls #{name}")
159
- @pre_commands << "source #{name}" if ls_out =~ /^\s*\S+\/#{File.basename(name)}\s*$/
160
- end
161
- end
162
- commands = @pre_commands.clone
163
- commands << "sudo -p 'sudo password: ' #{command}"
164
- command_line = commands.join(' && ')
165
- channel.exec(command_line) do |ch, success|
166
- ch.on_data do |ch, data|
167
- if data =~ /sudo password: /
168
- ch.send_data("#{@password[:password]}\n")
169
- else
170
- buf << data
171
- end
172
- end
146
+ private
147
+
148
+ def pre_commands(sources)
149
+ if @pre_commands.nil?
150
+ @pre_commands = []
151
+ unless @ssh.nil?
152
+ sources.each do |name|
153
+ ls_out = @ssh.exec!("ls #{name}")
154
+ @pre_commands << "source #{name}" if ls_out =~ /^\s*\S+\/#{File.basename(name)}\s*$/
173
155
  end
174
156
  end
175
- @ssh.loop
176
- result = buf.compact.join('')
177
157
  end
178
- result
158
+ @pre_commands.clone
179
159
  end
180
-
160
+
181
161
  # connect to the host machine
162
+ # note, you should not need to call the connect method.
163
+ # @see {#session}
182
164
  def connect
183
165
  if @ssh.nil?
184
- @ssh = Net::SSH.start(@machine, @user, @password)
166
+ options = @password.merge({
167
+ :timeout=>2,
168
+ :auth_methods => %w(publickey hostbased password)
169
+ })
170
+ options = @password.merge({:verbose=>Logger::DEBUG}) if @choices[:ssh_debug]
171
+ @logger.debug { "connect: @machine=>#{@machine}, @user=>#{@user}, options=>#{options.inspect}" }
172
+ @ssh = Net::SSH.start(@machine, @user, options)
185
173
  # @ssh.forward.local(@port, @machine, @port)
186
174
  end
187
175
  end
188
176
 
189
- # disconnect from the host machine
177
+ # disconnect from the host machine.
178
+ # note, you should not need to call the disconnect method.
179
+ # @see {#session}
190
180
  def disconnect
191
181
  if @ssh
192
182
  @ssh.close
@@ -194,19 +184,24 @@ class HostMachine
194
184
  end
195
185
  end
196
186
 
197
- private
198
-
199
187
  # Does the local directory tree and the remote directory tree contain the same files?
200
188
  # Calculates a MD5 hash for each file then compares the hashes
201
189
  # @param [String] local_path local directory
202
190
  # @param [String] remote_path remote directory
191
+ # @return [Boolean] asserted if the files in both directory trees are identical
203
192
  def same_files?(local_path, remote_path)
204
- remote_md5 = @ssh.exec!(md5_command_line(remote_path))
205
- local_md5 = `#{md5_command_line(local_path)}`
206
- @logger.debug { "same_files? local_md5 => #{local_md5}, remote_md5 => #{remote_md5}"}
207
- remote_md5 == local_md5
193
+ result = false
194
+ unless @ssh.nil?
195
+ remote_md5 = @ssh.exec!(md5_command_line(remote_path))
196
+ local_md5 = `#{md5_command_line(local_path)}`
197
+ @logger.debug { "same_files? local_md5 => #{local_md5}, remote_md5 => #{remote_md5}"}
198
+ result = (remote_md5 == local_md5)
199
+ end
200
+ result
208
201
  end
209
202
 
203
+ # @param [String] dirname the directory name to use in building the md5 command line
204
+ # @return [String] the command line for finding the md5 hash value
210
205
  def md5_command_line(dirname)
211
206
  line = "cat \`find #{dirname} -type f | sort\` | ruby -e \"require 'digest/md5';puts Digest::MD5.hexdigest(STDIN.read)\""
212
207
  @logger.debug { line }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: royw-drbman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roy Wright
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-14 00:00:00 -07:00
12
+ date: 2009-07-16 00:00:00 -07:00
13
13
  default_executable: drbman
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -100,6 +100,7 @@ files:
100
100
  - examples/primes/lib/primes/sieve_of_eratosthenes.rb
101
101
  - examples/primes/spec/primes_spec.rb
102
102
  - examples/primes/spec/spec_helper.rb
103
+ - examples/primes/stress_test.rb
103
104
  - lib/drbman.rb
104
105
  - lib/drbman/cli.rb
105
106
  - lib/drbman/drb_pool.rb
@@ -146,3 +147,4 @@ test_files:
146
147
  - examples/primes/lib/primes.rb
147
148
  - examples/primes/spec/primes_spec.rb
148
149
  - examples/primes/spec/spec_helper.rb
150
+ - examples/primes/stress_test.rb