Merge lp:~brad-marshall/charms/trusty/ceph-osd/add-nrpe-checks into lp:~openstack-charmers-archive/charms/trusty/ceph-osd/trunk
- Trusty Tahr (14.04)
- add-nrpe-checks
- Merge into trunk
Proposed by
Brad Marshall
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 34 | ||||
Proposed branch: | lp:~brad-marshall/charms/trusty/ceph-osd/add-nrpe-checks | ||||
Merge into: | lp:~openstack-charmers-archive/charms/trusty/ceph-osd/trunk | ||||
Diff against target: |
566 lines (+488/-0) 8 files modified
charm-helpers-hooks.yaml (+1/-0) config.yaml (+11/-0) files/nagios/check_ceph_status.py (+44/-0) files/nagios/collect_ceph_status.sh (+18/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+222/-0) hooks/charmhelpers/contrib/charmsupport/volumes.py (+156/-0) hooks/hooks.py (+32/-0) metadata.yaml (+4/-0) |
||||
To merge this branch: | bzr merge lp:~brad-marshall/charms/trusty/ceph-osd/add-nrpe-checks | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Disapprove | ||
Review via email: mp+241496@code.launchpad.net |
Commit message
Description of the change
Adds nrpe-external-
To post a comment you must log in.
- 35. By Brad Marshall
-
[bradm] Fixes from pep8 run
- 36. By Brad Marshall
-
[bradm] Removed nagios check files that were moved to nrpe-external-
master charm
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'charm-helpers-hooks.yaml' | |||
2 | --- charm-helpers-hooks.yaml 2014-09-27 02:28:51 +0000 | |||
3 | +++ charm-helpers-hooks.yaml 2014-11-18 01:06:55 +0000 | |||
4 | @@ -7,3 +7,4 @@ | |||
5 | 7 | - utils | 7 | - utils |
6 | 8 | - contrib.openstack.alternatives | 8 | - contrib.openstack.alternatives |
7 | 9 | - contrib.network.ip | 9 | - contrib.network.ip |
8 | 10 | - contrib.charmsupport | ||
9 | 10 | 11 | ||
10 | === modified file 'config.yaml' | |||
11 | --- config.yaml 2014-10-06 22:11:14 +0000 | |||
12 | +++ config.yaml 2014-11-18 01:06:55 +0000 | |||
13 | @@ -121,3 +121,14 @@ | |||
14 | 121 | order for this charm to function correctly, the privacy extension must be | 121 | order for this charm to function correctly, the privacy extension must be |
15 | 122 | disabled and a non-temporary address must be configured/available on | 122 | disabled and a non-temporary address must be configured/available on |
16 | 123 | your network interface. | 123 | your network interface. |
17 | 124 | nagios_context: | ||
18 | 125 | default: "juju" | ||
19 | 126 | type: string | ||
20 | 127 | description: | | ||
21 | 128 | Used by the nrpe-external-master subordinate charm. | ||
22 | 129 | A string that will be prepended to instance name to set the host name | ||
23 | 130 | in nagios. So for instance the hostname would be something like: | ||
24 | 131 | juju-myservice-0 | ||
25 | 132 | If you're running multiple environments with the same services in them | ||
26 | 133 | this allows you to differentiate between them. | ||
27 | 134 | |||
28 | 124 | 135 | ||
29 | === added directory 'files/nagios' | |||
30 | === added file 'files/nagios/check_ceph_status.py' | |||
31 | --- files/nagios/check_ceph_status.py 1970-01-01 00:00:00 +0000 | |||
32 | +++ files/nagios/check_ceph_status.py 2014-11-18 01:06:55 +0000 | |||
33 | @@ -0,0 +1,44 @@ | |||
34 | 1 | #!/usr/bin/env python | ||
35 | 2 | |||
36 | 3 | # Copyright (C) 2014 Canonical | ||
37 | 4 | # All Rights Reserved | ||
38 | 5 | # Author: Jacek Nykis <jacek.nykis@canonical.com> | ||
39 | 6 | |||
40 | 7 | import re | ||
41 | 8 | import argparse | ||
42 | 9 | import subprocess | ||
43 | 10 | import nagios_plugin | ||
44 | 11 | |||
45 | 12 | |||
46 | 13 | def check_ceph_status(args): | ||
47 | 14 | if args.status_file: | ||
48 | 15 | nagios_plugin.check_file_freshness(args.status_file, 3600) | ||
49 | 16 | with open(args.status_file, "r") as f: | ||
50 | 17 | lines = f.readlines() | ||
51 | 18 | status_data = dict(l.strip().split(' ', 1) for l in lines if len(l) > 1) | ||
52 | 19 | else: | ||
53 | 20 | lines = subprocess.check_output(["ceph", "status"]).split('\n') | ||
54 | 21 | status_data = dict(l.strip().split(' ', 1) for l in lines if len(l) > 1) | ||
55 | 22 | |||
56 | 23 | if ('health' not in status_data | ||
57 | 24 | or 'monmap' not in status_data | ||
58 | 25 | or 'osdmap'not in status_data): | ||
59 | 26 | raise nagios_plugin.UnknownError('UNKNOWN: status data is incomplete') | ||
60 | 27 | |||
61 | 28 | if status_data['health'] != 'HEALTH_OK': | ||
62 | 29 | msg = 'CRITICAL: ceph health status: "{}"'.format(status_data['health']) | ||
63 | 30 | raise nagios_plugin.CriticalError(msg) | ||
64 | 31 | osds = re.search("^.*: (\d+) osds: (\d+) up, (\d+) in", status_data['osdmap']) | ||
65 | 32 | if osds.group(1) > osds.group(2): # not all OSDs are "up" | ||
66 | 33 | msg = 'CRITICAL: Some OSDs are not up. Total: {}, up: {}'.format( | ||
67 | 34 | osds.group(1), osds.group(2)) | ||
68 | 35 | raise nagios_plugin.CriticalError(msg) | ||
69 | 36 | print "All OK" | ||
70 | 37 | |||
71 | 38 | |||
72 | 39 | if __name__ == '__main__': | ||
73 | 40 | parser = argparse.ArgumentParser(description='Check ceph status') | ||
74 | 41 | parser.add_argument('-f', '--file', dest='status_file', | ||
75 | 42 | default=False, help='Optional file with "ceph status" output') | ||
76 | 43 | args = parser.parse_args() | ||
77 | 44 | nagios_plugin.try_check(check_ceph_status, args) | ||
78 | 0 | 45 | ||
79 | === added file 'files/nagios/collect_ceph_status.sh' | |||
80 | --- files/nagios/collect_ceph_status.sh 1970-01-01 00:00:00 +0000 | |||
81 | +++ files/nagios/collect_ceph_status.sh 2014-11-18 01:06:55 +0000 | |||
82 | @@ -0,0 +1,18 @@ | |||
83 | 1 | #!/bin/bash | ||
84 | 2 | # Copyright (C) 2014 Canonical | ||
85 | 3 | # All Rights Reserved | ||
86 | 4 | # Author: Jacek Nykis <jacek.nykis@canonical.com> | ||
87 | 5 | |||
88 | 6 | LOCK=/var/lock/ceph-status.lock | ||
89 | 7 | lockfile-create -r2 --lock-name $LOCK > /dev/null 2>&1 | ||
90 | 8 | if [ $? -ne 0 ]; then | ||
91 | 9 | exit 1 | ||
92 | 10 | fi | ||
93 | 11 | trap "rm -f $LOCK > /dev/null 2>&1" exit | ||
94 | 12 | |||
95 | 13 | DATA_DIR="/var/lib/nagios" | ||
96 | 14 | if [ ! -d $DATA_DIR ]; then | ||
97 | 15 | mkdir -p $DATA_DIR | ||
98 | 16 | fi | ||
99 | 17 | |||
100 | 18 | ceph status >${DATA_DIR}/cat-ceph-status.txt | ||
101 | 0 | 19 | ||
102 | === added directory 'hooks/charmhelpers/contrib/charmsupport' | |||
103 | === added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py' | |||
104 | === added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
105 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 | |||
106 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2014-11-18 01:06:55 +0000 | |||
107 | @@ -0,0 +1,222 @@ | |||
108 | 1 | """Compatibility with the nrpe-external-master charm""" | ||
109 | 2 | # Copyright 2012 Canonical Ltd. | ||
110 | 3 | # | ||
111 | 4 | # Authors: | ||
112 | 5 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
113 | 6 | |||
114 | 7 | import subprocess | ||
115 | 8 | import pwd | ||
116 | 9 | import grp | ||
117 | 10 | import os | ||
118 | 11 | import re | ||
119 | 12 | import shlex | ||
120 | 13 | import yaml | ||
121 | 14 | |||
122 | 15 | from charmhelpers.core.hookenv import ( | ||
123 | 16 | config, | ||
124 | 17 | local_unit, | ||
125 | 18 | log, | ||
126 | 19 | relation_ids, | ||
127 | 20 | relation_set, | ||
128 | 21 | ) | ||
129 | 22 | |||
130 | 23 | from charmhelpers.core.host import service | ||
131 | 24 | |||
132 | 25 | # This module adds compatibility with the nrpe-external-master and plain nrpe | ||
133 | 26 | # subordinate charms. To use it in your charm: | ||
134 | 27 | # | ||
135 | 28 | # 1. Update metadata.yaml | ||
136 | 29 | # | ||
137 | 30 | # provides: | ||
138 | 31 | # (...) | ||
139 | 32 | # nrpe-external-master: | ||
140 | 33 | # interface: nrpe-external-master | ||
141 | 34 | # scope: container | ||
142 | 35 | # | ||
143 | 36 | # and/or | ||
144 | 37 | # | ||
145 | 38 | # provides: | ||
146 | 39 | # (...) | ||
147 | 40 | # local-monitors: | ||
148 | 41 | # interface: local-monitors | ||
149 | 42 | # scope: container | ||
150 | 43 | |||
151 | 44 | # | ||
152 | 45 | # 2. Add the following to config.yaml | ||
153 | 46 | # | ||
154 | 47 | # nagios_context: | ||
155 | 48 | # default: "juju" | ||
156 | 49 | # type: string | ||
157 | 50 | # description: | | ||
158 | 51 | # Used by the nrpe subordinate charms. | ||
159 | 52 | # A string that will be prepended to instance name to set the host name | ||
160 | 53 | # in nagios. So for instance the hostname would be something like: | ||
161 | 54 | # juju-myservice-0 | ||
162 | 55 | # If you're running multiple environments with the same services in them | ||
163 | 56 | # this allows you to differentiate between them. | ||
164 | 57 | # | ||
165 | 58 | # 3. Add custom checks (Nagios plugins) to files/nrpe-external-master | ||
166 | 59 | # | ||
167 | 60 | # 4. Update your hooks.py with something like this: | ||
168 | 61 | # | ||
169 | 62 | # from charmsupport.nrpe import NRPE | ||
170 | 63 | # (...) | ||
171 | 64 | # def update_nrpe_config(): | ||
172 | 65 | # nrpe_compat = NRPE() | ||
173 | 66 | # nrpe_compat.add_check( | ||
174 | 67 | # shortname = "myservice", | ||
175 | 68 | # description = "Check MyService", | ||
176 | 69 | # check_cmd = "check_http -w 2 -c 10 http://localhost" | ||
177 | 70 | # ) | ||
178 | 71 | # nrpe_compat.add_check( | ||
179 | 72 | # "myservice_other", | ||
180 | 73 | # "Check for widget failures", | ||
181 | 74 | # check_cmd = "/srv/myapp/scripts/widget_check" | ||
182 | 75 | # ) | ||
183 | 76 | # nrpe_compat.write() | ||
184 | 77 | # | ||
185 | 78 | # def config_changed(): | ||
186 | 79 | # (...) | ||
187 | 80 | # update_nrpe_config() | ||
188 | 81 | # | ||
189 | 82 | # def nrpe_external_master_relation_changed(): | ||
190 | 83 | # update_nrpe_config() | ||
191 | 84 | # | ||
192 | 85 | # def local_monitors_relation_changed(): | ||
193 | 86 | # update_nrpe_config() | ||
194 | 87 | # | ||
195 | 88 | # 5. ln -s hooks.py nrpe-external-master-relation-changed | ||
196 | 89 | # ln -s hooks.py local-monitors-relation-changed | ||
197 | 90 | |||
198 | 91 | |||
199 | 92 | class CheckException(Exception): | ||
200 | 93 | pass | ||
201 | 94 | |||
202 | 95 | |||
203 | 96 | class Check(object): | ||
204 | 97 | shortname_re = '[A-Za-z0-9-_]+$' | ||
205 | 98 | service_template = (""" | ||
206 | 99 | #--------------------------------------------------- | ||
207 | 100 | # This file is Juju managed | ||
208 | 101 | #--------------------------------------------------- | ||
209 | 102 | define service {{ | ||
210 | 103 | use active-service | ||
211 | 104 | host_name {nagios_hostname} | ||
212 | 105 | service_description {nagios_hostname}[{shortname}] """ | ||
213 | 106 | """{description} | ||
214 | 107 | check_command check_nrpe!{command} | ||
215 | 108 | servicegroups {nagios_servicegroup} | ||
216 | 109 | }} | ||
217 | 110 | """) | ||
218 | 111 | |||
219 | 112 | def __init__(self, shortname, description, check_cmd): | ||
220 | 113 | super(Check, self).__init__() | ||
221 | 114 | # XXX: could be better to calculate this from the service name | ||
222 | 115 | if not re.match(self.shortname_re, shortname): | ||
223 | 116 | raise CheckException("shortname must match {}".format( | ||
224 | 117 | Check.shortname_re)) | ||
225 | 118 | self.shortname = shortname | ||
226 | 119 | self.command = "check_{}".format(shortname) | ||
227 | 120 | # Note: a set of invalid characters is defined by the | ||
228 | 121 | # Nagios server config | ||
229 | 122 | # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= | ||
230 | 123 | self.description = description | ||
231 | 124 | self.check_cmd = self._locate_cmd(check_cmd) | ||
232 | 125 | |||
233 | 126 | def _locate_cmd(self, check_cmd): | ||
234 | 127 | search_path = ( | ||
235 | 128 | '/', | ||
236 | 129 | os.path.join(os.environ['CHARM_DIR'], | ||
237 | 130 | 'files/nrpe-external-master'), | ||
238 | 131 | '/usr/lib/nagios/plugins', | ||
239 | 132 | '/usr/local/lib/nagios/plugins', | ||
240 | 133 | ) | ||
241 | 134 | parts = shlex.split(check_cmd) | ||
242 | 135 | for path in search_path: | ||
243 | 136 | if os.path.exists(os.path.join(path, parts[0])): | ||
244 | 137 | command = os.path.join(path, parts[0]) | ||
245 | 138 | if len(parts) > 1: | ||
246 | 139 | command += " " + " ".join(parts[1:]) | ||
247 | 140 | return command | ||
248 | 141 | log('Check command not found: {}'.format(parts[0])) | ||
249 | 142 | return '' | ||
250 | 143 | |||
251 | 144 | def write(self, nagios_context, hostname): | ||
252 | 145 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | ||
253 | 146 | self.command) | ||
254 | 147 | with open(nrpe_check_file, 'w') as nrpe_check_config: | ||
255 | 148 | nrpe_check_config.write("# check {}\n".format(self.shortname)) | ||
256 | 149 | nrpe_check_config.write("command[{}]={}\n".format( | ||
257 | 150 | self.command, self.check_cmd)) | ||
258 | 151 | |||
259 | 152 | if not os.path.exists(NRPE.nagios_exportdir): | ||
260 | 153 | log('Not writing service config as {} is not accessible'.format( | ||
261 | 154 | NRPE.nagios_exportdir)) | ||
262 | 155 | else: | ||
263 | 156 | self.write_service_config(nagios_context, hostname) | ||
264 | 157 | |||
265 | 158 | def write_service_config(self, nagios_context, hostname): | ||
266 | 159 | for f in os.listdir(NRPE.nagios_exportdir): | ||
267 | 160 | if re.search('.*{}.cfg'.format(self.command), f): | ||
268 | 161 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | ||
269 | 162 | |||
270 | 163 | templ_vars = { | ||
271 | 164 | 'nagios_hostname': hostname, | ||
272 | 165 | 'nagios_servicegroup': nagios_context, | ||
273 | 166 | 'description': self.description, | ||
274 | 167 | 'shortname': self.shortname, | ||
275 | 168 | 'command': self.command, | ||
276 | 169 | } | ||
277 | 170 | nrpe_service_text = Check.service_template.format(**templ_vars) | ||
278 | 171 | nrpe_service_file = '{}/service__{}_{}.cfg'.format( | ||
279 | 172 | NRPE.nagios_exportdir, hostname, self.command) | ||
280 | 173 | with open(nrpe_service_file, 'w') as nrpe_service_config: | ||
281 | 174 | nrpe_service_config.write(str(nrpe_service_text)) | ||
282 | 175 | |||
283 | 176 | def run(self): | ||
284 | 177 | subprocess.call(self.check_cmd) | ||
285 | 178 | |||
286 | 179 | |||
287 | 180 | class NRPE(object): | ||
288 | 181 | nagios_logdir = '/var/log/nagios' | ||
289 | 182 | nagios_exportdir = '/var/lib/nagios/export' | ||
290 | 183 | nrpe_confdir = '/etc/nagios/nrpe.d' | ||
291 | 184 | |||
292 | 185 | def __init__(self, hostname=None): | ||
293 | 186 | super(NRPE, self).__init__() | ||
294 | 187 | self.config = config() | ||
295 | 188 | self.nagios_context = self.config['nagios_context'] | ||
296 | 189 | self.unit_name = local_unit().replace('/', '-') | ||
297 | 190 | if hostname: | ||
298 | 191 | self.hostname = hostname | ||
299 | 192 | else: | ||
300 | 193 | self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) | ||
301 | 194 | self.checks = [] | ||
302 | 195 | |||
303 | 196 | def add_check(self, *args, **kwargs): | ||
304 | 197 | self.checks.append(Check(*args, **kwargs)) | ||
305 | 198 | |||
306 | 199 | def write(self): | ||
307 | 200 | try: | ||
308 | 201 | nagios_uid = pwd.getpwnam('nagios').pw_uid | ||
309 | 202 | nagios_gid = grp.getgrnam('nagios').gr_gid | ||
310 | 203 | except: | ||
311 | 204 | log("Nagios user not set up, nrpe checks not updated") | ||
312 | 205 | return | ||
313 | 206 | |||
314 | 207 | if not os.path.exists(NRPE.nagios_logdir): | ||
315 | 208 | os.mkdir(NRPE.nagios_logdir) | ||
316 | 209 | os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) | ||
317 | 210 | |||
318 | 211 | nrpe_monitors = {} | ||
319 | 212 | monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}} | ||
320 | 213 | for nrpecheck in self.checks: | ||
321 | 214 | nrpecheck.write(self.nagios_context, self.hostname) | ||
322 | 215 | nrpe_monitors[nrpecheck.shortname] = { | ||
323 | 216 | "command": nrpecheck.command, | ||
324 | 217 | } | ||
325 | 218 | |||
326 | 219 | service('restart', 'nagios-nrpe-server') | ||
327 | 220 | |||
328 | 221 | for rid in relation_ids("local-monitors"): | ||
329 | 222 | relation_set(relation_id=rid, monitors=yaml.dump(monitors)) | ||
330 | 0 | 223 | ||
331 | === added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py' | |||
332 | --- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000 | |||
333 | +++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2014-11-18 01:06:55 +0000 | |||
334 | @@ -0,0 +1,156 @@ | |||
335 | 1 | ''' | ||
336 | 2 | Functions for managing volumes in juju units. One volume is supported per unit. | ||
337 | 3 | Subordinates may have their own storage, provided it is on its own partition. | ||
338 | 4 | |||
339 | 5 | Configuration stanzas: | ||
340 | 6 | volume-ephemeral: | ||
341 | 7 | type: boolean | ||
342 | 8 | default: true | ||
343 | 9 | description: > | ||
344 | 10 | If false, a volume is mounted as sepecified in "volume-map" | ||
345 | 11 | If true, ephemeral storage will be used, meaning that log data | ||
346 | 12 | will only exist as long as the machine. YOU HAVE BEEN WARNED. | ||
347 | 13 | volume-map: | ||
348 | 14 | type: string | ||
349 | 15 | default: {} | ||
350 | 16 | description: > | ||
351 | 17 | YAML map of units to device names, e.g: | ||
352 | 18 | "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }" | ||
353 | 19 | Service units will raise a configure-error if volume-ephemeral | ||
354 | 20 | is 'true' and no volume-map value is set. Use 'juju set' to set a | ||
355 | 21 | value and 'juju resolved' to complete configuration. | ||
356 | 22 | |||
357 | 23 | Usage: | ||
358 | 24 | from charmsupport.volumes import configure_volume, VolumeConfigurationError | ||
359 | 25 | from charmsupport.hookenv import log, ERROR | ||
360 | 26 | def post_mount_hook(): | ||
361 | 27 | stop_service('myservice') | ||
362 | 28 | def post_mount_hook(): | ||
363 | 29 | start_service('myservice') | ||
364 | 30 | |||
365 | 31 | if __name__ == '__main__': | ||
366 | 32 | try: | ||
367 | 33 | configure_volume(before_change=pre_mount_hook, | ||
368 | 34 | after_change=post_mount_hook) | ||
369 | 35 | except VolumeConfigurationError: | ||
370 | 36 | log('Storage could not be configured', ERROR) | ||
371 | 37 | ''' | ||
372 | 38 | |||
373 | 39 | # XXX: Known limitations | ||
374 | 40 | # - fstab is neither consulted nor updated | ||
375 | 41 | |||
376 | 42 | import os | ||
377 | 43 | from charmhelpers.core import hookenv | ||
378 | 44 | from charmhelpers.core import host | ||
379 | 45 | import yaml | ||
380 | 46 | |||
381 | 47 | |||
382 | 48 | MOUNT_BASE = '/srv/juju/volumes' | ||
383 | 49 | |||
384 | 50 | |||
385 | 51 | class VolumeConfigurationError(Exception): | ||
386 | 52 | '''Volume configuration data is missing or invalid''' | ||
387 | 53 | pass | ||
388 | 54 | |||
389 | 55 | |||
390 | 56 | def get_config(): | ||
391 | 57 | '''Gather and sanity-check volume configuration data''' | ||
392 | 58 | volume_config = {} | ||
393 | 59 | config = hookenv.config() | ||
394 | 60 | |||
395 | 61 | errors = False | ||
396 | 62 | |||
397 | 63 | if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'): | ||
398 | 64 | volume_config['ephemeral'] = True | ||
399 | 65 | else: | ||
400 | 66 | volume_config['ephemeral'] = False | ||
401 | 67 | |||
402 | 68 | try: | ||
403 | 69 | volume_map = yaml.safe_load(config.get('volume-map', '{}')) | ||
404 | 70 | except yaml.YAMLError as e: | ||
405 | 71 | hookenv.log("Error parsing YAML volume-map: {}".format(e), | ||
406 | 72 | hookenv.ERROR) | ||
407 | 73 | errors = True | ||
408 | 74 | if volume_map is None: | ||
409 | 75 | # probably an empty string | ||
410 | 76 | volume_map = {} | ||
411 | 77 | elif not isinstance(volume_map, dict): | ||
412 | 78 | hookenv.log("Volume-map should be a dictionary, not {}".format( | ||
413 | 79 | type(volume_map))) | ||
414 | 80 | errors = True | ||
415 | 81 | |||
416 | 82 | volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME']) | ||
417 | 83 | if volume_config['device'] and volume_config['ephemeral']: | ||
418 | 84 | # asked for ephemeral storage but also defined a volume ID | ||
419 | 85 | hookenv.log('A volume is defined for this unit, but ephemeral ' | ||
420 | 86 | 'storage was requested', hookenv.ERROR) | ||
421 | 87 | errors = True | ||
422 | 88 | elif not volume_config['device'] and not volume_config['ephemeral']: | ||
423 | 89 | # asked for permanent storage but did not define volume ID | ||
424 | 90 | hookenv.log('Ephemeral storage was requested, but there is no volume ' | ||
425 | 91 | 'defined for this unit.', hookenv.ERROR) | ||
426 | 92 | errors = True | ||
427 | 93 | |||
428 | 94 | unit_mount_name = hookenv.local_unit().replace('/', '-') | ||
429 | 95 | volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name) | ||
430 | 96 | |||
431 | 97 | if errors: | ||
432 | 98 | return None | ||
433 | 99 | return volume_config | ||
434 | 100 | |||
435 | 101 | |||
436 | 102 | def mount_volume(config): | ||
437 | 103 | if os.path.exists(config['mountpoint']): | ||
438 | 104 | if not os.path.isdir(config['mountpoint']): | ||
439 | 105 | hookenv.log('Not a directory: {}'.format(config['mountpoint'])) | ||
440 | 106 | raise VolumeConfigurationError() | ||
441 | 107 | else: | ||
442 | 108 | host.mkdir(config['mountpoint']) | ||
443 | 109 | if os.path.ismount(config['mountpoint']): | ||
444 | 110 | unmount_volume(config) | ||
445 | 111 | if not host.mount(config['device'], config['mountpoint'], persist=True): | ||
446 | 112 | raise VolumeConfigurationError() | ||
447 | 113 | |||
448 | 114 | |||
449 | 115 | def unmount_volume(config): | ||
450 | 116 | if os.path.ismount(config['mountpoint']): | ||
451 | 117 | if not host.umount(config['mountpoint'], persist=True): | ||
452 | 118 | raise VolumeConfigurationError() | ||
453 | 119 | |||
454 | 120 | |||
455 | 121 | def managed_mounts(): | ||
456 | 122 | '''List of all mounted managed volumes''' | ||
457 | 123 | return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts()) | ||
458 | 124 | |||
459 | 125 | |||
460 | 126 | def configure_volume(before_change=lambda: None, after_change=lambda: None): | ||
461 | 127 | '''Set up storage (or don't) according to the charm's volume configuration. | ||
462 | 128 | Returns the mount point or "ephemeral". before_change and after_change | ||
463 | 129 | are optional functions to be called if the volume configuration changes. | ||
464 | 130 | ''' | ||
465 | 131 | |||
466 | 132 | config = get_config() | ||
467 | 133 | if not config: | ||
468 | 134 | hookenv.log('Failed to read volume configuration', hookenv.CRITICAL) | ||
469 | 135 | raise VolumeConfigurationError() | ||
470 | 136 | |||
471 | 137 | if config['ephemeral']: | ||
472 | 138 | if os.path.ismount(config['mountpoint']): | ||
473 | 139 | before_change() | ||
474 | 140 | unmount_volume(config) | ||
475 | 141 | after_change() | ||
476 | 142 | return 'ephemeral' | ||
477 | 143 | else: | ||
478 | 144 | # persistent storage | ||
479 | 145 | if os.path.ismount(config['mountpoint']): | ||
480 | 146 | mounts = dict(managed_mounts()) | ||
481 | 147 | if mounts.get(config['mountpoint']) != config['device']: | ||
482 | 148 | before_change() | ||
483 | 149 | unmount_volume(config) | ||
484 | 150 | mount_volume(config) | ||
485 | 151 | after_change() | ||
486 | 152 | else: | ||
487 | 153 | before_change() | ||
488 | 154 | mount_volume(config) | ||
489 | 155 | after_change() | ||
490 | 156 | return config['mountpoint'] | ||
491 | 0 | 157 | ||
492 | === modified file 'hooks/hooks.py' | |||
493 | --- hooks/hooks.py 2014-09-30 03:41:06 +0000 | |||
494 | +++ hooks/hooks.py 2014-11-18 01:06:55 +0000 | |||
495 | @@ -20,6 +20,8 @@ | |||
496 | 20 | relation_ids, | 20 | relation_ids, |
497 | 21 | related_units, | 21 | related_units, |
498 | 22 | relation_get, | 22 | relation_get, |
499 | 23 | relations_of_type, | ||
500 | 24 | local_unit, | ||
501 | 23 | Hooks, | 25 | Hooks, |
502 | 24 | UnregisteredHookError, | 26 | UnregisteredHookError, |
503 | 25 | service_name | 27 | service_name |
504 | @@ -48,6 +50,8 @@ | |||
505 | 48 | format_ipv6_addr | 50 | format_ipv6_addr |
506 | 49 | ) | 51 | ) |
507 | 50 | 52 | ||
508 | 53 | from charmhelpers.contrib.charmsupport.nrpe import NRPE | ||
509 | 54 | |||
510 | 51 | hooks = Hooks() | 55 | hooks = Hooks() |
511 | 52 | 56 | ||
512 | 53 | 57 | ||
513 | @@ -203,6 +207,34 @@ | |||
514 | 203 | fatal=True) | 207 | fatal=True) |
515 | 204 | 208 | ||
516 | 205 | 209 | ||
517 | 210 | @hooks.hook('nrpe-external-master-relation-joined', | ||
518 | 211 | 'nrpe-external-master-relation-changed') | ||
519 | 212 | def update_nrpe_config(): | ||
520 | 213 | # Find out if nrpe set nagios_hostname | ||
521 | 214 | hostname = None | ||
522 | 215 | host_context = None | ||
523 | 216 | for rel in relations_of_type('nrpe-external-master'): | ||
524 | 217 | if 'nagios_hostname' in rel: | ||
525 | 218 | hostname = rel['nagios_hostname'] | ||
526 | 219 | host_context = rel['nagios_host_context'] | ||
527 | 220 | break | ||
528 | 221 | nrpe = NRPE(hostname=hostname) | ||
529 | 222 | apt_install('python-dbus') | ||
530 | 223 | |||
531 | 224 | if host_context: | ||
532 | 225 | current_unit = "%s:%s" % (host_context, local_unit()) | ||
533 | 226 | else: | ||
534 | 227 | current_unit = local_unit() | ||
535 | 228 | |||
536 | 229 | nrpe.add_check( | ||
537 | 230 | shortname='ceph-osd', | ||
538 | 231 | description='process check {%s}' % current_unit, | ||
539 | 232 | check_cmd='check_upstart_job ceph-osd', | ||
540 | 233 | ) | ||
541 | 234 | |||
542 | 235 | nrpe.write() | ||
543 | 236 | |||
544 | 237 | |||
545 | 206 | if __name__ == '__main__': | 238 | if __name__ == '__main__': |
546 | 207 | try: | 239 | try: |
547 | 208 | hooks.execute(sys.argv) | 240 | hooks.execute(sys.argv) |
548 | 209 | 241 | ||
549 | === added symlink 'hooks/nrpe-external-master-relation-changed' | |||
550 | === target is u'hooks.py' | |||
551 | === added symlink 'hooks/nrpe-external-master-relation-joined' | |||
552 | === target is u'hooks.py' | |||
553 | === modified file 'metadata.yaml' | |||
554 | --- metadata.yaml 2014-10-06 22:11:14 +0000 | |||
555 | +++ metadata.yaml 2014-11-18 01:06:55 +0000 | |||
556 | @@ -1,6 +1,10 @@ | |||
557 | 1 | name: ceph-osd | 1 | name: ceph-osd |
558 | 2 | summary: Highly scalable distributed storage - Ceph OSD storage | 2 | summary: Highly scalable distributed storage - Ceph OSD storage |
559 | 3 | maintainer: James Page <james.page@ubuntu.com> | 3 | maintainer: James Page <james.page@ubuntu.com> |
560 | 4 | provides: | ||
561 | 5 | nrpe-external-master: | ||
562 | 6 | interface: nrpe-external-master | ||
563 | 7 | scope: container | ||
564 | 4 | categories: | 8 | categories: |
565 | 5 | - misc | 9 | - misc |
566 | 6 | description: | | 10 | description: | |
Thank for the mp. The new nrpe support is very gratefully received !
I've taken this branch and centralised the common code between this and the other nrpe branches and moved it to charm-helpers. To land it I created a new branch from this one which has now been merged into the 'next' charm. The 'next' charms will overwrite the stable ones in a couple of weeks.