rest-ftp-daemon 0.305.0 → 0.306.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 +1 -1
- data/README.md +1 -1
- data/lib/rest-ftp-daemon.rb +2 -0
- data/lib/rest-ftp-daemon/api/config.rb +2 -2
- data/lib/rest-ftp-daemon/api/dashboard.rb +9 -3
- data/lib/rest-ftp-daemon/api/root.rb +2 -3
- data/lib/rest-ftp-daemon/help_apis.rb +12 -0
- data/lib/rest-ftp-daemon/help_views.rb +74 -0
- data/lib/rest-ftp-daemon/helpers.rb +10 -103
- data/lib/rest-ftp-daemon/job.rb +7 -6
- data/lib/rest-ftp-daemon/job_queue.rb +2 -1
- data/lib/rest-ftp-daemon/notification.rb +2 -1
- data/lib/rest-ftp-daemon/paginate.rb +2 -1
- data/lib/rest-ftp-daemon/remote.rb +6 -0
- data/lib/rest-ftp-daemon/remote_ftp.rb +1 -1
- data/lib/rest-ftp-daemon/remote_sftp.rb +1 -1
- data/lib/rest-ftp-daemon/static/css/main.css +11 -1
- data/lib/rest-ftp-daemon/views/dashboard.haml +12 -7
- data/lib/rest-ftp-daemon/views/dashboard_counters.haml +1 -1
- data/lib/rest-ftp-daemon/views/dashboard_footer.haml +8 -7
- data/lib/rest-ftp-daemon/views/dashboard_header.haml +2 -2
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +2 -2
- data/lib/rest-ftp-daemon/views/dashboard_rates.haml +7 -11
- data/lib/rest-ftp-daemon/views/dashboard_table.haml +10 -11
- data/lib/rest-ftp-daemon/views/dashboard_tokens.haml +4 -13
- data/lib/rest-ftp-daemon/views/dashboard_workers.haml +4 -5
- data/lib/rest-ftp-daemon/workers/conchita.rb +0 -5
- data/lib/rest-ftp-daemon/workers/reporter.rb +0 -5
- data/lib/rest-ftp-daemon/workers/transfer.rb +0 -5
- data/rest-ftp-daemon.gemspec +1 -1
- data/spec/rest-ftp-daemon/features/jobs_spec.rb +1 -1
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6edd22943111a9e497528ea36e6890af33f9ef47
         | 
| 4 | 
            +
              data.tar.gz: caeb1289c6f8e878fe926e90d9c8b693c80f26e0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 1c4e9793292e1bd0020fd96f9f9d0770200b04aaafe4a8630833021545baa3a4b10ca1d480c52fa5cb80bfa84a1c162f48febdf4995f0cccc2a68baebd76c440
         | 
| 7 | 
            +
              data.tar.gz: f269efbb1a414c0610a2bbdbaba41b641a75e1b97e84c270c1dbb1d1590b871f828d06d538a91cdd3af6ec441a3d7c9ac68ec76485063bbb214b5b2afd3da66b
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    
    
        data/lib/rest-ftp-daemon.rb
    CHANGED
    
    | @@ -18,6 +18,8 @@ require_relative "shared/patch_haml" | |
| 18 18 | 
             
            # Project's libs
         | 
| 19 19 | 
             
            require_relative "rest-ftp-daemon/constants"
         | 
| 20 20 | 
             
            require_relative "rest-ftp-daemon/exceptions"
         | 
| 21 | 
            +
            require_relative "rest-ftp-daemon/help_views"
         | 
| 22 | 
            +
            require_relative "rest-ftp-daemon/help_apis"
         | 
| 21 23 | 
             
            require_relative "rest-ftp-daemon/helpers"
         | 
| 22 24 | 
             
            require_relative "rest-ftp-daemon/metrics"
         | 
| 23 25 | 
             
            require_relative "rest-ftp-daemon/paginate"
         | 
| @@ -8,7 +8,7 @@ module RestFtpDaemon | |
| 8 8 | 
             
                  desc "Show daemon config"
         | 
| 9 9 | 
             
                  get "/" do
         | 
| 10 10 | 
             
                    status 200
         | 
| 11 | 
            -
                    return  | 
| 11 | 
            +
                    return get_censored_config
         | 
| 12 12 | 
             
                  end
         | 
| 13 13 |  | 
| 14 14 | 
             
                  desc "Reload daemon config"
         | 
| @@ -16,7 +16,7 @@ module RestFtpDaemon | |
| 16 16 | 
             
                    if Conf[:allow_reload]==true
         | 
| 17 17 | 
             
                      Conf.reload!
         | 
| 18 18 | 
             
                      status 200
         | 
| 19 | 
            -
                      return  | 
| 19 | 
            +
                      return get_censored_config
         | 
| 20 20 | 
             
                    else
         | 
| 21 21 | 
             
                      status 403
         | 
| 22 22 | 
             
                      return "Config reload not permitted"
         | 
| @@ -11,11 +11,17 @@ module RestFtpDaemon | |
| 11 11 | 
             
                  ### HELPERS
         | 
| 12 12 | 
             
                  helpers do
         | 
| 13 13 | 
             
                    def render name, values={}
         | 
| 14 | 
            +
                      # Prepare template engine
         | 
| 14 15 | 
             
                      template = File.read("#{Conf.app_libs}/views/#{name}.haml")
         | 
| 15 | 
            -
             | 
| 16 16 | 
             
                      haml_engine = Haml::Engine.new(template, encoding: Encoding::UTF_8)
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                       | 
| 17 | 
            +
             | 
| 18 | 
            +
                      # Inject helpers
         | 
