1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__ = """SyslogProcessing
15 Class for turning syslog events into Zenoss Events
16 """
17
18 import re
19 import logging
20 slog = logging.getLogger("zen.Syslog")
21
22 import Globals
23 from Products.ZenEvents.syslog_h import *
24
25 import socket
26
27
28 parsers = (
29
30 r"^(?P<summary>-- (?P<eventClassKey>MARK) --)",
31
32
33 r"^(?P<component>.+)\[(?P<ntseverity>\D+)\] (?P<ntevid>\d+) (?P<summary>.*)",
34
35
36 r"%CARD-\S+:(SLOT\d+) %(?P<eventClassKey>\S+): (?P<summary>.*)",
37
38
39 r"%(?P<eventClassKey>(?P<component>\S+)-\d-\S+): (?P<summary>.*)",
40
41
42 r"^(?P<ipAddress>\S+)\s+(?P<summary>(?P<eventClassKey>CisACS_\d\d_\S+)\s+(?P<eventKey>\S+)\s.*)",
43
44
45 r"device_id=\S+\s+\[\S+\](?P<eventClassKey>\S+\d+):\s+(?P<summary>.*)\s+\((?P<originalTime>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)\)",
46
47
48 r"(?P<component>\S+)\[(?P<pid>\d+)\]:\s*(?P<summary>.*)",
49
50
51 r"(?P<component>\S+): (?P<summary>.*)",
52
53
54 r"^(?P<deviceModel>[^\[]+)\[(?P<deviceManufacturer>ADTRAN)\]:(?P<component>[^\|]+\|\d+\|\d+)\|(?P<summary>.*)",
55
56 r"^date=.+ (?P<summary>devname=.+ log_id=(?P<eventClassKey>\d+) type=(?P<component>\S+).+)",
57
58
59 r"^(?P<component>\S+)(\.|\s)[A-Z]{3} \d \S+ \d\d:\d\d:\d\d-\d\d:\d\d:\d\d \d{5} \d{2} \d{5} \S+ \d{4} \d{3,5} (- )*(?P<summary>.*) \d{4} \d{4}",
60
61
62 r"^Process (?P<process_id>\d+), Nbr (?P<device>\d+\.\d+\.\d+\.\d+) on (?P<interface>\w+/\d+) from (?P<start_state>\w+) to (?P<end_state>\w+), (?P<summary>.+)",
63 )
64
65
66 compiledParsers = []
67 for regex in parsers:
68 try:
69 compiled = re.compile(regex)
70 compiledParsers.append(compiled)
71 except:
72 pass
73
74
76 """
77 Class to process syslog messages and convert them into events viewable
78 in the Zenoss event console.
79 """
80
81 - def __init__(self,sendEvent,minpriority,parsehost,monitor,defaultPriority):
82 """
83 Initializer
84
85 @param sendEvent: message from a remote host
86 @type sendEvent: string
87 @param minpriority: ignore anything under this priority
88 @type minpriority: integer
89 @param parsehost: hostname where this parser is running
90 @type parsehost: string
91 @param monitor: name of the distributed collector monitor
92 @type monitor: string
93 @param defaultPriority: priority to use if it can't be understood from the received packet
94 @type defaultPriority: integer
95 """
96 self.minpriority = minpriority
97 self.parsehost = parsehost
98 self.sendEvent = sendEvent
99 self.monitor = monitor
100 self.defaultPriority = defaultPriority
101
102
103 - def process(self, msg, ipaddr, host, rtime):
104 """
105 Process an event from syslog and convert to a Zenoss event
106
107 @param msg: message from a remote host
108 @type msg: string
109 @param ipaddr: IP address of the remote host
110 @type ipaddr: string
111 @param host: remote host's name
112 @type host: string
113 @param rtime: time as reported by the remote host
114 @type rtime: string
115 """
116 evt = dict(device=host,
117 ipAddress=ipaddr,
118 firstTime=rtime,
119 lastTime=rtime,
120 eventGroup='syslog')
121 slog.debug("host=%s, ip=%s", host, ipaddr)
122 slog.debug(msg)
123
124 evt, msg = self.parsePRI(evt, msg)
125 if evt['priority'] > self.minpriority: return
126
127 evt, msg = self.parseHEADER(evt, msg)
128 evt = self.parseTag(evt, msg)
129
130 evt = self.buildEventClassKey(evt)
131 evt['monitor'] = self.monitor
132 self.sendEvent(evt)
133
134
136 """
137 Parse RFC-3164 PRI part of syslog message to get facility and priority.
138
139 @param evt: dictionary of event properties
140 @type evt: dictionary
141 @param msg: message from host
142 @type msg: string
143 @return: tuple of dictionary of event properties and the message
144 @type: (dictionary, string)
145 """
146 pri = self.defaultPriority
147 fac = None
148 if msg[:1] == '<':
149 pos = msg.find('>')
150 fac, pri = LOG_UNPACK(int(msg[1:pos]))
151 msg = msg[pos+1:]
152 elif msg and msg[0] < ' ':
153 fac, pri = LOG_KERN, ord(msg[0])
154 msg = msg[1:]
155 evt['facility'] = fac_names.get(fac,"unknown")
156 evt['priority'] = pri
157 evt['severity'] = self.defaultSeverityMap(pri)
158 slog.debug("fac=%s pri=%s", fac, pri)
159 slog.debug("facility=%s severity=%s", evt['facility'], evt['severity'])
160 return evt, msg
161
162
164 """
165 Default mapping from syslog priority to severity.
166
167 @param pri: syslog priority from host
168 @type pri: integer
169 @return: numeric severity
170 @type: integer
171 """
172 sev = 1
173 if pri < 3: sev = 5
174 elif pri == 3: sev = 4
175 elif pri == 4: sev = 3
176 elif pri == 5 or pri == 6: sev = 2
177 return sev
178
179
180 timeParse = \
181 re.compile("^(\S{3} [\d ]{2} [\d ]{2}:[\d ]{2}:[\d ]{2}) (.*)").search
182 notHostSearch = re.compile("[\[:]").search
184 """
185 Parse RFC-3164 HEADER part of syslog message. TIMESTAMP format is:
186 MMM HH:MM:SS and host is next token without the characters '[' or ':'.
187
188 @param evt: dictionary of event properties
189 @type evt: dictionary
190 @param msg: message from host
191 @type msg: string
192 @return: tuple of dictionary of event properties and the message
193 @type: (dictionary, string)
194 """
195 slog.debug(msg)
196 m = re.sub("Kiwi_Syslog_Daemon \d+: \d+: "
197 "\S{3} [\d ]{2} [\d ]{2}:[\d ]{2}:[^:]+: ", "", msg)
198 m = self.timeParse(msg)
199 if m:
200 slog.debug("parseHEADER timestamp=%s", m.group(1))
201 evt['originalTime'] = m.group(1)
202 msg = m.group(2).strip()
203 msglist = msg.split()
204 if self.parsehost and not self.notHostSearch(msglist[0]):
205 device = msglist[0]
206 if device.find('@') >= 0:
207 device = device.split('@', 1)[1]
208 slog.debug("parseHEADER hostname=%s", evt['device'])
209 msg = " ".join(msglist[1:])
210 evt['device'] = device
211 return evt, msg
212
213
215 """
216 Parse the RFC-3164 tag of the syslog message using the regex defined
217 at the top of this module.
218
219 @param evt: dictionary of event properties
220 @type evt: dictionary
221 @param msg: message from host
222 @type msg: string
223 @return: dictionary of event properties
224 @type: dictionary
225 """
226 slog.debug(msg)
227 for parser in compiledParsers:
228 slog.debug("tag regex: %s", parser.pattern)
229 m = parser.search(msg)
230 if not m: continue
231 slog.debug("tag match: %s", m.groupdict())
232 evt.update(m.groupdict())
233 break
234 else:
235 slog.info("No matching parser: '%s'", msg)
236 evt['summary'] = msg
237 return evt
238
239
241 """
242 Build the key used to find an events dictionary record. If eventClass
243 is defined it is used. For NT events "Source_Evid" is used. For other
244 syslog events we use the summary of the event to perform a full text
245 or'ed search.
246
247 @param evt: dictionary of event properties
248 @type evt: dictionary
249 @return: dictionary of event properties
250 @type: dictionary
251 """
252 if evt.has_key('eventClassKey') or evt.has_key( 'eventClass'):
253 return evt
254 elif evt.has_key( 'ntevid'):
255 evt['eventClassKey'] = "%s_%s" % (evt['component'],evt['ntevid'])
256 elif evt.has_key( 'component'):
257 evt['eventClassKey'] = evt['component']
258 if evt.has_key( 'eventClassKey'):
259 slog.debug("eventClassKey=%s", evt['eventClassKey'])
260 try:
261 evt['eventClassKey'] = evt['eventClassKey'].decode('latin-1')
262 except:
263 evt['eventClassKey'] = evt['eventClassKey'].decode('utf-8')
264 else:
265 slog.debug("No eventClassKey assigned")
266 return evt
267