Coverage for hooks/monitors_relation_changed.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/python
2# monitors-relation-changed - Process monitors.yaml into remote nagios monitors
3# Copyright Canonical 2012 Canonical Ltd. All Rights Reserved
4# Author: Clint Byrum <clint.byrum@canonical.com>
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
19import sys
20import os
21import yaml
22import re
23from collections import defaultdict
25from charmhelpers.core.hookenv import (
26 relation_get,
27 ingress_address,
28 related_units,
29 relation_ids,
30 log,
31 DEBUG,
32)
34from common import (
35 customize_service,
36 get_pynag_host,
37 get_pynag_service,
38 refresh_hostgroups,
39 initialize_inprogress_config,
40 flush_inprogress_config,
41)
44REQUIRED_REL_DATA_KEYS = ["target-address", "monitors", "target-id"]
47def _prepare_relation_data(unit, rid):
48 relation_data = relation_get(unit=unit, rid=rid)
50 if not relation_data:
51 msg = "no relation data found for unit {} in relation {} - " "skipping".format(
52 unit, rid
53 )
54 log(msg, level=DEBUG)
55 return {}
57 if rid.split(":")[0] == "nagios":
58 # Fake it for the more generic 'nagios' relation
59 relation_data["target-id"] = unit.replace("/", "-")
60 relation_data["monitors"] = {"monitors": {"remote": {}}}
62 if not relation_data.get("target-address"):
63 relation_data["target-address"] = ingress_address(unit=unit, rid=rid)
65 for key in REQUIRED_REL_DATA_KEYS:
66 if not relation_data.get(key):
67 # Note: it seems that some applications don't provide monitors over
68 # the relation at first (e.g. gnocchi). After a few hook runs,
69 # though, they add the key. For this reason I think using a logging
70 # level higher than DEBUG could be misleading
71 msg = "{} not found for unit {} in relation {} - " "skipping".format(
72 key, unit, rid
73 )
74 log(msg, level=DEBUG)
75 return {}
77 return relation_data
80def _collect_relation_data():
81 all_relations = defaultdict(dict)
82 for relname in ["nagios", "monitors"]:
83 for relid in relation_ids(relname):
84 for unit in related_units(relid):
85 relation_data = _prepare_relation_data(unit=unit, rid=relid)
86 if relation_data:
87 all_relations[relid][unit] = relation_data
89 return all_relations
92def main(argv): # noqa: C901
93 # Note that one can pass in args positionally, 'monitors.yaml targetid
94 # and target-address' so the hook can be tested without being in a hook
95 # context.
96 #
97 if len(argv) > 1:
98 relation_settings = {"monitors": open(argv[1]).read(), "target-id": argv[2]}
99 if len(argv) > 3:
100 relation_settings["target-address"] = argv[3]
101 all_relations = {"monitors:99": {"testing/0": relation_settings}}
102 else:
103 all_relations = _collect_relation_data()
105 # Hack to work around http://pad.lv/1025478
106 targets_with_addresses = set()
107 for relid, units in all_relations.iteritems():
108 for unit, relation_settings in units.items():
109 if "target-id" in relation_settings:
110 targets_with_addresses.add(relation_settings["target-id"])
111 new_all_relations = {}
112 for relid, units in all_relations.iteritems():
113 for unit, relation_settings in units.items():
114 if relation_settings["target-id"] in targets_with_addresses:
115 if relid not in new_all_relations:
116 new_all_relations[relid] = {}
117 new_all_relations[relid][unit] = relation_settings
118 all_relations = new_all_relations
120 initialize_inprogress_config()
121 # make a dict of machine ids to target-id hostnames
122 all_hosts = {}
123 for relid, units in all_relations.items():
124 for unit, relation_settings in units.iteritems():
125 machine_id = relation_settings.get("machine_id", None)
126 if machine_id:
127 all_hosts[machine_id] = relation_settings["target-id"]
128 for relid, units in all_relations.items():
129 apply_relation_config(relid, units, all_hosts)
130 refresh_hostgroups()
131 flush_inprogress_config()
132 os.system("service nagios3 reload")
135def apply_relation_config(relid, units, all_hosts): # noqa: C901
136 for unit, relation_settings in units.iteritems():
137 monitors = relation_settings["monitors"]
138 target_id = relation_settings["target-id"]
139 machine_id = relation_settings.get("machine_id", None)
140 parent_host = None
141 if machine_id:
142 container_regex = re.compile(r"(\d+)/lx[cd]/\d+")
143 if container_regex.search(machine_id):
144 parent_machine = container_regex.search(machine_id).group(1)
145 if parent_machine in all_hosts:
146 parent_host = all_hosts[parent_machine]
148 # If not set, we don't mess with it, as multiple services may feed
149 # monitors in for a particular address. Generally a primary will set
150 # this to its own private-address
151 target_address = relation_settings.get("target-address", None)
153 if type(monitors) != dict:
154 monitors = yaml.safe_load(monitors)
156 # Output nagios config
157 host = get_pynag_host(target_id)
158 if not target_address:
159 raise Exception("No Target Address provied by NRPE service!")
160 host.set_attribute("address", target_address)
161 if parent_host:
162 # We assume that we only want one parent and will overwrite any
163 # existing parents for this host.
164 host.set_attribute("parents", parent_host)
165 host.save()
167 for mon_family, mons in monitors["monitors"]["remote"].iteritems():
168 for mon_name, mon in mons.iteritems():
169 service_name = "%s-%s" % (target_id, mon_name)
170 service = get_pynag_service(target_id, service_name)
171 if customize_service(service, mon_family, mon_name, mon):
172 service.save()
173 else:
174 print(
175 "Ignoring %s due to unknown family %s" % (mon_name, mon_family)
176 )
179if __name__ == "__main__":
180 main(sys.argv)