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

1import subprocess 

2import socket 

3import os 

4import os.path 

5import re 

6import shutil 

7import tempfile 

8 

9from charmhelpers.core.hookenv import ( 

10 log, 

11 network_get, 

12 network_get_primary_address, 

13 unit_get, 

14 config, 

15) 

16 

17from pynag import Model 

18 

19INPROGRESS_DIR = "/etc/nagios3-inprogress" 

20INPROGRESS_CFG = "/etc/nagios3-inprogress/nagios.cfg" 

21INPROGRESS_CONF_D = "/etc/nagios3-inprogress/conf.d" 

22CHARM_CFG = "/etc/nagios3-inprogress/conf.d/charm.cfg" 

23MAIN_NAGIOS_BAK = "/etc/nagios3.bak" 

24MAIN_NAGIOS_DIR = "/etc/nagios3" 

25MAIN_NAGIOS_CFG = "/etc/nagios3/nagios.cfg" 

26PLUGIN_PATH = "/usr/lib/nagios/plugins" 

27 

28Model.cfg_file = INPROGRESS_CFG 

29Model.pynag_directory = INPROGRESS_CONF_D 

30 

31reduce_RE = re.compile(r"[\W_]") 

32 

33 

34def check_ip(n): 

35 try: 

36 socket.inet_pton(socket.AF_INET, n) 

37 return True 

38 except socket.error: 

39 try: 

40 socket.inet_pton(socket.AF_INET6, n) 

41 return True 

42 except socket.error: 

43 return False 

44 

45 

46def get_local_ingress_address(binding="website"): 

47 # using network-get to retrieve the address details if available. 

48 log("Getting hostname for binding %s" % binding) 

49 try: 

50 network_info = network_get(binding) 

51 if network_info is not None and "ingress-addresses" in network_info: 

52 log("Using ingress-addresses") 

53 hostname = network_info["ingress-addresses"][0] 

54 log(hostname) 

55 return hostname 

56 except NotImplementedError: 

57 # We'll fallthrough to the Pre 2.3 code below. 

58 pass 

59 

60 # Pre 2.3 output 

61 try: 

62 hostname = network_get_primary_address(binding) 

63 log("Using primary-addresses") 

64 except NotImplementedError: 

65 # pre Juju 2.0 

66 hostname = unit_get("private-address") 

67 log("Using unit_get private address") 

68 log(hostname) 

69 return hostname 

70 

71 

72def get_remote_relation_attr(remote_unit, attr_name, relation_id=None): 

73 args = ["relation-get", attr_name, remote_unit] 

74 if relation_id is not None: 

75 args.extend(["-r", relation_id]) 

76 return subprocess.check_output(args).strip() 

77 

78 

79def get_ip_and_hostname(remote_unit, relation_id=None): 

80 hostname = get_remote_relation_attr(remote_unit, "ingress-address", relation_id) 

81 if hostname is None or not len(hostname): 

82 hostname = get_remote_relation_attr(remote_unit, "private-address", relation_id) 

83 

84 if hostname is None or not len(hostname): 

85 log("relation-get failed") 

86 return 2 

87 if check_ip(hostname): 

88 # Some providers don't provide hostnames, so use the remote unit name. 

89 ip_address = hostname 

90 else: 

91 ip_address = socket.getaddrinfo(hostname, None)[0][4][0] 

92 return (ip_address, remote_unit.replace("/", "-")) 

93 

94 

95def refresh_hostgroups(): # noqa:C901 

96 """ Not the most efficient thing but since we're only 

97 parsing what is already on disk here its not too bad """ 

98 hosts = [x["host_name"] for x in Model.Host.objects.all if x["host_name"]] 

99 

100 hgroups = {} 

101 for host in hosts: 

102 try: 

103 (service, unit_id) = host.rsplit("-", 1) 

104 except ValueError: 

105 continue 

106 if service in hgroups: 

107 hgroups[service].append(host) 

108 else: 

109 hgroups[service] = [host] 

110 

111 # Find existing autogenerated 

112 auto_hgroups = Model.Hostgroup.objects.filter(notes__contains="#autogenerated#") 

