3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004-2005 Juan Luis Frances Jiminez |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU General Public License |
8 | as published by the Free Software Foundation; either version 2 |
9 | of the License, or (at your option) any later version. |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
15 +-------------------------------------------------------------------------+
17 require_once "config.inc.php";
19 class Bweb extends DB {
27 public $db_link; // Database link
29 private $config_file; // Config filename
30 private $config; // Loaded config from bacula.conf
31 private $catalogs = array(); // Catalog array
35 function __construct()
37 $this->bwcfg = new BW_Config();
40 // Checking if config file exist and is readable
41 if( !$this->bwcfg->Check_Config_File() )
42 die( "Unable to load configuration file" );
44 $this->bwcfg->Load_Config();
45 $this->catalog_nb = $this->bwcfg->Count_Catalogs();
48 // Select which catalog to connect to
49 if( isset( $_POST['catalog_id'] ) )
50 $dsn = $this->bwcfg->Get_Dsn( $_POST['catalog_id'] );
52 $dsn = $this->bwcfg->Get_Dsn( 0 );
54 // Connect to the database
55 $this->db_link = $this->connect( $dsn );
57 if (DB::isError($this->db_link)) {
58 die( 'Unable to connect to catalog <br />' . $this->db_link->getMessage());
60 $this->driver = $dsn['phptype'];
61 register_shutdown_function(array(&$this,'close') );
64 // Initialize smarty template classe
66 // Initialize smarty gettext function
67 $this->init_gettext();
70 if( $this->catalog_nb > 1 ) {
71 // Set current catalog in header template
72 if(isset( $_POST['catalog_id'] ) )
73 $this->tpl->assign( 'catalog_current', $_POST['catalog_id'] );
75 $this->tpl->assign( 'catalogs', $this->bwcfg->Get_Catalogs() );
82 // Initialize Smarty template classe
85 $this->tpl = new Smarty();
87 $this->tpl->compile_check = true;
88 $this->tpl->debugging = false;
89 $this->tpl->force_compile = true;
91 $this->tpl->template_dir = "./templates";
92 $this->tpl->compile_dir = "./templates_c";
93 $this->tpl->config_dir = "./configs";
96 function init_gettext()
98 global $smarty_gettext_path;
100 if ( function_exists("gettext") ) {
101 require_once( BW_SMARTY_GETTEXT . "smarty_gettext.php" );
102 $this->tpl->register_block('t','smarty_translate');
104 $language = $this->bwcfg->Get_Config_Param("lang");
105 $domain = "messages";
106 putenv("LANG=$language");
107 setlocale(LC_ALL, $language);
108 bindtextdomain($domain,"./locale");
112 function smarty_translate($params, $text, &$smarty) {
115 $smarty->register_block('t','smarty_translate');
121 $this->db_link->disconnect();
124 // Return humanized size with default unit of GB
125 // if auto provide for unit argument, automaticaly decide which unit
126 function human_file_size( $size, $decimal = 2, $unit = 'auto' )
130 $units = array('B','KB','MB','GB','TB');
137 if ( $hsize >= 1024 ) {
138 $hsize = $hsize / 1024;
147 $p = array_search( $unit, $units);
148 $hsize = $hsize / pow(1024,$p);
152 $hsize = sprintf("%." . $decimal . "f", $hsize);
153 $hsize = $hsize . ' ' . $units[$unit_id];
163 switch( $this->driver )
166 $query = "SELECT table_schema AS 'database', sum( data_length + index_length) AS 'dbsize' ";
167 $query .= "FROM information_schema.TABLES ";
168 $query .= "WHERE table_schema = 'bacula' ";
169 $query .= "GROUP BY table_schema";
172 $query = "SELECT pg_database_size('bacula') AS dbsize";
175 // Not yet implemented
180 $result = $this->db_link->query( $query );
182 if(! PEAR::isError( $result ) )
184 $db = $result->fetchRow( DB_FETCHMODE_ASSOC );
185 $database_size =+ $db['dbsize'];
187 die( "Unable to get database size<br />" . $result->getMessage() );
189 return $this->human_file_size( $database_size );
190 } // end function GetDbSize()
192 public function Get_Nb_Clients()
194 $clients = $this->db_link->query("SELECT COUNT(*) AS nb_client FROM Client");
195 if( PEAR::isError($clients) )
196 die( "Unable to get client number" );
198 return $clients->fetchRow( DB_FETCHMODE_ASSOC );
201 // Return an array of volumes ordered by poolid and volume name
202 function GetVolumeList() {
208 // Get the list of pools id
209 $query = "SELECT Pool.poolid, Pool.name FROM Pool ORDER BY Pool.poolid";
211 //$this->db_link->setFetchMode(DB_FETCHMODE_ASSOC);
212 $pools = $this->db_link->query( $query );
214 if( PEAR::isError( $pools ) )
215 die("Error: Failed to get pool list <br />SQL Query: $query<br />" . $pools->getMessage() );
217 while( $pool = $pools->fetchRow( DB_FETCHMODE_ASSOC ) ) {
218 switch( $this->driver )
221 $query = "SELECT Media.volumename, Media.volbytes, Media.volstatus, Media.mediatype, Media.lastwritten, Media.volretention
222 FROM Media LEFT JOIN Pool ON Media.poolid = Pool.poolid
223 WHERE Media.poolid = '". $pool['poolid'] . "' ORDER BY Media.volumename";
226 $query = "SELECT media.volumename, media.volbytes, media.volstatus, media.mediatype, media.lastwritten, media.volretention
227 FROM media LEFT JOIN pool ON media.poolid = pool.poolid
228 WHERE media.poolid = '". $pool['poolid'] . "' ORDER BY media.volumename";
231 $query = ""; // not yet implemented
237 $medias = $this->db_link->query( $query );
239 if( PEAR::isError( $medias ) ) {
240 die( "Failed to get media list for pool $volume[0] <br /> " . $medias->getMessage() );
242 if( $debug ) echo "Found " . $medias->numRows() . " medias for pool " . $pool['name'] . " <br />";
244 // Create array key for each pool
245 if( !array_key_exists( $pool['name'], $volumes) )
247 $volumes[ $pool['name'] ] = array();
249 while( $media = $medias->fetchRow( DB_FETCHMODE_ASSOC ) ) {
253 // If the pool is empty (no volumes in this pool)
254 if( $medias->numRows() == 0 ) {
255 if( $debug ) echo "No media in pool " . $pool['name'] . "<br />";
257 if( $media['lastwritten'] != "0000-00-00 00:00:00" ) {
258 // Calculate expiration date if the volume is Full
259 if( $media['volstatus'] == 'Full' ) {
260 $expire_date = strtotime($media['lastwritten']) + $media['volretention'];
261 $media['expire'] = strftime("%Y-%m-%d", $expire_date);
263 $media['expire'] = 'N/A';
265 // Media used bytes in a human format
266 $media['volbytes'] = $this->human_file_size( $media['volbytes'] );
268 $media['lastwritten'] = "N/A";
269 $media['expire'] = "N/A";
270 $media['volbytes'] = "0 KB";
274 if( count( $volumes[ $pool['name'] ] ) % 2)
275 $media['class'] = 'odd';
277 // Add the media in pool array
278 array_push( $volumes[ $pool['name']], $media);
284 } // end function GetVolumeList()
286 public function CountJobsbyLevel( $delay = LAST_DAY, $level = 'F' )
288 $end_date = mktime();
289 $start_date = $end_date - $delay;
291 $start_date = date( "Y-m-d H:i:s", $start_date );
292 $end_date = date( "Y-m-d H:i:s", $end_date );
294 $query = "SELECT COUNT(JobId) as jobs FROM Job ";
295 $query .= "WHERE (EndTime BETWEEN '$start_date' AND '$end_date') AND ";
296 $query .= "Level = '$level' ";
298 $result = $this->db_link->query( $query );
300 if (PEAR::isError( $result ) ) {
301 die( "Unable to get number of jobs with $level status from catalog <br />" . $result->getMessage() );
303 $jobs = $result->fetchRow( DB_FETCHMODE_ASSOC );
304 return $jobs['jobs'];
309 public function CountJobs( $delay = LAST_DAY, $status = 'any' )
311 $query = "SELECT COUNT(JobId) AS job_nb FROM Job ";
315 // Interval condition for SQL query
316 if( $delay != ALL ) {
317 $end_date = mktime();
318 $start_date = $end_date - $delay;
320 $start_date = date( "Y-m-d H:i:s", $start_date );
321 $end_date = date( "Y-m-d H:i:s", $end_date );
323 $where_delay = "WHERE EndTime BETWEEN '$start_date' AND '$end_date' ";
326 if( $status != 'any' ) {
330 $where_status = "JobStatus = 'T' ";
333 $where_status = "JobStatus IN ('f','E') ";
336 $where_status = "JobStatus = 'A' ";
339 $where_status = "JobStatus IN ('F','S','M','m','s','j','c','d','t') ";
344 if( !empty($where_delay) )
345 $query = $query . $where_delay . 'AND ' . $where_status;
347 if( !empty($where_status) )
348 $query = $query . 'WHERE ' . $where_status;
351 $jobs = $this->db_link->query( $query );
353 if (PEAR::isError( $jobs ) ) {
354 die( "Unable to get last $status jobs number from catalog <br />" . $jobs->getMessage() );
356 $jobs = $jobs->fetchRow( DB_FETCHMODE_ASSOC );
357 return $jobs['job_nb'];
361 // Return the list of Pools in a array
362 public function Get_Pools_List()
364 $pool_list = array();
367 $query = "SELECT Name, PoolId FROM Pool";
369 $result = $this->db_link->query ( $query );
371 if( PEAR::isError( $result ) ) {
372 die( "Unable to get the pool list from catalog" );
374 while( $pool = $result->fetchRow(DB_FETCHMODE_ASSOC) ) {
375 array_push( $pool_list, array( $pool['Name'] => $pool['PoolId'] ) );
381 public function Get_BackupJob_Names()
383 $query = "SELECT Name FROM Job GROUP BY Name";
384 $backupjobs = array();
386 $result = $this->db_link->query( $query );
388 if (PEAR::isError( $result ) ) {
389 die("Unable to get BackupJobs list from catalog" );
391 while( $backupjob = $result->fetchRow( DB_FETCHMODE_ASSOC ) ) {
392 array_push( $backupjobs, $backupjob["Name"] );
398 // Return elasped time string for a job
399 function Get_ElapsedTime( $start_time, $end_time )
401 $diff = $end_time - $start_time;
403 $daysDiff = sprintf("%02d", floor($diff/60/60/24) );
404 $diff -= $daysDiff*60*60*24;
406 $hrsDiff = sprintf("%02d", floor($diff/60/60) );
407 $diff -= $hrsDiff*60*60;
409 $minsDiff = sprintf("%02d", floor($diff/60) );
410 $diff -= $minsDiff*60;
411 $secsDiff = sprintf("%02d", $diff );
414 return $daysDiff . 'day(s) ' . $hrsDiff.':' . $minsDiff . ':' . $secsDiff;
416 return $hrsDiff . ':' . $minsDiff . ':' . $secsDiff;
419 public function Get_ElapsedTime_Job( $delay = LAST_DAY )
424 // Interval calculation
425 $end_date = mktime();
426 $start_date = $end_date - $delay;
428 $start_date = date( "Y-m-d H:i:s", $start_date );
429 $end_date = date( "Y-m-d H:i:s", $end_date );
431 switch( $this->driver )
434 $query = "SELECT UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime) AS elapsed from Job ";
435 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
438 $result = $this->db_link->query( $query );
440 if( PEAR::isError($result) ){
441 die( "Unable to get elapsed time for jobs from catalog<br />query = $query <br />" . $result->getMessage() );
443 while( $time = $result->fetchRow( DB_FETCHMODE_ASSOC ) ) {
444 //echo 'elapsed = ' . $time['elapsed'] . '<br />';
445 $total_elapsed += $time['elapsed'];
447 // Verify if elapsed time is more than 1 day
448 if ( $total_elapsed > LAST_DAY ) {
449 return date("%d days H:i:s", $total_elapsed );
451 return date("H:i:s", $total_elapsed );
456 // Return Jobs statistics for a specific interval such as
457 // - Completed jobs number
458 // - Failed jobs number
459 // - Waiting jobs number
460 // The returned values will be used by a Bgraph classe
461 public function GetJobsStatistics( $type = 'completed', $delay = LAST_DAY )
469 // Interval calculation
470 $end_date = mktime();
471 $start_date = $end_date - $delay;
473 $start_date = date( "Y-m-d H:i:s", $start_date );
474 $end_date = date( "Y-m-d H:i:s", $end_date );
476 $interval_where = "(EndTime BETWEEN '$start_date' AND '$end_date') AND ";
482 $where = $interval_where . "JobStatus = 'T' ";
483 $label = "Completed";
485 case 'terminated_errors':
486 $where = $interval_where . "JobStatus = 'E' ";
487 $label = "Terminated with errors";
490 $where = $interval_where . "JobStatus = 'f' ";
494 $where = "JobStatus IN ('F','S','M','m','s','j','c','d','t') ";
498 $where = "JobStatus = 'C' ";
499 $label = "Created but not running";
502 $where = "JobStatus = 'R' ";
506 $where = $interval_where . "JobStatus IN ('e','f') ";
511 $query = 'SELECT COUNT(JobId) AS ' . $type . ' ';
512 $query .= 'FROM Job ';
513 $query .= "WHERE $where ";
515 //echo 'query = ' . $query . '<br />';
517 $jobs = $this->db_link->query( $query );
519 if (PEAR::isError( $jobs ) ) {
520 die( "Unable to get last $type jobs status from catalog<br />" . $status->getMessage() );
522 $res = $jobs->fetchRow();
523 return array( $label , current($res) );
525 } // end function GetJobsStatistics()
527 public function GetPoolsStatistics( $pools )
529 foreach( $pools as $pool_name => $pool ) {
531 $query = "SELECT COUNT(*) AS nb_vol FROM Media WHERE PoolId = '$pool'";
532 //echo $query . '<br />';
533 //echo 'Pool name ' . $pool_name . '<br />';
534 $result = $this->db_link->query( $query );
536 if( PEAR::isError( $result ) ) {
537 die("Unable to get volume number from catalog");
539 $nb_vol = $result->fetchRow(DB_FETCHMODE_ASSOC);
540 return array( $pool_name, $nb_vol['nb_vol'] );
545 public function GetStoredFiles( $delay = LAST_DAY )
549 $query = "SELECT SUM(JobFiles) AS stored_files FROM Job ";
551 // Interval calculation
552 $end_date = mktime();
553 $start_date = $end_date - $delay;
555 $start_date = date( "Y-m-d H:i:s", $start_date );
556 $end_date = date( "Y-m-d H:i:s", $end_date );
559 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
561 $result = $this->db_link->query( $query );
563 if( !PEAR::isError($result) ) {
564 $nbfiles = $result->fetchRow(DB_FETCHMODE_ASSOC);
565 $totalfiles = $totalfiles + $nbfiles['stored_files'];
567 die("Unable to get protected files from catalog <br />" . $result->getMessage() );
573 public function GetStoredBytes( $delay = LAST_DAY )
575 $query = "SELECT SUM(JobBytes) as stored_bytes FROM Job ";
577 // Interval calculation
578 $end_date = mktime();
579 $start_date = $end_date - $delay;
581 $start_date = date( "Y-m-d H:i:s", $start_date );
582 $end_date = date( "Y-m-d H:i:s", $end_date );
585 $query .= "WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
587 $result = $this->db_link->query( $query );
589 if( PEAR::isError( $result ) ) {
590 die( "Unable to get Job Bytes from catalog" );
592 return $result->fetchRow( DB_FETCHMODE_ASSOC );
596 public function GetStoredBytesByInterval( $start_date, $end_date )
598 $query = "SELECT SUM(JobBytes) as stored_bytes, EndTime FROM Job WHERE EndTime BETWEEN '$start_date' AND '$end_date'";
600 $result = $this->db_link->query( $query );
602 if( PEAR::isError( $result ) ) {
603 die( "Unable to get Job Bytes from catalog" );
606 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
608 $day = date( "D d", strtotime($end_date) );
610 if( isset( $tmp['stored_bytes'] ) ) {
611 $hbytes = $this->human_file_size( $tmp['stored_bytes'], 3, 'GB');
612 $hbytes = explode( " ", $hbytes );
613 $stored_bytes = $hbytes[0];
616 return array( $day, $stored_bytes );
620 public function GetStoredBytesByJob( $jobname, $start_date, $end_date )
622 $query = "SELECT SUM(JobBytes) as stored_bytes, EndTime FROM Job ";
623 $query .= "WHERE ( EndTime BETWEEN '$start_date' AND '$end_date' ) AND ";
624 $query .= "Name = '$jobname'";
626 $result = $this->db_link->query( $query );
628 if( PEAR::isError( $result ) ) {
629 die( "Unable to get Job Bytes from catalog" );
632 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
634 $day = date( "D d", strtotime($end_date) );
636 if( isset( $tmp['stored_bytes'] ) ) {
637 $hbytes = $this->human_file_size( $tmp['stored_bytes'], 3, 'GB');
638 $hbytes = explode( " ", $hbytes );
639 $stored_bytes = $hbytes[0];
642 return array( $day, $stored_bytes );
646 public function GetStoredFilesByJob( $jobname, $start_date, $end_date )
648 $query = "SELECT SUM(JobFiles) as stored_files, EndTime FROM Job ";
649 $query .= "WHERE ( EndTime BETWEEN '$start_date' AND '$end_date' ) AND ";
650 $query .= "Name = '$jobname'";
652 $result = $this->db_link->query( $query );
654 if( PEAR::isError( $result ) ) {
655 die( "Unable to get Job Files from catalog" );
658 $tmp = $result->fetchRow( DB_FETCHMODE_ASSOC );
660 $day = date( "D d", strtotime($end_date) );
661 $stored_files = $tmp['stored_files'];
663 return array( $day, $stored_files );