~ubuntu-branches/ubuntu/wily/prewikka/wily

« back to all changes in this revision

Viewing changes to prewikka/views/stats.py

  • Committer: Bazaar Package Importer
  • Author(s): Pierre Chifflier
  • Date: 2010-03-18 17:34:33 UTC
  • mfrom: (1.1.11 upstream) (8.1.6 sid)
  • Revision ID: james.westby@ubuntu.com-20100318173433-2dyo1uo0r570y4i9
Tags: 1.0.0-1
New upstream release 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2009 PreludeIDS Technologies. All Rights Reserved.
 
2
# Author: Nicolas Delon <nicolas.delon@prelude-ids.com>
 
3
# Author: Yoann Vandoorselaere <yoann.v@prelude-ids.com>
 
4
#
 
5
# This file is part of the Prewikka program.
 
6
#
 
7
# This program is free software; you can redistribute it and/or modify
 
8
# it under the terms of the GNU General Public License as published by
 
9
# the Free Software Foundation; either version 2, or (at your option)
 
10
# any later version.
 
11
#
 
12
# This program is distributed in the hope that it will be useful,
 
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
# GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License
 
18
# along with this program; see the file COPYING.  If not, write to
 
19
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
20
 
 
21
import sys
 
22
import time
 
23
import copy
 
24
import urllib
 
25
import datetime
 
26
 
 
27
from prewikka import User, view, Chart, utils, resolve
 
28
 
 
29
try:
 
30
    import GeoIP
 
31
    geoip = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
 
32
except:
 
33
    geoip = None
 
34
 
 
35
 
 
36
DEFAULT_WIDTH = 800
 
37
DEFAULT_HEIGHT = 450
 
38
 
 
39
 
 
40
class DistributionStatsParameters(view.Parameters):
 
41
    def register(self):
 
42
        self.optional("timeline_type", str, default="hour", save=True)
 
43
        self.optional("from_year", int, save=True)
 
44
        self.optional("from_month", int, save=True)
 
45
        self.optional("from_day", int, save=True)
 
46
        self.optional("from_hour", int, save=True)
 
47
        self.optional("from_min", int, save=True)
 
48
        self.optional("to_year", int, save=True)
 
49
        self.optional("to_month", int, save=True)
 
50
        self.optional("to_day", int, save=True)
 
51
        self.optional("to_hour", int, save=True)
 
52
        self.optional("to_min", int, save=True)
 
53
        self.optional("filter", str, save=True)
 
54
        self.optional("idmef_filter", str)
 
55
        self.optional("apply", str)
 
56
 
 
57
    def normalize(self, view_name, user):
 
58
        do_save = self.has_key("_save")
 
59
 
 
60
        view.Parameters.normalize(self, view_name, user)
 
61
 
 
62
        if do_save and not self.has_key("filter"):
 
63
            user.delConfigValue(view_name, "filter")
 
64
 
 
65
 
 
66
class StatsSummary(view.View):
 
67
    view_name = "stats_summary"
 
68
    view_template = "StatsSummary"
 
69
    view_permission = [ ]
 
70
    view_parameters = view.Parameters
 
71
 
 
72
    def render(self):
 
73
        pass
 
74
 
 
75
 
 
76
 
 
77
 
 
78
class DistributionStats(view.View):
 
79
    view_template = "Stats"
 
80
    view_permissions = [ User.PERM_IDMEF_VIEW ]
 
81
    view_parameters = DistributionStatsParameters
 
82
 
 
83
    def _getNameFromMap(self, name, names_and_colors):
 
84
        if names_and_colors.has_key(name):
 
85
            return names_and_colors[name][0]
 
86
 
 
87
        return name
 
88
 
 
89
    def _namesAndColors2ColorMap(self, names_and_colors):
 
90
        d = utils.OrderedDict()
 
91
        for name, color in names_and_colors.values():
 
92
            d[name] = color
 
93
 
 
94
        return d
 
95
 
 
96
    def _getBaseURL(self):
 
97
        start = long(time.mktime(self._period_start))
 
98
 
 
99
        if self.parameters["timeline_type"] in ("month", "day", "hour"):
 
100
            unit = self.parameters["timeline_type"]
 
101
            value = 1
 
102
        else:
 
103
            delta = long(time.mktime(self._period_end)) - start
 
104
            if delta > 3600:
 
105
                unit = "day"
 
106
                value = delta / (24 * 3600) + 1
 
107
            else:
 
108
                unit = "hour"
 
109
                value = delta / 3600 + 1
 
110
 
 
111
        filter_str = ""
 
112
        if self.parameters.has_key("filter"):
 
113
            filter_str = "&amp;" + urllib.urlencode({"filter": self.parameters["filter"]})
 
