Page MenuHomeGRNET

No OneTemporary

File Metadata

Created
Fri, Aug 8, 8:43 PM
diff --git a/app/assets/javascripts/clients.js b/app/assets/javascripts/clients.js
index f28100b..3a433e5 100644
--- a/app/assets/javascripts/clients.js
+++ b/app/assets/javascripts/clients.js
@@ -1,41 +1,46 @@
$(document).ready(function() {
if ($('#select-files').size() > 0) {
$('#file-selector').hide();
$('#reset-button').hide();
$('#select-files').click(function() {
$('#file-selector').show();
$('#reset-button').show();
});
}
});
$(document).ready(function() {
if ($('#file-submitter').size() > 0) {
$("#file-tree").on("select_node.jstree",
function(evt, data) {
- add_input(data.node.id);
+ if (data.instance.is_leaf(data.node)) {
+ add_input(data.node.id, $('#file-tree').jstree(true).get_path(data.node, '/'));
+ }
});
$("#file-tree").on("deselect_node.jstree",
function(evt, data) {
remove_input(data.node.id);
});
}
if ($('#invitation_user_id').size() > 0) {
$('#invitation_user_id').chosen();
}
});
-function add_input(id) {
+function add_input(id, name) {
$('#file-submitter').
append('<input type="hidden" name="files[]" multiple="multiple" value="' + id + '" id="js-file-id-' + id + '" class="js-file-input"/>');
+ $('#restore_data_files').
+ append('<li id="restore_data_files_' + id + '">' + name + '</li>');
if ($('.js-file-input').size() > 0 && $('#file-submitter > input[type="submit"]').attr('disabled') == 'disabled') {
$('#file-submitter > input[type="submit"]').attr('disabled', false);
}
}
function remove_input(id) {
$('#js-file-id-' + id).remove();
+ $('#restore_data_files_' + id).remove();
if ($('.js-file-input').size() == 0) {
$('#file-submitter > input[type="submit"]').attr('disabled', true);
}
}
diff --git a/app/controllers/clients_controller.rb b/app/controllers/clients_controller.rb
index 8ddbc87..c915113 100644
--- a/app/controllers/clients_controller.rb
+++ b/app/controllers/clients_controller.rb
@@ -1,189 +1,189 @@
class ClientsController < ApplicationController
before_action :require_logged_in
before_action :fetch_client, only: [:show, :jobs, :logs, :stats, :users, :restore, :run_restore,
:restore_selected, :remove_user]
before_action :fetch_logs, only: [:logs]
before_action :require_non_blocked_client, only: [:restore, :restore_selected, :run_restore]
# GET /clients
# POST /clients
def index
@client_ids = Client.for_user(current_user.id).pluck(:ClientId)
@clients = Client.where(ClientId: @client_ids).includes(:jobs)
@hosts = current_user.hosts.not_baculized
fetch_jobs_info
get_charts
end
# GET /clients/1
def show
@schedules = @client.host.job_templates.map(&:schedule)
@filesets = @client.host.job_templates.map(&:fileset)
@jobs = @client.jobs.backup_type.terminated.group(:name).maximum(:EndTime)
end
# GET /clients/1/jobs
def jobs
@jobs = @client.recent_jobs.page(params[:page])
end
# GET /clients/1/logs
def logs; end
# GET /clients/1/stats
# POST /clients/1/stats
def stats
get_charts
end
# GET /clients/1/users
def users
@users = @client.host.users
if @client.manually_inserted?
@invitation = @client.host.invitations.new
excluded_ids = @users.pluck(:id) + @client.host.invitations.pluck(:user_id)
@available_users = User.where(enabled: true).institutional.
where.not(id: excluded_ids).pluck(:username, :id)
end
end
# DELETE /clients/1/user
def remove_user
user = @client.host.users.find(params[:user_id])
redirect_path = users_client_path(@client)
if @client.host.users.count == 1
flash[:alert] = 'You can not remove the last user'
elsif @client.host.users.delete(user)
flash[:success] =
if @client.manually_inserted?
'User successfully removed'
else
'User must be removed from the VM\'s list from your VM provider too (ViMa or Okeanos).'
end
if user.id == current_user.id
redirect_path = clients_path
end
else
flash[:alert] = 'User not removed, something went wrong'
end
redirect_to redirect_path
end
# GET /clients/1/restore
def restore
@restore_clients = Client.for_user(current_user.id)
return if @client.is_backed_up?
flash[:error] = 'Can not issue a restore for this client'
redirect_to client_path(@client)
end
# POST /clients/1/run_restore
def run_restore
@location = params[:restore_location].blank? ? '/tmp/bacula_restore' : params[:restore_location]
fileset = params[:fileset]
- restore_point = fetch_restore_point
- restore_client = fetch_restore_client
+ fetch_restore_point
+ fetch_restore_client
if params[:commit] == 'Restore All Files'
if @location.nil? || fileset.nil? ||
- !@client.host.restore(fileset, @location, restore_point, restore_client)
+ !@client.host.restore(fileset, @location, @restore_point, @restore_client)
flash[:error] = 'Something went wrong, try again later'
else
msg = "Restore job issued successfully, files will be soon available in #{@location}"
msg << " of client #{restore_client}" if restore_client.present?
flash[:success] = msg
end
render js: "window.location = '#{client_path(@client)}'"
else
- session[:job_ids] = @client.get_job_ids(fileset, restore_point)
- session[:restore_client] = restore_client
+ session[:job_ids] = @client.get_job_ids(fileset, @restore_point)
+ session[:restore_client] = @restore_client
Bvfs.new(@client, session[:job_ids]).update_cache
render 'select_files'
end
end
# POST /clients/1/restore_selected
def restore_selected
Bvfs.new(@client, session[:job_ids]).
restore_selected_files(params[:files], params[:location], nil, session[:restore_client])
session.delete(:job_ids)
session.delete(:restore_client)
flash[:success] =
"Restore job issued successfully, files will be soon available in #{params[:location]}"
redirect_to client_path(@client)
end
# GET /clients/1/tree?id=1
def tree
@client = Client.for_user(current_user.id).find(params[:client_id])
bvfs = Bvfs.new(@client, session[:job_ids])
pathid = params[:id].to_i
if pathid.nonzero?
bvfs.fetch_dirs(pathid)
else
bvfs.fetch_dirs
end
tree = bvfs.extract_dir_id_and_name.map do |id, name|
{ id: id, text: name, state: { checkbox_disabled: true }, children: true }
end
if pathid.nonzero?
bvfs.fetch_files(pathid)
bvfs.extract_file_id_and_name.each do |id, name|
tree << { id: id, text: name, type: 'file' }
end
end
render json: tree
end
private
def require_non_blocked_client
if @client.host.blocked?
flash[:error] = 'Client disabled by admins'
redirect_to clients_path
end
end
def fetch_client
@client = Client.for_user(current_user.id).find(params[:id])
@client_ids = [@client.id]
end
def fetch_restore_client
if params[:restore_client]
- Client.for_user(current_user.id).find_by(ClientId: params[:restore_client]).try(:name)
+ @restore_client = Client.for_user(current_user.id).find_by(ClientId: params[:restore_client]).try(:name)
end
end
def fetch_jobs_info
@stats = JobStats.new(@client_ids)
end
def get_charts
@job_status = ChartGenerator.job_statuses(@client_ids, days_ago)
@job_stats = ChartGenerator.job_stats(@client_ids, days_ago - 1)
end
def fetch_restore_point
if params['restore_time(4i)'].blank? || params['restore_time(5i)'].blank? ||
params[:restore_date].blank?
return nil
end
- restore_point =
- "#{params[:restore_date]} #{params['restore_time(4i)']}:#{params['restore_time(5i)']}:00"
- begin
- DateTime.strptime(restore_point, '%Y-%m-%d %H:%M:%S')
- rescue
- return nil
- end
- restore_point
+ @restore_point =
+ begin
+ DateTime.strptime(
+ "#{params[:restore_date]} #{params['restore_time(4i)']}:#{params['restore_time(5i)']}:00",
+ '%Y-%m-%d %H:%M:%S')
+ rescue
+ nil
+ end
end
end
diff --git a/app/views/clients/_file_selector.html.erb b/app/views/clients/_file_selector.html.erb
index 7b7c98c..aef99b9 100644
--- a/app/views/clients/_file_selector.html.erb
+++ b/app/views/clients/_file_selector.html.erb
@@ -1,22 +1,57 @@
<div id='file-selector' class="col-xs-4">
<div class='panel panel-default'>
<div class='panel-heading'>
<h3> Files </h3>
</div>
<div class="loader">
<h3>LOADING</h3>
</div>
<div id="file-tree"></div>
</div>
</div>
-<div class="col-xs-2">
- <%= form_tag(restore_selected_client_path, { id: 'file-submitter', style: 'display:none' }) do %>
- <%= submit_tag 'Restore Selected Files', class: 'btn btn-default', disabled: true %>
- <% end %>
-</div>
+<div class="col-xs-4">
+ <div class="panel panel-default" id="restore-details" style="display:none">
+ <div class="panel-heading">
+ <h3>Restore details</h3>
+ </div>
+ <div class="panel-body">
+ <div class="row">
+ <div class="col-xs-4" id="reset-button">
+ <%= link_to restore_client_path(@client), class: 'btn btn-default' do %>
+ <span class="glyphicon glyphicon-menu-left text-primary"></span>
+ Cancel
+ <% end %>
+ </div>
-<div class="col-xs-1" id="reset-button">
- <%= link_to 'Cancel', restore_client_path(@client), role: 'button',
- class: 'btn btn-default text-warning' %>
+ <div class="col-xs-4 text-right">
+ <%= form_tag(restore_selected_client_path, { id: 'file-submitter' }) do %>
+ <%= submit_tag 'Restore Selected Files', class: 'btn btn-default', disabled: true %>
+ <% end %>
+ </div>
+ </div>
+ <hr />
+ <br />
+ <div class="row">
+ <table class="table table-bordered table-condensed">
+ <tr>
+ <td>DateTime</td>
+ <td id="restore_data_point"></td>
+ </tr>
+ <tr>
+ <td>Restore Location</td>
+ <td id="restore_data_location"></td>
+ </tr>
+ <tr>
+ <td>Restore Client</td>
+ <td id="restore_data_client"></td>
+ </tr>
+ <tr>
+ <td>Files</td>
+ <td><ul id="restore_data_files"></ul></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
</div>
diff --git a/app/views/clients/restore.html.erb b/app/views/clients/restore.html.erb
index 8e356f0..7399d41 100644
--- a/app/views/clients/restore.html.erb
+++ b/app/views/clients/restore.html.erb
@@ -1,77 +1,74 @@
<div class="row">
<div class="col-xs-4">
<% if @client.is_backed_up? %>
<div class="panel panel-default">
<div class="panel-heading">
<h3>Restore files for "<%= @client.name %>"</h3>
</div>
<div class="panel-body">
<%= bootstrap_form_tag(url: run_restore_client_path(@client), remote: true,
layout: :horizontal, label_col: 'col-xs-4', control_col: 'col-xs-7',
html: { id: 'basic-form' } ) do |f| %>
<%= help_block('Restore to most recent backup by leaving date and time blank',
'col-xs-4', 'col-xs-7') %>
<%= f.text_field :restore_date %>
<%= f.time_select :restore_time, ignore_date: true, minute_step: 30, prompt: true %>
<%= f.select(:fileset,
options_from_collection_for_select(@client.file_sets, :id, :file_set)) %>
<%= f.text_field :restore_location, placeholder: '/tmp/default_restore' %>
<hr />
<%= help_block(
'Restore and backup clients <strong>must</strong> have the same encryption key'.html_safe,
'col-xs-4', 'col-xs-7') %>
<%= f.select(
:restore_client,
options_from_collection_for_select(@restore_clients,
:id, :name, @client.id),
label: tooltip_label('Restore Client',
'Client where the backup will be restored to')) %>
<hr />
<div class="row">
- <div class="col-xs-5 col-xs-offset-6 text-right">
+ <div class="col-xs-4 col-xs-offset-4">
<%= f.submit 'Select Specific Files', id: 'select-files', class: 'btn btn-primary' %>
</div>
- </div>
- <br />
- <div class="row">
- <div class="col-xs-3 col-xs-offset-8">
+ <div class="col-xs-4">
<%= f.submit 'Restore All Files', class: 'btn btn-warning text-right',
data: { confirm: "This will restore all your files" }
%>
</div>
</div>
<% end %>
</div>
</div>
<% else %>
<div class="alert alert-warning">
<p>Can not issue a restore for this client. It does not have any successful backups</p>
</div>
<% end %>
<%= link_to client_path(@client), class: 'btn btn-default' do %>
<span class="glyphicon glyphicon-menu-left text-primary"></span>
Back to client
<% end %>
</div>
<%= render partial: 'file_selector' %>
</div>
<script type="text/javascript">
<% min_date, max_date = @client.backup_enabled_datetime_range %>
var minDate = '<%= min_date %>';
var maxDate = '<%= max_date %>';
$('#restore_date').datepicker({
dateFormat: 'yy-mm-dd',
minDate: minDate,
maxDate: maxDate
});
</script>
diff --git a/app/views/clients/select_files.js.erb b/app/views/clients/select_files.js.erb
index c92a56e..77c4f70 100644
--- a/app/views/clients/select_files.js.erb
+++ b/app/views/clients/select_files.js.erb
@@ -1,27 +1,30 @@
$('#basic-form input').attr('disabled', true);
$('#basic-form select').attr('disabled', true);
$('.loader').hide();
$('#file-tree').jstree({
'core': {
'data' : {
"url" : "<%= escape_javascript(tree_client_path(client_id: @client.id)) %>",
"data" : function(node) {
return { "id" : node.id };
},
"dataType" : "json"
}
},
'checkbox' : {
'three_state': false
},
"types" : {
"file" : {
"icon" : "glyphicon glyphicon-file"
}
},
"plugins" : ["types", "checkbox"]
});
-$('#file-submitter').show();
+$('#restore-details').show();
$('#file-submitter').
append('<input type="hidden" name="location" value="<%= escape_javascript(@location) %>"/>');
+$('#restore_data_point').html("<%= @restore_point || Time.now.strftime('%Y-%m-%d %H:%M:%S') %>");
+$('#restore_data_location').html("<%= @location %>");
+$('#restore_data_client').html("<%= @restore_client || @client.name %>");

Event Timeline