#!/usr/bin/env python3 import json import pathlib import subprocess """ This is an ugly hack. Puppet exported resources are very nice to generate monitoring configuration along with your Puppet resources. As you define something like an Apache virtual host, you can create a Nagios service check for it. But this requires a PuppetDB, and does not play nice with having no central Puppet infra. With its sibling script up.py, this script takes the JSON files generated by that, and manipulates them. This script moves Nagios resources to a specific host and does ugly trickery to fool Puppet into accepting that. This is like exported resources, but you don't need to declare a resource as exported. """ def load_json(path): with open(path) as f: return json.load(f) def save_json(r, path): with open(path, "w") as f: json.dump(r, f) nagios_catalog_file = pathlib.Path("build/puppet/build/output/nagios.h1.int.pdp7.net/catalog.json") if nagios_catalog_file.exists(): nagios_catalog = load_json(nagios_catalog_file) nagios_contacts = [r for r in nagios_catalog["resources"] if r["type"] == "Nagios_contact"] assert len(nagios_contacts) == 1, f"found multiple nagios contacts {nagios_contacts}" nagios_contact = nagios_contacts[0] ail = subprocess.run(["ansible-inventory", "--list"], check=True, stdout=subprocess.PIPE) inventory = json.loads(ail.stdout) total_hosts_in_inventory = len(inventory["_meta"]["hostvars"].keys()) k8s_hosts_in_inventory = len(inventory["k8s"]["hosts"]) unmonitored_hosts_in_inventory = len(inventory["unmonitored"]["hosts"]) puppet_hosts_in_inventory = total_hosts_in_inventory - k8s_hosts_in_inventory - unmonitored_hosts_in_inventory catalog_files = list(pathlib.Path("build/puppet/build/output/").glob("*/catalog.json")) if nagios_catalog_file.exists(): assert len(catalog_files) == puppet_hosts_in_inventory, f"catalogs {catalog_files} quantity different from total hosts in inventory {puppet_hosts_in_inventory}" nagios_resources = [] nagios_edge_targets = [] def is_nagios_resource(r): return r["type"].startswith("Nagios") def is_nagios_edge(e): return e["target"].startswith("Nagios") for catalog_file in catalog_files: if catalog_file == nagios_catalog_file: continue catalog = load_json(catalog_file) nagios_resources += [r for r in catalog["resources"] if is_nagios_resource(r)] catalog["resources"] = [r for r in catalog["resources"] if not is_nagios_resource(r)] nagios_edge_targets += [e["target"] for e in catalog["edges"] if is_nagios_edge(e)] catalog["edges"] = [e for e in catalog["edges"] if not is_nagios_edge(e)] save_json(catalog, catalog_file) if nagios_catalog_file.exists(): nagios_contact_position = nagios_catalog["resources"].index(nagios_contact) def copy_parameters(r): for p in ["require", "notify", "owner"]: r["parameters"][p] = nagios_contact["parameters"][p] return r nagios_catalog["resources"] = ( nagios_catalog["resources"][0:nagios_contact_position] + list(map(copy_parameters, nagios_resources)) + nagios_catalog["resources"][nagios_contact_position:] ) nagios_catalog["edges"] += [{"source": "Class[Nagios]", "target": t} for t in nagios_edge_targets] save_json(nagios_catalog, nagios_catalog_file)