Merge lp:~james-page/charms/trusty/ceph-osd/network-splits into lp:~openstack-charmers-archive/charms/trusty/ceph-osd/next

Proposed by James Page
Status: Merged
Approved by: Edward Hope-Morley
Approved revision: 30
Merged at revision: 24
Proposed branch: lp:~james-page/charms/trusty/ceph-osd/network-splits
Merge into: lp:~openstack-charmers-archive/charms/trusty/ceph-osd/next
Diff against target: 335 lines (+190/-21)
6 files modified
charm-helpers-sync.yaml (+1/-0)
config.yaml (+10/-0)
hooks/ceph.py (+4/-3)
hooks/charmhelpers/contrib/network/ip.py (+156/-0)
hooks/hooks.py (+11/-18)
templates/ceph.conf (+8/-0)
To merge this branch: bzr merge lp:~james-page/charms/trusty/ceph-osd/network-splits
Reviewer Review Type Date Requested Status
James Page Needs Resubmitting
Edward Hope-Morley Needs Fixing
Review via email: mp+228267@code.launchpad.net

Description of the change

Add support for public and cluster network configuration.

To post a comment you must log in.
Revision history for this message
Edward Hope-Morley (hopem) wrote :

One minor issue (inline comment) but otherwise LGTM.

review: Needs Fixing
30. By James Page

Only directly zap the disk for older ceph versions

Revision history for this message
James Page (james-page) wrote :

Updated to only directly zap for older ceph versions.

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

