From 3ade30c04a6b4f62b1d65fa5b14a3425bbabd453 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Thu, 9 Oct 2025 02:21:17 -0400 Subject: [PATCH] Add script to shot tempalte coverage * Add a script to compare the defined metrics with those used in the Zabbix template. * Add a Makefile target to run the above script. --- Makefile | 5 ++ zabbix_templates/coverage.py | 129 +++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100755 zabbix_templates/coverage.py diff --git a/Makefile b/Makefile index 3f12140..14e8ad9 100644 --- a/Makefile +++ b/Makefile @@ -110,6 +110,11 @@ endif query-tests: cd tests ; ./run-tests.sh +# Compare the sample metrics with the Zabbix template +template-coverage: + $(PYTHON) zabbix_templates/coverage.py sample-config/pgmon-metrics.yml zabbix_templates/pgmon_templates.yaml + + # Install the script at the specified base directory (common components) install-common: # Set up directories diff --git a/zabbix_templates/coverage.py b/zabbix_templates/coverage.py new file mode 100755 index 0000000..5a9d9f8 --- /dev/null +++ b/zabbix_templates/coverage.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +# Compare the items defined in a Zabbix template with the metrics defined in a config file. + +import sys + +import yaml + +# Special built in metrics +SPECIAL = { + "agent_version", + "latest_version_info" +} + + +class NonMetricItemError(Exception): + """ + A given item does not directly use a metric + """ + + +def read_metrics(file): + """ + Read the metrics from a config file and return the list of names + + params: + file: the name of the file to read + + returns: + list of metric named defined in the file + + raises: + yaml.parser.ParserError: invalid yaml file + """ + names = set() + config = None + + with open(file, "r", encoding="utf-8") as f: + config = yaml.safe_load(f) + + try: + for m in config["metrics"].keys(): + names.add(m) + except KeyError: + pass + + return names + + +def extract_metric(item): + """ + Extract the metric from an item definition + + params: + item: the item/discovery/prototype definition dict + + returns: + the name of the metric used in the item + + raises: + NonMetricItemError: the item does not directly use a metric + """ + try: + if item["type"] == "HTTP_AGENT": + url = item["url"] + if url.startswith("http://localhost:{$AGENT_PORT}"): + return url.split("/")[-1] + except KeyError: + raise NonMetricItemError() + + raise NonMetricItemError() + + +def read_template(file): + """ + Read the items from a Zabbix template and return the list of metric names + + params: + file: the name of the file to read + + returns: + list of metric named used in the file + + raises: + yaml.parser.ParserError: invalid yaml file + """ + names = set() + config = None + + with open(file, "r", encoding="utf-8") as f: + config = yaml.safe_load(f) + + try: + for template in config["zabbix_export"]["templates"]: + for item in template["items"]: + try: + names.add(extract_metric(item)) + except NonMetricItemError: + pass + + for rule in template["discovery_rules"]: + try: + names.add(extract_metric(rule)) + except NonMetricItemError: + pass + + for proto in rule["item_prototypes"]: + try: + names.add(extract_metric(proto)) + except NonMetricItemError: + pass + except KeyError: + pass + + return names + + +if __name__ == '__main__': + config_file = sys.argv[1] + config_metrics = read_metrics(config_file) + + template_file = sys.argv[2] + template_metrics = read_template(template_file) - SPECIAL + + config_only = config_metrics - template_metrics + template_only = template_metrics - config_metrics + + print("Config only: {}".format(sorted(list(config_only)))) + print("Template only: {}".format(sorted(list(template_only))))