113 auto_hgroups = [x.get_attribute("hostgroup_name") for x in auto_hgroups] 

114 

115 # Delete the ones not in hgroups 

116 to_delete = set(auto_hgroups).difference(set(hgroups.keys())) 

117 for hgroup_name in to_delete: 

118 try: 

119 hgroup = Model.Hostgroup.objects.get_by_shortname(hgroup_name) 

120 hgroup.delete() 

121 except (ValueError, KeyError): 

122 pass 

123 

124 for hgroup_name, members in hgroups.iteritems(): 

125 try: 

126 hgroup = Model.Hostgroup.objects.get_by_shortname(hgroup_name) 

127 except (ValueError, KeyError): 

128 hgroup = Model.Hostgroup() 

129 hgroup.set_filename(CHARM_CFG) 

130 hgroup.set_attribute("hostgroup_name", hgroup_name) 

131 hgroup.set_attribute("notes", "#autogenerated#") 

132 

133 hgroup.set_attribute("members", ",".join(members)) 

134 hgroup.save() 

135 

136 

137def _make_check_command(args): 

138 args = [str(arg) for arg in args] 

139 # There is some worry of collision, but the uniqueness of the initial 

140 # command should be enough. 

141 signature = reduce_RE.sub("_", "".join([os.path.basename(arg) for arg in args])) 

142 Model.Command.objects.reload_cache() 

143 try: 

144 cmd = Model.Command.objects.get_by_shortname(signature) 

145 except (ValueError, KeyError): 

146 cmd = Model.Command() 

147 cmd.set_attribute("command_name", signature) 

148 cmd.set_attribute("command_line", " ".join(args)) 

149 cmd.save() 

150 return signature 

151 

152 

153def _extend_args(args, cmd_args, switch, value): 

154 args.append(value) 

155 cmd_args.extend((switch, '"$ARG%d$"' % len(args))) 

156 

157 

158def customize_http(service, name, extra): 

159 args = [] 

160 cmd_args = [] 

161 plugin = os.path.join(PLUGIN_PATH, "check_http") 

162 port = extra.get("port", 80) 

163 path = extra.get("path", "/") 

164 args = [port, path] 

165 cmd_args = [plugin, "-p", '"$ARG1$"', "-u", '"$ARG2$"'] 

166 if "status" in extra: 

167 _extend_args(args, cmd_args, "-e", extra["status"]) 

168 if "host" in extra: 

169 _extend_args(args, cmd_args, "-H", extra["host"]) 

170 cmd_args.extend(("-I", "$HOSTADDRESS$")) 

171 else: 

172 cmd_args.extend(("-H", "$HOSTADDRESS$")) 

173 check_timeout = config("check_timeout") 

174 if check_timeout is not None: 

175 cmd_args.extend(("-t", check_timeout)) 

176 check_command = _make_check_command(cmd_args) 

177 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

178 service.set_attribute("check_command", cmd) 

179 return True 

180 

181 

182def customize_mysql(service, name, extra): 

183 plugin = os.path.join(PLUGIN_PATH, "check_mysql") 

184 args = [] 

185 cmd_args = [plugin, "-H", "$HOSTADDRESS$"] 

186 if "user" in extra: 

187 _extend_args(args, cmd_args, "-u", extra["user"]) 

188 if "password" in extra: 

189 _extend_args(args, cmd_args, "-p", extra["password"]) 

190 check_timeout = config("check_timeout") 

191 if check_timeout is not None: 

192 cmd_args.extend(("-t", check_timeout)) 

193 check_command = _make_check_command(cmd_args) 

194 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

195 service.set_attribute("check_command", cmd) 

196 return True 

197 

198 

199def customize_pgsql(service, name, extra): 

200 plugin = os.path.join(PLUGIN_PATH, "check_pgsql") 

201 args = [] 

202 cmd_args = [plugin, "-H", "$HOSTADDRESS$"] 

203 check_timeout = config("check_timeout") 

204 if check_timeout is not None: 

205 cmd_args.extend(("-t", check_timeout)) 

