]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/cephadm/tests/test_services.py
import quincy beta 17.1.0
[ceph.git] / ceph / src / pybind / mgr / cephadm / tests / test_services.py
CommitLineData
a4b75251
TL
1from textwrap import dedent
2import json
3import yaml
4
f91f0fd5
TL
5import pytest
6
20effc67 7from unittest.mock import MagicMock, call, patch, ANY
e306af50 8
a4b75251 9from cephadm.serve import CephadmServe
f91f0fd5 10from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \
f67539c2 11 RbdMirrorService, CrashService, CephadmDaemonDeploySpec
f91f0fd5
TL
12from cephadm.services.iscsi import IscsiService
13from cephadm.services.nfs import NFSService
f67539c2 14from cephadm.services.osd import OSDService
f91f0fd5
TL
15from cephadm.services.monitoring import GrafanaService, AlertmanagerService, PrometheusService, \
16 NodeExporterService
a4b75251
TL
17from cephadm.module import CephadmOrchestrator
18from ceph.deployment.service_spec import IscsiServiceSpec, MonitoringSpec, AlertManagerSpec, \
20effc67
TL
19 ServiceSpec, RGWSpec, GrafanaSpec, SNMPGatewaySpec, IngressSpec, PlacementSpec
20from cephadm.tests.fixtures import with_host, with_service, _run_cephadm, async_side_effect
f91f0fd5
TL
21
22from orchestrator import OrchestratorError
f67539c2 23from orchestrator._interface import DaemonDescription
e306af50
TL
24
25
b3b6e05e
TL
26class FakeInventory:
27 def get_addr(self, name: str) -> str:
28 return '1.2.3.4'
29
30
e306af50
TL
31class 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
56class 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
144class 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
236class 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
443service_name: alertmanager
444placement:
445 count: 1
446spec:
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
469class 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
503class 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
658class 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
784class 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 })