Merge lp:~brad-marshall/charms/trusty/keystone/add-haproxy-nrpe-fix-servicegroups into lp:~openstack-charmers-archive/charms/trusty/keystone/next

Proposed by Brad Marshall
Status: Merged
Merged at revision: 124
Proposed branch: lp:~brad-marshall/charms/trusty/keystone/add-haproxy-nrpe-fix-servicegroups
Merge into: lp:~openstack-charmers-archive/charms/trusty/keystone/next
Diff against target: 1360 lines (+50/-608)
9 files modified
config.yaml (+6/-0)
hooks/charmhelpers/contrib/openstack/context.py (+28/-9)
hooks/charmhelpers/contrib/openstack/files/__init__.py (+0/-18)
hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh (+0/-32)
hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh (+0/-30)
hooks/charmhelpers/contrib/openstack/templates/zeromq (+14/-0)
hooks/charmhelpers/core/strutils.py (+0/-42)
hooks/charmhelpers/core/unitdata.py (+0/-477)
hooks/keystone_hooks.py (+2/-0)
To merge this branch: bzr merge lp:~brad-marshall/charms/trusty/keystone/add-haproxy-nrpe-fix-servicegroups
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+250706@code.launchpad.net

Description of the change

Synced charmhelpers, added nagios_servicegroup config option, and added haproxy nrpe checks.

To post a comment you must log in.
123. By Edward Hope-Morley

[trivial] charmhelpers sync

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #2221 keystone-next for brad-marshall mp250706
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/2221/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #2010 keystone-next for brad-marshall mp250706
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/2010/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #2167 keystone-next for brad-marshall mp250706
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
  ERROR subprocess encountered error code 1
  make: *** [test] Error 1

Full amulet test output: http://paste.ubuntu.com/10396869/
Build: http://10.245.162.77:8080/job/charm_amulet_test/2167/

124. By Brad Marshall