114
 
 
115
        return utils.create_link("alert_listing", { "timeline_unit": unit,
 
116
                                                    "timeline_value": value,
 
117
                                                    "timeline_start": start }) + filter_str
 
118
 
 
119
 
 
120
    def _addDistributionChart(self, title, value_name, width, height, path, criteria, sub_url_handler, limit=-1, dns=False, names_and_colors={}):
 
121
        base_url = self._getBaseURL()
 
122
        chart = { "title": title, "value_name": value_name }
 
123
 
 
124
        distribution = Chart.DistributionChart(self.user, width, height)
 
125
        if names_and_colors:
 
126
            distribution.setColorMap(self._namesAndColors2ColorMap(names_and_colors))
 
127
 
 
128
        chart["chart"] = distribution
 
129
        chart["render"] = (distribution, title, base_url)
 
130
 
 
131
        results = self.env.idmef_db.getValues([ path + "/group_by", "count(%s)/order_desc" % path ],
 
132
                                              criteria=criteria + [ path ], limit=limit)
 
133
        if results:
 
134
            for value, count in results:
 
135
                if dns and value:
 
136
                    v = resolve.AddressResolve(value)
 
137
                else:
 
138
                    v = self._getNameFromMap(value or _(u"n/a"), names_and_colors)
 
139
 
 
140
                distribution.addLabelValuePair(v, count, base_url + "&amp;" + sub_url_handler(value))
 
141
 
 
142
        distribution.render(title)
 
143
        self.dataset["charts"].append(chart)
 
144
 
 
145
    def _processTimeCriteria(self):
 
146
        now = time.time()
 
147
        self._period_end = time.localtime(now)
 
148
 
 
149
        if self.parameters["timeline_type"] == "hour":
 
150
            self.dataset["timeline_hour_selected"] = "selected=\"selected\""
 
151
            self._period_start = time.localtime(now - 3600)
 
152
 
 
153
        elif self.parameters["timeline_type"] == "day":
 
154
            self.dataset["timeline_day_selected"] = "selected=\"selected\""
 
155
            tm = time.localtime(now - 24 * 3600)
 
156
            self._period_start = time.localtime(now - 24 * 3600)
 
157
 
 
158
        elif self.parameters["timeline_type"] == "month":
 
159
            self.dataset["timeline_month_selected"] = "selected=\"selected\""
 
160
            tm = list(time.localtime(now))
 
161
            tm[1] -= 1
 
162
            self._period_start = time.localtime(time.mktime(tm))
 
163
 
 
164
        else:
 
165
            self.dataset["timeline_custom_selected"] = "selected=\"selected\""
 
166
            self._period_start = time.struct_time((self.parameters["from_year"], self.parameters["from_month"],
 
167
                                                   self.parameters["from_day"], self.parameters["from_hour"],
 
168
                                                   self.parameters["from_min"], 0, 0, 0, -1))
 
169
            self._period_end = time.struct_time((self.parameters["to_year"], self.parameters["to_month"],
 
170
                                                 self.parameters["to_day"], self.parameters["to_hour"],
 
171
                                                 self.parameters["to_min"], 0, 0, 0, -1))
 
172
 
 
173
        self.dataset["from_year"] = "%.4d" % self._period_start.tm_year
 
174
        self.dataset["from_month"] = "%.2d" % self._period_start.tm_mon
 
175
        self.dataset["from_day"] = "%.2d" % self._period_start.tm_mday
 
176
        self.dataset["from_hour"] = "%.2d" % self._period_start.tm_hour
 
177
        self.dataset["from_min"] = "%.2d" % self._period_start.tm_min
 
178
 
 
179
        self.dataset["to_year"] = "%.4d" % self._period_end.tm_year
 
180
        self.dataset["to_month"] = "%.2d" % self._period_end.tm_mon
 
181
        self.dataset["to_day"] = "%.2d" % self._period_end.tm_mday
 
182
        self.dataset["to_hour"] = "%.2d" % self._period_end.tm_hour
 
183
        self.dataset["to_min"] = "%.2d" % self._period_end.tm_min
 
184
 
 
185
        criteria = [ "alert.create_time >= '%d-%d-%d %d:%d:%d' && alert.create_time < '%d-%d-%d %d:%d:%d'" % \
 
186
                     (self._period_start.tm_year, self._period_start.tm_mon, self._period_start.tm_mday,
 
187
                      self._period_start.tm_hour, self._period_start.tm_min, self._period_start.tm_sec,
 
188
                      self._period_end.tm_year, self._period_end.tm_mon, self._period_end.tm_mday,
 
189
                      self._period_end.tm_hour, self._period_end.tm_min, self._period_end.tm_sec) ]
 
190
 
 
191
        return criteria
 
192
 
 
193
    def _processFilterCriteria(self):
 
194
        c = [ ]
 
195
        if self.parameters.has_key("idmef_filter"):
 
