Skip to content

Commit 71087e3

Browse files
authored
Merge branch 'master' into patch-3
2 parents 0a7e71b + 2791119 commit 71087e3

15 files changed

+1255
-19
lines changed

Dockerfile-test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
FROM ubuntu:latest
22

33
RUN apt-get update && apt-get upgrade -y
4-
RUN apt-get -y install build-essential python-setuptools python2.7 python2.7-dev libssl-dev git tox python-pip
4+
RUN apt-get -y install build-essential python3.6 python3.6-dev python3-pip libssl-dev git
55

66
WORKDIR /home/elastalert
77

88
ADD requirements*.txt ./
9-
RUN pip install -r requirements-dev.txt
9+
RUN pip3 install -r requirements-dev.txt

docs/source/elastalert.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ The default value is ``.raw`` for Elasticsearch 2 and ``.keyword`` for Elasticse
203203

204204
``skip_invalid``: If ``True``, skip invalid files instead of exiting.
205205

206-
=======
207206
Logging
208207
-------
209208

docs/source/recipes/writing_filters.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ a field that appears to have the value "foo bar", unless it is not analyzed. Con
5757
matching on analyzed fields, use query_string. See https://www.elastic.co/guide/en/elasticsearch/guide/current/term-vs-full-text.html
5858

5959
`terms <https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html>`_
60-
*****
60+
*****************************************************************************************************
6161

6262

6363

docs/source/ruletypes.rst

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ Rule Configuration Cheat Sheet
5858
+--------------------------------------------------------------+ |
5959
| ``kibana4_end_timedelta`` (time, default: 10 min) | |
6060
+--------------------------------------------------------------+ |
61+
| ``generate_kibana_discover_url`` (boolean, default False) | |
62+
+--------------------------------------------------------------+ |
63+
| ``kibana_discover_app_url`` (string, no default) | |
64+
+--------------------------------------------------------------+ |
65+
| ``kibana_discover_version`` (string, no default) | |
66+
+--------------------------------------------------------------+ |
67+
| ``kibana_discover_index_pattern_id`` (string, no default) | |
68+
+--------------------------------------------------------------+ |
69+
| ``kibana_discover_columns`` (list of strs, default _source) | |
70+
+--------------------------------------------------------------+ |
71+
| ``kibana_discover_from_timedelta`` (time, default: 10 min) | |
72+
+--------------------------------------------------------------+ |
73+
| ``kibana_discover_to_timedelta`` (time, default: 10 min) | |
74+
+--------------------------------------------------------------+ |
6175
| ``use_local_time`` (boolean, default True) | |
6276
+--------------------------------------------------------------+ |
6377
| ``realert`` (time, default: 1 min) | |
@@ -510,6 +524,85 @@ This value is added in back of the event. For example,
510524

511525
``kibana4_end_timedelta: minutes: 2``
512526

527+
generate_kibana_discover_url
528+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
529+
530+
``generate_kibana_discover_url``: Enables the generation of the ``kibana_discover_url`` variable for the Kibana Discover application.
531+
This setting requires the following settings are also configured:
532+
533+
- ``kibana_discover_app_url``
534+
- ``kibana_discover_version``
535+
- ``kibana_discover_index_pattern_id``
536+
537+
``generate_kibana_discover_url: true``
538+
539+
kibana_discover_app_url
540+
^^^^^^^^^^^^^^^^^^^^^^^
541+
542+
``kibana_discover_app_url``: The url of the Kibana Discover application used to generate the ``kibana_discover_url`` variable.
543+
This value can use `$VAR` and `${VAR}` references to expand environment variables.
544+
545+
``kibana_discover_app_url: http://kibana:5601/#/discover``
546+
547+
kibana_discover_version
548+
^^^^^^^^^^^^^^^^^^^^^^^
549+
550+
``kibana_discover_version``: Specifies the version of the Kibana Discover application.
551+
552+
The currently supported versions of Kibana Discover are:
553+
554+
- `5.6`
555+
- `6.0`, `6.1`, `6.2`, `6.3`, `6.4`, `6.5`, `6.6`, `6.7`, `6.8`
556+
- `7.0`, `7.1`, `7.2`, `7.3`
557+
558+
``kibana_discover_version: '7.3'``
559+
560+
kibana_discover_index_pattern_id
561+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
562+
563+
``kibana_discover_index_pattern_id``: The id of the index pattern to link to in the Kibana Discover application.
564+
These ids are usually generated and can be found in url of the index pattern management page, or by exporting its saved object.
565+
566+
Example export of an index pattern's saved object:
567+
568+
.. code-block:: text
569+
570+
[
571+
{
572+
"_id": "4e97d188-8a45-4418-8a37-07ed69b4d34c",
573+
"_type": "index-pattern",
574+
"_source": { ... }
575+
}
576+
]
577+
578+
You can modify an index pattern's id by exporting the saved object, modifying the ``_id`` field, and re-importing.
579+
580+
``kibana_discover_index_pattern_id: 4e97d188-8a45-4418-8a37-07ed69b4d34c``
581+
582+
kibana_discover_columns
583+
^^^^^^^^^^^^^^^^^^^^^^^
584+
585+
``kibana_discover_columns``: The columns to display in the generated Kibana Discover application link.
586+
Defaults to the ``_source`` column.
587+
588+
``kibana_discover_columns: [ timestamp, message ]``
589+
590+
kibana_discover_from_timedelta
591+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
592+
593+
``kibana_discover_from_timedelta``: The offset to the `from` time of the Kibana Discover link's time range.
594+
The `from` time is calculated by subtracting this timedelta from the event time. Defaults to 10 minutes.
595+
596+
``kibana_discover_from_timedelta: minutes: 2``
597+
598+
kibana_discover_to_timedelta
599+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
600+
601+
``kibana_discover_to_timedelta``: The offset to the `to` time of the Kibana Discover link's time range.
602+
The `to` time is calculated by adding this timedelta to the event time. Defaults to 10 minutes.
603+
604+
``kibana_discover_to_timedelta: minutes: 2``
605+
513606
use_local_time
514607
^^^^^^^^^^^^^^
515608

