Skip to content

Commit 5c38504

Browse files
Cleanup Kibana Parameter Source
With this commit we remove some dead code from the Kibana parameter source. We also turn `dashboard` into a mandatory parameter and add tests. Relates elastic#53
1 parent 5426f7f commit 5c38504

File tree

5 files changed

+137
-37
lines changed

5 files changed

+137
-37
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ The generator allows data to be generated in real-time or against a set date/tin
312312

313313
### elasticlogs\_kibana\_source
314314

315-
This parameter source supports simulating two different types of dashboards.
315+
This parameter source supports simulating three different types of dashboards. One of the following needs to be selected by specifying the mandatory parameter `dashboard`:
316316

317317
**traffic** - This dashboard contains 7 visualisations and presents different types of traffic statistics. In structure it is similar to the `Nginx Overview` dashboard that comes with the Filebeat Nginx Module. It does aggregate across all records in the index and is therefore a quite 'heavy' dashboard.
318318

eventdata/parameter_sources/elasticlogs_kibana_source.py

+9-31
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class ElasticlogsKibanaSource:
5555
Simulates a set of sample Kibana dashboards for the elasticlogs data set.
5656
5757
It expects the parameter hash to contain the following keys:
58-
"dashboard" - String indicating which dashboard to simulate. Options are 'traffic', 'content_issues' and 'discover'. Defaults to 'traffic'.
58+
"dashboard" - String indicating which dashboard to simulate. Options are 'traffic', 'content_issues' and 'discover'.
5959
"query_string" - String indicating file to load or list of strings indicating actual query parameters to randomize during benchmarking. Defaults
6060
to ["*"], If a list has been specified, a random value will be selected.
6161
"index_pattern" - String or list of strings representing the index pattern to query. Defaults to 'elasticlogs-*'. If a list has
@@ -85,13 +85,14 @@ def __init__(self, track, params, **kwargs):
8585
self._indices = track.indices
8686
self._index_pattern = params.get("index_pattern", "elasticlogs-*")
8787
self._query_string_list = ["*"]
88-
self._dashboard = "traffic"
88+
self._dashboard = params["dashboard"]
8989
self._discover_size = params.get("discover_size", 500)
9090
self._ignore_throttled = params.get("ignore_throttled", True)
9191
self._debug = params.get("debug", False)
9292
self._pre_filter_shard_size = params.get("pre_filter_shard_size", 1)
9393
self._window_length = params.get("window_length", "1d")
9494
self.infinite = True
95+
self.utcnow = kwargs.get("utcnow", datetime.datetime.utcnow)
9596

9697
random.seed()
9798

@@ -106,11 +107,8 @@ def __init__(self, track, params, **kwargs):
106107
else:
107108
self._query_string_list = params["query_string"]
108109

109-
if "dashboard" in params.keys():
110-
if params["dashboard"] in available_dashboards:
111-
self._dashboard = params["dashboard"]
112-
else:
113-
logger.info("[kibana] Illegal dashboard configured (%s). Using default dashboard instead.", params["dashboard"])
110+
if self._dashboard not in available_dashboards:
111+
raise ConfigurationError("Unknown dashboard [{}]. Must be one of {}.".format(self._dashboard, available_dashboards))
114112

115113
key = "{}_@timestamp".format(self._index_pattern)
116114
if key in gs.global_fieldstats.keys():
@@ -121,8 +119,8 @@ def __init__(self, track, params, **kwargs):
121119
else:
122120
self._fieldstats_provided = False
123121

124-
re1 = re.compile("^(\d+\.*\d*)([dhm])$")
125-
re2 = re.compile("^(\d+\.*\d*)%$")
122+
re1 = re.compile(r"^(\d+\.*\d*)([dhm])$")
123+
re2 = re.compile(r"^(\d+\.*\d*)%$")
126124

127125
m1 = re1.match(self._window_length)
128126
m2 = re2.match(self._window_length)
@@ -218,7 +216,7 @@ def __window_boundary_to_ms(self, wb):
218216
return wb["ts_ms"]
219217

220218
def __current_epoch_ms(self):
221-
dt = datetime.datetime.utcnow()
219+
dt = self.utcnow()
222220
return (dt-epoch).total_seconds() *1000
223221

224222
def __parse_window_parameters(self, window_spec):
@@ -250,7 +248,7 @@ def __parse_window_parameters(self, window_spec):
250248

251249
window_end_spec.append({"type": "relative", "offset_ms": offset_ms})
252250
elif m2:
253-
c = re.split("\D+", i)
251+
c = re.split(r"\D+", i)
254252

255253
dt = datetime.datetime.strptime("{} {} {} {} {} {} UTC".format(c[0], c[1], c[2], c[3], c[4], c[5]), "%Y %m %d %H %M %S %Z")
256254
epoch_ms = (int)((dt-epoch).total_seconds() * 1000)
@@ -279,21 +277,6 @@ def __parse_window_parameters(self, window_spec):
279277

280278
return window_end_spec
281279

