royw-drbman 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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