* @copyright (C) 2006-2014 Adam Armstrong * */ echo " Adding MIN RRA to very old RRD files:" . PHP_EOL; $graphdata['hr_processes'] = 'hr_processes.rrd'; $graphdata['hr_users'] = 'hr_users.rrd'; $graphdata['ucd_contexts'] = 'ucd_ssRawContexts.rrd'; $graphdata['ucd_load'] = 'ucd_load.rrd'; $graphdata['ucd_interrupts'] = 'ucd_ssRawInterrupts.rrd'; foreach ($graphdata as $graph => $graphfile) { echo " * {$graph}: "; foreach (dbFetchRows("SELECT DISTINCT hostname FROM `device_graphs`,`devices` WHERE `devices`.device_id=`device_graphs`.device_id AND graph='{$graph}'") as $entry) { // get_rrd_path expects a $device array, but it only uses the 'hostname' index, so we get away with this. $filename = get_rrd_path($entry, $graphfile); if (file_exists($filename)) { $info = rrdtool_file_info($filename); $has_min = FALSE; foreach ($info['RRA'] as $rra) { if ($rra['cf'] == 'MIN') { $has_min = TRUE; break; } } if (!$has_min) { shell_exec("scripts/rrdtoolx.py addrra {$filename} {$filename}.new RRA:MIN:0.5:6:1440 RRA:MIN:0.5:96:360 RRA:MIN:0.5:288:1440"); rename("{$filename}.new", $filename); echo "U"; } else { echo '.'; } }
/** * Poll a table or oids from SNMP and build an RRD based on an array of arguments. * * Current limitations: * - single MIB and RRD file for all graphs * - single table per MIB * - if set definition 'call_function', than poll used specific function for snmp walk/get, * else by default used snmpwalk_cache_oid() * - allowed oids only with simple numeric index (oid.0, oid.33), NOT allowed (oid.1.2.23) * - only numeric data * * Example of (full) args array: * array( * 'file' => 'someTable.rrd', // [MANDATORY] RRD filename, but if not set used MIB_table.rrd as filename * 'call_function' => 'snmpwalk_cache_oid' // [OPTIONAL] Which function to use for snmp poll, bu default snmpwalk_cache_oid() * 'mib' => 'SOMETHING-MIB', // [OPTIONAL] MIB or list of MIBs separated by a colon * 'mib_dir' => 'something', // [OPTIONAL] OS MIB directory or array of directories * 'graphs' => array('one','two'), // [OPTIONAL] List of graph_types that this table provides * 'table' => 'someTable', // [RECOMENDED] Table name for OIDs * 'numeric' => '.1.3.6.1.4.1.555.4.1.1.48', // [OPTIONAL] Numeric table OID * 'ds_rename' => array('http' => ''), // [OPTIONAL] Array for renaming OIDs to DSes * 'oids' => array( // List of OIDs you can use as key: full OID name * 'someOid' => array( // OID name (You can use OID name, like 'cpvIKECurrSAs') * 'descr' => 'Current IKE SAs', // [OPTIONAL] Description of the OID contents * 'numeric' => '.1.3.6.1.4.1.555.4.1.1.48.45', // [OPTIONAL] Numeric OID * 'index' => '0', // [OPTIONAL] OID index, if not set equals '0' * 'ds_name' => 'IKECurrSAs', // [OPTIONAL] DS name, if not set used OID name truncated to 19 chars * 'ds_type' => 'GAUGE', // [OPTIONAL] DS type, if not set equals 'COUNTER' * 'ds_min' => '0', // [OPTIONAL] Min value for DS, if not set equals 'U' * 'ds_max' => '30000' // [OPTIONAL] Max value for DS, if not set equals '100000000000' * ) * ) * */ function collect_table($device, $oids_def, &$graphs) { $rrd = array(); $mib = NULL; $mib_dirs = NULL; $use_walk = isset($oids_def['table']) && $oids_def['table']; // Use snmpwalk by default $call_function = strtolower($oids_def['call_function']); switch ($call_function) { case 'snmp_get_multi': $use_walk = FALSE; break; case 'snmpwalk_cache_oid': default: $call_function = 'snmpwalk_cache_oid'; if (!$use_walk) { // Break because we should use snmpwalk, but walking table not set return FALSE; } } if (isset($oids_def['numeric'])) { $oids_def['numeric'] = '.' . trim($oids_def['numeric'], '. '); } // Remove trailing dot if (isset($oids_def['mib'])) { $mib = $oids_def['mib']; } if (isset($oids_def['mib_dir'])) { $mib_dirs = mib_dirs($oids_def['mib_dir']); } if (isset($oids_def['file'])) { $rrd_file = $oids_def['file']; } else { if ($mib && isset($oids_def['table'])) { // Try to use MIB & tableName as rrd_file $rrd_file = strtolower(safename($mib . '_' . $oids_def['table'])) . '.rrd'; } else { print_debug(" WARNING, not have rrd filename."); return FALSE; // Not have RRD filename } } // Get MIBS/Tables/OIDs permissions if ($use_walk) { // if use table walk, than check only this table permission (not oids) if (dbFetchCell("SELECT COUNT(*) FROM `devices_mibs` WHERE `device_id` = ? AND `mib` = ? AND `table_name` = ?\n AND (`oid` = '' OR `oid` IS NULL) AND `disabled` = '1'", array($device['device_id'], $mib, $oids_def['table']))) { print_debug(" WARNING, table '" . $oids_def['table'] . "' for '{$mib}' disabled and skipped."); return FALSE; // table disabled, exit } $oids_ok = TRUE; } else { // if use multi_get, than get all disabled oids $oids_disabled = dbFetchColumn("SELECT `oid` FROM `devices_mibs` WHERE `device_id` = ? AND `mib` = ?\n AND (`oid` != '' AND `oid` IS NOT NULL) AND `disabled` = '1'", array($device['device_id'], $mib)); $oids_ok = empty($oids_disabled); // if empty disabled, than set to TRUE } $search = array(); $replace = array(); if (is_array($oids_def['ds_rename'])) { foreach ($oids_def['ds_rename'] as $s => $r) { $search[] = $s; $replace[] = $r; } } // rrd DS limit is 20 bytes (19 chars + NULL terminator) $ds_len = 19; $oids = array(); $oids_index = array(); foreach ($oids_def['oids'] as $oid => $entry) { if (is_numeric($entry['numeric']) && isset($oids_def['numeric'])) { $entry['numeric'] = $oids_def['numeric'] . '.' . $entry['numeric']; // Numeric oid, for future using } if (!isset($entry['index'])) { $entry['index'] = '0'; } if (!isset($entry['ds_type'])) { $entry['ds_type'] = 'COUNTER'; } if (!isset($entry['ds_min'])) { $entry['ds_min'] = 'U'; } if (!isset($entry['ds_max'])) { $entry['ds_max'] = '100000000000'; } if (!isset($entry['ds_name'])) { // Convert OID name to DS name $ds_name = $oid; if (is_array($oids_def['ds_rename'])) { $ds_name = str_replace($search, $replace, $ds_name); } } else { $ds_name = $entry['ds_name']; } if (strlen($ds_name) > $ds_len) { $ds_name = truncate($ds_name, $ds_len, ''); } if (isset($oids_def['no_index']) && $oids_def['no_index'] == TRUE) { $oids[] = $oid; } else { $oids[] = $oid . '.' . $entry['index']; } $oids_index[] = array('index' => $entry['index'], 'oid' => $oid); if (!$use_walk) { // Check permissions for snmp_get_multi _ONLY_ // if at least one oid missing in $oids_disabled than TRUE $oids_ok = $oids_ok || !in_array($oid, $oids_disabled); } $rrd['rrd_create'][] = ' DS:' . $ds_name . ':' . $entry['ds_type'] . ':600:' . $entry['ds_min'] . ':' . $entry['ds_max']; if ($GLOBALS['debug']) { $rrd['ds_list'][] = $ds_name; } // Make DS lists for compare with RRD file in debug } if (!$use_walk && !$oids_ok) { print_debug(" WARNING, oids '" . implode("', '", array_keys($oids_def['oids'])) . "' for '{$mib}' disabled and skipped."); return FALSE; // All oids disabled, exit } switch ($call_function) { case 'snmpwalk_cache_oid': $data = snmpwalk_cache_oid($device, $oids_def['table'], array(), $mib, $mib_dirs); break; case 'snmp_get_multi': $data = snmp_get_multi($device, $oids, "-OQUs", $mib, $mib_dirs); break; } if (isset($GLOBALS['exec_status']['exitcode']) && $GLOBALS['exec_status']['exitcode'] !== 0) { // Break because latest snmp walk/get return not good exitstatus (wrong mib/timeout/error/etc) print_debug(" WARNING, latest snmp walk/get return not good exitstatus for '{$mib}', RRD update skipped."); return FALSE; } if (isset($oids_def['no_index']) && $oids_def['no_index'] == TRUE) { $data[0] = $data['']; } foreach ($oids_index as $entry) { $index = $entry['index']; $oid = $entry['oid']; if (is_numeric($data[$index][$oid])) { $rrd['ok'] = TRUE; // We have any data for current rrd_file $rrd['rrd_update'][] = $data[$index][$oid]; } else { $rrd['rrd_update'][] = 'U'; } } // Ok, all previous checks done, update RRD, table/oids permissions, $graphs if (isset($rrd['ok']) && $rrd['ok']) { // Create/update RRD file $rrd_create = implode('', $rrd['rrd_create']); $rrd_update = 'N:' . implode(':', $rrd['rrd_update']); rrdtool_create($device, $rrd_file, $rrd_create); rrdtool_update($device, $rrd_file, $rrd_update); foreach ($oids_def['graphs'] as $graph) { $graphs[$graph] = TRUE; // Set all graphs to TRUE } // Compare DSes form RRD file with DSes from array if (OBS_DEBUG) { $graph_template = "\$config['graph_types']['device']['GRAPH_CHANGE_ME'] = array(\n"; $graph_template .= " 'file' => '{$rrd_file}',\n"; $graph_template .= " 'ds' => array(\n"; $rrd_file_info = rrdtool_file_info(get_rrd_path($device, $rrd_file)); foreach ($rrd_file_info['DS'] as $ds => $nothing) { $ds_list[] = $ds; $graph_template .= " '{$ds}' => array('label' => '{$ds}'),\n"; } $graph_template .= " )\n);"; $in_args = array_diff($rrd['ds_list'], $ds_list); if ($in_args) { print_message("%rWARNING%n, in file '%W" . $rrd_file_info['filename'] . "%n' different DS lists. NOT have: " . implode(', ', $in_args)); } $in_file = array_diff($ds_list, $rrd['ds_list']); if ($in_file) { print_message("%rWARNING%n, in file '%W" . $rrd_file_info['filename'] . "%n' different DS lists. Excess: " . implode(', ', $in_file)); } // Print example for graph template using rrd_file and ds list print_message($graph_template); } } else { if ($use_walk) { // Table NOT exist on device! // Disable polling table (only if table not enabled manually in DB) if (!dbFetchCell("SELECT COUNT(*) FROM `devices_mibs` WHERE `device_id` = ? AND `mib` = ?\n AND `table_name` = ? AND (`oid` = '' OR `oid` IS NULL)", array($device['device_id'], $mib, $oids_def['table']))) { dbInsert(array('device_id' => $device['device_id'], 'mib' => $mib, 'table_name' => $oids_def['table'], 'disabled' => '1'), 'devices_mibs'); } print_debug(" WARNING, table '" . $oids_def['table'] . "' for '{$mib}' disabled."); } else { // OIDs NOT exist on device! // Disable polling oids (only if table not enabled manually in DB) foreach (array_keys($oids_def['oids']) as $oid) { if (!dbFetchCell("SELECT COUNT(*) FROM `devices_mibs` WHERE `device_id` = ? AND `mib` = ?\n AND `oid` = ?", array($device['device_id'], $mib, $oid))) { dbInsert(array('device_id' => $device['device_id'], 'mib' => $mib, 'oid' => $oid, 'disabled' => '1'), 'devices_mibs'); } } print_debug(" WARNING, oids '" . implode("', '", array_keys($oids_def['oids'])) . "' for '{$mib}' disabled."); } } // Return obtained snmp data return $data; }
/** * Draw RRD file based on it's structure * @host * @plugin * @pinst * @type * @tinst * @opts * @return Commandline to call RRDGraph in order to generate the final graph */ function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null, $opts = array()) { global $config; $timespan_def = null; if (!isset($opts['timespan'])) { $timespan_def = reset($config['timespan']); } else { foreach ($config['timespan'] as &$ts) { if ($ts['name'] == $opts['timespan']) { $timespan_def = $ts; } } } if (!isset($opts['rrd_opts'])) { $opts['rrd_opts'] = array(); } if (isset($opts['logarithmic']) && $opts['logarithmic']) { array_unshift($opts['rrd_opts'], '-o'); } $rrdinfo = null; $rrdfile = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst); foreach ($config['datadirs'] as $datadir) { if (is_file($datadir . '/' . $rrdfile . '.rrd')) { $rrdinfo = rrdtool_file_info($datadir . '/' . $rrdfile . '.rrd'); if (isset($rrdinfo['RRA']) && is_array($rrdinfo['RRA'])) { break; } else { $rrdinfo = null; } } } if (is_null($rrdinfo)) { return false; } $graph = array(); $has_avg = false; $has_max = false; $has_min = false; reset($rrdinfo['RRA']); $l_max = 0; while (list($k, $v) = each($rrdinfo['RRA'])) { if ($v['cf'] == 'MAX') { $has_max = true; } else { if ($v['cf'] == 'AVERAGE') { $has_avg = true; } else { if ($v['cf'] == 'MIN') { $has_min = true; } } } } // Build legend. This may not work for all RRDs, i don't know :) if ($has_avg) { $graph[] = "COMMENT: Last"; } if ($has_min) { $graph[] = "COMMENT: Min"; } if ($has_max) { $graph[] = "COMMENT: Max"; } if ($has_avg) { $graph[] = "COMMENT: Avg\\n"; } reset($rrdinfo['DS']); while (list($k, $v) = each($rrdinfo['DS'])) { if (strlen($k) > $l_max) { $l_max = strlen($k); } if ($has_min) { $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, $rrdinfo['filename'], $k); } if ($has_avg) { $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, $rrdinfo['filename'], $k); } if ($has_max) { $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, $rrdinfo['filename'], $k); } } if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) { $n = 1; reset($rrdinfo['DS']); while (list($k, $v) = each($rrdinfo['DS'])) { $graph[] = sprintf('LINE:%s_%s', $k, $has_min ? 'min' : 'avg'); $graph[] = sprintf('CDEF:%s_var=%s_%s,%s_%s,-', $k, $k, $has_max ? 'max' : 'avg', $k, $has_min ? 'min' : 'avg'); $graph[] = sprintf('AREA:%s_var#%s::STACK', $k, rrd_get_color($n++, false)); } } reset($rrdinfo['DS']); $n = 1; while (list($k, $v) = each($rrdinfo['DS'])) { $graph[] = sprintf('LINE1:%s_avg#%s:%s ', $k, rrd_get_color($n++, true), $k . substr(' ', 0, $l_max - strlen($k))); if (isset($opts['tinylegend']) && $opts['tinylegend']) { continue; } if ($has_avg) { $graph[] = sprintf('GPRINT:%s_avg:AVERAGE:%%5.1lf%%s', $k, $has_max || $has_min || $has_avg ? ',' : "\\l"); } if ($has_min) { $graph[] = sprintf('GPRINT:%s_min:MIN:%%5.1lf%%s', $k, $has_max || $has_avg ? ',' : "\\l"); } if ($has_max) { $graph[] = sprintf('GPRINT:%s_max:MAX:%%5.1lf%%s', $k, $has_avg ? ',' : "\\l"); } if ($has_avg) { $graph[] = sprintf('GPRINT:%s_avg:LAST:%%5.1lf%%s\\l', $k); } } #$rrd_cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-t', $rrdfile); #$rrd_cmd = array(RRDTOOL, 'graph', '-', '-E', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height']); $rrd_cmd = array('-', '-E', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height']); if ($config['rrd_width'] <= "300") { $small_opts = array('--font', "LEGEND:7:mono", '--font', "AXIS:6:mono", "--font-render-mode", "normal"); $rrd_cmd = array_merge($rrd_cmd, $small_opts); } $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts_array'], $opts['rrd_opts'], $graph); $cmd = RRDTOOL; $cmd = ''; for ($i = 1; $i < count($rrd_cmd); $i++) { $cmd .= ' ' . escapeshellarg($rrd_cmd[$i]); } return $cmd; }