Merge lp:~hopem/charms/trusty/ceph-radosgw/lp1513524 into lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next

Proposed by Edward Hope-Morley
Status: Rejected
Rejected by: James Page
Proposed branch: lp:~hopem/charms/trusty/ceph-radosgw/lp1513524
Merge into: lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next
Diff against target: 892 lines (+320/-224)
8 files modified
config.yaml (+12/-0)
hooks/ceph_radosgw_context.py (+120/-27)
hooks/hooks.py (+70/-86)
hooks/utils.py (+58/-33)
templates/ceph.conf (+3/-0)
templates/rgw.conf (+25/-0)
unit_tests/test_ceph_radosgw_context.py (+32/-11)
unit_tests/test_hooks.py (+0/-67)
To merge this branch: bzr merge lp:~hopem/charms/trusty/ceph-radosgw/lp1513524
Reviewer Review Type Date Requested Status
Liam Young (community) Needs Fixing
OpenStack Charmers Pending
Chris Holcombe Pending
Review via email: mp+286474@code.launchpad.net

This proposal supersedes a proposal from 2016-02-11.

To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #574 ceph-radosgw-next for hopem mp285808
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/574/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #488 ceph-radosgw-next for hopem mp285808
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/488/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #235 ceph-radosgw-next for hopem mp285808
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/15073352/
Build: http://10.245.162.36:8080/job/charm_amulet_test/235/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #489 ceph-radosgw-next for hopem mp285808
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/489/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #575 ceph-radosgw-next for hopem mp285808
    LINT FAIL: lint-test failed

LINT Results (max last 2 lines):
make: *** [lint] Error 1
ERROR:root:Make target returned non-zero.

Full lint test output: http://paste.ubuntu.com/15074156/
Build: http://10.245.162.36:8080/job/charm_lint_check/575/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #236 ceph-radosgw-next for hopem mp285808
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/15074271/
Build: http://10.245.162.36:8080/job/charm_amulet_test/236/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #576 ceph-radosgw-next for hopem mp285808
    LINT FAIL: lint-test failed

LINT Results (max last 2 lines):
make: *** [lint] Error 1
ERROR:root:Make target returned non-zero.

Full lint test output: http://paste.ubuntu.com/15074967/
Build: http://10.245.162.36:8080/job/charm_lint_check/576/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #490 ceph-radosgw-next for hopem mp285808
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/490/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #237 ceph-radosgw-next for hopem mp285808
    AMULET OK: passed

Build: http://10.245.162.36:8080/job/charm_amulet_test/237/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_lint_check #577 ceph-radosgw-next for hopem mp285808
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/577/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #491 ceph-radosgw-next for hopem mp285808
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/491/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #238 ceph-radosgw-next for hopem mp285808
    AMULET OK: passed

Build: http://10.245.162.36:8080/job/charm_amulet_test/238/

Revision history for this message
Chris Holcombe (xfactor973) wrote : Posted in a previous version of this proposal

Hey Ed thanks for writing this patch! This looks great. My only comments are just little nits about catching errors and what you would like to do with them. You could handle them locally or raise but it would be best to be explicit about it. Other than that this looks good and I'm looking forward to trying it out

review: Needs Fixing
Revision history for this message
Edward Hope-Morley (hopem) wrote : Posted in a previous version of this proposal

Thanks for the review Chris! I've fixed up based on your comments and added some inline responses.

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

charm_lint_check #820 ceph-radosgw-next for hopem mp286474
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/820/

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

charm_unit_test #723 ceph-radosgw-next for hopem mp286474
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/723/

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

charm_amulet_test #320 ceph-radosgw-next for hopem mp286474
    AMULET OK: passed

Build: http://10.245.162.36:8080/job/charm_amulet_test/320/

Revision history for this message
Chris Holcombe (xfactor973) wrote : Posted in a previous version of this proposal

Cool. Lets get this merged!

review: Approve
67. By Edward Hope-Morley

sync /next

68. By Edward Hope-Morley

very minor edit

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

charm_lint_check #912 ceph-radosgw-next for hopem mp286474
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/912/

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

charm_unit_test #813 ceph-radosgw-next for hopem mp286474
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/813/

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

charm_amulet_test #359 ceph-radosgw-next for hopem mp286474
    AMULET OK: passed

Build: http://10.245.162.36:8080/job/charm_amulet_test/359/

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

See inline comment

review: Needs Fixing
Revision history for this message
Edward Hope-Morley (hopem) wrote :

Hey Liam, a good question and I responded inline.

69. By Edward Hope-Morley

only write /etc/hosts if hostname not v6 resolvable

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

charm_unit_test #1003 ceph-radosgw-next for hopem mp286474
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/1003/

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

charm_lint_check #1167 ceph-radosgw-next for hopem mp286474
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/1167/

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

charm_amulet_test #447 ceph-radosgw-next for hopem mp286474
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/15171430/
Build: http://10.245.162.36:8080/job/charm_amulet_test/447/