196
            c.append(unicode(self.parameters["idmef_filter"]))
 
197
 
 
198
        self.dataset["current_filter"] = self.parameters.get("filter", "")
 
199
        if self.parameters.has_key("filter"):
 
200
            f = self.env.db.getAlertFilter(self.user.login, self.parameters["filter"])
 
201
            if f:
 
202
                c.append(unicode(f))
 
203
 
 
204
        return c
 
205
 
 
206
    def _processCriteria(self):
 
207
        criteria = [ ]
 
208
        criteria += self._processTimeCriteria()
 
209
        criteria += self._processFilterCriteria()
 
210
 
 
211
        return criteria
 
212
 
 
213
    def render(self):
 
214
        self.dataset["hidden_parameters"] = [ ("view", self.view_name) ]
 
215
        self.dataset["charts"] = [ ]
 
216
        self.dataset["filters"] = self.env.db.getAlertFilterNames(self.user.login)
 
217
        self.dataset["timeline_hour_selected"] = ""
 
218
        self.dataset["timeline_day_selected"] = ""
 
219
        self.dataset["timeline_month_selected"] = ""
 
220
        self.dataset["timeline_custom_selected"] = ""
 
221
 
 
222
    def _setPeriod(self):
 
223
        tm = time.localtime()
 
224
 
 
225
        period = "from %s/%s/%s %s:%s to %s/%s/%s %s:%s" % \
 
226
                 (self.dataset["from_year"], self.dataset["from_month"], self.dataset["from_day"],
 
227
                  self.dataset["from_hour"], self.dataset["from_min"],
 
228
                  self.dataset["to_year"], self.dataset["to_month"], self.dataset["to_day"],
 
229
                  self.dataset["to_hour"], self.dataset["to_min"])
 
230
 
 
231
        if self.parameters["timeline_type"] == "month":
 
232
            self.dataset["period"] = "Period: current month (%s)" % period
 
233
        elif self.parameters["timeline_type"] == "day":
 
234
            self.dataset["period"] = "Period: today (%s)" % period
 
235
        elif self.parameters["timeline_type"] == "hour":
 
236
            self.dataset["period"] = "Period: current hour (%s)" % period
 
237
        else:
 
238
            self.dataset["period"] = "Period: %s" % period
 
239
 
 
240
    def genPathValueURI(self, path, value, index=0, type=None):
 
241
        if value:
 
242
            operator = "="
 
243
        else:
 
244
            value = ""
 
245
            operator = "!"
 
246
 
 
247
        if type == None:
 
248
            type = path.split(".")[1]
 
249
 
 
250
            idx = type.find("(")
 
251
            if idx != -1:
 
252
                type = type[0: idx]
 
253
 
 
254
            if type == "assessment":
 
255
                type = "classification"
 
256
 
 
257
            if type not in ("classification", "source", "target", "analyzer"):
 
258
                raise Exception, "The path '%s' cannot be mapped to a column" % path
 
259
 
 
260
        return utils.urlencode({ "%s_object_%d" % (type, index): path, "%s_operator_%d" % (type, index): operator, "%s_value_%d" % (type, index): value })
 
261
 
 
262
 
 
263
 
 
264
class GenericTimelineStats(DistributionStats):
 
265
    def _getAlertCount(self, criteria, link, zoom_view):
 
266
        d = {}
 
267
 
 
268
        results = self.env.idmef_db.getValues(self._getSelection(), criteria)
 
269
        if not results:
 
270
            return d
 
271
 
 
272
        for name, count in results:
 
273
            if zoom_view == "alert_listing":
 
274
                link += "&amp;" + self.genPathValueURI(self._path, name)
 
275
 
 
276
            d[self._getNameFromMap(name or _(u"n/a"), self._names_and_colors)] = (count, link)
 
277
 
 
278
        return d
 
279
 
 
280
    def _newTimeline(self, user, width, height, stacked=False):
 
281
        if stacked:
 
282
            timeline = Chart.StackedTimelineChart(user, width, height)
 
283
        else:
 
284
            timeline = Chart.TimelineChart(user, width, height)
 
285
 
 
286
        if not self.parameters.has_key("idmef_filter"):
 
287
            timeline.enableMultipleValues(self._namesAndColors2ColorMap(self._names_and_colors))
 
288
 
 
289
        return timeline
 
290
 
 
291
    def _getTimeCrit(self, start, step):
 
292
        tm1 = start #time.localtime(start)
 
293
        tm2 = start+step #time.localtime(start + step)
 
294
 
 
295
        c = [ "alert.create_time >= '%d-%d-%d %d:%d:%d' && alert.create_time < '%d-%d-%d %d:%d:%d'" % \
 
296
              (tm1.year, tm1.month, tm1.day, tm1.hour, tm1.minute, tm1.second,
 
297
               tm2.year, tm2.month, tm2.day, tm2.hour, tm2.minute, tm2.second) ]
 
