Merge lp:~abentley/charms/precise/charmworld/nagios into lp:~juju-jitsu/charms/precise/charmworld/trunk
- Precise Pangolin (12.04)
- nagios
- Merge into trunk
Proposed by
Aaron Bentley
Status: | Merged |
---|---|
Merged at revision: | 36 |
Proposed branch: | lp:~abentley/charms/precise/charmworld/nagios |
Merge into: | lp:~juju-jitsu/charms/precise/charmworld/trunk |
Diff against target: |
550 lines (+375/-84) 13 files modified
charmsupport/hookenv.py (+150/-0) charmsupport/nrpe.py (+169/-0) config.yaml (+10/-0) files/nrpe-external-master/check_ingest.sh (+5/-0) hooks/config-changed (+3/-1) hooks/install (+1/-1) hooks/nrpe-external-master-relation-changed (+2/-0) hooks/upgrade-charm (+3/-0) metadata.yaml (+3/-0) revision (+1/-1) run-write-errors (+16/-0) shhh.py (+0/-81) update-nrpe.py (+12/-0) |
To merge this branch: | bzr merge lp:~abentley/charms/precise/charmworld/nagios |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju-Jitsu Hackers | Pending | ||
Review via email: mp+145970@code.launchpad.net |
Commit message
Description of the change
Implement nagios support
Change the way errors are reported so that any errors running ingest cause a
file, ~ubuntu/
used for a nagios check.
The files in charmsupport are copied almost verbatim from lp:charmsupport r27.
(Exception, the change in lp:~pjdc/charmsupport/hookenv-vs-nrpe was applied.)
Ideally we will switch to a packaged version of charmsupport, but not if its
nagios support is broken!
The shhh.py file is deleted because it is no longer needed.
To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'charmsupport' | |||
2 | === added file 'charmsupport/__init__.py' | |||
3 | === added file 'charmsupport/hookenv.py' | |||
4 | --- charmsupport/hookenv.py 1970-01-01 00:00:00 +0000 | |||
5 | +++ charmsupport/hookenv.py 2013-01-31 20:16:33 +0000 | |||
6 | @@ -0,0 +1,150 @@ | |||
7 | 1 | "Interactions with the Juju environment" | ||
8 | 2 | # source: 27:lp:charmsupport | ||
9 | 3 | # Copyright 2012 Canonical Ltd. | ||
10 | 4 | # | ||
11 | 5 | # Authors: | ||
12 | 6 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
13 | 7 | |||
14 | 8 | import os | ||
15 | 9 | import json | ||
16 | 10 | import yaml | ||
17 | 11 | import subprocess | ||
18 | 12 | |||
19 | 13 | CRITICAL = "CRITICAL" | ||
20 | 14 | ERROR = "ERROR" | ||
21 | 15 | WARNING = "WARNING" | ||
22 | 16 | INFO = "INFO" | ||
23 | 17 | DEBUG = "DEBUG" | ||
24 | 18 | def log(message, level=DEBUG): | ||
25 | 19 | "Write a message to the juju log" | ||
26 | 20 | subprocess.call( [ 'juju-log', '-l', level, message ] ) | ||
27 | 21 | |||
28 | 22 | class Serializable(object): | ||
29 | 23 | "Wrapper, an object that can be serialized to yaml or json" | ||
30 | 24 | def __init__(self, obj): | ||
31 | 25 | # wrap the object | ||
32 | 26 | super(Serializable, self).__init__() | ||
33 | 27 | self._wrapped_obj = obj | ||
34 | 28 | |||
35 | 29 | def __getattr__(self, attr): | ||
36 | 30 | # see if this object has attr | ||
37 | 31 | if attr in self.__dict__: | ||
38 | 32 | return getattr(self, attr) | ||
39 | 33 | # proxy to the wrapped object | ||
40 | 34 | return self[attr] | ||
41 | 35 | |||
42 | 36 | def __getitem__(self, key): | ||
43 | 37 | return self._wrapped_obj[key] | ||
44 | 38 | |||
45 | 39 | def json(self): | ||
46 | 40 | "Serialize the object to json" | ||
47 | 41 | return json.dumps(self._wrapped_obj) | ||
48 | 42 | |||
49 | 43 | def yaml(self): | ||
50 | 44 | "Serialize the object to yaml" | ||
51 | 45 | return yaml.dump(self._wrapped_obj) | ||
52 | 46 | |||
53 | 47 | def execution_environment(): | ||
54 | 48 | """A convenient bundling of the current execution context""" | ||
55 | 49 | context = {} | ||
56 | 50 | context['conf'] = config() | ||
57 | 51 | context['unit'] = local_unit() | ||
58 | 52 | context['rel'] = relations_of_type() | ||
59 | 53 | context['env'] = os.environ | ||
60 | 54 | return context | ||
61 | 55 | |||
62 | 56 | def in_relation_hook(): | ||
63 | 57 | "Determine whether we're running in a relation hook" | ||
64 | 58 | return os.environ.has_key('JUJU_RELATION') | ||
65 | 59 | |||
66 | 60 | def relation_type(): | ||
67 | 61 | "The scope for the current relation hook" | ||
68 | 62 | return os.environ['JUJU_RELATION'] | ||
69 | 63 | def relation_id(): | ||
70 | 64 | "The relation ID for the current relation hook" | ||
71 | 65 | return os.environ['JUJU_RELATION_ID'] | ||
72 | 66 | def local_unit(): | ||
73 | 67 | "Local unit ID" | ||
74 | 68 | return os.environ['JUJU_UNIT_NAME'] | ||
75 | 69 | def remote_unit(): | ||
76 | 70 | "The remote unit for the current relation hook" | ||
77 | 71 | return os.environ['JUJU_REMOTE_UNIT'] | ||
78 | 72 | |||
79 | 73 | def config(scope=None): | ||
80 | 74 | "Juju charm configuration" | ||
81 | 75 | config_cmd_line = ['config-get'] | ||
82 | 76 | if scope is not None: | ||
83 | 77 | config_cmd_line.append(scope) | ||
84 | 78 | config_cmd_line.append('--format=json') | ||
85 | 79 | try: | ||
86 | 80 | config_data = json.loads(subprocess.check_output(config_cmd_line)) | ||
87 | 81 | except (ValueError, OSError, subprocess.CalledProcessError) as err: | ||
88 | 82 | log(str(err), level=ERROR) | ||
89 | 83 | raise err | ||
90 | 84 | return Serializable(config_data) | ||
91 | 85 | |||
92 | 86 | def relation_ids(reltype=None): | ||
93 | 87 | "A list of relation_ids" | ||
94 | 88 | reltype = reltype or relation_type() | ||
95 | 89 | relids = [] | ||
96 | 90 | relid_cmd_line = ['relation-ids', '--format=json', reltype] | ||
97 | 91 | relids.extend(json.loads(subprocess.check_output(relid_cmd_line))) | ||
98 | 92 | return relids | ||
99 | 93 | |||
100 | 94 | def related_units(relid=None): | ||
101 | 95 | "A list of related units" | ||
102 | 96 | relid = relid or relation_id() | ||
103 | 97 | units_cmd_line = ['relation-list', '--format=json', '-r', relid] | ||
104 | 98 | units = json.loads(subprocess.check_output(units_cmd_line)) | ||
105 | 99 | return units | ||
106 | 100 | |||
107 | 101 | def relation_for_unit(unit=None): | ||
108 | 102 | "Get the json represenation of a unit's relation" | ||
109 | 103 | unit = unit or remote_unit() | ||
110 | 104 | relation_cmd_line = ['relation-get', '--format=json', '-', unit] | ||
111 | 105 | try: | ||
112 | 106 | relation = json.loads(subprocess.check_output(relation_cmd_line)) | ||
113 | 107 | except (ValueError, OSError, subprocess.CalledProcessError), err: | ||
114 | 108 | log(str(err), level=ERROR) | ||
115 | 109 | raise err | ||
116 | 110 | for key in relation: | ||
117 | 111 | if key.endswith('-list'): | ||
118 | 112 | relation[key] = relation[key].split() | ||
119 | 113 | relation['__unit__'] = unit | ||
120 | 114 | return Serializable(relation) | ||
121 | 115 | |||
122 | 116 | def relations_for_id(relid=None): | ||
123 | 117 | "Get relations of a specific relation ID" | ||
124 | 118 | relation_data = [] | ||
125 | 119 | relid = relid or relation_ids() | ||
126 | 120 | for unit in related_units(relid): | ||
127 | 121 | unit_data = relation_for_unit(unit) | ||
128 | 122 | unit_data['__relid__'] = relid | ||
129 | 123 | relation_data.append(unit_data) | ||
130 | 124 | return relation_data | ||
131 | 125 | |||
132 | 126 | def relations_of_type(reltype=None): | ||
133 | 127 | "Get relations of a specific type" | ||
134 | 128 | relation_data = [] | ||
135 | 129 | if in_relation_hook(): | ||
136 | 130 | reltype = reltype or relation_type() | ||
137 | 131 | for relid in relation_ids(reltype): | ||
138 | 132 | for relation in relations_for_id(relid): | ||
139 | 133 | relation['__relid__'] = relid | ||
140 | 134 | relation_data.append(relation) | ||
141 | 135 | return relation_data | ||
142 | 136 | |||
143 | 137 | class UnregisteredHookError(Exception): pass | ||
144 | 138 | |||
145 | 139 | class Hooks(object): | ||
146 | 140 | def __init__(self): | ||
147 | 141 | super(Hooks, self).__init__() | ||
148 | 142 | self._hooks = {} | ||
149 | 143 | def register(self, name, function): | ||
150 | 144 | self._hooks[name] = function | ||
151 | 145 | def execute(self, args): | ||
152 | 146 | hook_name = os.path.basename(args[0]) | ||
153 | 147 | if hook_name in self._hooks: | ||
154 | 148 | self._hooks[hook_name]() | ||
155 | 149 | else: | ||
156 | 150 | raise UnregisteredHookError(hook_name) | ||
157 | 0 | 151 | ||
158 | === added file 'charmsupport/nrpe.py' | |||
159 | --- charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 | |||
160 | +++ charmsupport/nrpe.py 2013-01-31 20:16:33 +0000 | |||
161 | @@ -0,0 +1,169 @@ | |||
162 | 1 | """Compatibility with the nrpe-external-master charm""" | ||
163 | 2 | # source: 27:lp:charmsupport | ||
164 | 3 | # Copyright 2012 Canonical Ltd. | ||
165 | 4 | # | ||
166 | 5 | # Authors: | ||
167 | 6 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
168 | 7 | |||
169 | 8 | import subprocess | ||
170 | 9 | import pwd | ||
171 | 10 | import grp | ||
172 | 11 | import os | ||
173 | 12 | import re | ||
174 | 13 | import shlex | ||
175 | 14 | |||
176 | 15 | from hookenv import config, local_unit | ||
177 | 16 | |||
178 | 17 | # This module adds compatibility with the nrpe_external_master | ||
179 | 18 | # subordinate charm. To use it in your charm: | ||
180 | 19 | # | ||
181 | 20 | # 1. Update metadata.yaml | ||
182 | 21 | # | ||
183 | 22 | # provides: | ||
184 | 23 | # (...) | ||
185 | 24 | # nrpe-external-master: | ||
186 | 25 | # interface: nrpe-external-master | ||
187 | 26 | # scope: container | ||
188 | 27 | # | ||
189 | 28 | # 2. Add the following to config.yaml | ||
190 | 29 | # | ||
191 | 30 | # nagios_context: | ||
192 | 31 | # default: "juju" | ||
193 | 32 | # type: string | ||
194 | 33 | # description: | | ||
195 | 34 | # Used by the nrpe-external-master subordinate charm. | ||
196 | 35 | # A string that will be prepended to instance name to set the host name | ||
197 | 36 | # in nagios. So for instance the hostname would be something like: | ||
198 | 37 | # juju-myservice-0 | ||
199 | 38 | # If you're running multiple environments with the same services in them | ||
200 | 39 | # this allows you to differentiate between them. | ||
201 | 40 | # | ||
202 | 41 | # 3. Add custom checks (Nagios plugins) to files/nrpe-external-master | ||
203 | 42 | # | ||
204 | 43 | # 4. Update your hooks.py with something like this: | ||
205 | 44 | # | ||
206 | 45 | # import nrpe | ||
207 | 46 | # (...) | ||
208 | 47 | # def update_nrpe_config(): | ||
209 | 48 | # nrpe_compat = NRPE("myservice") | ||
210 | 49 | # nrpe_compat.add_check( | ||
211 | 50 | # shortname = "myservice", | ||
212 | 51 | # description = "Check MyService", | ||
213 | 52 | # check_cmd = "check_http -w 2 -c 10 http://localhost" | ||
214 | 53 | # ) | ||
215 | 54 | # nrpe_compat.add_check( | ||
216 | 55 | # "myservice_other", | ||
217 | 56 | # "Check for widget failures", | ||
218 | 57 | # check_cmd = "/srv/myapp/scripts/widget_check" | ||
219 | 58 | # ) | ||
220 | 59 | # nrpe_compat.write() | ||
221 | 60 | # | ||
222 | 61 | # def config_changed(): | ||
223 | 62 | # (...) | ||
224 | 63 | # update_nrpe_config() | ||
225 | 64 | # def nrpe_external_master_relation_changed(): | ||
226 | 65 | # update_nrpe_config() | ||
227 | 66 | # | ||
228 | 67 | # 5. ln -s hooks.py nrpe-external-master-relation-changed | ||
229 | 68 | |||
230 | 69 | class CheckException(Exception): pass | ||
231 | 70 | class Check(object): | ||
232 | 71 | shortname_re = '[A-Za-z0-9-_]*' | ||
233 | 72 | service_template = """ | ||
234 | 73 | #--------------------------------------------------- | ||
235 | 74 | # This file is Juju managed | ||
236 | 75 | #--------------------------------------------------- | ||
237 | 76 | define service {{ | ||
238 | 77 | use active-service | ||
239 | 78 | host_name {nagios_hostname} | ||
240 | 79 | service_description {nagios_hostname}[{shortname}] {description} | ||
241 | 80 | check_command check_nrpe!check_{shortname} | ||
242 | 81 | servicegroups {nagios_servicegroup} | ||
243 | 82 | }} | ||
244 | 83 | """ | ||
245 | 84 | def __init__(self, shortname, description, check_cmd): | ||
246 | 85 | super(Check, self).__init__() | ||
247 | 86 | # XXX: could be better to calculate this from the service name | ||
248 | 87 | if not re.match(self.shortname_re, shortname): | ||
249 | 88 | raise CheckException("shortname must match {}".format(Check.shortname_re)) | ||
250 | 89 | self.shortname = shortname | ||
251 | 90 | # Note: a set of invalid characters is defined by the Nagios server config | ||
252 | 91 | # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= | ||
253 | 92 | self.description = description | ||
254 | 93 | self.check_cmd = self._locate_cmd(check_cmd) | ||
255 | 94 | |||
256 | 95 | def _locate_cmd(self, check_cmd): | ||
257 | 96 | search_path = ( | ||
258 | 97 | '/', | ||
259 | 98 | os.path.join(os.environ['CHARM_DIR'], 'files/nrpe-external-master'), | ||
260 | 99 | '/usr/lib/nagios/plugins', | ||
261 | 100 | ) | ||
262 | 101 | command = shlex.split(check_cmd) | ||
263 | 102 | for path in search_path: | ||
264 | 103 | if os.path.exists(os.path.join(path,command[0])): | ||
265 | 104 | return os.path.join(path, command[0]) + " " + " ".join(command[1:]) | ||
266 | 105 | subprocess.call(['juju-log', 'Check command not found: {}'.format(command[0])]) | ||
267 | 106 | return '' | ||
268 | 107 | |||
269 | 108 | def write(self, nagios_context, hostname): | ||
270 | 109 | for f in os.listdir(NRPE.nagios_exportdir): | ||
271 | 110 | if re.search('.*check_{}.cfg'.format(self.shortname), f): | ||
272 | 111 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | ||
273 | 112 | |||
274 | 113 | templ_vars = { | ||
275 | 114 | 'nagios_hostname': hostname, | ||
276 | 115 | 'nagios_servicegroup': nagios_context, | ||
277 | 116 | 'description': self.description, | ||
278 | 117 | 'shortname': self.shortname, | ||
279 | 118 | } | ||
280 | 119 | nrpe_service_text = Check.service_template.format(**templ_vars) | ||
281 | 120 | nrpe_service_file = '{}/service__{}_check_{}.cfg'.format( | ||
282 | 121 | NRPE.nagios_exportdir, hostname, self.shortname) | ||
283 | 122 | with open(nrpe_service_file, 'w') as nrpe_service_config: | ||
284 | 123 | nrpe_service_config.write(str(nrpe_service_text)) | ||
285 | 124 | |||
286 | 125 | nrpe_check_file = '/etc/nagios/nrpe.d/check_{}.cfg'.format(self.shortname) | ||
287 | 126 | with open(nrpe_check_file, 'w') as nrpe_check_config: | ||
288 | 127 | nrpe_check_config.write("# check {}\n".format(self.shortname)) | ||
289 | 128 | nrpe_check_config.write("command[check_{}]={}\n".format( | ||
290 | 129 | self.shortname, self.check_cmd)) | ||
291 | 130 | |||
292 | 131 | def run(self): | ||
293 | 132 | subprocess.call(self.check_cmd) | ||
294 | 133 | |||
295 | 134 | class NRPE(object): | ||
296 | 135 | nagios_logdir = '/var/log/nagios' | ||
297 | 136 | nagios_exportdir = '/var/lib/nagios/export' | ||
298 | 137 | nrpe_confdir = '/etc/nagios/nrpe.d' | ||
299 | 138 | def __init__(self): | ||
300 | 139 | super(NRPE, self).__init__() | ||
301 | 140 | self.config = config() | ||
302 | 141 | self.nagios_context = self.config['nagios_context'] | ||
303 | 142 | self.unit_name = local_unit().replace('/', '-') | ||
304 | 143 | self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) | ||
305 | 144 | self.checks = [] | ||
306 | 145 | |||
307 | 146 | def add_check(self, *args, **kwargs): | ||
308 | 147 | self.checks.append( Check(*args, **kwargs) ) | ||
309 | 148 | |||
310 | 149 | def write(self): | ||
311 | 150 | try: | ||
312 | 151 | nagios_uid = pwd.getpwnam('nagios').pw_uid | ||
313 | 152 | nagios_gid = grp.getgrnam('nagios').gr_gid | ||
314 | 153 | except: | ||
315 | 154 | subprocess.call(['juju-log', "Nagios user not set up, nrpe checks not updated"]) | ||
316 | 155 | return | ||
317 | 156 | |||
318 | 157 | if not os.path.exists(NRPE.nagios_exportdir): | ||
319 | 158 | subprocess.call(['juju-log', 'Exiting as {} is not accessible'.format(NRPE.nagios_exportdir)]) | ||
320 | 159 | return | ||
321 | 160 | |||
322 | 161 | if not os.path.exists(NRPE.nagios_logdir): | ||
323 | 162 | os.mkdir(NRPE.nagios_logdir) | ||
324 | 163 | os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) | ||
325 | 164 | |||
326 | 165 | for nrpecheck in self.checks: | ||
327 | 166 | nrpecheck.write(self.nagios_context, self.hostname) | ||
328 | 167 | |||
329 | 168 | if os.path.isfile('/etc/init.d/nagios-nrpe-server'): | ||
330 | 169 | subprocess.call(['service', 'nagios-nrpe-server', 'reload']) | ||
331 | 0 | 170 | ||
332 | === modified file 'config.yaml' | |||
333 | --- config.yaml 2013-01-23 21:21:06 +0000 | |||
334 | +++ config.yaml 2013-01-31 20:16:33 +0000 | |||
335 | @@ -15,3 +15,13 @@ | |||
336 | 15 | type: string | 15 | type: string |
337 | 16 | default: "" | 16 | default: "" |
338 | 17 | description: "Address to mail errors to." | 17 | description: "Address to mail errors to." |
339 | 18 | nagios_context: | ||
340 | 19 | default: "juju" | ||
341 | 20 | type: string | ||
342 | 21 | description: | | ||
343 | 22 | Used by the nrpe-external-master subordinate charm. | ||
344 | 23 | A string that will be prepended to instance name to set the host name | ||
345 | 24 | in nagios. So for instance the hostname would be something like: | ||
346 | 25 | juju-myservice-0 | ||
347 | 26 | If you're running multiple environments with the same services in them | ||
348 | 27 | this allows you to differentiate between them. | ||
349 | 18 | 28 | ||
350 | === added directory 'files' | |||
351 | === added directory 'files/nrpe-external-master' | |||
352 | === added file 'files/nrpe-external-master/check_ingest.sh' | |||
353 | --- files/nrpe-external-master/check_ingest.sh 1970-01-01 00:00:00 +0000 | |||
354 | +++ files/nrpe-external-master/check_ingest.sh 2013-01-31 20:16:33 +0000 | |||
355 | @@ -0,0 +1,5 @@ | |||
356 | 1 | #!/bin/sh | ||
357 | 2 | if [ -f /home/ubuntu/var/ingest-errors ]; then | ||
358 | 3 | echo Charmworld ingest failing | ||
359 | 4 | exit 1 | ||
360 | 5 | fi | ||
361 | 0 | 6 | ||
362 | === modified file 'hooks/config-changed' | |||
363 | --- hooks/config-changed 2013-01-29 15:45:19 +0000 | |||
364 | +++ hooks/config-changed 2013-01-31 20:16:33 +0000 | |||
365 | @@ -79,9 +79,11 @@ | |||
366 | 79 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin | 79 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin |
367 | 80 | $mailto | 80 | $mailto |
368 | 81 | 81 | ||
370 | 82 | */$interval * * * * ubuntu HOME=/home/ubuntu INI=$CONFIG_FILE ~ubuntu/shhh.py $project_dir/scripts/ingest | 82 | */$interval * * * * ubuntu HOME=/home/ubuntu INI=$CONFIG_FILE ~ubuntu/run-write-errors /home/ubuntu/var/ingest-errors $project_dir/scripts/ingest |
371 | 83 | @daily ubuntu HOME=/home/ubuntu INI=$project_dir/production.ini $project_dir/bin/python $project_dir/charmworld/jobs/cstat.py | 83 | @daily ubuntu HOME=/home/ubuntu INI=$project_dir/production.ini $project_dir/bin/python $project_dir/charmworld/jobs/cstat.py |
372 | 84 | 84 | ||
373 | 85 | EOF | 85 | EOF |
374 | 86 | 86 | ||
375 | 87 | fi | 87 | fi |
376 | 88 | # nagios_context may have changed. | ||
377 | 89 | ./update-nrpe.py | ||
378 | 88 | 90 | ||
379 | === modified file 'hooks/install' | |||
380 | --- hooks/install 2013-01-24 17:34:53 +0000 | |||
381 | +++ hooks/install 2013-01-31 20:16:33 +0000 | |||
382 | @@ -17,4 +17,4 @@ | |||
383 | 17 | postfix reload | 17 | postfix reload |
384 | 18 | mkdir -p ~ubuntu/var/charms | 18 | mkdir -p ~ubuntu/var/charms |
385 | 19 | chown -R ubuntu.ubuntu ~ubuntu/var | 19 | chown -R ubuntu.ubuntu ~ubuntu/var |
387 | 20 | cp shhh.py ~ubuntu | 20 | install -o ubuntu -g ubuntu run-write-errors ~ubuntu |
388 | 21 | 21 | ||
389 | === added file 'hooks/nrpe-external-master-relation-changed' | |||
390 | --- hooks/nrpe-external-master-relation-changed 1970-01-01 00:00:00 +0000 | |||
391 | +++ hooks/nrpe-external-master-relation-changed 2013-01-31 20:16:33 +0000 | |||
392 | @@ -0,0 +1,2 @@ | |||
393 | 1 | #!/bin/sh | ||
394 | 2 | ./update-nrpe.py | ||
395 | 0 | 3 | ||
396 | === modified file 'hooks/upgrade-charm' | |||
397 | --- hooks/upgrade-charm 2013-01-24 17:34:53 +0000 | |||
398 | +++ hooks/upgrade-charm 2013-01-31 20:16:33 +0000 | |||
399 | @@ -10,3 +10,6 @@ | |||
400 | 10 | fi | 10 | fi |
401 | 11 | hooks/config-changed | 11 | hooks/config-changed |
402 | 12 | hooks/restart | 12 | hooks/restart |
403 | 13 | if [ -f $HOME/shhh.py ]; then | ||
404 | 14 | rm $HOME/shhh.py | ||
405 | 15 | fi | ||
406 | 13 | 16 | ||
407 | === modified file 'metadata.yaml' | |||
408 | --- metadata.yaml 2012-08-20 19:57:46 +0000 | |||
409 | +++ metadata.yaml 2013-01-31 20:16:33 +0000 | |||
410 | @@ -6,6 +6,9 @@ | |||
411 | 6 | provides: | 6 | provides: |
412 | 7 | website: | 7 | website: |
413 | 8 | interface: http | 8 | interface: http |
414 | 9 | nrpe-external-master: | ||
415 | 10 | interface: nrpe-external-master | ||
416 | 11 | scope: container | ||
417 | 9 | requires: | 12 | requires: |
418 | 10 | database: | 13 | database: |
419 | 11 | interface: mongodb | 14 | interface: mongodb |
420 | 12 | 15 | ||
421 | === modified file 'revision' | |||
422 | --- revision 2013-01-29 15:32:47 +0000 | |||
423 | +++ revision 2013-01-31 20:16:33 +0000 | |||
424 | @@ -1,1 +1,1 @@ | |||
426 | 1 | 18 | 1 | 19 |
427 | 2 | 2 | ||
428 | === added file 'run-write-errors' | |||
429 | --- run-write-errors 1970-01-01 00:00:00 +0000 | |||
430 | +++ run-write-errors 2013-01-31 20:16:33 +0000 | |||
431 | @@ -0,0 +1,16 @@ | |||
432 | 1 | #!/bin/sh | ||
433 | 2 | # Run a command, and if it exits with non-zero status, leave its output behind | ||
434 | 3 | # in the file specified. Any existing file will be removed. | ||
435 | 4 | error_file=$1 | ||
436 | 5 | shift | ||
437 | 6 | "$@" > $error_file.tmp 2>&1 | ||
438 | 7 | status=$? | ||
439 | 8 | if [ $status -ne 0 ]; then | ||
440 | 9 | mv $error_file.tmp $error_file | ||
441 | 10 | else | ||
442 | 11 | rm $error_file.tmp | ||
443 | 12 | if [ -f $error_file ]; then | ||
444 | 13 | rm $error_file | ||
445 | 14 | fi | ||
446 | 15 | fi | ||
447 | 16 | exit $status | ||
448 | 0 | 17 | ||
449 | === removed file 'shhh.py' | |||
450 | --- shhh.py 2013-01-24 21:00:17 +0000 | |||
451 | +++ shhh.py 1970-01-01 00:00:00 +0000 | |||
452 | @@ -1,81 +0,0 @@ | |||
453 | 1 | #! /usr/bin/python -S | ||
454 | 2 | # | ||
455 | 3 | # Copyright 2009 Canonical Ltd. This software is licensed under the | ||
456 | 4 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
457 | 5 | |||
458 | 6 | """ | ||
459 | 7 | Run a command and suppress output unless it returns a non-zero exit status | ||
460 | 8 | """ | ||
461 | 9 | |||
462 | 10 | __metaclass__ = type | ||
463 | 11 | |||
464 | 12 | from subprocess import ( | ||
465 | 13 | PIPE, | ||
466 | 14 | Popen, | ||
467 | 15 | ) | ||
468 | 16 | import sys | ||
469 | 17 | |||
470 | 18 | |||
471 | 19 | def shhh(cmd): | ||
472 | 20 | r"""Run a command and suppress output unless it returns a non-zero exitcode | ||
473 | 21 | |||
474 | 22 | If output is generated, stderr will be output before stdout, so output | ||
475 | 23 | order may be messed up if the command attempts to control order by | ||
476 | 24 | flushing stdout at points or setting it to unbuffered. | ||
477 | 25 | |||
478 | 26 | |||
479 | 27 | To test, we invoke both this method and this script with some commands | ||
480 | 28 | and examine the output and exitvalue | ||
481 | 29 | |||
482 | 30 | >>> python = sys.executable | ||
483 | 31 | |||
484 | 32 | >>> def shhh_script(cmd): | ||
485 | 33 | ... from subprocess import Popen, PIPE | ||
486 | 34 | ... script = '%s %s' % (python, __file__) | ||
487 | 35 | ... cmd = "%s '%s'" % (script, cmd) | ||
488 | 36 | ... p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) | ||
489 | 37 | ... (out, err) = p.communicate() | ||
490 | 38 | ... return (out, err, p.returncode) | ||
491 | 39 | |||
492 | 40 | >>> cmd = '''%s -c "import sys; sys.exit(%d)"''' % (python, 0) | ||
493 | 41 | >>> shhh(cmd) | ||
494 | 42 | 0 | ||
495 | 43 | >>> shhh_script(cmd) | ||
496 | 44 | ('', '', 0) | ||
497 | 45 | |||
498 | 46 | >>> cmd = '''%s -c "import sys; sys.exit(%d)"''' % (python, 1) | ||
499 | 47 | >>> shhh(cmd) | ||
500 | 48 | 1 | ||
501 | 49 | >>> shhh_script(cmd) | ||
502 | 50 | ('', '', 1) | ||
503 | 51 | |||
504 | 52 | >>> cmd = '''%s -c "import sys; print 666; sys.exit(%d)"''' % ( | ||
505 | 53 | ... python, 42) | ||
506 | 54 | >>> shhh(cmd) | ||
507 | 55 | 666 | ||
508 | 56 | 42 | ||
509 | 57 | >>> shhh_script(cmd) | ||
510 | 58 | ('666\n', '', 42) | ||
511 | 59 | |||
512 | 60 | >>> cmd = ( | ||
513 | 61 | ... '''%s -c "import sys; print 666; ''' | ||
514 | 62 | ... '''print >> sys.stderr, 667; sys.exit(42)"''' % python | ||
515 | 63 | ... ) | ||
516 | 64 | >>> shhh_script(cmd) | ||
517 | 65 | ('666\n', '667\n', 42) | ||
518 | 66 | """ | ||
519 | 67 | |||
520 | 68 | process = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True) | ||
521 | 69 | (out, err) = process.communicate() | ||
522 | 70 | if process.returncode == 0: | ||
523 | 71 | return 0 | ||
524 | 72 | else: | ||
525 | 73 | sys.stderr.write(err) | ||
526 | 74 | sys.stdout.write(out) | ||
527 | 75 | return process.returncode | ||
528 | 76 | |||
529 | 77 | |||
530 | 78 | if __name__ == '__main__': | ||
531 | 79 | cmd = ' '.join(sys.argv[1:]) | ||
532 | 80 | sys.exit(shhh(cmd)) | ||
533 | 81 | |||
534 | 82 | 0 | ||
535 | === added file 'update-nrpe.py' | |||
536 | --- update-nrpe.py 1970-01-01 00:00:00 +0000 | |||
537 | +++ update-nrpe.py 2013-01-31 20:16:33 +0000 | |||
538 | @@ -0,0 +1,12 @@ | |||
539 | 1 | #!/usr/bin/env python | ||
540 | 2 | from charmsupport import nrpe | ||
541 | 3 | |||
542 | 4 | |||
543 | 5 | def update_nrpe_config(): | ||
544 | 6 | nrpe_compat = nrpe.NRPE() | ||
545 | 7 | nrpe_compat.add_check('ingest', 'Check ingest runs', 'check_ingest.sh') | ||
546 | 8 | nrpe_compat.write() | ||
547 | 9 | |||
548 | 10 | |||
549 | 11 | if __name__ == '__main__': | ||
550 | 12 | update_nrpe_config() |
lgtm but I'd like to move the new files into a scripts directory. I've
started one in my branch as well. Things like the run-write-errors and
update-nrpe.py could find a home there and help prevent polluting the
root of the file tree.
https:/ /codereview. appspot. com/7241058/