Merge lp:~clint-fewbar/charms/precise/nagios/add-monitors-2 into lp:charms/nagios

Proposed by Clint Byrum
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
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.

To post a comment you must log in.
Revision history for this message
Clint Byrum (clint-fewbar) wrote :

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

Revision history for this message
Mark Mims (mark-mims) wrote :

Please add default user/pass instructions...
maybe mention how to use `juju ssh nagios/0 sudo cat /var/lib/juju/nagios.passwd`

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'
53Binary 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

Subscribers

People subscribed via source and target branches

to all changes: