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
=== renamed file 'README' => 'README.md'
=== modified file 'config.yaml'
--- config.yaml 2012-11-22 10:47:03 +0000
+++ config.yaml 2013-02-07 16:02:24 +0000
@@ -39,6 +39,24 @@
39 .39 .
40 These devices are the range of devices that will be checked for and40 These devices are the range of devices that will be checked for and
41 used across all service units.41 used across all service units.
42 osd-journal:
43 type: string
44 description: |
45 The device to use as a shared journal drive for all OSD's. By default
46 no journal device will be used.
47 .
48 Only supported with ceph >= 0.55.
49 osd-format:
50 type: string
51 default: xfs
52 description: |
53 Format of filesystem to use for OSD devices; supported formats include:
54 .
55 xfs (Default >= 0.55)
56 ext4 (Only option < 0.55)
57 btrfs (experimental and not recommended)
58 .
59 Only supported with ceph >= 0.55.
42 osd-reformat:60 osd-reformat:
43 type: string61 type: string
44 description: |62 description: |
@@ -47,7 +65,7 @@
47 prevent data loss.65 prevent data loss.
48 .66 .
49 Specifying this option (any value) forces a reformat of any OSD devices67 Specifying this option (any value) forces a reformat of any OSD devices
50 found which are not already mounted. 68 found which are not already mounted.
51 ephemeral-unmount:69 ephemeral-unmount:
52 type: string70 type: string
53 description: |71 description: |
5472
=== modified file 'hooks/ceph.py'
--- hooks/ceph.py 2012-10-19 15:50:18 +0000
+++ hooks/ceph.py 2013-02-07 16:02:24 +0000
@@ -12,6 +12,7 @@
12import time12import time
13import utils13import utils
14import os14import os
15import apt_pkg as apt
1516
16LEADER = 'leader'17LEADER = 'leader'
17PEON = 'peon'18PEON = 'peon'
@@ -84,6 +85,12 @@
84 # Ignore any errors for this call85 # Ignore any errors for this call
85 subprocess.call(cmd)86 subprocess.call(cmd)
8687
88DISK_FORMATS = [
89 'xfs',
90 'ext4',
91 'btrfs'
92 ]
93
8794
88def is_osd_disk(dev):95def is_osd_disk(dev):
89 try:96 try:
@@ -108,6 +115,11 @@
108 subprocess.call(cmd)115 subprocess.call(cmd)
109116
110117
118def zap_disk(dev):
119 cmd = ['sgdisk', '--zap-all', dev]
120 subprocess.check_call(cmd)
121
122
111_bootstrap_keyring = "/var/lib/ceph/bootstrap-osd/ceph.keyring"123_bootstrap_keyring = "/var/lib/ceph/bootstrap-osd/ceph.keyring"
112124
113125
@@ -207,3 +219,17 @@
207 if 'key' in element:219 if 'key' in element:
208 key = element.split(' = ')[1].strip() # IGNORE:E1103220 key = element.split(' = ')[1].strip() # IGNORE:E1103
209 return key221 return key
222
223
224def get_ceph_version():
225 apt.init()
226 cache = apt.Cache()
227 pkg = cache['ceph']
228 if pkg.current_ver:
229 return apt.upstream_version(pkg.current_ver.ver_str)
230 else:
231 return None
232
233
234def version_compare(a, b):
235 return apt.version_compare(a, b)
210236
=== modified file 'hooks/hooks.py'
--- hooks/hooks.py 2012-11-12 10:00:27 +0000
+++ hooks/hooks.py 2013-02-07 16:02:24 +0000
@@ -19,14 +19,16 @@
1919
2020
21def install_upstart_scripts():21def install_upstart_scripts():
22 for x in glob.glob('files/upstart/*.conf'):22 # Only install upstart configurations for older versions
23 shutil.copy(x, '/etc/init/')23 if ceph.get_ceph_version() < "0.55.1":
24 for x in glob.glob('files/upstart/*.conf'):
25 shutil.copy(x, '/etc/init/')
2426
2527
26def install():28def install():
27 utils.juju_log('INFO', 'Begin install hook.')29 utils.juju_log('INFO', 'Begin install hook.')
28 utils.configure_source()30 utils.configure_source()
29 utils.install('ceph', 'gdisk', 'ntp')31 utils.install('ceph', 'gdisk', 'ntp', 'btrfs-tools')
30 install_upstart_scripts()32 install_upstart_scripts()
31 utils.juju_log('INFO', 'End install hook.')33 utils.juju_log('INFO', 'End install hook.')
3234
@@ -36,35 +38,48 @@
36 'auth_supported': utils.config_get('auth-supported'),38 'auth_supported': utils.config_get('auth-supported'),
37 'mon_hosts': ' '.join(get_mon_hosts()),39 'mon_hosts': ' '.join(get_mon_hosts()),
38 'fsid': utils.config_get('fsid'),40 'fsid': utils.config_get('fsid'),
41 'version': ceph.get_ceph_version()
39 }42 }
4043
41 with open('/etc/ceph/ceph.conf', 'w') as cephconf:44 with open('/etc/ceph/ceph.conf', 'w') as cephconf:
42 cephconf.write(utils.render_template('ceph.conf', cephcontext))45 cephconf.write(utils.render_template('ceph.conf', cephcontext))
4346
47JOURNAL_ZAPPED = '/var/lib/ceph/journal_zapped'
48
4449
45def config_changed():50def config_changed():
46 utils.juju_log('INFO', 'Begin config-changed hook.')51 utils.juju_log('INFO', 'Begin config-changed hook.')
4752
48 utils.juju_log('INFO', 'Monitor hosts are ' + repr(get_mon_hosts()))53 utils.juju_log('INFO', 'Monitor hosts are ' + repr(get_mon_hosts()))
4954
50 fsid = utils.config_get('fsid')55 # Pre-flight checks
51 if fsid == '':56 if not utils.config_get('fsid'):
52 utils.juju_log('CRITICAL', 'No fsid supplied, cannot proceed.')57 utils.juju_log('CRITICAL', 'No fsid supplied, cannot proceed.')
53 sys.exit(1)58 sys.exit(1)
5459 if not utils.config_get('monitor-secret'):
55 monitor_secret = utils.config_get('monitor-secret')
56 if monitor_secret == '':
57 utils.juju_log('CRITICAL',60 utils.juju_log('CRITICAL',
58 'No monitor-secret supplied, cannot proceed.')61 'No monitor-secret supplied, cannot proceed.')
59 sys.exit(1)62 sys.exit(1)
63 if utils.config_get('osd-format') not in ceph.DISK_FORMATS:
64 utils.juju_log('CRITICAL',
65 'Invalid OSD disk format configuration specified')
66 sys.exit(1)
6067
61 emit_cephconf()68 emit_cephconf()
6269
63 e_mountpoint = utils.config_get('ephemeral-unmount')70 e_mountpoint = utils.config_get('ephemeral-unmount')
64 if (e_mountpoint != "" and71 if (e_mountpoint and
65 filesystem_mounted(e_mountpoint)):72 filesystem_mounted(e_mountpoint)):
66 subprocess.call(['umount', e_mountpoint])73 subprocess.call(['umount', e_mountpoint])
6774
75 osd_journal = utils.config_get('osd-journal')
76 if (osd_journal and
77 not os.path.exists(JOURNAL_ZAPPED) and
78 os.path.exists(osd_journal)):
79 ceph.zap_disk(osd_journal)
80 with open(JOURNAL_ZAPPED, 'w') as zapped:
81 zapped.write('DONE')
82
68 for dev in utils.config_get('osd-devices').split(' '):83 for dev in utils.config_get('osd-devices').split(' '):
69 osdize(dev)84 osdize(dev)
7085
@@ -96,9 +111,22 @@
96 return hosts111 return hosts
97112
98113
114def update_monfs():
115 hostname = utils.get_unit_hostname()
116 monfs = '/var/lib/ceph/mon/ceph-{}'.format(hostname)
117 upstart = '{}/upstart'.format(monfs)
118 if (os.path.exists(monfs) and
119 not os.path.exists(upstart)):
120 # Mark mon as managed by upstart so that
121 # it gets start correctly on reboots
122 with open(upstart, 'w'):
123 pass
124
125
99def bootstrap_monitor_cluster():126def bootstrap_monitor_cluster():
100 hostname = utils.get_unit_hostname()127 hostname = utils.get_unit_hostname()
101 done = '/var/lib/ceph/mon/ceph-{}/done'.format(hostname)128 done = '/var/lib/ceph/mon/ceph-{}/done'.format(hostname)
129 upstart = '/var/lib/ceph/mon/ceph-{}/upstart'.format(hostname)
102 secret = utils.config_get('monitor-secret')130 secret = utils.config_get('monitor-secret')
103 keyring = '/var/lib/ceph/tmp/{}.mon.keyring'.format(hostname)131 keyring = '/var/lib/ceph/tmp/{}.mon.keyring'.format(hostname)
104132
@@ -118,6 +146,8 @@
118146
119 with open(done, 'w'):147 with open(done, 'w'):
120 pass148 pass
149 with open(upstart, 'w'):
150 pass
121151
122 subprocess.check_call(['start', 'ceph-mon-all-starter'])152 subprocess.check_call(['start', 'ceph-mon-all-starter'])
123 except:153 except:
@@ -127,7 +157,7 @@
127157
128158
129def reformat_osd():159def reformat_osd():
130 if utils.config_get('osd-reformat') != "":160 if utils.config_get('osd-reformat'):
131 return True161 return True
132 else:162 else:
133 return False163 return False
@@ -151,7 +181,23 @@
151 'Looks like {} is in use, skipping.'.format(dev))181 'Looks like {} is in use, skipping.'.format(dev))
152 return182 return
153183
154 subprocess.call(['ceph-disk-prepare', dev])184 cmd = ['ceph-disk-prepare']
185 # Later versions of ceph support more options
186 if ceph.get_ceph_version() >= "0.55":
187 osd_format = utils.config_get('osd-format')
188 if osd_format:
189 cmd.append('--fs-type')
190 cmd.append(osd_format)
191 cmd.append(dev)
192 osd_journal = utils.config_get('osd-journal')
193 if (osd_journal and
194 os.path.exists(osd_journal)):
195 cmd.append(osd_journal)
196 else:
197 # Just provide the device - no other options
198 # for older versions of ceph
199 cmd.append(dev)
200 subprocess.call(cmd)
155201
156202
157def device_mounted(dev):203def device_mounted(dev):
@@ -272,6 +318,7 @@
272 utils.juju_log('INFO', 'Begin upgrade-charm hook.')318 utils.juju_log('INFO', 'Begin upgrade-charm hook.')
273 emit_cephconf()319 emit_cephconf()
274 install_upstart_scripts()320 install_upstart_scripts()
321 update_monfs()
275 utils.juju_log('INFO', 'End upgrade-charm hook.')322 utils.juju_log('INFO', 'End upgrade-charm hook.')
276323
277324
278325
=== modified file 'hooks/utils.py'
--- hooks/utils.py 2012-11-12 09:32:06 +0000
+++ hooks/utils.py 2013-02-07 16:02:24 +0000
@@ -11,16 +11,19 @@
11import subprocess11import subprocess
12import socket12import socket
13import sys13import sys
14import re
1415
1516
16def do_hooks(hooks):17def do_hooks(hooks):
17 hook = os.path.basename(sys.argv[0])18 hook = os.path.basename(sys.argv[0])
1819
19 try:20 try:
20 hooks[hook]()21 hook_func = hooks[hook]
21 except KeyError:22 except KeyError:
22 juju_log('INFO',23 juju_log('INFO',
23 "This charm doesn't know how to handle '{}'.".format(hook))24 "This charm doesn't know how to handle '{}'.".format(hook))
25 else:
26 hook_func()
2427
2528
26def install(*pkgs):29def install(*pkgs):
@@ -41,6 +44,12 @@
41 install('python-jinja2')44 install('python-jinja2')
42 import jinja245 import jinja2
4346
47try:
48 import dns.resolver
49except ImportError:
50 install('python-dnspython')
51 import dns.resolver
52
4453
45def render_template(template_name, context, template_dir=TEMPLATES_DIR):54def render_template(template_name, context, template_dir=TEMPLATES_DIR):
46 templates = jinja2.Environment(55 templates = jinja2.Environment(
@@ -88,6 +97,18 @@
88 ]97 ]
89 subprocess.check_call(cmd)98 subprocess.check_call(cmd)
9099
100
101def enable_pocket(pocket):
102 apt_sources = "/etc/apt/sources.list"
103 with open(apt_sources, "r") as sources:
104 lines = sources.readlines()
105 with open(apt_sources, "w") as sources:
106 for line in lines:
107 if pocket in line:
108 sources.write(re.sub('^# deb', 'deb', line))
109 else:
110 sources.write(line)
111
91# Protocols112# Protocols
92TCP = 'TCP'113TCP = 'TCP'
93UDP = 'UDP'114UDP = 'UDP'
@@ -136,7 +157,11 @@
136 cmd.append(attribute)157 cmd.append(attribute)
137 if unit:158 if unit:
138 cmd.append(unit)159 cmd.append(unit)
139 return subprocess.check_output(cmd).strip() # IGNORE:E1103160 value = str(subprocess.check_output(cmd)).strip()
161 if value == "":
162 return None
163 else:
164 return value
140165
141166
142def relation_set(**kwargs):167def relation_set(**kwargs):
@@ -159,7 +184,11 @@
159 'unit-get',184 'unit-get',
160 attribute185 attribute
161 ]186 ]
162 return subprocess.check_output(cmd).strip() # IGNORE:E1103187 value = str(subprocess.check_output(cmd)).strip()
188 if value == "":
189 return None
190 else:
191 return value
163192
164193
165def config_get(attribute):194def config_get(attribute):
@@ -167,7 +196,11 @@
167 'config-get',196 'config-get',
168 attribute197 attribute
169 ]198 ]
170 return subprocess.check_output(cmd).strip() # IGNORE:E1103199 value = str(subprocess.check_output(cmd)).strip()
200 if value == "":
201 return None
202 else:
203 return value
171204
172205
173def get_unit_hostname():206def get_unit_hostname():
@@ -175,9 +208,13 @@
175208
176209
177def get_host_ip(hostname=unit_get('private-address')):210def get_host_ip(hostname=unit_get('private-address')):
178 cmd = [211 try:
179 'dig',212 # Test to see if already an IPv4 address
180 '+short',213 socket.inet_aton(hostname)
181 hostname214 return hostname
182 ]215 except socket.error:
183 return subprocess.check_output(cmd).strip() # IGNORE:E1103216 # This may throw an NXDOMAIN exception; in which case
217 # things are badly broken so just let it kill the hook
218 answers = dns.resolver.query(hostname, 'A')
219 if answers:
220 return answers[0].address
184221
=== modified file 'revision'
--- revision 2012-11-12 10:00:27 +0000
+++ revision 2013-02-07 16:02:24 +0000
@@ -1,1 +1,1 @@
188191
22
=== modified file 'templates/ceph.conf'
--- templates/ceph.conf 2012-10-18 06:19:52 +0000
+++ templates/ceph.conf 2013-02-07 16:02:24 +0000
@@ -1,5 +1,11 @@
1[global]1[global]
2{% if version < "0.51" %}
2 auth supported = {{ auth_supported }}3 auth supported = {{ auth_supported }}
4{% else %}
5 auth cluster required = {{ auth_supported }}
6 auth service required = {{ auth_supported }}
7 auth client required = {{ auth_supported }}
8{% endif %}
3 keyring = /etc/ceph/$cluster.$name.keyring9 keyring = /etc/ceph/$cluster.$name.keyring
4 mon host = {{ mon_hosts }}10 mon host = {{ mon_hosts }}
5 fsid = {{ fsid }}11 fsid = {{ fsid }}

Subscribers

People subscribed via source and target branches

to all changes: