Merge lp:~brad-marshall/charms/trusty/heat/add-nrpe-checks into lp:~openstack-charmers-archive/charms/trusty/heat/trunk

Proposed by Brad Marshall
Status: Merged
Merged at revision: 28
Proposed branch: lp:~brad-marshall/charms/trusty/heat/add-nrpe-checks
Merge into: lp:~openstack-charmers-archive/charms/trusty/heat/trunk
Diff against target: 558 lines (+459/-2)
7 files modified
charm-helpers.yaml (+1/-0)
config.yaml (+10/-0)
hooks/charmhelpers/contrib/charmsupport/nrpe.py (+222/-0)
hooks/charmhelpers/contrib/charmsupport/volumes.py (+156/-0)
hooks/heat_relations.py (+58/-2)
hooks/heat_utils.py (+8/-0)
metadata.yaml (+4/-0)
To merge this branch: bzr merge lp:~brad-marshall/charms/trusty/heat/add-nrpe-checks
Reviewer Review Type Date Requested Status
Liam Young Pending
Review via email: mp+241920@code.launchpad.net

This proposal supersedes a proposal from 2014-11-12.

Description of the change

Adds nrpe-external-master interface and adds basic nrpe checks.

To post a comment you must log in.
Revision history for this message
Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_amulet_test #368 trusty-heat for brad-marshall mp241493
    AMULET FAIL: amulet-test missing

AMULET Results (max last 5 lines):
INFO:root:Workspace dir: /var/lib/jenkins/workspace/charm_amulet_test
INFO:root:Reading file: Makefile
INFO:root:Searching for: ['@juju test']
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

Full amulet test output: http://paste.ubuntu.com/8955708/
Build: http://10.98.191.181:8080/job/charm_amulet_test/368/

Revision history for this message
Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_lint_check #988 trusty-heat for brad-marshall mp241493
    LINT FAIL: lint-test failed

LINT Results (max last 5 lines):
ERROR:root:Make target returned non-zero.
  Running flake8 tests: hooks/heat_relations.py:163:80: E501 line too long (92 > 79 characters)
  hooks/heat_relations.py:190:22: E251 unexpected spaces around keyword / parameter equals
  hooks/heat_relations.py:190:24: E251 unexpected spaces around keyword / parameter equals
  make: *** [lint] Error 1

Full lint test output: http://paste.ubuntu.com/8955709/
Build: http://10.98.191.181:8080/job/charm_lint_check/988/

Revision history for this message
Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_unit_test #823 trusty-heat for brad-marshall mp241493
    UNIT FAIL: unit-test failed

UNIT Results (max last 5 lines):
  heat_utils 46 6 87% 70-78
  TOTAL 170 42 75%
  Ran 21 tests in 0.504s
  FAILED (errors=2)
  make: *** [test] Error 1

Full unit test output: http://paste.ubuntu.com/8955710/
Build: http://10.98.191.181:8080/job/charm_unit_test/823/

Revision history for this message
Liam Young (gnuoy) wrote : Posted in a previous version of this proposal

Thanks again for the sorely needed nagios checks. Just a few comments...

Please steal the services() method from nova-cloud-controller and add it to hooks/heat_utils.py and use that to get a list services rather than defining a new list in update_nagios().

Please can the check_upstart_job go into charmhelpers

There appears to be some lint errors:
Running flake8 tests: hooks/heat_relations.py:163:80: E501 line too long (92 > 79 characters)
hooks/heat_relations.py:190:22: E251 unexpected spaces around keyword / parameter equals
hooks/heat_relations.py:190:24: E251 unexpected spaces around keyword / parameter equals
Makefile:5: recipe for target 'lint' failed
make: *** [lint] Error 1

review: Needs Fixing
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_lint_check #1091 trusty-heat for brad-marshall mp241493
    LINT OK: passed

LINT Results (max last 5 lines):
INFO:root:Searching for: ['@flake8']
INFO:root:command: make -f Makefile lint
  Running flake8 tests: OK
  Running charm proof: W: metadata name (heat) must match directory name (trusty-heat) exactly for local deployment.
  OK

