rest-ftp-daemon 0.90.1 → 0.94.4
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.
- 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,
|