View source
<?php
define('PROD_MONITOR_REQUIREMENT_INFO', -1);
define('PROD_MONITOR_REQUIREMENT_OK', 0);
define('PROD_MONITOR_REQUIREMENT_WARNING', 1);
define('PROD_MONITOR_REQUIREMENT_ERROR', 2);
if (!module_exists('update')) {
define('UPDATE_DEFAULT_URL', 'http://updates.drupal.org/release-history');
define('UPDATE_NOT_SECURE', 1);
define('UPDATE_REVOKED', 2);
define('UPDATE_NOT_SUPPORTED', 3);
define('UPDATE_NOT_CURRENT', 4);
define('UPDATE_CURRENT', 5);
define('UPDATE_NOT_CHECKED', -1);
define('UPDATE_UNKNOWN', -2);
define('UPDATE_NOT_FETCHED', -3);
define('UPDATE_MAX_FETCH_ATTEMPTS', 2);
}
function prod_monitor_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/help#prod_monitor':
$output .= '<p>' . t('Production monitor is a module that can connect to the <strong>Production check</strong> module using <strong>XMLRPC</strong> and an <strong>API key</strong>. It will retrieve all specified data from the remote site to create a satus page and monitoring facility in a central place.') . '<br />';
$output .= t('You can add multiple sites and configure per site what data you wish (not) to monitor, allowing you to setup a central Drupal site that will monitor all of your sites that have the <em>Production check</em> module with <em>XMLRPC</em> enabled.') . '<br />';
$output .= t('The <strong>data retrieval</strong> mechanism can be called <strong>manually</strong> and is integrated with the <strong>cron</strong>, so you get a fresh update of data each cron run.') . '</p>';
break;
case 'admin/reports/prod-monitor':
$output .= '<p><strong>' . t('Site overview table') . '</strong><br />';
$output .= t('The overview table gives you an overview of what sites you have added together with their status. The status will be the highest error detected in the retrieved data set.') . '<br />';
$output .= t('The per site functions <strong>View</strong>, <strong>Edit</strong>, <strong>Fetch data</strong>, <strong>Flush</strong> and <strong>Delete</strong> should be self explanatory.') . '</p>';
case 'admin/reports/prod-monitor/site/%/edit':
$output .= '<p><strong>' . t('Website URL & API key') . '</strong><br />';
$output .= t("To add a site, enter it's <strong>full url</strong>, including the protocol, but omitting the <em>xmlrpc.php</em> part and the <strong>API key</strong> that you have configured for it using the <strong>Production check</strong> module. Now click the <strong>Get settings</strong> button.") . '<br />';
$output .= t('All of the checks that the <em>Production check</em> module can perform are fetched from the remote site and presented as an array of checkboxes. Finally you can configure what exactly you wish to monitor for this site, then hit the <strong>Add site</strong> button.') . '<br />';
$output .= t('Each time you edit a site, the settings are fetched from the remote server so that any new checks that might have been added to the <em>Production check</em> module there are always up to date in the monitoring section.') . '<br />';
$output .= t('<strong>Fetch data immediately</strong> does exactly what it says and fetches all the configured data from the remote site and will direct you to the report page.') . '</p>';
break;
case 'admin/reports/prod-monitor/module-lookup':
$output .= '<p><strong>' . t('Module name') . '</strong><br />';
$output .= t("Enter (part of) the module's machine name to see what sites are using the module.") . '<br />';
break;
case 'admin/reports/prod-monitor/site/%':
case 'admin/reports/prod-monitor/site/%/view':
$output .= '<p>' . t("This is an overview of all checks performed by the <em>Production check</em> module and their status <strong>on the remote site</strong>. You can click the links inside the report to jump to the module's settings page, or to go to the project page of a module, in case you need to download it for installation.") . '</p>';
break;
}
return $output;
}
function prod_monitor_perm() {
return array(
'access production monitor',
);
}
function prod_monitor_menu() {
$items = array();
$items['admin/reports/prod-monitor'] = array(
'title' => 'Production monitor',
'description' => 'Setup the Production monitor.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'prod_monitor_overview_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'includes/prod_monitor.admin.inc',
);
$items['admin/reports/prod-monitor/overview'] = array(
'title' => 'Overview',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/reports/prod-monitor/module-lookup'] = array(
'title' => 'Module lookup',
'description' => 'Searches monitored applications for module versions.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'prod_monitor_module_lookup_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 1,
);
$items['admin/reports/prod-monitor/site/%'] = array(
'title' => 'View',
'description' => 'View the Production monitor report page.',
'page callback' => 'prod_monitor_status',
'page arguments' => array(
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_NORMAL_ITEM,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 10,
);
$items['admin/reports/prod-monitor/site/%/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/reports/prod-monitor/site/%/view/status'] = array(
'title' => 'Status',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => 0,
);
$items['admin/reports/prod-monitor/site/%prod_monitor_perf/view/performance'] = array(
'title' => 'Performance',
'description' => t('View the performance data for this site.'),
'page callback' => 'prod_monitor_performance',
'page arguments' => array(
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 12,
);
$items['admin/reports/prod-monitor/site/%prod_monitor/view/updates'] = array(
'title' => 'Updates',
'description' => 'View the Production monitor modules update page.',
'page callback' => 'prod_monitor_updates',
'page arguments' => array(
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 15,
);
$items['admin/reports/prod-monitor/site/%/update-check'] = array(
'title' => 'Updates',
'description' => 'Refresh Production monitor modules update page.',
'page callback' => 'prod_monitor_updates_check',
'page arguments' => array(
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_CALLBACK,
'file' => 'includes/prod_monitor.admin.inc',
);
$items['admin/reports/prod-monitor/site/%/edit'] = array(
'title' => 'Edit',
'description' => 'Edit website',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'prod_monitor_overview_form',
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 20,
);
$items['admin/reports/prod-monitor/site/%/flush'] = array(
'title' => 'Flush',
'description' => "Flush website's data.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'prod_monitor_flush_form',
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 30,
);
$items['admin/reports/prod-monitor/site/%/fetch'] = array(
'title' => 'Fetch',
'description' => "Fetch website's data.",
'page callback' => 'prod_monitor_fetch_data',
'page arguments' => array(
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 35,
);
$items['admin/reports/prod-monitor/site/%/delete'] = array(
'title' => 'Delete',
'description' => 'Delete website',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'prod_monitor_delete_form',
4,
),
'access callback' => 'user_access',
'access arguments' => array(
'access production monitor',
),
'type' => MENU_LOCAL_TASK,
'file' => 'includes/prod_monitor.admin.inc',
'weight' => 40,
);
return $items;
}
function prod_monitor_load($id) {
$modules = _prod_monitor_get_site_modules($id);
if (!isset($modules['projects']) || empty($modules['projects'])) {
$modules = FALSE;
}
return $modules;
}
function prod_monitor_perf_load($id) {
$data = array();
$data['id'] = $id;
$data['data'] = _prod_monitor_get_performance_data($id);
if (empty($data['data'])) {
return FALSE;
}
return $data;
}
function prod_monitor_theme() {
return array(
'prod_monitor_update_report' => array(
'arguments' => array(
'id' => NULL,
'last' => NULL,
'data' => NULL,
),
'file' => 'includes/prod_monitor.theme.inc',
),
'prod_monitor_update_version' => array(
'arguments' => array(
'version' => NULL,
'tag' => NULL,
'class' => NULL,
),
'file' => 'includes/prod_monitor.theme.inc',
),
'prod_monitor_status_report' => array(
'arguments' => array(
'requirements' => NULL,
),
'file' => 'includes/prod_monitor.theme.inc',
),
'prod_monitor_performance' => array(
'arguments' => array(
'data' => NULL,
),
'template' => 'templates/prod-monitor-performance',
),
);
}
function template_preprocess_prod_monitor_performance(&$vars) {
$vars['graphs'] = array();
foreach ($vars['data'] as $module => $data_set) {
$count = $i = 0;
foreach ($data_set as $time => $params) {
$vars['graphs'][$module]['title'] = $params['title'];
if (is_array($params['data'])) {
$count = count($params['data']);
foreach ($params['data'] as $title => $row) {
if (!isset($row[1])) {
$row[1] = '';
}
if ($i < $count) {
$vars['graphs'][$module][$row[1]]['cols'][] = $title;
$i++;
}
$vars['graphs'][$module][$row[1]]['rows'][$time][] = (int) $row[0];
}
}
else {
$vars['graphs'][$module]['message'] = $params['data'];
}
}
if (count($vars['graphs'][$module]) > 2) {
unset($vars['graphs'][$module]['message']);
}
}
}
function prod_monitor_cron() {
if (variable_get('prod_monitor_cron_running', FALSE)) {
watchdog('prod_monitor', 'Last cron run was not properly terminated!', array(), WATCHDOG_ERROR);
}
$sites = _prod_monitor_get_sites(variable_get('prod_monitor_cron_start_at', 0));
variable_set('prod_monitor_cron_running', TRUE);
$cron_start = time();
$time_limit = 180;
$elapsed = $process = 0;
foreach ($sites as $id => $site_info) {
$elapsed = time() - $cron_start;
if ($elapsed < $time_limit) {
_prod_monitor_retrieve_data($id, $site_info);
_prod_monitor_db_connect_check($id, $site_info);
$process++;
}
else {
variable_set('prod_monitor_cron_start_at', $id);
break;
}
}
if ($process >= count($sites)) {
variable_set('prod_monitor_cron_start_at', 0);
}
watchdog('prod_monitor', '!count sites updated successfully in !time seconds.', array(
'!count' => $process,
'!time' => $elapsed,
), WATCHDOG_NOTICE);
variable_del('prod_monitor_cron_running');
}
function _prod_monitor_retrieve_functions($url, $api_key, $msg = TRUE) {
$url = rtrim($url, '/') . '/xmlrpc.php';
$functions = xmlrpc($url, 'prod_check.get_settings', $api_key);
if (!$functions) {
drupal_set_message(t('Failed to retrieve settings form !link, please verify the given URL and try again!', array(
'!link' => l('remote site', $url, array(
'attributes' => array(
'title' => t('remote site'),
),
)),
)), 'error');
}
elseif ($msg) {
drupal_set_message(t('Settings form updated, please adjust your settings.'));
}
return $functions;
}
function _prod_monitor_retrieve_data($id, $site_info, $msg = FALSE) {
$url = rtrim($site_info['url'], '/') . '/xmlrpc.php';
$api_key = $site_info['settings']['api_key'];
$checks = $site_info['settings']['checks'];
$data = xmlrpc($url, 'prod_check.get_data', $api_key, $checks);
if (!$data) {
watchdog('prod_monitor', 'Could not retrieve settings data for %link', array(
'%link' => $site_info['url'],
), WATCHDOG_ERROR);
if ($msg) {
drupal_set_message(t('Data for %link not successfully fetched. Errors have been !link.', array(
'%link' => $site_info['url'],
'!link' => l(t('logged'), 'admin/reports/dblog'),
)), 'error');
}
}
else {
$module_list = array();
if (isset($data['prod_mon']['prod_check_module_list'])) {
$module_list = $data['prod_mon']['prod_check_module_list'];
unset($data['prod_mon']['prod_check_module_list']);
}
$perf_data = array();
if (isset($data['perf_data'])) {
$perf_data = $data['perf_data'];
unset($data['perf_data']);
}
$site = new stdClass();
$site->id = $id;
$site->data = serialize($data);
$site->lastupdate = time();
$result = drupal_write_record('prod_monitor_sites', $site, array(
'id',
));
if (!$result) {
watchdog('prod_monitor', 'Could not update data for %link', array(
'%link' => $site_info['url'],
), WATCHDOG_ERROR);
if ($msg) {
drupal_set_message(t('Data for %link not successfully saved. Errors have been !link.', array(
'%link' => $site_info['url'],
'!link' => l(t('logged'), 'admin/reports/dblog'),
)), 'error');
}
}
else {
if ($msg) {
drupal_set_message(t('Data for %link successfully updated.', array(
'%link' => $site_info['url'],
)));
}
if (!empty($module_list)) {
$modules = _prod_monitor_get_site_modules($id, TRUE);
$update = array();
if (!empty($modules)) {
$update = array(
'id',
);
}
$modules = new stdClass();
$modules->id = $id;
$modules->projects = serialize($module_list['projects']);
$modules->sitekey = $module_list['site_key'];
$modules->lastfetch = $module_list['last_update'];
$result = drupal_write_record('prod_monitor_site_modules', $modules, $update);
if (!$result) {
watchdog('prod_monitor', 'Could not update module data for %link', array(
'%link' => $site_info['url'],
), WATCHDOG_ERROR);
if ($msg) {
drupal_set_message(t('Module data for %link not successfully saved. Errors have been !link.', array(
'%link' => $site_info['url'],
'!link' => l(t('logged'), 'admin/reports/dblog'),
)), 'error');
}
}
elseif ($msg) {
drupal_set_message(t('Module data for %link successfully updated.', array(
'%link' => $site_info['url'],
)));
}
}
if (!empty($perf_data)) {
foreach ($perf_data as $module => $module_data) {
$performance = new stdClass();
$performance->id = $id;
$performance->module = $module;
$performance->data = serialize($module_data);
$performance->fetched = time();
$result = drupal_write_record('prod_monitor_site_performance', $performance);
if (!$result) {
watchdog('prod_monitor', 'Could not update performance data for %link', array(
'%link' => $site_info['url'],
), WATCHDOG_ERROR);
if ($msg) {
drupal_set_message(t('Performance data for %link not successfully saved. Errors have been !link.', array(
'%link' => $site_info['url'],
'!link' => l(t('logged'), 'admin/reports/dblog'),
)), 'error');
}
}
elseif ($msg) {
drupal_set_message(t('Performance data for %link successfully updated.', array(
'%link' => $site_info['url'],
)));
}
}
}
}
}
}
function _prod_monitor_db_connect_check($id, $site_info) {
if (empty($site_info['settings']['dbconnect_path'])) {
return;
}
$dbconnect_path = rtrim($site_info['url'], '/') . '/' . $site_info['settings']['dbconnect_path'];
$response = drupal_http_request($dbconnect_path);
if ($response->code !== '200' && $response->data !== 'OK') {
$site_info['status'] = PROD_MONITOR_REQUIREMENT_ERROR;
$response->data = 'NOK';
}
$site_data = _prod_monitor_get_site($id, 'data');
$site_data['data']['prod_mon']['prod_check_dbconnect'] = $response->code . ' ' . $response->data;
$site = new stdClass();
$site->id = $id;
if (isset($site_info['status'])) {
$site->status = $site_info['status'];
}
$site->data = serialize($site_data['data']);
$site->lastupdate = REQUEST_TIME;
$result = drupal_write_record('prod_monitor_sites', $site, array(
'id',
));
}
function _prod_monitor_get_sites($start_id = FALSE) {
$where = 'ORDER BY added DESC';
if ($start_id) {
$where = "WHERE id >= {$start_id} ORDER BY id ASC";
}
$sql = "SELECT * FROM {prod_monitor_sites} {$where}";
$result = db_query($sql);
$sites = array();
while ($row = db_fetch_array($result)) {
$id = $row['id'];
$row['data'] = unserialize($row['data']);
$status = -1;
if (!empty($row['data'])) {
foreach ($row['data'] as $set => $checks) {
foreach ($checks as $check => $results) {
$status = isset($results['severity']) && $results['severity'] > $status ? $results['severity'] : $status;
}
}
$data_status = TRUE;
}
else {
$data_status = FALSE;
}
switch ($status) {
case 0:
$status = 'ok';
break;
case 1:
$status = 'warning';
break;
case 2:
$status = 'error';
break;
default:
$status = '';
}
$sites[$id]['url'] = $row['url'];
$sites[$id]['settings'] = unserialize($row['settings']);
$sites[$id]['data'] = $data_status;
$sites[$id]['status'] = $status;
$sites[$id]['added'] = format_date($row['added'], 'small');
$sites[$id]['lastupdate'] = empty($row['lastupdate']) ? FALSE : format_date($row['lastupdate'], 'small');
}
return $sites;
}
function _prod_monitor_get_site($id, $type = 'settings') {
switch ($type) {
case 'settings':
$sql = "SELECT url, settings FROM {prod_monitor_sites} WHERE id = %d";
break;
case 'all':
$sql = "SELECT * FROM {prod_monitor_sites} WHERE id = %d";
break;
case 'data':
$sql = "SELECT data FROM {prod_monitor_sites} WHERE id = %d";
break;
}
$site = db_fetch_array(db_query($sql, $id));
if (!empty($site)) {
switch ($type) {
case 'all':
$site['data'] = unserialize($site['data']);
case 'settings':
$site['settings'] = unserialize($site['settings']);
break;
case 'data':
$site['data'] = unserialize($site['data']);
break;
}
}
return $site;
}
function _prod_monitor_get_site_modules($id, $exists = FALSE) {
if (!$exists) {
$sql = "SELECT * FROM {prod_monitor_site_modules} WHERE id = %d";
}
else {
$sql = "SELECT id FROM {prod_monitor_site_modules} WHERE id = %d";
}
$modules = db_fetch_array(db_query($sql, $id));
if (!empty($modules) && !$exists) {
$modules['projects'] = unserialize($modules['projects']);
$modules['available'] = unserialize($modules['available']);
}
return $modules;
}
function _prod_monitor_get_site_ignored($id) {
static $ignored = array();
if (!array_key_exists($id, $ignored)) {
$ignored[$id] = module_invoke_all('prod_monitor_ignore', $id) + array(
'updates' => array(),
);
}
return $ignored[$id];
}
function _prod_monitor_get_update_status($id) {
$sql = "SELECT updates FROM {prod_monitor_site_modules} WHERE id = %d";
return db_result(db_query($sql, $id));
}
function _prod_monitor_get_performance_data($id) {
$sql = "SELECT module, data, fetched FROM {prod_monitor_site_performance} WHERE id = %d";
$result = db_query($sql, $id);
$data = array();
while ($row = db_fetch_array($result)) {
$data[$row['module']][$row['fetched']] = unserialize($row['data']);
}
return $data;
}
function _prod_monitor_flush_data($id) {
$site = new stdClass();
$site->id = $id;
$site->data = serialize(array());
$site->lastupdate = 0;
return drupal_write_record('prod_monitor_sites', $site, array(
'id',
));
}
function _prod_monitor_delete_site($id) {
$sql = "DELETE FROM ps, pm USING {prod_monitor_sites} ps LEFT JOIN {prod_monitor_site_modules} pm ON ps.id = pm.id WHERE ps.id = %d";
return db_query($sql, $id);
}
function _prod_monitor_get_url($id) {
return _prod_monitor_sanitize_url(rtrim(db_result(db_query("SELECT url FROM {prod_monitor_sites} WHERE id = %d", $id)), '/'));
}
function _prod_monitor_sanitize_url($url) {
return preg_replace('/(:\\/\\/[^:]+:)[^@]+(@)/', "\$1.. . \$2", $url);
}