rest-ftp-daemon 0.90.1 → 0.94.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile.lock +8 -4
- data/README.md +16 -0
- data/bin/rest-ftp-daemon +1 -1
- data/lib/rest-ftp-daemon.rb +13 -3
- data/lib/rest-ftp-daemon/api/dashboard.rb +60 -0
- data/lib/rest-ftp-daemon/api/debug.rb +25 -0
- data/lib/rest-ftp-daemon/api/job_presenter.rb +33 -0
- data/lib/rest-ftp-daemon/api/jobs.rb +37 -116
- data/lib/rest-ftp-daemon/api/root.rb +39 -107
- data/lib/rest-ftp-daemon/api/routes.rb +16 -0
- data/lib/rest-ftp-daemon/api/status.rb +29 -0
- data/lib/rest-ftp-daemon/constants.rb +5 -3
- data/lib/rest-ftp-daemon/helpers.rb +38 -12
- data/lib/rest-ftp-daemon/job.rb +186 -131
- data/lib/rest-ftp-daemon/job_queue.rb +15 -16
- data/lib/rest-ftp-daemon/notification.rb +1 -2
- data/lib/rest-ftp-daemon/static/css/main.css +45 -4
- data/lib/rest-ftp-daemon/views/dashboard.haml +16 -55
- data/lib/rest-ftp-daemon/views/dashboard_counters.haml +14 -0
- data/lib/rest-ftp-daemon/views/dashboard_headers.haml +29 -0
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +37 -21
- data/lib/rest-ftp-daemon/views/dashboard_tokens.haml +6 -1
- data/lib/rest-ftp-daemon/views/dashboard_workers.haml +9 -25
- data/lib/rest-ftp-daemon/worker_pool.rb +1 -1
- data/rest-ftp-daemon.gemspec +1 -0
- metadata +24 -6
- data/lib/rest-ftp-daemon/api/defaults.rb +0 -66
- data/lib/rest-ftp-daemon/api/workers.rb +0 -49
- data/lib/rest-ftp-daemon/static/css/bootstrap.min.old1.css +0 -5
@@ -1,140 +1,72 @@
|
|
1
|
-
require 'haml'
|
2
|
-
require "facter"
|
3
|
-
require "sys/cpu"
|
4
|
-
|
5
|
-
|
6
1
|
module RestFtpDaemon
|
7
2
|
module API
|
8
|
-
|
9
3
|
class Root < Grape::API
|
10
4
|
|
11
5
|
|
12
6
|
####### CLASS CONFIG
|
13
7
|
|
14
|
-
include RestFtpDaemon::API::Defaults
|
15
8
|
logger RestFtpDaemon::Logger.new(:api, "API")
|
16
|
-
mount RestFtpDaemon::API::Jobs => '/jobs'
|
17
|
-
#add_swagger_documentation
|
18
|
-
# mount RestFtpDaemon::API::Workers => '/workers'
|
19
9
|
|
10
|
+
do_not_route_head!
|
11
|
+
do_not_route_options!
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
Root.logger.info(message, level)
|
26
|
-
end
|
13
|
+
# FIXME
|
14
|
+
# add_swagger_documentation
|
15
|
+
# default_error_formatter :json
|
16
|
+
format :json
|
27
17
|
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
####### INITIALIZATION
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
# Check that Queue and Pool are available
|
35
|
-
raise RestFtpDaemon::MissingQueue unless defined? $queue
|
36
|
-
raise RestFtpDaemon::MissingQueue unless defined? $pool
|
37
|
-
super
|
38
|
-
end
|
39
18
|
|
19
|
+
####### EXCETPIONS HANDLING
|
20
|
+
# FIXME
|
21
|
+
# rescue_from :all do |e|
|
22
|
+
# error_response(message: "Internal server error", status: 500)
|
23
|
+
# end
|
40
24
|
|
41
|
-
####### API DEFINITION
|
42
25
|
|
43
|
-
|
44
|
-
get '/' do
|
45
|
-
info "GET /"
|
26
|
+
####### HELPERS
|
46
27
|
|
47
|
-
|
48
|
-
Facter.loadfacts
|
49
|
-
@info_load = Sys::CPU.load_avg.first.to_f
|
50
|
-
@info_procs = (Facter.value :processorcount).to_i
|
51
|
-
@info_ipaddr = Facter.value(:ipaddress)
|
52
|
-
@info_memfree = Facter.value(:memoryfree)
|
28
|
+
helpers do
|
53
29
|
|
54
|
-
|
55
|
-
|
56
|
-
@info_norm = "N/A"
|
57
|
-
else
|
58
|
-
@info_norm = (100 * @info_load / @info_procs).round(1)
|
30
|
+
def info message, level = 0
|
31
|
+
Root.logger.info(message, level)
|
59
32
|
end
|
60
33
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
if params["only"].nil? || params["only"].blank?
|
69
|
-
@only = nil
|
70
|
-
else
|
71
|
-
@only = params["only"].to_sym
|
34
|
+
def api_error exception
|
35
|
+
{
|
36
|
+
:error => exception.message,
|
37
|
+
:message => exception.backtrace.first,
|
38
|
+
#:backtrace => exception.backtrace,
|
39
|
+
}
|
72
40
|
end
|
73
41
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# @jobs_popped = $queue.queued
|
79
|
-
else
|
80
|
-
@jobs_popped = $queue.popped_reverse_sorted_by_status @only
|
42
|
+
def render name, values={}
|
43
|
+
template = File.read("#{APP_LIBS}/views/#{name.to_s}.haml")
|
44
|
+
haml_engine = Haml::Engine.new(template)
|
45
|
+
haml_engine.render(binding, values)
|
81
46
|
end
|
82
47
|
|
83
|
-
|
84
|
-
|
48
|
+
def job_find job_id
|
49
|
+
return nil if ($queue.all_size==0)
|
85
50
|
|
86
|
-
|
87
|
-
|
51
|
+
# Find a job with exactly this id, or prefixed if not found
|
52
|
+
$queue.find_by_id(job_id) || $queue.find_by_id(job_id, true)
|
53
|
+
end
|
88
54
|
|
89
|
-
|
90
|
-
output = render :dashboard
|
55
|
+
end
|
91
56
|
|
92
|
-
# Send response
|
93
|
-
env['api.format'] = :html
|
94
|
-
format "html"
|
95
|
-
status 200
|
96
|
-
content_type "text/html"
|
97
|
-
body output
|
98
57
|
|
99
|
-
|
58
|
+
####### INITIALIZATION
|
100
59
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
status 200
|
105
|
-
return {
|
106
|
-
hostname: `hostname`.chomp,
|
107
|
-
version: APP_VER,
|
108
|
-
config: Settings.to_hash,
|
109
|
-
started: APP_STARTED,
|
110
|
-
uptime: (Time.now - APP_STARTED).round(1),
|
111
|
-
counters: $queue.counters,
|
112
|
-
status: $queue.popped_counts_by_status,
|
113
|
-
queue_size: $queue.all_size,
|
114
|
-
jobs_queued: $queue.queued.collect(&:id),
|
115
|
-
jobs_popped: $queue.popped.collect(&:id),
|
116
|
-
routes: RestFtpDaemon::API::Root::routes,
|
117
|
-
}
|
118
|
-
end
|
60
|
+
def initialize
|
61
|
+
# Call daddy
|
62
|
+
super
|
119
63
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
begin
|
125
|
-
raise RestFtpDaemon::DummyException
|
126
|
-
rescue RestFtpDaemon::RestFtpDaemonException => exception
|
127
|
-
status 501
|
128
|
-
api_error exception
|
129
|
-
rescue Exception => exception
|
130
|
-
status 501
|
131
|
-
api_error exception
|
132
|
-
else
|
133
|
-
status 200
|
134
|
-
{}
|
135
|
-
end
|
64
|
+
# Check that Queue and Pool are available
|
65
|
+
raise RestFtpDaemon::MissingQueue unless defined? $queue
|
66
|
+
raise RestFtpDaemon::MissingQueue unless defined? $pool
|
136
67
|
end
|
137
68
|
|
69
|
+
|
138
70
|
end
|
139
71
|
end
|
140
72
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
class Root < Grape::API
|
4
|
+
|
5
|
+
|
6
|
+
####### GET /status
|
7
|
+
|
8
|
+
# Server global status
|
9
|
+
get '/status' do
|
10
|
+
info "GET /status"
|
11
|
+
status 200
|
12
|
+
return {
|
13
|
+
hostname: `hostname`.chomp,
|
14
|
+
version: APP_VER,
|
15
|
+
config: Helpers.get_censored_config,
|
16
|
+
started: APP_STARTED,
|
17
|
+
uptime: (Time.now - APP_STARTED).round(1),
|
18
|
+
counters: $queue.counters,
|
19
|
+
status: $queue.counts_by_status,
|
20
|
+
jobs_count: $queue.all_size,
|
21
|
+
jobs_queued: $queue.queued.collect(&:id),
|
22
|
+
jobs_popped: $queue.popped.collect(&:id),
|
23
|
+
#routes: RestFtpDaemon::API::Root::routes,
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# Terrific constants
|
2
2
|
APP_NAME = "rest-ftp-daemon"
|
3
3
|
APP_CONF = "/etc/#{APP_NAME}.yml"
|
4
|
-
APP_VER = "0.
|
5
|
-
|
4
|
+
APP_VER = "0.94.4"
|
6
5
|
|
7
6
|
# Some global constants
|
8
7
|
IDENT_JOB_LEN = 4
|
@@ -15,7 +14,10 @@ DEFAULT_UPDATE_EVERY_KB = 2048
|
|
15
14
|
DEFAULT_WORKERS = 1
|
16
15
|
DEFAULT_LOGS_PROGNAME_TRIM = 12
|
17
16
|
|
18
|
-
|
19
17
|
# Initialize markers
|
20
18
|
APP_STARTED = Time.now
|
21
19
|
APP_LIBS = File.dirname(__FILE__)
|
20
|
+
|
21
|
+
# Debugging
|
22
|
+
DEBUG_FTP_COMMANDS = false
|
23
|
+
|
@@ -3,6 +3,13 @@ require 'securerandom'
|
|
3
3
|
module RestFtpDaemon
|
4
4
|
class Helpers
|
5
5
|
|
6
|
+
def self.get_censored_config
|
7
|
+
config = Settings.to_hash
|
8
|
+
config[:users] = Settings.users.keys if Settings.users
|
9
|
+
config[:endpoints] = Settings.endpoints.keys if Settings.endpoints
|
10
|
+
config
|
11
|
+
end
|
12
|
+
|
6
13
|
def self.format_bytes number, unit=""
|
7
14
|
return "Ø" if number.nil? || number.zero?
|
8
15
|
|
@@ -12,6 +19,11 @@ module RestFtpDaemon
|
|
12
19
|
"#{converted} #{units[index]}#{unit}"
|
13
20
|
end
|
14
21
|
|
22
|
+
def self.text_or_empty text
|
23
|
+
return "Ø" if text.nil? || text.empty?
|
24
|
+
text
|
25
|
+
end
|
26
|
+
|
15
27
|
def self.identifier len
|
16
28
|
rand(36**len).to_s(36)
|
17
29
|
end
|
@@ -20,12 +32,25 @@ module RestFtpDaemon
|
|
20
32
|
"[#{item}]"
|
21
33
|
end
|
22
34
|
|
23
|
-
def self.
|
24
|
-
|
35
|
+
def self.highlight_tokens(path)
|
36
|
+
path.gsub(/(\[[^\[]+\])/, '<span class="token">\1</span>')
|
25
37
|
end
|
26
38
|
|
27
39
|
def self.extract_filename path
|
28
|
-
|
40
|
+
# match everything that's after a slash at the end of the string
|
41
|
+
m = path.match /\/?([^\/]+)$/
|
42
|
+
return m[1] unless m.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.extract_dirname path
|
46
|
+
# match all the beginning of the string up to the last slash
|
47
|
+
m = path.match(/^(.*)\/[^\/]*$/)
|
48
|
+
return "/#{m[1]}" unless m.nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.extract_parent path
|
52
|
+
# m = path.match(/^(.*\/)[^\/]*\/+$/)
|
53
|
+
m = path.match(/^(.*\/)[^\/]+\/?$/)
|
29
54
|
return m[1] unless m.nil?
|
30
55
|
end
|
31
56
|
|
@@ -65,15 +90,16 @@ module RestFtpDaemon
|
|
65
90
|
"<div class=\"transfer-method label #{klass}\">#{method.upcase}</div>"
|
66
91
|
end
|
67
92
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
93
|
+
# Dates and times: date with time generator
|
94
|
+
def self.datetime_full param
|
95
|
+
return "-" if param.nil?
|
96
|
+
return param.to_datetime.strftime("%d.%m.%Y %H:%M")
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.datetime_short param
|
100
|
+
return "-" if param.nil?
|
101
|
+
return param.to_datetime.strftime("%d/%m %H:%M")
|
102
|
+
end
|
77
103
|
|
78
104
|
end
|
79
105
|
end
|
data/lib/rest-ftp-daemon/job.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#require 'net/ftptls'
|
2
|
-
|
3
1
|
require 'uri'
|
4
2
|
require 'net/ftp'
|
5
3
|
require 'double_bag_ftps'
|
@@ -7,18 +5,36 @@ require 'timeout'
|
|
7
5
|
|
8
6
|
module RestFtpDaemon
|
9
7
|
class Job < RestFtpDaemon::Common
|
10
|
-
|
8
|
+
|
9
|
+
FIELDS = [:source, :target, :priority, :notify, :overwrite, :mkdir]
|
10
|
+
|
11
|
+
attr_reader :id
|
11
12
|
attr_accessor :wid
|
12
13
|
|
14
|
+
attr_reader :error
|
15
|
+
attr_reader :status
|
16
|
+
|
17
|
+
attr_reader :started_at
|
18
|
+
attr_reader :updated_at
|
19
|
+
|
20
|
+
attr_reader :params
|
21
|
+
|
22
|
+
FIELDS.each do |field|
|
23
|
+
attr_reader field
|
24
|
+
end
|
25
|
+
|
13
26
|
def initialize job_id, params={}
|
14
27
|
# Call super
|
15
28
|
# super()
|
16
29
|
info "Job.initialize"
|
17
30
|
|
18
31
|
# Init context
|
19
|
-
@params = params
|
20
32
|
@id = job_id.to_s
|
21
33
|
#set :id, job_id
|
34
|
+
FIELDS.each do |field|
|
35
|
+
instance_variable_set("@#{field.to_s}", params[field])
|
36
|
+
end
|
37
|
+
@params = {}
|
22
38
|
|
23
39
|
# Protect with a mutex
|
24
40
|
@mutex = Mutex.new
|
@@ -27,40 +43,29 @@ module RestFtpDaemon
|
|
27
43
|
@logger = RestFtpDaemon::Logger.new(:workers, "JOB #{id}")
|
28
44
|
|
29
45
|
# Flag current job
|
30
|
-
|
31
|
-
status :created
|
46
|
+
@started_at = Time.now
|
47
|
+
@status = :created
|
32
48
|
|
33
49
|
# Send first notification
|
34
50
|
#info "Job.initialize/notify"
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def id
|
39
|
-
@id
|
40
|
-
end
|
41
|
-
|
42
|
-
def priority
|
43
|
-
get :priority
|
44
|
-
end
|
45
|
-
def get_status
|
46
|
-
get :status
|
51
|
+
client_notify "rftpd.queued"
|
47
52
|
end
|
48
53
|
|
49
54
|
def process
|
50
55
|
# Update job's status
|
51
|
-
|
56
|
+
@error = nil
|
52
57
|
|
53
58
|
# Prepare job
|
54
59
|
begin
|
55
|
-
info "Job.process
|
56
|
-
status :preparing
|
60
|
+
info "Job.process prepare"
|
61
|
+
@status = :preparing
|
57
62
|
prepare
|
58
63
|
|
59
64
|
rescue RestFtpDaemon::JobMissingAttribute => exception
|
60
65
|
return oops "rftpd.started", exception, :job_missing_attribute
|
61
66
|
|
62
|
-
rescue RestFtpDaemon::JobSourceNotFound => exception
|
63
|
-
|
67
|
+
# rescue RestFtpDaemon::JobSourceNotFound => exception
|
68
|
+
# return oops "rftpd.started", exception, :job_source_not_found
|
64
69
|
|
65
70
|
rescue RestFtpDaemon::JobUnresolvedTokens => exception
|
66
71
|
return oops "rftpd.started", exception, :job_unresolved_tokens
|
@@ -68,33 +73,32 @@ module RestFtpDaemon
|
|
68
73
|
rescue RestFtpDaemon::JobTargetUnparseable => exception
|
69
74
|
return oops "rftpd.started", exception, :job_target_unparseable
|
70
75
|
|
76
|
+
rescue RestFtpDaemon::JobTargetUnsupported => exception
|
77
|
+
return oops "rftpd.started", exception, :job_target_unsupported
|
78
|
+
|
71
79
|
rescue RestFtpDaemon::JobAssertionFailed => exception
|
72
80
|
return oops "rftpd.started", exception, :job_assertion_failed
|
73
81
|
|
74
82
|
rescue RestFtpDaemon::RestFtpDaemonException => exception
|
75
83
|
return oops "rftpd.started", exception, :job_prepare_failed
|
76
84
|
|
85
|
+
rescue URI::InvalidURIError => exception
|
86
|
+
return oops "rftpd.started", exception, :job_target_invalid
|
87
|
+
|
77
88
|
rescue Exception => exception
|
78
89
|
return oops "rftpd.started", exception, :job_prepare_unhandled, true
|
79
90
|
|
80
91
|
else
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
# Notify rftpd.start
|
87
|
-
info "Job.process/prepare notify started"
|
88
|
-
notify "rftpd.started", 0
|
89
|
-
info "Job.process/prepare notified started"
|
92
|
+
# Prepare done !
|
93
|
+
@status = :prepared
|
94
|
+
info "Job.process notify rftpd.started"
|
95
|
+
client_notify "rftpd.started", nil
|
90
96
|
end
|
91
97
|
|
92
|
-
info "Job.process prepare>transfer"
|
93
|
-
|
94
98
|
# Process job
|
95
99
|
begin
|
96
|
-
info "Job.process
|
97
|
-
status :starting
|
100
|
+
info "Job.process transfer"
|
101
|
+
@status = :starting
|
98
102
|
transfer
|
99
103
|
|
100
104
|
rescue Errno::EHOSTDOWN => exception
|
@@ -109,9 +113,15 @@ module RestFtpDaemon
|
|
109
113
|
rescue Net::FTPPermError => exception
|
110
114
|
return oops "rftpd.ended", exception, :job_perm_error
|
111
115
|
|
116
|
+
rescue OpenSSL::SSL::SSLError => exception
|
117
|
+
return oops "rftpd.ended", exception, :job_openssl_error
|
118
|
+
|
112
119
|
rescue Errno::EMFILE => exception
|
113
120
|
return oops "rftpd.ended", exception, :job_too_many_open_files
|
114
121
|
|
122
|
+
rescue RestFtpDaemon::JobSourceNotFound => exception
|
123
|
+
return oops "rftpd.ended", exception, :job_source_not_found
|
124
|
+
|
115
125
|
rescue RestFtpDaemon::JobTargetFileExists => exception
|
116
126
|
return oops "rftpd.ended", exception, :job_target_file_exists
|
117
127
|
|
@@ -128,31 +138,14 @@ module RestFtpDaemon
|
|
128
138
|
return oops "rftpd.ended", exception, :job_transfer_unhandled, true
|
129
139
|
|
130
140
|
else
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
# Notify rftpd.ended
|
136
|
-
notify "rftpd.ended", 0
|
141
|
+
# All done !
|
142
|
+
@status = :finished
|
143
|
+
info "Job.process notify rftpd.ended"
|
144
|
+
client_notify "rftpd.ended", nil
|
137
145
|
end
|
138
146
|
|
139
147
|
end
|
140
148
|
|
141
|
-
def describe
|
142
|
-
# Update realtime info
|
143
|
-
#u = up_time
|
144
|
-
#set :uptime, u.round(2) unless u.nil?
|
145
|
-
|
146
|
-
# Return the whole structure FIXME
|
147
|
-
@params.merge({
|
148
|
-
id: @id,
|
149
|
-
uptime: up_time.round(2)
|
150
|
-
})
|
151
|
-
# @mutex.synchronize do
|
152
|
-
# out = @params.clone
|
153
|
-
# end
|
154
|
-
end
|
155
|
-
|
156
149
|
def get attribute
|
157
150
|
@mutex.synchronize do
|
158
151
|
@params || {}
|
@@ -160,11 +153,15 @@ module RestFtpDaemon
|
|
160
153
|
end
|
161
154
|
end
|
162
155
|
|
156
|
+
def set_queued
|
157
|
+
@status = :queued
|
158
|
+
end
|
159
|
+
|
163
160
|
protected
|
164
161
|
|
165
|
-
def
|
166
|
-
return 0 if @
|
167
|
-
Time.now - @
|
162
|
+
def age
|
163
|
+
return 0 if @started_at.nil?
|
164
|
+
(Time.now - @started_at).round(2)
|
168
165
|
end
|
169
166
|
|
170
167
|
def wander time
|
@@ -184,21 +181,21 @@ module RestFtpDaemon
|
|
184
181
|
@mutex.synchronize do
|
185
182
|
@params || {}
|
186
183
|
# return unless @params.is_a? Enumerable
|
187
|
-
@
|
184
|
+
@updated_at = Time.now
|
188
185
|
@params[attribute] = value
|
189
186
|
end
|
190
187
|
end
|
191
188
|
|
192
|
-
def status status
|
193
|
-
set :status, status
|
194
|
-
end
|
195
|
-
|
196
189
|
def expand_path path
|
197
190
|
File.expand_path replace_tokens(path)
|
198
191
|
end
|
199
192
|
|
200
193
|
def expand_url path
|
201
|
-
URI::parse replace_tokens(path)
|
194
|
+
URI::parse replace_tokens(path)
|
195
|
+
end
|
196
|
+
|
197
|
+
def contains_brackets(item)
|
198
|
+
/\[.*\]/.match(item)
|
202
199
|
end
|
203
200
|
|
204
201
|
def replace_tokens path
|
@@ -213,34 +210,33 @@ module RestFtpDaemon
|
|
213
210
|
newpath = path.clone
|
214
211
|
vectors.each do |from, to|
|
215
212
|
next if to.to_s.blank?
|
216
|
-
#info "Job.replace_tokens #{Helpers.tokenize(from)} > #{to}"
|
217
213
|
newpath.gsub! Helpers.tokenize(from), to
|
218
214
|
end
|
219
215
|
|
220
216
|
# Ensure result does not contain tokens after replacement
|
221
|
-
raise RestFtpDaemon::JobUnresolvedTokens if
|
217
|
+
raise RestFtpDaemon::JobUnresolvedTokens if contains_brackets newpath
|
222
218
|
|
223
|
-
# All OK, return this
|
224
|
-
return newpath
|
219
|
+
# All OK, return this URL stripping multiple slashes
|
220
|
+
return newpath.gsub(/([^:])\/\//, '\1/')
|
225
221
|
end
|
226
222
|
|
227
223
|
def prepare
|
228
224
|
# Init
|
229
|
-
status :preparing
|
225
|
+
@status = :preparing
|
230
226
|
@source_method = :file
|
231
227
|
@target_method = nil
|
232
228
|
@source_path = nil
|
233
229
|
@target_url = nil
|
234
230
|
|
235
231
|
# Check source
|
236
|
-
raise RestFtpDaemon::JobMissingAttribute unless @
|
237
|
-
@source_path = expand_path @
|
232
|
+
raise RestFtpDaemon::JobMissingAttribute unless @source
|
233
|
+
@source_path = expand_path @source
|
238
234
|
set :source_path, @source_path
|
239
235
|
set :source_method, :file
|
240
236
|
|
241
237
|
# Check target
|
242
|
-
raise RestFtpDaemon::JobMissingAttribute unless @
|
243
|
-
@target_url = expand_url @
|
238
|
+
raise RestFtpDaemon::JobMissingAttribute unless @target
|
239
|
+
@target_url = expand_url @target
|
244
240
|
set :target_url, @target_url.to_s
|
245
241
|
|
246
242
|
if @target_url.kind_of? URI::FTP
|
@@ -255,22 +251,21 @@ module RestFtpDaemon
|
|
255
251
|
# Check compliance
|
256
252
|
raise RestFtpDaemon::JobTargetUnparseable if @target_url.nil?
|
257
253
|
raise RestFtpDaemon::JobTargetUnsupported if @target_method.nil?
|
258
|
-
#raise RestFtpDaemon::JobSourceNotFound unless File.exists? @source_path
|
259
254
|
end
|
260
255
|
|
261
256
|
def transfer
|
262
257
|
# Method assertions and init
|
263
|
-
status :checking_source
|
258
|
+
@status = :checking_source
|
264
259
|
raise RestFtpDaemon::JobAssertionFailed unless @source_path && @target_url
|
265
260
|
@transfer_sent = 0
|
266
|
-
set :
|
261
|
+
set :source_processed, 0
|
267
262
|
|
268
263
|
# Guess source file names using Dir.glob
|
269
264
|
source_matches = Dir.glob @source_path
|
270
|
-
info "Job.transfer sources
|
265
|
+
info "Job.transfer sources #{source_matches.inspect}"
|
271
266
|
raise RestFtpDaemon::JobSourceNotFound if source_matches.empty?
|
272
|
-
set :
|
273
|
-
set :
|
267
|
+
set :source_count, source_matches.count
|
268
|
+
set :source_files, source_matches
|
274
269
|
|
275
270
|
# Guess target file name, and fail if present while we matched multiple sources
|
276
271
|
target_name = Helpers.extract_filename @target_url.path
|
@@ -280,7 +275,12 @@ module RestFtpDaemon
|
|
280
275
|
ftp_init
|
281
276
|
|
282
277
|
# Connect remote server, login and chdir
|
283
|
-
|
278
|
+
ftp_connect_and_login
|
279
|
+
|
280
|
+
# Connect remote server, login and chdir
|
281
|
+
#path = '/' + Helpers.extract_dirname(@target_url.path).to_s
|
282
|
+
path = Helpers.extract_dirname(@target_url.path).to_s
|
283
|
+
ftp_chdir_or_buildpath path
|
284
284
|
|
285
285
|
# Check source files presence and compute total size, they should be there, coming from Dir.glob()
|
286
286
|
@transfer_total = 0
|
@@ -296,7 +296,7 @@ module RestFtpDaemon
|
|
296
296
|
source_matches.each do |filename|
|
297
297
|
ftp_transfer filename, target_name
|
298
298
|
done += 1
|
299
|
-
set :
|
299
|
+
set :source_processed, done
|
300
300
|
end
|
301
301
|
|
302
302
|
# Add total transferred to counter
|
@@ -304,7 +304,7 @@ module RestFtpDaemon
|
|
304
304
|
|
305
305
|
# Close FTP connexion
|
306
306
|
info "Job.transfer disconnecting"
|
307
|
-
status :disconnecting
|
307
|
+
@status = :disconnecting
|
308
308
|
@ftp.close
|
309
309
|
end
|
310
310
|
|
@@ -316,68 +316,127 @@ module RestFtpDaemon
|
|
316
316
|
info "Job.oops si[#{signal_name}] er[#{error_name.to_s}] ex[#{exception.class}]"
|
317
317
|
|
318
318
|
# Update job's internal status
|
319
|
-
|
320
|
-
|
319
|
+
@status = :failed
|
320
|
+
@error = error_name
|
321
|
+
set :error_name, error_name
|
321
322
|
set :error_exception, exception.class
|
323
|
+
set :error_message, exception.message
|
322
324
|
|
323
325
|
# Build status stack
|
324
|
-
|
326
|
+
notif_status = nil
|
325
327
|
if include_backtrace
|
326
328
|
set :error_backtrace, exception.backtrace
|
327
|
-
|
329
|
+
notif_status = {
|
328
330
|
backtrace: exception.backtrace,
|
329
331
|
}
|
330
332
|
end
|
331
333
|
|
334
|
+
# Increment counter for this error
|
335
|
+
$queue.counter_inc "err_#{error_name}"
|
336
|
+
$queue.counter_inc :jobs_failed
|
337
|
+
|
332
338
|
# Prepare notification if signal given
|
333
339
|
return unless signal_name
|
334
|
-
|
340
|
+
client_notify signal_name, error_name, notif_status
|
335
341
|
end
|
336
342
|
|
337
343
|
def ftp_init
|
338
344
|
# Method assertions
|
339
|
-
info "Job.ftp_init"
|
340
|
-
status :ftp_init
|
345
|
+
info "Job.ftp_init asserts"
|
346
|
+
@status = :ftp_init
|
341
347
|
raise RestFtpDaemon::JobAssertionFailed if @target_method.nil? || @target_url.nil?
|
342
348
|
|
349
|
+
info "Job.ftp_init target_method [#{@target_method}]"
|
343
350
|
case @target_method
|
344
351
|
when :ftp
|
345
|
-
info "Job.ftp_init scheme: ftp"
|
346
352
|
@ftp = Net::FTP.new
|
347
353
|
when :ftps
|
348
|
-
info "Job.transfer scheme: ftps"
|
349
354
|
@ftp = DoubleBagFTPS.new
|
350
355
|
@ftp.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
|
351
356
|
@ftp.ftps_mode = DoubleBagFTPS::EXPLICIT
|
352
357
|
else
|
353
|
-
info "Job.transfer scheme
|
358
|
+
info "Job.transfer unknown scheme [#{@target_url.scheme}]"
|
359
|
+
railse RestFtpDaemon::JobTargetUnsupported
|
354
360
|
end
|
361
|
+
|
362
|
+
# Common setup
|
363
|
+
@ftp.debug_mode = DEBUG_FTP_COMMANDS
|
364
|
+
@ftp.passive = true
|
355
365
|
end
|
356
366
|
|
357
|
-
def
|
358
|
-
|
367
|
+
def ftp_connect_and_login
|
368
|
+
#@status = :ftp_connect
|
359
369
|
# connect_timeout_sec = (Settings.transfer.connect_timeout_sec rescue nil) || DEFAULT_CONNECT_TIMEOUT_SEC
|
360
370
|
|
361
371
|
# Method assertions
|
362
|
-
|
363
|
-
|
372
|
+
host = @target_url.host
|
373
|
+
info "Job.ftp_connect connect [#{host}]"
|
374
|
+
@status = :ftp_connect
|
364
375
|
raise RestFtpDaemon::JobAssertionFailed if @ftp.nil? || @target_url.nil?
|
365
|
-
@ftp.connect(
|
366
|
-
@ftp.passive = true
|
376
|
+
@ftp.connect(host)
|
367
377
|
|
368
|
-
|
369
|
-
|
370
|
-
|
378
|
+
@status = :ftp_login
|
379
|
+
info "Job.ftp_connect login [#{@target_url.user}]"
|
380
|
+
@ftp.login @target_url.user, @target_url.password
|
381
|
+
end
|
371
382
|
|
372
|
-
|
373
|
-
|
374
|
-
path
|
375
|
-
|
383
|
+
def ftp_chdir_or_buildpath path
|
384
|
+
# Method assertions
|
385
|
+
info "Job.ftp_chdir [#{path}] mkdir: #{@mkdir}"
|
386
|
+
raise RestFtpDaemon::JobAssertionFailed if path.nil?
|
387
|
+
@status = :ftp_chdir
|
388
|
+
|
389
|
+
# Extract directory from path
|
390
|
+
if @mkdir
|
391
|
+
# Split dir in parts
|
392
|
+
info "Job.ftp_chdir buildpath [#{path}]"
|
393
|
+
ftp_buildpath path
|
394
|
+
else
|
395
|
+
# Directly chdir if not mkdir requested
|
396
|
+
info "Job.ftp_chdir chdir [#{path}]"
|
397
|
+
@ftp.chdir path
|
398
|
+
end
|
376
399
|
end
|
377
400
|
|
401
|
+
def ftp_buildpath path
|
402
|
+
# Init
|
403
|
+
pref = "Job.ftp_buildpath [#{path}]"
|
404
|
+
|
405
|
+
begin
|
406
|
+
# Try to chdir in this directory
|
407
|
+
@ftp.chdir(path)
|
408
|
+
|
409
|
+
rescue Net::FTPPermError => exception
|
410
|
+
# If not possible because the directory is missing
|
411
|
+
parent = Helpers.extract_parent(path)
|
412
|
+
info "#{pref} chdir failed - parent [#{parent}]"
|
413
|
+
|
414
|
+
# And only if we still have something to "dive up into"
|
415
|
+
if parent
|
416
|
+
# Do the same for the parent
|
417
|
+
ftp_buildpath parent
|
418
|
+
|
419
|
+
# Then finally create this dir and chdir
|
420
|
+
info "#{pref} > now mkdir [#{path}]"
|
421
|
+
@ftp.mkdir path
|
422
|
+
|
423
|
+
# And get into it (this chdir is not rescue'd on purpose in order to throw the ex)
|
424
|
+
info "#{pref} > now chdir [#{path}]"
|
425
|
+
@ftp.chdir(path)
|
426
|
+
end
|
427
|
+
|
428
|
+
end
|
429
|
+
|
430
|
+
# Now we were able to chdir inside, just tell it
|
431
|
+
info "#{pref} changed to [#{@ftp.pwd}]"
|
432
|
+
end
|
433
|
+
|
434
|
+
|
378
435
|
def ftp_presence target_name
|
436
|
+
# FIXME / TODO: try with nlst
|
437
|
+
|
379
438
|
# Method assertions
|
380
|
-
status :ftp_presence
|
439
|
+
@status = :ftp_presence
|
381
440
|
raise RestFtpDaemon::JobAssertionFailed if @ftp.nil? || @target_url.nil?
|
382
441
|
|
383
442
|
# Get file list, sometimes the response can be an empty value
|
@@ -390,24 +449,21 @@ module RestFtpDaemon
|
|
390
449
|
end
|
391
450
|
|
392
451
|
def ftp_transfer source_match, target_name = nil
|
393
|
-
#target_name
|
394
452
|
# Method assertions
|
395
|
-
info "Job.ftp_transfer source_match
|
453
|
+
info "Job.ftp_transfer source_match [#{source_match}]"
|
396
454
|
raise RestFtpDaemon::JobAssertionFailed if @ftp.nil?
|
397
455
|
raise RestFtpDaemon::JobAssertionFailed if source_match.nil?
|
398
|
-
#raise RestFtpDaemon::JobAssertionFailed if @transfer_total.nil?
|
399
|
-
status :ftp_transfer
|
400
456
|
|
401
457
|
# Use source filename if target path provided none (typically with multiple sources)
|
402
458
|
target_name ||= Helpers.extract_filename source_match
|
403
|
-
info "Job.ftp_transfer target_name
|
459
|
+
info "Job.ftp_transfer target_name [#{target_name}]"
|
460
|
+
set :source_processing, target_name
|
404
461
|
|
405
462
|
# Check for target file presence
|
406
|
-
status :checking_target
|
407
|
-
overwrite = !get(:overwrite).nil?
|
463
|
+
@status = :checking_target
|
408
464
|
present = ftp_presence target_name
|
409
465
|
if present
|
410
|
-
if overwrite
|
466
|
+
if @overwrite
|
411
467
|
# delete it first
|
412
468
|
info "Job.ftp_transfer removing target file"
|
413
469
|
@ftp.delete(target_name)
|
@@ -427,7 +483,7 @@ module RestFtpDaemon
|
|
427
483
|
chunk_size = update_every_kb * 1024
|
428
484
|
t0 = tstart = Time.now
|
429
485
|
notified_at = Time.now
|
430
|
-
status :uploading
|
486
|
+
@status = :uploading
|
431
487
|
@ftp.putbinaryfile(source_match, target_name, chunk_size) do |block|
|
432
488
|
# Update counters
|
433
489
|
@transfer_sent += block.bytesize
|
@@ -443,42 +499,41 @@ module RestFtpDaemon
|
|
443
499
|
set :progress, percent1
|
444
500
|
|
445
501
|
# Log progress
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
info "Job.ftp_transfer" +
|
502
|
+
stack = []
|
503
|
+
stack << "#{percent1} %"
|
504
|
+
stack << (Helpers.format_bytes @transfer_sent, "B")
|
505
|
+
stack << (Helpers.format_bytes @transfer_total, "B")
|
506
|
+
stack << (Helpers.format_bytes bitrate0, "bps")
|
507
|
+
info "Job.ftp_transfer" + stack.map{|txt| ("%#{DEFAULT_LOGS_PROGNAME_TRIM.to_i}s" % txt)}.join("\t")
|
452
508
|
|
453
509
|
# Update time pointer
|
454
510
|
t0 = Time.now
|
455
511
|
|
456
512
|
# Notify if requested
|
457
513
|
unless notify_after_sec.nil? || (notified_at + notify_after_sec > Time.now)
|
458
|
-
|
514
|
+
notif_status = {
|
459
515
|
progress: percent1,
|
460
516
|
transfer_sent: @transfer_sent,
|
461
517
|
transfer_total: @transfer_total,
|
462
518
|
transfer_bitrate: bitrate0
|
463
519
|
}
|
464
|
-
|
520
|
+
client_notify "rftpd.progress", nil, notif_status
|
465
521
|
notified_at = Time.now
|
466
522
|
end
|
467
523
|
|
468
524
|
end
|
469
525
|
|
470
526
|
# Compute final bitrate
|
471
|
-
|
472
|
-
tbitrate0 = get_bitrate(@transfer_total, tstart).round(0)
|
473
|
-
set :transfer_bitrate, tbitrate0
|
527
|
+
set :transfer_bitrate, get_bitrate(@transfer_total, tstart).round(0)
|
474
528
|
|
475
529
|
# Done
|
476
|
-
|
530
|
+
set :source_processing, nil
|
477
531
|
info "Job.ftp_transfer finished"
|
532
|
+
$queue.counter_inc :jobs_finished
|
478
533
|
end
|
479
534
|
|
480
|
-
def
|
481
|
-
RestFtpDaemon::Notification.new
|
535
|
+
def client_notify signal, error = nil, status = {}
|
536
|
+
RestFtpDaemon::Notification.new @notify, {
|
482
537
|
id: @id,
|
483
538
|
signal: signal,
|
484
539
|
error: error,
|