rest-ftp-daemon 0.247.1 → 0.250.0
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/Gemfile.lock +6 -6
- data/README.md +1 -1
- data/config.ru +3 -0
- data/lib/rest-ftp-daemon.rb +6 -0
- data/lib/rest-ftp-daemon/api/config.rb +25 -0
- data/lib/rest-ftp-daemon/api/dashboard.rb +1 -12
- data/lib/rest-ftp-daemon/api/debug.rb +37 -0
- data/lib/rest-ftp-daemon/api/job_presenter.rb +0 -2
- data/lib/rest-ftp-daemon/api/jobs.rb +1 -26
- data/lib/rest-ftp-daemon/api/root.rb +32 -107
- data/lib/rest-ftp-daemon/api/status.rb +41 -0
- data/lib/rest-ftp-daemon/constants.rb +4 -1
- data/lib/rest-ftp-daemon/job.rb +10 -5
- data/lib/rest-ftp-daemon/job_queue.rb +81 -60
- data/lib/rest-ftp-daemon/notification.rb +9 -2
- data/lib/rest-ftp-daemon/stats.rb +42 -0
- data/lib/rest-ftp-daemon/views/dashboard.haml +3 -2
- data/lib/rest-ftp-daemon/views/dashboard_footer.haml +0 -1
- data/lib/rest-ftp-daemon/views/dashboard_header.haml +3 -3
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +6 -6
- data/lib/rest-ftp-daemon/views/dashboard_rates.haml +36 -0
- data/lib/rest-ftp-daemon/views/dashboard_stats.haml +21 -0
- data/lib/rest-ftp-daemon/views/dashboard_table.haml +0 -8
- data/lib/rest-ftp-daemon/views/dashboard_tokens.haml +9 -7
- data/lib/rest-ftp-daemon/views/dashboard_workers.haml +27 -25
- data/lib/rest-ftp-daemon/worker_job.rb +1 -1
- data/rest-ftp-daemon.gemspec +1 -1
- data/spec/rest-ftp-daemon/features/{routes_spec.rb → debug_spec.rb} +3 -3
- data/spec/rest-ftp-daemon/features/status_spec.rb +2 -2
- metadata +11 -6
- data/lib/rest-ftp-daemon/views/dashboard_counters.haml +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56ef0db0f52451ad8dd48acea697310637a460a7
|
4
|
+
data.tar.gz: 8c1c45f0077e055a48c6d80bb1d9f60892019d2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8120b207886ab8a2667b68761e539a23cc547d04880844d0898ac6560a2edeea313033dca26de45b6af51f0715a97a3e7ad82e626b3915f2255d530ab0dadee
|
7
|
+
data.tar.gz: 43e8dff779837f56751a1accb9ddf69fe122cc31af4ec97c83ce556028b27199164bb9bc9b7aebe708b1a108e060a3fbb8a8752aae33d21f90eb137ab33d0378
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rest-ftp-daemon (0.
|
4
|
+
rest-ftp-daemon (0.250.0)
|
5
5
|
double-bag-ftps
|
6
6
|
facter
|
7
7
|
get_process_mem
|
@@ -13,7 +13,7 @@ PATH
|
|
13
13
|
newrelic_rpm
|
14
14
|
settingslogic
|
15
15
|
sys-cpu
|
16
|
-
thin (~> 1.6)
|
16
|
+
thin (~> 1.6.4)
|
17
17
|
|
18
18
|
GEM
|
19
19
|
remote: http://rubygems.org/
|
@@ -80,8 +80,8 @@ GEM
|
|
80
80
|
ice_nine (0.11.2)
|
81
81
|
json (1.8.3)
|
82
82
|
method_source (0.8.2)
|
83
|
-
minitest (5.
|
84
|
-
multi_json (1.12.
|
83
|
+
minitest (5.9.0)
|
84
|
+
multi_json (1.12.1)
|
85
85
|
multi_xml (0.5.5)
|
86
86
|
mustermann19 (0.4.3)
|
87
87
|
enumerable-lazy
|
@@ -120,7 +120,7 @@ GEM
|
|
120
120
|
powerpack (~> 0.1)
|
121
121
|
rainbow (>= 1.99.1, < 3.0)
|
122
122
|
ruby-progressbar (~> 1.4)
|
123
|
-
ruby-progressbar (1.8.
|
123
|
+
ruby-progressbar (1.8.1)
|
124
124
|
settingslogic (2.0.9)
|
125
125
|
slop (3.6.0)
|
126
126
|
sys-cpu (0.7.2)
|
@@ -130,7 +130,7 @@ GEM
|
|
130
130
|
eventmachine (~> 1.0, >= 1.0.4)
|
131
131
|
rack (~> 1.0)
|
132
132
|
thread_safe (0.3.5)
|
133
|
-
tilt (2.0.
|
133
|
+
tilt (2.0.4)
|
134
134
|
tzinfo (1.2.2)
|
135
135
|
thread_safe (~> 0.1)
|
136
136
|
unf (0.1.4)
|
data/README.md
CHANGED
data/config.ru
CHANGED
data/lib/rest-ftp-daemon.rb
CHANGED
@@ -10,6 +10,8 @@ require "net/http"
|
|
10
10
|
require "thread"
|
11
11
|
require "singleton"
|
12
12
|
require "newrelic_rpm"
|
13
|
+
require "grape"
|
14
|
+
require "grape-entity"
|
13
15
|
|
14
16
|
# Project's libs
|
15
17
|
require_relative "rest-ftp-daemon/constants"
|
@@ -23,6 +25,7 @@ require_relative "rest-ftp-daemon/logger"
|
|
23
25
|
require_relative "rest-ftp-daemon/paginate"
|
24
26
|
require_relative "rest-ftp-daemon/uri"
|
25
27
|
require_relative "rest-ftp-daemon/job_queue"
|
28
|
+
require_relative "rest-ftp-daemon/stats"
|
26
29
|
require_relative "rest-ftp-daemon/worker"
|
27
30
|
require_relative "rest-ftp-daemon/worker_conchita"
|
28
31
|
require_relative "rest-ftp-daemon/worker_job"
|
@@ -38,6 +41,9 @@ require_relative "rest-ftp-daemon/remote_sftp"
|
|
38
41
|
require_relative "rest-ftp-daemon/api/job_presenter"
|
39
42
|
require_relative "rest-ftp-daemon/api/jobs"
|
40
43
|
require_relative "rest-ftp-daemon/api/dashboard"
|
44
|
+
require_relative "rest-ftp-daemon/api/status"
|
45
|
+
require_relative "rest-ftp-daemon/api/config"
|
46
|
+
require_relative "rest-ftp-daemon/api/debug"
|
41
47
|
require_relative "rest-ftp-daemon/api/root"
|
42
48
|
|
43
49
|
# Haml monkey-patching
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
class Config < Grape::API
|
4
|
+
|
5
|
+
desc "Show daemon config"
|
6
|
+
get "/" do
|
7
|
+
status 200
|
8
|
+
return Helpers.get_censored_config
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Reload daemon config"
|
12
|
+
post "/reload" do
|
13
|
+
if Settings.at(:debug, :allow_reload)==true
|
14
|
+
Settings.reload!
|
15
|
+
status 200
|
16
|
+
return Helpers.get_censored_config
|
17
|
+
else
|
18
|
+
status 403
|
19
|
+
return "Config reload not permitted"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,21 +1,16 @@
|
|
1
|
-
require "grape"
|
2
1
|
require "haml"
|
3
2
|
require "sys/cpu"
|
3
|
+
require "get_process_mem"
|
4
4
|
require "facter"
|
5
5
|
|
6
6
|
module RestFtpDaemon
|
7
7
|
module API
|
8
8
|
|
9
|
-
# Offers an HTML dashboard through the Grape API (hum...)
|
10
9
|
class Dashbaord < Grape::API
|
11
10
|
|
12
11
|
### HELPERS
|
13
12
|
|
14
13
|
helpers do
|
15
|
-
def logger
|
16
|
-
Root.logger
|
17
|
-
end
|
18
|
-
|
19
14
|
def render name, values={}
|
20
15
|
template = File.read("#{APP_LIBS}/views/#{name}.haml")
|
21
16
|
|
@@ -65,12 +60,6 @@ module RestFtpDaemon
|
|
65
60
|
end
|
66
61
|
|
67
62
|
|
68
|
-
### Common request logging
|
69
|
-
before do
|
70
|
-
log_info "HTTP #{request.request_method} #{request.fullpath}", params
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
63
|
### DASHBOARD
|
75
64
|
desc "Show a global dashboard"
|
76
65
|
get "/" do
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
class Debug < Grape::API
|
4
|
+
|
5
|
+
desc "Show app routes, params encodings"
|
6
|
+
get "/" do
|
7
|
+
# Encodings
|
8
|
+
encodings = {}
|
9
|
+
jobs = $queue.jobs
|
10
|
+
|
11
|
+
jobs.each do |job|
|
12
|
+
# here = out[job.id] = {}
|
13
|
+
me = encodings[job.id] = {}
|
14
|
+
|
15
|
+
me[:error] = job.error.encoding.to_s unless job.error.nil?
|
16
|
+
me[:status] = job.status.encoding.to_s unless job.status.nil?
|
17
|
+
|
18
|
+
Job::FIELDS.each do |name|
|
19
|
+
value = job.send(name)
|
20
|
+
me[name] = value.encoding.to_s if value.is_a? String
|
21
|
+
end
|
22
|
+
|
23
|
+
job.infos.each do |name, value|
|
24
|
+
me["infos_#{name}"] = value.encoding.to_s if value.is_a? String
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Build response
|
29
|
+
return {
|
30
|
+
routes: RestFtpDaemon::API::Root.routes,
|
31
|
+
encodings: encodings,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,26 +1,7 @@
|
|
1
|
-
require "grape"
|
2
|
-
|
3
1
|
module RestFtpDaemon
|
4
2
|
module API
|
5
3
|
class Jobs < Grape::API
|
6
4
|
|
7
|
-
### HELPERS
|
8
|
-
|
9
|
-
helpers do
|
10
|
-
def logger
|
11
|
-
Root.logger
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
### Common request logging
|
17
|
-
before do
|
18
|
-
log_info "HTTP #{request.request_method} #{request.fullpath}", params
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
### READ ONE JOB
|
23
|
-
|
24
5
|
desc "Read job with ID"
|
25
6
|
params do
|
26
7
|
requires :id, type: String, desc: "ID of the Job to read"
|
@@ -47,9 +28,6 @@ module RestFtpDaemon
|
|
47
28
|
end
|
48
29
|
end
|
49
30
|
|
50
|
-
|
51
|
-
### READ ALL JOBS
|
52
|
-
|
53
31
|
desc "List all Jobs"
|
54
32
|
get "/" do
|
55
33
|
begin
|
@@ -67,9 +45,6 @@ module RestFtpDaemon
|
|
67
45
|
end
|
68
46
|
end
|
69
47
|
|
70
|
-
|
71
|
-
### CREATE A JOB
|
72
|
-
|
73
48
|
desc "Create a new job"
|
74
49
|
params do
|
75
50
|
requires :source, type: String, desc: "Source file pattern"
|
@@ -102,7 +77,7 @@ module RestFtpDaemon
|
|
102
77
|
$queue.push job
|
103
78
|
|
104
79
|
# Increment a counter
|
105
|
-
$
|
80
|
+
$stats.increment :jobs, :received
|
106
81
|
|
107
82
|
rescue JSON::ParserError => exception
|
108
83
|
log_error "JSON::ParserError: #{exception.message}"
|
@@ -1,23 +1,50 @@
|
|
1
|
-
require "grape"
|
2
|
-
require "get_process_mem"
|
3
|
-
|
4
1
|
module RestFtpDaemon
|
5
2
|
module API
|
6
3
|
class Root < Grape::API
|
7
4
|
|
5
|
+
### LOGGING & HELPERS
|
6
|
+
|
7
|
+
helpers do
|
8
|
+
def logger
|
9
|
+
Root.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_request
|
13
|
+
if env.nil?
|
14
|
+
puts "HTTP_ENV_IS_NIL: #{env.inspect}"
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
18
|
+
request_method = env['REQUEST_METHOD']
|
19
|
+
request_path = env['REQUEST_PATH']
|
20
|
+
request_uri = env['REQUEST_URI']
|
21
|
+
log_info "HTTP #{request_method} #{request_uri}", params
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
before do
|
26
|
+
log_request
|
27
|
+
end
|
28
|
+
|
29
|
+
|
8
30
|
### CLASS CONFIG
|
9
31
|
|
10
32
|
helpers RestFtpDaemon::LoggerHelper
|
11
33
|
logger RestFtpDaemon::LoggerPool.instance.get :api
|
12
|
-
|
13
34
|
do_not_route_head!
|
14
35
|
do_not_route_options!
|
15
|
-
|
36
|
+
# version 'v1'
|
16
37
|
format :json
|
17
38
|
content_type :json, 'application/json; charset=utf-8'
|
18
39
|
|
40
|
+
|
41
|
+
### MOUNTPOINTS
|
42
|
+
|
43
|
+
mount RestFtpDaemon::API::Status => MOUNT_STATUS
|
19
44
|
mount RestFtpDaemon::API::Jobs => MOUNT_JOBS
|
20
45
|
mount RestFtpDaemon::API::Dashbaord => MOUNT_BOARD
|
46
|
+
mount RestFtpDaemon::API::Config => MOUNT_CONFIG
|
47
|
+
mount RestFtpDaemon::API::Debug => MOUNT_DEBUG
|
21
48
|
|
22
49
|
|
23
50
|
### INITIALIZATION
|
@@ -32,114 +59,12 @@ module RestFtpDaemon
|
|
32
59
|
end
|
33
60
|
|
34
61
|
|
35
|
-
### HELPERS
|
36
|
-
|
37
|
-
helpers do
|
38
|
-
def logger
|
39
|
-
Root.logger
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
### Common request logging
|
45
|
-
|
46
|
-
before do
|
47
|
-
log_info "HTTP #{request.request_method} #{request.fullpath}", params
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
62
|
### ROOT URL ACCESS
|
52
63
|
|
53
64
|
get "/" do
|
54
65
|
redirect Helpers.dashboard_filter_url()
|
55
66
|
end
|
56
67
|
|
57
|
-
|
58
|
-
### SHOW ROUTES
|
59
|
-
|
60
|
-
desc "Show application routes"
|
61
|
-
get "/routes" do
|
62
|
-
status 200
|
63
|
-
return RestFtpDaemon::API::Root.routes
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
### SHOW STATUS
|
68
|
-
|
69
|
-
desc "Show daemon status"
|
70
|
-
get "/status" do
|
71
|
-
mem = GetProcessMem.new
|
72
|
-
status 200
|
73
|
-
return {
|
74
|
-
hostname: `hostname`.to_s.chomp,
|
75
|
-
version: APP_VER,
|
76
|
-
started: APP_STARTED,
|
77
|
-
uptime: (Time.now - APP_STARTED).round(1),
|
78
|
-
counters: $queue.counters,
|
79
|
-
memory_bytes: mem.bytes.to_i,
|
80
|
-
memory_mb: mem.mb.round(0),
|
81
|
-
status: $queue.counts_by_status,
|
82
|
-
workers: $pool.worker_variables,
|
83
|
-
jobs_count: $queue.jobs_count,
|
84
|
-
jobs_queued: $queue.queued_ids,
|
85
|
-
}
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
### SHOW CONFIG
|
90
|
-
|
91
|
-
desc "Show daemon config"
|
92
|
-
get "/config" do
|
93
|
-
status 200
|
94
|
-
return Helpers.get_censored_config
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
desc "List all Jobs params encodings"
|
99
|
-
get "/encodings" do
|
100
|
-
# Get jobs to display
|
101
|
-
encodings = {}
|
102
|
-
jobs = $queue.jobs
|
103
|
-
|
104
|
-
jobs.each do |job|
|
105
|
-
# here = out[job.id] = {}
|
106
|
-
me = encodings[job.id] = {}
|
107
|
-
|
108
|
-
me[:error] = job.error.encoding.to_s unless job.error.nil?
|
109
|
-
me[:status] = job.status.encoding.to_s unless job.status.nil?
|
110
|
-
|
111
|
-
Job::FIELDS.each do |name|
|
112
|
-
value = job.send(name)
|
113
|
-
me[name] = value.encoding.to_s if value.is_a? String
|
114
|
-
end
|
115
|
-
|
116
|
-
job.infos.each do |name, value|
|
117
|
-
me["infos_#{name}"] = value.encoding.to_s if value.is_a? String
|
118
|
-
end
|
119
|
-
|
120
|
-
# # Computed fields
|
121
|
-
# expose :age
|
122
|
-
# expose :exectime
|
123
|
-
end
|
124
|
-
|
125
|
-
encodings
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
### RELOAD CONFIG
|
130
|
-
|
131
|
-
desc "Reload daemon config"
|
132
|
-
post "/config/reload" do
|
133
|
-
if Settings.at(:debug, :allow_reload)==true
|
134
|
-
Settings.reload!
|
135
|
-
status 200
|
136
|
-
return Helpers.get_censored_config
|
137
|
-
else
|
138
|
-
status 403
|
139
|
-
return "Config reload not permitted"
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
68
|
end
|
144
69
|
end
|
145
70
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "get_process_mem"
|
2
|
+
|
3
|
+
module RestFtpDaemon
|
4
|
+
module API
|
5
|
+
class Status < Grape::API
|
6
|
+
|
7
|
+
desc "Show daemon status"
|
8
|
+
get "/" do
|
9
|
+
mem = GetProcessMem.new
|
10
|
+
status 200
|
11
|
+
return {
|
12
|
+
hostname: `hostname`.to_s.chomp,
|
13
|
+
version: APP_VER,
|
14
|
+
|
15
|
+
started: APP_STARTED,
|
16
|
+
uptime: (Time.now - APP_STARTED).round(1),
|
17
|
+
|
18
|
+
memory_bytes: mem.bytes.to_i,
|
19
|
+
memory_mb: mem.mb.round(0),
|
20
|
+
|
21
|
+
stats: $stats.stats,
|
22
|
+
|
23
|
+
jobs_count: $queue.jobs_count,
|
24
|
+
status: $queue.jobs_count_by_status,
|
25
|
+
|
26
|
+
# jobs_finished: $queue.jobs_finished,
|
27
|
+
#rate_by_pool1: $queue.rate_by_pool,
|
28
|
+
rate_by_pool: $queue.rate_by(:pool),
|
29
|
+
rate_by_targethost: $queue.rate_by(:targethost),
|
30
|
+
jobs_by_status: $queue.jobs_by_status,
|
31
|
+
|
32
|
+
#rates_per_host: $queue.rates_per_host,
|
33
|
+
|
34
|
+
workers: $pool.worker_variables,
|
35
|
+
#kpis: $queue.queued_ids,
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Terrific constants
|
2
2
|
APP_NAME = "rest-ftp-daemon"
|
3
3
|
APP_NICK = "rftpd"
|
4
|
-
APP_VER = "0.
|
4
|
+
APP_VER = "0.250.0"
|
5
5
|
|
6
6
|
# Provide default config file information
|
7
7
|
APP_LIB = File.expand_path(File.dirname(__FILE__))
|
@@ -92,6 +92,9 @@ BIND_PORT_LOCALHOST = "127.0.0.1"
|
|
92
92
|
ENV_PRODUCTION = "production"
|
93
93
|
MOUNT_JOBS = "/jobs"
|
94
94
|
MOUNT_BOARD = "/board"
|
95
|
+
MOUNT_STATUS = "/status"
|
96
|
+
MOUNT_DEBUG = "/debug"
|
97
|
+
MOUNT_CONFIG = "/config"
|
95
98
|
|
96
99
|
|
97
100
|
# Notifications
|
data/lib/rest-ftp-daemon/job.rb
CHANGED
@@ -246,6 +246,10 @@ module RestFtpDaemon
|
|
246
246
|
(Time.now - @queued_at).round(2)
|
247
247
|
end
|
248
248
|
|
249
|
+
def targethost
|
250
|
+
get(:target_host)
|
251
|
+
end
|
252
|
+
|
249
253
|
def json_target
|
250
254
|
utf8 @target_method
|
251
255
|
end
|
@@ -314,6 +318,7 @@ module RestFtpDaemon
|
|
314
318
|
raise RestFtpDaemon::JobMissingAttribute unless @target
|
315
319
|
target_uri = expand_url @target
|
316
320
|
set_info :target_uri, target_uri.to_s
|
321
|
+
set_info :target_host, target_uri.host
|
317
322
|
@target_path = Path.new target_uri.path, true
|
318
323
|
|
319
324
|
#puts "@target_path: #{@target_path.inspect}"
|
@@ -485,9 +490,9 @@ module RestFtpDaemon
|
|
485
490
|
set_status JOB_STATUS_DISCONNECTING
|
486
491
|
@finished_at = Time.now
|
487
492
|
|
488
|
-
# Update
|
489
|
-
$
|
490
|
-
$
|
493
|
+
# Update stats
|
494
|
+
$stats.increment :jobs, :finished
|
495
|
+
$stats.add :global, :transferred, @transfer_total
|
491
496
|
end
|
492
497
|
|
493
498
|
def remote_push source, target
|
@@ -652,8 +657,8 @@ module RestFtpDaemon
|
|
652
657
|
end
|
653
658
|
|
654
659
|
# Increment counter for this error
|
655
|
-
$
|
656
|
-
$
|
660
|
+
$stats.increment :errors, error
|
661
|
+
$stats.increment :jobs, :failed
|
657
662
|
|
658
663
|
# Prepare notification if signal given
|
659
664
|
return unless event
|
@@ -35,10 +35,6 @@ module RestFtpDaemon
|
|
35
35
|
@last_id = 0
|
36
36
|
@prefix = Helpers.identifier JOB_IDENT_LEN
|
37
37
|
log_info "JobQueue initialized (prefix: #{@prefix})"
|
38
|
-
|
39
|
-
# Mutex for counters
|
40
|
-
@counters = {}
|
41
|
-
@mutex_counters = Mutex.new
|
42
38
|
end
|
43
39
|
|
44
40
|
def generate_id
|
@@ -48,63 +44,58 @@ module RestFtpDaemon
|
|
48
44
|
prefixed_id @last_id
|
49
45
|
end
|
50
46
|
|
51
|
-
|
52
|
-
@
|
53
|
-
|
54
|
-
@counters[name] += value
|
55
|
-
end
|
47
|
+
def jobs_queued
|
48
|
+
@queues
|
49
|
+
#@queues.map { |status, jobs| jobs.size }
|
56
50
|
end
|
57
51
|
|
58
|
-
|
59
|
-
|
60
|
-
|
52
|
+
# Statistics on average rates
|
53
|
+
def rate_by method_name
|
54
|
+
# Init
|
55
|
+
rates = {}
|
56
|
+
return unless Job.new(0, {}).respond_to? method_name
|
61
57
|
|
62
|
-
|
63
|
-
@
|
64
|
-
|
58
|
+
# Group jobs by method_name
|
59
|
+
jobs_grouped = @jobs.group_by do |job|
|
60
|
+
job.send(method_name)
|
65
61
|
end
|
66
|
-
end
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
# Inside each group, sum up rates for interesting statuses
|
64
|
+
jobs_grouped.map do |group, jobs|
|
65
|
+
# Store their sum
|
66
|
+
rates[group] = rates_by_status (jobs)
|
71
67
|
end
|
72
|
-
end
|
73
68
|
|
74
|
-
|
75
|
-
|
69
|
+
# Return the rate
|
70
|
+
rates
|
76
71
|
end
|
77
72
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@jobs.reject { |job| job.status == JOB_STATUS_QUEUED }
|
82
|
-
|
83
|
-
# Status filtering: only those jobs
|
84
|
-
else
|
85
|
-
@jobs.select { |job| job.status.to_s == status.to_s }
|
86
|
-
|
87
|
-
end
|
73
|
+
# Queue infos
|
74
|
+
def jobs_count
|
75
|
+
@jobs.length
|
88
76
|
end
|
89
77
|
|
90
|
-
def
|
78
|
+
def jobs_by_status
|
91
79
|
statuses = {}
|
92
80
|
@jobs.group_by { |job| job.status }.map { |status, jobs| statuses[status] = jobs.size }
|
93
81
|
statuses
|
94
82
|
end
|
83
|
+
alias jobs_count_by_status jobs_by_status
|
95
84
|
|
96
|
-
def
|
97
|
-
@jobs.
|
85
|
+
def jobs_ids
|
86
|
+
@jobs.collect(&:id)
|
98
87
|
end
|
99
88
|
|
100
|
-
def
|
101
|
-
@
|
89
|
+
def empty?
|
90
|
+
@queue.empty?
|
102
91
|
end
|
103
92
|
|
104
|
-
def
|
105
|
-
@
|
93
|
+
def num_waiting
|
94
|
+
@waiting.size
|
106
95
|
end
|
107
96
|
|
97
|
+
|
98
|
+
# Queue access
|
108
99
|
def find_by_id id, prefixed = false
|
109
100
|
# Build a prefixed id if expected
|
110
101
|
id = prefixed_id(id) if prefixed
|
@@ -115,7 +106,6 @@ module RestFtpDaemon
|
|
115
106
|
@jobs.find { |item| item.id == id }
|
116
107
|
end
|
117
108
|
|
118
|
-
|
119
109
|
def push job
|
120
110
|
# Check that item responds to "priorty" method
|
121
111
|
raise "JobQueue.push: job should respond to priority method" unless job.respond_to? :priority
|
@@ -174,42 +164,52 @@ module RestFtpDaemon
|
|
174
164
|
alias shift pop
|
175
165
|
alias deq pop
|
176
166
|
|
177
|
-
def empty?
|
178
|
-
@queue.empty?
|
179
|
-
end
|
180
|
-
|
181
167
|
def clear
|
182
168
|
@queue.clear
|
183
169
|
end
|
184
170
|
|
185
|
-
|
186
|
-
|
171
|
+
# Jobs acess and searching
|
172
|
+
def jobs_with_status status
|
173
|
+
# No status filter: return all execept queued
|
174
|
+
if status.empty?
|
175
|
+
@jobs.reject { |job| job.status == JOB_STATUS_QUEUED }
|
176
|
+
|
177
|
+
# Status filtering: only those jobs
|
178
|
+
else
|
179
|
+
@jobs.select { |job| job.status.to_s == status.to_s }
|
180
|
+
|
181
|
+
end
|
187
182
|
end
|
188
183
|
|
184
|
+
# Jobs cleanup
|
189
185
|
def expire status, maxage, verbose = false
|
190
186
|
# FIXME: clean both @jobs and @queue
|
191
187
|
# Init
|
192
188
|
return if status.nil? || maxage <= 0
|
193
189
|
|
194
|
-
# Compute oldest
|
195
|
-
|
196
|
-
|
197
|
-
# Verbose output ?
|
198
|
-
log_info "JobQueue.expire \t[#{status}] \tbefore \t[#{before}]" if verbose
|
190
|
+
# Compute oldest limit
|
191
|
+
time_limit = Time.now - maxage.to_i
|
192
|
+
log_info "JobQueue.expire limit [#{time_limit}] status [#{status}]" if verbose
|
199
193
|
|
200
194
|
@mutex.synchronize do
|
201
195
|
# Delete jobs from the queue when they match status and age limits
|
202
196
|
@jobs.delete_if do |job|
|
197
|
+
# log_info "testing job [#{job.id}] updated_at [#{job.updated_at}]"
|
203
198
|
|
204
|
-
# Skip if wrong status, updated_at invalid, or
|
205
|
-
next unless job.status == status
|
199
|
+
# Skip if wrong status, updated_at invalid, or updated since time_limit
|
200
|
+
next unless job.status == status
|
206
201
|
next if job.updated_at.nil?
|
207
|
-
next if job.updated_at
|
202
|
+
next if job.updated_at >= time_limit
|
208
203
|
|
209
204
|
# Ok, we have to clean it up ..
|
210
|
-
log_info "expire [#{status}]
|
211
|
-
log_info "#{LOG_INDENT}unqueued" if @queue.delete(job)
|
205
|
+
log_info "expire [#{status}]: job [#{job.id}] updated_at [#{job.updated_at}]"
|
212
206
|
|
207
|
+
# From any queues, remove it
|
208
|
+
@queues.each do |pool, jobs|
|
209
|
+
log_info "#{LOG_INDENT}unqueued from [#{pool}]" if jobs.delete(job)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Remember we have to delete the original job !
|
213
213
|
true
|
214
214
|
end
|
215
215
|
end
|
@@ -222,11 +222,32 @@ module RestFtpDaemon
|
|
222
222
|
"#{@prefix}.#{id}"
|
223
223
|
end
|
224
224
|
|
225
|
+
def rates_by_status jobs
|
226
|
+
rates = {}
|
227
|
+
|
228
|
+
# Sub-group by status
|
229
|
+
jobs.group_by(&:status).each do |status, jobset|
|
230
|
+
|
231
|
+
# Extract bitrate values
|
232
|
+
bitrates = jobset.collect do |job|
|
233
|
+
job.get(:transfer_bitrate)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Store their sum
|
237
|
+
rates[status] = bitrates.reject(&:nil?).sum
|
238
|
+
# rates["#{status}_ids"] = jobset.collect(&:id).join(', ')
|
239
|
+
end
|
240
|
+
|
241
|
+
# Return rates
|
242
|
+
rates
|
243
|
+
end
|
244
|
+
|
225
245
|
if Settings.newrelic_enabled?
|
226
|
-
add_transaction_tracer :push,
|
227
|
-
add_transaction_tracer :pop,
|
228
|
-
add_transaction_tracer :expire,
|
229
|
-
add_transaction_tracer :
|
246
|
+
add_transaction_tracer :push, category: :task
|
247
|
+
add_transaction_tracer :pop, category: :task
|
248
|
+
add_transaction_tracer :expire, category: :task
|
249
|
+
add_transaction_tracer :rate_by, category: :task
|
250
|
+
add_transaction_tracer :jobs_count_by_status, category: :task
|
230
251
|
end
|
231
252
|
|
232
253
|
end
|
@@ -75,8 +75,9 @@ module RestFtpDaemon
|
|
75
75
|
# Post notification, handle server response / multi-lines
|
76
76
|
log_info "sending #{data}"
|
77
77
|
response = http.post uri.path, data, headers
|
78
|
-
response_lines = response.body.lines
|
79
78
|
|
79
|
+
# Log reponse body
|
80
|
+
response_lines = response.body.lines
|
80
81
|
if response_lines.size > 1
|
81
82
|
human_size = Helpers.format_bytes(response.body.bytesize, "B")
|
82
83
|
log_info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", response_lines
|
@@ -85,8 +86,14 @@ module RestFtpDaemon
|
|
85
86
|
end
|
86
87
|
|
87
88
|
# Handle exceptions
|
89
|
+
rescue Net::OpenTimeout => ex
|
90
|
+
log_error "Net::OpenTimeout: #{ex.inspect}"
|
91
|
+
|
92
|
+
rescue SocketError => ex
|
93
|
+
log_error "SocketError: #{ex.inspect}"
|
94
|
+
|
88
95
|
rescue StandardError => ex
|
89
|
-
log_error "
|
96
|
+
log_error "UNHANDLED EXCEPTION: #{ex.inspect}"
|
90
97
|
end
|
91
98
|
|
92
99
|
def log_context
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
|
3
|
+
# Queue that stores all the Jobs waiting to be processed or fully processed
|
4
|
+
class Stats
|
5
|
+
attr_reader :stats
|
6
|
+
|
7
|
+
if Settings.newrelic_enabled?
|
8
|
+
include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@stats = {}
|
13
|
+
@mutex_stats = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def set group, name, value
|
17
|
+
@mutex_stats.synchronize do
|
18
|
+
@stats[group] ||= {}
|
19
|
+
@stats[group][name] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def get group, name
|
24
|
+
@mutex_stats.synchronize do
|
25
|
+
@stats[group][name] if @stats[group]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add group, name, value
|
30
|
+
@mutex_stats.synchronize do
|
31
|
+
@stats[group] ||= {}
|
32
|
+
@stats[group][name] ||= 0
|
33
|
+
@stats[group][name] += value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def increment group, name
|
38
|
+
add group, name, 1
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
-# coding: utf-8
|
2
|
-
- trans = $queue.counter_get :transferred
|
3
2
|
- info_procs = (Facter.value :processorcount).to_i
|
4
3
|
- info_load = Sys::CPU.load_avg.first.to_f
|
5
4
|
- info_norm = info_procs.zero? ? "N/A" : (100 * info_load / info_procs).round(1)
|
6
|
-
-
|
5
|
+
- info_trans = $stats.get :global, :transferred
|
6
|
+
- info_processed = $stats.get(:global, :processed)
|
7
7
|
- mem = GetProcessMem.new
|
8
8
|
|
9
9
|
|
@@ -35,6 +35,6 @@
|
|
35
35
|
|
36
36
|
.btn-group.btn-group-sm
|
37
37
|
.btn.btn-default.btn-success Transferred
|
38
|
-
.btn.btn-default= Helpers.format_bytes(
|
38
|
+
.btn.btn-default= Helpers.format_bytes(info_trans, "B", 1)
|
39
39
|
|
40
40
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
-# coding: utf-8
|
2
|
-
-
|
2
|
+
- jobs_count_by_status = $queue.jobs_count_by_status
|
3
3
|
- counts_all = $queue.jobs_count
|
4
4
|
- jobs = @paginate.subset
|
5
5
|
|
@@ -9,7 +9,7 @@
|
|
9
9
|
ALL (#{counts_all})
|
10
10
|
|
11
11
|
.btn-group.btn-group-xs.filters
|
12
|
-
-
|
12
|
+
- jobs_count_by_status.each do |status, count|
|
13
13
|
- klass = (status.to_s == @filter) ? "btn-info" : ""
|
14
14
|
%a.btn.btn-default{href: Helpers.dashboard_filter_url(status), class: klass}
|
15
15
|
#{status} (#{count})
|
@@ -50,10 +50,10 @@
|
|
50
50
|
%tbody.jobs
|
51
51
|
= render :dashboard_table, {jobs: jobs}
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
%thead
|
54
|
+
%tr
|
55
|
+
%td{colspan: 14}
|
56
|
+
%br
|
57
57
|
|
58
58
|
- unless jobs.empty?
|
59
59
|
%tbody.jobs
|
@@ -0,0 +1,36 @@
|
|
1
|
+
-# coding: utf-8
|
2
|
+
|
3
|
+
- groups = {pool: "pool", targethost: "target host"}
|
4
|
+
|
5
|
+
|
6
|
+
%h2 Transfer rates
|
7
|
+
|
8
|
+
%table.table.table-striped.table-hover.table-condensed
|
9
|
+
|
10
|
+
- groups.each do |group_by, group_title|
|
11
|
+
- rates_by_status = $queue.rate_by(group_by)
|
12
|
+
|
13
|
+
%thead
|
14
|
+
%tr
|
15
|
+
%th= group_title
|
16
|
+
%th.text-right=JOB_STATUS_FINISHED
|
17
|
+
%th.text-right=JOB_STATUS_UPLOADING
|
18
|
+
|
19
|
+
%tbody
|
20
|
+
- rates_by_status.each do |group, rates|
|
21
|
+
- next if group.nil?
|
22
|
+
|
23
|
+
%tr.info
|
24
|
+
%td= group
|
25
|
+
%td.text-right
|
26
|
+
= Helpers.format_bytes(rates[JOB_STATUS_FINISHED], "bps")
|
27
|
+
%td.text-right
|
28
|
+
= Helpers.format_bytes(rates[JOB_STATUS_UPLOADING], "bps")
|
29
|
+
-# %td= rates.inspect
|
30
|
+
|
31
|
+
%thead
|
32
|
+
%tr
|
33
|
+
%td{colspan: 3}
|
34
|
+
%br
|
35
|
+
|
36
|
+
-# JOB_STATUS_UPLOADING
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-# coding: utf-8
|
2
|
+
%h2 Stats
|
3
|
+
|
4
|
+
%table.table.table-striped.table-hover.table-condensed
|
5
|
+
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
%th group
|
9
|
+
%th name
|
10
|
+
%th.text-right value
|
11
|
+
|
12
|
+
%tbody
|
13
|
+
|
14
|
+
- $stats.stats.each do |group, values|
|
15
|
+
|
16
|
+
- values.each do |name, value|
|
17
|
+
%tr
|
18
|
+
%td= group
|
19
|
+
%td= name
|
20
|
+
%td.text-right= value
|
21
|
+
|
@@ -77,14 +77,6 @@
|
|
77
77
|
.label.label-default.flag.worker-label= job.priority
|
78
78
|
|
79
79
|
%td
|
80
|
-
|
81
80
|
.label.flag.worker-label{class: Helpers.job_runs_style(runs)}= runs
|
82
81
|
|
83
|
-
-#%td
|
84
|
-
- unless job.priority.nil?
|
85
|
-
.label.label-default.flag.worker-label= job.priority
|
86
|
-
|
87
|
-
- unless job.wid.nil?
|
88
|
-
.label.label-warning.flag.worker-label= job.wid
|
89
|
-
|
90
82
|
|
@@ -3,11 +3,13 @@
|
|
3
3
|
|
4
4
|
%table.table.table-striped.table-hover.table-condensed
|
5
5
|
|
6
|
-
%
|
7
|
-
%th token
|
8
|
-
%th value
|
9
|
-
|
10
|
-
- tokens.each do |token, value|
|
6
|
+
%thead
|
11
7
|
%tr
|
12
|
-
%
|
13
|
-
%
|
8
|
+
%th token
|
9
|
+
%th value
|
10
|
+
|
11
|
+
%tbody
|
12
|
+
- tokens.each do |token, value|
|
13
|
+
%tr
|
14
|
+
%td= Helpers.tokenize token
|
15
|
+
%td= Helpers.hide_credentials_from_url value
|
@@ -3,33 +3,35 @@
|
|
3
3
|
|
4
4
|
%table.table.table-striped.table-hover.table-condensed
|
5
5
|
|
6
|
-
%
|
7
|
-
%
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
%thead
|
7
|
+
%tr
|
8
|
+
%th pool
|
9
|
+
%th worker
|
10
|
+
%th status
|
11
|
+
%th job
|
12
|
+
%th.text-right seen
|
12
13
|
|
13
|
-
|
14
|
-
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
%tbody
|
15
|
+
- @worker_variables.each do |vars|
|
16
|
+
- wid = vars[:wid]
|
17
|
+
- status = vars[:status]
|
18
|
+
- alive = $pool.worker_alive? wid
|
19
|
+
- trclass = WORKER_STYLES[status]
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
- unless alive
|
22
|
+
- trclass = "danger"
|
23
|
+
- status = "DEAD"
|
22
24
|
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
%tr{class: trclass.to_s}
|
27
|
+
%td= vars[:pool]
|
28
|
+
%td= wid
|
29
|
+
%td= status
|
30
|
+
%td= vars[:jid]
|
31
|
+
%td.text-right
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
- if vars[:updated_at].is_a? Time
|
34
|
+
- no_news_for = (Time.now - vars[:updated_at]).round(0)
|
35
|
+
= Helpers.formatted_duration no_news_for
|
36
|
+
- else
|
37
|
+
= "?"
|
@@ -86,7 +86,7 @@ module RestFtpDaemon
|
|
86
86
|
worker_status WORKER_STATUS_FINISHED, job
|
87
87
|
|
88
88
|
# Increment total processed jobs count
|
89
|
-
$
|
89
|
+
$stats.increment :global, :processed
|
90
90
|
|
91
91
|
rescue RestFtpDaemon::JobTimeout => ex
|
92
92
|
log_error "JOB TIMED OUT", ex.backtrace
|
data/rest-ftp-daemon.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency "rubocop", "~> 0.32.0"
|
32
32
|
spec.add_development_dependency "pry"
|
33
33
|
|
34
|
-
spec.add_runtime_dependency "thin", "~> 1.6"
|
34
|
+
spec.add_runtime_dependency "thin", "~> 1.6.4"
|
35
35
|
spec.add_runtime_dependency "grape"
|
36
36
|
spec.add_runtime_dependency "grape-entity"
|
37
37
|
spec.add_runtime_dependency "settingslogic"
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe "
|
3
|
+
describe "Debug", feature: true do
|
4
4
|
|
5
|
-
let!(:response) { get
|
5
|
+
let!(:response) { get MOUNT_DEBUG }
|
6
6
|
|
7
|
-
describe "GET
|
7
|
+
describe "GET #{MOUNT_DEBUG}" do
|
8
8
|
|
9
9
|
it "responds successfully" do
|
10
10
|
expect(response.status).to eq 200
|
@@ -2,9 +2,9 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe "Status", feature: true do
|
4
4
|
|
5
|
-
let!(:response) { get
|
5
|
+
let!(:response) { get MOUNT_STATUS }
|
6
6
|
|
7
|
-
describe "GET
|
7
|
+
describe "GET #{MOUNT_DEBUG}" do
|
8
8
|
|
9
9
|
it "responds successfully" do
|
10
10
|
expect(response.status).to eq 200
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-ftp-daemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.250.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno MEDICI
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-05-
|
11
|
+
date: 2016-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 1.6.4
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.6.4
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: grape
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -282,10 +282,13 @@ files:
|
|
282
282
|
- bin/rest-ftp-daemon
|
283
283
|
- config.ru
|
284
284
|
- lib/rest-ftp-daemon.rb
|
285
|
+
- lib/rest-ftp-daemon/api/config.rb
|
285
286
|
- lib/rest-ftp-daemon/api/dashboard.rb
|
287
|
+
- lib/rest-ftp-daemon/api/debug.rb
|
286
288
|
- lib/rest-ftp-daemon/api/job_presenter.rb
|
287
289
|
- lib/rest-ftp-daemon/api/jobs.rb
|
288
290
|
- lib/rest-ftp-daemon/api/root.rb
|
291
|
+
- lib/rest-ftp-daemon/api/status.rb
|
289
292
|
- lib/rest-ftp-daemon/array.rb
|
290
293
|
- lib/rest-ftp-daemon/constants.rb
|
291
294
|
- lib/rest-ftp-daemon/exceptions.rb
|
@@ -305,12 +308,14 @@ files:
|
|
305
308
|
- lib/rest-ftp-daemon/settings.rb
|
306
309
|
- lib/rest-ftp-daemon/static/css/bootstrap.css
|
307
310
|
- lib/rest-ftp-daemon/static/css/main.css
|
311
|
+
- lib/rest-ftp-daemon/stats.rb
|
308
312
|
- lib/rest-ftp-daemon/uri.rb
|
309
313
|
- lib/rest-ftp-daemon/views/dashboard.haml
|
310
|
-
- lib/rest-ftp-daemon/views/dashboard_counters.haml
|
311
314
|
- lib/rest-ftp-daemon/views/dashboard_footer.haml
|
312
315
|
- lib/rest-ftp-daemon/views/dashboard_header.haml
|
313
316
|
- lib/rest-ftp-daemon/views/dashboard_jobs.haml
|
317
|
+
- lib/rest-ftp-daemon/views/dashboard_rates.haml
|
318
|
+
- lib/rest-ftp-daemon/views/dashboard_stats.haml
|
314
319
|
- lib/rest-ftp-daemon/views/dashboard_table.haml
|
315
320
|
- lib/rest-ftp-daemon/views/dashboard_tokens.haml
|
316
321
|
- lib/rest-ftp-daemon/views/dashboard_workers.haml
|
@@ -321,8 +326,8 @@ files:
|
|
321
326
|
- rest-ftp-daemon.gemspec
|
322
327
|
- rest-ftp-daemon.yml.sample
|
323
328
|
- spec/rest-ftp-daemon/features/dashboard_spec.rb
|
329
|
+
- spec/rest-ftp-daemon/features/debug_spec.rb
|
324
330
|
- spec/rest-ftp-daemon/features/jobs_spec.rb
|
325
|
-
- spec/rest-ftp-daemon/features/routes_spec.rb
|
326
331
|
- spec/rest-ftp-daemon/features/status_spec.rb
|
327
332
|
- spec/spec_helper.rb
|
328
333
|
- spec/support/config.yml
|