Merge lp:~brad-marshall/charms/trusty/rabbitmq-server/nagios-fixes-sync-charmhelpers into lp:charms/trusty/rabbitmq-server
- Trusty Tahr (14.04)
- nagios-fixes-sync-charmhelpers
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 85 |
Proposed branch: | lp:~brad-marshall/charms/trusty/rabbitmq-server/nagios-fixes-sync-charmhelpers |
Merge into: | lp:charms/trusty/rabbitmq-server |
Diff against target: |
1626 lines (+1012/-144) 29 files modified
charm-helpers.yaml (+1/-1) config.yaml (+6/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+41/-7) hooks/charmhelpers/contrib/hahelpers/cluster.py (+5/-1) hooks/charmhelpers/contrib/network/ip.py (+84/-1) hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+5/-2) hooks/charmhelpers/contrib/openstack/context.py (+29/-10) hooks/charmhelpers/contrib/openstack/files/__init__.py (+18/-0) hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh (+32/-0) hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh (+30/-0) hooks/charmhelpers/contrib/openstack/ip.py (+37/-0) hooks/charmhelpers/contrib/openstack/templates/ceph.conf (+15/-0) hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg (+58/-0) hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend (+24/-0) hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf (+24/-0) hooks/charmhelpers/contrib/openstack/templates/zeromq (+14/-0) hooks/charmhelpers/contrib/openstack/utils.py (+7/-72) hooks/charmhelpers/contrib/python/packages.py (+2/-2) hooks/charmhelpers/core/fstab.py (+4/-4) hooks/charmhelpers/core/host.py (+5/-5) hooks/charmhelpers/core/services/helpers.py (+12/-4) hooks/charmhelpers/core/strutils.py (+42/-0) hooks/charmhelpers/core/sysctl.py (+13/-7) hooks/charmhelpers/core/templating.py (+3/-3) hooks/charmhelpers/core/unitdata.py (+477/-0) hooks/charmhelpers/fetch/archiveurl.py (+10/-10) hooks/charmhelpers/fetch/giturl.py (+1/-1) hooks/rabbit_utils.py (+8/-0) hooks/rabbitmq_server_relations.py (+5/-14) |
To merge this branch: | bzr merge lp:~brad-marshall/charms/trusty/rabbitmq-server/nagios-fixes-sync-charmhelpers |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marco Ceppi (community) | Approve | ||
Adam Israel (community) | Approve | ||
Review Queue (community) | automated testing | Needs Fixing | |
OpenStack Charmers | Pending | ||
Review via email: mp+251551@code.launchpad.net |
Commit message
Description of the change
Add nagios-servicegroup config option, add nrpe check for service
uosci-testing-bot (uosci-testing-bot) wrote : | # |
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #2393 rabbitmq-server for brad-marshall mp251551
LINT FAIL: lint-test failed
LINT Results (max last 2 lines):
unit_
make: *** [lint] Error 1
Full lint test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #2313 rabbitmq-server for brad-marshall mp251551
AMULET FAIL: amulet-test failed
AMULET Results (max last 2 lines):
ERROR subprocess encountered error code 1
make: *** [functional_test] Error 1
Full amulet test output: http://
Build: http://
- 86. By Brad Marshall
-
[bradm] Remove init nrpe checks, something isn't quite right with them for the checks.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #2491 rabbitmq-server for brad-marshall mp251551
LINT FAIL: lint-test failed
LINT Results (max last 2 lines):
unit_
make: *** [lint] Error 1
Full lint test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #2363 rabbitmq-server for brad-marshall mp251551
AMULET FAIL: amulet-test failed
AMULET Results (max last 2 lines):
ERROR subprocess encountered error code 1
make: *** [functional_test] Error 1
Full amulet test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #2281 rabbitmq-server for brad-marshall mp251551
UNIT OK: passed
Review Queue (review-queue) wrote : | # |
This items has failed automated testing! Results available here http://
Antonio Rosales (arosales) wrote : | # |
Running /tmp/bundletest
Testing ssl connection to rabbitmq-server.
Failed to create an ssl connection to 54.173.110.197:5671
[SSL: CERTIFICATE_
This also appears to be occurring across the other substrates.
@Brad,
Are you seeing this in your testing also?
-thanks,
Antonio
Adam Israel (aisrael) wrote : | # |
Hi Brad,
Thanks for your work on improving the rabbitmq-server charm. I reviewed the merge proposal and your changes look solid. There are issues with the unit tests upstream, and bugs have been opened against those. I don't see anything in this MP that would caused the test failures, so you have a +1 from me.
Preview Diff
1 | === modified file 'charm-helpers.yaml' | |||
2 | --- charm-helpers.yaml 2015-01-23 08:23:05 +0000 | |||
3 | +++ charm-helpers.yaml 2015-03-06 06:41:14 +0000 | |||
4 | @@ -4,7 +4,7 @@ | |||
5 | 4 | - fetch | 4 | - fetch |
6 | 5 | - core | 5 | - core |
7 | 6 | - contrib.charmsupport | 6 | - contrib.charmsupport |
9 | 7 | - contrib.openstack | 7 | - contrib.openstack|inc=* |
10 | 8 | - contrib.storage | 8 | - contrib.storage |
11 | 9 | - contrib.peerstorage | 9 | - contrib.peerstorage |
12 | 10 | - contrib.python.packages | 10 | - contrib.python.packages |
13 | 11 | 11 | ||
14 | === modified file 'config.yaml' | |||
15 | --- config.yaml 2015-01-23 08:28:27 +0000 | |||
16 | +++ config.yaml 2015-03-06 06:41:14 +0000 | |||
17 | @@ -45,6 +45,12 @@ | |||
18 | 45 | juju-myservice-0 | 45 | juju-myservice-0 |
19 | 46 | If you're running multiple environments with the same services in them | 46 | If you're running multiple environments with the same services in them |
20 | 47 | this allows you to differentiate between them. | 47 | this allows you to differentiate between them. |
21 | 48 | nagios_servicegroups: | ||
22 | 49 | default: "" | ||
23 | 50 | type: string | ||
24 | 51 | description: | | ||
25 | 52 | A comma-separated list of nagios servicegroups. | ||
26 | 53 | If left empty, the nagios_context will be used as the servicegroup | ||
27 | 48 | # HA configuration settings | 54 | # HA configuration settings |
28 | 49 | vip: | 55 | vip: |
29 | 50 | type: string | 56 | type: string |
30 | 51 | 57 | ||
31 | === modified file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
32 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-01-26 09:45:59 +0000 | |||
33 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-03-06 06:41:14 +0000 | |||
34 | @@ -24,6 +24,8 @@ | |||
35 | 24 | import pwd | 24 | import pwd |
36 | 25 | import grp | 25 | import grp |
37 | 26 | import os | 26 | import os |
38 | 27 | import glob | ||
39 | 28 | import shutil | ||
40 | 27 | import re | 29 | import re |
41 | 28 | import shlex | 30 | import shlex |
42 | 29 | import yaml | 31 | import yaml |
43 | @@ -161,7 +163,7 @@ | |||
44 | 161 | log('Check command not found: {}'.format(parts[0])) | 163 | log('Check command not found: {}'.format(parts[0])) |
45 | 162 | return '' | 164 | return '' |
46 | 163 | 165 | ||
48 | 164 | def write(self, nagios_context, hostname, nagios_servicegroups=None): | 166 | def write(self, nagios_context, hostname, nagios_servicegroups): |
49 | 165 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | 167 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( |
50 | 166 | self.command) | 168 | self.command) |
51 | 167 | with open(nrpe_check_file, 'w') as nrpe_check_config: | 169 | with open(nrpe_check_file, 'w') as nrpe_check_config: |
52 | @@ -177,14 +179,11 @@ | |||
53 | 177 | nagios_servicegroups) | 179 | nagios_servicegroups) |
54 | 178 | 180 | ||
55 | 179 | def write_service_config(self, nagios_context, hostname, | 181 | def write_service_config(self, nagios_context, hostname, |
57 | 180 | nagios_servicegroups=None): | 182 | nagios_servicegroups): |
58 | 181 | for f in os.listdir(NRPE.nagios_exportdir): | 183 | for f in os.listdir(NRPE.nagios_exportdir): |
59 | 182 | if re.search('.*{}.cfg'.format(self.command), f): | 184 | if re.search('.*{}.cfg'.format(self.command), f): |
60 | 183 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | 185 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) |
61 | 184 | 186 | ||
62 | 185 | if not nagios_servicegroups: | ||
63 | 186 | nagios_servicegroups = nagios_context | ||
64 | 187 | |||
65 | 188 | templ_vars = { | 187 | templ_vars = { |
66 | 189 | 'nagios_hostname': hostname, | 188 | 'nagios_hostname': hostname, |
67 | 190 | 'nagios_servicegroup': nagios_servicegroups, | 189 | 'nagios_servicegroup': nagios_servicegroups, |
68 | @@ -211,10 +210,10 @@ | |||
69 | 211 | super(NRPE, self).__init__() | 210 | super(NRPE, self).__init__() |
70 | 212 | self.config = config() | 211 | self.config = config() |
71 | 213 | self.nagios_context = self.config['nagios_context'] | 212 | self.nagios_context = self.config['nagios_context'] |
73 | 214 | if 'nagios_servicegroups' in self.config: | 213 | if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']: |
74 | 215 | self.nagios_servicegroups = self.config['nagios_servicegroups'] | 214 | self.nagios_servicegroups = self.config['nagios_servicegroups'] |
75 | 216 | else: | 215 | else: |
77 | 217 | self.nagios_servicegroups = 'juju' | 216 | self.nagios_servicegroups = self.nagios_context |
78 | 218 | self.unit_name = local_unit().replace('/', '-') | 217 | self.unit_name = local_unit().replace('/', '-') |
79 | 219 | if hostname: | 218 | if hostname: |
80 | 220 | self.hostname = hostname | 219 | self.hostname = hostname |
81 | @@ -322,3 +321,38 @@ | |||
82 | 322 | check_cmd='check_status_file.py -f ' | 321 | check_cmd='check_status_file.py -f ' |
83 | 323 | '/var/lib/nagios/service-check-%s.txt' % svc, | 322 | '/var/lib/nagios/service-check-%s.txt' % svc, |
84 | 324 | ) | 323 | ) |
85 | 324 | |||
86 | 325 | |||
87 | 326 | def copy_nrpe_checks(): | ||
88 | 327 | """ | ||
89 | 328 | Copy the nrpe checks into place | ||
90 | 329 | |||
91 | 330 | """ | ||
92 | 331 | NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' | ||
93 | 332 | nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks', | ||
94 | 333 | 'charmhelpers', 'contrib', 'openstack', | ||
95 | 334 | 'files') | ||
96 | 335 | |||
97 | 336 | if not os.path.exists(NAGIOS_PLUGINS): | ||
98 | 337 | os.makedirs(NAGIOS_PLUGINS) | ||
99 | 338 | for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")): | ||
100 | 339 | if os.path.isfile(fname): | ||
101 | 340 | shutil.copy2(fname, | ||
102 | 341 | os.path.join(NAGIOS_PLUGINS, os.path.basename(fname))) | ||
103 | 342 | |||
104 | 343 | |||
105 | 344 | def add_haproxy_checks(nrpe, unit_name): | ||
106 | 345 | """ | ||
107 | 346 | Add checks for each service in list | ||
108 | 347 | |||
109 | 348 | :param NRPE nrpe: NRPE object to add check to | ||
110 | 349 | :param str unit_name: Unit name to use in check description | ||
111 | 350 | """ | ||
112 | 351 | nrpe.add_check( | ||
113 | 352 | shortname='haproxy_servers', | ||
114 | 353 | description='Check HAProxy {%s}' % unit_name, | ||
115 | 354 | check_cmd='check_haproxy.sh') | ||
116 | 355 | nrpe.add_check( | ||
117 | 356 | shortname='haproxy_queue', | ||
118 | 357 | description='Check HAProxy queue depth {%s}' % unit_name, | ||
119 | 358 | check_cmd='check_haproxy_queue_depth.sh') | ||
120 | 325 | 359 | ||
121 | === modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py' | |||
122 | --- hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-01-26 09:45:59 +0000 | |||
123 | +++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-03-06 06:41:14 +0000 | |||
124 | @@ -48,6 +48,9 @@ | |||
125 | 48 | from charmhelpers.core.decorators import ( | 48 | from charmhelpers.core.decorators import ( |
126 | 49 | retry_on_exception, | 49 | retry_on_exception, |
127 | 50 | ) | 50 | ) |
128 | 51 | from charmhelpers.core.strutils import ( | ||
129 | 52 | bool_from_string, | ||
130 | 53 | ) | ||
131 | 51 | 54 | ||
132 | 52 | 55 | ||
133 | 53 | class HAIncompleteConfig(Exception): | 56 | class HAIncompleteConfig(Exception): |
134 | @@ -164,7 +167,8 @@ | |||
135 | 164 | . | 167 | . |
136 | 165 | returns: boolean | 168 | returns: boolean |
137 | 166 | ''' | 169 | ''' |
139 | 167 | if config_get('use-https') == "yes": | 170 | use_https = config_get('use-https') |
140 | 171 | if use_https and bool_from_string(use_https): | ||
141 | 168 | return True | 172 | return True |
142 | 169 | if config_get('ssl_cert') and config_get('ssl_key'): | 173 | if config_get('ssl_cert') and config_get('ssl_key'): |
143 | 170 | return True | 174 | return True |
144 | 171 | 175 | ||
145 | === modified file 'hooks/charmhelpers/contrib/network/ip.py' | |||
146 | --- hooks/charmhelpers/contrib/network/ip.py 2015-01-26 09:45:59 +0000 | |||
147 | +++ hooks/charmhelpers/contrib/network/ip.py 2015-03-06 06:41:14 +0000 | |||
148 | @@ -17,13 +17,16 @@ | |||
149 | 17 | import glob | 17 | import glob |
150 | 18 | import re | 18 | import re |
151 | 19 | import subprocess | 19 | import subprocess |
152 | 20 | import six | ||
153 | 21 | import socket | ||
154 | 20 | 22 | ||
155 | 21 | from functools import partial | 23 | from functools import partial |
156 | 22 | 24 | ||
157 | 23 | from charmhelpers.core.hookenv import unit_get | 25 | from charmhelpers.core.hookenv import unit_get |
158 | 24 | from charmhelpers.fetch import apt_install | 26 | from charmhelpers.fetch import apt_install |
159 | 25 | from charmhelpers.core.hookenv import ( | 27 | from charmhelpers.core.hookenv import ( |
161 | 26 | log | 28 | log, |
162 | 29 | WARNING, | ||
163 | 27 | ) | 30 | ) |
164 | 28 | 31 | ||
165 | 29 | try: | 32 | try: |
166 | @@ -365,3 +368,83 @@ | |||
167 | 365 | return True | 368 | return True |
168 | 366 | 369 | ||
169 | 367 | return False | 370 | return False |
170 | 371 | |||
171 | 372 | |||
172 | 373 | def is_ip(address): | ||
173 | 374 | """ | ||
174 | 375 | Returns True if address is a valid IP address. | ||
175 | 376 | """ | ||
176 | 377 | try: | ||
177 | 378 | # Test to see if already an IPv4 address | ||
178 | 379 | socket.inet_aton(address) | ||
179 | 380 | return True | ||
180 | 381 | except socket.error: | ||
181 | 382 | return False | ||
182 | 383 | |||
183 | 384 | |||
184 | 385 | def ns_query(address): | ||
185 | 386 | try: | ||
186 | 387 | import dns.resolver | ||
187 | 388 | except ImportError: | ||
188 | 389 | apt_install('python-dnspython') | ||
189 | 390 | import dns.resolver | ||
190 | 391 | |||
191 | 392 | if isinstance(address, dns.name.Name): | ||
192 | 393 | rtype = 'PTR' | ||
193 | 394 | elif isinstance(address, six.string_types): | ||
194 | 395 | rtype = 'A' | ||
195 | 396 | else: | ||
196 | 397 | return None | ||
197 | 398 | |||
198 | 399 | answers = dns.resolver.query(address, rtype) | ||
199 | 400 | if answers: | ||
200 | 401 | return str(answers[0]) | ||
201 | 402 | return None | ||
202 | 403 | |||
203 | 404 | |||
204 | 405 | def get_host_ip(hostname, fallback=None): | ||
205 | 406 | """ | ||
206 | 407 | Resolves the IP for a given hostname, or returns | ||
207 | 408 | the input if it is already an IP. | ||
208 | 409 | """ | ||
209 | 410 | if is_ip(hostname): | ||
210 | 411 | return hostname | ||
211 | 412 | |||
212 | 413 | ip_addr = ns_query(hostname) | ||
213 | 414 | if not ip_addr: | ||
214 | 415 | try: | ||
215 | 416 | ip_addr = socket.gethostbyname(hostname) | ||
216 | 417 | except: | ||
217 | 418 | log("Failed to resolve hostname '%s'" % (hostname), | ||
218 | 419 | level=WARNING) | ||
219 | 420 | return fallback | ||
220 | 421 | return ip_addr | ||
221 | 422 | |||
222 | 423 | |||
223 | 424 | def get_hostname(address, fqdn=True): | ||
224 | 425 | """ | ||
225 | 426 | Resolves hostname for given IP, or returns the input | ||
226 | 427 | if it is already a hostname. | ||
227 | 428 | """ | ||
228 | 429 | if is_ip(address): | ||
229 | 430 | try: | ||
230 | 431 | import dns.reversename | ||
231 | 432 | except ImportError: | ||
232 | 433 | apt_install("python-dnspython") | ||
233 | 434 | import dns.reversename | ||
234 | 435 | |||
235 | 436 | rev = dns.reversename.from_address(address) | ||
236 | 437 | result = ns_query(rev) | ||
237 | 438 | if not result: | ||
238 | 439 | return None | ||
239 | 440 | else: | ||
240 | 441 | result = address | ||
241 | 442 | |||
242 | 443 | if fqdn: | ||
243 | 444 | # strip trailing . | ||
244 | 445 | if result.endswith('.'): | ||
245 | 446 | return result[:-1] | ||
246 | 447 | else: | ||
247 | 448 | return result | ||
248 | 449 | else: | ||
249 | 450 | return result.split('.')[0] | ||
250 | 368 | 451 | ||
251 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
252 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:45:59 +0000 | |||
253 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-03-06 06:41:14 +0000 | |||
254 | @@ -71,16 +71,19 @@ | |||
255 | 71 | services.append(this_service) | 71 | services.append(this_service) |
256 | 72 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', | 72 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
257 | 73 | 'ceph-osd', 'ceph-radosgw'] | 73 | 'ceph-osd', 'ceph-radosgw'] |
258 | 74 | # Openstack subordinate charms do not expose an origin option as that | ||
259 | 75 | # is controlled by the principle | ||
260 | 76 | ignore = ['neutron-openvswitch'] | ||
261 | 74 | 77 | ||
262 | 75 | if self.openstack: | 78 | if self.openstack: |
263 | 76 | for svc in services: | 79 | for svc in services: |
265 | 77 | if svc['name'] not in use_source: | 80 | if svc['name'] not in use_source + ignore: |
266 | 78 | config = {'openstack-origin': self.openstack} | 81 | config = {'openstack-origin': self.openstack} |
267 | 79 | self.d.configure(svc['name'], config) | 82 | self.d.configure(svc['name'], config) |
268 | 80 | 83 | ||
269 | 81 | if self.source: | 84 | if self.source: |
270 | 82 | for svc in services: | 85 | for svc in services: |
272 | 83 | if svc['name'] in use_source: | 86 | if svc['name'] in use_source and svc['name'] not in ignore: |
273 | 84 | config = {'source': self.source} | 87 | config = {'source': self.source} |
274 | 85 | self.d.configure(svc['name'], config) | 88 | self.d.configure(svc['name'], config) |
275 | 86 | 89 | ||
276 | 87 | 90 | ||
277 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' | |||
278 | --- hooks/charmhelpers/contrib/openstack/context.py 2015-01-26 09:45:59 +0000 | |||
279 | +++ hooks/charmhelpers/contrib/openstack/context.py 2015-03-06 06:41:14 +0000 | |||
280 | @@ -191,7 +191,7 @@ | |||
281 | 191 | unit=local_unit()) | 191 | unit=local_unit()) |
282 | 192 | if set_hostname != access_hostname: | 192 | if set_hostname != access_hostname: |
283 | 193 | relation_set(relation_settings={hostname_key: access_hostname}) | 193 | relation_set(relation_settings={hostname_key: access_hostname}) |
285 | 194 | return ctxt # Defer any further hook execution for now.... | 194 | return None # Defer any further hook execution for now.... |
286 | 195 | 195 | ||
287 | 196 | password_setting = 'password' | 196 | password_setting = 'password' |
288 | 197 | if self.relation_prefix: | 197 | if self.relation_prefix: |
289 | @@ -279,9 +279,25 @@ | |||
290 | 279 | class IdentityServiceContext(OSContextGenerator): | 279 | class IdentityServiceContext(OSContextGenerator): |
291 | 280 | interfaces = ['identity-service'] | 280 | interfaces = ['identity-service'] |
292 | 281 | 281 | ||
293 | 282 | def __init__(self, service=None, service_user=None): | ||
294 | 283 | self.service = service | ||
295 | 284 | self.service_user = service_user | ||
296 | 285 | |||
297 | 282 | def __call__(self): | 286 | def __call__(self): |
298 | 283 | log('Generating template context for identity-service', level=DEBUG) | 287 | log('Generating template context for identity-service', level=DEBUG) |
299 | 284 | ctxt = {} | 288 | ctxt = {} |
300 | 289 | |||
301 | 290 | if self.service and self.service_user: | ||
302 | 291 | # This is required for pki token signing if we don't want /tmp to | ||
303 | 292 | # be used. | ||
304 | 293 | cachedir = '/var/cache/%s' % (self.service) | ||
305 | 294 | if not os.path.isdir(cachedir): | ||
306 | 295 | log("Creating service cache dir %s" % (cachedir), level=DEBUG) | ||
307 | 296 | mkdir(path=cachedir, owner=self.service_user, | ||
308 | 297 | group=self.service_user, perms=0o700) | ||
309 | 298 | |||
310 | 299 | ctxt['signing_dir'] = cachedir | ||
311 | 300 | |||
312 | 285 | for rid in relation_ids('identity-service'): | 301 | for rid in relation_ids('identity-service'): |
313 | 286 | for unit in related_units(rid): | 302 | for unit in related_units(rid): |
314 | 287 | rdata = relation_get(rid=rid, unit=unit) | 303 | rdata = relation_get(rid=rid, unit=unit) |
315 | @@ -291,15 +307,16 @@ | |||
316 | 291 | auth_host = format_ipv6_addr(auth_host) or auth_host | 307 | auth_host = format_ipv6_addr(auth_host) or auth_host |
317 | 292 | svc_protocol = rdata.get('service_protocol') or 'http' | 308 | svc_protocol = rdata.get('service_protocol') or 'http' |
318 | 293 | auth_protocol = rdata.get('auth_protocol') or 'http' | 309 | auth_protocol = rdata.get('auth_protocol') or 'http' |
328 | 294 | ctxt = {'service_port': rdata.get('service_port'), | 310 | ctxt.update({'service_port': rdata.get('service_port'), |
329 | 295 | 'service_host': serv_host, | 311 | 'service_host': serv_host, |
330 | 296 | 'auth_host': auth_host, | 312 | 'auth_host': auth_host, |
331 | 297 | 'auth_port': rdata.get('auth_port'), | 313 | 'auth_port': rdata.get('auth_port'), |
332 | 298 | 'admin_tenant_name': rdata.get('service_tenant'), | 314 | 'admin_tenant_name': rdata.get('service_tenant'), |
333 | 299 | 'admin_user': rdata.get('service_username'), | 315 | 'admin_user': rdata.get('service_username'), |
334 | 300 | 'admin_password': rdata.get('service_password'), | 316 | 'admin_password': rdata.get('service_password'), |
335 | 301 | 'service_protocol': svc_protocol, | 317 | 'service_protocol': svc_protocol, |
336 | 302 | 'auth_protocol': auth_protocol} | 318 | 'auth_protocol': auth_protocol}) |
337 | 319 | |||
338 | 303 | if context_complete(ctxt): | 320 | if context_complete(ctxt): |
339 | 304 | # NOTE(jamespage) this is required for >= icehouse | 321 | # NOTE(jamespage) this is required for >= icehouse |
340 | 305 | # so a missing value just indicates keystone needs | 322 | # so a missing value just indicates keystone needs |
341 | @@ -1021,6 +1038,8 @@ | |||
342 | 1021 | for unit in related_units(rid): | 1038 | for unit in related_units(rid): |
343 | 1022 | ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) | 1039 | ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) |
344 | 1023 | ctxt['zmq_host'] = relation_get('host', unit, rid) | 1040 | ctxt['zmq_host'] = relation_get('host', unit, rid) |
345 | 1041 | ctxt['zmq_redis_address'] = relation_get( | ||
346 | 1042 | 'zmq_redis_address', unit, rid) | ||
347 | 1024 | 1043 | ||
348 | 1025 | return ctxt | 1044 | return ctxt |
349 | 1026 | 1045 | ||
350 | 1027 | 1046 | ||
351 | === added directory 'hooks/charmhelpers/contrib/openstack/files' | |||
352 | === added file 'hooks/charmhelpers/contrib/openstack/files/__init__.py' | |||
353 | --- hooks/charmhelpers/contrib/openstack/files/__init__.py 1970-01-01 00:00:00 +0000 | |||
354 | +++ hooks/charmhelpers/contrib/openstack/files/__init__.py 2015-03-06 06:41:14 +0000 | |||
355 | @@ -0,0 +1,18 @@ | |||
356 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
357 | 2 | # | ||
358 | 3 | # This file is part of charm-helpers. | ||
359 | 4 | # | ||
360 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
361 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
362 | 7 | # published by the Free Software Foundation. | ||
363 | 8 | # | ||
364 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
365 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
366 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
367 | 12 | # GNU Lesser General Public License for more details. | ||
368 | 13 | # | ||
369 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
370 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
371 | 16 | |||
372 | 17 | # dummy __init__.py to fool syncer into thinking this is a syncable python | ||
373 | 18 | # module | ||
374 | 0 | 19 | ||
375 | === added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh' | |||
376 | --- hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 1970-01-01 00:00:00 +0000 | |||
377 | +++ hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 2015-03-06 06:41:14 +0000 | |||
378 | @@ -0,0 +1,32 @@ | |||
379 | 1 | #!/bin/bash | ||
380 | 2 | #-------------------------------------------- | ||
381 | 3 | # This file is managed by Juju | ||
382 | 4 | #-------------------------------------------- | ||
383 | 5 | # | ||
384 | 6 | # Copyright 2009,2012 Canonical Ltd. | ||
385 | 7 | # Author: Tom Haddon | ||
386 | 8 | |||
387 | 9 | CRITICAL=0 | ||
388 | 10 | NOTACTIVE='' | ||
389 | 11 | LOGFILE=/var/log/nagios/check_haproxy.log | ||
390 | 12 | AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}') | ||
391 | 13 | |||
392 | 14 | for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'}); | ||
393 | 15 | do | ||
394 | 16 | output=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 --regex="class=\"(active|backup)(2|3).*${appserver}" -e ' 200 OK') | ||
395 | 17 | if [ $? != 0 ]; then | ||
396 | 18 | date >> $LOGFILE | ||
397 | 19 | echo $output >> $LOGFILE | ||
398 | 20 | /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -v | grep $appserver >> $LOGFILE 2>&1 | ||
399 | 21 | CRITICAL=1 | ||
400 | 22 | NOTACTIVE="${NOTACTIVE} $appserver" | ||
401 | 23 | fi | ||
402 | 24 | done | ||
403 | 25 | |||
404 | 26 | if [ $CRITICAL = 1 ]; then | ||
405 | 27 | echo "CRITICAL:${NOTACTIVE}" | ||
406 | 28 | exit 2 | ||
407 | 29 | fi | ||
408 | 30 | |||
409 | 31 | echo "OK: All haproxy instances looking good" | ||
410 | 32 | exit 0 | ||
411 | 0 | 33 | ||
412 | === added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh' | |||
413 | --- hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000 | |||
414 | +++ hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 2015-03-06 06:41:14 +0000 | |||
415 | @@ -0,0 +1,30 @@ | |||
416 | 1 | #!/bin/bash | ||
417 | 2 | #-------------------------------------------- | ||
418 | 3 | # This file is managed by Juju | ||
419 | 4 | #-------------------------------------------- | ||
420 | 5 | # | ||
421 | 6 | # Copyright 2009,2012 Canonical Ltd. | ||
422 | 7 | # Author: Tom Haddon | ||
423 | 8 | |||
424 | 9 | # These should be config options at some stage | ||
425 | 10 | CURRQthrsh=0 | ||
426 | 11 | MAXQthrsh=100 | ||
427 | 12 | |||
428 | 13 | AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}') | ||
429 | 14 | |||
430 | 15 | HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v) | ||
431 | 16 | |||
432 | 17 | for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}') | ||
433 | 18 | do | ||
434 | 19 | CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3) | ||
435 | 20 | MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4) | ||
436 | 21 | |||
437 | 22 | if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then | ||
438 | 23 | echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ" | ||
439 | 24 | exit 2 | ||
440 | 25 | fi | ||
441 | 26 | done | ||
442 | 27 | |||
443 | 28 | echo "OK: All haproxy queue depths looking good" | ||
444 | 29 | exit 0 | ||
445 | 30 | |||
446 | 0 | 31 | ||
447 | === modified file 'hooks/charmhelpers/contrib/openstack/ip.py' | |||
448 | --- hooks/charmhelpers/contrib/openstack/ip.py 2015-01-26 09:45:59 +0000 | |||
449 | +++ hooks/charmhelpers/contrib/openstack/ip.py 2015-03-06 06:41:14 +0000 | |||
450 | @@ -26,6 +26,8 @@ | |||
451 | 26 | ) | 26 | ) |
452 | 27 | from charmhelpers.contrib.hahelpers.cluster import is_clustered | 27 | from charmhelpers.contrib.hahelpers.cluster import is_clustered |
453 | 28 | 28 | ||
454 | 29 | from functools import partial | ||
455 | 30 | |||
456 | 29 | PUBLIC = 'public' | 31 | PUBLIC = 'public' |
457 | 30 | INTERNAL = 'int' | 32 | INTERNAL = 'int' |
458 | 31 | ADMIN = 'admin' | 33 | ADMIN = 'admin' |
459 | @@ -107,3 +109,38 @@ | |||
460 | 107 | "clustered=%s)" % (net_type, clustered)) | 109 | "clustered=%s)" % (net_type, clustered)) |
461 | 108 | 110 | ||
462 | 109 | return resolved_address | 111 | return resolved_address |
463 | 112 | |||
464 | 113 | |||
465 | 114 | def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC, | ||
466 | 115 | override=None): | ||
467 | 116 | """Returns the correct endpoint URL to advertise to Keystone. | ||
468 | 117 | |||
469 | 118 | This method provides the correct endpoint URL which should be advertised to | ||
470 | 119 | the keystone charm for endpoint creation. This method allows for the url to | ||
471 | 120 | be overridden to force a keystone endpoint to have specific URL for any of | ||
472 | 121 | the defined scopes (admin, internal, public). | ||
473 | 122 | |||
474 | 123 | :param configs: OSTemplateRenderer config templating object to inspect | ||
475 | 124 | for a complete https context. | ||
476 | 125 | :param url_template: str format string for creating the url template. Only | ||
477 | 126 | two values will be passed - the scheme+hostname | ||
478 | 127 | returned by the canonical_url and the port. | ||
479 | 128 | :param endpoint_type: str endpoint type to resolve. | ||
480 | 129 | :param override: str the name of the config option which overrides the | ||
481 | 130 | endpoint URL defined by the charm itself. None will | ||
482 | 131 | disable any overrides (default). | ||
483 | 132 | """ | ||
484 | 133 | if override: | ||
485 | 134 | # Return any user-defined overrides for the keystone endpoint URL. | ||
486 | 135 | user_value = config(override) | ||
487 | 136 | if user_value: | ||
488 | 137 | return user_value.strip() | ||
489 | 138 | |||
490 | 139 | return url_template % (canonical_url(configs, endpoint_type), port) | ||
491 | 140 | |||
492 | 141 | |||
493 | 142 | public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC) | ||
494 | 143 | |||
495 | 144 | internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL) | ||
496 | 145 | |||
497 | 146 | admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN) | ||
498 | 110 | 147 | ||
499 | === added file 'hooks/charmhelpers/contrib/openstack/templates/ceph.conf' | |||
500 | --- hooks/charmhelpers/contrib/openstack/templates/ceph.conf 1970-01-01 00:00:00 +0000 | |||
501 | +++ hooks/charmhelpers/contrib/openstack/templates/ceph.conf 2015-03-06 06:41:14 +0000 | |||
502 | @@ -0,0 +1,15 @@ | |||
503 | 1 | ############################################################################### | ||
504 | 2 | # [ WARNING ] | ||
505 | 3 | # cinder configuration file maintained by Juju | ||
506 | 4 | # local changes may be overwritten. | ||
507 | 5 | ############################################################################### | ||
508 | 6 | [global] | ||
509 | 7 | {% if auth -%} | ||
510 | 8 | auth_supported = {{ auth }} | ||
511 | 9 | keyring = /etc/ceph/$cluster.$name.keyring | ||
512 | 10 | mon host = {{ mon_hosts }} | ||
513 | 11 | {% endif -%} | ||
514 | 12 | log to syslog = {{ use_syslog }} | ||
515 | 13 | err to syslog = {{ use_syslog }} | ||
516 | 14 | clog to syslog = {{ use_syslog }} | ||
517 | 15 | |||
518 | 0 | 16 | ||
519 | === added file 'hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg' | |||
520 | --- hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 1970-01-01 00:00:00 +0000 | |||
521 | +++ hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 2015-03-06 06:41:14 +0000 | |||
522 | @@ -0,0 +1,58 @@ | |||
523 | 1 | global | ||
524 | 2 | log {{ local_host }} local0 | ||
525 | 3 | log {{ local_host }} local1 notice | ||
526 | 4 | maxconn 20000 | ||
527 | 5 | user haproxy | ||
528 | 6 | group haproxy | ||
529 | 7 | spread-checks 0 | ||
530 | 8 | |||
531 | 9 | defaults | ||
532 | 10 | log global | ||
533 | 11 | mode tcp | ||
534 | 12 | option tcplog | ||
535 | 13 | option dontlognull | ||
536 | 14 | retries 3 | ||
537 | 15 | timeout queue 1000 | ||
538 | 16 | timeout connect 1000 | ||
539 | 17 | {% if haproxy_client_timeout -%} | ||
540 | 18 | timeout client {{ haproxy_client_timeout }} | ||
541 | 19 | {% else -%} | ||
542 | 20 | timeout client 30000 | ||
543 | 21 | {% endif -%} | ||
544 | 22 | |||
545 | 23 | {% if haproxy_server_timeout -%} | ||
546 | 24 | timeout server {{ haproxy_server_timeout }} | ||
547 | 25 | {% else -%} | ||
548 | 26 | timeout server 30000 | ||
549 | 27 | {% endif -%} | ||
550 | 28 | |||
551 | 29 | listen stats {{ stat_port }} | ||
552 | 30 | mode http | ||
553 | 31 | stats enable | ||
554 | 32 | stats hide-version | ||
555 | 33 | stats realm Haproxy\ Statistics | ||
556 | 34 | stats uri / | ||
557 | 35 | stats auth admin:password | ||
558 | 36 | |||
559 | 37 | {% if frontends -%} | ||
560 | 38 | {% for service, ports in service_ports.items() -%} | ||
561 | 39 | frontend tcp-in_{{ service }} | ||
562 | 40 | bind *:{{ ports[0] }} | ||
563 | 41 | {% if ipv6 -%} | ||
564 | 42 | bind :::{{ ports[0] }} | ||
565 | 43 | {% endif -%} | ||
566 | 44 | {% for frontend in frontends -%} | ||
567 | 45 | acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }} | ||
568 | 46 | use_backend {{ service }}_{{ frontend }} if net_{{ frontend }} | ||
569 | 47 | {% endfor -%} | ||
570 | 48 | default_backend {{ service }}_{{ default_backend }} | ||
571 | 49 | |||
572 | 50 | {% for frontend in frontends -%} | ||
573 | 51 | backend {{ service }}_{{ frontend }} | ||
574 | 52 | balance leastconn | ||
575 | 53 | {% for unit, address in frontends[frontend]['backends'].items() -%} | ||
576 | 54 | server {{ unit }} {{ address }}:{{ ports[1] }} check | ||
577 | 55 | {% endfor %} | ||
578 | 56 | {% endfor -%} | ||
579 | 57 | {% endfor -%} | ||
580 | 58 | {% endif -%} | ||
581 | 0 | 59 | ||
582 | === added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend' | |||
583 | --- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 1970-01-01 00:00:00 +0000 | |||
584 | +++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 2015-03-06 06:41:14 +0000 | |||
585 | @@ -0,0 +1,24 @@ | |||
586 | 1 | {% if endpoints -%} | ||
587 | 2 | {% for ext_port in ext_ports -%} | ||
588 | 3 | Listen {{ ext_port }} | ||
589 | 4 | {% endfor -%} | ||
590 | 5 | {% for address, endpoint, ext, int in endpoints -%} | ||
591 | 6 | <VirtualHost {{ address }}:{{ ext }}> | ||
592 | 7 | ServerName {{ endpoint }} | ||
593 | 8 | SSLEngine on | ||
594 | 9 | SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} | ||
595 | 10 | SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }} | ||
596 | 11 | ProxyPass / http://localhost:{{ int }}/ | ||
597 | 12 | ProxyPassReverse / http://localhost:{{ int }}/ | ||
598 | 13 | ProxyPreserveHost on | ||
599 | 14 | </VirtualHost> | ||
600 | 15 | {% endfor -%} | ||
601 | 16 | <Proxy *> | ||
602 | 17 | Order deny,allow | ||
603 | 18 | Allow from all | ||
604 | 19 | </Proxy> | ||
605 | 20 | <Location /> | ||
606 | 21 | Order allow,deny | ||
607 | 22 | Allow from all | ||
608 | 23 | </Location> | ||
609 | 24 | {% endif -%} | ||
610 | 0 | 25 | ||
611 | === added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf' | |||
612 | --- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 1970-01-01 00:00:00 +0000 | |||
613 | +++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 2015-03-06 06:41:14 +0000 | |||
614 | @@ -0,0 +1,24 @@ | |||
615 | 1 | {% if endpoints -%} | ||
616 | 2 | {% for ext_port in ext_ports -%} | ||
617 | 3 | Listen {{ ext_port }} | ||
618 | 4 | {% endfor -%} | ||
619 | 5 | {% for address, endpoint, ext, int in endpoints -%} | ||
620 | 6 | <VirtualHost {{ address }}:{{ ext }}> | ||
621 | 7 | ServerName {{ endpoint }} | ||
622 | 8 | SSLEngine on | ||
623 | 9 | SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} | ||
624 | 10 | SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }} | ||
625 | 11 | ProxyPass / http://localhost:{{ int }}/ | ||
626 | 12 | ProxyPassReverse / http://localhost:{{ int }}/ | ||
627 | 13 | ProxyPreserveHost on | ||
628 | 14 | </VirtualHost> | ||
629 | 15 | {% endfor -%} | ||
630 | 16 | <Proxy *> | ||
631 | 17 | Order deny,allow | ||
632 | 18 | Allow from all | ||
633 | 19 | </Proxy> | ||
634 | 20 | <Location /> | ||
635 | 21 | Order allow,deny | ||
636 | 22 | Allow from all | ||
637 | 23 | </Location> | ||
638 | 24 | {% endif -%} | ||
639 | 0 | 25 | ||
640 | === added file 'hooks/charmhelpers/contrib/openstack/templates/zeromq' | |||
641 | --- hooks/charmhelpers/contrib/openstack/templates/zeromq 1970-01-01 00:00:00 +0000 | |||
642 | +++ hooks/charmhelpers/contrib/openstack/templates/zeromq 2015-03-06 06:41:14 +0000 | |||
643 | @@ -0,0 +1,14 @@ | |||
644 | 1 | {% if zmq_host -%} | ||
645 | 2 | # ZeroMQ configuration (restart-nonce: {{ zmq_nonce }}) | ||
646 | 3 | rpc_backend = zmq | ||
647 | 4 | rpc_zmq_host = {{ zmq_host }} | ||
648 | 5 | {% if zmq_redis_address -%} | ||
649 | 6 | rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis | ||
650 | 7 | matchmaker_heartbeat_freq = 15 | ||
651 | 8 | matchmaker_heartbeat_ttl = 30 | ||
652 | 9 | [matchmaker_redis] | ||
653 | 10 | host = {{ zmq_redis_address }} | ||
654 | 11 | {% else -%} | ||
655 | 12 | rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing | ||
656 | 13 | {% endif -%} | ||
657 | 14 | {% endif -%} | ||
658 | 0 | 15 | ||
659 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
660 | --- hooks/charmhelpers/contrib/openstack/utils.py 2015-01-26 09:45:59 +0000 | |||
661 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2015-03-06 06:41:14 +0000 | |||
662 | @@ -23,12 +23,13 @@ | |||
663 | 23 | import subprocess | 23 | import subprocess |
664 | 24 | import json | 24 | import json |
665 | 25 | import os | 25 | import os |
666 | 26 | import socket | ||
667 | 27 | import sys | 26 | import sys |
668 | 28 | 27 | ||
669 | 29 | import six | 28 | import six |
670 | 30 | import yaml | 29 | import yaml |
671 | 31 | 30 | ||
672 | 31 | from charmhelpers.contrib.network import ip | ||
673 | 32 | |||
674 | 32 | from charmhelpers.core.hookenv import ( | 33 | from charmhelpers.core.hookenv import ( |
675 | 33 | config, | 34 | config, |
676 | 34 | log as juju_log, | 35 | log as juju_log, |
677 | @@ -103,6 +104,7 @@ | |||
678 | 103 | ('2.1.0', 'juno'), | 104 | ('2.1.0', 'juno'), |
679 | 104 | ('2.2.0', 'juno'), | 105 | ('2.2.0', 'juno'), |
680 | 105 | ('2.2.1', 'kilo'), | 106 | ('2.2.1', 'kilo'), |
681 | 107 | ('2.2.2', 'kilo'), | ||
682 | 106 | ]) | 108 | ]) |
683 | 107 | 109 | ||
684 | 108 | DEFAULT_LOOPBACK_SIZE = '5G' | 110 | DEFAULT_LOOPBACK_SIZE = '5G' |
685 | @@ -420,77 +422,10 @@ | |||
686 | 420 | else: | 422 | else: |
687 | 421 | zap_disk(block_device) | 423 | zap_disk(block_device) |
688 | 422 | 424 | ||
760 | 423 | 425 | is_ip = ip.is_ip | |
761 | 424 | def is_ip(address): | 426 | ns_query = ip.ns_query |
762 | 425 | """ | 427 | get_host_ip = ip.get_host_ip |
763 | 426 | Returns True if address is a valid IP address. | 428 | get_hostname = ip.get_hostname |
693 | 427 | """ | ||
694 | 428 | try: | ||
695 | 429 | # Test to see if already an IPv4 address | ||
696 | 430 | socket.inet_aton(address) | ||
697 | 431 | return True | ||
698 | 432 | except socket.error: | ||
699 | 433 | return False | ||
700 | 434 | |||
701 | 435 | |||
702 | 436 | def ns_query(address): | ||
703 | 437 | try: | ||
704 | 438 | import dns.resolver | ||
705 | 439 | except ImportError: | ||
706 | 440 | apt_install('python-dnspython') | ||
707 | 441 | import dns.resolver | ||
708 | 442 | |||
709 | 443 | if isinstance(address, dns.name.Name): | ||
710 | 444 | rtype = 'PTR' | ||
711 | 445 | elif isinstance(address, six.string_types): | ||
712 | 446 | rtype = 'A' | ||
713 | 447 | else: | ||
714 | 448 | return None | ||
715 | 449 | |||
716 | 450 | answers = dns.resolver.query(address, rtype) | ||
717 | 451 | if answers: | ||
718 | 452 | return str(answers[0]) | ||
719 | 453 | return None | ||
720 | 454 | |||
721 | 455 | |||
722 | 456 | def get_host_ip(hostname): | ||
723 | 457 | """ | ||
724 | 458 | Resolves the IP for a given hostname, or returns | ||
725 | 459 | the input if it is already an IP. | ||
726 | 460 | """ | ||
727 | 461 | if is_ip(hostname): | ||
728 | 462 | return hostname | ||
729 | 463 | |||
730 | 464 | return ns_query(hostname) | ||
731 | 465 | |||
732 | 466 | |||
733 | 467 | def get_hostname(address, fqdn=True): | ||
734 | 468 | """ | ||
735 | 469 | Resolves hostname for given IP, or returns the input | ||
736 | 470 | if it is already a hostname. | ||
737 | 471 | """ | ||
738 | 472 | if is_ip(address): | ||
739 | 473 | try: | ||
740 | 474 | import dns.reversename | ||
741 | 475 | except ImportError: | ||
742 | 476 | apt_install('python-dnspython') | ||
743 | 477 | import dns.reversename | ||
744 | 478 | |||
745 | 479 | rev = dns.reversename.from_address(address) | ||
746 | 480 | result = ns_query(rev) | ||
747 | 481 | if not result: | ||
748 | 482 | return None | ||
749 | 483 | else: | ||
750 | 484 | result = address | ||
751 | 485 | |||
752 | 486 | if fqdn: | ||
753 | 487 | # strip trailing . | ||
754 | 488 | if result.endswith('.'): | ||
755 | 489 | return result[:-1] | ||
756 | 490 | else: | ||
757 | 491 | return result | ||
758 | 492 | else: | ||
759 | 493 | return result.split('.')[0] | ||
764 | 494 | 429 | ||
765 | 495 | 430 | ||
766 | 496 | def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'): | 431 | def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'): |
767 | 497 | 432 | ||
768 | === modified file 'hooks/charmhelpers/contrib/python/packages.py' | |||
769 | --- hooks/charmhelpers/contrib/python/packages.py 2015-01-26 09:45:59 +0000 | |||
770 | +++ hooks/charmhelpers/contrib/python/packages.py 2015-03-06 06:41:14 +0000 | |||
771 | @@ -17,8 +17,6 @@ | |||
772 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
773 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
774 | 19 | 19 | ||
775 | 20 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
776 | 21 | |||
777 | 22 | from charmhelpers.fetch import apt_install, apt_update | 20 | from charmhelpers.fetch import apt_install, apt_update |
778 | 23 | from charmhelpers.core.hookenv import log | 21 | from charmhelpers.core.hookenv import log |
779 | 24 | 22 | ||
780 | @@ -29,6 +27,8 @@ | |||
781 | 29 | apt_install('python-pip') | 27 | apt_install('python-pip') |
782 | 30 | from pip import main as pip_execute | 28 | from pip import main as pip_execute |
783 | 31 | 29 | ||
784 | 30 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
785 | 31 | |||
786 | 32 | 32 | ||
787 | 33 | def parse_options(given, available): | 33 | def parse_options(given, available): |
788 | 34 | """Given a set of options, check if available""" | 34 | """Given a set of options, check if available""" |
789 | 35 | 35 | ||
790 | === modified file 'hooks/charmhelpers/core/fstab.py' | |||
791 | --- hooks/charmhelpers/core/fstab.py 2015-01-26 09:45:59 +0000 | |||
792 | +++ hooks/charmhelpers/core/fstab.py 2015-03-06 06:41:14 +0000 | |||
793 | @@ -17,11 +17,11 @@ | |||
794 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
795 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
796 | 19 | 19 | ||
797 | 20 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
798 | 21 | |||
799 | 22 | import io | 20 | import io |
800 | 23 | import os | 21 | import os |
801 | 24 | 22 | ||
802 | 23 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
803 | 24 | |||
804 | 25 | 25 | ||
805 | 26 | class Fstab(io.FileIO): | 26 | class Fstab(io.FileIO): |
806 | 27 | """This class extends file in order to implement a file reader/writer | 27 | """This class extends file in order to implement a file reader/writer |
807 | @@ -77,7 +77,7 @@ | |||
808 | 77 | for line in self.readlines(): | 77 | for line in self.readlines(): |
809 | 78 | line = line.decode('us-ascii') | 78 | line = line.decode('us-ascii') |
810 | 79 | try: | 79 | try: |
812 | 80 | if line.strip() and not line.startswith("#"): | 80 | if line.strip() and not line.strip().startswith("#"): |
813 | 81 | yield self._hydrate_entry(line) | 81 | yield self._hydrate_entry(line) |
814 | 82 | except ValueError: | 82 | except ValueError: |
815 | 83 | pass | 83 | pass |
816 | @@ -104,7 +104,7 @@ | |||
817 | 104 | 104 | ||
818 | 105 | found = False | 105 | found = False |
819 | 106 | for index, line in enumerate(lines): | 106 | for index, line in enumerate(lines): |
821 | 107 | if not line.startswith("#"): | 107 | if line.strip() and not line.strip().startswith("#"): |
822 | 108 | if self._hydrate_entry(line) == entry: | 108 | if self._hydrate_entry(line) == entry: |
823 | 109 | found = True | 109 | found = True |
824 | 110 | break | 110 | break |
825 | 111 | 111 | ||
826 | === modified file 'hooks/charmhelpers/core/host.py' | |||
827 | --- hooks/charmhelpers/core/host.py 2015-01-26 09:45:59 +0000 | |||
828 | +++ hooks/charmhelpers/core/host.py 2015-03-06 06:41:14 +0000 | |||
829 | @@ -191,11 +191,11 @@ | |||
830 | 191 | 191 | ||
831 | 192 | 192 | ||
832 | 193 | def write_file(path, content, owner='root', group='root', perms=0o444): | 193 | def write_file(path, content, owner='root', group='root', perms=0o444): |
834 | 194 | """Create or overwrite a file with the contents of a string""" | 194 | """Create or overwrite a file with the contents of a byte string.""" |
835 | 195 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) | 195 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) |
836 | 196 | uid = pwd.getpwnam(owner).pw_uid | 196 | uid = pwd.getpwnam(owner).pw_uid |
837 | 197 | gid = grp.getgrnam(group).gr_gid | 197 | gid = grp.getgrnam(group).gr_gid |
839 | 198 | with open(path, 'w') as target: | 198 | with open(path, 'wb') as target: |
840 | 199 | os.fchown(target.fileno(), uid, gid) | 199 | os.fchown(target.fileno(), uid, gid) |
841 | 200 | os.fchmod(target.fileno(), perms) | 200 | os.fchmod(target.fileno(), perms) |
842 | 201 | target.write(content) | 201 | target.write(content) |
843 | @@ -305,11 +305,11 @@ | |||
844 | 305 | ceph_client_changed function. | 305 | ceph_client_changed function. |
845 | 306 | """ | 306 | """ |
846 | 307 | def wrap(f): | 307 | def wrap(f): |
848 | 308 | def wrapped_f(*args): | 308 | def wrapped_f(*args, **kwargs): |
849 | 309 | checksums = {} | 309 | checksums = {} |
850 | 310 | for path in restart_map: | 310 | for path in restart_map: |
851 | 311 | checksums[path] = file_hash(path) | 311 | checksums[path] = file_hash(path) |
853 | 312 | f(*args) | 312 | f(*args, **kwargs) |
854 | 313 | restarts = [] | 313 | restarts = [] |
855 | 314 | for path in restart_map: | 314 | for path in restart_map: |
856 | 315 | if checksums[path] != file_hash(path): | 315 | if checksums[path] != file_hash(path): |
857 | @@ -361,7 +361,7 @@ | |||
858 | 361 | ip_output = (line for line in ip_output if line) | 361 | ip_output = (line for line in ip_output if line) |
859 | 362 | for line in ip_output: | 362 | for line in ip_output: |
860 | 363 | if line.split()[1].startswith(int_type): | 363 | if line.split()[1].startswith(int_type): |
862 | 364 | matched = re.search('.*: (bond[0-9]+\.[0-9]+)@.*', line) | 364 | matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line) |
863 | 365 | if matched: | 365 | if matched: |
864 | 366 | interface = matched.groups()[0] | 366 | interface = matched.groups()[0] |
865 | 367 | else: | 367 | else: |
866 | 368 | 368 | ||
867 | === modified file 'hooks/charmhelpers/core/services/helpers.py' | |||
868 | --- hooks/charmhelpers/core/services/helpers.py 2015-01-26 09:45:59 +0000 | |||
869 | +++ hooks/charmhelpers/core/services/helpers.py 2015-03-06 06:41:14 +0000 | |||
870 | @@ -45,12 +45,14 @@ | |||
871 | 45 | """ | 45 | """ |
872 | 46 | name = None | 46 | name = None |
873 | 47 | interface = None | 47 | interface = None |
874 | 48 | required_keys = [] | ||
875 | 49 | 48 | ||
876 | 50 | def __init__(self, name=None, additional_required_keys=None): | 49 | def __init__(self, name=None, additional_required_keys=None): |
877 | 50 | if not hasattr(self, 'required_keys'): | ||
878 | 51 | self.required_keys = [] | ||
879 | 52 | |||
880 | 51 | if name is not None: | 53 | if name is not None: |
881 | 52 | self.name = name | 54 | self.name = name |
883 | 53 | if additional_required_keys is not None: | 55 | if additional_required_keys: |
884 | 54 | self.required_keys.extend(additional_required_keys) | 56 | self.required_keys.extend(additional_required_keys) |
885 | 55 | self.get_data() | 57 | self.get_data() |
886 | 56 | 58 | ||
887 | @@ -134,7 +136,10 @@ | |||
888 | 134 | """ | 136 | """ |
889 | 135 | name = 'db' | 137 | name = 'db' |
890 | 136 | interface = 'mysql' | 138 | interface = 'mysql' |
892 | 137 | required_keys = ['host', 'user', 'password', 'database'] | 139 | |
893 | 140 | def __init__(self, *args, **kwargs): | ||
894 | 141 | self.required_keys = ['host', 'user', 'password', 'database'] | ||
895 | 142 | super(HttpRelation).__init__(self, *args, **kwargs) | ||
896 | 138 | 143 | ||
897 | 139 | 144 | ||
898 | 140 | class HttpRelation(RelationContext): | 145 | class HttpRelation(RelationContext): |
899 | @@ -146,7 +151,10 @@ | |||
900 | 146 | """ | 151 | """ |
901 | 147 | name = 'website' | 152 | name = 'website' |
902 | 148 | interface = 'http' | 153 | interface = 'http' |
904 | 149 | required_keys = ['host', 'port'] | 154 | |
905 | 155 | def __init__(self, *args, **kwargs): | ||
906 | 156 | self.required_keys = ['host', 'port'] | ||
907 | 157 | super(HttpRelation).__init__(self, *args, **kwargs) | ||
908 | 150 | 158 | ||
909 | 151 | def provide_data(self): | 159 | def provide_data(self): |
910 | 152 | return { | 160 | return { |
911 | 153 | 161 | ||
912 | === added file 'hooks/charmhelpers/core/strutils.py' | |||
913 | --- hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000 | |||
914 | +++ hooks/charmhelpers/core/strutils.py 2015-03-06 06:41:14 +0000 | |||
915 | @@ -0,0 +1,42 @@ | |||
916 | 1 | #!/usr/bin/env python | ||
917 | 2 | # -*- coding: utf-8 -*- | ||
918 | 3 | |||
919 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
920 | 5 | # | ||
921 | 6 | # This file is part of charm-helpers. | ||
922 | 7 | # | ||
923 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
924 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
925 | 10 | # published by the Free Software Foundation. | ||
926 | 11 | # | ||
927 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
928 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
929 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
930 | 15 | # GNU Lesser General Public License for more details. | ||
931 | 16 | # | ||
932 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
933 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
934 | 19 | |||
935 | 20 | import six | ||
936 | 21 | |||
937 | 22 | |||
938 | 23 | def bool_from_string(value): | ||
939 | 24 | """Interpret string value as boolean. | ||
940 | 25 | |||
941 | 26 | Returns True if value translates to True otherwise False. | ||
942 | 27 | """ | ||
943 | 28 | if isinstance(value, six.string_types): | ||
944 | 29 | value = six.text_type(value) | ||
945 | 30 | else: | ||
946 | 31 | msg = "Unable to interpret non-string value '%s' as boolean" % (value) | ||
947 | 32 | raise ValueError(msg) | ||
948 | 33 | |||
949 | 34 | value = value.strip().lower() | ||
950 | 35 | |||
951 | 36 | if value in ['y', 'yes', 'true', 't']: | ||
952 | 37 | return True | ||
953 | 38 | elif value in ['n', 'no', 'false', 'f']: | ||
954 | 39 | return False | ||
955 | 40 | |||
956 | 41 | msg = "Unable to interpret string value '%s' as boolean" % (value) | ||
957 | 42 | raise ValueError(msg) | ||
958 | 0 | 43 | ||
959 | === modified file 'hooks/charmhelpers/core/sysctl.py' | |||
960 | --- hooks/charmhelpers/core/sysctl.py 2015-01-26 09:45:59 +0000 | |||
961 | +++ hooks/charmhelpers/core/sysctl.py 2015-03-06 06:41:14 +0000 | |||
962 | @@ -17,8 +17,6 @@ | |||
963 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
964 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
965 | 19 | 19 | ||
966 | 20 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
967 | 21 | |||
968 | 22 | import yaml | 20 | import yaml |
969 | 23 | 21 | ||
970 | 24 | from subprocess import check_call | 22 | from subprocess import check_call |
971 | @@ -26,25 +24,33 @@ | |||
972 | 26 | from charmhelpers.core.hookenv import ( | 24 | from charmhelpers.core.hookenv import ( |
973 | 27 | log, | 25 | log, |
974 | 28 | DEBUG, | 26 | DEBUG, |
975 | 27 | ERROR, | ||
976 | 29 | ) | 28 | ) |
977 | 30 | 29 | ||
978 | 30 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
979 | 31 | |||
980 | 31 | 32 | ||
981 | 32 | def create(sysctl_dict, sysctl_file): | 33 | def create(sysctl_dict, sysctl_file): |
982 | 33 | """Creates a sysctl.conf file from a YAML associative array | 34 | """Creates a sysctl.conf file from a YAML associative array |
983 | 34 | 35 | ||
986 | 35 | :param sysctl_dict: a dict of sysctl options eg { 'kernel.max_pid': 1337 } | 36 | :param sysctl_dict: a YAML-formatted string of sysctl options eg "{ 'kernel.max_pid': 1337 }" |
987 | 36 | :type sysctl_dict: dict | 37 | :type sysctl_dict: str |
988 | 37 | :param sysctl_file: path to the sysctl file to be saved | 38 | :param sysctl_file: path to the sysctl file to be saved |
989 | 38 | :type sysctl_file: str or unicode | 39 | :type sysctl_file: str or unicode |
990 | 39 | :returns: None | 40 | :returns: None |
991 | 40 | """ | 41 | """ |
993 | 41 | sysctl_dict = yaml.load(sysctl_dict) | 42 | try: |
994 | 43 | sysctl_dict_parsed = yaml.safe_load(sysctl_dict) | ||
995 | 44 | except yaml.YAMLError: | ||
996 | 45 | log("Error parsing YAML sysctl_dict: {}".format(sysctl_dict), | ||
997 | 46 | level=ERROR) | ||
998 | 47 | return | ||
999 | 42 | 48 | ||
1000 | 43 | with open(sysctl_file, "w") as fd: | 49 | with open(sysctl_file, "w") as fd: |
1002 | 44 | for key, value in sysctl_dict.items(): | 50 | for key, value in sysctl_dict_parsed.items(): |
1003 | 45 | fd.write("{}={}\n".format(key, value)) | 51 | fd.write("{}={}\n".format(key, value)) |
1004 | 46 | 52 | ||
1006 | 47 | log("Updating sysctl_file: %s values: %s" % (sysctl_file, sysctl_dict), | 53 | log("Updating sysctl_file: %s values: %s" % (sysctl_file, sysctl_dict_parsed), |
1007 | 48 | level=DEBUG) | 54 | level=DEBUG) |
1008 | 49 | 55 | ||
1009 | 50 | check_call(["sysctl", "-p", sysctl_file]) | 56 | check_call(["sysctl", "-p", sysctl_file]) |
1010 | 51 | 57 | ||
1011 | === modified file 'hooks/charmhelpers/core/templating.py' | |||
1012 | --- hooks/charmhelpers/core/templating.py 2015-01-26 09:45:59 +0000 | |||
1013 | +++ hooks/charmhelpers/core/templating.py 2015-03-06 06:41:14 +0000 | |||
1014 | @@ -21,7 +21,7 @@ | |||
1015 | 21 | 21 | ||
1016 | 22 | 22 | ||
1017 | 23 | def render(source, target, context, owner='root', group='root', | 23 | def render(source, target, context, owner='root', group='root', |
1019 | 24 | perms=0o444, templates_dir=None): | 24 | perms=0o444, templates_dir=None, encoding='UTF-8'): |
1020 | 25 | """ | 25 | """ |
1021 | 26 | Render a template. | 26 | Render a template. |
1022 | 27 | 27 | ||
1023 | @@ -64,5 +64,5 @@ | |||
1024 | 64 | level=hookenv.ERROR) | 64 | level=hookenv.ERROR) |
1025 | 65 | raise e | 65 | raise e |
1026 | 66 | content = template.render(context) | 66 | content = template.render(context) |
1029 | 67 | host.mkdir(os.path.dirname(target), owner, group) | 67 | host.mkdir(os.path.dirname(target), owner, group, perms=0o755) |
1030 | 68 | host.write_file(target, content, owner, group, perms) | 68 | host.write_file(target, content.encode(encoding), owner, group, perms) |
1031 | 69 | 69 | ||
1032 | === added file 'hooks/charmhelpers/core/unitdata.py' | |||
1033 | --- hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000 | |||
1034 | +++ hooks/charmhelpers/core/unitdata.py 2015-03-06 06:41:14 +0000 | |||
1035 | @@ -0,0 +1,477 @@ | |||
1036 | 1 | #!/usr/bin/env python | ||
1037 | 2 | # -*- coding: utf-8 -*- | ||
1038 | 3 | # | ||
1039 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
1040 | 5 | # | ||
1041 | 6 | # This file is part of charm-helpers. | ||
1042 | 7 | # | ||
1043 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
1044 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1045 | 10 | # published by the Free Software Foundation. | ||
1046 | 11 | # | ||
1047 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
1048 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1049 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1050 | 15 | # GNU Lesser General Public License for more details. | ||
1051 | 16 | # | ||
1052 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
1053 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1054 | 19 | # | ||
1055 | 20 | # | ||
1056 | 21 | # Authors: | ||
1057 | 22 | # Kapil Thangavelu <kapil.foss@gmail.com> | ||
1058 | 23 | # | ||
1059 | 24 | """ | ||
1060 | 25 | Intro | ||
1061 | 26 | ----- | ||
1062 | 27 | |||
1063 | 28 | A simple way to store state in units. This provides a key value | ||
1064 | 29 | storage with support for versioned, transactional operation, | ||
1065 | 30 | and can calculate deltas from previous values to simplify unit logic | ||
1066 | 31 | when processing changes. | ||
1067 | 32 | |||
1068 | 33 | |||
1069 | 34 | Hook Integration | ||
1070 | 35 | ---------------- | ||
1071 | 36 | |||
1072 | 37 | There are several extant frameworks for hook execution, including | ||
1073 | 38 | |||
1074 | 39 | - charmhelpers.core.hookenv.Hooks | ||
1075 | 40 | - charmhelpers.core.services.ServiceManager | ||
1076 | 41 | |||
1077 | 42 | The storage classes are framework agnostic, one simple integration is | ||
1078 | 43 | via the HookData contextmanager. It will record the current hook | ||
1079 | 44 | execution environment (including relation data, config data, etc.), | ||
1080 | 45 | setup a transaction and allow easy access to the changes from | ||
1081 | 46 | previously seen values. One consequence of the integration is the | ||
1082 | 47 | reservation of particular keys ('rels', 'unit', 'env', 'config', | ||
1083 | 48 | 'charm_revisions') for their respective values. | ||
1084 | 49 | |||
1085 | 50 | Here's a fully worked integration example using hookenv.Hooks:: | ||
1086 | 51 | |||
1087 | 52 | from charmhelper.core import hookenv, unitdata | ||
1088 | 53 | |||
1089 | 54 | hook_data = unitdata.HookData() | ||
1090 | 55 | db = unitdata.kv() | ||
1091 | 56 | hooks = hookenv.Hooks() | ||
1092 | 57 | |||
1093 | 58 | @hooks.hook | ||
1094 | 59 | def config_changed(): | ||
1095 | 60 | # Print all changes to configuration from previously seen | ||
1096 | 61 | # values. | ||
1097 | 62 | for changed, (prev, cur) in hook_data.conf.items(): | ||
1098 | 63 | print('config changed', changed, | ||
1099 | 64 | 'previous value', prev, | ||
1100 | 65 | 'current value', cur) | ||
1101 | 66 | |||
1102 | 67 | # Get some unit specific bookeeping | ||
1103 | 68 | if not db.get('pkg_key'): | ||
1104 | 69 | key = urllib.urlopen('https://example.com/pkg_key').read() | ||
1105 | 70 | db.set('pkg_key', key) | ||
1106 | 71 | |||
1107 | 72 | # Directly access all charm config as a mapping. | ||
1108 | 73 | conf = db.getrange('config', True) | ||
1109 | 74 | |||
1110 | 75 | # Directly access all relation data as a mapping | ||
1111 | 76 | rels = db.getrange('rels', True) | ||
1112 | 77 | |||
1113 | 78 | if __name__ == '__main__': | ||
1114 | 79 | with hook_data(): | ||
1115 | 80 | hook.execute() | ||
1116 | 81 | |||
1117 | 82 | |||
1118 | 83 | A more basic integration is via the hook_scope context manager which simply | ||
1119 | 84 | manages transaction scope (and records hook name, and timestamp):: | ||
1120 | 85 | |||
1121 | 86 | >>> from unitdata import kv | ||
1122 | 87 | >>> db = kv() | ||
1123 | 88 | >>> with db.hook_scope('install'): | ||
1124 | 89 | ... # do work, in transactional scope. | ||
1125 | 90 | ... db.set('x', 1) | ||
1126 | 91 | >>> db.get('x') | ||
1127 | 92 | 1 | ||
1128 | 93 | |||
1129 | 94 | |||
1130 | 95 | Usage | ||
1131 | 96 | ----- | ||
1132 | 97 | |||
1133 | 98 | Values are automatically json de/serialized to preserve basic typing | ||
1134 | 99 | and complex data struct capabilities (dicts, lists, ints, booleans, etc). | ||
1135 | 100 | |||
1136 | 101 | Individual values can be manipulated via get/set:: | ||
1137 | 102 | |||
1138 | 103 | >>> kv.set('y', True) | ||
1139 | 104 | >>> kv.get('y') | ||
1140 | 105 | True | ||
1141 | 106 | |||
1142 | 107 | # We can set complex values (dicts, lists) as a single key. | ||
1143 | 108 | >>> kv.set('config', {'a': 1, 'b': True'}) | ||
1144 | 109 | |||
1145 | 110 | # Also supports returning dictionaries as a record which | ||
1146 | 111 | # provides attribute access. | ||
1147 | 112 | >>> config = kv.get('config', record=True) | ||
1148 | 113 | >>> config.b | ||
1149 | 114 | True | ||
1150 | 115 | |||
1151 | 116 | |||
1152 | 117 | Groups of keys can be manipulated with update/getrange:: | ||
1153 | 118 | |||
1154 | 119 | >>> kv.update({'z': 1, 'y': 2}, prefix="gui.") | ||
1155 | 120 | >>> kv.getrange('gui.', strip=True) | ||
1156 | 121 | {'z': 1, 'y': 2} | ||
1157 | 122 | |||
1158 | 123 | When updating values, its very helpful to understand which values | ||
1159 | 124 | have actually changed and how have they changed. The storage | ||
1160 | 125 | provides a delta method to provide for this:: | ||
1161 | 126 | |||
1162 | 127 | >>> data = {'debug': True, 'option': 2} | ||
1163 | 128 | >>> delta = kv.delta(data, 'config.') | ||
1164 | 129 | >>> delta.debug.previous | ||
1165 | 130 | None | ||
1166 | 131 | >>> delta.debug.current | ||
1167 | 132 | True | ||
1168 | 133 | >>> delta | ||
1169 | 134 | {'debug': (None, True), 'option': (None, 2)} | ||
1170 | 135 | |||
1171 | 136 | Note the delta method does not persist the actual change, it needs to | ||
1172 | 137 | be explicitly saved via 'update' method:: | ||
1173 | 138 | |||
1174 | 139 | >>> kv.update(data, 'config.') | ||
1175 | 140 | |||
1176 | 141 | Values modified in the context of a hook scope retain historical values | ||
1177 | 142 | associated to the hookname. | ||
1178 | 143 | |||
1179 | 144 | >>> with db.hook_scope('config-changed'): | ||
1180 | 145 | ... db.set('x', 42) | ||
1181 | 146 | >>> db.gethistory('x') | ||
1182 | 147 | [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'), | ||
1183 | 148 | (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')] | ||
1184 | 149 | |||
1185 | 150 | """ | ||
1186 | 151 | |||
1187 | 152 | import collections | ||
1188 | 153 | import contextlib | ||
1189 | 154 | import datetime | ||
1190 | 155 | import json | ||
1191 | 156 | import os | ||
1192 | 157 | import pprint | ||
1193 | 158 | import sqlite3 | ||
1194 | 159 | import sys | ||
1195 | 160 | |||
1196 | 161 | __author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>' | ||
1197 | 162 | |||
1198 | 163 | |||
1199 | 164 | class Storage(object): | ||
1200 | 165 | """Simple key value database for local unit state within charms. | ||
1201 | 166 | |||
1202 | 167 | Modifications are automatically committed at hook exit. That's | ||
1203 | 168 | currently regardless of exit code. | ||
1204 | 169 | |||
1205 | 170 | To support dicts, lists, integer, floats, and booleans values | ||
1206 | 171 | are automatically json encoded/decoded. | ||
1207 | 172 | """ | ||
1208 | 173 | def __init__(self, path=None): | ||
1209 | 174 | self.db_path = path | ||
1210 | 175 | if path is None: | ||
1211 | 176 | self.db_path = os.path.join( | ||
1212 | 177 | os.environ.get('CHARM_DIR', ''), '.unit-state.db') | ||
1213 | 178 | self.conn = sqlite3.connect('%s' % self.db_path) | ||
1214 | 179 | self.cursor = self.conn.cursor() | ||
1215 | 180 | self.revision = None | ||
1216 | 181 | self._closed = False | ||
1217 | 182 | self._init() | ||
1218 | 183 | |||
1219 | 184 | def close(self): | ||
1220 | 185 | if self._closed: | ||
1221 | 186 | return | ||
1222 | 187 | self.flush(False) | ||
1223 | 188 | self.cursor.close() | ||
1224 | 189 | self.conn.close() | ||
1225 | 190 | self._closed = True | ||
1226 | 191 | |||
1227 | 192 | def _scoped_query(self, stmt, params=None): | ||
1228 | 193 | if params is None: | ||
1229 | 194 | params = [] | ||
1230 | 195 | return stmt, params | ||
1231 | 196 | |||
1232 | 197 | def get(self, key, default=None, record=False): | ||
1233 | 198 | self.cursor.execute( | ||
1234 | 199 | *self._scoped_query( | ||
1235 | 200 | 'select data from kv where key=?', [key])) | ||
1236 | 201 | result = self.cursor.fetchone() | ||
1237 | 202 | if not result: | ||
1238 | 203 | return default | ||
1239 | 204 | if record: | ||
1240 | 205 | return Record(json.loads(result[0])) | ||
1241 | 206 | return json.loads(result[0]) | ||
1242 | 207 | |||
1243 | 208 | def getrange(self, key_prefix, strip=False): | ||
1244 | 209 | stmt = "select key, data from kv where key like '%s%%'" % key_prefix | ||
1245 | 210 | self.cursor.execute(*self._scoped_query(stmt)) | ||
1246 | 211 | result = self.cursor.fetchall() | ||
1247 | 212 | |||
1248 | 213 | if not result: | ||
1249 | 214 | return None | ||
1250 | 215 | if not strip: | ||
1251 | 216 | key_prefix = '' | ||
1252 | 217 | return dict([ | ||
1253 | 218 | (k[len(key_prefix):], json.loads(v)) for k, v in result]) | ||
1254 | 219 | |||
1255 | 220 | def update(self, mapping, prefix=""): | ||
1256 | 221 | for k, v in mapping.items(): | ||
1257 | 222 | self.set("%s%s" % (prefix, k), v) | ||
1258 | 223 | |||
1259 | 224 | def unset(self, key): | ||
1260 | 225 | self.cursor.execute('delete from kv where key=?', [key]) | ||
1261 | 226 | if self.revision and self.cursor.rowcount: | ||
1262 | 227 | self.cursor.execute( | ||
1263 | 228 | 'insert into kv_revisions values (?, ?, ?)', | ||
1264 | 229 | [key, self.revision, json.dumps('DELETED')]) | ||
1265 | 230 | |||
1266 | 231 | def set(self, key, value): | ||
1267 | 232 | serialized = json.dumps(value) | ||
1268 | 233 | |||
1269 | 234 | self.cursor.execute( | ||
1270 | 235 | 'select data from kv where key=?', [key]) | ||
1271 | 236 | exists = self.cursor.fetchone() | ||
1272 | 237 | |||
1273 | 238 | # Skip mutations to the same value | ||
1274 | 239 | if exists: | ||
1275 | 240 | if exists[0] == serialized: | ||
1276 | 241 | return value | ||
1277 | 242 | |||
1278 | 243 | if not exists: | ||
1279 | 244 | self.cursor.execute( | ||
1280 | 245 | 'insert into kv (key, data) values (?, ?)', | ||
1281 | 246 | (key, serialized)) | ||
1282 | 247 | else: | ||
1283 | 248 | self.cursor.execute(''' | ||
1284 | 249 | update kv | ||
1285 | 250 | set data = ? | ||
1286 | 251 | where key = ?''', [serialized, key]) | ||
1287 | 252 | |||
1288 | 253 | # Save | ||
1289 | 254 | if not self.revision: | ||
1290 | 255 | return value | ||
1291 | 256 | |||
1292 | 257 | self.cursor.execute( | ||
1293 | 258 | 'select 1 from kv_revisions where key=? and revision=?', | ||
1294 | 259 | [key, self.revision]) | ||
1295 | 260 | exists = self.cursor.fetchone() | ||
1296 | 261 | |||
1297 | 262 | if not exists: | ||
1298 | 263 | self.cursor.execute( | ||
1299 | 264 | '''insert into kv_revisions ( | ||
1300 | 265 | revision, key, data) values (?, ?, ?)''', | ||
1301 | 266 | (self.revision, key, serialized)) | ||
1302 | 267 | else: | ||
1303 | 268 | self.cursor.execute( | ||
1304 | 269 | ''' | ||
1305 | 270 | update kv_revisions | ||
1306 | 271 | set data = ? | ||
1307 | 272 | where key = ? | ||
1308 | 273 | and revision = ?''', | ||
1309 | 274 | [serialized, key, self.revision]) | ||
1310 | 275 | |||
1311 | 276 | return value | ||
1312 | 277 | |||
1313 | 278 | def delta(self, mapping, prefix): | ||
1314 | 279 | """ | ||
1315 | 280 | return a delta containing values that have changed. | ||
1316 | 281 | """ | ||
1317 | 282 | previous = self.getrange(prefix, strip=True) | ||
1318 | 283 | if not previous: | ||
1319 | 284 | pk = set() | ||
1320 | 285 | else: | ||
1321 | 286 | pk = set(previous.keys()) | ||
1322 | 287 | ck = set(mapping.keys()) | ||
1323 | 288 | delta = DeltaSet() | ||
1324 | 289 | |||
1325 | 290 | # added | ||
1326 | 291 | for k in ck.difference(pk): | ||
1327 | 292 | delta[k] = Delta(None, mapping[k]) | ||
1328 | 293 | |||
1329 | 294 | # removed | ||
1330 | 295 | for k in pk.difference(ck): | ||
1331 | 296 | delta[k] = Delta(previous[k], None) | ||
1332 | 297 | |||
1333 | 298 | # changed | ||
1334 | 299 | for k in pk.intersection(ck): | ||
1335 | 300 | c = mapping[k] | ||
1336 | 301 | p = previous[k] | ||
1337 | 302 | if c != p: | ||
1338 | 303 | delta[k] = Delta(p, c) | ||
1339 | 304 | |||
1340 | 305 | return delta | ||
1341 | 306 | |||
1342 | 307 | @contextlib.contextmanager | ||
1343 | 308 | def hook_scope(self, name=""): | ||
1344 | 309 | """Scope all future interactions to the current hook execution | ||
1345 | 310 | revision.""" | ||
1346 | 311 | assert not self.revision | ||
1347 | 312 | self.cursor.execute( | ||
1348 | 313 | 'insert into hooks (hook, date) values (?, ?)', | ||
1349 | 314 | (name or sys.argv[0], | ||
1350 | 315 | datetime.datetime.utcnow().isoformat())) | ||
1351 | 316 | self.revision = self.cursor.lastrowid | ||
1352 | 317 | try: | ||
1353 | 318 | yield self.revision | ||
1354 | 319 | self.revision = None | ||
1355 | 320 | except: | ||
1356 | 321 | self.flush(False) | ||
1357 | 322 | self.revision = None | ||
1358 | 323 | raise | ||
1359 | 324 | else: | ||
1360 | 325 | self.flush() | ||
1361 | 326 | |||
1362 | 327 | def flush(self, save=True): | ||
1363 | 328 | if save: | ||
1364 | 329 | self.conn.commit() | ||
1365 | 330 | elif self._closed: | ||
1366 | 331 | return | ||
1367 | 332 | else: | ||
1368 | 333 | self.conn.rollback() | ||
1369 | 334 | |||
1370 | 335 | def _init(self): | ||
1371 | 336 | self.cursor.execute(''' | ||
1372 | 337 | create table if not exists kv ( | ||
1373 | 338 | key text, | ||
1374 | 339 | data text, | ||
1375 | 340 | primary key (key) | ||
1376 | 341 | )''') | ||
1377 | 342 | self.cursor.execute(''' | ||
1378 | 343 | create table if not exists kv_revisions ( | ||
1379 | 344 | key text, | ||
1380 | 345 | revision integer, | ||
1381 | 346 | data text, | ||
1382 | 347 | primary key (key, revision) | ||
1383 | 348 | )''') | ||
1384 | 349 | self.cursor.execute(''' | ||
1385 | 350 | create table if not exists hooks ( | ||
1386 | 351 | version integer primary key autoincrement, | ||
1387 | 352 | hook text, | ||
1388 | 353 | date text | ||
1389 | 354 | )''') | ||
1390 | 355 | self.conn.commit() | ||
1391 | 356 | |||
1392 | 357 | def gethistory(self, key, deserialize=False): | ||
1393 | 358 | self.cursor.execute( | ||
1394 | 359 | ''' | ||
1395 | 360 | select kv.revision, kv.key, kv.data, h.hook, h.date | ||
1396 | 361 | from kv_revisions kv, | ||
1397 | 362 | hooks h | ||
1398 | 363 | where kv.key=? | ||
1399 | 364 | and kv.revision = h.version | ||
1400 | 365 | ''', [key]) | ||
1401 | 366 | if deserialize is False: | ||
1402 | 367 | return self.cursor.fetchall() | ||
1403 | 368 | return map(_parse_history, self.cursor.fetchall()) | ||
1404 | 369 | |||
1405 | 370 | def debug(self, fh=sys.stderr): | ||
1406 | 371 | self.cursor.execute('select * from kv') | ||
1407 | 372 | pprint.pprint(self.cursor.fetchall(), stream=fh) | ||
1408 | 373 | self.cursor.execute('select * from kv_revisions') | ||
1409 | 374 | pprint.pprint(self.cursor.fetchall(), stream=fh) | ||
1410 | 375 | |||
1411 | 376 | |||
1412 | 377 | def _parse_history(d): | ||
1413 | 378 | return (d[0], d[1], json.loads(d[2]), d[3], | ||
1414 | 379 | datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f")) | ||
1415 | 380 | |||
1416 | 381 | |||
1417 | 382 | class HookData(object): | ||
1418 | 383 | """Simple integration for existing hook exec frameworks. | ||
1419 | 384 | |||
1420 | 385 | Records all unit information, and stores deltas for processing | ||
1421 | 386 | by the hook. | ||
1422 | 387 | |||
1423 | 388 | Sample:: | ||
1424 | 389 | |||
1425 | 390 | from charmhelper.core import hookenv, unitdata | ||
1426 | 391 | |||
1427 | 392 | changes = unitdata.HookData() | ||
1428 | 393 | db = unitdata.kv() | ||
1429 | 394 | hooks = hookenv.Hooks() | ||
1430 | 395 | |||
1431 | 396 | @hooks.hook | ||
1432 | 397 | def config_changed(): | ||
1433 | 398 | # View all changes to configuration | ||
1434 | 399 | for changed, (prev, cur) in changes.conf.items(): | ||
1435 | 400 | print('config changed', changed, | ||
1436 | 401 | 'previous value', prev, | ||
1437 | 402 | 'current value', cur) | ||
1438 | 403 | |||
1439 | 404 | # Get some unit specific bookeeping | ||
1440 | 405 | if not db.get('pkg_key'): | ||
1441 | 406 | key = urllib.urlopen('https://example.com/pkg_key').read() | ||
1442 | 407 | db.set('pkg_key', key) | ||
1443 | 408 | |||
1444 | 409 | if __name__ == '__main__': | ||
1445 | 410 | with changes(): | ||
1446 | 411 | hook.execute() | ||
1447 | 412 | |||
1448 | 413 | """ | ||
1449 | 414 | def __init__(self): | ||
1450 | 415 | self.kv = kv() | ||
1451 | 416 | self.conf = None | ||
1452 | 417 | self.rels = None | ||
1453 | 418 | |||
1454 | 419 | @contextlib.contextmanager | ||
1455 | 420 | def __call__(self): | ||
1456 | 421 | from charmhelpers.core import hookenv | ||
1457 | 422 | hook_name = hookenv.hook_name() | ||
1458 | 423 | |||
1459 | 424 | with self.kv.hook_scope(hook_name): | ||
1460 | 425 | self._record_charm_version(hookenv.charm_dir()) | ||
1461 | 426 | delta_config, delta_relation = self._record_hook(hookenv) | ||
1462 | 427 | yield self.kv, delta_config, delta_relation | ||
1463 | 428 | |||
1464 | 429 | def _record_charm_version(self, charm_dir): | ||
1465 | 430 | # Record revisions.. charm revisions are meaningless | ||
1466 | 431 | # to charm authors as they don't control the revision. | ||
1467 | 432 | # so logic dependnent on revision is not particularly | ||
1468 | 433 | # useful, however it is useful for debugging analysis. | ||
1469 | 434 | charm_rev = open( | ||
1470 | 435 | os.path.join(charm_dir, 'revision')).read().strip() | ||
1471 | 436 | charm_rev = charm_rev or '0' | ||
1472 | 437 | revs = self.kv.get('charm_revisions', []) | ||
1473 | 438 | if charm_rev not in revs: | ||
1474 | 439 | revs.append(charm_rev.strip() or '0') | ||
1475 | 440 | self.kv.set('charm_revisions', revs) | ||
1476 | 441 | |||
1477 | 442 | def _record_hook(self, hookenv): | ||
1478 | 443 | data = hookenv.execution_environment() | ||
1479 | 444 | self.conf = conf_delta = self.kv.delta(data['conf'], 'config') | ||
1480 | 445 | self.rels = rels_delta = self.kv.delta(data['rels'], 'rels') | ||
1481 | 446 | self.kv.set('env', data['env']) | ||
1482 | 447 | self.kv.set('unit', data['unit']) | ||
1483 | 448 | self.kv.set('relid', data.get('relid')) | ||
1484 | 449 | return conf_delta, rels_delta | ||
1485 | 450 | |||
1486 | 451 | |||
1487 | 452 | class Record(dict): | ||
1488 | 453 | |||
1489 | 454 | __slots__ = () | ||
1490 | 455 | |||
1491 | 456 | def __getattr__(self, k): | ||
1492 | 457 | if k in self: | ||
1493 | 458 | return self[k] | ||
1494 | 459 | raise AttributeError(k) | ||
1495 | 460 | |||
1496 | 461 | |||
1497 | 462 | class DeltaSet(Record): | ||
1498 | 463 | |||
1499 | 464 | __slots__ = () | ||
1500 | 465 | |||
1501 | 466 | |||
1502 | 467 | Delta = collections.namedtuple('Delta', ['previous', 'current']) | ||
1503 | 468 | |||
1504 | 469 | |||
1505 | 470 | _KV = None | ||
1506 | 471 | |||
1507 | 472 | |||
1508 | 473 | def kv(): | ||
1509 | 474 | global _KV | ||
1510 | 475 | if _KV is None: | ||
1511 | 476 | _KV = Storage() | ||
1512 | 477 | return _KV | ||
1513 | 0 | 478 | ||
1514 | === modified file 'hooks/charmhelpers/fetch/archiveurl.py' | |||
1515 | --- hooks/charmhelpers/fetch/archiveurl.py 2015-01-26 09:45:59 +0000 | |||
1516 | +++ hooks/charmhelpers/fetch/archiveurl.py 2015-03-06 06:41:14 +0000 | |||
1517 | @@ -18,6 +18,16 @@ | |||
1518 | 18 | import hashlib | 18 | import hashlib |
1519 | 19 | import re | 19 | import re |
1520 | 20 | 20 | ||
1521 | 21 | from charmhelpers.fetch import ( | ||
1522 | 22 | BaseFetchHandler, | ||
1523 | 23 | UnhandledSource | ||
1524 | 24 | ) | ||
1525 | 25 | from charmhelpers.payload.archive import ( | ||
1526 | 26 | get_archive_handler, | ||
1527 | 27 | extract, | ||
1528 | 28 | ) | ||
1529 | 29 | from charmhelpers.core.host import mkdir, check_hash | ||
1530 | 30 | |||
1531 | 21 | import six | 31 | import six |
1532 | 22 | if six.PY3: | 32 | if six.PY3: |
1533 | 23 | from urllib.request import ( | 33 | from urllib.request import ( |
1534 | @@ -35,16 +45,6 @@ | |||
1535 | 35 | ) | 45 | ) |
1536 | 36 | from urlparse import urlparse, urlunparse, parse_qs | 46 | from urlparse import urlparse, urlunparse, parse_qs |
1537 | 37 | 47 | ||
1538 | 38 | from charmhelpers.fetch import ( | ||
1539 | 39 | BaseFetchHandler, | ||
1540 | 40 | UnhandledSource | ||
1541 | 41 | ) | ||
1542 | 42 | from charmhelpers.payload.archive import ( | ||
1543 | 43 | get_archive_handler, | ||
1544 | 44 | extract, | ||
1545 | 45 | ) | ||
1546 | 46 | from charmhelpers.core.host import mkdir, check_hash | ||
1547 | 47 | |||
1548 | 48 | 48 | ||
1549 | 49 | def splituser(host): | 49 | def splituser(host): |
1550 | 50 | '''urllib.splituser(), but six's support of this seems broken''' | 50 | '''urllib.splituser(), but six's support of this seems broken''' |
1551 | 51 | 51 | ||
1552 | === modified file 'hooks/charmhelpers/fetch/giturl.py' | |||
1553 | --- hooks/charmhelpers/fetch/giturl.py 2015-01-26 09:45:59 +0000 | |||
1554 | +++ hooks/charmhelpers/fetch/giturl.py 2015-03-06 06:41:14 +0000 | |||
1555 | @@ -32,7 +32,7 @@ | |||
1556 | 32 | apt_install("python-git") | 32 | apt_install("python-git") |
1557 | 33 | from git import Repo | 33 | from git import Repo |
1558 | 34 | 34 | ||
1560 | 35 | from git.exc import GitCommandError | 35 | from git.exc import GitCommandError # noqa E402 |
1561 | 36 | 36 | ||
1562 | 37 | 37 | ||
1563 | 38 | class GitUrlFetchHandler(BaseFetchHandler): | 38 | class GitUrlFetchHandler(BaseFetchHandler): |
1564 | 39 | 39 | ||
1565 | === modified file 'hooks/rabbit_utils.py' | |||
1566 | --- hooks/rabbit_utils.py 2015-01-22 15:37:28 +0000 | |||
1567 | +++ hooks/rabbit_utils.py 2015-03-06 06:41:14 +0000 | |||
1568 | @@ -570,3 +570,11 @@ | |||
1569 | 570 | if svcs: | 570 | if svcs: |
1570 | 571 | _map.append((f, svcs)) | 571 | _map.append((f, svcs)) |
1571 | 572 | return OrderedDict(_map) | 572 | return OrderedDict(_map) |
1572 | 573 | |||
1573 | 574 | |||
1574 | 575 | def services(): | ||
1575 | 576 | ''' Returns a list of services associate with this charm ''' | ||
1576 | 577 | _services = [] | ||
1577 | 578 | for v in restart_map().values(): | ||
1578 | 579 | _services = _services + v | ||
1579 | 580 | return list(set(_services)) | ||
1580 | 573 | 581 | ||
1581 | === modified file 'hooks/rabbitmq_server_relations.py' | |||
1582 | --- hooks/rabbitmq_server_relations.py 2015-01-27 08:43:50 +0000 | |||
1583 | +++ hooks/rabbitmq_server_relations.py 2015-03-06 06:41:14 +0000 | |||
1584 | @@ -60,7 +60,7 @@ | |||
1585 | 60 | service_stop, | 60 | service_stop, |
1586 | 61 | service_restart, | 61 | service_restart, |
1587 | 62 | ) | 62 | ) |
1589 | 63 | from charmhelpers.contrib.charmsupport.nrpe import NRPE | 63 | from charmhelpers.contrib.charmsupport import nrpe |
1590 | 64 | from charmhelpers.contrib.ssl.service import ServiceCA | 64 | from charmhelpers.contrib.ssl.service import ServiceCA |
1591 | 65 | 65 | ||
1592 | 66 | from charmhelpers.contrib.peerstorage import ( | 66 | from charmhelpers.contrib.peerstorage import ( |
1593 | @@ -474,29 +474,20 @@ | |||
1594 | 474 | os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq.py')) | 474 | os.path.join(NAGIOS_PLUGINS, 'check_rabbitmq.py')) |
1595 | 475 | 475 | ||
1596 | 476 | # Find out if nrpe set nagios_hostname | 476 | # Find out if nrpe set nagios_hostname |
1604 | 477 | hostname = None | 477 | hostname = nrpe.get_nagios_hostname() |
1605 | 478 | host_context = None | 478 | myunit = nrpe.get_nagios_unit_name() |
1606 | 479 | for rel in relations_of_type('nrpe-external-master'): | 479 | |
1600 | 480 | if 'nagios_hostname' in rel: | ||
1601 | 481 | hostname = rel['nagios_hostname'] | ||
1602 | 482 | host_context = rel['nagios_host_context'] | ||
1603 | 483 | break | ||
1607 | 484 | # create unique user and vhost for each unit | 480 | # create unique user and vhost for each unit |
1608 | 485 | current_unit = local_unit().replace('/', '-') | 481 | current_unit = local_unit().replace('/', '-') |
1609 | 486 | user = 'nagios-%s' % current_unit | 482 | user = 'nagios-%s' % current_unit |
1610 | 487 | vhost = 'nagios-%s' % current_unit | 483 | vhost = 'nagios-%s' % current_unit |
1611 | 488 | password = rabbit.get_rabbit_password(user) | 484 | password = rabbit.get_rabbit_password(user) |
1612 | 489 | 485 | ||
1613 | 490 | if host_context: | ||
1614 | 491 | myunit = "%s:%s" % (host_context, local_unit()) | ||
1615 | 492 | else: | ||
1616 | 493 | myunit = local_unit() | ||
1617 | 494 | |||
1618 | 495 | rabbit.create_vhost(vhost) | 486 | rabbit.create_vhost(vhost) |
1619 | 496 | rabbit.create_user(user, password) | 487 | rabbit.create_user(user, password) |
1620 | 497 | rabbit.grant_permissions(user, vhost) | 488 | rabbit.grant_permissions(user, vhost) |
1621 | 498 | 489 | ||
1623 | 499 | nrpe_compat = NRPE(hostname=hostname) | 490 | nrpe_compat = nrpe.NRPE(hostname=hostname) |
1624 | 500 | nrpe_compat.add_check( | 491 | nrpe_compat.add_check( |
1625 | 501 | shortname=rabbit.RABBIT_USER, | 492 | shortname=rabbit.RABBIT_USER, |
1626 | 502 | description='Check RabbitMQ {%s}' % myunit, | 493 | description='Check RabbitMQ {%s}' % myunit, |
charm_unit_test #2183 rabbitmq-server for brad-marshall mp251551
UNIT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_unit_ test/2183/