Skip to content

Commit

Permalink
T6949: adds blackbox exporter (vyos#4255)
Browse files Browse the repository at this point in the history
* T6949: adds blackbox exporter

* T6949: adds basic config generation

* T6949: extract shared module config options

* T6949: switch to ipv4/6 literals

* T6949: moves config file to /run

* T6949: adds dns query name option

* T6949: adds dns query type values

* T6949: adds blackbox exporter to debian/control
  • Loading branch information
nvollmar authored Dec 31, 2024
1 parent 6f649d3 commit 8f0c2c8
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 0 deletions.
21 changes: 21 additions & 0 deletions data/templates/prometheus/blackbox_exporter.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' runuser -u node_exporter -- ' if vrf is vyos_defined else '' %}
[Unit]
Description=Blackbox Exporter
Documentation=https://github.com/prometheus/blackbox_exporter
After=network.target

[Service]
{% if vrf is not vyos_defined %}
User=node_exporter
{% endif %}
ExecStart={{ vrf_command }}/usr/sbin/blackbox_exporter \
{% if listen_address is vyos_defined %}
{% for address in listen_address %}
--web.listen-address={{ address }}:{{ port }} \
{% endfor %}
{% else %}
--web.listen-address=:{{ port }} \
{% endif %}
--config.file=/run/blackbox_exporter/config.yml
[Install]
WantedBy=multi-user.target
23 changes: 23 additions & 0 deletions data/templates/prometheus/blackbox_exporter.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
modules:
{% if modules is defined and modules.dns is defined and modules.dns.name is defined %}
{% for module_name, module_config in modules.dns.name.items() %}
{{ module_name }}:
prober: dns
timeout: {{ module_config.timeout }}s
dns:
query_name: "{{ module_config.query_name }}"
query_type: "{{ module_config.query_type }}"
preferred_ip_protocol: "{{ module_config.preferred_ip_protocol | replace('v', '') }}"
ip_protocol_fallback: {{ 'true' if module_config.ip_protocol_fallback is vyos_defined else 'false' }}
{% endfor %}
{% endif %}
{% if modules is defined and modules.icmp is vyos_defined and modules.icmp.name is vyos_defined %}
{% for module_name, module_config in modules.icmp.name.items() %}
{{ module_name }}:
prober: icmp
timeout: {{ module_config.timeout }}s
icmp:
preferred_ip_protocol: "{{ module_config.preferred_ip_protocol | replace('v', '') }}"
ip_protocol_fallback: {{ 'true' if module_config.ip_protocol_fallback is vyos_defined else 'false' }}
{% endfor %}
{% endif %}
3 changes: 3 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ Depends:
# For "service monitoring prometheus frr-exporter"
frr-exporter,
# End "service monitoring prometheus frr-exporter"
# For "service monitoring prometheus blackbox-exporter"
blackbox-exporter,
# End "service monitoring prometheus blackbox-exporter"
# For "service monitoring telegraf"
telegraf (>= 1.20),
# End "service monitoring telegraf"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!-- include start from monitoring/blackbox-module-commons.xml.i -->
<leafNode name="timeout">
<properties>
<help>Timeout in seconds for the probe request</help>
<valueHelp>
<format>u32:1-60</format>
<description>Timeout in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-60"/>
</constraint>
<constraintErrorMessage>Timeout must be between 1 and 60 seconds</constraintErrorMessage>
</properties>
<defaultValue>5</defaultValue>
</leafNode>
<leafNode name="preferred-ip-protocol">
<properties>
<help>Preferred IP protocol for this module</help>
<valueHelp>
<format>ipv4</format>
<description>Prefer IPv4</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>Prefer IPv6</description>
</valueHelp>
<constraint>
<regex>(ipv4|ipv6)</regex>
</constraint>
</properties>
<defaultValue>ip6</defaultValue>
</leafNode>
<leafNode name="ip-protocol-fallback">
<properties>
<help>Allow fallback to other IP protocol if necessary</help>
<valueless/>
</properties>
</leafNode>
<!-- include end -->
76 changes: 76 additions & 0 deletions interface-definitions/service_monitoring_prometheus.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,82 @@
#include <include/interface/vrf.xml.i>
</children>
</node>
<node name="blackbox-exporter">
<properties>
<help>Prometheus exporter for probing endpoints</help>
</properties>
<children>
#include <include/listen-address.xml.i>
#include <include/port-number.xml.i>
<leafNode name="port">
<defaultValue>9115</defaultValue>
</leafNode>
#include <include/interface/vrf.xml.i>
<node name="modules">
<properties>
<help>Configure blackbox exporter modules</help>
</properties>
<children>
<node name="dns">
<properties>
<help>Configure dns module</help>
</properties>
<children>
<tagNode name="name">
<properties>
<help>Name of the dns module</help>
</properties>
<children>
<leafNode name="query-name">
<properties>
<help>Name to be queried</help>
<constraint>
<validator name="fqdn"/>
</constraint>
</properties>
</leafNode>
<leafNode name="query-type">
<properties>
<help>DNS query type</help>
<valueHelp>
<format>ANY</format>
<description>Query any DNS record</description>
</valueHelp>
<valueHelp>
<format>A</format>
<description>Query IPv4 address record</description>
</valueHelp>
<valueHelp>
<format>AAAA</format>
<description>Query IPv6 address record</description>
</valueHelp>
</properties>
<defaultValue>ANY</defaultValue>
</leafNode>
#include <include/monitoring/blackbox-exporter-module-commons.xml.i>
</children>
</tagNode>
</children>
</node>
<node name="icmp">
<properties>
<help>Configure icmp module</help>
</properties>
<children>
<tagNode name="name">
<properties>
<help>Name of the icmp module</help>
</properties>
<children>
#include <include/monitoring/blackbox-exporter-module-commons.xml.i>
</children>
</tagNode>
</children>
</node>
</children>
</node>
</children>
</node>
</children>
</node>
</children>
Expand Down
74 changes: 74 additions & 0 deletions smoketest/scripts/cli/test_service_monitoring_prometheus.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@

