]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/cephadm/tests/test_osd_removal.py
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / pybind / mgr / cephadm / tests / test_osd_removal.py
1 import json
2
3 from cephadm.services.osd import OSDRemovalQueue, OSD
4 import pytest
5 from tests import mock
6 from datetime import datetime
7
8
9 class MockOSD:
10
11 def __init__(self, osd_id):
12 self.osd_id = osd_id
13
14
15 class TestOSDRemoval:
16
17 @pytest.mark.parametrize(
18 "osd_id, osd_df, expected",
19 [
20 # missing 'nodes' key
21 (1, dict(nodes=[]), -1),
22 # missing 'pgs' key
23 (1, dict(nodes=[dict(id=1)]), -1),
24 # id != osd_id
25 (1, dict(nodes=[dict(id=999, pgs=1)]), -1),
26 # valid
27 (1, dict(nodes=[dict(id=1, pgs=1)]), 1),
28 ]
29 )
30 def test_get_pg_count(self, rm_util, osd_id, osd_df, expected):
31 with mock.patch("cephadm.services.osd.RemoveUtil.osd_df", return_value=osd_df):
32 assert rm_util.get_pg_count(osd_id) == expected
33
34 @pytest.mark.parametrize(
35 "osds, ok_to_stop, expected",
36 [
37 # no osd_ids provided
38 ([], [False], []),
39 # all osds are ok_to_stop
40 ([1, 2], [True], [1, 2]),
41 # osds are ok_to_stop after the second iteration
42 ([1, 2], [False, True], [2]),
43 # osds are never ok_to_stop, (taking the sample size `(len(osd_ids))` into account),
44 # expected to get False
45 ([1, 2], [False, False], []),
46 ]
47 )
48 def test_find_stop_threshold(self, rm_util, osds, ok_to_stop, expected):
49 with mock.patch("cephadm.services.osd.RemoveUtil.ok_to_stop", side_effect=ok_to_stop):
50 assert rm_util.find_osd_stop_threshold(osds) == expected
51
52 def test_process_removal_queue(self, rm_util):
53 # TODO: !
54 # rm_util.process_removal_queue()
55 pass
56
57 def test_ok_to_stop(self, rm_util):
58 rm_util.ok_to_stop([MockOSD(1)])
59 rm_util._run_mon_cmd.assert_called_with({'prefix': 'osd ok-to-stop', 'ids': ['1']})
60
61 def test_safe_to_destroy(self, rm_util):
62 rm_util.safe_to_destroy([1])
63 rm_util._run_mon_cmd.assert_called_with({'prefix': 'osd safe-to-destroy', 'ids': ['1']})
64
65 def test_destroy_osd(self, rm_util):
66 rm_util.destroy_osd(1)
67 rm_util._run_mon_cmd.assert_called_with(
68 {'prefix': 'osd destroy-actual', 'id': 1, 'yes_i_really_mean_it': True})
69
70 def test_purge_osd(self, rm_util):
71 rm_util.purge_osd(1)
72 rm_util._run_mon_cmd.assert_called_with(
73 {'prefix': 'osd purge-actual', 'id': 1, 'yes_i_really_mean_it': True})
74
75 def test_load(self, cephadm_module, rm_util):
76 data = json.dumps([
77 {
78 "osd_id": 35,
79 "started": True,
80 "draining": True,
81 "stopped": False,
82 "replace": False,
83 "force": False,
84 "nodename": "node2",
85 "drain_started_at": "2020-09-14T11:41:53.960463",
86 "drain_stopped_at": None,
87 "drain_done_at": None,
88 "process_started_at": "2020-09-14T11:41:52.245832"
89 }
90 ])
91 cephadm_module.set_store('osd_remove_queue', data)
92 cephadm_module.to_remove_osds.load_from_store()
93
94 expected = OSDRemovalQueue(cephadm_module)
95 expected.osds.add(OSD(osd_id=35, remove_util=rm_util, draining=True))
96 assert cephadm_module.to_remove_osds == expected
97
98
99 class TestOSD:
100
101 def test_start(self, osd_obj):
102 assert osd_obj.started is False
103 osd_obj.start()
104 assert osd_obj.started is True
105 assert osd_obj.stopped is False
106
107 def test_start_draining_purge(self, osd_obj):
108 assert osd_obj.draining is False
109 assert osd_obj.drain_started_at is None
110 ret = osd_obj.start_draining()
111 osd_obj.rm_util.reweight_osd.assert_called_with(osd_obj, 0.0)
112 assert isinstance(osd_obj.drain_started_at, datetime)
113 assert osd_obj.draining is True
114 assert osd_obj.replace is False
115 assert ret is True
116
117 def test_start_draining_replace(self, osd_obj):
118 assert osd_obj.draining is False
119 assert osd_obj.drain_started_at is None
120 osd_obj.replace = True
121 ret = osd_obj.start_draining()
122 osd_obj.rm_util.set_osd_flag.assert_called_with([osd_obj], 'out')
123 assert isinstance(osd_obj.drain_started_at, datetime)
124 assert osd_obj.draining is True
125 assert osd_obj.replace is True
126 assert ret is True
127
128 def test_start_draining_stopped(self, osd_obj):
129 osd_obj.stopped = True
130 ret = osd_obj.start_draining()
131 assert osd_obj.drain_started_at is None
132 assert ret is False
133 assert osd_obj.draining is False
134
135 def test_stop_draining_replace(self, osd_obj):
136 osd_obj.replace = True
137 ret = osd_obj.stop_draining()
138 osd_obj.rm_util.set_osd_flag.assert_called_with([osd_obj], 'in')
139 assert isinstance(osd_obj.drain_stopped_at, datetime)
140 assert osd_obj.draining is False
141 assert ret is True
142
143 def test_stop_draining_purge(self, osd_obj):
144 osd_obj.original_weight = 1.0
145 ret = osd_obj.stop_draining()
146 osd_obj.rm_util.reweight_osd.assert_called_with(osd_obj, 1.0)
147 assert isinstance(osd_obj.drain_stopped_at, datetime)
148 assert osd_obj.draining is False
149 assert ret is True
150
151 @mock.patch('cephadm.services.osd.OSD.stop_draining')
152 def test_stop(self, stop_draining_mock, osd_obj):
153 osd_obj.stop()
154 assert osd_obj.started is False
155 assert osd_obj.stopped is True
156 stop_draining_mock.assert_called_once()
157
158 @pytest.mark.parametrize(
159 "draining, empty, expected",
160 [
161 # must be !draining! and !not empty! to yield True
162 (True, not True, True),
163 # not draining and not empty
164 (False, not True, False),
165 # not draining and empty
166 (False, True, False),
167 # draining and empty
168 (True, True, False),
169 ]
170 )
171 def test_is_draining(self, osd_obj, draining, empty, expected):
172 with mock.patch("cephadm.services.osd.OSD.is_empty", new_callable=mock.PropertyMock(return_value=empty)):
173 osd_obj.draining = draining
174 assert osd_obj.is_draining is expected
175
176 @mock.patch("cephadm.services.osd.RemoveUtil.ok_to_stop")
177 def test_is_ok_to_stop(self, _, osd_obj):
178 osd_obj.is_ok_to_stop
179 osd_obj.rm_util.ok_to_stop.assert_called_once()
180
181 @pytest.mark.parametrize(
182 "pg_count, expected",
183 [
184 (0, True),
185 (1, False),
186 (9999, False),
187 (-1, False),
188 ]
189 )
190 def test_is_empty(self, osd_obj, pg_count, expected):
191 with mock.patch("cephadm.services.osd.OSD.get_pg_count", return_value=pg_count):
192 assert osd_obj.is_empty is expected
193
194 @mock.patch("cephadm.services.osd.RemoveUtil.safe_to_destroy")
195 def test_safe_to_destroy(self, _, osd_obj):
196 osd_obj.safe_to_destroy()
197 osd_obj.rm_util.safe_to_destroy.assert_called_once()
198
199 @mock.patch("cephadm.services.osd.RemoveUtil.set_osd_flag")
200 def test_down(self, _, osd_obj):
201 osd_obj.down()
202 osd_obj.rm_util.set_osd_flag.assert_called_with([osd_obj], 'down')
203
204 @mock.patch("cephadm.services.osd.RemoveUtil.destroy_osd")
205 def test_destroy_osd(self, _, osd_obj):
206 osd_obj.destroy()
207 osd_obj.rm_util.destroy_osd.assert_called_once()
208
209 @mock.patch("cephadm.services.osd.RemoveUtil.purge_osd")
210 def test_purge(self, _, osd_obj):
211 osd_obj.purge()
212 osd_obj.rm_util.purge_osd.assert_called_once()
213
214 @mock.patch("cephadm.services.osd.RemoveUtil.get_pg_count")
215 def test_pg_count(self, _, osd_obj):
216 osd_obj.get_pg_count()
217 osd_obj.rm_util.get_pg_count.assert_called_once()
218
219 def test_drain_status_human_not_started(self, osd_obj):
220 assert osd_obj.drain_status_human() == 'not started'
221
222 def test_drain_status_human_started(self, osd_obj):
223 osd_obj.started = True
224 assert osd_obj.drain_status_human() == 'started'
225
226 def test_drain_status_human_draining(self, osd_obj):
227 osd_obj.started = True
228 osd_obj.draining = True
229 assert osd_obj.drain_status_human() == 'draining'
230
231 def test_drain_status_human_done(self, osd_obj):
232 osd_obj.started = True
233 osd_obj.draining = False
234 osd_obj.drain_done_at = datetime.utcnow()
235 assert osd_obj.drain_status_human() == 'done, waiting for purge'
236
237
238 class TestOSDRemovalQueue:
239
240 def test_queue_size(self, osd_obj):
241 q = OSDRemovalQueue(mock.Mock())
242 assert q.queue_size() == 0
243 q.osds.add(osd_obj)
244 assert q.queue_size() == 1
245
246 @mock.patch("cephadm.services.osd.OSD.start")
247 @mock.patch("cephadm.services.osd.OSD.exists")
248 def test_enqueue(self, exist, start, osd_obj):
249 q = OSDRemovalQueue(mock.Mock())
250 q.enqueue(osd_obj)
251 osd_obj.start.assert_called_once()
252
253 @mock.patch("cephadm.services.osd.OSD.stop")
254 @mock.patch("cephadm.services.osd.OSD.exists")
255 def test_rm_raise(self, exist, stop, osd_obj):
256 q = OSDRemovalQueue(mock.Mock())
257 with pytest.raises(KeyError):
258 q.rm(osd_obj)
259 osd_obj.stop.assert_called_once()
260
261 @mock.patch("cephadm.services.osd.OSD.stop")
262 @mock.patch("cephadm.services.osd.OSD.exists")
263 def test_rm(self, exist, stop, osd_obj):
264 q = OSDRemovalQueue(mock.Mock())
265 q.osds.add(osd_obj)
266 q.rm(osd_obj)
267 osd_obj.stop.assert_called_once()