LGTM

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charm-helpers-sync.yaml'
2--- charm-helpers-sync.yaml 2014-03-25 17:04:06 +0000
3+++ charm-helpers-sync.yaml 2014-07-28 14:50:58 +0000
4@@ -6,3 +6,4 @@
5 - contrib.storage.linux:
6 - utils
7 - contrib.openstack.alternatives
8+ - contrib.network.ip
9
10=== modified file 'config.yaml'
11--- config.yaml 2014-03-25 18:44:23 +0000
12+++ config.yaml 2014-07-28 14:50:58 +0000
13@@ -83,3 +83,13 @@
14 default: False
15 description: |
16 If set to True, supporting services will log to syslog.
17+ ceph-public-network:
18+ type: string
19+ description: |
20+ The IP address and netmask of the public (front-side) network (e.g.,
21+ 192.168.0.0/24)
22+ ceph-cluster-network:
23+ type: string
24+ description: |
25+ The IP address and netmask of the cluster (back-side) network (e.g.,
26+ 192.168.0.0/24)
27
28=== modified file 'hooks/ceph.py'
29--- hooks/ceph.py 2014-07-23 11:34:57 +0000
30+++ hooks/ceph.py 2014-07-28 14:50:58 +0000
31@@ -337,6 +337,8 @@
32 if osd_format:
33 cmd.append('--fs-type')
34 cmd.append(osd_format)
35+ if reformat_osd:
36+ cmd.append('--zap-disk')
37 cmd.append(dev)
38 if osd_journal and os.path.exists(osd_journal):
39 cmd.append(osd_journal)
40@@ -344,9 +346,8 @@
41 # Just provide the device - no other options
42 # for older versions of ceph
43 cmd.append(dev)
44-
45- if reformat_osd:
46- zap_disk(dev)
47+ if reformat_osd:
48+ zap_disk(dev)
49
50 subprocess.check_call(cmd)
51
52
53=== added directory 'hooks/charmhelpers/contrib/network'
54=== added file 'hooks/charmhelpers/contrib/network/__init__.py'
55=== added file 'hooks/charmhelpers/contrib/network/ip.py'
56--- hooks/charmhelpers/contrib/network/ip.py 1970-01-01 00:00:00 +0000
57+++ hooks/charmhelpers/contrib/network/ip.py 2014-07-28 14:50:58 +0000
58@@ -0,0 +1,156 @@
59+import sys
60+
61+from functools import partial
62+
63+from charmhelpers.fetch import apt_install
64+from charmhelpers.core.hookenv import (
65+ ERROR, log,
66+)
67+
68+try:
69+ import netifaces
70+except ImportError:
71+ apt_install('python-netifaces')
72+ import netifaces
73+
74+try:
75+ import netaddr
76+except ImportError:
77+ apt_install('python-netaddr')
78+ import netaddr
79+
80+
81+def _validate_cidr(network):
82+ try:
83+ netaddr.IPNetwork(network)
84+ except (netaddr.core.AddrFormatError, ValueError):
85+ raise ValueError("Network (%s) is not in CIDR presentation format" %
86+ network)
87+
88+
89+def get_address_in_network(network, fallback=None, fatal=False):
90+ """
91+ Get an IPv4 or IPv6 address within the network from the host.
92+
93+ :param network (str): CIDR presentation format. For example,
94+ '192.168.1.0/24'.
95+ :param fallback (str): If no address is found, return fallback.
96+ :param fatal (boolean): If no address is found, fallback is not
97+ set and fatal is True then exit(1).
98+
99+ """
100+
101+ def not_found_error_out():
102+ log("No IP address found in network: %s" % network,
103+ level=ERROR)
104+ sys.exit(1)
105+
106+ if network is None:
107+ if fallback is not None:
108+ return fallback
109+ else:
110+ if fatal:
111+ not_found_error_out()
112+
113+ _validate_cidr(network)
114+ network = netaddr.IPNetwork(network)
115+ for iface in netifaces.interfaces():
116+ addresses = netifaces.ifaddresses(iface)
117+ if network.version == 4 and netifaces.AF_INET in addresses:
118+ addr = addresses[netifaces.AF_INET][0]['addr']
119+ netmask = addresses[netifaces.AF_INET][0]['netmask']
120+ cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
121+ if cidr in network:
122+ return str(cidr.ip)
123+ if network.version == 6 and netifaces.AF_INET6 in addresses:
124+ for addr in addresses[netifaces.AF_INET6]:
125+ if not addr['addr'].startswith('fe80'):
126+ cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
127+ addr['netmask']))
128+ if cidr in network:
129+ return str(cidr.ip)
130+
131+ if fallback is not None:
132+ return fallback
133+
134+ if fatal:
135+ not_found_error_out()
136+
137+ return None
138+
139+
140+def is_ipv6(address):
141+ '''Determine whether provided address is IPv6 or not'''
142+ try:
143+ address = netaddr.IPAddress(address)
144+ except netaddr.AddrFormatError:
145+ # probably a hostname - so not an address at all!
146+ return False
147+ else:
148+ return address.version == 6
149+
150+
151+def is_address_in_network(network, address):
152+ """
153+ Determine whether the provided address is within a network range.
154+
155+ :param network (str): CIDR presentation format. For example,
156+ '192.168.1.0/24'.
157+ :param address: An individual IPv4 or IPv6 address without a net
158+ mask or subnet prefix. For example, '192.168.1.1'.
159+ :returns boolean: Flag indicating whether address is in network.
160+ """
161+ try:
162+ network = netaddr.IPNetwork(network)
163+ except (netaddr.core.AddrFormatError, ValueError):
164+ raise ValueError("Network (%s) is not in CIDR presentation format" %
165+ network)
166+ try:
167+ address = netaddr.IPAddress(address)
168+ except (netaddr.core.AddrFormatError, ValueError):
169+ raise ValueError("Address (%s) is not in correct presentation format" %
170+ address)
171+ if address in network:
172+ return True
173+ else:
174+ return False
175+
176+
177+def _get_for_address(address, key):
178+ """Retrieve an attribute of or the physical interface that
179+ the IP address provided could be bound to.
180+
181+ :param address (str): An individual IPv4 or IPv6 address without a net
182+ mask or subnet prefix. For example, '192.168.1.1'.
183+ :param key: 'iface' for the physical interface name or an attribute
184+ of the configured interface, for example 'netmask'.
185+ :returns str: Requested attribute or None if address is not bindable.
186+ """
187+ address = netaddr.IPAddress(address)
188+ for iface in netifaces.interfaces():
189+ addresses = netifaces.ifaddresses(iface)
190+ if address.version == 4 and netifaces.AF_INET in addresses:
191+ addr = addresses[netifaces.AF_INET][0]['addr']
192+ netmask = addresses[netifaces.AF_INET][0]['netmask']
193+ cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
194+ if address in cidr:
195+ if key == 'iface':
196+ return iface
197+ else:
198+ return addresses[netifaces.AF_INET][0][key]
199+ if address.version == 6 and netifaces.AF_INET6 in addresses:
200+ for addr in addresses[netifaces.AF_INET6]:
201+ if not addr['addr'].startswith('fe80'):
202+ cidr = netaddr.IPNetwork("%s/%s" % (addr['addr'],
203+ addr['netmask']))
204+ if address in cidr:
205+ if key == 'iface':
206+ return iface
207+ else:
208+ return addr[key]
209+ return None
210+
211+
212+get_iface_for_address = partial(_get_for_address, key='iface')
213+
214+get_netmask_for_address = partial(_get_for_address, key='netmask')
215
216=== modified file 'hooks/hooks.py'
217--- hooks/hooks.py 2014-07-23 11:34:57 +0000
218+++ hooks/hooks.py 2014-07-28 14:50:58 +0000
219@@ -42,6 +42,7 @@
220 )
221
222 from charmhelpers.contrib.openstack.alternatives import install_alternative
223+from charmhelpers.contrib.network.ip import is_ipv6
224
225 hooks = Hooks()
226
227@@ -55,12 +56,10 @@
228
229 @hooks.hook('install')
230 def install():
231- log('Begin install hook.')
232 add_source(config('source'), config('key'))
233 apt_update(fatal=True)
234 apt_install(packages=ceph.PACKAGES, fatal=True)
235 install_upstart_scripts()
236- log('End install hook.')
237
238
239 def emit_cephconf():
240@@ -73,7 +72,9 @@
241 'fsid': get_fsid(),
242 'old_auth': cmp_pkgrevno('ceph', "0.51") < 0,
243 'osd_journal_size': config('osd-journal-size'),
244- 'use_syslog': str(config('use-syslog')).lower()
245+ 'use_syslog': str(config('use-syslog')).lower(),
246+ 'ceph_public_network': config('ceph-public-network'),
247+ 'ceph_cluster_network': config('ceph-cluster-network'),
248 }
249 # Install ceph.conf as an alternative to support
250 # co-existence with other charms that write this file
251@@ -89,8 +90,6 @@
252
253 @hooks.hook('config-changed')
254 def config_changed():
255- log('Begin config-changed hook.')
256-
257 # Pre-flight checks
258 if config('osd-format') not in ceph.DISK_FORMATS:
259 log('Invalid OSD disk format configuration specified', level=ERROR)
260@@ -115,18 +114,18 @@
261 config('osd-journal'), config('osd-reformat'))
262 ceph.start_osds(get_devices())
263
264- log('End config-changed hook.')
265-
266
267 def get_mon_hosts():
268 hosts = []
269 for relid in relation_ids('mon'):
270 for unit in related_units(relid):
271- hosts.append(
272- '{}:6789'.format(get_host_ip(relation_get('private-address',
273- unit, relid)))
274- )
275-
276+ addr = relation_get('ceph-public-address', unit, relid) or \
277+ get_host_ip(relation_get('private-address', unit, relid))
278+ if addr is not None:
279+ if is_ipv6(addr):
280+ hosts.append('[{}]:6789'.format(addr))
281+ else:
282+ hosts.append('{}:6789'.format(addr))
283 hosts.sort()
284 return hosts
285
286@@ -166,8 +165,6 @@
287 @hooks.hook('mon-relation-changed',
288 'mon-relation-departed')
289 def mon_relation():
290- log('Begin mon-relation hook.')
291-
292 bootstrap_key = relation_get('osd_bootstrap_key')
293 if get_fsid() and get_auth() and bootstrap_key:
294 log('mon has provided conf- scanning disks')
295@@ -180,18 +177,14 @@
296 else:
297 log('mon cluster has not yet provided conf')
298
299- log('End mon-relation hook.')
300-
301
302 @hooks.hook('upgrade-charm')
303 def upgrade_charm():
304- log('Begin upgrade-charm hook.')
305 if get_fsid() and get_auth():
306 emit_cephconf()
307 install_upstart_scripts()
308 apt_install(packages=filter_installed_packages(ceph.PACKAGES),
309 fatal=True)
310- log('End upgrade-charm hook.')
311
312
313 if __name__ == '__main__':
314
315=== modified file 'templates/ceph.conf'
316--- templates/ceph.conf 2014-07-23 11:34:57 +0000
317+++ templates/ceph.conf 2014-07-28 14:50:58 +0000
318@@ -9,10 +9,18 @@
319 keyring = /etc/ceph/$cluster.$name.keyring
320 mon host = {{ mon_hosts }}
321 fsid = {{ fsid }}
322+
323 log to syslog = {{ use_syslog }}
324 err to syslog = {{ use_syslog }}
325 clog to syslog = {{ use_syslog }}
326
327+{%- if ceph_public_network is string %}
328+ public network = {{ ceph_public_network }}
329+{%- endif %}
330+{%- if ceph_cluster_network is string %}
331+ cluster network = {{ ceph_cluster_network }}
332+{%- endif %}
333+
334 [mon]
335 keyring = /var/lib/ceph/mon/$cluster-$id/keyring
336

Subscribers

People subscribed via source and target branches