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

Proposed by Brad Marshall
Status: Merged
Merged at revision: 55
Proposed branch: lp:~brad-marshall/charms/trusty/openstack-dashboard/add-haproxy-nrpe-fix-servicegroups
Merge into: lp:~openstack-charmers-archive/charms/trusty/openstack-dashboard/next
Diff against target: 1234 lines (+856/-44)
24 files modified
charm-helpers.yaml (+1/-1)
config.yaml (+6/-1)
hooks/charmhelpers/contrib/charmsupport/nrpe.py (+41/-7)
hooks/charmhelpers/contrib/hahelpers/cluster.py (+5/-1)
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+5/-2)
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/utils.py (+1/-0)
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/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/horizon_hooks.py (+2/-0)
To merge this branch: bzr merge lp:~brad-marshall/charms/trusty/openstack-dashboard/add-haproxy-nrpe-fix-servicegroups
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+250698@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.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #2214 openstack-dashboard-next for brad-marshall mp250698
    LINT OK: passed

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

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

charm_unit_test #2003 openstack-dashboard-next for brad-marshall mp250698
    UNIT OK: passed

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

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

charm_amulet_test #2160 openstack-dashboard-next for brad-marshall mp250698
    AMULET FAIL: amulet-test missing

AMULET Results (max last 2 lines):
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.

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

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 'charm-helpers.yaml'
2--- charm-helpers.yaml 2015-01-13 14:46:23 +0000
3+++ charm-helpers.yaml 2015-02-24 06:03:04 +0000
4@@ -3,7 +3,7 @@
5 include:
6 - core
7 - fetch
8- - contrib.openstack
9+ - contrib.openstack|inc=*
10 - contrib.storage.linux
11 - contrib.hahelpers
12 - contrib.storage
13
14=== modified file 'config.yaml'
15--- config.yaml 2015-01-13 14:46:23 +0000
16+++ config.yaml 2015-02-24 06:03:04 +0000
17@@ -137,4 +137,9 @@
18 default: "-H localhost -I 127.0.0.1 -u '/' -e 200,301,302"
19 type: string
20 description: The parameters to pass to the nrpe plugin check_http.
21-
22+ nagios_servicegroups:
23+ default: ""
24+ type: string
25+ description: |
26+ A comma-separated list of nagios servicegroups.
27+ If left empty, the nagios_context will be used as the servicegroup
28
29=== modified file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py'
30--- hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-01-26 09:46:38 +0000
31+++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-02-24 06:03:04 +0000
32@@ -24,6 +24,8 @@
33 import pwd
34 import grp
35 import os
36+import glob
37+import shutil
38 import re
39 import shlex
40 import yaml
41@@ -161,7 +163,7 @@
42 log('Check command not found: {}'.format(parts[0]))
43 return ''
44
45- def write(self, nagios_context, hostname, nagios_servicegroups=None):
46+ def write(self, nagios_context, hostname, nagios_servicegroups):
47 nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format(
48 self.command)
49 with open(nrpe_check_file, 'w') as nrpe_check_config:
50@@ -177,14 +179,11 @@
51 nagios_servicegroups)
52
53 def write_service_config(self, nagios_context, hostname,
54- nagios_servicegroups=None):
55+ nagios_servicegroups):
56 for f in os.listdir(NRPE.nagios_exportdir):
57 if re.search('.*{}.cfg'.format(self.command), f):
58 os.remove(os.path.join(NRPE.nagios_exportdir, f))
59
60- if not nagios_servicegroups:
61- nagios_servicegroups = nagios_context
62-
63 templ_vars = {
64 'nagios_hostname': hostname,
65 'nagios_servicegroup': nagios_servicegroups,
66@@ -211,10 +210,10 @@
67 super(NRPE, self).__init__()
68 self.config = config()
69 self.nagios_context = self.config['nagios_context']
70- if 'nagios_servicegroups' in self.config:
71+ if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']:
72 self.nagios_servicegroups = self.config['nagios_servicegroups']
73 else:
74- self.nagios_servicegroups = 'juju'
75+ self.nagios_servicegroups = self.nagios_context
76 self.unit_name = local_unit().replace('/', '-')
77 if hostname:
78 self.hostname = hostname
79@@ -322,3 +321,38 @@
80 check_cmd='check_status_file.py -f '
81 '/var/lib/nagios/service-check-%s.txt' % svc,
82 )
83+
84+
85+def copy_nrpe_checks():
86+ """
87+ Copy the nrpe checks into place
88+
89+ """
90+ NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
91+ nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks',
92+ 'charmhelpers', 'contrib', 'openstack',
93+ 'files')
94+
95+ if not os.path.exists(NAGIOS_PLUGINS):
96+ os.makedirs(NAGIOS_PLUGINS)
97+ for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")):
98+ if os.path.isfile(fname):
99+ shutil.copy2(fname,
100+ os.path.join(NAGIOS_PLUGINS, os.path.basename(fname)))
101+
102+
103+def add_haproxy_checks(nrpe, unit_name):
104+ """
105+ Add checks for each service in list
106+
107+ :param NRPE nrpe: NRPE object to add check to
108+ :param str unit_name: Unit name to use in check description
109+ """
110+ nrpe.add_check(
111+ shortname='haproxy_servers',
112+ description='Check HAProxy {%s}' % unit_name,
113+ check_cmd='check_haproxy.sh')
114+ nrpe.add_check(
115+ shortname='haproxy_queue',
116+ description='Check HAProxy queue depth {%s}' % unit_name,
117+ check_cmd='check_haproxy_queue_depth.sh')
118
119=== modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py'
120--- hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-01-26 09:46:38 +0000
121+++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-02-24 06:03:04 +0000
122@@ -48,6 +48,9 @@
123 from charmhelpers.core.decorators import (
124 retry_on_exception,
125 )
126+from charmhelpers.core.strutils import (
127+ bool_from_string,
128+)
129
130
131 class HAIncompleteConfig(Exception):
132@@ -164,7 +167,8 @@
133 .
134 returns: boolean
135 '''
136- if config_get('use-https') == "yes":
137+ use_https = config_get('use-https')
138+ if use_https and bool_from_string(use_https):
139 return True
140 if config_get('ssl_cert') and config_get('ssl_key'):
141 return True
142
143=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py'
144--- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:46:38 +0000
145+++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-02-24 06:03:04 +0000
146@@ -71,16 +71,19 @@
147 services.append(this_service)
148 use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph',
149 'ceph-osd', 'ceph-radosgw']
150+ # Openstack subordinate charms do not expose an origin option as that
151+ # is controlled by the principle
152+ ignore = ['neutron-openvswitch']
153
154 if self.openstack:
155 for svc in services:
156- if svc['name'] not in use_source:
157+ if svc['name'] not in use_source + ignore:
158 config = {'openstack-origin': self.openstack}
159 self.d.configure(svc['name'], config)
160
161 if self.source:
162 for svc in services:
163- if svc['name'] in use_source:
164+ if svc['name'] in use_source and svc['name'] not in ignore:
165 config = {'source': self.source}
166 self.d.configure(svc['name'], config)
167
168
169=== added directory 'hooks/charmhelpers/contrib/openstack/files'
170=== added file 'hooks/charmhelpers/contrib/openstack/files/__init__.py'
171--- hooks/charmhelpers/contrib/openstack/files/__init__.py 1970-01-01 00:00:00 +0000
172+++ hooks/charmhelpers/contrib/openstack/files/__init__.py 2015-02-24 06:03:04 +0000
173@@ -0,0 +1,18 @@
174+# Copyright 2014-2015 Canonical Limited.
175+#
176+# This file is part of charm-helpers.
177+#
178+# charm-helpers is free software: you can redistribute it and/or modify
179+# it under the terms of the GNU Lesser General Public License version 3 as
180+# published by the Free Software Foundation.
181+#
182+# charm-helpers is distributed in the hope that it will be useful,
183+# but WITHOUT ANY WARRANTY; without even the implied warranty of
184+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
185+# GNU Lesser General Public License for more details.
186+#
187+# You should have received a copy of the GNU Lesser General Public License
188+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
189+
190+# dummy __init__.py to fool syncer into thinking this is a syncable python
191+# module
192
193=== added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh'
194--- hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 1970-01-01 00:00:00 +0000
195+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 2015-02-24 06:03:04 +0000
196@@ -0,0 +1,32 @@
197+#!/bin/bash
198+#--------------------------------------------
199+# This file is managed by Juju
200+#--------------------------------------------
201+#
202+# Copyright 2009,2012 Canonical Ltd.
203+# Author: Tom Haddon
204+
205+CRITICAL=0
206+NOTACTIVE=''
207+LOGFILE=/var/log/nagios/check_haproxy.log
208+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
209+
210+for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'});
211+do
212+ 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')
213+ if [ $? != 0 ]; then
214+ date >> $LOGFILE
215+ echo $output >> $LOGFILE
216+ /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -v | grep $appserver >> $LOGFILE 2>&1
217+ CRITICAL=1
218+ NOTACTIVE="${NOTACTIVE} $appserver"
219+ fi
220+done
221+
222+if [ $CRITICAL = 1 ]; then
223+ echo "CRITICAL:${NOTACTIVE}"
224+ exit 2
225+fi
226+
227+echo "OK: All haproxy instances looking good"
228+exit 0
229
230=== added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh'
231--- hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000
232+++ hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 2015-02-24 06:03:04 +0000
233@@ -0,0 +1,30 @@
234+#!/bin/bash
235+#--------------------------------------------
236+# This file is managed by Juju
237+#--------------------------------------------
238+#
239+# Copyright 2009,2012 Canonical Ltd.
240+# Author: Tom Haddon
241+
242+# These should be config options at some stage
243+CURRQthrsh=0
244+MAXQthrsh=100
245+
246+AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}')
247+
248+HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v)
249+
250+for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}')
251+do
252+ CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3)
253+ MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4)
254+
255+ if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then
256+ echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ"
257+ exit 2
258+ fi
259+done
260+
261+echo "OK: All haproxy queue depths looking good"
262+exit 0
263+
264
265=== modified file 'hooks/charmhelpers/contrib/openstack/ip.py'
266--- hooks/charmhelpers/contrib/openstack/ip.py 2015-01-26 09:46:38 +0000
267+++ hooks/charmhelpers/contrib/openstack/ip.py 2015-02-24 06:03:04 +0000
268@@ -26,6 +26,8 @@
269 )
270 from charmhelpers.contrib.hahelpers.cluster import is_clustered
271
272+from functools import partial
273+
274 PUBLIC = 'public'
275 INTERNAL = 'int'
276 ADMIN = 'admin'
277@@ -107,3 +109,38 @@
278 "clustered=%s)" % (net_type, clustered))
279
280 return resolved_address
281+
282+
283+def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC,
284+ override=None):
285+ """Returns the correct endpoint URL to advertise to Keystone.
286+
287+ This method provides the correct endpoint URL which should be advertised to
288+ the keystone charm for endpoint creation. This method allows for the url to
289+ be overridden to force a keystone endpoint to have specific URL for any of
290+ the defined scopes (admin, internal, public).
291+
292+ :param configs: OSTemplateRenderer config templating object to inspect
293+ for a complete https context.
294+ :param url_template: str format string for creating the url template. Only
295+ two values will be passed - the scheme+hostname
296+ returned by the canonical_url and the port.
297+ :param endpoint_type: str endpoint type to resolve.
298+ :param override: str the name of the config option which overrides the
299+ endpoint URL defined by the charm itself. None will
300+ disable any overrides (default).
301+ """
302+ if override:
303+ # Return any user-defined overrides for the keystone endpoint URL.
304+ user_value = config(override)
305+ if user_value:
306+ return user_value.strip()
307+
308+ return url_template % (canonical_url(configs, endpoint_type), port)
309+
310+
311+public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC)
312+
313+internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL)
314+
315+admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN)
316
317=== added file 'hooks/charmhelpers/contrib/openstack/templates/ceph.conf'
318--- hooks/charmhelpers/contrib/openstack/templates/ceph.conf 1970-01-01 00:00:00 +0000
319+++ hooks/charmhelpers/contrib/openstack/templates/ceph.conf 2015-02-24 06:03:04 +0000
320@@ -0,0 +1,15 @@
321+###############################################################################
322+# [ WARNING ]
323+# cinder configuration file maintained by Juju
324+# local changes may be overwritten.
325+###############################################################################
326+[global]
327+{% if auth -%}
328+ auth_supported = {{ auth }}
329+ keyring = /etc/ceph/$cluster.$name.keyring
330+ mon host = {{ mon_hosts }}
331+{% endif -%}
332+ log to syslog = {{ use_syslog }}
333+ err to syslog = {{ use_syslog }}
334+ clog to syslog = {{ use_syslog }}
335+
336
337=== added file 'hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg'
338--- hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 1970-01-01 00:00:00 +0000
339+++ hooks/charmhelpers/contrib/openstack/templates/haproxy.cfg 2015-02-24 06:03:04 +0000
340@@ -0,0 +1,58 @@
341+global
342+ log {{ local_host }} local0
343+ log {{ local_host }} local1 notice
344+ maxconn 20000
345+ user haproxy
346+ group haproxy
347+ spread-checks 0
348+
349+defaults
350+ log global
351+ mode tcp
352+ option tcplog
353+ option dontlognull
354+ retries 3
355+ timeout queue 1000
356+ timeout connect 1000
357+{% if haproxy_client_timeout -%}
358+ timeout client {{ haproxy_client_timeout }}
359+{% else -%}
360+ timeout client 30000
361+{% endif -%}
362+
363+{% if haproxy_server_timeout -%}
364+ timeout server {{ haproxy_server_timeout }}
365+{% else -%}
366+ timeout server 30000
367+{% endif -%}
368+
369+listen stats {{ stat_port }}
370+ mode http
371+ stats enable
372+ stats hide-version
373+ stats realm Haproxy\ Statistics
374+ stats uri /
375+ stats auth admin:password
376+
377+{% if frontends -%}
378+{% for service, ports in service_ports.items() -%}
379+frontend tcp-in_{{ service }}
380+ bind *:{{ ports[0] }}
381+ {% if ipv6 -%}
382+ bind :::{{ ports[0] }}
383+ {% endif -%}
384+ {% for frontend in frontends -%}
385+ acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }}
386+ use_backend {{ service }}_{{ frontend }} if net_{{ frontend }}
387+ {% endfor -%}
388+ default_backend {{ service }}_{{ default_backend }}
389+
390+{% for frontend in frontends -%}
391+backend {{ service }}_{{ frontend }}
392+ balance leastconn
393+ {% for unit, address in frontends[frontend]['backends'].items() -%}
394+ server {{ unit }} {{ address }}:{{ ports[1] }} check
395+ {% endfor %}
396+{% endfor -%}
397+{% endfor -%}
398+{% endif -%}
399
400=== added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend'
401--- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 1970-01-01 00:00:00 +0000
402+++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend 2015-02-24 06:03:04 +0000
403@@ -0,0 +1,24 @@
404+{% if endpoints -%}
405+{% for ext_port in ext_ports -%}
406+Listen {{ ext_port }}
407+{% endfor -%}
408+{% for address, endpoint, ext, int in endpoints -%}
409+<VirtualHost {{ address }}:{{ ext }}>
410+ ServerName {{ endpoint }}
411+ SSLEngine on
412+ SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
413+ SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }}
414+ ProxyPass / http://localhost:{{ int }}/
415+ ProxyPassReverse / http://localhost:{{ int }}/
416+ ProxyPreserveHost on
417+</VirtualHost>
418+{% endfor -%}
419+<Proxy *>
420+ Order deny,allow
421+ Allow from all
422+</Proxy>
423+<Location />
424+ Order allow,deny
425+ Allow from all
426+</Location>
427+{% endif -%}
428
429=== added file 'hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf'
430--- hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 1970-01-01 00:00:00 +0000
431+++ hooks/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf 2015-02-24 06:03:04 +0000
432@@ -0,0 +1,24 @@
433+{% if endpoints -%}
434+{% for ext_port in ext_ports -%}
435+Listen {{ ext_port }}
436+{% endfor -%}
437+{% for address, endpoint, ext, int in endpoints -%}
438+<VirtualHost {{ address }}:{{ ext }}>
439+ ServerName {{ endpoint }}
440+ SSLEngine on
441+ SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
442+ SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }}
443+ ProxyPass / http://localhost:{{ int }}/
444+ ProxyPassReverse / http://localhost:{{ int }}/
445+ ProxyPreserveHost on
446+</VirtualHost>
447+{% endfor -%}
448+<Proxy *>
449+ Order deny,allow
450+ Allow from all
451+</Proxy>
452+<Location />
453+ Order allow,deny
454+ Allow from all
455+</Location>
456+{% endif -%}
457
458=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
459--- hooks/charmhelpers/contrib/openstack/utils.py 2015-01-26 09:46:38 +0000
460+++ hooks/charmhelpers/contrib/openstack/utils.py 2015-02-24 06:03:04 +0000
461@@ -103,6 +103,7 @@
462 ('2.1.0', 'juno'),
463 ('2.2.0', 'juno'),
464 ('2.2.1', 'kilo'),
465+ ('2.2.2', 'kilo'),
466 ])
467
468 DEFAULT_LOOPBACK_SIZE = '5G'
469
470=== modified file 'hooks/charmhelpers/contrib/python/packages.py'
471--- hooks/charmhelpers/contrib/python/packages.py 2015-01-26 09:46:38 +0000
472+++ hooks/charmhelpers/contrib/python/packages.py 2015-02-24 06:03:04 +0000
473@@ -17,8 +17,6 @@
474 # You should have received a copy of the GNU Lesser General Public License
475 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
476
477-__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"
478-
479 from charmhelpers.fetch import apt_install, apt_update
480 from charmhelpers.core.hookenv import log
481
482@@ -29,6 +27,8 @@
483 apt_install('python-pip')
484 from pip import main as pip_execute
485
486+__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"
487+
488
489 def parse_options(given, available):
490 """Given a set of options, check if available"""
491
492=== modified file 'hooks/charmhelpers/core/fstab.py'
493--- hooks/charmhelpers/core/fstab.py 2015-01-26 09:46:38 +0000
494+++ hooks/charmhelpers/core/fstab.py 2015-02-24 06:03:04 +0000
495@@ -17,11 +17,11 @@
496 # You should have received a copy of the GNU Lesser General Public License
497 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
498
499-__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
500-
501 import io
502 import os
503
504+__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
505+
506
507 class Fstab(io.FileIO):
508 """This class extends file in order to implement a file reader/writer
509@@ -77,7 +77,7 @@
510 for line in self.readlines():
511 line = line.decode('us-ascii')
512 try:
513- if line.strip() and not line.startswith("#"):
514+ if line.strip() and not line.strip().startswith("#"):
515 yield self._hydrate_entry(line)
516 except ValueError:
517 pass
518@@ -104,7 +104,7 @@
519
520 found = False
521 for index, line in enumerate(lines):
522- if not line.startswith("#"):
523+ if line.strip() and not line.strip().startswith("#"):
524 if self._hydrate_entry(line) == entry:
525 found = True
526 break
527
528=== modified file 'hooks/charmhelpers/core/host.py'
529--- hooks/charmhelpers/core/host.py 2015-01-26 09:46:38 +0000
530+++ hooks/charmhelpers/core/host.py 2015-02-24 06:03:04 +0000
531@@ -191,11 +191,11 @@
532
533
534 def write_file(path, content, owner='root', group='root', perms=0o444):
535- """Create or overwrite a file with the contents of a string"""
536+ """Create or overwrite a file with the contents of a byte string."""
537 log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
538 uid = pwd.getpwnam(owner).pw_uid
539 gid = grp.getgrnam(group).gr_gid
540- with open(path, 'w') as target:
541+ with open(path, 'wb') as target:
542 os.fchown(target.fileno(), uid, gid)
543 os.fchmod(target.fileno(), perms)
544 target.write(content)
545@@ -305,11 +305,11 @@
546 ceph_client_changed function.
547 """
548 def wrap(f):
549- def wrapped_f(*args):
550+ def wrapped_f(*args, **kwargs):
551 checksums = {}
552 for path in restart_map:
553 checksums[path] = file_hash(path)
554- f(*args)
555+ f(*args, **kwargs)
556 restarts = []
557 for path in restart_map:
558 if checksums[path] != file_hash(path):
559@@ -361,7 +361,7 @@
560 ip_output = (line for line in ip_output if line)
561 for line in ip_output:
562 if line.split()[1].startswith(int_type):
563- matched = re.search('.*: (bond[0-9]+\.[0-9]+)@.*', line)
564+ matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line)
565 if matched:
566 interface = matched.groups()[0]
567 else:
568
569=== added file 'hooks/charmhelpers/core/strutils.py'
570--- hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000
571+++ hooks/charmhelpers/core/strutils.py 2015-02-24 06:03:04 +0000
572@@ -0,0 +1,42 @@
573+#!/usr/bin/env python
574+# -*- coding: utf-8 -*-
575+
576+# Copyright 2014-2015 Canonical Limited.
577+#
578+# This file is part of charm-helpers.
579+#
580+# charm-helpers is free software: you can redistribute it and/or modify
581+# it under the terms of the GNU Lesser General Public License version 3 as
582+# published by the Free Software Foundation.
583+#
584+# charm-helpers is distributed in the hope that it will be useful,
585+# but WITHOUT ANY WARRANTY; without even the implied warranty of
586+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
587+# GNU Lesser General Public License for more details.
588+#
589+# You should have received a copy of the GNU Lesser General Public License
590+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
591+
592+import six
593+
594+
595+def bool_from_string(value):
596+ """Interpret string value as boolean.
597+
598+ Returns True if value translates to True otherwise False.
599+ """
600+ if isinstance(value, six.string_types):
601+ value = six.text_type(value)
602+ else:
603+ msg = "Unable to interpret non-string value '%s' as boolean" % (value)
604+ raise ValueError(msg)
605+
606+ value = value.strip().lower()
607+
608+ if value in ['y', 'yes', 'true', 't']:
609+ return True
610+ elif value in ['n', 'no', 'false', 'f']:
611+ return False
612+
613+ msg = "Unable to interpret string value '%s' as boolean" % (value)
614+ raise ValueError(msg)
615
616=== modified file 'hooks/charmhelpers/core/sysctl.py'
617--- hooks/charmhelpers/core/sysctl.py 2015-01-26 09:46:38 +0000
618+++ hooks/charmhelpers/core/sysctl.py 2015-02-24 06:03:04 +0000
619@@ -17,8 +17,6 @@
620 # You should have received a copy of the GNU Lesser General Public License
621 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
622
623-__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
624-
625 import yaml
626
627 from subprocess import check_call
628@@ -26,25 +24,33 @@
629 from charmhelpers.core.hookenv import (
630 log,
631 DEBUG,
632+ ERROR,
633 )
634
635+__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
636+
637
638 def create(sysctl_dict, sysctl_file):
639 """Creates a sysctl.conf file from a YAML associative array
640
641- :param sysctl_dict: a dict of sysctl options eg { 'kernel.max_pid': 1337 }
642- :type sysctl_dict: dict
643+ :param sysctl_dict: a YAML-formatted string of sysctl options eg "{ 'kernel.max_pid': 1337 }"
644+ :type sysctl_dict: str
645 :param sysctl_file: path to the sysctl file to be saved
646 :type sysctl_file: str or unicode
647 :returns: None
648 """
649- sysctl_dict = yaml.load(sysctl_dict)
650+ try:
651+ sysctl_dict_parsed = yaml.safe_load(sysctl_dict)
652+ except yaml.YAMLError:
653+ log("Error parsing YAML sysctl_dict: {}".format(sysctl_dict),
654+ level=ERROR)
655+ return
656
657 with open(sysctl_file, "w") as fd:
658- for key, value in sysctl_dict.items():
659+ for key, value in sysctl_dict_parsed.items():
660 fd.write("{}={}\n".format(key, value))
661
662- log("Updating sysctl_file: %s values: %s" % (sysctl_file, sysctl_dict),
663+ log("Updating sysctl_file: %s values: %s" % (sysctl_file, sysctl_dict_parsed),
664 level=DEBUG)
665
666 check_call(["sysctl", "-p", sysctl_file])
667
668=== modified file 'hooks/charmhelpers/core/templating.py'
669--- hooks/charmhelpers/core/templating.py 2015-01-26 09:46:38 +0000
670+++ hooks/charmhelpers/core/templating.py 2015-02-24 06:03:04 +0000
671@@ -21,7 +21,7 @@
672
673
674 def render(source, target, context, owner='root', group='root',
675- perms=0o444, templates_dir=None):
676+ perms=0o444, templates_dir=None, encoding='UTF-8'):
677 """
678 Render a template.
679
680@@ -64,5 +64,5 @@
681 level=hookenv.ERROR)
682 raise e
683 content = template.render(context)
684- host.mkdir(os.path.dirname(target), owner, group)
685- host.write_file(target, content, owner, group, perms)
686+ host.mkdir(os.path.dirname(target), owner, group, perms=0o755)
687+ host.write_file(target, content.encode(encoding), owner, group, perms)
688
689=== added file 'hooks/charmhelpers/core/unitdata.py'
690--- hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000
691+++ hooks/charmhelpers/core/unitdata.py 2015-02-24 06:03:04 +0000
692@@ -0,0 +1,477 @@
693+#!/usr/bin/env python
694+# -*- coding: utf-8 -*-
695+#
696+# Copyright 2014-2015 Canonical Limited.
697+#
698+# This file is part of charm-helpers.
699+#
700+# charm-helpers is free software: you can redistribute it and/or modify
701+# it under the terms of the GNU Lesser General Public License version 3 as
702+# published by the Free Software Foundation.
703+#
704+# charm-helpers is distributed in the hope that it will be useful,
705+# but WITHOUT ANY WARRANTY; without even the implied warranty of
706+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
707+# GNU Lesser General Public License for more details.
708+#
709+# You should have received a copy of the GNU Lesser General Public License
710+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
711+#
712+#
713+# Authors:
714+# Kapil Thangavelu <kapil.foss@gmail.com>
715+#
716+"""
717+Intro
718+-----
719+
720+A simple way to store state in units. This provides a key value
721+storage with support for versioned, transactional operation,
722+and can calculate deltas from previous values to simplify unit logic
723+when processing changes.
724+
725+
726+Hook Integration
727+----------------
728+
729+There are several extant frameworks for hook execution, including
730+
731+ - charmhelpers.core.hookenv.Hooks
732+ - charmhelpers.core.services.ServiceManager
733+
734+The storage classes are framework agnostic, one simple integration is
735+via the HookData contextmanager. It will record the current hook
736+execution environment (including relation data, config data, etc.),
737+setup a transaction and allow easy access to the changes from
738+previously seen values. One consequence of the integration is the
739+reservation of particular keys ('rels', 'unit', 'env', 'config',
740+'charm_revisions') for their respective values.
741+
742+Here's a fully worked integration example using hookenv.Hooks::
743+
744+ from charmhelper.core import hookenv, unitdata
745+
746+ hook_data = unitdata.HookData()
747+ db = unitdata.kv()
748+ hooks = hookenv.Hooks()
749+
750+ @hooks.hook
751+ def config_changed():
752+ # Print all changes to configuration from previously seen
753+ # values.
754+ for changed, (prev, cur) in hook_data.conf.items():
755+ print('config changed', changed,
756+ 'previous value', prev,
757+ 'current value', cur)
758+
759+ # Get some unit specific bookeeping
760+ if not db.get('pkg_key'):
761+ key = urllib.urlopen('https://example.com/pkg_key').read()
762+ db.set('pkg_key', key)
763+
764+ # Directly access all charm config as a mapping.
765+ conf = db.getrange('config', True)
766+
767+ # Directly access all relation data as a mapping
768+ rels = db.getrange('rels', True)
769+
770+ if __name__ == '__main__':
771+ with hook_data():
772+ hook.execute()
773+
774+
775+A more basic integration is via the hook_scope context manager which simply
776+manages transaction scope (and records hook name, and timestamp)::
777+
778+ >>> from unitdata import kv
779+ >>> db = kv()
780+ >>> with db.hook_scope('install'):
781+ ... # do work, in transactional scope.
782+ ... db.set('x', 1)
783+ >>> db.get('x')
784+ 1
785+
786+
787+Usage
788+-----
789+
790+Values are automatically json de/serialized to preserve basic typing
791+and complex data struct capabilities (dicts, lists, ints, booleans, etc).
792+
793+Individual values can be manipulated via get/set::
794+
795+ >>> kv.set('y', True)
796+ >>> kv.get('y')
797+ True
798+
799+ # We can set complex values (dicts, lists) as a single key.
800+ >>> kv.set('config', {'a': 1, 'b': True'})
801+
802+ # Also supports returning dictionaries as a record which
803+ # provides attribute access.
804+ >>> config = kv.get('config', record=True)
805+ >>> config.b
806+ True
807+
808+
809+Groups of keys can be manipulated with update/getrange::
810+
811+ >>> kv.update({'z': 1, 'y': 2}, prefix="gui.")
812+ >>> kv.getrange('gui.', strip=True)
813+ {'z': 1, 'y': 2}
814+
815+When updating values, its very helpful to understand which values
816+have actually changed and how have they changed. The storage
817+provides a delta method to provide for this::
818+
819+ >>> data = {'debug': True, 'option': 2}
820+ >>> delta = kv.delta(data, 'config.')
821+ >>> delta.debug.previous
822+ None
823+ >>> delta.debug.current
824+ True
825+ >>> delta
826+ {'debug': (None, True), 'option': (None, 2)}
827+
828+Note the delta method does not persist the actual change, it needs to
829+be explicitly saved via 'update' method::
830+
831+ >>> kv.update(data, 'config.')
832+
833+Values modified in the context of a hook scope retain historical values
834+associated to the hookname.
835+
836+ >>> with db.hook_scope('config-changed'):
837+ ... db.set('x', 42)
838+ >>> db.gethistory('x')
839+ [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'),
840+ (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')]
841+
842+"""
843+
844+import collections
845+import contextlib
846+import datetime
847+import json
848+import os
849+import pprint
850+import sqlite3
851+import sys
852+
853+__author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>'
854+
855+
856+class Storage(object):
857+ """Simple key value database for local unit state within charms.
858+
859+ Modifications are automatically committed at hook exit. That's
860+ currently regardless of exit code.
861+
862+ To support dicts, lists, integer, floats, and booleans values
863+ are automatically json encoded/decoded.
864+ """
865+ def __init__(self, path=None):
866+ self.db_path = path
867+ if path is None:
868+ self.db_path = os.path.join(
869+ os.environ.get('CHARM_DIR', ''), '.unit-state.db')
870+ self.conn = sqlite3.connect('%s' % self.db_path)
871+ self.cursor = self.conn.cursor()
872+ self.revision = None
873+ self._closed = False
874+ self._init()
875+
876+ def close(self):
877+ if self._closed:
878+ return
879+ self.flush(False)
880+ self.cursor.close()
881+ self.conn.close()
882+ self._closed = True
883+
884+ def _scoped_query(self, stmt, params=None):
885+ if params is None:
886+ params = []
887+ return stmt, params
888+
889+ def get(self, key, default=None, record=False):
890+ self.cursor.execute(
891+ *self._scoped_query(
892+ 'select data from kv where key=?', [key]))
893+ result = self.cursor.fetchone()
894+ if not result:
895+ return default
896+ if record:
897+ return Record(json.loads(result[0]))
898+ return json.loads(result[0])
899+
900+ def getrange(self, key_prefix, strip=False):
901+ stmt = "select key, data from kv where key like '%s%%'" % key_prefix
902+ self.cursor.execute(*self._scoped_query(stmt))
903+ result = self.cursor.fetchall()
904+
905+ if not result:
906+ return None
907+ if not strip:
908+ key_prefix = ''
909+ return dict([
910+ (k[len(key_prefix):], json.loads(v)) for k, v in result])
911+
912+ def update(self, mapping, prefix=""):
913+ for k, v in mapping.items():
914+ self.set("%s%s" % (prefix, k), v)
915+
916+ def unset(self, key):
917+ self.cursor.execute('delete from kv where key=?', [key])
918+ if self.revision and self.cursor.rowcount:
919+ self.cursor.execute(
920+ 'insert into kv_revisions values (?, ?, ?)',
921+ [key, self.revision, json.dumps('DELETED')])
922+
923+ def set(self, key, value):
924+ serialized = json.dumps(value)
925+
926+ self.cursor.execute(
927+ 'select data from kv where key=?', [key])
928+ exists = self.cursor.fetchone()
929+
930+ # Skip mutations to the same value
931+ if exists:
932+ if exists[0] == serialized:
933+ return value
934+
935+ if not exists:
936+ self.cursor.execute(
937+ 'insert into kv (key, data) values (?, ?)',
938+ (key, serialized))
939+ else:
940+ self.cursor.execute('''
941+ update kv
942+ set data = ?
943+ where key = ?''', [serialized, key])
944+
945+ # Save
946+ if not self.revision:
947+ return value
948+
949+ self.cursor.execute(
950+ 'select 1 from kv_revisions where key=? and revision=?',
951+ [key, self.revision])
952+ exists = self.cursor.fetchone()
953+
954+ if not exists:
955+ self.cursor.execute(
956+ '''insert into kv_revisions (
957+ revision, key, data) values (?, ?, ?)''',
958+ (self.revision, key, serialized))
959+ else:
960+ self.cursor.execute(
961+ '''
962+ update kv_revisions
963+ set data = ?
964+ where key = ?
965+ and revision = ?''',
966+ [serialized, key, self.revision])
967+
968+ return value
969+
970+ def delta(self, mapping, prefix):
971+ """
972+ return a delta containing values that have changed.
973+ """
974+ previous = self.getrange(prefix, strip=True)
975+ if not previous:
976+ pk = set()
977+ else:
978+ pk = set(previous.keys())
979+ ck = set(mapping.keys())
980+ delta = DeltaSet()
981+
982+ # added
983+ for k in ck.difference(pk):
984+ delta[k] = Delta(None, mapping[k])
985+
986+ # removed
987+ for k in pk.difference(ck):
988+ delta[k] = Delta(previous[k], None)
989+
990+ # changed
991+ for k in pk.intersection(ck):
992+ c = mapping[k]
993+ p = previous[k]
994+ if c != p:
995+ delta[k] = Delta(p, c)
996+
997+ return delta
998+
999+ @contextlib.contextmanager
1000+ def hook_scope(self, name=""):
1001+ """Scope all future interactions to the current hook execution
1002+ revision."""
1003+ assert not self.revision
1004+ self.cursor.execute(
1005+ 'insert into hooks (hook, date) values (?, ?)',
1006+ (name or sys.argv[0],
1007+ datetime.datetime.utcnow().isoformat()))
1008+ self.revision = self.cursor.lastrowid
1009+ try:
1010+ yield self.revision
1011+ self.revision = None
1012+ except:
1013+ self.flush(False)
1014+ self.revision = None
1015+ raise
1016+ else:
1017+ self.flush()
1018+
1019+ def flush(self, save=True):
1020+ if save:
1021+ self.conn.commit()
1022+ elif self._closed:
1023+ return
1024+ else:
1025+ self.conn.rollback()
1026+
1027+ def _init(self):
1028+ self.cursor.execute('''
1029+ create table if not exists kv (
1030+ key text,
1031+ data text,
1032+ primary key (key)
1033+ )''')
1034+ self.cursor.execute('''
1035+ create table if not exists kv_revisions (
1036+ key text,
1037+ revision integer,
1038+ data text,
1039+ primary key (key, revision)
1040+ )''')
1041+ self.cursor.execute('''
1042+ create table if not exists hooks (
1043+ version integer primary key autoincrement,
1044+ hook text,
1045+ date text
1046+ )''')
1047+ self.conn.commit()
1048+
1049+ def gethistory(self, key, deserialize=False):
1050+ self.cursor.execute(
1051+ '''
1052+ select kv.revision, kv.key, kv.data, h.hook, h.date
1053+ from kv_revisions kv,
1054+ hooks h
1055+ where kv.key=?
1056+ and kv.revision = h.version
1057+ ''', [key])
1058+ if deserialize is False:
1059+ return self.cursor.fetchall()
1060+ return map(_parse_history, self.cursor.fetchall())
1061+
1062+ def debug(self, fh=sys.stderr):
1063+ self.cursor.execute('select * from kv')
1064+ pprint.pprint(self.cursor.fetchall(), stream=fh)
1065+ self.cursor.execute('select * from kv_revisions')
1066+ pprint.pprint(self.cursor.fetchall(), stream=fh)
1067+
1068+
1069+def _parse_history(d):
1070+ return (d[0], d[1], json.loads(d[2]), d[3],
1071+ datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f"))
1072+
1073+
1074+class HookData(object):
1075+ """Simple integration for existing hook exec frameworks.
1076+
1077+ Records all unit information, and stores deltas for processing
1078+ by the hook.
1079+
1080+ Sample::
1081+
1082+ from charmhelper.core import hookenv, unitdata
1083+
1084+ changes = unitdata.HookData()
1085+ db = unitdata.kv()
1086+ hooks = hookenv.Hooks()
1087+
1088+ @hooks.hook
1089+ def config_changed():
1090+ # View all changes to configuration
1091+ for changed, (prev, cur) in changes.conf.items():
1092+ print('config changed', changed,
1093+ 'previous value', prev,
1094+ 'current value', cur)
1095+
1096+ # Get some unit specific bookeeping
1097+ if not db.get('pkg_key'):
1098+ key = urllib.urlopen('https://example.com/pkg_key').read()
1099+ db.set('pkg_key', key)
1100+
1101+ if __name__ == '__main__':
1102+ with changes():
1103+ hook.execute()
1104+
1105+ """
1106+ def __init__(self):
1107+ self.kv = kv()
1108+ self.conf = None
1109+ self.rels = None
1110+
1111+ @contextlib.contextmanager
1112+ def __call__(self):
1113+ from charmhelpers.core import hookenv
1114+ hook_name = hookenv.hook_name()
1115+
1116+ with self.kv.hook_scope(hook_name):
1117+ self._record_charm_version(hookenv.charm_dir())
1118+ delta_config, delta_relation = self._record_hook(hookenv)
1119+ yield self.kv, delta_config, delta_relation
1120+
1121+ def _record_charm_version(self, charm_dir):
1122+ # Record revisions.. charm revisions are meaningless
1123+ # to charm authors as they don't control the revision.
1124+ # so logic dependnent on revision is not particularly
1125+ # useful, however it is useful for debugging analysis.
1126+ charm_rev = open(
1127+ os.path.join(charm_dir, 'revision')).read().strip()
1128+ charm_rev = charm_rev or '0'
1129+ revs = self.kv.get('charm_revisions', [])
1130+ if charm_rev not in revs:
1131+ revs.append(charm_rev.strip() or '0')
1132+ self.kv.set('charm_revisions', revs)
1133+
1134+ def _record_hook(self, hookenv):
1135+ data = hookenv.execution_environment()
1136+ self.conf = conf_delta = self.kv.delta(data['conf'], 'config')
1137+ self.rels = rels_delta = self.kv.delta(data['rels'], 'rels')
1138+ self.kv.set('env', data['env'])
1139+ self.kv.set('unit', data['unit'])
1140+ self.kv.set('relid', data.get('relid'))
1141+ return conf_delta, rels_delta
1142+
1143+
1144+class Record(dict):
1145+
1146+ __slots__ = ()
1147+
1148+ def __getattr__(self, k):
1149+ if k in self:
1150+ return self[k]
1151+ raise AttributeError(k)
1152+
1153+
1154+class DeltaSet(Record):
1155+
1156+ __slots__ = ()
1157+
1158+
1159+Delta = collections.namedtuple('Delta', ['previous', 'current'])
1160+
1161+
1162+_KV = None
1163+
1164+
1165+def kv():
1166+ global _KV
1167+ if _KV is None:
1168+ _KV = Storage()
1169+ return _KV
1170
1171=== modified file 'hooks/charmhelpers/fetch/archiveurl.py'
1172--- hooks/charmhelpers/fetch/archiveurl.py 2015-01-26 09:46:38 +0000
1173+++ hooks/charmhelpers/fetch/archiveurl.py 2015-02-24 06:03:04 +0000
1174@@ -18,6 +18,16 @@
1175 import hashlib
1176 import re
1177
1178+from charmhelpers.fetch import (
1179+ BaseFetchHandler,
1180+ UnhandledSource
1181+)
1182+from charmhelpers.payload.archive import (
1183+ get_archive_handler,
1184+ extract,
1185+)
1186+from charmhelpers.core.host import mkdir, check_hash
1187+
1188 import six
1189 if six.PY3:
1190 from urllib.request import (
1191@@ -35,16 +45,6 @@
1192 )
1193 from urlparse import urlparse, urlunparse, parse_qs
1194
1195-from charmhelpers.fetch import (
1196- BaseFetchHandler,
1197- UnhandledSource
1198-)
1199-from charmhelpers.payload.archive import (
1200- get_archive_handler,
1201- extract,
1202-)
1203-from charmhelpers.core.host import mkdir, check_hash
1204-
1205
1206 def splituser(host):
1207 '''urllib.splituser(), but six's support of this seems broken'''
1208
1209=== modified file 'hooks/charmhelpers/fetch/giturl.py'
1210--- hooks/charmhelpers/fetch/giturl.py 2015-01-26 09:46:38 +0000
1211+++ hooks/charmhelpers/fetch/giturl.py 2015-02-24 06:03:04 +0000
1212@@ -32,7 +32,7 @@
1213 apt_install("python-git")
1214 from git import Repo
1215
1216-from git.exc import GitCommandError
1217+from git.exc import GitCommandError # noqa E402
1218
1219
1220 class GitUrlFetchHandler(BaseFetchHandler):
1221
1222=== modified file 'hooks/horizon_hooks.py'
1223--- hooks/horizon_hooks.py 2015-02-11 11:36:55 +0000
1224+++ hooks/horizon_hooks.py 2015-02-24 06:03:04 +0000
1225@@ -214,7 +214,9 @@
1226 hostname = nrpe.get_nagios_hostname()
1227 current_unit = nrpe.get_nagios_unit_name()
1228 nrpe_setup = nrpe.NRPE(hostname=hostname)
1229+ nrpe.copy_nrpe_checks()
1230 nrpe.add_init_service_checks(nrpe_setup, services(), current_unit)
1231+ nrpe.add_haproxy_checks(nrpe_setup, current_unit)
1232 conf = nrpe_setup.config
1233 check_http_params = conf.get('nagios_check_http_params')
1234 if check_http_params:

Subscribers

People subscribed via source and target branches