Merge lp:~sinzui/charms/precise/juju-gui/unstable-nagios into lp:~juju-gui/charms/precise/juju-gui/trunk
- Precise Pangolin (12.04)
- unstable-nagios
- Merge into trunk
Proposed by
Curtis Hovey
Status: | Merged |
---|---|
Merged at revision: | 90 |
Proposed branch: | lp:~sinzui/charms/precise/juju-gui/unstable-nagios |
Merge into: | lp:~juju-gui/charms/precise/juju-gui/trunk |
Diff against target: |
444 lines (+377/-2) 8 files modified
config.yaml (+11/-1) files/nrpe-external-master/check-app-access.sh (+18/-0) metadata.yaml (+3/-0) revision (+1/-1) scripts/charmsupport/hookenv.py (+150/-0) scripts/charmsupport/nrpe.py (+169/-0) scripts/update-nrpe.py (+14/-0) tests/20-functional.test (+11/-0) |
To merge this branch: | bzr merge lp:~sinzui/charms/precise/juju-gui/unstable-nagios |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
charmers | Pending | ||
Review via email: mp+180209@code.launchpad.net |
Commit message
Description of the change
Add nagios-
This branch merges nagios-
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote : | # |
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 2013-08-06 10:54:37 +0000 |
3 | +++ config.yaml 2013-08-14 18:21:07 +0000 |
4 | @@ -158,7 +158,7 @@ |
5 | canvas. This is also known as browse mode. |
6 | - 'minimized': the charmbrowser will be minimized by default, and hidden. |
7 | type: string |
8 | - default: sidebar |
9 | + default: sidebar |
10 | show-get-juju-button: |
11 | description: | |
12 | There are deployment modes for Juju GUI which are not intended as regular |
13 | @@ -173,3 +173,13 @@ |
14 | the only server in the future. |
15 | type: boolean |
16 | default: false |
17 | + nagios_context: |
18 | + description: | |
19 | + Used by the nrpe-external-master subordinate charm. |
20 | + A string that will be prepended to instance name to set the host name |
21 | + in nagios. So for instance the hostname would be something like: |
22 | + juju-myservice-0 |
23 | + If you're running multiple environments with the same services in them |
24 | + this allows you to differentiate between them. |
25 | + type: string |
26 | + default: "juju" |
27 | |
28 | === added directory 'files' |
29 | === added directory 'files/nrpe-external-master' |
30 | === added file 'files/nrpe-external-master/check-app-access.sh' |
31 | --- files/nrpe-external-master/check-app-access.sh 1970-01-01 00:00:00 +0000 |
32 | +++ files/nrpe-external-master/check-app-access.sh 2013-08-14 18:21:07 +0000 |
33 | @@ -0,0 +1,18 @@ |
34 | +#!/bin/bash |
35 | +SITE_CONF='/etc/apache2/sites-enabled/juju-gui' |
36 | +ADDRESS='https://127.0.0.1:443/juju-ui/version.js' |
37 | +LIFE_SIGN='jujuGuiVersionInfo' |
38 | + |
39 | +if [[ ! -f $SITE_CONF ]]; then |
40 | + echo Apache is not configured serve juju-gui. |
41 | + exit 2 |
42 | +fi |
43 | + |
44 | +match=$(curl -k $ADDRESS | grep "$LIFE_SIGN") |
45 | + |
46 | +if [[ -n "$match" ]]; then |
47 | + exit 0 |
48 | +else |
49 | + echo juju-gui did not return content indicating it was loading. |
50 | + exit 2 |
51 | +fi |
52 | |
53 | === added symlink 'hooks/nrpe-external-master-relation-changed' |
54 | === target is u'../scripts/update-nrpe.py' |
55 | === modified file 'metadata.yaml' |
56 | --- metadata.yaml 2013-06-11 14:13:45 +0000 |
57 | +++ metadata.yaml 2013-08-14 18:21:07 +0000 |
58 | @@ -22,3 +22,6 @@ |
59 | provides: |
60 | web: |
61 | interface: http |
62 | + nrpe-external-master: |
63 | + interface: nrpe-external-master |
64 | + scope: container |
65 | |
66 | === modified file 'revision' |
67 | --- revision 2013-08-08 14:13:41 +0000 |
68 | +++ revision 2013-08-14 18:21:07 +0000 |
69 | @@ -1,1 +1,1 @@ |
70 | -68 |
71 | +69 |
72 | |
73 | === added directory 'scripts' |
74 | === added directory 'scripts/charmsupport' |
75 | === added file 'scripts/charmsupport/__init__.py' |
76 | === added file 'scripts/charmsupport/hookenv.py' |
77 | --- scripts/charmsupport/hookenv.py 1970-01-01 00:00:00 +0000 |
78 | +++ scripts/charmsupport/hookenv.py 2013-08-14 18:21:07 +0000 |
79 | @@ -0,0 +1,150 @@ |
80 | +"Interactions with the Juju environment" |
81 | +# source: 27:lp:charmsupport |
82 | +# Copyright 2012 Canonical Ltd. |
83 | +# |
84 | +# Authors: |
85 | +# Matthew Wedgwood <matthew.wedgwood@canonical.com> |
86 | + |
87 | +import os |
88 | +import json |
89 | +import yaml |
90 | +import subprocess |
91 | + |
92 | +CRITICAL = "CRITICAL" |
93 | +ERROR = "ERROR" |
94 | +WARNING = "WARNING" |
95 | +INFO = "INFO" |
96 | +DEBUG = "DEBUG" |
97 | +def log(message, level=DEBUG): |
98 | + "Write a message to the juju log" |
99 | + subprocess.call( [ 'juju-log', '-l', level, message ] ) |
100 | + |
101 | +class Serializable(object): |
102 | + "Wrapper, an object that can be serialized to yaml or json" |
103 | + def __init__(self, obj): |
104 | + # wrap the object |
105 | + super(Serializable, self).__init__() |
106 | + self._wrapped_obj = obj |
107 | + |
108 | + def __getattr__(self, attr): |
109 | + # see if this object has attr |
110 | + if attr in self.__dict__: |
111 | + return getattr(self, attr) |
112 | + # proxy to the wrapped object |
113 | + return self[attr] |
114 | + |
115 | + def __getitem__(self, key): |
116 | + return self._wrapped_obj[key] |
117 | + |
118 | + def json(self): |
119 | + "Serialize the object to json" |
120 | + return json.dumps(self._wrapped_obj) |
121 | + |
122 | + def yaml(self): |
123 | + "Serialize the object to yaml" |
124 | + return yaml.dump(self._wrapped_obj) |
125 | + |
126 | +def execution_environment(): |
127 | + """A convenient bundling of the current execution context""" |
128 | + context = {} |
129 | + context['conf'] = config() |
130 | + context['unit'] = local_unit() |
131 | + context['rel'] = relations_of_type() |
132 | + context['env'] = os.environ |
133 | + return context |
134 | + |
135 | +def in_relation_hook(): |
136 | + "Determine whether we're running in a relation hook" |
137 | + return os.environ.has_key('JUJU_RELATION') |
138 | + |
139 | +def relation_type(): |
140 | + "The scope for the current relation hook" |
141 | + return os.environ['JUJU_RELATION'] |
142 | +def relation_id(): |
143 | + "The relation ID for the current relation hook" |
144 | + return os.environ['JUJU_RELATION_ID'] |
145 | +def local_unit(): |
146 | + "Local unit ID" |
147 | + return os.environ['JUJU_UNIT_NAME'] |
148 | +def remote_unit(): |
149 | + "The remote unit for the current relation hook" |
150 | + return os.environ['JUJU_REMOTE_UNIT'] |
151 | + |
152 | +def config(scope=None): |
153 | + "Juju charm configuration" |
154 | + config_cmd_line = ['config-get'] |
155 | + if scope is not None: |
156 | + config_cmd_line.append(scope) |
157 | + config_cmd_line.append('--format=json') |
158 | + try: |
159 | + config_data = json.loads(subprocess.check_output(config_cmd_line)) |
160 | + except (ValueError, OSError, subprocess.CalledProcessError) as err: |
161 | + log(str(err), level=ERROR) |
162 | + raise err |
163 | + return Serializable(config_data) |
164 | + |
165 | +def relation_ids(reltype=None): |
166 | + "A list of relation_ids" |
167 | + reltype = reltype or relation_type() |
168 | + relids = [] |
169 | + relid_cmd_line = ['relation-ids', '--format=json', reltype] |
170 | + relids.extend(json.loads(subprocess.check_output(relid_cmd_line))) |
171 | + return relids |
172 | + |
173 | +def related_units(relid=None): |
174 | + "A list of related units" |
175 | + relid = relid or relation_id() |
176 | + units_cmd_line = ['relation-list', '--format=json', '-r', relid] |
177 | + units = json.loads(subprocess.check_output(units_cmd_line)) |
178 | + return units |
179 | + |
180 | +def relation_for_unit(unit=None): |
181 | + "Get the json represenation of a unit's relation" |
182 | + unit = unit or remote_unit() |
183 | + relation_cmd_line = ['relation-get', '--format=json', '-', unit] |
184 | + try: |
185 | + relation = json.loads(subprocess.check_output(relation_cmd_line)) |
186 | + except (ValueError, OSError, subprocess.CalledProcessError), err: |
187 | + log(str(err), level=ERROR) |
188 | + raise err |
189 | + for key in relation: |
190 | + if key.endswith('-list'): |
191 | + relation[key] = relation[key].split() |
192 | + relation['__unit__'] = unit |
193 | + return Serializable(relation) |
194 | + |
195 | +def relations_for_id(relid=None): |
196 | + "Get relations of a specific relation ID" |
197 | + relation_data = [] |
198 | + relid = relid or relation_ids() |
199 | + for unit in related_units(relid): |
200 | + unit_data = relation_for_unit(unit) |
201 | + unit_data['__relid__'] = relid |
202 | + relation_data.append(unit_data) |
203 | + return relation_data |
204 | + |
205 | +def relations_of_type(reltype=None): |
206 | + "Get relations of a specific type" |
207 | + relation_data = [] |
208 | + if in_relation_hook(): |
209 | + reltype = reltype or relation_type() |
210 | + for relid in relation_ids(reltype): |
211 | + for relation in relations_for_id(relid): |
212 | + relation['__relid__'] = relid |
213 | + relation_data.append(relation) |
214 | + return relation_data |
215 | + |
216 | +class UnregisteredHookError(Exception): pass |
217 | + |
218 | +class Hooks(object): |
219 | + def __init__(self): |
220 | + super(Hooks, self).__init__() |
221 | + self._hooks = {} |
222 | + def register(self, name, function): |
223 | + self._hooks[name] = function |
224 | + def execute(self, args): |
225 | + hook_name = os.path.basename(args[0]) |
226 | + if hook_name in self._hooks: |
227 | + self._hooks[hook_name]() |
228 | + else: |
229 | + raise UnregisteredHookError(hook_name) |
230 | |
231 | === added file 'scripts/charmsupport/nrpe.py' |
232 | --- scripts/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 |
233 | +++ scripts/charmsupport/nrpe.py 2013-08-14 18:21:07 +0000 |
234 | @@ -0,0 +1,169 @@ |
235 | +"""Compatibility with the nrpe-external-master charm""" |
236 | +# source: 27:lp:charmsupport |
237 | +# Copyright 2012 Canonical Ltd. |
238 | +# |
239 | +# Authors: |
240 | +# Matthew Wedgwood <matthew.wedgwood@canonical.com> |
241 | + |
242 | +import subprocess |
243 | +import pwd |
244 | +import grp |
245 | +import os |
246 | +import re |
247 | +import shlex |
248 | + |
249 | +from hookenv import config, local_unit |
250 | + |
251 | +# This module adds compatibility with the nrpe_external_master |
252 | +# subordinate charm. To use it in your charm: |
253 | +# |
254 | +# 1. Update metadata.yaml |
255 | +# |
256 | +# provides: |
257 | +# (...) |
258 | +# nrpe-external-master: |
259 | +# interface: nrpe-external-master |
260 | +# scope: container |
261 | +# |
262 | +# 2. Add the following to config.yaml |
263 | +# |
264 | +# nagios_context: |
265 | +# default: "juju" |
266 | +# type: string |
267 | +# description: | |
268 | +# Used by the nrpe-external-master subordinate charm. |
269 | +# A string that will be prepended to instance name to set the host name |
270 | +# in nagios. So for instance the hostname would be something like: |
271 | +# juju-myservice-0 |
272 | +# If you're running multiple environments with the same services in them |
273 | +# this allows you to differentiate between them. |
274 | +# |
275 | +# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master |
276 | +# |
277 | +# 4. Update your hooks.py with something like this: |
278 | +# |
279 | +# import nrpe |
280 | +# (...) |
281 | +# def update_nrpe_config(): |
282 | +# nrpe_compat = NRPE("myservice") |
283 | +# nrpe_compat.add_check( |
284 | +# shortname = "myservice", |
285 | +# description = "Check MyService", |
286 | +# check_cmd = "check_http -w 2 -c 10 http://localhost" |
287 | +# ) |
288 | +# nrpe_compat.add_check( |
289 | +# "myservice_other", |
290 | +# "Check for widget failures", |
291 | +# check_cmd = "/srv/myapp/scripts/widget_check" |
292 | +# ) |
293 | +# nrpe_compat.write() |
294 | +# |
295 | +# def config_changed(): |
296 | +# (...) |
297 | +# update_nrpe_config() |
298 | +# def nrpe_external_master_relation_changed(): |
299 | +# update_nrpe_config() |
300 | +# |
301 | +# 5. ln -s hooks.py nrpe-external-master-relation-changed |
302 | + |
303 | +class CheckException(Exception): pass |
304 | +class Check(object): |
305 | + shortname_re = '[A-Za-z0-9-_]*' |
306 | + service_template = """ |
307 | +#--------------------------------------------------- |
308 | +# This file is Juju managed |
309 | +#--------------------------------------------------- |
310 | +define service {{ |
311 | + use active-service |
312 | + host_name {nagios_hostname} |
313 | + service_description {nagios_hostname}[{shortname}] {description} |
314 | + check_command check_nrpe!check_{shortname} |
315 | + servicegroups {nagios_servicegroup} |
316 | +}} |
317 | +""" |
318 | + def __init__(self, shortname, description, check_cmd): |
319 | + super(Check, self).__init__() |
320 | + # XXX: could be better to calculate this from the service name |
321 | + if not re.match(self.shortname_re, shortname): |
322 | + raise CheckException("shortname must match {}".format(Check.shortname_re)) |
323 | + self.shortname = shortname |
324 | + # Note: a set of invalid characters is defined by the Nagios server config |
325 | + # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= |
326 | + self.description = description |
327 | + self.check_cmd = self._locate_cmd(check_cmd) |
328 | + |
329 | + def _locate_cmd(self, check_cmd): |
330 | + search_path = ( |
331 | + '/', |
332 | + os.path.join(os.environ['CHARM_DIR'], 'files/nrpe-external-master'), |
333 | + '/usr/lib/nagios/plugins', |
334 | + ) |
335 | + command = shlex.split(check_cmd) |
336 | + for path in search_path: |
337 | + if os.path.exists(os.path.join(path,command[0])): |
338 | + return os.path.join(path, command[0]) + " " + " ".join(command[1:]) |
339 | + subprocess.call(['juju-log', 'Check command not found: {}'.format(command[0])]) |
340 | + return '' |
341 | + |
342 | + def write(self, nagios_context, hostname): |
343 | + for f in os.listdir(NRPE.nagios_exportdir): |
344 | + if re.search('.*check_{}.cfg'.format(self.shortname), f): |
345 | + os.remove(os.path.join(NRPE.nagios_exportdir, f)) |
346 | + |
347 | + templ_vars = { |
348 | + 'nagios_hostname': hostname, |
349 | + 'nagios_servicegroup': nagios_context, |
350 | + 'description': self.description, |
351 | + 'shortname': self.shortname, |
352 | + } |
353 | + nrpe_service_text = Check.service_template.format(**templ_vars) |
354 | + nrpe_service_file = '{}/service__{}_check_{}.cfg'.format( |
355 | + NRPE.nagios_exportdir, hostname, self.shortname) |
356 | + with open(nrpe_service_file, 'w') as nrpe_service_config: |
357 | + nrpe_service_config.write(str(nrpe_service_text)) |
358 | + |
359 | + nrpe_check_file = '/etc/nagios/nrpe.d/check_{}.cfg'.format(self.shortname) |
360 | + with open(nrpe_check_file, 'w') as nrpe_check_config: |
361 | + nrpe_check_config.write("# check {}\n".format(self.shortname)) |
362 | + nrpe_check_config.write("command[check_{}]={}\n".format( |
363 | + self.shortname, self.check_cmd)) |
364 | + |
365 | + def run(self): |
366 | + subprocess.call(self.check_cmd) |
367 | + |
368 | +class NRPE(object): |
369 | + nagios_logdir = '/var/log/nagios' |
370 | + nagios_exportdir = '/var/lib/nagios/export' |
371 | + nrpe_confdir = '/etc/nagios/nrpe.d' |
372 | + def __init__(self): |
373 | + super(NRPE, self).__init__() |
374 | + self.config = config() |
375 | + self.nagios_context = self.config['nagios_context'] |
376 | + self.unit_name = local_unit().replace('/', '-') |
377 | + self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) |
378 | + self.checks = [] |
379 | + |
380 | + def add_check(self, *args, **kwargs): |
381 | + self.checks.append( Check(*args, **kwargs) ) |
382 | + |
383 | + def write(self): |
384 | + try: |
385 | + nagios_uid = pwd.getpwnam('nagios').pw_uid |
386 | + nagios_gid = grp.getgrnam('nagios').gr_gid |
387 | + except: |
388 | + subprocess.call(['juju-log', "Nagios user not set up, nrpe checks not updated"]) |
389 | + return |
390 | + |
391 | + if not os.path.exists(NRPE.nagios_exportdir): |
392 | + subprocess.call(['juju-log', 'Exiting as {} is not accessible'.format(NRPE.nagios_exportdir)]) |
393 | + return |
394 | + |
395 | + if not os.path.exists(NRPE.nagios_logdir): |
396 | + os.mkdir(NRPE.nagios_logdir) |
397 | + os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) |
398 | + |
399 | + for nrpecheck in self.checks: |
400 | + nrpecheck.write(self.nagios_context, self.hostname) |
401 | + |
402 | + if os.path.isfile('/etc/init.d/nagios-nrpe-server'): |
403 | + subprocess.call(['service', 'nagios-nrpe-server', 'reload']) |
404 | |
405 | === added file 'scripts/update-nrpe.py' |
406 | --- scripts/update-nrpe.py 1970-01-01 00:00:00 +0000 |
407 | +++ scripts/update-nrpe.py 2013-08-14 18:21:07 +0000 |
408 | @@ -0,0 +1,14 @@ |
409 | +#!/usr/bin/env python |
410 | +from charmsupport import nrpe |
411 | + |
412 | + |
413 | +def update_nrpe_config(): |
414 | + nrpe_compat = nrpe.NRPE() |
415 | + nrpe_compat.add_check( |
416 | + 'App is accessible', 'Check the app can be downloaded', |
417 | + 'check-app-access.sh') |
418 | + nrpe_compat.write() |
419 | + |
420 | + |
421 | +if __name__ == '__main__': |
422 | + update_nrpe_config() |
423 | |
424 | === modified file 'tests/20-functional.test' |
425 | --- tests/20-functional.test 2013-08-08 13:53:22 +0000 |
426 | +++ tests/20-functional.test 2013-08-14 18:21:07 +0000 |
427 | @@ -226,6 +226,17 @@ |
428 | self.handle_browser_warning() |
429 | self.assertEnvironmentIsConnected() |
430 | |
431 | + def test_nrpe_check_available(self): |
432 | + # Make sure the check-app-access.sh script's ADDRESS is available. |
433 | + unit_info = self.juju_deploy( |
434 | + self.charm, options={'juju-gui-source': JUJU_GUI_TEST_BRANCH}) |
435 | + hostname = unit_info['public-address'] |
436 | + conn = httplib.HTTPSConnection(hostname) |
437 | + # This request matches the ADDRESS var in the script. |
438 | + conn.request('GET', '/juju-ui/version.js') |
439 | + message = 'ADDRESS in check-app-access.sh is not accessible.' |
440 | + self.assertEqual(200, conn.getresponse().status, message) |
441 | + |
442 | |
443 | if __name__ == '__main__': |
444 | unittest.main(verbosity=2) |
Reviewers: mp+180209_ code.launchpad. net,
Message:
Please take a look.
Description: external- master support
Add nagios-
This branch merges nagios- external- master support from the stable charm
to this unstable charm. Conflicts had to be resolved. in
20-functional.test and in config.yaml.
https:/ /code.launchpad .net/~sinzui/ charms/ precise/ juju-gui/ unstable- nagios/ +merge/ 180209
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/12942043/
Affected files: external- master/ check-app- access. sh charmsupport/ __init_ _.py charmsupport/ hookenv. py charmsupport/ nrpe.py update- nrpe.py functional. test
A [revision details]
M config.yaml
A files/nrpe-
M metadata.yaml
M revision
A scripts/
A scripts/
A scripts/
A scripts/
M tests/20-