munin_api.module 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. // $Id$
  3. /**
  4. * @file
  5. * Munin API for Drupal.
  6. *
  7. * @author Frederic G. MARAND
  8. *
  9. * @copyright (c) 2011 Ouest Systèmes Informatiques
  10. *
  11. * Licensed under the General Public License version 2 or later.
  12. *
  13. * TODO Field name should be sanitized by "s/[^A-Za-z0-9_]/_/g"
  14. * @link http://munin-monitoring.org/wiki/notes_on_datasource_names @endlink
  15. */
  16. define('MUNIN_API_COUNTER', 'COUNTER');
  17. define('MUNIN_API_DERIVE', 'DERIVE');
  18. define('MUNIN_API_GAUGE', 'GAUGE');
  19. define('MUNIN_API_ABSOLUTE', 'ABSOLUTE'); // Counters reset upon reading (uncommon)
  20. define('MUNIN_API_DRAW_AREA', 'AREA');
  21. define('MUNIN_API_DRAW_LINE0', 'LINE0'); // Invisible line, but triggers graphs scaling
  22. define('MUNIN_API_DRAW_LINE1', 'LINE1'); // Default on Munin 2.0
  23. define('MUNIN_API_DRAW_LINE2', 'LINE2'); // Default on Munin < 2.0
  24. define('MUNIN_API_DRAW_LINE3', 'LINE3');
  25. define('MUNIN_API_DRAW_STACK', 'STACK');
  26. // Styles below are only supported on Munin >= 1.3.3
  27. define('MUNIN_API_DRAW_LINESTACK1', 'LINESTACK1');
  28. define('MUNIN_API_DRAW_LINESTACK2', 'LINESTACK2');
  29. define('MUNIN_API_DRAW_LINESTACK3', 'LINESTACK3');
  30. define('MUNIN_API_DRAW_AREASTACK', 'AREASTACK');
  31. /**
  32. * Finalize result pages for Munin interactions.
  33. *
  34. * - text content, not HTML
  35. * - non cacheable, even for anonymous users
  36. */
  37. function _munin_api_page_closure($ret) {
  38. drupal_set_header('Content-type: text/plain');
  39. $GLOBALS['conf']['cache'] = CACHE_DISABLED; // prevent page_set_cache() in drupal_page_footer().
  40. print $ret;
  41. drupal_page_footer();
  42. exit();
  43. }
  44. /**
  45. * Display and log an incorrect hook implementation.
  46. *
  47. * @param string $hook
  48. * @param string $module
  49. *
  50. * @return string
  51. */
  52. function _munin_report_hook_error($hook, $module) {
  53. $message = t('Incorrect implementation of hook_!hook() in module @module.');
  54. $params = array(
  55. '!hook' => $hook,
  56. '@module' => $module,
  57. );
  58. drupal_set_message(strtr($message, $params), 'error');
  59. watchdog('munin_api', $message, $params, WATCHDOG_ERROR);
  60. return '<p>'. l(t('Back'), 'admin/reports/munin_api/'. $module) .'</p>';
  61. }
  62. /**
  63. * Menu access callback for Munin config fetches.
  64. *
  65. * TODO: define rules, then code
  66. */
  67. function munin_api_access_config($module) {
  68. return TRUE; // For now, protect at the web server level
  69. }
  70. /**
  71. * Menu access callback for Munin data fetches.
  72. *
  73. * TODO: define rules, then code
  74. */
  75. function munin_api_access_fetch($module) {
  76. return TRUE; // For now, protect at the web server level
  77. }
  78. function munin_api_admin_settings($form_state) {
  79. $form = array();
  80. $form['munin_api_watchdog'] = array(
  81. '#title' => t('Munin API Watchdog'),
  82. '#description' => t('Enables Drupal-level monitoring of Munin. If enabled, it will regularly make sure the time interval between Munin probes is within the expected range and raise watchdog alerts accordingly. Init should only be used on pages with a low-occurrence rate, and will not work properly if caching mode is set to "aggressive" or "external" (Pressflow).'),
  83. '#type' => 'radios',
  84. '#options' => array(
  85. 'none' => t('Disabled'),
  86. 'cron' => t('On cron runs'),
  87. 'init' => t('On page init'),
  88. ),
  89. '#default_value' => variable_get('munin_api_watchdog', 'cron'),
  90. );
  91. $default_path = variable_get('munin_api_init_path', '/^$/');
  92. $form['munin_api_init_path'] = array(
  93. '#title' => t('Init path'),
  94. '#description' => t('For page init watchdog mode, specify the regular expression for the paths that should trigger a watchdog check'),
  95. '#type' => 'textfield',
  96. '#default_value' => $default_path,
  97. );
  98. drupal_add_js(drupal_get_path('module', 'munin_api') .'/munin_api.js');
  99. drupal_add_js(array(
  100. 'munin_api' => array(
  101. 'path' => $default_path,
  102. )), 'setting');
  103. $form = system_settings_form($form);
  104. return $form;
  105. }
  106. /**
  107. * Implements hook_cron().
  108. */
  109. function munin_api_cron() {
  110. if (variable_get('munin_api_watchdog', 'cron') == 'cron') {
  111. _munin_api_watchdog_munin();
  112. }
  113. }
  114. /**
  115. * Implements hook_init().
  116. *
  117. * Only trigger Munin API watchdog on selected pages.
  118. */
  119. function munin_api_init() {
  120. if (variable_get('munin_api_watchdog', 'cron') == 'init') {
  121. $path = $_GET['q'];
  122. $regex = variable_get('munin_api_init_path', '/^$/');
  123. $sts = preg_match($regex, $path);
  124. if ($sts) {
  125. _munin_api_watchdog_munin();
  126. }
  127. }
  128. }
  129. function munin_api_menu() {
  130. $items = array();
  131. $items['admin/settings/munin_api'] = array(
  132. 'title' => 'Munin',
  133. 'description' => 'Munin API settings',
  134. 'page callback' => 'drupal_get_form',
  135. 'page arguments' => array('munin_api_admin_settings'),
  136. 'access arguments' => array('administer site configuration'),
  137. );
  138. $items['admin/reports/munin_api'] = array(
  139. 'title' => 'Munin',
  140. 'description' => 'Reports about Munin data collectors and their probes',
  141. 'page callback' => 'munin_api_page_report_global',
  142. 'access arguments' => array('access site reports'),
  143. );
  144. $items['admin/reports/munin_api/list'] = array(
  145. 'type' => MENU_DEFAULT_LOCAL_TASK,
  146. 'title' => 'General',
  147. 'weight' => -1,
  148. );
  149. foreach (module_implements('munin_api_info') as $module_name) {
  150. $module = module_invoke($module_name, 'munin_api_info');
  151. $graphs = element_children($module);
  152. $items['admin/reports/munin_api/'. $module_name] = array(
  153. 'type' => MENU_LOCAL_TASK,
  154. 'title' => $module['#title'],
  155. 'description' => $module['#description'],
  156. 'page callback' => 'munin_api_page_report_instance',
  157. 'page arguments' => array($module_name, $module),
  158. 'access arguments' => array('access site reports'),
  159. );
  160. foreach ($graphs as $graph_name) {
  161. $items['munin_api/'. $graph_name] = array(
  162. 'type' => MENU_CALLBACK,
  163. 'page callback' => 'munin_api_page_fetch',
  164. 'page arguments' => array($module_name, $module, $graph_name),
  165. 'access callback' => 'munin_api_access_fetch',
  166. 'access arguments' => array($module_name, $graph_name),
  167. );
  168. $items['munin_api/'. $graph_name .'/config'] = array(
  169. 'type' => MENU_CALLBACK,
  170. 'page callback' => 'munin_api_page_config',
  171. 'page arguments' => array($module_name, $graph_name),
  172. 'access callback' => 'munin_api_access_config',
  173. 'access arguments' => array($module_name, $graph_name),
  174. );
  175. }
  176. }
  177. ksort($items);
  178. return $items;
  179. }
  180. /**
  181. * Implements hook_munin_api_info().
  182. *
  183. * @return
  184. * An array of Munin probes informations, index by probe name.
  185. */
  186. function munin_api_munin_api_info() {
  187. $int = array(
  188. '#graph_printf' => "'%d'",
  189. );
  190. $ret = array(
  191. '#title' => t('Munin'),
  192. '#description' => t('Munin monitoring'),
  193. 'munin_munin' => array(
  194. '#title' => t('Munin-node'),
  195. '#info' => t('Monitor the monitoring solution.'),
  196. '#graph_vlabel' => t('Seconds since last probe'),
  197. 'munin_seconds' => $int + array(
  198. '#label' => t('Seconds since last probe'),
  199. '#type' => MUNIN_API_GAUGE,
  200. '#info' => t('Seconds since last probe should hover around 300. 0 means no probe found.'),
  201. '#warning' => '290:310',
  202. '#critical' => '1:600',
  203. ),
  204. ),
  205. );
  206. return $ret;
  207. }
  208. /**
  209. * Implements hook_munin_api_fetch().
  210. */
  211. function munin_api_munin_api_fetch($graph_name, $log = TRUE) {
  212. switch ($graph_name) {
  213. case 'munin_munin':
  214. $request_time = time();
  215. $last = variable_get('munin_api_last', 0);
  216. $ret['munin_seconds'] = $last ? $request_time - $last : 0;
  217. if ($log) {
  218. variable_set('munin_api_last', $request_time);
  219. }
  220. break;
  221. }
  222. return $ret;
  223. }
  224. /**
  225. * Page callback for munin config fetches.
  226. */
  227. function munin_api_page_config($module_name, $graph_name) {
  228. $module_info = module_invoke($module_name, 'munin_api_info');
  229. if (!is_array($module_info)) {
  230. return _munin_report_hook_error('munin_api_info', $module);
  231. }
  232. $info = $module_info[$graph_name];
  233. $config = array(
  234. 'graph_title' => $info['#title'],
  235. 'graph_info' => $info['#info'],
  236. 'graph_category' => 'Drupal',
  237. );
  238. foreach (element_properties($info) as $property_name) {
  239. if (!in_array($property_name, array('#title', '#info'))) {
  240. $config[drupal_substr($property_name, 1)] = $info[$property_name];
  241. }
  242. }
  243. foreach (element_children($info) as $field_name) {
  244. foreach (element_properties($info[$field_name]) as $property_name) {
  245. $config[$field_name .'.'. drupal_substr($property_name, 1)] = $info[$field_name][$property_name];
  246. }
  247. }
  248. $ret = '';
  249. foreach ($config as $k => $v) {
  250. $ret .= $k .' '. $v . PHP_EOL;
  251. }
  252. _munin_api_page_closure($ret);
  253. }
  254. /**
  255. * Page callback for munin data fetches.
  256. */
  257. function munin_api_page_fetch($module_name, $module, $graph_name) {
  258. $data = module_invoke($module_name, 'munin_api_fetch', $graph_name);
  259. if (!is_array($data)) {
  260. return _munin_report_hook_error('munin_api_fetch', $module_name);
  261. }
  262. $ret = '';
  263. foreach ($data as $field => $value) {
  264. $ret .= $field .'.value '. $value . PHP_EOL;
  265. }
  266. _munin_api_page_closure($ret);
  267. }
  268. /**
  269. * Page callback for Munin global report.
  270. *
  271. * @return string
  272. */
  273. function munin_api_page_report_global() {
  274. $header = array(
  275. t('Module'),
  276. t('Graph'),
  277. t('Description'),
  278. t('Fields'),
  279. );
  280. $rows = array();
  281. foreach (module_implements('munin_api_info') as $module_name) {
  282. $info = module_invoke($module_name, 'munin_api_info');
  283. $rows[] = array(
  284. array(
  285. 'data' => l($info['#title'], 'admin/reports/munin_api/'. $module_name),
  286. 'colspan' => 2,
  287. ),
  288. array(
  289. 'colspan' => 2,
  290. 'data' => $info['#description'],
  291. ),
  292. );
  293. foreach (element_children($info) as $name) {
  294. $title = $info[$name]['#title'] ? $info[$name]['#title'] : $name;
  295. $rows[] = array(
  296. '&nbsp;',
  297. $title,
  298. isset($info[$name]['#info']) ? $info[$name]['#info'] : t('&lt;missing&gt;'),
  299. count(element_children($info[$name])),
  300. );
  301. }
  302. }
  303. $ret = theme('table', $header, $rows);
  304. return $ret;
  305. }
  306. /**
  307. * Page callback for Munin instance report.
  308. *
  309. * @return string
  310. */
  311. function munin_api_page_report_instance($module_name, $module_info) {
  312. if (!is_array($module_info)) {
  313. return _munin_report_hook_error('munin_api_info', $module_name);
  314. }
  315. $header = array(
  316. t('Name'),
  317. t('Title / Description'),
  318. t('Type'),
  319. t('Debug'),
  320. );
  321. $error = array('class' => 'error');
  322. $rows = array();
  323. foreach (element_children($module_info) as $graph_name) {
  324. $data = module_invoke($module_name, 'munin_api_fetch', $graph_name, FALSE);
  325. if (!is_array($data)) {
  326. return _munin_report_hook_error('munin_api_fetch', $module_name);
  327. }
  328. $rows[] = array(
  329. array(
  330. 'data' => $module_info[$graph_name]['#title'] ? $module_info[$graph_name]['#title'] : $graph_name,
  331. 'colspan' => 3,
  332. ),
  333. l(t('config'), 'munin_api/'. $graph_name .'/config'),
  334. );
  335. foreach (element_children($module_info[$graph_name]) as $field_name) {
  336. $rows[] = array(
  337. '&nbsp;',
  338. isset($module_info[$graph_name][$field_name]['#label']) ? $module_info[$graph_name][$field_name]['#label'] : t('&lt;missing&gt;'),
  339. $module_info[$graph_name][$field_name]['#type'],
  340. $data[$field_name],
  341. );
  342. unset($data[$field_name]);
  343. }
  344. foreach ($data as $field_name => $field_value) {
  345. $rows[] = array(
  346. '&nbsp;',
  347. $error + array('data' => $field_name),
  348. $error + array('data' => check_plain('<unconfigured>')),
  349. $error + array('data' => $field_value),
  350. );
  351. }
  352. }
  353. $ret = theme('table', $header, $rows);
  354. return $ret;
  355. }
  356. /**
  357. * Munin API watchdog implementation.
  358. *
  359. * @return void
  360. */
  361. function _munin_api_watchdog_munin() {
  362. $seconds = munin_api_munin_api_fetch('munin_munin', FALSE);
  363. $seconds = reset($seconds);
  364. $info = munin_api_munin_api_info();
  365. $info = $info['munin_munin']['munin_seconds'];
  366. $warning = explode(':', $info['#warning']);
  367. $critical = explode(':', $info['#critical']);
  368. if (/* $seconds < $critical[0] || */ $seconds > $critical[1]) {
  369. $level = WATCHDOG_CRITICAL;
  370. }
  371. elseif (/* $seconds < $warning[0] || */ $seconds > $warning[1]) {
  372. $level = WATCHDOG_WARNING;
  373. }
  374. else {
  375. $level = WATCHDOG_DEBUG;
  376. }
  377. if ($level < WATCHDOG_DEBUG) { // meaning MORE urgent
  378. watchdog('munin_api', 'Munin last probe came @last seconds ago.', array(
  379. '@last' => $seconds,
  380. ), $level);
  381. }
  382. }