PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/contrib/php-collection/functions.php

http://github.com/octo/collectd
PHP | 841 lines | 646 code | 83 blank | 112 comment | 253 complexity | 19ef043188dce37a70c5936c452a4c00 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php // vim:fenc=utf-8:filetype=php:ts=4
  2. /*
  3. * Copyright (C) 2009 Bruno Prémont <bonbons AT linux-vserver.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it under
  6. * the terms of the GNU General Public License as published by the Free Software
  7. * Foundation; only version 2 of the License is applicable.
  8. *
  9. * This program is distributed in the hope that it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. * details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with
  15. * this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. define('REGEXP_HOST', '/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/');
  19. define('REGEXP_PLUGIN', '/^[a-zA-Z0-9_.-]+$/');
  20. /**
  21. * Read input variable from GET, POST or COOKIE taking
  22. * care of magic quotes
  23. * @name Name of value to return
  24. * @array User-input array ($_GET, $_POST or $_COOKIE)
  25. * @default Default value
  26. * @return $default if name in unknown in $array, otherwise
  27. * input value with magic quotes stripped off
  28. */
  29. function read_var($name, &$array, $default = null) {
  30. if (isset($array[$name])) {
  31. if (is_array($array[$name])) {
  32. if (get_magic_quotes_gpc()) {
  33. $ret = array();
  34. while (list($k, $v) = each($array[$name]))
  35. $ret[stripslashes($k)] = stripslashes($v);
  36. return $ret;
  37. } else
  38. return $array[$name];
  39. } else if (is_string($array[$name]) && get_magic_quotes_gpc()) {
  40. return stripslashes($array[$name]);
  41. } else
  42. return $array[$name];
  43. } else
  44. return $default;
  45. }
  46. /**
  47. * Alphabetically compare host names, comparing label
  48. * from tld to node name
  49. */
  50. function collectd_compare_host($a, $b) {
  51. $ea = explode('.', $a);
  52. $eb = explode('.', $b);
  53. $i = count($ea) - 1;
  54. $j = count($eb) - 1;
  55. while ($i >= 0 && $j >= 0)
  56. if (($r = strcmp($ea[$i--], $eb[$j--])) != 0)
  57. return $r;
  58. return 0;
  59. }
  60. function collectd_walk(&$options) {
  61. global $config;
  62. foreach($config['datadirs'] as $datadir)
  63. if ($dh = @opendir($datadir)) {
  64. while (($hdent = readdir($dh)) !== false) {
  65. if ($hdent == '.' || $hdent == '..' || !is_dir($datadir.'/'.$hdent))
  66. continue;
  67. if (!preg_match(REGEXP_HOST, $hdent))
  68. continue;
  69. if (isset($options['cb_host']) && ($options['cb_host'] === false || !$options['cb_host']($options, $hdent)))
  70. continue;
  71. if ($dp = @opendir($datadir.'/'.$hdent)) {
  72. while (($pdent = readdir($dp)) !== false) {
  73. if ($pdent == '.' || $pdent == '..' || !is_dir($datadir.'/'.$hdent.'/'.$pdent))
  74. continue;
  75. if ($i = strpos($pdent, '-')) {
  76. $plugin = substr($pdent, 0, $i);
  77. $pinst = substr($pdent, $i+1);
  78. } else {
  79. $plugin = $pdent;
  80. $pinst = '';
  81. }
  82. if (isset($options['cb_plugin']) && ($options['cb_plugin'] === false || !$options['cb_plugin']($options, $hdent, $plugin)))
  83. continue;
  84. if (isset($options['cb_pinst']) && ($options['cb_pinst'] === false || !$options['cb_pinst']($options, $hdent, $plugin, $pinst)))
  85. continue;
  86. if ($dt = @opendir($datadir.'/'.$hdent.'/'.$pdent)) {
  87. while (($tdent = readdir($dt)) !== false) {
  88. if ($tdent == '.' || $tdent == '..' || !is_file($datadir.'/'.$hdent.'/'.$pdent.'/'.$tdent))
  89. continue;
  90. if (substr($tdent, strlen($tdent)-4) != '.rrd')
  91. continue;
  92. $tdent = substr($tdent, 0, strlen($tdent)-4);
  93. if ($i = strpos($tdent, '-')) {
  94. $type = substr($tdent, 0, $i);
  95. $tinst = substr($tdent, $i+1);
  96. } else {
  97. $type = $tdent;
  98. $tinst = '';
  99. }
  100. if (isset($options['cb_type']) && ($options['cb_type'] === false || !$options['cb_type']($options, $hdent, $plugin, $pinst, $type)))
  101. continue;
  102. if (isset($options['cb_tinst']) && ($options['cb_tinst'] === false || !$options['cb_tinst']($options, $hdent, $plugin, $pinst, $type, $tinst)))
  103. continue;
  104. }
  105. closedir($dt);
  106. }
  107. }
  108. closedir($dp);
  109. }
  110. }
  111. closedir($dh);
  112. } else
  113. error_log('Failed to open datadir: '.$datadir);
  114. return true;
  115. }
  116. function _collectd_list_cb_host(&$options, $host) {
  117. if ($options['cb_plugin'] === false) {
  118. $options['result'][] = $host;
  119. return false;
  120. } else if (isset($options['filter_host'])) {
  121. if ($options['filter_host'] == '@all') {
  122. return true; // We take anything
  123. } else if (substr($options['filter_host'], 0, 2) == '@.') {
  124. if ($host == substr($options['filter_host'], 2) || substr($host, 0, 1-strlen($options['filter_host'])) == substr($options['filter_host'], 1))
  125. return true; // Host part of domain
  126. else
  127. return false;
  128. } else if ($options['filter_host'] == $host) {
  129. return true;
  130. } else
  131. return false;
  132. } else
  133. return true;
  134. }
  135. function _collectd_list_cb_plugin(&$options, $host, $plugin) {
  136. if ($options['cb_pinst'] === false) {
  137. $options['result'][] = $plugin;
  138. return false;
  139. } else if (isset($options['filter_plugin'])) {
  140. if ($options['filter_plugin'] == '@all')
  141. return true;
  142. else if ($options['filter_plugin'] == $plugin)
  143. return true;
  144. else
  145. return false;
  146. } else
  147. return true;
  148. }
  149. function _collectd_list_cb_pinst(&$options, $host, $plugin, $pinst) {
  150. if ($options['cb_type'] === false) {
  151. $options['result'][] = $pinst;
  152. return false;
  153. } else if (isset($options['filter_pinst'])) {
  154. if ($options['filter_pinst'] == '@all')
  155. return true;
  156. else if (strncmp($options['filter_pinst'], '@merge_', 7) == 0)
  157. return true;
  158. else if ($options['filter_pinst'] == $pinst)
  159. return true;
  160. else
  161. return false;
  162. } else
  163. return true;
  164. }
  165. function _collectd_list_cb_type(&$options, $host, $plugin, $pinst, $type) {
  166. if ($options['cb_tinst'] === false) {
  167. $options['result'][] = $type;
  168. return false;
  169. } else if (isset($options['filter_type'])) {
  170. if ($options['filter_type'] == '@all')
  171. return true;
  172. else if ($options['filter_type'] == $type)
  173. return true;
  174. else
  175. return false;
  176. } else
  177. return true;
  178. }
  179. function _collectd_list_cb_tinst(&$options, $host, $plugin, $pinst, $type, $tinst) {
  180. $options['result'][] = $tinst;
  181. return false;
  182. }
  183. function _collectd_list_cb_graph(&$options, $host, $plugin, $pinst, $type, $tinst) {
  184. if (isset($options['filter_tinst'])) {
  185. if ($options['filter_tinst'] == '@all') {
  186. } else if ($options['filter_tinst'] == $tinst) {
  187. } else if (strncmp($options['filter_tinst'], '@merge', 6) == 0) {
  188. // Need to exclude @merge with non-existent meta graph
  189. } else
  190. return false;
  191. }
  192. if (isset($options['filter_pinst']) && strncmp($options['filter_pinst'], '@merge', 6) == 0)
  193. $pinst = $options['filter_pinst'];
  194. if (isset($options['filter_tinst']) && strncmp($options['filter_tinst'], '@merge', 6) == 0)
  195. $tinst = $options['filter_tinst'];
  196. $ident = collectd_identifier($host, $plugin, $pinst, $type, $tinst);
  197. if (!in_array($ident, $options['ridentifiers'])) {
  198. $options['ridentifiers'][] = $ident;
  199. $options['result'][] = array('host'=>$host, 'plugin'=>$plugin, 'pinst'=>$pinst, 'type'=>$type, 'tinst'=>$tinst);
  200. }
  201. }
  202. /**
  203. * Fetch list of hosts found in collectd's datadirs.
  204. * @return Sorted list of hosts (sorted by label from rigth to left)
  205. */
  206. function collectd_list_hosts() {
  207. $options = array(
  208. 'result' => array(),
  209. 'cb_host' => '_collectd_list_cb_host',
  210. 'cb_plugin' => false,
  211. 'cb_pinst' => false,
  212. 'cb_type' => false,
  213. 'cb_tinst' => false
  214. );
  215. collectd_walk($options);
  216. $hosts = array_unique($options['result']);
  217. usort($hosts, 'collectd_compare_host');
  218. return $hosts;
  219. }
  220. /**
  221. * Fetch list of plugins found in collectd's datadirs for given host.
  222. * @arg_host Name of host for which to return plugins
  223. * @return Sorted list of plugins (sorted alphabetically)
  224. */
  225. function collectd_list_plugins($arg_host, $arg_plugin = null) {
  226. $options = array(
  227. 'result' => array(),
  228. 'cb_host' => '_collectd_list_cb_host',
  229. 'cb_plugin' => '_collectd_list_cb_plugin',
  230. 'cb_pinst' => is_null($arg_plugin) ? false : '_collectd_list_cb_pinst',
  231. 'cb_type' => false,
  232. 'cb_tinst' => false,
  233. 'filter_host' => $arg_host,
  234. 'filter_plugin' => $arg_plugin
  235. );
  236. collectd_walk($options);
  237. $plugins = array_unique($options['result']);
  238. sort($plugins);
  239. return $plugins;
  240. }
  241. /**
  242. * Fetch list of types found in collectd's datadirs for given host+plugin+instance
  243. * @arg_host Name of host
  244. * @arg_plugin Name of plugin
  245. * @arg_pinst Plugin instance
  246. * @return Sorted list of types (sorted alphabetically)
  247. */
  248. function collectd_list_types($arg_host, $arg_plugin, $arg_pinst, $arg_type = null) {
  249. $options = array(
  250. 'result' => array(),
  251. 'cb_host' => '_collectd_list_cb_host',
  252. 'cb_plugin' => '_collectd_list_cb_plugin',
  253. 'cb_pinst' => '_collectd_list_cb_pinst',
  254. 'cb_type' => '_collectd_list_cb_type',
  255. 'cb_tinst' => is_null($arg_type) ? false : '_collectd_list_cb_tinst',
  256. 'filter_host' => $arg_host,
  257. 'filter_plugin' => $arg_plugin,
  258. 'filter_pinst' => $arg_pinst,
  259. 'filter_type' => $arg_type
  260. );
  261. collectd_walk($options);
  262. $types = array_unique($options['result']);
  263. sort($types);
  264. return $types;
  265. }
  266. function collectd_list_graphs($arg_host, $arg_plugin, $arg_pinst, $arg_type, $arg_tinst) {
  267. $options = array(
  268. 'result' => array(),
  269. 'ridentifiers' => array(),
  270. 'cb_host' => '_collectd_list_cb_host',
  271. 'cb_plugin' => '_collectd_list_cb_plugin',
  272. 'cb_pinst' => '_collectd_list_cb_pinst',
  273. 'cb_type' => '_collectd_list_cb_type',
  274. 'cb_tinst' => '_collectd_list_cb_graph',
  275. 'filter_host' => $arg_host,
  276. 'filter_plugin' => $arg_plugin,
  277. 'filter_pinst' => $arg_pinst,
  278. 'filter_type' => $arg_type,
  279. 'filter_tinst' => $arg_tinst == '@' ? '@merge' : $arg_tinst
  280. );
  281. collectd_walk($options);
  282. return $options['result'];
  283. }
  284. /**
  285. * Parse symlinks in order to get an identifier that collectd understands
  286. * (e.g. virtualisation is collected on host for individual VMs and can be
  287. * symlinked to the VM's hostname, support FLUSH for these by flushing
  288. * on the host-identifier instead of VM-identifier)
  289. * @host Host name
  290. * @plugin Plugin name
  291. * @pinst Plugin instance
  292. * @type Type name
  293. * @tinst Type instance
  294. * @return Identifier that collectd's FLUSH command understands
  295. */
  296. function collectd_identifier($host, $plugin, $pinst, $type, $tinst) {
  297. global $config;
  298. $rrd_realpath = null;
  299. $orig_identifier = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, strlen($pinst) ? '-' : '', $pinst, $type, strlen($tinst) ? '-' : '', $tinst);
  300. $identifier = null;
  301. foreach ($config['datadirs'] as $datadir)
  302. if (is_file($datadir.'/'.$orig_identifier.'.rrd')) {
  303. $rrd_realpath = realpath($datadir.'/'.$orig_identifier.'.rrd');
  304. break;
  305. }
  306. if ($rrd_realpath) {
  307. $identifier = basename($rrd_realpath);
  308. $identifier = substr($identifier, 0, strlen($identifier)-4);
  309. $rrd_realpath = dirname($rrd_realpath);
  310. $identifier = basename($rrd_realpath).'/'.$identifier;
  311. $rrd_realpath = dirname($rrd_realpath);
  312. $identifier = basename($rrd_realpath).'/'.$identifier;
  313. }
  314. if (is_null($identifier))
  315. return $orig_identifier;
  316. else
  317. return $identifier;
  318. }
  319. /**
  320. * Tell collectd that it should FLUSH all data it has regarding the
  321. * graph we are about to generate.
  322. * @host Host name
  323. * @plugin Plugin name
  324. * @pinst Plugin instance
  325. * @type Type name
  326. * @tinst Type instance
  327. */
  328. function collectd_flush($identifier) {
  329. global $config;
  330. if (!$config['collectd_sock'])
  331. return false;
  332. if (is_null($identifier) || (is_array($identifier) && count($identifier) == 0) || !(is_string($identifier) || is_array($identifier)))
  333. return false;
  334. $u_errno = 0;
  335. $u_errmsg = '';
  336. if ($socket = @fsockopen($config['collectd_sock'], 0, $u_errno, $u_errmsg)) {
  337. $cmd = 'FLUSH plugin=rrdtool';
  338. if (is_array($identifier)) {
  339. foreach ($identifier as $val)
  340. $cmd .= sprintf(' identifier="%s"', $val);
  341. } else
  342. $cmd .= sprintf(' identifier="%s"', $identifier);
  343. $cmd .= "\n";
  344. $r = fwrite($socket, $cmd, strlen($cmd));
  345. if ($r === false || $r != strlen($cmd))
  346. error_log(sprintf("graph.php: Failed to write whole command to unix-socket: %d out of %d written", $r === false ? -1 : $r, strlen($cmd)));
  347. $resp = fgets($socket);
  348. if ($resp === false)
  349. error_log(sprintf("graph.php: Failed to read response from collectd for command: %s", trim($cmd)));
  350. $n = (int)$resp;
  351. while ($n-- > 0)
  352. fgets($socket);
  353. fclose($socket);
  354. } else
  355. error_log(sprintf("graph.php: Failed to open unix-socket to collectd: %d: %s", $u_errno, $u_errmsg));
  356. }
  357. class CollectdColor {
  358. private $r = 0;
  359. private $g = 0;
  360. private $b = 0;
  361. function __construct($value = null) {
  362. if (is_null($value)) {
  363. } else if (is_array($value)) {
  364. if (isset($value['r']))
  365. $this->r = $value['r'] > 0 ? ($value['r'] > 1 ? 1 : $value['r']) : 0;
  366. if (isset($value['g']))
  367. $this->g = $value['g'] > 0 ? ($value['g'] > 1 ? 1 : $value['g']) : 0;
  368. if (isset($value['b']))
  369. $this->b = $value['b'] > 0 ? ($value['b'] > 1 ? 1 : $value['b']) : 0;
  370. } else if (is_string($value)) {
  371. $matches = array();
  372. if ($value == 'random') {
  373. $this->randomize();
  374. } else if (preg_match('/([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])([0-9A-Fa-f][0-9A-Fa-f])/', $value, $matches)) {
  375. $this->r = ('0x'.$matches[1]) / 255.0;
  376. $this->g = ('0x'.$matches[2]) / 255.0;
  377. $this->b = ('0x'.$matches[3]) / 255.0;
  378. }
  379. } else if (is_a($value, 'CollectdColor')) {
  380. $this->r = $value->r;
  381. $this->g = $value->g;
  382. $this->b = $value->b;
  383. }
  384. }
  385. function randomize() {
  386. $this->r = rand(0, 255) / 255.0;
  387. $this->g = rand(0, 255) / 255.0;
  388. $this->b = 0.0;
  389. $min = 0.0;
  390. $max = 1.0;
  391. if (($this->r + $this->g) < 1.0) {
  392. $min = 1.0 - ($this->r + $this->g);
  393. } else {
  394. $max = 2.0 - ($this->r + $this->g);
  395. }
  396. $this->b = $min + ((rand(0, 255)/255.0) * ($max - $min));
  397. }
  398. function fade($bkgnd = null, $alpha = 0.25) {
  399. if (is_null($bkgnd) || !is_a($bkgnd, 'CollectdColor')) {
  400. $bg_r = 1.0;
  401. $bg_g = 1.0;
  402. $bg_b = 1.0;
  403. } else {
  404. $bg_r = $bkgnd->r;
  405. $bg_g = $bkgnd->g;
  406. $bg_b = $bkgnd->b;
  407. }
  408. $this->r = $alpha * $this->r + ((1.0 - $alpha) * $bg_r);
  409. $this->g = $alpha * $this->g + ((1.0 - $alpha) * $bg_g);
  410. $this->b = $alpha * $this->b + ((1.0 - $alpha) * $bg_b);
  411. }
  412. function as_array() {
  413. return array('r'=>$this->r, 'g'=>$this->g, 'b'=>$this->b);
  414. }
  415. function as_string() {
  416. $r = (int)($this->r*255);
  417. $g = (int)($this->g*255);
  418. $b = (int)($this->b*255);
  419. return sprintf('%02x%02x%02x', $r > 255 ? 255 : $r, $g > 255 ? 255 : $g, $b > 255 ? 255 : $b);
  420. }
  421. }
  422. /**
  423. * Helper function to strip quotes from RRD output
  424. * @str RRD-Info generated string
  425. * @return String with one surrounding pair of quotes stripped
  426. */
  427. function rrd_strip_quotes($str) {
  428. if ($str[0] == '"' && $str[strlen($str)-1] == '"')
  429. return substr($str, 1, strlen($str)-2);
  430. else
  431. return $str;
  432. }
  433. function rrd_escape($str) {
  434. return str_replace(array('\\', ':'), array('\\\\', '\\:'), $str);
  435. }
  436. /**
  437. * Determine useful information about RRD file
  438. * @file Name of RRD file to analyse
  439. * @return Array describing the RRD file
  440. */
  441. function rrd_info($file) {
  442. $info = array('filename'=>$file);
  443. $rrd = popen(RRDTOOL.' info '.escapeshellarg($file), 'r');
  444. if ($rrd) {
  445. while (($s = fgets($rrd)) !== false) {
  446. $p = strpos($s, '=');
  447. if ($p === false)
  448. continue;
  449. $key = trim(substr($s, 0, $p));
  450. $value = trim(substr($s, $p+1));
  451. if (strncmp($key,'ds[', 3) == 0) {
  452. /* DS definition */
  453. $p = strpos($key, ']');
  454. $ds = substr($key, 3, $p-3);
  455. if (!isset($info['DS']))
  456. $info['DS'] = array();
  457. $ds_key = substr($key, $p+2);
  458. if (strpos($ds_key, '[') === false) {
  459. if (!isset($info['DS']["$ds"]))
  460. $info['DS']["$ds"] = array();
  461. $info['DS']["$ds"]["$ds_key"] = rrd_strip_quotes($value);
  462. }
  463. } else if (strncmp($key, 'rra[', 4) == 0) {
  464. /* RRD definition */
  465. $p = strpos($key, ']');
  466. $rra = substr($key, 4, $p-4);
  467. if (!isset($info['RRA']))
  468. $info['RRA'] = array();
  469. $rra_key = substr($key, $p+2);
  470. if (strpos($rra_key, '[') === false) {
  471. if (!isset($info['RRA']["$rra"]))
  472. $info['RRA']["$rra"] = array();
  473. $info['RRA']["$rra"]["$rra_key"] = rrd_strip_quotes($value);
  474. }
  475. } else if (strpos($key, '[') === false) {
  476. $info[$key] = rrd_strip_quotes($value);
  477. }
  478. }
  479. pclose($rrd);
  480. }
  481. return $info;
  482. }
  483. function rrd_get_color($code, $line = true) {
  484. global $config;
  485. $name = ($line ? 'f_' : 'h_').$code;
  486. if (!isset($config['rrd_colors'][$name])) {
  487. $c_f = new CollectdColor('random');
  488. $c_h = new CollectdColor($c_f);
  489. $c_h->fade();
  490. $config['rrd_colors']['f_'.$code] = $c_f->as_string();
  491. $config['rrd_colors']['h_'.$code] = $c_h->as_string();
  492. }
  493. return $config['rrd_colors'][$name];
  494. }
  495. /**
  496. * Draw RRD file based on its structure
  497. * @host
  498. * @plugin
  499. * @pinst
  500. * @type
  501. * @tinst
  502. * @opts
  503. * @return Commandline to call RRDGraph in order to generate the final graph
  504. */
  505. function collectd_draw_rrd($host, $plugin, $pinst = null, $type, $tinst = null, $opts = array()) {
  506. global $config;
  507. $timespan_def = null;
  508. if (!isset($opts['timespan']))
  509. $timespan_def = reset($config['timespan']);
  510. else foreach ($config['timespan'] as &$ts)
  511. if ($ts['name'] == $opts['timespan'])
  512. $timespan_def = $ts;
  513. if (!isset($opts['rrd_opts']))
  514. $opts['rrd_opts'] = array();
  515. if (isset($opts['logarithmic']) && $opts['logarithmic'])
  516. array_unshift($opts['rrd_opts'], '-o');
  517. $rrdinfo = null;
  518. $rrdfile = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
  519. foreach ($config['datadirs'] as $datadir)
  520. if (is_file($datadir.'/'.$rrdfile.'.rrd')) {
  521. $rrdinfo = rrd_info($datadir.'/'.$rrdfile.'.rrd');
  522. if (isset($rrdinfo['RRA']) && is_array($rrdinfo['RRA']))
  523. break;
  524. else
  525. $rrdinfo = null;
  526. }
  527. if (is_null($rrdinfo))
  528. return false;
  529. $graph = array();
  530. $has_avg = false;
  531. $has_max = false;
  532. $has_min = false;
  533. reset($rrdinfo['RRA']);
  534. $l_max = 0;
  535. while (list($k, $v) = each($rrdinfo['RRA'])) {
  536. if ($v['cf'] == 'MAX')
  537. $has_max = true;
  538. else if ($v['cf'] == 'AVERAGE')
  539. $has_avg = true;
  540. else if ($v['cf'] == 'MIN')
  541. $has_min = true;
  542. }
  543. reset($rrdinfo['DS']);
  544. while (list($k, $v) = each($rrdinfo['DS'])) {
  545. if (strlen($k) > $l_max)
  546. $l_max = strlen($k);
  547. if ($has_min)
  548. $graph[] = sprintf('DEF:%s_min=%s:%s:MIN', $k, rrd_escape($rrdinfo['filename']), $k);
  549. if ($has_avg)
  550. $graph[] = sprintf('DEF:%s_avg=%s:%s:AVERAGE', $k, rrd_escape($rrdinfo['filename']), $k);
  551. if ($has_max)
  552. $graph[] = sprintf('DEF:%s_max=%s:%s:MAX', $k, rrd_escape($rrdinfo['filename']), $k);
  553. }
  554. if ($has_min && $has_max || $has_min && $has_avg || $has_avg && $has_max) {
  555. $n = 1;
  556. reset($rrdinfo['DS']);
  557. while (list($k, $v) = each($rrdinfo['DS'])) {
  558. $graph[] = sprintf('LINE:%s_%s', $k, $has_min ? 'min' : 'avg');
  559. $graph[] = sprintf('CDEF:%s_var=%s_%s,%s_%s,-', $k, $k, $has_max ? 'max' : 'avg', $k, $has_min ? 'min' : 'avg');
  560. $graph[] = sprintf('AREA:%s_var#%s::STACK', $k, rrd_get_color($n++, false));
  561. }
  562. }
  563. reset($rrdinfo['DS']);
  564. $n = 1;
  565. while (list($k, $v) = each($rrdinfo['DS'])) {
  566. $graph[] = sprintf('LINE1:%s_avg#%s:%s ', $k, rrd_get_color($n++, true), $k.substr(' ', 0, $l_max-strlen($k)));
  567. if (isset($opts['tinylegend']) && $opts['tinylegend'])
  568. continue;
  569. if ($has_avg)
  570. $graph[] = sprintf('GPRINT:%s_avg:AVERAGE:%%5.1lf%%s Avg%s', $k, $has_max || $has_min || $has_avg ? ',' : "\\l");
  571. if ($has_min)
  572. $graph[] = sprintf('GPRINT:%s_min:MIN:%%5.1lf%%s Max%s', $k, $has_max || $has_avg ? ',' : "\\l");
  573. if ($has_max)
  574. $graph[] = sprintf('GPRINT:%s_max:MAX:%%5.1lf%%s Max%s', $k, $has_avg ? ',' : "\\l");
  575. if ($has_avg)
  576. $graph[] = sprintf('GPRINT:%s_avg:LAST:%%5.1lf%%s Last\\l', $k);
  577. }
  578. $rrd_cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrdfile);
  579. $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts'], $opts['rrd_opts'], $graph);
  580. $cmd = RRDTOOL;
  581. for ($i = 1; $i < count($rrd_cmd); $i++)
  582. $cmd .= ' '.escapeshellarg($rrd_cmd[$i]);
  583. return $cmd;
  584. }
  585. /**
  586. * Draw RRD file based on its structure
  587. * @timespan
  588. * @host
  589. * @plugin
  590. * @pinst
  591. * @type
  592. * @tinst
  593. * @opts
  594. * @return Commandline to call RRDGraph in order to generate the final graph
  595. */
  596. function collectd_draw_generic($timespan, $host, $plugin, $pinst = null, $type, $tinst = null) {
  597. global $config, $GraphDefs;
  598. $timespan_def = null;
  599. foreach ($config['timespan'] as &$ts)
  600. if ($ts['name'] == $timespan)
  601. $timespan_def = $ts;
  602. if (is_null($timespan_def))
  603. $timespan_def = reset($config['timespan']);
  604. if (!isset($GraphDefs[$type]))
  605. return false;
  606. $rrd_file = sprintf('%s/%s%s%s/%s%s%s', $host, $plugin, is_null($pinst) ? '' : '-', $pinst, $type, is_null($tinst) ? '' : '-', $tinst);
  607. $rrd_cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $rrd_file);
  608. $rrd_cmd = array_merge($rrd_cmd, $config['rrd_opts']);
  609. $rrd_args = $GraphDefs[$type];
  610. foreach ($config['datadirs'] as $datadir) {
  611. $file = $datadir.'/'.$rrd_file.'.rrd';
  612. if (!is_file($file))
  613. continue;
  614. $file = str_replace(":", "\\:", $file);
  615. $rrd_args = str_replace('{file}', rrd_escape($file), $rrd_args);
  616. $rrdgraph = array_merge($rrd_cmd, $rrd_args);
  617. $cmd = RRDTOOL;
  618. for ($i = 1; $i < count($rrdgraph); $i++)
  619. $cmd .= ' '.escapeshellarg($rrdgraph[$i]);
  620. return $cmd;
  621. }
  622. return false;
  623. }
  624. /**
  625. * Draw stack-graph for set of RRD files
  626. * @opts Graph options like colors
  627. * @sources List of array(name, file, ds)
  628. * @return Commandline to call RRDGraph in order to generate the final graph
  629. */
  630. function collectd_draw_meta_stack(&$opts, &$sources) {
  631. global $config;
  632. $timespan_def = null;
  633. if (!isset($opts['timespan']))
  634. $timespan_def = reset($config['timespan']);
  635. else foreach ($config['timespan'] as &$ts)
  636. if ($ts['name'] == $opts['timespan'])
  637. $timespan_def = $ts;
  638. if (!isset($opts['title']))
  639. $opts['title'] = 'Unknown title';
  640. if (!isset($opts['rrd_opts']))
  641. $opts['rrd_opts'] = array();
  642. if (!isset($opts['colors']))
  643. $opts['colors'] = array();
  644. if (isset($opts['logarithmic']) && $opts['logarithmic'])
  645. array_unshift($opts['rrd_opts'], '-o');
  646. $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
  647. $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
  648. $max_inst_name = 0;
  649. foreach($sources as &$inst_data) {
  650. $inst_name = str_replace('!', '_', $inst_data['name']);
  651. $file = $inst_data['file'];
  652. $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
  653. if (strlen($inst_name) > $max_inst_name)
  654. $max_inst_name = strlen($inst_name);
  655. if (!is_file($file))
  656. continue;
  657. $cmd[] = 'DEF:'.$inst_name.'_min='.rrd_escape($file).':'.$ds.':MIN';
  658. $cmd[] = 'DEF:'.$inst_name.'_avg='.rrd_escape($file).':'.$ds.':AVERAGE';
  659. $cmd[] = 'DEF:'.$inst_name.'_max='.rrd_escape($file).':'.$ds.':MAX';
  660. $cmd[] = 'CDEF:'.$inst_name.'_nnl='.$inst_name.'_avg,UN,0,'.$inst_name.'_avg,IF';
  661. }
  662. $inst_data = end($sources);
  663. $inst_name = $inst_data['name'];
  664. $cmd[] = 'CDEF:'.$inst_name.'_stk='.$inst_name.'_nnl';
  665. $inst_data1 = end($sources);
  666. while (($inst_data0 = prev($sources)) !== false) {
  667. $inst_name0 = str_replace('!', '_', $inst_data0['name']);
  668. $inst_name1 = str_replace('!', '_', $inst_data1['name']);
  669. $cmd[] = 'CDEF:'.$inst_name0.'_stk='.$inst_name0.'_nnl,'.$inst_name1.'_stk,+';
  670. $inst_data1 = $inst_data0;
  671. }
  672. foreach($sources as &$inst_data) {
  673. $inst_name = str_replace('!', '_', $inst_data['name']);
  674. $legend = sprintf('%s', $inst_data['name']);
  675. while (strlen($legend) < $max_inst_name)
  676. $legend .= ' ';
  677. $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
  678. if (isset($opts['colors'][$inst_name]))
  679. $line_color = new CollectdColor($opts['colors'][$inst_name]);
  680. else
  681. $line_color = new CollectdColor('random');
  682. $area_color = new CollectdColor($line_color);
  683. $area_color->fade();
  684. $cmd[] = 'AREA:'.$inst_name.'_stk#'.$area_color->as_string();
  685. $cmd[] = 'LINE1:'.$inst_name.'_stk#'.$line_color->as_string().':'.$legend;
  686. if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
  687. $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
  688. $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
  689. $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
  690. $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
  691. }
  692. }
  693. $rrdcmd = RRDTOOL;
  694. for ($i = 1; $i < count($cmd); $i++)
  695. $rrdcmd .= ' '.escapeshellarg($cmd[$i]);
  696. return $rrdcmd;
  697. }
  698. /**
  699. * Draw stack-graph for set of RRD files
  700. * @opts Graph options like colors
  701. * @sources List of array(name, file, ds)
  702. * @return Commandline to call RRDGraph in order to generate the final graph
  703. */
  704. function collectd_draw_meta_line(&$opts, &$sources) {
  705. global $config;
  706. $timespan_def = null;
  707. if (!isset($opts['timespan']))
  708. $timespan_def = reset($config['timespan']);
  709. else foreach ($config['timespan'] as &$ts)
  710. if ($ts['name'] == $opts['timespan'])
  711. $timespan_def = $ts;
  712. if (!isset($opts['title']))
  713. $opts['title'] = 'Unknown title';
  714. if (!isset($opts['rrd_opts']))
  715. $opts['rrd_opts'] = array();
  716. if (!isset($opts['colors']))
  717. $opts['colors'] = array();
  718. if (isset($opts['logarithmic']) && $opts['logarithmic'])
  719. array_unshift($opts['rrd_opts'], '-o');
  720. $cmd = array(RRDTOOL, 'graph', '-', '-a', 'PNG', '-w', $config['rrd_width'], '-h', $config['rrd_height'], '-s', -1*$timespan_def['seconds'], '-t', $opts['title']);
  721. $cmd = array_merge($cmd, $config['rrd_opts'], $opts['rrd_opts']);
  722. $max_inst_name = 0;
  723. foreach ($sources as &$inst_data) {
  724. $inst_name = str_replace('!', '_', $inst_data['name']);
  725. $file = $inst_data['file'];
  726. $ds = isset($inst_data['ds']) ? $inst_data['ds'] : 'value';
  727. if (strlen($inst_name) > $max_inst_name)
  728. $max_inst_name = strlen($inst_name);
  729. if (!is_file($file))
  730. continue;
  731. $cmd[] = 'DEF:'.$inst_name.'_min='.rrd_escape($file).':'.$ds.':MIN';
  732. $cmd[] = 'DEF:'.$inst_name.'_avg='.rrd_escape($file).':'.$ds.':AVERAGE';
  733. $cmd[] = 'DEF:'.$inst_name.'_max='.rrd_escape($file).':'.$ds.':MAX';
  734. }
  735. foreach ($sources as &$inst_data) {
  736. $inst_name = str_replace('!', '_', $inst_data['name']);
  737. $legend = sprintf('%s', $inst_name);
  738. while (strlen($legend) < $max_inst_name)
  739. $legend .= ' ';
  740. $number_format = isset($opts['number_format']) ? $opts['number_format'] : '%6.1lf';
  741. if (isset($opts['colors'][$inst_name]))
  742. $line_color = new CollectdColor($opts['colors'][$inst_name]);
  743. else
  744. $line_color = new CollectdColor('random');
  745. $cmd[] = 'LINE1:'.$inst_name.'_avg#'.$line_color->as_string().':'.$legend;
  746. if (!(isset($opts['tinylegend']) && $opts['tinylegend'])) {
  747. $cmd[] = 'GPRINT:'.$inst_name.'_min:MIN:'.$number_format.' Min,';
  748. $cmd[] = 'GPRINT:'.$inst_name.'_avg:AVERAGE:'.$number_format.' Avg,';
  749. $cmd[] = 'GPRINT:'.$inst_name.'_max:MAX:'.$number_format.' Max,';
  750. $cmd[] = 'GPRINT:'.$inst_name.'_avg:LAST:'.$number_format.' Last\\l';
  751. }
  752. }
  753. $rrdcmd = RRDTOOL;
  754. for ($i = 1; $i < count($cmd); $i++)
  755. $rrdcmd .= ' '.escapeshellarg($cmd[$i]);
  756. return $rrdcmd;
  757. }
  758. ?>