]> git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/mgr/dashboard/test_osd.py
import 15.2.0 Octopus source
[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_mark_out_and_in(self):
65 self._post('/api/osd/0/mark_out')
66 self.assertStatus(200)
67
68 self._post('/api/osd/0/mark_in')
69 self.assertStatus(200)
70
71 def test_mark_down(self):
72 self._post('/api/osd/0/mark_down')
73 self.assertStatus(200)
74
75 def test_reweight(self):
76 self._post('/api/osd/0/reweight', {'weight': 0.4})
77 self.assertStatus(200)
78
79 def get_reweight_value():
80 self._get('/api/osd/0')
81 response = self.jsonBody()
82 if 'osd_map' in response and 'weight' in response['osd_map']:
83 return round(response['osd_map']['weight'], 1)
84 self.wait_until_equal(get_reweight_value, 0.4, 10)
85 self.assertStatus(200)
86
87 # Undo
88 self._post('/api/osd/0/reweight', {'weight': 1})
89
90 def test_create_lost_destroy_remove(self):
91 # Create
92 self._task_post('/api/osd', {
93 'method': 'bare',
94 'data': {
95 'uuid': 'f860ca2e-757d-48ce-b74a-87052cad563f',
96 'svc_id': 5
97 },
98 'tracking_id': 'bare-5'
99 })
100 self.assertStatus(201)
101 # Lost
102 self._post('/api/osd/5/mark_lost')
103 self.assertStatus(200)
104 # Destroy
105 self._post('/api/osd/5/destroy')
106 self.assertStatus(200)
107 # Purge
108 self._post('/api/osd/5/purge')
109 self.assertStatus(200)
110
111 def test_create_with_drive_group(self):
112 data = {
113 'method': 'drive_groups',
114 'data': [
115 {
116 'service_type': 'osd',
117 'service_id': 'test',
118 'host_pattern': '*',
119 'data_devices': {
120 'vendor': 'abc',
121 'model': 'cba',
122 'rotational': True,
123 'size': '4 TB'
124 },
125 'wal_devices': {
126 'vendor': 'def',
127 'model': 'fed',
128 'rotational': False,
129 'size': '1 TB'
130 },
131 'db_devices': {
132 'vendor': 'ghi',
133 'model': 'ihg',
134 'rotational': False,
135 'size': '512 GB'
136 },
137 'wal_slots': 5,
138 'db_slots': 5,
139 'encrypted': True
140 }
141 ],
142 'tracking_id': 'test'
143 }
144 self._post('/api/osd', data)
145 self.assertStatus(201)
146
147 def test_safe_to_destroy(self):
148 osd_dump = json.loads(self._ceph_cmd(['osd', 'dump', '-f', 'json']))
149 max_id = max(map(lambda e: e['osd'], osd_dump['osds']))
150
151 def get_pg_status_equal_unknown(osd_ids):
152 self._get('/api/osd/safe_to_destroy?ids={}'.format(osd_ids))
153 if 'message' in self.jsonBody():
154 return 'pgs have unknown state' in self.jsonBody()['message']
155 return False
156
157 # 1 OSD safe to destroy
158 unused_osd_id = max_id + 10
159 self.wait_until_equal(
160 lambda: get_pg_status_equal_unknown(unused_osd_id), False, 30)
161 self.assertStatus(200)
162 self.assertJsonBody({
163 'is_safe_to_destroy': True,
164 'active': [],
165 'missing_stats': [],
166 'safe_to_destroy': [unused_osd_id],
167 'stored_pgs': [],
168 })
169
170 # multiple OSDs safe to destroy
171 unused_osd_ids = [max_id + 11, max_id + 12]
172 self.wait_until_equal(
173 lambda: get_pg_status_equal_unknown(str(unused_osd_ids)), False, 30)
174 self.assertStatus(200)
175 self.assertJsonBody({
176 'is_safe_to_destroy': True,
177 'active': [],
178 'missing_stats': [],
179 'safe_to_destroy': unused_osd_ids,
180 'stored_pgs': [],
181 })
182
183 # 1 OSD unsafe to destroy
184 def get_destroy_status():
185 self._get('/api/osd/safe_to_destroy?ids=0')
186 if 'is_safe_to_destroy' in self.jsonBody():
187 return self.jsonBody()['is_safe_to_destroy']
188 return None
189 self.wait_until_equal(get_destroy_status, False, 10)
190 self.assertStatus(200)
191
192 def test_osd_devices(self):
193 data = self._get('/api/osd/0/devices')
194 self.assertStatus(200)
195 self.assertSchema(data, JList(JObj({
196 'daemons': JList(str),
197 'devid': str,
198 'location': JList(JObj({
199 'host': str,
200 'dev': str,
201 'path': str
202 }))
203 })))
204
205
206 class OsdFlagsTest(DashboardTestCase):
207 def __init__(self, *args, **kwargs):
208 super(OsdFlagsTest, self).__init__(*args, **kwargs)
209 self._initial_flags = sorted( # These flags cannot be unset
210 ['sortbitwise', 'recovery_deletes', 'purged_snapdirs',
211 'pglog_hardlimit'])
212
213 @classmethod
214 def _get_cluster_osd_flags(cls):
215 return sorted(
216 json.loads(cls._ceph_cmd(['osd', 'dump',
217 '--format=json']))['flags_set'])
218
219 @classmethod
220 def _put_flags(cls, flags):
221 cls._put('/api/osd/flags', data={'flags': flags})
222 return sorted(cls._resp.json())
223
224 def test_list_osd_flags(self):
225 flags = self._get('/api/osd/flags')
226 self.assertStatus(200)
227 self.assertEqual(len(flags), 4)
228 self.assertEqual(sorted(flags), self._initial_flags)
229
230 def test_add_osd_flag(self):
231 flags = self._put_flags([
232 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout',
233 'pause', 'pglog_hardlimit'
234 ])
235 self.assertEqual(flags, sorted([
236 'sortbitwise', 'recovery_deletes', 'purged_snapdirs', 'noout',
237 'pause', 'pglog_hardlimit'
238 ]))
239
240 # Restore flags
241 self._put_flags(self._initial_flags)