282-
def __unit_string_to_milliseconds(self, string):
283-
m = re.match(r"^(\d+\.*\d*)([dhm])$", string)
284-
if m:
285-
val = float(m.group(1))
286-
unit = m.group(2)
287-
288-
if unit == "m":
289-
return False, int(60*val*1000)
290-
elif unit == "h":
291-
return False, int(3600*val*1000)
292-
else:
293-
return False, int(86400*val*1000)
294-
else:
295-
return True, None
296-
297280
def __determine_interval(self, window_size_seconds, target_bars, max_bars):
298281
available_intervals = [
299282
{"unit": "1s", "length_sec": 1},
@@ -341,11 +324,6 @@ def __determine_interval(self, window_size_seconds, target_bars, max_bars):
341324
def __get_preference(self):
342325
return int(round(time.time() * 1000))
343326

344-
def __print_ts(self, ts):
345-
ts_s = int(ts / 1000)
346-
dt = datetime.datetime.utcfromtimestamp(ts_s)
347-
return dt.isoformat()
348-
349327
def __content_issues_dashboard(self, index_pattern, query_string, interval, ts_min_ms, ts_max_ms, ignore_throttled):
350328
preference = self.__get_preference()
351329
header = {

tests/parameter_sources/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@
1414
# KIND, either express or implied. See the License for the
1515
# specific language governing permissions and limitations
1616
# under the License.
17+
18+
19+
class StaticTrack:
20+
def __init__(self):
21+
self.indices = []

tests/parameter_sources/elasticlogs_bulk_source_test.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
from eventdata.parameter_sources.elasticlogs_bulk_source import ElasticlogsBulkSource
19+
from tests.parameter_sources import StaticTrack
1920

2021

2122
class StaticEventGenerator:
@@ -32,11 +33,6 @@ def generate_event(self):
3233
return self.doc, self.index, self.type
3334

3435

35-
class StaticTrack:
36-
def __init__(self):
37-
self.indices = []
38-
39-
4036
def test_generates_a_complete_bulk():
4137
expected_bulk_size = 10
4238

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
from datetime import datetime
19+
from unittest import mock
20+
21+
import pytest
22+
23+
from eventdata.parameter_sources.elasticlogs_kibana_source import ElasticlogsKibanaSource, ConfigurationError
24+
from tests.parameter_sources import StaticTrack
25+
26+
27+
@mock.patch("time.time")
28+
def test_create_discover(time):
29+
time.return_value = 5000
30+
31+
param_source = ElasticlogsKibanaSource(track=StaticTrack(), params={
32+
"dashboard": "discover"
33+
}, utcnow=lambda: datetime(year=2019, month=11, day=11))
34+
response = param_source.params()
35+
36+
assert response == {
37+
"body": [
38+
{
39+
"index": "elasticlogs-*",
40+
"ignore_unavailable": True,
41+
"preference": 5000000,
42+
"ignore_throttled": True
43+
},
44+
{
45+
"version": True,
46+
"size": 500,
47+
"sort": [
48+
{"@timestamp": {"order": "desc", "unmapped_type": "boolean"}}
49+
],
50+
"_source": {"excludes": []},
51+
"aggs": {
52+
"2": {
53+
"date_histogram": {
54+
"field": "@timestamp",
55+
"interval": "30m",
56+
"time_zone": "Europe/London",
57+
"min_doc_count": 1}
58+
}
59+
},
60+
"stored_fields": ["*"],
61+
"script_fields": {},
62+
"docvalue_fields": ["@timestamp"],
63+
"query": {
64+
"bool": {
65+
"must": [
66+
{
67+
"query_string": {
68+
"query": "*",
69+
"analyze_wildcard": True,
70+
"default_field": "*"
71+
}
72+
},
73+
{
74+
"range": {
75+
"@timestamp": {
76+
"gte": 1573344000000,
77+
"lte": 1573430400000,
78+
"format": "epoch_millis"
79+
}
80+
}
81+
}
82+
],
83+
"filter": [],
84+
"should": [],
85+
"must_not": []
86+
}
87+
},
88+
"highlight": {
89+
"pre_tags": ["@kibana-highlighted-field@"],
90+
"post_tags": ["@/kibana-highlighted-field@"],
91+
"fields": {"*": {}},
92+
"fragment_size": 2147483647
93+
}
94+
}
95+
],
96+
"meta_data": {
97+
"interval": "30m",
98+
"index_pattern": "elasticlogs-*",
99+
"query_string": "*",
100+
"dashboard": "discover",
101+
"window_length": "1d",
102+
"ignore_throttled": True,
103+
"pre_filter_shard_size": 1,
104+
"debug": False
105+
}
106+
}
107+
108+
109+
def test_dashboard_is_mandatory():
110+
with pytest.raises(KeyError) as ex:
111+
ElasticlogsKibanaSource(track=StaticTrack(), params={})
112+
113+
assert "'dashboard'" == str(ex.value)
114+
115+
116+
def test_invalid_dashboard_raises_error():
117+
with pytest.raises(ConfigurationError) as ex:
118+
ElasticlogsKibanaSource(track=StaticTrack(), params={"dashboard": "unknown"})
119+
120+
assert "Unknown dashboard [unknown]. Must be one of ['traffic', 'content_issues', 'discover']." == str(ex.value)
121+

0 commit comments

Comments
 (0)