http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_DEFAULT_TEMPLATE.vm ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_DEFAULT_TEMPLATE.vm b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_DEFAULT_TEMPLATE.vm new file mode 100644 index 0000000..3926cc8 --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_DEFAULT_TEMPLATE.vm @@ -0,0 +1,301 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + #set ( $alert = $alertList[0] ) +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <meta name="viewport" content="width=device-width"/> + <title>$alert["alertSubject"]</title> + <style> + body { + width: 100% !important; + min-width: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + margin: 0; + padding: 0; + } + + table { + border-spacing: 0; + border-collapse: collapse; + } + + table th, + table td { + padding: 3px 0 3px 0; + } + + .body { + width: 100%; + } + + p, a, h1, h2, h3, ul, ol, li { + font-family: Helvetica, Arial, sans-serif; + font-weight: normal; + margin: 0; + padding: 0; + } + + p { + font-size: 14px; + line-height: 19px; + } + + a { + color: #3294b1; + } + + h1 { + font-size: 36px; + margin: 15px 0 5px 0; + } + + h2 { + font-size: 32px; + } + + h3 { + font-size: 28px; + } + + ul, ol { + margin: 0 0 0 25px; + padding: 0; + } + + .btn { + background: #2ba6cb !important; + border: 1px solid #2284a1; + padding: 10px 20px 10px 20px; + text-align: center; + } + + .btn:hover { + background: #2795b6 !important; + } + + .btn a { + color: #FFFFFF; + text-decoration: none; + font-weight: bold; + padding: 10px 20px 10px 20px; + } + + .tableBordered { + border-top: 1px solid #b9e5ff; + } + + .tableBordered th { + background: #ECF8FF; + } + + .tableBordered th p { + font-weight: bold; + color: #3294b1; + } + + .tableBordered th, + .tableBordered td { + color: #333333; + border-bottom: 1px solid #b9e5ff; + text-align: center; + padding-bottom: 5px; + } + + .panel { + height: 100px; + } + </style> +</head> +<body> +<table class="body"> + <tr> + <td align="center" valign="top" style="background: #999999; padding: 0 0 0 0;"> + <!-- Header --> + <table width="580"> + <tr> + <td style="padding: 0 0 0 0;" align="left"> + <p style="color:#FFFFFF;font-weight: bold; font-size: 22px">Eagle Alert Notification</p> + </td> + </tr> + </table> + </td> + </tr> + <tr> + <td align="center" valign="top"> + <!-- Eagle Body --> + <table width="580"> + <tr> + <!-- Title --> + <td align="center"> + <h2>Warning: $alert["alertSubject"]</h2> + </td> + </tr> + <tr> + <!-- Time --> + <td> + <table width="580"> + <tr> + <td> + <p><b>Detected Time: $alert["alertTime"]</b></p> + </td> + #set ( $alertSeverity = $alert["alertSeverity"] ) + #if (!$alertSeverity || ("$alertSeverity" == "")) + #set ( $alert["alertSeverity"] = "WARNING") + #end + <td align="right"> + <p><b> + Severity: + #if ($alert["alertSeverity"] == "WARNING") + <span>$alert["alertSeverity"]</span> + #else + <span style="color: #FF0000;">$alert["alertSeverity"]</span> + #end + </b></p> + </td> + </tr> + </table> + </td> + </tr> + + <tr> + <!-- Basic Information --> + <td style="padding: 20px 0 10px 0;"> + <p><b>Alert Message </b></p> + </td> + </tr> + <tr> + <!-- Description --> + <td valign="top" + style="background: #ECF8FF; border: 1px solid #b9e5ff; padding: 10px 10px 12px 20px;"> + <p>$alert["alertBody"]</p> + </td> + </tr> + <tr> + <!-- Basic Information --> + <td style="padding: 20px 0 10px 0;"> + <p><b>Alert Detail</b></p> + </td> + </tr> + <tr> + <!-- Basic Information Content --> + <td> + <table class="tableBordered" width="580"> + <tr> + <th> + <p>Policy Name</p> + </th> + <td> + <p><a href="$alert["policyDetailUrl"]">$alert["policyId"]</a></p> + </td> + </tr> + <tr> + <th> + <p>Severity Level</p> + </th> + <td> + <p>$alert["alertSeverity"]</p> + </td> + </tr> + <tr> + <th> + <p>Alert Stream</p> + </th> + <td> + <p>$alert["streamId"]</p> + </td> + </tr> + <tr> + <th> + <p>Created Time</p> + </th> + <td> + <p>$alert["alertTime"]</p> + </td> + </tr> + <tr> + <th> + <p>Created By</p> + </th> + <td> + <p>$alert["creator"]</p> + </td> + </tr> + </table> + </td> + </tr> +## <tr> +## <!-- View Detail --> +## <td align="center" style="padding: 10px 0 0 0;"> +## <table width="580"> +## <tr> +## <td class="btn"> +## <a href="$alert["policyDetailUrl"]">View Policy Details</a> +## </td> +## </tr> +## </table> +## </td> +## </tr> + + <tr> + <!-- View Detail --> + <td align="center" style="padding: 10px 0 0 0;"> + <table width="580"> + <tr> + <td class="btn"> + <a href="$alert["alertDetailUrl"]">View Alert on Eagle</a> + </td> + </tr> + </table> + </td> + </tr> + <tr> + <!-- Actions Required --> + <td style="padding: 20px 0 10px 0;"> + <p><b>Actions Required</b></p> + </td> + </tr> + <tr> + <!-- Possible Root Causes Content --> + <td class="panel" valign="top" + style="background: #F4F4F4; border: 1px solid #AAAAAA; padding: 10px 10px 12px 10px;"> + <p> + The alert notification was automatically detected and sent by Eagle according to policy: $alert["policyId"]. + To follow-up on this, please verify the alert and diagnose the root cause with Eagle: + </p> + <p></p> + <ul> + <li><p><a href="$alert["alertDetailUrl"]">View alert detail</a></p></li> + <li><p><a href="$alert["policyDetailUrl"]">View policy detail</a></p></li> + <li><p><a href="$alert["homeUrl"]">View eagle home</a></p></li> + </ul> + </td> + </tr> + <tr> + <!-- Copyright --> + <td align="center"> + <p><i>Powered by <a href="http://eagle.incubator.apache.org">Apache Eagle</a> (version: $alert["version"])</i></p> + </td> + </tr> + </table> + </td> + </tr> +</table> +</body> +</html> \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_INLINED_TEMPLATE.vm ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_INLINED_TEMPLATE.vm b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_INLINED_TEMPLATE.vm new file mode 100644 index 0000000..0e3d5fe --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_INLINED_TEMPLATE.vm @@ -0,0 +1,259 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +#set ( $alert = $alertList[0] ) + +## Generate Alert Color +#set($alertColor = "#337ab7") +#if($alert["alertSeverity"] == "WARNING") + #set($alertColor = "#FF9F00") +#elseif($alert["alertSeverity"] == "CRITICAL" || $alert["alertSeverity"] == "FETAL") + #set($alertColor = "#d43f3a") +#elseif ($alert["alertSeverity"] == "OK") + #set($alertColor = "#68B90F") +#end + +<html xmlns="http://www.w3.org/1999/xhtml" + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> +<head> + <meta name="viewport" content="width=device-width"/> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <title>[$alert["alertSeverity"] $alert["alertSubject"]</title> + <style type="text/css"> + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: none; + width: 100% !important; + height: 100%; + line-height: 1.6em; + } + + body { + background-color: #f6f6f6; + } + + @media only screen and (max-width: 640px) { + body { + padding: 0 !important; + } + + h1 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h2 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h3 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h4 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + + h1 { + font-size: 22px !important; + } + + h2 { + font-size: 18px !important; + } + + h3 { + font-size: 16px !important; + } + + .container { + padding: 0 !important; + width: 100% !important; + } + + .content { + padding: 0 !important; + } + + .content-wrap { + padding: 10px !important; + } + + .invoice { + width: 100% !important; + } + } + </style> +</head> + +<body itemscope itemtype="http://schema.org/EmailMessage" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" + bgcolor="#f6f6f6"> + +<table class="body-wrap" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" + bgcolor="#f6f6f6"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" + valign="top"></td> + <td class="container" width="600" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" + valign="top"> + <div class="content" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> + <table class="main" width="100%" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" + bgcolor="#fff"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="alert alert-warning" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: $alertColor; margin: 0; padding: 20px;" + align="center" bgcolor="$alertColor" valign="top"> + <strong >$alert["alertSeverity"]: </strong> + $alert["alertSubject"] + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-wrap" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" + valign="top"> + <table width="100%" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + <small>CATEGORY:</small> <strong style="color: $alertColor">#if($alert["alertCategory"]) $alert["alertCategory"] #else N/A #end</strong> <small>TIME:</small> <strong>$alert["alertTime"]</strong> + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0 4px; padding: 10px 10px; background-color: #eee;" + valign="top"> + <div style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top;"> + $alert["alertBody"] + </div> + </td> + </tr> + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block aligncenter" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: center; margin: 0; padding: 0 0 10px;" + align="center" valign="top"> + <table class="invoice" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; text-align: left; width: 90%; margin: 10px auto;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + <table class="invoice-items" cellpadding="0" cellspacing="0" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> + Severity + </td> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 5px 0;" + valign="top"> $alert["alertSeverity"] + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top">Category + </td> + <td class="alignright" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: left; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + align="right" valign="top"> + #if($alert["alertCategory"]) + $alert["alertCategory"] + #else + N/A + #end + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top">Cluster + </td> + <td class="alignright" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: left; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + align="right" valign="top">$alert["siteId"] + </td> + </tr> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + valign="top">Policy + </td> + <td class="alignright" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; text-align: left; border-top-width: 1px; border-top-color: #eee; border-top-style: solid; margin: 0; padding: 5px 0;" + align="right" valign="top"><a + href="$alert["policyDetailUrl"]" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #999; text-decoration: underline; margin: 0;">$alert[ + "policyId"]</a> + </td> + </tr> + </table> + </td> + </tr> + </table> + </td> + </tr> + + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" + valign="top"> + <a href="$alert["alertDetailUrl"]" class="btn-primary" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: $alertColor; margin: 0; border-color: $alertColor; border-style: solid; border-width: 10px 20px;"> + View Alert Details + </a> + </td> + </tr> + </table> + </td> + </tr> + </table> + <div class="footer" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> + <table width="100%" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> + <td class="aligncenter content-block" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" + align="center" valign="top"> + Powered by <a href="http://eagle.incubator.apache.org" + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">Apache + Eagle</a> (version: $alert["version"]) + </td> + </tr> + </table> + </div> + </div> + </td> + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" + valign="top"></td> + </tr> +</table> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_LIGHT_TEMPLATE.vm ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_LIGHT_TEMPLATE.vm b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_LIGHT_TEMPLATE.vm new file mode 100644 index 0000000..0eb5efc --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/main/resources/ALERT_LIGHT_TEMPLATE.vm @@ -0,0 +1,495 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + + #set ( $alert = $alertList[0] ) +<head> + <meta name="viewport" content="width=device-width" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>[$alert["alertSeverity"]$alert["alertSubject"]</title> + <style rel="stylesheet" type="text/css"> + /* ------------------------------------- + GLOBAL + A very basic CSS reset + ------------------------------------- */ + * { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + box-sizing: border-box; + font-size: 14px; + } + + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing: antialiased; + -webkit-text-size-adjust: none; + width: 100% !important; + height: 100%; + line-height: 1.6em; + /* 1.6em * 14px = 22.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 22px;*/ + } + + /* Let's make sure all tables have defaults */ + table td { + vertical-align: top; + } + + /* ------------------------------------- + BODY & CONTAINER + ------------------------------------- */ + body { + background-color: #f6f6f6; + } + + .body-wrap { + background-color: #f6f6f6; + width: 100%; + } + + .container { + display: block !important; + max-width: 600px !important; + margin: 0 auto !important; + /* makes it centered */ + clear: both !important; + } + + .content { + max-width: 600px; + margin: 0 auto; + display: block; + padding: 20px; + } + + /* ------------------------------------- + HEADER, FOOTER, MAIN + ------------------------------------- */ + .main { + background-color: #fff; + border: 1px solid #e9e9e9; + border-radius: 3px; + } + + .content-wrap { + padding: 20px; + } + + .content-block { + padding: 0 0 20px; + } + + .header { + width: 100%; + margin-bottom: 20px; + } + + .footer { + width: 100%; + clear: both; + color: #999; + padding: 20px; + } + .footer p, .footer a, .footer td { + color: #999; + font-size: 12px; + } + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1, h2, h3 { + font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + color: #000; + margin: 40px 0 0; + line-height: 1.2em; + font-weight: 400; + } + + h1 { + font-size: 32px; + font-weight: 500; + /* 1.2em * 32px = 38.4px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 38px;*/ + } + + h2 { + font-size: 24px; + /* 1.2em * 24px = 28.8px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 29px;*/ + } + + h3 { + font-size: 18px; + /* 1.2em * 18px = 21.6px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 22px;*/ + } + + h4 { + font-size: 14px; + font-weight: 600; + } + + p, ul, ol { + margin-bottom: 10px; + font-weight: normal; + } + p li, ul li, ol li { + margin-left: 5px; + list-style-position: inside; + } + + + /* ------------------------------------- + LINKS & BUTTONS + ------------------------------------- */ + a { + color: #348eda; + text-decoration: underline; + } + + .btn-primary { + text-decoration: none; + color: #FFF; + background-color: #348eda; + border: solid #348eda; + border-width: 10px 20px; + line-height: 2em; + /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 28px;*/ + font-weight: bold; + text-align: center; + cursor: pointer; + display: inline-block; + border-radius: 5px; + text-transform: capitalize; + } + + .btn-warning { + text-decoration: none; + color: #FFF; + background-color: #f0ad4e; + border-color: solid #eea236; + /*background-color: #348eda;*/ + /*border: solid #348eda;*/ + border-width: 10px 20px; + line-height: 2em; + /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 28px;*/ + font-weight: bold; + text-align: center; + cursor: pointer; + display: inline-block; + border-radius: 5px; + text-transform: capitalize; + } + + .btn-danger { + text-decoration: none; + color: #fff; + background-color: #d9534f; + border-color: solid #d43f3a; + /*color: #FFF;*/ + /*background-color: #348eda;*/ + /*border: solid #348eda;*/ + border-width: 10px 20px; + line-height: 2em; + /* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */ + /*line-height: 28px;*/ + font-weight: bold; + text-align: center; + cursor: pointer; + display: inline-block; + border-radius: 5px; + text-transform: capitalize; + } + + .text-light { + color: #eee; + } + + .text-primary { + color: #348eda; + } + + .text-warning { + color: #eea236 + } + + .text-danger { + color: #d43f3a; + } + + .label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; + } + + .label-ok { + background-color: #777; + } + .label-default { + background-color: #777; + } + .label-primary { + background-color: #337ab7; + } + .label-critical { + background-color: #d9534f; + } + .label-warning { + background-color: #f0ad4e; + } + + /* ------------------------------------- + OTHER STYLES THAT MIGHT BE USEFUL + ------------------------------------- */ + .last { + margin-bottom: 0; + } + + .first { + margin-top: 0; + } + + .aligncenter { + text-align: center; + } + + .alignright { + text-align: right; + } + + .alignleft { + text-align: left; + } + + .clear { + clear: both; + } + + /* ------------------------------------- + ALERTS + Change the class depending on warning email, good email or bad email + ------------------------------------- */ + .alert { + font-size: 16px; + color: #fff; + font-weight: 500; + padding: 20px; + text-align: center; + border-radius: 3px 3px 0 0; + } + .alert a { + color: #fff; + text-decoration: none; + font-weight: 500; + font-size: 16px; + } + .alert.alert-warning { + background-color: #FF9F00; + } + .alert.alert-bad { + background-color: #D0021B; + } + .alert.alert-critical { + background-color: #D0021B; + } + .alert.alert-fetal { + background-color: #D0021B; + } + .alert.alert-ok { + background-color: #68B90F; + } + + .alert-body { + margin-bottom: 10px; + padding: 8px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0,0,0,.05); + box-shadow: 0 1px 1px rgba(0,0,0,.05); + } + .alert-body-default { + border-color: #eee; + } + .alert-body-primary { + border-color: #337ab7; + } + .alert-body-warning { + border-color: #faebcc; + } + .alert-body-danger { + border-color: #ebccd1; + } + /* ------------------------------------- + INVOICE + Styles for the billing table + ------------------------------------- */ + .invoice { + margin: 40px auto; + text-align: left; + width: 80%; + } + .invoice td { + padding: 5px 0; + } + .invoice .invoice-items { + width: 100%; + } + .invoice .invoice-items td { + border-bottom: #eee 1px solid; + } + .invoice .invoice-items .total td { + border-top: 2px solid #333; + border-bottom: 2px solid #333; + font-weight: 700; + } + + /* ------------------------------------- + RESPONSIVE AND MOBILE FRIENDLY STYLES + ------------------------------------- */ + @media only screen and (max-width: 640px) { + body { + padding: 0 !important; + } + h1, h2, h3, h4 { + font-weight: 800 !important; + margin: 20px 0 5px !important; + } + h1 { + font-size: 22px !important; + } + + h2 { + font-size: 18px !important; + } + h3 { + font-size: 16px !important; + } + .container { + padding: 0 !important; + width: 100% !important; + } + .content { + padding: 0 !important; + } + .content-wrap { + padding: 10px !important; + } + .invoice { + width: 100% !important; + } + } + /*# sourceMappingURL=styles.css.map */ + </style> +</head> + +<body itemscope itemtype="http://schema.org/EmailMessage"> + +<table class="body-wrap"> + <tr> + <td></td> + <td class="container" width="600"> + <div class="content"> + <table class="main" width="100%" cellpadding="0" cellspacing="0"> + <tr> + <td class="alert alert-$alert["alertSeverity"].toLowerCase()"> + <strong>$alert["alertSeverity"]</strong> $alert["alertSubject"] + </td> + </tr> + <tr> + <td class="content-wrap"> + <table width="100%" cellpadding="0" cellspacing="0"> + <tr> + <td class="content-block"> + <table> + <tbody> + <tr> + <td>Category:</td> + <td><strong>$alert["alertCategory"]</strong></td> + <td>Time:</td> + <td><strong>$alert["alertTime"]</strong></td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr> + <td class="content-block"> + <div class="alert-body alert-body-default"> + $alert["alertBody"] + </div> + </td> + </tr> + <tr> + <td class="content-block invoice"> + <table class="invoice-items" cellpadding="0" cellspacing="0"> + <tbody> + <tr> + <td>Cluster</td> + <td>$alert["siteId"]</td> + </tr> + <tr> + <td>Policy</td> + <td><a href="$alert["policyDetailUrl"]">$alert["policyId"]</a></td> + </tr> + </tbody> + </table> + </td> + </tr> + <tr> + <td class="content-block"> + <a href="$alert["alertDetailUrl"]" class="btn-primary">View Alert Details</a> + </td> + </tr> + <tr> + <td class="content-block"> + <i>Note: The alert was automatically detected by <a href="#">Eagle</a>.</i> + </td> + </tr> + </table> + </td> + </tr> + </table> + <div class="footer"> + <table width="100%"> + <tr> + <td class="aligncenter content-block">Powered by <a href="http://eagle.incubator.apache.org">Apache Eagle</a> (version: $alert["version"])</td> + </tr> + </table> + </div> + </div> + </td> + <td></td> + </tr> +</table> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/AlertPublisherTestHelper.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/AlertPublisherTestHelper.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/AlertPublisherTestHelper.java index ee865b3..bd06c9b 100644 --- a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/AlertPublisherTestHelper.java +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/AlertPublisherTestHelper.java @@ -16,21 +16,20 @@ */ package org.apache.eagle.alert.engine.publisher; -import org.apache.eagle.alert.engine.coordinator.PolicyDefinition; -import org.apache.eagle.alert.engine.coordinator.StreamColumn; -import org.apache.eagle.alert.engine.coordinator.StreamDefinition; -import org.apache.eagle.alert.engine.coordinator.StreamPartition; +import org.apache.eagle.alert.engine.coordinator.*; +import org.apache.eagle.alert.engine.model.AlertPublishEvent; import org.apache.eagle.alert.engine.model.AlertStreamEvent; import org.apache.eagle.alert.engine.publisher.dedup.DedupCache; import org.junit.Assert; import java.util.Arrays; +import java.util.HashMap; public class AlertPublisherTestHelper { public static AlertStreamEvent mockEvent(String policyId){ StreamDefinition stream = createStream(); - PolicyDefinition policy = createPolicy(stream.getStreamId(), policyId); + PolicyDefinition policy = createPolicyGroupByStreamId(stream.getStreamId(), policyId); return createEvent(stream, policy, new Object[] {System.currentTimeMillis(), "host1", "testPolicy-host1-01", "open", 0, 0}); } @@ -42,8 +41,15 @@ public class AlertPublisherTestHelper { event.setStreamId(stream.getStreamId()); event.setTimestamp(System.currentTimeMillis()); event.setCreatedTime(System.currentTimeMillis()); + event.setSubject("Namenode Disk Used 98%"); + event.setBody("Disk Usage of Test cluster's name node (<a href=\"#\">namenode.hostname.domain</a>) is <strong style=\"color: red\">98%</strong> at <strong>2016-11-30 12:30:45</strong>, exceeding alert threshold <strong>90</strong>%"); event.setData(data); event.ensureAlertId(); + event.setSeverity(AlertSeverity.CRITICAL); + event.setCategory("HDFS"); + event.setContext(new HashMap<String,Object>(){{ + put(AlertPublishEvent.SITE_ID_KEY,"TestCluster"); + }}); Assert.assertNotNull(event.getAlertId()); return event; } @@ -84,7 +90,7 @@ public class AlertPublisherTestHelper { return sd; } - public static PolicyDefinition createPolicy(String streamName, String policyName) { + public static PolicyDefinition createPolicyGroupByStreamId(String streamName, String policyName) { PolicyDefinition pd = new PolicyDefinition(); PolicyDefinition.Definition def = new PolicyDefinition.Definition(); // expression, something like "PT5S,dynamic,1,host" @@ -103,4 +109,5 @@ public class AlertPublisherTestHelper { pd.addPartition(sp); return pd; } + } http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDedupWithoutStateTest.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDedupWithoutStateTest.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDedupWithoutStateTest.java index 6e59fa2..c48df9a 100644 --- a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDedupWithoutStateTest.java +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDedupWithoutStateTest.java @@ -46,7 +46,7 @@ public class DefaultDedupWithoutStateTest { "PT10S", Arrays.asList(new String[] {"alertKey"}), null, null, dedupCache); StreamDefinition stream = createStream(); - PolicyDefinition policy = createPolicy(stream.getStreamId(), "testPolicy"); + PolicyDefinition policy = createPolicyGroupByStreamId(stream.getStreamId(), "testPolicy"); int[] hostIndex = new int[] {1, 2, 3}; String[] states = new String[] {"OPEN", "WARN", "CLOSE"}; http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDeduplicatorTest.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDeduplicatorTest.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDeduplicatorTest.java index 700e3ee..297b790 100644 --- a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDeduplicatorTest.java +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/dedup/DefaultDeduplicatorTest.java @@ -45,7 +45,7 @@ public class DefaultDeduplicatorTest { "PT1M", Arrays.asList(new String[] {"alertKey"}), "state", "close", dedupCache); StreamDefinition stream = createStream(); - PolicyDefinition policy = createPolicy(stream.getStreamId(), "testPolicy"); + PolicyDefinition policy = createPolicyGroupByStreamId(stream.getStreamId(), "testPolicy"); AlertStreamEvent e1 = createEvent(stream, policy, new Object[] { System.currentTimeMillis(), "host1", "testPolicy-host1-01", "OPEN", 0, 0 http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityAlertTemplateEngineTest.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityAlertTemplateEngineTest.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityAlertTemplateEngineTest.java new file mode 100644 index 0000000..b893213 --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityAlertTemplateEngineTest.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.eagle.alert.engine.publisher.template; + +import com.typesafe.config.ConfigFactory; +import org.apache.eagle.alert.engine.coordinator.AlertDefinition; +import org.apache.eagle.alert.engine.coordinator.PolicyDefinition; +import org.apache.eagle.alert.engine.coordinator.StreamColumn; +import org.apache.eagle.alert.engine.coordinator.StreamDefinition; +import org.apache.eagle.alert.engine.model.AlertStreamEvent; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class VelocityAlertTemplateEngineTest { + @Test + public void testVelocityAlertTemplate () { + AlertTemplateEngine templateEngine = new VelocityAlertTemplateEngine(); + templateEngine.init(ConfigFactory.load()); + templateEngine.register(mockPolicy("testPolicy")); + AlertStreamEvent event = templateEngine.filter(mockAlertEvent("testPolicy")); + Assert.assertEquals("Alert (2016-11-30 07:31:15): cpu usage on hadoop of cluster test_cluster at localhost is 0.98, " + + "exceeding thread hold: 90%. (policy: testPolicy, description: Policy for monitoring cpu usage > 90%), " + + "definition: from HADOOP_JMX_METRIC_STREAM[site == \"test_cluster\" and metric == \"cpu.usage\" and value > 0.9] " + + "select site, metric, host, role, value insert into capacityUsageAlert", event.getBody()); + Assert.assertEquals("Name Node Usage Exceed 90%, reach 98.0% now", event.getSubject()); + } + + @Test + public void testVelocityAlertTemplateWithoutTemplate () { + AlertTemplateEngine templateEngine = new VelocityAlertTemplateEngine(); + templateEngine.init(ConfigFactory.load()); + templateEngine.register(mockPolicyWithoutTemplate("testPolicyName")); + AlertStreamEvent event = templateEngine.filter(mockAlertEvent("testPolicyName")); + System.out.print(event.getBody()); + Assert.assertEquals("Message: Alert {stream=ALERT_STREAM,timestamp=2016-11-30 07:31:15,923," + + "data={site=test_cluster, role=hadoop, metric=cpu.usage, host=localhost, value=0.98}, " + + "policyId=testPolicyName, createdBy=junit, metaVersion=SAMPLE_META_VERSION} " + + "(Auto-generated alert message as template not defined in policy testPolicyName)", event.getBody()); + Assert.assertEquals("testPolicyName", event.getSubject()); + } + + private static PolicyDefinition mockPolicy (String policyId) { + PolicyDefinition pd = new PolicyDefinition(); + PolicyDefinition.Definition def = new PolicyDefinition.Definition(); + def.setValue("from HADOOP_JMX_METRIC_STREAM[site == \"test_cluster\" and metric == \"cpu.usage\" and value > 0.9] " + + "select site, metric, host, role, value insert into capacityUsageAlert"); + def.setType("siddhi"); + pd.setDefinition(def); + pd.setInputStreams(Collections.singletonList("HADOOP_JMX_METRIC_STREAM")); + pd.setOutputStreams(Collections.singletonList("capacityUsageAlert")); + pd.setName(policyId); + pd.setDescription("Policy for monitoring cpu usage > 90%"); + AlertDefinition alertDefinition = new AlertDefinition(); + alertDefinition.setSubject("Name Node Usage Exceed 90%, reach #set($usage_per = $value * 100)$usage_per% now"); + alertDefinition.setBody("Alert ($CREATED_TIME): cpu usage on $role of cluster $site at $host is $value, exceeding thread hold: 90%. " + + "(policy: $POLICY_ID, description: $POLICY_DESC), definition: $POLICY_DEFINITION"); + pd.setAlertDefinition(alertDefinition); + return pd; + } + private static PolicyDefinition mockPolicyWithoutTemplate (String policyId) { + PolicyDefinition pd = new PolicyDefinition(); + PolicyDefinition.Definition def = new PolicyDefinition.Definition(); + def.setValue("from HADOOP_JMX_METRIC_STREAM[site == \"test_cluster\" and metric == \"cpu.usage\" and value > 0.9] " + + "select site, metric, host, role, value insert into capacityUsageAlert"); + def.setType("siddhi"); + pd.setDefinition(def); + pd.setInputStreams(Collections.singletonList("HADOOP_JMX_METRIC_STREAM")); + pd.setOutputStreams(Collections.singletonList("capacityUsageAlert")); + pd.setName(policyId); + pd.setDescription("Policy for monitoring cpu usage > 90%"); + return pd; + } + + private AlertStreamEvent mockAlertEvent (String policyId) { + AlertStreamEvent event = new AlertStreamEvent(); + event.setCreatedBy("junit"); + event.setCreatedTime(1480491075923L); + event.setPolicyId(policyId); + event.setStreamId("ALERT_STREAM"); + event.setSchema(mockAlertStreamDefinition("ALERT_STREAM")); + event.setMetaVersion("SAMPLE_META_VERSION"); + event.setTimestamp(1480491075923L); + event.setData(new Object[]{"test_cluster", "cpu.usage", "localhost", "hadoop", 0.98}); + event.ensureAlertId(); + return event; + } + + private StreamDefinition mockAlertStreamDefinition(String streamId){ + StreamDefinition streamDefinition = new StreamDefinition(); + streamDefinition.setStreamId(streamId); + streamDefinition.setSiteId("test_cluster"); + List<StreamColumn> columns = new ArrayList<>(); + StreamColumn column = new StreamColumn(); + column.setName("site"); + column.setType(StreamColumn.Type.STRING); + columns.add(column); + column = new StreamColumn(); + column.setName("metric"); + column.setType(StreamColumn.Type.STRING); + columns.add(column); + column = new StreamColumn(); + column.setName("host"); + column.setType(StreamColumn.Type.STRING); + columns.add(column); + column = new StreamColumn(); + column.setName("role"); + column.setType(StreamColumn.Type.STRING); + columns.add(column); + column = new StreamColumn(); + column.setName("value"); + column.setType(StreamColumn.Type.STRING); + columns.add(column); + + streamDefinition.setColumns(columns); + return streamDefinition; + } +} http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateParserTest.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateParserTest.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateParserTest.java new file mode 100644 index 0000000..269cb71 --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateParserTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.eagle.alert.engine.publisher.template; + +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class VelocityTemplateParserTest { + @Test + public void testParseVelocityTemplate() { + String templateString = "This alert ($category) was generated because $reason and $REASON from $source at $created_time"; + VelocityTemplateParser parser = new VelocityTemplateParser(templateString); + Assert.assertEquals(5, parser.getReferenceNames().size()); + Assert.assertArrayEquals(new String[]{"category", "reason", "REASON", "source", "created_time"}, parser.getReferenceNames().toArray()); + } + + + @Test(expected = ParseErrorException.class) + public void testParseInvalidVelocityTemplate() { + String templateString = "This alert ($category) was generated because $reason and $REASON from $source at $created_time #if() #fi"; + VelocityTemplateParser parser = new VelocityTemplateParser(templateString); + Assert.assertEquals(5, parser.getReferenceNames().size()); + Assert.assertArrayEquals(new String[]{"category", "reason", "REASON", "source", "created_time"}, parser.getReferenceNames().toArray()); + } + + @Test + public void testValidateVelocityContext() { + String templateString = "This alert ($category) was generated because $reason and $REASON from $source at $created_time"; + VelocityTemplateParser parser = new VelocityTemplateParser(templateString); + Map<String,Object> context = new HashMap<>(); + context.put("category", "UNKNOWN"); + context.put("reason", "timeout"); + context.put("REASON", "IO error"); + context.put("source","localhost"); + context.put("created_time", "2016-11-30 05:52:47,053"); + parser.validateContext(context); + } + + @Test(expected = MethodInvocationException.class) + public void testValidateInvalidVelocityContext() { + String templateString = "This alert ($category) was generated because $reason and $REASON from $source at $created_time"; + VelocityTemplateParser parser = new VelocityTemplateParser(templateString); + parser.validateContext(new HashMap<>()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateTest.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateTest.java b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateTest.java new file mode 100644 index 0000000..b67f394 --- /dev/null +++ b/eagle-core/eagle-alert-parent/eagle-alert/alert-engine/src/test/java/org/apache/eagle/alert/engine/publisher/template/VelocityTemplateTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.eagle.alert.engine.publisher.template; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.parser.node.ASTReference; +import org.apache.velocity.runtime.parser.node.ASTprocess; +import org.apache.velocity.runtime.resource.loader.StringResourceLoader; +import org.apache.velocity.runtime.resource.util.StringResourceRepository; +import org.apache.velocity.runtime.visitor.NodeViewMode; +import org.junit.Assert; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class VelocityTemplateTest { + private static final Logger LOG = LoggerFactory.getLogger(VelocityTemplateTest.class); + + @Test + public void testVelocityTemplate() { + String templateString = "This alert ($category) was generated because $reason and $REASON from $source at $created_time"; + String resultString = "This alert ($category) was generated because timeout and IO error from localhost at 2016-11-30 05:52:47,053"; + VelocityEngine engine = new VelocityEngine(); + engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.Log4JLogChute"); + engine.setProperty("runtime.log.logsystem.log4j.logger", LOG.getName()); + engine.setProperty(Velocity.RESOURCE_LOADER, "string"); + engine.addProperty("string.resource.loader.class", StringResourceLoader.class.getName()); + engine.addProperty("string.resource.loader.repository.static", "false"); + // engine.addProperty("runtime.references.strict", "true"); + engine.init(); + + StringResourceRepository repo = (StringResourceRepository) engine.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT); + repo.putStringResource("alert_template", ""); + repo.putStringResource("alert_template", templateString); + + Assert.assertEquals(templateString, repo.getStringResource("alert_template").getBody()); + + VelocityContext context = new VelocityContext(); + context.put("reason", "timeout"); + context.put("REASON", "IO error"); + context.put("source","localhost"); + context.put("created_time", "2016-11-30 05:52:47,053"); + + Template velocityTemplate = engine.getTemplate("alert_template"); + ASTprocess data = (ASTprocess) velocityTemplate.getData(); + ReferenceContext referenceContext = new ReferenceContext(); + data.jjtAccept(referenceContext,null); + Assert.assertEquals(5, referenceContext.getReferences().size()); + StringWriter writer = new StringWriter(); + velocityTemplate.merge(context, writer); + velocityTemplate.process(); + Assert.assertEquals(resultString, writer.toString()); + } + + private class ReferenceContext extends NodeViewMode { + private List<ASTReference> references = new ArrayList<>(); + + @Override + public Object visit(ASTReference node, Object data) { + references.add(node); + return super.visit(node, data); + } + + public List<ASTReference> getReferences() { + return this.references; + } + + public List<String> getReferenceNames() { + return this.references.stream().map(ASTReference::getRootString).collect(Collectors.toList()); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamProvider.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamProvider.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamProvider.java index 13080a1..eaa9ea0 100644 --- a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamProvider.java +++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamProvider.java @@ -19,6 +19,7 @@ package org.apache.eagle.app.messaging; import backtype.storm.spout.Scheme; import com.typesafe.config.Config; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,19 +60,19 @@ public class KafkaStreamProvider implements StreamProvider<KafkaStreamSink, Kafk KafkaStreamSinkConfig sinkConfig = new KafkaStreamSinkConfig(); sinkConfig.setTopicId(getSinkTopicName(streamId,config)); sinkConfig.setBrokerList(config.getString("dataSinkConfig.brokerList")); - sinkConfig.setSerializerClass(config.hasPath("dataSinkConfig.serializerClass") + sinkConfig.setSerializerClass(hasNonBlankConfigPath(config, "dataSinkConfig.serializerClass") ? config.getString("dataSinkConfig.serializerClass") : "kafka.serializer.StringEncoder"); - sinkConfig.setKeySerializerClass(config.hasPath("dataSinkConfig.keySerializerClass") + sinkConfig.setKeySerializerClass(hasNonBlankConfigPath(config, "dataSinkConfig.keySerializerClass") ? config.getString("dataSinkConfig.keySerializerClass") : "kafka.serializer.StringEncoder"); // new added properties for async producer - sinkConfig.setNumBatchMessages(config.hasPath("dataSinkConfig.numBatchMessages") + sinkConfig.setNumBatchMessages(hasNonBlankConfigPath(config, "dataSinkConfig.numBatchMessages") ? config.getString("dataSinkConfig.numBatchMessages") : "1024"); - sinkConfig.setProducerType(config.hasPath("dataSinkConfig.producerType") + sinkConfig.setProducerType(hasNonBlankConfigPath(config, "dataSinkConfig.producerType") ? config.getString("dataSinkConfig.producerType") : "async"); - sinkConfig.setMaxQueueBufferMs(config.hasPath("dataSinkConfig.maxQueueBufferMs") + sinkConfig.setMaxQueueBufferMs(hasNonBlankConfigPath(config, "dataSinkConfig.maxQueueBufferMs") ? config.getString("dataSinkConfig.maxQueueBufferMs") : "3000"); - sinkConfig.setRequestRequiredAcks(config.hasPath("dataSinkConfig.requestRequiredAcks") + sinkConfig.setRequestRequiredAcks(hasNonBlankConfigPath(config, "dataSinkConfig.requestRequiredAcks") ? config.getString("dataSinkConfig.requestRequiredAcks") : "1"); return sinkConfig; @@ -82,6 +83,10 @@ public class KafkaStreamProvider implements StreamProvider<KafkaStreamSink, Kafk return new KafkaStreamSink(); } + private boolean hasNonBlankConfigPath(Config config, String configName) { + return config.hasPath(configName) && StringUtils.isNotBlank(config.getString(configName)); + } + @Override public KafkaStreamSourceConfig getSourceConfig(String streamId, Config config) { KafkaStreamSourceConfig sourceConfig = new KafkaStreamSourceConfig(); @@ -89,31 +94,31 @@ public class KafkaStreamProvider implements StreamProvider<KafkaStreamSink, Kafk sourceConfig.setTopicId(getSourceTopicName(streamId,config)); sourceConfig.setBrokerZkQuorum(config.getString("dataSourceConfig.zkConnection")); - if (config.hasPath("dataSourceConfig.fetchSize")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.fetchSize")) { sourceConfig.setFetchSize(config.getInt("dataSourceConfig.fetchSize")); } - if (config.hasPath("dataSourceConfig.transactionZKRoot")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.transactionZKRoot")) { sourceConfig.setTransactionZKRoot(config.getString("dataSourceConfig.transactionZKRoot")); } - if (config.hasPath("dataSourceConfig.consumerGroupId")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.consumerGroupId")) { sourceConfig.setConsumerGroupId(config.getString("dataSourceConfig.consumerGroupId")); } - if (config.hasPath("dataSourceConfig.brokerZkPath")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.brokerZkPath")) { sourceConfig.setBrokerZkPath(config.getString("dataSourceConfig.brokerZkPath")); } - if (config.hasPath("dataSourceConfig.txZkServers")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.txZkServers")) { sourceConfig.setTransactionZkServers(config.getString("dataSourceConfig.txZkServers")); } - if (config.hasPath("dataSourceConfig.transactionStateUpdateMS")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.transactionStateUpdateMS")) { sourceConfig.setTransactionStateUpdateMS(config.getLong("dataSourceConfig.transactionStateUpdateMS")); } - if (config.hasPath("dataSourceConfig.startOffsetTime")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.startOffsetTime")) { sourceConfig.setStartOffsetTime(config.getInt("dataSourceConfig.startOffsetTime")); } - if (config.hasPath("dataSourceConfig.forceFromStart")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.forceFromStart")) { sourceConfig.setForceFromStart(config.getBoolean("dataSourceConfig.forceFromStart")); } - if (config.hasPath("dataSourceConfig.schemeCls")) { + if (hasNonBlankConfigPath(config, "dataSourceConfig.schemeCls")) { try { sourceConfig.setSchemaClass((Class<? extends Scheme>) Class.forName(config.getString("dataSourceConfig.schemeCls"))); } catch (ClassNotFoundException e) { http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSource.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSource.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSource.java index 9edf644..5cc5145 100644 --- a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSource.java +++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSource.java @@ -22,6 +22,7 @@ import backtype.storm.spout.SpoutOutputCollector; import backtype.storm.task.TopologyContext; import backtype.storm.topology.OutputFieldsDeclarer; import com.google.common.base.Preconditions; +import org.apache.commons.lang3.StringUtils; import org.apache.eagle.alert.engine.spout.SchemeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -115,7 +116,7 @@ public class KafkaStreamSource extends StormStreamSource<KafkaStreamSourceConfig String brokerZkPath = config.getBrokerZkPath(); BrokerHosts hosts; - if (brokerZkPath == null) { + if (StringUtils.isNotBlank(brokerZkPath)) { hosts = new ZkHosts(zkConnString); } else { hosts = new ZkHosts(zkConnString, brokerZkPath); @@ -127,9 +128,9 @@ public class KafkaStreamSource extends StormStreamSource<KafkaStreamSourceConfig groupId); // transaction zkServers to store kafka consumer offset. Default to use storm zookeeper - if (config.getTransactionZkServers() != null) { + if (StringUtils.isNotBlank(config.getTransactionZkServers())) { String[] txZkServers = config.getTransactionZkServers().split(","); - spoutConfig.zkServers = Arrays.asList(txZkServers).stream().map(server -> server.split(":")[0]).collect(Collectors.toList()); + spoutConfig.zkServers = Arrays.stream(txZkServers).map(server -> server.split(":")[0]).collect(Collectors.toList()); spoutConfig.zkPort = Integer.parseInt(txZkServers[0].split(":")[1]); LOG.info("txZkServers:" + spoutConfig.zkServers + ", zkPort:" + spoutConfig.zkPort); } http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSourceConfig.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSourceConfig.java b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSourceConfig.java index 5a9c162..e6fdb83 100644 --- a/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSourceConfig.java +++ b/eagle-core/eagle-app/eagle-app-base/src/main/java/org/apache/eagle/app/messaging/KafkaStreamSourceConfig.java @@ -19,8 +19,6 @@ package org.apache.eagle.app.messaging; import org.apache.eagle.metadata.model.StreamSourceConfig; public class KafkaStreamSourceConfig implements StreamSourceConfig { - - private static final String DEFAULT_CONFIG_PREFIX = "dataSourceConfig"; private static final String DEFAULT_CONSUMER_GROUP_ID = "eagleKafkaSource"; private static final String DEFAULT_TRANSACTION_ZK_ROOT = "/consumers"; private static final Class<? extends backtype.storm.spout.Scheme> DEFAULT_KAFKA_SCHEMA = JsonSchema.class; @@ -34,7 +32,7 @@ public class KafkaStreamSourceConfig implements StreamSourceConfig { private int fetchSize = 1048576; private String transactionZKRoot = DEFAULT_TRANSACTION_ZK_ROOT; private String consumerGroupId = DEFAULT_CONSUMER_GROUP_ID; - private String brokerZkPath = null; + private String brokerZkPath = "/brokers"; private long transactionStateUpdateMS = 2000; private int startOffsetTime = -1; private boolean forceFromStart = false; http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/AlertEntity.java ---------------------------------------------------------------------- diff --git a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/AlertEntity.java b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/AlertEntity.java index fa85496..1a09a2d 100644 --- a/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/AlertEntity.java +++ b/eagle-core/eagle-metadata/eagle-metadata-base/src/main/java/org/apache/eagle/metadata/model/AlertEntity.java @@ -42,6 +42,10 @@ public class AlertEntity extends TaggedLogAPIEntity { private String policyValue; @Column("c") private Map<String, Object> alertData; + @Column("d") + private String alertSubject; + @Column("e") + private String alertBody; public List<String> getAppIds() { return appIds; @@ -70,4 +74,21 @@ public class AlertEntity extends TaggedLogAPIEntity { valueChanged("alertData"); } + public String getAlertBody() { + return alertBody; + } + + public void setAlertBody(String alertBody) { + this.alertBody = alertBody; + valueChanged("alertBody"); + } + + public String getAlertSubject() { + return alertSubject; + } + + public void setAlertSubject(String alertSubject) { + this.alertSubject = alertSubject; + valueChanged("alertSubject"); + } } http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/1c81c086/eagle-hadoop-metric/src/main/resources/META-INF/providers/org.apache.eagle.metric.HadoopMetricMonitorAppProdiver.xml ---------------------------------------------------------------------- diff --git a/eagle-hadoop-metric/src/main/resources/META-INF/providers/org.apache.eagle.metric.HadoopMetricMonitorAppProdiver.xml b/eagle-hadoop-metric/src/main/resources/META-INF/providers/org.apache.eagle.metric.HadoopMetricMonitorAppProdiver.xml index 07270a5..87b57d6 100644 --- a/eagle-hadoop-metric/src/main/resources/META-INF/providers/org.apache.eagle.metric.HadoopMetricMonitorAppProdiver.xml +++ b/eagle-hadoop-metric/src/main/resources/META-INF/providers/org.apache.eagle.metric.HadoopMetricMonitorAppProdiver.xml @@ -25,7 +25,7 @@ <property> <name>dataSinkConfig.HADOOP_JMX_METRIC_STREAM.topic</name> <displayName>JMX Metric Kafka Topic</displayName> - <value>hadoop_jmx_metric_{SITE_ID}</value> + <value>hadoop_jmx_metric</value> <description>Hadoop JMX metric kafka topic name for stream: HADOOP_JMX_METRIC_STREAM</description> <required>true</required> </property> @@ -36,6 +36,54 @@ <description>Sourced Kafka Brokers like broker1,broker2,...</description> <required>true</required> </property> + <property> + <name>dataSourceConfig.zkConnection</name> + <displayName>Zookeeper Quorum</displayName> + <value>localhost:2181</value> + <description>Kafka Zookeeper Quorum</description> + <required>true</required> + </property> + <property> + <name>dataSourceConfig.fetchSize</name> + <displayName>Kafka Fetch Size</displayName> + <value>1048576</value> + <description>Kafka Fetch Size</description> + <required>false</required> + </property> + <property> + <name>dataSourceConfig.transactionZKRoot</name> + <displayName>Kafka Transaction Root Path</displayName> + <value>/consumers</value> + <description>Kafka Transaction Root Path</description> + <required>false</required> + </property> + <property> + <name>dataSourceConfig.consumerGroupId</name> + <displayName>Kafka Consumer Group ID</displayName> + <value>eagleConsumer</value> + <description>Kafka Consumer Group ID</description> + <required>false</required> + </property> + <property> + <name>dataSourceConfig.brokerZkPath</name> + <displayName>Kafka Broker ZkPath</displayName> + <description>Kafka Broker ZkPath</description> + <required>false</required> + </property> + <property> + <name>dataSourceConfig.txZkServers</name> + <displayName>Kafka Transaction Zookeeper Servers</displayName> + <description>Kafka Transaction Zookeeper Servers</description> + <value>localhost:2181</value> + <required>true</required> + </property> + <property> + <name>dataSourceConfig.transactionStateUpdateMS</name> + <value>2000</value> + <displayName>Kafka Transaction Status Update MS</displayName> + <description>Kafka Transaction Status Update MS</description> + <required>false</required> + </property> </configuration> <streams> <stream>