Merge lp:~mthaddon/charms/precise/haproxy/get-upstream into lp:charms/haproxy

Proposed by Tom Haddon
Status: Merged
Merged at revision: 63
Proposed branch: lp:~mthaddon/charms/precise/haproxy/get-upstream
Merge into: lp:charms/haproxy
Diff against target: 358 lines (+273/-0) (has conflicts)
8 files modified
config.yaml (+10/-0)
files/nrpe-external-master/check_haproxy.sh (+33/-0)
files/nrpe-external-master/check_haproxy_queue_depth.sh (+30/-0)
hooks/hooks.py (+13/-0)
hooks/nrpe.py (+170/-0)
metadata.yaml (+3/-0)
revision (+4/-0)
templates/nrpe_service.tmpl (+10/-0)
Text conflict in revision
To merge this branch: bzr merge lp:~mthaddon/charms/precise/haproxy/get-upstream
Reviewer Review Type Date Requested Status
Juan L. Negron (community) Approve
Review via email: mp+139978@code.launchpad.net

Description of the change

Include nrpe-external-master relation, and ability to inject pre-install hooks if necessary.

To post a comment you must log in.
71. By Tom Haddon

Remove illegal characters from description string

Revision history for this message
Juan L. Negron (negronjl) wrote :

Reviewing this now.

-Juan

Revision history for this message
Juan L. Negron (negronjl) wrote :

This seems to be sane.

Approving ... merging.

