]>
Commit | Line | Data |
---|---|---|
a4b75251 TL |
1 | from textwrap import dedent |
2 | import json | |
3 | import yaml | |
4 | ||
f91f0fd5 TL |
5 | import pytest |
6 | ||
20effc67 | 7 | from unittest.mock import MagicMock, call, patch, ANY |
e306af50 | 8 | |
a4b75251 | 9 | from cephadm.serve import CephadmServe |
f91f0fd5 | 10 | from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \ |
f67539c2 | 11 | RbdMirrorService, CrashService, CephadmDaemonDeploySpec |
f91f0fd5 TL |
12 | from cephadm.services.iscsi import IscsiService |
13 | from cephadm.services.nfs import NFSService | |
f67539c2 | 14 | from cephadm.services.osd import OSDService |
f91f0fd5 TL |
15 | from cephadm.services.monitoring import GrafanaService, AlertmanagerService, PrometheusService, \ |
16 | NodeExporterService | |
a4b75251 TL |
17 | from cephadm.module import CephadmOrchestrator |
18 | from ceph.deployment.service_spec import IscsiServiceSpec, MonitoringSpec, AlertManagerSpec, \ | |
20effc67 TL |
19 | ServiceSpec, RGWSpec, GrafanaSpec, SNMPGatewaySpec, IngressSpec, PlacementSpec |
20 | from cephadm.tests.fixtures import with_host, with_service, _run_cephadm, async_side_effect | |
f91f0fd5 TL |
21 | |
22 | from orchestrator import OrchestratorError | |
f67539c2 | 23 | from orchestrator._interface import DaemonDescription |
e306af50 TL |
24 | |
25 | ||
b3b6e05e TL |
26 | class FakeInventory: |
27 | def get_addr(self, name: str) -> str: | |
28 | return '1.2.3.4' | |
29 | ||
30 | ||
e306af50 TL |
31 | class FakeMgr: |
32 | def __init__(self): | |
33 | self.config = '' | |
34 | self.check_mon_command = MagicMock(side_effect=self._check_mon_command) | |
f67539c2 TL |
35 | self.mon_command = MagicMock(side_effect=self._check_mon_command) |
36 | self.template = MagicMock() | |
37 | self.log = MagicMock() | |
b3b6e05e | 38 | self.inventory = FakeInventory() |
e306af50 | 39 | |
cd265ab1 | 40 | def _check_mon_command(self, cmd_dict, inbuf=None): |
e306af50 TL |
41 | prefix = cmd_dict.get('prefix') |
42 | if prefix == 'get-cmd': | |
43 | return 0, self.config, '' | |
44 | if prefix == 'set-cmd': | |
45 | self.config = cmd_dict.get('value') | |
46 | return 0, 'value set', '' | |
47 | return -1, '', 'error' | |
48 | ||
f67539c2 TL |
49 | def get_minimal_ceph_conf(self) -> str: |
50 | return '' | |
51 | ||
a4b75251 TL |
52 | def get_mgr_ip(self) -> str: |
53 | return '1.2.3.4' | |
54 | ||
e306af50 TL |
55 | |
56 | class TestCephadmService: | |
57 | def test_set_service_url_on_dashboard(self): | |
58 | # pylint: disable=protected-access | |
59 | mgr = FakeMgr() | |
60 | service_url = 'http://svc:1000' | |
f6b5b4d7 | 61 | service = GrafanaService(mgr) |
e306af50 TL |
62 | service._set_service_url_on_dashboard('svc', 'get-cmd', 'set-cmd', service_url) |
63 | assert mgr.config == service_url | |
64 | ||
65 | # set-cmd should not be called if value doesn't change | |
66 | mgr.check_mon_command.reset_mock() | |
67 | service._set_service_url_on_dashboard('svc', 'get-cmd', 'set-cmd', service_url) | |
68 | mgr.check_mon_command.assert_called_once_with({'prefix': 'get-cmd'}) | |
f91f0fd5 TL |
69 | |
70 | def _get_services(self, mgr): | |
71 | # services: | |
72 | osd_service = OSDService(mgr) | |
73 | nfs_service = NFSService(mgr) | |
74 | mon_service = MonService(mgr) | |
75 | mgr_service = MgrService(mgr) | |
76 | mds_service = MdsService(mgr) | |
77 | rgw_service = RgwService(mgr) | |
78 | rbd_mirror_service = RbdMirrorService(mgr) | |
79 | grafana_service = GrafanaService(mgr) | |
80 | alertmanager_service = AlertmanagerService(mgr) | |
81 | prometheus_service = PrometheusService(mgr) | |
82 | node_exporter_service = NodeExporterService(mgr) | |
83 | crash_service = CrashService(mgr) | |
84 | iscsi_service = IscsiService(mgr) | |
85 | cephadm_services = { | |
86 | 'mon': mon_service, | |
87 | 'mgr': mgr_service, | |
88 | 'osd': osd_service, | |
89 | 'mds': mds_service, | |
90 | 'rgw': rgw_service, | |
91 | 'rbd-mirror': rbd_mirror_service, | |
92 | 'nfs': nfs_service, | |
93 | 'grafana': grafana_service, | |
94 | 'alertmanager': alertmanager_service, | |
95 | 'prometheus': prometheus_service, | |
96 | 'node-exporter': node_exporter_service, | |
97 | 'crash': crash_service, | |
98 | 'iscsi': iscsi_service, | |
99 | } | |
100 | return cephadm_services | |
101 | ||
102 | def test_get_auth_entity(self): | |
103 | mgr = FakeMgr() | |
104 | cephadm_services = self._get_services(mgr) | |
105 | ||
106 | for daemon_type in ['rgw', 'rbd-mirror', 'nfs', "iscsi"]: | |
107 | assert "client.%s.id1" % (daemon_type) == \ | |
108 | cephadm_services[daemon_type].get_auth_entity("id1", "host") | |
109 | assert "client.%s.id1" % (daemon_type) == \ | |
110 | cephadm_services[daemon_type].get_auth_entity("id1", "") | |
111 | assert "client.%s.id1" % (daemon_type) == \ | |
112 | cephadm_services[daemon_type].get_auth_entity("id1") | |
113 | ||
114 | assert "client.crash.host" == \ | |
115 | cephadm_services["crash"].get_auth_entity("id1", "host") | |
116 | with pytest.raises(OrchestratorError): | |
f67539c2 TL |
117 | cephadm_services["crash"].get_auth_entity("id1", "") |
118 | cephadm_services["crash"].get_auth_entity("id1") | |
f91f0fd5 TL |
119 | |
120 | assert "mon." == cephadm_services["mon"].get_auth_entity("id1", "host") | |
121 | assert "mon." == cephadm_services["mon"].get_auth_entity("id1", "") | |
122 | assert "mon." == cephadm_services["mon"].get_auth_entity("id1") | |
123 | ||
124 | assert "mgr.id1" == cephadm_services["mgr"].get_auth_entity("id1", "host") | |
125 | assert "mgr.id1" == cephadm_services["mgr"].get_auth_entity("id1", "") | |
126 | assert "mgr.id1" == cephadm_services["mgr"].get_auth_entity("id1") | |
127 | ||
128 | for daemon_type in ["osd", "mds"]: | |
129 | assert "%s.id1" % daemon_type == \ | |
130 | cephadm_services[daemon_type].get_auth_entity("id1", "host") | |
131 | assert "%s.id1" % daemon_type == \ | |
132 | cephadm_services[daemon_type].get_auth_entity("id1", "") | |
133 | assert "%s.id1" % daemon_type == \ | |
134 | cephadm_services[daemon_type].get_auth_entity("id1") | |
135 | ||
f67539c2 | 136 | # services based on CephadmService shouldn't have get_auth_entity |
f91f0fd5 | 137 | with pytest.raises(AttributeError): |
20effc67 | 138 | for daemon_type in ['grafana', 'alertmanager', 'prometheus', 'node-exporter']: |
f91f0fd5 TL |
139 | cephadm_services[daemon_type].get_auth_entity("id1", "host") |
140 | cephadm_services[daemon_type].get_auth_entity("id1", "") | |
141 | cephadm_services[daemon_type].get_auth_entity("id1") | |
f67539c2 TL |
142 | |
143 | ||
144 | class TestISCSIService: | |
145 | ||
146 | mgr = FakeMgr() | |
147 | iscsi_service = IscsiService(mgr) | |
148 | ||
149 | iscsi_spec = IscsiServiceSpec(service_type='iscsi', service_id="a") | |
150 | iscsi_spec.daemon_type = "iscsi" | |
151 | iscsi_spec.daemon_id = "a" | |
152 | iscsi_spec.spec = MagicMock() | |
153 | iscsi_spec.spec.daemon_type = "iscsi" | |
154 | iscsi_spec.spec.ssl_cert = '' | |
155 | iscsi_spec.api_user = "user" | |
156 | iscsi_spec.api_password = "password" | |
157 | iscsi_spec.api_port = 5000 | |
158 | iscsi_spec.api_secure = False | |
159 | iscsi_spec.ssl_cert = "cert" | |
160 | iscsi_spec.ssl_key = "key" | |
161 | ||
162 | mgr.spec_store = MagicMock() | |
163 | mgr.spec_store.all_specs.get.return_value = iscsi_spec | |
164 | ||
165 | def test_iscsi_client_caps(self): | |
166 | ||
167 | iscsi_daemon_spec = CephadmDaemonDeploySpec( | |
168 | host='host', daemon_id='a', service_name=self.iscsi_spec.service_name()) | |
169 | ||
170 | self.iscsi_service.prepare_create(iscsi_daemon_spec) | |
171 | ||
172 | expected_caps = ['mon', | |
173 | 'profile rbd, allow command "osd blocklist", allow command "config-key get" with "key" prefix "iscsi/"', | |
174 | 'mgr', 'allow command "service status"', | |
175 | 'osd', 'allow rwx'] | |
176 | ||
177 | expected_call = call({'prefix': 'auth get-or-create', | |
178 | 'entity': 'client.iscsi.a', | |
179 | 'caps': expected_caps}) | |
180 | expected_call2 = call({'prefix': 'auth caps', | |
181 | 'entity': 'client.iscsi.a', | |
182 | 'caps': expected_caps}) | |
183 | ||
184 | assert expected_call in self.mgr.mon_command.mock_calls | |
185 | assert expected_call2 in self.mgr.mon_command.mock_calls | |
186 | ||
187 | @patch('cephadm.utils.resolve_ip') | |
188 | def test_iscsi_dashboard_config(self, mock_resolve_ip): | |
189 | ||
190 | self.mgr.check_mon_command = MagicMock() | |
191 | self.mgr.check_mon_command.return_value = ('', '{"gateways": {}}', '') | |
192 | ||
193 | # Case 1: use IPV4 address | |
194 | id1 = DaemonDescription(daemon_type='iscsi', hostname="testhost1", | |
195 | daemon_id="a", ip='192.168.1.1') | |
196 | daemon_list = [id1] | |
197 | mock_resolve_ip.return_value = '192.168.1.1' | |
198 | ||
199 | self.iscsi_service.config_dashboard(daemon_list) | |
200 | ||
201 | dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', | |
202 | 'name': 'testhost1'}, | |
203 | 'http://user:password@192.168.1.1:5000') | |
204 | ||
205 | assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls | |
206 | ||
207 | # Case 2: use IPV6 address | |
208 | self.mgr.check_mon_command.reset_mock() | |
209 | ||
210 | id1 = DaemonDescription(daemon_type='iscsi', hostname="testhost1", | |
211 | daemon_id="a", ip='FEDC:BA98:7654:3210:FEDC:BA98:7654:3210') | |
212 | mock_resolve_ip.return_value = 'FEDC:BA98:7654:3210:FEDC:BA98:7654:3210' | |
213 | ||
214 | self.iscsi_service.config_dashboard(daemon_list) | |
215 | ||
216 | dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', | |
217 | 'name': 'testhost1'}, | |
218 | 'http://user:password@[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:5000') | |
219 | ||
220 | assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls | |
221 | ||
222 | # Case 3: IPV6 Address . Secure protocol | |
223 | self.mgr.check_mon_command.reset_mock() | |
224 | ||
225 | self.iscsi_spec.api_secure = True | |
226 | ||
227 | self.iscsi_service.config_dashboard(daemon_list) | |
228 | ||
229 | dashboard_expected_call = call({'prefix': 'dashboard iscsi-gateway-add', | |
230 | 'name': 'testhost1'}, | |
231 | 'https://user:password@[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:5000') | |
232 | ||
233 | assert dashboard_expected_call in self.mgr.check_mon_command.mock_calls | |
a4b75251 TL |
234 | |
235 | ||
236 | class TestMonitoring: | |
237 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
238 | def test_alertmanager_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
20effc67 | 239 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) |
a4b75251 TL |
240 | |
241 | with with_host(cephadm_module, 'test'): | |
242 | with with_service(cephadm_module, AlertManagerSpec()): | |
243 | ||
244 | y = dedent(""" | |
245 | # This file is generated by cephadm. | |
246 | # See https://prometheus.io/docs/alerting/configuration/ for documentation. | |
247 | ||
248 | global: | |
249 | resolve_timeout: 5m | |
250 | ||
251 | route: | |
252 | receiver: 'default' | |
253 | routes: | |
254 | - group_by: ['alertname'] | |
255 | group_wait: 10s | |
256 | group_interval: 10s | |
257 | repeat_interval: 1h | |
258 | receiver: 'ceph-dashboard' | |
259 | ||
260 | receivers: | |
261 | - name: 'default' | |
262 | webhook_configs: | |
263 | - name: 'ceph-dashboard' | |
264 | webhook_configs: | |
265 | - url: 'http://[::1]:8080/api/prometheus_receiver' | |
266 | """).lstrip() | |
267 | ||
268 | _run_cephadm.assert_called_with( | |
269 | 'test', | |
270 | 'alertmanager.test', | |
271 | 'deploy', | |
272 | [ | |
273 | '--name', 'alertmanager.test', | |
20effc67 | 274 | '--meta-json', '{"service_name": "alertmanager", "ports": [9093, 9094], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', |
a4b75251 TL |
275 | '--config-json', '-', '--tcp-ports', '9093 9094' |
276 | ], | |
277 | stdin=json.dumps({"files": {"alertmanager.yml": y}, "peers": []}), | |
278 | image='')\ | |
279 | ||
280 | ||
281 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
282 | def test_prometheus_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
20effc67 | 283 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) |
a4b75251 TL |
284 | |
285 | with with_host(cephadm_module, 'test'): | |
286 | with with_service(cephadm_module, MonitoringSpec('node-exporter')) as _, \ | |
287 | with_service(cephadm_module, MonitoringSpec('prometheus')) as _: | |
288 | ||
289 | y = dedent(""" | |
290 | # This file is generated by cephadm. | |
291 | global: | |
292 | scrape_interval: 10s | |
293 | evaluation_interval: 10s | |
294 | rule_files: | |
295 | - /etc/prometheus/alerting/* | |
296 | scrape_configs: | |
297 | - job_name: 'ceph' | |
298 | honor_labels: true | |
299 | static_configs: | |
300 | - targets: | |
301 | - '[::1]:8081' | |
302 | ||
303 | - job_name: 'node' | |
304 | static_configs: | |
305 | - targets: ['[1::4]:9100'] | |
306 | labels: | |
307 | instance: 'test' | |
308 | ||
309 | """).lstrip() | |
310 | ||
311 | _run_cephadm.assert_called_with( | |
312 | 'test', | |
313 | 'prometheus.test', | |
314 | 'deploy', | |
315 | [ | |
316 | '--name', 'prometheus.test', | |
317 | '--meta-json', | |
20effc67 | 318 | '{"service_name": "prometheus", "ports": [9095], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', |
a4b75251 TL |
319 | '--config-json', '-', |
320 | '--tcp-ports', '9095' | |
321 | ], | |
322 | stdin=json.dumps({"files": {"prometheus.yml": y}}), | |
323 | image='') | |
324 | ||
325 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
326 | @patch("cephadm.module.CephadmOrchestrator.get_mgr_ip", lambda _: '1::4') | |
327 | @patch("cephadm.services.monitoring.verify_tls", lambda *_: None) | |
328 | def test_grafana_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
20effc67 | 329 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) |
a4b75251 TL |
330 | |
331 | with with_host(cephadm_module, 'test'): | |
332 | cephadm_module.set_store('grafana_crt', 'c') | |
333 | cephadm_module.set_store('grafana_key', 'k') | |
334 | with with_service(cephadm_module, MonitoringSpec('prometheus')) as _, \ | |
20effc67 | 335 | with_service(cephadm_module, GrafanaSpec('grafana')) as _: |
a4b75251 TL |
336 | files = { |
337 | 'grafana.ini': dedent(""" | |
338 | # This file is generated by cephadm. | |
339 | [users] | |
340 | default_theme = light | |
341 | [auth.anonymous] | |
342 | enabled = true | |
343 | org_name = 'Main Org.' | |
344 | org_role = 'Viewer' | |
345 | [server] | |
346 | domain = 'bootstrap.storage.lab' | |
347 | protocol = https | |
348 | cert_file = /etc/grafana/certs/cert_file | |
349 | cert_key = /etc/grafana/certs/cert_key | |
350 | http_port = 3000 | |
351 | http_addr = | |
352 | [security] | |
20effc67 TL |
353 | disable_initial_admin_creation = true |
354 | cookie_secure = true | |
355 | cookie_samesite = none | |
a4b75251 TL |
356 | allow_embedding = true""").lstrip(), # noqa: W291 |
357 | 'provisioning/datasources/ceph-dashboard.yml': dedent(""" | |
358 | # This file is generated by cephadm. | |
359 | deleteDatasources: | |
360 | - name: 'Dashboard1' | |
361 | orgId: 1 | |
362 | ||
363 | datasources: | |
364 | - name: 'Dashboard1' | |
365 | type: 'prometheus' | |
366 | access: 'proxy' | |
367 | orgId: 1 | |
368 | url: 'http://[1::4]:9095' | |
369 | basicAuth: false | |
370 | isDefault: true | |
371 | editable: false | |
372 | """).lstrip(), | |
373 | 'certs/cert_file': dedent(""" | |
374 | # generated by cephadm | |
375 | c""").lstrip(), | |
376 | 'certs/cert_key': dedent(""" | |
377 | # generated by cephadm | |
378 | k""").lstrip(), | |
379 | } | |
380 | ||
381 | _run_cephadm.assert_called_with( | |
382 | 'test', | |
383 | 'grafana.test', | |
384 | 'deploy', | |
385 | [ | |
386 | '--name', 'grafana.test', | |
387 | '--meta-json', | |
20effc67 | 388 | '{"service_name": "grafana", "ports": [3000], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', |
a4b75251 TL |
389 | '--config-json', '-', '--tcp-ports', '3000'], |
390 | stdin=json.dumps({"files": files}), | |
391 | image='') | |
392 | ||
20effc67 TL |
393 | @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) |
394 | def test_grafana_initial_admin_pw(self, cephadm_module: CephadmOrchestrator): | |
395 | with with_host(cephadm_module, 'test'): | |
396 | with with_service(cephadm_module, GrafanaSpec(initial_admin_password='secure')): | |
397 | out = cephadm_module.cephadm_services['grafana'].generate_config( | |
398 | CephadmDaemonDeploySpec('test', 'daemon', 'grafana')) | |
399 | assert out == ( | |
400 | { | |
401 | 'files': | |
402 | { | |
403 | 'certs/cert_file': ANY, | |
404 | 'certs/cert_key': ANY, | |
405 | 'grafana.ini': | |
406 | '# This file is generated by cephadm.\n' | |
407 | '[users]\n' | |
408 | ' default_theme = light\n' | |
409 | '[auth.anonymous]\n' | |
410 | ' enabled = true\n' | |
411 | " org_name = 'Main Org.'\n" | |
412 | " org_role = 'Viewer'\n" | |
413 | '[server]\n' | |
414 | " domain = 'bootstrap.storage.lab'\n" | |
415 | ' protocol = https\n' | |
416 | ' cert_file = /etc/grafana/certs/cert_file\n' | |
417 | ' cert_key = /etc/grafana/certs/cert_key\n' | |
418 | ' http_port = 3000\n' | |
419 | ' http_addr = \n' | |
420 | '[security]\n' | |
421 | ' admin_user = admin\n' | |
422 | ' admin_password = secure\n' | |
423 | ' cookie_secure = true\n' | |
424 | ' cookie_samesite = none\n' | |
425 | ' allow_embedding = true', | |
426 | 'provisioning/datasources/ceph-dashboard.yml': | |
427 | '# This file is generated by cephadm.\n' | |
428 | 'deleteDatasources:\n' | |
429 | '\n' | |
430 | 'datasources:\n' | |
431 | } | |
432 | }, | |
433 | [], | |
434 | ) | |
435 | ||
a4b75251 TL |
436 | @patch("cephadm.serve.CephadmServe._run_cephadm") |
437 | def test_monitoring_ports(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
20effc67 | 438 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) |
a4b75251 TL |
439 | |
440 | with with_host(cephadm_module, 'test'): | |
441 | ||
442 | yaml_str = """service_type: alertmanager | |
443 | service_name: alertmanager | |
444 | placement: | |
445 | count: 1 | |
446 | spec: | |
447 | port: 4200 | |
448 | """ | |
449 | yaml_file = yaml.safe_load(yaml_str) | |
450 | spec = ServiceSpec.from_json(yaml_file) | |
451 | ||
452 | with patch("cephadm.services.monitoring.AlertmanagerService.generate_config", return_value=({}, [])): | |
453 | with with_service(cephadm_module, spec): | |
454 | ||
455 | CephadmServe(cephadm_module)._check_daemons() | |
456 | ||
457 | _run_cephadm.assert_called_with( | |
458 | 'test', 'alertmanager.test', 'deploy', [ | |
459 | '--name', 'alertmanager.test', | |
20effc67 | 460 | '--meta-json', '{"service_name": "alertmanager", "ports": [4200, 9094], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', |
a4b75251 TL |
461 | '--config-json', '-', |
462 | '--tcp-ports', '4200 9094', | |
463 | '--reconfig' | |
464 | ], | |
465 | stdin='{}', | |
466 | image='') | |
467 | ||
468 | ||
469 | class TestRGWService: | |
470 | ||
471 | @pytest.mark.parametrize( | |
472 | "frontend, ssl, expected", | |
473 | [ | |
474 | ('beast', False, 'beast endpoint=[fd00:fd00:fd00:3000::1]:80'), | |
475 | ('beast', True, | |
476 | 'beast ssl_endpoint=[fd00:fd00:fd00:3000::1]:443 ssl_certificate=config://rgw/cert/rgw.foo'), | |
477 | ('civetweb', False, 'civetweb port=[fd00:fd00:fd00:3000::1]:80'), | |
478 | ('civetweb', True, | |
479 | 'civetweb port=[fd00:fd00:fd00:3000::1]:443s ssl_certificate=config://rgw/cert/rgw.foo'), | |
480 | ] | |
481 | ) | |
482 | @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) | |
483 | def test_rgw_update(self, frontend, ssl, expected, cephadm_module: CephadmOrchestrator): | |
484 | with with_host(cephadm_module, 'host1'): | |
20effc67 TL |
485 | cephadm_module.cache.update_host_networks('host1', { |
486 | 'fd00:fd00:fd00:3000::/64': { | |
487 | 'if0': ['fd00:fd00:fd00:3000::1'] | |
488 | } | |
489 | }) | |
a4b75251 TL |
490 | s = RGWSpec(service_id="foo", |
491 | networks=['fd00:fd00:fd00:3000::/64'], | |
492 | ssl=ssl, | |
493 | rgw_frontend_type=frontend) | |
494 | with with_service(cephadm_module, s) as dds: | |
495 | _, f, _ = cephadm_module.check_mon_command({ | |
496 | 'prefix': 'config get', | |
497 | 'who': f'client.{dds[0]}', | |
498 | 'key': 'rgw_frontends', | |
499 | }) | |
500 | assert f == expected | |
20effc67 TL |
501 | |
502 | ||
503 | class TestSNMPGateway: | |
504 | ||
505 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
506 | def test_snmp_v2c_deployment(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
507 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
508 | ||
509 | spec = SNMPGatewaySpec( | |
510 | snmp_version='V2c', | |
511 | snmp_destination='192.168.1.1:162', | |
512 | credentials={ | |
513 | 'snmp_community': 'public' | |
514 | }) | |
515 | ||
516 | config = { | |
517 | "destination": spec.snmp_destination, | |
518 | "snmp_version": spec.snmp_version, | |
519 | "snmp_community": spec.credentials.get('snmp_community') | |
520 | } | |
521 | ||
522 | with with_host(cephadm_module, 'test'): | |
523 | with with_service(cephadm_module, spec): | |
524 | _run_cephadm.assert_called_with( | |
525 | 'test', | |
526 | 'snmp-gateway.test', | |
527 | 'deploy', | |
528 | [ | |
529 | '--name', 'snmp-gateway.test', | |
530 | '--meta-json', | |
531 | '{"service_name": "snmp-gateway", "ports": [9464], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', | |
532 | '--config-json', '-', | |
533 | '--tcp-ports', '9464' | |
534 | ], | |
535 | stdin=json.dumps(config), | |
536 | image='' | |
537 | ) | |
538 | ||
539 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
540 | def test_snmp_v2c_with_port(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
541 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
542 | ||
543 | spec = SNMPGatewaySpec( | |
544 | snmp_version='V2c', | |
545 | snmp_destination='192.168.1.1:162', | |
546 | credentials={ | |
547 | 'snmp_community': 'public' | |
548 | }, | |
549 | port=9465) | |
550 | ||
551 | config = { | |
552 | "destination": spec.snmp_destination, | |
553 | "snmp_version": spec.snmp_version, | |
554 | "snmp_community": spec.credentials.get('snmp_community') | |
555 | } | |
556 | ||
557 | with with_host(cephadm_module, 'test'): | |
558 | with with_service(cephadm_module, spec): | |
559 | _run_cephadm.assert_called_with( | |
560 | 'test', | |
561 | 'snmp-gateway.test', | |
562 | 'deploy', | |
563 | [ | |
564 | '--name', 'snmp-gateway.test', | |
565 | '--meta-json', | |
566 | '{"service_name": "snmp-gateway", "ports": [9465], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', | |
567 | '--config-json', '-', | |
568 | '--tcp-ports', '9465' | |
569 | ], | |
570 | stdin=json.dumps(config), | |
571 | image='' | |
572 | ) | |
573 | ||
574 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
575 | def test_snmp_v3nopriv_deployment(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
576 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
577 | ||
578 | spec = SNMPGatewaySpec( | |
579 | snmp_version='V3', | |
580 | snmp_destination='192.168.1.1:162', | |
581 | engine_id='8000C53F00000000', | |
582 | credentials={ | |
583 | 'snmp_v3_auth_username': 'myuser', | |
584 | 'snmp_v3_auth_password': 'mypassword' | |
585 | }) | |
586 | ||
587 | config = { | |
588 | 'destination': spec.snmp_destination, | |
589 | 'snmp_version': spec.snmp_version, | |
590 | 'snmp_v3_auth_protocol': 'SHA', | |
591 | 'snmp_v3_auth_username': 'myuser', | |
592 | 'snmp_v3_auth_password': 'mypassword', | |
593 | 'snmp_v3_engine_id': '8000C53F00000000' | |
594 | } | |
595 | ||
596 | with with_host(cephadm_module, 'test'): | |
597 | with with_service(cephadm_module, spec): | |
598 | _run_cephadm.assert_called_with( | |
599 | 'test', | |
600 | 'snmp-gateway.test', | |
601 | 'deploy', | |
602 | [ | |
603 | '--name', 'snmp-gateway.test', | |
604 | '--meta-json', | |
605 | '{"service_name": "snmp-gateway", "ports": [9464], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', | |
606 | '--config-json', '-', | |
607 | '--tcp-ports', '9464' | |
608 | ], | |
609 | stdin=json.dumps(config), | |
610 | image='' | |
611 | ) | |
612 | ||
613 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
614 | def test_snmp_v3priv_deployment(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
615 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
616 | ||
617 | spec = SNMPGatewaySpec( | |
618 | snmp_version='V3', | |
619 | snmp_destination='192.168.1.1:162', | |
620 | engine_id='8000C53F00000000', | |
621 | auth_protocol='MD5', | |
622 | privacy_protocol='AES', | |
623 | credentials={ | |
624 | 'snmp_v3_auth_username': 'myuser', | |
625 | 'snmp_v3_auth_password': 'mypassword', | |
626 | 'snmp_v3_priv_password': 'mysecret', | |
627 | }) | |
628 | ||
629 | config = { | |
630 | 'destination': spec.snmp_destination, | |
631 | 'snmp_version': spec.snmp_version, | |
632 | 'snmp_v3_auth_protocol': 'MD5', | |
633 | 'snmp_v3_auth_username': spec.credentials.get('snmp_v3_auth_username'), | |
634 | 'snmp_v3_auth_password': spec.credentials.get('snmp_v3_auth_password'), | |
635 | 'snmp_v3_engine_id': '8000C53F00000000', | |
636 | 'snmp_v3_priv_protocol': spec.privacy_protocol, | |
637 | 'snmp_v3_priv_password': spec.credentials.get('snmp_v3_priv_password'), | |
638 | } | |
639 | ||
640 | with with_host(cephadm_module, 'test'): | |
641 | with with_service(cephadm_module, spec): | |
642 | _run_cephadm.assert_called_with( | |
643 | 'test', | |
644 | 'snmp-gateway.test', | |
645 | 'deploy', | |
646 | [ | |
647 | '--name', 'snmp-gateway.test', | |
648 | '--meta-json', | |
649 | '{"service_name": "snmp-gateway", "ports": [9464], "ip": null, "deployed_by": [], "rank": null, "rank_generation": null, "extra_container_args": null}', | |
650 | '--config-json', '-', | |
651 | '--tcp-ports', '9464' | |
652 | ], | |
653 | stdin=json.dumps(config), | |
654 | image='' | |
655 | ) | |
656 | ||
657 | ||
658 | class TestIngressService: | |
659 | ||
660 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
661 | def test_ingress_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
662 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
663 | ||
664 | with with_host(cephadm_module, 'test'): | |
665 | cephadm_module.cache.update_host_networks('test', { | |
666 | '1.2.3.0/24': { | |
667 | 'if0': ['1.2.3.4/32'] | |
668 | } | |
669 | }) | |
670 | ||
671 | # the ingress backend | |
672 | s = RGWSpec(service_id="foo", placement=PlacementSpec(count=1), | |
673 | rgw_frontend_type='beast') | |
674 | ||
675 | ispec = IngressSpec(service_type='ingress', | |
676 | service_id='test', | |
677 | backend_service='rgw.foo', | |
678 | frontend_port=8089, | |
679 | monitor_port=8999, | |
680 | monitor_user='admin', | |
681 | monitor_password='12345', | |
682 | keepalived_password='12345', | |
683 | virtual_interface_networks=['1.2.3.0/24'], | |
684 | virtual_ip="1.2.3.4/32") | |
685 | with with_service(cephadm_module, s) as _, with_service(cephadm_module, ispec) as _: | |
686 | # generate the keepalived conf based on the specified spec | |
687 | keepalived_generated_conf = cephadm_module.cephadm_services['ingress'].keepalived_generate_config( | |
688 | CephadmDaemonDeploySpec(host='test', daemon_id='ingress', service_name=ispec.service_name())) | |
689 | ||
690 | keepalived_expected_conf = { | |
691 | 'files': | |
692 | { | |
693 | 'keepalived.conf': | |
694 | '# This file is generated by cephadm.\n' | |
695 | 'vrrp_script check_backend {\n ' | |
696 | 'script "/usr/bin/curl http://localhost:8999/health"\n ' | |
697 | 'weight -20\n ' | |
698 | 'interval 2\n ' | |
699 | 'rise 2\n ' | |
700 | 'fall 2\n}\n\n' | |
701 | 'vrrp_instance VI_0 {\n ' | |
702 | 'state MASTER\n ' | |
703 | 'priority 100\n ' | |
704 | 'interface if0\n ' | |
705 | 'virtual_router_id 51\n ' | |
706 | 'advert_int 1\n ' | |
707 | 'authentication {\n ' | |
708 | 'auth_type PASS\n ' | |
709 | 'auth_pass 12345\n ' | |
710 | '}\n ' | |
711 | 'unicast_src_ip 1::4\n ' | |
712 | 'unicast_peer {\n ' | |
713 | '}\n ' | |
714 | 'virtual_ipaddress {\n ' | |
715 | '1.2.3.4/32 dev if0\n ' | |
716 | '}\n ' | |
717 | 'track_script {\n ' | |
718 | 'check_backend\n }\n' | |
719 | '}' | |
720 | } | |
721 | } | |
722 | ||
723 | # check keepalived config | |
724 | assert keepalived_generated_conf[0] == keepalived_expected_conf | |
725 | ||
726 | # generate the haproxy conf based on the specified spec | |
727 | haproxy_generated_conf = cephadm_module.cephadm_services['ingress'].haproxy_generate_config( | |
728 | CephadmDaemonDeploySpec(host='test', daemon_id='ingress', service_name=ispec.service_name())) | |
729 | ||
730 | haproxy_expected_conf = { | |
731 | 'files': | |
732 | { | |
733 | 'haproxy.cfg': | |
734 | '# This file is generated by cephadm.' | |
735 | '\nglobal\n log ' | |
736 | '127.0.0.1 local2\n ' | |
737 | 'chroot /var/lib/haproxy\n ' | |
738 | 'pidfile /var/lib/haproxy/haproxy.pid\n ' | |
739 | 'maxconn 8000\n ' | |
740 | 'daemon\n ' | |
741 | 'stats socket /var/lib/haproxy/stats\n' | |
742 | '\ndefaults\n ' | |
743 | 'mode http\n ' | |
744 | 'log global\n ' | |
745 | 'option httplog\n ' | |
746 | 'option dontlognull\n ' | |
747 | 'option http-server-close\n ' | |
748 | 'option forwardfor except 127.0.0.0/8\n ' | |
749 | 'option redispatch\n ' | |
750 | 'retries 3\n ' | |
751 | 'timeout queue 20s\n ' | |
752 | 'timeout connect 5s\n ' | |
753 | 'timeout http-request 1s\n ' | |
754 | 'timeout http-keep-alive 5s\n ' | |
755 | 'timeout client 1s\n ' | |
756 | 'timeout server 1s\n ' | |
757 | 'timeout check 5s\n ' | |
758 | 'maxconn 8000\n' | |
759 | '\nfrontend stats\n ' | |
760 | 'mode http\n ' | |
761 | 'bind 1.2.3.4:8999\n ' | |
762 | 'bind localhost:8999\n ' | |
763 | 'stats enable\n ' | |
764 | 'stats uri /stats\n ' | |
765 | 'stats refresh 10s\n ' | |
766 | 'stats auth admin:12345\n ' | |
767 | 'http-request use-service prometheus-exporter if { path /metrics }\n ' | |
768 | 'monitor-uri /health\n' | |
769 | '\nfrontend frontend\n ' | |
770 | 'bind 1.2.3.4:8089\n ' | |
771 | 'default_backend backend\n\n' | |
772 | 'backend backend\n ' | |
773 | 'option forwardfor\n ' | |
774 | 'balance static-rr\n ' | |
775 | 'option httpchk HEAD / HTTP/1.0\n ' | |
776 | 'server ' | |
777 | + haproxy_generated_conf[1][0] + ' 1::4:80 check weight 100\n' | |
778 | } | |
779 | } | |
780 | ||
781 | assert haproxy_generated_conf[0] == haproxy_expected_conf | |
782 | ||
783 | ||
784 | class TestCephFsMirror: | |
785 | @patch("cephadm.serve.CephadmServe._run_cephadm") | |
786 | def test_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): | |
787 | _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) | |
788 | with with_host(cephadm_module, 'test'): | |
789 | with with_service(cephadm_module, ServiceSpec('cephfs-mirror')): | |
790 | cephadm_module.assert_issued_mon_command({ | |
791 | 'prefix': 'mgr module enable', | |
792 | 'module': 'mirroring' | |
793 | }) |