drbqs 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/README.md +137 -128
- data/VERSION +1 -1
- data/docs/FormatExecute.md +119 -0
- data/docs/GettingStarted.md +242 -0
- data/drbqs.gemspec +36 -13
- data/example/command/server_def.rb +4 -5
- data/example/execute/execute.rb +41 -0
- data/example/execute/server.rb +14 -0
- data/example/execute/task.rb +0 -0
- data/example/mandelbrot/README.md +15 -0
- data/example/mandelbrot/execute.rb +10 -0
- data/example/mandelbrot/mandelbrot.rb +56 -0
- data/example/mandelbrot/server.rb +49 -0
- data/example/server/server.rb +3 -6
- data/example/simple/README.md +18 -0
- data/example/simple/execute.rb +11 -0
- data/example/simple/server.rb +8 -0
- data/example/simple/task.rb +11 -0
- data/example/sum/server_def.rb +1 -1
- data/example/sum2/execute_def.rb +21 -8
- data/example/sum2/server_def.rb +8 -7
- data/example/transfer/file.rb +42 -8
- data/example/transfer/server_def.rb +43 -9
- data/lib/drbqs.rb +1 -1
- data/lib/drbqs/command_line/command_execute.rb +3 -3
- data/lib/drbqs/command_line/command_line.rb +1 -1
- data/lib/drbqs/execute/execute_node.rb +50 -0
- data/lib/drbqs/execute/process_define.rb +102 -54
- data/lib/drbqs/execute/register.rb +241 -87
- data/lib/drbqs/execute/server_define.rb +69 -58
- data/lib/drbqs/ext/task.rb +2 -0
- data/lib/drbqs/ext/task/command_task.rb +43 -0
- data/lib/drbqs/manage/manage.rb +5 -4
- data/lib/drbqs/manage/ssh_shell.rb +2 -8
- data/lib/drbqs/node/connection.rb +1 -1
- data/lib/drbqs/node/node.rb +8 -14
- data/lib/drbqs/node/task_client.rb +1 -1
- data/lib/drbqs/server/history.rb +5 -1
- data/lib/drbqs/server/message.rb +7 -34
- data/lib/drbqs/server/queue.rb +14 -2
- data/lib/drbqs/server/server.rb +86 -43
- data/lib/drbqs/server/server_hook.rb +3 -0
- data/lib/drbqs/server/test/node.rb +1 -1
- data/lib/drbqs/server/test/prof.rb +50 -0
- data/lib/drbqs/server/test/server.rb +2 -2
- data/lib/drbqs/server/transfer_setting.rb +23 -11
- data/lib/drbqs/setting/base.rb +15 -0
- data/lib/drbqs/setting/data_container.rb +1 -1
- data/lib/drbqs/setting/execute.rb +3 -3
- data/lib/drbqs/setting/node.rb +1 -1
- data/lib/drbqs/setting/server.rb +2 -2
- data/lib/drbqs/task/registrar.rb +39 -0
- data/lib/drbqs/task/task.rb +139 -59
- data/lib/drbqs/task/task_generator.rb +93 -116
- data/lib/drbqs/utility/misc.rb +15 -10
- data/lib/drbqs/utility/temporary.rb +7 -2
- data/lib/drbqs/utility/transfer/transfer.rb +81 -0
- data/lib/drbqs/utility/transfer/transfer_client.rb +68 -69
- data/lib/drbqs/utility/transfer/transfer_client_connect.rb +83 -0
- data/lib/drbqs/utility/transfer/transfer_file_list.rb +40 -0
- data/spec/execute/def/execute1.rb +4 -4
- data/spec/execute/def/execute2.rb +24 -0
- data/spec/execute/process_define_spec.rb +43 -6
- data/spec/execute/register_spec.rb +403 -9
- data/spec/execute/server_define_spec.rb +1 -1
- data/spec/ext/task/command_task_spec.rb +16 -0
- data/spec/integration_test/01_basic_usage_spec.rb +1 -1
- data/spec/integration_test/02_use_generator_spec.rb +2 -2
- data/spec/integration_test/04_use_unix_domain_spec.rb +1 -1
- data/spec/integration_test/05_server_exit_signal_spec.rb +1 -1
- data/spec/integration_test/06_node_exit_after_task_spec.rb +4 -4
- data/spec/integration_test/08_shutdown_unused_nodes_spec.rb +2 -2
- data/spec/integration_test/09_server_process_data_spec.rb +1 -1
- data/spec/integration_test/definition/server01.rb +4 -5
- data/spec/integration_test/definition/server02.rb +2 -4
- data/spec/node/node_spec.rb +34 -0
- data/spec/server/message_spec.rb +1 -1
- data/spec/server/queue_spec.rb +34 -7
- data/spec/server/server_spec.rb +21 -9
- data/spec/server/transfer_setting_spec.rb +59 -24
- data/spec/setting/base_spec.rb +11 -0
- data/spec/setting/data_container_spec.rb +8 -0
- data/spec/spec_helper.rb +1 -7
- data/spec/task/registrar_spec.rb +34 -0
- data/spec/task/task_generator_spec.rb +15 -15
- data/spec/task/task_spec.rb +132 -23
- data/spec/utility/misc_spec.rb +2 -2
- data/spec/utility/transfer/transfer_client_connect_spec.rb +90 -0
- data/spec/utility/transfer/transfer_file_list_spec.rb +27 -0
- data/spec/{task/file_transfer_spec.rb → utility/transfer/transfer_spec.rb} +24 -24
- metadata +66 -45
- data/lib/drbqs/manage/execute_node.rb +0 -50
- data/lib/drbqs/server/prof.rb +0 -48
- data/lib/drbqs/task/command_task.rb +0 -43
- data/lib/drbqs/utility/transfer/file_transfer.rb +0 -73
@@ -1,142 +1,119 @@
|
|
1
1
|
module DRbQS
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
class Task
|
3
|
+
class Generator
|
4
|
+
# @param [Hash] data Names of instance variables and their values,
|
5
|
+
# which can be accessed in {DRbQS::Task::Generator#set}.
|
6
|
+
def initialize(data = {})
|
7
|
+
@registrar = DRbQS::Task::Registrar.new(data)
|
8
|
+
@fiber = nil
|
9
|
+
@iterate = nil
|
10
|
+
@task_set = nil
|
11
|
+
@fiber_init = nil
|
12
|
+
@wait = false
|
9
13
|
end
|
10
|
-
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
when DRbQS::Task
|
15
|
-
Fiber.yield(arg)
|
16
|
-
when Array
|
17
|
-
arg.each { |t| Fiber.yield(t) }
|
18
|
-
else
|
19
|
-
raise "Invalid type of an argument."
|
15
|
+
def have_next?
|
16
|
+
!!@fiber
|
20
17
|
end
|
21
|
-
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def wait_all_tasks
|
28
|
-
Fiber.yield(:wait)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class TaskGenerator
|
33
|
-
def initialize(data = {})
|
34
|
-
@source = DRbQS::TaskSource.new(data)
|
35
|
-
@fiber = nil
|
36
|
-
@iterate = nil
|
37
|
-
@task_set = nil
|
38
|
-
@fiber_init = nil
|
39
|
-
@wait = false
|
40
|
-
end
|
41
|
-
|
42
|
-
def have_next?
|
43
|
-
!!@fiber
|
44
|
-
end
|
45
|
-
|
46
|
-
def waiting?
|
47
|
-
@wait
|
48
|
-
end
|
49
|
-
|
50
|
-
# The options :generate and :collect are available.
|
51
|
-
# opts[:generate] is the number of tasks per one generation.
|
52
|
-
# The generator creates a task set from opts[:collect] tasks.
|
53
|
-
def set(opts = {}, &block)
|
54
|
-
@iterate = opts[:generate] || 1
|
55
|
-
@task_set = opts[:collect]
|
56
|
-
if @iterate < 1 || (@task_set && @task_set < 1)
|
57
|
-
raise ArgumentError, "Invalid options of task creation on generator."
|
19
|
+
def waiting?
|
20
|
+
@wait
|
58
21
|
end
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
22
|
+
|
23
|
+
# @param [Hash] opts The options of task generation
|
24
|
+
# @option opts [Fixnum] :generate Set the number of tasks per one generation
|
25
|
+
# @option opts [Fixnum] :collect The generator creates a task set consisting of opts[:collect] tasks.
|
26
|
+
def set(opts = {}, &block)
|
27
|
+
unless block_given?
|
28
|
+
raise ArgumentError, "Creation of a task generator needs block."
|
29
|
+
end
|
30
|
+
@iterate = opts[:generate] || 1
|
31
|
+
@task_set = opts[:collect]
|
32
|
+
if @iterate < 1 || (@task_set && @task_set < 1)
|
33
|
+
raise ArgumentError, "Invalid options of task creation on generator."
|
34
|
+
end
|
35
|
+
@fiber_init = lambda do
|
36
|
+
@fiber = Fiber.new do
|
37
|
+
begin
|
38
|
+
@registrar.instance_eval(&block)
|
39
|
+
rescue => err
|
40
|
+
new_err = self.class.new("Error on generating tasks: #{err.to_s} (#{err.class})")
|
41
|
+
new_err.set_backtrace(err.backtrace)
|
42
|
+
raise new_err
|
43
|
+
end
|
44
|
+
nil
|
67
45
|
end
|
68
|
-
nil
|
69
46
|
end
|
70
47
|
end
|
71
|
-
end
|
72
48
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
49
|
+
# Initialize fider to create tasks.
|
50
|
+
# This method must be called in thread to create tasks.
|
51
|
+
def init
|
52
|
+
@fiber_init.call if @fiber_init
|
53
|
+
end
|
78
54
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
55
|
+
# Return an array of new tasks.
|
56
|
+
def new_tasks
|
57
|
+
if @fiber
|
58
|
+
@wait = false
|
59
|
+
task_ary = []
|
60
|
+
iteration = @iterate
|
61
|
+
iteration *= @task_set if @task_set
|
62
|
+
iteration.times do |i|
|
63
|
+
if task_new = @fiber.resume
|
64
|
+
case task_new
|
65
|
+
when DRbQS::Task
|
66
|
+
task_ary << task_new
|
67
|
+
when Array
|
68
|
+
task_ary.concat(task_new)
|
69
|
+
when :wait
|
70
|
+
@wait = true
|
71
|
+
break
|
72
|
+
else
|
73
|
+
raise RuntimeError, "Invalid object created by fiber to create tasks."
|
74
|
+
end
|
96
75
|
else
|
97
|
-
|
76
|
+
@fiber = nil
|
77
|
+
break
|
98
78
|
end
|
99
|
-
else
|
100
|
-
@fiber = nil
|
101
|
-
break
|
102
79
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
80
|
+
if task_ary.size > 0
|
81
|
+
if @task_set
|
82
|
+
task_ary = task_ary.each_slice(@task_set).map do |ary|
|
83
|
+
DRbQS::Task::TaskSet.new(ary)
|
84
|
+
end
|
108
85
|
end
|
86
|
+
return task_ary
|
109
87
|
end
|
110
|
-
return task_ary
|
111
88
|
end
|
89
|
+
nil
|
112
90
|
end
|
113
|
-
nil
|
114
|
-
end
|
115
91
|
|
116
|
-
|
92
|
+
DEBUG_TASK_PROGRESS = 1000
|
117
93
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
94
|
+
# Create all tasks for test and return [group_number, task_number] if all tasks created properly.
|
95
|
+
def debug_all_tasks(opts = {})
|
96
|
+
limit = opts[:limit]
|
97
|
+
progress = opts[:progress]
|
98
|
+
group_number = 0
|
99
|
+
task_number = 0
|
100
|
+
while ary = new_tasks
|
101
|
+
ary.each do |t|
|
102
|
+
unless DRbQS::Task === t
|
103
|
+
raise RuntimeError, "Invalid #{i}th task: #{t.inspect}"
|
104
|
+
end
|
105
|
+
task_number += 1
|
106
|
+
if progress && (task_number % DEBUG_TASK_PROGRESS == 0)
|
107
|
+
puts "#{task_number} tasks have been created."
|
108
|
+
end
|
109
|
+
if limit && task_number > limit
|
110
|
+
break
|
111
|
+
end
|
135
112
|
end
|
113
|
+
group_number += 1
|
136
114
|
end
|
137
|
-
group_number
|
115
|
+
[group_number, task_number]
|
138
116
|
end
|
139
|
-
[group_number, task_number]
|
140
117
|
end
|
141
118
|
end
|
142
119
|
end
|
data/lib/drbqs/utility/misc.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
require 'sys/proctable'
|
2
2
|
|
3
3
|
module DRbQS
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module Misc
|
5
|
+
class LoggerDummy
|
6
|
+
def info(*args)
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
def warn(*args)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
def error(*args)
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
+
def debug(*args)
|
16
|
+
end
|
15
17
|
end
|
16
|
-
end
|
17
18
|
|
18
|
-
module Misc
|
19
19
|
# :port
|
20
20
|
# :host
|
21
21
|
# :unix
|
@@ -56,6 +56,11 @@ module DRbQS
|
|
56
56
|
end
|
57
57
|
module_function :time_to_history_string
|
58
58
|
|
59
|
+
def time_to_history_string2(t)
|
60
|
+
t.strftime("%m-%d %H:%M:%S")
|
61
|
+
end
|
62
|
+
module_function :time_to_history_string2
|
63
|
+
|
59
64
|
STRINGS_FOR_KEY = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
60
65
|
|
61
66
|
def random_key(size = 20)
|
@@ -22,8 +22,13 @@ module DRbQS
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# Return new path of temporary file.
|
25
|
-
|
26
|
-
|
25
|
+
# @param [String] basename Set the basename of created filename
|
26
|
+
def self.file(basename = nil)
|
27
|
+
if basename
|
28
|
+
File.join(self.directory, basename)
|
29
|
+
else
|
30
|
+
filename.create(:add => :always)
|
31
|
+
end
|
27
32
|
end
|
28
33
|
|
29
34
|
# Make root of temporary directory empty.
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module DRbQS
|
2
|
+
# To compress files, we use gzip and tar command.
|
3
|
+
# Note that if we compress files then we delete the original files.
|
4
|
+
class Transfer
|
5
|
+
@files = Queue.new
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Add path to queue of which files is going to be transfered to server.
|
9
|
+
# @param [String] path The file path that we want to send to a server.
|
10
|
+
# @param [Hash] opts The options for transfering a file.
|
11
|
+
# @option opts [true,false] :compress Compress the file by gzip before transfering.
|
12
|
+
# @option opts [String] :rename Change basename to the specified name.
|
13
|
+
def enqueue(path, opts = {})
|
14
|
+
if opts[:rename]
|
15
|
+
new_path = FileName.create(File.join(File.dirname(path), opts[:rename]), :directory => :parent)
|
16
|
+
FileUtils.mv(path, new_path)
|
17
|
+
path = new_path
|
18
|
+
end
|
19
|
+
if opts[:compress]
|
20
|
+
if File.directory?(path)
|
21
|
+
gz_path = "#{path.sub(/\/$/, '')}.tar.gz"
|
22
|
+
cmd = "tar czf #{gz_path} -C #{File.dirname(path)} #{File.basename(path)} > /dev/null 2>&1"
|
23
|
+
else
|
24
|
+
gz_path = path + '.gz'
|
25
|
+
cmd = "gzip --best #{path} > /dev/null 2>&1"
|
26
|
+
end
|
27
|
+
if File.exist?(gz_path)
|
28
|
+
raise "File has already existed: #{gz_path}"
|
29
|
+
elsif !system(cmd)
|
30
|
+
raise "Can not compress: #{path}"
|
31
|
+
end
|
32
|
+
FileUtils.rm_r(path) if File.exist?(path)
|
33
|
+
path_to_send = gz_path
|
34
|
+
else
|
35
|
+
path_to_send = path
|
36
|
+
end
|
37
|
+
@files.enq(path_to_send)
|
38
|
+
File.basename(path_to_send)
|
39
|
+
end
|
40
|
+
|
41
|
+
def compress_enqueue(path)
|
42
|
+
enqueue(path, :compress => true)
|
43
|
+
end
|
44
|
+
|
45
|
+
def dequeue
|
46
|
+
@files.deq
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty?
|
50
|
+
@files.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def dequeue_all
|
54
|
+
files = []
|
55
|
+
until empty?
|
56
|
+
files << dequeue
|
57
|
+
end
|
58
|
+
files.empty? ? nil : files
|
59
|
+
end
|
60
|
+
|
61
|
+
# Decompress a file in the file directory of a server.
|
62
|
+
# @param [DRbQS::Server] server Current server
|
63
|
+
# @param [String] filename File path to decompress
|
64
|
+
def decompress(server, filename)
|
65
|
+
dir = server.transfer_directory
|
66
|
+
path = File.join(dir, filename)
|
67
|
+
if File.exist?(path)
|
68
|
+
case path
|
69
|
+
when /\.tar\.gz$/
|
70
|
+
cmd = "tar xvzf #{path} -C #{dir} > /dev/null 2>&1"
|
71
|
+
when /\.gz$/
|
72
|
+
cmd = "gunzip #{path} > /dev/null 2>&1"
|
73
|
+
else
|
74
|
+
cmd = nil
|
75
|
+
end
|
76
|
+
system(cmd) if cmd
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,90 +1,89 @@
|
|
1
|
-
require '
|
1
|
+
require 'drbqs/utility/transfer/transfer_client_connect.rb'
|
2
2
|
|
3
3
|
module DRbQS
|
4
|
+
class Transfer
|
5
|
+
class Client
|
6
|
+
attr_reader :directory, :local, :sftp
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@directory =
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# Transfer files to directory on DRbQS server over sftp.
|
19
|
-
# Note that after we transfer files we delete the original files.
|
20
|
-
class SFTP < DRbQS::TransferClient::ClientBase
|
21
|
-
attr_reader :user, :host, :directory
|
22
|
-
|
23
|
-
def initialize(user, host, directory)
|
24
|
-
super(directory)
|
25
|
-
@user = user
|
26
|
-
@host = host
|
8
|
+
def initialize(server_directory, same_host, host, user)
|
9
|
+
unless Pathname.new(server_directory).absolute?
|
10
|
+
raise ArgumentError, "Directory of server must be absolute."
|
11
|
+
end
|
12
|
+
@directory = server_directory
|
13
|
+
@same_host = same_host
|
14
|
+
@local = DRbQS::Transfer::Client::Local.new(@directory)
|
15
|
+
if host && user
|
16
|
+
@sftp = DRbQS::Transfer::Client::SFTP.new(user, host, @directory)
|
17
|
+
else
|
18
|
+
@sftp = nil
|
19
|
+
end
|
27
20
|
end
|
28
21
|
|
29
|
-
# Transfer and delete +files+.
|
30
22
|
def transfer(files)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
transfered = false
|
24
|
+
if @same_host
|
25
|
+
begin
|
26
|
+
@local.transfer(files)
|
27
|
+
transfered = true
|
28
|
+
rescue
|
29
|
+
end
|
30
|
+
end
|
31
|
+
if !transfered
|
32
|
+
unless @sftp
|
33
|
+
raise "Can not transfer files."
|
35
34
|
end
|
35
|
+
@sftp.transfer(files)
|
36
36
|
end
|
37
|
-
rescue => err
|
38
|
-
raise err.class, "user=#{@user}, host=#{@host}, directory=#{@directory}; #{err.to_s}", err.backtrace
|
39
37
|
end
|
40
|
-
end
|
41
38
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
def download(files, readonly = nil)
|
40
|
+
download_files = nil
|
41
|
+
if @same_host
|
42
|
+
begin
|
43
|
+
if readonly
|
44
|
+
download_files = files
|
45
|
+
else
|
46
|
+
download_files = @local.download(files)
|
47
|
+
end
|
48
|
+
rescue
|
49
|
+
end
|
46
50
|
end
|
51
|
+
if !download_files
|
52
|
+
unless @sftp
|
53
|
+
raise "SFTP is not prepared."
|
54
|
+
end
|
55
|
+
download_files = @sftp.download(files)
|
56
|
+
end
|
57
|
+
download_files
|
47
58
|
end
|
48
|
-
end
|
49
|
-
|
50
|
-
attr_reader :directory, :local, :sftp
|
51
|
-
|
52
|
-
def initialize(dir)
|
53
|
-
@directory = File.expand_path(dir)
|
54
|
-
@local = DRbQS::TransferClient::Local.new(@directory)
|
55
|
-
@sftp = nil
|
56
|
-
end
|
57
|
-
|
58
|
-
def make_directory
|
59
|
-
FileUtils.mkdir_p(@directory)
|
60
|
-
end
|
61
59
|
|
62
|
-
|
63
|
-
|
64
|
-
end
|
60
|
+
class << self
|
61
|
+
@transfer = nil
|
65
62
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
info
|
70
|
-
end
|
63
|
+
def get
|
64
|
+
@transfer
|
65
|
+
end
|
71
66
|
|
72
|
-
|
73
|
-
|
74
|
-
if on_same_host
|
75
|
-
begin
|
76
|
-
@local.transfer(files)
|
77
|
-
transfered = true
|
78
|
-
rescue
|
67
|
+
def set(transfer)
|
68
|
+
@transfer = transfer
|
79
69
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
70
|
+
|
71
|
+
def transfer_to_server
|
72
|
+
if files = DRbQS::Transfer.dequeue_all
|
73
|
+
if @transfer
|
74
|
+
begin
|
75
|
+
@transfer.transfer(files)
|
76
|
+
rescue Exception => err
|
77
|
+
err_new = err.class.new("#{err.to_s} (#{err.class}); Can not send file: #{files.join(", ")}")
|
78
|
+
err_new.set_backtrace(err.backtrace)
|
79
|
+
raise err_new
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise "Server does not set transfer settings. Can not send file: #{files.join(", ")}"
|
83
|
+
end
|
84
|
+
end
|
84
85
|
end
|
85
|
-
@sftp.transfer(files)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
89
|
-
|
90
89
|
end
|