Skip to content

Commit 4127d5b

Browse files
authored
Merge pull request ceph#59194 from rhcs-dashboard/add-ssl-prometheus-federate
mgr/dashboard: Add ssl prometheus federate Reviewed-by: Redouane Kachach <[email protected]>
2 parents 624f695 + 9b9ce53 commit 4127d5b

File tree

17 files changed

+548
-120
lines changed

17 files changed

+548
-120
lines changed

src/pybind/mgr/cephadm/module.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from functools import wraps
1313
from tempfile import TemporaryDirectory, NamedTemporaryFile
1414
from urllib.error import HTTPError
15+
from urllib.parse import urlparse
1516
from threading import Event
1617

1718
from ceph.deployment.service_spec import PrometheusSpec
@@ -3248,13 +3249,25 @@ def set_custom_prometheus_alerts(self, alerts_file: str) -> str:
32483249

32493250
@handle_orch_error
32503251
def set_prometheus_target(self, url: str) -> str:
3252+
try:
3253+
parsed_url = urlparse(url)
3254+
host = parsed_url.hostname
3255+
port = parsed_url.port
3256+
if not host:
3257+
return 'Invalid URL. Hostname is missing.'
3258+
ipaddress.ip_address(host)
3259+
url = f"{host}:{port}" if port else host
3260+
except ValueError as e:
3261+
return f'Invalid url. {str(e)}'
32513262
prometheus_spec = cast(PrometheusSpec, self.spec_store['prometheus'].spec)
3263+
if not prometheus_spec:
3264+
return "Service prometheus not found\n"
3265+
# Add the target URL if it does not already exist
32523266
if url not in prometheus_spec.targets:
32533267
prometheus_spec.targets.append(url)
32543268
else:
32553269
return f"Target '{url}' already exists.\n"
3256-
if not prometheus_spec:
3257-
return "Service prometheus not found\n"
3270+
# Redeploy daemons after applying the configuration
32583271
daemons: List[orchestrator.DaemonDescription] = self.cache.get_daemons_by_type('prometheus')
32593272
spec = ServiceSpec.from_json(prometheus_spec.to_json())
32603273
self.apply([spec], no_overwrite=False)
@@ -3294,6 +3307,12 @@ def get_prometheus_access_info(self) -> Dict[str, str]:
32943307
'password': password,
32953308
'certificate': self.cert_mgr.get_root_ca()}
32963309

3310+
@handle_orch_error
3311+
def get_security_config(self) -> Dict[str, bool]:
3312+
security_enabled, mgmt_gw_enabled, _ = self._get_security_config()
3313+
return {'security_enabled': security_enabled,
3314+
'mgmt_gw_enabled': mgmt_gw_enabled}
3315+
32973316
@handle_orch_error
32983317
def get_alertmanager_access_info(self) -> Dict[str, str]:
32993318
security_enabled, _, _ = self._get_security_config()