| 19 | 
            +
                      scope_object = eval("self", binding)
         | 
| 20 | 
            +
                      scope_object.extend RestFtpDaemon::HelpViews
         | 
| 21 | 
            +
                      scope_object.extend RestFtpDaemon::Helpers
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                      # Do the rendering !
         | 
| 24 | 
            +
                      haml_engine.render(scope_object, values)
         | 
| 19 25 | 
             
                    end
         | 
| 20 26 |  | 
| 21 27 | 
             
                    def build_dashboard filter = ''
         | 
| @@ -5,8 +5,6 @@ module RestFtpDaemon | |
| 5 5 | 
             
                class Root < Grape::API
         | 
| 6 6 | 
             
                  include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 8 | 
             
                  ### LOGGING & HELPERS
         | 
| 11 9 | 
             
                  helpers do
         | 
| 12 10 | 
             
                    def log_prefix
         | 
| @@ -35,6 +33,7 @@ module RestFtpDaemon | |
| 35 33 | 
             
                  end
         | 
| 36 34 |  | 
| 37 35 | 
             
                  ### CLASS CONFIG
         | 
| 36 | 
            +
                  helpers RestFtpDaemon::HelpApis
         | 
| 38 37 | 
             
                  helpers BmcDaemonLib::LoggerHelper
         | 
| 39 38 | 
             
                  logger BmcDaemonLib::LoggerPool.instance.get :api
         | 
| 40 39 | 
             
                  do_not_route_head!
         | 
| @@ -70,7 +69,7 @@ module RestFtpDaemon | |
| 70 69 |  | 
| 71 70 | 
             
                  ### ENDPOINTS
         | 
| 72 71 | 
             
                  get "/" do
         | 
| 73 | 
            -
                    redirect  | 
| 72 | 
            +
                    redirect dashboard_url()
         | 
| 74 73 | 
             
                  end
         | 
| 75 74 |  | 
| 76 75 | 
             
                end
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            module RestFtpDaemon
         | 
| 2 | 
            +
              module HelpViews
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def dashboard_job_url job
         | 
| 5 | 
            +
                  "#{MOUNT_JOBS}/#{job.id}" if job.respond_to? :id
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def job_runs_style runs
         | 
| 9 | 
            +
                  return  "label-outline"   if runs <= 0
         | 
| 10 | 
            +
                  return  "label-info"      if runs == 1
         | 
| 11 | 
            +
                  return  "label-warning"   if runs == 2
         | 
| 12 | 
            +
                  return  "label-danger"    if runs > 2
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def job_method_label method
         | 
| 16 | 
            +
                  return if method.nil?
         | 
| 17 | 
            +
                  klass = case method
         | 
| 18 | 
            +
                  when JOB_METHOD_FILE
         | 
| 19 | 
            +
                    "label-primary"
         | 
| 20 | 
            +
                  when JOB_METHOD_FTP
         | 
| 21 | 
            +
                    "label-warning"
         | 
| 22 | 
            +
                  when JOB_METHOD_FTPS
         | 
| 23 | 
            +
                    "label-success"
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    "label-default"
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  "<div class=\"transfer-method label #{klass}\">#{method.upcase}</div>"
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def datetime_short datetime
         | 
| 31 | 
            +
                  # return param.class
         | 
| 32 | 
            +
                  return "-" if datetime.nil?
         | 
| 33 | 
            +
                  return "?" unless datetime.respond_to? :to_date
         | 
| 34 | 
            +
                  return datetime.to_datetime.strftime("%H:%M:%S") if datetime.to_date == Time.now.to_date
         | 
| 35 | 
            +
                  datetime.to_datetime.strftime("%d/%m %H:%M:%S")
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def formatted_duration duration
         | 
| 39 | 
            +
                  out = []
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  hours = duration / (60 * 60)
         | 
| 42 | 
            +
                  minutes = (duration / 60) % 60
         | 
| 43 | 
            +
                  seconds = duration % 60
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  out << "#{hours}h" if hours > 0
         | 
| 46 | 
            +
                  out << "#{minutes}mn" if (minutes > 0) || (hours > 0)
         | 
| 47 | 
            +
                  out << "#{seconds}s"
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  out.join(" ")
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def remove_credentials path
         | 
| 53 | 
            +
                  return unless path.is_a? String
         | 
| 54 | 
            +
                  path.sub(/([a-z]+:\/\/[^\/]+):[^\/]+\@/, '\1@')
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def token_to_label name, url = ''
         | 
| 58 | 
            +
                  clean_url = remove_credentials url
         | 
| 59 | 
            +
                  sprintf '<span class="token" title="%s">%s</span>', clean_url, name
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def token_highlight path
         | 
| 63 | 
            +
                  return unless path.is_a? String
         | 
