]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/mgr/dashboard/test_health.py
08551e488ab99164f79afe1e1cd9302c75c30b1d
[ceph.git] / ceph / qa / tasks / mgr / dashboard / test_health.py
1 # -*- coding: utf-8 -*-
2 from __future__ import absolute_import
3
4 from .helper import DashboardTestCase, JAny, JLeaf, JList, JObj
5
6
7 class HealthTest(DashboardTestCase):
8 CEPHFS = True
9
10 __pg_info_schema = JObj({
11 'object_stats': JObj({
12 'num_objects': int,
13 'num_object_copies': int,
14 'num_objects_degraded': int,
15 'num_objects_misplaced': int,
16 'num_objects_unfound': int
17 }),
18 'pgs_per_osd': float,
19 'statuses': JObj({}, allow_unknown=True, unknown_schema=int)
20 })
21
22 __mdsmap_schema = JObj({
23 'session_autoclose': int,
24 'balancer': str,
25 'up': JObj({}, allow_unknown=True),
26 'last_failure_osd_epoch': int,
27 'in': JList(int),
28 'last_failure': int,
29 'max_file_size': int,
30 'explicitly_allowed_features': int,
31 'damaged': JList(int),
32 'tableserver': int,
33 'failed': JList(int),
34 'metadata_pool': int,
35 'epoch': int,
36 'stopped': JList(int),
37 'max_mds': int,
38 'compat': JObj({
39 'compat': JObj({}, allow_unknown=True),
40 'ro_compat': JObj({}, allow_unknown=True),
41 'incompat': JObj({}, allow_unknown=True)
42 }),
43 'min_compat_client': str,
44 'data_pools': JList(int),
45 'info': JObj({}, allow_unknown=True),
46 'fs_name': str,
47 'created': str,
48 'standby_count_wanted': int,
49 'enabled': bool,
50 'modified': str,
51 'session_timeout': int,
52 'flags': int,
53 'ever_allowed_features': int,
54 'root': int
55 })
56
57 def test_minimal_health(self):
58 data = self._get('/api/health/minimal')
59 self.assertStatus(200)
60 schema = JObj({
61 'client_perf': JObj({
62 'read_bytes_sec': int,
63 'read_op_per_sec': int,
64 'recovering_bytes_per_sec': int,
65 'write_bytes_sec': int,
66 'write_op_per_sec': int
67 }),
68 'df': JObj({
69 'stats': JObj({
70 'total_avail_bytes': int,
71 'total_bytes': int,
72 'total_used_raw_bytes': int,
73 })
74 }),
75 'fs_map': JObj({
76 'filesystems': JList(
77 JObj({
78 'mdsmap': self.__mdsmap_schema
79 }),
80 ),
81 'standbys': JList(JObj({}, allow_unknown=True)),
82 }),
83 'health': JObj({
84 'checks': JList(JObj({}, allow_unknown=True)),
85 'mutes': JList(JObj({}, allow_unknown=True)),
86 'status': str,
87 }),
88 'hosts': int,
89 'iscsi_daemons': JObj({
90 'up': int,
91 'down': int
92 }),
93 'mgr_map': JObj({
94 'active_name': str,
95 'standbys': JList(JLeaf(dict))
96 }),
97 'mon_status': JObj({
98 'monmap': JObj({
99 'mons': JList(JLeaf(dict)),
100 }),
101 'quorum': JList(int)
102 }),
103 'osd_map': JObj({
104 'osds': JList(
105 JObj({
106 'in': int,
107 'up': int,
108 })),
109 }),
110 'pg_info': self.__pg_info_schema,
111 'pools': JList(JLeaf(dict)),
112 'rgw': int,
113 'scrub_status': str
114 })
115 self.assertSchema(data, schema)
116
117 def test_full_health(self):
118 data = self._get('/api/health/full')
119 self.assertStatus(200)
120 module_info_schema = JObj({
121 'can_run': bool,
122 'error_string': str,
123 'name': str,
124 'module_options': JObj(
125 {},
126 allow_unknown=True,
127 unknown_schema=JObj({
128 'name': str,
129 'type': str,
130 'level': str,
131 'flags': int,
132 'default_value': str,
133 'min': str,
134 'max': str,
135 'enum_allowed': JList(str),
136 'see_also': JList(str),
137 'desc': str,
138 'long_desc': str,
139 'tags': JList(str),
140 })),
141 })
142 schema = JObj({
143 'client_perf': JObj({
144 'read_bytes_sec': int,
145 'read_op_per_sec': int,
146 'recovering_bytes_per_sec': int,
147 'write_bytes_sec': int,
148 'write_op_per_sec': int
149 }),
150 'df': JObj({
151 'pools': JList(JObj({
152 'stats': JObj({
153 'stored': int,
154 'stored_data': int,
155 'stored_omap': int,
156 'objects': int,
157 'kb_used': int,
158 'bytes_used': int,
159 'data_bytes_used': int,
160 'omap_bytes_used': int,
161 'percent_used': float,
162 'max_avail': int,
163 'quota_objects': int,
164 'quota_bytes': int,
165 'dirty': int,
166 'rd': int,
167 'rd_bytes': int,
168 'wr': int,
169 'wr_bytes': int,
170 'compress_bytes_used': int,
171 'compress_under_bytes': int,
172 'stored_raw': int
173 }),
174 'name': str,
175 'id': int
176 })),
177 'stats': JObj({
178 'total_avail_bytes': int,
179 'total_bytes': int,
180 'total_used_bytes': int,
181 'total_used_raw_bytes': int,
182 'total_used_raw_ratio': float,
183 'num_osds': int,
184 'num_per_pool_osds': int,
185 'num_per_pool_omap_osds': int
186 })
187 }),
188 'fs_map': JObj({
189 'compat': JObj({
190 'compat': JObj({}, allow_unknown=True, unknown_schema=str),
191 'incompat': JObj(
192 {}, allow_unknown=True, unknown_schema=str),
193 'ro_compat': JObj(
194 {}, allow_unknown=True, unknown_schema=str)
195 }),
196 'default_fscid': int,
197 'epoch': int,
198 'feature_flags': JObj(
199 {}, allow_unknown=True, unknown_schema=bool),
200 'filesystems': JList(
201 JObj({
202 'id': int,
203 'mdsmap': self.__mdsmap_schema
204 }),
205 ),
206 'standbys': JList(JObj({}, allow_unknown=True)),
207 }),
208 'health': JObj({
209 'checks': JList(JObj({}, allow_unknown=True)),
210 'mutes': JList(JObj({}, allow_unknown=True)),
211 'status': str,
212 }),
213 'hosts': int,
214 'iscsi_daemons': JObj({
215 'up': int,
216 'down': int
217 }),
218 'mgr_map': JObj({
219 'active_addr': str,
220 'active_addrs': JObj({
221 'addrvec': JList(JObj({
222 'addr': str,
223 'nonce': int,
224 'type': str
225 }))
226 }),
227 'active_change': str, # timestamp
228 'active_mgr_features': int,
229 'active_gid': int,
230 'active_name': str,
231 'always_on_modules': JObj({}, allow_unknown=True),
232 'available': bool,
233 'available_modules': JList(module_info_schema),
234 'epoch': int,
235 'modules': JList(str),
236 'services': JObj(
237 {'dashboard': str}, # This module should always be present
238 allow_unknown=True, unknown_schema=str
239 ),
240 'standbys': JList(JObj({
241 'available_modules': JList(module_info_schema),
242 'gid': int,
243 'name': str,
244 'mgr_features': int
245 }, allow_unknown=True))
246 }, allow_unknown=True),
247 'mon_status': JObj({
248 'election_epoch': int,
249 'extra_probe_peers': JList(JAny(none=True)),
250 'feature_map': JObj(
251 {}, allow_unknown=True, unknown_schema=JList(JObj({
252 'features': str,
253 'num': int,
254 'release': str
255 }))
256 ),
257 'features': JObj({
258 'quorum_con': str,
259 'quorum_mon': JList(str),
260 'required_con': str,
261 'required_mon': JList(str)
262 }),
263 'monmap': JObj({
264 # TODO: expand on monmap schema
265 'mons': JList(JLeaf(dict)),
266 }, allow_unknown=True),
267 'name': str,
268 'outside_quorum': JList(int),
269 'quorum': JList(int),
270 'quorum_age': int,
271 'rank': int,
272 'state': str,
273 # TODO: What type should be expected here?
274 'sync_provider': JList(JAny(none=True))
275 }),
276 'osd_map': JObj({
277 # TODO: define schema for crush map and osd_metadata, among
278 # others
279 'osds': JList(
280 JObj({
281 'in': int,
282 'up': int,
283 }, allow_unknown=True)),
284 }, allow_unknown=True),
285 'pg_info': self.__pg_info_schema,
286 'pools': JList(JLeaf(dict)),
287 'rgw': int,
288 'scrub_status': str
289 })
290 self.assertSchema(data, schema)
291
292 cluster_pools = self.ceph_cluster.mon_manager.list_pools()
293 self.assertEqual(len(cluster_pools), len(data['pools']))
294 for pool in data['pools']:
295 self.assertIn(pool['pool_name'], cluster_pools)
296
297 @DashboardTestCase.RunAs('test', 'test', ['pool-manager'])
298 def test_health_permissions(self):
299 data = self._get('/api/health/full')
300 self.assertStatus(200)
301
302 schema = JObj({
303 'client_perf': JObj({}, allow_unknown=True),
304 'df': JObj({}, allow_unknown=True),
305 'health': JObj({
306 'checks': JList(JObj({}, allow_unknown=True)),
307 'mutes': JList(JObj({}, allow_unknown=True)),
308 'status': str
309 }),
310 'pools': JList(JLeaf(dict)),
311 })
312 self.assertSchema(data, schema)
313
314 cluster_pools = self.ceph_cluster.mon_manager.list_pools()
315 self.assertEqual(len(cluster_pools), len(data['pools']))
316 for pool in data['pools']:
317 self.assertIn(pool['pool_name'], cluster_pools)