src/pybind/mgr/cephadm/services/monitoring.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,17 @@ def generate_config(
507507

508508
alertmanager_user, alertmanager_password = self.mgr._get_alertmanager_credentials()
509509
prometheus_user, prometheus_password = self.mgr._get_prometheus_credentials()
510+
federate_path = self.get_target_cluster_federate_path(targets)
511+
cluster_credentials: Dict[str, Any] = {}
512+
cluster_credentials_files: Dict[str, Any] = {'files': {}}
510513
FSID = self.mgr._cluster_fsid
514+
if targets:
515+
if 'dashboard' in self.mgr.get('mgr_map')['modules']:
516+
cluster_credentials_files, cluster_credentials = self.mgr.remote(
517+
'dashboard', 'get_cluster_credentials_files', targets
518+
)
519+
else:
520+
logger.error("dashboard module not found")
511521

512522
# generate the prometheus configuration
513523
context = {
@@ -526,7 +536,9 @@ def generate_config(
526536
'external_prometheus_targets': targets,
527537
'cluster_fsid': FSID,
528538
'nfs_sd_url': nfs_sd_url,
529-
'smb_sd_url': smb_sd_url
539+
'smb_sd_url': smb_sd_url,
540+
'clusters_credentials': cluster_credentials,
541+
'federate_path': federate_path
530542
}
531543

532544
ip_to_bind_to = ''
@@ -563,6 +575,7 @@ def generate_config(
563575
'web_config': '/etc/prometheus/web.yml',
564576
'use_url_prefix': mgmt_gw_enabled
565577
}
578+
r['files'].update(cluster_credentials_files['files'])
566579
else:
567580
r = {
568581
'files': {
@@ -674,6 +687,12 @@ def ok_to_stop(self,
674687
return HandleCommandResult(-errno.EBUSY, '', warn_message)
675688
return HandleCommandResult(0, warn_message, '')
676689

690+
def get_target_cluster_federate_path(self, targets: List[str]) -> str:
691+
for target in targets:
692+
if ':' in target:
693+
return '/federate'
694+
return '/prometheus/federate'
695+
677696

678697
class NodeExporterService(CephadmService):
679698
TYPE = 'node-exporter'

src/pybind/mgr/cephadm/templates/services/prometheus/prometheus.yml.j2

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
global:
33
scrape_interval: 10s
44
evaluation_interval: 10s
5-
{% if not security_enabled %}
65
external_labels:
76
cluster: {{ cluster_fsid }}
8-
{% endif %}
97

108
rule_files:
119
- /etc/prometheus/alerting/*
@@ -39,15 +37,18 @@ alerting:
3937

4038
scrape_configs:
4139
- job_name: 'ceph'
40+
relabel_configs:
41+
- source_labels: [__address__]
42+
target_label: cluster
43+
replacement: {{ cluster_fsid }}
44+
- source_labels: [instance]
45+
target_label: instance
46+
replacement: 'ceph_cluster'
4247
{% if security_enabled %}
4348
scheme: https
4449
tls_config:
4550
ca_file: root_cert.pem
4651
honor_labels: true
47-
relabel_configs:
48-
- source_labels: [instance]
49-
target_label: instance
50-
replacement: 'ceph_cluster'
5152
http_sd_configs:
5253
- url: {{ mgr_prometheus_sd_url }}
5354
basic_auth:
@@ -57,19 +58,16 @@ scrape_configs:
5758
ca_file: root_cert.pem
5859
{% else %}
5960
honor_labels: true
60-
relabel_configs:
61-
- source_labels: [__address__]
62-
target_label: cluster
63-
replacement: {{ cluster_fsid }}
64-
- source_labels: [instance]
65-
target_label: instance
66-
replacement: 'ceph_cluster'
6761
http_sd_configs:
6862
- url: {{ mgr_prometheus_sd_url }}
6963
{% endif %}
7064

7165
{% if node_exporter_sd_url %}
7266
- job_name: 'node'
67+
relabel_configs:
68+
- source_labels: [__address__]
69+
target_label: cluster
70+
replacement: {{ cluster_fsid }}
7371
{% if security_enabled %}
7472
scheme: https
7573
tls_config:
@@ -86,15 +84,15 @@ scrape_configs:
8684
{% else %}
8785
http_sd_configs:
8886
- url: {{ node_exporter_sd_url }}
89-
relabel_configs:
90-
- source_labels: [__address__]
91-
target_label: cluster
92-
replacement: {{ cluster_fsid }}
9387
{% endif %}
9488
{% endif %}
9589

9690
{% if haproxy_sd_url %}
9791
- job_name: 'haproxy'
92+
relabel_configs:
93+
- source_labels: [__address__]
94+
target_label: cluster
95+
replacement: {{ cluster_fsid }}
9896
{% if security_enabled %}
9997
scheme: https
10098
tls_config:
@@ -109,15 +107,15 @@ scrape_configs:
109107
{% else %}
110108
http_sd_configs:
111109
- url: {{ haproxy_sd_url }}
112-
relabel_configs:
113-
- source_labels: [__address__]
114-
target_label: cluster
115-
replacement: {{ cluster_fsid }}
116110
{% endif %}
117111
{% endif %}
118112

119113
{% if ceph_exporter_sd_url %}
120114
- job_name: 'ceph-exporter'
115+
relabel_configs:
116+
- source_labels: [__address__]
117+
target_label: cluster
118+
replacement: {{ cluster_fsid }}
121119
{% if security_enabled %}
122120
honor_labels: true
123121
scheme: https
@@ -132,10 +130,6 @@ scrape_configs:
132130
ca_file: root_cert.pem
133131
{% else %}
134132
honor_labels: true
135-
relabel_configs:
136-
- source_labels: [__address__]
137-
target_label: cluster
138-
replacement: {{ cluster_fsid }}
139133
http_sd_configs:
140134
- url: {{ ceph_exporter_sd_url }}
141135
{% endif %}
@@ -201,17 +195,30 @@ scrape_configs:
201195
{% endif %}
202196
{% endif %}
203197

204-
{% if not security_enabled %}
205-
- job_name: 'federate'
198+
{% for url, details in clusters_credentials.items() %}
199+
- job_name: 'federate_{{ loop.index }}'
206200
scrape_interval: 15s
207201
honor_labels: true
208-
metrics_path: '/federate'
202+
metrics_path: {{ federate_path }}
203+
relabel_configs:
204+
- source_labels: [__address__]
205+
target_label: cluster
206+
replacement: {{ cluster_fsid }}
207+
{% if security_enabled %}
208+
scheme: https
209+
tls_config:
210+
ca_file: {{ details['cert_file_name'] }}
211+
basic_auth:
212+
username: {{ details['user'] }}
213+
password: {{ details['password'] }}
214+
{% endif %}
209215
params:
210216
'match[]':
211217
- '{job="ceph"}'
212218
- '{job="node"}'
213219
- '{job="haproxy"}'
214220
- '{job="ceph-exporter"}'
215221
static_configs:
216-
- targets: {{ external_prometheus_targets }}
217-
{% endif %}
222+
- targets: ['{{ url }}']
223+
{% endfor %}
224+

src/pybind/mgr/cephadm/tests/test_cephadm.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,36 @@ def test_get_unique_name(self, cephadm_module):
159159
new_mgr = cephadm_module.get_unique_name('mgr', 'myhost', existing)
160160
match_glob(new_mgr, 'myhost.*')
161161

162+
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
163+
def test_valid_url(self, cephadm_module):
164+
# Test with valid IPv4 and IPv6 urls
165+
test_cases = [
166+
("http://192.168.100.100:9090", "prometheus multi-cluster targets updated"), # Valid IPv4
167+
("https://192.168.100.100/prometheus", "prometheus multi-cluster targets updated"), # Valid IPv4 without port
168+
("http://[2001:0db8:85a3::8a2e:0370:7334]:9090", "prometheus multi-cluster targets updated"), # Valid IPv6 with port
169+
("https://[2001:0db8:85a3::8a2e:0370:7334]/prometheus", "prometheus multi-cluster targets updated"), # Valid IPv6 without port
170+
]
171+
with with_host(cephadm_module, 'test'):
172+
with with_service(cephadm_module, ServiceSpec(service_type='prometheus'), CephadmOrchestrator.apply_prometheus, 'test'):
173+
for url, expected_output in test_cases:
174+
c = cephadm_module.set_prometheus_target(url)
175+
assert wait(cephadm_module, c) == expected_output
176+
177+
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
178+
def test_invalid_url(self, cephadm_module):
179+
# Test with invalid IPv4 and IPv6 urls
180+
test_cases = [
181+
("https://192.168.100.100:99999", "Invalid url. Port out of range 0-65535"), # Port out of range
182+
("http://[2001:0db8:85a3::8a2e:0370:7334]:99999", "Invalid url. Port out of range 0-65535"), # IPv6 with invalid port
183+
("https://192.168.100.999:9090", "Invalid url. '192.168.100.999' does not appear to be an IPv4 or IPv6 address"), # Invalid IPv4
184+
("http://[fe80:2030:31:24]:9090", "Invalid url. 'fe80:2030:31:24' does not appear to be an IPv4 or IPv6 address") # Invalid IPv6
185+
]
186+
with with_host(cephadm_module, 'test'):
187+
with with_service(cephadm_module, ServiceSpec(service_type='prometheus'), CephadmOrchestrator.apply_prometheus, 'test'):
188+
for url, expected_output in test_cases:
189+
c = cephadm_module.set_prometheus_target(url)
190+
assert wait(cephadm_module, c) == expected_output
191+
162192
@mock.patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('[]'))
163193
def test_host(self, cephadm_module):
164194
assert wait(cephadm_module, cephadm_module.get_hosts()) == []

0 commit comments

Comments
 (0)