206 check_command = _make_check_command(cmd_args) 

207 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

208 service.set_attribute("check_command", cmd) 

209 return True 

210 

211 

212def customize_nrpe(service, name, extra): 

213 plugin = os.path.join(PLUGIN_PATH, "check_nrpe") 

214 args = [] 

215 cmd_args = [plugin, "-H", "$HOSTADDRESS$"] 

216 if name in ("mem", "swap"): 

217 cmd_args.extend(("-c", "check_%s" % name)) 

218 elif "command" in extra: 

219 cmd_args.extend(("-c", extra["command"])) 

220 else: 

221 cmd_args.extend(("-c", extra)) 

222 check_timeout = config("check_timeout") 

223 if check_timeout is not None: 

224 cmd_args.extend(("-t", check_timeout)) 

225 check_command = _make_check_command(cmd_args) 

226 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

227 service.set_attribute("check_command", cmd) 

228 return True 

229 

230 

231def customize_rpc(service, name, extra): 

232 """ Customize the check_rpc plugin to check things like nfs.""" 

233 plugin = os.path.join(PLUGIN_PATH, "check_rpc") 

234 args = [] 

235 # /usr/lib/nagios/plugins/check_rpc -H <host> -C <rpc_command> 

236 cmd_args = [plugin, "-H", "$HOSTADDRESS$"] 

237 if "rpc_command" in extra: 

238 cmd_args.extend(("-C", extra["rpc_command"])) 

239 if "program_version" in extra: 

240 cmd_args.extend(("-c", extra["program_version"])) 

241 

242 check_command = _make_check_command(cmd_args) 

243 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

244 service.set_attribute("check_command", cmd) 

245 return True 

246 

247 

248def customize_tcp(service, name, extra): 

249 """ Customize tcp can be used to check things like memcached. """ 

250 plugin = os.path.join(PLUGIN_PATH, "check_tcp") 

251 args = [] 

252 # /usr/lib/nagios/plugins/check_tcp -H <host> -E 

253 cmd_args = [plugin, "-H", "$HOSTADDRESS$", "-E"] 

254 if "port" in extra: 

255 cmd_args.extend(("-p", extra["port"])) 

256 if "string" in extra: 

257 cmd_args.extend(("-s", "'{}'".format(extra["string"]))) 

258 if "expect" in extra: 

259 cmd_args.extend(("-e", extra["expect"])) 

260 if "warning" in extra: 

261 cmd_args.extend(("-w", extra["warning"])) 

262 if "critical" in extra: 

263 cmd_args.extend(("-c", extra["critical"])) 

264 if "timeout" in extra: 

265 cmd_args.extend(("-t", extra["timeout"])) 

266 check_timeout = config("check_timeout") 

267 if check_timeout is not None: 

268 cmd_args.extend(("-t", check_timeout)) 

269 

270 check_command = _make_check_command(cmd_args) 

271 cmd = "%s!%s" % (check_command, "!".join([str(x) for x in args])) 

272 service.set_attribute("check_command", cmd) 

273 return True 

274 

275 

276def customize_service(service, family, name, extra): 

277 """ The monitors.yaml names are mapped to methods that customize services. """ 

278 customs = { 

279 "http": customize_http, 

280 "mysql": customize_mysql, 

281 "nrpe": customize_nrpe, 

282 "tcp": customize_tcp, 

283 "rpc": customize_rpc, 

284 "pgsql": customize_pgsql, 

285 } 

286 if family in customs: 

287 return customs[family](service, name, extra) 

288 return False 

289 

290 

291def update_localhost(): 

292 """ Update the localhost definition to use the ubuntu icons.""" 

293 

294 Model.cfg_file = MAIN_NAGIOS_CFG 

295 Model.pynag_directory = os.path.join(MAIN_NAGIOS_DIR, "conf.d") 

296 hosts = Model.Host.objects.filter(host_name="localhost", object_type="host") 

297 for host in hosts: 

298 host.icon_image = "base/ubuntu.png" 

299 host.icon_image_alt = "Ubuntu Linux" 

300 host.vrml_image = "ubuntu.png" 

