diff --git a/app/models/client.rb b/app/models/client.rb index eb737b5..e258eb2 100644 --- a/app/models/client.rb +++ b/app/models/client.rb @@ -1,143 +1,143 @@ # Bacula Client class. # All hosts that are getting backed up with Bacula have a Client entry, with # attributes concerning the Client. class Client < ActiveRecord::Base establish_connection BACULA_CONF self.table_name = "#{connection_config[:database]}.Client" self.primary_key = :ClientId alias_attribute :name, :Name alias_attribute :uname, :Uname alias_attribute :auto_prune, :AutoPrune alias_attribute :file_retention, :FileRetention alias_attribute :job_retention, :JobRetention has_many :jobs, foreign_key: :ClientId has_one :host, foreign_key: :name, primary_key: :Name scope :for_user, ->(user_id) { joins(host: :users).where(users: { id: user_id }) } DAY_SECS = 60 * 60 * 24 - delegate :manually_inserted?, :origin, to: :host + delegate :manually_inserted?, :origin, :quota, to: :host, allow_nil: true # Fetches the client's job_templates that are already persisted to # Bacula's configuration # # @return [ActiveRecord::Relation] of `JobTemplate` def persisted_jobs host.job_templates.where(baculized: true).includes(:fileset, :schedule) end # Fetches the client's performed jobs in reverse chronological order # # @return [ActiveRecord::Relation] of `Job` def recent_jobs jobs.order(EndTime: :desc).includes(:file_set, :logs) end # Helper method. It shows the client's job retention, # (which is expressed in seconds) in days. # # @return [Integer] def job_retention_days job_retention / DAY_SECS end # Helper method. It shows the client's file retention, # (which is expressed in seconds) in days. # # @return [Integer] def file_retention_days file_retention / DAY_SECS end # Helper method for auto_prune # # @return [String] 'yes' or 'no' def auto_prune_human auto_prune == 1 ? 'yes' : 'no' end # Helper method for displayin the last job's datetime in a nice format. def last_job_date_formatted if job_time = last_job_datetime I18n.l(job_time, format: :long) end end # Helper method for fetching the last job's datetime def last_job_datetime jobs.backup_type.last.try(:end_time) end # Fetches the first and last job's end times. # # @return [Array] of datetimes in proper format def backup_enabled_datetime_range jobs.backup_type.pluck(:end_time).minmax.map { |x| x.strftime('%Y-%m-%d') } end # Shows if a client has any backup jobs to Bacule config # # @return [Boolean] def is_backed_up? jobs.backup_type.any? end # Shows the total file size of the jobs that run for a specific client # # @return [Integer] Size in Bytes def backup_jobs_size jobs.backup_type.map(&:job_bytes).sum end # Shows the total files' count for the jobs that run for a specific client # # @return [Integer] File count def files_count jobs.map(&:job_files).sum end # Fetches the client's jobs that are running at the moment # # @return [Integer] def running_jobs jobs.running.count end # Displays the bacula config that is generated from the client's # host # # @return [String] def bacula_config return unless host host.baculize_config.join("\n") end # Fetches the job ids that will construct the desired restore # # @param file_set_id[Integer] the fileset # @param restore_point[Datetime] the restore point # # @return [Array] of ids def get_job_ids(file_set_id, restore_point) job_ids = {} backup_jobs = jobs.backup_type.terminated.where(file_set_id: file_set_id) backup_jobs = backup_jobs.where('EndTime < ?', restore_point) if restore_point job_ids['F'] = backup_jobs.where(level: 'F').pluck(:JobId).last return [] if job_ids['F'].nil? job_ids['D'] = backup_jobs.where(level: 'D').where("JobId > ?", job_ids['F']).pluck(:JobId).last job_ids['I'] = backup_jobs.where(level: 'I'). where("JobId > ?", job_ids['D'] || job_ids['F'] ).pluck(:JobId) job_ids.values.flatten.compact end # Fetches the bacula filesets that are associated with the client def file_sets FileSet.joins(:jobs).where(Job: { JobId: job_ids }).uniq end end diff --git a/app/views/admin/clients/_client.html.erb b/app/views/admin/clients/_client.html.erb index 1966a0d..b8332ca 100644 --- a/app/views/admin/clients/_client.html.erb +++ b/app/views/admin/clients/_client.html.erb @@ -1,19 +1,19 @@ <tr> <td><%= link_to_if client.host.present?, "##{client.id}", admin_client_path(client) %></td> <td><%= link_to_if client.host.present?, client.name, admin_client_path(client) %></td> <td><%= client.uname %></td> <td><%= client.host.try(:origin) %></td> <td><%= @stats.active_jobs[client.id] || 0 %></td> <td><%= @stats.last_jobs[client.id] %></td> <td><%= client.file_retention_days %></td> <td><%= client.job_retention_days %></td> <td><%= number_to_human_size(@stats.jobs_sizes[client.id]) %></td> + <td><%= number_to_human_size(client.quota) %></td> <td> - <%= pretty_percentage( - @stats.jobs_sizes[client.id].to_f / Archiving.settings[:client_quota], - Archiving.settings[:client_quota] - ) - %> + <% if client.quota.to_f > 0 %> + <%= pretty_percentage(@stats.jobs_sizes[client.id].to_f / client.quota, client.quota) %> + <% end %> + </td> <td><%= number_by_magnitude(@stats.jobs_files[client.id]) %></td> <td><%= client.auto_prune_human %></td> </tr> diff --git a/app/views/admin/clients/_client_details.html.erb b/app/views/admin/clients/_client_details.html.erb index 161b7bd..e077892 100644 --- a/app/views/admin/clients/_client_details.html.erb +++ b/app/views/admin/clients/_client_details.html.erb @@ -1,44 +1,56 @@ <div class="col-xs-4"> <div class="table-responsive"> <table class="table table-striped table-bordered table-condensed "> <tr> <td>Name</td> <td><%= @client.name %></td> </tr> <tr> <td>Uname</td> <td><%= @client.uname %></td> </tr> <tr> <td>Active Jobs</td> <td><%= @client.running_jobs %></td> </tr> <tr> <td>Last Backup</td> <td> <%= @client.last_job_date_formatted %> </td> </tr> <tr> <td>File Retention</td> <td><%= @client.file_retention_days %> days</td> </tr> <tr> <td>Job Retention</td> <td><%= @client.job_retention_days %> days</td> </tr> <tr> <td>Total Space Used</td> <td><%= number_to_human_size @client.backup_jobs_size %></td> </tr> <tr> + <td>Client Quota</td> + <td><%= number_to_human_size @client.quota %></td> + </tr> + <tr> + <td>Space Used %</td> + <td> + <% if @client.quota.to_f > 0 %> + <%= pretty_percentage(@client.backup_jobs_size.to_f / @client.quota, @client.quota) %> + <% end %> + </td> + </tr> + <tr> <td>Files count</td> <td><%= number_by_magnitude(@client.files_count) %></td> </tr> <tr> <td>Auto Prune</td> <td><%= @client.auto_prune_human %></td> </tr> </table> </div> </div> diff --git a/app/views/admin/clients/index.html.erb b/app/views/admin/clients/index.html.erb index 61053ea..bcfe5d5 100644 --- a/app/views/admin/clients/index.html.erb +++ b/app/views/admin/clients/index.html.erb @@ -1,28 +1,29 @@ <h1>Bacula Clients</h1> <div class="datatable-wrapper"> <div class="table-responsive"> <table id="admin_clients" class="table table-striped table-bordered table-condensed"> <thead> <tr> <th class="no-search">ClientId</th> <th>Name</th> <th>Uname</th> <th>Client Type</th> <th class="no-search">Active Jobs</th> <th class="no-search">Last Backup</th> - <th class="no-search">FileRetention (days)</th> - <th class="no-search">JobRetention (days)</th> + <th class="no-search">File Ret. (days)</th> + <th class="no-search">Job Ret. (days)</th> <th class="no-search">Space Used</th> + <th class="no-search">Quota</th> <th class="no-search">Space Used %</th> - <th class="no-search">File count</th> + <th class="no-search">Files</th> <th class="neither-search-nor-order">AutoPrune</th> </tr> </thead> <tbody> <%= render partial: 'client', collection: @clients %> </tbody> </table> </div> </div> diff --git a/app/views/clients/_client.html.erb b/app/views/clients/_client.html.erb index 5a57832..0e68e35 100644 --- a/app/views/clients/_client.html.erb +++ b/app/views/clients/_client.html.erb @@ -1,18 +1,17 @@ <tr> <td><%= link_to client.name, client %></td> <td><%= client.uname %></td> <td><%= @stats.active_jobs[client.id] || 0 %></td> <td><%= @stats.last_jobs[client.id] %></td> <td><%= client.file_retention_days %></td> <td><%= client.job_retention_days %></td> <td><%= number_to_human_size(@stats.jobs_sizes[client.id]) %></td> + <td><%= number_to_human_size(client.quota) %></td> <td> - <%= pretty_percentage( - @stats.jobs_sizes[client.id].to_f / Archiving.settings[:client_quota], - Archiving.settings[:client_quota] - ) - %> + <% if client.quota.to_f > 0 %> + <%= pretty_percentage(@stats.jobs_sizes[client.id].to_f / client.quota, client.quota) %> + <% end %> </td> <td><%= number_by_magnitude(@stats.jobs_files[client.id]) %></td> <td><%= client.auto_prune_human %></td> </tr> diff --git a/app/views/clients/_client_details.html.erb b/app/views/clients/_client_details.html.erb index 809241e..c5304a9 100644 --- a/app/views/clients/_client_details.html.erb +++ b/app/views/clients/_client_details.html.erb @@ -1,50 +1,62 @@ <div class="col-xs-4"> <div class="table-responsive"> <table class="table table-striped table-bordered table-condensed "> <tr> <td>Name</td> <td><%= @client.name %></td> </tr> <tr> <td>Uname</td> <td><%= @client.uname %></td> </tr> <tr> <td>Active Jobs</td> <td><%= @client.running_jobs %></td> </tr> <tr> <td>Last Backup</td> <td> <%= @client.last_job_date_formatted %> </td> </tr> <tr> <td>File Retention</td> <td><%= @client.file_retention_days %> days</td> </tr> <tr> <td>Job Retention</td> <td><%= @client.job_retention_days %> days</td> </tr> <tr> <td>Total Space Used</td> <td><%= number_to_human_size @client.backup_jobs_size %></td> </tr> <tr> + <td>Client Quota</td> + <td><%= number_to_human_size @client.quota %></td> + </tr> + <tr> + <td>Space Used %</td> + <td> + <% if @client.quota.to_f > 0 %> + <%= pretty_percentage(@client.backup_jobs_size.to_f / @client.quota, @client.quota) %> + <% end %> + </td> + </tr> + <tr> <td>Files count</td> <td><%= number_by_magnitude(@client.files_count) %></td> </tr> <tr> <td>Auto Prune</td> <td><%= @client.auto_prune_human %></td> </tr> </table> </div> <div> <% if @client.is_backed_up? %> <%= link_to 'Restore Files', restore_client_path(@client), class: "btn btn-warning", role: "button" %> <% end %> </div> </div> diff --git a/app/views/clients/index.html.erb b/app/views/clients/index.html.erb index f3f16f8..325bfac 100644 --- a/app/views/clients/index.html.erb +++ b/app/views/clients/index.html.erb @@ -1,55 +1,56 @@ <div class="row right"> <%= link_to 'New Client', new_host_path, class: 'btn btn-primary', role: 'button' %> </div> <h1>My Bacula Clients</h1> <div class="table-responsive"> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <th>Name</th> <th>Uname</th> <th>Active Jobs</th> <th>Last Backup</th> - <th>FileRetention (days)</th> - <th>JobRetention (days)</th> + <th>File Ret. (days)</th> + <th>Job Ret. (days)</th> <th>Space Used</th> + <th>Quota</th> <th>Space Used %</th> - <th>File count</th> + <th>Files</th> <th>AutoPrune</th> </tr> </thead> <tbody> <%= render partial: 'client', collection: @clients %> </tbody> </table> </div> <% if @hosts.any? %> <h1>My Pending Hosts</h1> <div class="table-responsive"> <table class="table table-striped table-bordered table-condensed"> <thead> <tr> <th>Name</th> <th>FQDN</th> <th>Port</th> <th>Password</th> <th>FileRetention (days)</th> <th>JobRetention (days)</th> <th>AutoPrune</th> <th>Verified</th> <th>Created At</th> </tr> </thead> <tbody> <%= render partial: 'hosts/host', collection: @hosts %> </tbody> </table> </div> <% end %> <%= render partial: 'client_graphs', locals: { path: clients_path } %>