From e769a80b6ca896bacadefa66bbc374a9be3b39f7 Mon Sep 17 00:00:00 2001 From: mgirgisf Date: Tue, 25 Mar 2025 10:17:23 +0100 Subject: [PATCH] Enable prom-exporter with tls Change-Id: Ibfb485dc6855af6e34e78e2d473a6edb485562b8 --- ceilometer/polling/manager.py | 26 +++++++- ceilometer/polling/prom_exporter.py | 10 +-- ceilometer/tests/unit/test_prom_exporter.py | 61 +++++++++++++++++-- ...omethus-exporter-tls-76e78d4f4a52c6c4.yaml | 4 ++ 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 releasenotes/notes/enable-promethus-exporter-tls-76e78d4f4a52c6c4.yaml diff --git a/ceilometer/polling/manager.py b/ceilometer/polling/manager.py index fabcd28d12..2fdc9b8d19 100644 --- a/ceilometer/polling/manager.py +++ b/ceilometer/polling/manager.py @@ -106,6 +106,17 @@ POLLING_OPTS = [ default=False, help='Whether the polling service should ignore ' 'disabled projects or not.'), + cfg.BoolOpt('prometheus_tls_enable', + default=False, + help='Whether it will expose tls metrics or not'), + cfg.StrOpt('prometheus_tls_certfile', + default=None, + help='The certificate file to allow this ceilometer to ' + 'expose tls scrape endpoints'), + cfg.StrOpt('prometheus_tls_keyfile', + default=None, + help='The private key to allow this ceilometer to ' + 'expose tls scrape endpoints'), ] @@ -473,7 +484,20 @@ class AgentManager(cotyledon.Service): address = netutils.parse_host_port(addr) if address[0] is None or address[1] is None: LOG.warning('Ignoring invalid address: %s', addr) - prom_exporter.export(address[0], address[1]) + certfile = self.conf.polling.prometheus_tls_certfile + keyfile = self.conf.polling.prometheus_tls_keyfile + if self.conf.polling.prometheus_tls_enable: + if not certfile or not keyfile: + raise ValueError( + "Certfile and keyfile must be provided." + ) + else: + certfile = keyfile = None + prom_exporter.export( + address[0], + address[1], + certfile, + keyfile) self._keystone = None self._keystone_last_exception = None diff --git a/ceilometer/polling/prom_exporter.py b/ceilometer/polling/prom_exporter.py index a3617bb225..9e42456efa 100644 --- a/ceilometer/polling/prom_exporter.py +++ b/ceilometer/polling/prom_exporter.py @@ -19,10 +19,12 @@ import prometheus_client as prom CEILOMETER_REGISTRY = prom.CollectorRegistry() -def export(prometheus_iface, prometheus_port): - prom.start_http_server(port=prometheus_port, - addr=prometheus_iface, - registry=CEILOMETER_REGISTRY) +def export(prom_iface, prom_port, tls_cert=None, tls_key=None): + prom.start_http_server(port=prom_port, + addr=prom_iface, + registry=CEILOMETER_REGISTRY, + certfile=tls_cert, + keyfile=tls_key) def collect_metrics(samples): diff --git a/ceilometer/tests/unit/test_prom_exporter.py b/ceilometer/tests/unit/test_prom_exporter.py index 1ba8a59e39..c96faf54c3 100644 --- a/ceilometer/tests/unit/test_prom_exporter.py +++ b/ceilometer/tests/unit/test_prom_exporter.py @@ -243,12 +243,65 @@ class TestPromExporter(base.BaseTestCase): manager.AgentManager(0, CONF) export.assert_has_calls([ - call('127.0.0.1', 9101), - call('127.0.0.1', 9102), - call('::1', 9103), - call('localhost', 9104), + call('127.0.0.1', 9101, None, None), + call('127.0.0.1', 9102, None, None), + call('::1', 9103, None, None), + call('localhost', 9104, None, None), ]) + @mock.patch('ceilometer.polling.prom_exporter.export') + def test_export_called_tls_disabled(self, export): + CONF = service.prepare_service([], []) + CONF.polling.enable_prometheus_exporter = True + CONF.polling.prometheus_tls_enable = False + CONF.polling.prometheus_tls_certfile = "cert.pem" + CONF.polling.prometheus_listen_addresses = [ + '127.0.0.1:9101', + '127.0.0.1:9102', + '[::1]:9103', + 'localhost:9104', + ] + manager.AgentManager(0, CONF) + + export.assert_has_calls([ + call('127.0.0.1', 9101, None, None), + call('127.0.0.1', 9102, None, None), + call('::1', 9103, None, None), + call('localhost', 9104, None, None), + ]) + + @mock.patch('ceilometer.polling.prom_exporter.export') + def test_export_called_with_tls(self, export): + CONF = service.prepare_service([], []) + CONF.polling.enable_prometheus_exporter = True + CONF.polling.prometheus_listen_addresses = [ + '127.0.0.1:9101', + '127.0.0.1:9102', + '[::1]:9103', + 'localhost:9104', + ] + CONF.polling.prometheus_tls_enable = True + CONF.polling.prometheus_tls_certfile = "cert.pem" + CONF.polling.prometheus_tls_keyfile = "key.pem" + manager.AgentManager(0, CONF) + + export.assert_has_calls([ + call('127.0.0.1', 9101, "cert.pem", "key.pem"), + call('127.0.0.1', 9102, "cert.pem", "key.pem"), + call('::1', 9103, "cert.pem", "key.pem"), + call('localhost', 9104, "cert.pem", "key.pem"), + ]) + + @mock.patch('ceilometer.polling.prom_exporter.export') + def test_export_fails_if_incomplete_tls(self, export): + CONF = service.prepare_service([], []) + CONF.polling.enable_prometheus_exporter = True + CONF.polling.prometheus_listen_addresses = ['127.0.0.1:9101'] + CONF.polling.prometheus_tls_enable = True + CONF.polling.prometheus_tls_certfile = "cert.pem" + CONF.polling.prometheus_tls_keyfile = None # Missing key + self.assertRaises(ValueError, manager.AgentManager, 0, CONF) + def test_collect_metrics(self): prom_exporter.collect_metrics(self.test_image_size) sample_dict_1 = {'counter': 'image.size', diff --git a/releasenotes/notes/enable-promethus-exporter-tls-76e78d4f4a52c6c4.yaml b/releasenotes/notes/enable-promethus-exporter-tls-76e78d4f4a52c6c4.yaml new file mode 100644 index 0000000000..10cb6e2a20 --- /dev/null +++ b/releasenotes/notes/enable-promethus-exporter-tls-76e78d4f4a52c6c4.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Enhanced the Prometheus exporter to support TLS for exposing metrics securely. \ No newline at end of file