3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004 Juan Luis Francés Jiménez |
5 | Copyright 2010-2011, Davide Franco |
7 | This program is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU General Public License |
9 | as published by the Free Software Foundation; either version 2 |
10 | of the License, or (at your option) any later version. |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. |
16 +-------------------------------------------------------------------------+
23 public $db_link; // Database link
25 private $config_file; // Config filename
26 private $config; // Loaded config from bacula.conf
27 private $catalogs = array(); // Catalog array
29 private $catalog_current_id;
32 function __construct()
34 $this->bwcfg = new Config();
37 // Loading configuration from config file
38 $this->bwcfg->Load_Config();
39 $this->catalog_nb = $this->bwcfg->Count_Catalogs();
41 // Initialize smarty template classe
43 // Initialize smarty gettext function
44 $this->init_gettext();
47 if( isset($_POST['catalog_id']) ) {
48 $this->catalog_current_id = $_POST['catalog_id'];
49 $_SESSION['catalog_id'] = $this->catalog_current_id;
51 elseif( isset( $_SESSION['catalog_id'] ) )
52 $this->catalog_current_id = $_SESSION['catalog_id'];
54 $this->catalog_current_id = 0;
55 $_SESSION['catalog_id'] = $this->catalog_current_id;
58 $this->tpl->assign( 'catalog_current_id', $this->catalog_current_id );
61 $dsn = $this->bwcfg->Get_Dsn( $this->catalog_current_id );
63 // Connect to the database
64 $options = array( 'portability' => DB_PORTABILITY_ALL );
65 $this->db_link = $this->connect( $dsn, $options );
67 if (DB::isError($this->db_link)) {
68 $this->TriggerDBError('Unable to connect to catalog', $this->db_link);
70 $this->driver = $dsn['phptype'];
71 register_shutdown_function(array(&$this,'close') );
72 $this->db_link->setFetchMode(DB_FETCHMODE_ASSOC);
76 if( $this->catalog_nb > 1 ) {
78 $this->tpl->assign( 'catalogs', $this->bwcfg->Get_Catalogs() );
80 $this->tpl->assign( 'catalog_nb', $this->catalog_nb );
84 // Initialize Smarty template classe
87 $this->tpl = new Smarty();
89 $this->tpl->compile_check = true;
90 $this->tpl->debugging = false;
91 $this->tpl->force_compile = true;
93 $this->tpl->template_dir = "./templates";
94 $this->tpl->compile_dir = "./templates_c";
97 function init_gettext()
99 global $smarty_gettext_path;
101 if ( function_exists("gettext") ) {
102 require_once( BW_SMARTY_GETTEXT . "smarty_gettext.php" );
103 $this->tpl->register_block('t','smarty_translate');
105 $language = $this->bwcfg->Get_Config_Param("lang");
106 $domain = "messages";
107 putenv("LANG=$language");
108 setlocale(LC_ALL, $language);
109 bindtextdomain($domain,"./locale");
113 function smarty_translate($params, $text, &$smarty) {
116 $smarty->register_block('t','smarty_translate');
122 $this->db_link->disconnect();
130 switch( $this->driver )
133 $query = "SELECT table_schema AS 'database', sum( data_length + index_length) AS 'dbsize' ";
134 $query .= "FROM information_schema.TABLES ";
135 $query .= "WHERE table_schema = 'bacula' ";
136 $query .= "GROUP BY table_schema";
139 $query = "SELECT pg_database_size('bacula') AS dbsize";
142 // Not yet implemented
147 $result = $this->db_link->query( $query );
149 if(! PEAR::isError( $result ) )
151 $db = $result->fetchRow();
152 $database_size = $db['dbsize'];
154 $this->TriggerDBError( 'Unable to get database size', $result);
156 return CUtils::Get_Human_Size( $database_size );
157 } // end function GetDbSize()
159 public function Get_Nb_Clients()
161 $clients = $this->db_link->query("SELECT COUNT(*) AS nb_client FROM Client");
162 if( PEAR::isError($clients) )
163 $this->TriggerDBError("Unable to get client number", $clients );
165 return $clients->fetchRow( DB_FETCHMODE_ASSOC );
168 // Return an array of volumes ordered by poolid and volume name
169 function GetVolumeList() {
175 // Get the list of pools id
176 $query = "SELECT Pool.poolid, Pool.name FROM Pool ORDER BY Pool.poolid";
178 //$this->db_link->setFetchMode(DB_FETCHMODE_ASSOC);
179 $pools = $this->db_link->query( $query );
181 if( PEAR::isError( $pools ) )
182 $this->TriggerDBError("Failed to get pool list", $pools );
184 while( $pool = $pools->fetchRow( DB_FETCHMODE_ASSOC ) ) {
185 switch( $this->driver )
188 $query = "SELECT Media.volumename, Media.volbytes, Media.volstatus, Media.mediatype, Media.lastwritten, Media.volretention
189 FROM Media LEFT JOIN Pool ON Media.poolid = Pool.poolid
190 WHERE Media.poolid = '". $pool['poolid'] . "' ORDER BY Media.volumename";
193 $query = "SELECT media.volumename, media.volbytes, media.volstatus, media.mediatype, media.lastwritten, media.volretention
194 FROM media LEFT JOIN pool ON media.poolid = pool.poolid
195 WHERE media.poolid = '". $pool['poolid'] . "' ORDER BY media.volumename";
198 $query = ""; // not yet implemented
204 $medias = $this->db_link->query( $query );
206 if( PEAR::isError( $medias ) ) {
207 $this->TriggerDBError("Failed to get media list for pool", $medias);
209 if( $debug ) echo "Found " . $medias->numRows() . " medias for pool " . $pool['name'] . " <br />";
211 // Create array key for each pool
212 if( !array_key_exists( $pool['name'], $volumes) )
214 $volumes[ $pool['name'] ] = array();
216 while( $media = $medias->fetchRow( DB_FETCHMODE_ASSOC ) ) {
220 // If the pool is empty (no volumes in this pool)
221 if( $medias->numRows() == 0 ) {
222 if( $debug ) echo "No media in pool " . $pool['name'] . "<br />";
224 if( $media['lastwritten'] != "0000-00-00 00:00:00" ) {
225 // Calculate expiration date if the volume is Full
226 if( $media['volstatus'] == 'Full' ) {
227 $expire_date = strtotime($media['lastwritten']) + $media['volretention'];
228 $media['expire'] = strftime("%Y-%m-%d", $expire_date);
230 $media['expire'] = 'N/A';
232 // Media used bytes in a human format
233 $media['volbytes'] = CUtils::Get_Human_Size( $media['volbytes'] );
235 $media['lastwritten'] = "N/A";
236 $media['expire'] = "N/A";
237 $media['volbytes'] = "0 KB";
241 if( count( $volumes[ $pool['name'] ] ) % 2)
242 $media['class'] = 'odd';
244 // Add the media in pool array
245 array_push( $volumes[ $pool['name']], $media);
251 } // end function GetVolumeList()
253 public function CountJobsbyLevel( $delay = LAST_DAY, $level = 'F' )
255 $end_date = mktime();
256 $start_date = $end_date - $delay;
258 $start_date = date( "Y-m-d H:i:s", $start_date );
259 $end_date = date( "Y-m-d H:i:s", $end_date );
261 $query = "SELECT COUNT(JobId) as jobs FROM Job ";
262 $query .= "WHERE (EndTime BETWEEN '$start_date' AND '$end_date') AND ";
263 $query .= "Level = '$level' ";
265 $result = $this->db_link->query( $query );
267 if (PEAR::isError( $result ) ) {
268 $this->TriggerDBError( 'Unable to get number of jobs with ' . $level . ' status from catalog', $result);
270 $jobs = $result->fetchRow();
271 return $jobs['jobs'];
276 public function CountJobs( $delay = LAST_DAY, $status = 'any' )
278 $query = "SELECT COUNT(JobId) AS job_nb FROM Job ";
282 // Interval condition for SQL query
283 if( $delay != ALL ) {
284 $end_date = mktime();
285 $start_date = $end_date - $delay;
287 $start_date = date( "Y-m-d H:i:s", $start_date );
288 $end_date = date( "Y-m-d H:i:s", $end_date );
290 $where_delay = "WHERE EndTime BETWEEN '$start_date' AND '$end_date' ";
293 if( $status != 'any' ) {
297 $where_status = "JobStatus = 'T' ";
300 $where_status = "JobStatus IN ('f','E') ";
303 $where_status = "JobStatus = 'A' ";
306 $where_status = "JobStatus IN ('F','S','M','m','s','j','c','d','t') ";
311 if( !empty($where_delay) )
312 $query = $query . $where_delay . 'AND ' . $where_status;
314 if( !empty($where_status) )
315 $query = $query . 'WHERE ' . $where_status;
318 $jobs = $this->db_link->query( $query );
320 if (PEAR::isError( $jobs ) ) {
321 $this->TriggerDBError("Unable to get last $status jobs number from catalog", $jobs);
323 $jobs = $jobs->fetchRow( DB_FETCHMODE_ASSOC );
324 return $jobs['job_nb'];
328 // Return the list of Pools in a array
329 public function Get_Pools_List()
332 $query = "SELECT Name, PoolId FROM Pool";
333 $result = $this->db_link->query ( $query );
335 if( PEAR::isError( $result ) ) {
336 $this->TriggerDBError( "Unable to get the pool list from catalog", $result );
342 public function Get_BackupJob_Names()
346 switch( $this->driver )
350 $query = "SELECT name FROM Job GROUP BY name ORDER BY name";
353 $query = "SELECT name FROM Job GROUP BY name ORDER BY name";
357 $backupjobs = array();
359 $result = $this->db_link->query( $query );
361 if (PEAR::isError( $result ) ) {
362 $this->TriggerDBError("Unable to get BackupJobs list from catalog", $result );
364 while( $backupjob = $result->fetchRow() ) {
365 array_push( $backupjobs, $backupjob["name"] );
371 // Return Jobs statistics for a specific interval such as
372 // - Completed jobs number
373 // - Failed jobs number
374 // - Waiting jobs number
375 // The returned values will be used by a Bgraph classe
376 public function GetJobsStatistics( $type = 'completed', $delay = LAST_DAY )
384 // Interval calculation
385 $end_date = mktime();
386 $start_date = $end_date - $delay;
388 $start_date = date( "Y-m-d H:i:s", $start_date );
389 $end_date = date( "Y-m-d H:i:s", $end_date );
391 $interval_where = "(EndTime BETWEEN '$start_date' AND '$end_date') AND ";
397 $where = $interval_where . "JobStatus = 'T' ";
398 $label = "Completed";
400 case 'terminated_errors':
401 $where = $interval_where . "JobStatus = 'E' ";
402 $label = "Terminated with errors";
405 $where = $interval_where . "JobStatus = 'f' ";
409 $where = "JobStatus IN ('F','S','M','m','s','j','c','d','t') ";
413 $where = "JobStatus = 'C' ";
414 $label = "Created but not running";
417 $where = "JobStatus = 'R' ";
421 $where = $interval_where . "JobStatus IN ('e','f') ";
426 $query = 'SELECT COUNT(JobId) AS count ';
427 $query .= 'FROM Job ';
428 $query .= "WHERE $where ";
430 $res = $this->db_link->query( $query );
432 if (PEAR::isError( $res ) ) {
433 $this->TriggerDBError( 'Unable to get last' . $type . 'jobs status from catalog', $res);
435 $result = $res->fetchRow();
436 return array( $label, $result['count'] );
438 } // end function GetJobsStatistics()
440 public function CountVolumesByPool( $pool_id )
446 switch( $this->driver )
450 $query = 'SELECT COUNT(*) as vols,Pool.name as pool_name ';
451 $query .= 'FROM Media ';
452 $query .= 'RIGHT JOIN Pool ON (Media.PoolId = Pool.PoolId) ';
453 $query .= 'WHERE Media.poolid = ' . $pool_id;
456 $query = 'SELECT COUNT(*) as vols,Pool.name as pool_name ';
457 $query .= 'FROM Media ';
458 $query .= 'RIGHT JOIN Pool ON (Media.PoolId = Pool.PoolId) ';
459 $query .= 'WHERE Media.poolid = ' . $pool_id;
460 $query .= 'GROUP BY pool.name';
464 $res = $this->db_link->query( $query );
465 if( PEAR::isError( $res ) )
466 $this->triggerDBError( 'Unable to get volume number from pool', $res );
468 $vols = $res->fetchRow( );
470 return array( $vols['pool_name'], $vols['vols'] );
473 public function GetStoredFiles( $delay = LAST_DAY )
477 $query = "SELECT SUM(JobFiles) AS stored_files FROM Job ";
479 // Interval calculation
480 $end_date = mktime();
481 $start_date = $end_date - $delay;
483 $start_date = date( "Y-m-d H:i:s", $start_date );
484 $end_date = date( "Y-m-d H:i:s", $end_date );
487 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
489 $result = $this->db_link->query( $query );
491 if( !PEAR::isError($result) ) {
492 $nbfiles = $result->fetchRow(DB_FETCHMODE_ASSOC);
493 $totalfiles = $totalfiles + $nbfiles['stored_files'];
495 $this->TriggerDBError("Unable to get protected files from catalog", $result);
501 public function GetStoredBytes( $delay = LAST_DAY )
503 $query = "SELECT SUM(JobBytes) as stored_bytes FROM Job ";
505 // Interval calculation
506 $end_date = mktime();
507 $start_date = $end_date - $delay;
509 $start_date = date( "Y-m-d H:i:s", $start_date );
510 $end_date = date( "Y-m-d H:i:s", $end_date );
513 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
515 $result = $this->db_link->query( $query );
517 if( PEAR::isError( $result ) ) {
518 $this->TriggerDBError("Unable to get Job Bytes from catalog", $result );
520 return $result->fetchRow( DB_FETCHMODE_ASSOC );
524 public function GetStoredBytesByInterval( $start_date, $end_date )
528 switch($this->driver) {
531 $query = "SELECT SUM(JobBytes) as stored_bytes FROM Job WHERE (EndTime BETWEEN '$start_date' AND '$end_date')";
534 $query = "SELECT SUM(JobBytes) as stored_bytes FROM Job WHERE (EndTime BETWEEN '$start_date' AND '$end_date')";
538 $result = $this->db_link->query( $query );
540 if( PEAR::isError( $result ) ) {
541 $this->TriggerDBError( "Unable to get Job Bytes from catalog", $result );
544 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
546 $day = date( "D d", strtotime($end_date) );
548 if( isset( $tmp['stored_bytes'] ) ) {
549 $hbytes = CUtils::Get_Human_Size( $tmp['stored_bytes'], 3, 'GB' );
550 $hbytes = explode( " ", $hbytes );
551 $stored_bytes = $hbytes[0];
554 return array( $day, $stored_bytes );
558 public function GetStoredBytesByJob( $jobname, $start_date, $end_date )
562 switch( $this->driver )
566 $query = "SELECT SUM(JobBytes),EndTime as stored_bytes FROM Job ";
567 $query .= "WHERE ( EndTime BETWEEN '$start_date' AND '$end_date' ) AND ";
568 $query .= "Name = '$jobname'";
569 $query .= "GROUP BY EndTime";
572 $query = "SELECT SUM(jobbytes),endtime as stored_bytes FROM job ";
573 $query .= "WHERE ( endtime BETWEEN timestamp '$start_date' AND timestamp '$end_date' ) AND ";
574 $query .= "name = '$jobname'";
575 $query .= "GROUP BY EndTime";
579 $result = $this->db_link->query( $query );
581 if( PEAR::isError( $result ) ) {
582 $this->TriggerDBError("Unable to get Job Bytes from catalog", $result );
585 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
587 $day = date( "D d", strtotime($end_date) );
589 if( isset( $tmp['stored_bytes'] ) ) {
590 $hbytes = CUtils::Get_Human_Size( $tmp['stored_bytes'], 3, 'GB' );
591 $hbytes = explode( " ", $hbytes );
592 $stored_bytes = $hbytes[0];
595 return array( $day, $stored_bytes );
599 public function GetStoredFilesByJob( $jobname, $start_date, $end_date )
603 switch( $this->driver )
607 $query = "SELECT SUM(JobFiles),EndTime as stored_bytes FROM Job ";
608 $query .= "WHERE ( EndTime BETWEEN '$start_date' AND '$end_date' ) AND ";
609 $query .= "Name = '$jobname'";
610 $query .= "GROUP BY EndTime";
613 $query = "SELECT SUM(jobfiles),endtime as stored_bytes FROM job ";
614 $query .= "WHERE ( endtime BETWEEN timestamp '$start_date' AND timestamp '$end_date' ) AND ";
615 $query .= "name = '$jobname'";
616 $query .= "GROUP BY EndTime";
620 $result = $this->db_link->query( $query );
622 if( PEAR::isError( $result ) ) {
623 $this->TriggerDBError("Unable to get Job Files from catalog", $result);
626 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
628 $day = date( "D d", strtotime($end_date) );
629 $stored_files = $tmp['stored_files'];
631 return array( $day, $stored_files );
635 private function TriggerDBError( $message, $db_error)
637 echo 'Error: ' . $message . '<br />';
638 echo 'Standard Message: ' . $db_error->getMessage() . '<br />';
639 echo 'Standard Code: ' . $db_error->getCode() . '<br />';
640 echo 'DBMS/User Message: ' . $db_error->getUserInfo() . '<br />';
641 echo 'DBMS/Debug Message: ' . $db_error->getDebugInfo() . '<br />';