70. By Edward Hope-Morley

fix logs

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

charm_lint_check #1169 ceph-radosgw-next for hopem mp286474
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/1169/

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

charm_unit_test #1006 ceph-radosgw-next for hopem mp286474
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/1006/

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

charm_amulet_test #449 ceph-radosgw-next for hopem mp286474
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/15172636/
Build: http://10.245.162.36:8080/job/charm_amulet_test/449/

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

charm_amulet_test #462 ceph-radosgw-next for hopem mp286474
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/15175252/
Build: http://10.245.162.36:8080/job/charm_amulet_test/462/

Revision history for this message
Ryan Beisner (1chb1n) wrote :

Please rebase your branch and push it back to get updated tests. Thank you.

71. By Edward Hope-Morley

sync /next

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

charm_lint_check #1296 ceph-radosgw-next for hopem mp286474
    LINT OK: passed

Build: http://10.245.162.36:8080/job/charm_lint_check/1296/

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

charm_unit_test #1073 ceph-radosgw-next for hopem mp286474
    UNIT OK: passed

Build: http://10.245.162.36:8080/job/charm_unit_test/1073/

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

charm_amulet_test #473 ceph-radosgw-next for hopem mp286474
    AMULET OK: passed

Build: http://10.245.162.36:8080/job/charm_amulet_test/473/

Revision history for this message
Edward Hope-Morley (hopem) wrote :

I've migrated this to review.openstack.org as per the new workflow - https://review.openstack.org/#/c/287162/

Unmerged revisions

71. By Edward Hope-Morley

sync /next

70. By Edward Hope-Morley

fix logs

69. By Edward Hope-Morley

only write /etc/hosts if hostname not v6 resolvable

68. By Edward Hope-Morley

very minor edit

67. By Edward Hope-Morley

sync /next

66. By Edward Hope-Morley

post-review fixups

65. By Edward Hope-Morley

fix lint error

64. By Edward Hope-Morley

fx amulet

63. By Edward Hope-Morley

fix amulet

62. By Edward Hope-Morley

[hopem,r=]