301 host.statusmap_image = "base/ubuntu.gd2" 

302 host.save() 

303 

304 

305def get_pynag_host(target_id, owner_unit=None, owner_relation=None): 

306 try: 

307 host = Model.Host.objects.get_by_shortname(target_id) 

308 except (ValueError, KeyError): 

309 host = Model.Host() 

310 host.set_filename(CHARM_CFG) 

311 host.set_attribute("host_name", target_id) 

312 host.set_attribute("use", "generic-host") 

313 # Adding the ubuntu icon image definitions to the host. 

314 host.set_attribute("icon_image", "base/ubuntu.png") 

315 host.set_attribute("icon_image_alt", "Ubuntu Linux") 

316 host.set_attribute("vrml_image", "ubuntu.png") 

317 host.set_attribute("statusmap_image", "base/ubuntu.gd2") 

318 host.save() 

319 host = Model.Host.objects.get_by_shortname(target_id) 

320 apply_host_policy(target_id, owner_unit, owner_relation) 

321 return host 

322 

323 

324def get_pynag_service(target_id, service_name): 

325 services = Model.Service.objects.filter( 

326 host_name=target_id, service_description=service_name 

327 ) 

328 if len(services) == 0: 

329 service = Model.Service() 

330 service.set_filename(CHARM_CFG) 

331 service.set_attribute("service_description", service_name) 

332 service.set_attribute("host_name", target_id) 

333 service.set_attribute("use", "generic-service") 

334 else: 

335 service = services[0] 

336 return service 

337 

338 

339def apply_host_policy(target_id, owner_unit, owner_relation): 

340 ssh_service = get_pynag_service(target_id, "SSH") 

341 ssh_service.set_attribute("check_command", "check_ssh") 

342 ssh_service.save() 

343 

344 

345def _replace_in_config(find_me, replacement): 

346 with open(INPROGRESS_CFG) as cf: 

347 with tempfile.NamedTemporaryFile(dir=INPROGRESS_DIR, delete=False) as new_cf: 

348 for line in cf: 

349 new_cf.write(line.replace(find_me, replacement)) 

350 new_cf.flush() 

351 os.chmod(new_cf.name, 0o644) 

352 os.unlink(INPROGRESS_CFG) 

353 os.rename(new_cf.name, INPROGRESS_CFG) 

354 

355 

356def _commit_in_config(find_me, replacement): 

357 with open(MAIN_NAGIOS_CFG) as cf: 

358 with tempfile.NamedTemporaryFile(dir=MAIN_NAGIOS_DIR, delete=False) as new_cf: 

359 for line in cf: 

360 new_cf.write(line.replace(find_me, replacement)) 

361 new_cf.flush() 

362 os.chmod(new_cf.name, 0o644) 

363 os.unlink(MAIN_NAGIOS_CFG) 

364 os.rename(new_cf.name, MAIN_NAGIOS_CFG) 

365 

366 

367def initialize_inprogress_config(): 

368 if os.path.exists(INPROGRESS_DIR): 

369 shutil.rmtree(INPROGRESS_DIR) 

370 shutil.copytree(MAIN_NAGIOS_DIR, INPROGRESS_DIR) 

371 _replace_in_config(MAIN_NAGIOS_DIR, INPROGRESS_DIR) 

372 if os.path.exists(CHARM_CFG): 

373 os.unlink(CHARM_CFG) 

374 

375 

376def flush_inprogress_config(): 

377 if not os.path.exists(INPROGRESS_DIR): 

378 return 

379 if os.path.exists(MAIN_NAGIOS_BAK): 

380 shutil.rmtree(MAIN_NAGIOS_BAK) 

381 if os.path.exists(MAIN_NAGIOS_DIR): 

382 shutil.move(MAIN_NAGIOS_DIR, MAIN_NAGIOS_BAK) 

383 shutil.move(INPROGRESS_DIR, MAIN_NAGIOS_DIR) 

384 # now that directory has been changed need to update the config file 

385 # to reflect the real stuff.. 

386 _commit_in_config(INPROGRESS_DIR, MAIN_NAGIOS_DIR)