Merge lp:~jjo/charms/trusty/neutron-openvswitch/add-nrpe-checks-lp1530227 into lp:~openstack-charmers-archive/charms/trusty/neutron-openvswitch/next
- Trusty Tahr (14.04)
- add-nrpe-checks-lp1530227
- Merge into next
Proposed by
JuanJo Ciarlante
Status: | Work in progress |
---|---|
Proposed branch: | lp:~jjo/charms/trusty/neutron-openvswitch/add-nrpe-checks-lp1530227 |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/neutron-openvswitch/next |
Diff against target: |
998 lines (+914/-0) 7 files modified
charm-helpers-hooks.yaml (+1/-0) config.yaml (+6/-0) files/nrpe-external-master/neutron-check-tun_ids.py (+219/-0) hooks/charmhelpers/contrib/charmsupport/__init__.py (+15/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+458/-0) hooks/charmhelpers/contrib/charmsupport/volumes.py (+175/-0) hooks/neutron_ovs_hooks.py (+40/-0) |
To merge this branch: | bzr merge lp:~jjo/charms/trusty/neutron-openvswitch/add-nrpe-checks-lp1530227 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenStack Charmers | Pending | ||
Review via email: mp+281931@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Unmerged revisions
- 99. By JuanJo Ciarlante
-
[jjo] WIP: add NRPE support via NRPESet passed at relation time to nova-compute principal lp#1530227
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 2015-09-28 09:47:21 +0000 | |||
3 | +++ charm-helpers-hooks.yaml 2016-01-07 21:40:19 +0000 | |||
4 | @@ -11,3 +11,4 @@ | |||
5 | 11 | - payload.execd | 11 | - payload.execd |
6 | 12 | - contrib.network.ip | 12 | - contrib.network.ip |
7 | 13 | - contrib.python.packages | 13 | - contrib.python.packages |
8 | 14 | - contrib.charmsupport | ||
9 | 14 | 15 | ||
10 | === modified file 'config.yaml' | |||
11 | --- config.yaml 2015-09-15 07:47:30 +0000 | |||
12 | +++ config.yaml 2016-01-07 21:40:19 +0000 | |||
13 | @@ -106,3 +106,9 @@ | |||
14 | 106 | which do not include a neutron-gateway (do not require l3, lbaas or vpnaas | 106 | which do not include a neutron-gateway (do not require l3, lbaas or vpnaas |
15 | 107 | services) and should only be used in-conjunction with flat or VLAN provider | 107 | services) and should only be used in-conjunction with flat or VLAN provider |
16 | 108 | networks configurations. | 108 | networks configurations. |
17 | 109 | enable-nrpe-checks: | ||
18 | 110 | type: boolean | ||
19 | 111 | default: true | ||
20 | 112 | description: | | ||
21 | 113 | Provide nrpe data to main charm (ie nova-compute) using 'neutron-plugin' | ||
22 | 114 | relation via the 'nrpe-checks' key | ||
23 | 109 | 115 | ||
24 | === added directory 'files' | |||
25 | === added directory 'files/nrpe-external-master' | |||
26 | === added file 'files/nrpe-external-master/neutron-check-tun_ids.py' | |||
27 | --- files/nrpe-external-master/neutron-check-tun_ids.py 1970-01-01 00:00:00 +0000 | |||
28 | +++ files/nrpe-external-master/neutron-check-tun_ids.py 2016-01-07 21:40:19 +0000 | |||
29 | @@ -0,0 +1,219 @@ | |||
30 | 1 | #!/usr/bin/python | ||
31 | 2 | # vim: si et sw=4 ts=4 | ||
32 | 3 | # | ||
33 | 4 | # Author: JuanJo Ciarlante <jjo@canonical.com> | ||
34 | 5 | # Copyright (C) 2015 Canonical | ||
35 | 6 | # License: GPLv3 | ||
36 | 7 | """ | ||
37 | 8 | Verify that all local OVS tun flows match the ones expected from locally | ||
38 | 9 | running nova instances | ||
39 | 10 | |||
40 | 11 | Example usage: | ||
41 | 12 | {0} | ||
42 | 13 | {0} -i br-tun # peek at other OVS interface | ||
43 | 14 | {0} --conf-file=/etc/nova/nova.conf # config file to peek creds | ||
44 | 15 | |||
45 | 16 | """ | ||
46 | 17 | import sys | ||
47 | 18 | import os | ||
48 | 19 | import re | ||
49 | 20 | import logging | ||
50 | 21 | import argparse | ||
51 | 22 | import ConfigParser | ||
52 | 23 | import socket | ||
53 | 24 | import subprocess | ||
54 | 25 | |||
55 | 26 | from neutronclient.v2_0 import client as neutron_client | ||
56 | 27 | from novaclient import client as nova_client | ||
57 | 28 | |||
58 | 29 | |||
59 | 30 | (STATUS_OK, STATUS_WARN, STATUS_CRIT, STATUS_UNKNOWN) = range(0, 4) | ||
60 | 31 | |||
61 | 32 | |||
62 | 33 | def get_creds(args): | ||
63 | 34 | """ return creds dictionary from conf-file (/etc/nova/nova.conf), | ||
64 | 35 | overridden by OS_ environment vars """ | ||
65 | 36 | config = ConfigParser.RawConfigParser() | ||
66 | 37 | config.read(args.conf_file) | ||
67 | 38 | config_creds_section_prefix = { | ||
68 | 39 | 'DEFAULT': 'neutron_admin_', | ||
69 | 40 | 'neutron': 'admin_', | ||
70 | 41 | } | ||
71 | 42 | |||
72 | 43 | creds = {} | ||
73 | 44 | for key in ('auth_url', 'username', 'password', 'tenant_name'): | ||
74 | 45 | env_key = "OS_{}".format(key.upper()) | ||
75 | 46 | value = os.environ.get(env_key) | ||
76 | 47 | # If no creds from environment, try known possible config_creds_keys | ||
77 | 48 | if value: | ||
78 | 49 | logging.info("get_creds: found {} as env['{}']={}".format( | ||
79 | 50 | key, env_key, value if key != 'password' else '...')) | ||
80 | 51 | if not value: | ||
81 | 52 | for section, prefix in config_creds_section_prefix.iteritems(): | ||
82 | 53 | try: | ||
83 | 54 | value = config.get(section, prefix + key) | ||
84 | 55 | logging.info("get_creds: found {}.{}={}".format( | ||
85 | 56 | section, prefix + key, | ||
86 | 57 | value if key != 'password' else '...')) | ||
87 | 58 | break | ||
88 | 59 | except ConfigParser.NoOptionError: | ||
89 | 60 | pass | ||
90 | 61 | except ConfigParser.NoSectionError: | ||
91 | 62 | pass | ||
92 | 63 | if value: | ||
93 | 64 | creds[key] = value | ||
94 | 65 | else: | ||
95 | 66 | raise KeyError("Couldn't find config value for '{}'".format(key)) | ||
96 | 67 | |||
97 | 68 | logging.debug("creds: username={username} tenant_name={tenant_name} " | ||
98 | 69 | "auth_url={auth_url} password=...".format(**creds)) | ||
99 | 70 | |||
100 | 71 | return creds | ||
101 | 72 | |||
102 | 73 | |||
103 | 74 | def nova_list_instances(nova_cli, host): | ||
104 | 75 | "return instances ids running at host" | ||
105 | 76 | logging.info('getting all instances running at host="{}" ...'.format( | ||
106 | 77 | socket.gethostname())) | ||
107 | 78 | search_opts = {'all_tenants': 1, 'host': host} | ||
108 | 79 | instances = [server.id for server in | ||
109 | 80 | nova_cli.servers.list(search_opts=search_opts) | ||
110 | 81 | if server.status == 'ACTIVE'] | ||
111 | 82 | logging.info('instances count={}'.format(len(instances))) | ||
112 | 83 | logging.debug('instances: {}'.format(instances)) | ||
113 | 84 | return instances | ||
114 | 85 | |||
115 | 86 | |||
116 | 87 | def instances_port_nets(neutron_cli, instances): | ||
117 | 88 | "return instances ports attached network ids" | ||
118 | 89 | logging.info('getting all instances networks ...') | ||
119 | 90 | instances_nets = set() | ||
120 | 91 | for instance in instances: | ||
121 | 92 | for ports in neutron_cli.list_ports( | ||
122 | 93 | device_id=instance, fields=['id', 'network_id']).values(): | ||
123 | 94 | for port in ports: | ||
124 | 95 | instances_nets.add(port['network_id']) | ||
125 | 96 | logging.info('instances networks count={}'.format(len(instances_nets))) | ||
126 | 97 | logging.debug('instances networks: {}'.format(instances_nets)) | ||
127 | 98 | return instances_nets | ||
128 | 99 | |||
129 | 100 | |||
130 | 101 | def neutron_networks_by_id(neutron_cli): | ||
131 | 102 | "return all neutron networks, keyed by id" | ||
132 | 103 | logging.info('getting all neutron networks ...') | ||
133 | 104 | all_nets = neutron_cli.list_networks().get('networks') | ||
134 | 105 | logging.info('neutron networks count={}'.format(len(all_nets))) | ||
135 | 106 | logging.debug('neutron networks: {}'.format(all_nets)) | ||
136 | 107 | networks_by_id = {net['id']: net for net in all_nets} | ||
137 | 108 | return networks_by_id | ||
138 | 109 | |||
139 | 110 | |||
140 | 111 | def get_instances_tun_ids(instances_nets, all_nets_by_id): | ||
141 | 112 | """return tun_ids from for passed instances networks | ||
142 | 113 | by looking up all_nets_by_id info""" | ||
143 | 114 | logging.info('getting network segmentation_id info for all instances...') | ||
144 | 115 | SEG_ID = 'provider:segmentation_id' | ||
145 | 116 | NET_TYPE = 'provider:network_type' | ||
146 | 117 | instances_tun_ids = {all_nets_by_id[net_id].get(SEG_ID) | ||
147 | 118 | for net_id in instances_nets | ||
148 | 119 | if all_nets_by_id[net_id].get(NET_TYPE) in | ||
149 | 120 | ('gre', 'vxlan')} | ||
150 | 121 | logging.info('instances_tun_ids: {}'.format(instances_tun_ids)) | ||
151 | 122 | return instances_tun_ids | ||
152 | 123 | |||
153 | 124 | |||
154 | 125 | def get_ovs_tun_ids(interface): | ||
155 | 126 | """get local tun_ids from ovs-ofctl output, ala: | ||
156 | 127 | ovs-ofctl dump-flows br-tun |egrep -o 'tun_id=\w+' """ | ||
157 | 128 | logging.info('local tun_ids: running: ovs-ofctl dump-flows {}'.format( | ||
158 | 129 | interface)) | ||
159 | 130 | ovs_dump = subprocess.Popen(["ovs-ofctl", "dump-flows", interface], | ||
160 | 131 | stdin=None, | ||
161 | 132 | stdout=subprocess.PIPE, | ||
162 | 133 | stderr=subprocess.PIPE) | ||
163 | 134 | ovs_tun_ids = set() | ||
164 | 135 | # match lines with: ... tun_id=0x<TUN_ID> ... | ||
165 | 136 | for line in ovs_dump.stdout: | ||
166 | 137 | match = re.search("tun_id=(?P<tun_id>0x\w+)", line) | ||
167 | 138 | if match: | ||
168 | 139 | ovs_tun_ids.add(int(match.group(1), 16)) | ||
169 | 140 | logging.info('ovs_tun_ids: {}'.format(ovs_tun_ids)) | ||
170 | 141 | return ovs_tun_ids | ||
171 | 142 | |||
172 | 143 | |||
173 | 144 | def nrpe_check_tun_ids(expected_tun_ids, local_tun_ids, all_nets_by_id): | ||
174 | 145 | # order is important: substract local_tun_ids from expected_tun_ids, | ||
175 | 146 | # result should be empty | ||
176 | 147 | tun_ids_diff = expected_tun_ids.difference(local_tun_ids) | ||
177 | 148 | rc = STATUS_OK | ||
178 | 149 | msg = [] | ||
179 | 150 | if tun_ids_diff: | ||
180 | 151 | tun_ids_str = ' '.join(['tun_id=0x{0:x}'.format(x) | ||
181 | 152 | for x in tun_ids_diff]) | ||
182 | 153 | msg.append('CRITICAL: host={} missing local tun_ids: {}'.format( | ||
183 | 154 | socket.gethostname(), tun_ids_str)) | ||
184 | 155 | # helper dict by tun_id | ||
185 | 156 | net_by_tun_id = {net_val.get('provider:segmentation_id'): net_val | ||
186 | 157 | for net_id, net_val in all_nets_by_id.iteritems()} | ||
187 | 158 | for tun_id in tun_ids_diff: | ||
188 | 159 | net = net_by_tun_id.get(tun_id, {}) | ||
189 | 160 | msg.append('CRITICAL: tun_id=0x{0:x} network.id={id} ' | ||
190 | 161 | 'network.name="{name}"'.format(tun_id, **net)) | ||
191 | 162 | logging.info('exp_tun_ids: {}'.format(sorted(expected_tun_ids))) | ||
192 | 163 | logging.info('loc_tun_ids: {}'.format(sorted(local_tun_ids))) | ||
193 | 164 | rc = STATUS_CRIT | ||
194 | 165 | else: | ||
195 | 166 | msg.append('OK: host={} all needed tun_ids present: {}' | ||
196 | 167 | ''.format(socket.gethostname(), list(local_tun_ids))) | ||
197 | 168 | return (rc, msg) | ||
198 | 169 | |||
199 | 170 | |||
200 | 171 | def parse_args(): | ||
201 | 172 | parser = argparse.ArgumentParser( | ||
202 | 173 | description=__doc__.format(*sys.argv), | ||
203 | 174 | formatter_class=argparse.RawDescriptionHelpFormatter) | ||
204 | 175 | parser.add_argument('--conf-file', default='/etc/nova/nova.conf', | ||
205 | 176 | help='config file to peek creds from') | ||
206 | 177 | parser.add_argument('-i', '--interface', default='br-tun', | ||
207 | 178 | help='OVS iface where to find tun_ids, as: ' | ||
208 | 179 | 'ovs-ofctl dump-flows <interface>') | ||
209 | 180 | parser.add_argument('--test', default=False, action='store_true', | ||
210 | 181 | help='simulate missing local tun_ids, force CRITICAL') | ||
211 | 182 | parser.add_argument('--verbose', default=False, action='store_true') | ||
212 | 183 | parser.add_argument('--debug', default=False, action='store_true') | ||
213 | 184 | return parser.parse_args() | ||
214 | 185 | |||
215 | 186 | |||
216 | 187 | if __name__ == '__main__': | ||
217 | 188 | args = parse_args() | ||
218 | 189 | if args.verbose: | ||
219 | 190 | logging.basicConfig(level=logging.INFO) | ||
220 | 191 | if args.debug: | ||
221 | 192 | logging.basicConfig(level=logging.DEBUG) | ||
222 | 193 | |||
223 | 194 | # initialize needed clients | ||
224 | 195 | creds = get_creds(args) | ||
225 | 196 | logging.info("initializing nova_client") | ||
226 | 197 | nova_cli = nova_client.Client(1.1, creds['username'], creds['password'], | ||
227 | 198 | creds['tenant_name'], creds['auth_url']) | ||
228 | 199 | logging.info("initializing neutron_client") | ||
229 | 200 | neutron_cli = neutron_client.Client(**creds) | ||
230 | 201 | |||
231 | 202 | # instances: local instances id-s (ie running at this host) | ||
232 | 203 | # instances_nets: local instances' networks id-s | ||
233 | 204 | # all_nets_by_id: all neutron networks, keyed by id | ||
234 | 205 | # exp_tun_ids: local instances' networks' segmentation_id-s | ||
235 | 206 | # loc_tun_ids: locally present tun_ids from ovs-ofctl dump-flows br-tun | ||
236 | 207 | instances = nova_list_instances(nova_cli, socket.gethostname()) | ||
237 | 208 | instances_nets = instances_port_nets(neutron_cli, instances) | ||
238 | 209 | all_nets_by_id = neutron_networks_by_id(neutron_cli) | ||
239 | 210 | exp_tun_ids = get_instances_tun_ids(instances_nets, all_nets_by_id) | ||
240 | 211 | loc_tun_ids = get_ovs_tun_ids(args.interface) | ||
241 | 212 | |||
242 | 213 | if args.test: | ||
243 | 214 | logging.info('TEST: remove a local tun_id'.format(args.interface)) | ||
244 | 215 | loc_tun_ids.pop() | ||
245 | 216 | |||
246 | 217 | rc, msg = nrpe_check_tun_ids(exp_tun_ids, loc_tun_ids, all_nets_by_id) | ||
247 | 218 | print "\n".join(msg) | ||
248 | 219 | sys.exit(rc) | ||
249 | 0 | 220 | ||
250 | === added directory 'hooks/charmhelpers/contrib/charmsupport' | |||
251 | === added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py' | |||
252 | --- hooks/charmhelpers/contrib/charmsupport/__init__.py 1970-01-01 00:00:00 +0000 | |||
253 | +++ hooks/charmhelpers/contrib/charmsupport/__init__.py 2016-01-07 21:40:19 +0000 | |||
254 | @@ -0,0 +1,15 @@ | |||
255 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
256 | 2 | # | ||
257 | 3 | # This file is part of charm-helpers. | ||
258 | 4 | # | ||
259 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
260 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
261 | 7 | # published by the Free Software Foundation. | ||
262 | 8 | # | ||
263 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
264 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
265 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
266 | 12 | # GNU Lesser General Public License for more details. | ||
267 | 13 | # | ||
268 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
269 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
270 | 0 | 16 | ||
271 | === added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
272 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 | |||
273 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2016-01-07 21:40:19 +0000 | |||
274 | @@ -0,0 +1,458 @@ | |||
275 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
276 | 2 | # | ||
277 | 3 | # This file is part of charm-helpers. | ||
278 | 4 | # | ||
279 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
280 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
281 | 7 | # published by the Free Software Foundation. | ||
282 | 8 | # | ||
283 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
284 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
285 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
286 | 12 | # GNU Lesser General Public License for more details. | ||
287 | 13 | # | ||
288 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
289 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
290 | 16 | |||
291 | 17 | """Compatibility with the nrpe-external-master charm""" | ||
292 | 18 | # Copyright 2012 Canonical Ltd. | ||
293 | 19 | # | ||
294 | 20 | # Authors: | ||
295 | 21 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
296 | 22 | |||
297 | 23 | import subprocess | ||
298 | 24 | import pwd | ||
299 | 25 | import grp | ||
300 | 26 | import os | ||
301 | 27 | import glob | ||
302 | 28 | import shutil | ||
303 | 29 | import re | ||
304 | 30 | import shlex | ||
305 | 31 | import yaml | ||
306 | 32 | |||
307 | 33 | from charmhelpers.core.hookenv import ( | ||
308 | 34 | config, | ||
309 | 35 | local_unit, | ||
310 | 36 | log, | ||
311 | 37 | relation_ids, | ||
312 | 38 | relation_set, | ||
313 | 39 | relations_of_type, | ||
314 | 40 | ) | ||
315 | 41 | |||
316 | 42 | from charmhelpers.core.host import ( | ||
317 | 43 | service, | ||
318 | 44 | rsync, | ||
319 | 45 | ) | ||
320 | 46 | NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' | ||
321 | 47 | |||
322 | 48 | # This module adds compatibility with the nrpe-external-master and plain nrpe | ||
323 | 49 | # subordinate charms. To use it in your charm: | ||
324 | 50 | # | ||
325 | 51 | # 1. Update metadata.yaml | ||
326 | 52 | # | ||
327 | 53 | # provides: | ||
328 | 54 | # (...) | ||
329 | 55 | # nrpe-external-master: | ||
330 | 56 | # interface: nrpe-external-master | ||
331 | 57 | # scope: container | ||
332 | 58 | # | ||
333 | 59 | # and/or | ||
334 | 60 | # | ||
335 | 61 | # provides: | ||
336 | 62 | # (...) | ||
337 | 63 | # local-monitors: | ||
338 | 64 | # interface: local-monitors | ||
339 | 65 | # scope: container | ||
340 | 66 | |||
341 | 67 | # | ||
342 | 68 | # 2. Add the following to config.yaml | ||
343 | 69 | # | ||
344 | 70 | # nagios_context: | ||
345 | 71 | # default: "juju" | ||
346 | 72 | # type: string | ||
347 | 73 | # description: | | ||
348 | 74 | # Used by the nrpe subordinate charms. | ||
349 | 75 | # A string that will be prepended to instance name to set the host name | ||
350 | 76 | # in nagios. So for instance the hostname would be something like: | ||
351 | 77 | # juju-myservice-0 | ||
352 | 78 | # If you're running multiple environments with the same services in them | ||
353 | 79 | # this allows you to differentiate between them. | ||
354 | 80 | # nagios_servicegroups: | ||
355 | 81 | # default: "" | ||
356 | 82 | # type: string | ||
357 | 83 | # description: | | ||
358 | 84 | # A comma-separated list of nagios servicegroups. | ||
359 | 85 | # If left empty, the nagios_context will be used as the servicegroup | ||
360 | 86 | # | ||
361 | 87 | # 3. Add custom checks (Nagios plugins) to files/nrpe-external-master | ||
362 | 88 | # | ||
363 | 89 | # 4. Update your hooks.py with something like this: | ||
364 | 90 | # | ||
365 | 91 | # from charmsupport.nrpe import NRPE | ||
366 | 92 | # (...) | ||
367 | 93 | # def update_nrpe_config(): | ||
368 | 94 | # nrpe_compat = NRPE() | ||
369 | 95 | # nrpe_compat.add_check( | ||
370 | 96 | # shortname = "myservice", | ||
371 | 97 | # description = "Check MyService", | ||
372 | 98 | # check_cmd = "check_http -w 2 -c 10 http://localhost" | ||
373 | 99 | # ) | ||
374 | 100 | # nrpe_compat.add_check( | ||
375 | 101 | # "myservice_other", | ||
376 | 102 | # "Check for widget failures", | ||
377 | 103 | # check_cmd = "/srv/myapp/scripts/widget_check" | ||
378 | 104 | # ) | ||
379 | 105 | # nrpe_compat.write() | ||
380 | 106 | # | ||
381 | 107 | # def config_changed(): | ||
382 | 108 | # (...) | ||
383 | 109 | # update_nrpe_config() | ||
384 | 110 | # | ||
385 | 111 | # def nrpe_external_master_relation_changed(): | ||
386 | 112 | # update_nrpe_config() | ||
387 | 113 | # | ||
388 | 114 | # def local_monitors_relation_changed(): | ||
389 | 115 | # update_nrpe_config() | ||
390 | 116 | # | ||
391 | 117 | # 5. ln -s hooks.py nrpe-external-master-relation-changed | ||
392 | 118 | # ln -s hooks.py local-monitors-relation-changed | ||
393 | 119 | |||
394 | 120 | |||
395 | 121 | class CheckException(Exception): | ||
396 | 122 | pass | ||
397 | 123 | |||
398 | 124 | |||
399 | 125 | class Check(object): | ||
400 | 126 | shortname_re = '[A-Za-z0-9-_]+$' | ||
401 | 127 | service_template = (""" | ||
402 | 128 | #--------------------------------------------------- | ||
403 | 129 | # This file is Juju managed | ||
404 | 130 | #--------------------------------------------------- | ||
405 | 131 | define service {{ | ||
406 | 132 | use active-service | ||
407 | 133 | host_name {nagios_hostname} | ||
408 | 134 | service_description {nagios_hostname}[{shortname}] """ | ||
409 | 135 | """{description} | ||
410 | 136 | check_command check_nrpe!{command} | ||
411 | 137 | servicegroups {nagios_servicegroup} | ||
412 | 138 | }} | ||
413 | 139 | """) | ||
414 | 140 | |||
415 | 141 | def __init__(self, shortname, description, check_cmd): | ||
416 | 142 | super(Check, self).__init__() | ||
417 | 143 | # XXX: could be better to calculate this from the service name | ||
418 | 144 | if not re.match(self.shortname_re, shortname): | ||
419 | 145 | raise CheckException("shortname must match {}".format( | ||
420 | 146 | Check.shortname_re)) | ||
421 | 147 | self.shortname = shortname | ||
422 | 148 | self.command = "check_{}".format(shortname) | ||
423 | 149 | # Note: a set of invalid characters is defined by the | ||
424 | 150 | # Nagios server config | ||
425 | 151 | # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= | ||
426 | 152 | self.description = description | ||
427 | 153 | self.check_cmd = self._locate_cmd(check_cmd) | ||
428 | 154 | |||
429 | 155 | def _locate_cmd(self, check_cmd): | ||
430 | 156 | search_path = ( | ||
431 | 157 | '/usr/lib/nagios/plugins', | ||
432 | 158 | '/usr/local/lib/nagios/plugins', | ||
433 | 159 | ) | ||
434 | 160 | parts = shlex.split(check_cmd) | ||
435 | 161 | for path in search_path: | ||
436 | 162 | if os.path.exists(os.path.join(path, parts[0])): | ||
437 | 163 | command = os.path.join(path, parts[0]) | ||
438 | 164 | if len(parts) > 1: | ||
439 | 165 | command += " " + " ".join(parts[1:]) | ||
440 | 166 | return command | ||
441 | 167 | log('Check command not found: {}'.format(parts[0])) | ||
442 | 168 | return '' | ||
443 | 169 | |||
444 | 170 | def write(self, nagios_context, hostname, nagios_servicegroups): | ||
445 | 171 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | ||
446 | 172 | self.command) | ||
447 | 173 | with open(nrpe_check_file, 'w') as nrpe_check_config: | ||
448 | 174 | nrpe_check_config.write("# check {}\n".format(self.shortname)) | ||
449 | 175 | nrpe_check_config.write("command[{}]={}\n".format( | ||
450 | 176 | self.command, self.check_cmd)) | ||
451 | 177 | |||
452 | 178 | if not os.path.exists(NRPE.nagios_exportdir): | ||
453 | 179 | log('Not writing service config as {} is not accessible'.format( | ||
454 | 180 | NRPE.nagios_exportdir)) | ||
455 | 181 | else: | ||
456 | 182 | self.write_service_config(nagios_context, hostname, | ||
457 | 183 | nagios_servicegroups) | ||
458 | 184 | |||
459 | 185 | def write_service_config(self, nagios_context, hostname, | ||
460 | 186 | nagios_servicegroups): | ||
461 | 187 | for f in os.listdir(NRPE.nagios_exportdir): | ||
462 | 188 | if re.search('.*{}.cfg'.format(self.command), f): | ||
463 | 189 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | ||
464 | 190 | |||
465 | 191 | templ_vars = { | ||
466 | 192 | 'nagios_hostname': hostname, | ||
467 | 193 | 'nagios_servicegroup': nagios_servicegroups, | ||
468 | 194 | 'description': self.description, | ||
469 | 195 | 'shortname': self.shortname, | ||
470 | 196 | 'command': self.command, | ||
471 | 197 | } | ||
472 | 198 | nrpe_service_text = Check.service_template.format(**templ_vars) | ||
473 | 199 | nrpe_service_file = '{}/service__{}_{}.cfg'.format( | ||
474 | 200 | NRPE.nagios_exportdir, hostname, self.command) | ||
475 | 201 | with open(nrpe_service_file, 'w') as nrpe_service_config: | ||
476 | 202 | nrpe_service_config.write(str(nrpe_service_text)) | ||
477 | 203 | |||
478 | 204 | def run(self): | ||
479 | 205 | subprocess.call(self.check_cmd) | ||
480 | 206 | |||
481 | 207 | |||
482 | 208 | class Cron(object): | ||
483 | 209 | def __init__(self, filename, cron_freq, cron_user, cron_cmd): | ||
484 | 210 | super(Cron, self).__init__() | ||
485 | 211 | self.filename = filename | ||
486 | 212 | self.cron_freq = cron_freq | ||
487 | 213 | self.cron_user = cron_user | ||
488 | 214 | self.cron_cmd = cron_cmd | ||
489 | 215 | |||
490 | 216 | def write(self): | ||
491 | 217 | cron_filename = '/etc/cron.d/{}'.format(self.filename) | ||
492 | 218 | log("cron.write: {}".format(cron_filename)) | ||
493 | 219 | with open(cron_filename, 'w') as cron_file: | ||
494 | 220 | cron_file.write("# cron {}\n".format(self.filename)) | ||
495 | 221 | cron_file.write("{} {} {}\n".format(self.cron_freq, | ||
496 | 222 | self.cron_user, | ||
497 | 223 | self.cron_cmd)) | ||
498 | 224 | |||
499 | 225 | class NagiosPlugin(object): | ||
500 | 226 | def __init__(self, filename): | ||
501 | 227 | super(NagiosPlugin, self).__init__() | ||
502 | 228 | self.filename = filename | ||
503 | 229 | |||
504 | 230 | def write(self): | ||
505 | 231 | if not os.path.exists(NAGIOS_PLUGINS): | ||
506 | 232 | os.makedirs(NAGIOS_PLUGINS) | ||
507 | 233 | if os.path.exists(self.filename): | ||
508 | 234 | log("NagiosPlugin.write: {} {}".format(self.filename, NAGIOS_PLUGINS)) | ||
509 | 235 | rsync(self.filename, NAGIOS_PLUGINS) | ||
510 | 236 | else: | ||
511 | 237 | log("SKIPPED: NagiosPlugin.write: {} {}".format(self.filename, NAGIOS_PLUGINS)) | ||
512 | 238 | |||
513 | 239 | |||
514 | 240 | class NRPE(object): | ||
515 | 241 | nagios_logdir = '/var/log/nagios' | ||
516 | 242 | nagios_exportdir = '/var/lib/nagios/export' | ||
517 | 243 | nrpe_confdir = '/etc/nagios/nrpe.d' | ||
518 | 244 | |||
519 | 245 | def __init__(self, hostname=None): | ||
520 | 246 | super(NRPE, self).__init__() | ||
521 | 247 | self.config = config() | ||
522 | 248 | self.nagios_context = self.config['nagios_context'] | ||
523 | 249 | if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']: | ||
524 | 250 | self.nagios_servicegroups = self.config['nagios_servicegroups'] | ||
525 | 251 | else: | ||
526 | 252 | self.nagios_servicegroups = self.nagios_context | ||
527 | 253 | self.unit_name = local_unit().replace('/', '-') | ||
528 | 254 | if hostname: | ||
529 | 255 | self.hostname = hostname | ||
530 | 256 | else: | ||
531 | 257 | self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) | ||
532 | 258 | self.checks = [] | ||
533 | 259 | self.crons = [] | ||
534 | 260 | self.nagios_plugins = [] | ||
535 | 261 | |||
536 | 262 | def add_check(self, *args, **kwargs): | ||
537 | 263 | self.checks.append(Check(*args, **kwargs)) | ||
538 | 264 | |||
539 | 265 | def add_cron(self, *args, **kwargs): | ||
540 | 266 | self.crons.append(Cron(*args, **kwargs)) | ||
541 | 267 | |||
542 | 268 | def add_nagios_plugin(self, *args, **kwargs): | ||
543 | 269 | self.nagios_plugins.append(NagiosPlugin(*args, **kwargs)) | ||
544 | 270 | |||
545 | 271 | def add_from_config(self, config_key): | ||
546 | 272 | saved_config = config() | ||
547 | 273 | nrpe_set = NRPESet(saved_config.get(config_key)) | ||
548 | 274 | log('NRPE.add_from_config: nrpe_set={}'.format(str(nrpe_set))) | ||
549 | 275 | if nrpe_set: | ||
550 | 276 | for check in nrpe_set.checks: | ||
551 | 277 | self.add_check(*check) | ||
552 | 278 | for cron in nrpe_set.crons: | ||
553 | 279 | self.add_cron(*cron) | ||
554 | 280 | for nagios_plugins in nrpe_set.nagios_plugins: | ||
555 | 281 | self.add_nagios_plugin(*nagios_plugins) | ||
556 | 282 | |||
557 | 283 | def write(self): | ||
558 | 284 | try: | ||
559 | 285 | nagios_uid = pwd.getpwnam('nagios').pw_uid | ||
560 | 286 | nagios_gid = grp.getgrnam('nagios').gr_gid | ||
561 | 287 | except: | ||
562 | 288 | log("Nagios user not set up, nrpe checks not updated") | ||
563 | 289 | return | ||
564 | 290 | |||
565 | 291 | if not os.path.exists(NRPE.nagios_logdir): | ||
566 | 292 | os.mkdir(NRPE.nagios_logdir) | ||
567 | 293 | os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) | ||
568 | 294 | |||
569 | 295 | nrpe_monitors = {} | ||
570 | 296 | monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}} | ||
571 | 297 | for nrpecheck in self.checks: | ||
572 | 298 | nrpecheck.write(self.nagios_context, self.hostname, | ||
573 | 299 | self.nagios_servicegroups) | ||
574 | 300 | nrpe_monitors[nrpecheck.shortname] = { | ||
575 | 301 | "command": nrpecheck.command, | ||
576 | 302 | } | ||
577 | 303 | |||
578 | 304 | for cron in self.crons: | ||
579 | 305 | cron.write() | ||
580 | 306 | |||
581 | 307 | for nagios_plugins in self.nagios_plugins: | ||
582 | 308 | nagios_plugins.write() | ||
583 | 309 | |||
584 | 310 | service('restart', 'nagios-nrpe-server') | ||
585 | 311 | |||
586 | 312 | monitor_ids = relation_ids("local-monitors") + \ | ||
587 | 313 | relation_ids("nrpe-external-master") | ||
588 | 314 | for rid in monitor_ids: | ||
589 | 315 | relation_set(relation_id=rid, monitors=yaml.dump(monitors)) | ||
590 | 316 | |||
591 | 317 | |||
592 | 318 | def get_nagios_hostcontext(relation_name='nrpe-external-master'): | ||
593 | 319 | """ | ||
594 | 320 | Query relation with nrpe subordinate, return the nagios_host_context | ||
595 | 321 | |||
596 | 322 | :param str relation_name: Name of relation nrpe sub joined to | ||
597 | 323 | """ | ||
598 | 324 | for rel in relations_of_type(relation_name): | ||
599 | 325 | if 'nagios_hostname' in rel: | ||
600 | 326 | return rel['nagios_host_context'] | ||
601 | 327 | |||
602 | 328 | |||
603 | 329 | def get_nagios_hostname(relation_name='nrpe-external-master'): | ||
604 | 330 | """ | ||
605 | 331 | Query relation with nrpe subordinate, return the nagios_hostname | ||
606 | 332 | |||
607 | 333 | :param str relation_name: Name of relation nrpe sub joined to | ||
608 | 334 | """ | ||
609 | 335 | for rel in relations_of_type(relation_name): | ||
610 | 336 | if 'nagios_hostname' in rel: | ||
611 | 337 | return rel['nagios_hostname'] | ||
612 | 338 | |||
613 | 339 | |||
614 | 340 | def get_nagios_unit_name(relation_name='nrpe-external-master'): | ||
615 | 341 | """ | ||
616 | 342 | Return the nagios unit name prepended with host_context if needed | ||
617 | 343 | |||
618 | 344 | :param str relation_name: Name of relation nrpe sub joined to | ||
619 | 345 | """ | ||
620 | 346 | host_context = get_nagios_hostcontext(relation_name) | ||
621 | 347 | if host_context: | ||
622 | 348 | unit = "%s:%s" % (host_context, local_unit()) | ||
623 | 349 | else: | ||
624 | 350 | unit = local_unit() | ||
625 | 351 | return unit | ||
626 | 352 | |||
627 | 353 | |||
628 | 354 | def add_init_service_checks(nrpe, services, unit_name): | ||
629 | 355 | """ | ||
630 | 356 | Add checks for each service in list | ||
631 | 357 | |||
632 | 358 | :param NRPE nrpe: NRPE object to add check to | ||
633 | 359 | :param list services: List of services to check | ||
634 | 360 | :param str unit_name: Unit name to use in check description | ||
635 | 361 | """ | ||
636 | 362 | for svc in services: | ||
637 | 363 | upstart_init = '/etc/init/%s.conf' % svc | ||
638 | 364 | sysv_init = '/etc/init.d/%s' % svc | ||
639 | 365 | if os.path.exists(upstart_init): | ||
640 | 366 | nrpe.add_check( | ||
641 | 367 | shortname=svc, | ||
642 | 368 | description='process check {%s}' % unit_name, | ||
643 | 369 | check_cmd='check_upstart_job %s' % svc | ||
644 | 370 | ) | ||
645 | 371 | elif os.path.exists(sysv_init): | ||
646 | 372 | cronpath = '/etc/cron.d/nagios-service-check-%s' % svc | ||
647 | 373 | cron_file = ('*/5 * * * * root ' | ||
648 | 374 | '/usr/local/lib/nagios/plugins/check_exit_status.pl ' | ||
649 | 375 | '-s /etc/init.d/%s status > ' | ||
650 | 376 | '/var/lib/nagios/service-check-%s.txt\n' % (svc, | ||
651 | 377 | svc) | ||
652 | 378 | ) | ||
653 | 379 | f = open(cronpath, 'w') | ||
654 | 380 | f.write(cron_file) | ||
655 | 381 | f.close() | ||
656 | 382 | nrpe.add_check( | ||
657 | 383 | shortname=svc, | ||
658 | 384 | description='process check {%s}' % unit_name, | ||
659 | 385 | check_cmd='check_status_file.py -f ' | ||
660 | 386 | '/var/lib/nagios/service-check-%s.txt' % svc, | ||
661 | 387 | ) | ||
662 | 388 | |||
663 | 389 | |||
664 | 390 | def copy_nrpe_checks(): | ||
665 | 391 | """ | ||
666 | 392 | Copy the nrpe checks into place | ||
667 | 393 | |||
668 | 394 | """ | ||
669 | 395 | nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks', | ||
670 | 396 | 'charmhelpers', 'contrib', 'openstack', | ||
671 | 397 | 'files') | ||
672 | 398 | |||
673 | 399 | if not os.path.exists(NAGIOS_PLUGINS): | ||
674 | 400 | os.makedirs(NAGIOS_PLUGINS) | ||
675 | 401 | for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")): | ||
676 | 402 | if os.path.isfile(fname): | ||
677 | 403 | shutil.copy2(fname, | ||
678 | 404 | os.path.join(NAGIOS_PLUGINS, os.path.basename(fname))) | ||
679 | 405 | |||
680 | 406 | |||
681 | 407 | def add_haproxy_checks(nrpe, unit_name): | ||
682 | 408 | """ | ||
683 | 409 | Add checks for each service in list | ||
684 | 410 | |||
685 | 411 | :param NRPE nrpe: NRPE object to add check to | ||
686 | 412 | :param str unit_name: Unit name to use in check description | ||
687 | 413 | """ | ||
688 | 414 | nrpe.add_check( | ||
689 | 415 | shortname='haproxy_servers', | ||
690 | 416 | description='Check HAProxy {%s}' % unit_name, | ||
691 | 417 | check_cmd='check_haproxy.sh') | ||
692 | 418 | nrpe.add_check( | ||
693 | 419 | shortname='haproxy_queue', | ||
694 | 420 | description='Check HAProxy queue depth {%s}' % unit_name, | ||
695 | 421 | check_cmd='check_haproxy_queue_depth.sh') | ||
696 | 422 | |||
697 | 423 | class NRPESet: | ||
698 | 424 | checks = [] | ||
699 | 425 | crons = [] | ||
700 | 426 | nagios_plugins = [] | ||
701 | 427 | def __init__(self, yaml_str='null'): | ||
702 | 428 | init_values = None | ||
703 | 429 | if yaml_str: | ||
704 | 430 | init_values = yaml.safe_load(yaml_str) | ||
705 | 431 | log('NRPESet: init_values={}'.format(init_values)) | ||
706 | 432 | if type(init_values) == type({}): | ||
707 | 433 | self.checks = init_values.get('checks', []) | ||
708 | 434 | self.crons = init_values.get('crons', []) | ||
709 | 435 | self.nagios_plugins = init_values.get('nagios_plugins', []) | ||
710 | 436 | |||
711 | 437 | def add_check(self, shortname, description, check_cmd): | ||
712 | 438 | log('NRPESet: add_check({})'.format((shortname, description, check_cmd))) | ||
713 | 439 | self.checks.append((shortname, description, check_cmd)) | ||
714 | 440 | |||
715 | 441 | def add_init_service_checks(self, services, unit_name=None): | ||
716 | 442 | if not unit_name: | ||
717 | 443 | unit_name = local_unit().replace('/', '-') | ||
718 | 444 | log('NRPESet: add_init_service_checks({}, {})'.format(services, unit_name)) | ||
719 | 445 | add_init_service_checks(self, services, unit_name) | ||
720 | 446 | |||
721 | 447 | def add_cron(self, filename, cron_freq, cron_user, cron_cmd): | ||
722 | 448 | log('NRPESet: add_cron({}, ...)'.format(filename)) | ||
723 | 449 | self.crons.append((filename, cron_freq, cron_user, cron_cmd)) | ||
724 | 450 | |||
725 | 451 | def add_nagios_plugin(self, filename): | ||
726 | 452 | log('NRPESet: add_nagios_plugin({})'.format(filename)) | ||
727 | 453 | self.nagios_plugins.append((filename,)) | ||
728 | 454 | |||
729 | 455 | def __str__(self): | ||
730 | 456 | return yaml.safe_dump({'checks': self.checks, | ||
731 | 457 | 'crons': self.crons, | ||
732 | 458 | 'nagios_plugins': self.nagios_plugins}) | ||
733 | 0 | 459 | ||
734 | === added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py' | |||
735 | --- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000 | |||
736 | +++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2016-01-07 21:40:19 +0000 | |||
737 | @@ -0,0 +1,175 @@ | |||
738 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
739 | 2 | # | ||
740 | 3 | # This file is part of charm-helpers. | ||
741 | 4 | # | ||
742 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
743 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
744 | 7 | # published by the Free Software Foundation. | ||
745 | 8 | # | ||
746 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
747 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
748 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
749 | 12 | # GNU Lesser General Public License for more details. | ||
750 | 13 | # | ||
751 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
752 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
753 | 16 | |||
754 | 17 | ''' | ||
755 | 18 | Functions for managing volumes in juju units. One volume is supported per unit. | ||
756 | 19 | Subordinates may have their own storage, provided it is on its own partition. | ||
757 | 20 | |||
758 | 21 | Configuration stanzas:: | ||
759 | 22 | |||
760 | 23 | volume-ephemeral: | ||
761 | 24 | type: boolean | ||
762 | 25 | default: true | ||
763 | 26 | description: > | ||
764 | 27 | If false, a volume is mounted as sepecified in "volume-map" | ||
765 | 28 | If true, ephemeral storage will be used, meaning that log data | ||
766 | 29 | will only exist as long as the machine. YOU HAVE BEEN WARNED. | ||
767 | 30 | volume-map: | ||
768 | 31 | type: string | ||
769 | 32 | default: {} | ||
770 | 33 | description: > | ||
771 | 34 | YAML map of units to device names, e.g: | ||
772 | 35 | "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }" | ||
773 | 36 | Service units will raise a configure-error if volume-ephemeral | ||
774 | 37 | is 'true' and no volume-map value is set. Use 'juju set' to set a | ||
775 | 38 | value and 'juju resolved' to complete configuration. | ||
776 | 39 | |||
777 | 40 | Usage:: | ||
778 | 41 | |||
779 | 42 | from charmsupport.volumes import configure_volume, VolumeConfigurationError | ||
780 | 43 | from charmsupport.hookenv import log, ERROR | ||
781 | 44 | def post_mount_hook(): | ||
782 | 45 | stop_service('myservice') | ||
783 | 46 | def post_mount_hook(): | ||
784 | 47 | start_service('myservice') | ||
785 | 48 | |||
786 | 49 | if __name__ == '__main__': | ||
787 | 50 | try: | ||
788 | 51 | configure_volume(before_change=pre_mount_hook, | ||
789 | 52 | after_change=post_mount_hook) | ||
790 | 53 | except VolumeConfigurationError: | ||
791 | 54 | log('Storage could not be configured', ERROR) | ||
792 | 55 | |||
793 | 56 | ''' | ||
794 | 57 | |||
795 | 58 | # XXX: Known limitations | ||
796 | 59 | # - fstab is neither consulted nor updated | ||
797 | 60 | |||
798 | 61 | import os | ||
799 | 62 | from charmhelpers.core import hookenv | ||
800 | 63 | from charmhelpers.core import host | ||
801 | 64 | import yaml | ||
802 | 65 | |||
803 | 66 | |||
804 | 67 | MOUNT_BASE = '/srv/juju/volumes' | ||
805 | 68 | |||
806 | 69 | |||
807 | 70 | class VolumeConfigurationError(Exception): | ||
808 | 71 | '''Volume configuration data is missing or invalid''' | ||
809 | 72 | pass | ||
810 | 73 | |||
811 | 74 | |||
812 | 75 | def get_config(): | ||
813 | 76 | '''Gather and sanity-check volume configuration data''' | ||
814 | 77 | volume_config = {} | ||
815 | 78 | config = hookenv.config() | ||
816 | 79 | |||
817 | 80 | errors = False | ||
818 | 81 | |||
819 | 82 | if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'): | ||
820 | 83 | volume_config['ephemeral'] = True | ||
821 | 84 | else: | ||
822 | 85 | volume_config['ephemeral'] = False | ||
823 | 86 | |||
824 | 87 | try: | ||
825 | 88 | volume_map = yaml.safe_load(config.get('volume-map', '{}')) | ||
826 | 89 | except yaml.YAMLError as e: | ||
827 | 90 | hookenv.log("Error parsing YAML volume-map: {}".format(e), | ||
828 | 91 | hookenv.ERROR) | ||
829 | 92 | errors = True | ||
830 | 93 | if volume_map is None: | ||
831 | 94 | # probably an empty string | ||
832 | 95 | volume_map = {} | ||
833 | 96 | elif not isinstance(volume_map, dict): | ||
834 | 97 | hookenv.log("Volume-map should be a dictionary, not {}".format( | ||
835 | 98 | type(volume_map))) | ||
836 | 99 | errors = True | ||
837 | 100 | |||
838 | 101 | volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME']) | ||
839 | 102 | if volume_config['device'] and volume_config['ephemeral']: | ||
840 | 103 | # asked for ephemeral storage but also defined a volume ID | ||
841 | 104 | hookenv.log('A volume is defined for this unit, but ephemeral ' | ||
842 | 105 | 'storage was requested', hookenv.ERROR) | ||
843 | 106 | errors = True | ||
844 | 107 | elif not volume_config['device'] and not volume_config['ephemeral']: | ||
845 | 108 | # asked for permanent storage but did not define volume ID | ||
846 | 109 | hookenv.log('Ephemeral storage was requested, but there is no volume ' | ||
847 | 110 | 'defined for this unit.', hookenv.ERROR) | ||
848 | 111 | errors = True | ||
849 | 112 | |||
850 | 113 | unit_mount_name = hookenv.local_unit().replace('/', '-') | ||
851 | 114 | volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name) | ||
852 | 115 | |||
853 | 116 | if errors: | ||
854 | 117 | return None | ||
855 | 118 | return volume_config | ||
856 | 119 | |||
857 | 120 | |||
858 | 121 | def mount_volume(config): | ||
859 | 122 | if os.path.exists(config['mountpoint']): | ||
860 | 123 | if not os.path.isdir(config['mountpoint']): | ||
861 | 124 | hookenv.log('Not a directory: {}'.format(config['mountpoint'])) | ||
862 | 125 | raise VolumeConfigurationError() | ||
863 | 126 | else: | ||
864 | 127 | host.mkdir(config['mountpoint']) | ||
865 | 128 | if os.path.ismount(config['mountpoint']): | ||
866 | 129 | unmount_volume(config) | ||
867 | 130 | if not host.mount(config['device'], config['mountpoint'], persist=True): | ||
868 | 131 | raise VolumeConfigurationError() | ||
869 | 132 | |||
870 | 133 | |||
871 | 134 | def unmount_volume(config): | ||
872 | 135 | if os.path.ismount(config['mountpoint']): | ||
873 | 136 | if not host.umount(config['mountpoint'], persist=True): | ||
874 | 137 | raise VolumeConfigurationError() | ||
875 | 138 | |||
876 | 139 | |||
877 | 140 | def managed_mounts(): | ||
878 | 141 | '''List of all mounted managed volumes''' | ||
879 | 142 | return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts()) | ||
880 | 143 | |||
881 | 144 | |||
882 | 145 | def configure_volume(before_change=lambda: None, after_change=lambda: None): | ||
883 | 146 | '''Set up storage (or don't) according to the charm's volume configuration. | ||
884 | 147 | Returns the mount point or "ephemeral". before_change and after_change | ||
885 | 148 | are optional functions to be called if the volume configuration changes. | ||
886 | 149 | ''' | ||
887 | 150 | |||
888 | 151 | config = get_config() | ||
889 | 152 | if not config: | ||
890 | 153 | hookenv.log('Failed to read volume configuration', hookenv.CRITICAL) | ||
891 | 154 | raise VolumeConfigurationError() | ||
892 | 155 | |||
893 | 156 | if config['ephemeral']: | ||
894 | 157 | if os.path.ismount(config['mountpoint']): | ||
895 | 158 | before_change() | ||
896 | 159 | unmount_volume(config) | ||
897 | 160 | after_change() | ||
898 | 161 | return 'ephemeral' | ||
899 | 162 | else: | ||
900 | 163 | # persistent storage | ||
901 | 164 | if os.path.ismount(config['mountpoint']): | ||
902 | 165 | mounts = dict(managed_mounts()) | ||
903 | 166 | if mounts.get(config['mountpoint']) != config['device']: | ||
904 | 167 | before_change() | ||
905 | 168 | unmount_volume(config) | ||
906 | 169 | mount_volume(config) | ||
907 | 170 | after_change() | ||
908 | 171 | else: | ||
909 | 172 | before_change() | ||
910 | 173 | mount_volume(config) | ||
911 | 174 | after_change() | ||
912 | 175 | return config['mountpoint'] | ||
913 | 0 | 176 | ||
914 | === modified file 'hooks/neutron_ovs_hooks.py' | |||
915 | --- hooks/neutron_ovs_hooks.py 2015-11-12 09:33:27 +0000 | |||
916 | +++ hooks/neutron_ovs_hooks.py 2016-01-07 21:40:19 +0000 | |||
917 | @@ -1,5 +1,6 @@ | |||
918 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
919 | 2 | 2 | ||
920 | 3 | import os | ||
921 | 3 | import sys | 4 | import sys |
922 | 4 | 5 | ||
923 | 5 | from copy import deepcopy | 6 | from copy import deepcopy |
924 | @@ -16,6 +17,7 @@ | |||
925 | 16 | log, | 17 | log, |
926 | 17 | relation_set, | 18 | relation_set, |
927 | 18 | relation_ids, | 19 | relation_ids, |
928 | 20 | local_unit, | ||
929 | 19 | ) | 21 | ) |
930 | 20 | 22 | ||
931 | 21 | from charmhelpers.core.host import ( | 23 | from charmhelpers.core.host import ( |
932 | @@ -45,6 +47,7 @@ | |||
933 | 45 | REQUIRED_INTERFACES, | 47 | REQUIRED_INTERFACES, |
934 | 46 | check_optional_relations, | 48 | check_optional_relations, |
935 | 47 | ) | 49 | ) |
936 | 50 | from charmhelpers.contrib.charmsupport import nrpe | ||
937 | 48 | 51 | ||
938 | 49 | hooks = Hooks() | 52 | hooks = Hooks() |
939 | 50 | CONFIGS = register_configs() | 53 | CONFIGS = register_configs() |
940 | @@ -72,6 +75,10 @@ | |||
941 | 72 | for rid in relation_ids('neutron-plugin'): | 75 | for rid in relation_ids('neutron-plugin'): |
942 | 73 | neutron_plugin_joined(relation_id=rid) | 76 | neutron_plugin_joined(relation_id=rid) |
943 | 74 | 77 | ||
944 | 78 | if config_value_changed('enable-nrpe-checks'): | ||
945 | 79 | for rid in relation_ids('neutron-plugin'): | ||
946 | 80 | neutron_plugin_joined(rid) | ||
947 | 81 | |||
948 | 75 | 82 | ||
949 | 76 | @hooks.hook('neutron-plugin-api-relation-changed') | 83 | @hooks.hook('neutron-plugin-api-relation-changed') |
950 | 77 | @restart_on_change(restart_map()) | 84 | @restart_on_change(restart_map()) |
951 | @@ -87,6 +94,37 @@ | |||
952 | 87 | neutron_plugin_joined(relation_id=rid) | 94 | neutron_plugin_joined(relation_id=rid) |
953 | 88 | 95 | ||
954 | 89 | 96 | ||
955 | 97 | def neutron_plugin_nrpe_checks(): | ||
956 | 98 | log('neutron_plugin_nrpe_checks: enable-nrpe-checks={}'.format( | ||
957 | 99 | config('enable-nrpe-checks'))) | ||
958 | 100 | if not config('enable-nrpe-checks'): | ||
959 | 101 | return '' | ||
960 | 102 | current_unit = local_unit().replace('/', '-') | ||
961 | 103 | nrpe_set = nrpe.NRPESet() | ||
962 | 104 | nrpe_set.add_nagios_plugin( | ||
963 | 105 | os.path.join(os.getenv('CHARM_DIR'), | ||
964 | 106 | 'files', 'nrpe-external-master', | ||
965 | 107 | 'neutron-check-tun_ids.py')) | ||
966 | 108 | nrpe_set.add_cron( | ||
967 | 109 | filename='nagios-check-tun_ids', | ||
968 | 110 | cron_freq='*/5 * * * *', | ||
969 | 111 | cron_user='root', | ||
970 | 112 | cron_cmd='{}/neutron-check-tun_ids.py > ' | ||
971 | 113 | '/var/lib/nagios/neutron-check-tun_ids.txt' | ||
972 | 114 | ''.format(nrpe.NAGIOS_PLUGINS), | ||
973 | 115 | ) | ||
974 | 116 | nrpe_set.add_check( | ||
975 | 117 | shortname='neutron_tun_ids', | ||
976 | 118 | description='Check neutron ovs tun_ids {%s}' % current_unit, | ||
977 | 119 | check_cmd='check_status_file.py -f ' | ||
978 | 120 | '/var/lib/nagios/neutron-check-tun_ids.txt' | ||
979 | 121 | ) | ||
980 | 122 | nrpe_set.add_init_service_checks( | ||
981 | 123 | ['openvswitch-switch', 'neutron-plugin-openvswitch-agent']) | ||
982 | 124 | log('neutron_plugin_nrpe_checks: nrpe_set={}'.format(str(nrpe_set))) | ||
983 | 125 | return str(nrpe_set) | ||
984 | 126 | |||
985 | 127 | |||
986 | 90 | @hooks.hook('neutron-plugin-relation-joined') | 128 | @hooks.hook('neutron-plugin-relation-joined') |
987 | 91 | def neutron_plugin_joined(relation_id=None): | 129 | def neutron_plugin_joined(relation_id=None): |
988 | 92 | if enable_local_dhcp(): | 130 | if enable_local_dhcp(): |
989 | @@ -100,8 +138,10 @@ | |||
990 | 100 | pkgs.extend(METADATA_PACKAGES) | 138 | pkgs.extend(METADATA_PACKAGES) |
991 | 101 | purge_packages(pkgs) | 139 | purge_packages(pkgs) |
992 | 102 | secret = get_shared_secret() if enable_nova_metadata() else None | 140 | secret = get_shared_secret() if enable_nova_metadata() else None |
993 | 141 | nrpe_checks = neutron_plugin_nrpe_checks() | ||
994 | 103 | rel_data = { | 142 | rel_data = { |
995 | 104 | 'metadata-shared-secret': secret, | 143 | 'metadata-shared-secret': secret, |
996 | 144 | 'nrpe-checks': nrpe_checks, | ||
997 | 105 | } | 145 | } |
998 | 106 | relation_set(relation_id=relation_id, **rel_data) | 146 | relation_set(relation_id=relation_id, **rel_data) |
999 | 107 | 147 |