}
private function execCommand($director, array $command, $user) {
+ $cmd = '';
if(!is_null($director) && $this->isValidDirector($director) === false) {
$output = array(BconsoleError::MSG_ERROR_INVALID_DIRECTOR, '');
$exitcode = BconsoleError::ERROR_INVALID_DIRECTOR;
$ret = (array_key_exists('versionid', $result) === true) ? $result['versionid'] : false;
return $ret;
}
+
+ public function getDatabaseSize() {
+ $configuration = $this->Application->getModule('configuration');
+ $db_params = $this->getDBParams();
+ $dbtype = $db_params['type'];
+ $dbname = $db_params['name'];
+
+ $db = new ActiveRecord();
+ $connection = $db->getDbConnection();
+ $connection->setActive(true);
+ $pdo = $connection->getPdoInstance();
+
+ $dbsize = 0;
+ if ($configuration->isPostgreSQLType($dbtype)) {
+ $sql = "SELECT pg_database_size('$dbname') AS dbsize";
+ $result = $pdo->query($sql);
+ $dbsize = $result->fetch();
+ } else if ($configuration->isMySQLType($dbtype)) {
+ $sql = "SELECT Sum(data_length + index_length) AS dbsize FROM information_schema.tables";
+ $result = $pdo->query($sql);
+ $dbsize = $result->fetch();
+ } else if ($configuration->isSQLiteType($dbtype)) {
+ $sql = "PRAGMA page_count";
+ $result = $pdo->query($sql);
+ $page_count = $result->fetch();
+ $sql = "PRAGMA page_size";
+ $result = $pdo->query($sql);
+ $page_size = $result->fetch();
+ $dbsize = array('dbsize' => ($page_count['page_count'] * $page_size['page_size']));
+ }
+ $pdo = null;
+ return $dbsize['dbsize'];
+ }
}
-?>
\ No newline at end of file
+?>
}
return $jobids;
}
+
+ public function getJobTotals() {
+ $jobtotals = array('bytes' => 0, 'files' => 0);
+ $connection = JobRecord::finder()->getDbConnection();
+ $connection->setActive(true);
+ $sql = "SELECT sum(JobFiles) AS files, sum(JobBytes) AS bytes FROM Job";
+ $pdo = $connection->getPdoInstance();
+ $result = $pdo->query($sql);
+ $ret = $result->fetch();
+ $jobtotals['bytes'] = $ret['bytes'];
+ $jobtotals['files'] = $ret['files'];
+ $pdo = null;
+ return $jobtotals;
+ }
}
?>
'L' => array('value' => 'Commiting data', 'description' =>'Committing data (last despool)')
);
+ private $jobStatesOK = array('T', 'D');
+ private $jobStatesWarning = array('W');
+ private $jobStatesError = array('E', 'e', 'f', 'I');
+ private $jobStatesCancel = array('A');
+ private $jobStatesRunning = array('C', 'R', 'B', 'F', 'S', 'm', 'M', 's', 'j', 'c', 'd','t', 'p', 'i', 'a', 'l', 'L');
+
private $runningJobStates = array('C', 'R');
/**
}
+ public function getJobStatesByType($type) {
+ $statesByType = array();
+ $states = array();
+ switch($type) {
+ case 'ok':
+ $states = $this->jobStatesOK;
+ break;
+ case 'warning':
+ $states = $this->jobStatesWarning;
+ break;
+ case 'error':
+ $states = $this->jobStatesError;
+ break;
+ case 'cancel':
+ $states = $this->jobStatesCancel;
+ break;
+ case 'running':
+ $states = $this->jobStatesRunning;
+ break;
+ }
+
+ for ($i = 0; $i < count($states); $i++) {
+ $statesByType[$states[$i]] = $this->getJobState($states[$i]);
+ }
+
+ return $statesByType;
+ }
+
public function isValidJobLevel($jobLevel) {
return array_key_exists($jobLevel, $this->getJobLevels());
}
}
});
+var GraphPieClass = Class.create({
+ jobs: [],
+ container: null,
+ series: null,
+ pie: null,
+ graph_options: {
+ colors: ['#63c422', '#d70808', '#FFFF66', 'orange', 'blue'],
+ HtmlText: false,
+ fontColor: '#ffffff',
+ grid: {
+ verticalLines : false,
+ horizontalLines : false,
+ outlineWidth: 0,
+ },
+ xaxis: { showLabels : false,},
+ yaxis: { showLabels : false },
+ pie: {
+ show : true,
+ explode : 5,
+ labelFormatter: PieGraph.pie_label_formatter,
+ shadowSize: 4,
+ fillOpacity: 1,
+ sizeRatio: 0.6
+ },
+ mouse: {
+ track : true,
+ trackFormatter: PieGraph.pie_track_formatter,
+ relative: true
+ },
+ legend: {
+ position : 'se',
+ backgroundColor : '#D2E8FF',
+ margin: 0
+ }
+ },
+ initialize: function(jobs, container_id) {
+ this.jobs = jobs;
+ this.container = document.getElementById(container_id);
+ this.series = this.prepare_series();
+ this.draw_grah();
+ },
+ prepare_series: function() {
+ var series = [];
+ var label, serie;
+ var job_types = Object.keys(this.jobs);
+ var jobs_count;
+ for (var i = 0; i < job_types.length; i++) {
+ label = job_types[i];
+ jobs_count = this.jobs[label].length;
+ serie = {
+ data: [[0, jobs_count]],
+ label: label + ' (' + jobs_count.toString() + ')'
+ }
+ series.push(serie);
+ }
+ return series;
+ },
+ draw_grah: function() {
+ this.pie = Flotr.draw(this.container, this.series, this.graph_options);
+ }
+});
var iso_date_to_timestamp = function(iso_date) {
var date_split = iso_date.split(' ');
var Units = {
get_decimal_size: function(size) {
+ if (size === null) {
+ size = 0;
+ }
+
size = parseInt(size, 10);
var size_unit = 'B';
var units = ['K', 'M', 'G', 'T', 'P'];
}
}
+var Strings = {
+ limits: {
+ label: 15
+ },
+ get_short_label: function(txt) {
+ var short_txt = txt;
+ var cut = ((this.limits.label - 2) / 2);
+ if (txt.length > this.limits.label) {
+ short_txt = txt.substr(0, cut) + '..' + txt.substr(-cut);
+ }
+ return short_txt;
+ }
+}
+
+var PieGraph = {
+ pie_label_formatter: function (total, value) {
+ var percents = (100 * value / total).toFixed(1);
+ if (percents >= 1) {
+ percents = percents.toString() + '%';
+ } else {
+ percents = '';
+ }
+ return percents;
+ },
+ pie_track_formatter: function(e) {
+ return e.series.label;
+ }
+}
+
var Formatters = {
formatter: [
{css_class: 'size', format_func: Units.get_decimal_size}
return cookie_val;
}
}
+
+var Dashboard = {
+ stats: null,
+ txt: null,
+ pie: null,
+ noval: '-',
+ ids: {
+ clients: {
+ no: 'clients_no',
+ most: 'clients_most',
+ jobs: 'clients_jobs'
+ },
+ jobs: {
+ to_view: 'jobs_to_view',
+ most: 'jobs_most',
+ most_count: 'jobs_most_count'
+ },
+ jobtotals: {
+ total_bytes: 'jobs_total_bytes',
+ total_files: 'jobs_total_files'
+ },
+ database: {
+ size: 'database_size'
+ },
+ pools: {
+ no: 'pools_no',
+ most: 'pools_most',
+ jobs: 'pools_jobs'
+ },
+ pie_summary: 'jobs_summary_graph'
+ },
+ update_all: function(statistics, txt) {
+ this.stats = statistics;
+ this.txt = txt;
+ this.update_pie_jobstatus();
+ this.update_clients();
+ this.update_job_access();
+ this.update_jobs();
+ this.update_jobtotals();
+ this.update_database();
+ this.update_pools();
+ },
+ update_clients: function() {
+ var clients = this.stats.clients_occupancy;
+ var most_occuped_client = this.noval;
+ var occupancy = -1;
+ for (client in clients) {
+ if (occupancy < clients[client]) {
+ most_occuped_client = client;
+ occupancy = clients[client];
+ }
+ }
+
+ if (occupancy === -1) {
+ occupancy = 0;
+ }
+
+ document.getElementById(this.ids.clients.no).textContent = Object.keys(this.stats.clients).length;
+ document.getElementById(this.ids.clients.most).setAttribute('title', most_occuped_client);
+ document.getElementById(this.ids.clients.most).textContent = Strings.get_short_label(most_occuped_client);
+ document.getElementById(this.ids.clients.jobs).textContent = occupancy;
+ },
+ update_job_access: function() {
+ var jobs_combobox= document.getElementById(this.ids.jobs.to_view);
+ jobs_combobox.innerHTML = '';
+ var last_jobs = this.stats.jobs.slice(0, 100);
+ for (var i = 0; i < last_jobs.length; i++) {
+ var opt = document.createElement('OPTION');
+ var txt = '[' + last_jobs[i].jobid + '] ' + last_jobs[i].name + ' (' + this.txt.level + ': ' + last_jobs[i].level + ' ' + this.txt.status + ': ' + last_jobs[i].jobstatus + ' ' + this.txt.starttime + ': ' + last_jobs[i].starttime + ')';
+ var label = document.createTextNode(txt);
+ opt.value = last_jobs[i].jobid;
+ opt.appendChild(label);
+ jobs_combobox.appendChild(opt);
+ }
+ },
+ update_jobs: function() {
+ var jobs = this.stats.jobs_occupancy;
+ var most_occuped_job = this.noval;
+ var occupancy = -1;
+ for (job in jobs) {
+ if (occupancy < jobs[job]) {
+ most_occuped_job = job;
+ occupancy = jobs[job];
+ }
+ }
+
+ if (occupancy === -1) {
+ occupancy = 0;
+ }
+
+ document.getElementById(this.ids.jobs.most).setAttribute('title',most_occuped_job);
+ document.getElementById(this.ids.jobs.most).textContent = Strings.get_short_label(most_occuped_job);
+ document.getElementById(this.ids.jobs.most_count).textContent = occupancy;
+ },
+ update_jobtotals: function() {
+ document.getElementById(this.ids.jobtotals.total_bytes).textContent = Units.get_decimal_size(this.stats.jobtotals.bytes);
+ document.getElementById(this.ids.jobtotals.total_files).textContent = this.stats.jobtotals.files || 0;
+ },
+ update_database: function() {
+ document.getElementById(this.ids.database.size).textContent = Units.get_decimal_size(this.stats.dbsize);
+ },
+ update_pools: function() {
+ var pools = this.stats.pools_occupancy;
+ var most_occuped_pool = this.noval;
+ var occupancy = -1;
+ for (pool in pools) {
+ if (occupancy < pools[pool]) {
+ most_occuped_pool = pool;
+ occupancy = pools[pool];
+ }
+ }
+
+ if (occupancy === -1) {
+ occupancy = 0;
+ }
+
+ document.getElementById(this.ids.pools.no).textContent = Object.keys(this.stats.pools).length;
+ document.getElementById(this.ids.pools.most).setAttribute('title', most_occuped_pool);
+ document.getElementById(this.ids.pools.most).textContent = Strings.get_short_label(most_occuped_pool);
+ document.getElementById(this.ids.pools.jobs).textContent = occupancy;
+ },
+ update_pie_jobstatus: function() {
+ if (PanelWindow.currentWindowId === 'dashboard') {
+ if (this.pie != null) {
+ this.pie.pie.destroy();
+ }
+ this.pie = new GraphPieClass(this.stats.jobs_summary, this.ids.pie_summary);
+ }
+ }
+}
var PanelWindowClass = Class.create({
- currentWindow: null,
- windowElements: ['container', 'graphs'],
+ currentWindowId: null,
+ windowIds: ['dashboard', 'container', 'graphs'],
onShow: null,
+ initialize: function() {
+ this.currentWindowId = this.windowIds[0];
+ },
+
hideOthers: function() {
- this.windowElements.each(function(element) {
- if(element != this.currentWindow) {
- Effect.toggle(element, 'slide', {duration: 0.3, afterFinish : function() {
- $(element).hide();
- }.bind(element)
+ var hide_panel_by_id = function(id) {
+ var el = $(id);
+ if(el.visible() === true && id != this.currentWindowId) {
+ Effect.toggle(el, 'slide', {
+ duration: 0.3,
+ afterFinish: function() {
+ el.hide();
+ }.bind(el)
});
}
- }.bind(this));
+ }
+ for (var i = 0, j = 1; i < this.windowIds.length; i++, j++) {
+ hide_panel_by_id(this.windowIds[i]);
+ }
},
show: function(id) {
return;
}
- this.currentWindow = id;
+ this.currentWindowId = id;
Effect.toggle(id, 'slide', {
duration: 0.3,
beforeStart: function() {
if (this.onShow) {
this.onShow();
}
+ setContentWidth();
}.bind(this)
});
}
},
setInitElementId: function(id) {
this.initElementId = id;
+ },
+ quickJumpToElement: function(id, btn_id, panel_obj) {
+ this.setInitElementId(id);
+ panel_obj.show('container');
+ if (this.isWindowOpen() === true) {
+ this.openConfigurationById(id);
+ } else {
+ $(btn_id).click();
+ }
}
});
--- /dev/null
+var Statistics = {
+ jobs: null,
+ clients: null,
+ pools: null,
+ jobtotals: null,
+ dbsize: null,
+ clients_occupancy: {},
+ pools_occupancy: {},
+ jobs_summary: [],
+ grab_statistics: function(data, jobstates) {
+ this.jobs = data.jobs;
+ this.clients = data.clients;
+ this.pools = data.pools;
+ this.jobtotals = data.jobtotals;
+ this.dbsize = data.dbsize;
+ var jobs_count = this.jobs.length;
+ var clients_occupancy = {};
+ var pools_occupancy = {};
+ var jobs_occupancy = {};
+ var jobs_summary = {
+ ok: [],
+ error: [],
+ warning: [],
+ cancel: [],
+ running: []
+ };
+ var status_type;
+ for (var i = 0; i < jobs_count; i++) {
+ if (typeof(clients_occupancy[this.jobs[i].clientid]) === 'undefined') {
+ clients_occupancy[this.jobs[i].clientid] = 1;
+ } else {
+ clients_occupancy[this.jobs[i].clientid] += 1;
+ }
+
+ if (typeof(pools_occupancy[this.jobs[i].poolid]) === 'undefined') {
+ pools_occupancy[this.jobs[i].poolid] = 1;
+ } else {
+ pools_occupancy[this.jobs[i].poolid] += 1;
+ }
+
+ if (typeof(jobs_occupancy[this.jobs[i].name]) === 'undefined') {
+ jobs_occupancy[this.jobs[i].name] = 1;
+ } else {
+ jobs_occupancy[this.jobs[i].name] += 1;
+ }
+ if (jobstates.hasOwnProperty(this.jobs[i].jobstatus)) {
+ status_type = jobstates[this.jobs[i].jobstatus].type;
+ if (status_type == 'ok' && this.jobs[i].joberrors > 0) {
+ status_type = 'warning';
+ }
+ jobs_summary[status_type].push(this.jobs[i]);
+ }
+ }
+ var clients_ids = Object.keys(clients_occupancy);
+ for (var i = 0; i < clients_ids.length; i++) {
+ for (var j = 0; j < this.clients.length; j++) {
+ if (clients_ids[i] == this.clients[j].clientid) {
+ this.clients_occupancy[this.clients[j].name] = clients_occupancy[clients_ids[i]];
+ }
+ }
+ }
+
+ var pools_ids = Object.keys(pools_occupancy);
+ for (var i = 0; i < pools_ids.length; i++) {
+ for (var j = 0; j < this.pools.length; j++) {
+ if (pools_ids[i] == this.pools[j].poolid) {
+ this.pools_occupancy[this.pools[j].name] = pools_occupancy[pools_ids[i]];
+ }
+ }
+ }
+
+ this.jobs_occupancy = jobs_occupancy;
+ this.jobs_summary = jobs_summary;
+ }
+}
msgid "Media Type"
msgstr "Media Type"
+msgid "Dashboard"
+msgstr "Dashboard"
+
msgid "Go to started job after start:"
msgstr "Go to started job after start:"
+msgid "Jobs status summary"
+msgstr "Jobs status summary"
+
+msgid "Quick job access"
+msgstr "Quick job access"
+
+msgid "Finished jobs"
+msgstr "Finished jobs"
+
+msgid "Most often used:"
+msgstr "Most often used:"
+
+msgid "Number of clients:"
+msgstr "Number of clients:"
+
+msgid "jobs"
+msgstr "jobs"
+
+msgid "Jobs count on most used:"
+msgstr "Jobs count on most used:"
+
+msgid "level"
+msgstr "level"
+
+msgid "status"
+msgstr "status"
+
+msgid "Jump to job (last 100):"
+msgstr "Jump to job (last 100):"
+
+msgid "View"
+msgstr "View"
+
+msgid "Total bytes:"
+msgstr "Total bytes:"
+
+msgid "Total files:"
+msgstr "Total files:"
+
+msgid "Database size:"
+msgstr "Database size:"
+
+msgid "Number of pools:"
+msgstr "Number of pools:"
+
+msgid "times"
+msgstr "times"
+
+msgid "Execution count most used:"
+msgstr "Execution count most used:"
+
+msgid "Job Totals"
+msgstr "Job Totals"
+
+msgid "Restore Wizard"
+msgstr "Restore Wizard"
+
+msgid "Perform Restore"
+msgstr "Perform Restore"
+
+msgid "Configuration Wizard"
+msgstr "Configuration Wizard"
+
+msgid "Baculum Settings"
+msgstr "Baculum Settings"
+
+msgid "start time"
+msgstr "start time"
+
msgid "Media Type"
msgstr "Media Type"
+msgid "Dashboard"
+msgstr "Start"
+
msgid "Go to started job after start:"
msgstr "Przejdź do uruchomionego zadania po starcie:"
+msgid "Jobs status summary"
+msgstr "Podsumowanie statusu zadań"
+
+msgid "Quick job access"
+msgstr "Quick job access"
+
+msgid "Finished jobs"
+msgstr "Zakończone zadania"
+
+msgid "Most often used:"
+msgstr "Najczęściej używany:"
+
+msgid "Number of clients:"
+msgstr "Ilość klientów:"
+
+msgid "jobs"
+msgstr "zadań"
+
+msgid "Jobs count on most used:"
+msgstr "Ilość zadań na najczęściej używanym:"
+
+msgid "level"
+msgstr "poziom"
+
+msgid "status"
+msgstr "status"
+
+msgid "Jump to job (last 100):"
+msgstr "Skocz do zadania (ostatnie 100):"
+
+msgid "View"
+msgstr "Zobacz"
+
+msgid "Total bytes:"
+msgstr "Całkowita ilość danych:"
+
+msgid "Total files:"
+msgstr "Całkowita ilość plików:"
+
+msgid "Database size:"
+msgstr "Rozmiar bazy danych:"
+
+msgid "Number of pools:"
+msgstr "Ilość puli wolumenów:"
+
+msgid "times"
+msgstr "razy"
+
+msgid "Execution count most used:"
+msgstr "Ilość wykonań najczęściej używanego:"
+
+msgid "Job Totals"
+msgstr "Podsumowanie zadań"
+
+msgid "Restore Wizard"
+msgstr "Kreator przywracania danych"
+
+msgid "Perform Restore"
+msgstr "Wykonaj przywracanie danych"
+
+msgid "Configuration Wizard"
+msgstr "Kreator Konfiguracji"
+
+msgid "Baculum Settings"
+msgstr "Ustawienia Baculum"
+
+msgid "start time"
+msgstr "czas rozpoczęcia"
+
<com:TClientScript ScriptUrl=<%~ ../JavaScript/opentip.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/excanvas.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/tooltip.js %> />
+ <com:TClientScript ScriptUrl=<%~ ../JavaScript/misc.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/slide-window.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/configuration-window.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/panel-window.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/flotr2.js %> />
<com:TClientScript ScriptUrl=<%~ ../JavaScript/graph.js %> />
- <com:TClientScript ScriptUrl=<%~ ../JavaScript/misc.js %> />
+ <com:TClientScript ScriptUrl=<%~ ../JavaScript/statistics.js %> />
<com:TContentPlaceHolder ID="Main" />
</com:TForm>
</body>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2015 Marcin Haba
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+class DbSize extends BaculumAPI {
+ public function get() {
+ $dbsize = $this->getModule('db')->getDatabaseSize();
+ $this->output = $dbsize;
+ $this->error = JobError::ERROR_NO_ERRORS;
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2015 Marcin Haba
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+class JobTotals extends BaculumAPI {
+ public function get() {
+ $jobtotals = $this->getModule('job')->getJobTotals();
+ $this->output = $jobtotals;
+ $this->error = JobError::ERROR_NO_ERRORS;
+ }
+}
+?>
<%@ MasterClass="Application.Layouts.Main" Theme="Baculum-v1"%>
<com:TContent ID="Main">
<div id="top">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo.png" alt="Baculum" />
+ <img id="logo" src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo.png" alt="Baculum" />
<div id="directors"><com:TLabel ForControl="Director" Text="<%[ Director: ]%>" />
<com:TActiveDropDownList ID="Director" OnTextChanged="director" />
</div>
<div id="panel_switcher">
- <com:TActiveLinkButton ID="Workspace" Text="<%[ Workspace ]%>" Attributes.onclick="PanelWindow.show('container');" />
- <com:TActiveLinkButton ID="Graphs" Text="<%[ Graphs ]%>" Attributes.onclick="PanelWindow.show('graphs');" />
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/dashboard.png" alt="" onclick="$('<%=$this->Dashboard->ClientID%>').click()" />
+ <com:TActiveLinkButton ID="Dashboard" Text="<%[ Dashboard ]%>" Attributes.onclick="PanelWindow.show('dashboard'); return false;" />
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/workspace.png" alt="" onclick="$('<%=$this->Workspace->ClientID%>').click()"/>
+ <com:TActiveLinkButton ID="Workspace" Text="<%[ Workspace ]%>" Attributes.onclick="PanelWindow.show('container'); return false;" />
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/graphs.png" alt="" onclick="$('<%=$this->Graphs->ClientID%>').click()" />
+ <com:TActiveLinkButton ID="Graphs" Text="<%[ Graphs ]%>" Attributes.onclick="PanelWindow.show('graphs'); return false;" />
</div>
<com:Application.Portlets.TrayBar ID="TrayBar" />
</div>
- <div id="container">
+ <div id="dashboard">
+ <div>
+ <div class="dashboard_graph">
+ <div id="jobs_summary_graph">
+ <script type="text/javascript">
+ var oJobsStates = <%=$this->jobs_states%>;
+ </script>
+ </div>
+ <span><%[ Jobs status summary ]%></span>
+ </div>
+ <div id="dashboard_icons">
+ <fieldset class="dashboard_field">
+ <legend><%[ Clients ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/client.png" alt="" />
+ <p><span><%[ Number of clients: ]%></span><span id="clients_no"></span></p>
+ <p><span><%[ Most often used: ]%></span><span id="clients_most"></span></p>
+ <p><span><%[ Jobs count on most used: ]%></span><span id="clients_jobs"></span> <%[ jobs ]%></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Quick job access ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/quick-access.png" alt="" />
+ <p><span><%[ Jump to job (last 100): ]%></span></p><select id="jobs_to_view"></select>
+ <p class="right"><a href="javascript:void(0)" onclick="SlideWindow.getObj('JobWindow').quickJumpToElement($('jobs_to_view').value, '<%=$this->JobBtn->ClientID%>', PanelWindow);"><%[ View ]%></a></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Finished jobs ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/finished-jobs.png" alt="" />
+ <p><span><%[ Most often used: ]%></span><span id="jobs_most"></span></p>
+ <p><span><%[ Execution count most used: ]%></span><span id="jobs_most_count"></span> <%[ times ]%></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Database ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/database.png" alt="" />
+ <p><span><%[ Database type: ]%></span><span><%=$this->dbtype%></span></p>
+ <p><span><%[ Database size: ]%></span><span id="database_size"></span></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Pools ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/tapes.png" alt="" />
+ <p><span><%[ Number of pools: ]%></span><span id="pools_no"></span></p>
+ <p><span><%[ Most often used: ]%></span><span id="pools_most"></span></p>
+ <p><span><%[ Jobs count on most used: ]%></span><span id="pools_jobs"></span> <%[ jobs ]%></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Job Totals ]%></legend>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/jobtotals.png" alt="" />
+ <p><span><%[ Total bytes: ]%></span><span id="jobs_total_bytes"></span></p>
+ <p><span><%[ Total files: ]%></span><span id="jobs_total_files"></span></p>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Restore Wizard ]%></legend>
+ <a class="big" href="<%=$this->Service->constructUrl('RestoreWizard')%>" style="line-height: 73px; margin: 0 45px"><img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/restore.png" alt="" /><%[ Perform Restore ]%></a>
+ </fieldset>
+ <fieldset class="dashboard_field">
+ <legend><%[ Configuration Wizard ]%></legend>
+ <a class="big" href="<%=$this->Service->constructUrl('ConfigurationWizard')%>" style="line-height: 73px; margin: 0 45px"><img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/setting.png" alt="" /><%[ Baculum Settings ]%></a>
+ </fieldset>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ <div id="container" style="display: none;">
<div id="menu-left">
<com:TActiveButton ID="StorageBtn" CssClass="storage-btn">
<prop:Attributes.onmouseover>
var graph;
document.observe("dom:loaded", function() {
PanelWindow.onShow = function() {
- if(typeof(graph) == "undefined") {
+ if (PanelWindow.currentWindowId === 'graphs' && typeof(graph) == "undefined") {
graph = new GraphClass(jobs, graph_lang, 'graphs_content', 'legend', 'time_range', '<%=$this->DateFrom->ClientID%>', '<%=$this->DateTo->ClientID%>', '<%=$this->Clients->ClientID%>', '<%=$this->Jobs->ClientID%>');
+ } else if (PanelWindow.currentWindowId === 'dashboard') {
+ Dashboard.update_all(Statistics, TEXT);
}
};
});
'SlideWindow.getObj("' . $this->initWindowId . 'Window").setInitElementId("' . $this->initElementId . '");'
: '')%>
<%=(!is_null($this->openWindow) ?
- '$("' . $this->openWindow . '").click(); window.history.pushState("", "", "/");'
+ 'PanelWindow.show("container");
+ $("' . $this->openWindow . '").click();
+ window.history.pushState("", "", "/");'
: '')%>
});
+ var TEXT = {
+ level: '<%[ level ]%>',
+ status: '<%[ status ]%>',
+ starttime: '<%[ start time ]%>'
+ }
</script>
</com:TContent>
public $initElementId = null;
+ public $jobs_states = null;
+
+ public $dbtype = null;
+
public $windowIds = array('Storage', 'Client', 'Volume', 'Pool', 'Job', 'JobRun');
public function onInit($param) {
if(!$this->IsPostBack && !$this->IsCallBack) {
$directors = $this->getModule('api')->get(array('directors'))->output;
- if(!array_key_exists('director', $_SESSION)) {
+ if(!array_key_exists('director', $_SESSION) || $directors[0] != $_SESSION['director']) {
$_SESSION['director'] = $directors[0];
}
$this->Director->dataSource = array_combine($directors, $directors);
$this->Director->SelectedValue = $_SESSION['director'];
$this->Director->dataBind();
+ $this->dbtype = $this->getModule('configuration')->getDbNameByType($appConfig['db']['type']);
+ $this->setJobsStates();
$this->setJobs();
$this->setClients();
$this->setWindowOpen();
return json_encode($this->jobs);
}
+ public function setJobsStates() {
+ $jobs_summary = array(
+ 'ok' => array(),
+ 'error' => array(),
+ 'warning' => array(),
+ 'cancel' => array(),
+ 'running' => array()
+ );
+ $job_types = $jobs_summary;
+ $job_states = array();
+
+ $misc = $this->getModule('misc');
+ foreach($job_types as $type => $arr) {
+ $states = $misc->getJobStatesByType($type);
+ foreach($states as $state => $desc) {
+ $desc['type'] = $type;
+ $jobs_states[$state] = $desc;
+ }
+ }
+
+ $this->jobs_states = json_encode($jobs_states);
+ }
+
public function setJobs() {
$this->jobs = $this->getModule('api')->get(array('jobs'));
$jobs = array('@' => Prado::localize('select job'));
}
$this->Clients->dataSource = $clients;
$this->Clients->dataBind();
-
}
public function setWindowOpen() {
class Monitor extends BaculumPage {
public function onInit($param) {
parent::onInit($param);
- $_SESSION['monitor_data'] = array('jobs' => array(), 'running_jobs' => array(), 'terminated_jobs' => array());
+ $_SESSION['monitor_data'] = array(
+ 'jobs' => array(),
+ 'running_jobs' => array(),
+ 'terminated_jobs' => array(),
+ 'pools' => array(),
+ 'jobtotals' => array(),
+ 'dbsize' => 0
+ );
+
$_SESSION['monitor_data']['jobs'] = $this->Application->getModule('api')->get(array('jobs'))->output;
+ $_SESSION['monitor_data']['clients'] = $this->getModule('api')->get(array('clients'))->output;
+ $_SESSION['monitor_data']['pools'] = $this->getModule('api')->get(array('pools'))->output;
+ $_SESSION['monitor_data']['jobtotals'] = $this->getModule('api')->get(array('jobs', 'totals'))->output;
+ $_SESSION['monitor_data']['dbsize'] = $this->getModule('api')->get(array('dbsize'))->output;
$runningJobStates = $this->Application->getModule('misc')->getRunningJobStates();
var timeout_handler;
var last_callback_time = 0;
var callback_time_offset = 0;
+ var oData;
document.observe("dom:loaded", function() {
oMonitor = function() {
return new Ajax.Request('<%=$this->Service->constructUrl("Monitor")%>', {
if (timeout_handler) {
clearTimeout(timeout_handler);
}
- var jobs = (response.responseText).evalJSON();
- if (jobs.running_jobs.length > 0) {
+ oData = (response.responseText).evalJSON();
+ Statistics.grab_statistics(oData, oJobsStates);
+
+ if (PanelWindow.currentWindowId === 'dashboard') {
+ Dashboard.update_all(Statistics, TEXT);
+ }
+
+ if (oData.running_jobs.length > 0) {
refreshInterval = callback_time_offset + default_fast_refresh_interval;
} else {
refreshInterval = default_refresh_interval;
}
job_callback_func();
status_callback_func();
- $('running_jobs').update(jobs.running_jobs.length);
- $('finished_jobs').update(jobs.terminated_jobs.length);
+ $('running_jobs').update(oData.running_jobs.length);
+ $('finished_jobs').update(oData.terminated_jobs.length);
timeout_handler = setTimeout("oMonitor()", refreshInterval);
}
});
<url ServiceParameter="API.JobsShow" pattern="jobs/show/" />
<url ServiceParameter="API.JobShow" pattern="jobs/show/{id}/" parameters.id="\d+" />
<url ServiceParameter="API.JobShow" pattern="jobs/show/name/{name}/" parameters.name=".+" />
+ <url ServiceParameter="API.JobTotals" pattern="jobs/totals/" />
<url ServiceParameter="API.RestoreRun" pattern="jobs/restore/" />
<!-- END Jobs -->
<!-- START BVFS -->
<url ServiceParameter="API.FileSets" pattern="filesets/" />
<url ServiceParameter="API.FileSet" pattern="filesets/{id}/" parameters.id="\d+" />
<url ServiceParameter="API.ConsoleCommand" pattern="console/" />
+ <url ServiceParameter="API.DbSize" pattern="dbsize/" />
</module>
<module id="globalization" class="TGlobalization">
<translation type="gettext" source="Application.Lang" marker="@@" autosave="false" cache="false" DefaultCulture="en" />
font-size: 11px;
}
-a {
+a, a.big {
font-size: 12px;
color: white;
padding: 3px 4px;
text-decoration: none;
}
-a:hover {
+a:hover, a.big {
text-decoration: underline;
}
+
+a.big {
+ font-size: 17px;
+ font-weight: bold;
+}
+
#container {
margin: 0 auto;
- min-width: 975px;
+ min-width: 987px;
max-width: 100%;
border-right: 1px solid black;
background-color: #585758;
#top {
margin: 0 auto;
- min-width: 975px;
+ min-width: 987px;
max-width: 100%;
height: 51px;
background-image: url('bls_top.png');
}
#top img {
- padding: 11px 0 11px 35px;
+ padding: 11px 0 11px 5px;
float: left;
}
+#logo {
+ margin-left: 20px;
+}
+
#menu-left {
position: absolute;
width: 74px;
}
#panel_switcher {
- margin: 17px 20px 0 0;
+ margin: 2px 6px 0 0;
font-weight: bold;
}
+#panel_switcher img {
+ margin: 0;
+ padding-left: 12px;
+ float: none;
+ cursor: pointer;
+}
+
#tray_bar {
- margin: 2px 30px 2px 0;
+ margin: 2px 0;
}
#tray_bar img {
float: none;
margin: 0;
- padding: auto 12px;
+ padding: auto 7px;
}
#tray_bar span{
#bottom {
max-width: 100%;
- min-width: 975px;
+ min-width: 987px;
height: 11px;
margin: 0 auto;
background: transparent url('bls_bottom.png') repeat-x top left;
background-color: rgb(163, 180, 197);
}
-#graphs {
- height: 600px;
+#graphs, #dashboard {
min-width: 954px;
max-width: 100%;
padding: 10px;
height: 425px;
}
+.dashboard_graph {
+ height: 450px;
+ text-align: center;
+ font-size: 12px;
+ color: white;
+ font-weight: bold;
+ float: right;
+}
+
+#dashboard_icons {
+ min-width: 500px;
+ min-height: 600px;
+}
+
+.dashboard_field {
+ max-width: 450px;
+ min-width: 320px;
+ height: 86px;
+ margin: 4px;
+ border-radius: 5px;
+ float: left;
+}
+
+.dashboard_field img {
+ float: left;
+}
+
+.dashboard_field p {
+ font-size: 10px;
+ margin-top: 5px;
+}
+
+.dashboard_field p span:nth-of-type(1) {
+ display: inline-block;
+ width: 122px;
+}
+
+.dashboard_field p span:nth-of-type(2) {
+ font-size: 12px;
+ padding-left: 5px;
+ font-weight: bold;
+}
+
+.dashboard_field a {
+ text-decoration: underline;
+}
+
+#jobs_summary_graph {
+ width: 380px;
+ height: 380px;
+}
+
+#jobs_to_view {
+ width: 240px;
+ margin: 0 2px 0 5px;
+}
+
/* Overwrite date picker classes */
.TDatePicker_default {
z-index: 20;