]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/mgr/dashboard/test_osd.py
import 15.2.5
[ceph.git] / ceph / qa / tasks / mgr / dashboard / test_osd.py
1 # -*- coding: utf-8 -*-
2
3 from __future__ import absolute_import
4
5 import json
6
7 from .helper import DashboardTestCase, JObj, JAny, JList, JLeaf, JTuple
8
9
10 class OsdTest(DashboardTestCase):
11
12 AUTH_ROLES = ['cluster-manager']
13
14 @classmethod
15 def setUpClass(cls):
16 super(OsdTest, cls).setUpClass()
17 cls._load_module('test_orchestrator')
18 cmd = ['orch', 'set', 'backend', 'test_orchestrator']
19 cls.mgr_cluster.mon_manager.raw_cluster_cmd(*cmd)
20
21 def tearDown(self):
22 self._post('/api/osd/0/mark_in')
23
24 @DashboardTestCase.RunAs('test', 'test', ['block-manager'])
25 def test_access_permissions(self):
26 self._get('/api/osd')
27 self.assertStatus(403)
28 self._get('/api/osd/0')
29 self.assertStatus(403)
30
31 def assert_in_and_not_none(self, data, properties):
32 self.assertSchema(data, JObj({p: JAny(none=False) for p in properties}, allow_unknown=True))
33
34 def test_list(self):
35 data = self._get('/api/osd')
36 self.assertStatus(200)
37
38 self.assertGreaterEqual(len(data), 1)
39 data = data[0]
40 self.assert_in_and_not_none(data, ['host', 'tree', 'state', 'stats', 'stats_history'])
41 self.assert_in_and_not_none(data['host'], ['name'])
42 self.assert_in_and_not_none(data['tree'], ['id'])
43 self.assert_in_and_not_none(data['stats'], ['numpg', 'stat_bytes_used', 'stat_bytes',
44 'op_r', 'op_w'])
45 self.assert_in_and_not_none(data['stats_history'], ['op_out_bytes', 'op_in_bytes'])
46 self.assertSchema(data['stats_history']['op_out_bytes'],
47 JList(JTuple([JLeaf(float), JLeaf(float)])))
48
49 def test_details(self):
50 data = self._get('/api/osd/0')
51 self.assertStatus(200)
52 self.assert_in_and_not_none(data, ['osd_metadata', 'histogram'])
53 self.assert_in_and_not_none(data['histogram'], ['osd'])
54 self.assert_in_and_not_none(data['histogram']['osd'], ['op_w_latency_in_bytes_histogram',
55 'op_r_latency_out_bytes_histogram'])
56
57 def test_scrub(self):
58 self._post('/api/osd/0/scrub?deep=False')
59 self.assertStatus(200)
60
61 self._post('/api/osd/0/scrub?deep=True')
62 self.assertStatus(200)
63
64 def test_safe_to_delete(self):
65 data = self._get('/api/osd/safe_to_delete?svc_ids=0')
66 self.assertStatus(200)
67 self.assertSchema(data, JObj({
68 'is_safe_to_delete': JAny(none=True),
69 'message': str
70 }))
71 self.assertTrue(data['is_safe_to_delete'])
72
73 def test_osd_smart(self):
74 self._get('/api/osd/0/smart')
75 self.assertStatus(200)
76
77 def test_mark_out_and_in(self):
78 self._post('/api/osd/0/mark_out')
79 self.assertStatus(200)
80
81 self._post('/api/osd/0/mark_in')
82 self.assertStatus(200)
83
84 def test_mark_down(self):
85 self._post('/api/osd/0/mark_down')
86 self.assertStatus(200)
87
88 def test_reweight(self):
89 self._post('/api/osd/0/reweight', {'weight': 0.4})
90 self.assertStatus(200)
91
92 def get_reweight_value():
93 self._get('/api/osd/0')
94 response = self.jsonBody()
95 if 'osd_map' in response and 'weight' in response['osd_map']:
96 return round(response['osd_map']['weight'], 1)
97 self.wait_until_equal(get_reweight_value, 0.4, 10)
98 self.assertStatus(200)
99
100 # Undo
101 self._post('/api/osd/0/reweight', {'weight': 1})
102
103 def test_create_lost_destroy_remove(self):
104 # Create
105 self._task_post('/api/osd', {
106 'method': 'bare',
107 'data': {
108 'uuid': 'f860ca2e-757d-48ce-b74a-87052cad563f',
109 'svc_id': 5
110 },
111 'tracking_id': 'bare-5'
112 })
113 self.assertStatus(201)
114
115 # invalid method
116 self._task_post('/api/osd', {
117 'method': 'xyz',
118 'data': {
119 'uuid': 'f860ca2e-757d-48ce-b74a-87052cad563f',
120 'svc_id': 5
121 },
122 'tracking_id': 'bare-5'
123 })
124 self.assertStatus(400)
125
126 # Lost
127 self._post('/api/osd/5/mark_lost')
128 self.assertStatus(200)
129 # Destroy
130 self._post('/api/osd/5/destroy')
131 self.assertStatus(200)
132 # Purge
133 self._post('/api/osd/5/purge')
134 self.assertStatus(200)
135
136 def test_create_with_drive_group(self):
137 data = {
138 'method': 'drive_groups',
139 'data': [
140 {
141 'service_type': 'osd',
142 'service_id': 'test',
143 'host_pattern': '*',
144 'data_devices': {
145 'vendor': 'abc',
146 'model': 'cba',
147 'rotational': True,
148 'size': '4 TB'
149 },
150 'wal_devices': {
151 'vendor': 'def',
152 'model': 'fed',
153 'rotational': False,
154 'size': '1 TB'
155 },
156 'db_devices': {
157 'vendor': 'ghi',
158 'model': 'ihg',
159 'rotational': False,
160 'size': '512 GB'
161 },
162 'wal_slots': 5,
163 'db_slots': 5,
164 'encrypted': True
165 }
166 ],
167 'tracking_id': 'test'
168 }
169 self._post('/api/osd', data)
170 self.assertStatus(201)
171
172 def test_safe_to_destroy(self):
173 osd_dump = json.loads(self._ceph_cmd(['osd', 'dump', '-f', 'json']))
174 max_id = max(map(lambda e: e['osd'], osd_dump['osds']))
175
176 def get_pg_status_equal_unknown(osd_ids):
177 self._get('/api/osd/safe_to_destroy?ids={}'.format(osd_ids))
178 if 'message' in self.jsonBody():
179 return 'pgs have unknown state' in self.jsonBody()['message']
180 return False
181
182 # 1 OSD safe to destroy
183 unused_osd_id = max_id + 10
184 self.wait_until_equal(
185 lambda: get_pg_status_equal_unknown(unused_osd_id), False, 30)
186 self.assertStatus(200)
187 self.assertJsonBody({
188 'is_safe_to_destroy': True,
189 'active': [],
190 'missing_stats': [],
191 'safe_to_destroy': [unused_osd_id],
192 'stored_pgs': [],
193 })
194
195 # multiple OSDs safe to destroy
196 unused_osd_ids = [max_id + 11, max_id + 12]
197 self.wait_until_equal(
198 lambda: get_pg_status_equal_unknown(str(unused_osd_ids)), False, 30)
199 self.assertStatus(200)
200 self.assertJsonBody({
201 'is_safe_to_destroy': True,
202 'active': [],
203 'missing_stats': [],
204 'safe_to_destroy': unused_osd_ids,
205 'stored_pgs': [],
206 })
207
208 # 1 OSD unsafe to destroy
209 def get_destroy_status():
210 self._get('/api/osd/safe_to_destroy?ids=0')
211 if 'is_safe_to_destroy' in self.jsonBody():
212 return self.jsonBody()['is_safe_to_destroy']
213 return None
214 self.wait_until_equal(get_destroy_status, False, 10)
215 self.assertStatus(200)
216
217 def test_osd_devices(self):
218 data = self._get('/api/osd/0/devices')
219 self.assertStatus(200)
220 self.assertSchema(data, JList(JObj({
221 'daemons': JList(str),
222 'devid': str,
223 'location': JList(JObj({
224 'host': str,
225 'dev': str,
226 'path': str
227 }))
228 })))
229
230
231 class OsdFlagsTest(DashboardTestCase):
232 def __init__(self, *args, **kwargs):
233 super(OsdFlagsTest, self).__init__(*args, **kwargs)
234 self._initial_flags = sorted( # These flags cannot be unset
235 ['sortbitwise', 'recovery_deletes', 'purged_snapdirs',
236 'pglog_hardlimit'])
237
238 @classmethod
239 def _get_cluster_osd_flags(cls):
240 return sorted(
241 json.loads(cls._ceph_cmd(['osd', 'dump',
242 '--format=json']))['flags_set'])
243
244 @classmethod
245 def _put_flags(cls, flags):
246 cls._put('/api/osd/flags', data={'flags': flags})
247 return sorted(cls._resp.json())
248
249 def test_list_osd_flags(self):
250 flags = self._get('/api/osd/flags')
251 self.assertStatus(200)
252 self.assertEqual(len(flags), 4)
253 self.assertEqual(sorted(flags), self._initial_flags)
254
255 def test_add_osd_flag(self):
256 flags = self._put_flags([
257 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout',
258 'pause', 'pglog_hardlimit'
259 ])
260 self.assertEqual(flags, sorted([
261 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout',
262 'pause', 'pglog_hardlimit'
263 ]))
264
265 # Restore flags
266 self._put_flags(self._initial_flags)