diff options
13 files changed, 272 insertions, 332 deletions
diff --git a/data/templates/frr_exporter/frr_exporter.service.j2 b/data/templates/prometheus/frr_exporter.service.j2 index c3892e42b..c3892e42b 100644 --- a/data/templates/frr_exporter/frr_exporter.service.j2 +++ b/data/templates/prometheus/frr_exporter.service.j2 diff --git a/data/templates/node_exporter/node_exporter.service.j2 b/data/templates/prometheus/node_exporter.service.j2 index 62e7e6774..62e7e6774 100644 --- a/data/templates/node_exporter/node_exporter.service.j2 +++ b/data/templates/prometheus/node_exporter.service.j2 diff --git a/debian/control b/debian/control index 76ca83dcd..08b86356a 100644 --- a/debian/control +++ b/debian/control @@ -235,12 +235,12 @@ Depends: squidclient, squidguard, # End "service webproxy" -# For "service monitoring node-exporter" +# For "service monitoring prometheus node-exporter" node-exporter, -# End "service monitoring node-exporter" -# For "service monitoring frr-exporter" +# End "service monitoring prometheus node-exporter" +# For "service monitoring prometheus frr-exporter" frr-exporter, -# End "service monitoring frr-exporter" +# End "service monitoring prometheus frr-exporter" # For "service monitoring telegraf" telegraf (>= 1.20), # End "service monitoring telegraf" diff --git a/interface-definitions/include/version/monitoring-version.xml.i b/interface-definitions/include/version/monitoring-version.xml.i index 6a275a5d8..2e2e0116e 100644 --- a/interface-definitions/include/version/monitoring-version.xml.i +++ b/interface-definitions/include/version/monitoring-version.xml.i @@ -1,3 +1,3 @@ <!-- include start from include/version/monitoring-version.xml.i --> -<syntaxVersion component='monitoring' version='1'></syntaxVersion> +<syntaxVersion component='monitoring' version='2'></syntaxVersion> <!-- include end --> diff --git a/interface-definitions/service_monitoring_frr_exporter.xml.in b/interface-definitions/service_monitoring_frr_exporter.xml.in deleted file mode 100644 index 96aee3ab4..000000000 --- a/interface-definitions/service_monitoring_frr_exporter.xml.in +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="service"> - <children> - <node name="monitoring"> - <children> - <node name="frr-exporter" owner="${vyos_conf_scripts_dir}/service_monitoring_frr-exporter.py"> - <properties> - <help>Prometheus exporter for FRR metrics</help> - <priority>1280</priority> - </properties> - <children> - #include <include/listen-address.xml.i> - #include <include/port-number.xml.i> - <leafNode name="port"> - <defaultValue>9342</defaultValue> - </leafNode> - #include <include/interface/vrf.xml.i> - </children> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/interface-definitions/service_monitoring_node_exporter.xml.in b/interface-definitions/service_monitoring_node_exporter.xml.in deleted file mode 100644 index a11d2304f..000000000 --- a/interface-definitions/service_monitoring_node_exporter.xml.in +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="service"> - <children> - <node name="monitoring"> - <children> - <node name="node-exporter" owner="${vyos_conf_scripts_dir}/service_monitoring_node-exporter.py"> - <properties> - <help>Prometheus exporter for hardware and operating system metrics</help> - <priority>1280</priority> - </properties> - <children> - #include <include/listen-address.xml.i> - #include <include/port-number.xml.i> - <leafNode name="port"> - <defaultValue>9100</defaultValue> - </leafNode> - #include <include/interface/vrf.xml.i> - </children> - </node> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/interface-definitions/service_monitoring_prometheus.xml.in b/interface-definitions/service_monitoring_prometheus.xml.in new file mode 100644 index 000000000..24f31e15c --- /dev/null +++ b/interface-definitions/service_monitoring_prometheus.xml.in @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<interfaceDefinition> + <node name="service"> + <children> + <node name="monitoring"> + <children> + <node name="prometheus" owner="${vyos_conf_scripts_dir}/service_monitoring_prometheus.py"> + <properties> + <help>Prometheus metric exporter</help> + <priority>1280</priority> + </properties> + <children> + <node name="node-exporter"> + <properties> + <help>Prometheus exporter for hardware and operating system metrics</help> + </properties> + <children> + #include <include/listen-address.xml.i> + #include <include/port-number.xml.i> + <leafNode name="port"> + <defaultValue>9100</defaultValue> + </leafNode> + #include <include/interface/vrf.xml.i> + </children> + </node> + <node name="frr-exporter"> + <properties> + <help>Prometheus exporter for FRR metrics</help> + </properties> + <children> + #include <include/listen-address.xml.i> + #include <include/port-number.xml.i> + <leafNode name="port"> + <defaultValue>9342</defaultValue> + </leafNode> + #include <include/interface/vrf.xml.i> + </children> + </node> + </children> + </node> + </children> + </node> + </children> + </node> +</interfaceDefinition> diff --git a/smoketest/scripts/cli/test_service_monitoring_node-exporter.py b/smoketest/scripts/cli/test_service_monitoring_node-exporter.py deleted file mode 100755 index e18a3f7a2..000000000 --- a/smoketest/scripts/cli/test_service_monitoring_node-exporter.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2024 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import unittest - -from base_vyostest_shim import VyOSUnitTestSHIM -from vyos.utils.process import process_named_running -from vyos.utils.file import read_file - -PROCESS_NAME = 'node_exporter' -base_path = ['service', 'monitoring', 'node-exporter'] -service_file = '/etc/systemd/system/node_exporter.service' -listen_if = 'dum3421' -listen_ip = '192.0.2.1' - - -class TestMonitoringNodeExporter(VyOSUnitTestSHIM.TestCase): - @classmethod - def setUpClass(cls): - # call base-classes classmethod - super(TestMonitoringNodeExporter, cls).setUpClass() - # create a test interfaces - cls.cli_set( - cls, ['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'] - ) - - @classmethod - def tearDownClass(cls): - cls.cli_delete(cls, ['interfaces', 'dummy', listen_if]) - super(TestMonitoringNodeExporter, cls).tearDownClass() - - def tearDown(self): - self.cli_delete(base_path) - self.cli_commit() - self.assertFalse(process_named_running(PROCESS_NAME)) - - def test_01_basic_config(self): - self.cli_set(base_path + ['listen-address', listen_ip]) - - # commit changes - self.cli_commit() - - file_content = read_file(service_file) - self.assertIn(f'{listen_ip}:9100', file_content) - - # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_monitoring_frr-exporter.py b/smoketest/scripts/cli/test_service_monitoring_prometheus.py index 230171c11..dae103e4b 100755 --- a/smoketest/scripts/cli/test_service_monitoring_frr-exporter.py +++ b/smoketest/scripts/cli/test_service_monitoring_prometheus.py @@ -20,18 +20,21 @@ from base_vyostest_shim import VyOSUnitTestSHIM from vyos.utils.process import process_named_running from vyos.utils.file import read_file -PROCESS_NAME = 'frr_exporter' -base_path = ['service', 'monitoring', 'frr-exporter'] -service_file = '/etc/systemd/system/frr_exporter.service' +NODE_EXPORTER_PROCESS_NAME = 'node_exporter' +FRR_EXPORTER_PROCESS_NAME = 'frr_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' -class TestMonitoringFrrExporter(VyOSUnitTestSHIM.TestCase): +class TestMonitoringPrometheus(VyOSUnitTestSHIM.TestCase): @classmethod def setUpClass(cls): # call base-classes classmethod - super(TestMonitoringFrrExporter, cls).setUpClass() + super(TestMonitoringPrometheus, cls).setUpClass() # create a test interfaces cls.cli_set( cls, ['interfaces', 'dummy', listen_if, 'address', listen_ip + '/32'] @@ -40,24 +43,37 @@ class TestMonitoringFrrExporter(VyOSUnitTestSHIM.TestCase): @classmethod def tearDownClass(cls): cls.cli_delete(cls, ['interfaces', 'dummy', listen_if]) - super(TestMonitoringFrrExporter, cls).tearDownClass() + super(TestMonitoringPrometheus, cls).tearDownClass() def tearDown(self): self.cli_delete(base_path) self.cli_commit() - self.assertFalse(process_named_running(PROCESS_NAME)) + self.assertFalse(process_named_running(NODE_EXPORTER_PROCESS_NAME)) + self.assertFalse(process_named_running(FRR_EXPORTER_PROCESS_NAME)) + + def test_01_node_exporter(self): + self.cli_set(base_path + ['node-exporter', 'listen-address', listen_ip]) + + # commit changes + self.cli_commit() + + file_content = read_file(node_exporter_service_file) + self.assertIn(f'{listen_ip}:9100', file_content) + + # Check for running process + self.assertTrue(process_named_running(NODE_EXPORTER_PROCESS_NAME)) - def test_01_basic_config(self): - self.cli_set(base_path + ['listen-address', listen_ip]) + def test_02_frr_exporter(self): + self.cli_set(base_path + ['frr-exporter', 'listen-address', listen_ip]) # commit changes self.cli_commit() - file_content = read_file(service_file) + file_content = read_file(frr_exporter_service_file) self.assertIn(f'{listen_ip}:9342', file_content) # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) + self.assertTrue(process_named_running(FRR_EXPORTER_PROCESS_NAME)) if __name__ == '__main__': diff --git a/src/conf_mode/service_monitoring_frr-exporter.py b/src/conf_mode/service_monitoring_frr-exporter.py deleted file mode 100755 index 01527d579..000000000 --- a/src/conf_mode/service_monitoring_frr-exporter.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2024 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os - -from sys import exit - -from vyos.config import Config -from vyos.configdict import is_node_changed -from vyos.configverify import verify_vrf -from vyos.template import render -from vyos.utils.process import call -from vyos import ConfigError -from vyos import airbag - - -airbag.enable() - -service_file = '/etc/systemd/system/frr_exporter.service' -systemd_service = 'frr_exporter.service' - - -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - base = ['service', 'monitoring', 'frr-exporter'] - if not conf.exists(base): - return None - - config_data = conf.get_config_dict( - base, key_mangling=('-', '_'), get_first_key=True - ) - config_data = conf.merge_defaults(config_data, recursive=True) - - tmp = is_node_changed(conf, base + ['vrf']) - if tmp: - config_data.update({'restart_required': {}}) - - return config_data - - -def verify(config_data): - # bail out early - looks like removal from running config - if not config_data: - return None - - verify_vrf(config_data) - return None - - -def generate(config_data): - if not config_data: - # Delete systemd files - if os.path.isfile(service_file): - os.unlink(service_file) - return None - - # Render frr_exporter service_file - render(service_file, 'frr_exporter/frr_exporter.service.j2', config_data) - return None - - -def apply(config_data): - # Reload systemd manager configuration - call('systemctl daemon-reload') - if not config_data: - call(f'systemctl stop {systemd_service}') - return - - # we need to restart the service if e.g. the VRF name changed - systemd_action = 'reload-or-restart' - if 'restart_required' in config_data: - systemd_action = 'restart' - - call(f'systemctl {systemd_action} {systemd_service}') - - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/service_monitoring_node-exporter.py b/src/conf_mode/service_monitoring_node-exporter.py deleted file mode 100755 index db34bb5d0..000000000 --- a/src/conf_mode/service_monitoring_node-exporter.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2024 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -import os - -from sys import exit - -from vyos.config import Config -from vyos.configdict import is_node_changed -from vyos.configverify import verify_vrf -from vyos.template import render -from vyos.utils.process import call -from vyos import ConfigError -from vyos import airbag - - -airbag.enable() - -service_file = '/etc/systemd/system/node_exporter.service' -systemd_service = 'node_exporter.service' - - -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - base = ['service', 'monitoring', 'node-exporter'] - if not conf.exists(base): - return None - - config_data = conf.get_config_dict( - base, key_mangling=('-', '_'), get_first_key=True - ) - config_data = conf.merge_defaults(config_data, recursive=True) - - tmp = is_node_changed(conf, base + ['vrf']) - if tmp: - config_data.update({'restart_required': {}}) - - return config_data - - -def verify(config_data): - # bail out early - looks like removal from running config - if not config_data: - return None - - verify_vrf(config_data) - return None - - -def generate(config_data): - if not config_data: - # Delete systemd files - if os.path.isfile(service_file): - os.unlink(service_file) - return None - - # Render node_exporter service_file - render(service_file, 'node_exporter/node_exporter.service.j2', config_data) - return None - - -def apply(config_data): - # Reload systemd manager configuration - call('systemctl daemon-reload') - if not config_data: - call(f'systemctl stop {systemd_service}') - return - - # we need to restart the service if e.g. the VRF name changed - systemd_action = 'reload-or-restart' - if 'restart_required' in config_data: - systemd_action = 'restart' - - call(f'systemctl {systemd_action} {systemd_service}') - - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) diff --git a/src/conf_mode/service_monitoring_prometheus.py b/src/conf_mode/service_monitoring_prometheus.py new file mode 100755 index 000000000..e0a9fc4ef --- /dev/null +++ b/src/conf_mode/service_monitoring_prometheus.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2024 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +from sys import exit + +from vyos.config import Config +from vyos.configdict import is_node_changed +from vyos.configverify import verify_vrf +from vyos.template import render +from vyos.utils.process import call +from vyos import ConfigError +from vyos import airbag + + +airbag.enable() + +node_exporter_service_file = '/etc/systemd/system/node_exporter.service' +node_exporter_systemd_service = 'node_exporter.service' + +frr_exporter_service_file = '/etc/systemd/system/frr_exporter.service' +frr_exporter_systemd_service = 'frr_exporter.service' + + +def get_config(config=None): + if config: + conf = config + else: + conf = Config() + base = ['service', 'monitoring', 'prometheus'] + if not conf.exists(base): + return None + + monitoring = conf.get_config_dict( + base, key_mangling=('-', '_'), get_first_key=True, with_recursive_defaults=True + ) + + tmp = is_node_changed(conf, base + ['node-exporter', 'vrf']) + if tmp: + monitoring.update({'node_exporter_restart_required': {}}) + + tmp = is_node_changed(conf, base + ['frr-exporter', 'vrf']) + if tmp: + monitoring.update({'frr_exporter_restart_required': {}}) + + return monitoring + + +def verify(monitoring): + if not monitoring: + return None + + if 'node_exporter' in monitoring: + verify_vrf(monitoring['node_exporter']) + + if 'frr_exporter' in monitoring: + verify_vrf(monitoring['frr_exporter']) + + return None + + +def generate(monitoring): + if not monitoring or 'node_exporter' not in monitoring: + # Delete systemd files + if os.path.isfile(node_exporter_service_file): + os.unlink(node_exporter_service_file) + + if not monitoring or 'frr_exporter' not in monitoring: + # Delete systemd files + if os.path.isfile(frr_exporter_service_file): + os.unlink(frr_exporter_service_file) + + if not monitoring: + return None + + if 'node_exporter' in monitoring: + # Render node_exporter node_exporter_service_file + render( + node_exporter_service_file, + 'prometheus/node_exporter.service.j2', + monitoring['node_exporter'], + ) + + if 'frr_exporter' in monitoring: + # Render frr_exporter service_file + render( + frr_exporter_service_file, + 'prometheus/frr_exporter.service.j2', + monitoring['frr_exporter'], + ) + + return None + + +def apply(monitoring): + # Reload systemd manager configuration + call('systemctl daemon-reload') + if not monitoring or 'node_exporter' not in 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: + return + + if 'node_exporter' in monitoring: + # we need to restart the service if e.g. the VRF name changed + systemd_action = 'reload-or-restart' + if 'node_exporter_restart_required' in monitoring: + systemd_action = 'restart' + + call(f'systemctl {systemd_action} {node_exporter_systemd_service}') + + if 'frr_exporter' in monitoring: + # we need to restart the service if e.g. the VRF name changed + systemd_action = 'reload-or-restart' + if 'frr_exporter_restart_required' in monitoring: + systemd_action = 'restart' + + call(f'systemctl {systemd_action} {frr_exporter_systemd_service}') + + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) diff --git a/src/migration-scripts/monitoring/1-to-2 b/src/migration-scripts/monitoring/1-to-2 new file mode 100644 index 000000000..8bdaebae9 --- /dev/null +++ b/src/migration-scripts/monitoring/1-to-2 @@ -0,0 +1,50 @@ +# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# T6953: merge node and frr exporter under prometheus section + +from vyos.configtree import ConfigTree + +old_base = ['service', 'monitoring'] +new_base = ['service', 'monitoring', 'prometheus'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(old_base): + # Nothing to do + return + + if config.exists(old_base + ['node-exporter']): + if config.exists(old_base + ['node-exporter', 'listen-address']): + tmp = config.return_value(old_base + ['node-exporter', 'listen-address']) + config.set(new_base + ['node-exporter', 'listen-address'], value=tmp) + if config.exists(old_base + ['node-exporter', 'port']): + tmp = config.return_value(old_base + ['node-exporter', 'port']) + config.set(new_base + ['node-exporter', 'port'], value=tmp) + if config.exists(old_base + ['node-exporter', 'vrf']): + tmp = config.return_value(old_base + ['node-exporter', 'vrf']) + config.set(new_base + ['node-exporter', 'vrf'], value=tmp) + config.delete(old_base + ['node-exporter']) + + if config.exists(old_base + ['frr-exporter']): + if config.exists(old_base + ['frr-exporter', 'listen-address']): + tmp = config.return_value(old_base + ['frr-exporter', 'listen-address']) + config.set(new_base + ['frr-exporter', 'listen-address'], value=tmp) + if config.exists(old_base + ['frr-exporter', 'port']): + tmp = config.return_value(old_base + ['frr-exporter', 'port']) + config.set(new_base + ['frr-exporter', 'port'], value=tmp) + if config.exists(old_base + ['frr-exporter', 'vrf']): + tmp = config.return_value(old_base + ['frr-exporter', 'vrf']) + config.set(new_base + ['frr-exporter', 'vrf'], value=tmp) + config.delete(old_base + ['frr-exporter']) |