Merge lp:~clint-fewbar/charms/precise/nagios/add-monitors-2 into lp:charms/nagios
- Precise Pangolin (12.04)
- add-monitors-2
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Mark Mims |
Approved revision: | 38 |
Merged at revision: | 8 |
Proposed branch: | lp:~clint-fewbar/charms/precise/nagios/add-monitors-2 |
Merge into: | lp:charms/nagios |
Diff against target: |
809 lines (+517/-163) 16 files modified
.bzrignore (+1/-0) README (+18/-5) config.yaml (+8/-0) example.monitors.yaml (+30/-0) hooks/common.py (+222/-54) hooks/install (+16/-1) hooks/monitors-relation-changed (+125/-0) hooks/mymonitors-relation-joined (+17/-0) hooks/nagios-relation-broken (+0/-12) hooks/nagios-relation-changed (+0/-79) hooks/nagios-relation-departed (+0/-10) hooks/test-common.py (+50/-0) hooks/upgrade-charm (+14/-1) metadata.yaml (+4/-0) monitors.yaml (+11/-0) revision (+1/-1) |
To merge this branch: | bzr merge lp:~clint-fewbar/charms/precise/nagios/add-monitors-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mark Mims (community) | Approve | ||
Review via email: mp+117999@code.launchpad.net |
Commit message
A significant refactor of the charm's relations.
* Deprecate the 'monitoring' interface - never used in any other charm in the official store.
* Adds the 'monitors' interface to communicate complex monitoring information.
* Reworks to use 'pynag' library (embedded in a deb) for nagios configuration editting.
* Adds 'extraconfig' option for users to add extra config options.
* Enables 'external commands' so that checks can be rescheduled by an administrator.
Description of the change
A significant refactor of the charm's relations.
* Deprecate the 'monitoring' interface - never used in any other charm in the official store.
* Adds the 'monitors' interface to communicate complex monitoring information.
* Reworks to use 'pynag' library (embedded in a deb) for Nagios configuration editing.
* Adds 'extraconfig' option for users to add extra config options.
* Enables 'external commands' so that checks can be rescheduled by an administrator.
Clint Byrum (clint-fewbar) wrote : | # |
Mark Mims (mark-mims) wrote : | # |
Please add default user/pass instructions...
maybe mention how to use `juju ssh nagios/0 sudo cat /var/lib/
Preview Diff
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2012-08-02 21:28:44 +0000 |
4 | @@ -0,0 +1,1 @@ |
5 | +data |
6 | |
7 | === modified file 'README' |
8 | --- README 2012-05-14 07:24:34 +0000 |
9 | +++ README 2012-08-02 21:28:44 +0000 |
10 | @@ -10,8 +10,21 @@ |
11 | |
12 | This should result in your Nagios monitoring all of the service units. |
13 | |
14 | -TODO: |
15 | -- Add a nagios-nrpe subordinate charm to make it easier to use local |
16 | - plugins via NRPE |
17 | -- Create a proper 'monitoring' interface that charms can use to define |
18 | - what they want monitored remotely. |
19 | +monitors interface |
20 | +================== |
21 | + |
22 | +The monitors interface expects three fields: |
23 | + |
24 | +* monitors - YAML matching the monitors yaml spec. See |
25 | + example.monitors.yaml for more information. |
26 | +* target-id - Assign any monitors to this target host definition. |
27 | +* target-address - Optional, specifies the host of the target to |
28 | + monitor. This must be specified by at least one unit so that the |
29 | + intended target-id will be monitorable. |
30 | + |
31 | +nrpe |
32 | +==== |
33 | + |
34 | +There is an NRPE subordinate charm which must be used for any local |
35 | +monitors. See the 'nrpe' charm's README for information on how to |
36 | +make use of it. |
37 | |
38 | === added file 'config.yaml' |
39 | --- config.yaml 1970-01-01 00:00:00 +0000 |
40 | +++ config.yaml 2012-08-02 21:28:44 +0000 |
41 | @@ -0,0 +1,8 @@ |
42 | +options: |
43 | + extraconfig: |
44 | + type: string |
45 | + default: "" |
46 | + description: | |
47 | + Any additional nagios configuration you would like to |
48 | + add can be set into this element. It will be placed in |
49 | + /etc/nagios3/conf.d/extra.cfg |
50 | |
51 | === added directory 'debs' |
52 | === added file 'debs/pynag_0.4.2-1_all.deb' |
53 | Binary files debs/pynag_0.4.2-1_all.deb 1970-01-01 00:00:00 +0000 and debs/pynag_0.4.2-1_all.deb 2012-08-02 21:28:44 +0000 differ |
54 | === added file 'example.monitors.yaml' |
55 | --- example.monitors.yaml 1970-01-01 00:00:00 +0000 |
56 | +++ example.monitors.yaml 2012-08-02 21:28:44 +0000 |
57 | @@ -0,0 +1,30 @@ |
58 | +# Version of the spec, mostly ignored but 0.3 is the current one |
59 | +version: '0.3' |
60 | +# Dict with just 'local' and 'remote' as parts |
61 | +monitors: |
62 | + # local monitors need an agent to be handled. See nrpe charm for |
63 | + # some example implementations |
64 | + local: |
65 | + # procrunning checks for a running process named X (no path) |
66 | + procrunning: |
67 | + # Multiple procrunning can be defined, this is the "name" of it |
68 | + nagios3: |
69 | + min: 1 |
70 | + max: 1 |
71 | + executable: nagios3 |
72 | + # Remote monitors can be polled directly by a remote system |
73 | + remote: |
74 | + # do a request on the HTTP protocol |
75 | + http: |
76 | + nagios: |
77 | + port: 80 |
78 | + path: /nagios3/ |
79 | + # expected status response (otherwise just look for 200) |
80 | + status: 'HTTP/1.1 401' |
81 | + # Use as the Host: header (the server address will still be used to connect() to) |
82 | + host: www.fewbar.com |
83 | + mysql: |
84 | + # Named basic check |
85 | + basic: |
86 | + username: monitors |
87 | + password: abcdefg123456 |
88 | |
89 | === modified file 'hooks/common.py' |
90 | --- hooks/common.py 2012-05-14 23:48:24 +0000 |
91 | +++ hooks/common.py 2012-08-02 21:28:44 +0000 |
92 | @@ -2,7 +2,26 @@ |
93 | import socket |
94 | import os |
95 | import os.path |
96 | - |
97 | +import re |
98 | +import sqlite3 |
99 | +import shutil |
100 | +import tempfile |
101 | + |
102 | +from pynag import Model |
103 | + |
104 | +INPROGRESS_DIR = '/etc/nagios3-inprogress' |
105 | +INPROGRESS_CFG = '/etc/nagios3-inprogress/nagios.cfg' |
106 | +INPROGRESS_CONF_D = '/etc/nagios3-inprogress/conf.d' |
107 | +CHARM_CFG = '/etc/nagios3-inprogress/conf.d/charm.cfg' |
108 | +MAIN_NAGIOS_BAK = '/etc/nagios3.bak' |
109 | +MAIN_NAGIOS_DIR = '/etc/nagios3' |
110 | +MAIN_NAGIOS_CFG = '/etc/nagios3/nagios.cfg' |
111 | +PLUGIN_PATH = '/usr/lib/nagios/plugins' |
112 | + |
113 | +Model.cfg_file = INPROGRESS_CFG |
114 | +Model.pynag_directory = INPROGRESS_CONF_D |
115 | + |
116 | +reduce_RE = re.compile('[\W_]') |
117 | |
118 | def check_ip(n): |
119 | try: |
120 | @@ -28,59 +47,208 @@ |
121 | if check_ip(hostname): |
122 | # Some providers don't provide hostnames, so use the remote unit name. |
123 | ip_address = hostname |
124 | - hostname = remote_unit.replace('/','-') |
125 | else: |
126 | ip_address = socket.getaddrinfo(hostname, None)[0][4][0] |
127 | - return (ip_address, hostname) |
128 | - |
129 | -# relationId-hostname-config.cfg |
130 | -host_config_path_template = '/etc/nagios3/conf.d/%s-%s-config.cfg' |
131 | - |
132 | -hostgroup_template = """ |
133 | -define hostgroup { |
134 | - hostgroup_name %(name)s |
135 | - alias %(alias)s |
136 | - members %(members)s |
137 | -} |
138 | -""" |
139 | -hostgroup_path_template = '/etc/nagios3/conf.d/%s-hostgroup.cfg' |
140 | - |
141 | - |
142 | -def remove_hostgroup(relation_id): |
143 | - hostgroup_path = hostgroup_path_template % (relation_id) |
144 | - if os.path.exists(hostgroup_path): |
145 | - os.unlink(hostgroup_path) |
146 | - |
147 | - |
148 | -def handle_hostgroup(relation_id): |
149 | - p = subprocess.Popen(["relation-list","-r",relation_id], |
150 | - stdout=subprocess.PIPE) |
151 | - services = {} |
152 | - for unit in p.stdout: |
153 | - unit = unit.strip() |
154 | - service_name = unit.strip().split('/')[0] |
155 | - (_, hostname) = get_ip_and_hostname(unit, relation_id) |
156 | - if service_name in services: |
157 | - services[service_name].add(hostname) |
158 | + return (ip_address, remote_unit.replace('/', '-')) |
159 | + |
160 | + |
161 | +def refresh_hostgroups(): |
162 | + """ Not the most efficient thing but since we're only |
163 | + parsing what is already on disk here its not too bad """ |
164 | + hosts = [ x['host_name'] for x in Model.Host.objects.all if x['host_name'] ] |
165 | + |
166 | + hgroups = {} |
167 | + for host in hosts: |
168 | + try: |
169 | + (service, unit_id) = host.rsplit('-', 1) |
170 | + except ValueError: |
171 | + continue |
172 | + if service in hgroups: |
173 | + hgroups[service].append(host) |
174 | else: |
175 | - services[service_name] = set([hostname]) |
176 | - p.communicate() |
177 | - if p.returncode != 0: |
178 | - raise RuntimeError('relation-list failed with code %d' % p.returncode) |
179 | - |
180 | - hostgroup_path = hostgroup_path_template % (relation_id) |
181 | - for service, members in services.iteritems(): |
182 | - with open(hostgroup_path, 'w') as outfile: |
183 | - outfile.write(hostgroup_template % {'name': service, |
184 | - 'alias': service, 'members': ','.join(members)}) |
185 | - |
186 | -def refresh_hostgroups(relation_name): |
187 | - p = subprocess.Popen(["relation-ids",relation_name], |
188 | - stdout=subprocess.PIPE) |
189 | - relids = [ relation_id.strip() for relation_id in p.stdout ] |
190 | - for relation_id in relids: |
191 | - remove_hostgroup(relation_id) |
192 | - handle_hostgroup(relation_id) |
193 | - p.communicate() |
194 | - if p.returncode != 0: |
195 | - raise RuntimeError('relation-ids failed with code %d' % p.returncode) |
196 | + hgroups[service] = [host] |
197 | + |
198 | + # Find existing autogenerated |
199 | + auto_hgroups = Model.Hostgroup.objects.filter(notes__contains='#autogenerated#') |
200 | + auto_hgroups = [ x.get_attribute('hostgroup_name') for x in auto_hgroups ] |
201 | + |
202 | + # Delete the ones not in hgroups |
203 | + to_delete = set(auto_hgroups).difference(set(hgroups.keys())) |
204 | + for hgroup_name in to_delete: |
205 | + try: |
206 | + hgroup = Model.Hostgroup.objects.get_by_shortname(hgroup_name) |
207 | + hgroup.delete() |
208 | + except ValueError: |
209 | + pass |
210 | + |
211 | + for hgroup_name, members in hgroups.iteritems(): |
212 | + try: |
213 | + hgroup = Model.Hostgroup.objects.get_by_shortname(hgroup_name) |
214 | + except ValueError: |
215 | + hgroup = Model.Hostgroup() |
216 | + hgroup.set_filename(CHARM_CFG) |
217 | + hgroup.set_attribute('hostgroup_name', hgroup_name) |
218 | + hgroup.set_attribute('notes', '#autogenerated#') |
219 | + |
220 | + hgroup.set_attribute('members', ','.join(members)) |
221 | + hgroup.save() |
222 | + |
223 | + |
224 | +def _make_check_command(args): |
225 | + args = [str(arg) for arg in args] |
226 | + # There is some worry of collision, but the uniqueness of the initial |
227 | + # command should be enough. |
228 | + signature = reduce_RE.sub('_', ''.join( |
229 | + [os.path.basename(arg) for arg in args])) |
230 | + Model.Command.objects.reload_cache() |
231 | + try: |
232 | + cmd = Model.Command.objects.get_by_shortname(signature) |
233 | + except ValueError: |
234 | + cmd = Model.Command() |
235 | + cmd.set_attribute('command_name', signature) |
236 | + cmd.set_attribute('command_line', ' '.join(args)) |
237 | + cmd.save() |
238 | + return signature |
239 | + |
240 | +def _extend_args(args, cmd_args, switch, value): |
241 | + args.append(value) |
242 | + cmd_args.extend((switch, '"$ARG%d$"' % len(args))) |
243 | + |
244 | +def customize_http(service, name, extra): |
245 | + args = [] |
246 | + cmd_args = [] |
247 | + plugin = os.path.join(PLUGIN_PATH, 'check_http') |
248 | + port = extra.get('port', 80) |
249 | + path = extra.get('path', '/') |
250 | + args = [port, path] |
251 | + cmd_args = [plugin, '-p', '"$ARG1$"', '-u', '"$ARG2$"'] |
252 | + if 'status' in extra: |
253 | + _extend_args(args, cmd_args, '-e', extra['status']) |
254 | + if 'host' in extra: |
255 | + _extend_args(args, cmd_args, '-H', extra['host']) |
256 | + cmd_args.extend(('-I', '$HOSTADDRESS$')) |
257 | + else: |
258 | + cmd_args.extend(('-H', '$HOSTADDRESS$')) |
259 | + check_command = _make_check_command(cmd_args) |
260 | + cmd = '%s!%s' % (check_command, '!'.join([str(x) for x in args])) |
261 | + service.set_attribute('check_command', cmd) |
262 | + return True |
263 | + |
264 | + |
265 | +def customize_mysql(service, name, extra): |
266 | + plugin = os.path.join(PLUGIN_PATH, 'check_mysql') |
267 | + args = [] |
268 | + cmd_args = [plugin,'-H', '$HOSTADDRESS$'] |
269 | + if 'user' in extra: |
270 | + _extend_args(args, cmd_args, '-u', extra['user']) |
271 | + if 'password' in extra: |
272 | + _extend_args(args, cmd_args, '-p', extra['password']) |
273 | + check_command = _make_check_command(cmd_args) |
274 | + cmd = '%s!%s' % (check_command, '!'.join([str(x) for x in args])) |
275 | + service.set_attribute('check_command', cmd) |
276 | + return True |
277 | + |
278 | + |
279 | +def customize_nrpe(service, name, extra): |
280 | + plugin = os.path.join(PLUGIN_PATH, 'check_nrpe') |
281 | + args = [] |
282 | + cmd_args = [plugin,'-H', '$HOSTADDRESS$'] |
283 | + if name in ('mem','swap'): |
284 | + cmd_args.extend(('-c', 'check_%s' % name)) |
285 | + elif 'command' in extra: |
286 | + cmd_args.extend(('-c', extra['command'])) |
287 | + else: |
288 | + return False |
289 | + check_command = _make_check_command(cmd_args) |
290 | + cmd = '%s!%s' % (check_command, '!'.join([str(x) for x in args])) |
291 | + service.set_attribute('check_command', cmd) |
292 | + return True |
293 | + |
294 | + |
295 | +def customize_service(service, family, name, extra): |
296 | + customs = { 'http': customize_http, |
297 | + 'mysql': customize_mysql, |
298 | + 'nrpe': customize_nrpe} |
299 | + if family in customs: |
300 | + return customs[family](service, name, extra) |
301 | + return False |
302 | + |
303 | + |
304 | +def get_pynag_host(target_id, owner_unit=None, owner_relation=None): |
305 | + try: |
306 | + host = Model.Host.objects.get_by_shortname(target_id) |
307 | + except ValueError: |
308 | + host = Model.Host() |
309 | + host.set_filename(CHARM_CFG) |
310 | + host.set_attribute('host_name', target_id) |
311 | + host.set_attribute('use', 'generic-host') |
312 | + host.save() |
313 | + host = Model.Host.objects.get_by_shortname(target_id) |
314 | + apply_host_policy(target_id, owner_unit, owner_relation) |
315 | + return host |
316 | + |
317 | + |
318 | +def get_pynag_service(target_id, service_name): |
319 | + services = Model.Service.objects.filter(host_name=target_id, |
320 | + service_description=service_name) |
321 | + if len(services) == 0: |
322 | + service = Model.Service() |
323 | + service.set_filename(CHARM_CFG) |
324 | + service.set_attribute('service_description', service_name) |
325 | + service.set_attribute('host_name', target_id) |
326 | + service.set_attribute('use', 'generic-service') |
327 | + else: |
328 | + service = services[0] |
329 | + return service |
330 | + |
331 | + |
332 | +def apply_host_policy(target_id, owner_unit, owner_relation): |
333 | + ssh_service = get_pynag_service(target_id, 'SSH') |
334 | + ssh_service.set_attribute('check_command', 'check_ssh') |
335 | + ssh_service.save() |
336 | + |
337 | + |
338 | +def get_valid_relations(): |
339 | + for x in subprocess.Popen(['relation-ids', 'monitors'], |
340 | + stdout=subprocess.PIPE).stdout: |
341 | + yield x.strip() |
342 | + for x in subprocess.Popen(['relation-ids', 'nagios'], |
343 | + stdout=subprocess.PIPE).stdout: |
344 | + yield x.strip() |
345 | + |
346 | + |
347 | +def get_valid_units(relation_id): |
348 | + for x in subprocess.Popen(['relation-list', '-r', relation_id], |
349 | + stdout=subprocess.PIPE).stdout: |
350 | + yield x.strip() |
351 | + |
352 | + |
353 | +def _replace_in_config(find_me, replacement): |
354 | + with open(INPROGRESS_CFG) as cf: |
355 | + with tempfile.NamedTemporaryFile(dir=INPROGRESS_DIR, delete=False) as new_cf: |
356 | + for line in cf: |
357 | + new_cf.write(line.replace(find_me, replacement)) |
358 | + new_cf.flush() |
359 | + os.chmod(new_cf.name, 0644) |
360 | + os.unlink(INPROGRESS_CFG) |
361 | + os.rename(new_cf.name, INPROGRESS_CFG) |
362 | + |
363 | + |
364 | +def initialize_inprogress_config(): |
365 | + if os.path.exists(INPROGRESS_DIR): |
366 | + shutil.rmtree(INPROGRESS_DIR) |
367 | + shutil.copytree(MAIN_NAGIOS_DIR, INPROGRESS_DIR) |
368 | + _replace_in_config(MAIN_NAGIOS_DIR, INPROGRESS_DIR) |
369 | + if os.path.exists(CHARM_CFG): |
370 | + os.unlink(CHARM_CFG) |
371 | + |
372 | + |
373 | +def flush_inprogress_config(): |
374 | + if not os.path.exists(INPROGRESS_DIR): |
375 | + return |
376 | + _replace_in_config(INPROGRESS_DIR, MAIN_NAGIOS_DIR) |
377 | + if os.path.exists(MAIN_NAGIOS_BAK): |
378 | + shutil.rmtree(MAIN_NAGIOS_BAK) |
379 | + if os.path.exists(MAIN_NAGIOS_DIR): |
380 | + shutil.move(MAIN_NAGIOS_DIR, MAIN_NAGIOS_BAK) |
381 | + shutil.move(INPROGRESS_DIR, MAIN_NAGIOS_DIR) |
382 | |
383 | === added symlink 'hooks/config-changed' |
384 | === target is u'upgrade-charm' |
385 | === modified file 'hooks/install' |
386 | --- hooks/install 2012-05-13 23:04:19 +0000 |
387 | +++ hooks/install 2012-08-02 21:28:44 +0000 |
388 | @@ -17,7 +17,22 @@ |
389 | echo nagios3-cgi nagios3/adminpassword-repeat password $PASSWORD | debconf-set-selections |
390 | |
391 | DEBIAN_FRONTEND=noninteractive apt-get -qy \ |
392 | - install nagios3 nagios-plugins python-cheetah dnsutils debconf-utils |
393 | + install nagios3 nagios-plugins python-cheetah dnsutils debconf-utils nagios-nrpe-plugin |
394 | + |
395 | +# Ideally these would be moved into the distro ASAP |
396 | +if [ -d debs ] ; then |
397 | + dpkg -i debs/*.deb |
398 | +fi |
399 | + |
400 | +# enable external commands per README.Debian file |
401 | +if ! grep '^check_external_commands=1$' /etc/nagios3/nagios.cfg ; then |
402 | + echo check_external_commands=1 >> /etc/nagios3/nagios.cfg |
403 | +fi |
404 | +# || :'s are for idempotency |
405 | +service nagios3 stop || : |
406 | +dpkg-statoverride --update --add nagios www-data 2710 /var/lib/nagios3/rw || : |
407 | +dpkg-statoverride --update --add nagios nagios 751 /var/lib/nagios3 || : |
408 | +service nagios3 start |
409 | |
410 | # For the admin interface |
411 | open-port 80 |
412 | |
413 | === removed symlink 'hooks/legacy-relation-changed' |
414 | === target was u'nagios-relation-changed' |
415 | === removed symlink 'hooks/legacy-relation-departed' |
416 | === target was u'nagios-relation-departed' |
417 | === added symlink 'hooks/monitors-relation-broken' |
418 | === target is u'monitors-relation-changed' |
419 | === added file 'hooks/monitors-relation-changed' |
420 | --- hooks/monitors-relation-changed 1970-01-01 00:00:00 +0000 |
421 | +++ hooks/monitors-relation-changed 2012-08-02 21:28:44 +0000 |
422 | @@ -0,0 +1,125 @@ |
423 | +#!/usr/bin/python |
424 | +# monitors-relation-changed - Process monitors.yaml into remote nagios monitors |
425 | +# Copyright Canonical 2012 Canonical Ltd. All Rights Reserved |
426 | +# Author: Clint Byrum <clint.byrum@canonical.com> |
427 | +# |
428 | +# This program is free software: you can redistribute it and/or modify |
429 | +# it under the terms of the GNU General Public License as published by |
430 | +# the Free Software Foundation, either version 3 of the License, or |
431 | +# (at your option) any later version. |
432 | +# |
433 | +# This program is distributed in the hope that it will be useful, |
434 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
435 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
436 | +# GNU General Public License for more details. |
437 | +# |
438 | +# You should have received a copy of the GNU General Public License |
439 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
440 | + |
441 | +import sys |
442 | +import os |
443 | +import subprocess |
444 | +import yaml |
445 | +import json |
446 | +import re |
447 | +import string |
448 | + |
449 | + |
450 | +from common import (customize_service, get_pynag_host, |
451 | + get_pynag_service, refresh_hostgroups, |
452 | + get_valid_relations, get_valid_units, |
453 | + initialize_inprogress_config, flush_inprogress_config) |
454 | + |
455 | + |
456 | +def main(argv): |
457 | + # Note that one can pass in args positionally, 'monitors.yaml targetid |
458 | + # and target-address' so the hook can be tested without being in a hook |
459 | + # context. |
460 | + # |
461 | + if len(argv) > 1: |
462 | + relation_settings = {'monitors': open(argv[1]).read(), |
463 | + 'target-id': argv[2]} |
464 | + if len(argv) > 3: |
465 | + relation_settings['target-address'] = argv[3] |
466 | + all_relations = {'monitors:99': {'testing/0': relation_settings}} |
467 | + else: |
468 | + all_relations = {} |
469 | + for relid in get_valid_relations(): |
470 | + (relname, relnum) = relid.split(':') |
471 | + for unit in get_valid_units(relid): |
472 | + relation_settings = json.loads( |
473 | + subprocess.check_output(['relation-get', '--format=json', |
474 | + '-r', relid, |
475 | + '-',unit]).strip()) |
476 | + |
477 | + if relation_settings is None or relation_settings == '': |
478 | + continue |
479 | + |
480 | + if relname == 'monitors': |
481 | + if ('monitors' not in relation_settings |
482 | + or 'target-id' not in relation_settings): |
483 | + continue |
484 | + else: |
485 | + # Fake it for the more generic 'nagios' relation' |
486 | + relation_settings['target-id'] = unit.replace('/','-') |
487 | + relation_settings['target-address'] = relation_settings['private-address'] |
488 | + relation_settings['monitors'] = {'monitors': {'remote': {} } } |
489 | + |
490 | + if relid not in all_relations: |
491 | + all_relations[relid] = {} |
492 | + |
493 | + all_relations[relid][unit] = relation_settings |
494 | + |
495 | + # Hack to work around http://pad.lv/1025478 |
496 | + targets_with_addresses = set() |
497 | + for relid, units in all_relations.iteritems(): |
498 | + for unit, relation_settings in units.iteritems(): |
499 | + if 'target-address' in relation_settings: |
500 | + targets_with_addresses.add(relation_settings['target-id']) |
501 | + new_all_relations = {} |
502 | + for relid, units in all_relations.iteritems(): |
503 | + for unit, relation_settings in units.iteritems(): |
504 | + if relation_settings['target-id'] in targets_with_addresses: |
505 | + if relid not in new_all_relations: |
506 | + new_all_relations[relid] = {} |
507 | + new_all_relations[relid][unit] = relation_settings |
508 | + all_relations = new_all_relations |
509 | + |
510 | + initialize_inprogress_config() |
511 | + for relid, units in all_relations.iteritems(): |
512 | + apply_relation_config(relid, units) |
513 | + refresh_hostgroups() |
514 | + flush_inprogress_config() |
515 | + os.system('service nagios3 reload') |
516 | + |
517 | +def apply_relation_config(relid, units): |
518 | + for unit, relation_settings in units.iteritems(): |
519 | + monitors = relation_settings['monitors'] |
520 | + target_id = relation_settings['target-id'] |
521 | + # If not set, we don't mess with it, as multiple services may feed |
522 | + # monitors in for a particular address. Generally a primary will set this |
523 | + # to its own private-address |
524 | + target_address = relation_settings.get('target-address', None) |
525 | + |
526 | + if type(monitors) != dict: |
527 | + monitors = yaml.safe_load(monitors) |
528 | + |
529 | + # Output nagios config |
530 | + host = get_pynag_host(target_id) |
531 | + |
532 | + if target_address is not None: |
533 | + host.set_attribute('address', target_address) |
534 | + host.save() |
535 | + |
536 | + for mon_family, mons in monitors['monitors']['remote'].iteritems(): |
537 | + for mon_name, mon in mons.iteritems(): |
538 | + service_name = '%s-%s' % (target_id, mon_name) |
539 | + service = get_pynag_service(target_id, service_name) |
540 | + if customize_service(service, mon_family, mon_name, mon): |
541 | + service.save() |
542 | + else: |
543 | + print('Ignoring %s due to unknown family %s' % (mon_name, |
544 | + mon_family)) |
545 | + |
546 | +if __name__ == '__main__': |
547 | + main(sys.argv) |
548 | |
549 | === added symlink 'hooks/monitors-relation-departed' |
550 | === target is u'monitors-relation-changed' |
551 | === added file 'hooks/mymonitors-relation-joined' |
552 | --- hooks/mymonitors-relation-joined 1970-01-01 00:00:00 +0000 |
553 | +++ hooks/mymonitors-relation-joined 2012-08-02 21:28:44 +0000 |
554 | @@ -0,0 +1,17 @@ |
555 | +#!/bin/bash |
556 | +if [ -n "$JUJU_RELATION_ID" ] ; then |
557 | + # single relation joined |
558 | + rels=$JUJU_RELATION_ID |
559 | +else |
560 | + # Refresh from upgrade or some other place |
561 | + rels=`relation-ids mymonitors` |
562 | +fi |
563 | + |
564 | +target_id=${JUJU_UNIT_NAME//\//-} |
565 | + |
566 | +for rel in $rels ; do |
567 | + relation-set -r $rel \ |
568 | + monitors="`cat monitors.yaml`" \ |
569 | + target-address=`unit-get private-address` \ |
570 | + target-id=$target_id |
571 | +done |
572 | |
573 | === added symlink 'hooks/nagios-relation-broken' |
574 | === target is u'monitors-relation-changed' |
575 | === removed file 'hooks/nagios-relation-broken' |
576 | --- hooks/nagios-relation-broken 2012-05-14 07:15:54 +0000 |
577 | +++ hooks/nagios-relation-broken 1970-01-01 00:00:00 +0000 |
578 | @@ -1,12 +0,0 @@ |
579 | -#!/usr/bin/python |
580 | -import glob |
581 | -import os |
582 | - |
583 | -import common |
584 | - |
585 | -common.remove_hostgroup(os.environ['JUJU_RELATION_ID']) |
586 | -glob_target = common.host_config_path_template % (os.environ['JUJU_RELATION_ID'], '*') |
587 | -print 'Removing relation config files: %s' % (glob_target) |
588 | -for oldconfig in glob.glob(glob_target): |
589 | - print 'Removing %s' % (oldconfig) |
590 | - os.unlink(oldconfig) |
591 | |
592 | === added symlink 'hooks/nagios-relation-changed' |
593 | === target is u'monitors-relation-changed' |
594 | === removed file 'hooks/nagios-relation-changed' |
595 | --- hooks/nagios-relation-changed 2012-05-14 07:15:54 +0000 |
596 | +++ hooks/nagios-relation-changed 1970-01-01 00:00:00 +0000 |
597 | @@ -1,79 +0,0 @@ |
598 | -#!/usr/bin/env python |
599 | - |
600 | -import string |
601 | -import sys |
602 | -import os |
603 | -import os.path |
604 | -import yaml |
605 | -import subprocess |
606 | -from common import * |
607 | - |
608 | -from Cheetah.Template import Template |
609 | - |
610 | -def write_service_template(service, host, description, command): |
611 | - service = service.replace("__hostname__", host) |
612 | - service = service.replace("__description__", description) |
613 | - service = service.replace("__command__", command) |
614 | - return service |
615 | - |
616 | -def write_host_template(host, hostname, ip_address): |
617 | - host = host.replace("__hostname__", hostname) |
618 | - host = host.replace("__alias__", hostname) |
619 | - host = host.replace("__address__", ip_address) |
620 | - return host |
621 | - |
622 | - |
623 | -def main(): |
624 | - for var in ['JUJU_REMOTE_UNIT', 'JUJU_RELATION_ID']: |
625 | - if var not in os.environ: |
626 | - print "%s must be set" % (var) |
627 | - return 1 |
628 | - relation_id = os.environ["JUJU_RELATION_ID"] |
629 | - relation_name = os.path.basename(sys.argv[0]).split('-')[0] |
630 | - remote_unit = os.environ["JUJU_REMOTE_UNIT"] |
631 | - |
632 | - service_name, _ = remote_unit.split("/") |
633 | - (ip_address, hostname) = get_ip_and_hostname(remote_unit) |
634 | - |
635 | - nagios_service = "" |
636 | - host_template = """ |
637 | - define host { |
638 | - use generic-host ; Name of host template to use |
639 | - host_name __hostname__ |
640 | - alias __alias__ |
641 | - address __address__ |
642 | - } |
643 | -""" |
644 | - service_template = """ |
645 | - define service { |
646 | - use generic-service ; Name of service template to use |
647 | - host_name __hostname__ |
648 | - service_description __description__ |
649 | - check_command __command__ |
650 | - } |
651 | -""" |
652 | - |
653 | - # write a single host |
654 | - host_template = write_host_template(host_template, hostname, ip_address) |
655 | - nagios_service += host_template |
656 | - |
657 | - # all hosts should be running SSH |
658 | - nagios_service += write_service_template(service_template, hostname, 'SSH', 'check_ssh') |
659 | - |
660 | - namespace = {'hostname': hostname, 'nagios_config':nagios_service} |
661 | - t = Template(open('hooks/templates/nagios.tmpl').read(), searchList=[namespace]) |
662 | - config_file = host_config_path_template % (relation_id, hostname) |
663 | - f = open(config_file, 'w') |
664 | - f.write(str(t)) |
665 | - f.close() |
666 | - |
667 | - refresh_hostgroups(relation_name) |
668 | - |
669 | - print "Restarting nagios" |
670 | - subprocess.call(["service", "nagios3", "restart"]) |
671 | - return 0 |
672 | - |
673 | -if __name__ == '__main__': |
674 | - sys.exit(main()) |
675 | - |
676 | - |
677 | |
678 | === added symlink 'hooks/nagios-relation-departed' |
679 | === target is u'monitors-relation-changed' |
680 | === removed file 'hooks/nagios-relation-departed' |
681 | --- hooks/nagios-relation-departed 2012-05-14 07:15:54 +0000 |
682 | +++ hooks/nagios-relation-departed 1970-01-01 00:00:00 +0000 |
683 | @@ -1,10 +0,0 @@ |
684 | -#!/usr/bin/python |
685 | - |
686 | -import common |
687 | -import os |
688 | - |
689 | -relation_id = os.environ['JUJU_RELATION_ID'] |
690 | -(_,hostname) = common.get_ip_and_hostname(os.environ['JUJU_REMOTE_UNIT']) |
691 | -os.unlink(common.host_config_path_template % (relation_id, hostname)) |
692 | -common.refresh_hostgroups(os.path.basename(sys.argv[0]).split('-')[0]) |
693 | -subprocess.call(['service','nagios3','restart']) |
694 | |
695 | === added file 'hooks/test-common.py' |
696 | --- hooks/test-common.py 1970-01-01 00:00:00 +0000 |
697 | +++ hooks/test-common.py 2012-08-02 21:28:44 +0000 |
698 | @@ -0,0 +1,50 @@ |
699 | +from common import ObjectTagCollection |
700 | +import os |
701 | + |
702 | +from tempfile import NamedTemporaryFile |
703 | + |
704 | +""" This is meant to test the ObjectTagCollection bits. It should |
705 | + probably be made into a proper unit test. """ |
706 | + |
707 | +x = ObjectTagCollection('test-units') |
708 | +y = ObjectTagCollection('test-relids') |
709 | + |
710 | +o = NamedTemporaryFile(delete=False) |
711 | +o2 = NamedTemporaryFile(delete=False) |
712 | +o3 = NamedTemporaryFile(delete=True) |
713 | +o.write('some content') |
714 | +o.flush() |
715 | + |
716 | +x.tag_object(o.name, 'box-9') |
717 | +x.tag_object(o.name, 'nrpe-1') |
718 | +y.tag_object(o.name, 'monitors:2') |
719 | +x.tag_object(o2.name, 'box-10') |
720 | +x.tag_object(o2.name, 'nrpe-2') |
721 | +y.tag_object(o2.name, 'monitors:2') |
722 | +x.tag_object(o3.name, 'other-0') |
723 | +y.tag_object(o3.name, 'monitors:3') |
724 | +x.untag_object(o.name, 'box-9') |
725 | +x.cleanup_untagged() |
726 | + |
727 | +if not os.path.exists(o.name): |
728 | + raise RuntimeError(o.name) |
729 | + |
730 | +x.kill_tag('nrpe-1') |
731 | +x.cleanup_untagged() |
732 | + |
733 | +if os.path.exists(o.name): |
734 | + raise RuntimeError(o.name) |
735 | + |
736 | +if not os.path.exists(o2.name): |
737 | + raise RuntimeError(o2.name) |
738 | + |
739 | +y.kill_tag('monitors:2') |
740 | +y.cleanup_untagged(['monitors:1','monitors:3']) |
741 | + |
742 | +if os.path.exists(o.name): |
743 | + raise RuntimeError(o2.name) |
744 | + |
745 | +if os.path.exists(o2.name): |
746 | + raise RuntimeError(o2.name) |
747 | + |
748 | +x.destroy() |
749 | |
750 | === modified file 'hooks/upgrade-charm' |
751 | --- hooks/upgrade-charm 2012-05-14 07:15:54 +0000 |
752 | +++ hooks/upgrade-charm 2012-08-02 21:28:44 +0000 |
753 | @@ -1,2 +1,15 @@ |
754 | #!/bin/sh |
755 | -juju-log -l WARNING 'Relations have been radically changed. Its best to remove any existing relationships and re-establish them.' |
756 | +set -e |
757 | +legacy_relations="`relation-ids legacy`" |
758 | +if [ -n "$legacy_relations" ] ; then |
759 | + juju-log -l WARNING 'Relations have been radically changed. The monitoring interface is not supported anymore.' |
760 | + juju-log -l WARNING 'Please use the generic juju-info or the monitors interface' |
761 | +fi |
762 | +if [ -n "`config-get extraconfig`" ] ; then |
763 | + config-get extraconfig > /etc/nagios3/conf.d/extra.cfg |
764 | +else |
765 | + rm -f /etc/nagios3/conf.d/extra.cfg |
766 | +fi |
767 | +# Refresh these hooks entirely |
768 | +hooks/mymonitors-relation-joined |
769 | +hooks/monitors-relation-changed |
770 | |
771 | === modified file 'metadata.yaml' |
772 | --- metadata.yaml 2012-05-22 22:10:53 +0000 |
773 | +++ metadata.yaml 2012-08-02 21:28:44 +0000 |
774 | @@ -8,8 +8,12 @@ |
775 | provides: |
776 | website: |
777 | interface: http |
778 | + mymonitors: |
779 | + interface: monitors |
780 | requires: |
781 | legacy: |
782 | interface: monitoring |
783 | nagios: |
784 | interface: juju-info |
785 | + monitors: |
786 | + interface: monitors |
787 | |
788 | === added file 'monitors.yaml' |
789 | --- monitors.yaml 1970-01-01 00:00:00 +0000 |
790 | +++ monitors.yaml 2012-08-02 21:28:44 +0000 |
791 | @@ -0,0 +1,11 @@ |
792 | +version: '0.3' |
793 | +monitors: |
794 | + local: |
795 | + procrunning: |
796 | + min: 1 |
797 | + name: '/usr/sbin/nagios3' |
798 | + remote: |
799 | + http: |
800 | + nagios: |
801 | + path: /nagios3/ |
802 | + status: 'HTTP/1.1 401' |
803 | |
804 | === modified file 'revision' |
805 | --- revision 2012-05-14 23:48:24 +0000 |
806 | +++ revision 2012-08-02 21:28:44 +0000 |
807 | @@ -1,1 +1,1 @@ |
808 | -22 |
809 | +40 |
For an example of the changes needed to add remote and local monitors, see
https:/ /code.launchpad .net/~clint- fewbar/ charms/ precise/ mysql/add- monitors/ +merge/ 118000