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 +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
|