]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/tools/advisor/test/test_rule_parser.py
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / tools / advisor / test / test_rule_parser.py
1 # Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 # This source code is licensed under both the GPLv2 (found in the
3 # COPYING file in the root directory) and Apache 2.0 License
4 # (found in the LICENSE.Apache file in the root directory).
5
6 import os
7 import unittest
8
9 from advisor.db_log_parser import DatabaseLogs, DataSource
10 from advisor.db_options_parser import DatabaseOptions
11 from advisor.rule_parser import RulesSpec
12
13 RuleToSuggestions = {
14 "stall-too-many-memtables": ["inc-bg-flush", "inc-write-buffer"],
15 "stall-too-many-L0": [
16 "inc-max-subcompactions",
17 "inc-max-bg-compactions",
18 "inc-write-buffer-size",
19 "dec-max-bytes-for-level-base",
20 "inc-l0-slowdown-writes-trigger",
21 ],
22 "stop-too-many-L0": [
23 "inc-max-bg-compactions",
24 "inc-write-buffer-size",
25 "inc-l0-stop-writes-trigger",
26 ],
27 "stall-too-many-compaction-bytes": [
28 "inc-max-bg-compactions",
29 "inc-write-buffer-size",
30 "inc-hard-pending-compaction-bytes-limit",
31 "inc-soft-pending-compaction-bytes-limit",
32 ],
33 "level0-level1-ratio": ["l0-l1-ratio-health-check"],
34 }
35
36
37 class TestAllRulesTriggered(unittest.TestCase):
38 def setUp(self):
39 # load the Rules
40 this_path = os.path.abspath(os.path.dirname(__file__))
41 ini_path = os.path.join(this_path, "input_files/triggered_rules.ini")
42 self.db_rules = RulesSpec(ini_path)
43 self.db_rules.load_rules_from_spec()
44 self.db_rules.perform_section_checks()
45 # load the data sources: LOG and OPTIONS
46 log_path = os.path.join(this_path, "input_files/LOG-0")
47 options_path = os.path.join(this_path, "input_files/OPTIONS-000005")
48 db_options_parser = DatabaseOptions(options_path)
49 self.column_families = db_options_parser.get_column_families()
50 db_logs_parser = DatabaseLogs(log_path, self.column_families)
51 self.data_sources = {
52 DataSource.Type.DB_OPTIONS: [db_options_parser],
53 DataSource.Type.LOG: [db_logs_parser],
54 }
55
56 def test_triggered_conditions(self):
57 conditions_dict = self.db_rules.get_conditions_dict()
58 rules_dict = self.db_rules.get_rules_dict()
59 # Make sure none of the conditions is triggered beforehand
60 for cond in conditions_dict.values():
61 self.assertFalse(cond.is_triggered(), repr(cond))
62 for rule in rules_dict.values():
63 self.assertFalse(
64 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
65 )
66
67 # # Trigger the conditions as per the data sources.
68 # trigger_conditions(, conditions_dict)
69
70 # Get the set of rules that have been triggered
71 triggered_rules = self.db_rules.get_triggered_rules(
72 self.data_sources, self.column_families
73 )
74
75 # Make sure each condition and rule is triggered
76 for cond in conditions_dict.values():
77 if cond.get_data_source() is DataSource.Type.TIME_SERIES:
78 continue
79 self.assertTrue(cond.is_triggered(), repr(cond))
80
81 for rule in rules_dict.values():
82 self.assertIn(rule, triggered_rules)
83 # Check the suggestions made by the triggered rules
84 for sugg in rule.get_suggestions():
85 self.assertIn(sugg, RuleToSuggestions[rule.name])
86
87 for rule in triggered_rules:
88 self.assertIn(rule, rules_dict.values())
89 for sugg in RuleToSuggestions[rule.name]:
90 self.assertIn(sugg, rule.get_suggestions())
91
92
93 class TestConditionsConjunctions(unittest.TestCase):
94 def setUp(self):
95 # load the Rules
96 this_path = os.path.abspath(os.path.dirname(__file__))
97 ini_path = os.path.join(this_path, "input_files/test_rules.ini")
98 self.db_rules = RulesSpec(ini_path)
99 self.db_rules.load_rules_from_spec()
100 self.db_rules.perform_section_checks()
101 # load the data sources: LOG and OPTIONS
102 log_path = os.path.join(this_path, "input_files/LOG-1")
103 options_path = os.path.join(this_path, "input_files/OPTIONS-000005")
104 db_options_parser = DatabaseOptions(options_path)
105 self.column_families = db_options_parser.get_column_families()
106 db_logs_parser = DatabaseLogs(log_path, self.column_families)
107 self.data_sources = {
108 DataSource.Type.DB_OPTIONS: [db_options_parser],
109 DataSource.Type.LOG: [db_logs_parser],
110 }
111
112 def test_condition_conjunctions(self):
113 conditions_dict = self.db_rules.get_conditions_dict()
114 rules_dict = self.db_rules.get_rules_dict()
115 # Make sure none of the conditions is triggered beforehand
116 for cond in conditions_dict.values():
117 self.assertFalse(cond.is_triggered(), repr(cond))
118 for rule in rules_dict.values():
119 self.assertFalse(
120 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
121 )
122
123 # Trigger the conditions as per the data sources.
124 self.db_rules.trigger_conditions(self.data_sources)
125
126 # Check for the conditions
127 conds_triggered = ["log-1-true", "log-2-true", "log-3-true"]
128 conds_not_triggered = ["log-4-false", "options-1-false"]
129 for cond in conds_triggered:
130 self.assertTrue(conditions_dict[cond].is_triggered(), repr(cond))
131 for cond in conds_not_triggered:
132 self.assertFalse(conditions_dict[cond].is_triggered(), repr(cond))
133
134 # Check for the rules
135 rules_triggered = ["multiple-conds-true"]
136 rules_not_triggered = [
137 "single-condition-false",
138 "multiple-conds-one-false",
139 "multiple-conds-all-false",
140 ]
141 for rule_name in rules_triggered:
142 rule = rules_dict[rule_name]
143 self.assertTrue(
144 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
145 )
146 for rule_name in rules_not_triggered:
147 rule = rules_dict[rule_name]
148 self.assertFalse(
149 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
150 )
151
152
153 class TestSanityChecker(unittest.TestCase):
154 def setUp(self):
155 this_path = os.path.abspath(os.path.dirname(__file__))
156 ini_path = os.path.join(this_path, "input_files/rules_err1.ini")
157 db_rules = RulesSpec(ini_path)
158 db_rules.load_rules_from_spec()
159 self.rules_dict = db_rules.get_rules_dict()
160 self.conditions_dict = db_rules.get_conditions_dict()
161 self.suggestions_dict = db_rules.get_suggestions_dict()
162
163 def test_rule_missing_suggestions(self):
164 regex = ".*rule must have at least one suggestion.*"
165 with self.assertRaisesRegex(ValueError, regex):
166 self.rules_dict["missing-suggestions"].perform_checks()
167
168 def test_rule_missing_conditions(self):
169 regex = ".*rule must have at least one condition.*"
170 with self.assertRaisesRegex(ValueError, regex):
171 self.rules_dict["missing-conditions"].perform_checks()
172
173 def test_condition_missing_regex(self):
174 regex = ".*provide regex for log condition.*"
175 with self.assertRaisesRegex(ValueError, regex):
176 self.conditions_dict["missing-regex"].perform_checks()
177
178 def test_condition_missing_options(self):
179 regex = ".*options missing in condition.*"
180 with self.assertRaisesRegex(ValueError, regex):
181 self.conditions_dict["missing-options"].perform_checks()
182
183 def test_condition_missing_expression(self):
184 regex = ".*expression missing in condition.*"
185 with self.assertRaisesRegex(ValueError, regex):
186 self.conditions_dict["missing-expression"].perform_checks()
187
188 def test_suggestion_missing_option(self):
189 regex = ".*provide option or description.*"
190 with self.assertRaisesRegex(ValueError, regex):
191 self.suggestions_dict["missing-option"].perform_checks()
192
193 def test_suggestion_missing_description(self):
194 regex = ".*provide option or description.*"
195 with self.assertRaisesRegex(ValueError, regex):
196 self.suggestions_dict["missing-description"].perform_checks()
197
198
199 class TestParsingErrors(unittest.TestCase):
200 def setUp(self):
201 self.this_path = os.path.abspath(os.path.dirname(__file__))
202
203 def test_condition_missing_source(self):
204 ini_path = os.path.join(self.this_path, "input_files/rules_err2.ini")
205 db_rules = RulesSpec(ini_path)
206 regex = ".*provide source for condition.*"
207 with self.assertRaisesRegex(NotImplementedError, regex):
208 db_rules.load_rules_from_spec()
209
210 def test_suggestion_missing_action(self):
211 ini_path = os.path.join(self.this_path, "input_files/rules_err3.ini")
212 db_rules = RulesSpec(ini_path)
213 regex = ".*provide action for option.*"
214 with self.assertRaisesRegex(ValueError, regex):
215 db_rules.load_rules_from_spec()
216
217 def test_section_no_name(self):
218 ini_path = os.path.join(self.this_path, "input_files/rules_err4.ini")
219 db_rules = RulesSpec(ini_path)
220 regex = "Parsing error: needed section header:.*"
221 with self.assertRaisesRegex(ValueError, regex):
222 db_rules.load_rules_from_spec()
223
224
225 if __name__ == "__main__":
226 unittest.main()