Merge lp:~timkuhlman/charms/trusty/rsyslog/nrpe into lp:charms/trusty/rsyslog

Proposed by Tim Kuhlman
Status: Merged
Merged at revision: 22
Proposed branch: lp:~timkuhlman/charms/trusty/rsyslog/nrpe
Merge into: lp:charms/trusty/rsyslog
Diff against target: 727 lines (+627/-7)
8 files modified
charm-helpers.yaml (+2/-0)
config.yaml (+16/-0)
hooks/charmhelpers/contrib/charmsupport/__init__.py (+15/-0)
hooks/charmhelpers/contrib/charmsupport/nrpe.py (+398/-0)
hooks/charmhelpers/contrib/charmsupport/volumes.py (+175/-0)
hooks/hooks.py (+16/-6)
metadata.yaml (+3/-0)
unit_tests/test_hooks.py (+2/-1)
To merge this branch: bzr merge lp:~timkuhlman/charms/trusty/rsyslog/nrpe
Reviewer Review Type Date Requested Status
Stuart Bishop (community) Approve
Andrew McLeod (community) Needs Fixing
Adam Israel (community) Needs Fixing
Review Queue (community) automated testing Needs Fixing
Review via email: mp+292988@code.launchpad.net

Description of the change

Adds an nrpe relation and nagios check for the rsyslog daemon.

To post a comment you must log in.
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3951/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3952/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/3906/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/3907/

review: Needs Fixing (automated testing)
Revision history for this message
Adam Israel (aisrael) wrote :

Hi Tim,

Here's the logs from my test run.

http://pastebin.ubuntu.com/16508662/

review: Needs Fixing
24. By Tim Kuhlman

Mock nrpe also

Revision history for this message
Tim Kuhlman (timkuhlman) wrote :

I pushed up a fix for the failing test.

On 05/19/2016 10:13 AM, Adam Israel wrote:
> Review: Needs Fixing
>
> Hi Tim,
>
> Here's the logs from my test run.
>
> http://pastebin.ubuntu.com/16508662/
>

--
Tim Kuhlman
CDO - IS - Foxtrot

Revision history for this message
Andrew McLeod (admcleod) wrote :

Hey Tim,

Those tests are passing now, but there are still some failures on make lint and charm-proof:

http://pastebin.ubuntu.com/16514759/

review: Needs Fixing
25. By Tim Kuhlman

Remove spaces around keyword parameters =

Revision history for this message
Tim Kuhlman (timkuhlman) wrote :

I fixed the spacing around the '=' the other import errors are interesting one those are lines I didn't touch and it seems flake8 is just fooled by the sys.path.insert of the charmhelpers that is happening at the top of the file. Likely that isn't actually needed but I am out of time to test it for sure today. If no one beats me to it I will test that tomorrow..

26. By Tim Kuhlman

Removed unneeded sys.path modifications that lint doesn't like

Revision history for this message
Stuart Bishop (stub) wrote :

Looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charm-helpers.yaml'
2--- charm-helpers.yaml 2014-04-21 21:03:13 +0000
3+++ charm-helpers.yaml 2016-05-20 17:02:41 +0000
4@@ -3,3 +3,5 @@
5 include:
6 - core
7 - fetch
8+ - contrib.charmsupport
9+
10
11=== modified file 'config.yaml'
12--- config.yaml 2016-03-24 21:20:46 +0000
13+++ config.yaml 2016-05-20 17:02:41 +0000
14@@ -19,6 +19,22 @@
15 type: string
16 default: "*.*"
17 description: "Syslog style selector to specify which logs to forward. For example '*.crit' or 'auth.*'"
18+ nagios_context:
19+ default: "juju"
20+ type: string
21+ description: >
22+ Used by the nrpe-external-master subordinate charm.
23+ A string that will be prepended to instance name to set the host name
24+ in nagios. So for instance the hostname would be something like:
25+ juju-rsyslog-0
26+ If you're running multiple environments with the same services in them
27+ this allows you to differentiate between them.
28+ nagios_servicegroups:
29+ default: ""
30+ type: string
31+ description: >
32+ A comma-separated list of nagios servicegroups.
33+ If left empty, the nagios_context will be used as the servicegroup
34 syslog_rotate:
35 type: int
36 default: 7
37
38=== added directory 'hooks/charmhelpers/contrib/charmsupport'
39=== added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py'
40--- hooks/charmhelpers/contrib/charmsupport/__init__.py 1970-01-01 00:00:00 +0000
41+++ hooks/charmhelpers/contrib/charmsupport/__init__.py 2016-05-20 17:02:41 +0000
42@@ -0,0 +1,15 @@
43+# Copyright 2014-2015 Canonical Limited.
44+#
45+# This file is part of charm-helpers.
46+#
47+# charm-helpers is free software: you can redistribute it and/or modify
48+# it under the terms of the GNU Lesser General Public License version 3 as
49+# published by the Free Software Foundation.
50+#
51+# charm-helpers is distributed in the hope that it will be useful,
52+# but WITHOUT ANY WARRANTY; without even the implied warranty of
53+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
54+# GNU Lesser General Public License for more details.
55+#
56+# You should have received a copy of the GNU Lesser General Public License
57+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
58
59=== added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py'
60--- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000
61+++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2016-05-20 17:02:41 +0000
62@@ -0,0 +1,398 @@
63+# Copyright 2014-2015 Canonical Limited.
64+#
65+# This file is part of charm-helpers.
66+#
67+# charm-helpers is free software: you can redistribute it and/or modify
68+# it under the terms of the GNU Lesser General Public License version 3 as
69+# published by the Free Software Foundation.
70+#
71+# charm-helpers is distributed in the hope that it will be useful,
72+# but WITHOUT ANY WARRANTY; without even the implied warranty of
73+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
74+# GNU Lesser General Public License for more details.
75+#
76+# You should have received a copy of the GNU Lesser General Public License
77+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
78+
79+"""Compatibility with the nrpe-external-master charm"""
80+# Copyright 2012 Canonical Ltd.
81+#
82+# Authors:
83+# Matthew Wedgwood <matthew.wedgwood@canonical.com>
84+
85+import subprocess
86+import pwd
87+import grp
88+import os
89+import glob
90+import shutil
91+import re
92+import shlex
93+import yaml
94+
95+from charmhelpers.core.hookenv import (
96+ config,
97+ local_unit,
98+ log,
99+ relation_ids,
100+ relation_set,
101+ relations_of_type,
102+)
103+
104+from charmhelpers.core.host import service
105+
106+# This module adds compatibility with the nrpe-external-master and plain nrpe
107+# subordinate charms. To use it in your charm:
108+#
109+# 1. Update metadata.yaml
110+#
111+# provides:
112+# (...)
113+# nrpe-external-master:
114+# interface: nrpe-external-master
115+# scope: container
116+#
117+# and/or
118+#
119+# provides:
120+# (...)
121+# local-monitors:
122+# interface: local-monitors
123+# scope: container
124+
125+#
126+# 2. Add the following to config.yaml
127+#
128+# nagios_context:
129+# default: "juju"
130+# type: string
131+# description: |
132+# Used by the nrpe subordinate charms.
133+# A string that will be prepended to instance name to set the host name
134+# in nagios. So for instance the hostname would be something like:
135+# juju-myservice-0
136+# If you're running multiple environments with the same services in them
137+# this allows you to differentiate between them.
138+# nagios_servicegroups:
139+# default: ""
140+# type: string
141+# description: |
142+# A comma-separated list of nagios servicegroups.
143+# If left empty, the nagios_context will be used as the servicegroup
144+#
145+# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
146+#
147+# 4. Update your hooks.py with something like this:
148+#
149+# from charmsupport.nrpe import NRPE
150+# (...)
151+# def update_nrpe_config():
152+# nrpe_compat = NRPE()
153+# nrpe_compat.add_check(
154+# shortname = "myservice",
155+# description = "Check MyService",
156+# check_cmd = "check_http -w 2 -c 10 http://localhost"
157+# )
158+# nrpe_compat.add_check(
159+# "myservice_other",
160+# "Check for widget failures",
161+# check_cmd = "/srv/myapp/scripts/widget_check"
162+# )
163+# nrpe_compat.write()
164+#
165+# def config_changed():
166+# (...)
167+# update_nrpe_config()
168+#
169+# def nrpe_external_master_relation_changed():
170+# update_nrpe_config()
171+#
172+# def local_monitors_relation_changed():
173+# update_nrpe_config()
174+#
175+# 5. ln -s hooks.py nrpe-external-master-relation-changed
176+# ln -s hooks.py local-monitors-relation-changed
177+
178+
179+class CheckException(Exception):
180+ pass
181+
182+
183+class Check(object):
184+ shortname_re = '[A-Za-z0-9-_]+$'
185+ service_template = ("""
186+#---------------------------------------------------
187+# This file is Juju managed
188+#---------------------------------------------------
189+define service {{
190+ use active-service
191+ host_name {nagios_hostname}
192+ service_description {nagios_hostname}[{shortname}] """
193+ """{description}
194+ check_command check_nrpe!{command}
195+ servicegroups {nagios_servicegroup}
196+}}
197+""")
198+
199+ def __init__(self, shortname, description, check_cmd):
200+ super(Check, self).__init__()
201+ # XXX: could be better to calculate this from the service name
202+ if not re.match(self.shortname_re, shortname):
203+ raise CheckException("shortname must match {}".format(
204+ Check.shortname_re))
205+ self.shortname = shortname
206+ self.command = "check_{}".format(shortname)
207+ # Note: a set of invalid characters is defined by the
208+ # Nagios server config
209+ # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()=
210+ self.description = description
211+ self.check_cmd = self._locate_cmd(check_cmd)
212+
213+ def _get_check_filename(self):
214+ return os.path.join(NRPE.nrpe_confdir, '{}.cfg'.format(self.command))
215+
216+ def _get_service_filename(self, hostname):
217+ return os.path.join(NRPE.nagios_exportdir,
218+ 'service__{}_{}.cfg'.format(hostname, self.command))
219+
220+ def _locate_cmd(self, check_cmd):
221+ search_path = (
222+ '/usr/lib/nagios/plugins',
223+ '/usr/local/lib/nagios/plugins',
224+ )
225+ parts = shlex.split(check_cmd)
226+ for path in search_path:
227+ if os.path.exists(os.path.join(path, parts[0])):
228+ command = os.path.join(path, parts[0])
229+ if len(parts) > 1:
230+ command += " " + " ".join(parts[1:])
231+ return command
232+ log('Check command not found: {}'.format(parts[0]))
233+ return ''
234+
235+ def _remove_service_files(self):
236+ if not os.path.exists(NRPE.nagios_exportdir):
237+ return
238+ for f in os.listdir(NRPE.nagios_exportdir):
239+ if f.endswith('_{}.cfg'.format(self.command)):
240+ os.remove(os.path.join(NRPE.nagios_exportdir, f))
241+
242+ def remove(self, hostname):
243+ nrpe_check_file = self._get_check_filename()
244+ if os.path.exists(nrpe_check_file):
245+ os.remove(nrpe_check_file)
246+ self._remove_service_files()
247+
248+ def write(self, nagios_context, hostname, nagios_servicegroups):
249+ nrpe_check_file = self._get_check_filename()
250+ with open(nrpe_check_file, 'w') as nrpe_check_config:
251+ nrpe_check_config.write("# check {}\n".format(self.shortname))
252+ nrpe_check_config.write("command[{}]={}\n".format(
253+ self.command, self.check_cmd))
254+
255+ if not os.path.exists(NRPE.nagios_exportdir):
256+ log('Not writing service config as {} is not accessible'.format(
257+ NRPE.nagios_exportdir))
258+ else:
259+ self.write_service_config(nagios_context, hostname,
260+ nagios_servicegroups)
261+
262+ def write_service_config(self, nagios_context, hostname,
263+ nagios_servicegroups):
264+ self._remove_service_files()
265+
266+ templ_vars = {
267+ 'nagios_hostname': hostname,
268+ 'nagios_servicegroup': nagios_servicegroups,
269+ 'description': self.description,
270+ 'shortname': self.shortname,
271+ 'command': self.command,
272+ }
273+ nrpe_service_text = Check.service_template.format(**templ_vars)
274+ nrpe_service_file = self._get_service_filename(hostname)
275+ with open(nrpe_service_file, 'w') as nrpe_service_config:
276+ nrpe_service_config.write(str(nrpe_service_text))
277+
278+ def run(self):
279+ subprocess.call(self.check_cmd)
280+
281+
282+class NRPE(object):
283+ nagios_logdir = '/var/log/nagios'
284+ nagios_exportdir = '/var/lib/nagios/export'
285+ nrpe_confdir = '/etc/nagios/nrpe.d'
286+
287+ def __init__(self, hostname=None):
288+ super(NRPE, self).__init__()
289+ self.config = config()
290+ self.nagios_context = self.config['nagios_context']
291+ if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']:
292+ self.nagios_servicegroups = self.config['nagios_servicegroups']
293+ else:
294+ self.nagios_servicegroups = self.nagios_context
295+ self.unit_name = local_unit().replace('/', '-')
296+ if hostname:
297+ self.hostname = hostname
298+ else:
299+ nagios_hostname = get_nagios_hostname()
300+ if nagios_hostname:
301+ self.hostname = nagios_hostname
302+ else:
303+ self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
304+ self.checks = []
305+
306+ def add_check(self, *args, **kwargs):
307+ self.checks.append(Check(*args, **kwargs))
308+
309+ def remove_check(self, *args, **kwargs):
310+ if kwargs.get('shortname') is None:
311+ raise ValueError('shortname of check must be specified')
312+
313+ # Use sensible defaults if they're not specified - these are not
314+ # actually used during removal, but they're required for constructing
315+ # the Check object; check_disk is chosen because it's part of the
316+ # nagios-plugins-basic package.
317+ if kwargs.get('check_cmd') is None:
318+ kwargs['check_cmd'] = 'check_disk'
319+ if kwargs.get('description') is None:
320+ kwargs['description'] = ''
321+
322+ check = Check(*args, **kwargs)
323+ check.remove(self.hostname)
324+
325+ def write(self):
326+ try:
327+ nagios_uid = pwd.getpwnam('nagios').pw_uid
328+ nagios_gid = grp.getgrnam('nagios').gr_gid
329+ except:
330+ log("Nagios user not set up, nrpe checks not updated")
331+ return
332+
333+ if not os.path.exists(NRPE.nagios_logdir):
334+ os.mkdir(NRPE.nagios_logdir)
335+ os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
336+
337+ nrpe_monitors = {}
338+ monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}}
339+ for nrpecheck in self.checks:
340+ nrpecheck.write(self.nagios_context, self.hostname,
341+ self.nagios_servicegroups)
342+ nrpe_monitors[nrpecheck.shortname] = {
343+ "command": nrpecheck.command,
344+ }
345+
346+ service('restart', 'nagios-nrpe-server')
347+
348+ monitor_ids = relation_ids("local-monitors") + \
349+ relation_ids("nrpe-external-master")
350+ for rid in monitor_ids:
351+ relation_set(relation_id=rid, monitors=yaml.dump(monitors))
352+
353+
354+def get_nagios_hostcontext(relation_name='nrpe-external-master'):
355+ """
356+ Query relation with nrpe subordinate, return the nagios_host_context
357+
358+ :param str relation_name: Name of relation nrpe sub joined to
359+ """
360+ for rel in relations_of_type(relation_name):
361+ if 'nagios_host_context' in rel:
362+ return rel['nagios_host_context']
363+
364+
365+def get_nagios_hostname(relation_name='nrpe-external-master'):
366+ """
367+ Query relation with nrpe subordinate, return the nagios_hostname
368+
369+ :param str relation_name: Name of relation nrpe sub joined to
370+ """
371+ for rel in relations_of_type(relation_name):
372+ if 'nagios_hostname' in rel:
373+ return rel['nagios_hostname']
374+
375+
376+def get_nagios_unit_name(relation_name='nrpe-external-master'):
377+ """
378+ Return the nagios unit name prepended with host_context if needed
379+
380+ :param str relation_name: Name of relation nrpe sub joined to
381+ """
382+ host_context = get_nagios_hostcontext(relation_name)
383+ if host_context:
384+ unit = "%s:%s" % (host_context, local_unit())
385+ else:
386+ unit = local_unit()
387+ return unit
388+
389+
390+def add_init_service_checks(nrpe, services, unit_name):
391+ """
392+ Add checks for each service in list
393+
394+ :param NRPE nrpe: NRPE object to add check to
395+ :param list services: List of services to check
396+ :param str unit_name: Unit name to use in check description
397+ """
398+ for svc in services:
399+ upstart_init = '/etc/init/%s.conf' % svc
400+ sysv_init = '/etc/init.d/%s' % svc
401+ if os.path.exists(upstart_init):
402+ # Don't add a check for these services from neutron-gateway
403+ if svc not in ['ext-port', 'os-charm-phy-nic-mtu']:
404+ nrpe.add_check(
405+ shortname=svc,
406+ description='process check {%s}' % unit_name,
407+ check_cmd='check_upstart_job %s' % svc
408+ )
409+ elif os.path.exists(sysv_init):
410+ cronpath = '/etc/cron.d/nagios-service-check-%s' % svc
411+ cron_file = ('*/5 * * * * root '
412+ '/usr/local/lib/nagios/plugins/check_exit_status.pl '
413+ '-s /etc/init.d/%s status > '
414+ '/var/lib/nagios/service-check-%s.txt\n' % (svc,
415+ svc)
416+ )
417+ f = open(cronpath, 'w')
418+ f.write(cron_file)
419+ f.close()
420+ nrpe.add_check(
421+ shortname=svc,
422+ description='process check {%s}' % unit_name,
423+ check_cmd='check_status_file.py -f '
424+ '/var/lib/nagios/service-check-%s.txt' % svc,
425+ )
426+
427+
428+def copy_nrpe_checks():
429+ """
430+ Copy the nrpe checks into place
431+
432+ """
433+ NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
434+ nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks',
435+ 'charmhelpers', 'contrib', 'openstack',
436+ 'files')
437+
438+ if not os.path.exists(NAGIOS_PLUGINS):
439+ os.makedirs(NAGIOS_PLUGINS)
440+ for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")):
441+ if os.path.isfile(fname):
442+ shutil.copy2(fname,
443+ os.path.join(NAGIOS_PLUGINS, os.path.basename(fname)))
444+
445+
446+def add_haproxy_checks(nrpe, unit_name):
447+ """
448+ Add checks for each service in list
449+
450+ :param NRPE nrpe: NRPE object to add check to
451+ :param str unit_name: Unit name to use in check description
452+ """
453+ nrpe.add_check(
454+ shortname='haproxy_servers',
455+ description='Check HAProxy {%s}' % unit_name,
456+ check_cmd='check_haproxy.sh')
457+ nrpe.add_check(
458+ shortname='haproxy_queue',
459+ description='Check HAProxy queue depth {%s}' % unit_name,
460+ check_cmd='check_haproxy_queue_depth.sh')
461
462=== added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py'
463--- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000
464+++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2016-05-20 17:02:41 +0000
465@@ -0,0 +1,175 @@
466+# Copyright 2014-2015 Canonical Limited.
467+#
468+# This file is part of charm-helpers.
469+#
470+# charm-helpers is free software: you can redistribute it and/or modify
471+# it under the terms of the GNU Lesser General Public License version 3 as
472+# published by the Free Software Foundation.
473+#
474+# charm-helpers is distributed in the hope that it will be useful,
475+# but WITHOUT ANY WARRANTY; without even the implied warranty of
476+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
477+# GNU Lesser General Public License for more details.
478+#
479+# You should have received a copy of the GNU Lesser General Public License
480+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
481+
482+'''
483+Functions for managing volumes in juju units. One volume is supported per unit.
484+Subordinates may have their own storage, provided it is on its own partition.
485+
486+Configuration stanzas::
487+
488+ volume-ephemeral:
489+ type: boolean
490+ default: true
491+ description: >
492+ If false, a volume is mounted as sepecified in "volume-map"
493+ If true, ephemeral storage will be used, meaning that log data
494+ will only exist as long as the machine. YOU HAVE BEEN WARNED.
495+ volume-map:
496+ type: string
497+ default: {}
498+ description: >
499+ YAML map of units to device names, e.g:
500+ "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }"
501+ Service units will raise a configure-error if volume-ephemeral
502+ is 'true' and no volume-map value is set. Use 'juju set' to set a
503+ value and 'juju resolved' to complete configuration.
504+
505+Usage::
506+
507+ from charmsupport.volumes import configure_volume, VolumeConfigurationError
508+ from charmsupport.hookenv import log, ERROR
509+ def post_mount_hook():
510+ stop_service('myservice')
511+ def post_mount_hook():
512+ start_service('myservice')
513+
514+ if __name__ == '__main__':
515+ try:
516+ configure_volume(before_change=pre_mount_hook,
517+ after_change=post_mount_hook)
518+ except VolumeConfigurationError:
519+ log('Storage could not be configured', ERROR)
520+
521+'''
522+
523+# XXX: Known limitations
524+# - fstab is neither consulted nor updated
525+
526+import os
527+from charmhelpers.core import hookenv
528+from charmhelpers.core import host
529+import yaml
530+
531+
532+MOUNT_BASE = '/srv/juju/volumes'
533+
534+
535+class VolumeConfigurationError(Exception):
536+ '''Volume configuration data is missing or invalid'''
537+ pass
538+
539+
540+def get_config():
541+ '''Gather and sanity-check volume configuration data'''
542+ volume_config = {}
543+ config = hookenv.config()
544+
545+ errors = False
546+
547+ if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'):
548+ volume_config['ephemeral'] = True
549+ else:
550+ volume_config['ephemeral'] = False
551+
552+ try:
553+ volume_map = yaml.safe_load(config.get('volume-map', '{}'))
554+ except yaml.YAMLError as e:
555+ hookenv.log("Error parsing YAML volume-map: {}".format(e),
556+ hookenv.ERROR)
557+ errors = True
558+ if volume_map is None:
559+ # probably an empty string
560+ volume_map = {}
561+ elif not isinstance(volume_map, dict):
562+ hookenv.log("Volume-map should be a dictionary, not {}".format(
563+ type(volume_map)))
564+ errors = True
565+
566+ volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME'])
567+ if volume_config['device'] and volume_config['ephemeral']:
568+ # asked for ephemeral storage but also defined a volume ID
569+ hookenv.log('A volume is defined for this unit, but ephemeral '
570+ 'storage was requested', hookenv.ERROR)
571+ errors = True
572+ elif not volume_config['device'] and not volume_config['ephemeral']:
573+ # asked for permanent storage but did not define volume ID
574+ hookenv.log('Ephemeral storage was requested, but there is no volume '
575+ 'defined for this unit.', hookenv.ERROR)
576+ errors = True
577+
578+ unit_mount_name = hookenv.local_unit().replace('/', '-')
579+ volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name)
580+
581+ if errors:
582+ return None
583+ return volume_config
584+
585+
586+def mount_volume(config):
587+ if os.path.exists(config['mountpoint']):
588+ if not os.path.isdir(config['mountpoint']):
589+ hookenv.log('Not a directory: {}'.format(config['mountpoint']))
590+ raise VolumeConfigurationError()
591+ else:
592+ host.mkdir(config['mountpoint'])
593+ if os.path.ismount(config['mountpoint']):
594+ unmount_volume(config)
595+ if not host.mount(config['device'], config['mountpoint'], persist=True):
596+ raise VolumeConfigurationError()
597+
598+
599+def unmount_volume(config):
600+ if os.path.ismount(config['mountpoint']):
601+ if not host.umount(config['mountpoint'], persist=True):
602+ raise VolumeConfigurationError()
603+
604+
605+def managed_mounts():
606+ '''List of all mounted managed volumes'''
607+ return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts())
608+
609+
610+def configure_volume(before_change=lambda: None, after_change=lambda: None):
611+ '''Set up storage (or don't) according to the charm's volume configuration.
612+ Returns the mount point or "ephemeral". before_change and after_change
613+ are optional functions to be called if the volume configuration changes.
614+ '''
615+
616+ config = get_config()
617+ if not config:
618+ hookenv.log('Failed to read volume configuration', hookenv.CRITICAL)
619+ raise VolumeConfigurationError()
620+
621+ if config['ephemeral']:
622+ if os.path.ismount(config['mountpoint']):
623+ before_change()
624+ unmount_volume(config)
625+ after_change()
626+ return 'ephemeral'
627+ else:
628+ # persistent storage
629+ if os.path.ismount(config['mountpoint']):
630+ mounts = dict(managed_mounts())
631+ if mounts.get(config['mountpoint']) != config['device']:
632+ before_change()
633+ unmount_volume(config)
634+ mount_volume(config)
635+ after_change()
636+ else:
637+ before_change()
638+ mount_volume(config)
639+ after_change()
640+ return config['mountpoint']
641
642=== modified file 'hooks/hooks.py'
643--- hooks/hooks.py 2016-03-24 21:20:46 +0000
644+++ hooks/hooks.py 2016-05-20 17:02:41 +0000
645@@ -3,16 +3,11 @@
646 import os
647 import sys
648
649-_HERE = os.path.abspath(os.path.dirname(__file__))
650-
651-sys.path.insert(0, os.path.join(_HERE, 'charmhelpers'))
652-
653 from charmhelpers.core.host import (
654 service_start,
655 service_stop,
656 service_restart,
657 )
658-
659 from charmhelpers.core.hookenv import (
660 Hooks,
661 close_port,
662@@ -21,11 +16,12 @@
663 log as juju_log,
664 charm_dir,
665 )
666-
667+from charmhelpers.contrib.charmsupport import nrpe
668 from charmhelpers.fetch import (
669 apt_install
670 )
671
672+
673 DEFAULT_RSYSLOG_PORT = 514
674 DEFAULT_RELP_PORT = 2514
675 DEFAULT_RSYSLOG_PATH = os.path.join(os.path.sep, 'etc', 'rsyslog.d')
676@@ -120,6 +116,18 @@
677 logrotate_config.write(template.render(**params))
678
679
680+@hooks.hook("nrpe-external-master-relation-changed")
681+@hooks.hook("local-monitors-relation-changed")
682+def update_nrpe_config():
683+ nrpe_compat = nrpe.NRPE()
684+ nrpe_compat.add_check(
685+ shortname="rsyslog",
686+ description="Check rsyslog is running",
687+ check_cmd="check_procs -c 1: -C rsyslogd"
688+ )
689+ nrpe_compat.write()
690+
691+
692 def update_rsyslog_config(template_name, **params):
693 template = get_config_template(template_name)
694
695@@ -142,5 +150,7 @@
696 # configuration changed, restart rsyslog
697 service_restart("rsyslog")
698
699+ update_nrpe_config()
700+
701 if __name__ == "__main__":
702 hooks.execute(sys.argv)
703
704=== added symlink 'hooks/nrpe-external-master-relation-changed'
705=== target is u'hooks.py'
706=== modified file 'metadata.yaml'
707--- metadata.yaml 2014-09-04 20:51:59 +0000
708+++ metadata.yaml 2016-05-20 17:02:41 +0000
709@@ -23,3 +23,6 @@
710 provides:
711 aggregator:
712 interface: syslog
713+ nrpe-external-master:
714+ interface: nrpe-external-master
715+ scope: container
716
717=== modified file 'unit_tests/test_hooks.py'
718--- unit_tests/test_hooks.py 2016-03-28 16:16:07 +0000
719+++ unit_tests/test_hooks.py 2016-05-20 17:02:41 +0000
720@@ -25,7 +25,8 @@
721 "close_port",
722 "juju_log",
723 "charm_dir",
724- "config_get"
725+ "config_get",
726+ "nrpe"
727 ]
728
729

Subscribers

People subscribed via source and target branches