NODE_EXPORTER_PROCESS_NAME = 'node_exporter'
FRR_EXPORTER_PROCESS_NAME = 'frr_exporter'
BLACKBOX_EXPORTER_PROCESS_NAME = 'blackbox_exporter'

base_path = ['service', 'monitoring', 'prometheus']
listen_if = 'dum3421'
listen_ip = '192.0.2.1'
node_exporter_service_file = '/etc/systemd/system/node_exporter.service'
frr_exporter_service_file = '/etc/systemd/system/frr_exporter.service'
blackbox_exporter_service_file = '/etc/systemd/system/blackbox_exporter.service'


class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase):
Expand Down Expand Up @@ -75,6 +77,78 @@ def test_02_frr_exporter(self):
# Check for running process
self.assertTrue(process_named_running(FRR_EXPORTER_PROCESS_NAME))

def test_03_blackbox_exporter(self):
self.cli_set(base_path + ['blackbox-exporter', 'listen-address', listen_ip])

# commit changes
self.cli_commit()

file_content = read_file(blackbox_exporter_service_file)
self.assertIn(f'{listen_ip}:9115', file_content)

# Check for running process
self.assertTrue(process_named_running(BLACKBOX_EXPORTER_PROCESS_NAME))

def test_04_blackbox_exporter_with_config(self):
self.cli_set(base_path + ['blackbox-exporter', 'listen-address', listen_ip])
self.cli_set(
base_path
+ [
'blackbox-exporter',
'modules',
'dns',
'name',
'dns_ip4',
'preferred-ip-protocol',
'ipv4',
]
)
self.cli_set(
base_path
+ [
'blackbox-exporter',
'modules',
'dns',
'name',
'dns_ip4',
'query-name',
'vyos.io',
]
)
self.cli_set(
base_path
+ [
'blackbox-exporter',
'modules',
'dns',
'name',
'dns_ip4',
'query-type',
'A',
]
)
self.cli_set(
base_path
+ [
'blackbox-exporter',
'modules',
'icmp',
'name',
'icmp_ip6',
'preferred-ip-protocol',
'ipv6',
]
)