Full lint test output: http://paste.ubuntu.com/9052189/
Build: http://10.98.191.181:8080/job/charm_lint_check/1091/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_unit_test #925 trusty-heat for brad-marshall mp241493
    UNIT FAIL: unit-test failed

UNIT Results (max last 5 lines):
  heat_utils 51 10 80% 70-78, 141-144
  TOTAL 177 49 72%
  Ran 21 tests in 0.492s
  FAILED (errors=2)
  make: *** [test] Error 1

Full unit test output: http://paste.ubuntu.com/9052192/
Build: http://10.98.191.181:8080/job/charm_unit_test/925/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

UOSCI bot says:
charm_amulet_test #433 trusty-heat for brad-marshall mp241493
    AMULET FAIL: amulet-test missing

AMULET Results (max last 5 lines):
INFO:root:Workspace dir: /var/lib/jenkins/workspace/charm_amulet_test
INFO:root:Reading file: Makefile
INFO:root:Searching for: ['@juju test']
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

Full amulet test output: http://paste.ubuntu.com/9052239/
Build: http://10.98.191.181:8080/job/charm_amulet_test/433/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_amulet_test #436 trusty-heat for brad-marshall mp241920
    AMULET FAIL: amulet-test missing

AMULET Results (max last 5 lines):
INFO:root:Workspace dir: /var/lib/jenkins/workspace/charm_amulet_test
INFO:root:Reading file: Makefile
INFO:root:Searching for: ['@juju test']
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

Full amulet test output: http://paste.ubuntu.com/9052691/
Build: http://10.98.191.181:8080/job/charm_amulet_test/436/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_lint_check #1094 trusty-heat for brad-marshall mp241920
    LINT OK: passed

LINT Results (max last 5 lines):
INFO:root:Searching for: ['@flake8']
INFO:root:command: make -f Makefile lint
  Running flake8 tests: OK
  Running charm proof: W: metadata name (heat) must match directory name (trusty-heat) exactly for local deployment.
  OK

Full lint test output: http://paste.ubuntu.com/9052693/
Build: http://10.98.191.181:8080/job/charm_lint_check/1094/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_unit_test #928 trusty-heat for brad-marshall mp241920
    UNIT FAIL: unit-test failed

UNIT Results (max last 5 lines):
  heat_utils 51 10 80% 70-78, 141-144
  TOTAL 177 49 72%
  Ran 21 tests in 0.524s
  FAILED (errors=2)
  make: *** [test] Error 1

Full unit test output: http://paste.ubuntu.com/9052695/
Build: http://10.98.191.181:8080/job/charm_unit_test/928/

33. By Brad Marshall

[bradm] Removed nagios check files as they're now in n-e-m charm, added support for sysvinit daemon monitoring.

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_lint_check #1120 trusty-heat for brad-marshall mp241920
    LINT OK: passed

LINT Results (max last 5 lines):
INFO:root:Searching for: ['@flake8']
INFO:root:command: make -f Makefile lint
  Running flake8 tests: OK
  Running charm proof: W: metadata name (heat) must match directory name (trusty-heat) exactly for local deployment.
  OK

Full lint test output: http://paste.ubuntu.com/9063927/
Build: http://10.98.191.181:8080/job/charm_lint_check/1120/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_unit_test #954 trusty-heat for brad-marshall mp241920
    UNIT FAIL: unit-test failed

UNIT Results (max last 5 lines):
  heat_utils 51 10 80% 70-78, 141-144
  TOTAL 185 57 69%
  Ran 21 tests in 0.472s
  FAILED (errors=2)
  make: *** [test] Error 1

Full unit test output: http://paste.ubuntu.com/9063930/
Build: http://10.98.191.181:8080/job/charm_unit_test/954/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_amulet_test #462 trusty-heat for brad-marshall mp241920
    AMULET FAIL: amulet-test missing

AMULET Results (max last 5 lines):
INFO:root:Workspace dir: /var/lib/jenkins/workspace/charm_amulet_test
INFO:root:Reading file: Makefile
INFO:root:Searching for: ['@juju test']
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

Full amulet test output: http://paste.ubuntu.com/9064037/
Build: http://10.98.191.181:8080/job/charm_amulet_test/462/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_lint_check #1244 trusty-heat for brad-marshall mp241920
    LINT OK: passed

