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