Merge lp:~hopem/charms/trusty/ceph-radosgw/lp1513524 into lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next
- Trusty Tahr (14.04)
- lp1513524
- Merge into next
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 | ||||
Related bugs: |
|
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.
Commit message
Description of the change
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
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
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://
Build: http://
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
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://
Build: http://
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://
Build: http://
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://
Build: http://
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
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
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
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
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
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
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.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #820 ceph-radosgw-next for hopem mp286474
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #723 ceph-radosgw-next for hopem mp286474
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #320 ceph-radosgw-next for hopem mp286474
AMULET OK: passed
Chris Holcombe (xfactor973) wrote : Posted in a previous version of this proposal | # |
Cool. Lets get this merged!
- 67. By Edward Hope-Morley
-
sync /next
- 68. By Edward Hope-Morley
-
very minor edit
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #912 ceph-radosgw-next for hopem mp286474
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #813 ceph-radosgw-next for hopem mp286474
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #359 ceph-radosgw-next for hopem mp286474
AMULET OK: passed
Liam Young (gnuoy) wrote : | # |
See inline comment
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
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1003 ceph-radosgw-next for hopem mp286474
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #1167 ceph-radosgw-next for hopem mp286474
LINT OK: passed
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://
Build: http://
- 70. By Edward Hope-Morley
-
fix logs
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #1169 ceph-radosgw-next for hopem mp286474
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1006 ceph-radosgw-next for hopem mp286474
UNIT OK: passed
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://
Build: http://
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://
Build: http://
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
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #1296 ceph-radosgw-next for hopem mp286474
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1073 ceph-radosgw-next for hopem mp286474
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #473 ceph-radosgw-next for hopem mp286474
AMULET OK: passed
Edward Hope-Morley (hopem) wrote : | # |
I've migrated this to review.
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
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¶ms=$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) |
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/