Add ipv6 support
Closes-Bug: 1513524

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 2016-01-22 13:29:40 +0000
3+++ config.yaml 2016-02-23 15:58:17 +0000
4@@ -153,3 +153,15 @@
5 description: |
6 Connect timeout configuration in ms for haproxy, used in HA
7 configurations. If not provided, default value of 5000ms is used.
8+ prefer-ipv6:
9+ type: boolean
10+ default: False
11+ description: |
12+ If True enables IPv6 support. The charm will expect network interfaces
13+ to be configured with an IPv6 address. If set to False (default) IPv4
14+ is expected.
15+ .
16+ NOTE: these charms do not currently support IPv6 privacy extension. In
17+ order for this charm to function correctly, the privacy extension must be
18+ disabled and a non-temporary address must be configured/available on
19+ your network interface.
20
21=== modified file 'hooks/ceph_radosgw_context.py'
22--- hooks/ceph_radosgw_context.py 2016-01-11 12:21:07 +0000
23+++ hooks/ceph_radosgw_context.py 2016-02-23 15:58:17 +0000
24@@ -1,3 +1,11 @@
25+import os
26+import re
27+import socket
28+import tempfile
29+import glob
30+import shutil
31+import subprocess
32+
33 from charmhelpers.contrib.openstack import context
34 from charmhelpers.contrib.hahelpers.cluster import (
35 determine_api_port,
36@@ -5,17 +13,69 @@
37 )
38 from charmhelpers.core.host import cmp_pkgrevno
39 from charmhelpers.core.hookenv import (
40+ DEBUG,
41 WARNING,
42 config,
43 log,
44 relation_ids,
45 related_units,
46 relation_get,
47- unit_get,
48-)
49-import os
50-import socket
51-import dns.resolver
52+ status_set,
53+)
54+from charmhelpers.contrib.network.ip import (
55+ format_ipv6_addr,
56+ get_host_ip,
57+ get_ipv6_addr,
58+)
59+
60+
61+def is_apache_24():
62+ if os.path.exists('/etc/apache2/conf-available'):
63+ return True
64+ else:
65+ return False
66+
67+
68+class ApacheContext(context.OSContextGenerator):
69+ interfaces = ['http']
70+ service_namespace = 'ceph-radosgw'
71+
72+ def __call__(self):
73+ ctxt = {}
74+ if config('use-embedded-webserver'):
75+ log("Skipping ApacheContext since we are using the embedded "
76+ "webserver")
77+ return {}
78+
79+ status_set('maintenance', 'configuring apache')
80+
81+ src = 'files/www/*'
82+ dst = '/var/www/'
83+ log("Installing www scripts", level=DEBUG)
84+ try:
85+ for x in glob.glob(src):
86+ shutil.copy(x, dst)
87+ except IOError as e:
88+ log("Error copying files from '%s' to '%s': %s" % (src, dst, e),
89+ level=WARNING)
90+
91+ try:
92+ subprocess.check_call(['a2enmod', 'fastcgi'])
93+ subprocess.check_call(['a2enmod', 'rewrite'])
94+ except subprocess.CalledProcessError as e:
95+ log("Error enabling apache modules - %s" % e, level=WARNING)
96+
97+ try:
98+ if is_apache_24():
99+ subprocess.check_call(['a2dissite', '000-default'])
100+ else:
101+ subprocess.check_call(['a2dissite', 'default'])
102+ except subprocess.CalledProcessError as e:
103+ log("Error disabling apache sites - %s" % e, level=WARNING)
104+
105+ ctxt['hostname'] = socket.gethostname()
106+ ctxt['port'] = determine_api_port(config('port'), singlenode_mode=True)
107+ return ctxt
108
109
110 class HAProxyContext(context.HAProxyContext):
111@@ -66,24 +126,60 @@
112 return {}
113
114
115+def ensure_host_resolvable_v6(hostname):
116+ """Ensure that we can resolve our hostname to an IPv6 address by adding it
117+ to /etc/hosts if it is not already resolvable.
118+ """
119+ try:
120+ socket.getaddrinfo(hostname, None, socket.AF_INET6)
121+ except socket.gaierror:
122+ log("Host '%s' is not ipv6 resolvable - adding to /etc/hosts" %
123+ hostname, level=DEBUG)
124+ else:
125+ log("Host '%s' appears to be ipv6 resolvable" % (hostname),
126+ level=DEBUG)
127+ return
128+
129+ # This must be the backend address used by haproxy
130+ host_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
131+ dtmp = tempfile.mkdtemp()
132+ try:
133+ tmp_hosts = os.path.join(dtmp, 'hosts')
134+ shutil.copy('/etc/hosts', tmp_hosts)
135+ with open(tmp_hosts, 'a+') as fd:
136+ lines = fd.readlines()
137+ for line in lines:
138+ key = "^%s\s+" % (host_addr)
139+ if re.search(key, line):
140+ break
141+ else:
142+ fd.write("%s\t%s\n" % (host_addr, hostname))
143+
144+ os.rename(tmp_hosts, '/etc/hosts')
145+ finally:
146+ shutil.rmtree(dtmp)
147+
148+
149 class MonContext(context.OSContextGenerator):
150 interfaces = ['ceph-radosgw']
151
152 def __call__(self):
153 if not relation_ids('mon'):
154 return {}
155- hosts = []
156+ mon_hosts = []
157 auths = []
158 for relid in relation_ids('mon'):
159 for unit in related_units(relid):
160 ceph_public_addr = relation_get('ceph-public-address', unit,
161 relid)
162 if ceph_public_addr:
163- host_ip = self.get_host_ip(ceph_public_addr)
164- hosts.append('{}:6789'.format(host_ip))
165+ host_ip = format_ipv6_addr(ceph_public_addr) or \
166+ get_host_ip(ceph_public_addr)
167+ mon_hosts.append('{}:6789'.format(host_ip))
168 _auth = relation_get('auth', unit, relid)
169 if _auth:
170 auths.append(_auth)
171+
172 if len(set(auths)) != 1:
173 e = ("Inconsistent or absent auth returned by mon units. Setting "
174 "auth_supported to 'none'")
175@@ -91,17 +187,28 @@
176 auth = 'none'
177 else:
178 auth = auths[0]
179- hosts.sort()
180+
181+ # /etc/init.d/radosgw mandates that a dns name is used for this
182+ # parameter so ensure that address is resolvable
183+ host = socket.gethostname()
184+ if config('prefer-ipv6'):
185+ ensure_host_resolvable_v6(host)
186+
187+ port = determine_apache_port(config('port'), singlenode_mode=True)
188+ if config('prefer-ipv6'):
189+ port = "[::]:%s" % (port)
190+
191+ mon_hosts.sort()
192 ctxt = {
193 'auth_supported': auth,
194- 'mon_hosts': ' '.join(hosts),
195- 'hostname': socket.gethostname(),
196+ 'mon_hosts': ' '.join(mon_hosts),
197+ 'hostname': host,
198 'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
199 'use_syslog': str(config('use-syslog')).lower(),
200 'embedded_webserver': config('use-embedded-webserver'),
201 'loglevel': config('loglevel'),
202- 'port': determine_apache_port(config('port'),
203- singlenode_mode=True)
204+ 'port': port,
205+ 'ipv6': config('prefer-ipv6')
206 }
207
208 certs_path = '/var/lib/ceph/nss'
209@@ -121,17 +228,3 @@
210 return ctxt
211
212 return {}
213-
214- def get_host_ip(self, hostname=None):
215- try:
216- if not hostname:
217- hostname = unit_get('private-address')
218- # Test to see if already an IPv4 address
219- socket.inet_aton(hostname)
220- return hostname
221- except socket.error:
222- # This may throw an NXDOMAIN exception; in which case
223- # things are badly broken so just let it kill the hook
224- answers = dns.resolver.query(hostname, 'A')
225- if answers:
226- return answers[0].address
227
228=== modified file 'hooks/hooks.py'
229--- hooks/hooks.py 2016-02-17 16:02:22 +0000
230+++ hooks/hooks.py 2016-02-23 15:58:17 +0000
231@@ -1,17 +1,16 @@
232 #!/usr/bin/python
233-
234 #
235-# Copyright 2012 Canonical Ltd.
236+# Copyright 2016 Canonical Ltd.
237 #
238 # Authors:
239 # James Page <james.page@ubuntu.com>
240+# Edward Hope-Morley <edward.hope-morley@canonical.com>
241 #
242
243-import shutil
244+import os
245 import subprocess
246 import sys
247-import glob
248-import os
249+
250 import ceph
251
252 from charmhelpers.core.hookenv import (
253@@ -39,27 +38,17 @@
254 lsb_release,
255 restart_on_change,
256 )
257-from charmhelpers.contrib.hahelpers.cluster import (
258- determine_apache_port,
259-)
260-from utils import (
261- render_template,
262- enable_pocket,
263- is_apache_24,
264- CEPHRG_HA_RES,
265- register_configs,
266- REQUIRED_INTERFACES,
267- check_optional_relations,
268-)
269 from charmhelpers.payload.execd import execd_preinstall
270 from charmhelpers.core.host import (
271 cmp_pkgrevno,
272 mkdir,
273 )
274-
275 from charmhelpers.contrib.network.ip import (
276+ format_ipv6_addr,
277+ get_ipv6_addr,
278 get_iface_for_address,
279 get_netmask_for_address,
280+ is_ipv6,
281 )
282 from charmhelpers.contrib.openstack.ip import (
283 canonical_url,
284@@ -72,18 +61,17 @@
285 send_request_if_needed,
286 is_request_complete,
287 )
288-
289-APACHE_PORTS_CONF = '/etc/apache2/ports.conf'
290+from utils import (
291+ enable_pocket,
292+ CEPHRG_HA_RES,
293+ register_configs,
294+ REQUIRED_INTERFACES,
295+ check_optional_relations,
296+ setup_ipv6,
297+)
298
299 hooks = Hooks()
300 CONFIGS = register_configs()
301-
302-
303-def install_www_scripts():
304- for x in glob.glob('files/www/*'):
305- shutil.copy(x, '/var/www/')
306-
307-
308 NSS_DIR = '/var/lib/ceph/nss'
309
310
311@@ -145,43 +133,6 @@
312 os.makedirs('/etc/ceph')
313
314
315-def emit_apacheconf():
316- apachecontext = {
317- "hostname": unit_get('private-address'),
318- "port": determine_apache_port(config('port'), singlenode_mode=True)
319- }
320- site_conf = '/etc/apache2/sites-available/rgw'
321- if is_apache_24():
322- site_conf = '/etc/apache2/sites-available/rgw.conf'
323- with open(site_conf, 'w') as apacheconf:
324- apacheconf.write(render_template('rgw', apachecontext))
325-
326-
327-def apache_sites():
328- if is_apache_24():
329- subprocess.check_call(['a2dissite', '000-default'])
330- else:
331- subprocess.check_call(['a2dissite', 'default'])
332- subprocess.check_call(['a2ensite', 'rgw'])
333-
334-
335-def apache_modules():
336- subprocess.check_call(['a2enmod', 'fastcgi'])
337- subprocess.check_call(['a2enmod', 'rewrite'])
338-
339-
340-def apache_reload():
341- subprocess.call(['service', 'apache2', 'reload'])
342-
343-
344-def apache_ports():
345- portscontext = {
346- "port": determine_apache_port(config('port'), singlenode_mode=True)
347- }
348- with open(APACHE_PORTS_CONF, 'w') as portsconf:
349- portsconf.write(render_template('ports.conf', portscontext))
350-
351-
352 def setup_keystone_certs(unit=None, rid=None):
353 """
354 Get CA and signing certs from Keystone used to decrypt revoked token list.
355@@ -213,6 +164,9 @@
356 for key in required_keys:
357 settings[key] = rdata.get(key)
358
359+ if is_ipv6(settings.get('auth_host')):
360+ settings['auth_host'] = format_ipv6_addr(settings.get('auth_host'))
361+
362 if not all(settings.values()):
363 log("Missing relation settings (%s) - skipping cert setup" %
364 (', '.join([k for k in settings.keys() if not settings[k]])),
365@@ -288,18 +242,28 @@
366 '/etc/haproxy/haproxy.cfg': ['haproxy']})
367 def config_changed():
368 install_packages()
369+
370+ if config('prefer-ipv6'):
371+ status_set('maintenance', 'configuring ipv6')
372+ setup_ipv6()
373+
374+ for r_id in relation_ids('identity-service'):
375+ identity_changed(relid=r_id)
376+
377+ for r_id in relation_ids('cluster'):
378+ cluster_joined(rid=r_id)
379+
380 CONFIGS.write_all()
381+
382 if not config('use-embedded-webserver'):
383- status_set('maintenance', 'configuring apache')
384- emit_apacheconf()
385- install_www_scripts()
386- apache_sites()
387- apache_modules()
388- apache_ports()
389- apache_reload()
390+ try:
391+ subprocess.check_call(['a2ensite', 'rgw'])
392+ except subprocess.CalledProcessError as e:
393+ log("Error enabling apache module 'rgw' - %s" % e, level=WARNING)
394
395- for r_id in relation_ids('identity-service'):
396- identity_changed(relid=r_id)
397+ # Ensure started but do a soft reload
398+ subprocess.call(['service', 'apache2', 'start'])
399+ subprocess.call(['service', 'apache2', 'reload'])
400
401
402 @hooks.hook('mon-relation-departed',
403@@ -373,8 +337,18 @@
404 restart()
405
406
407-@hooks.hook('cluster-relation-changed',
408- 'cluster-relation-joined')
409+@hooks.hook('cluster-relation-joined')
410+@restart_on_change({'/etc/haproxy/haproxy.cfg': ['haproxy']})
411+def cluster_joined(rid=None):
412+ settings = {}
413+ if config('prefer-ipv6'):
414+ private_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
415+ settings['private-address'] = private_addr
416+
417+ relation_set(relation_id=rid, **settings)
418+
419+
420+@hooks.hook('cluster-relation-changed')
421 @restart_on_change({'/etc/haproxy/haproxy.cfg': ['haproxy']})
422 def cluster_changed():
423 CONFIGS.write_all()
424@@ -384,17 +358,12 @@
425
426 @hooks.hook('ha-relation-joined')
427 def ha_relation_joined():
428- # Obtain the config values necessary for the cluster config. These
429- # include multicast port and interface to bind to.
430- corosync_bindiface = config('ha-bindiface')
431- corosync_mcastport = config('ha-mcastport')
432 vip = config('vip')
433 if not vip:
434- log('Unable to configure hacluster as vip not provided',
435- level=ERROR)
436+ log('Unable to configure hacluster as vip not provided', level=ERROR)
437 sys.exit(1)
438+
439 # Obtain resources
440- # SWIFT_HA_RES = 'grp_swift_vips'
441 resources = {
442 'res_cephrg_haproxy': 'lsb:haproxy'
443 }
444@@ -404,15 +373,25 @@
445
446 vip_group = []
447 for vip in vip.split():
448+ if is_ipv6(vip):
449+ res_rgw_vip = 'ocf:heartbeat:IPv6addr'
450+ vip_params = 'ipv6addr'
451+ else:
452+ res_rgw_vip = 'ocf:heartbeat:IPaddr2'
453+ vip_params = 'ip'
454+
455 iface = get_iface_for_address(vip)
456+ netmask = get_netmask_for_address(vip)
457+
458 if iface is not None:
459 vip_key = 'res_cephrg_{}_vip'.format(iface)
460- resources[vip_key] = 'ocf:heartbeat:IPaddr2'
461+ resources[vip_key] = res_rgw_vip
462 resource_params[vip_key] = (
463- 'params ip="{vip}" cidr_netmask="{netmask}"'
464- ' nic="{iface}"'.format(vip=vip,
465+ 'params {ip}="{vip}" cidr_netmask="{netmask}"'
466+ ' nic="{iface}"'.format(ip=vip_params,
467+ vip=vip,
468 iface=iface,
469- netmask=get_netmask_for_address(vip))
470+ netmask=netmask)
471 )
472 vip_group.append(vip_key)
473
474@@ -426,6 +405,11 @@
475 'cl_cephrg_haproxy': 'res_cephrg_haproxy'
476 }
477
478+ # Obtain the config values necessary for the cluster config. These
479+ # include multicast port and interface to bind to.
480+ corosync_bindiface = config('ha-bindiface')
481+ corosync_mcastport = config('ha-mcastport')
482+
483 relation_set(init_services=init_services,
484 corosync_bindiface=corosync_bindiface,
485 corosync_mcastport=corosync_mcastport,
486
487=== modified file 'hooks/utils.py'
488--- hooks/utils.py 2015-10-12 10:56:01 +0000
489+++ hooks/utils.py 2016-02-23 15:58:17 +0000
490@@ -1,26 +1,43 @@
491 #
492-# Copyright 2012 Canonical Ltd.
493+# Copyright 2016 Canonical Ltd.
494 #
495 # Authors:
496 # James Page <james.page@ubuntu.com>
497 # Paul Collins <paul.collins@canonical.com>
498+# Edward Hope-Morley <edward.hope-morley@canonical.com>
499 #
500
501-import socket
502 import re
503-import os
504-import dns.resolver
505 import jinja2
506+
507 from copy import deepcopy
508 from collections import OrderedDict
509-from charmhelpers.core.hookenv import unit_get, relation_ids, status_get
510-from charmhelpers.contrib.openstack import context, templating
511-from charmhelpers.contrib.openstack.utils import set_os_workload_status
512+
513+import ceph_radosgw_context
514+
515+from charmhelpers.core.hookenv import (
516+ relation_ids,
517+ status_get,
518+)
519+from charmhelpers.contrib.openstack import (
520+ context,
521+ templating,
522+)
523+from charmhelpers.contrib.openstack.utils import (
524+ os_release,
525+ set_os_workload_status,
526+)
527 from charmhelpers.contrib.hahelpers.cluster import get_hacluster_config
528-from charmhelpers.core.host import cmp_pkgrevno
529-from charmhelpers.fetch import filter_installed_packages
530-
531-import ceph_radosgw_context
532+from charmhelpers.core.host import (
533+ cmp_pkgrevno,
534+ lsb_release,
535+)
536+from charmhelpers.fetch import (
537+ apt_install,
538+ apt_update,
539+ add_source,
540+ filter_installed_packages,
541+)
542
543 # The interface is said to be satisfied if anyone of the interfaces in the
544 # list has a complete context.
545@@ -32,6 +49,9 @@
546 TEMPLATES = 'templates/'
547 HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
548 CEPH_CONF = '/etc/ceph/ceph.conf'
549+APACHE_CONF = '/etc/apache2/sites-available/rgw'
550+APACHE_24_CONF = '/etc/apache2/sites-available/rgw.conf'
551+APACHE_PORTS_CONF = '/etc/apache2/ports.conf'
552
553 BASE_RESOURCE_MAP = OrderedDict([
554 (HAPROXY_CONF, {
555@@ -39,6 +59,18 @@
556 ceph_radosgw_context.HAProxyContext()],
557 'services': ['haproxy'],
558 }),
559+ (APACHE_CONF, {
560+ 'contexts': [ceph_radosgw_context.ApacheContext()],
561+ 'services': ['apache2'],
562+ }),
563+ (APACHE_24_CONF, {
564+ 'contexts': [ceph_radosgw_context.ApacheContext()],
565+ 'services': ['apache2'],
566+ }),
567+ (APACHE_PORTS_CONF, {
568+ 'contexts': [ceph_radosgw_context.ApacheContext()],
569+ 'services': ['apache2'],
570+ }),
571 (CEPH_CONF, {
572 'contexts': [ceph_radosgw_context.MonContext()],
573 'services': ['radosgw'],
574@@ -92,28 +124,6 @@
575 sources.write(line)
576
577
578-def get_host_ip(hostname=None):
579- try:
580- if not hostname:
581- hostname = unit_get('private-address')
582- # Test to see if already an IPv4 address
583- socket.inet_aton(hostname)
584- return hostname
585- except socket.error:
586- # This may throw an NXDOMAIN exception; in which case
587- # things are badly broken so just let it kill the hook
588- answers = dns.resolver.query(hostname, 'A')
589- if answers:
590- return answers[0].address
591-
592-
593-def is_apache_24():
594- if os.path.exists('/etc/apache2/conf-available'):
595- return True
596- else:
597- return False
598-
599-
600 def check_optional_relations(configs):
601 required_interfaces = {}
602 if relation_ids('ha'):
603@@ -132,3 +142,18 @@
604 return status_get()
605 else:
606 return 'unknown', 'No optional relations'
607+
608+
609+def setup_ipv6():
610+ ubuntu_rel = lsb_release()['DISTRIB_CODENAME'].lower()
611+ if ubuntu_rel < "trusty":
612+ raise Exception("IPv6 is not supported in the charms for Ubuntu "
613+ "versions less than Trusty 14.04")
614+
615+ # Need haproxy >= 1.5.3 for ipv6 so for Trusty if we are <= Kilo we need to
616+ # use trusty-backports otherwise we can use the UCA.
617+ if ubuntu_rel == 'trusty' and os_release('ceph-common') < 'liberty':
618+ add_source('deb http://archive.ubuntu.com/ubuntu trusty-backports '
619+ 'main')
620+ apt_update()
621+ apt_install('haproxy/trusty-backports', fatal=True)
622
623=== modified file 'templates/ceph.conf'
624--- templates/ceph.conf 2016-01-11 12:21:07 +0000
625+++ templates/ceph.conf 2016-02-23 15:58:17 +0000
626@@ -11,6 +11,9 @@
627 err to syslog = {{ use_syslog }}
628 clog to syslog = {{ use_syslog }}
629 debug rgw = {{ loglevel }}/5
630+{% if ipv6 -%}
631+ms bind ipv6 = true
632+{% endif %}
633
634 [client.radosgw.gateway]
635 host = {{ hostname }}
636
637=== added file 'templates/rgw.conf'
638--- templates/rgw.conf 1970-01-01 00:00:00 +0000
639+++ templates/rgw.conf 2016-02-23 15:58:17 +0000
640@@ -0,0 +1,25 @@
641+<IfModule mod_fastcgi.c>
642+ FastCgiExternalServer /var/www/s3gw.fcgi -socket /tmp/radosgw.sock
643+</IfModule>
644+
645+<VirtualHost *:{{ port }}>
646+ ServerName {{ hostname }}
647+ ServerAdmin ceph@ubuntu.com
648+ DocumentRoot /var/www
649+ RewriteEngine On
650+ RewriteRule ^/([a-zA-Z0-9-_.]*)([/]?.*) /s3gw.fcgi?page=$1&params=$2&%{QUERY_STRING} [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
651+ <IfModule mod_fastcgi.c>
652+ <Directory /var/www>
653+ Options +ExecCGI
654+ AllowOverride All
655+ SetHandler fastcgi-script
656+ Order allow,deny
657+ Allow from all
658+ AuthBasicAuthoritative Off
659+ </Directory>
660+ </IfModule>
661+ AllowEncodedSlashes On
662+ ErrorLog /var/log/apache2/error.log
663+ CustomLog /var/log/apache2/access.log combined
664+ ServerSignature Off
665+</VirtualHost>
666
667=== modified file 'unit_tests/test_ceph_radosgw_context.py'
668--- unit_tests/test_ceph_radosgw_context.py 2016-02-17 22:46:07 +0000
669+++ unit_tests/test_ceph_radosgw_context.py 2016-02-23 15:58:17 +0000
670@@ -13,6 +13,7 @@
671 'related_units',
672 'cmp_pkgrevno',
673 'socket',
674+ 'is_apache_24',
675 ]
676
677
678@@ -147,8 +148,9 @@
679 super(MonContextTest, self).setUp(context, TO_PATCH)
680 self.config.side_effect = self.test_config.get
681
682- def test_ctxt(self):
683- self.socket.gethostname.return_value = '10.0.0.10'
684+ @patch.object(context, 'ensure_host_resolvable_v6')
685+ def test_ctxt(self, mock_ensure_rsv_v6):
686+ self.socket.gethostname.return_value = 'testhost'
687 mon_ctxt = context.MonContext()
688 addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
689
690@@ -157,6 +159,7 @@
691 return addresses.pop()
692 elif attr == 'auth':
693 return 'cephx'
694+
695 self.relation_get.side_effect = _relation_get
696 self.relation_ids.return_value = ['mon:6']
697 self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
698@@ -164,17 +167,26 @@
699 'auth_supported': 'cephx',
700 'embedded_webserver': False,
701 'disable_100_continue': True,
702- 'hostname': '10.0.0.10',
703+ 'hostname': 'testhost',
704 'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
705 'old_auth': False,
706 'use_syslog': 'false',
707 'loglevel': 1,
708- 'port': 70
709+ 'port': 70,
710+ 'ipv6': False
711 }
712 self.assertEqual(expect, mon_ctxt())
713+ self.assertFalse(mock_ensure_rsv_v6.called)
714+
715+ self.test_config.set('prefer-ipv6', True)
716+ addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
717+ expect['ipv6'] = True
718+ expect['port'] = "[::]:%s" % (70)
719+ self.assertEqual(expect, mon_ctxt())
720+ self.assertTrue(mock_ensure_rsv_v6.called)
721
722 def test_ctxt_missing_data(self):
723- self.socket.gethostname.return_value = '10.0.0.10'
724+ self.socket.gethostname.return_value = 'testhost'
725 mon_ctxt = context.MonContext()
726 self.relation_get.return_value = None
727 self.relation_ids.return_value = ['mon:6']
728@@ -182,7 +194,7 @@
729 self.assertEqual({}, mon_ctxt())
730
731 def test_ctxt_inconsistent_auths(self):
732- self.socket.gethostname.return_value = '10.0.0.10'
733+ self.socket.gethostname.return_value = 'testhost'
734 mon_ctxt = context.MonContext()
735 addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
736 auths = ['cephx', 'cephy', 'cephz']
737@@ -199,17 +211,18 @@
738 'auth_supported': 'none',
739 'embedded_webserver': False,
740 'disable_100_continue': True,
741- 'hostname': '10.0.0.10',
742+ 'hostname': 'testhost',
743 'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
744 'old_auth': False,
745 'use_syslog': 'false',
746 'loglevel': 1,
747- 'port': 70
748+ 'port': 70,
749+ 'ipv6': False
750 }
751 self.assertEqual(expect, mon_ctxt())
752
753 def test_ctxt_consistent_auths(self):
754- self.socket.gethostname.return_value = '10.0.0.10'
755+ self.socket.gethostname.return_value = 'testhost'
756 mon_ctxt = context.MonContext()
757 addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
758 auths = ['cephx', 'cephx', 'cephx']
759@@ -226,11 +239,19 @@
760 'auth_supported': 'cephx',
761 'embedded_webserver': False,
762 'disable_100_continue': True,
763- 'hostname': '10.0.0.10',
764+ 'hostname': 'testhost',
765 'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
766 'old_auth': False,
767 'use_syslog': 'false',
768 'loglevel': 1,
769- 'port': 70
770+ 'port': 70,
771+ 'ipv6': False
772 }
773 self.assertEqual(expect, mon_ctxt())
774+
775+
776+class ApacheContextTest(CharmTestCase):
777+
778+ def setUp(self):
779+ super(ApacheContextTest, self).setUp(context, TO_PATCH)
780+ self.config.side_effect = self.test_config.get
781
782=== modified file 'unit_tests/test_hooks.py'
783--- unit_tests/test_hooks.py 2016-01-22 13:29:40 +0000
784+++ unit_tests/test_hooks.py 2016-02-23 15:58:17 +0000
785@@ -6,7 +6,6 @@
786
787 from test_utils import (
788 CharmTestCase,
789- patch_open
790 )
791 from charmhelpers.contrib.openstack.ip import PUBLIC
792
793@@ -34,8 +33,6 @@
794 'enable_pocket',
795 'get_iface_for_address',
796 'get_netmask_for_address',
797- 'glob',
798- 'is_apache_24',
799 'log',
800 'lsb_release',
801 'open_port',
802@@ -44,8 +41,6 @@
803 'relation_set',
804 'relation_get',
805 'related_units',
806- 'render_template',
807- 'shutil',
808 'status_set',
809 'subprocess',
810 'sys',
811@@ -62,11 +57,6 @@
812 self.test_config.set('key', 'secretkey')
813 self.test_config.set('use-syslog', False)
814
815- def test_install_www_scripts(self):
816- self.glob.glob.return_value = ['files/www/bob']
817- ceph_hooks.install_www_scripts()
818- self.shutil.copy.assert_called_with('files/www/bob', '/var/www/')
819-
820 def test_install_ceph_optimised_packages(self):
821 self.lsb_release.return_value = {'DISTRIB_CODENAME': 'vivid'}
822 fastcgi_source = (
823@@ -122,69 +112,12 @@
824 self.enable_pocket.assert_called_with('multiverse')
825 self.os.makedirs.called_with('/var/lib/ceph/nss')
826
827- def test_emit_apacheconf(self):
828- self.is_apache_24.return_value = True
829- self.unit_get.return_value = '10.0.0.1'
830- apachecontext = {
831- "hostname": '10.0.0.1',
832- "port": 70,
833- }
834- vhost_file = '/etc/apache2/sites-available/rgw.conf'
835- with patch_open() as (_open, _file):
836- ceph_hooks.emit_apacheconf()
837- _open.assert_called_with(vhost_file, 'w')
838- self.render_template.assert_called_with('rgw', apachecontext)
839-
840- def test_apache_sites24(self):
841- self.is_apache_24.return_value = True
842- ceph_hooks.apache_sites()
843- calls = [
844- call(['a2dissite', '000-default']),
845- call(['a2ensite', 'rgw']),
846- ]
847- self.subprocess.check_call.assert_has_calls(calls)
848-
849- def test_apache_sites22(self):
850- self.is_apache_24.return_value = False
851- ceph_hooks.apache_sites()
852- calls = [
853- call(['a2dissite', 'default']),
854- call(['a2ensite', 'rgw']),
855- ]
856- self.subprocess.check_call.assert_has_calls(calls)
857-
858- def test_apache_modules(self):
859- ceph_hooks.apache_modules()
860- calls = [
861- call(['a2enmod', 'fastcgi']),
862- call(['a2enmod', 'rewrite']),
863- ]
864- self.subprocess.check_call.assert_has_calls(calls)
865-
866- def test_apache_reload(self):
867- ceph_hooks.apache_reload()
868- calls = [
869- call(['service', 'apache2', 'reload']),
870- ]
871- self.subprocess.call.assert_has_calls(calls)
872-
873- @patch.object(ceph_hooks, 'apache_ports', lambda *args: True)
874 @patch.object(ceph_hooks, 'mkdir', lambda *args: None)
875 def test_config_changed(self):
876 _install_packages = self.patch('install_packages')
877- _emit_apacheconf = self.patch('emit_apacheconf')
878- _install_www_scripts = self.patch('install_www_scripts')
879- _apache_sites = self.patch('apache_sites')
880- _apache_modules = self.patch('apache_modules')
881- _apache_reload = self.patch('apache_reload')
882 ceph_hooks.config_changed()
883 self.assertTrue(_install_packages.called)
884 self.CONFIGS.write_all.assert_called_with()
885- self.assertTrue(_emit_apacheconf.called)
886- self.assertTrue(_install_www_scripts.called)
887- self.assertTrue(_apache_sites.called)
888- self.assertTrue(_apache_modules.called)
889- self.assertTrue(_apache_reload.called)
890
891 @patch.object(ceph_hooks, 'is_request_complete',
892 lambda *args, **kwargs: True)

Subscribers

People subscribed via source and target branches