| 64 | 
            +
                  path.gsub(/\[([^\[]+)\]/, token_to_label('\1'))
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def text_or_empty text
         | 
| 68 | 
            +
                  return "-" if text.nil? || text.empty?
         | 
| 69 | 
            +
                  text
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
             | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
| @@ -1,14 +1,7 @@ | |
| 1 1 | 
             
            module RestFtpDaemon
         | 
| 2 | 
            -
               | 
| 2 | 
            +
              module Helpers
         | 
| 3 3 |  | 
| 4 | 
            -
                def  | 
| 5 | 
            -
                  config = Conf.to_hash
         | 
| 6 | 
            -
                  config[:users] = Conf[:users].keys if Conf[:users]
         | 
| 7 | 
            -
                  config[:endpoints] = Conf[:endpoints].keys if Conf[:endpoints]
         | 
| 8 | 
            -
                  config
         | 
| 9 | 
            -
                end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                def self.format_bytes number, unit="", decimals = 0
         | 
| 4 | 
            +
                def format_bytes number, unit="", decimals = 0
         | 
| 12 5 | 
             
                  return "Ø" if number.nil? || number.to_f.zero?
         | 
| 13 6 |  | 
| 14 7 | 
             
                  units = ["", "k", "M", "G", "T", "P" ]
         | 
| @@ -20,110 +13,24 @@ module RestFtpDaemon | |
| 20 13 | 
             
                  "#{truncated} #{units[index]}#{unit}"
         | 
| 21 14 | 
             
                end
         | 
| 22 15 |  | 
| 23 | 
            -
                def  | 
| 24 | 
            -
                  return "-" if text.nil? || text.empty?
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                  text
         | 
| 27 | 
            -
                end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                def self.identifier len
         | 
| 16 | 
            +
                def identifier len
         | 
| 30 17 | 
             
                  rand(36**len).to_s(36)
         | 
| 31 18 | 
             
                end
         | 
| 32 19 |  | 
| 33 | 
            -
                def  | 
| 20 | 
            +
                def tokenize item
         | 
| 34 21 | 
             
                  return unless item.is_a? String
         | 
| 35 22 | 
             
                  "[#{item}]"
         | 
| 36 23 | 
             
                end
         | 
| 37 24 |  | 
| 38 | 
            -
                def  | 
| 39 | 
            -
                   | 
| 40 | 
            -
                  path.gsub(/\[([^\[]+)\]/, '<span class="token">\1</span>')
         | 
| 41 | 
            -
                end
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                def self.extract_filename path
         | 
| 44 | 
            -
                  return unless path.is_a? String
         | 
| 45 | 
            -
                  # match everything that's after a slash at the end of the string
         | 
| 46 | 
            -
                  m = path.match(/\/?([^\/]+)$/)
         | 
| 47 | 
            -
                  return m[1] unless m.nil?
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                def self.extract_dirname path
         | 
| 51 | 
            -
                  return unless path.is_a? String
         | 
| 52 | 
            -
                  # match all the beginning of the string up to the last slash
         | 
| 53 | 
            -
                  m = path.match(/^(.*)\/[^\/]*$/)
         | 
| 54 | 
            -
                  return "/#{m[1]}" unless m.nil?
         | 
| 55 | 
            -
                end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                def self.extract_parent path
         | 
| 58 | 
            -
                  return unless path.is_a? String
         | 
| 59 | 
            -
                  m = path.match(/^(.*)\/([^\/]+)\/?$/)
         | 
| 60 | 
            -
                  return m[1], m[2] unless m.nil?
         | 
| 61 | 
            -
                end
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                def self.job_method_label method
         | 
| 64 | 
            -
                  return if method.nil?
         | 
| 65 | 
            -
                  klass = case method
         | 
| 66 | 
            -
                  when JOB_METHOD_FILE
         | 
| 67 | 
            -
                    "label-primary"
         | 
| 68 | 
            -
                  when JOB_METHOD_FTP
         | 
| 69 | 
            -
                    "label-warning"
         | 
| 70 | 
            -
                  when JOB_METHOD_FTPS
         | 
| 71 | 
            -
                    "label-success"
         | 
| 72 | 
            -
                  else
         | 
| 73 | 
            -
                    "label-default"
         | 
| 74 | 
            -
                  end
         | 
| 75 | 
            -
                  "<div class=\"transfer-method label #{klass}\">#{method.upcase}</div>"
         | 
| 76 | 
            -
                end
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                def self.job_runs_style runs
         | 
| 79 | 
            -
                  return  "label-outline"     if runs <= 0
         | 
| 80 | 
            -
                  return  "label-info"  if runs == 1
         | 
| 81 | 
            -
                  return  "label-warning"  if runs == 2
         | 
| 82 | 
            -
                  return  "label-danger"   if runs > 2
         | 
| 25 | 
            +
                def dashboard_url filter = ''
         | 
| 26 | 
            +
                  "#{MOUNT_BOARD}/#{filter}"
         | 
| 83 27 | 
             
                end
         | 
| 84 28 |  | 
| 85 29 | 
             
                # Dates and times: date with time generator
         | 
| 86 | 
            -
                def  | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
                end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                def self.datetime_short datetime
         | 
| 93 | 
            -
                  # return param.class
         | 
| 94 | 
            -
                  return "-" if datetime.nil?
         | 
| 95 | 
            -
                  return "?" unless datetime.respond_to? :to_date
         | 
| 96 | 
            -
                  return datetime.to_datetime.strftime("%H:%M:%S") if datetime.to_date == Time.now.to_date
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                  datetime.to_datetime.strftime("%d/%m %H:%M:%S")
         | 
| 99 | 
            -
                end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                def self.hide_credentials_from_url path
         | 
| 102 | 
            -
                  return unless path.is_a? String
         | 
| 103 | 
            -
                  path.sub(/([a-z]+:\/\/[^\/]+):[^\/]+\@/, '\1@')
         | 
| 104 | 
            -
                end
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                def self.formatted_duration duration
         | 
| 107 | 
            -
                out = []
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                hours = duration / (60 * 60)
         | 
| 110 | 
            -
                minutes = (duration / 60) % 60
         | 
| 111 | 
            -
                seconds = duration % 60
         | 
| 112 | 
            -
             | 
| 113 | 
            -
                out << "#{hours}h" if hours > 0
         | 
| 114 | 
            -
                out << "#{minutes}mn" if (minutes > 0) || (hours > 0)
         | 
| 115 | 
            -
                out << "#{seconds}s"
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                out.join(" ")
         | 
| 118 | 
            -
                end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                def self.dashboard_job_url job
         | 
| 121 | 
            -
                  "#{MOUNT_JOBS}/#{job.id}" if job.respond_to? :id
         | 
| 122 | 
            -
                end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                def self.dashboard_filter_url filter = ''
         | 
| 125 | 
            -
                  "#{MOUNT_BOARD}/#{filter}"
         | 
| 126 | 
            -
                end
         | 
| 30 | 
            +
                # def datetime_full datetime
         | 
| 31 | 
            +
                #   return "-"  if datetime.nil?
         | 
| 32 | 
            +
                #   datetime.to_datetime.strftime("%d.%m.%Y %H:%M:%S")
         | 
| 33 | 
            +
                # end
         | 
| 127 34 |  | 
| 128 35 | 
             
              end
         | 
| 129 36 | 
             
            end
         | 
    
        data/lib/rest-ftp-daemon/job.rb
    CHANGED
    
    | @@ -5,6 +5,7 @@ module RestFtpDaemon | |
| 5 5 | 
             
              class Job
         | 
| 6 6 | 
             
                include BmcDaemonLib::LoggerHelper
         | 
| 7 7 | 
             
                include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
         | 
| 8 | 
            +
                include Helpers
         | 
| 8 9 |  | 
| 9 10 | 
             
                # Class constants
         | 
| 10 11 | 
             
                FIELDS = [:source, :target, :label, :priority, :pool, :notify, :overwrite, :mkdir, :tempfile]
         | 
| @@ -286,7 +287,7 @@ module RestFtpDaemon | |
| 286 287 | 
             
                  newpath = path.clone
         | 
| 287 288 | 
             
                  vectors.each do |from, to|
         | 
| 288 289 | 
             
                    next if to.to_s.blank?
         | 
| 289 | 
            -
                    newpath.gsub!  | 
| 290 | 
            +
                    newpath.gsub! tokenize(from), to
         | 
| 290 291 | 
             
                  end
         | 
| 291 292 |  | 
| 292 293 | 
             
                  # Ensure result does not contain tokens after replacement
         | 
| @@ -507,7 +508,7 @@ module RestFtpDaemon | |
| 507 508 | 
             
                  # Compute temp target name
         | 
| 508 509 | 
             
                  tempname = nil
         | 
| 509 510 | 
             
                  if @tempfile
         | 
| 510 | 
            -
                    tempname = "#{target.name}.temp-#{ | 
| 511 | 
            +
                    tempname = "#{target.name}.temp-#{identifier(JOB_TEMPFILE_LEN)}"
         | 
| 511 512 | 
             
                    log_debug "Job.remote_push tempname [#{tempname}]"
         | 
| 512 513 | 
             
                  end
         | 
| 513 514 |  | 
| @@ -515,7 +516,7 @@ module RestFtpDaemon | |
| 515 516 | 
             
                  if @overwrite
         | 
| 516 517 | 
             
                    @remote.remove! target
         | 
| 517 518 | 
             
                  elsif size = @remote.present?(target)
         | 
| 518 | 
            -
                    log_debug "Job.remote_push existing (#{ | 
| 519 | 
            +
                    log_debug "Job.remote_push existing (#{format_bytes size, 'B'})"
         | 
| 519 520 | 
             
                    raise RestFtpDaemon::JobTargetFileExists
         | 
| 520 521 | 
             
                  end
         | 
| 521 522 |  | 
| @@ -564,9 +565,9 @@ module RestFtpDaemon | |
| 564 565 | 
             
                    # Log progress
         | 
| 565 566 | 
             
                    stack = []
         | 
| 566 567 | 
             
                    stack << "#{percent0} %"
         | 
| 567 | 
            -
                    stack << ( | 
| 568 | 
            -
                    stack << ( | 
| 569 | 
            -
                    stack << ( | 
| 568 | 
            +
                    stack << (format_bytes @transfer_sent, "B")
         | 
| 569 | 
            +
                    stack << (format_bytes @transfer_total, "B")
         | 
| 570 | 
            +
                    stack << (format_bytes @current_bitrate.round(0), "bps")
         | 
| 570 571 | 
             
                    stack2 = stack.map { |txt| ("%#{LOG_PIPE_LEN.to_i}s" % txt) }.join("\t")
         | 
| 571 572 | 
             
                    log_debug "progress #{stack2} \t#{name}"
         | 
| 572 573 |  | 
| @@ -4,6 +4,7 @@ module RestFtpDaemon | |
| 4 4 | 
             
              class JobQueue
         | 
| 5 5 | 
             
                include BmcDaemonLib::LoggerHelper
         | 
| 6 6 | 
             
                include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
         | 
| 7 | 
            +
                include Helpers
         | 
| 7 8 |  | 
| 8 9 | 
             
                # Class options
         | 
| 9 10 | 
             
                attr_reader :logger
         | 
| @@ -27,7 +28,7 @@ module RestFtpDaemon | |
| 27 28 |  | 
| 28 29 | 
             
                  # Identifiers generator
         | 
| 29 30 | 
             
                  @last_id = 0
         | 
| 30 | 
            -
                  @prefix =  | 
| 31 | 
            +
                  @prefix = identifier JOB_IDENT_LEN
         | 
| 31 32 | 
             
                  log_info "JobQueue initialized (prefix: #{@prefix})"
         | 
| 32 33 | 
             
                end
         | 
| 33 34 |  | 
| @@ -5,6 +5,7 @@ require 'rest_client' | |
| 5 5 | 
             
            module RestFtpDaemon
         | 
| 6 6 | 
             
              class Notification
         | 
| 7 7 | 
             
                include BmcDaemonLib::LoggerHelper
         | 
| 8 | 
            +
                include Helpers
         | 
| 8 9 |  | 
| 9 10 | 
             
                # Class options
         | 
| 10 11 | 
             
                attr_reader :logger
         | 
| @@ -22,7 +23,7 @@ module RestFtpDaemon | |
| 22 23 | 
             
                  @params = params
         | 
| 23 24 |  | 
| 24 25 | 
             
                  # Generate a random key
         | 
| 25 | 
            -
                  @id =  | 
| 26 | 
            +
                  @id = identifier(NOTIFY_IDENTIFIER_LEN)
         | 
| 26 27 | 
             
                  @jid = nil
         | 
| 27 28 |  | 
| 28 29 | 
             
                  # Logger
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            module RestFtpDaemon
         | 
| 2 2 | 
             
              class Paginate
         | 
| 3 | 
            +
                include Helpers
         | 
| 3 4 |  | 
| 4 5 | 
             
                # Class options
         | 
| 5 6 | 
             
                attr_writer :filter
         | 
| @@ -53,7 +54,7 @@ module RestFtpDaemon | |
| 53 54 |  | 
| 54 55 | 
             
                def link p
         | 
| 55 56 | 
             
                  klass = (p == @page)? "primary" : "default"
         | 
| 56 | 
            -
                  url =  | 
| 57 | 
            +
                  url = dashboard_url(@filter)
         | 
| 57 58 | 
             
                  "<a class='btn btn-%s' href='%s?page=%d'>%p</a>" % [
         | 
| 58 59 | 
             
                    klass,
         | 
| 59 60 | 
             
                    @filter,
         | 
| @@ -65,7 +65,7 @@ module RestFtpDaemon | |
| 65 65 | 
             
                def chdir_or_create directory, mkdir = false
         | 
| 66 66 | 
             
                  # Init, extract my parent name and my own name
         | 
| 67 67 | 
             
                  log_debug "RemoteFTP.chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
         | 
| 68 | 
            -
                  parent, _current =  | 
| 68 | 
            +
                  parent, _current = extract_parent(directory)
         | 
| 69 69 |  | 
| 70 70 | 
             
                  # Access this directory
         | 
| 71 71 | 
             
                  begin
         | 
| @@ -64,7 +64,7 @@ module RestFtpDaemon | |
| 64 64 | 
             
                def chdir_or_create directory, mkdir = false
         | 
| 65 65 | 
             
                  # Init, extract my parent name and my own name
         | 
| 66 66 | 
             
                  log_debug "RemoteSFTP.chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
         | 
| 67 | 
            -
                  parent, _current =  | 
| 67 | 
            +
                  parent, _current = extract_parent(directory)
         | 
| 68 68 |  | 
| 69 69 | 
             
                  # Access this directory
         | 
| 70 70 | 
             
                  begin
         | 
| @@ -20,6 +20,8 @@ body, table tr td, .fixed { | |
| 20 20 | 
             
              margin: 0 2px;
         | 
| 21 21 | 
             
              border: 1px solid silver;
         | 
| 22 22 | 
             
              border-radius: 0.25em;
         | 
| 23 | 
            +
              cursor: pointer;
         | 
| 24 | 
            +
              cursor: hand;
         | 
| 23 25 | 
             
              }
         | 
| 24 26 |  | 
| 25 27 | 
             
            .label {
         | 
| @@ -59,7 +61,6 @@ a.class { | |
| 59 61 | 
             
              text-decoration: underline;
         | 
| 60 62 | 
             
              }
         | 
| 61 63 |  | 
| 62 | 
            -
             | 
| 63 64 | 
             
            .debug {
         | 
| 64 65 | 
             
              border: 1px solid orange;
         | 
| 65 66 | 
             
            }
         | 
| @@ -73,6 +74,15 @@ a.class { | |
| 73 74 | 
             
            }
         | 
| 74 75 |  | 
| 75 76 |  | 
| 77 | 
            +
             | 
| 78 | 
            +
            /* Table styles
         | 
| 79 | 
            +
            -------------------------------------------------- */
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            table.table th {
         | 
| 82 | 
            +
              text-transform: uppercase;
         | 
| 83 | 
            +
              }
         | 
| 84 | 
            +
             | 
| 85 | 
            +
             | 
| 76 86 | 
             
            /* Header styles
         | 
| 77 87 | 
             
            -------------------------------------------------- */
         | 
| 78 88 | 
             
            .header-indicators {
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            - title = "#{Conf.host} [#{Conf.app_env}] #{Conf.app_name} #{Conf.app_ver}"
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            -# coding: utf-8
         | 
| 2 4 | 
             
            !!! 5
         | 
| 3 5 | 
             
            %html{:lang => "en"}
         | 
| @@ -6,7 +8,7 @@ | |
| 6 8 | 
             
                %link{ href:"/css/bootstrap.css" , rel: "stylesheet"}
         | 
| 7 9 | 
             
                %link{ href:"/css/main.css" , rel: "stylesheet"}
         | 
| 8 10 | 
             
                -# %link{ href:"http://fonts.googleapis.com/css?family=Inconsolata:400,700" , rel: "stylesheet"}
         | 
| 9 | 
            -
                %title= | 
| 11 | 
            +
                %title= title
         | 
| 10 12 |  | 
| 11 13 | 
             
              %body
         | 
| 12 14 |  | 
| @@ -16,20 +18,23 @@ | |
| 16 18 |  | 
| 17 19 | 
             
                .container-fluid
         | 
| 18 20 | 
             
                  .row
         | 
| 19 | 
            -
                     | 
| 21 | 
            +
                    .col-md-12
         | 
| 20 22 | 
             
                      = render :dashboard_jobs
         | 
| 21 23 |  | 
| 22 24 | 
             
                  .row
         | 
| 23 | 
            -
                     | 
| 24 | 
            -
                      = render :dashboard_tokens, {tokens: Conf[:endpoints] || {}}
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                    #box-workers.col-md-3
         | 
| 25 | 
            +
                    .col-md-5
         | 
| 27 26 | 
             
                      = render :dashboard_workers
         | 
| 28 27 |  | 
| 29 | 
            -
                     | 
| 28 | 
            +
                    .col-md-4
         | 
| 30 29 | 
             
                      = render :dashboard_counters
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    .col-md-3
         | 
| 31 32 | 
             
                      = render :dashboard_rates
         | 
| 32 33 |  | 
| 34 | 
            +
                  .row
         | 
| 35 | 
            +
                    .col-md-12
         | 
| 36 | 
            +
                      = render :dashboard_tokens
         | 
| 37 | 
            +
             | 
| 33 38 | 
             
                .footer
         | 
| 34 39 | 
             
                  .container-fluid
         | 
| 35 40 | 
             
                    = render :dashboard_footer
         | 
| @@ -5,19 +5,20 @@ | |
| 5 5 |  | 
| 6 6 | 
             
              ©
         | 
| 7 7 | 
             
              = "2014-#{Time.now.year}"
         | 
| 8 | 
            -
              %a{href: "http://bmconseil.com"}Bruno Medici Consultant
         | 
| 8 | 
            +
              %a{href: "http://bmconseil.com/?ref=rftpd-dashbaord"}Bruno Medici Consultant
         | 
| 9 9 |  | 
| 10 10 | 
             
              ·
         | 
| 11 | 
            -
              %a{href: "http://github.com/bmedici/rest-ftp-daemon"} GitHub
         | 
| 11 | 
            +
              %a{href: "http://github.com/bmedici/rest-ftp-daemon/"} GitHub
         | 
| 12 12 | 
             
              ·
         | 
| 13 | 
            -
              %a{href: "http://refactorcop.com/bmedici/rest-ftp-daemon"} RefactorCop
         | 
| 13 | 
            +
              %a{href: "http://refactorcop.com/bmedici/rest-ftp-daemon/"} RefactorCop
         | 
| 14 14 |  | 
| 15 15 |  | 
| 16 16 | 
             
            .footer-indicators.pull-right
         | 
| 17 17 |  | 
| 18 | 
            -
               | 
| 19 | 
            -
                .btn | 
| 20 | 
            -
             | 
| 18 | 
            +
              - if Conf.host
         | 
| 19 | 
            +
                .btn-group.btn-group-sm
         | 
| 20 | 
            +
                  .btn.btn-default.btn-info Host
         | 
| 21 | 
            +
                  .btn.btn-default= Conf.host
         | 
| 21 22 |  | 
| 22 23 | 
             
              .btn-group.btn-group-sm
         | 
| 23 24 | 
             
                .btn.btn-default.btn-info IP
         | 
| @@ -29,4 +30,4 @@ | |
| 29 30 |  | 
| 30 31 | 
             
              .btn-group.btn-group-sm
         | 
| 31 32 | 
             
                .btn.btn-default.btn-info Started
         | 
| 32 | 
            -
                .btn.btn-default=  | 
| 33 | 
            +
                .btn.btn-default= datetime_short(Conf.app_started)
         | 
| @@ -27,7 +27,7 @@ | |
| 27 27 |  | 
| 28 28 | 
             
              .btn-group.btn-group-sm
         | 
| 29 29 | 
             
                .btn.btn-default.btn-warning Mem
         | 
| 30 | 
            -
                .btn.btn-default=  | 
| 30 | 
            +
                .btn.btn-default= format_bytes(mem.bytes, "B")
         | 
| 31 31 |  | 
| 32 32 | 
             
              .btn-group.btn-group-sm
         | 
| 33 33 | 
             
                .btn.btn-default.btn-success Processed
         | 
| @@ -35,4 +35,4 @@ | |
| 35 35 |  | 
| 36 36 | 
             
              .btn-group.btn-group-sm
         | 
| 37 37 | 
             
                .btn.btn-default.btn-success Transferred
         | 
| 38 | 
            -
                .btn.btn-default=  | 
| 38 | 
            +
                .btn.btn-default= format_bytes(info_trans, "B", 1)
         | 
| @@ -5,13 +5,13 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            .btn-group.btn-group-xs.filters
         | 
| 7 7 | 
             
              - klass = @filter.empty? ? "btn-info" : ""
         | 
| 8 | 
            -
              %a.btn.btn-default{href:  | 
| 8 | 
            +
              %a.btn.btn-default{href: dashboard_url(), class: klass}
         | 
| 9 9 | 
             
                ALL (#{counts_all})
         | 
| 10 10 |  | 
| 11 11 | 
             
            .btn-group.btn-group-xs.filters
         | 
| 12 12 | 
             
              - jobs_by_status.each do |status, count|
         | 
| 13 13 | 
             
                - klass = (status.to_s == @filter) ? "btn-info" : ""
         | 
| 14 | 
            -
                %a.btn.btn-default{href:  | 
| 14 | 
            +
                %a.btn.btn-default{href: dashboard_url(status), class: klass}
         | 
| 15 15 | 
             
                  #{status} (#{count})
         | 
| 16 16 |  | 
| 17 17 |  | 
| @@ -1,18 +1,19 @@ | |
| 1 1 | 
             
            -# coding: utf-8
         | 
| 2 2 |  | 
| 3 | 
            -
            - groups = {pool: "pool", targethost: "target | 
| 3 | 
            +
            - groups = {pool: "pool", targethost: "target"}
         | 
| 4 4 |  | 
| 5 5 |  | 
| 6 6 | 
             
            %h2 Transfer rates
         | 
| 7 7 |  | 
| 8 | 
            -
            %table.table.table-striped.table-hover.table-condensed
         | 
| 9 8 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 9 | 
            +
            - groups.each do |group_by, group_title|
         | 
| 10 | 
            +
              - rates_by_status = $queue.rate_by(group_by)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              %table.table.table-striped.table-hover.table-condensed
         | 
| 12 13 |  | 
| 13 14 | 
             
                %thead
         | 
| 14 15 | 
             
                  %tr
         | 
| 15 | 
            -
                    %th= group_title
         | 
| 16 | 
            +
                    %th= "by #{group_title}"
         | 
| 16 17 | 
             
                    %th.text-right bitrate
         | 
| 17 18 |  | 
| 18 19 | 
             
                %tbody
         | 
| @@ -24,9 +25,4 @@ | |
| 24 25 | 
             
                      %td
         | 
| 25 26 | 
             
                        = group
         | 
| 26 27 | 
             
                      %td.text-right
         | 
| 27 | 
            -
                        =  | 
| 28 | 
            -
             | 
| 29 | 
            -
                %thead
         | 
| 30 | 
            -
                  %tr
         | 
| 31 | 
            -
                    %td{colspan: 3}
         | 
| 32 | 
            -
                      %br
         | 
| 28 | 
            +
                        = format_bytes(rate, "bps")
         | 
| @@ -13,7 +13,7 @@ | |
| 13 13 |  | 
| 14 14 | 
             
              %tr{class: trclass.to_s}
         | 
| 15 15 | 
             
                %td
         | 
| 16 | 
            -
                  %a{href:  | 
| 16 | 
            +
                  %a{href: dashboard_job_url(job)}
         | 
| 17 17 | 
             
                    %b= job.id
         | 
| 18 18 |  | 
| 19 19 | 
             
                %td= job.label
         | 
| @@ -21,17 +21,16 @@ | |
| 21 21 | 
             
                %td= job.pool
         | 
| 22 22 |  | 
| 23 23 | 
             
                %td{title: job.get_info(:source, :path)}
         | 
| 24 | 
            -
                  =  | 
| 24 | 
            +
                  = token_highlight job.source
         | 
| 25 25 |  | 
| 26 26 | 
             
                %td
         | 
| 27 | 
            -
                  =  | 
| 28 | 
            -
                  =# Helpers.job_method_label job.target_method
         | 
| 27 | 
            +
                  = job_method_label (job.get_info :target, :method)
         | 
| 29 28 |  | 
| 30 29 | 
             
                %td{title: job.get_info(:target, :url)}
         | 
| 31 | 
            -
                  =  | 
| 30 | 
            +
                  = token_highlight job.target
         | 
| 32 31 |  | 
| 33 32 | 
             
                %td
         | 
| 34 | 
            -
                  =  | 
| 33 | 
            +
                  = datetime_short(job.queued_at)
         | 
| 35 34 |  | 
| 36 35 | 
             
                %td
         | 
| 37 36 | 
             
                  %span.push-status
         | 
| @@ -54,19 +53,19 @@ | |
| 54 53 | 
             
                  - if job.status == JOB_STATUS_UPLOADING
         | 
| 55 54 | 
             
                    .progress
         | 
| 56 55 | 
             
                      .progress-bar{style:"width: #{progress}%;"}
         | 
| 57 | 
            -
                        =  | 
| 56 | 
            +
                        = format_bytes(job.get_info(:transfer, :sent), "B")
         | 
| 58 57 |  | 
| 59 58 | 
             
                  - else
         | 
| 60 59 | 
             
                    .error{title: job.get_info(:error, :message)}
         | 
| 61 | 
            -
                      =  | 
| 60 | 
            +
                      = text_or_empty(job.error)
         | 
| 62 61 |  | 
| 63 62 | 
             
                %td.nobr.text-right
         | 
| 64 | 
            -
                  =  | 
| 63 | 
            +
                  = format_bytes(job.get_info(:transfer, :total), "B")
         | 
| 65 64 |  | 
| 66 65 | 
             
                %td.nobr.text-right{title: "time: #{job.exectime} s"}
         | 
| 67 66 | 
             
                  - if bitrate
         | 
| 68 67 | 
             
                    %span.push-bitrate
         | 
| 69 | 
            -
                      =  | 
| 68 | 
            +
                      = format_bytes(bitrate, "bps")
         | 
| 70 69 |  | 
| 71 70 | 
             
                %td
         | 
| 72 71 | 
             
                  - unless job.wid.nil?
         | 
| @@ -77,4 +76,4 @@ | |
| 77 76 | 
             
                    .label.label-default.flag.worker-label= job.priority
         | 
| 78 77 |  | 
| 79 78 | 
             
                %td
         | 
| 80 | 
            -
                  .label.flag.worker-label{class:  | 
| 79 | 
            +
                  .label.flag.worker-label{class: job_runs_style(runs)}= runs
         | 
| @@ -1,15 +1,6 @@ | |
| 1 1 | 
             
            -# coding: utf-8
         | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
            %table.table.table-striped.table-hover.table-condensed
         | 
| 2 | 
            +
            - tokens = Conf[:endpoints] || {}
         | 
| 5 3 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 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
         | 
| 4 | 
            +
            %h2 Endpoint tokens
         | 
| 5 | 
            +
            - tokens.each do |name, value|
         | 
| 6 | 
            +
              = token_to_label name, value
         | 
| @@ -4,21 +4,20 @@ | |
| 4 4 | 
             
              - log_error "Dashboard: invalid WorkerPool"
         | 
| 5 5 | 
             
            - variables = $pool.worker_variables
         | 
| 6 6 |  | 
| 7 | 
            -
            %h2  | 
| 7 | 
            +
            %h2 Worker status
         | 
| 8 8 |  | 
| 9 9 | 
             
            %table.table.table-striped.table-hover.table-condensed
         | 
| 10 10 |  | 
| 11 11 | 
             
              %thead
         | 
| 12 12 | 
             
                %tr
         | 
| 13 | 
            -
                  %th pool
         | 
| 14 13 | 
             
                  %th worker
         | 
| 14 | 
            +
                  %th pool
         | 
| 15 15 | 
             
                  %th status
         | 
| 16 16 | 
             
                  %th job
         | 
| 17 17 | 
             
                  %th.text-right seen
         | 
| 18 18 |  | 
| 19 19 | 
             
              %tbody
         | 
| 20 20 | 
             
                - variables.each do |wid, vars|
         | 
| 21 | 
            -
                  -# wid = vars[:wid]
         | 
| 22 21 | 
             
                  - status = vars[:status]
         | 
| 23 22 | 
             
                  - alive = $pool.worker_alive? wid
         | 
| 24 23 | 
             
                  - trclass = WORKER_STYLES[status]
         | 
| @@ -28,14 +27,14 @@ | |
| 28 27 | 
             
                    - status = "DEAD"
         | 
| 29 28 |  | 
| 30 29 | 
             
                  %tr{class: trclass.to_s}
         | 
| 31 | 
            -
                    %td= vars[:pool]
         | 
| 32 30 | 
             
                    %td= wid
         | 
| 31 | 
            +
                    %td= vars[:pool]
         | 
| 33 32 | 
             
                    %td= status
         | 
| 34 33 | 
             
                    %td= vars[:jid]
         | 
| 35 34 | 
             
                    %td.text-right
         | 
| 36 35 |  | 
| 37 36 | 
             
                      - if vars[:updated_at].is_a? Time
         | 
| 38 37 | 
             
                        - no_news_for = (Time.now - vars[:updated_at]).round(0)
         | 
| 39 | 
            -
                        =  | 
| 38 | 
            +
                        = formatted_duration no_news_for
         | 
| 40 39 | 
             
                      - else
         | 
| 41 40 | 
             
                        = "?"
         | 
| @@ -39,10 +39,5 @@ module RestFtpDaemon | |
| 39 39 | 
             
                  @config["clean_#{status}"] || 0
         | 
| 40 40 | 
             
                end
         | 
| 41 41 |  | 
| 42 | 
            -
                # NewRelic instrumentation
         | 
| 43 | 
            -
                add_transaction_tracer :worker_init,       category: :task
         | 
| 44 | 
            -
                add_transaction_tracer :worker_after,      category: :task
         | 
| 45 | 
            -
                add_transaction_tracer :worker_process,    category: :task
         | 
| 46 | 
            -
             | 
| 47 42 | 
             
              end
         | 
| 48 43 | 
             
            end
         | 
| @@ -100,10 +100,5 @@ module RestFtpDaemon | |
| 100 100 | 
             
                  job.oops_after_crash ex unless job.nil?
         | 
| 101 101 | 
             
                end
         | 
| 102 102 |  | 
| 103 | 
            -
                # NewRelic instrumentation
         | 
| 104 | 
            -
                add_transaction_tracer :worker_init,       category: :task
         | 
| 105 | 
            -
                add_transaction_tracer :worker_after,      category: :task
         | 
| 106 | 
            -
                add_transaction_tracer :worker_process,    category: :task
         | 
| 107 | 
            -
             | 
| 108 103 | 
             
              end
         | 
| 109 104 | 
             
            end
         | 
    
        data/rest-ftp-daemon.gemspec
    CHANGED
    
    
| @@ -42,7 +42,7 @@ describe "Jobs", feature: true do | |
| 42 42 |  | 
| 43 43 | 
             
                  it "assigns a status" do
         | 
| 44 44 | 
             
                    response = JSON.parse post(MOUNT_JOBS, json: params)
         | 
| 45 | 
            -
                    expect(response["status"]).to match(/^(queued|failed)$/)
         | 
| 45 | 
            +
                    expect(response["status"]).to match(/^(queued|failed|preparing)$/)
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 48 | 
             
                  it "assigns a pool" do
         | 
    
        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.306.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-07- | 
| 11 | 
            +
            date: 2016-07-25 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         | 
| @@ -335,6 +335,8 @@ files: | |
| 335 335 | 
             
            - lib/rest-ftp-daemon/constants.rb
         | 
| 336 336 | 
             
            - lib/rest-ftp-daemon/counters.rb
         | 
| 337 337 | 
             
            - lib/rest-ftp-daemon/exceptions.rb
         | 
| 338 | 
            +
            - lib/rest-ftp-daemon/help_apis.rb
         | 
| 339 | 
            +
            - lib/rest-ftp-daemon/help_views.rb
         | 
| 338 340 | 
             
            - lib/rest-ftp-daemon/helpers.rb
         | 
| 339 341 | 
             
            - lib/rest-ftp-daemon/job.rb
         | 
| 340 342 | 
             
            - lib/rest-ftp-daemon/job_queue.rb
         |