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