1<?php defined('SYSPATH') OR die('No direct access allowed.'); 2 3/** 4 * Retrieves and manipulates current status of hosts (and services?) 5 */ 6class Data_Model extends System_Model 7{ 8 9 private $XML = array(); 10 public $DS = array(); 11 public $MACRO = array(); 12 private $RRD = array(); 13 public $STRUCT = array(); 14 public $TIMERANGE = array(); 15 public $PAGE_DEF = array(); 16 public $PAGE_GRAPH = array(); 17 public $XPORT = ""; 18 public $TEMPLATE_FILE = ""; 19 public $GRAPH_TYPE = 'normal'; 20 /* 21 * 22 * 23 */ 24 public function __construct(){ 25 $this->config = new Config_Model(); 26 $this->config->read_config(); 27 $this->auth = new Auth_Model(); 28 } 29 30 /* 31 * Get All Special Templates 32 * 33 */ 34 public function getSpecialTemplates(){ 35 $conf = $this->config->conf; 36 $templates = array(); 37 if (is_dir($conf['special_template_dir'])){ 38 if ($dh = opendir($conf['special_template_dir'])) { 39 while (($file = readdir($dh)) !== false) { 40 if ($file == "." || $file == "..") 41 continue; 42 if (!preg_match("/(.*)\.php$/", $file, $template)) 43 continue; 44 $templates[] = $template[1]; 45 } 46 } 47 } 48 if(empty($templates)){ 49 return FALSE; 50 }else{ 51 sort($templates); 52 return $templates; 53 } 54 } 55 56 public function getFirstSpecialTemplate(){ 57 $templates = $this->getSpecialTemplates(); 58 if($templates === FALSE){ 59 return FALSE; 60 }else{ 61 return $templates[0]; 62 } 63 } 64 /* 65 * 66 * 67 */ 68 public function getHosts() { 69 $hosts = array(); 70 $conf = $this->config->conf; 71 $i = 0; 72 if (is_dir($conf['rrdbase'])) { 73 if ($dh = opendir($conf['rrdbase'])) { 74 while (($file = readdir($dh)) !== false) { 75 if ($file == "." || $file == ".." || $file == ".pnp-internal") 76 continue; 77 78 if (is_file($conf['rrdbase'] . "/" . $file) ) 79 continue; 80 81 if($this->auth->is_authorized($file) === FALSE) 82 continue; 83 84 $stat = stat($conf['rrdbase'] . "/" . $file); 85 $age = (time() - $stat['mtime']); 86 $hosts[$i]['name'] = $file; 87 $hosts[$i]['sort'] = strtoupper($file); 88 if ($age < $conf['max_age']) { 89 $hosts[$i]['state'] = 'active'; 90 } else { 91 $hosts[$i]['state'] = 'inactive'; 92 } 93 $i++; 94 } 95 closedir($dh); 96 } else { 97 throw new Kohana_User_Exception('Perfdata Dir', "Can not open $path"); 98 } 99 } 100 if(empty($hosts)){ 101 throw new Kohana_Exception('error.perfdata-dir-empty', $conf['rrdbase'] ); 102 }else{ 103 # Obtain a list of columns 104 foreach ($hosts as $key => $row) { 105 $sort[$key] = $row['sort']; 106 } 107 # Sort the data with volume descending, edition ascending 108 # Add $data as the last parameter, to sort by the common key 109 array_multisort($sort, SORT_ASC, $hosts); 110 } 111 return $hosts; 112 } 113 114 115 /* 116 * 117 * 118 */ 119 function getRawServices($hostname) { 120 $services = array (); 121 $host = array(); 122 $conf = $this->config->conf; 123 $i = 0; 124 $path = $conf['rrdbase'] . $hostname; 125 if (is_dir($path)) { 126 if ($dh = opendir($path)) { 127 while ( ($file = readdir($dh) ) !== false) { 128 if ($file == "." || $file == "..") 129 continue; 130 131 if (!preg_match("/(.*)\.xml$/", $file, $servicedesc)) 132 continue; 133 134 $fullpath = $path . "/" . $file; 135 $stat = stat("$fullpath"); 136 $age = (time() - $stat['mtime']); 137 138 $state = "active"; 139 if ($age > $conf['max_age']) { # 6Stunden 140 $state = "inactive"; 141 } 142 $services[$i]['state'] = $state; 143 $services[$i]['name'] = $servicedesc[1]; 144 $i++; 145 } 146 } 147 }else{ 148 throw new Kohana_Exception('error.perfdata-dir-for-host', $path, $hostname ); 149 } 150 if( is_array($services) && (!empty($services))){ 151 # Obtain a list of columns 152 foreach ($services as $key => $row) { 153 $sort[$key] = $row['name']; 154 } 155 # Sort the data with volume descending, edition ascending 156 # Add $data as the last parameter, to sort by the common key 157 array_multisort($sort, SORT_STRING, $services); 158 }else{ 159 throw new Kohana_Exception('error.host-perfdata-dir-empty', $path, $hostname ); 160 } 161 return $services; 162 } 163 /* 164 * 165 * 166 */ 167 function getServices($hostname) { 168 $services = array (); 169 $host = array(); 170 $i = 0; 171 $service_list = $this->getRawServices($hostname); 172 foreach( $service_list as $s ){ 173 if(!$this->readXML($hostname, $s['name'], FALSE)){ 174 continue; 175 } 176 if($s['name'] == "_HOST_"){ 177 // Check authorization 178 if($this->auth->is_authorized((string) $this->XML->NAGIOS_AUTH_HOSTNAME, "_HOST_") === FALSE) 179 continue; 180 181 $host[0]['name'] = "_HOST_"; 182 $host[0]['hostname'] = (string) $this->XML->NAGIOS_HOSTNAME; 183 $host[0]['state'] = $s['state']; 184 $host[0]['servicedesc'] = "Host Perfdata"; 185 $host[0]['is_multi'] = (string) $this->XML->DATASOURCE[0]->IS_MULTI[0]; 186 }else{ 187 // Check authorization 188 if($this->auth->is_authorized((string) $this->XML->NAGIOS_AUTH_HOSTNAME, (string) $this->XML->NAGIOS_AUTH_SERVICEDESC) === FALSE ) 189 continue; 190 191 $services[$i]['name'] = $s['name']; 192 // Sorting check_multi 193 if( (string) $this->XML->NAGIOS_MULTI_PARENT == ""){ 194 $services[$i]['sort'] = strtoupper($s['name']); 195 }else{ 196 $services[$i]['sort'] = strtoupper((string) $this->XML->NAGIOS_MULTI_PARENT); 197 $services[$i]['sort'] .= (string) $this->XML->DATASOURCE[0]->IS_MULTI[0]; 198 $services[$i]['sort'] .= strtoupper($s['name']); 199 } 200 $services[$i]['state'] = $s['state']; 201 $services[$i]['hostname'] = (string) $this->XML->NAGIOS_DISP_HOSTNAME; 202 $services[$i]['servicedesc'] = (string) $this->XML->NAGIOS_DISP_SERVICEDESC; 203 $services[$i]['is_multi'] = (string) $this->XML->DATASOURCE[0]->IS_MULTI[0]; 204 } 205 $i++; 206 } 207 #print Kohana::debug($services); 208 if( is_array($services) && (!empty($services))){ 209 # Obtain a list of columns 210 foreach ($services as $key => $row) { 211 $sort[$key] = $row['sort']; 212 } 213 # Sort the data with volume descending, edition ascending 214 # Add $data as the last parameter, to sort by the common key 215 array_multisort($sort, SORT_STRING, $services); 216 } 217 if(is_array($host) && (!empty($host))){ 218 array_unshift($services, $host[0]); 219 } 220 return $services; 221 } 222 223 /* 224 * 225 * 226 */ 227 public function getFirstService($hostname) { 228 $conf = $this->config->conf; 229 $services = $this->getServices($hostname); 230 foreach ($services as $srv) { 231 if ($srv['state'] == "active" ) { 232 break; 233 } 234 } 235 if(empty($srv)){ 236 throw new Kohana_Exception('error.get-first-service', $hostname ); 237 } 238 return $srv['name']; 239 } 240 241 /* 242 * 243 * 244 */ 245 public function getFirstHost() { 246 $conf = $this->config->conf; 247 $hosts = $this->getHosts(); 248 foreach ($hosts as $host) { 249 if ($host['state'] == "active" ) { 250 break; 251 } 252 } 253 if(empty($host)){ 254 throw new Kohana_Exception('error.get-first-host'); 255 } 256 return $host['name']; 257 } 258 259 /* 260 * 261 * 262 */ 263 public function readXML ($hostname, $servicedesc, $throw_exception=TRUE){ 264 $conf = $this->config->conf; 265 $xmlfile = $conf['rrdbase'].$hostname."/".$servicedesc.".xml"; 266 $xml = array(); 267 if (file_exists($xmlfile)) { 268 libxml_use_internal_errors(TRUE); 269 libxml_clear_errors(TRUE); 270 if(! $xml = simplexml_load_file($xmlfile) ){; 271 if( $throw_exception == TRUE ){ 272 $errors = '<br>'; 273 foreach(libxml_get_errors() as $error) { 274 $errors .= $error->message."<br>"; 275 } 276 throw new Kohana_Exception('error.xml-generic_error',$xmlfile, $errors); 277 }else{ 278 return FALSE; 279 } 280 } 281 $this->XML = array(); 282 $this->MACRO = array(); 283 $this->MACRO['AUTH_SERVICEDESC'] = ''; 284 $this->MACRO['AUTH_HOSTNAME'] = ''; 285 $this->DS = array(); 286 // Throw excaption without a valid structure version 287 if(!isset($xml->XML->VERSION) && $throw_exception == TRUE){ 288 throw new Kohana_Exception('error.xml-structure-without-version-tag',$xmlfile); 289 } 290 if(!isset($xml->XML->VERSION) && $throw_exception == FALSE){ 291 return FALSE; 292 } 293 foreach ( $xml as $key=>$val ){ 294 if(preg_match('/^NAGIOS_(.*)$/', $key, $match)){ 295 #print $match[1]." => ".$val."\n"; 296 $key = $match[1]; 297 $this->MACRO[$key] = (string) $val; 298 } 299 } 300 $i=0; 301 foreach ( $xml->DATASOURCE as $datasource ){ 302 foreach ( $datasource as $key=>$val){ 303 #print "$key => $val\n"; 304 #$$key[$i] = (string) $val; 305 $this->DS[$i][$key] = (string) $val; 306 } 307 $i++; 308 } 309 $this->XML = $xml; 310 return TRUE; 311 }else{ 312 throw new Kohana_Exception('error.xml-not-found', $xmlfile); 313 } 314 } 315 316 /* 317 * 318 * 319 */ 320 public function buildDataStruct ($host = FALSE, $service = FALSE, $view = NULL, $source = NULL){ 321 if($host === FALSE && $service === FALSE){ 322 return FALSE; 323 } 324 $conf = $this->config->conf; 325 326 /* 327 * Special templates without Host/Service 328 */ 329 if($host == '__special' ){ 330 // $service contains the template name 331 $this->includeTemplate($service,'special'); 332 }else{ 333 if( $this->readXML($host,$service) == FALSE ){ 334 throw new Kohana_Exception('error.xml-not-found', "Undefined error"); 335 return false; 336 } 337 $this->includeTemplate($this->DS[0]['TEMPLATE']); 338 } 339 if(isset($this->TIMERANGE['type']) && $this->TIMERANGE['type'] == "start-end"){ 340 $view = intval($view); 341 $i=0; 342 foreach( $this->RRD['def'] as $key=>$val){ 343 if( ! is_null($source) && $source != $key ){ 344 continue; 345 } 346 $tmp_struct = array(); 347 $tmp_struct['LEVEL'] = $i; 348 $tmp_struct['VIEW'] = $view; 349 $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;; 350 $tmp_struct['SOURCE'] = $key; 351 $tmp_struct['RRD_CALL'] = $this->TIMERANGE['cmd'] . " ". $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key]; 352 $tmp_struct['TIMERANGE'] = $this->TIMERANGE; 353 $tmp_struct['GRAPH_WIDTH'] = $this->getGraphDimensions('width', $tmp_struct['RRD_CALL']); 354 $tmp_struct['GRAPH_HEIGHT'] = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']); 355 if(isset($this->RRD['ds_name'][$key]) ){ 356 $tmp_struct['ds_name'] = $this->RRD['ds_name'][$key]; 357 }elseif(array_key_exists($i, $this->DS)){ 358 $tmp_struct['ds_name'] = $this->DS[$i]['NAME']; 359 }else{ 360 $tmp_struct['ds_name'] = "UNDEF"; 361 } 362 $tmp_struct['MACRO'] = $this->MACRO; 363 if(isset($this->XML->XML->VERSION)){ 364 $tmp_struct['VERSION'] = pnp::xml_version_check( (string) $this->XML->XML->VERSION); 365 }else{ 366 $tmp_struct['VERSION'] = pnp::xml_version_check("0"); 367 } 368 $this->addToDataStruct($tmp_struct); 369 $i++; 370 } 371 return; 372 } 373 if( $view === ""){ 374 $v = 0; 375 foreach($this->config->views as $view_key=>$view_val){ 376 $i=0; 377 foreach( $this->RRD['def'] as $key=>$val){ 378 if( ! is_null($source) && $source != $key ){ 379 continue; 380 } 381 $tmp_struct = array(); 382 $tmp_struct['LEVEL'] = $i; 383 $tmp_struct['VIEW'] = $view_key; 384 $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;; 385 $tmp_struct['SOURCE'] = $key; 386 $tmp_struct['RRD_CALL'] = $this->TIMERANGE[$v]['cmd'] . " " . $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key]; 387 $tmp_struct['GRAPH_WIDTH'] = $this->getGraphDimensions('width', $tmp_struct['RRD_CALL']); 388 $tmp_struct['GRAPH_HEIGHT'] = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']); 389 if(isset($this->RRD['ds_name'][$key]) ){ 390 $tmp_struct['ds_name'] = $this->RRD['ds_name'][$key]; 391 }elseif(array_key_exists($i, $this->DS)){ 392 $tmp_struct['ds_name'] = $this->DS[$i]['NAME']; 393 }else{ 394 $tmp_struct['ds_name'] = "UNDEF"; 395 } 396 $tmp_struct['TIMERANGE'] = $this->TIMERANGE[$v]; 397 $tmp_struct['MACRO'] = $this->MACRO; 398 if(isset($this->XML->XML->VERSION)){ 399 $tmp_struct['VERSION'] = pnp::xml_version_check( (string) $this->XML->XML->VERSION); 400 }else{ 401 $tmp_struct['VERSION'] = pnp::xml_version_check("0"); 402 } 403 $this->addToDataStruct($tmp_struct); 404 $i++; 405 } 406 $v++; 407 } 408 }else{ 409 $view = intval($view); 410 $i=0; 411 foreach( $this->RRD['def'] as $key=>$val){ 412 if( ! is_null($source) && $source != $key ){ 413 continue; 414 } 415 $tmp_struct = array(); 416 $tmp_struct['LEVEL'] = $i; 417 $tmp_struct['VIEW'] = $view; 418 $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;; 419 $tmp_struct['SOURCE'] = $key; 420 $tmp_struct['RRD_CALL'] = $this->TIMERANGE[$view]['cmd'] . " ". $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key]; 421 $tmp_struct['TIMERANGE'] = $this->TIMERANGE[$view]; 422 $tmp_struct['GRAPH_WIDTH'] = $this->getGraphDimensions('width', $tmp_struct['RRD_CALL']); 423 $tmp_struct['GRAPH_HEIGHT'] = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']); 424 if(isset($this->RRD['ds_name'][$key]) ){ 425 $tmp_struct['ds_name'] = $this->RRD['ds_name'][$key]; 426 }elseif(array_key_exists($i, $this->DS)){ 427 $tmp_struct['ds_name'] = $this->DS[$i]['NAME']; 428 }else{ 429 $tmp_struct['ds_name'] = "UNDEF"; 430 } 431 $tmp_struct['MACRO'] = $this->MACRO; 432 if(isset($this->XML->XML->VERSION)){ 433 $tmp_struct['VERSION'] = pnp::xml_version_check( (string) $this->XML->XML->VERSION); 434 }else{ 435 $tmp_struct['VERSION'] = pnp::xml_version_check("0"); 436 } 437 $this->addToDataStruct($tmp_struct); 438 $i++; 439 } 440 } 441 } 442 443 /* 444 * 445 * 446 */ 447 private function addToDataStruct ($data=FALSE) { 448 if($data === FALSE) 449 return FALSE; 450 451 array_push($this->STRUCT, $data); 452 } 453 454 /* 455 * 456 * 457 */ 458 private function includeTemplate($template=FALSE,$type='normal'){ 459 if($template===FALSE){ 460 return FALSE; 461 } 462 $this->RRD = array(); 463 /* 464 * Normal PNP Templates 465 */ 466 if($type == 'normal'){ 467 $template_file = $this->findTemplate( $template ); 468 $this->TEMPLATE_FILE = $template_file; 469 $hostname = $this->MACRO['HOSTNAME']; 470 $servicedesc = $this->MACRO['SERVICEDESC']; 471 $TIMERANGE = $this->TIMERANGE; 472 }elseif($type == 'special'){ 473 $template_file = $this->findTemplate( $template, $type ); 474 $TIMERANGE = $this->TIMERANGE; 475 } 476 $def = FALSE; 477 $opt = FALSE; 478 $ds_name = FALSE; 479 /* 480 * 0.4.x Template compatibility 481 */ 482 foreach($this->DS as $key=>$val ){ 483 $key++; 484 foreach(array_keys($val) as $tag){ 485 ${$tag}[$key] = $val[$tag]; 486 } 487 } 488 foreach($this->MACRO as $key=>$val ){ 489 ${"NAGIOS_".$key} = $val; 490 } 491 if(isset($RRDFILE[1])){ 492 $rrdfile = $RRDFILE[1]; 493 } 494 // Include template 495 if($template_file == FALSE){ 496 throw new Kohana_Exception('error.no-templates-found'); 497 }else{ 498 ob_start(); 499 include($template_file); 500 ob_end_clean(); 501 } 502 // Compatibility for very old Templates 503 if(!is_array($def) && $def != FALSE){ 504 $tmp[1] = $def; 505 $def = $tmp; 506 } 507 if(!is_array($opt) && $opt != FALSE){ 508 $tmp[1] = $opt; 509 $opt = $tmp; 510 } 511 if(!is_array($ds_name) && $ds_name != FALSE){ 512 $tmp[1] = $ds_name; 513 $ds_name = $tmp; 514 } 515 // 516 if($def != FALSE){ 517 $this->RRD['def'] = $this->array_reindex($def); 518 }else{ 519 throw new Kohana_Exception('error.template-without-def', $template_file); 520 } 521 if($opt != FALSE ){ 522 $this->RRD['opt'] = $this->array_reindex($opt); 523 }else{ 524 throw new Kohana_Exception('error.template-without-opt', $template_file); 525 } 526 if( $ds_name != FALSE ){ 527 $this->RRD['ds_name'] = $this->array_reindex($ds_name); 528 } 529 return TRUE; 530 } 531 532 # 533 # 534 # 535 private function getGraphDimensions($search, $command){ 536 if($search == 'width'){ 537 if(preg_match_all('/(-w|--width|--width=)\s([0-9]+)\s/i',$command,$match)){ 538 $value = array_pop($match[2]); 539 return $value; 540 }else{ 541 return $this->config->conf['graph_width']; 542 } 543 } 544 if($search == 'height'){ 545 if(preg_match_all('/(-h|--height|--height=)\s([0-9]+)\s/i',$command,$match)){ 546 $value = array_pop($match[2]); 547 return $value; 548 }else{ 549 return $this->config->conf['graph_height']; 550 } 551 } 552 return FALSE; 553 } 554 # 555 # 556 # 557 private function array_reindex($data){ 558 $i=0; 559 foreach($data as $d){ 560 $tmp[$i] = $d; 561 $i++; 562 } 563 return $tmp; 564 } 565 566 /* 567 * 568 * 569 */ 570 public function findTemplate($template,$type='normal'){ 571 $conf = $this->config->conf; 572 /* 573 * Normal templates 574 */ 575 if($type == 'normal'){ 576 // Build a list of directories to search for templates 577 $template_dirs = array(); 578 if(array_key_exists('template_dirs', $this->config->conf)){ 579 foreach($this->config->conf['template_dirs'] as $dir){ 580 $template_dirs[] = $dir; 581 } 582 } 583 foreach(Kohana::config('core.template_dirs') as $dir){ 584 $template_dirs[] = $dir; 585 } 586 #throw new Kohana_Exception(print_r($template_dirs,TRUE)); 587 foreach($template_dirs as $dir){ 588 $match = $this->findRecursiveTemplate($template,$dir); 589 if($match != FALSE){ 590 return $match; 591 } 592 } 593 /* 594 * Fallback to default templates 595 */ 596 foreach($template_dirs as $dir){ 597 $match = $this->findRecursiveTemplate('default',$dir); 598 if($match != FALSE){ 599 return $match; 600 } 601 } 602 return FALSE; 603 } 604 /* 605 * Special Templates 606 */ 607 if($type == 'special'){ 608 if (is_readable($conf['special_template_dir'] . '/' . $template . '.php')) { 609 $template_file = $conf['special_template_dir'].'/' . $template . '.php'; 610 }else{ 611 throw new Kohana_Exception("Special Template '$template' not found"); 612 } 613 return $template_file; 614 } 615 } 616 617 /* 618 * 619 * 620 */ 621 function findRecursiveTemplate($template, $dir) { 622 if(!is_readable($dir)){ 623 return FALSE; 624 } 625 $template_file = ""; 626 $r_template_file = ""; 627 $r_template = ""; 628 $recursive = explode("_", $template); 629 if($this->config->conf['enable_recursive_template_search'] == 1){ 630 $i = 0; 631 foreach ($recursive as $value) { 632 if ($i == 0) { 633 $r_template = $value; 634 } else { 635 $r_template = $r_template . '_' . $value; 636 } 637 $file = $dir . '/' . $r_template . '.php'; 638 if (is_readable($file)) { 639 $r_template_file = $file; 640 } 641 $i++; 642 } 643 if($r_template_file != ""){ 644 return $r_template_file; 645 }else{ 646 return FALSE; 647 } 648 }else{ 649 $file = $dir . '/' . $template . '.php'; 650 if (is_readable($file)) { 651 return $file; 652 }else{ 653 return FALSE; 654 } 655 } 656 } 657 658 public function getTimeRange($start=FALSE ,$end=FALSE ,$view="") { 659 if($end != FALSE){ 660 // we are in a custom timerange 661 if(!is_numeric($end)){ 662 $timestamp = strtotime($end); 663 if(!$timestamp){ 664 throw new Kohana_User_Exception('Wrong Format', "End -> $end"); 665 }else{ 666 $end = $timestamp; 667 } 668 } 669 }else{ 670 $end = time(); 671 } 672 if($start != FALSE ){ 673 // we are in a custom timerange 674 if(!is_numeric($start)){ 675 $timestamp = strtotime($start); 676 if(!$timestamp){ 677 throw new Kohana_User_Exception('Wrong Format', "Start -> $start"); 678 }else{ 679 $start = $timestamp; 680 } 681 } 682 } 683 if($start && $end){ 684 $timerange['title'] = Kohana::lang('common.timerange-selector-link'); 685 $timerange['start'] = $start; 686 $timerange['f_start'] = date($this->config->conf['date_fmt'],$start); 687 $timerange['end'] = $end; 688 $timerange['f_end'] = date($this->config->conf['date_fmt'],$end); 689 $timerange['cmd'] = " --start $start --end $end "; 690 $timerange['type'] = "start-end"; 691 $this->TIMERANGE = $timerange; 692 return; 693 } 694 695 $view=intval( pnp::clean($view) ); 696 if($view >= count($this->config->views)){ 697 $view = 1; 698 } 699 700 if(!$end){ 701 $end = time(); 702 }elseif(!is_numeric($end)){ 703 $timestamp = strtotime($end); 704 if(!$timestamp){ 705 throw new Kohana_User_Exception('Wrong Format', "$end"); 706 }else{ 707 $end = $timestamp; 708 } 709 }else{ 710 $end = $end; 711 } 712 713 if(!$start){ 714 $start = ( $end - $this->config->views[$view]['start']); 715 }elseif(!is_numeric($start)){ 716 $timestamp = strtotime($start); 717 if(!$timestamp){ 718 throw new Kohana_User_Exception('Wrong Format', "$start"); 719 }else{ 720 $start = $timestamp; 721 } 722 }else{ 723 $start = pnp::clean($start); 724 } 725 726 if($start >= $end){ 727 //Fixme i18n 728 throw new Kohana_User_Exception('Wrong Timerange', "start >= end"); 729 } 730 731 $timerange['title'] = $this->config->views[$view]['title']; 732 $timerange['start'] = $start; 733 $timerange['f_start'] = date($this->config->conf['date_fmt'],$start); 734 $timerange['end'] = $end; 735 $timerange['f_end'] = date($this->config->conf['date_fmt'],$end); 736 $timerange['cmd'] = " --start $start --end $end "; 737 $timerange['type'] = "views"; 738 for ($i = 0; $i < count($this->config->views); $i++) { 739 $timerange[$i]['title'] = $this->config->views[$i]['title']; 740 $timerange[$i]['start'] = $end - $this->config->views[$i]['start']; 741 $timerange[$i]['f_start'] = date($this->config->conf['date_fmt'],$end - $this->config->views[$i]['start']); 742 $timerange[$i]['end'] = $end; 743 $timerange[$i]['f_end'] = date($this->config->conf['date_fmt'],$end); 744 $timerange[$i]['cmd'] = " --start " . ($end - $this->config->views[$i]['start']) . " --end $end" ; 745 } 746 $this->TIMERANGE = $timerange; 747 } 748 749 public function buildBasketStruct($basket,$view = NULL){ 750 if(is_array($basket) && (!empty($basket))){ 751 if($view == ""){ 752 $views = array_keys($this->config->views); 753 }else{ 754 $views = array($view); 755 } 756 foreach($views as $view){ 757 foreach($basket as $item){ 758 # explode host::service::source 759 $slices = explode("::",$item); 760 if(count($slices) == 2) 761 $this->buildDataStruct($slices[0], $slices[1], $view); 762 if(count($slices) == 3) 763 $this->buildDataStruct($slices[0], $slices[1], $view, $slices[2]); 764 } 765 # break on custom time ranges 766 if(isset($this->TIMERANGE['type']) && $this->TIMERANGE['type'] == "start-end"){ 767 break; 768 } 769 } 770 } 771 } 772 773 public function buildPageStruct($page,$view){ 774 $servicelist = array(); 775 $this->parse_page_cfg($page); 776 $hosts = $this->getHostsByPage(); 777 # No regex so we keep the order defined by config 778 if($this->PAGE_DEF['use_regex'] == 0){ 779 #Loop through graph definitions 780 foreach($this->PAGE_GRAPH as $graph){ 781 $hosts_to_search_for = explode(",", $graph['host_name']); 782 foreach($hosts_to_search_for as $host){ 783 if(in_array($host, $hosts)){ 784 $services = $this->getServices($host); 785 foreach($services as $service) { 786 // search for definition 787 $data = $this->filterServiceByPage($graph,$host,$service); 788 if($data){ 789 $servicelist[] = array( 'host' => $host, 'service' => $service['name'], 'source' => $data['source']); 790 791 } 792 } 793 } 794 } 795 } 796 }else{ 797 foreach($hosts as $host){ 798 $services = $this->getServices($host); 799 foreach($services as $service) { 800 // search for definition 801 $data = $this->filterServiceByPage($this->PAGE_GRAPH,$host,$service); 802 if($data){ 803 $servicelist[] = array( 'host' => $host, 'service' => $service['name'], 'source' => $data['source']); 804 } 805 } 806 } 807 } 808 #print Kohana::debug($servicelist); 809 if(empty($servicelist)){ 810 $this->ERROR = "ERROR: ". Kohana::lang('error.no-data-for-page', $page.".cfg" ); 811 }else{ 812 foreach($servicelist as $s){ 813 $this->buildDataStruct($s['host'],$s['service'],$view,$s['source']); 814 } 815 } 816 } 817 818 819 public function parse_page_cfg($page){ 820 $page_cfg = $this->config->conf['page_dir'].$page.".cfg"; 821 if(is_readable($page_cfg)){ 822 $data = file($page_cfg); 823 }else{ 824 throw new Kohana_Exception('error.page-not-readable', $page.".cfg" ); 825 } 826 $l = 0; 827 $line = ""; 828 $tag = ""; 829 $inside=0; 830 $this->PAGE_DEF['page_name'] = 'UNDEF'; 831 $this->PAGE_DEF['use_regex'] = 0; 832 $this->PAGE_GRAPH = array(); 833 $allowed_tags = array("page", "graph"); 834 foreach($data as $line){ 835 if(preg_match('/(^#|^;)/',$line)) { 836 continue; 837 } 838 839 preg_match('/define\s+(\w+)\W+{/' ,$line, $tag); 840 if(isset($tag[1]) && in_array($tag[1],$allowed_tags)){ 841 $inside = 1; 842 $t = $tag[1]; 843 $l++; 844 continue; 845 } 846 if(preg_match('/\s?(\w+)\s+([^#]+)(#.*)?$/',$line, $key) && $inside == 1){ 847 $k=strtolower($key[1]); 848 $v=$key[2]; 849 if($t=='page'){ 850 $this->PAGE_DEF[$k] = trim($v); 851 }elseif($t=='graph'){ 852 $this->PAGE_GRAPH[$l][$k] = trim($v); 853 } 854 } 855 if(preg_match('/}/',$line)){ 856 $inside=0; 857 $t = ""; 858 continue; 859 } 860 } 861 } 862 863 /* 864 * 865 */ 866 public function getHostsByPage(){ 867 $hosts = $this->getHosts(); 868 $new_hosts = array(); 869 foreach( $hosts as $host){ 870 if($host['state'] == "inactive"){ 871 continue; 872 } 873 if($tmp = $this->filterHostByPage($host['name'])){ 874 $new_hosts[] = $tmp; 875 } 876 } 877 return $new_hosts; 878 } 879 /* 880 * 881 */ 882 private function filterHostByPage($host){ 883 if(isset($this->PAGE_DEF['use_regex']) && $this->PAGE_DEF['use_regex'] == 1){ 884 // Search Host by regex 885 foreach( $this->PAGE_GRAPH as $g ){ 886 if(isset($g['host_name']) && preg_match('/'.$g['host_name'].'/',$host)){ 887 return $host; 888 } 889 } 890 }else{ 891 foreach( $this->PAGE_GRAPH as $g ){ 892 $hosts_to_search_for = explode(",", $g['host_name']); 893 if(isset($g['host_name']) && in_array($host ,$hosts_to_search_for) ){ 894 return $host; 895 } 896 } 897 } 898 return FALSE; 899 } 900 901 private function filterServiceByPage($g,$host,$service){ 902 $data = array(); 903 if(isset($this->PAGE_DEF['use_regex']) && $this->PAGE_DEF['use_regex'] == 1){ 904 // Search Host by regex 905 foreach( $this->PAGE_GRAPH as $g ){ 906 if(isset($g['host_name']) && preg_match('/'.$g['host_name'].'/',$host)){ 907 if(isset($g['service_desc']) && preg_match('/'.$g['service_desc'].'/',$service['name'])){ 908 $data['service_desc'] = $g['service_desc']; 909 $data['host_name'] = $g['host_name']; 910 $data['source'] = NULL; 911 // if we only want a single image 912 if(isset($g['source'])){ 913 $this->readXML($host,$service['name']); 914 $this->includeTemplate($this->DS[0]['TEMPLATE']); 915 $source = intval($g['source']); 916 if(array_key_exists($source,$this->RRD['def'])){ 917 $data['source'] = $source; 918 } 919 } 920 return $data; 921 } 922 } 923 } 924 }else{ 925 $hosts_to_search_for = explode(",", $g['host_name']); 926 $services_to_search_for = explode(",", $g['service_desc']); 927 if(isset($g['host_name']) && in_array($host ,$hosts_to_search_for) ){ 928 if(isset($g['service_desc']) && in_array($service['name'] ,$services_to_search_for) ){ 929 $data['service_desc'] = $g['service_desc']; 930 $data['host_name'] = $g['host_name']; 931 $data['source'] = NULL; 932 // if we only want a single image 933 if(isset($g['source'])){ 934 $this->readXML($host,$service['name']); 935 $this->includeTemplate($this->DS[0]['TEMPLATE']); 936 $source = intval($g['source']); 937 if(array_key_exists($source,$this->RRD['def'])){ 938 $data['source'] = $source; 939 } 940 } 941 return $data; 942 } 943 } 944 } 945 return FALSE; 946 } 947 948 /* 949 * 950 */ 951 public function getPages() { 952 $pages = array(); 953 if (is_dir($this->config->conf['page_dir'])) { 954 if ($dh = opendir($this->config->conf['page_dir'])) { 955 while (($file = readdir($dh)) !== false) { 956 if(preg_match('/(.*)\.cfg$/',basename($file),$page)){ 957 $pages[] = urlencode($page[1]); 958 } 959 } 960 closedir($dh); 961 } else { 962 die("Cannot open directory: $path"); 963 } 964 } 965 if(empty($pages)){ 966 return FALSE; 967 }else{ 968 969 natsort($pages); 970 } 971 return $pages; 972 } 973 974 /* 975 * 976 */ 977 public function getFirstPage(){ 978 $pages = $this->getPages(); 979 if(empty($pages)){ 980 return FALSE; 981 }else{ 982 return urldecode($pages[0]); 983 } 984 } 985 986 /* 987 * 988 */ 989 public function getPageDetails($page){ 990 $this->parse_page_cfg($page); 991 return $this->PAGE_DEF['page_name']; 992 } 993 994 /* 995 * 996 */ 997 public function buildXport($host,$service){ 998 // FIXME add max rows to config 999 $this->XPORT = " -m 2000"; 1000 $this->XPORT .= " --start=".$this->TIMERANGE['start']; 1001 $this->XPORT .= " --end=".$this->TIMERANGE['end']; 1002 $this->readXML($host,$service); 1003 $count = 0; 1004 $RRAs = array('MIN','MAX','AVERAGE'); 1005 foreach($this->DS as $key=>$value){ 1006 foreach($RRAs as $RRA){ 1007 $this->XPORT .= sprintf(" DEF:%d%s=%s:%d:%s ",$count,$RRA,$value['RRDFILE'],$value['DS'],$RRA); 1008 $this->XPORT .= sprintf(" XPORT:%d%s:%s_%s " ,$count,$RRA,$value['NAME'],$RRA); 1009 } 1010 $count++; 1011 } 1012 } 1013 /* 1014 * 1015 */ 1016 public function xml2csv($string){ 1017 $xml = simplexml_load_string($string); 1018 $csv = "timestamp"; 1019 foreach($xml->meta->legend->entry as $key=>$value){ 1020 $csv .= ";" . $value ; 1021 } 1022 1023 /* 1024 * from rrdtool 1.5 on, timestamp ( value->t ) is not set anymore 1025 * from 1.6 on it can be reactivated by -- showrime option to rrdtool xport 1026 * here we take care of the timestamp ourselves regardless which 1027 * rrdtool version is used 1028 */ 1029 $timestamp2 = $xml->meta->start; 1030 $step = $xml->meta->step; 1031 $csv .= "\n"; 1032 foreach($xml->data->row as $key=>$value){ 1033 $csv .= $timestamp2; 1034 foreach($value->v as $item){ 1035 $csv .= ";".floatval((string) $item); 1036 } 1037 $csv .= "\n"; 1038 $timestamp2 = $timestamp2 + $step; 1039 } 1040 return $csv; 1041 } 1042 1043 /* 1044 * 1045 * Used in Special Templates to gather data 1046 */ 1047 public function tplGetData ($hostname, $servicedesc, $throw_exception=TRUE){ 1048 $conf = $this->config->conf; 1049 $xmlfile = $conf['rrdbase'].$hostname."/".$servicedesc.".xml"; 1050 $data = array(); 1051 if (file_exists($xmlfile)) { 1052 $xml = simplexml_load_file($xmlfile); 1053 // Throw excaption without a valid structure version 1054 if(!isset($xml->XML->VERSION) && $throw_exception == TRUE){ 1055 throw new Kohana_Exception('error.xml-structure-without-version-tag',$xmlfile); 1056 } 1057 if(!isset($xml->XML->VERSION) && $throw_exception == FALSE){ 1058 return FALSE; 1059 } 1060 foreach ( $xml as $key=>$val ){ 1061 if(preg_match('/^NAGIOS_(.*)$/', $key, $match)){ 1062 $key = $match[1]; 1063 $data['MACRO'][$key] = (string) $val; 1064 } 1065 } 1066 $i=0; 1067 foreach ( $xml->DATASOURCE as $datasource ){ 1068 foreach ( $datasource as $key=>$val){ 1069 $data['DS'][$i][$key] = (string) $val; 1070 } 1071 $i++; 1072 } 1073 return $data; 1074 }else{ 1075 throw new Kohana_Exception('error.xml-not-found', $xmlfile); 1076 } 1077 } 1078 /* 1079 * 1080 * Used in Special Templates to gather data 1081 */ 1082 public function tplGetServices ($hostregex=FALSE, $serviceregex = ''){ 1083 if($hostregex === FALSE){ 1084 return FALSE; 1085 } 1086 $hostregex = sprintf("/%s/",$hostregex); 1087 $serviceregex = sprintf("/%s/",$serviceregex); 1088 $hosts = $this->getHosts(); 1089 $new_hosts = array(); 1090 foreach( $hosts as $host){ 1091 if(preg_match($hostregex,$host['name'])){ 1092 $new_hosts[] = $host['name']; 1093 } 1094 } 1095 1096 if(empty($new_hosts)){ 1097 throw new Kohana_Exception('error.tpl-no-hosts-found', $hostregex); 1098 } 1099 1100 $i = 0; 1101 $new_services = array(); 1102 foreach($new_hosts as $host){ 1103 $services = $this->getRawServices($host); 1104 if(empty($services)){ 1105 throw new Kohana_Exception('error.tpl-no-services-found', $serviceregex); 1106 } 1107 foreach($services as $service){ 1108 if(preg_match($serviceregex, $service['name'])){ 1109 $new_services[$i]['hostname'] = $host; 1110 $new_services[$i]['host'] = $host; 1111 $new_services[$i]['service_description'] = $service['name']; 1112 $new_services[$i]['service'] = $service['name']; 1113 $i++; 1114 } 1115 } 1116 } 1117 1118 if(empty($new_services)){ 1119 throw new Kohana_Exception('error.tpl-no-services-found', $serviceregex); 1120 } 1121 1122 return $new_services; 1123 1124 } 1125} 1126