]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
11fdf7f2
TL
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
6import os
7import unittest
1e59de90 8
11fdf7f2
TL
9from advisor.db_log_parser import DatabaseLogs, DataSource
10from advisor.db_options_parser import DatabaseOptions
1e59de90 11from advisor.rule_parser import RulesSpec
11fdf7f2
TL
12
13RuleToSuggestions = {
1e59de90 14 "stall-too-many-memtables": ["inc-bg-flush", "inc-write-buffer"],
11fdf7f2 15 "stall-too-many-L0": [
1e59de90
TL
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",
11fdf7f2
TL
21 ],
22 "stop-too-many-L0": [
1e59de90
TL
23 "inc-max-bg-compactions",
24 "inc-write-buffer-size",
25 "inc-l0-stop-writes-trigger",
11fdf7f2
TL
26 ],
27 "stall-too-many-compaction-bytes": [
1e59de90
TL
28 "inc-max-bg-compactions",
29 "inc-write-buffer-size",
30 "inc-hard-pending-compaction-bytes-limit",
31 "inc-soft-pending-compaction-bytes-limit",
11fdf7f2 32 ],
1e59de90 33 "level0-level1-ratio": ["l0-l1-ratio-health-check"],
11fdf7f2
TL
34}
35
36
37class TestAllRulesTriggered(unittest.TestCase):
38 def setUp(self):
39 # load the Rules
40 this_path = os.path.abspath(os.path.dirname(__file__))
1e59de90 41 ini_path = os.path.join(this_path, "input_files/triggered_rules.ini")
11fdf7f2
TL
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
1e59de90
TL
46 log_path = os.path.join(this_path, "input_files/LOG-0")
47 options_path = os.path.join(this_path, "input_files/OPTIONS-000005")
11fdf7f2
TL
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],
1e59de90 53 DataSource.Type.LOG: [db_logs_parser],
11fdf7f2
TL
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(
1e59de90 64 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
11fdf7f2
TL
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
93class TestConditionsConjunctions(unittest.TestCase):
94 def setUp(self):
95 # load the Rules
96 this_path = os.path.abspath(os.path.dirname(__file__))
1e59de90 97 ini_path = os.path.join(this_path, "input_files/test_rules.ini")
11fdf7f2
TL
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
1e59de90
TL
102 log_path = os.path.join(this_path, "input_files/LOG-1")
103 options_path = os.path.join(this_path, "input_files/OPTIONS-000005")
11fdf7f2
TL
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],
1e59de90 109 DataSource.Type.LOG: [db_logs_parser],
11fdf7f2
TL
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(
1e59de90 120 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
11fdf7f2
TL
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
1e59de90
TL
127 conds_triggered = ["log-1-true", "log-2-true", "log-3-true"]
128 conds_not_triggered = ["log-4-false", "options-1-false"]
11fdf7f2
TL
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
1e59de90 135 rules_triggered = ["multiple-conds-true"]
11fdf7f2 136 rules_not_triggered = [
1e59de90
TL
137 "single-condition-false",
138 "multiple-conds-one-false",
139 "multiple-conds-all-false",
11fdf7f2
TL
140 ]
141 for rule_name in rules_triggered:
142 rule = rules_dict[rule_name]
143 self.assertTrue(
1e59de90 144 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
11fdf7f2
TL
145 )
146 for rule_name in rules_not_triggered:
147 rule = rules_dict[rule_name]
148 self.assertFalse(
1e59de90 149 rule.is_triggered(conditions_dict, self.column_families), repr(rule)
11fdf7f2
TL
150 )
151
152
153class TestSanityChecker(unittest.TestCase):
154 def setUp(self):
155 this_path = os.path.abspath(os.path.dirname(__file__))
1e59de90 156 ini_path = os.path.join(this_path, "input_files/rules_err1.ini")
11fdf7f2
TL
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):
1e59de90 164 regex = ".*rule must have at least one suggestion.*"
11fdf7f2 165 with self.assertRaisesRegex(ValueError, regex):
1e59de90 166 self.rules_dict["missing-suggestions"].perform_checks()
11fdf7f2
TL
167
168 def test_rule_missing_conditions(self):
1e59de90 169 regex = ".*rule must have at least one condition.*"
11fdf7f2 170 with self.assertRaisesRegex(ValueError, regex):
1e59de90 171 self.rules_dict["missing-conditions"].perform_checks()
11fdf7f2
TL
172
173 def test_condition_missing_regex(self):
1e59de90 174 regex = ".*provide regex for log condition.*"
11fdf7f2 175 with self.assertRaisesRegex(ValueError, regex):
1e59de90 176 self.conditions_dict["missing-regex"].perform_checks()
11fdf7f2
TL
177
178 def test_condition_missing_options(self):
1e59de90 179 regex = ".*options missing in condition.*"
11fdf7f2 180 with self.assertRaisesRegex(ValueError, regex):
1e59de90 181 self.conditions_dict["missing-options"].perform_checks()
11fdf7f2
TL
182
183 def test_condition_missing_expression(self):
1e59de90 184 regex = ".*expression missing in condition.*"
11fdf7f2 185 with self.assertRaisesRegex(ValueError, regex):
1e59de90 186 self.conditions_dict["missing-expression"].perform_checks()
11fdf7f2
TL
187
188 def test_suggestion_missing_option(self):
1e59de90 189 regex = ".*provide option or description.*"
11fdf7f2 190 with self.assertRaisesRegex(ValueError, regex):
1e59de90 191 self.suggestions_dict["missing-option"].perform_checks()
11fdf7f2
TL
192
193 def test_suggestion_missing_description(self):
1e59de90 194 regex = ".*provide option or description.*"
11fdf7f2 195 with self.assertRaisesRegex(ValueError, regex):
1e59de90 196 self.suggestions_dict["missing-description"].perform_checks()
11fdf7f2
TL
197
198
199class 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):
1e59de90 204 ini_path = os.path.join(self.this_path, "input_files/rules_err2.ini")
11fdf7f2 205 db_rules = RulesSpec(ini_path)
1e59de90 206 regex = ".*provide source for condition.*"
11fdf7f2
TL
207 with self.assertRaisesRegex(NotImplementedError, regex):
208 db_rules.load_rules_from_spec()
209
210 def test_suggestion_missing_action(self):
1e59de90 211 ini_path = os.path.join(self.this_path, "input_files/rules_err3.ini")
11fdf7f2 212 db_rules = RulesSpec(ini_path)
1e59de90 213 regex = ".*provide action for option.*"
11fdf7f2
TL
214 with self.assertRaisesRegex(ValueError, regex):
215 db_rules.load_rules_from_spec()
216
217 def test_section_no_name(self):
1e59de90 218 ini_path = os.path.join(self.this_path, "input_files/rules_err4.ini")
11fdf7f2 219 db_rules = RulesSpec(ini_path)
1e59de90 220 regex = "Parsing error: needed section header:.*"
11fdf7f2
TL
221 with self.assertRaisesRegex(ValueError, regex):
222 db_rules.load_rules_from_spec()
223
224
1e59de90 225if __name__ == "__main__":
11fdf7f2 226 unittest.main()