rest-ftp-daemon 0.72b → 0.85.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,14 @@
1
1
  # Global libs
2
2
  require 'rubygems'
3
3
  require 'json'
4
- require 'securerandom'
5
4
  # require 'celluloid/autostart'
6
5
 
7
6
  # My libs
7
+ require 'rest-ftp-daemon/constants'
8
8
  require 'rest-ftp-daemon/config'
9
9
  require 'rest-ftp-daemon/exceptions'
10
10
  require 'rest-ftp-daemon/common'
11
+ require 'rest-ftp-daemon/helpers'
11
12
  require 'rest-ftp-daemon/uri'
12
13
  require 'rest-ftp-daemon/job_queue'
13
14
  require 'rest-ftp-daemon/worker_pool'
@@ -1,7 +1,5 @@
1
1
  # encoding: UTF-8
2
2
  require 'grape'
3
- SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB" ]
4
-
5
3
 
6
4
  module RestFtpDaemon
7
5
  module API
@@ -45,13 +43,6 @@ module RestFtpDaemon
45
43
  # end
46
44
 
47
45
  helpers do
48
- def format_nice_bytes( number )
49
- return "Ø" if number.nil? || number.zero?
50
- index = ( Math.log( number ) / Math.log( 2 ) ).to_i / 10
51
- converted = number.to_i / ( 1024 ** index )
52
- "#{converted} #{SIZE_UNITS[index]}"
53
- end
54
-
55
46
  def api_error exception
