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 +3 -0
- data/Rakefile +6 -0
- data/VERSION +1 -1
- data/drbman.gemspec +5 -3
- data/examples/primes/lib/drb_server/prime_helper.rb +26 -16
- data/examples/primes/lib/primes/cli.rb +6 -0
- data/examples/primes/lib/primes/sieve_of_eratosthenes.rb +38 -19
- data/examples/primes/spec/primes_spec.rb +16 -5
- data/examples/primes/stress_test.rb +18 -0
- data/lib/drbman/cli.rb +6 -0
- data/lib/drbman/drb_pool.rb +6 -0
- data/lib/drbman/drbman.rb +28 -31
- data/lib/drbman/host_machine.rb +67 -72
- metadata +4 -2
data/.gitignore
CHANGED
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/drbman.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{drbman}
|
5
|
-
s.version = "0.0.
|
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-
|
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
|
-
|
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((
|
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 =
|
32
|
-
DRb.start_service(
|
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
|
-
#
|
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
|
-
|
5
|
-
|
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(@
|
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(
|
55
|
+
def primes(maximum, drbman)
|
35
56
|
indices = []
|
36
|
-
if
|
37
|
-
composites = calc_composites(
|
57
|
+
if maximum > 2
|
58
|
+
composites = calc_composites(maximum, drbman)
|
38
59
|
flat_comps = composites.flatten.uniq
|
39
|
-
indices = calc_indices(flat_comps,
|
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(
|
49
|
-
sqr_primes = primes(Math.sqrt(
|
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,
|
57
|
-
# @logger.debug { "thread(#{ip}, #{n})" }
|
77
|
+
threads << Thread.new(ip, maximum) do |prime, max|
|
58
78
|
drbman.get_object do |prime_helper|
|
59
|
-
|
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 <<
|
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,
|
90
|
+
def calc_indices(flat_comps, maximum)
|
72
91
|
indices = []
|
73
|
-
flags = Array.new(
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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}"
|
data/lib/drbman/cli.rb
CHANGED
@@ -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
|
data/lib/drbman/drb_pool.rb
CHANGED
@@ -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
|
data/lib/drbman/drbman.rb
CHANGED
@@ -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.
|
19
|
-
# @option choices [String] :run the name of the file to run on the host machine.
|
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
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
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
|
-
#
|
30
|
-
#
|
31
|
-
#
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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]')
|
47
|
-
raise ArgumentError.new('Missing choices[:hosts]') if
|
48
|
-
raise ArgumentError.new('Missing choices[:dirs]')
|
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 =
|
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
|
-
|
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.
|
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.
|
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
|
data/lib/drbman/host_machine.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
# @
|
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
|
122
|
-
|
123
|
-
|
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
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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.
|
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-
|
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
|