示例#1
0
function process_syslog($entry, $update)
{
    global $config;
    global $rules;
    global $device_rules;
    global $maint;
    foreach ($config['syslog']['filter'] as $bi) {
        if (strpos($entry['msg'], $bi) !== FALSE) {
            //echo('D-'.$bi);
            return FALSE;
        }
    }
    $entry['msg_orig'] = $entry['msg'];
    // Initial rewrites
    $entry['host'] = strtolower(trim($entry['host']));
    // Rewrite priority and level from strings to numbers
    $entry['priority'] = priority_string_to_numeric($entry['priority']);
    $entry['level'] = priority_string_to_numeric($entry['level']);
    $entry['device_id'] = get_cache($entry['host'], 'device_id');
    //print_vars($entry);
    //print_vars($GLOBALS['dev_cache']);
    if ($entry['device_id']) {
        $os = get_cache($entry['host'], 'os');
        $os_group = get_cache($entry['host'], 'os_group');
        if (in_array($os, array('ios', 'iosxe', 'catos', 'asa'))) {
            $matches = array();
            #      if (preg_match('#%(?P<program>.*):( ?)(?P<msg>.*)#', $entry['msg'], $matches)) {
            #        $entry['msg'] = $matches['msg'];
            #        $entry['program'] = $matches['program'];
            #      }
            #      unset($matches);
            //NOTE. Please include examples for syslog entries, to know why need some preg_replace()
            if (strstr($entry['msg'], '%')) {
                //10.0.0.210||23||4||4||26644:||2013-11-08 07:19:24|| 033884: Nov  8 07:19:23.993: %FW-4-TCP_OoO_SEG: Dropping TCP Segment: seq:-1169729434 1500 bytes is out-of-order; expected seq:3124765814. Reason: TCP reassembly queue overflow - session 10.10.32.37:56316 to 93.186.239.142:80 on zone-pair Local->Internet class All_Inspection||26644
                //hostname||17||5||5||192462650:||2014-06-17 11:16:01|| %SSH-5-SSH2_SESSION: SSH2 Session request from 10.95.0.42 (tty = 0) using crypto cipher 'aes256-cbc', hmac 'hmac-sha1' Succeeded||192462650
                if (strpos($entry['msg'], ': %')) {
                    list(, $entry['msg']) = explode(': %', $entry['msg'], 2);
                    $entry['msg'] = "%" . $entry['msg'];
                }
                $entry['msg'] = preg_replace("/^%(.+?):\\ /", "\\1||", $entry['msg']);
            } else {
                $entry['msg'] = preg_replace("/^.*[0-9]:/", "", $entry['msg']);
                $entry['msg'] = preg_replace("/^[0-9][0-9]\\ [A-Z]{3}:/", "", $entry['msg']);
                $entry['msg'] = preg_replace("/^(.+?):\\ /", "\\1||", $entry['msg']);
            }
            //$entry['msg'] = preg_replace("/^.+\.[0-9]{3}:/", "", $entry['msg']); /// FIXME. Show which entries this should replace. It's broke all entries with 'IP:PORT'.
            $entry['msg'] = preg_replace("/^.+-Traceback=/", "Traceback||", $entry['msg']);
            list($entry['program'], $entry['msg']) = explode("||", $entry['msg'], 2);
            $entry['msg'] = preg_replace("/^[0-9]+:/", "", $entry['msg']);
            if (!$entry['program']) {
                $entry['msg'] = preg_replace("/^([0-9A-Z\\-]+?):\\ /", "\\1||", $entry['msg']);
                list($entry['program'], $entry['msg']) = explode("||", $entry['msg'], 2);
            }
            if (!$entry['msg']) {
                $entry['msg'] = $entry['program'];
                unset($entry['program']);
            }
        } else {
            if ($os == 'iosxr') {
                //1.1.1.1||23||5||5||920:||2014-11-26 17:29:48||RP/0/RSP0/CPU0:Nov 26 16:29:48.161 : bgp[1046]: %ROUTING-BGP-5-ADJCHANGE : neighbor 1.1.1.2 Up (VRF: default) (AS: 11111) ||920
                //1.1.1.2||23||6||6||253:||2014-11-26 17:30:21||RP/0/RSP0/CPU0:Nov 26 16:30:21.710 : SSHD_[65755]: %SECURITY-SSHD-6-INFO_GENERAL : Client closes socket connection ||253
                //1.1.1.3||local0||err||err||83||2015-01-14 07:29:45||oly-er-01 LC/0/0/CPU0:Jan 14 07:29:45.556 CET: pfilter_ea[301]: %L2-PFILTER_EA-3-ERR_IM_CAPS : uidb set  acl failed on interface Bundle-Ether1.1501.ip43696. (null) ||94795
                list(, $entry['msg']) = explode(': %', $entry['msg'], 2);
                list($entry['program'], $entry['msg']) = explode(' : ', $entry['msg'], 2);
            } else {
                if ($os == 'linux' && get_cache($entry['host'], 'version') == 'Point') {
                    // Cisco WAP200 and similar
                    $matches = array();
                    if (preg_match('#Log: \\[(?P<program>.*)\\] - (?P<msg>.*)#', $entry['msg'], $matches)) {
                        $entry['msg'] = $matches['msg'];
                        $entry['program'] = $matches['program'];
                    }
                    unset($matches);
                } else {
                    if ($os_group == 'unix') {
                        $matches = array();
                        // User_CommonName/123.213.132.231:39872 VERIFY OK: depth=1, /C=PL/ST=Malopolska/O=VLO/CN=v-lo.krakow.pl/emailAddress=root@v-lo.krakow.pl
                        if ($entry['facility'] == 'daemon' && preg_match('#/([0-9]{1,3}\\.) {3}[0-9]{1,3}:[0-9]{4,} ([A-Z]([A-Za-z])+( ?)) {2,}:#', $entry['msg'])) {
                            $entry['program'] = 'OpenVPN';
                        } else {
                            if ($entry['facility'] == 'mail' && preg_match('/^(((pop3|imap)\\-login)|((POP3|IMAP)\\(.*\\))):/', $entry['msg'])) {
                                $entry['program'] = 'Dovecot';
                            } else {
                                if (preg_match('/^(?P<program>(\\S((\\(|\\[).*(\\)|\\])))):(?P<msg>.*)$/', $entry['msg'], $matches)) {
                                    $entry['msg'] = $matches['msg'];
                                    $entry['program'] = $matches['program'];
                                } else {
                                    if (preg_match('/^(?P<program>[^\\s\\(\\[]*):\\ (?P<msg>.*)$/', $entry['msg'], $matches)) {
                                        $entry['msg'] = $matches['msg'];
                                        $entry['program'] = $matches['program'];
                                    } else {
                                        if (!empty($entry['program']) && preg_match('/^.*:\\ ' . $entry['program'] . ':\\ (?P<msg>[^(]+\\((?P<program>[^:]+):.*)$/', $entry['msg'], $matches)) {
                                            $entry['msg'] = $matches['msg'];
                                            $entry['program'] = $matches['program'];
                                        } else {
                                            if (empty($entry['program']) && !empty($entry['facility'])) {
                                                $entry['program'] = $entry['facility'];
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        unset($matches);
                    } else {
                        if ($os == 'ftos') {
                            if (empty($entry['program'])) {
                                //1.1.1.1||23||5||5||||2014-11-23 21:48:10|| Nov 23 21:48:10.745: hostname: %STKUNIT0-M:CP %SEC-5-LOGOUT: Exec session is terminated for user rancid on line vty0||
                                list(, , $entry['program'], $entry['msg']) = explode(': ', $entry['msg'], 4);
                                list(, $entry['program']) = explode(' %', $entry['program'], 2);
                            }
                            //Jun 3 02:33:23.489: %STKUNIT0-M:CP %SNMP-3-SNMP_AUTH_FAIL: SNMP Authentication failure for SNMP request from host 176.10.35.241
                            //Jun 1 17:11:50.806: %STKUNIT0-M:CP %ARPMGR-2-MAC_CHANGE: IP-4-ADDRMOVE: IP address 11.222.30.53 is moved from MAC address 52:54:00:7b:37:ad to MAC address 52:54:00:e4:ec:06 .
                            //if (strpos($entry['msg'], '%STKUNIT') === 0)
                            //{
                            //  list(, $entry['program'], $entry['msg']) = explode(': ', $entry['msg'], 3);
                            //  //$entry['timestamp'] = date("Y-m-d H:i:s", strtotime($entry['timestamp'])); // convert to timestamp
                            //  list(, $entry['program']) = explode(' %', $entry['program'], 2);
                            //}
                        } else {
                            if ($os == 'netscaler') {
                                //10/03/2013:16:49:07 GMT dk-lb001a PPE-4 : UI CMD_EXECUTED 10367926 : User so_readonly - Remote_ip 10.70.66.56 - Command "stat lb vserver" - Status "Success"
                                list(, , , $entry['msg']) = explode(' ', $entry['msg'], 4);
                                list($entry['program'], $entry['msg']) = explode(' : ', $entry['msg'], 3);
                            }
                        }
                    }
                }
            }
        }
        if ($entry['program'] == '') {
            /** FIXME, WHAT? Pls examples.
                $entry['program'] = $entry['msg'];
                unset($entry['msg']);
                */
            if ($entry['msg'] == '') {
                // Something wrong, both program and msg empty
                return $entry;
            }
        } else {
            if (strpos($entry['program'], '(BZ2') === 0) {
                // Wtf is BZ2LR and BZ@..
                /**
                 *Old: 10.10.34.10||3||6||6||hostapd:||2014-07-18 11:29:35|| ath2: STA c8:dd:c9:d1:d4:aa IEEE 802.11: associated||hostapd
                 *New: 10.10.34.10||3||6||6||(BZ2LR,00272250c1cd,v3.2.5.2791)||2014-12-12 09:36:39|| hostapd: ath2: STA dc:a9:71:1b:d6:c7 IEEE 802.11: associated||(BZ2LR,00272250c1cd,v3.2.5.2791)
                 */
                list($entry['program'], $entry['msg']) = explode(': ', $entry['msg'], 2);
            }
        }
        $entry['program'] = strtoupper($entry['program']);
        array_walk($entry, 'trim');
        if ($update) {
            $log_id = dbInsert(array('device_id' => $entry['device_id'], 'host' => $entry['host'], 'program' => $entry['program'], 'facility' => $entry['facility'], 'priority' => $entry['priority'], 'level' => $entry['level'], 'tag' => $entry['tag'], 'msg' => $entry['msg'], 'timestamp' => $entry['timestamp']), 'syslog');
        }
        //$req_dump = print_r(array($entry, $rules, $device_rules), TRUE);
        //$fp = fopen('/tmp/syslog.log', 'a');
        //fwrite($fp, $req_dump);
        //fclose($fp);
        $notification_type = 'syslog';
        /// FIXME, I not know how 'syslog_rules_assoc' is filled, I pass rules to all devices
        /// FIXME, this is copy-pasted from above, while not have WUI for syslog_rules_assoc
        foreach ($rules as $la_id => $rule) {
            if ((empty($device_rules) || isset($device_rules[$entry['device_id']][$la_id])) && preg_match($rule['la_rule'], $entry['msg_orig'])) {
                // Mark no notification during maintenance
                if (isset($maint['device'][$entry['device_id']]) || isset($maint['global']) && $maint['global'] > 0) {
                    $notified = '-1';
                } else {
                    $notified = '0';
                }
                $log_id = dbInsert(array('device_id' => $entry['device_id'], 'la_id' => $la_id, 'syslog_id' => $log_id, 'timestamp' => $entry['timestamp'], 'program' => $entry['program'], 'message' => $entry['msg_orig'], 'notified' => $notified), 'syslog_alerts');
                // Get contacts for $la_id
                $transports = get_alert_contacts($entry['device_id'], $la_id, $notification_type);
                // Add notification to queue
                if ($notified != '-1' && !empty($transports)) {
                    $device = device_by_id_cache($entry['device_id']);
                    $message_tags = array('ALERT_STATE' => "SYSLOG", 'ALERT_URL' => generate_url(array('page' => 'device', 'device' => $device['device_id'], 'tab' => 'alert', 'entity_type' => 'syslog')), 'ALERT_ID' => $la_id, 'ALERT_MESSAGE' => $rule['la_descr'], 'CONDITIONS' => $rule['la_rule'], 'METRICS' => $entry['msg'], 'SYSLOG_RULE' => $rule['la_rule'], 'SYSLOG_MESSAGE' => $entry['msg'], 'SYSLOG_PROGRAM' => $entry['program'], 'TIMESTAMP' => $entry['timestamp'], 'DEVICE_HOSTNAME' => $device['hostname'], 'DEVICE_LINK' => generate_device_link($device), 'DEVICE_HARDWARE' => $device['hardware'], 'DEVICE_OS' => $device['os_text'] . ' ' . $device['version'] . ' ' . $device['features'], 'DEVICE_LOCATION' => $device['location'], 'DEVICE_UPTIME' => deviceUptime($device));
                    $message_tags['TITLE'] = alert_generate_subject($device, 'SYSLOG', $message_tags);
                    $notification = array('device_id' => $entry['device_id'], 'log_id' => $log_id, 'aca_type' => $notification_type, 'severity' => $entry['priority'], 'endpoints' => json_encode($transports), 'notification_added' => time(), 'notification_lifetime' => 300, 'notification_entry' => json_encode($entry));
                    //unset($message_tags['ENTITY_GRAPHS_ARRAY']);
                    $notification['message_tags'] = json_encode($message_tags);
                    $notification_id = dbInsert($notification, 'notifications_queue');
                }
            }
        }
        unset($os);
    } else {
        if ($config['syslog']['unknown_hosts']) {
            if ($update) {
                array_walk($entry, 'trim');
                // Store entries for unknown hosts with NULL device_id
                $log_id = dbInsert(array('host' => $entry['host'], 'program' => $entry['program'], 'facility' => $entry['facility'], 'priority' => $entry['priority'], 'level' => $entry['level'], 'tag' => $entry['tag'], 'msg' => $entry['msg'], 'timestamp' => $entry['timestamp']), 'syslog');
                //var_dump($entry);
            }
        }
    }
    return $entry;
}
function process_alerts($device)
{
    global $config, $alert_rules, $alert_assoc;
    echo "处理警报 " . $device['hostname'] . PHP_EOL;
    $alert_table = cache_device_alert_table($device['device_id']);
    $sql = "SELECT * FROM `alert_table`";
    $sql .= " LEFT JOIN `alert_table-state` ON `alert_table`.`alert_table_id` = `alert_table-state`.`alert_table_id`";
    $sql .= " WHERE `device_id` = ? AND `alert_status` IS NOT NULL;";
    foreach (dbFetchRows($sql, array($device['device_id'])) as $entry) {
        echo 'Alert: ' . $entry['alert_table_id'] . ' Status: ' . $entry['alert_status'] . ' ';
        // If the alerter is now OK and has previously alerted, send an recovery notice.
        if ($entry['alert_status'] == '1' && $entry['has_alerted'] == '1') {
            $alert = $alert_rules[$entry['alert_test_id']];
            if (!$alert['suppress_recovery']) {
                $state = json_decode($entry['state'], TRUE);
                $conditions = json_decode($alert['conditions'], TRUE);
                $entity = get_entity_by_id_cache($entry['entity_type'], $entry['entity_id']);
                $metric_array = array();
                foreach ($state['metrics'] as $metric => $value) {
                    $metric_array[] = $metric . ' = ' . $value;
                }
                $message_tags = array('ALERT_STATE' => 'RECOVERY', 'ALERT_URL' => generate_url(array('page' => 'device', 'device' => $device['device_id'], 'tab' => 'alert', 'alert_entry' => $entry['alert_table_id'])), 'ALERT_MESSAGE' => $alert['alert_message'], 'METRICS' => implode(PHP_EOL . '             ', $metric_array), 'DURATION' => formatUptime(time() - $entry['last_failed']), 'ENTITY_LINK' => generate_entity_link($entry['entity_type'], $entry['entity_id'], $entity['entity_name']), 'ENTITY_NAME' => $entity['entity_name'], 'ENTITY_DESCRIPTION' => $entity['entity_descr'], 'DEVICE_HOSTNAME' => $device['hostname'], 'DEVICE_LINK' => generate_device_link($device), 'DEVICE_HARDWARE' => $device['hardware'], 'DEVICE_OS' => $device['os_text'] . ' ' . $device['version'] . ' ' . $device['features'], 'DEVICE_LOCATION' => $device['location'], 'DEVICE_UPTIME' => deviceUptime($device));
                $message['text'] = simple_template('alert/email_text.tpl', $message_tags, array('is_file' => TRUE));
                //$message_tags['CONDITIONS'] = nl2br($message_tags['CONDITIONS']);
                $message_tags['METRICS'] = nl2br($message_tags['METRICS']);
                $message['html'] = simple_template('alert/email_html.tpl', $message_tags, array('is_file' => TRUE));
                //logfile('debug.log', var_export($message, TRUE));
                alert_notify($device, alert_generate_subject('RECOVER', $device, $alert, $entity), $message, $entry['alert_test_id']);
                log_alert('恢复发送的通知', $device, $entry, 'RECOVER_NOTIFY');
            } else {
                echo '已限制恢复.';
                log_alert('限制恢复通知', $device, $entry, 'RECOVER_SUPPRESSED');
            }
            $update_array['last_recovered'] = time();
            $update_array['has_alerted'] = 0;
            dbUpdate($update_array, 'alert_table-state', '`alert_table_id` = ?', array($entry['alert_table_id']));
        }
        if ($entry['alert_status'] == '0') {
            echo '警报已停止. ';
            // Has this been alerted more frequently than the alert interval in the config?
            /// FIXME -- this should be configurable per-entity or per-checker
            if (time() - $entry['last_alerted'] < $config['alerts']['interval'] && !isset($GLOBALS['spam'])) {
                $entry['suppress_alert'] = TRUE;
            }
            // Check if alert has ignore_until set.
            if (is_numeric($entry['ignore_until']) && $entry['ignore_until'] > time()) {
                $entry['suppress_alert'] = TRUE;
            }
            // Check if alert has ignore_until_ok set.
            if (is_numeric($entry['ignore_until_ok']) && $entry['ignore_until_ok'] == '1') {
                $entry['suppress_alert'] = TRUE;
            }
            if ($entry['suppress_alert'] != TRUE) {
                echo 'Requires notification. ';
                $alert = $alert_rules[$entry['alert_test_id']];
                $state = json_decode($entry['state'], TRUE);
                $conditions = json_decode($alert['conditions'], TRUE);
                $entity = get_entity_by_id_cache($entry['entity_type'], $entry['entity_id']);
                $condition_array = array();
                foreach ($state['failed'] as $failed) {
                    $condition_array[] = $failed['metric'] . " " . $failed['condition'] . " " . $failed['value'] . " (" . $state['metrics'][$failed['metric']] . ")";
                }
                $graphs = "";
                $metric_array = array();
                foreach ($state['metrics'] as $metric => $value) {
                    $metric_array[] = $metric . ' = ' . $value;
                }
                if (is_array($config['entities'][$entry['entity_type']]['graph'])) {
                    // We can draw a graph for this type/metric pair!
                    $graph_array = $config['entities'][$entry['entity_type']]['graph'];
                    foreach ($graph_array as $key => $val) {
                        // Check to see if we need to do any substitution
                        if (substr($val, 0, 1) == "@") {
                            $nval = substr($val, 1);
                            echo " replaced " . $val . " with " . $entity[$nval] . " from entity. " . PHP_EOL . "<br />";
                            $graph_array[$key] = $entity[$nval];
                        }
                    }
                    //print_r($graph_array);
                    //logfile('debug.log', var_export($graph_array, TRUE));
                    $image_data_uri = generate_alert_graph($graph_array);
                    //print_r($image_data_uri);
                    //logfile('debug.log', var_export($image_data_uri, TRUE));
                    $graphs .= '<img src="' . $image_data_uri . '"><br />';
                    unset($graph_array);
                }
                $message_tags = array('ALERT_STATE' => 'ALERT', 'ALERT_URL' => generate_url(array('page' => 'device', 'device' => $device['device_id'], 'tab' => 'alert', 'alert_entry' => $entry['alert_table_id'])), 'ALERT_MESSAGE' => $alert['alert_message'], 'CONDITIONS' => implode(PHP_EOL . '             ', $condition_array), 'METRICS' => implode(PHP_EOL . '             ', $metric_array), 'DURATION' => formatUptime(time() - $entry['last_failed']), 'ENTITY_LINK' => generate_entity_link($entry['entity_type'], $entry['entity_id'], $entity['entity_name']), 'ENTITY_NAME' => $entity['entity_name'], 'ENTITY_DESCRIPTION' => $entity['entity_descr'], 'ENTITY_GRAPHS' => $graphs, 'DEVICE_HOSTNAME' => $device['hostname'], 'DEVICE_LINK' => generate_device_link($device), 'DEVICE_HARDWARE' => $device['hardware'], 'DEVICE_OS' => $device['os_text'] . ' ' . $device['version'] . ' ' . $device['features'], 'DEVICE_LOCATION' => $device['location'], 'DEVICE_UPTIME' => deviceUptime($device));
                $message['text'] = simple_template('alert/email_text.tpl', $message_tags, array('is_file' => TRUE));
                $message_tags['CONDITIONS'] = nl2br($message_tags['CONDITIONS']);
                $message_tags['METRICS'] = nl2br($message_tags['METRICS']);
                $message['html'] = simple_template('alert/email_html.tpl', $message_tags, array('is_file' => TRUE));
                //logfile('debug.log', var_export($message, TRUE));
                alert_notify($device, alert_generate_subject('ALERT', $device, $alert, $entity), $message, $entry['alert_test_id']);
                log_alert('Alert notification sent', $device, $entry, 'ALERT_NOTIFY');
                $update_array['last_alerted'] = time();
                $update_array['has_alerted'] = 1;
                dbUpdate($update_array, 'alert_table-state', '`alert_table_id` = ?', array($entry['alert_table_id']));
            } else {
                echo "没有通知要求. " . (time() - $entry['last_alerted']);
            }
        } else {
            if ($entry['alert_status'] == '1') {
                echo "状态: OK. ";
            } else {
                if ($entry['alert_status'] == '2') {
                    echo "Status: Notification Delayed. ";
                } else {
                    if ($entry['alert_status'] == '3') {
                        echo "Status: Notification Suppressed. ";
                    } else {
                        echo "未知的状态.";
                    }
                }
            }
        }
        echo PHP_EOL;
    }
}
示例#3
0
/**
 * Generate notifications for an alert entry
 *
 * @param array entry
 * @return NULL
 */
function alert_notifier($entry, $type = "alert")
{
    global $config, $alert_rules;
    $device = device_by_id_cache($entry['device_id']);
    $alert = $alert_rules[$entry['alert_test_id']];
    $state = json_decode($entry['state'], TRUE);
    $conditions = json_decode($alert['conditions'], TRUE);
    $entity = get_entity_by_id_cache($entry['entity_type'], $entry['entity_id']);
    $condition_array = array();
    foreach ($state['failed'] as $failed) {
        $condition_array[] = $failed['metric'] . " " . $failed['condition'] . " " . $failed['value'] . " (" . $state['metrics'][$failed['metric']] . ")";
    }
    $metric_array = array();
    foreach ($state['metrics'] as $metric => $value) {
        $metric_array[] = $metric . ' = ' . $value;
    }
    $graphs = array();
    $graph_done = array();
    foreach ($state['metrics'] as $metric => $value) {
        if ($config['email']['graphs'] !== FALSE && is_array($config['entities'][$entry['entity_type']]['metric_graphs'][$metric]) && !in_array($config['entities'][$entry['entity_type']]['metric_graphs'][$metric]['type'], $graph_done)) {
            $graph_array = $config['entities'][$entry['entity_type']]['metric_graphs'][$metric];
            foreach ($graph_array as $key => $val) {
                // Check to see if we need to do any substitution
                if (substr($val, 0, 1) == '@') {
                    $nval = substr($val, 1);
                    //echo(" replaced " . $val . " with " . $entity[$nval] . " from entity. " . PHP_EOL . "<br />");
                    $graph_array[$key] = $entity[$nval];
                }
            }
            $image_data_uri = generate_alert_graph($graph_array);
            $image_url = generate_graph_url($graph_array);
            $graphs[] = array('label' => $graph_array['type'], 'type' => $graph_array['type'], 'url' => $image_url, 'data' => $image_data_uri);
            $graph_done[] = $graph_array['type'];
        }
        unset($graph_array);
    }
    if ($config['email']['graphs'] !== FALSE && count($graph_done) == 0 && is_array($config['entities'][$entry['entity_type']]['graph'])) {
        // We can draw a graph for this type/metric pair!
        $graph_array = $config['entities'][$entry['entity_type']]['graph'];
        foreach ($graph_array as $key => $val) {
            // Check to see if we need to do any substitution
            if (substr($val, 0, 1) == '@') {
                $nval = substr($val, 1);
                //echo(" replaced ".$val." with ". $entity[$nval] ." from entity. ".PHP_EOL."<br />");
                $graph_array[$key] = $entity[$nval];
            }
        }
        //print_vars($graph_array);
        $image_data_uri = generate_alert_graph($graph_array);
        $image_url = generate_graph_url($graph_array);
        $graphs[] = array('label' => $graph_array['type'], 'type' => $graph_array['type'], 'url' => $image_url, 'data' => $image_data_uri);
        unset($graph_array);
    }
    $graphs_html = "";
    foreach ($graphs as $graph) {
        $graphs_html .= '<h4>' . $graph['type'] . '</h4>';
        $graphs_html .= '<a href="' . $graph['url'] . '"><img src="' . $graph['data'] . '"></a><br />';
    }
    //print_vars($graphs);
    //print_vars($graphs_html);
    //print_vars($entry);
    $message_tags = array('ALERT_STATE' => $entry['alert_status'] == '1' ? "RECOVER" : "ALERT", 'ALERT_URL' => generate_url(array('page' => 'device', 'device' => $device['device_id'], 'tab' => 'alert', 'alert_entry' => $entry['alert_table_id'])), 'ALERT_ID' => $entry['alert_table_id'], 'ALERT_MESSAGE' => $alert['alert_message'], 'CONDITIONS' => implode(PHP_EOL . '             ', $condition_array), 'METRICS' => implode(PHP_EOL . '             ', $metric_array), 'DURATION' => $entry['alert_status'] == '1' ? $entry['last_ok'] > 0 ? formatUptime(time() - $entry['last_ok']) . " (" . format_unixtime($entry['last_ok']) . ")" : "Unknown" : ($entry['last_ok'] > 0 ? formatUptime(time() - $entry['last_ok']) . " (" . format_unixtime($entry['last_ok']) . ")" : "Unknown"), 'ENTITY_LINK' => generate_entity_link($entry['entity_type'], $entry['entity_id'], $entity['entity_name']), 'ENTITY_NAME' => $entity['entity_name'], 'ENTITY_TYPE' => $alert['entity_type'], 'ENTITY_DESCRIPTION' => $entity['entity_descr'], 'ENTITY_GRAPHS_ARRAY' => json_encode($graphs), 'DEVICE_HOSTNAME' => $device['hostname'], 'DEVICE_LINK' => generate_device_link($device), 'DEVICE_HARDWARE' => $device['hardware'], 'DEVICE_OS' => $device['os_text'] . ' ' . $device['version'] . ' ' . $device['features'], 'DEVICE_LOCATION' => $device['location'], 'DEVICE_UPTIME' => deviceUptime($device));
    //logfile('debug.log', var_export($message, TRUE));
    $title = alert_generate_subject($device, $message_tags['ALERT_STATE'], $message_tags);
    $message_tags['TITLE'] = $title;
    $alert_id = $entry['alert_test_id'];
    $notify_status = FALSE;
    // Set alert notify status to FALSE by default
    $notification_type = 'alert';
    $transports = get_alert_contacts($device, $alert_id, $notification_type);
    if (!empty($transports)) {
        // WARNING, alerts queue currently experimental
        if (isset($config['alerts']['queue']) && isset($config['alerts']['queue'])) {
            // Add notification to queue
            $notification = array('device_id' => $device['device_id'], 'log_id' => $log_id, 'aca_type' => $notification_type, 'endpoints' => json_encode($transports), 'message_graphs' => $message_tags['ENTITY_GRAPHS_ARRAY'], 'notification_added' => time(), 'notification_lifetime' => 300, 'notification_entry' => json_encode($entry));
            $notification_message_tags = $message_tags;
            unset($notification_message_tags['ENTITY_GRAPHS_ARRAY']);
            $notification['message_tags'] = json_encode($notification_message_tags);
            $notification_id = dbInsert($notification, 'notifications_queue');
        } else {
            // Use classic instant notifications send
            $notification_count = 0;
            foreach ($transports as $method => $endpoints) {
                if (isset($config['alerts']['disable'][$method]) && $config['alerts']['disable'][$method]) {
                    continue;
                }
                // Skip if method disabled globally
                foreach ($endpoints as $endpoint) {
                    $method_include = $config['install_dir'] . "/includes/alerting/" . $method . ".inc.php";
                    if (is_file($method_include)) {
                        print_cli_data("Notifying", "[" . $method . "] " . $endpoint['contact_descr'] . ": " . $endpoint['contact_endpoint']);
                        // Split out endpoint data as stored JSON in the database into array for use in transport
                        // The original string also remains available as the contact_endpoint key
                        foreach (json_decode($endpoint['contact_endpoint']) as $field => $value) {
                            $endpoint[$field] = $value;
                        }
                        include $method_include;
                        // FIXME check success
                        // FIXME log notification + success/failure!
                        if ($notify_status['success']) {
                            $notification_count++;
                        }
                    } else {
                        print_cli_data("Missing include", $method_include);
                    }
                }
            }
            if ($notification_count) {
                dbUpdate(array('notified' => 1), 'alert_log', '`event_id` = ?', array($notification['log_id']));
            }
        }
    }
}