Page Menu
Home
GRNET
Search
Configure Global Search
Log In
Files
F1534184
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sun, Mar 1, 8:16 AM
Size
14 KB
Mime Type
text/x-diff
Expires
Tue, Mar 3, 8:16 AM (1 d, 11 h)
Engine
blob
Format
Raw Data
Handle
343362
Attached To
rARCHIVING archiving
View Options
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 975cb36..ddda4a8 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,43 +1,53 @@
module ApplicationHelper
# Custom helper for better display of big numbers
# @example number_by_magnitude(4242)
# "4.2K"
#
# @param number[Numeric]
# @return [String] human friendly respresentation
def number_by_magnitude(number)
number_to_human(number, units: { thousand: :K, million: :M, billion: :G })
end
# Creates a bootstrap form-group div with an additional 'Add' button next to the select field
#
# @param object[ActiveRecord::Object] the form's subject
# @param resource[Symbol] the objects class
# @param attr[Symbol] the select box's attribute
# @param attr_name[String] the attribute's display name
# @param options[Array] the select box options
# @param path[String] the add button's path
def select_with_errors_and_button(object, resource, attr, attr_name, options, path)
has_errors = object.errors[attr].present?
content_tag(:div, class: "form-group #{' has-error' if has_errors }") do
attr_label = label(resource, attr, attr_name, class: 'control-label col-xs-4 required')
select_div = content_tag(:div, class: 'col-xs-6') do
select_part = select_tag([resource, attr].join('_').to_sym,
options,
name: "#{resource}[#{attr}]",
class: 'form-control'
)
if has_errors
select_part.concat(content_tag(:span, class: 'help-block') { object.errors[attr].first })
end
select_part
end
button_part = content_tag(:div, class: 'col-xs-1') do
link_to 'Add', path, class: 'btn btn-primary', role: 'button'
end
attr_label.concat(select_div).concat(button_part)
end
end
+
+ # Returns a style class depending on the given parameter
+ #
+ # @param status[Char]
+ def success_class(status)
+ case status
+ when 'T' then 'success'
+ when 'E' then 'danger'
+ end
+ end
end
diff --git a/app/models/client.rb b/app/models/client.rb
index 730f61e..e9d5bbd 100644
--- a/app/models/client.rb
+++ b/app/models/client.rb
@@ -1,73 +1,84 @@
class Client < ActiveRecord::Base
self.table_name = :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
+ RECENT_JOBS_COUNT = 5
# 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).limit(RECENT_JOBS_COUNT).includes(:file_set)
+ 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
- def last_job_date
- jobs.maximum(:EndTime)
+ # Helper method for displayin the last job's datetime in a nice format.
+ def last_job_date_formatted
+ if job_time = jobs.backup_type.last.try(:end_time)
+ I18n.l(job_time, format: :long)
+ end
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
end
diff --git a/app/models/job.rb b/app/models/job.rb
index 1403f47..7f5fc83 100644
--- a/app/models/job.rb
+++ b/app/models/job.rb
@@ -1,45 +1,97 @@
class Job < ActiveRecord::Base
self.table_name = :Job
self.primary_key = :JobId
alias_attribute :job_id, :JobId
alias_attribute :job, :Job
alias_attribute :name, :Name
alias_attribute :type, :Type
alias_attribute :level, :Level
alias_attribute :client_id, :ClientId
alias_attribute :job_status, :JobStatus
alias_attribute :sched_time, :SchedTime
alias_attribute :start_time, :StartTime
alias_attribute :end_time, :EndTime
alias_attribute :real_end_time, :RealEndTime
alias_attribute :job_t_date, :JobTDate
alias_attribute :vol_session_id, :VolSessionId
alias_attribute :vol_session_time, :VolSessionTime
alias_attribute :job_files, :JobFiles
alias_attribute :job_bytes, :JobBytes
alias_attribute :read_bytes, :ReadBytes
alias_attribute :job_errors, :JobErrors
alias_attribute :job_missing_files, :JobMissingFiles
alias_attribute :pool_id, :PoolId
alias_attribute :file_set_id, :FileSetId
alias_attribute :prior_job_id, :PriorJobId
alias_attribute :purged_files, :PurgedFiles
alias_attribute :has_base, :HasBase
alias_attribute :has_cache, :HasCache
alias_attribute :reviewed, :Reviewed
alias_attribute :comment, :Comment
belongs_to :pool, foreign_key: :PoolId
belongs_to :file_set, foreign_key: :FileSetId
belongs_to :client, foreign_key: :ClientId
has_many :bacula_files, foreign_key: :JobId
has_many :base_files, foreign_key: :BaseJobId
has_many :job_media, foreign_key: :JobId
has_many :logs, foreign_key: :JobId
scope :running, -> { where(job_status: 'R') }
scope :backup_type, -> { where(type: 'B') }
scope :restore_type, -> { where(type: 'R') }
+
+ HUMAN_STATUS = {
+ 'A' => 'Canceled by user',
+ 'B' => 'Blocked',
+ 'C' => 'Created, not yet running',
+ 'D' => 'Verify found differences',
+ 'E' => 'Terminated with errors',
+ 'F' => 'Waiting for Client',
+ 'M' => 'Waiting for media mount',
+ 'R' => 'Running',
+ 'S' => 'Waiting for Storage daemon',
+ 'T' => 'Completed successfully',
+ 'a' => 'SD despooling attributes',
+ 'c' => 'Waiting for client resource',
+ 'd' => 'Waiting on maximum jobs',
+ 'e' => 'Non-fatal error',
+ 'f' => 'Fatal error',
+ 'i' => 'Doing batch insert file records',
+ 'j' => 'Waiting for job resource',
+ 'm' => 'Waiting for new media',
+ 'p' => 'Waiting on higher priority jobs',
+ 's' => 'Waiting for storage resource',
+ 't' => 'Waiting on start time'
+ }
+
+ def level_human
+ {
+ 'F' => 'Full',
+ 'D' => 'Differential',
+ 'I' => 'Incremental'
+ }[level]
+ end
+
+ def status_human
+ HUMAN_STATUS[job_status]
+ end
+
+ def fileset
+ file_set.try(:file_set) || '-'
+ end
+
+ def start_time_formatted
+ if start_time
+ I18n.l(start_time, format: :long)
+ end
+ end
+
+ def end_time_formatted
+ if end_time
+ I18n.l(end_time, format: :long)
+ end
+ end
end
diff --git a/app/views/clients/_client.html.erb b/app/views/clients/_client.html.erb
index 3f5a2eb..3e5337b 100644
--- a/app/views/clients/_client.html.erb
+++ b/app/views/clients/_client.html.erb
@@ -1,11 +1,11 @@
<tr>
<td><%= link_to client.name, client %></td>
<td><%= client.uname %></td>
<td><%= @active_jobs[client.id] || 0 %></td>
- <td><%= client.last_job_date %></td>
+ <td><%= client.last_job_date_formatted %></td>
<td><%= client.file_retention_days %></td>
<td><%= client.job_retention_days %></td>
<td><%= number_to_human_size(client.backup_jobs_size) %></td>
<td><%= number_by_magnitude(client.files_count) %></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 94cbb7d..7da5967 100644
--- a/app/views/clients/_client_details.html.erb
+++ b/app/views/clients/_client_details.html.erb
@@ -1,42 +1,48 @@
<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 %></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>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>
+ <%= link_to 'Restore Files', '#', class: "btn btn-warning", role: "button" %>
+ <%= link_to 'Take Backup', '#', class: "btn btn-success", role: "button" %>
+ </div>
</div>
diff --git a/app/views/clients/_job.html.erb b/app/views/clients/_job.html.erb
index a254cd5..7e65907 100644
--- a/app/views/clients/_job.html.erb
+++ b/app/views/clients/_job.html.erb
@@ -1,8 +1,13 @@
<tr>
<td><%= job.name %></td>
<td><%= job.job_type %></td>
<td><%= job.fileset.try(:name) %></td>
<td><%= job.restore_location %></td>
<td><%= job.schedule_human %></td>
<td><%= I18n.l(job.created_at, format: :long) %></td>
+ <td>
+ <% if job.enabled? && job.baculized? && job.backup? %>
+ <%= link_to 'Take Backup', '#', class: "btn btn-success", role: "button" %>
+ <% end %>
+ </td>
</tr>
diff --git a/app/views/clients/_jobs.html.erb b/app/views/clients/_jobs.html.erb
index aa1630e..f1f2c09 100644
--- a/app/views/clients/_jobs.html.erb
+++ b/app/views/clients/_jobs.html.erb
@@ -1,19 +1,20 @@
-<div class="col-xs-8">
+<div class="col-xs-12">
<div class="table-responsive">
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Fileset</th>
<th>Restore Location</th>
<th>Schedule</th>
<th>Created</th>
+ <th>Actions</th>
</tr>
</thead>
<tbody>
<%= render partial: 'clients/job', collection: @client.persisted_jobs, object: :job %>
</tbody>
</table>
</div>
</div>
diff --git a/app/views/clients/_recent_job.html.erb b/app/views/clients/_recent_job.html.erb
new file mode 100644
index 0000000..109626b
--- /dev/null
+++ b/app/views/clients/_recent_job.html.erb
@@ -0,0 +1,11 @@
+<tr class="<%= success_class(recent_job.job_status) %>">
+ <td><%= recent_job.name %></td>
+ <td><%= recent_job.job_id %></td>
+ <td><%= recent_job.level_human %></td>
+ <td><%= recent_job.fileset %></td>
+ <td><%= recent_job.start_time_formatted %></td>
+ <td><%= recent_job.end_time_formatted %></td>
+ <td><%= number_to_human_size(recent_job.job_bytes) %></td>
+ <td><%= number_by_magnitude(recent_job.job_files) %></td>
+ <td><%= recent_job.status_human %>
+</tr>
diff --git a/app/views/clients/_recent_jobs.html.erb b/app/views/clients/_recent_jobs.html.erb
new file mode 100644
index 0000000..d29f972
--- /dev/null
+++ b/app/views/clients/_recent_jobs.html.erb
@@ -0,0 +1,22 @@
+<div class="col-xs-12">
+ <div class="table-responsive">
+ <table class="table table-striped table-bordered table-condensed">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>JobId</th>
+ <th>Level</th>
+ <th>Fileset</th>
+ <th>Started At</th>
+ <th>Finished At</th>
+ <th>Bytes</th>
+ <th>Files</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ <tbody>
+ <%= render partial: 'clients/recent_job', collection: @client.recent_jobs %>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/app/views/clients/show.html.erb b/app/views/clients/show.html.erb
index c3efec2..8d04039 100644
--- a/app/views/clients/show.html.erb
+++ b/app/views/clients/show.html.erb
@@ -1,30 +1,38 @@
<p id="notice"><%= notice %></p>
<% if @client.host %>
<div class="row right">
<%= link_to 'Manage Client', host_path(@client.host), class: "btn btn-primary", role: "button" %>
</div>
<% end %>
<h2><%= @client.name %></h2>
<div class="row">
<div class="col-xs-4">
<h3>Client Details</h3>
</div>
<div class="col-xs-6">
<h3>Bacula Jobs</h3>
</div>
</div>
<div class="row">
<%= render partial: 'client_details' %>
- <%= render partial: 'jobs' %>
+ <div class="col-xs-8">
+ <div class="row">
+ <%= render partial: 'jobs' %>
+ </div>
+ <div class="row">
+ <div class="col-xs-6">
+ <h3>Recent Jobs</h3>
+ </div>
+ </div>
+ <div class="row">
+ <%= render partial: 'recent_jobs' %>
+ </div>
+ </div>
</div>
-<div>
- <%= link_to 'Restore Files', '#', class: "btn btn-warning", role: "button" %>
- <%= link_to 'Take Backup', '#', class: "btn btn-success", role: "button" %>
-</div>
<br/>
<%= link_to 'Back to clients', clients_path %>
Event Timeline
Log In to Comment