Merge lp:~hopem/charm-helpers/lp1522375 into lp:charm-helpers
- lp1522375
- Merge into devel
Proposed by
Edward Hope-Morley
Status: | Merged |
---|---|
Merged at revision: | 577 |
Proposed branch: | lp:~hopem/charm-helpers/lp1522375 |
Merge into: | lp:charm-helpers |
Diff against target: |
321 lines (+143/-83) 5 files modified
charmhelpers/contrib/openstack/context.py (+5/-83) charmhelpers/contrib/openstack/exceptions.py (+6/-0) charmhelpers/contrib/openstack/utils.py (+81/-0) charmhelpers/contrib/storage/linux/ceph.py (+41/-0) tests/contrib/storage/test_linux_ceph.py (+10/-0) |
To merge this branch: | bzr merge lp:~hopem/charm-helpers/lp1522375 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Page | Approve | ||
Review via email: mp+295612@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
James Page (james-page) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'charmhelpers/contrib/openstack/context.py' |
2 | --- charmhelpers/contrib/openstack/context.py 2016-04-04 18:06:59 +0000 |
3 | +++ charmhelpers/contrib/openstack/context.py 2016-05-25 12:22:56 +0000 |
4 | @@ -23,7 +23,6 @@ |
5 | from subprocess import check_call, CalledProcessError |
6 | |
7 | import six |
8 | -import yaml |
9 | |
10 | from charmhelpers.fetch import ( |
11 | apt_install, |
12 | @@ -50,6 +49,7 @@ |
13 | |
14 | from charmhelpers.core.sysctl import create as sysctl_create |
15 | from charmhelpers.core.strutils import bool_from_string |
16 | +from charmhelpers.contrib.openstack.exceptions import OSContextError |
17 | |
18 | from charmhelpers.core.host import ( |
19 | get_bond_master, |
20 | @@ -88,7 +88,10 @@ |
21 | is_address_in_network, |
22 | is_bridge_member, |
23 | ) |
24 | -from charmhelpers.contrib.openstack.utils import get_host_ip |
25 | +from charmhelpers.contrib.openstack.utils import ( |
26 | + config_flags_parser, |
27 | + get_host_ip, |
28 | +) |
29 | from charmhelpers.core.unitdata import kv |
30 | |
31 | try: |
32 | @@ -101,10 +104,6 @@ |
33 | ADDRESS_TYPES = ['admin', 'internal', 'public'] |
34 | |
35 | |
36 | -class OSContextError(Exception): |
37 | - pass |
38 | - |
39 | - |
40 | def ensure_packages(packages): |
41 | """Install but do not upgrade required plugin packages.""" |
42 | required = filter_installed_packages(packages) |
43 | @@ -125,83 +124,6 @@ |
44 | return True |
45 | |
46 | |
47 | -def config_flags_parser(config_flags): |
48 | - """Parses config flags string into dict. |
49 | - |
50 | - This parsing method supports a few different formats for the config |
51 | - flag values to be parsed: |
52 | - |
53 | - 1. A string in the simple format of key=value pairs, with the possibility |
54 | - of specifying multiple key value pairs within the same string. For |
55 | - example, a string in the format of 'key1=value1, key2=value2' will |
56 | - return a dict of: |
57 | - |
58 | - {'key1': 'value1', |
59 | - 'key2': 'value2'}. |
60 | - |
61 | - 2. A string in the above format, but supporting a comma-delimited list |
62 | - of values for the same key. For example, a string in the format of |
63 | - 'key1=value1, key2=value3,value4,value5' will return a dict of: |
64 | - |
65 | - {'key1', 'value1', |
66 | - 'key2', 'value2,value3,value4'} |
67 | - |
68 | - 3. A string containing a colon character (:) prior to an equal |
69 | - character (=) will be treated as yaml and parsed as such. This can be |
70 | - used to specify more complex key value pairs. For example, |
71 | - a string in the format of 'key1: subkey1=value1, subkey2=value2' will |
72 | - return a dict of: |
73 | - |
74 | - {'key1', 'subkey1=value1, subkey2=value2'} |
75 | - |
76 | - The provided config_flags string may be a list of comma-separated values |
77 | - which themselves may be comma-separated list of values. |
78 | - """ |
79 | - # If we find a colon before an equals sign then treat it as yaml. |
80 | - # Note: limit it to finding the colon first since this indicates assignment |
81 | - # for inline yaml. |
82 | - colon = config_flags.find(':') |
83 | - equals = config_flags.find('=') |
84 | - if colon > 0: |
85 | - if colon < equals or equals < 0: |
86 | - return yaml.safe_load(config_flags) |
87 | - |
88 | - if config_flags.find('==') >= 0: |
89 | - log("config_flags is not in expected format (key=value)", level=ERROR) |
90 | - raise OSContextError |
91 | - |
92 | - # strip the following from each value. |
93 | - post_strippers = ' ,' |
94 | - # we strip any leading/trailing '=' or ' ' from the string then |
95 | - # split on '='. |
96 | - split = config_flags.strip(' =').split('=') |
97 | - limit = len(split) |
98 | - flags = {} |
99 | - for i in range(0, limit - 1): |
100 | - current = split[i] |
101 | - next = split[i + 1] |
102 | - vindex = next.rfind(',') |
103 | - if (i == limit - 2) or (vindex < 0): |
104 | - value = next |
105 | - else: |
106 | - value = next[:vindex] |
107 | - |
108 | - if i == 0: |
109 | - key = current |
110 | - else: |
111 | - # if this not the first entry, expect an embedded key. |
112 | - index = current.rfind(',') |
113 | - if index < 0: |
114 | - log("Invalid config value(s) at index %s" % (i), level=ERROR) |
115 | - raise OSContextError |
116 | - key = current[index + 1:] |
117 | - |
118 | - # Add to collection. |
119 | - flags[key.strip(post_strippers)] = value.rstrip(post_strippers) |
120 | - |
121 | - return flags |
122 | - |
123 | - |
124 | class OSContextGenerator(object): |
125 | """Base class for all context generators.""" |
126 | interfaces = [] |
127 | |
128 | === added file 'charmhelpers/contrib/openstack/exceptions.py' |
129 | --- charmhelpers/contrib/openstack/exceptions.py 1970-01-01 00:00:00 +0000 |
130 | +++ charmhelpers/contrib/openstack/exceptions.py 2016-05-25 12:22:56 +0000 |
131 | @@ -0,0 +1,6 @@ |
132 | +class OSContextError(Exception): |
133 | + """Raised when an error occurs during context generation. |
134 | + |
135 | + This exception is principally used in contrib.openstack.context |
136 | + """ |
137 | + pass |
138 | |
139 | === modified file 'charmhelpers/contrib/openstack/utils.py' |
140 | --- charmhelpers/contrib/openstack/utils.py 2016-05-20 18:40:40 +0000 |
141 | +++ charmhelpers/contrib/openstack/utils.py 2016-05-25 12:22:56 +0000 |
142 | @@ -47,6 +47,7 @@ |
143 | charm_dir, |
144 | DEBUG, |
145 | INFO, |
146 | + ERROR, |
147 | related_units, |
148 | relation_ids, |
149 | relation_set, |
150 | @@ -83,6 +84,7 @@ |
151 | from charmhelpers.fetch import apt_install, apt_cache, install_remote |
152 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk |
153 | from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device |
154 | +from charmhelpers.contrib.openstack.exceptions import OSContextError |
155 | |
156 | CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" |
157 | CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' |
158 | @@ -1616,3 +1618,82 @@ |
159 | restart_functions) |
160 | return wrapped_f |
161 | return wrap |
162 | + |
163 | + |
164 | +def config_flags_parser(config_flags): |
165 | + """Parses config flags string into dict. |
166 | + |
167 | + This parsing method supports a few different formats for the config |
168 | + flag values to be parsed: |
169 | + |
170 | + 1. A string in the simple format of key=value pairs, with the possibility |
171 | + of specifying multiple key value pairs within the same string. For |
172 | + example, a string in the format of 'key1=value1, key2=value2' will |
173 | + return a dict of: |
174 | + |
175 | + {'key1': 'value1', |
176 | + 'key2': 'value2'}. |
177 | + |
178 | + 2. A string in the above format, but supporting a comma-delimited list |
179 | + of values for the same key. For example, a string in the format of |
180 | + 'key1=value1, key2=value3,value4,value5' will return a dict of: |
181 | + |
182 | + {'key1', 'value1', |
183 | + 'key2', 'value2,value3,value4'} |
184 | + |
185 | + 3. A string containing a colon character (:) prior to an equal |
186 | + character (=) will be treated as yaml and parsed as such. This can be |
187 | + used to specify more complex key value pairs. For example, |
188 | + a string in the format of 'key1: subkey1=value1, subkey2=value2' will |
189 | + return a dict of: |
190 | + |
191 | + {'key1', 'subkey1=value1, subkey2=value2'} |
192 | + |
193 | + The provided config_flags string may be a list of comma-separated values |
194 | + which themselves may be comma-separated list of values. |
195 | + """ |
196 | + # If we find a colon before an equals sign then treat it as yaml. |
197 | + # Note: limit it to finding the colon first since this indicates assignment |
198 | + # for inline yaml. |
199 | + colon = config_flags.find(':') |
200 | + equals = config_flags.find('=') |
201 | + if colon > 0: |
202 | + if colon < equals or equals < 0: |
203 | + return yaml.safe_load(config_flags) |
204 | + |
205 | + if config_flags.find('==') >= 0: |
206 | + juju_log("config_flags is not in expected format (key=value)", |
207 | + level=ERROR) |
208 | + raise OSContextError |
209 | + |
210 | + # strip the following from each value. |
211 | + post_strippers = ' ,' |
212 | + # we strip any leading/trailing '=' or ' ' from the string then |
213 | + # split on '='. |
214 | + split = config_flags.strip(' =').split('=') |
215 | + limit = len(split) |
216 | + flags = {} |
217 | + for i in range(0, limit - 1): |
218 | + current = split[i] |
219 | + next = split[i + 1] |
220 | + vindex = next.rfind(',') |
221 | + if (i == limit - 2) or (vindex < 0): |
222 | + value = next |
223 | + else: |
224 | + value = next[:vindex] |
225 | + |
226 | + if i == 0: |
227 | + key = current |
228 | + else: |
229 | + # if this not the first entry, expect an embedded key. |
230 | + index = current.rfind(',') |
231 | + if index < 0: |
232 | + juju_log("Invalid config value(s) at index %s" % (i), |
233 | + level=ERROR) |
234 | + raise OSContextError |
235 | + key = current[index + 1:] |
236 | + |
237 | + # Add to collection. |
238 | + flags[key.strip(post_strippers)] = value.rstrip(post_strippers) |
239 | + |
240 | + return flags |
241 | |
242 | === modified file 'charmhelpers/contrib/storage/linux/ceph.py' |
243 | --- charmhelpers/contrib/storage/linux/ceph.py 2016-04-20 10:51:23 +0000 |
244 | +++ charmhelpers/contrib/storage/linux/ceph.py 2016-05-25 12:22:56 +0000 |
245 | @@ -40,6 +40,7 @@ |
246 | CalledProcessError, |
247 | ) |
248 | from charmhelpers.core.hookenv import ( |
249 | + config, |
250 | local_unit, |
251 | relation_get, |
252 | relation_ids, |
253 | @@ -64,6 +65,7 @@ |
254 | ) |
255 | |
256 | from charmhelpers.core.kernel import modprobe |
257 | +from charmhelpers.contrib.openstack.utils import config_flags_parser |
258 | |
259 | KEYRING = '/etc/ceph/ceph.client.{}.keyring' |
260 | KEYFILE = '/etc/ceph/ceph.client.{}.key' |
261 | @@ -1204,3 +1206,42 @@ |
262 | for rid in relation_ids(relation): |
263 | log('Sending request {}'.format(request.request_id), level=DEBUG) |
264 | relation_set(relation_id=rid, broker_req=request.request) |
265 | + |
266 | + |
267 | +class CephConfContext(object): |
268 | + """Ceph config (ceph.conf) context. |
269 | + |
270 | + Supports user-provided Ceph configuration settings. Use can provide a |
271 | + dictionary as the value for the config-flags charm option containing |
272 | + Ceph configuration settings keyede by their section in ceph.conf. |
273 | + """ |
274 | + def __init__(self, permitted_sections=None): |
275 | + self.permitted_sections = permitted_sections or [] |
276 | + |
277 | + def __call__(self): |
278 | + conf = config('config-flags') |
279 | + if not conf: |
280 | + return {} |
281 | + |
282 | + conf = config_flags_parser(conf) |
283 | + if type(conf) != dict: |
284 | + log("Provided config-flags is not a dictionary - ignoring", |
285 | + level=WARNING) |
286 | + return {} |
287 | + |
288 | + permitted = self.permitted_sections |
289 | + if permitted: |
290 | + diff = set(conf.keys()).symmetric_difference(set(permitted)) |
291 | + if diff: |
292 | + log("Config-flags contains invalid keys '%s' - they will be " |
293 | + "ignored" % (', '.join(diff)), level=WARNING) |
294 | + |
295 | + ceph_conf = {} |
296 | + for key in conf: |
297 | + if permitted and key not in permitted: |
298 | + log("Ignoring key '%s'" % key, level=WARNING) |
299 | + continue |
300 | + |
301 | + ceph_conf[key] = conf[key] |
302 | + |
303 | + return ceph_conf |
304 | |
305 | === modified file 'tests/contrib/storage/test_linux_ceph.py' |
306 | --- tests/contrib/storage/test_linux_ceph.py 2016-04-20 10:51:23 +0000 |
307 | +++ tests/contrib/storage/test_linux_ceph.py 2016-05-25 12:22:56 +0000 |
308 | @@ -1298,3 +1298,13 @@ |
309 | self.assertEqual(actual['ops'][0]['replicas'], 4) |
310 | self.assertEqual(actual['ops'][0]['op'], 'create-pool') |
311 | self.assertEqual(actual['ops'][0]['name'], 'glance') |
312 | + |
313 | + @patch.object(ceph_utils, 'config') |
314 | + def test_ceph_conf_context(self, mock_config): |
315 | + mock_config.return_value = "{'osd': {'foo': 1}}" |
316 | + ctxt = ceph_utils.CephConfContext()() |
317 | + self.assertEqual({'osd': {'foo': 1}}, ctxt) |
318 | + ctxt = ceph_utils.CephConfContext(['osd', 'mon'])() |
319 | + mock_config.return_value = ("{'osd': {'foo': 1}," |
320 | + "'unknown': {'blah': 1}}") |
321 | + self.assertEqual({'osd': {'foo': 1}}, ctxt) |