56
47
  {
57
48
  :error => exception.class,
@@ -62,7 +53,7 @@ module RestFtpDaemon
62
53
  end
63
54
 
64
55
  def render name, values={}
65
- template = File.read("#{Settings.app_lib}/views/#{name.to_s}.haml")
56
+ template = File.read("#{APP_LIBS}/views/#{name.to_s}.haml")
66
57
  haml_engine = Haml::Engine.new(template)
67
58
  haml_engine.render(binding, values)
68
59
  end
@@ -6,22 +6,19 @@ module RestFtpDaemon
6
6
 
7
7
  ####### CLASS CONFIG
8
8
 
9
- #include RestFtpDaemon::API::Defaults
10
- #logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
11
-
12
- params do
13
- optional :overwrite, type: Integer, default: false
14
- end
9
+ # params do
10
+ # optional :overwrite, type: Integer, default: false
11
+ # end
15
12
 
16
13
 
17
14
  ####### INITIALIZATION
18
15
 
19
16
  def initialize
20
- $last_worker_id = 0
17
+ #$last_worker_id = 0
21
18
 
22
19
  # Check that Queue and Pool are available
23
20
  raise RestFtpDaemon::MissingQueue unless defined? $queue
24
- raise RestFtpDaemon::MissingQueue unless defined? $pool
21
+ raise RestFtpDaemon::MissingPool unless defined? $pool
25
22
 
26
23
  super
27
24
  end
@@ -31,6 +28,13 @@ module RestFtpDaemon
31
28
 
32
29
  helpers do
33
30
 
31
+ def next_job_id
32
+ @@last_worker_id ||= 0
33
+ @@last_worker_id += 1
34
+ #Helpers.identifier(IDENT_JOB_BYTES)
35
+ #SecureRandom.hex(IDENT_JOB_BYTES)
36
+ end
37
+
34
38
  def threads_with_id job_id
35
39
  $threads.list.select do |thread|
36
40
  next unless thread[:job].is_a? Job
@@ -39,7 +43,7 @@ module RestFtpDaemon
39
43
  end
40
44
 
41
45
  def job_describe job_id
42
- raise RestFtpDaemon::JobNotFound if ($queue.queued_size==0 && $queue.popped_size==0)
46
+ raise RestFtpDaemon::JobNotFound if ($queue.all_size==0)
43
47
 
44
48
  # Find job with this id
45
49
  found = $queue.all.select { |job| job.id == job_id }.first
@@ -136,18 +140,16 @@ module RestFtpDaemon
136
140
  begin
137
141
  # Extract params
138
142
  request.body.rewind
139
- params = JSON.parse request.body.read
143
+ params = JSON.parse(request.body.read, symbolize_names: true)
140
144
 
141
145
  # Create a new job
142
- job_id = $last_worker_id += 1
146
+ # job_id = $last_worker_id += 1
147
+ job_id = next_job_id
143
148
  job = Job.new(job_id, params)
144
149
 
145
- # And psuh it to the queue
150
+ # And push it to the queue
146
151
  $queue.push job
147
152
 
148
- # Later: start it asynchronously
149
- #job.future.process
150
-
151
153
  rescue JSON::ParserError => exception
152
154
  status 406
153
155
  api_error exception
@@ -165,9 +167,9 @@ module RestFtpDaemon
165
167
 
166
168
  protected
167
169
 
168
- def progname
169
- "API::Jobs"
170
- end
170
+ # def progname
171
+ # "API::Jobs"
172
+ # end
171
173
 
172
174
  end
173
175
  end
@@ -10,11 +10,11 @@ module RestFtpDaemon
10
10
 
11
11
 
12
12
  ####### CLASS CONFIG
13
- include RestFtpDaemon::API::Defaults
14
- logger ActiveSupport::Logger.new(Settings.logs.api, 'daily') unless Settings.logs.api.nil?
15
- #add_swagger_documentation
16
13
 
14
+ include RestFtpDaemon::API::Defaults
15
+ logger RestFtpDaemon::Logger.new(:api, "API")
17
16
  mount RestFtpDaemon::API::Jobs => '/jobs'
17
+ #add_swagger_documentation
18
18
  # mount RestFtpDaemon::API::Workers => '/workers'
19
19
 
20
20
 
@@ -22,7 +22,7 @@ module RestFtpDaemon
22
22
 
23
23
  helpers do
24
24
  def info message, level = 0
25
- Root.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Root")
25
+ Root.logger.info(message, level)
26
26
  end
27
27
 
28
28
  def job_list_by_status
@@ -32,7 +32,6 @@ module RestFtpDaemon
32
32
  statuses[item.get_status] ||= 0
33
33
  statuses[item.get_status] +=1
34
34
  end
35
-
36
35
  statuses
37
36
  end
38
37
 
@@ -42,12 +41,9 @@ module RestFtpDaemon
42
41
  ####### INITIALIZATION
43
42
 
44
43
  def initialize
45
- $last_worker_id = 0
46
-
47
44
  # Check that Queue and Pool are available
48
45
  raise RestFtpDaemon::MissingQueue unless defined? $queue
49
46
  raise RestFtpDaemon::MissingQueue unless defined? $pool
50
-
51
47
  super
52
48
  end
53
49
 
@@ -56,10 +52,7 @@ module RestFtpDaemon
56
52
 
57
53
  # Server global status
58
54
  get '/' do
59
- # Prepare data
60
- @jobs_all = $queue.all
61
- #@jobs_all_size = $queue.all_size
62
- #@jobs_all = $queue.all_size
55
+ info "GET /"
63
56
 
64
57
  # Initialize UsageWatch
65
58
  Facter.loadfacts
@@ -68,24 +61,42 @@ module RestFtpDaemon
68
61
  @info_ipaddr = Facter.value(:ipaddress)
69
62
  @info_memfree = Facter.value(:memoryfree)
70
63
 
71
-
72
64
  # Compute normalized load
73
- # puts "info_procs: #{info_procs}"
74
65
  if @info_procs.zero?
75
66
  @info_norm = "N/A"
76
67
  else
77
68
  @info_norm = (100 * @info_load / @info_procs).round(1)
78
69
  end
79
70
 
80
- # Compute total transferred
81
- @total_transferred = 0
82
- @jobs_all.each do |job|
83
- sent = job.get(:file_sent)
84
- @total_transferred += sent unless sent.nil?
71
+ # Jobs to display
72
+ all_jobs_in_queue = $queue.all
73
+
74
+ if params["only"].nil? || params["only"].blank?
75
+ @only = nil
76
+ else
77
+ @only = params["only"].to_sym
85
78
  end
86
79
 
80
+ case @only
81
+ when nil
82
+ @jobs = all_jobs_in_queue
83
+ when :queue
84
+ @jobs = $queue.queued
85
+ else
86
+ @jobs = $queue.by_status (@only)
87
+ end
88
+
89
+ # Count jobs for each status
90
+ @counts = {}
91
+ grouped = all_jobs_in_queue.group_by { |job| job.get(:status) }
92
+ grouped.each do |status, jobs|
93
+ @counts[status] = jobs.size
94
+ end
95
+
96
+ # Get workers status
97
+ @gworker_statuses = $pool.get_worker_statuses
98
+
87
99
  # Compile haml template
88
- @name = "Test"
89
100
  output = render :dashboard
90
101
 
91
102
  # Send response
@@ -99,14 +110,14 @@ module RestFtpDaemon
99
110
 
100
111
  # Server global status
101
112
  get '/status' do
102
- info "GET /"
113
+ info "GET /status"
103
114
  status 200
104
115
  return {
105
116
  hostname: `hostname`.chomp,
106
- version: Settings.app_ver,
117
+ version: APP_VER,
107
118
  config: Settings.to_hash,
108
- started: Settings.app_started,
109
- uptime: (Time.now - Settings.app_started).round(1),
119
+ started: APP_STARTED,
120
+ uptime: (Time.now - APP_STARTED).round(1),
110
121
  status: job_list_by_status,
111
122
  queue_size: $queue.all_size,
112
123
  jobs_queued: $queue.queued.collect(&:id),
@@ -117,7 +128,8 @@ module RestFtpDaemon
117
128
 
118
129
  # Server test
119
130
  get '/debug' do
120
- info "GET /debug/"
131
+ info "GET /debug"
132
+
121
133
  begin
122
134
  raise RestFtpDaemon::DummyException
123
135
  rescue RestFtpDaemon::RestFtpDaemonException => exception
@@ -4,45 +4,10 @@ module RestFtpDaemon
4
4
 
5
5
  protected
6
6
 
7
- def initialize
8
- # Logger
9
- @logger = ActiveSupport::Logger.new Settings.logs.workers, 'daily' unless Settings.logs.workers.nil?
10
- end
11
-
12
- def id
13
- end
14
-
15
- def progname
16
- end
7
+ # FIXME: should be moved to class itself to get rid of this parent class
17
8
 
18
9
  def info message, level = 0
19
- # progname = "Job [#{id}]" unless id.nil?
20
- # progname = "Worker [#{id}]" unless worker_id.nil?
21
- @logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname) unless @logger.nil?
22
- end
23
-
24
- def notify signal, error = 0, status = {}
25
- # Skip is not callback URL defined
26
- url = get :notify
27
- if url.nil?
28
- info "Skipping notification (no valid url provided) sig[#{signal}] e[#{error}] s#{status.inspect}"
29
- return
30
- end
31
-
32
- # Build notification
33
- n = RestFtpDaemon::Notification.new
34
- n.job_id = id
35
- n.url = url
36
- n.signal = signal
37
- n.error = error.inspect
38
- n.status = status
39
-
40
- # Now, send the notification
41
- info "Queuing notification key[#{n.key}] sig[#{signal}] url[#{url}]"
42
- Thread.new(n) do |thread|
43
- n.notify
44
- end
45
-
10
+ @logger.info(message, level) unless @logger.nil?
46
11
  end
47
12
 
48
13
  end
@@ -1,26 +1,21 @@
1
- require 'settingslogic'
2
-
3
- # Terrific assertions
4
- #raise "config.rb: APP_ROOT is not defined" unless defined? APP_ROOT
5
- APP_NAME = "rest-ftp-daemon"
6
- APP_CONF = "/etc/#{APP_NAME}.yml"
7
- APP_DEV = ARGV.include?("development") ? true : false
1
+ # Try to load Settingslogic
2
+ begin
3
+ require "settingslogic"
4
+ rescue LoadError
5
+ raise "config.rb warning: Settingslogic is needed to provide configuration values to the Gemspec file"
6
+ end
8
7
 
8
+ # Configuration class
9
9
  class Settings < Settingslogic
10
10
  # Read configuration
11
- source (File.exists? APP_CONF) ? APP_CONF : Hash.new
12
- namespace (APP_DEV ? "development" : "production")
11
+ namespace (defined?(APP_ENV) ? APP_ENV : "production")
12
+ source ((File.exists? APP_CONF) ? APP_CONF : Hash.new)
13
13
  suppress_errors true
14
14
 
15
- # Some constants
16
- self[:dev] = APP_DEV
17
- self[:app_name] = APP_NAME
18
- self[:app_lib] = File.expand_path File.dirname(__FILE__)
19
- self[:app_ver] = "0.72b"
20
- self[:app_started] = Time.now
21
- self[:default_trim_progname] = "18"
15
+ # Compute my PID filename
16
+ def pidfile
17
+ self["pidfile"] || "/tmp/#{APP_NAME}.port#{self['port'].to_s}.pid"
18
+ end
22
19
 
23
- # Some defaults
24
- self[:default_chunk_size] = "1000000"
25
- self[:default_notify_size] = "10000000"
26
20
  end
21
+
@@ -0,0 +1,21 @@
1
+ # Terrific constants
2
+ APP_NAME = "rest-ftp-daemon"
3
+ APP_CONF = "/etc/#{APP_NAME}.yml"
4
+ APP_VER = "0.85.2"
5
+
6
+
7
+ # Some global constants
8
+ IDENT_NOTIF_LEN = 4
9
+ IDENT_RANDOM_LEN = 8
10
+
11
+
12
+ # Some defaults
13
+ DEFAULT_CONNECT_TIMEOUT_SEC = 30
14
+ DEFAULT_UPDATE_EVERY_KB = 2048
15
+ DEFAULT_WORKERS = 1
16
+ DEFAULT_LOGS_PROGNAME_TRIM = 9
17
+
18
+
19
+ # Initialize markers
20
+ APP_STARTED = Time.now
21
+ APP_LIBS = File.dirname(__FILE__)
@@ -1,33 +1,20 @@
1
1
  module RestFtpDaemon
2
2
 
3
- class RestFtpDaemonException < StandardError; end
4
-
5
- class DummyException < RestFtpDaemonException; end
6
-
7
- class MissingQueue < RestFtpDaemonException; end
8
- class MissingPool < RestFtpDaemonException; end
9
-
10
-
11
- class RequestSourceMissing < RestFtpDaemonException; end
12
- class RequestSourceNotFound < RestFtpDaemonException; end
13
- class RequestTargetMissing < RestFtpDaemonException; end
14
- class RequestTargetScheme < RestFtpDaemonException; end
15
-
16
- class JobPrerequisitesNotMet < RestFtpDaemonException; end
17
-
18
- class JobNotFound < RestFtpDaemonException; end
19
- class JobSourceMissing < RestFtpDaemonException; end
20
- class JobSourceNotFound < RestFtpDaemonException; end
21
- class JobTargetMissing < RestFtpDaemonException; end
22
- class JobTargetUnsupported < RestFtpDaemonException; end
23
- class JobTargetUnparseable < RestFtpDaemonException; end
24
- #class JobTargetPermission < RestFtpDaemonException; end
25
- class JobTargetFileExists < RestFtpDaemonException; end
26
- class JobPrerequisitesNotMet < RestFtpDaemonException; end
27
-
28
-
29
- class NotificationMissingUrl < RestFtpDaemonException; end
30
- class NotificationMissingSignal < RestFtpDaemonException; end
31
-
3
+ class RestFtpDaemonException < StandardError; end
4
+
5
+ class DummyException < RestFtpDaemonException; end
6
+
7
+ class MissingQueue < RestFtpDaemonException; end
8
+ class MissingPool < RestFtpDaemonException; end
9
+
10
+ class JobException < RestFtpDaemonException; end
11
+ class JobNotFound < RestFtpDaemonException; end
12
+ class JobAssertionFailed < RestFtpDaemonException; end
13
+ class JobMissingAttribute < RestFtpDaemonException; end
14
+ class JobSourceNotFound < RestFtpDaemonException; end
15
+ class JobTargetUnsupported < RestFtpDaemonException; end
16
+ class JobTargetUnparseable < RestFtpDaemonException; end
17
+ class JobTargetFileExists < RestFtpDaemonException; end
18
+ class JobTooManyOpenFiles < RestFtpDaemonException; end
32
19
 
33
20
  end
@@ -0,0 +1,55 @@
1
+ require 'securerandom'
2
+
3
+
4
+ module RestFtpDaemon
5
+ class Helpers
6
+
7
+ def self.format_bytes number, unit=""
8
+ return "&Oslash;" if number.nil? || number.zero?
9
+
10
+ units = ["", "k", "M", "G", "T", "P" ]
11
+ index = ( Math.log( number ) / Math.log( 2 ) ).to_i / 10
12
+ converted = number.to_i / ( 1024 ** index )
13
+ "#{converted} #{units[index]}#{unit}"
14
+ end
15
+
16
+ def self.identifier len
17
+ rand(36**len).to_s(36)
18
+ end
19
+ #SecureRandom.hex(IDENT_JOB_BYTES)
20
+
21
+ def self.tokenize(item)
22
+ "[#{item}]"
23
+ end
24
+
25
+ def self.local_port_used? port
26
+ ip = '0.0.0.0'
27
+ timeout = 1
28
+ begin
29
+ Timeout::timeout(timeout) do
30
+ begin
31
+ TCPSocket.new(ip, port).close
32
+ true
33
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
34
+ false
35
+ rescue Errno::EADDRNOTAVAIL
36
+ "Settings.local_port_used: Errno::EADDRNOTAVAIL"
37
+ end
38
+ end
39
+ rescue Timeout::Error
40
+ false
41
+ end
42
+ end
43
+
44
+ # def snakecase
45
+ # gsub(/::/, '/').
46
+ # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
47
+ # gsub(/([a-z\d])([A-Z])/,'\1_\2').
48
+ # tr('-', '_').
49
+ # gsub(/\s/, '_').
50
+ # gsub(/__+/, '_').
51
+ # downcase
52
+ # end
53
+
54
+ end
55
+ end