# commit changes
self.cli_commit()

file_content = read_file(blackbox_exporter_service_file)
self.assertIn(f'{listen_ip}:9115', file_content)

# Check for running process
self.assertTrue(process_named_running(BLACKBOX_EXPORTER_PROCESS_NAME))


if __name__ == '__main__':
unittest.main(verbosity=2)
54 changes: 54 additions & 0 deletions src/conf_mode/service_monitoring_prometheus.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
frr_exporter_service_file = '/etc/systemd/system/frr_exporter.service'
frr_exporter_systemd_service = 'frr_exporter.service'

blackbox_exporter_service_file = '/etc/systemd/system/blackbox_exporter.service'
blackbox_exporter_systemd_service = 'blackbox_exporter.service'


def get_config(config=None):
if config:
Expand All @@ -57,6 +60,12 @@ def get_config(config=None):
if tmp:
monitoring.update({'frr_exporter_restart_required': {}})

tmp = False
for node in ['vrf', 'config-file']:
tmp = tmp or is_node_changed(conf, base + ['blackbox-exporter', node])
if tmp:
monitoring.update({'blackbox_exporter_restart_required': {}})

return monitoring


Expand All @@ -70,6 +79,22 @@ def verify(monitoring):
if 'frr_exporter' in monitoring:
verify_vrf(monitoring['frr_exporter'])

if 'blackbox_exporter' in monitoring:
verify_vrf(monitoring['blackbox_exporter'])

if (
'modules' in monitoring['blackbox_exporter']
and 'dns' in monitoring['blackbox_exporter']['modules']
and 'name' in monitoring['blackbox_exporter']['modules']['dns']
):
for mod_name, mod_config in monitoring['blackbox_exporter']['modules'][
'dns'
]['name'].items():
if 'query_name' not in mod_config:
raise ConfigError(
f'query name not specified in dns module {mod_name}'
)

return None


Expand All @@ -84,6 +109,11 @@ def generate(monitoring):
if os.path.isfile(frr_exporter_service_file):
os.unlink(frr_exporter_service_file)

if not monitoring or 'blackbox_exporter' not in monitoring:
# Delete systemd files
if os.path.isfile(blackbox_exporter_service_file):
os.unlink(blackbox_exporter_service_file)

if not monitoring:
return None

Expand All @@ -103,6 +133,20 @@ def generate(monitoring):
monitoring['frr_exporter'],
)

if 'blackbox_exporter' in monitoring:
# Render blackbox_exporter service_file
render(
blackbox_exporter_service_file,
'prometheus/blackbox_exporter.service.j2',
monitoring['blackbox_exporter'],
)
# Render blackbox_exporter config file
render(
'/run/blackbox_exporter/config.yml',
'prometheus/blackbox_exporter.yml.j2',
monitoring['blackbox_exporter'],
)

return None


Expand All @@ -113,6 +157,8 @@ def apply(monitoring):
call(f'systemctl stop {node_exporter_systemd_service}')
if not monitoring or 'frr_exporter' not in monitoring:
call(f'systemctl stop {frr_exporter_systemd_service}')
if not monitoring or 'blackbox_exporter' not in monitoring:
call(f'systemctl stop {blackbox_exporter_systemd_service}')

if not monitoring:
return
Expand All @@ -133,6 +179,14 @@ def apply(monitoring):

call(f'systemctl {systemd_action} {frr_exporter_systemd_service}')

if 'blackbox_exporter' in monitoring:
# we need to restart the service if e.g. the VRF name changed
systemd_action = 'reload-or-restart'
if 'blackbox_exporter_restart_required' in monitoring:
systemd_action = 'restart'

call(f'systemctl {systemd_action} {blackbox_exporter_systemd_service}')


if __name__ == '__main__':
try:
Expand Down

0 comments on commit 8f0c2c8

Please sign in to comment.