From: Bruno PrĂ©mont Date: Mon, 20 Apr 2009 20:37:42 +0000 (+0200) Subject: php-collection: add basic support for meta-selections X-Git-Tag: collectd-4.7.0~41 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=d1e62aa4497458ed86d20f5ef1b6c00d50596da0;p=collectd.git php-collection: add basic support for meta-selections Add support for new meta selections allowing addition of graphs with wildcard behavior. This adds support for @all selection which matches any values for the given identifier part. In addition those types for which meta graphs exist now also list the individual type instances in addition to the meta graph key for separate graphing. In order to support such new groups the lookup code has been refactored to use a single scanning function which recursively traveses the collectd RRD output directory for hosts, plugins, types and passing the discovered data to callback functions for use. The callbacks returns true to indicate traversal should continue on to next depth level and false to tell it to continue with next element. e.g. true on a host means it should look for plugins for given host, false to continue with next host. Signed-off-by: Florian Forster --- diff --git a/contrib/php-collection/browser.js b/contrib/php-collection/browser.js index 376f025b..4ddc424d 100644 --- a/contrib/php-collection/browser.js +++ b/contrib/php-collection/browser.js @@ -111,9 +111,22 @@ function refillSelect(options, select) { newOption.value = options[i].firstChild ? options[i].firstChild.data : ''; if (keepSelection && newOption.value == oldValue) newOption.setAttribute('selected', 'selected'); - if (keepSelection && optCnt == 1 && newOption.value == '@') { + if (newOption.value[0] == '@') { newOption.setAttribute('style', 'font-style: italic'); - newOption.appendChild(document.createTextNode('Meta graph')); + if (newOption.value == '@' || newOption.value == '@merge') + newOption.appendChild(document.createTextNode('Meta graph')); + else if (newOption.value == '@all') + newOption.appendChild(document.createTextNode('All entries')); + else if (newOption.value == '@merge_sum') + newOption.appendChild(document.createTextNode('Meta summed graph')); + else if (newOption.value == '@merge_avg') + newOption.appendChild(document.createTextNode('Meta averaged graph')); + else if (newOption.value == '@merge_stack') + newOption.appendChild(document.createTextNode('Meta stacked graph')); + else if (newOption.value == '@merge_line') + newOption.appendChild(document.createTextNode('Meta lines graph')); + else + newOption.appendChild(document.createTextNode(newOption.value)); } else newOption.appendChild(document.createTextNode(newOption.value)); select.appendChild(newOption); @@ -285,8 +298,25 @@ function GraphAppend() { var time_list = document.getElementById('timespan'); var timespan = time_list.selectedIndex >= 0 ? time_list.options[time_list.selectedIndex].value : ''; var tinyLegend = document.getElementById('tinylegend').checked; - var logarithmic = document.getElementById('logarithmic').checked - GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic); + var logarithmic = document.getElementById('logarithmic').checked; + if (host[0] == '@' || plugin[0] == '@' || pinst[0] == '@' || type[0] == '@' || (tinst[0] == '@' && tinst.substr(0, 5) != '@merge')) { + var query = 'action=list_graphs&host='+encodeURIComponent(host)+'&plugin='+encodeURIComponent(plugin)+'&plugin_instance='+encodeURIComponent(pinst); + query = query+'&type='+encodeURIComponent(type)+'&type_instance='+encodeURIComponent(tinst)+'×pan='+encodeURIComponent(timespan); + query = query+(logarithmic ? '&logarithmic=1' : '')+(tinyLegend ? '&tinylegend=1' : ''); + loadXMLDoc(dhtml_url, query); + } else + GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic); +} + +function ListOfGraph(response) { + var graphs = response ? response.getElementsByTagName('graph') : null; + if (graphs && graphs.length > 0) { + for (i = 0; i < graphs.length; i++) + GraphDoAppend(graphs[i].getAttribute('host'), graphs[i].getAttribute('plugin'), graphs[i].getAttribute('plugin_instance'), + graphs[i].getAttribute('type'), graphs[i].getAttribute('type_instance'), graphs[i].getAttribute('timespan'), + graphs[i].getAttribute('tinyLegend') == '1', graphs[i].getAttribute('logarithmic') == '1'); + } else + alert('No graph found for adding'); } function GraphDoAppend(host, plugin, pinst, type, tinst, timespan, tinyLegend, logarithmic) { diff --git a/contrib/php-collection/functions.php b/contrib/php-collection/functions.php index f555751a..fa2badce 100644 --- a/contrib/php-collection/functions.php +++ b/contrib/php-collection/functions.php @@ -61,23 +61,173 @@ function collectd_compare_host($a, $b) { return 0; } -/** - * Fetch list of hosts found in collectd's datadirs. - * @return Sorted list of hosts (sorted by label from rigth to left) - */ -function collectd_list_hosts() { +function collectd_walk(&$options) { global $config; - $hosts = array(); foreach($config['datadirs'] as $datadir) - if ($d = @opendir($datadir)) { - while (($dent = readdir($d)) !== false) - if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$dent) && preg_match(REGEXP_HOST, $dent)) - $hosts[] = $dent; - closedir($d); + if ($dh = @opendir($datadir)) { + while (($hdent = readdir($dh)) !== false) { + if ($hdent == '.' || $hdent == '..' || !is_dir($datadir.'/'.$hdent)) + continue; + if (!preg_match(REGEXP_HOST, $hdent)) + continue; + if (isset($options['cb_host']) && ($options['cb_host'] === false || !$options['cb_host']($options, $hdent))) + continue; + + if ($dp = @opendir($datadir.'/'.$hdent)) { + while (($pdent = readdir($dp)) !== false) { + if ($pdent == '.' || $pdent == '..' || !is_dir($datadir.'/'.$hdent.'/'.$pdent)) + continue; + if ($i = strpos($pdent, '-')) { + $plugin = substr($pdent, 0, $i); + $pinst = substr($pdent, $i+1); + } else { + $plugin = $pdent; + $pinst = ''; + } + if (isset($options['cb_plugin']) && ($options['cb_plugin'] === false || !$options['cb_plugin']($options, $hdent, $plugin))) + continue; + if (isset($options['cb_pinst']) && ($options['cb_pinst'] === false || !$options['cb_pinst']($options, $hdent, $plugin, $pinst))) + continue; + + if ($dt = @opendir($datadir.'/'.$hdent.'/'.$pdent)) { + while (($tdent = readdir($dt)) !== false) { + if ($tdent == '.' || $tdent == '..' || !is_file($datadir.'/'.$hdent.'/'.$pdent.'/'.$tdent)) + continue; + if (substr($tdent, strlen($tdent)-4) != '.rrd') + continue; + $tdent = substr($tdent, 0, strlen($tdent)-4); + if ($i = strpos($tdent, '-')) { + $type = substr($tdent, 0, $i); + $tinst = substr($tdent, $i+1); + } else { + $type = $tdent; + $tinst = ''; + } + if (isset($options['cb_type']) && ($options['cb_type'] === false || !$options['cb_type']($options, $hdent, $plugin, $pinst, $type))) + continue; + if (isset($options['cb_tinst']) && ($options['cb_tinst'] === false || !$options['cb_tinst']($options, $hdent, $plugin, $pinst, $type, $tinst))) + continue; + } + closedir($dt); + } + } + closedir($dp); + } + } + closedir($dh); } else error_log('Failed to open datadir: '.$datadir); - $hosts = array_unique($hosts); + return true; +} + +function _collectd_list_cb_host(&$options, $host) { + if ($options['cb_plugin'] === false) { + $options['result'][] = $host; + return false; + } else if (isset($options['filter_host'])) { + if ($options['filter_host'] == '@all') { + return true; // We take anything + } else if (substr($options['filter_host'], 0, 2) == '@.') { + if ($host == substr($options['filter_host'], 2) || substr($host, 0, 1-strlen($options['filter_host'])) == substr($options['filter_host'], 1)) + return true; // Host part of domain + else + return false; + } else if ($options['filter_host'] == $host) { + return true; + } else + return false; + } else + return true; +} + +function _collectd_list_cb_plugin(&$options, $host, $plugin) { + if ($options['cb_pinst'] === false) { + $options['result'][] = $plugin; + return false; + } else if (isset($options['filter_plugin'])) { + if ($options['filter_plugin'] == '@all') + return true; + else if ($options['filter_plugin'] == $plugin) + return true; + else + return false; + } else + return true; +} + +function _collectd_list_cb_pinst(&$options, $host, $plugin, $pinst) { + if ($options['cb_type'] === false) { + $options['result'][] = $pinst; + return false; + } else if (isset($options['filter_pinst'])) { + if ($options['filter_pinst'] == '@all') + return true; + else if (strncmp($options['filter_pinst'], '@merge_', 7) == 0) + return true; + else if ($options['filter_pinst'] == $pinst) + return true; + else + return false; + } else + return true; +} + +function _collectd_list_cb_type(&$options, $host, $plugin, $pinst, $type) { + if ($options['cb_tinst'] === false) { + $options['result'][] = $type; + return false; + } else if (isset($options['filter_type'])) { + if ($options['filter_type'] == '@all') + return true; + else if ($options['filter_type'] == $type) + return true; + else + return false; + } else + return true; +} + +function _collectd_list_cb_tinst(&$options, $host, $plugin, $pinst, $type, $tinst) { + $options['result'][] = $tinst; + return false; +} + +function _collectd_list_cb_graph(&$options, $host, $plugin, $pinst, $type, $tinst) { + if (isset($options['filter_tinst'])) { + if ($options['filter_tinst'] == '@all') { + } else if ($options['filter_tinst'] == $tinst) { + } else if (strncmp($options['filter_tinst'], '@merge', 6) == 0) { + // Need to exclude @merge with non-existent meta graph + } else + return false; + } + if (isset($options['filter_pinst']) && strncmp($options['filter_pinst'], '@merge', 6) == 0) + $pinst = $options['filter_pinst']; + if (isset($options['filter_tinst']) && strncmp($options['filter_tinst'], '@merge', 6) == 0) + $tinst = $options['filter_tinst']; + $ident = collectd_identifier($host, $plugin, $pinst, $type, $tinst); + if (!in_array($ident, $options['ridentifiers'])) { + $options['ridentifiers'][] = $ident; + $options['result'][] = array('host'=>$host, 'plugin'=>$plugin, 'pinst'=>$pinst, 'type'=>$type, 'tinst'=>$tinst); + } +} + +/** + * Fetch list of hosts found in collectd's datadirs. + * @return Sorted list of hosts (sorted by label from rigth to left) + */ +function collectd_list_hosts() { + $options = array( + 'result' => array(), + 'cb_host' => '_collectd_list_cb_host', + 'cb_plugin' => false, + 'cb_pinst' => false, + 'cb_type' => false, + 'cb_tinst' => false + ); + collectd_walk($options); + $hosts = array_unique($options['result']); usort($hosts, 'collectd_compare_host'); return $hosts; } @@ -87,123 +237,66 @@ function collectd_list_hosts() { * @arg_host Name of host for which to return plugins * @return Sorted list of plugins (sorted alphabetically) */ -function collectd_list_plugins($arg_host) { - global $config; - - $plugins = array(); - foreach ($config['datadirs'] as $datadir) - if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host))) { - while (($dent = readdir($d)) !== false) - if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) { - if ($i = strpos($dent, '-')) - $plugins[] = substr($dent, 0, $i); - else - $plugins[] = $dent; - } - closedir($d); - } - $plugins = array_unique($plugins); +function collectd_list_plugins($arg_host, $arg_plugin = null) { + $options = array( + 'result' => array(), + 'cb_host' => '_collectd_list_cb_host', + 'cb_plugin' => '_collectd_list_cb_plugin', + 'cb_pinst' => is_null($arg_plugin) ? false : '_collectd_list_cb_pinst', + 'cb_type' => false, + 'cb_tinst' => false, + 'filter_host' => $arg_host, + 'filter_plugin' => $arg_plugin + ); + collectd_walk($options); + $plugins = array_unique($options['result']); sort($plugins); return $plugins; } /** - * Fetch list of plugin instances found in collectd's datadirs for given host+plugin - * @arg_host Name of host - * @arg_plugin Name of plugin - * @return Sorted list of plugin instances (sorted alphabetically) - */ -function collectd_list_pinsts($arg_host, $arg_plugin) { - global $config; - - $pinsts = array(); - foreach ($config['datadirs'] as $datadir) - if (preg_match(REGEXP_HOST, $arg_host) && ($d = opendir($datadir.'/'.$arg_host))) { - while (($dent = readdir($d)) !== false) - if ($dent != '.' && $dent != '..' && is_dir($datadir.'/'.$arg_host.'/'.$dent)) { - if ($i = strpos($dent, '-')) { - $plugin = substr($dent, 0, $i); - $pinst = substr($dent, $i+1); - } else { - $plugin = $dent; - $pinst = ''; - } - if ($plugin == $arg_plugin) - $pinsts[] = $pinst; - } - closedir($d); - } - $pinsts = array_unique($pinsts); - sort($pinsts); - return $pinsts; -} - -/** * Fetch list of types found in collectd's datadirs for given host+plugin+instance * @arg_host Name of host * @arg_plugin Name of plugin * @arg_pinst Plugin instance * @return Sorted list of types (sorted alphabetically) */ -function collectd_list_types($arg_host, $arg_plugin, $arg_pinst) { - global $config; - - $types = array(); - $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : ''); - if (!preg_match(REGEXP_PLUGIN, $my_plugin)) - return $types; - foreach ($config['datadirs'] as $datadir) - if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) { - while (($dent = readdir($d)) !== false) - if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') { - $dent = substr($dent, 0, strlen($dent)-4); - if ($i = strpos($dent, '-')) - $types[] = substr($dent, 0, $i); - else - $types[] = $dent; - } - closedir($d); - } - $types = array_unique($types); +function collectd_list_types($arg_host, $arg_plugin, $arg_pinst, $arg_type = null) { + $options = array( + 'result' => array(), + 'cb_host' => '_collectd_list_cb_host', + 'cb_plugin' => '_collectd_list_cb_plugin', + 'cb_pinst' => '_collectd_list_cb_pinst', + 'cb_type' => '_collectd_list_cb_type', + 'cb_tinst' => is_null($arg_type) ? false : '_collectd_list_cb_tinst', + 'filter_host' => $arg_host, + 'filter_plugin' => $arg_plugin, + 'filter_pinst' => $arg_pinst, + 'filter_type' => $arg_type + ); + collectd_walk($options); + $types = array_unique($options['result']); sort($types); return $types; } -/** - * Fetch list of type instances found in collectd's datadirs for given host+plugin+instance+type - * @arg_host Name of host - * @arg_plugin Name of plugin - * @arg_pinst Plugin instance - * @arg_type Type - * @return Sorted list of type instances (sorted alphabetically) - */ -function collectd_list_tinsts($arg_host, $arg_plugin, $arg_pinst, $arg_type) { - global $config; - - $tinsts = array(); - $my_plugin = $arg_plugin . (strlen($arg_pinst) ? '-'.$arg_pinst : ''); - if (!preg_match(REGEXP_PLUGIN, $my_plugin)) - return $types; - foreach ($config['datadirs'] as $datadir) - if (preg_match(REGEXP_HOST, $arg_host) && ($d = @opendir($datadir.'/'.$arg_host.'/'.$my_plugin))) { - while (($dent = readdir($d)) !== false) - if ($dent != '.' && $dent != '..' && is_file($datadir.'/'.$arg_host.'/'.$my_plugin.'/'.$dent) && substr($dent, strlen($dent)-4) == '.rrd') { - $dent = substr($dent, 0, strlen($dent)-4); - if ($i = strpos($dent, '-')) { - $type = substr($dent, 0, $i); - $tinst = substr($dent, $i+1); - } else { - $type = $dent; - $tinst = ''; - } - if ($type == $arg_type) - $tinsts[] = $tinst; - } - closedir($d); - } - $tinsts = array_unique($tinsts); - sort($tinsts); - return $tinsts; +function collectd_list_graphs($arg_host, $arg_plugin, $arg_pinst, $arg_type, $arg_tinst) { + $options = array( + 'result' => array(), + 'ridentifiers' => array(), + 'cb_host' => '_collectd_list_cb_host', + 'cb_plugin' => '_collectd_list_cb_plugin', + 'cb_pinst' => '_collectd_list_cb_pinst', + 'cb_type' => '_collectd_list_cb_type', + 'cb_tinst' => '_collectd_list_cb_graph', + 'filter_host' => $arg_host, + 'filter_plugin' => $arg_plugin, + 'filter_pinst' => $arg_pinst, + 'filter_type' => $arg_type, + 'filter_tinst' => $arg_tinst == '@' ? '@merge' : $arg_tinst + ); + collectd_walk($options); + return $options['result']; } /** diff --git a/contrib/php-collection/graph.php b/contrib/php-collection/graph.php index 17749e06..b9fefa6a 100644 --- a/contrib/php-collection/graph.php +++ b/contrib/php-collection/graph.php @@ -164,7 +164,7 @@ $logscale = (boolean)read_var('logarithmic', $_GET, false); $tinylegend = (boolean)read_var('tinylegend', $_GET, false); // Check that at least 1 RRD exists for the specified request -$all_tinst = collectd_list_tinsts($host, $plugin, $pinst, $type); +$all_tinst = collectd_list_types($host, $plugin, $pinst, $type); if (count($all_tinst) == 0) return error404($graph_identifier, "No rrd file found for graphing"); @@ -182,7 +182,7 @@ if ($tinylegend) $opts['tinylegend'] = 1; $rrd_cmd = false; -if (isset($MetaGraphDefs[$type])) { +if ((is_null($tinst) || $tinst == '@merge') && isset($MetaGraphDefs[$type])) { $identifiers = array(); foreach ($all_tinst as &$atinst) $identifiers[] = collectd_identifier($host, $plugin, is_null($pinst) ? '' : $pinst, $type, $atinst); diff --git a/contrib/php-collection/index.php b/contrib/php-collection/index.php index 837d261d..5953fbd3 100644 --- a/contrib/php-collection/index.php +++ b/contrib/php-collection/index.php @@ -39,6 +39,22 @@ function dhtml_response_list(&$items, $method) { print(""); } +function dhtml_response_graphs(&$graphs, $method) { + header("Content-Type: text/xml"); + + print(''."\n"); + print("\n"); + printf(" %s\n", htmlspecialchars($method)); + print(" \n"); + foreach ($graphs as &$graph) + printf(' '."\n", + htmlspecialchars($graph['host']), htmlspecialchars($graph['plugin']), htmlspecialchars($graph['pinst']), + htmlspecialchars($graph['type']), htmlspecialchars($graph['tinst']), htmlspecialchars($graph['timespan']), + htmlspecialchars($graph['logarithmic']), htmlspecialchars($graph['tinyLegend'])); + print(" \n"); + print(""); +} + /** * Product page body with selection fields */ @@ -225,54 +241,87 @@ switch ($action) { case 'list_hosts': // Generate a list of hosts $hosts = collectd_list_hosts(); + if (count($hosts) > 1) + array_unshift($hosts, '@all'); return dhtml_response_list($hosts, 'ListOfHost'); case 'list_plugins': // Generate list of plugins for selected hosts - $arg_hosts = read_var('host', $_POST, array()); - if (!is_array($arg_hosts)) - $arg_hosts = array($arg_hosts); - $plugins = collectd_list_plugins(reset($arg_hosts)); + $arg_hosts = read_var('host', $_POST, ''); + if (is_array($arg_hosts)) + $arg_hosts = reset($arg_hosts); + $plugins = collectd_list_plugins($arg_hosts); + if (count($plugins) > 1) + array_unshift($plugins, '@all'); return dhtml_response_list($plugins, 'ListOfPlugin'); case 'list_pinsts': // Generate list of plugin_instances for selected hosts and plugin - $arg_hosts = read_var('host', $_POST, array()); - if (!is_array($arg_hosts)) - $arg_hosts = array($arg_hosts); + $arg_hosts = read_var('host', $_POST, ''); + if (is_array($arg_hosts)) + $arg_hosts = reset($arg_hosts); $arg_plugin = read_var('plugin', $_POST, ''); - $pinsts = collectd_list_pinsts(reset($arg_hosts), $arg_plugin); + $pinsts = collectd_list_plugins($arg_hosts, $arg_plugin); + if (count($pinsts) > 1) + array_unshift($pinsts, '@all' /* , '@merge_sum', '@merge_avg', '@merge_stack', '@merge_line' */); return dhtml_response_list($pinsts, 'ListOfPluginInstance'); case 'list_types': // Generate list of types for selected hosts, plugin and plugin-instance - $arg_hosts = read_var('host', $_POST, array()); - if (!is_array($arg_hosts)) - $arg_hosts = array($arg_hosts); + $arg_hosts = read_var('host', $_POST, ''); + if (is_array($arg_hosts)) + $arg_hosts = reset($arg_hosts); $arg_plugin = read_var('plugin', $_POST, ''); $arg_pinst = read_var('plugin_instance', $_POST, ''); - $types = collectd_list_types(reset($arg_hosts), $arg_plugin, $arg_pinst); + $types = collectd_list_types($arg_hosts, $arg_plugin, $arg_pinst); + if (count($types) > 1) + array_unshift($types, '@all'); return dhtml_response_list($types, 'ListOfType'); case 'list_tinsts': // Generate list of types for selected hosts, plugin and plugin-instance - $arg_hosts = read_var('host', $_POST, array()); - if (!is_array($arg_hosts)) - $arg_hosts = array($arg_hosts); + $arg_hosts = read_var('host', $_POST, ''); + if (is_array($arg_hosts)) + $arg_hosts = reset($arg_hosts); $arg_plugin = read_var('plugin', $_POST, ''); $arg_pinst = read_var('plugin_instance', $_POST, ''); $arg_type = read_var('type', $_POST, ''); - $tinsts = collectd_list_tinsts(reset($arg_hosts), $arg_plugin, $arg_pinst, $arg_type); - if (count($tinsts)) { - require('definitions.php'); - load_graph_definitions(); - if (isset($MetaGraphDefs[$arg_type])) { - $meta_tinsts = array('@'); - return dhtml_response_list($meta_tinsts, 'ListOfTypeInstance'); + $tinsts = collectd_list_types($arg_hosts, $arg_plugin, $arg_pinst, $arg_type); + if (count($tinsts)) + if ($arg_type != '@all') { + require('definitions.php'); + load_graph_definitions(); + if (isset($MetaGraphDefs[$arg_type])) + array_unshift($tinsts, '@merge'); + if (count($tinsts) > 1) + array_unshift($tinsts, '@all'); + } else { + array_unshift($tinsts, /* '@merge_sum', '@merge_avg', '@merge_stack', '@merge_line', */ '@merge'); + if (count($tinsts) > 1) + array_unshift($tinsts, '@all'); } - } return dhtml_response_list($tinsts, 'ListOfTypeInstance'); + case 'list_graphs': + // Generate list of types for selected hosts, plugin and plugin-instance + $arg_hosts = read_var('host', $_POST, ''); + if (is_array($arg_hosts)) + $arg_hosts = reset($arg_hosts); + $arg_plugin = read_var('plugin', $_POST, ''); + $arg_pinst = read_var('plugin_instance', $_POST, ''); + $arg_type = read_var('type', $_POST, ''); + $arg_tinst = read_var('type_instance', $_POST, ''); + $arg_log = (int)read_var('logarithmic', $_POST, '0'); + $arg_legend = (int)read_var('tinyLegend', $_POST, '0'); + $arg_period = read_var('timespan', $_POST, ''); + $graphs = collectd_list_graphs($arg_hosts, $arg_plugin, $arg_pinst, $arg_type, $arg_tinst); + foreach ($graphs as &$graph) { + $graph['logarithmic'] = $arg_log; + $graph['tinyLegend'] = $arg_legend; + $graph['timespan'] = $arg_period; + } + return dhtml_response_graphs($graphs, 'ListOfGraph'); + case 'overview': default: return build_page();