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