@@ -1305,7 +1398,7 @@ With ``alert_text_type: aggregation_summary_only``::
13051398
body = rule_name
13061399

13071400
aggregation_summary
1308-
+
1401+
13091402
ruletype_text is the string returned by RuleType.get_match_str.
13101403

13111404
field_values will contain every key value pair included in the results from Elasticsearch. These fields include "@timestamp" (or the value of ``timestamp_field``),
@@ -1689,7 +1782,7 @@ Provide absolute address of the pciture, for example: http://some.address.com/im
16891782
``slack_timeout``: You can specify a timeout value, in seconds, for making communicating with Slac. The default is 10. If a timeout occurs, the alert will be retried next time elastalert cycles.
16901783

16911784
Mattermost
1692-
~~~~~
1785+
~~~~~~~~~~
16931786

16941787
Mattermost alerter will send a notification to a predefined Mattermost channel. The body of the notification is formatted the same as with other alerters.
16951788

elastalert/elastalert.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
from croniter import croniter
2424
from elasticsearch.exceptions import ConnectionError
2525
from elasticsearch.exceptions import ElasticsearchException
26-
from elasticsearch.exceptions import TransportError
2726
from elasticsearch.exceptions import NotFoundError
27+
from elasticsearch.exceptions import TransportError
2828

2929
from . import kibana
30+
from .kibana_discover import generate_kibana_discover_url
3031
from .alerts import DebugAlerter
3132
from .config import load_conf
3233
from .enhancements import DropMatchException
@@ -1067,7 +1068,13 @@ def load_rule_changes(self):
10671068
if rule_file not in new_rule_hashes:
10681069
# Rule file was deleted
10691070
elastalert_logger.info('Rule file %s not found, stopping rule execution' % (rule_file))
1070-
self.rules = [rule for rule in self.rules if rule['rule_file'] != rule_file]
1071+
for rule in self.rules:
1072+
if rule['rule_file'] == rule_file:
1073+
break
1074+
else:
1075+
continue
1076+
self.scheduler.remove_job(job_id=rule['name'])
1077+
self.rules.remove(rule)
10711078
continue
10721079
if hash_value != new_rule_hashes[rule_file]:
10731080
# Rule file was changed, reload rule
@@ -1497,6 +1504,11 @@ def send_alert(self, matches, rule, alert_time=None, retried=False):
14971504
if kb_link:
14981505
matches[0]['kibana_link'] = kb_link
14991506

1507+
if rule.get('generate_kibana_discover_url'):
1508+
kb_link = generate_kibana_discover_url(rule, matches[0])
1509+
if kb_link:
1510+
matches[0]['kibana_discover_url'] = kb_link
1511+
15001512
# Enhancements were already run at match time if
15011513
# run_enhancements_first is set or
15021514
# retried==True, which means this is a retry of a failed alert