298
 
 
299
        return c
 
300
 
 
301
    def _getStep(self, type, absolute=False):
 
302
        start = None
 
303
 
 
304
        if type == "custom":
 
305
                type = self.getCustomUnit()
 
306
                start = datetime.datetime(*self._period_start[:6])
 
307
                end = datetime.datetime(*self._period_end[:6])
 
308
        else:
 
309
                end = datetime.datetime.today()
 
310
 
 
311
        if type == "min":
 
312
                if not start:
 
313
                        start = end - datetime.timedelta(seconds=60)
 
314
                step = datetime.timedelta(seconds=1)
 
315
                label_tm_index = "%Hh%M:%S"
 
316
                zoom_view = "alert_listing"
 
317
                timeline_type = "min"
 
318
                timeline_unit = ""
 
319
 
 
320
        elif type == "hour":
 
321
                if not start:
 
322
                        start = end - datetime.timedelta(minutes=60)
 
323
                step = datetime.timedelta(minutes=1)
 
324
                label_tm_index = "%Hh%M"
 
325
                zoom_view = "alert_listing"
 
326
                timeline_type = "min"
 
327
                timeline_unit = ""
 
328
 
 
329
        elif type == "day":
 
330
                if not start:
 
331
                        start = end - datetime.timedelta(hours=24)
 
332
                step = datetime.timedelta(hours=1)
 
333
                label_tm_index = "%d/%Hh"
 
334
                zoom_view = "stats_timeline"
 
335
                timeline_type = "custom"
 
336
                timeline_unit = "hour"
 
337
 
 
338
        elif type == "month":
 
339
                if not start:
 
340
                        start = end - datetime.timedelta(days=31)
 
341
                step = datetime.timedelta(days=1)
 
342
                label_tm_index = "%m/%d"
 
343
                zoom_view = "stats_timeline"
 
344
                timeline_type = "custom"
 
345
                timeline_unit = "day"
 
346
 
 
347
        elif type == "year":
 
348
                if not start:
 
349
                        start = end - datetime.timedelta(days=365)
 
350
                step = datetime.timedelta(days=31)
 
351
                label_tm_index = "%m/%d"
 
352
                zoom_view = "stats_timeline"
 
353
                timeline_type = "custom"
 
354
                timeline_unit = "day"
 
355
 
 
356
        return start, end, step, label_tm_index, zoom_view, timeline_type, timeline_unit
 
357
 
 
358
 
 
359
    def _setTimelineZoom(self, base_parameters, start, end):
 
360
        #tm = time.localtime(start)
 
361
        base_parameters["from_year"] = start.year
 
362
        base_parameters["from_month"] = start.month
 
363
        base_parameters["from_day"] = start.day
 
364
        base_parameters["from_hour"] = start.hour
 
365
        base_parameters["from_min"] = start.minute
 
366
 
 
367
        #tm = time.localtime(end)
 
368
        base_parameters["to_year"] = end.year
 
369
        base_parameters["to_month"] = end.month
 
370
        base_parameters["to_day"] = end.day
 
371
        base_parameters["to_hour"] = end.hour
 
372
        base_parameters["to_min"] = end.minute
 
373
 
 
374
    def _generateTimeline(self, user, width, height):
 
375
        start, end, step, format, zoom_view, timeline_type, timeline_time = self._getStep(self.parameters["timeline_type"])
 
376
        timeline = self._newTimeline(user, width, height)
 
377
 
 
378
        if timeline_type != "custom":
 
379
            base_parameters = { "timeline_unit": "min" }
 
380
        else:
 
381
            base_parameters = { "timeline_type": timeline_type }
 
382
 
 
383
        self.dataset["timeline_user_type"] = self.parameters.get("timeline_type")
 
384
 
 
385
        while start < end:
 
386
            c = self._getTimeCrit(start, step) + self._criteria
 
387
 
 
388
            if timeline_type != "custom":
 
389
                base_parameters["timeline_start"] = long(time.mktime(start.timetuple())) #long(start)
 
390
            else:
 
391
                self._setTimelineZoom(base_parameters, start, start + step)
 
392
 
 
393
            link = utils.create_link(zoom_view, base_parameters)
 
394
            count = self._getAlertCount(c, link, zoom_view)
 
395
            label = start.strftime(format)
 
396
 
 
397
            start += step
 
398
            timeline.addLabelValuePair(label, count, link)
 
399
 
 
400
        return timeline
 
401
 
 
402
    def getCustomUnit(self):
 
403
        start = long(time.mktime(self._period_start))
 
404
        delta = long(time.mktime(self._period_end)) - start
 
405
 
 
406
        if delta > 86400:
 
407
            unit = "month"
 