LINT Results (max last 5 lines):
INFO:root:command: make -f Makefile lint
  Running flake8 tests: OK
  Running charm proof: W: metadata name (heat) must match directory name (trusty-heat) exactly for local deployment.
  I: Categories are being deprecated in favor of tags. Please rename the "categories" field to "tags".
  OK

Full lint test output: http://paste.ubuntu.com/9281269/
Build: http://10.98.191.181:8080/job/charm_lint_check/1244/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_unit_test #1078 trusty-heat for brad-marshall mp241920
    UNIT FAIL: unit-test failed

UNIT Results (max last 5 lines):
  heat_utils 51 10 80% 70-78, 141-144
  TOTAL 185 57 69%
  Ran 21 tests in 0.434s
  FAILED (errors=2)
  make: *** [test] Error 1

Full unit test output: http://paste.ubuntu.com/9281270/
Build: http://10.98.191.181:8080/job/charm_unit_test/1078/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

UOSCI bot says:
charm_amulet_test #547 trusty-heat for brad-marshall mp241920
    AMULET FAIL: amulet-test missing

AMULET Results (max last 5 lines):
INFO:root:Workspace dir: /var/lib/jenkins/workspace/charm_amulet_test
INFO:root:Reading file: Makefile
INFO:root:Searching for: ['@juju test']
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