elastalert/kibana_discover.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# -*- coding: utf-8 -*-
2+
# flake8: noqa
3+
import datetime
4+
import logging
5+
import json
6+
import os.path
7+
import prison
8+
import urllib.parse
9+
10+
from .util import EAException
11+
from .util import lookup_es_key
12+
from .util import ts_add
13+
14+
kibana_default_timedelta = datetime.timedelta(minutes=10)
15+
16+
kibana5_kibana6_versions = frozenset(['5.6', '6.0', '6.1', '6.2', '6.3', '6.4', '6.5', '6.6', '6.7', '6.8'])
17+
kibana7_versions = frozenset(['7.0', '7.1', '7.2', '7.3'])
18+
19+
def generate_kibana_discover_url(rule, match):
20+
''' Creates a link for a kibana discover app. '''
21+
22+
discover_app_url = rule.get('kibana_discover_app_url')
23+
if not discover_app_url:
24+
logging.warning(
25+
'Missing kibana_discover_app_url for rule %s' % (
26+
rule.get('name', '<MISSING NAME>')
27+
)
28+
)
29+
return None
30+
31+
kibana_version = rule.get('kibana_discover_version')
32+
if not kibana_version:
33+
logging.warning(
34+
'Missing kibana_discover_version for rule %s' % (
35+
rule.get('name', '<MISSING NAME>')
36+
)
37+
)
38+
return None
39+
40+
index = rule.get('kibana_discover_index_pattern_id')
41+
if not index:
42+
logging.warning(
43+
'Missing kibana_discover_index_pattern_id for rule %s' % (
44+
rule.get('name', '<MISSING NAME>')
45+
)
46+
)
47+
return None
48+
49+
columns = rule.get('kibana_discover_columns', ['_source'])
50+
filters = rule.get('filter', [])
51+
52+
if 'query_key' in rule:
53+
query_keys = rule.get('compound_query_key', [rule['query_key']])
54+
else:
55+
query_keys = []
56+
57+
timestamp = lookup_es_key(match, rule['timestamp_field'])
58+
timeframe = rule.get('timeframe', kibana_default_timedelta)
59+
from_timedelta = rule.get('kibana_discover_from_timedelta', timeframe)
60+
from_time = ts_add(timestamp, -from_timedelta)
61+
to_timedelta = rule.get('kibana_discover_to_timedelta', timeframe)
62+
to_time = ts_add(timestamp, to_timedelta)
63+
64+
if kibana_version in kibana5_kibana6_versions:
65+
globalState = kibana6_disover_global_state(from_time, to_time)
66+
appState = kibana_discover_app_state(index, columns, filters, query_keys, match)
67+
68+
elif kibana_version in kibana7_versions:
69+
globalState = kibana7_disover_global_state(from_time, to_time)
70+
appState = kibana_discover_app_state(index, columns, filters, query_keys, match)
71+
72+
else:
73+
logging.warning(
74+
'Unknown kibana discover application version %s for rule %s' % (
75+
kibana_version,
76+
rule.get('name', '<MISSING NAME>')
77+
)
78+
)
79+
return None
80+
81+
return "%s?_g=%s&_a=%s" % (
82+
os.path.expandvars(discover_app_url),
83+
urllib.parse.quote(globalState),
84+
urllib.parse.quote(appState)
85+
)
86+
87+
88+
def kibana6_disover_global_state(from_time, to_time):
89+
return prison.dumps( {
90+
'refreshInterval': {
91+
'pause': True,
92+
'value': 0
93+
},
94+
'time': {
95+
'from': from_time,
96+
'mode': 'absolute',
97+
'to': to_time
98+
}
99+
} )
100+
101+
102+
def kibana7_disover_global_state(from_time, to_time):
103+
return prison.dumps( {
104+
'filters': [],
105+
'refreshInterval': {
106+
'pause': True,
107+
'value': 0
108+
},
109+
'time': {
110+
'from': from_time,
111+
'to': to_time
112+
}
113+
} )
114+
115+
116+
def kibana_discover_app_state(index, columns, filters, query_keys, match):
117+
app_filters = []
118+
119+
if filters:
120+
bool_filter = { 'must': filters }
121+
app_filters.append( {
122+
'$state': {
123+
'store': 'appState'
124+
},
125+
'bool': bool_filter,
126+
'meta': {
127+
'alias': 'filter',
128+
'disabled': False,
129+
'index': index,
130+
'key': 'bool',
131+
'negate': False,
132+
'type': 'custom',
133+
'value': json.dumps(bool_filter, separators=(',', ':'))
134+
},
135+
} )
136+
137+
for query_key in query_keys:
138+
query_value = lookup_es_key(match, query_key)
139+
140+
if query_value is None:
141+
app_filters.append( {
142+
'$state': {
143+
'store': 'appState'
144+
},
145+
'exists': {
146+
'field': query_key
147+
},
148+
'meta': {
149+
'alias': None,
150+
'disabled': False,
151+
'index': index,
152+
'key': query_key,
153+
'negate': True,
154+
'type': 'exists',
155+
'value': 'exists'
156+
}
157+
} )
158+
159+
else:
160+
app_filters.append( {
161+
'$state': {
162+
'store': 'appState'
163+
},
164+
'meta': {
165+
'alias': None,
166+
'disabled': False,
167+
'index': index,
168+
'key': query_key,
169+
'negate': False,
170+
'params': {
171+
'query': query_value,
172+
'type': 'phrase'
173+
},
174+
'type': 'phrase',
175+
'value': str(query_value)
176+
},
177+
'query': {
178+
'match': {
179+
query_key: {
180+
'query': query_value,
181+
'type': 'phrase'
182+
}
183+
}
184+
}
185+
} )
186+
187+
return prison.dumps( {
188+
'columns': columns,
189+
'filters': app_filters,
190+
'index': index,
191+
'interval': 'auto'
192+
} )

0 commit comments

Comments
 (0)