]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/mgr/dashboard/test_cluster_configuration.py
61d18000a45a4078a389d9213d4c59ccdb7f5c96
[ceph.git] / ceph / qa / tasks / mgr / dashboard / test_cluster_configuration.py
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
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
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
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
121 def test_create_cant_update_at_runtime(self):
122 config_name = 'public_bind_addr' # not updatable
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 = {
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
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 = {
263 'public_bind_addr': {'section': 'global', 'value': 'true'}, # not updatable
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(
277 'public_bind_addr'))
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
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.
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
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
325 'osd_scrub_max_preemptions', # osd-pg-scrub
326 'mon_allow_pool_delete' # pool-list
327 ]
328
329 for config_option in hard_coded_options:
330 self._get('/api/cluster_conf/{}'.format(config_option))
331 self.assertStatus(200)
332
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)
346 self.assertIn(data['type'], ['str', 'bool', 'float', 'int', 'size', 'uint', 'addr', 'addrvec', 'uuid',
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']])