Hide keyboard shortcuts

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/>. 

18 

19import sys 

20import os 

21import yaml 

22import re 

23from collections import defaultdict 

24 

25from charmhelpers.core.hookenv import ( 

26 relation_get, 

27 ingress_address, 

28 related_units, 

29 relation_ids, 

30 log, 

31 DEBUG, 

32) 

33 

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) 

42 

43 

44REQUIRED_REL_DATA_KEYS = ["target-address", "monitors", "target-id"] 

45 

46 

47def _prepare_relation_data(unit, rid): 

48 relation_data = relation_get(unit=unit, rid=rid) 

49 

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 {} 

56 

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": {}}} 

61 

62 if not relation_data.get("target-address"): 

63 relation_data["target-address"] = ingress_address(unit=unit, rid=rid) 

64 

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 {} 

76 

77 return relation_data 

78 

79 

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 

88 

89 return all_relations 

90 

91 

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() 

104 

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 

119 

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") 

133 

134 

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] 

147 

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) 

152 

153 if type(monitors) != dict: 

154 monitors = yaml.safe_load(monitors) 

155 

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() 

166 

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 ) 

177 

178 

179if __name__ == "__main__": 

180 main(sys.argv)