408
        elif delta > 3600:
 
409
            unit = "day"
 
410
        elif delta > 60:
 
411
            unit = "hour"
 
412
        else:
 
413
            unit = "min"
 
414
 
 
415
        return unit
 
416
 
 
417
    def _getSelection(self):
 
418
        return ("%s/group_by" % self._path, "count(%s)/order_desc" % self._path)
 
419
 
 
420
    def _addTimelineChart(self, title, value_name, width, height, path, criteria, limit=-1, names_and_colors={}, allow_stacked=False, value_callback=None, zoom_type=None):
 
421
        self._path = path
 
422
        self._limit = limit
 
423
        self._value_callback = value_callback
 
424
        self._criteria = criteria
 
425
        self._zoom_type = zoom_type
 
426
        self._names_and_colors = names_and_colors
 
427
 
 
428
        base_url = self._getBaseURL()
 
429
        chart = { "title": title, "value_name": value_name }
 
430
 
 
431
        if limit > 0:
 
432
            res = self.env.idmef_db.getValues(self._getSelection(), criteria = criteria, limit=self._limit)
 
433
 
 
434
            c = u""
 
435
            for name, count in res:
 
436
                if c:
 
437
                    c += " || "
 
438
 
 
439
                if name:
 
440
                    c += "%s = '%s'" % (self._path, utils.escape_criteria(name))
 
441
                else:
 
442
                    c += "! %s" % (self._path)
 
443
            if c:
 
444
                criteria.append(c)
 
445
 
 
446
        timeline = self._generateTimeline(self.user, width, height)
 
447
        timeline.render(title)
 
448
 
 
449
        chart["chart"] = timeline
 
450
        self.dataset["charts"].append(chart)
 
451
        self.dataset["zoom"] = self.parameters.get("zoom", None)
 
452
 
 
453
 
 
454
class CategorizationStats(DistributionStats, GenericTimelineStats):
 
455
    view_name = "stats_categorization"
 
