1<?php 2 3# 4# Some common functions for the Ganglia PHP website. 5# Assumes the Gmeta XML tree has already been parsed, 6# and the global variables $metrics, $clusters, and $hosts 7# have been set. 8# 9 10include_once ( dirname(__FILE__) . "/lib/json.php" ); 11 12# 13# Load event API driver. 14# 15$driver = ucfirst(strtolower( !isset($conf['overlay_events_provider']) ? "Json" : $conf['overlay_events_provider'] )); 16if (file_exists( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php")) { 17 include_once( dirname(__FILE__) . "/lib/Events/Driver_${driver}.php" ); 18} 19 20#------------------------------------------------------------------------------ 21# Allows a form of inheritance for template files. 22# If a file does not exist in the chosen template, the 23# default is used. Cuts down on code duplication. 24function template ($name) 25{ 26 global $conf; 27 28 $fn = "./templates/${conf['template_name']}/$name"; 29 $default = "./templates/default/$name"; 30 31 if (file_exists($fn)) { 32 return $fn; 33 } 34 else { 35 return $default; 36 } 37} 38 39#------------------------------------------------------------------------------ 40# Creates a hidden input field in a form. Used to save CGI variables. 41function hiddenvar ($name, $var) 42{ 43 44 $hidden = ""; 45 if ($var) { 46 #$url = rawurlencode($var); 47 $hidden = "<input type=\"hidden\" name=\"$name\" value=\"$var\">\n"; 48 } 49 return $hidden; 50} 51 52#------------------------------------------------------------------------------ 53# Gives a readable time string, from a "number of seconds" integer. 54# Often used to compute uptime. 55function uptime($uptimeS) 56{ 57 $uptimeD=intval($uptimeS/86400); 58 $uptimeS=$uptimeD ? $uptimeS % ($uptimeD*86400) : $uptimeS; 59 $uptimeH=intval($uptimeS/3600); 60 $uptimeS=$uptimeH ? $uptimeS % ($uptimeH*3600) : $uptimeS; 61 $uptimeM=intval($uptimeS/60); 62 $uptimeS=$uptimeM ? $uptimeS % ($uptimeM*60) : $uptimeS; 63 64 $s = ($uptimeD!=1) ? "s" : ""; 65 return sprintf("$uptimeD day$s, %d:%02d:%02d",$uptimeH,$uptimeM,$uptimeS); 66} 67 68#------------------------------------------------------------------------------ 69# Try to determine a nodes location in the cluster. Attempts to find the 70# LOCATION attribute first. Requires the host attribute array from 71# $hosts[$cluster][$name], where $name is the hostname. 72# Returns [-1,-1,-1] if we could not determine location. 73# 74function findlocation($attrs) 75{ 76 $rack=$rank=$plane=-1; 77 78 $loc=$attrs['LOCATION']; 79 if ($loc) { 80 sscanf($loc, "%d,%d,%d", $rack, $rank, $plane); 81 #echo "Found LOCATION: $rack, $rank, $plane.<br>"; 82 } 83 if ($rack<0 or $rank<0) { 84 # Try to parse the host name. Assumes a compute-<rack>-<rank> 85 # naming scheme. 86 $n=sscanf($attrs['NAME'], "compute-%d-%d", $rack, $rank); 87 $plane=0; 88 } 89 return array($rack,$rank,$plane); 90} 91 92 93#------------------------------------------------------------------------------ 94function cluster_sum($name, $metrics) 95{ 96 $sum = 0; 97 98 foreach ($metrics as $host => $val) 99 { 100 if(isset($val[$name]['VAL'])) $sum += $val[$name]['VAL']; 101 } 102 103 return $sum; 104} 105 106#------------------------------------------------------------------------------ 107function cluster_min($name, $metrics) 108{ 109 $min = ""; 110 111 foreach ($metrics as $host => $val) 112 { 113 $v = $val[$name]['VAL']; 114 if (!is_numeric($min) or $min < $v) 115 { 116 $min = $v; 117 $minhost = $host; 118 } 119 } 120 return array($min, $minhost); 121} 122 123#------------------------------------------------------------------------------ 124# 125# A useful function for giving the correct picture for a given 126# load. Scope is "node | cluster | grid". Value is 0 <= v <= 1. 127function load_image ($scope, $value) 128{ 129 global $conf; 130 131 $scaled_load = $value / $conf['load_scale']; 132 if ($scaled_load>1.00) { 133 $image = template("images/${scope}_overloaded.jpg"); 134 } 135 else if ($scaled_load>=0.75) { 136 $image = template("images/${scope}_75-100.jpg"); 137 } 138 else if ($scaled_load >= 0.50) { 139 $image = template("images/${scope}_50-74.jpg"); 140 } 141 else if ($scaled_load>=0.25) { 142 $image = template("images/${scope}_25-49.jpg"); 143 } 144 else { 145 $image = template("images/${scope}_0-24.jpg"); 146 } 147 148 return $image; 149} 150 151#------------------------------------------------------------------------------ 152# A similar function that specifies the background color for a graph 153# based on load. Quantizes the load figure into 6 sets. 154function load_color ($value) 155{ 156 global $conf; 157 158 $scaled_load = $value / $conf['load_scale']; 159 if ($scaled_load>1.00) { 160 $color = $conf['load_colors']["100+"]; 161 } 162 else if ($scaled_load>=0.75) { 163 $color = $conf['load_colors']["75-100"]; 164 } 165 else if ($scaled_load >= 0.50) { 166 $color = $conf['load_colors']["50-75"]; 167 } 168 else if ($scaled_load>=0.25) { 169 $color = $conf['load_colors']["25-50"]; 170 } 171 else if ($scaled_load < 0.0) 172 $color = $conf['load_colors']["down"]; 173 else { 174 $color = $conf['load_colors']["0-25"]; 175 } 176 177 return $color; 178} 179 180#------------------------------------------------------------------------------ 181# 182# Just a useful function to print the HTML for 183# the load/death of a cluster node 184function node_image ($metrics) 185{ 186 global $hosts_down; 187 188 # More rigorous checking if variables are set before trying to use them. 189 if ( isset($metrics['cpu_num']['VAL']) and $metrics['cpu_num']['VAL'] != 0 ) { 190 $cpu_num = $metrics['cpu_num']['VAL']; 191 } else { 192 $cpu_num = 1; 193 } 194 195 if ( isset($metrics['load_one']['VAL']) ) { 196 $load_one = $metrics['load_one']['VAL']; 197 } else { 198 $load_one = 0; 199 } 200 201 $value = $load_one / $cpu_num; 202 203 # Check if the host is down 204 # RFM - Added isset() check to eliminate error messages in ssl_error_log 205 if (isset($hosts_down) and $hosts_down) 206 $image = template("images/node_dead.jpg"); 207 else 208 $image = load_image("node", $value); 209 210 return $image; 211} 212 213#------------------------------------------------------------------------------ 214# 215# Finds the min/max over a set of metric graphs. Nodes is 216# an array keyed by host names. 217# 218function find_limits($clustername, 219 $nodes, 220 $metricname, 221 $start, 222 $end, 223 $metrics, 224 $conf, 225 $rrd_options) { 226 if (!count($metrics)) 227 return array(0, 0); 228 229 $firsthost = key($metrics); 230 231 if (array_key_exists($metricname, $metrics[$firsthost])) { 232 if ($metrics[$firsthost][$metricname]['TYPE'] == "string" 233 or $metrics[$firsthost][$metricname]['SLOPE'] == "zero") 234 return array(0,0); 235 } else { 236 return array(0,0); 237 } 238 239 $max = 0; 240 $min = 0; 241 if ($conf['graph_engine'] == "graphite") { 242 $target = $conf['graphite_prefix'] . 243 $clustername . ".[a-zA-Z0-9]*." . $metricname . ".sum"; 244 $raw_highestMax = file_get_contents($conf['graphite_url_base'] . "?target=highestMax(" . $target . ",1)&from=" . $start . "&until=" . $end . "&format=json"); 245 $highestMax = json_decode($raw_highestMax, TRUE); 246 $highestMaxDatapoints = $highestMax[0]['datapoints']; 247 $maxdatapoints = array(); 248 foreach ($highestMaxDatapoints as $datapoint) { 249 array_push($maxdatapoints, $datapoint[0]); 250 } 251 $max = max($maxdatapoints); 252 } else { 253 foreach (array_keys($nodes) as $host) { 254 $rrd_dir = "{$conf['rrds']}/$clustername/$host"; 255 $rrd_file = "$rrd_dir/$metricname.rrd"; 256 if (file_exists($rrd_file)) { 257 if (extension_loaded('rrd')) { 258 $values = rrd_fetch($rrd_file, 259 array("--start", $start, 260 "--end", $end, 261 "AVERAGE")); 262 263 $values = (array_filter(array_values($values['data']['sum']), 264 'is_finite')); 265 $thismax = max($values); 266 $thismin = min($values); 267 } else { 268 $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ". 269 "--start '$start' --end '$end' ". 270 "DEF:limits='$rrd_dir/$metricname.rrd':'sum':AVERAGE ". 271 "PRINT:limits:MAX:%.2lf ". 272 "PRINT:limits:MIN:%.2lf"; 273 $out = array(); 274 exec($command, $out); 275 if (isset($out[1])) { 276 $thismax = $out[1]; 277 } else { 278 $thismax = NULL; 279 } 280 if (!is_numeric($thismax)) 281 continue; 282 $thismin = $out[2]; 283 if (!is_numeric($thismin)) 284 continue; 285 } 286 287 if ($max < $thismax) 288 $max = $thismax; 289 290 if ($min > $thismin) 291 $min = $thismin; 292 //echo "$host: $thismin - $thismax<br>\n"; 293 } 294 } 295 } 296 return array($min, $max); 297} 298 299#------------------------------------------------------------------------------ 300# 301# Finds the avg of the given cluster & metric from the summary rrds. 302# 303function find_avg($clustername, $hostname, $metricname) 304{ 305 global $conf, $start, $end, $rrd_options; 306 $avg = 0; 307 308 if ($hostname) 309 $sum_dir = "${conf['rrds']}/$clustername/$hostname"; 310 else 311 $sum_dir = "${conf['rrds']}/$clustername/__SummaryInfo__"; 312 313 $command = $conf['rrdtool'] . " graph /dev/null $rrd_options ". 314 "--start $start --end $end ". 315 "DEF:avg='$sum_dir/$metricname.rrd':'sum':AVERAGE ". 316 "PRINT:avg:AVERAGE:%.2lf "; 317 exec($command, $out); 318 if ( isset($out[1]) ) 319 $avg = $out[1]; 320 else 321 $avg = 0; 322 #echo "$sum_dir: avg($metricname)=$avg<br>\n"; 323 return $avg; 324} 325 326#------------------------------------------------------------------------------ 327# Alternate between even and odd row styles. 328function rowstyle() 329{ 330 static $style; 331 332 if ($style == "even") { $style = "odd"; } 333 else { $style = "even"; } 334 335 return $style; 336} 337 338#------------------------------------------------------------------------------ 339# Return a version of the string which is safe for display on a web page. 340# Potentially dangerous characters are converted to HTML entities. 341# Resulting string is not URL-encoded. 342function clean_string( $string ) 343{ 344 return htmlentities( $string ); 345} 346#------------------------------------------------------------------------------ 347function sanitize ( $string ) { 348 return escapeshellcmd( clean_string( rawurldecode( $string ) ) ) ; 349} 350 351#------------------------------------------------------------------------------ 352# If arg is a valid number, return it. Otherwise, return null. 353function clean_number( $value ) 354{ 355 return is_numeric( $value ) ? $value : null; 356} 357 358#------------------------------------------------------------------------------ 359# Return true if string is a 3 or 6 character hex color.Return false otherwise. 360function is_valid_hex_color( $string ) 361{ 362 $return_value = false; 363 if( strlen( $string ) == 6 || strlen( $string ) == 3 ) { 364 if( preg_match( '/^[0-9a-fA-F]+$/', $string ) ) { 365 $return_value = true; 366 } 367 } 368 return $return_value; 369 370} 371 372#------------------------------------------------------------------------------ 373# Allowed view name characters are alphanumeric plus space, dash and underscore 374function is_proper_view_name( $string ) 375{ 376 if(preg_match("/[^a-zA-z0-9_\-\ ]/", $string)){ 377 return false; 378 } else { 379 return true; 380 } 381} 382 383 384#------------------------------------------------------------------------------ 385# Return a shortened version of a FQDN 386# if "hostname" is numeric only, assume it is an IP instead 387# 388function strip_domainname( $hostname ) { 389 $postition = strpos($hostname, '.'); 390 $name = substr( $hostname , 0, $postition ); 391 if ( FALSE === $postition || is_numeric($name) ) { 392 return $hostname; 393 } else { 394 return $name; 395 } 396} 397 398#------------------------------------------------------------------------------ 399# Read a file containing key value pairs 400function file_to_hash($filename, $sep) 401{ 402 403 $lines = file($filename, FILE_IGNORE_NEW_LINES); 404 405 foreach ($lines as $line) 406 { 407 list($k, $v) = explode($sep, rtrim($line)); 408 $params[$k] = $v; 409 } 410 411 return $params; 412} 413 414#------------------------------------------------------------------------------ 415# Read a file containing key value pairs 416# Multiple values permitted for each key 417function file_to_hash_multi($filename, $sep) 418{ 419 420 $lines = file($filename); 421 422 foreach ($lines as $line) 423 { 424 list($k, $v) = explode($sep, rtrim($line)); 425 $params[$k][] = $v; 426 } 427 428 return $params; 429} 430 431#------------------------------------------------------------------------------ 432# Obtain a list of distinct values from an array of arrays 433function hash_get_distinct_values($h) 434{ 435 $values = array(); 436 $values_done = array(); 437 foreach($h as $k => $v) 438 { 439 if($values_done[$v] != "x") 440 { 441 $values_done[$v] = "x"; 442 $values[] = $v; 443 } 444 } 445 return $values; 446} 447 448$filter_defs = array(); 449 450#------------------------------------------------------------------------------ 451# Scan $conf['filter_dir'] and populate $filter_defs 452function discover_filters() 453{ 454 global $conf, $filter_defs; 455 456 # Check whether filtering is configured or not 457 if(!isset($conf['filter_dir'])) 458 return; 459 460 if(!is_dir($conf['filter_dir'])) 461 { 462 error_log("discover_filters(): not a directory: ${conf['filter_dir']}"); 463 return; 464 } 465 466 if($dh = opendir($conf['filter_dir'])) 467 { 468 while(($filter_conf_filename = readdir($dh)) !== false) { 469 if(!is_dir($filter_conf_filename)) 470 { 471 # Parse the file contents 472 $full_filename = "${conf['filter_dir']}/$filter_conf_filename"; 473 $filter_params = file_to_hash($full_filename, '='); 474 $filter_shortname = $filter_params["shortname"]; 475 $filter_type = $filter_params["type"]; 476 if($filter_type = "url") 477 { 478 $filter_data_url = $filter_params['url']; 479 $filter_defs[$filter_shortname] = $filter_params; 480 $filter_defs[$filter_shortname]["data"] = file_to_hash($filter_data_url, ','); 481 $filter_defs[$filter_shortname]["choices"] = hash_get_distinct_values($filter_defs[$filter_shortname]["data"]); 482 } 483 } 484 } 485 closedir($dh); 486 } 487} 488 489$filter_permit_list = NULL; 490 491#------------------------------------------------------------------------------ 492# Initialise the filter permit list, if necessary 493function filter_init() 494{ 495 global $conf, $filter_permit_list, $filter_defs, $choose_filter; 496 497 if(!is_null($filter_permit_list)) 498 { 499 return; 500 } 501 502 if(!isset($conf['filter_dir'])) 503 { 504 $filter_permit_list = FALSE; 505 return; 506 } 507 508 $filter_permit_list = array(); 509 $filter_count = 0; 510 511 foreach($choose_filter as $filter_shortname => $filter_choice) 512 { 513 if($filter_choice == "") 514 continue; 515 516 $filter_params = $filter_defs[$filter_shortname]; 517 if($filter_count == 0) 518 { 519 foreach($filter_params["data"] as $key => $value) 520 { 521 if($value == $filter_choice) 522 $filter_permit_list[$key] = $key; 523 } 524 } 525 else 526 { 527 foreach($filter_permit_list as $key => $value) 528 { 529 $remove_key = TRUE; 530 if(isset($filter_params["data"][$key])) 531 { 532 if($filter_params["data"][$key] == $filter_choice) 533 { 534 $remove_key = FALSE; 535 } 536 } 537 if($remove_key) 538 { 539 unset($filter_permit_list[$key]); 540 } 541 } 542 } 543 $filter_count++; 544 } 545 546 if($filter_count == 0) 547 $filter_permit_list = FALSE; 548 549} 550 551#------------------------------------------------------------------------------ 552# Decide whether the given source is permitted by the filters, if any 553function filter_permit($source_name) 554{ 555 global $filter_permit_list; 556 557 filter_init(); 558 559 # Handle the case where filtering is not active 560 if(!is_array($filter_permit_list)) 561 return true; 562 563 return isset($filter_permit_list[$source_name]); 564} 565 566$VIEW_NAME_SEP = '--'; 567 568function viewName($view) { 569 global $VIEW_NAME_SEP; 570 571 $vn = ''; 572 if ($view['parent'] != NULL) 573 $vn = str_replace('/', $VIEW_NAME_SEP, $view['parent']) . $VIEW_NAME_SEP; 574 $vn .= $view['view_name']; 575 return $vn; 576} 577 578class ViewList { 579 private $available_views; 580 581 public function __construct() { 582 $this->available_views = get_available_views(); 583 } 584 585 public function viewExists($view_name) { 586 foreach ($this->available_views as $view) { 587 if ($view['view_name'] == $view_name) { 588 return TRUE; 589 } 590 } 591 return FALSE; 592 } 593 594 public function getView($view_name) { 595 foreach ($this->available_views as $view) { 596 if (viewName($view) == $view_name) { 597 return $view; 598 } 599 } 600 return NULL; 601 } 602 603 public function removeView($view_name) { 604 foreach ($this->available_views as $key => $view) { 605 if (viewName($view) == $view_name) { 606 unset($this->available_views[$key]); 607 return; 608 } 609 } 610 } 611 612 public function getViews() { 613 return $this->available_views; 614 } 615} 616 617function getViewItems($view, $range, $cs, $ce) { 618 $view_elements = get_view_graph_elements($view); 619 $view_items = array(); 620 if (count($view_elements) != 0) { 621 $graphargs = ""; 622 if ($cs) 623 $graphargs .= "&cs=" . rawurlencode($cs); 624 if ($ce) 625 $graphargs .= "&ce=" . rawurlencode($ce); 626 627 foreach ($view_elements as $element) { 628 $canBeDecomposed = isset($element['aggregate_graph']) || 629 ((strpos($element['graph_args'], 'vn=') !== FALSE) && 630 (strpos($element['graph_args'], 'item_id=') !== FALSE)); 631 $view_items[] = 632 array("legend" => isset($element['hostname']) ? 633 $element['hostname'] : "Aggregate graph", 634 "url_args" => htmlentities($element['graph_args']) . 635 "&r=" . $range . $graphargs, 636 "aggregate_graph" => isset($element['aggregate_graph']) ? 1 : 0, 637 "canBeDecomposed" => $canBeDecomposed ? 1 : 0); 638 } 639 } 640 return $view_items; 641} 642 643/////////////////////////////////////////////////////////////////////////////// 644// Get all the available views 645/////////////////////////////////////////////////////////////////////////////// 646function get_available_views() { 647 global $conf; 648 649 /* ----------------------------------------------------------------------- 650 Find available views by looking in the GANGLIA_DIR/conf directory 651 anything that matches view_*.json. Read them all and build a available_views 652 array 653 ----------------------------------------------------------------------- */ 654 $available_views = array(); 655 656 if ($handle = opendir($conf['views_dir'])) { 657 while (false !== ($file = readdir($handle))) { 658 if (preg_match("/^view_(.*)\.json$/", $file, $out)) { 659 $view_config_file = $conf['views_dir'] . "/" . $file; 660 if (!is_file ($view_config_file)) { 661 echo("Can't read view config file " . 662 $view_config_file . ". Please check permissions"); 663 } 664 665 $view = json_decode(file_get_contents($view_config_file), TRUE); 666 // Check whether view type has been specified ie. regex. 667 // If not it's standard view 668 $view_type = 669 isset($view['view_type']) ? $view['view_type'] : "standard"; 670 $default_size = isset($view['default_size']) ? 671 $view['default_size'] : $conf['default_view_graph_size']; 672 $view_parent = 673 isset($view['parent']) ? $view['parent'] : NULL; 674 $common_y_axis = 675 isset($view['common_y_axis']) ? $view['common_y_axis'] : 0; 676 677 $available_views[] = array ("file_name" => $view_config_file, 678 "view_name" => $view['view_name'], 679 "default_size" => $default_size, 680 "items" => $view['items'], 681 "view_type" => $view_type, 682 "parent" => $view_parent, 683 "common_y_axis" => $common_y_axis); 684 unset($view); 685 } 686 } 687 closedir($handle); 688 } 689 690 foreach ($available_views as $key => $row) { 691 $name[$key] = strtolower($row['view_name']); 692 } 693 694 @array_multisort($name, SORT_ASC, $available_views); 695 696 return $available_views; 697} 698 699/////////////////////////////////////////////////////////////////////////////// 700// Get image graph URLS 701// This function returns an array of graph URLs to be used when rendering the 702// view. It returns only the base ie. cluster, host, metric information. 703// It is up to the caller to add proper size information, time ranges etc. 704/////////////////////////////////////////////////////////////////////////////// 705function get_view_graph_elements($view) { 706 global $conf, $index_array; 707 708 retrieve_metrics_cache(); 709 710 $view_elements = array(); 711 712 // set the default size from the view or global config 713 if ( isset($conf['default_view_graph_size']) ) { 714 $default_size = $conf['default_view_graph_size']; 715 } 716 717 if ( isset($view['default_size']) ) { 718 $default_size = $view['default_size']; 719 } 720 721 722 switch ( $view['view_type'] ) { 723 case "standard": 724 // Does view have any items/graphs defined 725 if ( count($view['items']) == 0 ) { 726 continue; 727 // print "No graphs defined for this view. Please add some"; 728 } else { 729 // Loop through graph items 730 foreach ($view['items'] as $item_id => $item) { 731 // Check if item is an aggregate graph 732 if (isset($item['aggregate_graph'])) { 733 foreach ( $item['host_regex'] as $reg_id => $regex_array ) { 734 $graph_args_array[] = "hreg[]=" . urlencode($regex_array["regex"]); 735 } 736 737 if (isset($item['metric_regex'])) { 738 foreach ( $item['metric_regex'] as $reg_id => $regex_array ) { 739 $graph_args_array[] = 740 "mreg[]=" . urlencode($regex_array["regex"]); 741 $mreg[] = $regex_array["regex"]; 742 } 743 } 744 745 if ( isset($item['size']) ) { 746 $graph_args_array[] = "z=" . $item['size']; 747 } else { 748 $graph_args_array[] = "z=" . $default_size; 749 } 750 751 if ( isset($item['sortit']) ) { 752 $graph_args_array[] = "sortit=" . $item['sortit']; 753 } 754 755 // If graph type is not specified default to line graph 756 if (isset($item['graph_type']) && 757 in_array($item['graph_type'], array('line', 'stack'))) 758 $graph_args_array[] = "gtype=" . $item['graph_type']; 759 else 760 $graph_args_array[] = "gtype=line"; 761 762 if (isset($item['upper_limit'])) 763 $graph_args_array[] = "x=" . $item['upper_limit']; 764 765 if (isset($item['lower_limit'])) 766 $graph_args_array[] = "n=" . $item['lower_limit']; 767 768 if (isset($item['vertical_label'])) 769 $graph_args_array[] = "vl=" . urlencode($item['vertical_label']); 770 771 if (isset($item['title'])) 772 $graph_args_array[] = "title=" . urlencode($item['title']); 773 774 if (isset($item['metric'])) 775 $graph_args_array[] = "m=" . $item['metric']; 776 777 if (isset($item['glegend'])) 778 $graph_args_array[] = "glegend=" . $item["glegend"]; 779 780 if (isset($item['cluster'])) 781 $graph_args_array[] = "c=" . urlencode($item['cluster']); 782 783 if (isset($item['exclude_host_from_legend_label'])) 784 $graph_args_array[] = 785 "lgnd_xh=" . $item['exclude_host_from_legend_label']; 786 787 $graph_args_array[] = "aggregate=1"; 788 $view_elements[] = 789 array("graph_args" => join("&", $graph_args_array), 790 "aggregate_graph" => 1, 791 "name" => isset($item['title']) && $item['title'] != "" ? 792 $item['title'] : $mreg[0] . " Aggregate graph"); 793 794 unset($graph_args_array); 795 796 // Check whether it's a composite graph/report. 797 // It needs to have an item id 798 } else if ($item['item_id']) { 799 $graph_args_array[] = "vn=" . $view['view_name']; 800 $graph_args_array[] = "item_id=" . $item['item_id']; 801 802 $view_elements[] = 803 array("graph_args" => join("&", $graph_args_array)); 804 unset($graph_args_array); 805 806 // It's standard metric graph 807 } else { 808 // Is it a metric or a graph(report) 809 if (isset($item['metric'])) { 810 $graph_args_array[] = "m=" . $item['metric']; 811 $name = $item['metric']; 812 } else { 813 $graph_args_array[] = "g=" . urlencode($item['graph']); 814 $name = $item['graph']; 815 } 816 if ( isset($item['size']) ) { 817 $graph_args_array[] = "z=" . $item['size']; 818 } else { 819 $graph_args_array[] = "z=" . $default_size; 820 } 821 822 if (isset($item['hostname'])) { 823 $hostname = $item['hostname']; 824 $cluster = array_key_exists($hostname, $index_array['cluster']) ? 825 $index_array['cluster'][$hostname][0] : NULL; 826 $graph_args_array[] = "h=" . urlencode($hostname); 827 } else if (isset($item['cluster'])) { 828 $hostname = ""; 829 $cluster = $item['cluster']; 830 } else { 831 $hostname = ""; 832 $cluster = ""; 833 } 834 $graph_args_array[] = "c=" . urlencode($cluster); 835 836 if (isset($item['upper_limit'])) 837 $graph_args_array[] = "x=" . $item['upper_limit']; 838 839 if (isset($item['lower_limit'])) 840 $graph_args_array[] = "n=" . $item['lower_limit']; 841 842 if (isset($item['vertical_label'])) 843 $graph_args_array[] = "vl=" . urlencode($item['vertical_label']); 844 845 if (isset($item['title'])) 846 $graph_args_array[] = "title=" . urlencode($item['title']); 847 848 if (isset($item['warning'])) { 849 $view_e['warning'] = $item['warning']; 850 $graph_args_array[] = "warn=" . $item['warning']; 851 } 852 if (isset($item['critical'])) { 853 $view_e['critical'] = $item['critical']; 854 $graph_args_array[] = "crit=" . $item['critical']; 855 } 856 857 if (isset($item['alias'])) { 858 $view_e['alias'] = $item['alias']; 859 } 860 861 $view_e["graph_args"] = join("&", $graph_args_array); 862 $view_e['hostname'] = $hostname; 863 $view_e['cluster'] = $cluster; 864 $view_e['name'] = $name; 865 866 $view_elements[] = $view_e; 867 868 unset($view_e); 869 unset($graph_args_array); 870 } 871 } // end of foreach ( $view['items'] 872 } // end of if ( count($view['items']) 873 break; 874 875 /////////////////////////////////////////////////////////////////////////// 876 // Currently only supports matching hosts. 877 /////////////////////////////////////////////////////////////////////////// 878 case "regex": 879 foreach ($view['items'] as $item_id => $item) { 880 // Is it a metric or a graph(report) 881 if ( isset($item['metric']) ) { 882 $metric_suffix = "m=" . $item['metric']; 883 $name = $item['metric']; 884 } else { 885 $metric_suffix = "g=" . $item['graph']; 886 $name = $item['graph']; 887 } 888 889 // Find hosts matching a criteria 890 $query = $item['hostname']; 891 foreach ( $index_array['hosts'] as $key => $host_name ) { 892 if (preg_match("/$query/", $host_name)) { 893 $clusters = $index_array['cluster'][$host_name]; 894 foreach ($clusters AS $cluster) { 895 $graph_args_array[] = "h=" . urlencode($host_name); 896 $graph_args_array[] = "c=" . urlencode($cluster); 897 898 $view_elements[] = 899 array("graph_args" => $metric_suffix . "&" . join("&", $graph_args_array), 900 "hostname" => $host_name, 901 "cluster" => $cluster, 902 "name" => $name); 903 904 unset($graph_args_array); 905 } 906 } 907 } 908 } // end of foreach ( $view['items'] as $item_id => $item ) 909 break;; 910 } // end of switch ( $view['view_type'] ) { 911 return ($view_elements); 912} 913 914function legendEntry($vname, $legend_items) { 915 $legend = ""; 916 if (in_array("now", $legend_items)) 917 $legend .= "VDEF:{$vname}_last={$vname},LAST "; 918 919 if (in_array("min", $legend_items)) 920 $legend .= "VDEF:{$vname}_min={$vname},MINIMUM "; 921 922 if (in_array("avg", $legend_items)) 923 $legend .= "VDEF:{$vname}_avg={$vname},AVERAGE "; 924 925 if (in_array("max", $legend_items)) 926 $legend .= "VDEF:{$vname}_max={$vname},MAXIMUM "; 927 928 $terminate = FALSE; 929 if (in_array("now", $legend_items)) { 930 $legend .= "GPRINT:'{$vname}_last':'Now\:%5.1lf%s"; 931 $terminate = TRUE; 932 } 933 934 if (in_array("min", $legend_items)) { 935 if ($terminate) 936 $legend .= "' "; 937 $legend .= "GPRINT:'{$vname}_min':'Min\:%5.1lf%s"; 938 $terminate = TRUE; 939 } 940 941 if (in_array("avg", $legend_items)) { 942 if ($terminate) 943 $legend .= "' "; 944 $legend .= "GPRINT:'{$vname}_avg':'Avg\:%5.1lf%s"; 945 $terminate = TRUE; 946 } 947 948 if (in_array("max", $legend_items)) { 949 if ($terminate) 950 $legend .= "' "; 951 $legend .= "GPRINT:'{$vname}_max':'Max\:%5.1lf%s"; 952 $terminate = TRUE; 953 } 954 955 if ($terminate) 956 $legend .= "\\l' "; 957 958 return $legend; 959} 960 961/** 962 * Check if current user has a privilege (view, edit, etc) on a resource. 963 * If resource is unspecified, we assume GangliaAcl::ALL. 964 * 965 * Examples 966 * checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::EDIT, $conf ); // user has global edit? 967 * checkAccess( GangliaAcl::ALL_CLUSTERS, GangliaAcl::VIEW, $conf ); // user has global view? 968 * checkAccess( $cluster, GangliaAcl::EDIT, $conf ); // user can edit current cluster? 969 * checkAccess( 'cluster1', GangliaAcl::EDIT, $conf ); // user has edit privilege on cluster1? 970 * checkAccess( 'cluster1', GangliaAcl::VIEW, $conf ); // user has view privilege on cluster1? 971 */ 972function checkAccess($resource, $privilege, $conf) { 973 974 if(!is_array($conf)) { 975 trigger_error('checkAccess: $conf is not an array.', E_USER_ERROR); 976 } 977 if(!isSet($conf['auth_system'])) { 978 trigger_error("checkAccess: \$conf['auth_system'] is not defined.", E_USER_ERROR); 979 } 980 981 switch( $conf['auth_system'] ) { 982 case 'readonly': 983 $out = ($privilege == GangliaAcl::VIEW); 984 break; 985 986 case 'enabled': 987 // TODO: 'edit' needs to check for writeability of data directory. error log if edit is allowed but we're unable to due to fs problems. 988 989 $acl = GangliaAcl::getInstance(); 990 $auth = GangliaAuth::getInstance(); 991 992 if(!$auth->isAuthenticated()) { 993 $user = GangliaAcl::GUEST; 994 } else { 995 $user = $auth->getUser(); 996 } 997 998 if(!$acl->has($resource)) { 999 $resource = GangliaAcl::ALL_CLUSTERS; 1000 } 1001 1002 $out = false; 1003 if($acl->hasRole($user)) { 1004 $out = (bool) $acl->isAllowed($user, $resource, $privilege); 1005 } 1006 // error_log("checkAccess() user=$user, resource=$resource, priv=$privilege == $out"); 1007 break; 1008 1009 case 'disabled': 1010 $out = true; 1011 break; 1012 1013 default: 1014 trigger_error( "Invalid value '".$conf['auth_system']."' for \$conf['auth_system'].", E_USER_ERROR ); 1015 return false; 1016 } 1017 1018 return $out; 1019} 1020 1021function viewId($view_name) { 1022 $id = 'v_' . preg_replace('/[^a-zA-Z0-9_]/', '_', $view_name); 1023 return $id; 1024} 1025 1026/////////////////////////////////////////////////////////////////////////////// 1027// Taken from 1028// http://au2.php.net/manual/en/function.json-encode.php#80339 1029// Pretty print JSON 1030/////////////////////////////////////////////////////////////////////////////// 1031function json_prettyprint($json) 1032{ 1033 $tab = " "; 1034 $new_json = ""; 1035 $indent_level = 0; 1036 $in_string = false; 1037 1038 $len = strlen($json); 1039 1040 for($c = 0; $c < $len; $c++) 1041 { 1042 $char = $json[$c]; 1043 switch($char) 1044 { 1045 case '{': 1046 case '[': 1047 if(!$in_string) 1048 { 1049 $new_json .= $char . "\n" . str_repeat($tab, $indent_level+1); 1050 $indent_level++; 1051 } 1052 else 1053 { 1054 $new_json .= $char; 1055 } 1056 break; 1057 case '}': 1058 case ']': 1059 if(!$in_string) 1060 { 1061 $indent_level--; 1062 $new_json .= "\n" . str_repeat($tab, $indent_level) . $char; 1063 } 1064 else 1065 { 1066 $new_json .= $char; 1067 } 1068 break; 1069 case ',': 1070 if(!$in_string) 1071 { 1072 $new_json .= ",\n" . str_repeat($tab, $indent_level); 1073 } 1074 else 1075 { 1076 $new_json .= $char; 1077 } 1078 break; 1079 case ':': 1080 if(!$in_string) 1081 { 1082 $new_json .= ": "; 1083 } 1084 else 1085 { 1086 $new_json .= $char; 1087 } 1088 break; 1089 case '"': 1090 if($c > 0 && $json[$c-1] != '\\') 1091 { 1092 $in_string = !$in_string; 1093 } 1094 default: 1095 $new_json .= $char; 1096 break; 1097 } 1098 } 1099 1100 return $new_json; 1101} 1102 1103function ganglia_cache_metrics() { 1104 global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics; 1105 1106 require dirname(__FILE__) . '/lib/cache.php'; 1107} // end function ganglia_cache_metrics 1108 1109 1110////////////////////////////////////////////////////////////////////////////// 1111// 1112////////////////////////////////////////////////////////////////////////////// 1113function build_aggregate_graph_config ($graph_type, 1114 $line_width, 1115 $hreg, 1116 $mreg, 1117 $glegend, 1118 $exclude_host_from_legend_label, 1119 $sortit = true) { 1120 1121 global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics; 1122 1123 retrieve_metrics_cache(); 1124 1125 $color_count = count($conf['graph_colors']); 1126 1127 $graph_config["report_name"]=isset($mreg) ? sanitize(implode($mreg)) : NULL; 1128 $graph_config["title"]=isset($mreg) ? sanitize(implode($mreg)) : NULL; 1129 $graph_config["glegend"]=isset($glegend) ? sanitize($glegend) : "show"; 1130 1131 $counter = 0; 1132 1133 /////////////////////////////////////////////////////////////////////////// 1134 // Find matching hosts 1135 foreach ( $hreg as $key => $query ) { 1136 foreach ( $index_array['hosts'] as $key => $host_name ) { 1137 if ( preg_match("/$query/i", $host_name ) ) { 1138 // We can have same hostname in multiple clusters 1139 foreach ($index_array['cluster'][$host_name] AS $cluster) { 1140 $host_matches[] = $host_name . "|" . $cluster; 1141 } 1142 } 1143 } 1144 } 1145 1146 sort($host_matches); 1147 1148 if( isset($mreg)) { 1149 // Find matching metrics 1150 foreach ( $mreg as $key => $query ) { 1151 foreach ( $index_array['metrics'] as $metric_key => $m_name ) { 1152 if ( preg_match("/$query/i", $metric_key, $metric_subexpr ) ) { 1153 if (isset($metric_subexpr) && count($metric_subexpr) > 1) { 1154 $legend = array(); 1155 for ($i = 1; $i < count($metric_subexpr); $i++) { 1156 $legend[] = $metric_subexpr[$i]; 1157 } 1158 $metric_matches[$metric_key] = implode(' ', $legend); 1159 } else { 1160 $metric_matches[$metric_key] = $metric_key; 1161 } 1162 } 1163 } 1164 } 1165 if($sortit) { 1166 ksort($metric_matches); 1167 } 1168 } 1169 if( isset($metric_matches)){ 1170 $metric_matches_unique = array_unique($metric_matches); 1171 } 1172 else{ 1173 $metric_matches_unique = array($metric_name => $metric_name); 1174 } 1175 1176 if ( isset($host_matches)) { 1177 1178 $host_matches_unique = array_unique($host_matches); 1179 1180 // Create graph_config series from matched hosts and metrics 1181 foreach ( $host_matches_unique as $key => $host_cluster ) { 1182 1183 $out = explode("|", $host_cluster); 1184 1185 $host_name = $out[0]; 1186 $cluster_name = $out[1]; 1187 1188 foreach ( $metric_matches_unique as $m_name => $legend ) { 1189 1190 // We need to cycle the available colors 1191 $color_index = $counter % $color_count; 1192 1193 // next loop if there is no metric for this hostname 1194 if( !in_array($host_name, $index_array['metrics'][$m_name])) 1195 continue; 1196 1197 $label = ''; 1198 if ($exclude_host_from_legend_label) { 1199 $label = $legend; 1200 } else { 1201 if ($conf['strip_domainname'] == True ) 1202 $label = strip_domainname($host_name); 1203 else 1204 $label = $host_name; 1205 1206 if (isset($metric_matches) and count($metric_matches_unique) > 1) 1207 $label .= " $legend"; 1208 } 1209 1210 $graph_config['series'][] = array ( "hostname" => $host_name , "clustername" => $cluster_name, 1211 "metric" => $m_name, "color" => $conf['graph_colors'][$color_index], "label" => $label, "line_width" => $line_width, "type" => $graph_type); 1212 1213 $counter++; 1214 1215 } 1216 } 1217 } 1218 1219 return $graph_config; 1220 1221} // function build_aggregate_graph_config () { 1222 1223 1224////////////////////////////////////////////////////////////////////////////// 1225// 1226////////////////////////////////////////////////////////////////////////////// 1227function retrieve_metrics_cache ( $index = "all" ) { 1228 1229 global $conf, $index_array, $hosts, $grid, $clusters, $debug, $metrics, $context; 1230 1231 require dirname(__FILE__) . '/lib/cache.php'; 1232 return; 1233} // end of function get_metrics_cache () { 1234 1235function getHostOverViewData($hostname, 1236 $metrics, 1237 $cluster, 1238 $hosts_up, 1239 $hosts_down, 1240 $always_timestamp, 1241 $always_constant, 1242 $data) { 1243 $data->assign("extra", template("host_extra.tpl")); 1244 1245 $data->assign("host", $hostname); 1246 $data->assign("node_image", node_image($metrics)); 1247 1248 if ($hosts_up) 1249 $data->assign("node_msg", "This host is up and running."); 1250 else 1251 $data->assign("node_msg", "This host is down."); 1252 1253 # No reason to go on if this node is down. 1254 if ($hosts_down) 1255 return; 1256 1257 foreach ($metrics as $name => $v) { 1258 if ($v['TYPE'] == "string" or $v['TYPE']=="timestamp" or 1259 (isset($always_timestamp[$name]) and $always_timestamp[$name])) { 1260 $s_metrics[$name] = $v; 1261 } elseif ($v['SLOPE'] == "zero" or 1262 (isset($always_constant[$name]) and $always_constant[$name])) { 1263 $c_metrics[$name] = $v; 1264 } 1265 } 1266 1267 # in case this is not defined, set to LOCALTIME so uptime will be 0 in the display 1268 $boottime = null; 1269 if (isset($metrics['boottime']['VAL'])) 1270 $boottime = $metrics['boottime']['VAL']; 1271 else 1272 $boottime = $cluster['LOCALTIME']; 1273 1274 # Add the uptime metric for this host. Cannot be done in ganglia.php, 1275 # since it requires a fully-parsed XML tree. The classic contructor problem. 1276 $s_metrics['uptime']['TYPE'] = "string"; 1277 $s_metrics['uptime']['VAL'] = uptime($cluster['LOCALTIME'] - $boottime); 1278 $s_metrics['uptime']['TITLE'] = "Uptime"; 1279 1280 # Add the gmond started timestamps & last reported time (in uptime format) from 1281 # the HOST tag: 1282 $s_metrics['gmond_started']['TYPE'] = "timestamp"; 1283 $s_metrics['gmond_started']['VAL'] = $hosts_up['GMOND_STARTED']; 1284 $s_metrics['gmond_started']['TITLE'] = "Gmond Started"; 1285 $s_metrics['last_reported']['TYPE'] = "string"; 1286 $s_metrics['last_reported']['VAL'] = uptime($cluster['LOCALTIME'] - $hosts_up['REPORTED']); 1287 $s_metrics['last_reported']['TITLE'] = "Last Reported"; 1288 1289 $s_metrics['ip_address']['TITLE'] = "IP Address"; 1290 $s_metrics['ip_address']['VAL'] = $hosts_up['IP']; 1291 $s_metrics['ip_address']['TYPE'] = "string"; 1292 $s_metrics['location']['TITLE'] = "Location"; 1293 $s_metrics['location']['VAL'] = $hosts_up['LOCATION']; 1294 $s_metrics['location']['TYPE'] = "string"; 1295 1296 # String metrics 1297 if (is_array($s_metrics)) { 1298 $s_metrics_data = array(); 1299 ksort($s_metrics); 1300 foreach ($s_metrics as $name => $v) { 1301 # RFM - If units aren't defined for metric, make it be the empty string 1302 ! array_key_exists('UNITS', $v) and $v['UNITS'] = ""; 1303 if (isset($v['TITLE'])) { 1304 $s_metrics_data[$name]["name"] = $v['TITLE']; 1305 } else { 1306 $s_metrics_data[$name]["name"] = $name; 1307 } 1308 if ($v['TYPE']=="timestamp" or 1309 (isset($always_timestamp[$name]) and $always_timestamp[$name])) { 1310 $s_metrics_data[$name]["value"] = date("r", $v['VAL']); 1311 } else { 1312 $s_metrics_data[$name]["value"] = $v['VAL'] . " " . $v['UNITS']; 1313 } 1314 } 1315 } 1316 $data->assign("s_metrics_data", $s_metrics_data); 1317 1318 # Constant metrics. 1319 $c_metrics_data = null; 1320 if (isset($c_metrics) and is_array($c_metrics)) { 1321 $c_metrics_data = array(); 1322 ksort($c_metrics); 1323 foreach ($c_metrics as $name => $v) { 1324 if (isset($v['TITLE'])) { 1325 $c_metrics_data[$name]["name"] = $v['TITLE']; 1326 } else { 1327 $c_metrics_data[$name]["name"] = $name; 1328 } 1329 $c_metrics_data[$name]["value"] = "$v[VAL] $v[UNITS]"; 1330 } 1331 } 1332 $data->assign("c_metrics_data", $c_metrics_data); 1333} 1334 1335function buildMetricMaps($metrics, 1336 $always_timestamp, 1337 $always_constant, 1338 $baseGraphArgs) { 1339 $metricMap = NULL; 1340 $metricGroupMap = NULL; 1341 foreach ($metrics as $name => $metric) { 1342 if ($metric['TYPE'] == "string" or 1343 $metric['TYPE'] == "timestamp" or 1344 (isset($always_timestamp[$name]) and $always_timestamp[$name])) { 1345 } elseif ($metric['SLOPE'] == "zero" or 1346 (isset($always_constant[$name]) and $always_constant[$name])) { 1347 } else { 1348 $graphArgs = $baseGraphArgs . "&v=$metric[VAL]&m=$name"; 1349 # Adding units to graph 2003 by Jason Smith <smithj4@bnl.gov>. 1350 if ($metric['UNITS']) { 1351 $encodeUnits = rawurlencode($metric['UNITS']); 1352 $graphArgs .= "&vl=$encodeUnits"; 1353 } 1354 if (isset($metric['TITLE'])) { 1355 $title = $metric['TITLE']; 1356 $encodeTitle = rawurlencode($title); 1357 $graphArgs .= "&ti=$encodeTitle"; 1358 } 1359 // dump_var($graphArgs, "graphArgs"); 1360 1361 $metricMap[$name]['graph'] = $graphArgs; 1362 $metricMap[$name]['description'] = 1363 isset($metric['DESC']) ? $metric['DESC'] : ''; 1364 $metricMap[$name]['title'] = 1365 isset($metric['TITLE']) ? $metric['TITLE'] : ''; 1366 1367 # Setup an array of groups that can be used for sorting in group view 1368 if ( isset($metrics[$name]['GROUP']) ) { 1369 $groups = $metrics[$name]['GROUP']; 1370 } else { 1371 $groups = array(""); 1372 } 1373 1374 foreach ($groups as $group) { 1375 if (isset($metricGroupMap[$group])) { 1376 $metricGroupMap[$group] = 1377 array_merge($metricGroupMap[$group], (array)$name); 1378 } else { 1379 $metricGroupMap[$group] = array($name); 1380 } 1381 } 1382 } // if 1383 } // foreach 1384 return array($metricMap, $metricGroupMap); 1385} 1386 1387// keep url decoding until it looks good 1388function heuristic_urldecode($blob) { 1389 while (substr($blob,0,1) == "%") { 1390 $blob = rawurldecode($blob); 1391 } 1392 return $blob; 1393} 1394 1395// alternative passthru() implementation to avoid incomplete images shown in 1396// browsers. 1397function my_passthru($command) { 1398 $tf = tempnam('/tmp', 'ganglia-graph.'); 1399 $ret = exec("$command > $tf"); 1400 $size = filesize($tf); 1401 header("Content-Length: $size"); 1402 $fp = fopen($tf, 'rb'); 1403 fpassthru($fp); 1404 fclose($fp); 1405 unlink($tf); 1406} 1407 1408?> 1409