]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/orchestrator/tests/test_orchestrator.py
import ceph quincy 17.2.4
[ceph.git] / ceph / src / pybind / mgr / orchestrator / tests / test_orchestrator.py
CommitLineData
9f95a23c 1
f6b5b4d7 2import json
20effc67 3import textwrap
9f95a23c
TL
4
5import pytest
f6b5b4d7 6import yaml
9f95a23c 7
33c7a0ef
TL
8from ceph.deployment.hostspec import HostSpec
9from ceph.deployment.inventory import Devices, Device
f6b5b4d7 10from ceph.deployment.service_spec import ServiceSpec
9f95a23c 11from ceph.deployment import inventory
adb31ebb 12from ceph.utils import datetime_now
f67539c2 13from mgr_module import HandleCommandResult
f6b5b4d7
TL
14
15from test_orchestrator import TestOrchestrator as _TestOrchestrator
f6b5b4d7 16
a4b75251 17from orchestrator import InventoryHost, DaemonDescription, ServiceDescription, DaemonDescriptionStatus, OrchResult
9f95a23c 18from orchestrator import OrchestratorValidationError
f67539c2 19from orchestrator.module import to_format, Format, OrchestratorCli, preview_table_osd
a4b75251 20from unittest import mock
9f95a23c
TL
21
22
23def _test_resource(data, resource_class, extra=None):
24 # ensure we can deserialize and serialize
25 rsc = resource_class.from_json(data)
f67539c2 26 assert rsc.to_json() == resource_class.from_json(rsc.to_json()).to_json()
9f95a23c
TL
27
28 if extra:
29 # if there is an unexpected data provided
f67539c2
TL
30 data_copy = data.copy()
31 data_copy.update(extra)
9f95a23c 32 with pytest.raises(OrchestratorValidationError):
f67539c2 33 resource_class.from_json(data_copy)
9f95a23c
TL
34
35
36def test_inventory():
37 json_data = {
38 'name': 'host0',
39 'addr': '1.2.3.4',
40 'devices': [
41 {
42 'sys_api': {
43 'rotational': '1',
44 'size': 1024,
45 },
46 'path': '/dev/sda',
47 'available': False,
48 'rejected_reasons': [],
49 'lvs': []
50 }
51 ]
52 }
53 _test_resource(json_data, InventoryHost, {'abc': False})
54 for devices in json_data['devices']:
55 _test_resource(devices, inventory.Device)
56
57 json_data = [{}, {'name': 'host0', 'addr': '1.2.3.4'}, {'devices': []}]
58 for data in json_data:
59 with pytest.raises(OrchestratorValidationError):
60 InventoryHost.from_json(data)
61
62
63def test_daemon_description():
64 json_data = {
65 'hostname': 'test',
66 'daemon_type': 'mon',
f67539c2
TL
67 'daemon_id': 'a',
68 'status': -1,
9f95a23c
TL
69 }
70 _test_resource(json_data, DaemonDescription, {'abc': False})
71
f67539c2
TL
72 dd = DaemonDescription.from_json(json_data)
73 assert dd.status.value == DaemonDescriptionStatus.error.value
9f95a23c 74
f91f0fd5 75
9f95a23c
TL
76def test_apply():
77 to = _TestOrchestrator('', 0, 0)
78 completion = to.apply([
20effc67
TL
79 ServiceSpec(service_type='nfs', service_id='foo'),
80 ServiceSpec(service_type='nfs', service_id='foo'),
81 ServiceSpec(service_type='nfs', service_id='foo'),
9f95a23c 82 ])
20effc67 83 res = '<NFSServiceSpec for service_name=nfs.foo>'
f67539c2 84 assert completion.result == [res, res, res]
f6b5b4d7
TL
85
86
87def test_yaml():
88 y = """daemon_type: crash
89daemon_id: ubuntu
a4b75251 90daemon_name: crash.ubuntu
f6b5b4d7
TL
91hostname: ubuntu
92status: 1
93status_desc: starting
94is_active: false
95events:
adb31ebb 96- 2020-06-10T10:08:22.933241Z daemon:crash.ubuntu [INFO] "Deployed crash.ubuntu on
f6b5b4d7
TL
97 host 'ubuntu'"
98---
99service_type: crash
100service_name: crash
101placement:
102 host_pattern: '*'
103status:
104 container_image_id: 74803e884bea289d2d2d3ebdf6d37cd560499e955595695b1390a89800f4e37a
105 container_image_name: docker.io/ceph/daemon-base:latest-master-devel
adb31ebb
TL
106 created: '2020-06-10T10:37:31.051288Z'
107 last_refresh: '2020-06-10T10:57:40.715637Z'
f6b5b4d7
TL
108 running: 1
109 size: 1
110events:
adb31ebb 111- 2020-06-10T10:37:31.139159Z service:crash [INFO] "service was created"
f6b5b4d7
TL
112"""
113 types = (DaemonDescription, ServiceDescription)
114
115 for y, cls in zip(y.split('---\n'), types):
116 data = yaml.safe_load(y)
117 object = cls.from_json(data)
118
f67539c2
TL
119 assert to_format(object, Format.yaml, False, cls) == y
120 assert to_format([object], Format.yaml, True, cls) == y
f6b5b4d7 121
f67539c2
TL
122 j = json.loads(to_format(object, Format.json, False, cls))
123 assert to_format(cls.from_json(j), Format.yaml, False, cls) == y
f6b5b4d7
TL
124
125
126def test_event_multiline():
127 from .._interface import OrchestratorEvent
adb31ebb 128 e = OrchestratorEvent(datetime_now(), 'service', 'subject', 'ERROR', 'message')
f6b5b4d7
TL
129 assert OrchestratorEvent.from_json(e.to_json()) == e
130
adb31ebb 131 e = OrchestratorEvent(datetime_now(), 'service',
f91f0fd5 132 'subject', 'ERROR', 'multiline\nmessage')
f6b5b4d7 133 assert OrchestratorEvent.from_json(e.to_json()) == e
f91f0fd5
TL
134
135
f67539c2
TL
136def test_handle_command():
137 cmd = {
138 'prefix': 'orch daemon add',
139 'daemon_type': 'mon',
140 'placement': 'smithi044:[v2:172.21.15.44:3301,v1:172.21.15.44:6790]=c',
141 }
142 m = OrchestratorCli('orchestrator', 0, 0)
143 r = m._handle_command(None, cmd)
144 assert r == HandleCommandResult(
145 retval=-2, stdout='', stderr='No orchestrator configured (try `ceph orch set backend`)')
146
147
a4b75251
TL
148r = OrchResult([ServiceDescription(spec=ServiceSpec(service_type='osd'), running=123)])
149
150
151@mock.patch("orchestrator.OrchestratorCli.describe_service", return_value=r)
152def test_orch_ls(_describe_service):
153 cmd = {
154 'prefix': 'orch ls',
155 }
156 m = OrchestratorCli('orchestrator', 0, 0)
157 r = m._handle_command(None, cmd)
158 out = 'NAME PORTS RUNNING REFRESHED AGE PLACEMENT \n' \
159 'osd 123 - - '
160 assert r == HandleCommandResult(retval=0, stdout=out, stderr='')
161
20effc67
TL
162 cmd = {
163 'prefix': 'orch ls',
164 'format': 'yaml',
165 }
166 m = OrchestratorCli('orchestrator', 0, 0)
167 r = m._handle_command(None, cmd)
168 out = textwrap.dedent("""
169 service_type: osd
170 service_name: osd
171 spec:
172 filter_logic: AND
173 objectstore: bluestore
174 status:
175 running: 123
176 size: 0
177 """).lstrip()
178 assert r == HandleCommandResult(retval=0, stdout=out, stderr='')
179
a4b75251 180
33c7a0ef
TL
181dlist = OrchResult([DaemonDescription(daemon_type="osd", daemon_id="1"), DaemonDescription(
182 daemon_type="osd", daemon_id="10"), DaemonDescription(daemon_type="osd", daemon_id="2")])
183
184
185@mock.patch("orchestrator.OrchestratorCli.list_daemons", return_value=dlist)
186def test_orch_ps(_describe_service):
187
188 # Ensure natural sorting on daemon names (osd.1, osd.2, osd.10)
189 cmd = {
190 'prefix': 'orch ps'
191 }
192 m = OrchestratorCli('orchestrator', 0, 0)
193 r = m._handle_command(None, cmd)
2a845540
TL
194 expected_out = 'NAME HOST PORTS STATUS REFRESHED AGE MEM USE MEM LIM VERSION IMAGE ID \n'\
195 'osd.1 <unknown> unknown - - - - <unknown> <unknown> \n'\
196 'osd.2 <unknown> unknown - - - - <unknown> <unknown> \n'\
197 'osd.10 <unknown> unknown - - - - <unknown> <unknown> '
198 expected_out = [c for c in expected_out if c.isalpha()]
199 actual_out = [c for c in r.stdout if c.isalpha()]
200 assert r.retval == 0
201 assert expected_out == actual_out
202 assert r.stderr == ''
33c7a0ef
TL
203
204
205hlist = OrchResult([HostSpec("ceph-node-1"), HostSpec("ceph-node-2"), HostSpec("ceph-node-10")])
206
207
208@mock.patch("orchestrator.OrchestratorCli.get_hosts", return_value=hlist)
209def test_orch_host_ls(_describe_service):
210
211 # Ensure natural sorting on hostnames (ceph-node-1, ceph-node-2, ceph-node-10)
212 cmd = {
213 'prefix': 'orch host ls'
214 }
215 m = OrchestratorCli('orchestrator', 0, 0)
216 r = m._handle_command(None, cmd)
2a845540
TL
217 expected_out = 'HOST ADDR LABELS STATUS \n'\
218 'ceph-node-1 ceph-node-1 \n'\
219 'ceph-node-2 ceph-node-2 \n'\
220 'ceph-node-10 ceph-node-10 \n'\
221 '3 hosts in cluster'
222 expected_out = [c for c in expected_out if c.isalpha()]
223 actual_out = [c for c in r.stdout if c.isalpha()]
224 assert r.retval == 0
225 assert expected_out == actual_out
226 assert r.stderr == ''
33c7a0ef
TL
227
228
229def test_orch_device_ls():
230 devices = Devices([Device("/dev/vdb", available=True)])
231 ilist = OrchResult([InventoryHost("ceph-node-1", devices=devices), InventoryHost("ceph-node-2",
232 devices=devices), InventoryHost("ceph-node-10", devices=devices)])
233
234 with mock.patch("orchestrator.OrchestratorCli.get_inventory", return_value=ilist):
235 # Ensure natural sorting on hostnames (ceph-node-1, ceph-node-2, ceph-node-10)
236 cmd = {
237 'prefix': 'orch device ls'
238 }
239 m = OrchestratorCli('orchestrator', 0, 0)
240 r = m._handle_command(None, cmd)
2a845540
TL
241 expected_out = 'HOST PATH TYPE DEVICE ID SIZE AVAILABLE REFRESHED REJECT REASONS \n'\
242 'ceph-node-1 /dev/vdb unknown None 0 Yes 0s ago \n'\
243 'ceph-node-2 /dev/vdb unknown None 0 Yes 0s ago \n'\
244 'ceph-node-10 /dev/vdb unknown None 0 Yes 0s ago '
245 expected_out = [c for c in expected_out if c.isalpha()]
246 actual_out = [c for c in r.stdout if c.isalpha()]
247 assert r.retval == 0
248 assert expected_out == actual_out
249 assert r.stderr == ''
33c7a0ef
TL
250
251
f91f0fd5
TL
252def test_preview_table_osd_smoke():
253 data = [
254 {
255 'service_type': 'osd',
256 'data':
257 {
258 'foo host':
259 [
260 {
261 'osdspec': 'foo',
262 'error': '',
263 'data':
264 [
265 {
266 "block_db": "/dev/nvme0n1",
267 "block_db_size": "66.67 GB",
268 "data": "/dev/sdb",
269 "data_size": "300.00 GB",
270 "encryption": "None"
271 },
272 {
273 "block_db": "/dev/nvme0n1",
274 "block_db_size": "66.67 GB",
275 "data": "/dev/sdc",
276 "data_size": "300.00 GB",
277 "encryption": "None"
278 },
279 {
280 "block_db": "/dev/nvme0n1",
281 "block_db_size": "66.67 GB",
282 "data": "/dev/sdd",
283 "data_size": "300.00 GB",
284 "encryption": "None"
285 }
286 ]
287 }
288 ]
289 }
290 }
291 ]
292 preview_table_osd(data)