1
# Copyright (C) 2004,2005 PreludeIDS Technologies. All Rights Reserved.
2
# Author: Nicolas Delon <nicolas.delon@prelude-ids.com>
4
# This file is part of the Prewikka program.
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2, or (at your option)
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; see the file COPYING. If not, write to
18
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
from prewikka import view, User, utils
26
class SensorListingParameters(view.Parameters):
28
self.optional("filter_path", str)
29
self.optional("filter_value", str)
33
class HeartbeatAnalyzeParameters(view.Parameters):
35
self.mandatory("analyzerid", long)
39
class SensorMessagesDelete(SensorListingParameters):
41
SensorListingParameters.register(self)
42
self.optional("analyzerid", list, default=[])
43
self.optional("alerts", str, default=None)
44
self.optional("heartbeats", str, default=None)
48
def get_analyzer_status_from_latest_heartbeat(heartbeat_status, heartbeat_time,
49
heartbeat_interval, error_margin):
50
if heartbeat_status == "exiting":
51
return "offline", _("Offline")
53
if heartbeat_interval is None:
54
return "unknown", _("Unknown")
56
if time.time() - int(heartbeat_time) > int(heartbeat_interval) + error_margin:
57
return "missing", _("Missing")
59
return "online", _("Online")
62
def analyzer_cmp(x, y):
63
xmiss = x["status"] == "missing"
64
ymiss = y["status"] == "missing"
67
return cmp(x["name"], y["name"])
73
return cmp(x["name"], y["name"])
82
return cmp(x["node_name"], y["node_name"])
85
def getDominantStatus(d):
86
if d["missing"] > 0 or d["unknown"] > 0:
92
class SensorListing(view.View):
93
view_name = "sensor_listing"
94
view_parameters = SensorListingParameters
95
view_permissions = [ User.PERM_IDMEF_VIEW ]
96
view_template = "SensorListing"
99
self._heartbeat_count = int(env.config.general.getOptionValue("heartbeat_count", 30))
100
self._heartbeat_error_margin = int(env.config.general.getOptionValue("heartbeat_error_margin", 3))
102
def _analyzerPathStr(self, path):
118
if self.parameters.has_key("filter_path"):
119
criteria = "%s == '%s'" % (self.parameters["filter_path"],
120
utils.escape_criteria(self.parameters["filter_value"]))
125
for analyzerid in self.env.idmef_db.getAnalyzerids():
126
analyzer = self.env.idmef_db.getAnalyzer(analyzerid)
128
if analyzerid != analyzer["analyzerid"]:
131
analyzer["path"] = self._analyzerPathStr(analyzer["path"])
133
parameters = { "analyzerid": analyzer["analyzerid"] }
134
analyzer["alert_listing"] = utils.create_link("sensor_alert_listing", parameters)
135
analyzer["heartbeat_listing"] = utils.create_link("sensor_heartbeat_listing", parameters)
136
analyzer["heartbeat_analyze"] = utils.create_link("heartbeat_analyze", parameters)
138
if analyzer["node_name"]:
139
analyzer["node_name_link"] = utils.create_link(self.view_name,
140
{ "filter_path": "heartbeat.analyzer(-1).node.name",
141
"filter_value": analyzer["node_name"] })
143
if analyzer["node_location"]:
144
analyzer["node_location_link"] = utils.create_link(self.view_name,
145
{ "filter_path": "heartbeat.analyzer(-1).node.location",
146
"filter_value": analyzer["node_location"] })
149
for i in range(len(analyzer["node_addresses"])):
150
addr = analyzer["node_addresses"][i]
153
analyzer["node_addresses"][i] = {}
154
analyzer["node_addresses"][i]["value"] = addr
155
analyzer["node_addresses"][i]["inline_filter"] = utils.create_link(self.view_name,
156
{ "filter_path": "heartbeat.analyzer(-1).node.address.address",
157
"filter_value": addr })
159
analyzer["node_addresses"][i]["host_commands"] = []
160
for command in self.env.host_commands.keys():
161
analyzer["node_addresses"][i]["host_commands"].append((command.capitalize(),
162
utils.create_link("Command",
163
{ "origin": self.view_name,
164
"command": command, "host": addr })))
166
analyzer["status"], analyzer["status_meaning"] = \
167
get_analyzer_status_from_latest_heartbeat(analyzer["last_heartbeat_status"],
168
analyzer["last_heartbeat_time"],
169
analyzer["last_heartbeat_interval"],
170
self._heartbeat_error_margin)
172
analyzer["last_heartbeat_time"] = utils.time_to_ymdhms(time.localtime(int(analyzer["last_heartbeat_time"]))) + \
173
" %+.2d:%.2d" % utils.get_gmt_offset()
175
node_location = analyzer["node_location"] or _("Node location n/a")
176
node_name = analyzer.get("node_name") or _("Node name n/a")
177
osversion = analyzer["osversion"] or _("OS version n/a")
178
ostype = analyzer["ostype"] or _("OS type n/a")
179
addresses = analyzer["node_addresses"]
181
node_key = node_name + osversion + ostype
183
if not locations.has_key(node_location):
184
locations[node_location] = { "total": 1, "missing": 0, "unknown": 0, "offline": 0, "online": 0, "nodes": { } }
186
locations[node_location]["total"] += 1
188
if not locations[node_location]["nodes"].has_key(node_key):
189
locations[node_location]["nodes"][node_key] = { "total": 1, "missing": 0, "unknown": 0, "offline": 0, "online": 0,
191
"node_name": node_name, "node_location": node_location,
192
"ostype": ostype, "osversion": osversion,
193
"node_addresses": addresses, "node_key": node_key }
195
locations[node_location]["nodes"][node_key]["total"] += 1
197
status = analyzer["status"]
198
locations[node_location][status] += 1
199
locations[node_location]["nodes"][node_key][status] += 1
201
if status == "missing" or status == "unknown":
202
locations[node_location]["nodes"][node_key]["analyzers"].insert(0, analyzer)
204
locations[node_location]["nodes"][node_key]["analyzers"].append(analyzer)
206
self.dataset["locations"] = locations
209
class SensorMessagesDelete(SensorListing):
210
view_name = "sensor_messages_delete"
211
view_parameters = SensorMessagesDelete
212
view_permissions = [ User.PERM_IDMEF_VIEW, User.PERM_IDMEF_ALTER ]
214
def _addToCriteria(self, out, typestr, id, idx):
219
out += "! %s.analyzer(%d).analyzerid" % (typestr, idx)
221
out += "%s.analyzer(%d).analyzerid = '%s'" % (typestr, idx, utils.escape_criteria(id))
225
def _genCriteria(self, typestr, analyzerid):
229
l = analyzerid.split(":")
231
for id in l[:len(l) -1 ]:
232
criteria = self._addToCriteria(criteria, typestr, id, idx)
235
if typestr == "heartbeat":
238
return self._addToCriteria(criteria, typestr, l[-1], idx)
241
for analyzerid in self.parameters["analyzerid"]:
242
print "STAT ", analyzerid
244
if self.parameters.has_key("alerts"):
245
print self._genCriteria("alert", analyzerid)
246
print "NEWA=", len(self.env.idmef_db.getAlertIdents(self._genCriteria("alert", analyzerid)))
247
print "OLDA=", len(self.env.idmef_db.getAlertIdents("alert.analyzer.analyzerid == '%s'" % utils.escape_criteria(analyzerid.split(":")[-1])))
249
if self.parameters.has_key("heartbeats"):
251
print "NEWH=", len(self.env.idmef_db.getHeartbeatIdents(self._genCriteria("heartbeat", analyzerid)))
252
print "OLDH=", len(self.env.idmef_db.getHeartbeatIdents("heartbeat.analyzer(-1).analyzerid == '%s'" % utils.escape_criteria(analyzerid.split(":")[-1])))
254
#self.env.idmef_db.deleteHeartbeat(self.env.idmef_db.getHeartbeatIdents(criteria))
256
SensorListing.render(self)
260
class HeartbeatAnalyze(view.View):
261
view_name = "heartbeat_analyze"
262
view_parameters = HeartbeatAnalyzeParameters
263
view_permissions = [ User.PERM_IDMEF_VIEW ]
264
view_template = "HeartbeatAnalyze"
267
self._heartbeat_count = int(env.config.general.getOptionValue("heartbeat_count", 30))
268
self._heartbeat_error_margin = int(env.config.general.getOptionValue("heartbeat_error_margin", 3))
271
analyzerid = self.parameters["analyzerid"]
273
analyzer = self.env.idmef_db.getAnalyzer(analyzerid)
274
analyzer["last_heartbeat_time"] = str(analyzer["last_heartbeat_time"])
275
analyzer["events"] = [ ]
276
analyzer["status"] = "abnormal_offline"
277
analyzer["status_meaning"] = "abnormal offline"
280
idents = self.env.idmef_db.getHeartbeatIdents(criteria="heartbeat.analyzer(-1).analyzerid == %d" % analyzerid,
281
limit=self._heartbeat_count)
287
older = self.env.idmef_db.getHeartbeat(ident)
288
older_status = older.getAdditionalData("Analyzer status")
289
older_interval = older["heartbeat.heartbeat_interval"]
290
if not older_status or not older_interval:
292
older_time = older["heartbeat.create_time"]
293
total_interval += int(older_interval)
297
analyzer["status"], analyzer["status_meaning"] = \
298
get_analyzer_status_from_latest_heartbeat(older_status, older_time, older_interval,
299
self._heartbeat_error_margin)
300
if analyzer["status"] == "abnormal_offline":
301
analyzer["events"].append({ "value": "sensor is down since %s" % older_time, "type": "down"})
305
if newer_status == "starting":
306
if older_status == "exiting":
307
event = { "value": "normal sensor start at %s" % str(newer_time),
310
event = { "value": "unexpected sensor restart at %s" % str(newer_time),
311
"type": "unexpected_restart" }
313
if newer_status == "running":
314
if abs(int(newer_time) - int(older_time) - int(older_interval)) > self._heartbeat_error_margin:
315
event = { "value": "abnormal heartbeat interval between %s and %s" % (str(older_time), str(newer_time)),
316
"type": "abnormal_heartbeat_interval" }
319
if newer_status == "exiting":
320
event = { "value": "normal sensor stop at %s" % str(newer_time),
321
"type": "normal_stop" }
324
analyzer["events"].append(event)
327
newer_status = older_status
328
newer_interval = older_interval
329
newer_time = older_time
331
if not analyzer["events"]:
332
analyzer["events"].append({ "value":
333
"No anomaly in the last %d heartbeats (1 heartbeat every %d s average)" %
334
(self._heartbeat_count, total_interval / self._heartbeat_count),
335
"type": "no_anomaly" })
337
self.dataset["analyzer"] = analyzer