[bradm] Fixed merge conflicts

Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2015-01-21 14:38:54 +0000
3+++ config.yaml 2015-02-26 04:33:22 +0000
4@@ -248,4 +248,10 @@
5 juju-myservice-0
6 If you're running multiple environments with the same services in them
7 this allows you to differentiate between them.
8+ nagios_servicegroups:
9+ default: ""
10+ type: string
11+ description: |
12+ A comma-separated list of nagios servicegroups.
13+ If left empty, the nagios_context will be used as the servicegroup
14
15
16=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
17--- hooks/charmhelpers/contrib/openstack/context.py 2015-01-26 09:44:47 +0000
18+++ hooks/charmhelpers/contrib/openstack/context.py 2015-02-26 04:33:22 +0000
19@@ -279,9 +279,25 @@
20 class IdentityServiceContext(OSContextGenerator):
21 interfaces = ['identity-service']
22
23+ def __init__(self, service=None, service_user=None):
24+ self.service = service
25+ self.service_user = service_user
26+
27 def __call__(self):
28 log('Generating template context for identity-service', level=DEBUG)
29 ctxt = {}
30+
31+ if self.service and self.service_user:
32+ # This is required for pki token signing if we don't want /tmp to
33+ # be used.
34+ cachedir = '/var/cache/%s' % (self.service)
35+ if not os.path.isdir(cachedir):
36+ log("Creating service cache dir %s" % (cachedir), level=DEBUG)
37+ mkdir(path=cachedir, owner=self.service_user,
38+ group=self.service_user, perms=0o700)
39+
40+ ctxt['signing_dir'] = cachedir
41+
42 for rid in relation_ids('identity-service'):
43 for unit in related_units(rid):
44 rdata = relation_get(rid=rid, unit=unit)
45@@ -291,15 +307,16 @@
46 auth_host = format_ipv6_addr(auth_host) or auth_host
47 svc_protocol = rdata.get('service_protocol') or 'http'
48 auth_protocol = rdata.get('auth_protocol') or 'http'
49- ctxt = {'service_port': rdata.get('service_port'),
50- 'service_host': serv_host,
51- 'auth_host': auth_host,
52- 'auth_port': rdata.get('auth_port'),
53- 'admin_tenant_name': rdata.get('service_tenant'),
54- 'admin_user': rdata.get('service_username'),
55- 'admin_password': rdata.get('service_password'),
56- 'service_protocol': svc_protocol,
57- 'auth_protocol': auth_protocol}
58+ ctxt.update({'service_port': rdata.get('service_port'),
59+ 'service_host': serv_host,
60+ 'auth_host': auth_host,
61+ 'auth_port': rdata.get('auth_port'),
62+ 'admin_tenant_name': rdata.get('service_tenant'),
63+ 'admin_user': rdata.get('service_username'),
64+ 'admin_password': rdata.get('service_password'),
65+ 'service_protocol': svc_protocol,
66+ 'auth_protocol': auth_protocol})
67+
68 if context_complete(ctxt):
69 # NOTE(jamespage) this is required for >= icehouse
70 # so a missing value just indicates keystone needs
71@@ -1021,6 +1038,8 @@
72 for unit in related_units(rid):
73 ctxt['zmq_nonce'] = relation_get('nonce', unit, rid)
74 ctxt['zmq_host'] = relation_get('host', unit, rid)
75+ ctxt['zmq_redis_address'] = relation_get(
76+ 'zmq_redis_address', unit, rid)
77
78 return ctxt
79
80
81=== added directory 'hooks/charmhelpers/contrib/openstack/files'
82=== removed directory 'hooks/charmhelpers/contrib/openstack/files'
83=== added file 'hooks/charmhelpers/contrib/openstack/files/__init__.py'
84--- hooks/charmhelpers/contrib/openstack/files/__init__.py 1970-01-01 00:00:00 +0000
85+++ hooks/charmhelpers/contrib/openstack/files/__init__.py 2015-02-26 04:33:22 +0000
86@@ -0,0 +1,18 @@
87+# Copyright 2014-2015 Canonical Limited.
88+#
89+# This file is part of charm-helpers.
90+#
91+# charm-helpers is free software: you can redistribute it and/or modify
92+# it under the terms of the GNU Lesser General Public License version 3 as
93+# published by the Free Software Foundation.
94+#
95+# charm-helpers is distributed in the hope that it will be useful,
96+# but WITHOUT ANY WARRANTY; without even the implied warranty of
97+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
98+# GNU Lesser General Public License for more details.
99+#
100+# You should have received a copy of the GNU Lesser General Public License
101+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
102+
103+# dummy __init__.py to fool syncer into thinking this is a syncable python
104+# module
105
106=== removed file 'hooks/charmhelpers/contrib/openstack/files/__init__.py'
107--- hooks/charmhelpers/contrib/openstack/files/__init__.py 2015-02-24 11:05:07 +0000
108+++ hooks/charmhelpers/contrib/openstack/files/__init__.py 1970-01-01 00:00:00 +0000
109@@ -1,18 +0,0 @@
110-# Copyright 2014-2015 Canonical Limited.
111-#
112-# This file is part of charm-helpers.
113-#
114-# charm-helpers is free software: you can redistribute it and/or modify
115-# it under the terms of the GNU Lesser General Public License version 3 as
116-# published by the Free Software Foundation.
117-#
118-# charm-helpers is distributed in the hope that it will be useful,
119-# but WITHOUT ANY WARRANTY; without even the implied warranty of
120-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121-# GNU Lesser General Public License for more details.
122-#
123-# You should have received a copy of the GNU Lesser General Public License
124-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
125-
126-# dummy __init__.py to fool syncer into thinking this is a syncable python
127-# module
128
129=== added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh'
130--- hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 1970-01-01 00:00:00 +0000
131+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 2015-02-26 04:33:22 +0000
132@@ -0,0 +1,32 @@
133+#!/bin/bash
134+#--------------------------------------------
135+# This file is managed by Juju
136+#--------------------------------------------
137+#
138+# Copyright 2009,2012 Canonical Ltd.
139+# Author: Tom Haddon
140+
141+CRITICAL=0
142+NOTACTIVE=''
143+LOGFILE=/var/log/nagios/check_haproxy.log
144+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
145+
146+for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'});
147+do
148+ 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')
149+ if [ $? != 0 ]; then
150+ date >> $LOGFILE
151+ echo $output >> $LOGFILE
152+ /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -v | grep $appserver >> $LOGFILE 2>&1
153+ CRITICAL=1
154+ NOTACTIVE="${NOTACTIVE} $appserver"
155+ fi
156+done
157+
158+if [ $CRITICAL = 1 ]; then
159+ echo "CRITICAL:${NOTACTIVE}"
160+ exit 2
161+fi
162+
163+echo "OK: All haproxy instances looking good"
164+exit 0
165
166=== removed file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh'
167--- hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 2015-02-24 11:05:07 +0000
168+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 1970-01-01 00:00:00 +0000
169@@ -1,32 +0,0 @@
170-#!/bin/bash
171-#--------------------------------------------
172-# This file is managed by Juju
173-#--------------------------------------------
174-#
175-# Copyright 2009,2012 Canonical Ltd.
176-# Author: Tom Haddon
177-
178-CRITICAL=0
179-NOTACTIVE=''
180-LOGFILE=/var/log/nagios/check_haproxy.log
181-AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
182-
183-for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'});
184-do
185- 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')
186- if [ $? != 0 ]; then
187- date >> $LOGFILE
188- echo $output >> $LOGFILE
189- /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -v | grep $appserver >> $LOGFILE 2>&1
190- CRITICAL=1
191- NOTACTIVE="${NOTACTIVE} $appserver"
192- fi
193-done
194-
195-if [ $CRITICAL = 1 ]; then
196- echo "CRITICAL:${NOTACTIVE}"
197- exit 2
198-fi
199-
200-echo "OK: All haproxy instances looking good"
201-exit 0
202
203=== added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh'
204--- hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000
205+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 2015-02-26 04:33:22 +0000
206@@ -0,0 +1,30 @@
207+#!/bin/bash
208+#--------------------------------------------
209+# This file is managed by Juju
210+#--------------------------------------------
211+#
212+# Copyright 2009,2012 Canonical Ltd.
213+# Author: Tom Haddon
214+
215+# These should be config options at some stage
216+CURRQthrsh=0
217+MAXQthrsh=100
218+
219+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
220+
221+HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v)
222+
223+for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}')
224+do
225+ CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3)
226+ MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4)
227+
228+ if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then
229+ echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ"
230+ exit 2
231+ fi
232+done
233+
234+echo "OK: All haproxy queue depths looking good"
235+exit 0
236+
237
238=== removed file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh'
239--- hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 2015-02-24 11:05:07 +0000
240+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000
241@@ -1,30 +0,0 @@
242-#!/bin/bash
243-#--------------------------------------------
244-# This file is managed by Juju
245-#--------------------------------------------
246-#
247-# Copyright 2009,2012 Canonical Ltd.
248-# Author: Tom Haddon
249-
250-# These should be config options at some stage
251-CURRQthrsh=0
252-MAXQthrsh=100
253-
254-AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
255-
256-HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v)
257-
258-for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}')
259-do
260- CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3)
261- MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4)
262-
263- if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then
264- echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ"
265- exit 2
266- fi
267-done
268-
269-echo "OK: All haproxy queue depths looking good"
270-exit 0
271-
272
273=== added file 'hooks/charmhelpers/contrib/openstack/templates/zeromq'
274--- hooks/charmhelpers/contrib/openstack/templates/zeromq 1970-01-01 00:00:00 +0000
275+++ hooks/charmhelpers/contrib/openstack/templates/zeromq 2015-02-26 04:33:22 +0000
276@@ -0,0 +1,14 @@
277+{% if zmq_host -%}
278+# ZeroMQ configuration (restart-nonce: {{ zmq_nonce }})
279+rpc_backend = zmq
280+rpc_zmq_host = {{ zmq_host }}
281+{% if zmq_redis_address -%}
282+rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
283+matchmaker_heartbeat_freq = 15
284+matchmaker_heartbeat_ttl = 30
285+[matchmaker_redis]
286+host = {{ zmq_redis_address }}
287+{% else -%}
288+rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing
289+{% endif -%}
290+{% endif -%}
291
292=== added file 'hooks/charmhelpers/core/strutils.py'
293--- hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000
294+++ hooks/charmhelpers/core/strutils.py 2015-02-26 04:33:22 +0000
295@@ -0,0 +1,42 @@
296+#!/usr/bin/env python
297+# -*- coding: utf-8 -*-
298+
299+# Copyright 2014-2015 Canonical Limited.
300+#
301+# This file is part of charm-helpers.
302+#
303+# charm-helpers is free software: you can redistribute it and/or modify
304+# it under the terms of the GNU Lesser General Public License version 3 as
305+# published by the Free Software Foundation.
306+#
307+# charm-helpers is distributed in the hope that it will be useful,
308+# but WITHOUT ANY WARRANTY; without even the implied warranty of
309+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
310+# GNU Lesser General Public License for more details.
311+#
312+# You should have received a copy of the GNU Lesser General Public License
313+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
314+
315+import six
316+
317+
318+def bool_from_string(value):
319+ """Interpret string value as boolean.
320+
321+ Returns True if value translates to True otherwise False.
322+ """
323+ if isinstance(value, six.string_types):
324+ value = six.text_type(value)
325+ else:
326+ msg = "Unable to interpret non-string value '%s' as boolean" % (value)
327+ raise ValueError(msg)
328+
329+ value = value.strip().lower()
330+
331+ if value in ['y', 'yes', 'true', 't']:
332+ return True
333+ elif value in ['n', 'no', 'false', 'f']:
334+ return False
335+
336+ msg = "Unable to interpret string value '%s' as boolean" % (value)
337+ raise ValueError(msg)
338
339=== removed file 'hooks/charmhelpers/core/strutils.py'
340--- hooks/charmhelpers/core/strutils.py 2015-02-16 12:06:32 +0000
341+++ hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000
342@@ -1,42 +0,0 @@
343-#!/usr/bin/env python
344-# -*- coding: utf-8 -*-
345-
346-# Copyright 2014-2015 Canonical Limited.
347-#
348-# This file is part of charm-helpers.
349-#
350-# charm-helpers is free software: you can redistribute it and/or modify
351-# it under the terms of the GNU Lesser General Public License version 3 as
352-# published by the Free Software Foundation.
353-#
354-# charm-helpers is distributed in the hope that it will be useful,
355-# but WITHOUT ANY WARRANTY; without even the implied warranty of
356-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
357-# GNU Lesser General Public License for more details.
358-#
359-# You should have received a copy of the GNU Lesser General Public License
360-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
361-
362-import six
363-
364-
365-def bool_from_string(value):
366- """Interpret string value as boolean.
367-
368- Returns True if value translates to True otherwise False.
369- """
370- if isinstance(value, six.string_types):
371- value = six.text_type(value)
372- else:
373- msg = "Unable to interpret non-string value '%s' as boolean" % (value)
374- raise ValueError(msg)
375-
376- value = value.strip().lower()
377-
378- if value in ['y', 'yes', 'true', 't']:
379- return True
380- elif value in ['n', 'no', 'false', 'f']:
381- return False
382-
383- msg = "Unable to interpret string value '%s' as boolean" % (value)
384- raise ValueError(msg)
385
386=== added file 'hooks/charmhelpers/core/unitdata.py'
387--- hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000
388+++ hooks/charmhelpers/core/unitdata.py 2015-02-26 04:33:22 +0000
389@@ -0,0 +1,477 @@
390+#!/usr/bin/env python
391+# -*- coding: utf-8 -*-
392+#
393+# Copyright 2014-2015 Canonical Limited.
394+#
395+# This file is part of charm-helpers.
396+#
397+# charm-helpers is free software: you can redistribute it and/or modify
398+# it under the terms of the GNU Lesser General Public License version 3 as
399+# published by the Free Software Foundation.
400+#
401+# charm-helpers is distributed in the hope that it will be useful,
402+# but WITHOUT ANY WARRANTY; without even the implied warranty of
403+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
404+# GNU Lesser General Public License for more details.
405+#
406+# You should have received a copy of the GNU Lesser General Public License
407+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
408+#
409+#
410+# Authors:
411+# Kapil Thangavelu <kapil.foss@gmail.com>
412+#
413+"""
414+Intro
415+-----
416+
417+A simple way to store state in units. This provides a key value
418+storage with support for versioned, transactional operation,
419+and can calculate deltas from previous values to simplify unit logic
420+when processing changes.
421+
422+
423+Hook Integration
424+----------------
425+
426+There are several extant frameworks for hook execution, including
427+
428+ - charmhelpers.core.hookenv.Hooks
429+ - charmhelpers.core.services.ServiceManager
430+
431+The storage classes are framework agnostic, one simple integration is
432+via the HookData contextmanager. It will record the current hook
433+execution environment (including relation data, config data, etc.),
434+setup a transaction and allow easy access to the changes from
435+previously seen values. One consequence of the integration is the
436+reservation of particular keys ('rels', 'unit', 'env', 'config',
437+'charm_revisions') for their respective values.
438+
439+Here's a fully worked integration example using hookenv.Hooks::
440+
441+ from charmhelper.core import hookenv, unitdata
442+
443+ hook_data = unitdata.HookData()
444+ db = unitdata.kv()
445+ hooks = hookenv.Hooks()
446+
447+ @hooks.hook
448+ def config_changed():
449+ # Print all changes to configuration from previously seen
450+ # values.
451+ for changed, (prev, cur) in hook_data.conf.items():
452+ print('config changed', changed,
453+ 'previous value', prev,
454+ 'current value', cur)
455+
456+ # Get some unit specific bookeeping
457+ if not db.get('pkg_key'):
458+ key = urllib.urlopen('https://example.com/pkg_key').read()
459+ db.set('pkg_key', key)
460+
461+ # Directly access all charm config as a mapping.
462+ conf = db.getrange('config', True)
463+
464+ # Directly access all relation data as a mapping
465+ rels = db.getrange('rels', True)
466+
467+ if __name__ == '__main__':
468+ with hook_data():
469+ hook.execute()
470+
471+
472+A more basic integration is via the hook_scope context manager which simply
473+manages transaction scope (and records hook name, and timestamp)::
474+
475+ >>> from unitdata import kv
476+ >>> db = kv()
477+ >>> with db.hook_scope('install'):
478+ ... # do work, in transactional scope.
479+ ... db.set('x', 1)
480+ >>> db.get('x')
481+ 1
482+
483+
484+Usage
485+-----
486+
487+Values are automatically json de/serialized to preserve basic typing
488+and complex data struct capabilities (dicts, lists, ints, booleans, etc).
489+
490+Individual values can be manipulated via get/set::
491+
492+ >>> kv.set('y', True)
493+ >>> kv.get('y')
494+ True
495+
496+ # We can set complex values (dicts, lists) as a single key.
497+ >>> kv.set('config', {'a': 1, 'b': True'})
498+
499+ # Also supports returning dictionaries as a record which
500+ # provides attribute access.
501+ >>> config = kv.get('config', record=True)
502+ >>> config.b
503+ True
504+
505+
506+Groups of keys can be manipulated with update/getrange::
507+
508+ >>> kv.update({'z': 1, 'y': 2}, prefix="gui.")
509+ >>> kv.getrange('gui.', strip=True)
510+ {'z': 1, 'y': 2}
511+
512+When updating values, its very helpful to understand which values
513+have actually changed and how have they changed. The storage
514+provides a delta method to provide for this::
515+
516+ >>> data = {'debug': True, 'option': 2}
517+ >>> delta = kv.delta(data, 'config.')
518+ >>> delta.debug.previous
519+ None
520+ >>> delta.debug.current
521+ True
522+ >>> delta
523+ {'debug': (None, True), 'option': (None, 2)}
524+
525+Note the delta method does not persist the actual change, it needs to
526+be explicitly saved via 'update' method::
527+
528+ >>> kv.update(data, 'config.')
529+
530+Values modified in the context of a hook scope retain historical values
531+associated to the hookname.
532+
533+ >>> with db.hook_scope('config-changed'):
534+ ... db.set('x', 42)
535+ >>> db.gethistory('x')
536+ [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'),
537+ (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')]
538+
539+"""
540+
541+import collections
542+import contextlib
543+import datetime
544+import json
545+import os
546+import pprint
547+import sqlite3
548+import sys
549+
550+__author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>'
551+
552+
553+class Storage(object):
554+ """Simple key value database for local unit state within charms.
555+
556+ Modifications are automatically committed at hook exit. That's
557+ currently regardless of exit code.
558+
559+ To support dicts, lists, integer, floats, and booleans values
560+ are automatically json encoded/decoded.
561+ """
562+ def __init__(self, path=None):
563+ self.db_path = path
564+ if path is None:
565+ self.db_path = os.path.join(
566+ os.environ.get('CHARM_DIR', ''), '.unit-state.db')
567+ self.conn = sqlite3.connect('%s' % self.db_path)
568+ self.cursor = self.conn.cursor()
569+ self.revision = None
570+ self._closed = False
571+ self._init()
572+
573+ def close(self):
574+ if self._closed:
575+ return
576+ self.flush(False)
577+ self.cursor.close()
578+ self.conn.close()
579+ self._closed = True
580+
581+ def _scoped_query(self, stmt, params=None):
582+ if params is None:
583+ params = []
584+ return stmt, params
585+
586+ def get(self, key, default=None, record=False):
587+ self.cursor.execute(
588+ *self._scoped_query(
589+ 'select data from kv where key=?', [key]))
590+ result = self.cursor.fetchone()
591+ if not result:
592+ return default
593+ if record:
594+ return Record(json.loads(result[0]))
595+ return json.loads(result[0])
596+
597+ def getrange(self, key_prefix, strip=False):
598+ stmt = "select key, data from kv where key like '%s%%'" % key_prefix
599+ self.cursor.execute(*self._scoped_query(stmt))
600+ result = self.cursor.fetchall()
601+
602+ if not result:
603+ return None
604+ if not strip:
605+ key_prefix = ''
606+ return dict([
607+ (k[len(key_prefix):], json.loads(v)) for k, v in result])
608+
609+ def update(self, mapping, prefix=""):
610+ for k, v in mapping.items():
611+ self.set("%s%s" % (prefix, k), v)
612+
613+ def unset(self, key):
614+ self.cursor.execute('delete from kv where key=?', [key])
615+ if self.revision and self.cursor.rowcount:
616+ self.cursor.execute(
617+ 'insert into kv_revisions values (?, ?, ?)',
618+ [key, self.revision, json.dumps('DELETED')])
619+
620+ def set(self, key, value):
621+ serialized = json.dumps(value)
622+
623+ self.cursor.execute(
624+ 'select data from kv where key=?', [key])
625+ exists = self.cursor.fetchone()
626+
627+ # Skip mutations to the same value
628+ if exists:
629+ if exists[0] == serialized:
630+ return value
631+
632+ if not exists:
633+ self.cursor.execute(
634+ 'insert into kv (key, data) values (?, ?)',
635+ (key, serialized))
636+ else:
637+ self.cursor.execute('''
638+ update kv
639+ set data = ?
640+ where key = ?''', [serialized, key])
641+
642+ # Save
643+ if not self.revision:
644+ return value
645+
646+ self.cursor.execute(
647+ 'select 1 from kv_revisions where key=? and revision=?',
648+ [key, self.revision])
649+ exists = self.cursor.fetchone()
650+
651+ if not exists:
652+ self.cursor.execute(
653+ '''insert into kv_revisions (
654+ revision, key, data) values (?, ?, ?)''',
655+ (self.revision, key, serialized))
656+ else:
657+ self.cursor.execute(
658+ '''
659+ update kv_revisions
660+ set data = ?
661+ where key = ?
662+ and revision = ?''',
663+ [serialized, key, self.revision])
664+
665+ return value
666+
667+ def delta(self, mapping, prefix):
668+ """
669+ return a delta containing values that have changed.
670+ """
671+ previous = self.getrange(prefix, strip=True)
672+ if not previous:
673+ pk = set()
674+ else:
675+ pk = set(previous.keys())
676+ ck = set(mapping.keys())
677+ delta = DeltaSet()
678+
679+ # added
680+ for k in ck.difference(pk):
681+ delta[k] = Delta(None, mapping[k])
682+
683+ # removed
684+ for k in pk.difference(ck):
685+ delta[k] = Delta(previous[k], None)
686+
687+ # changed
688+ for k in pk.intersection(ck):
689+ c = mapping[k]
690+ p = previous[k]
691+ if c != p:
692+ delta[k] = Delta(p, c)
693+
694+ return delta
695+
696+ @contextlib.contextmanager
697+ def hook_scope(self, name=""):
698+ """Scope all future interactions to the current hook execution
699+ revision."""
700+ assert not self.revision
701+ self.cursor.execute(
702+ 'insert into hooks (hook, date) values (?, ?)',
703+ (name or sys.argv[0],
704+ datetime.datetime.utcnow().isoformat()))
705+ self.revision = self.cursor.lastrowid
706+ try:
707+ yield self.revision
708+ self.revision = None
709+ except:
710+ self.flush(False)
711+ self.revision = None
712+ raise
713+ else:
714+ self.flush()
715+
716+ def flush(self, save=True):
717+ if save:
718+ self.conn.commit()
719+ elif self._closed:
720+ return
721+ else:
722+ self.conn.rollback()
723+
724+ def _init(self):
725+ self.cursor.execute('''
726+ create table if not exists kv (
727+ key text,
728+ data text,
729+ primary key (key)
730+ )''')
731+ self.cursor.execute('''
732+ create table if not exists kv_revisions (
733+ key text,
734+ revision integer,
735+ data text,
736+ primary key (key, revision)
737+ )''')
738+ self.cursor.execute('''
739+ create table if not exists hooks (
740+ version integer primary key autoincrement,
741+ hook text,
742+ date text
743+ )''')
744+ self.conn.commit()
745+
746+ def gethistory(self, key, deserialize=False):
747+ self.cursor.execute(
748+ '''
749+ select kv.revision, kv.key, kv.data, h.hook, h.date
750+ from kv_revisions kv,
751+ hooks h
752+ where kv.key=?
753+ and kv.revision = h.version
754+ ''', [key])
755+ if deserialize is False:
756+ return self.cursor.fetchall()
757+ return map(_parse_history, self.cursor.fetchall())
758+
759+ def debug(self, fh=sys.stderr):
760+ self.cursor.execute('select * from kv')
761+ pprint.pprint(self.cursor.fetchall(), stream=fh)
762+ self.cursor.execute('select * from kv_revisions')
763+ pprint.pprint(self.cursor.fetchall(), stream=fh)
764+
765+
766+def _parse_history(d):
767+ return (d[0], d[1], json.loads(d[2]), d[3],
768+ datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f"))
769+
770+
771+class HookData(object):
772+ """Simple integration for existing hook exec frameworks.
773+
774+ Records all unit information, and stores deltas for processing
775+ by the hook.
776+
777+ Sample::
778+
779+ from charmhelper.core import hookenv, unitdata
780+
781+ changes = unitdata.HookData()
782+ db = unitdata.kv()
783+ hooks = hookenv.Hooks()
784+
785+ @hooks.hook
786+ def config_changed():
787+ # View all changes to configuration
788+ for changed, (prev, cur) in changes.conf.items():
789+ print('config changed', changed,
790+ 'previous value', prev,
791+ 'current value', cur)
792+
793+ # Get some unit specific bookeeping
794+ if not db.get('pkg_key'):
795+ key = urllib.urlopen('https://example.com/pkg_key').read()
796+ db.set('pkg_key', key)
797+
798+ if __name__ == '__main__':
799+ with changes():
800+ hook.execute()
801+
802+ """
803+ def __init__(self):
804+ self.kv = kv()
805+ self.conf = None
806+ self.rels = None
807+
808+ @contextlib.contextmanager
809+ def __call__(self):
810+ from charmhelpers.core import hookenv
811+ hook_name = hookenv.hook_name()
812+
813+ with self.kv.hook_scope(hook_name):
814+ self._record_charm_version(hookenv.charm_dir())
815+ delta_config, delta_relation = self._record_hook(hookenv)
816+ yield self.kv, delta_config, delta_relation
817+
818+ def _record_charm_version(self, charm_dir):
819+ # Record revisions.. charm revisions are meaningless
820+ # to charm authors as they don't control the revision.
821+ # so logic dependnent on revision is not particularly
822+ # useful, however it is useful for debugging analysis.
823+ charm_rev = open(
824+ os.path.join(charm_dir, 'revision')).read().strip()
825+ charm_rev = charm_rev or '0'
826+ revs = self.kv.get('charm_revisions', [])
827+ if charm_rev not in revs:
828+ revs.append(charm_rev.strip() or '0')
829+ self.kv.set('charm_revisions', revs)
830+
831+ def _record_hook(self, hookenv):
832+ data = hookenv.execution_environment()
833+ self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
834+ self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
835+ self.kv.set('env', data['env'])
836+ self.kv.set('unit', data['unit'])
837+ self.kv.set('relid', data.get('relid'))
838+ return conf_delta, rels_delta
839+
840+
841+class Record(dict):
842+
843+ __slots__ = ()
844+
845+ def __getattr__(self, k):
846+ if k in self:
847+ return self[k]
848+ raise AttributeError(k)
849+
850+
851+class DeltaSet(Record):
852+
853+ __slots__ = ()
854+
855+
856+Delta = collections.namedtuple('Delta', ['previous', 'current'])
857+
858+
859+_KV = None
860+
861+
862+def kv():
863+ global _KV
864+ if _KV is None:
865+ _KV = Storage()
866+ return _KV
867
868=== removed file 'hooks/charmhelpers/core/unitdata.py'
869--- hooks/charmhelpers/core/unitdata.py 2015-02-16 11:25:45 +0000
870+++ hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000
871@@ -1,477 +0,0 @@
872-#!/usr/bin/env python
873-# -*- coding: utf-8 -*-
874-#
875-# Copyright 2014-2015 Canonical Limited.
876-#
877-# This file is part of charm-helpers.
878-#
879-# charm-helpers is free software: you can redistribute it and/or modify
880-# it under the terms of the GNU Lesser General Public License version 3 as
881-# published by the Free Software Foundation.
882-#
883-# charm-helpers is distributed in the hope that it will be useful,
884-# but WITHOUT ANY WARRANTY; without even the implied warranty of
885-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
886-# GNU Lesser General Public License for more details.
887-#
888-# You should have received a copy of the GNU Lesser General Public License
889-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
890-#
891-#
892-# Authors:
893-# Kapil Thangavelu <kapil.foss@gmail.com>
894-#
895-"""
896-Intro
897------
898-
899-A simple way to store state in units. This provides a key value
900-storage with support for versioned, transactional operation,
901-and can calculate deltas from previous values to simplify unit logic
902-when processing changes.
903-
904-
905-Hook Integration
906-----------------
907-
908-There are several extant frameworks for hook execution, including
909-
910- - charmhelpers.core.hookenv.Hooks
911- - charmhelpers.core.services.ServiceManager
912-
913-The storage classes are framework agnostic, one simple integration is
914-via the HookData contextmanager. It will record the current hook
915-execution environment (including relation data, config data, etc.),
916-setup a transaction and allow easy access to the changes from
917-previously seen values. One consequence of the integration is the
918-reservation of particular keys ('rels', 'unit', 'env', 'config',
919-'charm_revisions') for their respective values.
920-
921-Here's a fully worked integration example using hookenv.Hooks::
922-
923- from charmhelper.core import hookenv, unitdata
924-
925- hook_data = unitdata.HookData()
926- db = unitdata.kv()
927- hooks = hookenv.Hooks()
928-
929- @hooks.hook
930- def config_changed():
931- # Print all changes to configuration from previously seen
932- # values.
933- for changed, (prev, cur) in hook_data.conf.items():
934- print('config changed', changed,
935- 'previous value', prev,
936- 'current value', cur)
937-
938- # Get some unit specific bookeeping
939- if not db.get('pkg_key'):
940- key = urllib.urlopen('https://example.com/pkg_key').read()
941- db.set('pkg_key', key)
942-
943- # Directly access all charm config as a mapping.
944- conf = db.getrange('config', True)
945-
946- # Directly access all relation data as a mapping
947- rels = db.getrange('rels', True)
948-
949- if __name__ == '__main__':
950- with hook_data():
951- hook.execute()
952-
953-
954-A more basic integration is via the hook_scope context manager which simply
955-manages transaction scope (and records hook name, and timestamp)::
956-
957- >>> from unitdata import kv
958- >>> db = kv()
959- >>> with db.hook_scope('install'):
960- ... # do work, in transactional scope.
961- ... db.set('x', 1)
962- >>> db.get('x')
963- 1
964-
965-
966-Usage
967------
968-
969-Values are automatically json de/serialized to preserve basic typing
970-and complex data struct capabilities (dicts, lists, ints, booleans, etc).
971-
972-Individual values can be manipulated via get/set::
973-
974- >>> kv.set('y', True)
975- >>> kv.get('y')
976- True
977-
978- # We can set complex values (dicts, lists) as a single key.
979- >>> kv.set('config', {'a': 1, 'b': True'})
980-
981- # Also supports returning dictionaries as a record which
982- # provides attribute access.
983- >>> config = kv.get('config', record=True)
984- >>> config.b
985- True
986-
987-
988-Groups of keys can be manipulated with update/getrange::
989-
990- >>> kv.update({'z': 1, 'y': 2}, prefix="gui.")
991- >>> kv.getrange('gui.', strip=True)
992- {'z': 1, 'y': 2}
993-
994-When updating values, its very helpful to understand which values
995-have actually changed and how have they changed. The storage
996-provides a delta method to provide for this::
997-
998- >>> data = {'debug': True, 'option': 2}
999- >>> delta = kv.delta(data, 'config.')
1000- >>> delta.debug.previous
1001- None
1002- >>> delta.debug.current
1003- True
1004- >>> delta
1005- {'debug': (None, True), 'option': (None, 2)}
1006-
1007-Note the delta method does not persist the actual change, it needs to
1008-be explicitly saved via 'update' method::
1009-
1010- >>> kv.update(data, 'config.')
1011-
1012-Values modified in the context of a hook scope retain historical values
1013-associated to the hookname.
1014-
1015- >>> with db.hook_scope('config-changed'):
1016- ... db.set('x', 42)
1017- >>> db.gethistory('x')
1018- [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'),
1019- (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')]
1020-
1021-"""
1022-
1023-import collections
1024-import contextlib
1025-import datetime
1026-import json
1027-import os
1028-import pprint
1029-import sqlite3
1030-import sys
1031-
1032-__author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>'
1033-
1034-
1035-class Storage(object):
1036- """Simple key value database for local unit state within charms.
1037-
1038- Modifications are automatically committed at hook exit. That's
1039- currently regardless of exit code.
1040-
1041- To support dicts, lists, integer, floats, and booleans values
1042- are automatically json encoded/decoded.
1043- """
1044- def __init__(self, path=None):
1045- self.db_path = path
1046- if path is None:
1047- self.db_path = os.path.join(
1048- os.environ.get('CHARM_DIR', ''), '.unit-state.db')
1049- self.conn = sqlite3.connect('%s' % self.db_path)
1050- self.cursor = self.conn.cursor()
1051- self.revision = None
1052- self._closed = False
1053- self._init()
1054-
1055- def close(self):
1056- if self._closed:
1057- return
1058- self.flush(False)
1059- self.cursor.close()
1060- self.conn.close()
1061- self._closed = True
1062-
1063- def _scoped_query(self, stmt, params=None):
1064- if params is None:
1065- params = []
1066- return stmt, params
1067-
1068- def get(self, key, default=None, record=False):
1069- self.cursor.execute(
1070- *self._scoped_query(
1071- 'select data from kv where key=?', [key]))
1072- result = self.cursor.fetchone()
1073- if not result:
1074- return default
1075- if record:
1076- return Record(json.loads(result[0]))
1077- return json.loads(result[0])
1078-
1079- def getrange(self, key_prefix, strip=False):
1080- stmt = "select key, data from kv where key like '%s%%'" % key_prefix
1081- self.cursor.execute(*self._scoped_query(stmt))
1082- result = self.cursor.fetchall()
1083-
1084- if not result:
1085- return None
1086- if not strip:
1087- key_prefix = ''
1088- return dict([
1089- (k[len(key_prefix):], json.loads(v)) for k, v in result])
1090-
1091- def update(self, mapping, prefix=""):
1092- for k, v in mapping.items():
1093- self.set("%s%s" % (prefix, k), v)
1094-
1095- def unset(self, key):
1096- self.cursor.execute('delete from kv where key=?', [key])
1097- if self.revision and self.cursor.rowcount:
1098- self.cursor.execute(
1099- 'insert into kv_revisions values (?, ?, ?)',
1100- [key, self.revision, json.dumps('DELETED')])
1101-
1102- def set(self, key, value):
1103- serialized = json.dumps(value)
1104-
1105- self.cursor.execute(
1106- 'select data from kv where key=?', [key])
1107- exists = self.cursor.fetchone()
1108-
1109- # Skip mutations to the same value
1110- if exists:
1111- if exists[0] == serialized:
1112- return value
1113-
1114- if not exists:
1115- self.cursor.execute(
1116- 'insert into kv (key, data) values (?, ?)',
1117- (key, serialized))
1118- else:
1119- self.cursor.execute('''
1120- update kv
1121- set data = ?
1122- where key = ?''', [serialized, key])
1123-
1124- # Save
1125- if not self.revision:
1126- return value
1127-
1128- self.cursor.execute(
1129- 'select 1 from kv_revisions where key=? and revision=?',
1130- [key, self.revision])
1131- exists = self.cursor.fetchone()
1132-
1133- if not exists:
1134- self.cursor.execute(
1135- '''insert into kv_revisions (
1136- revision, key, data) values (?, ?, ?)''',
1137- (self.revision, key, serialized))
1138- else:
1139- self.cursor.execute(
1140- '''
1141- update kv_revisions
1142- set data = ?
1143- where key = ?
1144- and revision = ?''',
1145- [serialized, key, self.revision])
1146-
1147- return value
1148-
1149- def delta(self, mapping, prefix):
1150- """
1151- return a delta containing values that have changed.
1152- """
1153- previous = self.getrange(prefix, strip=True)
1154- if not previous:
1155- pk = set()
1156- else:
1157- pk = set(previous.keys())
1158- ck = set(mapping.keys())
1159- delta = DeltaSet()
1160-
1161- # added
1162- for k in ck.difference(pk):
1163- delta[k] = Delta(None, mapping[k])
1164-
1165- # removed
1166- for k in pk.difference(ck):
1167- delta[k] = Delta(previous[k], None)
1168-
1169- # changed
1170- for k in pk.intersection(ck):
1171- c = mapping[k]
1172- p = previous[k]
1173- if c != p:
1174- delta[k] = Delta(p, c)
1175-
1176- return delta
1177-
1178- @contextlib.contextmanager
1179- def hook_scope(self, name=""):
1180- """Scope all future interactions to the current hook execution
1181- revision."""
1182- assert not self.revision
1183- self.cursor.execute(
1184- 'insert into hooks (hook, date) values (?, ?)',
1185- (name or sys.argv[0],
1186- datetime.datetime.utcnow().isoformat()))
1187- self.revision = self.cursor.lastrowid
1188- try:
1189- yield self.revision
1190- self.revision = None
1191- except:
1192- self.flush(False)
1193- self.revision = None
1194- raise
1195- else:
1196- self.flush()
1197-
1198- def flush(self, save=True):
1199- if save:
1200- self.conn.commit()
1201- elif self._closed:
1202- return
1203- else:
1204- self.conn.rollback()
1205-
1206- def _init(self):
1207- self.cursor.execute('''
1208- create table if not exists kv (
1209- key text,
1210- data text,
1211- primary key (key)
1212- )''')
1213- self.cursor.execute('''
1214- create table if not exists kv_revisions (
1215- key text,
1216- revision integer,
1217- data text,
1218- primary key (key, revision)
1219- )''')
1220- self.cursor.execute('''
1221- create table if not exists hooks (
1222- version integer primary key autoincrement,
1223- hook text,
1224- date text
1225- )''')
1226- self.conn.commit()
1227-
1228- def gethistory(self, key, deserialize=False):
1229- self.cursor.execute(
1230- '''
1231- select kv.revision, kv.key, kv.data, h.hook, h.date
1232- from kv_revisions kv,
1233- hooks h
1234- where kv.key=?
1235- and kv.revision = h.version
1236- ''', [key])
1237- if deserialize is False:
1238- return self.cursor.fetchall()
1239- return map(_parse_history, self.cursor.fetchall())
1240-
1241- def debug(self, fh=sys.stderr):
1242- self.cursor.execute('select * from kv')
1243- pprint.pprint(self.cursor.fetchall(), stream=fh)
1244- self.cursor.execute('select * from kv_revisions')
1245- pprint.pprint(self.cursor.fetchall(), stream=fh)
1246-
1247-
1248-def _parse_history(d):
1249- return (d[0], d[1], json.loads(d[2]), d[3],
1250- datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f"))
1251-
1252-
1253-class HookData(object):
1254- """Simple integration for existing hook exec frameworks.
1255-
1256- Records all unit information, and stores deltas for processing
1257- by the hook.
1258-
1259- Sample::
1260-
1261- from charmhelper.core import hookenv, unitdata
1262-
1263- changes = unitdata.HookData()
1264- db = unitdata.kv()
1265- hooks = hookenv.Hooks()
1266-
1267- @hooks.hook
1268- def config_changed():
1269- # View all changes to configuration
1270- for changed, (prev, cur) in changes.conf.items():
1271- print('config changed', changed,
1272- 'previous value', prev,
1273- 'current value', cur)
1274-
1275- # Get some unit specific bookeeping
1276- if not db.get('pkg_key'):
1277- key = urllib.urlopen('https://example.com/pkg_key').read()
1278- db.set('pkg_key', key)
1279-
1280- if __name__ == '__main__':
1281- with changes():
1282- hook.execute()
1283-
1284- """
1285- def __init__(self):
1286- self.kv = kv()
1287- self.conf = None
1288- self.rels = None
1289-
1290- @contextlib.contextmanager
1291- def __call__(self):
1292- from charmhelpers.core import hookenv
1293- hook_name = hookenv.hook_name()
1294-
1295- with self.kv.hook_scope(hook_name):
1296- self._record_charm_version(hookenv.charm_dir())
1297- delta_config, delta_relation = self._record_hook(hookenv)
1298- yield self.kv, delta_config, delta_relation
1299-
1300- def _record_charm_version(self, charm_dir):
1301- # Record revisions.. charm revisions are meaningless
1302- # to charm authors as they don't control the revision.
1303- # so logic dependnent on revision is not particularly
1304- # useful, however it is useful for debugging analysis.
1305- charm_rev = open(
1306- os.path.join(charm_dir, 'revision')).read().strip()
1307- charm_rev = charm_rev or '0'
1308- revs = self.kv.get('charm_revisions', [])
1309- if charm_rev not in revs:
1310- revs.append(charm_rev.strip() or '0')
1311- self.kv.set('charm_revisions', revs)
1312-
1313- def _record_hook(self, hookenv):
1314- data = hookenv.execution_environment()
1315- self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
1316- self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
1317- self.kv.set('env', data['env'])
1318- self.kv.set('unit', data['unit'])
1319- self.kv.set('relid', data.get('relid'))
1320- return conf_delta, rels_delta
1321-
1322-
1323-class Record(dict):
1324-
1325- __slots__ = ()
1326-
1327- def __getattr__(self, k):
1328- if k in self:
1329- return self[k]
1330- raise AttributeError(k)
1331-
1332-
1333-class DeltaSet(Record):
1334-
1335- __slots__ = ()
1336-
1337-
1338-Delta = collections.namedtuple('Delta', ['previous', 'current'])
1339-
1340-
1341-_KV = None
1342-
1343-
1344-def kv():
1345- global _KV
1346- if _KV is None:
1347- _KV = Storage()
1348- return _KV
1349
1350=== modified file 'hooks/keystone_hooks.py'
1351--- hooks/keystone_hooks.py 2015-02-17 09:22:46 +0000
1352+++ hooks/keystone_hooks.py 2015-02-26 04:33:22 +0000
1353@@ -563,7 +563,9 @@
1354 hostname = nrpe.get_nagios_hostname()
1355 current_unit = nrpe.get_nagios_unit_name()
1356 nrpe_setup = nrpe.NRPE(hostname=hostname)
1357+ nrpe.copy_nrpe_checks()
1358 nrpe.add_init_service_checks(nrpe_setup, services(), current_unit)
1359+ nrpe.add_haproxy_checks(nrpe_setup, current_unit)
1360 nrpe_setup.write()
1361
1362

Subscribers

People subscribed via source and target branches