Merge lp:~gnuoy/charms/trusty/ceph/add-nrpe-checks into lp:~openstack-charmers-archive/charms/trusty/ceph/next
- Trusty Tahr (14.04)
- add-nrpe-checks
- Merge into next
Proposed by
Liam Young
Status: | Merged |
---|---|
Merged at revision: | 93 |
Proposed branch: | lp:~gnuoy/charms/trusty/ceph/add-nrpe-checks |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/ceph/next |
Diff against target: |
830 lines (+689/-6) 12 files modified
charm-helpers-hooks.yaml (+1/-0) config.yaml (+9/-0) files/nagios/check_ceph_status.py (+44/-0) files/nagios/collect_ceph_status.sh (+18/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+308/-0) hooks/charmhelpers/contrib/charmsupport/volumes.py (+159/-0) hooks/charmhelpers/contrib/storage/linux/ceph.py (+43/-0) hooks/charmhelpers/core/decorators.py (+41/-0) hooks/charmhelpers/core/host.py (+7/-4) hooks/charmhelpers/fetch/__init__.py (+8/-1) hooks/hooks.py (+44/-1) metadata.yaml (+7/-0) |
To merge this branch: | bzr merge lp:~gnuoy/charms/trusty/ceph/add-nrpe-checks |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Approve | ||
Review via email:
|
Commit message
Description of the change
Add nrpe support. Based on branch from bradm with a few tweaks
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
uosci-testing-bot (uosci-testing-bot) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #676 ceph-next for gnuoy mp246155
LINT OK: passed
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #861 ceph-next for gnuoy mp246155
AMULET OK: passed
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Liam Young (gnuoy) wrote : | # |
<jamespage> gnuoy, as they are re-syncs + tweaks to the nrpe stuff in the charms, I'm happy to give a conditional +1 across the board based on osci checking things out OK
<gnuoy> jamespage, I'll take that! thanks
...
<gnuoy> jamespage, osci is still working through. But on the subject of those mps, does your +1 stand for branches with no amulet tests?
<jamespage> gnuoy, yes
review:
Approve
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-12-11 16:46:45 +0000 | |||
3 | +++ charm-helpers-hooks.yaml 2015-01-12 14:00:31 +0000 | |||
4 | @@ -9,3 +9,4 @@ | |||
5 | 9 | - payload.execd | 9 | - payload.execd |
6 | 10 | - contrib.openstack.alternatives | 10 | - contrib.openstack.alternatives |
7 | 11 | - contrib.network.ip | 11 | - contrib.network.ip |
8 | 12 | - contrib.charmsupport | ||
9 | 12 | 13 | ||
10 | === modified file 'config.yaml' | |||
11 | --- config.yaml 2014-11-25 18:29:07 +0000 | |||
12 | +++ config.yaml 2015-01-12 14:00:31 +0000 | |||
13 | @@ -161,3 +161,12 @@ | |||
14 | 161 | description: | | 161 | description: | |
15 | 162 | YAML-formatted associative array of sysctl key/value pairs to be set | 162 | YAML-formatted associative array of sysctl key/value pairs to be set |
16 | 163 | persistently e.g. '{ kernel.pid_max : 4194303 }'. | 163 | persistently e.g. '{ kernel.pid_max : 4194303 }'. |
17 | 164 | nagios_context: | ||
18 | 165 | default: "juju" | ||
19 | 166 | description: | | ||
20 | 167 | Used by the nrpe-external-master subordinate charm. | ||
21 | 168 | A string that will be prepended to instance name to set the host name | ||
22 | 169 | in nagios. So for instance the hostname would be something like: | ||
23 | 170 | juju-myservice-0 | ||
24 | 171 | If you're running multiple environments with the same services in them | ||
25 | 172 | this allows you to differentiate between them. | ||
26 | 164 | 173 | ||
27 | === added directory 'files/nagios' | |||
28 | === added file 'files/nagios/check_ceph_status.py' | |||
29 | --- files/nagios/check_ceph_status.py 1970-01-01 00:00:00 +0000 | |||
30 | +++ files/nagios/check_ceph_status.py 2015-01-12 14:00:31 +0000 | |||
31 | @@ -0,0 +1,44 @@ | |||
32 | 1 | #!/usr/bin/env python | ||
33 | 2 | |||
34 | 3 | # Copyright (C) 2014 Canonical | ||
35 | 4 | # All Rights Reserved | ||
36 | 5 | # Author: Jacek Nykis <jacek.nykis@canonical.com> | ||
37 | 6 | |||
38 | 7 | import re | ||
39 | 8 | import argparse | ||
40 | 9 | import subprocess | ||
41 | 10 | import nagios_plugin | ||
42 | 11 | |||
43 | 12 | |||
44 | 13 | def check_ceph_status(args): | ||
45 | 14 | if args.status_file: | ||
46 | 15 | nagios_plugin.check_file_freshness(args.status_file, 3600) | ||
47 | 16 | with open(args.status_file, "r") as f: | ||
48 | 17 | lines = f.readlines() | ||
49 | 18 | status_data = dict(l.strip().split(' ', 1) for l in lines if len(l) > 1) | ||
50 | 19 | else: | ||
51 | 20 | lines = subprocess.check_output(["ceph", "status"]).split('\n') | ||
52 | 21 | status_data = dict(l.strip().split(' ', 1) for l in lines if len(l) > 1) | ||
53 | 22 | |||
54 | 23 | if ('health' not in status_data | ||
55 | 24 | or 'monmap' not in status_data | ||
56 | 25 | or 'osdmap'not in status_data): | ||
57 | 26 | raise nagios_plugin.UnknownError('UNKNOWN: status data is incomplete') | ||
58 | 27 | |||
59 | 28 | if status_data['health'] != 'HEALTH_OK': | ||
60 | 29 | msg = 'CRITICAL: ceph health status: "{}"'.format(status_data['health']) | ||
61 | 30 | raise nagios_plugin.CriticalError(msg) | ||
62 | 31 | osds = re.search("^.*: (\d+) osds: (\d+) up, (\d+) in", status_data['osdmap']) | ||
63 | 32 | if osds.group(1) > osds.group(2): # not all OSDs are "up" | ||
64 | 33 | msg = 'CRITICAL: Some OSDs are not up. Total: {}, up: {}'.format( | ||
65 | 34 | osds.group(1), osds.group(2)) | ||
66 | 35 | raise nagios_plugin.CriticalError(msg) | ||
67 | 36 | print "All OK" | ||
68 | 37 | |||
69 | 38 | |||
70 | 39 | if __name__ == '__main__': | ||
71 | 40 | parser = argparse.ArgumentParser(description='Check ceph status') | ||
72 | 41 | parser.add_argument('-f', '--file', dest='status_file', | ||
73 | 42 | default=False, help='Optional file with "ceph status" output') | ||
74 | 43 | args = parser.parse_args() | ||
75 | 44 | nagios_plugin.try_check(check_ceph_status, args) | ||
76 | 0 | 45 | ||
77 | === added file 'files/nagios/collect_ceph_status.sh' | |||
78 | --- files/nagios/collect_ceph_status.sh 1970-01-01 00:00:00 +0000 | |||
79 | +++ files/nagios/collect_ceph_status.sh 2015-01-12 14:00:31 +0000 | |||
80 | @@ -0,0 +1,18 @@ | |||
81 | 1 | #!/bin/bash | ||
82 | 2 | # Copyright (C) 2014 Canonical | ||
83 | 3 | # All Rights Reserved | ||
84 | 4 | # Author: Jacek Nykis <jacek.nykis@canonical.com> | ||
85 | 5 | |||
86 | 6 | LOCK=/var/lock/ceph-status.lock | ||
87 | 7 | lockfile-create -r2 --lock-name $LOCK > /dev/null 2>&1 | ||
88 | 8 | if [ $? -ne 0 ]; then | ||
89 | 9 | exit 1 | ||
90 | 10 | fi | ||
91 | 11 | trap "rm -f $LOCK > /dev/null 2>&1" exit | ||
92 | 12 | |||
93 | 13 | DATA_DIR="/var/lib/nagios" | ||
94 | 14 | if [ ! -d $DATA_DIR ]; then | ||
95 | 15 | mkdir -p $DATA_DIR | ||
96 | 16 | fi | ||
97 | 17 | |||
98 | 18 | ceph status >${DATA_DIR}/cat-ceph-status.txt | ||
99 | 0 | 19 | ||
100 | === added directory 'hooks/charmhelpers/contrib/charmsupport' | |||
101 | === added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py' | |||
102 | === added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
103 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 | |||
104 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-01-12 14:00:31 +0000 | |||
105 | @@ -0,0 +1,308 @@ | |||
106 | 1 | """Compatibility with the nrpe-external-master charm""" | ||
107 | 2 | # Copyright 2012 Canonical Ltd. | ||
108 | 3 | # | ||
109 | 4 | # Authors: | ||
110 | 5 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
111 | 6 | |||
112 | 7 | import subprocess | ||
113 | 8 | import pwd | ||
114 | 9 | import grp | ||
115 | 10 | import os | ||
116 | 11 | import re | ||
117 | 12 | import shlex | ||
118 | 13 | import yaml | ||
119 | 14 | |||
120 | 15 | from charmhelpers.core.hookenv import ( | ||
121 | 16 | config, | ||
122 | 17 | local_unit, | ||
123 | 18 | log, | ||
124 | 19 | relation_ids, | ||
125 | 20 | relation_set, | ||
126 | 21 | relations_of_type, | ||
127 | 22 | ) | ||
128 | 23 | |||
129 | 24 | from charmhelpers.core.host import service | ||
130 | 25 | |||
131 | 26 | # This module adds compatibility with the nrpe-external-master and plain nrpe | ||
132 | 27 | # subordinate charms. To use it in your charm: | ||
133 | 28 | # | ||
134 | 29 | # 1. Update metadata.yaml | ||
135 | 30 | # | ||
136 | 31 | # provides: | ||
137 | 32 | # (...) | ||
138 | 33 | # nrpe-external-master: | ||
139 | 34 | # interface: nrpe-external-master | ||
140 | 35 | # scope: container | ||
141 | 36 | # | ||
142 | 37 | # and/or | ||
143 | 38 | # | ||
144 | 39 | # provides: | ||
145 | 40 | # (...) | ||
146 | 41 | # local-monitors: | ||
147 | 42 | # interface: local-monitors | ||
148 | 43 | # scope: container | ||
149 | 44 | |||
150 | 45 | # | ||
151 | 46 | # 2. Add the following to config.yaml | ||
152 | 47 | # | ||
153 | 48 | # nagios_context: | ||
154 | 49 | # default: "juju" | ||
155 | 50 | # type: string | ||
156 | 51 | # description: | | ||
157 | 52 | # Used by the nrpe subordinate charms. | ||
158 | 53 | # A string that will be prepended to instance name to set the host name | ||
159 | 54 | # in nagios. So for instance the hostname would be something like: | ||
160 | 55 | # juju-myservice-0 | ||
161 | 56 | # If you're running multiple environments with the same services in them | ||
162 | 57 | # this allows you to differentiate between them. | ||
163 | 58 | # nagios_servicegroups: | ||
164 | 59 | # default: "" | ||
165 | 60 | # type: string | ||
166 | 61 | # description: | | ||
167 | 62 | # A comma-separated list of nagios servicegroups. | ||
168 | 63 | # If left empty, the nagios_context will be used as the servicegroup | ||
169 | 64 | # | ||
170 | 65 | # 3. Add custom checks (Nagios plugins) to files/nrpe-external-master | ||
171 | 66 | # | ||
172 | 67 | # 4. Update your hooks.py with something like this: | ||
173 | 68 | # | ||
174 | 69 | # from charmsupport.nrpe import NRPE | ||
175 | 70 | # (...) | ||
176 | 71 | # def update_nrpe_config(): | ||
177 | 72 | # nrpe_compat = NRPE() | ||
178 | 73 | # nrpe_compat.add_check( | ||
179 | 74 | # shortname = "myservice", | ||
180 | 75 | # description = "Check MyService", | ||
181 | 76 | # check_cmd = "check_http -w 2 -c 10 http://localhost" | ||
182 | 77 | # ) | ||
183 | 78 | # nrpe_compat.add_check( | ||
184 | 79 | # "myservice_other", | ||
185 | 80 | # "Check for widget failures", | ||
186 | 81 | # check_cmd = "/srv/myapp/scripts/widget_check" | ||
187 | 82 | # ) | ||
188 | 83 | # nrpe_compat.write() | ||
189 | 84 | # | ||
190 | 85 | # def config_changed(): | ||
191 | 86 | # (...) | ||
192 | 87 | # update_nrpe_config() | ||
193 | 88 | # | ||
194 | 89 | # def nrpe_external_master_relation_changed(): | ||
195 | 90 | # update_nrpe_config() | ||
196 | 91 | # | ||
197 | 92 | # def local_monitors_relation_changed(): | ||
198 | 93 | # update_nrpe_config() | ||
199 | 94 | # | ||
200 | 95 | # 5. ln -s hooks.py nrpe-external-master-relation-changed | ||
201 | 96 | # ln -s hooks.py local-monitors-relation-changed | ||
202 | 97 | |||
203 | 98 | |||
204 | 99 | class CheckException(Exception): | ||
205 | 100 | pass | ||
206 | 101 | |||
207 | 102 | |||
208 | 103 | class Check(object): | ||
209 | 104 | shortname_re = '[A-Za-z0-9-_]+$' | ||
210 | 105 | service_template = (""" | ||
211 | 106 | #--------------------------------------------------- | ||
212 | 107 | # This file is Juju managed | ||
213 | 108 | #--------------------------------------------------- | ||
214 | 109 | define service {{ | ||
215 | 110 | use active-service | ||
216 | 111 | host_name {nagios_hostname} | ||
217 | 112 | service_description {nagios_hostname}[{shortname}] """ | ||
218 | 113 | """{description} | ||
219 | 114 | check_command check_nrpe!{command} | ||
220 | 115 | servicegroups {nagios_servicegroup} | ||
221 | 116 | }} | ||
222 | 117 | """) | ||
223 | 118 | |||
224 | 119 | def __init__(self, shortname, description, check_cmd): | ||
225 | 120 | super(Check, self).__init__() | ||
226 | 121 | # XXX: could be better to calculate this from the service name | ||
227 | 122 | if not re.match(self.shortname_re, shortname): | ||
228 | 123 | raise CheckException("shortname must match {}".format( | ||
229 | 124 | Check.shortname_re)) | ||
230 | 125 | self.shortname = shortname | ||
231 | 126 | self.command = "check_{}".format(shortname) | ||
232 | 127 | # Note: a set of invalid characters is defined by the | ||
233 | 128 | # Nagios server config | ||
234 | 129 | # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= | ||
235 | 130 | self.description = description | ||
236 | 131 | self.check_cmd = self._locate_cmd(check_cmd) | ||
237 | 132 | |||
238 | 133 | def _locate_cmd(self, check_cmd): | ||
239 | 134 | search_path = ( | ||
240 | 135 | '/usr/lib/nagios/plugins', | ||
241 | 136 | '/usr/local/lib/nagios/plugins', | ||
242 | 137 | ) | ||
243 | 138 | parts = shlex.split(check_cmd) | ||
244 | 139 | for path in search_path: | ||
245 | 140 | if os.path.exists(os.path.join(path, parts[0])): | ||
246 | 141 | command = os.path.join(path, parts[0]) | ||
247 | 142 | if len(parts) > 1: | ||
248 | 143 | command += " " + " ".join(parts[1:]) | ||
249 | 144 | return command | ||
250 | 145 | log('Check command not found: {}'.format(parts[0])) | ||
251 | 146 | return '' | ||
252 | 147 | |||
253 | 148 | def write(self, nagios_context, hostname, nagios_servicegroups=None): | ||
254 | 149 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | ||
255 | 150 | self.command) | ||
256 | 151 | with open(nrpe_check_file, 'w') as nrpe_check_config: | ||
257 | 152 | nrpe_check_config.write("# check {}\n".format(self.shortname)) | ||
258 | 153 | nrpe_check_config.write("command[{}]={}\n".format( | ||
259 | 154 | self.command, self.check_cmd)) | ||
260 | 155 | |||
261 | 156 | if not os.path.exists(NRPE.nagios_exportdir): | ||
262 | 157 | log('Not writing service config as {} is not accessible'.format( | ||
263 | 158 | NRPE.nagios_exportdir)) | ||
264 | 159 | else: | ||
265 | 160 | self.write_service_config(nagios_context, hostname, | ||
266 | 161 | nagios_servicegroups) | ||
267 | 162 | |||
268 | 163 | def write_service_config(self, nagios_context, hostname, | ||
269 | 164 | nagios_servicegroups=None): | ||
270 | 165 | for f in os.listdir(NRPE.nagios_exportdir): | ||
271 | 166 | if re.search('.*{}.cfg'.format(self.command), f): | ||
272 | 167 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | ||
273 | 168 | |||
274 | 169 | if not nagios_servicegroups: | ||
275 | 170 | nagios_servicegroups = nagios_context | ||
276 | 171 | |||
277 | 172 | templ_vars = { | ||
278 | 173 | 'nagios_hostname': hostname, | ||
279 | 174 | 'nagios_servicegroup': nagios_servicegroups, | ||
280 | 175 | 'description': self.description, | ||
281 | 176 | 'shortname': self.shortname, | ||
282 | 177 | 'command': self.command, | ||
283 | 178 | } | ||
284 | 179 | nrpe_service_text = Check.service_template.format(**templ_vars) | ||
285 | 180 | nrpe_service_file = '{}/service__{}_{}.cfg'.format( | ||
286 | 181 | NRPE.nagios_exportdir, hostname, self.command) | ||
287 | 182 | with open(nrpe_service_file, 'w') as nrpe_service_config: | ||
288 | 183 | nrpe_service_config.write(str(nrpe_service_text)) | ||
289 | 184 | |||
290 | 185 | def run(self): | ||
291 | 186 | subprocess.call(self.check_cmd) | ||
292 | 187 | |||
293 | 188 | |||
294 | 189 | class NRPE(object): | ||
295 | 190 | nagios_logdir = '/var/log/nagios' | ||
296 | 191 | nagios_exportdir = '/var/lib/nagios/export' | ||
297 | 192 | nrpe_confdir = '/etc/nagios/nrpe.d' | ||
298 | 193 | |||
299 | 194 | def __init__(self, hostname=None): | ||
300 | 195 | super(NRPE, self).__init__() | ||
301 | 196 | self.config = config() | ||
302 | 197 | self.nagios_context = self.config['nagios_context'] | ||
303 | 198 | if 'nagios_servicegroups' in self.config: | ||
304 | 199 | self.nagios_servicegroups = self.config['nagios_servicegroups'] | ||
305 | 200 | else: | ||
306 | 201 | self.nagios_servicegroups = 'juju' | ||
307 | 202 | self.unit_name = local_unit().replace('/', '-') | ||
308 | 203 | if hostname: | ||
309 | 204 | self.hostname = hostname | ||
310 | 205 | else: | ||
311 | 206 | self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) | ||
312 | 207 | self.checks = [] | ||
313 | 208 | |||
314 | 209 | def add_check(self, *args, **kwargs): | ||
315 | 210 | self.checks.append(Check(*args, **kwargs)) | ||
316 | 211 | |||
317 | 212 | def write(self): | ||
318 | 213 | try: | ||
319 | 214 | nagios_uid = pwd.getpwnam('nagios').pw_uid | ||
320 | 215 | nagios_gid = grp.getgrnam('nagios').gr_gid | ||
321 | 216 | except: | ||
322 | 217 | log("Nagios user not set up, nrpe checks not updated") | ||
323 | 218 | return | ||
324 | 219 | |||
325 | 220 | if not os.path.exists(NRPE.nagios_logdir): | ||
326 | 221 | os.mkdir(NRPE.nagios_logdir) | ||
327 | 222 | os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) | ||
328 | 223 | |||
329 | 224 | nrpe_monitors = {} | ||
330 | 225 | monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}} | ||
331 | 226 | for nrpecheck in self.checks: | ||
332 | 227 | nrpecheck.write(self.nagios_context, self.hostname, | ||
333 | 228 | self.nagios_servicegroups) | ||
334 | 229 | nrpe_monitors[nrpecheck.shortname] = { | ||
335 | 230 | "command": nrpecheck.command, | ||
336 | 231 | } | ||
337 | 232 | |||
338 | 233 | service('restart', 'nagios-nrpe-server') | ||
339 | 234 | |||
340 | 235 | for rid in relation_ids("local-monitors"): | ||
341 | 236 | relation_set(relation_id=rid, monitors=yaml.dump(monitors)) | ||
342 | 237 | |||
343 | 238 | |||
344 | 239 | def get_nagios_hostcontext(relation_name='nrpe-external-master'): | ||
345 | 240 | """ | ||
346 | 241 | Query relation with nrpe subordinate, return the nagios_host_context | ||
347 | 242 | |||
348 | 243 | :param str relation_name: Name of relation nrpe sub joined to | ||
349 | 244 | """ | ||
350 | 245 | for rel in relations_of_type(relation_name): | ||
351 | 246 | if 'nagios_hostname' in rel: | ||
352 | 247 | return rel['nagios_host_context'] | ||
353 | 248 | |||
354 | 249 | |||
355 | 250 | def get_nagios_hostname(relation_name='nrpe-external-master'): | ||
356 | 251 | """ | ||
357 | 252 | Query relation with nrpe subordinate, return the nagios_hostname | ||
358 | 253 | |||
359 | 254 | :param str relation_name: Name of relation nrpe sub joined to | ||
360 | 255 | """ | ||
361 | 256 | for rel in relations_of_type(relation_name): | ||
362 | 257 | if 'nagios_hostname' in rel: | ||
363 | 258 | return rel['nagios_hostname'] | ||
364 | 259 | |||
365 | 260 | |||
366 | 261 | def get_nagios_unit_name(relation_name='nrpe-external-master'): | ||
367 | 262 | """ | ||
368 | 263 | Return the nagios unit name prepended with host_context if needed | ||
369 | 264 | |||
370 | 265 | :param str relation_name: Name of relation nrpe sub joined to | ||
371 | 266 | """ | ||
372 | 267 | host_context = get_nagios_hostcontext(relation_name) | ||
373 | 268 | if host_context: | ||
374 | 269 | unit = "%s:%s" % (host_context, local_unit()) | ||
375 | 270 | else: | ||
376 | 271 | unit = local_unit() | ||
377 | 272 | return unit | ||
378 | 273 | |||
379 | 274 | |||
380 | 275 | def add_init_service_checks(nrpe, services, unit_name): | ||
381 | 276 | """ | ||
382 | 277 | Add checks for each service in list | ||
383 | 278 | |||
384 | 279 | :param NRPE nrpe: NRPE object to add check to | ||
385 | 280 | :param list services: List of services to check | ||
386 | 281 | :param str unit_name: Unit name to use in check description | ||
387 | 282 | """ | ||
388 | 283 | for svc in services: | ||
389 | 284 | upstart_init = '/etc/init/%s.conf' % svc | ||
390 | 285 | sysv_init = '/etc/init.d/%s' % svc | ||
391 | 286 | if os.path.exists(upstart_init): | ||
392 | 287 | nrpe.add_check( | ||
393 | 288 | shortname=svc, | ||
394 | 289 | description='process check {%s}' % unit_name, | ||
395 | 290 | check_cmd='check_upstart_job %s' % svc | ||
396 | 291 | ) | ||
397 | 292 | elif os.path.exists(sysv_init): | ||
398 | 293 | cronpath = '/etc/cron.d/nagios-service-check-%s' % svc | ||
399 | 294 | cron_file = ('*/5 * * * * root ' | ||
400 | 295 | '/usr/local/lib/nagios/plugins/check_exit_status.pl ' | ||
401 | 296 | '-s /etc/init.d/%s status > ' | ||
402 | 297 | '/var/lib/nagios/service-check-%s.txt\n' % (svc, | ||
403 | 298 | svc) | ||
404 | 299 | ) | ||
405 | 300 | f = open(cronpath, 'w') | ||
406 | 301 | f.write(cron_file) | ||
407 | 302 | f.close() | ||
408 | 303 | nrpe.add_check( | ||
409 | 304 | shortname=svc, | ||
410 | 305 | description='process check {%s}' % unit_name, | ||
411 | 306 | check_cmd='check_status_file.py -f ' | ||
412 | 307 | '/var/lib/nagios/service-check-%s.txt' % svc, | ||
413 | 308 | ) | ||
414 | 0 | 309 | ||
415 | === added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py' | |||
416 | --- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000 | |||
417 | +++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2015-01-12 14:00:31 +0000 | |||
418 | @@ -0,0 +1,159 @@ | |||
419 | 1 | ''' | ||
420 | 2 | Functions for managing volumes in juju units. One volume is supported per unit. | ||
421 | 3 | Subordinates may have their own storage, provided it is on its own partition. | ||
422 | 4 | |||
423 | 5 | Configuration stanzas:: | ||
424 | 6 | |||
425 | 7 | volume-ephemeral: | ||
426 | 8 | type: boolean | ||
427 | 9 | default: true | ||
428 | 10 | description: > | ||
429 | 11 | If false, a volume is mounted as sepecified in "volume-map" | ||
430 | 12 | If true, ephemeral storage will be used, meaning that log data | ||
431 | 13 | will only exist as long as the machine. YOU HAVE BEEN WARNED. | ||
432 | 14 | volume-map: | ||
433 | 15 | type: string | ||
434 | 16 | default: {} | ||
435 | 17 | description: > | ||
436 | 18 | YAML map of units to device names, e.g: | ||
437 | 19 | "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }" | ||
438 | 20 | Service units will raise a configure-error if volume-ephemeral | ||
439 | 21 | is 'true' and no volume-map value is set. Use 'juju set' to set a | ||
440 | 22 | value and 'juju resolved' to complete configuration. | ||
441 | 23 | |||
442 | 24 | Usage:: | ||
443 | 25 | |||
444 | 26 | from charmsupport.volumes import configure_volume, VolumeConfigurationError | ||
445 | 27 | from charmsupport.hookenv import log, ERROR | ||
446 | 28 | def post_mount_hook(): | ||
447 | 29 | stop_service('myservice') | ||
448 | 30 | def post_mount_hook(): | ||
449 | 31 | start_service('myservice') | ||
450 | 32 | |||
451 | 33 | if __name__ == '__main__': | ||
452 | 34 | try: | ||
453 | 35 | configure_volume(before_change=pre_mount_hook, | ||
454 | 36 | after_change=post_mount_hook) | ||
455 | 37 | except VolumeConfigurationError: | ||
456 | 38 | log('Storage could not be configured', ERROR) | ||
457 | 39 | |||
458 | 40 | ''' | ||
459 | 41 | |||
460 | 42 | # XXX: Known limitations | ||
461 | 43 | # - fstab is neither consulted nor updated | ||
462 | 44 | |||
463 | 45 | import os | ||
464 | 46 | from charmhelpers.core import hookenv | ||
465 | 47 | from charmhelpers.core import host | ||
466 | 48 | import yaml | ||
467 | 49 | |||
468 | 50 | |||
469 | 51 | MOUNT_BASE = '/srv/juju/volumes' | ||
470 | 52 | |||
471 | 53 | |||
472 | 54 | class VolumeConfigurationError(Exception): | ||
473 | 55 | '''Volume configuration data is missing or invalid''' | ||
474 | 56 | pass | ||
475 | 57 | |||
476 | 58 | |||
477 | 59 | def get_config(): | ||
478 | 60 | '''Gather and sanity-check volume configuration data''' | ||
479 | 61 | volume_config = {} | ||
480 | 62 | config = hookenv.config() | ||
481 | 63 | |||
482 | 64 | errors = False | ||
483 | 65 | |||
484 | 66 | if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'): | ||
485 | 67 | volume_config['ephemeral'] = True | ||
486 | 68 | else: | ||
487 | 69 | volume_config['ephemeral'] = False | ||
488 | 70 | |||
489 | 71 | try: | ||
490 | 72 | volume_map = yaml.safe_load(config.get('volume-map', '{}')) | ||
491 | 73 | except yaml.YAMLError as e: | ||
492 | 74 | hookenv.log("Error parsing YAML volume-map: {}".format(e), | ||
493 | 75 | hookenv.ERROR) | ||
494 | 76 | errors = True | ||
495 | 77 | if volume_map is None: | ||
496 | 78 | # probably an empty string | ||
497 | 79 | volume_map = {} | ||
498 | 80 | elif not isinstance(volume_map, dict): | ||
499 | 81 | hookenv.log("Volume-map should be a dictionary, not {}".format( | ||
500 | 82 | type(volume_map))) | ||
501 | 83 | errors = True | ||
502 | 84 | |||
503 | 85 | volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME']) | ||
504 | 86 | if volume_config['device'] and volume_config['ephemeral']: | ||
505 | 87 | # asked for ephemeral storage but also defined a volume ID | ||
506 | 88 | hookenv.log('A volume is defined for this unit, but ephemeral ' | ||
507 | 89 | 'storage was requested', hookenv.ERROR) | ||
508 | 90 | errors = True | ||
509 | 91 | elif not volume_config['device'] and not volume_config['ephemeral']: | ||
510 | 92 | # asked for permanent storage but did not define volume ID | ||
511 | 93 | hookenv.log('Ephemeral storage was requested, but there is no volume ' | ||
512 | 94 | 'defined for this unit.', hookenv.ERROR) | ||
513 | 95 | errors = True | ||
514 | 96 | |||
515 | 97 | unit_mount_name = hookenv.local_unit().replace('/', '-') | ||
516 | 98 | volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name) | ||
517 | 99 | |||
518 | 100 | if errors: | ||
519 | 101 | return None | ||
520 | 102 | return volume_config | ||
521 | 103 | |||
522 | 104 | |||
523 | 105 | def mount_volume(config): | ||
524 | 106 | if os.path.exists(config['mountpoint']): | ||
525 | 107 | if not os.path.isdir(config['mountpoint']): | ||
526 | 108 | hookenv.log('Not a directory: {}'.format(config['mountpoint'])) | ||
527 | 109 | raise VolumeConfigurationError() | ||
528 | 110 | else: | ||
529 | 111 | host.mkdir(config['mountpoint']) | ||
530 | 112 | if os.path.ismount(config['mountpoint']): | ||
531 | 113 | unmount_volume(config) | ||
532 | 114 | if not host.mount(config['device'], config['mountpoint'], persist=True): | ||
533 | 115 | raise VolumeConfigurationError() | ||
534 | 116 | |||
535 | 117 | |||
536 | 118 | def unmount_volume(config): | ||
537 | 119 | if os.path.ismount(config['mountpoint']): | ||
538 | 120 | if not host.umount(config['mountpoint'], persist=True): | ||
539 | 121 | raise VolumeConfigurationError() | ||
540 | 122 | |||
541 | 123 | |||
542 | 124 | def managed_mounts(): | ||
543 | 125 | '''List of all mounted managed volumes''' | ||
544 | 126 | return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts()) | ||
545 | 127 | |||
546 | 128 | |||
547 | 129 | def configure_volume(before_change=lambda: None, after_change=lambda: None): | ||
548 | 130 | '''Set up storage (or don't) according to the charm's volume configuration. | ||
549 | 131 | Returns the mount point or "ephemeral". before_change and after_change | ||
550 | 132 | are optional functions to be called if the volume configuration changes. | ||
551 | 133 | ''' | ||
552 | 134 | |||
553 | 135 | config = get_config() | ||
554 | 136 | if not config: | ||
555 | 137 | hookenv.log('Failed to read volume configuration', hookenv.CRITICAL) | ||
556 | 138 | raise VolumeConfigurationError() | ||
557 | 139 | |||
558 | 140 | if config['ephemeral']: | ||
559 | 141 | if os.path.ismount(config['mountpoint']): | ||
560 | 142 | before_change() | ||
561 | 143 | unmount_volume(config) | ||
562 | 144 | after_change() | ||
563 | 145 | return 'ephemeral' | ||
564 | 146 | else: | ||
565 | 147 | # persistent storage | ||
566 | 148 | if os.path.ismount(config['mountpoint']): | ||
567 | 149 | mounts = dict(managed_mounts()) | ||
568 | 150 | if mounts.get(config['mountpoint']) != config['device']: | ||
569 | 151 | before_change() | ||
570 | 152 | unmount_volume(config) | ||
571 | 153 | mount_volume(config) | ||
572 | 154 | after_change() | ||
573 | 155 | else: | ||
574 | 156 | before_change() | ||
575 | 157 | mount_volume(config) | ||
576 | 158 | after_change() | ||
577 | 159 | return config['mountpoint'] | ||
578 | 0 | 160 | ||
579 | === modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py' | |||
580 | --- hooks/charmhelpers/contrib/storage/linux/ceph.py 2014-11-26 09:07:27 +0000 | |||
581 | +++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2015-01-12 14:00:31 +0000 | |||
582 | @@ -372,3 +372,46 @@ | |||
583 | 372 | return None | 372 | return None |
584 | 373 | else: | 373 | else: |
585 | 374 | return None | 374 | return None |
586 | 375 | |||
587 | 376 | |||
588 | 377 | class CephBrokerRq(object): | ||
589 | 378 | """Ceph broker request. | ||
590 | 379 | |||
591 | 380 | Multiple operations can be added to a request and sent to the Ceph broker | ||
592 | 381 | to be executed. | ||
593 | 382 | |||
594 | 383 | Request is json-encoded for sending over the wire. | ||
595 | 384 | |||
596 | 385 | The API is versioned and defaults to version 1. | ||
597 | 386 | """ | ||
598 | 387 | def __init__(self, api_version=1): | ||
599 | 388 | self.api_version = api_version | ||
600 | 389 | self.ops = [] | ||
601 | 390 | |||
602 | 391 | def add_op_create_pool(self, name, replica_count=3): | ||
603 | 392 | self.ops.append({'op': 'create-pool', 'name': name, | ||
604 | 393 | 'replicas': replica_count}) | ||
605 | 394 | |||
606 | 395 | @property | ||
607 | 396 | def request(self): | ||
608 | 397 | return json.dumps({'api-version': self.api_version, 'ops': self.ops}) | ||
609 | 398 | |||
610 | 399 | |||
611 | 400 | class CephBrokerRsp(object): | ||
612 | 401 | """Ceph broker response. | ||
613 | 402 | |||
614 | 403 | Response is json-decoded and contents provided as methods/properties. | ||
615 | 404 | |||
616 | 405 | The API is versioned and defaults to version 1. | ||
617 | 406 | """ | ||
618 | 407 | def __init__(self, encoded_rsp): | ||
619 | 408 | self.api_version = None | ||
620 | 409 | self.rsp = json.loads(encoded_rsp) | ||
621 | 410 | |||
622 | 411 | @property | ||
623 | 412 | def exit_code(self): | ||
624 | 413 | return self.rsp.get('exit-code') | ||
625 | 414 | |||
626 | 415 | @property | ||
627 | 416 | def exit_msg(self): | ||
628 | 417 | return self.rsp.get('stderr') | ||
629 | 375 | 418 | ||
630 | === added file 'hooks/charmhelpers/core/decorators.py' | |||
631 | --- hooks/charmhelpers/core/decorators.py 1970-01-01 00:00:00 +0000 | |||
632 | +++ hooks/charmhelpers/core/decorators.py 2015-01-12 14:00:31 +0000 | |||
633 | @@ -0,0 +1,41 @@ | |||
634 | 1 | # | ||
635 | 2 | # Copyright 2014 Canonical Ltd. | ||
636 | 3 | # | ||
637 | 4 | # Authors: | ||
638 | 5 | # Edward Hope-Morley <opentastic@gmail.com> | ||
639 | 6 | # | ||
640 | 7 | |||
641 | 8 | import time | ||
642 | 9 | |||
643 | 10 | from charmhelpers.core.hookenv import ( | ||
644 | 11 | log, | ||
645 | 12 | INFO, | ||
646 | 13 | ) | ||
647 | 14 | |||
648 | 15 | |||
649 | 16 | def retry_on_exception(num_retries, base_delay=0, exc_type=Exception): | ||
650 | 17 | """If the decorated function raises exception exc_type, allow num_retries | ||
651 | 18 | retry attempts before raise the exception. | ||
652 | 19 | """ | ||
653 | 20 | def _retry_on_exception_inner_1(f): | ||
654 | 21 | def _retry_on_exception_inner_2(*args, **kwargs): | ||
655 | 22 | retries = num_retries | ||
656 | 23 | multiplier = 1 | ||
657 | 24 | while True: | ||
658 | 25 | try: | ||
659 | 26 | return f(*args, **kwargs) | ||
660 | 27 | except exc_type: | ||
661 | 28 | if not retries: | ||
662 | 29 | raise | ||
663 | 30 | |||
664 | 31 | delay = base_delay * multiplier | ||
665 | 32 | multiplier += 1 | ||
666 | 33 | log("Retrying '%s' %d more times (delay=%s)" % | ||
667 | 34 | (f.__name__, retries, delay), level=INFO) | ||
668 | 35 | retries -= 1 | ||
669 | 36 | if delay: | ||
670 | 37 | time.sleep(delay) | ||
671 | 38 | |||
672 | 39 | return _retry_on_exception_inner_2 | ||
673 | 40 | |||
674 | 41 | return _retry_on_exception_inner_1 | ||
675 | 0 | 42 | ||
676 | === modified file 'hooks/charmhelpers/core/host.py' | |||
677 | --- hooks/charmhelpers/core/host.py 2014-12-11 16:47:24 +0000 | |||
678 | +++ hooks/charmhelpers/core/host.py 2015-01-12 14:00:31 +0000 | |||
679 | @@ -162,13 +162,16 @@ | |||
680 | 162 | uid = pwd.getpwnam(owner).pw_uid | 162 | uid = pwd.getpwnam(owner).pw_uid |
681 | 163 | gid = grp.getgrnam(group).gr_gid | 163 | gid = grp.getgrnam(group).gr_gid |
682 | 164 | realpath = os.path.abspath(path) | 164 | realpath = os.path.abspath(path) |
685 | 165 | if os.path.exists(realpath): | 165 | path_exists = os.path.exists(realpath) |
686 | 166 | if force and not os.path.isdir(realpath): | 166 | if path_exists and force: |
687 | 167 | if not os.path.isdir(realpath): | ||
688 | 167 | log("Removing non-directory file {} prior to mkdir()".format(path)) | 168 | log("Removing non-directory file {} prior to mkdir()".format(path)) |
689 | 168 | os.unlink(realpath) | 169 | os.unlink(realpath) |
691 | 169 | else: | 170 | os.makedirs(realpath, perms) |
692 | 171 | os.chown(realpath, uid, gid) | ||
693 | 172 | elif not path_exists: | ||
694 | 170 | os.makedirs(realpath, perms) | 173 | os.makedirs(realpath, perms) |
696 | 171 | os.chown(realpath, uid, gid) | 174 | os.chown(realpath, uid, gid) |
697 | 172 | 175 | ||
698 | 173 | 176 | ||
699 | 174 | def write_file(path, content, owner='root', group='root', perms=0o444): | 177 | def write_file(path, content, owner='root', group='root', perms=0o444): |
700 | 175 | 178 | ||
701 | === modified file 'hooks/charmhelpers/fetch/__init__.py' | |||
702 | --- hooks/charmhelpers/fetch/__init__.py 2014-11-25 17:07:46 +0000 | |||
703 | +++ hooks/charmhelpers/fetch/__init__.py 2015-01-12 14:00:31 +0000 | |||
704 | @@ -64,9 +64,16 @@ | |||
705 | 64 | 'trusty-juno/updates': 'trusty-updates/juno', | 64 | 'trusty-juno/updates': 'trusty-updates/juno', |
706 | 65 | 'trusty-updates/juno': 'trusty-updates/juno', | 65 | 'trusty-updates/juno': 'trusty-updates/juno', |
707 | 66 | 'juno/proposed': 'trusty-proposed/juno', | 66 | 'juno/proposed': 'trusty-proposed/juno', |
708 | 67 | 'juno/proposed': 'trusty-proposed/juno', | ||
709 | 68 | 'trusty-juno/proposed': 'trusty-proposed/juno', | 67 | 'trusty-juno/proposed': 'trusty-proposed/juno', |
710 | 69 | 'trusty-proposed/juno': 'trusty-proposed/juno', | 68 | 'trusty-proposed/juno': 'trusty-proposed/juno', |
711 | 69 | # Kilo | ||
712 | 70 | 'kilo': 'trusty-updates/kilo', | ||
713 | 71 | 'trusty-kilo': 'trusty-updates/kilo', | ||
714 | 72 | 'trusty-kilo/updates': 'trusty-updates/kilo', | ||
715 | 73 | 'trusty-updates/kilo': 'trusty-updates/kilo', | ||
716 | 74 | 'kilo/proposed': 'trusty-proposed/kilo', | ||
717 | 75 | 'trusty-kilo/proposed': 'trusty-proposed/kilo', | ||
718 | 76 | 'trusty-proposed/kilo': 'trusty-proposed/kilo', | ||
719 | 70 | } | 77 | } |
720 | 71 | 78 | ||
721 | 72 | # The order of this list is very important. Handlers should be listed in from | 79 | # The order of this list is very important. Handlers should be listed in from |
722 | 73 | 80 | ||
723 | === modified file 'hooks/hooks.py' | |||
724 | --- hooks/hooks.py 2014-11-25 18:29:07 +0000 | |||
725 | +++ hooks/hooks.py 2015-01-12 14:00:31 +0000 | |||
726 | @@ -25,12 +25,15 @@ | |||
727 | 25 | relation_set, | 25 | relation_set, |
728 | 26 | remote_unit, | 26 | remote_unit, |
729 | 27 | Hooks, UnregisteredHookError, | 27 | Hooks, UnregisteredHookError, |
731 | 28 | service_name | 28 | service_name, |
732 | 29 | relations_of_type | ||
733 | 29 | ) | 30 | ) |
734 | 30 | from charmhelpers.core.host import ( | 31 | from charmhelpers.core.host import ( |
735 | 31 | service_restart, | 32 | service_restart, |
736 | 32 | umount, | 33 | umount, |
737 | 33 | mkdir, | 34 | mkdir, |
738 | 35 | write_file, | ||
739 | 36 | rsync, | ||
740 | 34 | cmp_pkgrevno | 37 | cmp_pkgrevno |
741 | 35 | ) | 38 | ) |
742 | 36 | from charmhelpers.fetch import ( | 39 | from charmhelpers.fetch import ( |
743 | @@ -56,8 +59,15 @@ | |||
744 | 56 | process_requests | 59 | process_requests |
745 | 57 | ) | 60 | ) |
746 | 58 | 61 | ||
747 | 62 | from charmhelpers.contrib.charmsupport import nrpe | ||
748 | 63 | |||
749 | 59 | hooks = Hooks() | 64 | hooks = Hooks() |
750 | 60 | 65 | ||
751 | 66 | NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' | ||
752 | 67 | SCRIPTS_DIR = '/usr/local/bin' | ||
753 | 68 | STATUS_FILE = '/var/lib/nagios/cat-ceph-status.txt' | ||
754 | 69 | STATUS_CRONFILE = '/etc/cron.d/cat-ceph-health' | ||
755 | 70 | |||
756 | 61 | 71 | ||
757 | 62 | def install_upstart_scripts(): | 72 | def install_upstart_scripts(): |
758 | 63 | # Only install upstart configurations for older versions | 73 | # Only install upstart configurations for older versions |
759 | @@ -152,6 +162,9 @@ | |||
760 | 152 | reformat_osd(), config('ignore-device-errors')) | 162 | reformat_osd(), config('ignore-device-errors')) |
761 | 153 | ceph.start_osds(get_devices()) | 163 | ceph.start_osds(get_devices()) |
762 | 154 | 164 | ||
763 | 165 | if relations_of_type('nrpe-external-master'): | ||
764 | 166 | update_nrpe_config() | ||
765 | 167 | |||
766 | 155 | 168 | ||
767 | 156 | def get_mon_hosts(): | 169 | def get_mon_hosts(): |
768 | 157 | hosts = [] | 170 | hosts = [] |
769 | @@ -334,6 +347,36 @@ | |||
770 | 334 | ceph.start_osds(get_devices()) | 347 | ceph.start_osds(get_devices()) |
771 | 335 | 348 | ||
772 | 336 | 349 | ||
773 | 350 | @hooks.hook('nrpe-external-master-relation-joined') | ||
774 | 351 | @hooks.hook('nrpe-external-master-relation-changed') | ||
775 | 352 | def update_nrpe_config(): | ||
776 | 353 | # python-dbus is used by check_upstart_job | ||
777 | 354 | apt_install('python-dbus') | ||
778 | 355 | log('Refreshing nagios checks') | ||
779 | 356 | if os.path.isdir(NAGIOS_PLUGINS): | ||
780 | 357 | rsync(os.path.join(os.getenv('CHARM_DIR'), 'files', 'nagios', | ||
781 | 358 | 'check_ceph_status.py'), | ||
782 | 359 | os.path.join(NAGIOS_PLUGINS, 'check_ceph_status.py')) | ||
783 | 360 | |||
784 | 361 | script = os.path.join(SCRIPTS_DIR, 'collect_ceph_status.sh') | ||
785 | 362 | rsync(os.path.join(os.getenv('CHARM_DIR'), 'files', | ||
786 | 363 | 'nagios', 'collect_ceph_status.sh'), | ||
787 | 364 | script) | ||
788 | 365 | cronjob = "{} root {}\n".format('*/5 * * * *', script) | ||
789 | 366 | write_file(STATUS_CRONFILE, cronjob) | ||
790 | 367 | |||
791 | 368 | # Find out if nrpe set nagios_hostname | ||
792 | 369 | hostname = nrpe.get_nagios_hostname() | ||
793 | 370 | current_unit = nrpe.get_nagios_unit_name() | ||
794 | 371 | nrpe_setup = nrpe.NRPE(hostname=hostname) | ||
795 | 372 | nrpe_setup.add_check( | ||
796 | 373 | shortname="ceph", | ||
797 | 374 | description='Check Ceph health {%s}' % current_unit, | ||
798 | 375 | check_cmd='check_ceph_status.py -f {}'.format(STATUS_FILE) | ||
799 | 376 | ) | ||
800 | 377 | nrpe_setup.write() | ||
801 | 378 | |||
802 | 379 | |||
803 | 337 | if __name__ == '__main__': | 380 | if __name__ == '__main__': |
804 | 338 | try: | 381 | try: |
805 | 339 | hooks.execute(sys.argv) | 382 | hooks.execute(sys.argv) |
806 | 340 | 383 | ||
807 | === added symlink 'hooks/nrpe-external-master-relation-changed' | |||
808 | === target is u'hooks.py' | |||
809 | === added symlink 'hooks/nrpe-external-master-relation-joined' | |||
810 | === target is u'hooks.py' | |||
811 | === modified file 'metadata.yaml' | |||
812 | --- metadata.yaml 2013-07-14 19:46:24 +0000 | |||
813 | +++ metadata.yaml 2015-01-12 14:00:31 +0000 | |||
814 | @@ -10,9 +10,16 @@ | |||
815 | 10 | mon: | 10 | mon: |
816 | 11 | interface: ceph | 11 | interface: ceph |
817 | 12 | provides: | 12 | provides: |
818 | 13 | nrpe-external-master: | ||
819 | 14 | interface: nrpe-external-master | ||
820 | 15 | scope: container | ||
821 | 13 | client: | 16 | client: |
822 | 14 | interface: ceph-client | 17 | interface: ceph-client |
823 | 15 | osd: | 18 | osd: |
824 | 16 | interface: ceph-osd | 19 | interface: ceph-osd |
825 | 17 | radosgw: | 20 | radosgw: |
826 | 18 | interface: ceph-radosgw | 21 | interface: ceph-radosgw |
827 | 22 | nrpe-external-master: | ||
828 | 23 | interface: nrpe-external-master | ||
829 | 24 | scope: container | ||
830 | 25 | gets: [nagios_hostname, nagios_host_context] |
charm_unit_test #705 ceph-next for gnuoy mp246155
UNIT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_unit_ test/705/