Merge lp:~james-page/charms/raring/ceph/trunk into lp:~charmers/charms/precise/ceph/trunk

Proposed by James Page
Status: Merged
Approved by: Mark Mims
Approved revision: 60
Merged at revision: 54
Proposed branch: lp:~james-page/charms/raring/ceph/trunk
Merge into: lp:~charmers/charms/precise/ceph/trunk
Diff against target: 385 lines (+157/-23)
6 files modified
config.yaml (+19/-1)
hooks/ceph.py (+26/-0)
hooks/hooks.py (+58/-11)
hooks/utils.py (+47/-10)
revision (+1/-1)
templates/ceph.conf (+6/-0)
To merge this branch: bzr merge lp:~james-page/charms/raring/ceph/trunk
Reviewer Review Type Date Requested Status
Mark Mims (community) Approve
Review via email: mp+142897@code.launchpad.net

Description of the change

Updates to support newer releases of ceph.

Updates are backwards compatibile with precise/0.48.x series and have been testing using the following combos:

precise/0.48.2
quantal/0.48.2
raring/0.56.1

To post a comment you must log in.
59. By James Page

Improve host ip resolution - fixes issues with maas managed DNS

Revision history for this message
Jim Baker (jimbaker) wrote :

My only concern is that in the function get_mon_hosts, it is possible that NXDOMAIN (which certainly seems could potentially happen) will result in None:6789 as one of the items in the hosts list:

            hosts.append(
                '{}:6789'.format(utils.get_host_ip(
                                    utils.relation_get('private-address',
                                                       unit, relid)))

This list is then passed on to ceph via /etc/ceph/ceph.conf, so this may be fine. But it also seems potentially problematic.

Maybe instead we should simply not append an unresolved address to hosts. Another possibility is to do a retry on get_host_ip. Alternatively instead of swallowing the error, we could force the juju admin to resolve this problem. That does sound more painful however.

60. By James Page

Allow get_host_ip helper to raise exception in the event that a IP address
cannot be resolved for the provided hostname.

Revision history for this message
Mark Mims (mark-mims) wrote :

this looks great.

This MP exposes a flaw in our current charm subnmission process. The alias lp:charms/ceph points to lp:~charmers/charms/precise/ceph/trunk. However, the proposed merge is from a raring branch. Should it go to precise, quantal, or should we do a new lp:~charmers/charms/raring/ceph/trunk and leave this merge out of the past series branches?

I'd reject because there's ambiguity in which branch(es) you'd like this merged into... but after talking on IRC, the branches are intended for precise even though they're raring branches. In the future we should submit against a specific series branch if that's possible and what you mean to do... otherwise, at least note the intended destination in the MP somewhere.

Thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'README' => 'README.md'
2=== modified file 'config.yaml'
3--- config.yaml 2012-11-22 10:47:03 +0000
4+++ config.yaml 2013-02-07 16:02:24 +0000
5@@ -39,6 +39,24 @@
6 .
7 These devices are the range of devices that will be checked for and
8 used across all service units.
9+ osd-journal:
10+ type: string
11+ description: |
12+ The device to use as a shared journal drive for all OSD's. By default
13+ no journal device will be used.
14+ .
15+ Only supported with ceph >= 0.55.
16+ osd-format:
17+ type: string
18+ default: xfs
19+ description: |
20+ Format of filesystem to use for OSD devices; supported formats include:
21+ .
22+ xfs (Default >= 0.55)
23+ ext4 (Only option < 0.55)
24+ btrfs (experimental and not recommended)
25+ .
26+ Only supported with ceph >= 0.55.
27 osd-reformat:
28 type: string
29 description: |
30@@ -47,7 +65,7 @@
31 prevent data loss.
32 .
33 Specifying this option (any value) forces a reformat of any OSD devices
34- found which are not already mounted.
35+ found which are not already mounted.
36 ephemeral-unmount:
37 type: string
38 description: |
39
40=== modified file 'hooks/ceph.py'
41--- hooks/ceph.py 2012-10-19 15:50:18 +0000
42+++ hooks/ceph.py 2013-02-07 16:02:24 +0000
43@@ -12,6 +12,7 @@
44 import time
45 import utils
46 import os
47+import apt_pkg as apt
48
49 LEADER = 'leader'
50 PEON = 'peon'
51@@ -84,6 +85,12 @@
52 # Ignore any errors for this call
53 subprocess.call(cmd)
54
55+DISK_FORMATS = [
56+ 'xfs',
57+ 'ext4',
58+ 'btrfs'
59+ ]
60+
61
62 def is_osd_disk(dev):
63 try:
64@@ -108,6 +115,11 @@
65 subprocess.call(cmd)
66
67
68+def zap_disk(dev):
69+ cmd = ['sgdisk', '--zap-all', dev]
70+ subprocess.check_call(cmd)
71+
72+
73 _bootstrap_keyring = "/var/lib/ceph/bootstrap-osd/ceph.keyring"
74
75
76@@ -207,3 +219,17 @@
77 if 'key' in element:
78 key = element.split(' = ')[1].strip() # IGNORE:E1103
79 return key
80+
81+
82+def get_ceph_version():
83+ apt.init()
84+ cache = apt.Cache()
85+ pkg = cache['ceph']
86+ if pkg.current_ver:
87+ return apt.upstream_version(pkg.current_ver.ver_str)
88+ else:
89+ return None
90+
91+
92+def version_compare(a, b):
93+ return apt.version_compare(a, b)
94
95=== modified file 'hooks/hooks.py'
96--- hooks/hooks.py 2012-11-12 10:00:27 +0000
97+++ hooks/hooks.py 2013-02-07 16:02:24 +0000
98@@ -19,14 +19,16 @@
99
100
101 def install_upstart_scripts():
102- for x in glob.glob('files/upstart/*.conf'):
103- shutil.copy(x, '/etc/init/')
104+ # Only install upstart configurations for older versions
105+ if ceph.get_ceph_version() < "0.55.1":
106+ for x in glob.glob('files/upstart/*.conf'):
107+ shutil.copy(x, '/etc/init/')
108
109
110 def install():
111 utils.juju_log('INFO', 'Begin install hook.')
112 utils.configure_source()
113- utils.install('ceph', 'gdisk', 'ntp')
114+ utils.install('ceph', 'gdisk', 'ntp', 'btrfs-tools')
115 install_upstart_scripts()
116 utils.juju_log('INFO', 'End install hook.')
117
118@@ -36,35 +38,48 @@
119 'auth_supported': utils.config_get('auth-supported'),
120 'mon_hosts': ' '.join(get_mon_hosts()),
121 'fsid': utils.config_get('fsid'),
122+ 'version': ceph.get_ceph_version()
123 }
124
125 with open('/etc/ceph/ceph.conf', 'w') as cephconf:
126 cephconf.write(utils.render_template('ceph.conf', cephcontext))
127
128+JOURNAL_ZAPPED = '/var/lib/ceph/journal_zapped'
129+
130
131 def config_changed():
132 utils.juju_log('INFO', 'Begin config-changed hook.')
133
134 utils.juju_log('INFO', 'Monitor hosts are ' + repr(get_mon_hosts()))
135
136- fsid = utils.config_get('fsid')
137- if fsid == '':
138+ # Pre-flight checks
139+ if not utils.config_get('fsid'):
140 utils.juju_log('CRITICAL', 'No fsid supplied, cannot proceed.')
141 sys.exit(1)
142-
143- monitor_secret = utils.config_get('monitor-secret')
144- if monitor_secret == '':
145+ if not utils.config_get('monitor-secret'):
146 utils.juju_log('CRITICAL',
147 'No monitor-secret supplied, cannot proceed.')
148 sys.exit(1)
149+ if utils.config_get('osd-format') not in ceph.DISK_FORMATS:
150+ utils.juju_log('CRITICAL',
151+ 'Invalid OSD disk format configuration specified')
152+ sys.exit(1)
153
154 emit_cephconf()
155
156 e_mountpoint = utils.config_get('ephemeral-unmount')
157- if (e_mountpoint != "" and
158+ if (e_mountpoint and
159 filesystem_mounted(e_mountpoint)):
160 subprocess.call(['umount', e_mountpoint])
161
162+ osd_journal = utils.config_get('osd-journal')
163+ if (osd_journal and
164+ not os.path.exists(JOURNAL_ZAPPED) and
165+ os.path.exists(osd_journal)):
166+ ceph.zap_disk(osd_journal)
167+ with open(JOURNAL_ZAPPED, 'w') as zapped:
168+ zapped.write('DONE')
169+
170 for dev in utils.config_get('osd-devices').split(' '):
171 osdize(dev)
172
173@@ -96,9 +111,22 @@
174 return hosts
175
176
177+def update_monfs():
178+ hostname = utils.get_unit_hostname()
179+ monfs = '/var/lib/ceph/mon/ceph-{}'.format(hostname)
180+ upstart = '{}/upstart'.format(monfs)
181+ if (os.path.exists(monfs) and
182+ not os.path.exists(upstart)):
183+ # Mark mon as managed by upstart so that
184+ # it gets start correctly on reboots
185+ with open(upstart, 'w'):
186+ pass
187+
188+
189 def bootstrap_monitor_cluster():
190 hostname = utils.get_unit_hostname()
191 done = '/var/lib/ceph/mon/ceph-{}/done'.format(hostname)
192+ upstart = '/var/lib/ceph/mon/ceph-{}/upstart'.format(hostname)
193 secret = utils.config_get('monitor-secret')
194 keyring = '/var/lib/ceph/tmp/{}.mon.keyring'.format(hostname)
195
196@@ -118,6 +146,8 @@
197
198 with open(done, 'w'):
199 pass
200+ with open(upstart, 'w'):
201+ pass
202
203 subprocess.check_call(['start', 'ceph-mon-all-starter'])
204 except:
205@@ -127,7 +157,7 @@
206
207
208 def reformat_osd():
209- if utils.config_get('osd-reformat') != "":
210+ if utils.config_get('osd-reformat'):
211 return True
212 else:
213 return False
214@@ -151,7 +181,23 @@
215 'Looks like {} is in use, skipping.'.format(dev))
216 return
217
218- subprocess.call(['ceph-disk-prepare', dev])
219+ cmd = ['ceph-disk-prepare']
220+ # Later versions of ceph support more options
221+ if ceph.get_ceph_version() >= "0.55":
222+ osd_format = utils.config_get('osd-format')
223+ if osd_format:
224+ cmd.append('--fs-type')
225+ cmd.append(osd_format)
226+ cmd.append(dev)
227+ osd_journal = utils.config_get('osd-journal')
228+ if (osd_journal and
229+ os.path.exists(osd_journal)):
230+ cmd.append(osd_journal)
231+ else:
232+ # Just provide the device - no other options
233+ # for older versions of ceph
234+ cmd.append(dev)
235+ subprocess.call(cmd)
236
237
238 def device_mounted(dev):
239@@ -272,6 +318,7 @@
240 utils.juju_log('INFO', 'Begin upgrade-charm hook.')
241 emit_cephconf()
242 install_upstart_scripts()
243+ update_monfs()
244 utils.juju_log('INFO', 'End upgrade-charm hook.')
245
246
247
248=== modified file 'hooks/utils.py'
249--- hooks/utils.py 2012-11-12 09:32:06 +0000
250+++ hooks/utils.py 2013-02-07 16:02:24 +0000
251@@ -11,16 +11,19 @@
252 import subprocess
253 import socket
254 import sys
255+import re
256
257
258 def do_hooks(hooks):
259 hook = os.path.basename(sys.argv[0])
260
261 try:
262- hooks[hook]()
263+ hook_func = hooks[hook]
264 except KeyError:
265 juju_log('INFO',
266 "This charm doesn't know how to handle '{}'.".format(hook))
267+ else:
268+ hook_func()
269
270
271 def install(*pkgs):
272@@ -41,6 +44,12 @@
273 install('python-jinja2')
274 import jinja2
275
276+try:
277+ import dns.resolver
278+except ImportError:
279+ install('python-dnspython')
280+ import dns.resolver
281+
282
283 def render_template(template_name, context, template_dir=TEMPLATES_DIR):
284 templates = jinja2.Environment(
285@@ -88,6 +97,18 @@
286 ]
287 subprocess.check_call(cmd)
288
289+
290+def enable_pocket(pocket):
291+ apt_sources = "/etc/apt/sources.list"
292+ with open(apt_sources, "r") as sources:
293+ lines = sources.readlines()
294+ with open(apt_sources, "w") as sources:
295+ for line in lines:
296+ if pocket in line:
297+ sources.write(re.sub('^# deb', 'deb', line))
298+ else:
299+ sources.write(line)
300+
301 # Protocols
302 TCP = 'TCP'
303 UDP = 'UDP'
304@@ -136,7 +157,11 @@
305 cmd.append(attribute)
306 if unit:
307 cmd.append(unit)
308- return subprocess.check_output(cmd).strip() # IGNORE:E1103
309+ value = str(subprocess.check_output(cmd)).strip()
310+ if value == "":
311+ return None
312+ else:
313+ return value
314
315
316 def relation_set(**kwargs):
317@@ -159,7 +184,11 @@
318 'unit-get',
319 attribute
320 ]
321- return subprocess.check_output(cmd).strip() # IGNORE:E1103
322+ value = str(subprocess.check_output(cmd)).strip()
323+ if value == "":
324+ return None
325+ else:
326+ return value
327
328
329 def config_get(attribute):
330@@ -167,7 +196,11 @@
331 'config-get',
332 attribute
333 ]
334- return subprocess.check_output(cmd).strip() # IGNORE:E1103
335+ value = str(subprocess.check_output(cmd)).strip()
336+ if value == "":
337+ return None
338+ else:
339+ return value
340
341
342 def get_unit_hostname():
343@@ -175,9 +208,13 @@
344
345
346 def get_host_ip(hostname=unit_get('private-address')):
347- cmd = [
348- 'dig',
349- '+short',
350- hostname
351- ]
352- return subprocess.check_output(cmd).strip() # IGNORE:E1103
353+ try:
354+ # Test to see if already an IPv4 address
355+ socket.inet_aton(hostname)
356+ return hostname
357+ except socket.error:
358+ # This may throw an NXDOMAIN exception; in which case
359+ # things are badly broken so just let it kill the hook
360+ answers = dns.resolver.query(hostname, 'A')
361+ if answers:
362+ return answers[0].address
363
364=== modified file 'revision'
365--- revision 2012-11-12 10:00:27 +0000
366+++ revision 2013-02-07 16:02:24 +0000
367@@ -1,1 +1,1 @@
368-88
369+91
370
371=== modified file 'templates/ceph.conf'
372--- templates/ceph.conf 2012-10-18 06:19:52 +0000
373+++ templates/ceph.conf 2013-02-07 16:02:24 +0000
374@@ -1,5 +1,11 @@
375 [global]
376+{% if version < "0.51" %}
377 auth supported = {{ auth_supported }}
378+{% else %}
379+ auth cluster required = {{ auth_supported }}
380+ auth service required = {{ auth_supported }}
381+ auth client required = {{ auth_supported }}
382+{% endif %}
383 keyring = /etc/ceph/$cluster.$name.keyring
384 mon host = {{ mon_hosts }}
385 fsid = {{ fsid }}

Subscribers

People subscribed via source and target branches

to all changes: