drbqs 0.0.8 → 0.0.9
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/README.md +151 -0
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/bin/drbqs-manage +27 -7
- data/bin/drbqs-node +1 -0
- data/drbqs.gemspec +25 -7
- data/example/transfer/file.rb +46 -0
- data/example/transfer/server_def.rb +19 -0
- data/lib/drbqs.rb +5 -2
- data/lib/drbqs/client.rb +14 -1
- data/lib/drbqs/config.rb +15 -3
- data/lib/drbqs/manage.rb +50 -3
- data/lib/drbqs/message.rb +26 -1
- data/lib/drbqs/node_list.rb +25 -0
- data/lib/drbqs/queue.rb +1 -0
- data/lib/drbqs/server.rb +10 -2
- data/lib/drbqs/ssh/host.rb +26 -0
- data/lib/drbqs/{ssh_shell.rb → ssh/shell.rb} +64 -17
- data/lib/drbqs/ssh/transfer.rb +70 -0
- data/lib/drbqs/task.rb +17 -4
- data/lib/drbqs/utils/filename.rb +118 -0
- data/spec/filename_spec.rb +44 -0
- data/spec/message_spec.rb +10 -0
- data/spec/node_list_spec.rb +40 -0
- data/spec/server_define_spec.rb +23 -12
- data/spec/ssh_shell_spec.rb +1 -1
- data/spec/task_spec.rb +13 -0
- data/spec/transfer_spec.rb +13 -0
- metadata +48 -6
- data/README.rdoc +0 -128
data/lib/drbqs/client.rb
CHANGED
@@ -18,9 +18,21 @@ module DRbQS
|
|
18
18
|
@process_continue = opts[:continue]
|
19
19
|
end
|
20
20
|
|
21
|
+
def transfer_file
|
22
|
+
until FileTransfer.empty?
|
23
|
+
path = FileTransfer.dequeue
|
24
|
+
unless @transfer.scp(path)
|
25
|
+
raise "Can not send file: #{path}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
private :transfer_file
|
30
|
+
|
21
31
|
def execute_task(marshal_obj, method_sym, args)
|
22
32
|
obj = Marshal.load(marshal_obj)
|
23
|
-
obj.__send__(method_sym, *args)
|
33
|
+
result = obj.__send__(method_sym, *args)
|
34
|
+
transfer_file
|
35
|
+
result
|
24
36
|
end
|
25
37
|
private :execute_task
|
26
38
|
|
@@ -29,6 +41,7 @@ module DRbQS
|
|
29
41
|
@connection = ConnectionClient.new(obj[:message], @logger)
|
30
42
|
node_id = @connection.get_id
|
31
43
|
@task_client = TaskClient.new(node_id, obj[:queue], obj[:result], @logger)
|
44
|
+
@transfer = obj[:transfer]
|
32
45
|
if ary = @connection.get_initialization
|
33
46
|
execute_task(*ary)
|
34
47
|
end
|
data/lib/drbqs/config.rb
CHANGED
@@ -3,10 +3,14 @@ require 'singleton'
|
|
3
3
|
|
4
4
|
module DRbQS
|
5
5
|
|
6
|
+
ACL_DEFAULT_PATH = 'acl.txt'
|
7
|
+
ACL_SAMPLE_PATH = 'acl.txt.sample'
|
8
|
+
HOST_FILE_DIRECTORY = 'host'
|
9
|
+
|
6
10
|
class Config
|
7
11
|
|
8
12
|
@@data = {
|
9
|
-
:dir => ENV['HOME'] + '/.drbqs/'
|
13
|
+
:dir => ENV['HOME'] + '/.drbqs/',
|
10
14
|
}
|
11
15
|
|
12
16
|
ACL_SAMPLE =<<SAMPLE
|
@@ -25,22 +29,30 @@ SAMPLE
|
|
25
29
|
@@data[:dir] = dir
|
26
30
|
end
|
27
31
|
|
32
|
+
def get_host_file_directory
|
33
|
+
get_path(HOST_FILE_DIRECTORY)
|
34
|
+
end
|
35
|
+
|
28
36
|
def check_directory_create
|
29
37
|
unless File.exist?(@@data[:dir])
|
30
38
|
FileUtils.mkdir_p(@@data[:dir])
|
31
39
|
FileUtils.chmod(0700, @@data[:dir])
|
32
40
|
end
|
41
|
+
host = get_host_file_directory
|
42
|
+
unless File.exist?(host)
|
43
|
+
FileUtils.mkdir_p(host)
|
44
|
+
end
|
33
45
|
end
|
34
46
|
|
35
47
|
def save_sample
|
36
|
-
path = get_path(
|
48
|
+
path = get_path(ACL_SAMPLE_PATH)
|
37
49
|
unless File.exist?(path)
|
38
50
|
open(path, 'w') { |f| f.print ACL_SAMPLE }
|
39
51
|
end
|
40
52
|
end
|
41
53
|
|
42
54
|
def get_acl_file
|
43
|
-
path = File.join(@@data[:dir],
|
55
|
+
path = File.join(@@data[:dir], ACL_DEFAULT_PATH)
|
44
56
|
if File.exist?(path)
|
45
57
|
return path
|
46
58
|
end
|
data/lib/drbqs/manage.rb
CHANGED
@@ -2,6 +2,40 @@ require 'socket'
|
|
2
2
|
|
3
3
|
module DRbQS
|
4
4
|
|
5
|
+
class SendCommand
|
6
|
+
MAX_WAIT_TIME = 10
|
7
|
+
|
8
|
+
def initialize(message)
|
9
|
+
@message = message
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_hostname
|
13
|
+
"Command of #{Socket.gethostname}"
|
14
|
+
end
|
15
|
+
private :get_hostname
|
16
|
+
|
17
|
+
def send_exit_signal
|
18
|
+
@message.write([:exit_server, get_hostname])
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_status
|
22
|
+
@message.write([:request_status, get_hostname])
|
23
|
+
i = 0
|
24
|
+
loop do
|
25
|
+
begin
|
26
|
+
mes = @message.take([:status, String], 0)
|
27
|
+
return mes[1]
|
28
|
+
rescue Rinda::RequestExpiredError
|
29
|
+
i += 1
|
30
|
+
if i > MAX_WAIT_TIME
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
sleep(1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
5
39
|
class Manage
|
6
40
|
def self.split_arguments(argv, split = '--')
|
7
41
|
if n = argv.index(split)
|
@@ -16,15 +50,28 @@ module DRbQS
|
|
16
50
|
Config.save_sample
|
17
51
|
end
|
18
52
|
|
19
|
-
def
|
53
|
+
def command_client(access_uri)
|
20
54
|
obj = DRbObject.new_with_uri(access_uri)
|
21
|
-
obj[:message]
|
55
|
+
DRbQS::SendCommand.new(obj[:message])
|
56
|
+
end
|
57
|
+
private :command_client
|
58
|
+
|
59
|
+
def send_exit_signal(access_uri)
|
60
|
+
command_client(access_uri).send_exit_signal
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_status(access_uri)
|
64
|
+
command_client(access_uri).get_status
|
22
65
|
end
|
23
66
|
|
24
67
|
def execute_over_ssh(dest, opts, command)
|
25
68
|
ssh = DRbQS::SSHShell.new(dest, opts)
|
26
|
-
ssh.get_environment
|
27
69
|
ssh.start(command)
|
28
70
|
end
|
71
|
+
|
72
|
+
def get_ssh_environment(dest, opts)
|
73
|
+
ssh = DRbQS::SSHShell.new(dest, opts)
|
74
|
+
ssh.get_environment
|
75
|
+
end
|
29
76
|
end
|
30
77
|
end
|
data/lib/drbqs/message.rb
CHANGED
@@ -28,11 +28,13 @@ module DRbQS
|
|
28
28
|
@node_list.set_alive(arg)
|
29
29
|
when :exit_server
|
30
30
|
@logger.info("Get exit message from #{arg.to_s}") if @logger
|
31
|
-
|
31
|
+
when :request_status
|
32
|
+
@logger.info("Get status request from #{arg.to_s}") if @logger
|
32
33
|
else
|
33
34
|
@logger.error("Invalid message from #{arg.to_s}") if @logger
|
34
35
|
return nil
|
35
36
|
end
|
37
|
+
return mes
|
36
38
|
end
|
37
39
|
private :manage_message
|
38
40
|
|
@@ -52,6 +54,29 @@ module DRbQS
|
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
57
|
+
def time_to_string(t)
|
58
|
+
t.strftime("%Y-%m-%d %H:%M:%S")
|
59
|
+
end
|
60
|
+
private :time_to_string
|
61
|
+
|
62
|
+
def send_status(calculating_task_id)
|
63
|
+
s = ''
|
64
|
+
@node_list.history.each do |node_id, hist|
|
65
|
+
s << sprintf("%4d %s\t", node_id, hist[0])
|
66
|
+
if hist.size == 3
|
67
|
+
s << "disconnected: (#{time_to_string(hist[1])} - #{time_to_string(hist[1])})\n"
|
68
|
+
else
|
69
|
+
task_ids = calculating_task_id[node_id]
|
70
|
+
s << "task: #{task_ids.map { |num| num.to_s }.join(', ')} (#{time_to_string(hist[1])})\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
begin
|
74
|
+
@message.take([:status, nil], 0)
|
75
|
+
rescue Rinda::RequestExpiredError
|
76
|
+
end
|
77
|
+
@message.write([:status, s])
|
78
|
+
end
|
79
|
+
|
55
80
|
def node_not_exist?
|
56
81
|
@node_list.empty?
|
57
82
|
end
|
data/lib/drbqs/node_list.rb
CHANGED
@@ -1,14 +1,38 @@
|
|
1
1
|
module DRbQS
|
2
|
+
class NodeHistory
|
3
|
+
def initialize
|
4
|
+
@data = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(id, id_str)
|
8
|
+
@data[id] = [id_str, Time.now]
|
9
|
+
end
|
10
|
+
|
11
|
+
def disconnect(id)
|
12
|
+
if @data[id]
|
13
|
+
@data[id] << Time.now
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
@data.each(&block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
2
22
|
class NodeList
|
23
|
+
attr_reader :history
|
24
|
+
|
3
25
|
def initialize
|
4
26
|
@id = 0
|
5
27
|
@list = {}
|
6
28
|
@check = []
|
29
|
+
@history = NodeHistory.new
|
7
30
|
end
|
8
31
|
|
9
32
|
def get_new_id(id_str)
|
10
33
|
@id += 1
|
11
34
|
@list[@id] = id_str
|
35
|
+
@history.add(@id, id_str)
|
12
36
|
@id
|
13
37
|
end
|
14
38
|
|
@@ -23,6 +47,7 @@ module DRbQS
|
|
23
47
|
def delete_not_alive
|
24
48
|
@check.each do |id|
|
25
49
|
@list.delete(id)
|
50
|
+
@history.disconnect(id)
|
26
51
|
end
|
27
52
|
deleted = @check
|
28
53
|
@check = []
|
data/lib/drbqs/queue.rb
CHANGED
data/lib/drbqs/server.rb
CHANGED
@@ -49,7 +49,8 @@ module DRbQS
|
|
49
49
|
@ts = {
|
50
50
|
:message => Rinda::TupleSpace.new,
|
51
51
|
:queue => Rinda::TupleSpace.new,
|
52
|
-
:result => Rinda::TupleSpace.new
|
52
|
+
:result => Rinda::TupleSpace.new,
|
53
|
+
:transfer => nil
|
53
54
|
}
|
54
55
|
if opts[:log_file]
|
55
56
|
@logger = Logger.new(opts[:log_file])
|
@@ -162,9 +163,16 @@ module DRbQS
|
|
162
163
|
end
|
163
164
|
end
|
164
165
|
|
166
|
+
def set_file_transfer(user, host, directory)
|
167
|
+
@ts[:transfer] = DRbQS::Transfer.new(user, host, directory)
|
168
|
+
end
|
169
|
+
|
165
170
|
def check_message
|
166
|
-
|
171
|
+
case @message.get_message
|
172
|
+
when :exit_server
|
167
173
|
self.exit
|
174
|
+
when :request_status
|
175
|
+
@message.send_status(@queue.calculating)
|
168
176
|
end
|
169
177
|
end
|
170
178
|
private :check_message
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'drbqs/config'
|
3
|
+
|
4
|
+
module DRbQS
|
5
|
+
class SSHHost
|
6
|
+
def initialize
|
7
|
+
@dir = DRbQS::Config.get_host_file_directory
|
8
|
+
@host_files = (Dir.glob("#{@dir}/*.yaml") + Dir.glob("#{@dir}/*.yml")).map { |s| File.basename(s) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(name)
|
12
|
+
if (name.size > 0) && (host = @host_files.find { |s| /^#{name}/ =~ s })
|
13
|
+
return File.join(@dir, host)
|
14
|
+
end
|
15
|
+
return nil
|
16
|
+
end
|
17
|
+
private :get
|
18
|
+
|
19
|
+
def get_options(name)
|
20
|
+
if path = get(name)
|
21
|
+
return [path, YAML.load_file(path)]
|
22
|
+
end
|
23
|
+
return [nil, {}]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -31,8 +31,11 @@ module DRbQS
|
|
31
31
|
if (@rvm || @rvm_init) && !(String === @rvm_init)
|
32
32
|
@rvm_init = DEFAULT_RVM_SCRIPT
|
33
33
|
end
|
34
|
-
@
|
34
|
+
@nohup_output = opts[:output] || DEFAULT_OUTPUT_FILE
|
35
35
|
@directory = opts[:dir]
|
36
|
+
@out = $stdout
|
37
|
+
@nice = opts[:nice]
|
38
|
+
@nohup = opts[:nohup]
|
36
39
|
end
|
37
40
|
|
38
41
|
def split_destination(dest)
|
@@ -55,38 +58,82 @@ module DRbQS
|
|
55
58
|
end
|
56
59
|
private :split_destination
|
57
60
|
|
61
|
+
def output_command(cmd, result)
|
62
|
+
@out.puts "#{@user}@#{@host}$ #{cmd}" if @out
|
63
|
+
@out.print result
|
64
|
+
end
|
65
|
+
private :output_command
|
66
|
+
|
67
|
+
def shell_exec_get_output(sh, cmd)
|
68
|
+
result = ''
|
69
|
+
pr_cmd = sh.execute!(cmd) do |sh_proc|
|
70
|
+
sh_proc.on_output do |pr, data|
|
71
|
+
result << data
|
72
|
+
end
|
73
|
+
sh_proc.on_error_output do |pr, data|
|
74
|
+
result << data
|
75
|
+
end
|
76
|
+
end
|
77
|
+
[pr_cmd, result]
|
78
|
+
end
|
79
|
+
private :shell_exec_get_output
|
80
|
+
|
58
81
|
def shell_exec(sh, cmd)
|
59
|
-
|
60
|
-
|
82
|
+
ary = shell_exec_get_output(sh, cmd)
|
83
|
+
output_command(cmd, ary[1])
|
84
|
+
ary
|
85
|
+
end
|
86
|
+
private :shell_exec
|
87
|
+
|
88
|
+
def shell_exec_check(sh, cmd)
|
89
|
+
ary = shell_exec(sh, cmd)
|
90
|
+
if ary[0].exit_status != 0
|
61
91
|
raise GetInvalidExitStatus, "Can not execute '#{cmd}' on #{@host} properly."
|
62
92
|
end
|
93
|
+
ary
|
63
94
|
end
|
64
|
-
private :
|
95
|
+
private :shell_exec_check
|
65
96
|
|
66
|
-
def execute_command(
|
97
|
+
def execute_command(&block)
|
67
98
|
Net::SSH.start(@host, @user, :port => @port) do |ssh|
|
68
99
|
ssh.shell(@shell) do |sh|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
sh.execute "exit"
|
100
|
+
shell_exec_check(sh, "cd #{@directory}") if @directory
|
101
|
+
shell_exec_check(sh, "source #{@rvm_init}") if @rvm_init
|
102
|
+
shell_exec_check(sh, "rvm use #{@rvm}") if @rvm
|
103
|
+
yield(sh)
|
104
|
+
shell_exec(sh, "exit")
|
76
105
|
end
|
77
106
|
end
|
78
107
|
end
|
79
108
|
private :execute_command
|
80
109
|
|
81
110
|
def get_environment
|
82
|
-
execute_command
|
83
|
-
|
84
|
-
|
85
|
-
|
111
|
+
execute_command do |sh|
|
112
|
+
['echo "directory: " `pwd`',
|
113
|
+
'echo "files:"',
|
114
|
+
'ls',
|
115
|
+
'if which rvm > /dev/null; then rvm info; else ruby -v; fi'].each do |cmd|
|
116
|
+
shell_exec(sh, cmd)
|
117
|
+
end
|
118
|
+
end
|
86
119
|
end
|
87
120
|
|
88
121
|
def start(*args)
|
89
|
-
|
122
|
+
cmd = args.join(' ')
|
123
|
+
if @nice
|
124
|
+
if Integer === @nice
|
125
|
+
cmd = "nice -n #{@nice.to_s} " + cmd
|
126
|
+
else
|
127
|
+
cmd = "nice " + cmd
|
128
|
+
end
|
129
|
+
end
|
130
|
+
execute_command do |sh|
|
131
|
+
if @nohup
|
132
|
+
pr, path = shell_exec_check(sh, "drbqs-manage new-filename #{@nohup_output}")
|
133
|
+
cmd = "nohup #{cmd} > #{path.strip} 2>&1 &"
|
134
|
+
end
|
135
|
+
shell_exec(sh, cmd)
|
136
|
+
end
|
90
137
|
end
|
91
138
|
end
|
92
139
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module DRbQS
|
4
|
+
|
5
|
+
# Transfer files to directory on DRbQS server.
|
6
|
+
# In this class we use scp command.
|
7
|
+
# Note that after we transfer files we delete the files.
|
8
|
+
class Transfer
|
9
|
+
|
10
|
+
# options
|
11
|
+
# :mkdir true or nil
|
12
|
+
def initialize(user, host, directory)
|
13
|
+
@user = user
|
14
|
+
@host = host
|
15
|
+
@directory = File.expand_path(directory)
|
16
|
+
FileUtils.mkdir_p(@directory)
|
17
|
+
end
|
18
|
+
|
19
|
+
def scp(path)
|
20
|
+
name = File.basename(path)
|
21
|
+
unless File.exist?(path)
|
22
|
+
raise ArgumentError, "File #{path} does not exist."
|
23
|
+
end
|
24
|
+
if system("scp -r #{path} #{@user}@#{@host}:#{File.join(@directory, name)} > /dev/null 2>&1")
|
25
|
+
FileUtils.rm_r(path)
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# To compress files, we use gzip and tar command.
|
33
|
+
# Note that if we compress files then we delete the source files.
|
34
|
+
module FileTransfer
|
35
|
+
@@files = Queue.new
|
36
|
+
|
37
|
+
def self.enqueue(path, compress = false)
|
38
|
+
if compress
|
39
|
+
if File.directory?(path)
|
40
|
+
gz_path = "#{path.sub(/\/$/, '')}.tar.gz"
|
41
|
+
cmd = "tar czf #{gz_path} -C #{File.dirname(path)} #{File.basename(path)} > /dev/null 2>&1"
|
42
|
+
else
|
43
|
+
gz_path = path + '.gz'
|
44
|
+
cmd = "gzip --best #{path} > /dev/null 2>&1"
|
45
|
+
end
|
46
|
+
if File.exist?(gz_path)
|
47
|
+
raise "File has already existed: #{gz_path}"
|
48
|
+
elsif !system(cmd)
|
49
|
+
raise "Can not compress: #{path}"
|
50
|
+
end
|
51
|
+
FileUtils.rm_r(path) if File.exist?(path)
|
52
|
+
@@files.enq(gz_path)
|
53
|
+
else
|
54
|
+
@@files.enq(path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.compress_enqueue(path)
|
59
|
+
self.enqueue(path, true)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.dequeue
|
63
|
+
@@files.deq
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.empty?
|
67
|
+
@@files.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|