-Juan

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2012-07-13 21:31:43 +0000
3+++ config.yaml 2012-12-21 11:12:22 +0000
4@@ -106,3 +106,13 @@
5 before the first variable, service_name, as above. Service options is a
6 comma separated list, server options will be appended as a string to
7 the individual server lines for a given listen stanza.
8+ nagios_context:
9+ default: "juju"
10+ type: string
11+ description: |
12+ Used by the nrpe-external-master subordinate charm.
13+ A string that will be prepended to instance name to set the host name
14+ in nagios. So for instance the hostname would be something like:
15+ juju-postgresql-0
16+ If you're running multiple environments with the same services in them
17+ this allows you to differentiate between them.
18
19=== added directory 'files'
20=== added directory 'files/nrpe-external-master'
21=== added file 'files/nrpe-external-master/check_haproxy.sh'
22--- files/nrpe-external-master/check_haproxy.sh 1970-01-01 00:00:00 +0000
23+++ files/nrpe-external-master/check_haproxy.sh 2012-12-21 11:12:22 +0000
24@@ -0,0 +1,33 @@
25+#!/bin/bash
26+#--------------------------------------------
27+# This file is managed by Juju
28+#--------------------------------------------
29+#
30+# Copyright 2009,2012 Canonical Ltd.
31+# Author: Tom Haddon
32+
33+CRITICAL=0
34+NOTACTIVE=''
35+LOGFILE=/var/log/nagios/check_haproxy.log
36+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
37+
38+for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'});
39+do
40+ output=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 10000 --regex="class=\"active(2|3).*${appserver}" -e ' 200 OK')
41+ if [ $? != 0 ]; then
42+ date >> $LOGFILE
43+ echo $output >> $LOGFILE
44+ /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 10000 -v | grep $appserver >> $LOGFILE 2>&1
45+ CRITICAL=1
46+ NOTACTIVE="${NOTACTIVE} $appserver"
47+ fi
48+done
49+
50+if [ $CRITICAL = 1 ]; then
51+ echo "CRITICAL:${NOTACTIVE}"
52+ exit 2
53+fi
54+
55+echo "OK: All haproxy instances looking good"
56+exit 0
57+
58
59=== added file 'files/nrpe-external-master/check_haproxy_queue_depth.sh'
60--- files/nrpe-external-master/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000
61+++ files/nrpe-external-master/check_haproxy_queue_depth.sh 2012-12-21 11:12:22 +0000
62@@ -0,0 +1,30 @@
63+#!/bin/bash
64+#--------------------------------------------
65+# This file is managed by Juju
66+#--------------------------------------------
67+#
68+# Copyright 2009,2012 Canonical Ltd.
69+# Author: Tom Haddon
70+
71+# These should be config options at some stage
72+CURRQthrsh=0
73+MAXQthrsh=100
74+
75+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
76+
77+HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 10000 -v)
78+
79+for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}')
80+do
81+ CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3)
82+ MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4)
83+
84+ if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then
85+ echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ"
86+ exit 2
87+ fi
88+done
89+
90+echo "OK: All haproxy queue depths looking good"
91+exit 0
92+
93
94=== modified file 'hooks/hooks.py'
95--- hooks/hooks.py 2012-11-20 15:50:24 +0000
96+++ hooks/hooks.py 2012-12-21 11:12:22 +0000
97@@ -10,6 +10,7 @@
98 import subprocess
99 import sys
100 import yaml
101+import nrpe
102
103
104 ###############################################################################
105@@ -511,6 +512,9 @@
106 # Hook functions
107 ###############################################################################
108 def install_hook():
109+ for f in glob.glob('exec.d/*/charm-pre-install'):
110+ if os.path.isfile(f) and os.access(f, os.X_OK):
111+ subprocess.check_call(['sh', '-c', f])
112 if not os.path.exists(default_haproxy_service_config_dir):
113 os.mkdir(default_haproxy_service_config_dir, 0600)
114 return ((apt_get_install("haproxy") == enable_haproxy()) is True)
115@@ -595,6 +599,12 @@
116 open("%s/%s.is.proxy" %
117 (default_haproxy_service_config_dir, service_name), 'a').close()
118
119+def update_nrpe_config():
120+ nrpe_compat = nrpe.NRPE()
121+ nrpe_compat.add_check('haproxy','Check HAProxy', 'check_haproxy.sh')
122+ nrpe_compat.add_check('haproxy_queue','Check HAProxy queue depth', 'check_haproxy_queue_depth.sh')
123+ nrpe_compat.write()
124+
125 ###############################################################################
126 # Main section
127 ###############################################################################
128@@ -602,6 +612,7 @@
129 install_hook()
130 elif hook_name == "config-changed":
131 config_changed()
132+ update_nrpe_config()
133 elif hook_name == "start":
134 start_hook()
135 elif hook_name == "stop":
136@@ -616,6 +627,8 @@
137 website_interface("joined")
138 elif hook_name == "website-relation-changed":
139 website_interface("changed")
140+elif hook_name == "nrpe-external-master-relation-changed":
141+ update_nrpe_config()
142 else:
143 print "Unknown hook"
144 sys.exit(1)
145
146=== added symlink 'hooks/nrpe-external-master-relation-changed'
147=== target is u'hooks.py'
148=== added file 'hooks/nrpe.py'
149--- hooks/nrpe.py 1970-01-01 00:00:00 +0000
150+++ hooks/nrpe.py 2012-12-21 11:12:22 +0000
151@@ -0,0 +1,170 @@
152+import json
153+import subprocess
154+import pwd
155+import grp
156+import os
157+import re
158+import shlex
159+
160+# This module adds compatibility with the nrpe_external_master
161+# subordinate charm. To use it in your charm:
162+#
163+# 1. Update metadata.yaml
164+#
165+# provides:
166+# (...)
167+# nrpe-external-master:
168+# interface: nrpe-external-master
169+# scope: container
170+#
171+# 2. Add the following to config.yaml
172+#
173+# nagios_context:
174+# default: "juju"
175+# type: string
176+# description: |
177+# Used by the nrpe-external-master subordinate charm.
178+# A string that will be prepended to instance name to set the host name
179+# in nagios. So for instance the hostname would be something like:
180+# juju-myservice-0
181+# If you're running multiple environments with the same services in them
182+# this allows you to differentiate between them.
183+#
184+# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
185+#
186+# 4. Update your hooks.py with something like this:
187+#
188+# import nrpe
189+# (...)
190+# def update_nrpe_config():
191+# nrpe_compat = NRPE("myservice")
192+# nrpe_compat.add_check(
193+# shortname = "myservice",
194+# description = "Check MyService",
195+# check_cmd = "check_http -w 2 -c 10 http://localhost"
196+# )
197+# nrpe_compat.add_check(
198+# "myservice_other",
199+# "Check for widget failures",
200+# check_cmd = "/srv/myapp/scripts/widget_check"
201+# )
202+# nrpe_compat.write()
203+#
204+# def config_changed():
205+# (...)
206+# update_nrpe_config()
207+
208+class ConfigurationError(Exception):
209+ '''An error interacting with the Juju config'''
210+ pass
211+def config_get(scope=None):
212+ '''Return the Juju config as a dictionary'''
213+ try:
214+ config_cmd_line = ['config-get']
215+ if scope is not None:
216+ config_cmd_line.append(scope)
217+ config_cmd_line.append('--format=json')
218+ return json.loads(subprocess.check_output(config_cmd_line))
219+ except (ValueError, OSError, subprocess.CalledProcessError) as error:
220+ subprocess.call(['juju-log', str(error)])
221+ raise ConfigurationError(str(error))
222+
223+class CheckException(Exception): pass
224+class Check(object):
225+ shortname_re = '[A-Za-z0-9-_]*'
226+ service_template = """
227+#---------------------------------------------------
228+# This file is Juju managed
229+#---------------------------------------------------
230+define service {{
231+ use active-service
232+ host_name {nagios_hostname}
233+ service_description {nagios_hostname} {shortname} {description}
234+ check_command check_nrpe!check_{shortname}
235+ servicegroups {nagios_servicegroup}
236+}}
237+"""
238+ def __init__(self, shortname, description, check_cmd):
239+ super(Check, self).__init__()
240+ # XXX: could be better to calculate this from the service name
241+ if not re.match(self.shortname_re, shortname):
242+ raise CheckException("shortname must match {}".format(Check.shortname_re))
243+ self.shortname = shortname
244+ self.description = description
245+ self.check_cmd = self._locate_cmd(check_cmd)
246+
247+ def _locate_cmd(self, check_cmd):
248+ search_path = (
249+ '/',
250+ os.path.join(os.environ['CHARM_DIR'], 'files/nrpe-external-master'),
251+ '/usr/lib/nagios/plugins',
252+ )
253+ command = shlex.split(check_cmd)
254+ for path in search_path:
255+ if os.path.exists(os.path.join(path,command[0])):
256+ return os.path.join(path, command[0]) + " " + " ".join(command[1:])
257+ subprocess.call(['juju-log', 'Check command not found: {}'.format(command[0])])
258+ return ''
259+
260+ def write(self, nagios_context, hostname):
261+ for f in os.listdir(NRPE.nagios_exportdir):
262+ if re.search('.*check_{}.cfg'.format(self.shortname), f):
263+ os.remove(os.path.join(NRPE.nagios_exportdir, f))
264+
265+ templ_vars = {
266+ 'nagios_hostname': hostname,
267+ 'nagios_servicegroup': nagios_context,
268+ 'description': self.description,
269+ 'shortname': self.shortname,
270+ }
271+ nrpe_service_text = Check.service_template.format(**templ_vars)
272+ nrpe_service_file = '{}/service__{}_check_{}.cfg'.format(
273+ NRPE.nagios_exportdir, hostname, self.shortname)
274+ with open(nrpe_service_file, 'w') as nrpe_service_config:
275+ nrpe_service_config.write(str(nrpe_service_text))
276+
277+ nrpe_check_file = '/etc/nagios/nrpe.d/check_{}.cfg'.format(self.shortname)
278+ with open(nrpe_check_file, 'w') as nrpe_check_config:
279+ nrpe_check_config.write("# check {}\n".format(self.shortname))
280+ nrpe_check_config.write("command[check_{}]={}\n".format(
281+ self.shortname, self.check_cmd))
282+
283+ def run(self):
284+ subprocess.call(self.check_cmd)
285+
286+class NRPE(object):
287+ nagios_logdir = '/var/log/nagios'
288+ nagios_exportdir = '/var/lib/nagios/export'
289+ nrpe_confdir = '/etc/nagios/nrpe.d'
290+ def __init__(self):
291+ super(NRPE, self).__init__()
292+ self.config = config_get()
293+ self.nagios_context = self.config['nagios_context']
294+ self.unit_name = os.environ['JUJU_UNIT_NAME'].replace('/', '-')
295+ self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
296+ self.checks = []
297+
298+ def add_check(self, *args, **kwargs):
299+ self.checks.append( Check(*args, **kwargs) )
300+
301+ def write(self):
302+ try:
303+ nagios_uid = pwd.getpwnam('nagios').pw_uid
304+ nagios_gid = grp.getgrnam('nagios').gr_gid
305+ except:
306+ subprocess.call(['juju-log', "Nagios user not set up, nrpe checks not updated"])
307+ return
308+
309+ if not os.path.exists(NRPE.nagios_exportdir):
310+ subprocess.call(['juju-log', 'Exiting as {} is not accessible'.format(NRPE.nagios_exportdir)])
311+ return
312+
313+ if not os.path.exists(NRPE.nagios_logdir):
314+ os.mkdir(NRPE.nagios_logdir)
315+ os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
316+
317+ for nrpecheck in self.checks:
318+ nrpecheck.write(self.nagios_context, self.hostname)
319+
320+ if os.path.isfile('/etc/init.d/nagios-nrpe-server'):
321+ subprocess.call(['service', 'nagios-nrpe-server', 'reload'])
322
323=== modified file 'metadata.yaml'
324--- metadata.yaml 2012-08-01 05:16:56 +0000
325+++ metadata.yaml 2012-12-21 11:12:22 +0000
326@@ -15,3 +15,6 @@
327 interface: http
328 munin:
329 interface: munin-node
330+ nrpe-external-master:
331+ interface: nrpe-external-master
332+ scope: container
333
334=== modified file 'revision'
335--- revision 2012-11-20 15:50:24 +0000
336+++ revision 2012-12-21 11:12:22 +0000
337@@ -1,1 +1,5 @@
338+<<<<<<< TREE
339 24
340+=======
341+31
342+>>>>>>> MERGE-SOURCE
343
344=== added directory 'templates'
345=== added file 'templates/nrpe_service.tmpl'
346--- templates/nrpe_service.tmpl 1970-01-01 00:00:00 +0000
347+++ templates/nrpe_service.tmpl 2012-12-21 11:12:22 +0000
348@@ -0,0 +1,10 @@
349+#---------------------------------------------------
350+# This file is Juju managed
351+#---------------------------------------------------
352+define service {
353+ use active-service
354+ host_name {{ nagios_hostname }}
355+ service_description {{ nagios_hostname }} {{ check.description }}
356+ check_command check_nrpe!check_{{ check.shortname }}
357+ servicegroups {{ nagios_servicegroup }}
358+}

Subscribers

People subscribed via source and target branches