]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | from __future__ import absolute_import |
2 | ||
3 | import time | |
4 | ||
5 | from .helper import DashboardTestCase | |
6 | ||
7 | ||
8 | class ClusterConfigurationTest(DashboardTestCase): | |
9 | ||
10 | def test_list(self): | |
11 | data = self._get('/api/cluster_conf') | |
12 | self.assertStatus(200) | |
13 | self.assertIsInstance(data, list) | |
14 | self.assertGreater(len(data), 1000) | |
15 | for conf in data: | |
16 | self._validate_single(conf) | |
17 | ||
18 | def test_get(self): | |
19 | data = self._get('/api/cluster_conf/admin_socket') | |
20 | self.assertStatus(200) | |
21 | self._validate_single(data) | |
22 | self.assertIn('enum_values', data) | |
23 | ||
24 | data = self._get('/api/cluster_conf/fantasy_name') | |
25 | self.assertStatus(404) | |
26 | ||
27 | def test_get_specific_db_config_option(self): | |
28 | config_name = 'mon_allow_pool_delete' | |
29 | ||
30 | orig_value = self._get_config_by_name(config_name) | |
31 | ||
32 | self._ceph_cmd(['config', 'set', 'mon', config_name, 'true']) | |
33 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
34 | [{'section': 'mon', 'value': 'true'}]) | |
35 | self.assertEqual(result, [{'section': 'mon', 'value': 'true'}]) | |
36 | ||
37 | self._ceph_cmd(['config', 'set', 'mon', config_name, 'false']) | |
38 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
39 | [{'section': 'mon', 'value': 'false'}]) | |
40 | self.assertEqual(result, [{'section': 'mon', 'value': 'false'}]) | |
41 | ||
42 | # restore value | |
43 | if orig_value: | |
44 | self._ceph_cmd(['config', 'set', 'mon', config_name, orig_value[0]['value']]) | |
45 | ||
81eedcae TL |
46 | def test_filter_config_options(self): |
47 | config_names = ['osd_scrub_during_recovery', 'osd_scrub_begin_hour', 'osd_scrub_end_hour'] | |
48 | data = self._get('/api/cluster_conf/filter?names={}'.format(','.join(config_names))) | |
49 | self.assertStatus(200) | |
50 | self.assertIsInstance(data, list) | |
51 | self.assertEqual(len(data), 3) | |
52 | for conf in data: | |
53 | self._validate_single(conf) | |
54 | self.assertIn(conf['name'], config_names) | |
55 | ||
56 | def test_filter_config_options_empty_names(self): | |
57 | self._get('/api/cluster_conf/filter?names=') | |
58 | self.assertStatus(404) | |
59 | self.assertEqual(self._resp.json()['detail'], 'Config options `` not found') | |
60 | ||
61 | def test_filter_config_options_unknown_name(self): | |
62 | self._get('/api/cluster_conf/filter?names=abc') | |
63 | self.assertStatus(404) | |
64 | self.assertEqual(self._resp.json()['detail'], 'Config options `abc` not found') | |
65 | ||
66 | def test_filter_config_options_contains_unknown_name(self): | |
67 | config_names = ['osd_scrub_during_recovery', 'osd_scrub_begin_hour', 'abc'] | |
68 | data = self._get('/api/cluster_conf/filter?names={}'.format(','.join(config_names))) | |
69 | self.assertStatus(200) | |
70 | self.assertIsInstance(data, list) | |
71 | self.assertEqual(len(data), 2) | |
72 | for conf in data: | |
73 | self._validate_single(conf) | |
74 | self.assertIn(conf['name'], config_names) | |
75 | ||
11fdf7f2 TL |
76 | def test_create(self): |
77 | config_name = 'debug_ms' | |
78 | orig_value = self._get_config_by_name(config_name) | |
79 | ||
80 | # remove all existing settings for equal preconditions | |
81 | self._clear_all_values_for_config_option(config_name) | |
82 | ||
83 | expected_result = [{'section': 'mon', 'value': '0/3'}] | |
84 | ||
85 | self._post('/api/cluster_conf', { | |
86 | 'name': config_name, | |
87 | 'value': expected_result | |
88 | }) | |
89 | self.assertStatus(201) | |
90 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
91 | expected_result) | |
92 | self.assertEqual(result, expected_result) | |
93 | ||
94 | # reset original value | |
95 | self._clear_all_values_for_config_option(config_name) | |
96 | self._reset_original_values(config_name, orig_value) | |
97 | ||
81eedcae TL |
98 | def test_delete(self): |
99 | config_name = 'debug_ms' | |
100 | orig_value = self._get_config_by_name(config_name) | |
101 | ||
102 | # set a config option | |
103 | expected_result = [{'section': 'mon', 'value': '0/3'}] | |
104 | self._post('/api/cluster_conf', { | |
105 | 'name': config_name, | |
106 | 'value': expected_result | |
107 | }) | |
108 | self.assertStatus(201) | |
109 | self._wait_for_expected_get_result(self._get_config_by_name, config_name, expected_result) | |
110 | ||
111 | # delete it and check if it's deleted | |
112 | self._delete('/api/cluster_conf/{}?section={}'.format(config_name, 'mon')) | |
113 | self.assertStatus(204) | |
114 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, None) | |
115 | self.assertEqual(result, None) | |
116 | ||
117 | # reset original value | |
118 | self._clear_all_values_for_config_option(config_name) | |
119 | self._reset_original_values(config_name, orig_value) | |
120 | ||
11fdf7f2 | 121 | def test_create_cant_update_at_runtime(self): |
81eedcae | 122 | config_name = 'public_bind_addr' # not updatable |
11fdf7f2 TL |
123 | config_value = [{'section': 'global', 'value': 'true'}] |
124 | orig_value = self._get_config_by_name(config_name) | |
125 | ||
126 | # try to set config option and check if it fails | |
127 | self._post('/api/cluster_conf', { | |
128 | 'name': config_name, | |
129 | 'value': config_value | |
130 | }) | |
131 | self.assertStatus(400) | |
132 | self.assertError(code='config_option_not_updatable_at_runtime', | |
133 | component='cluster_configuration', | |
134 | detail='Config option {} is/are not updatable at runtime'.format( | |
135 | config_name)) | |
136 | ||
137 | # check if config option value is still the original one | |
138 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
139 | orig_value) | |
140 | self.assertEqual(result, orig_value) | |
141 | ||
142 | def test_create_two_values(self): | |
143 | config_name = 'debug_ms' | |
144 | orig_value = self._get_config_by_name(config_name) | |
145 | ||
146 | # remove all existing settings for equal preconditions | |
147 | self._clear_all_values_for_config_option(config_name) | |
148 | ||
149 | expected_result = [{'section': 'mon', 'value': '0/3'}, | |
150 | {'section': 'osd', 'value': '0/5'}] | |
151 | ||
152 | self._post('/api/cluster_conf', { | |
153 | 'name': config_name, | |
154 | 'value': expected_result | |
155 | }) | |
156 | self.assertStatus(201) | |
157 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
158 | expected_result) | |
159 | self.assertEqual(result, expected_result) | |
160 | ||
161 | # reset original value | |
162 | self._clear_all_values_for_config_option(config_name) | |
163 | self._reset_original_values(config_name, orig_value) | |
164 | ||
165 | def test_create_can_handle_none_values(self): | |
166 | config_name = 'debug_ms' | |
167 | orig_value = self._get_config_by_name(config_name) | |
168 | ||
169 | # remove all existing settings for equal preconditions | |
170 | self._clear_all_values_for_config_option(config_name) | |
171 | ||
172 | self._post('/api/cluster_conf', { | |
173 | 'name': config_name, | |
174 | 'value': [{'section': 'mon', 'value': '0/3'}, | |
175 | {'section': 'osd', 'value': None}] | |
176 | }) | |
177 | self.assertStatus(201) | |
178 | ||
179 | expected_result = [{'section': 'mon', 'value': '0/3'}] | |
180 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
181 | expected_result) | |
182 | self.assertEqual(result, expected_result) | |
183 | ||
184 | # reset original value | |
185 | self._clear_all_values_for_config_option(config_name) | |
186 | self._reset_original_values(config_name, orig_value) | |
187 | ||
188 | def test_create_can_handle_boolean_values(self): | |
189 | config_name = 'mon_allow_pool_delete' | |
190 | orig_value = self._get_config_by_name(config_name) | |
191 | ||
192 | # remove all existing settings for equal preconditions | |
193 | self._clear_all_values_for_config_option(config_name) | |
194 | ||
195 | expected_result = [{'section': 'mon', 'value': 'true'}] | |
196 | ||
197 | self._post('/api/cluster_conf', { | |
198 | 'name': config_name, | |
199 | 'value': [{'section': 'mon', 'value': True}]}) | |
200 | self.assertStatus(201) | |
201 | ||
202 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
203 | expected_result) | |
204 | self.assertEqual(result, expected_result) | |
205 | ||
206 | # reset original value | |
207 | self._clear_all_values_for_config_option(config_name) | |
208 | self._reset_original_values(config_name, orig_value) | |
209 | ||
210 | def test_bulk_set(self): | |
211 | expected_result = { | |
212 | 'osd_max_backfills': {'section': 'osd', 'value': '1'}, | |
213 | 'osd_recovery_max_active': {'section': 'osd', 'value': '3'}, | |
214 | 'osd_recovery_max_single_start': {'section': 'osd', 'value': '1'}, | |
215 | 'osd_recovery_sleep': {'section': 'osd', 'value': '2.000000'} | |
216 | } | |
217 | orig_values = dict() | |
218 | ||
219 | for config_name in expected_result: | |
220 | orig_values[config_name] = self._get_config_by_name(config_name) | |
221 | ||
222 | # remove all existing settings for equal preconditions | |
223 | self._clear_all_values_for_config_option(config_name) | |
224 | ||
225 | self._put('/api/cluster_conf', {'options': expected_result}) | |
226 | self.assertStatus(200) | |
227 | ||
228 | for config_name, value in expected_result.items(): | |
229 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
230 | [value]) | |
231 | self.assertEqual(result, [value]) | |
232 | ||
233 | # reset original value | |
234 | self._clear_all_values_for_config_option(config_name) | |
235 | self._reset_original_values(config_name, orig_values[config_name]) | |
236 | ||
237 | def test_bulk_set_cant_update_at_runtime(self): | |
238 | config_options = { | |
81eedcae TL |
239 | 'public_bind_addr': {'section': 'global', 'value': '1.2.3.4:567'}, # not updatable |
240 | 'public_network': {'section': 'global', 'value': '10.0.0.0/8'} # not updatable | |
11fdf7f2 TL |
241 | } |
242 | orig_values = dict() | |
243 | ||
244 | for config_name in config_options: | |
245 | orig_values[config_name] = self._get_config_by_name(config_name) | |
246 | ||
247 | # try to set config options and see if it fails | |
248 | self._put('/api/cluster_conf', {'options': config_options}) | |
249 | self.assertStatus(400) | |
250 | self.assertError(code='config_option_not_updatable_at_runtime', | |
251 | component='cluster_configuration', | |
252 | detail='Config option {} is/are not updatable at runtime'.format( | |
253 | ', '.join(config_options.keys()))) | |
254 | ||
255 | # check if config option values are still the original ones | |
256 | for config_name, value in orig_values.items(): | |
257 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
258 | value) | |
259 | self.assertEqual(result, value) | |
260 | ||
261 | def test_bulk_set_cant_update_at_runtime_partial(self): | |
262 | config_options = { | |
81eedcae | 263 | 'public_bind_addr': {'section': 'global', 'value': 'true'}, # not updatable |
11fdf7f2 TL |
264 | 'log_to_stderr': {'section': 'global', 'value': 'true'} # updatable |
265 | } | |
266 | orig_values = dict() | |
267 | ||
268 | for config_name in config_options: | |
269 | orig_values[config_name] = self._get_config_by_name(config_name) | |
270 | ||
271 | # try to set config options and see if it fails | |
272 | self._put('/api/cluster_conf', {'options': config_options}) | |
273 | self.assertStatus(400) | |
274 | self.assertError(code='config_option_not_updatable_at_runtime', | |
275 | component='cluster_configuration', | |
276 | detail='Config option {} is/are not updatable at runtime'.format( | |
81eedcae | 277 | 'public_bind_addr')) |
11fdf7f2 TL |
278 | |
279 | # check if config option values are still the original ones | |
280 | for config_name, value in orig_values.items(): | |
281 | result = self._wait_for_expected_get_result(self._get_config_by_name, config_name, | |
282 | value) | |
283 | self.assertEqual(result, value) | |
284 | ||
81eedcae TL |
285 | def test_check_existence(self): |
286 | """ | |
287 | This test case is intended to check the existence of all hard coded config options used by | |
288 | the dashboard. | |
81eedcae TL |
289 | If you include further hard coded options in the dashboard, feel free to add them to the |
290 | list. | |
291 | """ | |
292 | hard_coded_options = [ | |
293 | 'osd_max_backfills', # osd-recv-speed | |
294 | 'osd_recovery_max_active', # osd-recv-speed | |
295 | 'osd_recovery_max_single_start', # osd-recv-speed | |
296 | 'osd_recovery_sleep', # osd-recv-speed | |
297 | 'osd_scrub_during_recovery', # osd-pg-scrub | |
298 | 'osd_scrub_begin_hour', # osd-pg-scrub | |
299 | 'osd_scrub_end_hour', # osd-pg-scrub | |
300 | 'osd_scrub_begin_week_day', # osd-pg-scrub | |
301 | 'osd_scrub_end_week_day', # osd-pg-scrub | |
302 | 'osd_scrub_min_interval', # osd-pg-scrub | |
303 | 'osd_scrub_max_interval', # osd-pg-scrub | |
304 | 'osd_deep_scrub_interval', # osd-pg-scrub | |
305 | 'osd_scrub_auto_repair', # osd-pg-scrub | |
306 | 'osd_max_scrubs', # osd-pg-scrub | |
307 | 'osd_scrub_priority', # osd-pg-scrub | |
308 | 'osd_scrub_sleep', # osd-pg-scrub | |
309 | 'osd_scrub_auto_repair_num_errors', # osd-pg-scrub | |
310 | 'osd_debug_deep_scrub_sleep', # osd-pg-scrub | |
311 | 'osd_deep_scrub_keys', # osd-pg-scrub | |
312 | 'osd_deep_scrub_large_omap_object_key_threshold', # osd-pg-scrub | |
313 | 'osd_deep_scrub_large_omap_object_value_sum_threshold', # osd-pg-scrub | |
314 | 'osd_deep_scrub_randomize_ratio', # osd-pg-scrub | |
315 | 'osd_deep_scrub_stride', # osd-pg-scrub | |
316 | 'osd_deep_scrub_update_digest_min_age', # osd-pg-scrub | |
81eedcae TL |
317 | 'osd_requested_scrub_priority', # osd-pg-scrub |
318 | 'osd_scrub_backoff_ratio', # osd-pg-scrub | |
319 | 'osd_scrub_chunk_max', # osd-pg-scrub | |
320 | 'osd_scrub_chunk_min', # osd-pg-scrub | |
321 | 'osd_scrub_cost', # osd-pg-scrub | |
322 | 'osd_scrub_interval_randomize_ratio', # osd-pg-scrub | |
323 | 'osd_scrub_invalid_stats', # osd-pg-scrub | |
324 | 'osd_scrub_load_threshold', # osd-pg-scrub | |
494da23a TL |
325 | 'osd_scrub_max_preemptions', # osd-pg-scrub |
326 | 'mon_allow_pool_delete' # pool-list | |
81eedcae TL |
327 | ] |
328 | ||
329 | for config_option in hard_coded_options: | |
330 | self._get('/api/cluster_conf/{}'.format(config_option)) | |
331 | self.assertStatus(200) | |
332 | ||
11fdf7f2 TL |
333 | def _validate_single(self, data): |
334 | self.assertIn('name', data) | |
335 | self.assertIn('daemon_default', data) | |
336 | self.assertIn('long_desc', data) | |
337 | self.assertIn('level', data) | |
338 | self.assertIn('default', data) | |
339 | self.assertIn('see_also', data) | |
340 | self.assertIn('tags', data) | |
341 | self.assertIn('min', data) | |
342 | self.assertIn('max', data) | |
343 | self.assertIn('services', data) | |
344 | self.assertIn('type', data) | |
345 | self.assertIn('desc', data) | |
9f95a23c | 346 | self.assertIn(data['type'], ['str', 'bool', 'float', 'int', 'size', 'uint', 'addr', 'addrvec', 'uuid', |
11fdf7f2 TL |
347 | 'secs']) |
348 | ||
349 | if 'value' in data: | |
350 | self.assertIn('source', data) | |
351 | self.assertIsInstance(data['value'], list) | |
352 | ||
353 | for entry in data['value']: | |
354 | self.assertIsInstance(entry, dict) | |
355 | self.assertIn('section', entry) | |
356 | self.assertIn('value', entry) | |
357 | ||
358 | def _wait_for_expected_get_result(self, get_func, get_params, expected_result, max_attempts=30, | |
359 | sleep_time=1): | |
360 | attempts = 0 | |
361 | while attempts < max_attempts: | |
362 | get_result = get_func(get_params) | |
363 | if get_result == expected_result: | |
364 | self.assertStatus(200) | |
365 | return get_result | |
366 | ||
367 | time.sleep(sleep_time) | |
368 | attempts += 1 | |
369 | ||
370 | def _get_config_by_name(self, conf_name): | |
371 | data = self._get('/api/cluster_conf/{}'.format(conf_name)) | |
372 | if 'value' in data: | |
373 | return data['value'] | |
374 | return None | |
375 | ||
376 | def _clear_all_values_for_config_option(self, config_name): | |
377 | values = self._get_config_by_name(config_name) | |
378 | if values: | |
379 | for value in values: | |
380 | self._ceph_cmd(['config', 'rm', value['section'], config_name]) | |
381 | ||
382 | def _reset_original_values(self, config_name, orig_values): | |
383 | if orig_values: | |
384 | for value in orig_values: | |
385 | self._ceph_cmd(['config', 'set', value['section'], config_name, value['value']]) |