]>
Commit | Line | Data |
---|---|---|
116b2602 | 1 | #!/usr/bin/env python |
13291274 | 2 | import os |
116b2602 RG |
3 | import requests |
4 | import subprocess | |
13291274 | 5 | import unittest |
630eb526 | 6 | from dnsdisttests import DNSDistTest, pickAvailablePort |
116b2602 | 7 | |
13291274 | 8 | @unittest.skipIf('SKIP_PROMETHEUS_TESTS' in os.environ, 'Prometheus tests are disabled') |
116b2602 RG |
9 | class TestPrometheus(DNSDistTest): |
10 | ||
11 | _webTimeout = 2.0 | |
630eb526 | 12 | _webServerPort = pickAvailablePort() |
116b2602 | 13 | _webServerBasicAuthPassword = 'secret' |
2c0392a5 | 14 | _webServerBasicAuthPasswordHashed = '$scrypt$ln=10,p=1,r=8$6DKLnvUYEeXWh3JNOd3iwg==$kSrhdHaRbZ7R74q3lGBqO1xetgxRxhmWzYJ2Qvfm7JM=' |
116b2602 | 15 | _webServerAPIKey = 'apisecret' |
2c0392a5 | 16 | _webServerAPIKeyHashed = '$scrypt$ln=10,p=1,r=8$9v8JxDfzQVyTpBkTbkUqYg==$bDQzAOHeK1G9UvTPypNhrX48w974ZXbFPtRKS34+aso=' |
412f99ef | 17 | _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPasswordHashed', '_webServerAPIKeyHashed'] |
116b2602 RG |
18 | _config_template = """ |
19 | newServer{address="127.0.0.1:%s"} | |
fa7e8b5d | 20 | webserver("127.0.0.1:%s") |
cfe95ada | 21 | setWebserverConfig({password="%s", apiKey="%s"}) |
525f14f0 RG |
22 | pc = newPacketCache(100, {maxTTL=86400, minTTL=1}) |
23 | getPool(""):setCache(pc) | |
e78fc5bd RG |
24 | |
25 | -- test custom metrics as well | |
26 | declareMetric('custom-metric1', 'counter', 'Custom counter') | |
27 | incMetric('custom-metric1') | |
28 | declareMetric('custom-metric2', 'gauge', 'Custom gauge') | |
29 | -- and custom names | |
30 | declareMetric('custom-metric3', 'counter', 'Custom counter', 'custom_prometheus_name') | |
116b2602 RG |
31 | """ |
32 | ||
33 | def checkPrometheusContentBasic(self, content): | |
34 | for line in content.splitlines(): | |
35 | if line.startswith('# HELP'): | |
36 | tokens = line.split(' ') | |
37 | self.assertGreaterEqual(len(tokens), 4) | |
38 | elif line.startswith('# TYPE'): | |
39 | tokens = line.split(' ') | |
4bfebc93 | 40 | self.assertEqual(len(tokens), 4) |
116b2602 RG |
41 | self.assertIn(tokens[3], ['counter', 'gauge', 'histogram']) |
42 | elif not line.startswith('#'): | |
43 | tokens = line.split(' ') | |
4bfebc93 | 44 | self.assertEqual(len(tokens), 2) |
e78fc5bd | 45 | if not line.startswith('dnsdist_') and not line.startswith('custom_prometheus_name'): |
116b2602 RG |
46 | raise AssertionError('Expecting prometheus metric to be prefixed by \'dnsdist_\', got: "%s"' % (line)) |
47 | ||
e78fc5bd RG |
48 | def checkMetric(self, content, name, expectedType, expectedValue): |
49 | typeFound = False | |
50 | helpFound = False | |
51 | valueFound = False | |
52 | for line in content.splitlines(): | |
53 | if name in str(line): | |
54 | tokens = line.split(' ') | |
55 | if line.startswith('# HELP'): | |
56 | self.assertGreaterEqual(len(tokens), 4) | |
57 | if tokens[2] == name: | |
58 | helpFound = True | |
59 | elif line.startswith('# TYPE'): | |
60 | self.assertEqual(len(tokens), 4) | |
61 | if tokens[2] == name: | |
62 | typeFound = True | |
63 | self.assertEqual(tokens[3], expectedType) | |
64 | elif not line.startswith('#'): | |
65 | self.assertEqual(len(tokens), 2) | |
66 | if tokens[0] == name: | |
67 | valueFound = True | |
68 | self.assertEqual(int(tokens[1]), expectedValue) | |
69 | ||
70 | self.assertTrue(typeFound) | |
71 | self.assertTrue(helpFound) | |
72 | self.assertTrue(valueFound) | |
73 | ||
116b2602 RG |
74 | def checkPrometheusContentPromtool(self, content): |
75 | output = None | |
76 | try: | |
77 | testcmd = ['promtool', 'check', 'metrics'] | |
78 | process = subprocess.Popen(testcmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) | |
79 | output = process.communicate(input=content) | |
80 | except subprocess.CalledProcessError as exc: | |
81 | raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, process.output)) | |
82 | ||
83 | # commented out because promtool returns 3 because of the "_total" suffix warnings | |
84 | #if process.returncode != 0: | |
85 | # raise AssertionError('%s failed (%d): %s' % (testcmd, process.returncode, output)) | |
86 | ||
87 | for line in output[0].splitlines(): | |
88 | if line.endswith(b"should have \"_total\" suffix"): | |
89 | continue | |
90 | raise AssertionError('%s returned an unexpected output. Faulty line is "%s", complete content is "%s"' % (testcmd, line, output)) | |
91 | ||
92 | def testMetrics(self): | |
93 | """ | |
94 | Prometheus: Retrieve metrics | |
95 | """ | |
96 | url = 'http://127.0.0.1:' + str(self._webServerPort) + '/metrics' | |
97 | r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout) | |
98 | self.assertTrue(r) | |
4bfebc93 | 99 | self.assertEqual(r.status_code, 200) |
116b2602 RG |
100 | self.checkPrometheusContentBasic(r.text) |
101 | self.checkPrometheusContentPromtool(r.content) | |
e78fc5bd RG |
102 | self.checkMetric(r.text, 'dnsdist_custom_metric1', 'counter', 1) |
103 | self.checkMetric(r.text, 'dnsdist_custom_metric2', 'gauge', 0) | |
104 | self.checkMetric(r.text, 'custom_prometheus_name', 'counter', 0) |