Skip to content

Commit bd063ab

Browse files
committed
Generate random data in rrd files
The checkmk container runs only for seconds before the e2e tests run, so no data is available when displaying metrics from this container. The script added watches for rrds beeing created and generates some random data, so metrics for the last 5 hours are available, even the container was started just seconds before.
1 parent 334aef7 commit bd063ab

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# this file is started by executing `generate_random_rrd_data.sh`
2+
# the docker entry-point-script will not start it directly because the
3+
# execution permissions are missing.
4+
# some paths are hard coded for usage in the checkmk docker container.
5+
6+
"""
7+
Tool to create data in rrd files.
8+
Paths are hard coded for usage inside of cmk docker container.
9+
10+
* using watchdog to wait for modification event to detect recent created rrd
11+
files, but after the configuration is written to the rrd files.
12+
* as no old data can be inserted into rrd files, the rrd files are then
13+
recreated using the configuration of the original rrd files
14+
* then random data for the previous 5 hours is inserted
15+
* different random data generator (random, static, sine) are used to generate
16+
the data
17+
* an internal list of modified files makes sure files are only updated once
18+
"""
19+
20+
21+
import math
22+
import os
23+
import random
24+
import re
25+
import time
26+
from collections import defaultdict
27+
28+
import rrdtool
29+
from watchdog.events import PatternMatchingEventHandler
30+
from watchdog.observers import Observer
31+
32+
33+
def random_random_function():
34+
choice = random.randint(0, 2)
35+
if choice == 0:
36+
value = random.randint(0, 500)
37+
return lambda x: value
38+
if choice == 1:
39+
return lambda x: random.random() * 100
40+
if choice == 2:
41+
offset = random.random()
42+
frequency = random.random()
43+
return lambda x: math.sin(offset + x / (1000.0 * (1 + frequency))) * 100
44+
raise Exception()
45+
46+
47+
def clone(original_rrd, clone_rrd):
48+
info = rrdtool.info(original_rrd)
49+
50+
metrics = set()
51+
for key, value in info.items():
52+
match = re.match(r"^ds\[([0-9]+)\]\.([^.]+)$", key)
53+
if match:
54+
id, ds_key = match.groups()
55+
metrics.add(id)
56+
if ds_key == "type" and value != "GAUGE":
57+
raise NotImplementedError(value)
58+
if ds_key == "minimal_heartbeat" and value != 8460:
59+
raise NotImplementedError(value)
60+
metrics_count = len(metrics)
61+
62+
rra_info = defaultdict(dict)
63+
for key, value in info.items():
64+
match = re.match(r"^rra\[([0-9]+)\]\.([^.]+)$", key)
65+
if match:
66+
id, rra_key = match.groups()
67+
rra_info[id][rra_key] = value
68+
69+
dss = []
70+
for i in range(1, metrics_count + 1):
71+
dss.append(f"DS:{i}:GAUGE:8460:U:U") # assuming fixed value here, see above
72+
rras = []
73+
for id, rra_info in rra_info.items():
74+
cf = rra_info["cf"]
75+
xff = rra_info["xff"]
76+
rows = rra_info["rows"]
77+
steps = rra_info["pdp_per_row"]
78+
rras.append(f"RRA:{cf}:{xff}:{steps}:{rows}")
79+
80+
rrdtool.create(
81+
clone_rrd,
82+
"--start",
83+
"0",
84+
"--step",
85+
"60",
86+
*dss,
87+
*rras,
88+
)
89+
90+
return metrics_count
91+
92+
93+
def modify_in_place(filename):
94+
filename_clone = "/tmp/clone.rrd"
95+
metrics_count = clone(filename, filename_clone)
96+
97+
random.seed(filename)
98+
99+
now = int(time.time())
100+
randoms = [random_random_function() for _ in range(metrics_count)]
101+
for t in range(now - 5 * 60 * 60, now, 60):
102+
metrics = ":".join(str(r(t)) for r in randoms)
103+
rrdtool.update(filename_clone, f"{int(t)}:{metrics}")
104+
105+
os.rename(filename_clone, filename)
106+
107+
# with open("original.json", "w") as original:
108+
# json.dump(rrdtool.info("Check_MK.rrd"), original, sort_keys=True, indent=4)
109+
# with open("clone.json", "w") as clone_fo:
110+
# json.dump(rrdtool.info("clone.rrd"), clone_fo, sort_keys=True, indent=4)
111+
112+
113+
class Handler(PatternMatchingEventHandler):
114+
def __init__(self, *arg, **kwargs):
115+
super().__init__(*arg, **kwargs)
116+
self.seen_filenames = set()
117+
118+
def on_modified(self, event):
119+
filename = event.src_path
120+
if filename in self.seen_filenames:
121+
return
122+
self.seen_filenames.add(filename)
123+
print(f"[GRRD] {filename}")
124+
modify_in_place(filename)
125+
126+
127+
def main():
128+
# hooks are called sync, so we have to fork
129+
if os.environ.get("GRRD_FORK"):
130+
pid = os.fork()
131+
if pid != 0:
132+
# parent process
133+
return
134+
# forking done
135+
136+
observer = Observer()
137+
observer.schedule(
138+
Handler(patterns=["*.rrd"], ignore_directories=True),
139+
"/omd/sites/cmk/var/check_mk/rrd/",
140+
recursive=True,
141+
)
142+
observer.start()
143+
try:
144+
while True:
145+
time.sleep(10)
146+
except KeyboardInterrupt:
147+
print("graceful shutdown")
148+
observer.stop()
149+
observer.join()
150+
151+
152+
if __name__ == "__main__":
153+
main()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
su - cmk -c "/usr/bin/env bash" << __EOF__
4+
python3 -m pip install watchdog
5+
export GRRD_FORK=1
6+
python3 -u /docker-entrypoint.d/post-start/generate_random_rrd_data.py
7+
__EOF__

0 commit comments

Comments
 (0)