456
 
 
457
    def _renderClassifications(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
458
        self._addDistributionChart(_("Top 10 Classifications"), _("Classification"), width, height,
 
459
                                   "alert.classification.text",
 
460
                                   criteria,
 
461
                                   lambda value: self.genPathValueURI("alert.classification.text", value, type="classification"),
 
462
                                   10)
 
463
 
 
464
    def _renderReferences(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
465
        self._addDistributionChart(_("Top 10 Alert References"), _("References"), width, height,
 
466
                                   "alert.classification.reference.name",
 
467
                                   criteria,
 
468
                                   lambda value: self.genPathValueURI("alert.classification.reference.name", value, type="classification"),
 
469
                                   10)
 
470
 
 
471
    def _renderImpactSeverities(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
472
        _severity_maps = utils.OrderedDict()
 
473
        _severity_maps["high"] = (_("High"), Chart.RED_STD)
 
474
        _severity_maps["medium"] = (_("Medium"), Chart.ORANGE_STD)
 
475
        _severity_maps["low"] = (_("Low"), Chart.GREEN_STD)
 
476
        _severity_maps["info"] = (_("Informational"), Chart.BLUE_STD)
 
477
        _severity_maps[None] = (_("N/a"), "000000")
 
478
 
 
479
        self._addDistributionChart(_("Severities"), _("Severity"), width, height,
 
480
                                   "alert.assessment.impact.severity",
 
481
                                   criteria,
 
482
                                   lambda value: self.genPathValueURI("alert.assessment.impact.severity", value, type="classification"), names_and_colors=_severity_maps)
 
483
 
 
484
    def _renderImpactTypes(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
485
        self._addDistributionChart(_("Alert Impact Types"), _("Impact Types"), width, height,
 
486
                                   "alert.assessment.impact.type",
 
487
                                   criteria,
 
488
                                   lambda value: self.genPathValueURI("alert.assessment.impact.type", value, type="classification"))
 
489
 
 
490
    def _renderClassificationsTrend(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
491
        GenericTimelineStats._addTimelineChart(self, "Top 10 Classifications Trend", None, width, height,
 
492
                                               "alert.classification.text", criteria, limit = 10, zoom_type="classifications_trend")
 
493
 
 
494
    def render(self):
 
495
        DistributionStats.render(self)
 
496
 
 
497
        self.dataset["title"] = "Alerts categorization"
 
498
 
 
499
        criteria = self._processCriteria()
 
500
 
 
501
        self._setPeriod()
 
502
 
 
503
        self._renderClassificationsTrend(criteria)
 
504
        self._renderClassifications(criteria)
 
505
        self._renderReferences(criteria)
 
506
        self._renderImpactSeverities(criteria)
 
507
        self._renderImpactTypes(criteria)
 
508
 
 
509
 
 
510
 
 
511
class SourceStats(DistributionStats, GenericTimelineStats):
 
512
    view_name = "stats_source"
 
513
 
 
514
    def _countryDistributionChart(self, criteria, width, height):
 
515
 
 
516
        base_url = self._getBaseURL()
 
517
        distribution = Chart.WorldChart(self.user, width, height)
 
518
 
 
519
        chart = { "title": _("Top Source Country"), "value_name": _("Country"), "chart": distribution }
 
520
 
 
521
        results = self.env.idmef_db.getValues([ "alert.source.node.address.address/group_by",
 
522
                                                "count(alert.source.node.address.address)"],
 
523
                                              criteria=criteria, limit=-1)
 
524
 
 
525
        if results:
 
526
            merge = { }
 
527
            for value, count in results:
 
528
                if value:
 
529
                    if distribution.needCountryCode():
 
530
                        nvalue = geoip.country_code_by_addr(value)
 
531
                    else:
 
532
                        nvalue = geoip.country_name_by_addr(value)
 
533
                else:
 
534
                        nvalue = None
 
535
 
 
536
                if not nvalue:
 
537
                    nvalue = _(u"n/a")
 
538
 
 
539
                if not merge.has_key(nvalue):
 
540
                   url_index = 0
 
541
                   merge[nvalue] = (0, 0, nvalue, "")
 
542
                else:
 
543
                   url_index = merge[nvalue][1]
 
544
 
 
545
                encode = "&amp;" + self.genPathValueURI("alert.source.node.address.address", value, type="source", index=url_index)
 
546
                merge[nvalue] = (merge[nvalue][0] + count, url_index + 1, nvalue, merge[nvalue][3] + encode)
 
547
 
 
548
            s = [ t[1] for t in merge.items() ]
 
549
            s.sort()
 
550
            s.reverse()
 
551
            results = s #[0:10]
 
552
 
 
553
            for item in results:
 
554
                distribution.addLabelValuePair(item[2], item[0])
 
555
 
 
556
        distribution.render("Top 10 Source Country")
 
557
        chart["filename"] = distribution.getHref()
 
558
        chart["type"] = distribution.getType()
 
559
        self.dataset["charts"].append(chart)
 
560
 
 
561
 
 
562
    def _renderCountry(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
563
        if geoip is not None:
 
564
            self._countryDistributionChart(criteria, width, height)
 
565
 
 
566
    def _renderAddresses(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
567
        self._addDistributionChart(_("Top 10 Source Addresses"), _("Address"), width, height,
 
568
                                   "alert.source.node.address.address",
 
569
                                   criteria,
 
570
                                   lambda value: self.genPathValueURI("alert.source.node.address.address", value, type="source"),
 
571
                                   10, dns=True)
 
572
 
 
573
    def _renderUsers(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
574
        self._addDistributionChart(_("Top 10 Source Users"), _("User"), width, height,
 
575
                                   "alert.source.user.user_id.name",
 
576
                                   criteria,
 
577
                                   lambda value: self.genPathValueURI("alert.source.user.user_id.name", value, type="source"),
 
578
                                   10)
 
579
 
 
580
    def _renderSourcesTrend(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
581
        GenericTimelineStats._addTimelineChart(self, "Top 10 Sources Trend", None, DEFAULT_WIDTH, DEFAULT_HEIGHT,
 
582
                                               "alert.source.node.address.address", criteria, 10, zoom_type="sources_trend")
 
583
 
 
584
    def render(self):
 
585
        DistributionStats.render(self)
 
586
 
 
587
        self.dataset["title"] = "Top Alert Sources"
 
588
 
 
589
        criteria = self._processCriteria()
 
590
 
 
591
        self._setPeriod()
 
592
 
 
593
        self._renderCountry(criteria)
 
594
        self._renderSourcesTrend(criteria)
 
595
        self._renderAddresses(criteria)
 
596
        self._renderUsers(criteria)
 
597
 
 
598
        resolve.process(self.env.dns_max_delay)
 
599
 
 
600
class TargetStats(DistributionStats):
 
601
    view_name = "stats_target"
 
602
 
 
603
    def _renderPorts(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
604
        base_url = self._getBaseURL()
 
605
        title = "Top 10 Targeted Ports"
 
606
        distribution = Chart.DistributionChart(self.user, width, height)
 
607
        chart = { "title": title, "value_name": "Port", "chart": distribution }
 
608
 
 
609
        criteria = criteria[:] + [ "(alert.target.service.iana_protocol_number == 6  ||"
 
610
                                   "alert.target.service.iana_protocol_number == 17  ||"
 
611
                                   "alert.target.service.iana_protocol_name =* 'tcp' ||"
 
612
                                   "alert.target.service.iana_protocol_name =* 'udp' ||"
 
613
                                   "alert.target.service.protocol =* 'udp'           ||"
 
614
                                   "alert.target.service.protocol =* 'tcp')" ]
 
615
 
 
616
        results = self.env.idmef_db.getValues([ "alert.target.service.port/group_by",
 
617
                                                "alert.target.service.iana_protocol_number/group_by",
 
618
                                                "alert.target.service.iana_protocol_name/group_by",
 
619
                                                "alert.target.service.protocol/group_by",
 
620
                                                "count(alert.target.service.port)/order_desc" ],
 
621
                                              criteria=criteria, limit=10)
 
622
        if not results:
 
623
            return
 
624
 
 
625
        merge = { _(u"n/a"): { }, u"tcp": { }, u"udp": { } }
 
626
 
 
627
        for port, iana_protocol_number, iana_protocol_name, protocol, count in results:
 
628
            if not port:
 
629
                continue
 
630
 
 
631
            if iana_protocol_number:
 
632
                protocol = utils.protocol_number_to_name(iana_protocol_number)
 
633
 
 
634
            elif iana_protocol_name:
 
635
                protocol = iana_protocol_name
 
636
 
 
637
            if not protocol:
 
638
                protocol = _(u"n/a")
 
639
 
 
640
            protocol = protocol.lower()
 
641
            if not merge.has_key(protocol):
 
642
                protocol = _(u"n/a")
 
643
 
 
644
            if not merge[protocol].has_key(port):
 
645
                merge[protocol][port] = 0
 
646
 
 
647
            merge[protocol][port] += count
 
648
 
 
649
        results = [ ]
 
650
 
 
651
        for protocol, values in merge.items():
 
652
            for port, count in values.items():
 
653
                results.append((port, protocol, count))
 
654
 
 
655
        results.sort(lambda x, y: int(y[2] - x[2]))
 
656
 
 
657
        for port, protocol, count in results:
 
658
            name = "%d / %s" % (port, protocol)
 
659
            distribution.addLabelValuePair(name, count, base_url + "&amp;" + "target_object_0=alert.target.service.port&amp;target_value_0=%d" % port)
 
660
 
 
661
        distribution.render(title)
 
662
        self.dataset["charts"].append(chart)
 
663
 
 
664
    def _renderAddresses(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
665
        self._addDistributionChart(_("Top 10 Targeted Addresses"), _("Address"), width, height,
 
666
                                   "alert.target.node.address.address",
 
667
                                   criteria,
 
668
                                   lambda value: self.genPathValueURI("alert.target.node.address.address", value, type="target"),
 
669
                                   10, dns=True)
 
670
 
 
671
    def _renderUsers(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
672
        self._addDistributionChart(_("Top 10 Targeted Users"), _("User"), width, height,
 
673
                                   "alert.target.user.user_id.name",
 
674
                                   criteria,
 
675
                                   lambda value: self.genPathValueURI("alert.target.user.user_id.name", value, type="target"),
 
676
                                   10)
 
677
 
 
678
    def render(self):
 
679
        DistributionStats.render(self)
 
680
 
 
681
        self.dataset["title"] = "Top Alert Targets"
 
682
 
 
683
        criteria = self._processCriteria()
 
684
 
 
685
        self._setPeriod()
 
686
 
 
687
        self._renderAddresses(criteria)
 
688
        self._renderPorts(criteria)
 
689
        self._renderUsers(criteria)
 
690
 
 
691
        resolve.process(self.env.dns_max_delay)
 
692
 
 
693
 
 
694
class AnalyzerStats(DistributionStats, GenericTimelineStats):
 
695
    view_name = "stats_analyzer"
 
696
 
 
697
    def _renderAnalyzers(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
698
        base_url = self._getBaseURL()
 
699
        title = "Top 10 analyzers"
 
700
        distribution = Chart.DistributionChart(self.user, width, height)
 
701
        chart = { "title": title, "value_name": "Analyzer", "chart": distribution }
 
702
 
 
703
        results = self.env.idmef_db.getValues([ "alert.analyzer(-1).name/group_by", "alert.analyzer(-1).node.name/group_by",
 
704
                                                "count(alert.analyzer(-1).name)/order_desc" ],
 
705
                                              criteria=criteria + [ "alert.analyzer(-1).name" ], limit=10)
 
706
        if results:
 
707
            for analyzer_name, node_name, count in results:
 
708
                value = analyzer_name or _(u"n/a")
 
709
                if node_name:
 
710
                    value = "%s on %s"  % (value, node_name)
 
711
 
 
712
                analyzer_criteria = self.genPathValueURI("alert.analyzer(-1).name", analyzer_name, type="analyzer")
 
713
                distribution.addLabelValuePair(value, count, base_url + "&amp;" + analyzer_criteria)
 
714
 
 
715
            distribution.render(title)
 
716
            self.dataset["charts"].append(chart)
 
717
 
 
718
    def _renderModels(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
719
        self._addDistributionChart(_("Top 10 Analyzer Models"), _("Model"), width, height,
 
720
                                   "alert.analyzer(-1).model",
 
721
                                   criteria,
 
722
                                   lambda value: self.genPathValueURI("alert.analyzer(-1).model", value, type="analyzer"),
 
723
                                   10)
 
724
 
 
725
    def _renderClasses(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
726
        self._addDistributionChart(_("Top 10 Analyzer Classes"), _("Class"), width, height,
 
727
                                   "alert.analyzer(-1).class",
 
728
                                   criteria,
 
729
                                   lambda value: self.genPathValueURI("alert.analyzer(-1).class", value, type="analyzer"),
 
730
                                   10)
 
731
 
 
732
    def _renderNodeAddresses(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
733
        self._addDistributionChart(_("Top 10 Analyzer Node Addresses"), _("Address"), width, height,
 
734
                                   "alert.analyzer(-1).node.address.address",
 
735
                                   criteria,
 
736
                                   lambda value: self.genPathValueURI("alert.analyzer(-1).node.address.address", value, type="analyzer"),
 
737
                                   10)
 
738
 
 
739
    def _renderNodeLocations(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
740
        self._addDistributionChart(_("Analyzer Locations"), _("Location"), width, height,
 
741
                                   "alert.analyzer(-1).node.location",
 
742
                                   criteria,
 
743
                                   lambda value: self.genPathValueURI("alert.analyzer(-1).node.location", value, type="analyzer"))
 
744
 
 
745
    def _renderClassesTrend(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
746
        GenericTimelineStats._addTimelineChart(self, "Top 10 Analyzer Classes Trend", None, width, height,
 
747
                                               "alert.analyzer(-1).class", criteria, limit = 10, zoom_type="analyzer_classes_trend")
 
748
 
 
749
    def render(self):
 
750
        DistributionStats.render(self)
 
751
 
 
752
        self.dataset["title"] = "Top Analyzers"
 
753
 
 
754
        criteria = self._processCriteria()
 
755
 
 
756
        self._setPeriod()
 
757
 
 
758
        self._renderClassesTrend(criteria)
 
759
        self._renderAnalyzers(criteria)
 
760
        self._renderModels(criteria)
 
761
        self._renderClasses(criteria)
 
762
        self._renderNodeAddresses(criteria)
 
763
        self._renderNodeLocations(criteria)
 
764
 
 
765
 
 
766
 
 
767
class TimelineStats(GenericTimelineStats, AnalyzerStats, CategorizationStats, SourceStats):
 
768
    view_name = "stats_timeline"
 
769
 
 
770
    def _renderTimelineChart(self, criteria, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT):
 
771
        _severity_maps = utils.OrderedDict()
 
772
        _severity_maps["high"] = (_("High"), Chart.RED_STD)
 
773
        _severity_maps["medium"] = (_("Medium"), Chart.ORANGE_STD)
 
774
        _severity_maps["low"] = (_("Low"), Chart.GREEN_STD)
 
775
        _severity_maps["info"] = (_("Informational"), Chart.BLUE_STD)
 
776
        _severity_maps[None] = (_("N/a"), "000000")
 
777
 
 
778
        GenericTimelineStats._addTimelineChart(self, "Timeline", None, width, height,
 
779
                                               "alert.assessment.impact.severity", criteria, names_and_colors=_severity_maps)
 
780
 
 
781
    def render(self):
 
782
        DistributionStats.render(self)
 
783
        self.dataset["title"] = "Timeline"
 
784
 
 
785
        criteria = self._processCriteria()
 
786
        self._setPeriod()
 
787
 
 
788
        type = self.parameters.get("type", None)
 
789
        if type == "analyzer_classes_trend":
 
790
                AnalyzerStats._renderClassesTrend(self, criteria)
 
791
 
 
792
        elif type == "classifications_trend":
 
793
                CategorizationStats._renderClassificationsTrend(self, criteria)
 
794
 
 
795
        elif type == "sources_trend":
 
796
                SourceStats._renderSourcesTrend(self, criteria)
 
797
        else:
 
798
                self._renderTimelineChart(criteria)
 
799
 
 
800
 
 
801
 
 
802
 
 
803
class AnalyzerTrendStats(GenericTimelineStats, AnalyzerStats):
 
804
    view_name = "stats_analyzer_trend"
 
805
 
 
806
    def render(self):
 
807
        DistributionStats.render(self)
 
808
        self.dataset["title"] = "Timeline"
 
809
 
 
810
        criteria = self._processCriteria()
 
811
        self._setPeriod()
 
812
 
 
813
        title = "Top 10 Analyzer Trend " + self.dataset["period"]
 
814
        AnalyzerStats._renderClassesTrend(self, criteria, width, height)
 
815