CLOUDSTACK-712: Feature Syslog Enhancements Signed-off-by: Anshul Gangwar <anshul.gang...@citrix.com> Signed-off-by: Sateesh Chodapuneedi <sate...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6a7156ad Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6a7156ad Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6a7156ad Branch: refs/heads/bvt Commit: 6a7156ad9af6ca173c0b685d69d5724d91d0a401 Parents: 2c176ab Author: Anshul Gangwar <anshul.gang...@citrix.com> Authored: Wed Mar 6 14:11:47 2013 +0530 Committer: Sateesh Chodapuneedi <sate...@apache.org> Committed: Fri Mar 29 09:53:05 2013 +0530 ---------------------------------------------------------------------- client/pom.xml | 5 + client/tomcatconf/log4j-cloud.xml.in | 15 + plugins/alert-handlers/syslog-alerts/pom.xml | 40 ++ .../cloudstack/syslog/AlertsSyslogAppender.java | 336 +++++++++++++++ .../syslog/AlertsSyslogAppenderTest.java | 61 +++ plugins/pom.xml | 1 + 6 files changed, 458 insertions(+), 0 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/client/pom.xml ---------------------------------------------------------------------- diff --git a/client/pom.xml b/client/pom.xml index 23892e1..61cda76 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -67,6 +67,11 @@ </dependency> <dependency> <groupId>org.apache.cloudstack</groupId> + <artifactId>cloud-plugin-syslog-alerts</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cloudstack</groupId> <artifactId>cloud-plugin-snmp-alerts</artifactId> <version>${project.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/client/tomcatconf/log4j-cloud.xml.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/log4j-cloud.xml.in b/client/tomcatconf/log4j-cloud.xml.in index ce4079f..0e7f004 100755 --- a/client/tomcatconf/log4j-cloud.xml.in +++ b/client/tomcatconf/log4j-cloud.xml.in @@ -74,6 +74,20 @@ under the License. <param name="ConversionPattern" value="%-5p [%c{3}] (%t:%x) %m%n"/> </layout> </appender> + + <!-- ============================== --> + <!-- Append alerts to the syslog if it is configured --> + <!-- ============================== --> + + <appender name="ALERTSYSLOG" class="org.apache.cloudstack.syslog.AlertsSyslogAppender"> + <param name="Threshold" value="WARN"/> + <param name="SyslogHosts" value=""/> + <param name="Facility" value="LOCAL6"/> + <layout class="org.apache.log4j.PatternLayout"> + <param name="ConversionPattern" value="%-5p [%c{3}] (%t:%x) %m%n"/> + </layout> + </appender> + <!-- ============================== --> <!-- send alert warnings+ as the SNMP trap if it is configured! --> <!-- ============================== --> @@ -166,6 +180,7 @@ under the License. <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <appender-ref ref="SNMP"/> + <appender-ref ref="ALERTSYSLOG"/> </logger> <!-- ======================= --> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/plugins/alert-handlers/syslog-alerts/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/alert-handlers/syslog-alerts/pom.xml b/plugins/alert-handlers/syslog-alerts/pom.xml new file mode 100644 index 0000000..21aa54a --- /dev/null +++ b/plugins/alert-handlers/syslog-alerts/pom.xml @@ -0,0 +1,40 @@ +<!-- + ~ 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 + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>cloudstack-plugins</artifactId> + <groupId>org.apache.cloudstack</groupId> + <version>4.2.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + <modelVersion>4.0.0</modelVersion> + <name>Apache CloudStack Plugin - Syslog Alerts</name> + <artifactId>cloud-plugin-syslog-alerts</artifactId> + + <dependencies> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>${cs.log4j.version}</version> + </dependency> + </dependencies> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/plugins/alert-handlers/syslog-alerts/src/org/apache/cloudstack/syslog/AlertsSyslogAppender.java ---------------------------------------------------------------------- diff --git a/plugins/alert-handlers/syslog-alerts/src/org/apache/cloudstack/syslog/AlertsSyslogAppender.java b/plugins/alert-handlers/syslog-alerts/src/org/apache/cloudstack/syslog/AlertsSyslogAppender.java new file mode 100644 index 0000000..d2f2565 --- /dev/null +++ b/plugins/alert-handlers/syslog-alerts/src/org/apache/cloudstack/syslog/AlertsSyslogAppender.java @@ -0,0 +1,336 @@ +// 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 + +package org.apache.cloudstack.syslog; + +import com.cloud.utils.net.NetUtils; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.net.SyslogAppender; +import org.apache.log4j.spi.LoggingEvent; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +public class AlertsSyslogAppender extends AppenderSkeleton { + String _syslogHosts = null; + String _delimiter = ","; + List<String> _syslogHostsList = null; + List<SyslogAppender> _syslogAppenders = null; + private String _facility; + private String _pairDelimiter = "//"; + private String _keyValueDelimiter = "::"; + private int alertType = -1; + private long dataCenterId = 0; + private long podId = 0; + private long clusterId = 0; + private String sysMessage = null; + public static final int LENGTH_OF_STRING_MESSAGE_AND_KEY_VALUE_DELIMITER = 9; + public static final int LENGTH_OF_STRING_MESSAGE = 8; + public static final String MESSAGE_DELIMITER_STRING = " "; + //add the alertType in this array it its level needs to be set to critical + private static final int[] criticalAlerts = {7, 8, 9, 10, 11, 12, 13, 15, 16, 19, 20}; + private static final Map<Integer, String> alertsMap; + + static { + Map<Integer, String> aMap = new HashMap<Integer, String>(27); + aMap.put(0, "availableMemory"); + aMap.put(1, "availableCpu"); + aMap.put(2, "availableStorage"); + aMap.put(3, "remainingStorageAllocated"); + aMap.put(4, "unallocatedVirtualNetworkpublicIp"); + aMap.put(5, "unallocatedPrivateIp"); + aMap.put(6, "availableSecondaryStorage"); + aMap.put(7, "host"); + aMap.put(8, "userVmState"); + aMap.put(9, "domainRouterVmState "); + aMap.put(10, "consoleProxyVmState"); + aMap.put(11, "routingConnection"); + aMap.put(12, "storageIssueSystemVms"); + aMap.put(13, "usageServerStatus"); + aMap.put(14, "managementNode"); + aMap.put(15, "domainRouterMigrate"); + aMap.put(16, "consoleProxyMigrate"); + aMap.put(17, "userVmMigrate"); + aMap.put(18, "unallocatedVlan"); + aMap.put(19, "ssvmStopped"); + aMap.put(20, "usageServerResult"); + aMap.put(21, "storageDelete"); + aMap.put(22, "updateResourceCount"); + aMap.put(23, "usageSanityResult"); + aMap.put(24, "unallocatedDirectAttachedPublicIp"); + aMap.put(25, "unallocatedLocalStorage"); + aMap.put(26, "resourceLimitExceeded"); + + alertsMap = Collections.unmodifiableMap(aMap); + } + + @Override + protected void append(LoggingEvent event) { + if (!isAsSevereAsThreshold(event.getLevel())) { + return; + } + + if (_syslogAppenders != null && !_syslogAppenders.isEmpty()) { + try { + String logMessage = event.getRenderedMessage(); + if (logMessage.contains("alertType") && logMessage.contains("message")) { + parseMessage(logMessage); + String syslogMessage = createSyslogMessage(); + + LoggingEvent syslogEvent = new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), + event.getLevel(), syslogMessage, null); + + for (SyslogAppender syslogAppender : _syslogAppenders) { + syslogAppender.append(syslogEvent); + } + } + } catch (Exception e) { + errorHandler.error(e.getMessage()); + } + } + } + + @Override + synchronized public void close() { + for (SyslogAppender syslogAppender : _syslogAppenders) { + syslogAppender.close(); + } + } + + @Override + public boolean requiresLayout() { + return true; + } + + void setSyslogAppenders() { + if (_syslogAppenders == null) { + _syslogAppenders = new ArrayList<SyslogAppender>(); + } + + if (_syslogHosts == null || _syslogHosts.trim().isEmpty()) { + reset(); + return; + } + + _syslogHostsList = parseSyslogHosts(_syslogHosts); + + if (!validateIpAddresses()) { + reset(); + errorHandler.error(" Invalid format for the IP Addresses parameter "); + return; + } + + for (String syslogHost : _syslogHostsList) { + _syslogAppenders.add(new SyslogAppender(getLayout(), syslogHost, SyslogAppender.getFacility(_facility))); + } + } + + private List<String> parseSyslogHosts(String syslogHosts) { + List<String> result = new ArrayList<String>(); + + final StringTokenizer tokenizer = new StringTokenizer(syslogHosts, _delimiter); + while (tokenizer.hasMoreTokens()) { + result.add(tokenizer.nextToken().trim()); + } + return result; + } + + private boolean validateIpAddresses() { + for (String ipAddress : _syslogHostsList) { + if (ipAddress.trim().equalsIgnoreCase("localhost")) { + continue; + } + if (!NetUtils.isValidIp(ipAddress)) { + return false; + } + } + return true; + } + + void parseMessage(String logMessage) { + final StringTokenizer messageSplitter = new StringTokenizer(logMessage, _pairDelimiter); + while (messageSplitter.hasMoreTokens()) { + final String pairToken = messageSplitter.nextToken(); + final StringTokenizer pairSplitter = new StringTokenizer(pairToken, _keyValueDelimiter); + String keyToken; + String valueToken; + + if (pairSplitter.hasMoreTokens()) { + keyToken = pairSplitter.nextToken().trim(); + } else { + break; + } + + if (pairSplitter.hasMoreTokens()) { + valueToken = pairSplitter.nextToken().trim(); + } else { + break; + } + + if (keyToken.equalsIgnoreCase("alertType") && !valueToken.equalsIgnoreCase("null")) { + alertType = Short.parseShort(valueToken); + } else if (keyToken.equalsIgnoreCase("dataCenterId") && !valueToken.equalsIgnoreCase("null")) { + dataCenterId = Long.parseLong(valueToken); + } else if (keyToken.equalsIgnoreCase("podId") && !valueToken.equalsIgnoreCase("null")) { + podId = Long.parseLong(valueToken); + } else if (keyToken.equalsIgnoreCase("clusterId") && !valueToken.equalsIgnoreCase("null")) { + clusterId = Long.parseLong(valueToken); + } else if (keyToken.equalsIgnoreCase("message") && !valueToken.equalsIgnoreCase("null")) { + sysMessage = getSyslogMessage(logMessage); + } + } + } + + String createSyslogMessage() { + StringBuilder message = new StringBuilder(); + message.append(severityOfAlert(alertType)).append(MESSAGE_DELIMITER_STRING); + InetAddress ip; + try { + ip = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + ip = null; + } + + if (ip != null) { + message.append(ip.getHostName()).append(MESSAGE_DELIMITER_STRING); + } else { + message.append("unknown" + MESSAGE_DELIMITER_STRING); + } + + if (alertType > 0) { + message.append("alertType").append(_keyValueDelimiter).append(" ").append(alertsMap.get(alertType)) + .append(MESSAGE_DELIMITER_STRING); + if (dataCenterId != 0) { + message.append("dataCenterId").append(_keyValueDelimiter).append(" ").append(dataCenterId) + .append(MESSAGE_DELIMITER_STRING); + } + + if (podId != 0) { + message.append("podId").append(_keyValueDelimiter).append(" ").append(podId) + .append(MESSAGE_DELIMITER_STRING); + } + + if (clusterId != 0) { + message.append("clusterId").append(_keyValueDelimiter).append(" ").append(clusterId) + .append(MESSAGE_DELIMITER_STRING); + } + + if (sysMessage != null) { + message.append("message").append(_keyValueDelimiter).append(" ").append(sysMessage); + } else { + errorHandler.error(" What is the use of alert without message "); + } + } else { + errorHandler.error(" Invalid alert Type "); + } + + return message.toString(); + } + + private String getSyslogMessage(String message) { + int lastIndexOfKeyValueDelimiter = message.lastIndexOf(_keyValueDelimiter); + int lastIndexOfMessageInString = message.lastIndexOf("message"); + + if (lastIndexOfKeyValueDelimiter - lastIndexOfMessageInString <= + LENGTH_OF_STRING_MESSAGE_AND_KEY_VALUE_DELIMITER) { + return message.substring(lastIndexOfKeyValueDelimiter + _keyValueDelimiter.length()).trim(); + } else if (lastIndexOfMessageInString < lastIndexOfKeyValueDelimiter) { + return message.substring( + lastIndexOfMessageInString + _keyValueDelimiter.length() + LENGTH_OF_STRING_MESSAGE).trim(); + } + + return message.substring(message.lastIndexOf("message" + _keyValueDelimiter) + + LENGTH_OF_STRING_MESSAGE_AND_KEY_VALUE_DELIMITER).trim(); + } + + private void reset() { + _syslogAppenders.clear(); + } + + public void setFacility(String facility) { + if (facility == null) { + return; + } + + this._facility = facility; + if (_syslogAppenders != null && !_syslogAppenders.isEmpty()) { + for (SyslogAppender syslogAppender : _syslogAppenders) { + syslogAppender.setFacility(facility); + } + } + } + + private String severityOfAlert(int alertType) { + if (isCritical(alertType)) { + return "CRITICAL"; + } else { + return "WARN"; + } + } + + private boolean isCritical(int alertType) { + for (int type : criticalAlerts) { + if (type == alertType) { + return true; + } + } + return false; + } + + public String getFacility() { + return _facility; + } + + public String getSyslogHosts() { + return _syslogHosts; + } + + public void setSyslogHosts(String syslogHosts) { + this._syslogHosts = syslogHosts; + this.setSyslogAppenders(); + } + + public String getDelimiter() { + return _delimiter; + } + + public void setDelimiter(String delimiter) { + this._delimiter = delimiter; + } + + public String getPairDelimiter() { + return _pairDelimiter; + } + + public void setPairDelimiter(String pairDelimiter) { + this._pairDelimiter = pairDelimiter; + } + + public String getKeyValueDelimiter() { + return _keyValueDelimiter; + } + + public void setKeyValueDelimiter(String keyValueDelimiter) { + this._keyValueDelimiter = keyValueDelimiter; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/plugins/alert-handlers/syslog-alerts/test/org/apache/cloudstack/syslog/AlertsSyslogAppenderTest.java ---------------------------------------------------------------------- diff --git a/plugins/alert-handlers/syslog-alerts/test/org/apache/cloudstack/syslog/AlertsSyslogAppenderTest.java b/plugins/alert-handlers/syslog-alerts/test/org/apache/cloudstack/syslog/AlertsSyslogAppenderTest.java new file mode 100644 index 0000000..68585ee --- /dev/null +++ b/plugins/alert-handlers/syslog-alerts/test/org/apache/cloudstack/syslog/AlertsSyslogAppenderTest.java @@ -0,0 +1,61 @@ +// 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 + +package org.apache.cloudstack.syslog; + +import org.apache.log4j.PatternLayout; +import org.junit.Before; +import org.junit.Test; + +import javax.naming.ConfigurationException; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +public class AlertsSyslogAppenderTest { + AlertsSyslogAppender _appender = new AlertsSyslogAppender(); + + @Before + public void setUp() throws ConfigurationException { + _appender.setLayout(new PatternLayout("%-5p [%c{3}] (%t:%x) %m%n")); + _appender.setFacility("LOCAL6"); + } + + @Test + public void setSyslogAppendersTest() { + _appender.setSyslogHosts("10.1.1.1,10.1.1.2"); + assertEquals(" error Syslog Appenders list size not as expected ", 2, _appender._syslogAppenders.size()); + } + + @Test + public void setSyslogAppendersNegativeTest() { + //setting invalid IP for Syslog Hosts + _appender.setSyslogHosts("10.1.1."); + assertTrue(" list was expected to be empty", _appender._syslogAppenders.isEmpty()); + } + + @Test + public void appendTest() { + String message = "alertType:: 14 // dataCenterId:: 0 // podId:: 0 // clusterId:: null // message:: Management" + + " server node 127.0.0.1 is up"; + _appender.parseMessage(message); + String createdMessage = _appender.createSyslogMessage(); + assertTrue(" message is not as expected ", createdMessage.contains("alertType:: managementNode" + + AlertsSyslogAppender.MESSAGE_DELIMITER_STRING + "message:: Management server node 127.0.0.1 is up")); + assertTrue("severity level not as expected ", createdMessage.contains("WARN")); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6a7156ad/plugins/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/pom.xml b/plugins/pom.xml index 39d9907..d7e8deb 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -59,6 +59,7 @@ <module>storage/volume/solidfire</module> <module>storage/volume/default</module> <module>alert-handlers/snmp-alerts</module> + <module>alert-handlers/syslog-alerts</module> </modules> <dependencies>