Full amulet test output: http://paste.ubuntu.com/9281355/
Build: http://10.98.191.181:8080/job/charm_amulet_test/547/

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-08-13 13:45:18 +0000
3+++ charm-helpers.yaml 2014-11-18 01:14:05 +0000
4@@ -10,3 +10,4 @@
5 - apache
6 - cluster
7 - payload.execd
8+ - contrib.charmsupport
9
10=== modified file 'config.yaml'
11--- config.yaml 2014-04-08 20:05:44 +0000
12+++ config.yaml 2014-11-18 01:14:05 +0000
13@@ -44,3 +44,13 @@
14 description: |
15 By default, all services will log into their corresponding log files.
16 Setting this to True will force all services to log to the syslog.
17+ nagios_context:
18+ default: "juju"
19+ type: string
20+ description: |
21+ Used by the nrpe-external-master subordinate charm.
22+ A string that will be prepended to instance name to set the host name
23+ in nagios. So for instance the hostname would be something like:
24+ juju-myservice-0
25+ If you're running multiple environments with the same services in them
26+ this allows you to differentiate between them.
27
28=== added directory 'files'
29=== added directory 'hooks/charmhelpers/contrib/charmsupport'
30=== added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py'
31=== added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py'
32--- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000
33+++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2014-11-18 01:14:05 +0000
34@@ -0,0 +1,222 @@
35+"""Compatibility with the nrpe-external-master charm"""
36+# Copyright 2012 Canonical Ltd.
37+#
38+# Authors:
39+# Matthew Wedgwood <matthew.wedgwood@canonical.com>
40+
41+import subprocess
42+import pwd
43+import grp
44+import os
45+import re
46+import shlex
47+import yaml
48+
49+from charmhelpers.core.hookenv import (
50+ config,
51+ local_unit,
52+ log,
53+ relation_ids,
54+ relation_set,
55+)
56+
57+from charmhelpers.core.host import service
58+
59+# This module adds compatibility with the nrpe-external-master and plain nrpe
60+# subordinate charms. To use it in your charm:
61+#
62+# 1. Update metadata.yaml
63+#
64+# provides:
65+# (...)
66+# nrpe-external-master:
67+# interface: nrpe-external-master
68+# scope: container
69+#
70+# and/or
71+#
72+# provides:
73+# (...)
74+# local-monitors:
75+# interface: local-monitors
76+# scope: container
77+
78+#
79+# 2. Add the following to config.yaml
80+#
81+# nagios_context:
82+# default: "juju"
83+# type: string
84+# description: |
85+# Used by the nrpe subordinate charms.
86+# A string that will be prepended to instance name to set the host name
87+# in nagios. So for instance the hostname would be something like:
88+# juju-myservice-0
89+# If you're running multiple environments with the same services in them
90+# this allows you to differentiate between them.
91+#
92+# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
93+#
94+# 4. Update your hooks.py with something like this:
95+#
96+# from charmsupport.nrpe import NRPE
97+# (...)
98+# def update_nrpe_config():
99+# nrpe_compat = NRPE()
100+# nrpe_compat.add_check(
101+# shortname = "myservice",
102+# description = "Check MyService",
103+# check_cmd = "check_http -w 2 -c 10 http://localhost"
104+# )
105+# nrpe_compat.add_check(
106+# "myservice_other",
107+# "Check for widget failures",
108+# check_cmd = "/srv/myapp/scripts/widget_check"
109+# )
110+# nrpe_compat.write()
111+#
112+# def config_changed():
113+# (...)
114+# update_nrpe_config()
115+#
116+# def nrpe_external_master_relation_changed():
117+# update_nrpe_config()
118+#
119+# def local_monitors_relation_changed():
120+# update_nrpe_config()
121+#
122+# 5. ln -s hooks.py nrpe-external-master-relation-changed
123+# ln -s hooks.py local-monitors-relation-changed
124+
125+
126+class CheckException(Exception):
127+ pass
128+
129+
130+class Check(object):
131+ shortname_re = '[A-Za-z0-9-_]+$'
132+ service_template = ("""
133+#---------------------------------------------------
134+# This file is Juju managed
135+#---------------------------------------------------
136+define service {{
137+ use active-service
138+ host_name {nagios_hostname}
139+ service_description {nagios_hostname}[{shortname}] """
140+ """{description}
141+ check_command check_nrpe!{command}
142+ servicegroups {nagios_servicegroup}
143+}}
144+""")
145+
146+ def __init__(self, shortname, description, check_cmd):
147+ super(Check, self).__init__()
148+ # XXX: could be better to calculate this from the service name
149+ if not re.match(self.shortname_re, shortname):
150+ raise CheckException("shortname must match {}".format(
151+ Check.shortname_re))
152+ self.shortname = shortname
153+ self.command = "check_{}".format(shortname)
154+ # Note: a set of invalid characters is defined by the
155+ # Nagios server config
156+ # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()=
157+ self.description = description
158+ self.check_cmd = self._locate_cmd(check_cmd)
159+
160+ def _locate_cmd(self, check_cmd):
161+ search_path = (
162+ '/',
163+ os.path.join(os.environ['CHARM_DIR'],
164+ 'files/nrpe-external-master'),
165+ '/usr/lib/nagios/plugins',
166+ '/usr/local/lib/nagios/plugins',
167+ )
168+ parts = shlex.split(check_cmd)
169+ for path in search_path:
170+ if os.path.exists(os.path.join(path, parts[0])):
171+ command = os.path.join(path, parts[0])
172+ if len(parts) > 1:
173+ command += " " + " ".join(parts[1:])
174+ return command
175+ log('Check command not found: {}'.format(parts[0]))
176+ return ''
177+
178+ def write(self, nagios_context, hostname):
179+ nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format(
180+ self.command)
181+ with open(nrpe_check_file, 'w') as nrpe_check_config:
182+ nrpe_check_config.write("# check {}\n".format(self.shortname))
183+ nrpe_check_config.write("command[{}]={}\n".format(
184+ self.command, self.check_cmd))
185+
186+ if not os.path.exists(NRPE.nagios_exportdir):
187+ log('Not writing service config as {} is not accessible'.format(
188+ NRPE.nagios_exportdir))
189+ else:
190+ self.write_service_config(nagios_context, hostname)
191+
192+ def write_service_config(self, nagios_context, hostname):
193+ for f in os.listdir(NRPE.nagios_exportdir):
194+ if re.search('.*{}.cfg'.format(self.command), f):
195+ os.remove(os.path.join(NRPE.nagios_exportdir, f))
196+
197+ templ_vars = {
198+ 'nagios_hostname': hostname,
199+ 'nagios_servicegroup': nagios_context,
200+ 'description': self.description,
201+ 'shortname': self.shortname,
202+ 'command': self.command,
203+ }
204+ nrpe_service_text = Check.service_template.format(**templ_vars)
205+ nrpe_service_file = '{}/service__{}_{}.cfg'.format(
206+ NRPE.nagios_exportdir, hostname, self.command)
207+ with open(nrpe_service_file, 'w') as nrpe_service_config:
208+ nrpe_service_config.write(str(nrpe_service_text))
209+
210+ def run(self):
211+ subprocess.call(self.check_cmd)
212+
213+
214+class NRPE(object):
215+ nagios_logdir = '/var/log/nagios'
216+ nagios_exportdir = '/var/lib/nagios/export'
217+ nrpe_confdir = '/etc/nagios/nrpe.d'
218+
219+ def __init__(self, hostname=None):
220+ super(NRPE, self).__init__()
221+ self.config = config()
222+ self.nagios_context = self.config['nagios_context']
223+ self.unit_name = local_unit().replace('/', '-')
224+ if hostname:
225+ self.hostname = hostname
226+ else:
227+ self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
228+ self.checks = []
229+
230+ def add_check(self, *args, **kwargs):
231+ self.checks.append(Check(*args, **kwargs))
232+
233+ def write(self):
234+ try:
235+ nagios_uid = pwd.getpwnam('nagios').pw_uid
236+ nagios_gid = grp.getgrnam('nagios').gr_gid
237+ except:
238+ log("Nagios user not set up, nrpe checks not updated")
239+ return
240+
241+ if not os.path.exists(NRPE.nagios_logdir):
242+ os.mkdir(NRPE.nagios_logdir)
243+ os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
244+
245+ nrpe_monitors = {}
246+ monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}}
247+ for nrpecheck in self.checks:
248+ nrpecheck.write(self.nagios_context, self.hostname)
249+ nrpe_monitors[nrpecheck.shortname] = {
250+ "command": nrpecheck.command,
251+ }
252+
253+ service('restart', 'nagios-nrpe-server')
254+
255+ for rid in relation_ids("local-monitors"):
256+ relation_set(relation_id=rid, monitors=yaml.dump(monitors))
257
258=== added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py'
259--- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000
260+++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2014-11-18 01:14:05 +0000
261@@ -0,0 +1,156 @@
262+'''
263+Functions for managing volumes in juju units. One volume is supported per unit.
264+Subordinates may have their own storage, provided it is on its own partition.
265+
266+Configuration stanzas:
267+ volume-ephemeral:
268+ type: boolean
269+ default: true
270+ description: >
271+ If false, a volume is mounted as sepecified in "volume-map"
272+ If true, ephemeral storage will be used, meaning that log data
273+ will only exist as long as the machine. YOU HAVE BEEN WARNED.
274+ volume-map:
275+ type: string
276+ default: {}
277+ description: >
278+ YAML map of units to device names, e.g:
279+ "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }"
280+ Service units will raise a configure-error if volume-ephemeral
281+ is 'true' and no volume-map value is set. Use 'juju set' to set a
282+ value and 'juju resolved' to complete configuration.
283+
284+Usage:
285+ from charmsupport.volumes import configure_volume, VolumeConfigurationError
286+ from charmsupport.hookenv import log, ERROR
287+ def post_mount_hook():
288+ stop_service('myservice')
289+ def post_mount_hook():
290+ start_service('myservice')
291+
292+ if __name__ == '__main__':
293+ try:
294+ configure_volume(before_change=pre_mount_hook,
295+ after_change=post_mount_hook)
296+ except VolumeConfigurationError:
297+ log('Storage could not be configured', ERROR)
298+'''
299+
300+# XXX: Known limitations
301+# - fstab is neither consulted nor updated
302+
303+import os
304+from charmhelpers.core import hookenv
305+from charmhelpers.core import host
306+import yaml
307+
308+
309+MOUNT_BASE = '/srv/juju/volumes'
310+
311+
312+class VolumeConfigurationError(Exception):
313+ '''Volume configuration data is missing or invalid'''
314+ pass
315+
316+
317+def get_config():
318+ '''Gather and sanity-check volume configuration data'''
319+ volume_config = {}
320+ config = hookenv.config()
321+
322+ errors = False
323+
324+ if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'):
325+ volume_config['ephemeral'] = True
326+ else:
327+ volume_config['ephemeral'] = False
328+
329+ try:
330+ volume_map = yaml.safe_load(config.get('volume-map', '{}'))
331+ except yaml.YAMLError as e:
332+ hookenv.log("Error parsing YAML volume-map: {}".format(e),
333+ hookenv.ERROR)
334+ errors = True
335+ if volume_map is None:
336+ # probably an empty string
337+ volume_map = {}
338+ elif not isinstance(volume_map, dict):
339+ hookenv.log("Volume-map should be a dictionary, not {}".format(
340+ type(volume_map)))
341+ errors = True
342+
343+ volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME'])
344+ if volume_config['device'] and volume_config['ephemeral']:
345+ # asked for ephemeral storage but also defined a volume ID
346+ hookenv.log('A volume is defined for this unit, but ephemeral '
347+ 'storage was requested', hookenv.ERROR)
348+ errors = True
349+ elif not volume_config['device'] and not volume_config['ephemeral']:
350+ # asked for permanent storage but did not define volume ID
351+ hookenv.log('Ephemeral storage was requested, but there is no volume '
352+ 'defined for this unit.', hookenv.ERROR)
353+ errors = True
354+
355+ unit_mount_name = hookenv.local_unit().replace('/', '-')
356+ volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name)
357+
358+ if errors:
359+ return None
360+ return volume_config
361+
362+
363+def mount_volume(config):
364+ if os.path.exists(config['mountpoint']):
365+ if not os.path.isdir(config['mountpoint']):
366+ hookenv.log('Not a directory: {}'.format(config['mountpoint']))
367+ raise VolumeConfigurationError()
368+ else:
369+ host.mkdir(config['mountpoint'])
370+ if os.path.ismount(config['mountpoint']):
371+ unmount_volume(config)
372+ if not host.mount(config['device'], config['mountpoint'], persist=True):
373+ raise VolumeConfigurationError()
374+
375+
376+def unmount_volume(config):
377+ if os.path.ismount(config['mountpoint']):
378+ if not host.umount(config['mountpoint'], persist=True):
379+ raise VolumeConfigurationError()
380+
381+
382+def managed_mounts():
383+ '''List of all mounted managed volumes'''
384+ return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts())
385+
386+
387+def configure_volume(before_change=lambda: None, after_change=lambda: None):
388+ '''Set up storage (or don't) according to the charm's volume configuration.
389+ Returns the mount point or "ephemeral". before_change and after_change
390+ are optional functions to be called if the volume configuration changes.
391+ '''
392+
393+ config = get_config()
394+ if not config:
395+ hookenv.log('Failed to read volume configuration', hookenv.CRITICAL)
396+ raise VolumeConfigurationError()
397+
398+ if config['ephemeral']:
399+ if os.path.ismount(config['mountpoint']):
400+ before_change()
401+ unmount_volume(config)
402+ after_change()
403+ return 'ephemeral'
404+ else:
405+ # persistent storage
406+ if os.path.ismount(config['mountpoint']):
407+ mounts = dict(managed_mounts())
408+ if mounts.get(config['mountpoint']) != config['device']:
409+ before_change()
410+ unmount_volume(config)
411+ mount_volume(config)
412+ after_change()
413+ else:
414+ before_change()
415+ mount_volume(config)
416+ after_change()
417+ return config['mountpoint']
418
419=== modified file 'hooks/heat_relations.py'
420--- hooks/heat_relations.py 2014-04-12 20:48:50 +0000
421+++ hooks/heat_relations.py 2014-11-18 01:14:05 +0000
422@@ -20,6 +20,8 @@
423 charm_dir,
424 log,
425 relation_set,
426+ relations_of_type,
427+ local_unit,
428 open_port,
429 unit_get
430 )
431@@ -45,6 +47,7 @@
432 from heat_utils import (
433 do_openstack_upgrade,
434 restart_map,
435+ services,
436 determine_packages,
437 register_configs,
438 HEAT_CONF,
439@@ -54,6 +57,8 @@
440
441 from charmhelpers.payload.execd import execd_preinstall
442
443+from charmhelpers.contrib.charmsupport.nrpe import NRPE
444+
445 hooks = Hooks()
446 CONFIGS = register_configs()
447
448@@ -69,8 +74,9 @@
449 if os.path.isdir(_files):
450 for f in os.listdir(_files):
451 f = os.path.join(_files, f)
452- log('Installing %s to /usr/bin' % f)
453- shutil.copy2(f, '/usr/bin')
454+ if os.path.isfile(f):
455+ log('Installing %s to /usr/bin' % f)
456+ shutil.copy2(f, '/usr/bin')
457
458 for port in API_PORTS.values():
459 open_port(port)
460@@ -81,6 +87,7 @@
461 def config_changed():
462 if openstack_upgrade_available('heat-engine'):
463 do_openstack_upgrade(CONFIGS)
464+ update_nrpe_config()
465 CONFIGS.write_all()
466
467
468@@ -154,6 +161,55 @@
469 CONFIGS.write_all()
470
471
472+@hooks.hook('nrpe-external-master-relation-joined',
473+ 'nrpe-external-master-relation-changed')
474+def update_nrpe_config():
475+ # Find out if nrpe set nagios_hostname
476+ hostname = None
477+ host_context = None
478+ for rel in relations_of_type('nrpe-external-master'):
479+ if 'nagios_hostname' in rel:
480+ hostname = rel['nagios_hostname']
481+ host_context = rel['nagios_host_context']
482+ break
483+ nrpe = NRPE(hostname=hostname)
484+ apt_install('python-dbus')
485+
486+ if host_context:
487+ current_unit = "%s:%s" % (host_context, local_unit())
488+ else:
489+ current_unit = local_unit()
490+
491+ services_to_monitor = services()
492+
493+ for service in services_to_monitor:
494+ upstart_init = '/etc/init/%s.conf' % service
495+ sysv_init = '/etc/init.d/%s' % service
496+
497+ if os.path.exists(upstart_init):
498+ nrpe.add_check(
499+ shortname=service,
500+ description='process check {%s}' % current_unit,
501+ check_cmd='check_upstart_job %s' % service,
502+ )
503+ elif os.path.exists(sysv_init):
504+ cronpath = '/etc/cron.d/nagios-service-check-%s' % service
505+ cron_template = '*/5 * * * * root \
506+/usr/local/lib/nagios/plugins/check_exit_status.pl -s /etc/init.d/%s \
507+status > /var/lib/nagios/service-check-%s.txt\n' % (service, service)
508+ f = open(cronpath, 'w')
509+ f.write(cron_template)
510+ f.close()
511+ nrpe.add_check(
512+ shortname=service,
513+ description='process check {%s}' % current_unit,
514+ check_cmd='check_status_file.py -f \
515+ /var/lib/nagios/service-check-%s.txt' % service,
516+ )
517+
518+ nrpe.write()
519+
520+
521 def main():
522 try:
523 hooks.execute(sys.argv)
524
525=== modified file 'hooks/heat_utils.py'
526--- hooks/heat_utils.py 2014-04-12 21:40:16 +0000
527+++ hooks/heat_utils.py 2014-11-18 01:14:05 +0000
528@@ -134,3 +134,11 @@
529 if svcs:
530 _map.append((f, svcs))
531 return OrderedDict(_map)
532+
533+
534+def services():
535+ ''' Returns a list of services associate with this charm '''
536+ _services = []
537+ for v in restart_map().values():
538+ _services = _services + v
539+ return list(set(_services))
540
541=== added symlink 'hooks/nrpe-external-master-relation-changed'
542=== target is u'heat_relations.py'
543=== added symlink 'hooks/nrpe-external-master-relation-joined'
544=== target is u'heat_relations.py'
545=== modified file 'metadata.yaml'
546--- metadata.yaml 2014-04-16 08:19:26 +0000
547+++ metadata.yaml 2014-11-18 01:14:05 +0000
548@@ -5,6 +5,10 @@
549 Heat is the main project in the OpenStack Orchestration program. It implements an
550 orchestration engine to launch multiple composite cloud applications based on
551 templates in the form of text files that can be treated like code.
552+provides:
553+ nrpe-external-master:
554+ interface: nrpe-external-master
555+ scope: container
556 categories:
557 - openstack
558 requires:

Subscribers

People subscribed via source and target branches