rest-ftp-daemon 0.85.2 → 0.90.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +16 -0
- data/bin/rest-ftp-daemon +52 -35
- data/lib/rest-ftp-daemon/api/jobs.rb +20 -13
- data/lib/rest-ftp-daemon/api/root.rb +12 -21
- data/lib/rest-ftp-daemon/constants.rb +3 -3
- data/lib/rest-ftp-daemon/exceptions.rb +2 -0
- data/lib/rest-ftp-daemon/helpers.rb +26 -2
- data/lib/rest-ftp-daemon/job.rb +128 -64
- data/lib/rest-ftp-daemon/job_queue.rb +59 -19
- data/lib/rest-ftp-daemon/notification.rb +4 -7
- data/lib/rest-ftp-daemon/static/css/bootstrap.css +2 -2
- data/lib/rest-ftp-daemon/static/css/main.css +17 -0
- data/lib/rest-ftp-daemon/views/dashboard.haml +43 -4
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +55 -74
- data/lib/rest-ftp-daemon/views/dashboard_workers.haml +1 -1
- data/lib/rest-ftp-daemon/worker_pool.rb +1 -1
- data/rest-ftp-daemon.yml.sample +4 -3
- metadata +3 -4
- data/test/helper.rb +0 -34
- data/test/test_rest-ftp-daemon.rb +0 -7
@@ -1,7 +1,10 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'securerandom'
|
2
3
|
|
3
4
|
module RestFtpDaemon
|
4
5
|
class JobQueue < Queue
|
6
|
+
attr_reader :queued
|
7
|
+
attr_reader :popped
|
5
8
|
|
6
9
|
def initialize
|
7
10
|
# # Logger
|
@@ -17,6 +20,12 @@ module RestFtpDaemon
|
|
17
20
|
self.taint
|
18
21
|
@mutex = Mutex.new
|
19
22
|
|
23
|
+
# Identifiers generator
|
24
|
+
@last_id = 0
|
25
|
+
#@prefix = SecureRandom.hex(IDENT_JOB_LEN)
|
26
|
+
@prefix = Helpers.identifier IDENT_JOB_LEN
|
27
|
+
info "queue initialized with prefix: #{@prefix}"
|
28
|
+
|
20
29
|
# Mutex for counters
|
21
30
|
@counters = {}
|
22
31
|
@mutex_counters = Mutex.new
|
@@ -39,6 +48,13 @@ module RestFtpDaemon
|
|
39
48
|
|
40
49
|
end
|
41
50
|
|
51
|
+
def generate_id
|
52
|
+
rand(36**8).to_s(36)
|
53
|
+
@last_id ||= 0
|
54
|
+
@last_id += 1
|
55
|
+
prefixed_id @last_id
|
56
|
+
end
|
57
|
+
|
42
58
|
def counter_add name, value
|
43
59
|
@mutex_counters.synchronize do
|
44
60
|
@counters[name] ||= 0
|
@@ -60,27 +76,45 @@ module RestFtpDaemon
|
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
63
|
-
def by_status status
|
79
|
+
# def by_status status
|
80
|
+
# return [] if status.nil?
|
81
|
+
|
82
|
+
# # Select jobs from the queue if their status is (status)
|
83
|
+
# all.select { |item| item.get(:status) == status.to_sym }
|
84
|
+
# end
|
85
|
+
|
86
|
+
def popped_reverse_sorted_by_status status
|
64
87
|
return [] if status.nil?
|
65
88
|
|
66
89
|
# Select jobs from the queue if their status is (status)
|
67
|
-
|
90
|
+
ordered_popped.reverse.select { |item| item.get(:status) == status.to_sym }
|
68
91
|
end
|
69
92
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@popped
|
93
|
+
def popped_counts_by_status
|
94
|
+
statuses = {}
|
95
|
+
@popped.group_by { |job| job.get(:status) }.map { |status, jobs| statuses[status] = jobs.size }
|
96
|
+
statuses
|
75
97
|
end
|
98
|
+
|
76
99
|
def all
|
100
|
+
# queued2 = @queued.clone
|
101
|
+
# return queued2.merge(@popped)
|
77
102
|
@queued + @popped
|
78
103
|
end
|
79
104
|
def all_size
|
80
105
|
@queued.length + @popped.length
|
81
106
|
end
|
82
107
|
|
83
|
-
def
|
108
|
+
def find_by_id id, prefixed = false
|
109
|
+
# Build a prefixed id if expected
|
110
|
+
id = prefixed_id(id) if prefixed
|
111
|
+
info "find_by_id (#{id}, #{prefixed}) > #{id}"
|
112
|
+
|
113
|
+
# Search in both queues
|
114
|
+
@queued.select { |item| item.id == id }.last || @popped.select { |item| item.id == id }.last
|
115
|
+
end
|
116
|
+
|
117
|
+
def push obj
|
84
118
|
# Check that item responds to "priorty" method
|
85
119
|
raise "JobQueue.push: object should respond to priority method" unless obj.respond_to? :priority
|
86
120
|
|
@@ -97,11 +131,7 @@ module RestFtpDaemon
|
|
97
131
|
alias << push
|
98
132
|
alias enq push
|
99
133
|
|
100
|
-
|
101
|
-
# Retrieves data from the queue. If the queue is empty, the calling thread is
|
102
|
-
# suspended until data is pushed onto the queue. If +non_block+ is true, the
|
103
|
-
# thread isn't suspended, and an exception is raised.
|
104
|
-
#
|
134
|
+
|
105
135
|
def pop(non_block=false)
|
106
136
|
@mutex.synchronize do
|
107
137
|
while true
|
@@ -110,7 +140,7 @@ module RestFtpDaemon
|
|
110
140
|
@waiting.push Thread.current
|
111
141
|
@mutex.sleep
|
112
142
|
else
|
113
|
-
return
|
143
|
+
return pick_one
|
114
144
|
end
|
115
145
|
end
|
116
146
|
end
|
@@ -130,9 +160,20 @@ module RestFtpDaemon
|
|
130
160
|
@waiting.size
|
131
161
|
end
|
132
162
|
|
163
|
+
def ordered_queue
|
164
|
+
@queued.sort_by { |item| [item.priority.to_i, - item.id.to_i] }
|
165
|
+
end
|
166
|
+
|
167
|
+
def ordered_popped
|
168
|
+
@popped.sort_by { |item| [item.get(:updated_at)] }
|
169
|
+
end
|
133
170
|
|
134
171
|
protected
|
135
172
|
|
173
|
+
def prefixed_id id
|
174
|
+
"#{@prefix}.#{id}"
|
175
|
+
end
|
176
|
+
|
136
177
|
def conchita_loop
|
137
178
|
info "conchita starting with: #{@conchita.inspect}"
|
138
179
|
loop do
|
@@ -175,21 +216,20 @@ module RestFtpDaemon
|
|
175
216
|
@logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname) unless @logger.nil?
|
176
217
|
end
|
177
218
|
|
178
|
-
def
|
219
|
+
def pick_one # called inside a mutex/sync
|
179
220
|
# Sort jobs by priority and get the biggest one
|
180
|
-
picked =
|
221
|
+
picked = ordered_queue.last
|
181
222
|
return nil if picked.nil?
|
182
223
|
|
183
|
-
#
|
224
|
+
# Move it away from the queue to the @popped array
|
184
225
|
@queued.delete_if { |item| item == picked }
|
185
|
-
|
186
|
-
# Stack it to popped items
|
187
226
|
@popped.push picked
|
188
227
|
|
189
228
|
# Return picked
|
190
229
|
picked
|
191
230
|
end
|
192
231
|
|
232
|
+
|
193
233
|
private
|
194
234
|
|
195
235
|
def info message, level = 0
|
@@ -32,12 +32,13 @@ module RestFtpDaemon
|
|
32
32
|
|
33
33
|
# Params
|
34
34
|
body = {
|
35
|
-
id: params[:id],
|
35
|
+
id: params[:id].to_s,
|
36
36
|
signal: params[:signal],
|
37
37
|
error: params[:error],
|
38
|
-
host:
|
38
|
+
host: Settings['host'].to_s,
|
39
39
|
}
|
40
|
-
body[:status] = params[:status]
|
40
|
+
body[:status] = params[:status] if (params[:status].is_a? Enumerable)
|
41
|
+
# && (!params[:status].empty?)
|
41
42
|
|
42
43
|
# Send message in a thread
|
43
44
|
Thread.new do |thread|
|
@@ -59,9 +60,5 @@ module RestFtpDaemon
|
|
59
60
|
|
60
61
|
protected
|
61
62
|
|
62
|
-
def get_hostname
|
63
|
-
`hostname`.chomp
|
64
|
-
end
|
65
|
-
|
66
63
|
end
|
67
64
|
end
|
@@ -3,8 +3,9 @@
|
|
3
3
|
%head
|
4
4
|
%meta{:charset => "utf-8"}/
|
5
5
|
%link{ href:"/css/bootstrap.css" , rel: "stylesheet"}
|
6
|
+
%link{ href:"/css/main.css" , rel: "stylesheet"}
|
6
7
|
|
7
|
-
%title="#{Settings.
|
8
|
+
%title="#{Settings['host']} [#{Settings.namespace}] #{APP_NAME}"
|
8
9
|
|
9
10
|
:css
|
10
11
|
|
@@ -47,6 +48,10 @@
|
|
47
48
|
.btn.btn-default.btn-success IP
|
48
49
|
.btn.btn-default= @info_ipaddr
|
49
50
|
|
51
|
+
.btn-group.btn-group-sm
|
52
|
+
.btn.btn-default.btn-success Host
|
53
|
+
.btn.btn-default= Settings['host']
|
54
|
+
|
50
55
|
.btn-group.btn-group-sm
|
51
56
|
.btn.btn-default.btn-warning Load
|
52
57
|
.btn.btn-default= @info_load.round(1)
|
@@ -65,9 +70,43 @@
|
|
65
70
|
|
66
71
|
|
67
72
|
.row
|
68
|
-
.col-md-
|
69
|
-
|
70
|
-
|
73
|
+
.col-md-10
|
74
|
+
|
75
|
+
%h2
|
76
|
+
Jobs
|
77
|
+
|
78
|
+
.btn-group.btn-group-md
|
79
|
+
- klass = @only.nil? ? "btn-info" : ""
|
80
|
+
%a.btn.btn-default{href: "?only=", class: klass}
|
81
|
+
ALL (#{@jobs_queued.size} + #{@jobs_popped.size})
|
82
|
+
.btn-group.btn-group-md
|
83
|
+
- @counts.each do |status, count|
|
84
|
+
- klass = (status == @only) ? "btn-info" : ""
|
85
|
+
%a.btn.btn-default{href: "?only=#{status}", class: klass}
|
86
|
+
#{status} (#{count})
|
87
|
+
|
88
|
+
%table.table.table-striped.table-hover.table-condensed
|
89
|
+
%tr
|
90
|
+
%th JID
|
91
|
+
%th prio
|
92
|
+
%th worker
|
93
|
+
%th source
|
94
|
+
%th target
|
95
|
+
%th status
|
96
|
+
%th error
|
97
|
+
%th progress
|
98
|
+
%th.text-right size
|
99
|
+
%th.text-right bitrate
|
100
|
+
|
101
|
+
- if @only.nil? && !@jobs_queued.empty?
|
102
|
+
= render :dashboard_jobs, {jobs: @jobs_queued, counts: @counts}
|
103
|
+
%tr
|
104
|
+
%th{colspan: 10}
|
105
|
+
|
106
|
+
= render :dashboard_jobs, {jobs: @jobs_popped, counts: @counts}
|
107
|
+
|
108
|
+
|
109
|
+
.col-md-2
|
71
110
|
= render :dashboard_workers, {}
|
72
111
|
|
73
112
|
= render :dashboard_tokens, {tokens: Settings.endpoints || {}}
|
@@ -1,75 +1,56 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
=
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
%
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
%
|
23
|
-
|
24
|
-
%
|
25
|
-
|
26
|
-
%
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
%
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
=
|
56
|
-
%td
|
57
|
-
.label.label-info= job.get :source_method
|
58
|
-
.label.label-primary= job.get :target_method
|
59
|
-
%td= Helpers.format_bytes(size, "B")
|
60
|
-
%td= status
|
61
|
-
%td= error
|
62
|
-
%td
|
63
|
-
- unless progress.nil?
|
64
|
-
.progress
|
65
|
-
.progress-bar{style:"width: #{progress}%;"}
|
66
|
-
= Helpers.format_bytes job.get(:transfer_sent), "B"
|
67
|
-
%td
|
68
|
-
- unless progress.nil?
|
69
|
-
= "#{progress}%"
|
70
|
-
- if (bitrate = job.get :transfer_bitrate)
|
71
|
-
= "|"
|
72
|
-
= Helpers.format_bytes(bitrate, "bps")
|
73
|
-
|
74
|
-
%td= job.wid
|
1
|
+
- jobs.each do |job|
|
2
|
+
- error = job.get :error
|
3
|
+
- status = job.get :status
|
4
|
+
- size = job.get :transfer_total
|
5
|
+
- progress = job.get :progress
|
6
|
+
- transfer_source_count = job.get(:transfer_source_count) || 0
|
7
|
+
- transfer_source_done = job.get(:transfer_source_done) || 0
|
8
|
+
|
9
|
+
- if error!=0 && !error.nil?
|
10
|
+
- trclass = "danger"
|
11
|
+
- elsif status == :uploading
|
12
|
+
- trclass = "info"
|
13
|
+
- elsif status == :finished
|
14
|
+
- trclass = "success"
|
15
|
+
- else
|
16
|
+
- trclass = "warning"
|
17
|
+
|
18
|
+
%tr{class: trclass}
|
19
|
+
|
20
|
+
%td= job.id
|
21
|
+
|
22
|
+
%td= job.get :priority
|
23
|
+
|
24
|
+
%td= job.wid
|
25
|
+
|
26
|
+
%td.fixed{title: job.get(:source_path)}
|
27
|
+
= Helpers.job_method_label job.get(:source_method)
|
28
|
+
= job.get :source
|
29
|
+
|
30
|
+
%td.fixed{title: job.get(:target_url)}
|
31
|
+
= Helpers.job_method_label job.get(:target_method)
|
32
|
+
= job.get :target
|
33
|
+
|
34
|
+
%td
|
35
|
+
= status
|
36
|
+
- if (transfer_source_count > 0) #&& (transfer_source_done < transfer_source_count)
|
37
|
+
%small= " #{transfer_source_done}/#{transfer_source_count}"
|
38
|
+
|
39
|
+
%td
|
40
|
+
= error
|
41
|
+
- unless progress.nil? || status == :finished
|
42
|
+
%small= "#{progress}%"
|
43
|
+
|
44
|
+
%td
|
45
|
+
- unless progress.nil?
|
46
|
+
.progress
|
47
|
+
.progress-bar{style:"width: #{progress}%;"}
|
48
|
+
= Helpers.format_bytes job.get(:transfer_sent), "B"
|
49
|
+
|
50
|
+
%td.nobr.text-right
|
51
|
+
= Helpers.format_bytes(size, "B")
|
52
|
+
|
53
|
+
%td.nobr.text-right
|
54
|
+
- if (bitrate = job.get :transfer_bitrate)
|
55
|
+
= Helpers.format_bytes(bitrate, "bps")
|
75
56
|
|
data/rest-ftp-daemon.yml.sample
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
defaults: &defaults
|
2
|
-
daemonize:
|
3
|
-
port:
|
2
|
+
daemonize: true
|
3
|
+
port: 3000
|
4
4
|
workers: 2
|
5
5
|
#adminpwd: "admin"
|
6
|
-
pidfile: /tmp/rftpd.pid
|
7
6
|
user: rftpd
|
8
7
|
group: rftpd
|
8
|
+
host: <%= `hostname`.chomp.split('.').first %>
|
9
9
|
|
10
10
|
transfer:
|
11
11
|
#update_every_kb: 500
|
@@ -26,3 +26,4 @@ preprod:
|
|
26
26
|
|
27
27
|
production:
|
28
28
|
<<: *defaults
|
29
|
+
port: 3200
|