Merge lp:~junaidali/charms/trusty/neutron-api-plumgrid/resynced into lp:~plumgrid-team/charms/trusty/neutron-api-plumgrid/trunk
- Trusty Tahr (14.04)
- resynced
- Merge into trunk
Proposed by
Junaid Ali
Status: | Merged |
---|---|
Merged at revision: | 22 |
Proposed branch: | lp:~junaidali/charms/trusty/neutron-api-plumgrid/resynced |
Merge into: | lp:~plumgrid-team/charms/trusty/neutron-api-plumgrid/trunk |
Diff against target: |
740 lines (+336/-132) 7 files modified
hooks/charmhelpers/contrib/network/ip.py (+22/-5) hooks/charmhelpers/contrib/openstack/context.py (+5/-83) hooks/charmhelpers/contrib/openstack/exceptions.py (+6/-0) hooks/charmhelpers/contrib/openstack/utils.py (+184/-21) hooks/charmhelpers/contrib/storage/linux/ceph.py (+41/-0) hooks/charmhelpers/core/host.py (+70/-23) hooks/charmhelpers/fetch/__init__.py (+8/-0) |
To merge this branch: | bzr merge lp:~junaidali/charms/trusty/neutron-api-plumgrid/resynced |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Bilal Baqar | Pending | ||
Review via email: mp+296483@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 22. By Junaid Ali
-
Resynced charm-helpers
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'hooks/charmhelpers/contrib/network/ip.py' |
2 | --- hooks/charmhelpers/contrib/network/ip.py 2016-04-22 04:35:32 +0000 |
3 | +++ hooks/charmhelpers/contrib/network/ip.py 2016-06-04 02:32:00 +0000 |
4 | @@ -214,7 +214,16 @@ |
5 | |
6 | def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, |
7 | fatal=True, exc_list=None): |
8 | - """Return the assigned IP address for a given interface, if any.""" |
9 | + """Return the assigned IP address for a given interface, if any. |
10 | + |
11 | + :param iface: network interface on which address(es) are expected to |
12 | + be found. |
13 | + :param inet_type: inet address family |
14 | + :param inc_aliases: include alias interfaces in search |
15 | + :param fatal: if True, raise exception if address not found |
16 | + :param exc_list: list of addresses to ignore |
17 | + :return: list of ip addresses |
18 | + """ |
19 | # Extract nic if passed /dev/ethX |
20 | if '/' in iface: |
21 | iface = iface.split('/')[-1] |
22 | @@ -315,6 +324,14 @@ |
23 | We currently only support scope global IPv6 addresses i.e. non-temporary |
24 | addresses. If no global IPv6 address is found, return the first one found |
25 | in the ipv6 address list. |
26 | + |
27 | + :param iface: network interface on which ipv6 address(es) are expected to |
28 | + be found. |
29 | + :param inc_aliases: include alias interfaces in search |
30 | + :param fatal: if True, raise exception if address not found |
31 | + :param exc_list: list of addresses to ignore |
32 | + :param dynamic_only: only recognise dynamic addresses |
33 | + :return: list of ipv6 addresses |
34 | """ |
35 | addresses = get_iface_addr(iface=iface, inet_type='AF_INET6', |
36 | inc_aliases=inc_aliases, fatal=fatal, |
37 | @@ -336,7 +353,7 @@ |
38 | cmd = ['ip', 'addr', 'show', iface] |
39 | out = subprocess.check_output(cmd).decode('UTF-8') |
40 | if dynamic_only: |
41 | - key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*") |
42 | + key = re.compile("inet6 (.+)/[0-9]+ scope global.* dynamic.*") |
43 | else: |
44 | key = re.compile("inet6 (.+)/[0-9]+ scope global.*") |
45 | |
46 | @@ -388,10 +405,10 @@ |
47 | Returns True if address is a valid IP address. |
48 | """ |
49 | try: |
50 | - # Test to see if already an IPv4 address |
51 | - socket.inet_aton(address) |
52 | + # Test to see if already an IPv4/IPv6 address |
53 | + address = netaddr.IPAddress(address) |
54 | return True |
55 | - except socket.error: |
56 | + except netaddr.AddrFormatError: |
57 | return False |
58 | |
59 | |
60 | |
61 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' |
62 | --- hooks/charmhelpers/contrib/openstack/context.py 2016-04-22 04:35:32 +0000 |
63 | +++ hooks/charmhelpers/contrib/openstack/context.py 2016-06-04 02:32:00 +0000 |
64 | @@ -23,7 +23,6 @@ |
65 | from subprocess import check_call, CalledProcessError |
66 | |
67 | import six |
68 | -import yaml |
69 | |
70 | from charmhelpers.fetch import ( |
71 | apt_install, |
72 | @@ -50,6 +49,7 @@ |
73 | |
74 | from charmhelpers.core.sysctl import create as sysctl_create |
75 | from charmhelpers.core.strutils import bool_from_string |
76 | +from charmhelpers.contrib.openstack.exceptions import OSContextError |
77 | |
78 | from charmhelpers.core.host import ( |
79 | get_bond_master, |
80 | @@ -88,7 +88,10 @@ |
81 | is_address_in_network, |
82 | is_bridge_member, |
83 | ) |
84 | -from charmhelpers.contrib.openstack.utils import get_host_ip |
85 | +from charmhelpers.contrib.openstack.utils import ( |
86 | + config_flags_parser, |
87 | + get_host_ip, |
88 | +) |
89 | from charmhelpers.core.unitdata import kv |
90 | |
91 | try: |
92 | @@ -101,10 +104,6 @@ |
93 | ADDRESS_TYPES = ['admin', 'internal', 'public'] |
94 | |
95 | |
96 | -class OSContextError(Exception): |
97 | - pass |
98 | - |
99 | - |
100 | def ensure_packages(packages): |
101 | """Install but do not upgrade required plugin packages.""" |
102 | required = filter_installed_packages(packages) |
103 | @@ -125,83 +124,6 @@ |
104 | return True |
105 | |
106 | |
107 | -def config_flags_parser(config_flags): |
108 | - """Parses config flags string into dict. |
109 | - |
110 | - This parsing method supports a few different formats for the config |
111 | - flag values to be parsed: |
112 | - |
113 | - 1. A string in the simple format of key=value pairs, with the possibility |
114 | - of specifying multiple key value pairs within the same string. For |
115 | - example, a string in the format of 'key1=value1, key2=value2' will |
116 | - return a dict of: |
117 | - |
118 | - {'key1': 'value1', |
119 | - 'key2': 'value2'}. |
120 | - |
121 | - 2. A string in the above format, but supporting a comma-delimited list |
122 | - of values for the same key. For example, a string in the format of |
123 | - 'key1=value1, key2=value3,value4,value5' will return a dict of: |
124 | - |
125 | - {'key1', 'value1', |
126 | - 'key2', 'value2,value3,value4'} |
127 | - |
128 | - 3. A string containing a colon character (:) prior to an equal |
129 | - character (=) will be treated as yaml and parsed as such. This can be |
130 | - used to specify more complex key value pairs. For example, |
131 | - a string in the format of 'key1: subkey1=value1, subkey2=value2' will |
132 | - return a dict of: |
133 | - |
134 | - {'key1', 'subkey1=value1, subkey2=value2'} |
135 | - |
136 | - The provided config_flags string may be a list of comma-separated values |
137 | - which themselves may be comma-separated list of values. |
138 | - """ |
139 | - # If we find a colon before an equals sign then treat it as yaml. |
140 | - # Note: limit it to finding the colon first since this indicates assignment |
141 | - # for inline yaml. |
142 | - colon = config_flags.find(':') |
143 | - equals = config_flags.find('=') |
144 | - if colon > 0: |
145 | - if colon < equals or equals < 0: |
146 | - return yaml.safe_load(config_flags) |
147 | - |
148 | - if config_flags.find('==') >= 0: |
149 | - log("config_flags is not in expected format (key=value)", level=ERROR) |
150 | - raise OSContextError |
151 | - |
152 | - # strip the following from each value. |
153 | - post_strippers = ' ,' |
154 | - # we strip any leading/trailing '=' or ' ' from the string then |
155 | - # split on '='. |
156 | - split = config_flags.strip(' =').split('=') |
157 | - limit = len(split) |
158 | - flags = {} |
159 | - for i in range(0, limit - 1): |
160 | - current = split[i] |
161 | - next = split[i + 1] |
162 | - vindex = next.rfind(',') |
163 | - if (i == limit - 2) or (vindex < 0): |
164 | - value = next |
165 | - else: |
166 | - value = next[:vindex] |
167 | - |
168 | - if i == 0: |
169 | - key = current |
170 | - else: |
171 | - # if this not the first entry, expect an embedded key. |
172 | - index = current.rfind(',') |
173 | - if index < 0: |
174 | - log("Invalid config value(s) at index %s" % (i), level=ERROR) |
175 | - raise OSContextError |
176 | - key = current[index + 1:] |
177 | - |
178 | - # Add to collection. |
179 | - flags[key.strip(post_strippers)] = value.rstrip(post_strippers) |
180 | - |
181 | - return flags |
182 | - |
183 | - |
184 | class OSContextGenerator(object): |
185 | """Base class for all context generators.""" |
186 | interfaces = [] |
187 | |
188 | === added file 'hooks/charmhelpers/contrib/openstack/exceptions.py' |
189 | --- hooks/charmhelpers/contrib/openstack/exceptions.py 1970-01-01 00:00:00 +0000 |
190 | +++ hooks/charmhelpers/contrib/openstack/exceptions.py 2016-06-04 02:32:00 +0000 |
191 | @@ -0,0 +1,6 @@ |
192 | +class OSContextError(Exception): |
193 | + """Raised when an error occurs during context generation. |
194 | + |
195 | + This exception is principally used in contrib.openstack.context |
196 | + """ |
197 | + pass |
198 | |
199 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' |
200 | --- hooks/charmhelpers/contrib/openstack/utils.py 2016-04-22 04:35:32 +0000 |
201 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2016-06-04 02:32:00 +0000 |
202 | @@ -25,6 +25,7 @@ |
203 | import re |
204 | import itertools |
205 | import functools |
206 | +import shutil |
207 | |
208 | import six |
209 | import tempfile |
210 | @@ -46,6 +47,7 @@ |
211 | charm_dir, |
212 | DEBUG, |
213 | INFO, |
214 | + ERROR, |
215 | related_units, |
216 | relation_ids, |
217 | relation_set, |
218 | @@ -82,6 +84,7 @@ |
219 | from charmhelpers.fetch import apt_install, apt_cache, install_remote |
220 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk |
221 | from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device |
222 | +from charmhelpers.contrib.openstack.exceptions import OSContextError |
223 | |
224 | CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" |
225 | CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' |
226 | @@ -100,6 +103,8 @@ |
227 | ('vivid', 'kilo'), |
228 | ('wily', 'liberty'), |
229 | ('xenial', 'mitaka'), |
230 | + ('yakkety', 'newton'), |
231 | + ('zebra', 'ocata'), # TODO: upload with real Z name |
232 | ]) |
233 | |
234 | |
235 | @@ -114,6 +119,8 @@ |
236 | ('2015.1', 'kilo'), |
237 | ('2015.2', 'liberty'), |
238 | ('2016.1', 'mitaka'), |
239 | + ('2016.2', 'newton'), |
240 | + ('2017.1', 'ocata'), |
241 | ]) |
242 | |
243 | # The ugly duckling - must list releases oldest to newest |
244 | @@ -138,46 +145,65 @@ |
245 | ['2.3.0', '2.4.0', '2.5.0']), |
246 | ('mitaka', |
247 | ['2.5.0', '2.6.0', '2.7.0']), |
248 | + ('newton', |
249 | + ['2.8.0']), |
250 | ]) |
251 | |
252 | # >= Liberty version->codename mapping |
253 | PACKAGE_CODENAMES = { |
254 | 'nova-common': OrderedDict([ |
255 | - ('12.0', 'liberty'), |
256 | - ('13.0', 'mitaka'), |
257 | + ('12', 'liberty'), |
258 | + ('13', 'mitaka'), |
259 | + ('14', 'newton'), |
260 | + ('15', 'ocata'), |
261 | ]), |
262 | 'neutron-common': OrderedDict([ |
263 | - ('7.0', 'liberty'), |
264 | - ('8.0', 'mitaka'), |
265 | + ('7', 'liberty'), |
266 | + ('8', 'mitaka'), |
267 | + ('9', 'newton'), |
268 | + ('10', 'ocata'), |
269 | ]), |
270 | 'cinder-common': OrderedDict([ |
271 | - ('7.0', 'liberty'), |
272 | - ('8.0', 'mitaka'), |
273 | + ('7', 'liberty'), |
274 | + ('8', 'mitaka'), |
275 | + ('9', 'newton'), |
276 | + ('10', 'ocata'), |
277 | ]), |
278 | 'keystone': OrderedDict([ |
279 | - ('8.0', 'liberty'), |
280 | - ('8.1', 'liberty'), |
281 | - ('9.0', 'mitaka'), |
282 | + ('8', 'liberty'), |
283 | + ('9', 'mitaka'), |
284 | + ('10', 'newton'), |
285 | + ('11', 'ocata'), |
286 | ]), |
287 | 'horizon-common': OrderedDict([ |
288 | - ('8.0', 'liberty'), |
289 | - ('9.0', 'mitaka'), |
290 | + ('8', 'liberty'), |
291 | + ('9', 'mitaka'), |
292 | + ('10', 'newton'), |
293 | + ('11', 'ocata'), |
294 | ]), |
295 | 'ceilometer-common': OrderedDict([ |
296 | - ('5.0', 'liberty'), |
297 | - ('6.0', 'mitaka'), |
298 | + ('5', 'liberty'), |
299 | + ('6', 'mitaka'), |
300 | + ('7', 'newton'), |
301 | + ('8', 'ocata'), |
302 | ]), |
303 | 'heat-common': OrderedDict([ |
304 | - ('5.0', 'liberty'), |
305 | - ('6.0', 'mitaka'), |
306 | + ('5', 'liberty'), |
307 | + ('6', 'mitaka'), |
308 | + ('7', 'newton'), |
309 | + ('8', 'ocata'), |
310 | ]), |
311 | 'glance-common': OrderedDict([ |
312 | - ('11.0', 'liberty'), |
313 | - ('12.0', 'mitaka'), |
314 | + ('11', 'liberty'), |
315 | + ('12', 'mitaka'), |
316 | + ('13', 'newton'), |
317 | + ('14', 'ocata'), |
318 | ]), |
319 | 'openstack-dashboard': OrderedDict([ |
320 | - ('8.0', 'liberty'), |
321 | - ('9.0', 'mitaka'), |
322 | + ('8', 'liberty'), |
323 | + ('9', 'mitaka'), |
324 | + ('10', 'newton'), |
325 | + ('11', 'ocata'), |
326 | ]), |
327 | } |
328 | |
329 | @@ -253,6 +279,7 @@ |
330 | def get_swift_codename(version): |
331 | '''Determine OpenStack codename that corresponds to swift version.''' |
332 | codenames = [k for k, v in six.iteritems(SWIFT_CODENAMES) if version in v] |
333 | + |
334 | if len(codenames) > 1: |
335 | # If more than one release codename contains this version we determine |
336 | # the actual codename based on the highest available install source. |
337 | @@ -264,6 +291,16 @@ |
338 | return codename |
339 | elif len(codenames) == 1: |
340 | return codenames[0] |
341 | + |
342 | + # NOTE: fallback - attempt to match with just major.minor version |
343 | + match = re.match('^(\d+)\.(\d+)', version) |
344 | + if match: |
345 | + major_minor_version = match.group(0) |
346 | + for codename, versions in six.iteritems(SWIFT_CODENAMES): |
347 | + for release_version in versions: |
348 | + if release_version.startswith(major_minor_version): |
349 | + return codename |
350 | + |
351 | return None |
352 | |
353 | |
354 | @@ -302,10 +339,13 @@ |
355 | if match: |
356 | vers = match.group(0) |
357 | |
358 | + # Generate a major version number for newer semantic |
359 | + # versions of openstack projects |
360 | + major_vers = vers.split('.')[0] |
361 | # >= Liberty independent project versions |
362 | if (package in PACKAGE_CODENAMES and |
363 | - vers in PACKAGE_CODENAMES[package]): |
364 | - return PACKAGE_CODENAMES[package][vers] |
365 | + major_vers in PACKAGE_CODENAMES[package]): |
366 | + return PACKAGE_CODENAMES[package][major_vers] |
367 | else: |
368 | # < Liberty co-ordinated project versions |
369 | try: |
370 | @@ -465,6 +505,9 @@ |
371 | 'mitaka': 'trusty-updates/mitaka', |
372 | 'mitaka/updates': 'trusty-updates/mitaka', |
373 | 'mitaka/proposed': 'trusty-proposed/mitaka', |
374 | + 'newton': 'xenial-updates/newton', |
375 | + 'newton/updates': 'xenial-updates/newton', |
376 | + 'newton/proposed': 'xenial-proposed/newton', |
377 | } |
378 | |
379 | try: |
380 | @@ -857,6 +900,47 @@ |
381 | return None |
382 | |
383 | |
384 | +def git_generate_systemd_init_files(templates_dir): |
385 | + """ |
386 | + Generate systemd init files. |
387 | + |
388 | + Generates and installs systemd init units and script files based on the |
389 | + *.init.in files contained in the templates_dir directory. |
390 | + |
391 | + This code is based on the openstack-pkg-tools package and its init |
392 | + script generation, which is used by the OpenStack packages. |
393 | + """ |
394 | + for f in os.listdir(templates_dir): |
395 | + if f.endswith(".init.in"): |
396 | + init_in_file = f |
397 | + init_file = f[:-8] |
398 | + service_file = "{}.service".format(init_file) |
399 | + |
400 | + init_in_source = os.path.join(templates_dir, init_in_file) |
401 | + init_source = os.path.join(templates_dir, init_file) |
402 | + service_source = os.path.join(templates_dir, service_file) |
403 | + |
404 | + init_dest = os.path.join('/etc/init.d', init_file) |
405 | + service_dest = os.path.join('/lib/systemd/system', service_file) |
406 | + |
407 | + shutil.copyfile(init_in_source, init_source) |
408 | + with open(init_source, 'a') as outfile: |
409 | + template = '/usr/share/openstack-pkg-tools/init-script-template' |
410 | + with open(template) as infile: |
411 | + outfile.write('\n\n{}'.format(infile.read())) |
412 | + |
413 | + cmd = ['pkgos-gen-systemd-unit', init_in_source] |
414 | + subprocess.check_call(cmd) |
415 | + |
416 | + if os.path.exists(init_dest): |
417 | + os.remove(init_dest) |
418 | + if os.path.exists(service_dest): |
419 | + os.remove(service_dest) |
420 | + shutil.move(init_source, init_dest) |
421 | + shutil.move(service_source, service_dest) |
422 | + os.chmod(init_dest, 0o755) |
423 | + |
424 | + |
425 | def os_workload_status(configs, required_interfaces, charm_func=None): |
426 | """ |
427 | Decorator to set workload status based on complete contexts |
428 | @@ -1573,3 +1657,82 @@ |
429 | restart_functions) |
430 | return wrapped_f |
431 | return wrap |
432 | + |
433 | + |
434 | +def config_flags_parser(config_flags): |
435 | + """Parses config flags string into dict. |
436 | + |
437 | + This parsing method supports a few different formats for the config |
438 | + flag values to be parsed: |
439 | + |
440 | + 1. A string in the simple format of key=value pairs, with the possibility |
441 | + of specifying multiple key value pairs within the same string. For |
442 | + example, a string in the format of 'key1=value1, key2=value2' will |
443 | + return a dict of: |
444 | + |
445 | + {'key1': 'value1', |
446 | + 'key2': 'value2'}. |
447 | + |
448 | + 2. A string in the above format, but supporting a comma-delimited list |
449 | + of values for the same key. For example, a string in the format of |
450 | + 'key1=value1, key2=value3,value4,value5' will return a dict of: |
451 | + |
452 | + {'key1', 'value1', |
453 | + 'key2', 'value2,value3,value4'} |
454 | + |
455 | + 3. A string containing a colon character (:) prior to an equal |
456 | + character (=) will be treated as yaml and parsed as such. This can be |
457 | + used to specify more complex key value pairs. For example, |
458 | + a string in the format of 'key1: subkey1=value1, subkey2=value2' will |
459 | + return a dict of: |
460 | + |
461 | + {'key1', 'subkey1=value1, subkey2=value2'} |
462 | + |
463 | + The provided config_flags string may be a list of comma-separated values |
464 | + which themselves may be comma-separated list of values. |
465 | + """ |
466 | + # If we find a colon before an equals sign then treat it as yaml. |
467 | + # Note: limit it to finding the colon first since this indicates assignment |
468 | + # for inline yaml. |
469 | + colon = config_flags.find(':') |
470 | + equals = config_flags.find('=') |
471 | + if colon > 0: |
472 | + if colon < equals or equals < 0: |
473 | + return yaml.safe_load(config_flags) |
474 | + |
475 | + if config_flags.find('==') >= 0: |
476 | + juju_log("config_flags is not in expected format (key=value)", |
477 | + level=ERROR) |
478 | + raise OSContextError |
479 | + |
480 | + # strip the following from each value. |
481 | + post_strippers = ' ,' |
482 | + # we strip any leading/trailing '=' or ' ' from the string then |
483 | + # split on '='. |
484 | + split = config_flags.strip(' =').split('=') |
485 | + limit = len(split) |
486 | + flags = {} |
487 | + for i in range(0, limit - 1): |
488 | + current = split[i] |
489 | + next = split[i + 1] |
490 | + vindex = next.rfind(',') |
491 | + if (i == limit - 2) or (vindex < 0): |
492 | + value = next |
493 | + else: |
494 | + value = next[:vindex] |
495 | + |
496 | + if i == 0: |
497 | + key = current |
498 | + else: |
499 | + # if this not the first entry, expect an embedded key. |
500 | + index = current.rfind(',') |
501 | + if index < 0: |
502 | + juju_log("Invalid config value(s) at index %s" % (i), |
503 | + level=ERROR) |
504 | + raise OSContextError |
505 | + key = current[index + 1:] |
506 | + |
507 | + # Add to collection. |
508 | + flags[key.strip(post_strippers)] = value.rstrip(post_strippers) |
509 | + |
510 | + return flags |
511 | |
512 | === modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py' |
513 | --- hooks/charmhelpers/contrib/storage/linux/ceph.py 2016-04-22 04:35:32 +0000 |
514 | +++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2016-06-04 02:32:00 +0000 |
515 | @@ -40,6 +40,7 @@ |
516 | CalledProcessError, |
517 | ) |
518 | from charmhelpers.core.hookenv import ( |
519 | + config, |
520 | local_unit, |
521 | relation_get, |
522 | relation_ids, |
523 | @@ -64,6 +65,7 @@ |
524 | ) |
525 | |
526 | from charmhelpers.core.kernel import modprobe |
527 | +from charmhelpers.contrib.openstack.utils import config_flags_parser |
528 | |
529 | KEYRING = '/etc/ceph/ceph.client.{}.keyring' |
530 | KEYFILE = '/etc/ceph/ceph.client.{}.key' |
531 | @@ -1204,3 +1206,42 @@ |
532 | for rid in relation_ids(relation): |
533 | log('Sending request {}'.format(request.request_id), level=DEBUG) |
534 | relation_set(relation_id=rid, broker_req=request.request) |
535 | + |
536 | + |
537 | +class CephConfContext(object): |
538 | + """Ceph config (ceph.conf) context. |
539 | + |
540 | + Supports user-provided Ceph configuration settings. Use can provide a |
541 | + dictionary as the value for the config-flags charm option containing |
542 | + Ceph configuration settings keyede by their section in ceph.conf. |
543 | + """ |
544 | + def __init__(self, permitted_sections=None): |
545 | + self.permitted_sections = permitted_sections or [] |
546 | + |
547 | + def __call__(self): |
548 | + conf = config('config-flags') |
549 | + if not conf: |
550 | + return {} |
551 | + |
552 | + conf = config_flags_parser(conf) |
553 | + if type(conf) != dict: |
554 | + log("Provided config-flags is not a dictionary - ignoring", |
555 | + level=WARNING) |
556 | + return {} |
557 | + |
558 | + permitted = self.permitted_sections |
559 | + if permitted: |
560 | + diff = set(conf.keys()).difference(set(permitted)) |
561 | + if diff: |
562 | + log("Config-flags contains invalid keys '%s' - they will be " |
563 | + "ignored" % (', '.join(diff)), level=WARNING) |
564 | + |
565 | + ceph_conf = {} |
566 | + for key in conf: |
567 | + if permitted and key not in permitted: |
568 | + log("Ignoring key '%s'" % key, level=WARNING) |
569 | + continue |
570 | + |
571 | + ceph_conf[key] = conf[key] |
572 | + |
573 | + return ceph_conf |
574 | |
575 | === modified file 'hooks/charmhelpers/core/host.py' |
576 | --- hooks/charmhelpers/core/host.py 2016-04-22 04:35:32 +0000 |
577 | +++ hooks/charmhelpers/core/host.py 2016-06-04 02:32:00 +0000 |
578 | @@ -128,11 +128,8 @@ |
579 | return subprocess.call(cmd) == 0 |
580 | |
581 | |
582 | -def systemv_services_running(): |
583 | - output = subprocess.check_output( |
584 | - ['service', '--status-all'], |
585 | - stderr=subprocess.STDOUT).decode('UTF-8') |
586 | - return [row.split()[-1] for row in output.split('\n') if '[ + ]' in row] |
587 | +_UPSTART_CONF = "/etc/init/{}.conf" |
588 | +_INIT_D_CONF = "/etc/init.d/{}" |
589 | |
590 | |
591 | def service_running(service_name): |
592 | @@ -140,22 +137,22 @@ |
593 | if init_is_systemd(): |
594 | return service('is-active', service_name) |
595 | else: |
596 | - try: |
597 | - output = subprocess.check_output( |
598 | - ['service', service_name, 'status'], |
599 | - stderr=subprocess.STDOUT).decode('UTF-8') |
600 | - except subprocess.CalledProcessError: |
601 | - return False |
602 | - else: |
603 | - # This works for upstart scripts where the 'service' command |
604 | - # returns a consistent string to represent running 'start/running' |
605 | - if ("start/running" in output or "is running" in output or |
606 | - "up and running" in output): |
607 | - return True |
608 | + if os.path.exists(_UPSTART_CONF.format(service_name)): |
609 | + try: |
610 | + output = subprocess.check_output( |
611 | + ['status', service_name], |
612 | + stderr=subprocess.STDOUT).decode('UTF-8') |
613 | + except subprocess.CalledProcessError: |
614 | + return False |
615 | + else: |
616 | + # This works for upstart scripts where the 'service' command |
617 | + # returns a consistent string to represent running 'start/running' |
618 | + if "start/running" in output: |
619 | + return True |
620 | + elif os.path.exists(_INIT_D_CONF.format(service_name)): |
621 | # Check System V scripts init script return codes |
622 | - if service_name in systemv_services_running(): |
623 | - return True |
624 | - return False |
625 | + return service('status', service_name) |
626 | + return False |
627 | |
628 | |
629 | def service_available(service_name): |
630 | @@ -179,7 +176,7 @@ |
631 | |
632 | |
633 | def adduser(username, password=None, shell='/bin/bash', system_user=False, |
634 | - primary_group=None, secondary_groups=None): |
635 | + primary_group=None, secondary_groups=None, uid=None): |
636 | """Add a user to the system. |
637 | |
638 | Will log but otherwise succeed if the user already exists. |
639 | @@ -190,15 +187,21 @@ |
640 | :param bool system_user: Whether to create a login or system user |
641 | :param str primary_group: Primary group for user; defaults to username |
642 | :param list secondary_groups: Optional list of additional groups |
643 | + :param int uid: UID for user being created |
644 | |
645 | :returns: The password database entry struct, as returned by `pwd.getpwnam` |
646 | """ |
647 | try: |
648 | user_info = pwd.getpwnam(username) |
649 | log('user {0} already exists!'.format(username)) |
650 | + if uid: |
651 | + user_info = pwd.getpwuid(int(uid)) |
652 | + log('user with uid {0} already exists!'.format(uid)) |
653 | except KeyError: |
654 | log('creating user {0}'.format(username)) |
655 | cmd = ['useradd'] |
656 | + if uid: |
657 | + cmd.extend(['--uid', str(uid)]) |
658 | if system_user or password is None: |
659 | cmd.append('--system') |
660 | else: |
661 | @@ -233,14 +236,58 @@ |
662 | return user_exists |
663 | |
664 | |
665 | -def add_group(group_name, system_group=False): |
666 | - """Add a group to the system""" |
667 | +def uid_exists(uid): |
668 | + """Check if a uid exists""" |
669 | + try: |
670 | + pwd.getpwuid(uid) |
671 | + uid_exists = True |
672 | + except KeyError: |
673 | + uid_exists = False |
674 | + return uid_exists |
675 | + |
676 | + |
677 | +def group_exists(groupname): |
678 | + """Check if a group exists""" |
679 | + try: |
680 | + grp.getgrnam(groupname) |
681 | + group_exists = True |
682 | + except KeyError: |
683 | + group_exists = False |
684 | + return group_exists |
685 | + |
686 | + |
687 | +def gid_exists(gid): |
688 | + """Check if a gid exists""" |
689 | + try: |
690 | + grp.getgrgid(gid) |
691 | + gid_exists = True |
692 | + except KeyError: |
693 | + gid_exists = False |
694 | + return gid_exists |
695 | + |
696 | + |
697 | +def add_group(group_name, system_group=False, gid=None): |
698 | + """Add a group to the system |
699 | + |
700 | + Will log but otherwise succeed if the group already exists. |
701 | + |
702 | + :param str group_name: group to create |
703 | + :param bool system_group: Create system group |
704 | + :param int gid: GID for user being created |
705 | + |
706 | + :returns: The password database entry struct, as returned by `grp.getgrnam` |
707 | + """ |
708 | try: |
709 | group_info = grp.getgrnam(group_name) |
710 | log('group {0} already exists!'.format(group_name)) |
711 | + if gid: |
712 | + group_info = grp.getgrgid(gid) |
713 | + log('group with gid {0} already exists!'.format(gid)) |
714 | except KeyError: |
715 | log('creating group {0}'.format(group_name)) |
716 | cmd = ['addgroup'] |
717 | + if gid: |
718 | + cmd.extend(['--gid', str(gid)]) |
719 | if system_group: |
720 | cmd.append('--system') |
721 | else: |
722 | |
723 | === modified file 'hooks/charmhelpers/fetch/__init__.py' |
724 | --- hooks/charmhelpers/fetch/__init__.py 2016-02-27 19:51:32 +0000 |
725 | +++ hooks/charmhelpers/fetch/__init__.py 2016-06-04 02:32:00 +0000 |
726 | @@ -106,6 +106,14 @@ |
727 | 'mitaka/proposed': 'trusty-proposed/mitaka', |
728 | 'trusty-mitaka/proposed': 'trusty-proposed/mitaka', |
729 | 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', |
730 | + # Newton |
731 | + 'newton': 'xenial-updates/newton', |
732 | + 'xenial-newton': 'xenial-updates/newton', |
733 | + 'xenial-newton/updates': 'xenial-updates/newton', |
734 | + 'xenial-updates/newton': 'xenial-updates/newton', |
735 | + 'newton/proposed': 'xenial-proposed/newton', |
736 | + 'xenial-newton/proposed': 'xenial-proposed/newton', |
737 | + 'xenial-proposed/newton': 'xenial-proposed/newton', |
738 | } |
739 | |
740 | # The order of this list is very important. Handlers should be listed in from |