Merge lp:~junaidali/charms/trusty/neutron-api-plumgrid/resynced into lp:~plumgrid-team/charms/trusty/neutron-api-plumgrid/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
Reviewer Review Type Date Requested Status
Bilal Baqar Pending
Review via email: mp+296483@code.launchpad.net
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
=== modified file 'hooks/charmhelpers/contrib/network/ip.py'
--- hooks/charmhelpers/contrib/network/ip.py 2016-04-22 04:35:32 +0000
+++ hooks/charmhelpers/contrib/network/ip.py 2016-06-04 02:32:00 +0000
@@ -214,7 +214,16 @@
214214
215def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,215def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
216 fatal=True, exc_list=None):216 fatal=True, exc_list=None):
217 """Return the assigned IP address for a given interface, if any."""217 """Return the assigned IP address for a given interface, if any.
218
219 :param iface: network interface on which address(es) are expected to
220 be found.
221 :param inet_type: inet address family
222 :param inc_aliases: include alias interfaces in search
223 :param fatal: if True, raise exception if address not found
224 :param exc_list: list of addresses to ignore
225 :return: list of ip addresses
226 """
218 # Extract nic if passed /dev/ethX227 # Extract nic if passed /dev/ethX
219 if '/' in iface:228 if '/' in iface:
220 iface = iface.split('/')[-1]229 iface = iface.split('/')[-1]
@@ -315,6 +324,14 @@
315 We currently only support scope global IPv6 addresses i.e. non-temporary324 We currently only support scope global IPv6 addresses i.e. non-temporary
316 addresses. If no global IPv6 address is found, return the first one found325 addresses. If no global IPv6 address is found, return the first one found
317 in the ipv6 address list.326 in the ipv6 address list.
327
328 :param iface: network interface on which ipv6 address(es) are expected to
329 be found.
330 :param inc_aliases: include alias interfaces in search
331 :param fatal: if True, raise exception if address not found
332 :param exc_list: list of addresses to ignore
333 :param dynamic_only: only recognise dynamic addresses
334 :return: list of ipv6 addresses
318 """335 """
319 addresses = get_iface_addr(iface=iface, inet_type='AF_INET6',336 addresses = get_iface_addr(iface=iface, inet_type='AF_INET6',
320 inc_aliases=inc_aliases, fatal=fatal,337 inc_aliases=inc_aliases, fatal=fatal,
@@ -336,7 +353,7 @@
336 cmd = ['ip', 'addr', 'show', iface]353 cmd = ['ip', 'addr', 'show', iface]
337 out = subprocess.check_output(cmd).decode('UTF-8')354 out = subprocess.check_output(cmd).decode('UTF-8')
338 if dynamic_only:355 if dynamic_only:
339 key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")356 key = re.compile("inet6 (.+)/[0-9]+ scope global.* dynamic.*")
340 else:357 else:
341 key = re.compile("inet6 (.+)/[0-9]+ scope global.*")358 key = re.compile("inet6 (.+)/[0-9]+ scope global.*")
342359
@@ -388,10 +405,10 @@
388 Returns True if address is a valid IP address.405 Returns True if address is a valid IP address.
389 """406 """
390 try:407 try:
391 # Test to see if already an IPv4 address408 # Test to see if already an IPv4/IPv6 address
392 socket.inet_aton(address)409 address = netaddr.IPAddress(address)
393 return True410 return True
394 except socket.error:411 except netaddr.AddrFormatError:
395 return False412 return False
396413
397414
398415
=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
--- hooks/charmhelpers/contrib/openstack/context.py 2016-04-22 04:35:32 +0000
+++ hooks/charmhelpers/contrib/openstack/context.py 2016-06-04 02:32:00 +0000
@@ -23,7 +23,6 @@
23from subprocess import check_call, CalledProcessError23from subprocess import check_call, CalledProcessError
2424
25import six25import six
26import yaml
2726
28from charmhelpers.fetch import (27from charmhelpers.fetch import (
29 apt_install,28 apt_install,
@@ -50,6 +49,7 @@
5049
51from charmhelpers.core.sysctl import create as sysctl_create50from charmhelpers.core.sysctl import create as sysctl_create
52from charmhelpers.core.strutils import bool_from_string51from charmhelpers.core.strutils import bool_from_string
52from charmhelpers.contrib.openstack.exceptions import OSContextError
5353
54from charmhelpers.core.host import (54from charmhelpers.core.host import (
55 get_bond_master,55 get_bond_master,
@@ -88,7 +88,10 @@
88 is_address_in_network,88 is_address_in_network,
89 is_bridge_member,89 is_bridge_member,
90)90)
91from charmhelpers.contrib.openstack.utils import get_host_ip91from charmhelpers.contrib.openstack.utils import (
92 config_flags_parser,
93 get_host_ip,
94)
92from charmhelpers.core.unitdata import kv95from charmhelpers.core.unitdata import kv
9396
94try:97try:
@@ -101,10 +104,6 @@
101ADDRESS_TYPES = ['admin', 'internal', 'public']104ADDRESS_TYPES = ['admin', 'internal', 'public']
102105
103106
104class OSContextError(Exception):
105 pass
106
107
108def ensure_packages(packages):107def ensure_packages(packages):
109 """Install but do not upgrade required plugin packages."""108 """Install but do not upgrade required plugin packages."""
110 required = filter_installed_packages(packages)109 required = filter_installed_packages(packages)
@@ -125,83 +124,6 @@
125 return True124 return True
126125
127126
128def config_flags_parser(config_flags):
129 """Parses config flags string into dict.
130
131 This parsing method supports a few different formats for the config
132 flag values to be parsed:
133
134 1. A string in the simple format of key=value pairs, with the possibility
135 of specifying multiple key value pairs within the same string. For
136 example, a string in the format of 'key1=value1, key2=value2' will
137 return a dict of:
138
139 {'key1': 'value1',
140 'key2': 'value2'}.
141
142 2. A string in the above format, but supporting a comma-delimited list
143 of values for the same key. For example, a string in the format of
144 'key1=value1, key2=value3,value4,value5' will return a dict of:
145
146 {'key1', 'value1',
147 'key2', 'value2,value3,value4'}
148
149 3. A string containing a colon character (:) prior to an equal
150 character (=) will be treated as yaml and parsed as such. This can be
151 used to specify more complex key value pairs. For example,
152 a string in the format of 'key1: subkey1=value1, subkey2=value2' will
153 return a dict of:
154
155 {'key1', 'subkey1=value1, subkey2=value2'}
156
157 The provided config_flags string may be a list of comma-separated values
158 which themselves may be comma-separated list of values.
159 """
160 # If we find a colon before an equals sign then treat it as yaml.
161 # Note: limit it to finding the colon first since this indicates assignment
162 # for inline yaml.
163 colon = config_flags.find(':')
164 equals = config_flags.find('=')
165 if colon > 0:
166 if colon < equals or equals < 0:
167 return yaml.safe_load(config_flags)
168
169 if config_flags.find('==') >= 0:
170 log("config_flags is not in expected format (key=value)", level=ERROR)
171 raise OSContextError
172
173 # strip the following from each value.
174 post_strippers = ' ,'
175 # we strip any leading/trailing '=' or ' ' from the string then
176 # split on '='.
177 split = config_flags.strip(' =').split('=')
178 limit = len(split)
179 flags = {}
180 for i in range(0, limit - 1):
181 current = split[i]
182 next = split[i + 1]
183 vindex = next.rfind(',')
184 if (i == limit - 2) or (vindex < 0):
185 value = next
186 else:
187 value = next[:vindex]
188
189 if i == 0:
190 key = current
191 else:
192 # if this not the first entry, expect an embedded key.
193 index = current.rfind(',')
194 if index < 0:
195 log("Invalid config value(s) at index %s" % (i), level=ERROR)
196 raise OSContextError
197 key = current[index + 1:]
198
199 # Add to collection.
200 flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
201
202 return flags
203
204
205class OSContextGenerator(object):127class OSContextGenerator(object):
206 """Base class for all context generators."""128 """Base class for all context generators."""
207 interfaces = []129 interfaces = []
208130
=== added file 'hooks/charmhelpers/contrib/openstack/exceptions.py'
--- hooks/charmhelpers/contrib/openstack/exceptions.py 1970-01-01 00:00:00 +0000
+++ hooks/charmhelpers/contrib/openstack/exceptions.py 2016-06-04 02:32:00 +0000
@@ -0,0 +1,6 @@
1class OSContextError(Exception):
2 """Raised when an error occurs during context generation.
3
4 This exception is principally used in contrib.openstack.context
5 """
6 pass
07
=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
--- hooks/charmhelpers/contrib/openstack/utils.py 2016-04-22 04:35:32 +0000
+++ hooks/charmhelpers/contrib/openstack/utils.py 2016-06-04 02:32:00 +0000
@@ -25,6 +25,7 @@
25import re25import re
26import itertools26import itertools
27import functools27import functools
28import shutil
2829
29import six30import six
30import tempfile31import tempfile
@@ -46,6 +47,7 @@
46 charm_dir,47 charm_dir,
47 DEBUG,48 DEBUG,
48 INFO,49 INFO,
50 ERROR,
49 related_units,51 related_units,
50 relation_ids,52 relation_ids,
51 relation_set,53 relation_set,
@@ -82,6 +84,7 @@
82from charmhelpers.fetch import apt_install, apt_cache, install_remote84from charmhelpers.fetch import apt_install, apt_cache, install_remote
83from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk85from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
84from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device86from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
87from charmhelpers.contrib.openstack.exceptions import OSContextError
8588
86CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"89CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
87CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'90CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
@@ -100,6 +103,8 @@
100 ('vivid', 'kilo'),103 ('vivid', 'kilo'),
101 ('wily', 'liberty'),104 ('wily', 'liberty'),
102 ('xenial', 'mitaka'),105 ('xenial', 'mitaka'),
106 ('yakkety', 'newton'),
107 ('zebra', 'ocata'), # TODO: upload with real Z name
103])108])
104109
105110
@@ -114,6 +119,8 @@
114 ('2015.1', 'kilo'),119 ('2015.1', 'kilo'),
115 ('2015.2', 'liberty'),120 ('2015.2', 'liberty'),
116 ('2016.1', 'mitaka'),121 ('2016.1', 'mitaka'),
122 ('2016.2', 'newton'),
123 ('2017.1', 'ocata'),
117])124])
118125
119# The ugly duckling - must list releases oldest to newest126# The ugly duckling - must list releases oldest to newest
@@ -138,46 +145,65 @@
138 ['2.3.0', '2.4.0', '2.5.0']),145 ['2.3.0', '2.4.0', '2.5.0']),
139 ('mitaka',146 ('mitaka',
140 ['2.5.0', '2.6.0', '2.7.0']),147 ['2.5.0', '2.6.0', '2.7.0']),
148 ('newton',
149 ['2.8.0']),
141])150])
142151
143# >= Liberty version->codename mapping152# >= Liberty version->codename mapping
144PACKAGE_CODENAMES = {153PACKAGE_CODENAMES = {
145 'nova-common': OrderedDict([154 'nova-common': OrderedDict([
146 ('12.0', 'liberty'),155 ('12', 'liberty'),
147 ('13.0', 'mitaka'),156 ('13', 'mitaka'),
157 ('14', 'newton'),
158 ('15', 'ocata'),
148 ]),159 ]),
149 'neutron-common': OrderedDict([160 'neutron-common': OrderedDict([
150 ('7.0', 'liberty'),161 ('7', 'liberty'),
151 ('8.0', 'mitaka'),162 ('8', 'mitaka'),
163 ('9', 'newton'),
164 ('10', 'ocata'),
152 ]),165 ]),
153 'cinder-common': OrderedDict([166 'cinder-common': OrderedDict([
154 ('7.0', 'liberty'),167 ('7', 'liberty'),
155 ('8.0', 'mitaka'),168 ('8', 'mitaka'),
169 ('9', 'newton'),
170 ('10', 'ocata'),
156 ]),171 ]),
157 'keystone': OrderedDict([172 'keystone': OrderedDict([
158 ('8.0', 'liberty'),173 ('8', 'liberty'),
159 ('8.1', 'liberty'),174 ('9', 'mitaka'),
160 ('9.0', 'mitaka'),175 ('10', 'newton'),
176 ('11', 'ocata'),
161 ]),177 ]),
162 'horizon-common': OrderedDict([178 'horizon-common': OrderedDict([
163 ('8.0', 'liberty'),179 ('8', 'liberty'),
164 ('9.0', 'mitaka'),180 ('9', 'mitaka'),
181 ('10', 'newton'),
182 ('11', 'ocata'),
165 ]),183 ]),
166 'ceilometer-common': OrderedDict([184 'ceilometer-common': OrderedDict([
167 ('5.0', 'liberty'),185 ('5', 'liberty'),
168 ('6.0', 'mitaka'),186 ('6', 'mitaka'),
187 ('7', 'newton'),
188 ('8', 'ocata'),
169 ]),189 ]),
170 'heat-common': OrderedDict([190 'heat-common': OrderedDict([
171 ('5.0', 'liberty'),191 ('5', 'liberty'),
172 ('6.0', 'mitaka'),192 ('6', 'mitaka'),
193 ('7', 'newton'),
194 ('8', 'ocata'),
173 ]),195 ]),
174 'glance-common': OrderedDict([196 'glance-common': OrderedDict([
175 ('11.0', 'liberty'),197 ('11', 'liberty'),
176 ('12.0', 'mitaka'),198 ('12', 'mitaka'),
199 ('13', 'newton'),
200 ('14', 'ocata'),
177 ]),201 ]),
178 'openstack-dashboard': OrderedDict([202 'openstack-dashboard': OrderedDict([
179 ('8.0', 'liberty'),203 ('8', 'liberty'),
180 ('9.0', 'mitaka'),204 ('9', 'mitaka'),
205 ('10', 'newton'),
206 ('11', 'ocata'),
181 ]),207 ]),
182}208}
183209
@@ -253,6 +279,7 @@
253def get_swift_codename(version):279def get_swift_codename(version):
254 '''Determine OpenStack codename that corresponds to swift version.'''280 '''Determine OpenStack codename that corresponds to swift version.'''
255 codenames = [k for k, v in six.iteritems(SWIFT_CODENAMES) if version in v]281 codenames = [k for k, v in six.iteritems(SWIFT_CODENAMES) if version in v]
282
256 if len(codenames) > 1:283 if len(codenames) > 1:
257 # If more than one release codename contains this version we determine284 # If more than one release codename contains this version we determine
258 # the actual codename based on the highest available install source.285 # the actual codename based on the highest available install source.
@@ -264,6 +291,16 @@
264 return codename291 return codename
265 elif len(codenames) == 1:292 elif len(codenames) == 1:
266 return codenames[0]293 return codenames[0]
294
295 # NOTE: fallback - attempt to match with just major.minor version
296 match = re.match('^(\d+)\.(\d+)', version)
297 if match:
298 major_minor_version = match.group(0)
299 for codename, versions in six.iteritems(SWIFT_CODENAMES):
300 for release_version in versions:
301 if release_version.startswith(major_minor_version):
302 return codename
303
267 return None304 return None
268305
269306
@@ -302,10 +339,13 @@
302 if match:339 if match:
303 vers = match.group(0)340 vers = match.group(0)
304341
342 # Generate a major version number for newer semantic
343 # versions of openstack projects
344 major_vers = vers.split('.')[0]
305 # >= Liberty independent project versions345 # >= Liberty independent project versions
306 if (package in PACKAGE_CODENAMES and346 if (package in PACKAGE_CODENAMES and
307 vers in PACKAGE_CODENAMES[package]):347 major_vers in PACKAGE_CODENAMES[package]):
308 return PACKAGE_CODENAMES[package][vers]348 return PACKAGE_CODENAMES[package][major_vers]
309 else:349 else:
310 # < Liberty co-ordinated project versions350 # < Liberty co-ordinated project versions
311 try:351 try:
@@ -465,6 +505,9 @@
465 'mitaka': 'trusty-updates/mitaka',505 'mitaka': 'trusty-updates/mitaka',
466 'mitaka/updates': 'trusty-updates/mitaka',506 'mitaka/updates': 'trusty-updates/mitaka',
467 'mitaka/proposed': 'trusty-proposed/mitaka',507 'mitaka/proposed': 'trusty-proposed/mitaka',
508 'newton': 'xenial-updates/newton',
509 'newton/updates': 'xenial-updates/newton',
510 'newton/proposed': 'xenial-proposed/newton',
468 }511 }
469512
470 try:513 try:
@@ -857,6 +900,47 @@
857 return None900 return None
858901
859902
903def git_generate_systemd_init_files(templates_dir):
904 """
905 Generate systemd init files.
906
907 Generates and installs systemd init units and script files based on the
908 *.init.in files contained in the templates_dir directory.
909
910 This code is based on the openstack-pkg-tools package and its init
911 script generation, which is used by the OpenStack packages.
912 """
913 for f in os.listdir(templates_dir):
914 if f.endswith(".init.in"):
915 init_in_file = f
916 init_file = f[:-8]
917 service_file = "{}.service".format(init_file)
918
919 init_in_source = os.path.join(templates_dir, init_in_file)
920 init_source = os.path.join(templates_dir, init_file)
921 service_source = os.path.join(templates_dir, service_file)
922
923 init_dest = os.path.join('/etc/init.d', init_file)
924 service_dest = os.path.join('/lib/systemd/system', service_file)
925
926 shutil.copyfile(init_in_source, init_source)
927 with open(init_source, 'a') as outfile:
928 template = '/usr/share/openstack-pkg-tools/init-script-template'
929 with open(template) as infile:
930 outfile.write('\n\n{}'.format(infile.read()))
931
932 cmd = ['pkgos-gen-systemd-unit', init_in_source]
933 subprocess.check_call(cmd)
934
935 if os.path.exists(init_dest):
936 os.remove(init_dest)
937 if os.path.exists(service_dest):
938 os.remove(service_dest)
939 shutil.move(init_source, init_dest)
940 shutil.move(service_source, service_dest)
941 os.chmod(init_dest, 0o755)
942
943
860def os_workload_status(configs, required_interfaces, charm_func=None):944def os_workload_status(configs, required_interfaces, charm_func=None):
861 """945 """
862 Decorator to set workload status based on complete contexts946 Decorator to set workload status based on complete contexts
@@ -1573,3 +1657,82 @@
1573 restart_functions)1657 restart_functions)
1574 return wrapped_f1658 return wrapped_f
1575 return wrap1659 return wrap
1660
1661
1662def config_flags_parser(config_flags):
1663 """Parses config flags string into dict.
1664
1665 This parsing method supports a few different formats for the config
1666 flag values to be parsed:
1667
1668 1. A string in the simple format of key=value pairs, with the possibility
1669 of specifying multiple key value pairs within the same string. For
1670 example, a string in the format of 'key1=value1, key2=value2' will
1671 return a dict of:
1672
1673 {'key1': 'value1',
1674 'key2': 'value2'}.
1675
1676 2. A string in the above format, but supporting a comma-delimited list
1677 of values for the same key. For example, a string in the format of
1678 'key1=value1, key2=value3,value4,value5' will return a dict of:
1679
1680 {'key1', 'value1',
1681 'key2', 'value2,value3,value4'}
1682
1683 3. A string containing a colon character (:) prior to an equal
1684 character (=) will be treated as yaml and parsed as such. This can be
1685 used to specify more complex key value pairs. For example,
1686 a string in the format of 'key1: subkey1=value1, subkey2=value2' will
1687 return a dict of:
1688
1689 {'key1', 'subkey1=value1, subkey2=value2'}
1690
1691 The provided config_flags string may be a list of comma-separated values
1692 which themselves may be comma-separated list of values.
1693 """
1694 # If we find a colon before an equals sign then treat it as yaml.
1695 # Note: limit it to finding the colon first since this indicates assignment
1696 # for inline yaml.
1697 colon = config_flags.find(':')
1698 equals = config_flags.find('=')
1699 if colon > 0:
1700 if colon < equals or equals < 0:
1701 return yaml.safe_load(config_flags)
1702
1703 if config_flags.find('==') >= 0:
1704 juju_log("config_flags is not in expected format (key=value)",
1705 level=ERROR)
1706 raise OSContextError
1707
1708 # strip the following from each value.
1709 post_strippers = ' ,'
1710 # we strip any leading/trailing '=' or ' ' from the string then
1711 # split on '='.
1712 split = config_flags.strip(' =').split('=')
1713 limit = len(split)
1714 flags = {}
1715 for i in range(0, limit - 1):
1716 current = split[i]
1717 next = split[i + 1]
1718 vindex = next.rfind(',')
1719 if (i == limit - 2) or (vindex < 0):
1720 value = next
1721 else:
1722 value = next[:vindex]
1723
1724 if i == 0:
1725 key = current
1726 else:
1727 # if this not the first entry, expect an embedded key.
1728 index = current.rfind(',')
1729 if index < 0:
1730 juju_log("Invalid config value(s) at index %s" % (i),
1731 level=ERROR)
1732 raise OSContextError
1733 key = current[index + 1:]
1734
1735 # Add to collection.
1736 flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
1737
1738 return flags
15761739
=== modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py'
--- hooks/charmhelpers/contrib/storage/linux/ceph.py 2016-04-22 04:35:32 +0000
+++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2016-06-04 02:32:00 +0000
@@ -40,6 +40,7 @@
40 CalledProcessError,40 CalledProcessError,
41)41)
42from charmhelpers.core.hookenv import (42from charmhelpers.core.hookenv import (
43 config,
43 local_unit,44 local_unit,
44 relation_get,45 relation_get,
45 relation_ids,46 relation_ids,
@@ -64,6 +65,7 @@
64)65)
6566
66from charmhelpers.core.kernel import modprobe67from charmhelpers.core.kernel import modprobe
68from charmhelpers.contrib.openstack.utils import config_flags_parser
6769
68KEYRING = '/etc/ceph/ceph.client.{}.keyring'70KEYRING = '/etc/ceph/ceph.client.{}.keyring'
69KEYFILE = '/etc/ceph/ceph.client.{}.key'71KEYFILE = '/etc/ceph/ceph.client.{}.key'
@@ -1204,3 +1206,42 @@
1204 for rid in relation_ids(relation):1206 for rid in relation_ids(relation):
1205 log('Sending request {}'.format(request.request_id), level=DEBUG)1207 log('Sending request {}'.format(request.request_id), level=DEBUG)
1206 relation_set(relation_id=rid, broker_req=request.request)1208 relation_set(relation_id=rid, broker_req=request.request)
1209
1210
1211class CephConfContext(object):
1212 """Ceph config (ceph.conf) context.
1213
1214 Supports user-provided Ceph configuration settings. Use can provide a
1215 dictionary as the value for the config-flags charm option containing
1216 Ceph configuration settings keyede by their section in ceph.conf.
1217 """
1218 def __init__(self, permitted_sections=None):
1219 self.permitted_sections = permitted_sections or []
1220
1221 def __call__(self):
1222 conf = config('config-flags')
1223 if not conf:
1224 return {}
1225
1226 conf = config_flags_parser(conf)
1227 if type(conf) != dict:
1228 log("Provided config-flags is not a dictionary - ignoring",
1229 level=WARNING)
1230 return {}
1231
1232 permitted = self.permitted_sections
1233 if permitted:
1234 diff = set(conf.keys()).difference(set(permitted))
1235 if diff:
1236 log("Config-flags contains invalid keys '%s' - they will be "
1237 "ignored" % (', '.join(diff)), level=WARNING)
1238
1239 ceph_conf = {}
1240 for key in conf:
1241 if permitted and key not in permitted:
1242 log("Ignoring key '%s'" % key, level=WARNING)
1243 continue
1244
1245 ceph_conf[key] = conf[key]
1246
1247 return ceph_conf
12071248
=== modified file 'hooks/charmhelpers/core/host.py'
--- hooks/charmhelpers/core/host.py 2016-04-22 04:35:32 +0000
+++ hooks/charmhelpers/core/host.py 2016-06-04 02:32:00 +0000
@@ -128,11 +128,8 @@
128 return subprocess.call(cmd) == 0128 return subprocess.call(cmd) == 0
129129
130130
131def systemv_services_running():131_UPSTART_CONF = "/etc/init/{}.conf"
132 output = subprocess.check_output(132_INIT_D_CONF = "/etc/init.d/{}"
133 ['service', '--status-all'],
134 stderr=subprocess.STDOUT).decode('UTF-8')
135 return [row.split()[-1] for row in output.split('\n') if '[ + ]' in row]
136133
137134
138def service_running(service_name):135def service_running(service_name):
@@ -140,22 +137,22 @@
140 if init_is_systemd():137 if init_is_systemd():
141 return service('is-active', service_name)138 return service('is-active', service_name)
142 else:139 else:
143 try:140 if os.path.exists(_UPSTART_CONF.format(service_name)):
144 output = subprocess.check_output(141 try:
145 ['service', service_name, 'status'],142 output = subprocess.check_output(
146 stderr=subprocess.STDOUT).decode('UTF-8')143 ['status', service_name],
147 except subprocess.CalledProcessError:144 stderr=subprocess.STDOUT).decode('UTF-8')
148 return False145 except subprocess.CalledProcessError:
149 else:146 return False
150 # This works for upstart scripts where the 'service' command147 else:
151 # returns a consistent string to represent running 'start/running'148 # This works for upstart scripts where the 'service' command
152 if ("start/running" in output or "is running" in output or149 # returns a consistent string to represent running 'start/running'
153 "up and running" in output):150 if "start/running" in output:
154 return True151 return True
152 elif os.path.exists(_INIT_D_CONF.format(service_name)):
155 # Check System V scripts init script return codes153 # Check System V scripts init script return codes
156 if service_name in systemv_services_running():154 return service('status', service_name)
157 return True155 return False
158 return False
159156
160157
161def service_available(service_name):158def service_available(service_name):
@@ -179,7 +176,7 @@
179176
180177
181def adduser(username, password=None, shell='/bin/bash', system_user=False,178def adduser(username, password=None, shell='/bin/bash', system_user=False,
182 primary_group=None, secondary_groups=None):179 primary_group=None, secondary_groups=None, uid=None):
183 """Add a user to the system.180 """Add a user to the system.
184181
185 Will log but otherwise succeed if the user already exists.182 Will log but otherwise succeed if the user already exists.
@@ -190,15 +187,21 @@
190 :param bool system_user: Whether to create a login or system user187 :param bool system_user: Whether to create a login or system user
191 :param str primary_group: Primary group for user; defaults to username188 :param str primary_group: Primary group for user; defaults to username
192 :param list secondary_groups: Optional list of additional groups189 :param list secondary_groups: Optional list of additional groups
190 :param int uid: UID for user being created
193191
194 :returns: The password database entry struct, as returned by `pwd.getpwnam`192 :returns: The password database entry struct, as returned by `pwd.getpwnam`
195 """193 """
196 try:194 try:
197 user_info = pwd.getpwnam(username)195 user_info = pwd.getpwnam(username)
198 log('user {0} already exists!'.format(username))196 log('user {0} already exists!'.format(username))
197 if uid:
198 user_info = pwd.getpwuid(int(uid))
199 log('user with uid {0} already exists!'.format(uid))
199 except KeyError:200 except KeyError:
200 log('creating user {0}'.format(username))201 log('creating user {0}'.format(username))
201 cmd = ['useradd']202 cmd = ['useradd']
203 if uid:
204 cmd.extend(['--uid', str(uid)])
202 if system_user or password is None:205 if system_user or password is None:
203 cmd.append('--system')206 cmd.append('--system')
204 else:207 else:
@@ -233,14 +236,58 @@
233 return user_exists236 return user_exists
234237
235238
236def add_group(group_name, system_group=False):239def uid_exists(uid):
237 """Add a group to the system"""240 """Check if a uid exists"""
241 try:
242 pwd.getpwuid(uid)
243 uid_exists = True
244 except KeyError:
245 uid_exists = False
246 return uid_exists
247
248
249def group_exists(groupname):
250 """Check if a group exists"""
251 try:
252 grp.getgrnam(groupname)
253 group_exists = True
254 except KeyError:
255 group_exists = False
256 return group_exists
257
258
259def gid_exists(gid):
260 """Check if a gid exists"""
261 try:
262 grp.getgrgid(gid)
263 gid_exists = True
264 except KeyError:
265 gid_exists = False
266 return gid_exists
267
268
269def add_group(group_name, system_group=False, gid=None):
270 """Add a group to the system
271
272 Will log but otherwise succeed if the group already exists.
273
274 :param str group_name: group to create
275 :param bool system_group: Create system group
276 :param int gid: GID for user being created
277
278 :returns: The password database entry struct, as returned by `grp.getgrnam`
279 """
238 try:280 try:
239 group_info = grp.getgrnam(group_name)281 group_info = grp.getgrnam(group_name)
240 log('group {0} already exists!'.format(group_name))282 log('group {0} already exists!'.format(group_name))
283 if gid:
284 group_info = grp.getgrgid(gid)
285 log('group with gid {0} already exists!'.format(gid))
241 except KeyError:286 except KeyError:
242 log('creating group {0}'.format(group_name))287 log('creating group {0}'.format(group_name))
243 cmd = ['addgroup']288 cmd = ['addgroup']
289 if gid:
290 cmd.extend(['--gid', str(gid)])
244 if system_group:291 if system_group:
245 cmd.append('--system')292 cmd.append('--system')
246 else:293 else:
247294
=== modified file 'hooks/charmhelpers/fetch/__init__.py'
--- hooks/charmhelpers/fetch/__init__.py 2016-02-27 19:51:32 +0000
+++ hooks/charmhelpers/fetch/__init__.py 2016-06-04 02:32:00 +0000
@@ -106,6 +106,14 @@
106 'mitaka/proposed': 'trusty-proposed/mitaka',106 'mitaka/proposed': 'trusty-proposed/mitaka',
107 'trusty-mitaka/proposed': 'trusty-proposed/mitaka',107 'trusty-mitaka/proposed': 'trusty-proposed/mitaka',
108 'trusty-proposed/mitaka': 'trusty-proposed/mitaka',108 'trusty-proposed/mitaka': 'trusty-proposed/mitaka',
109 # Newton
110 'newton': 'xenial-updates/newton',
111 'xenial-newton': 'xenial-updates/newton',
112 'xenial-newton/updates': 'xenial-updates/newton',
113 'xenial-updates/newton': 'xenial-updates/newton',
114 'newton/proposed': 'xenial-proposed/newton',
115 'xenial-newton/proposed': 'xenial-proposed/newton',
116 'xenial-proposed/newton': 'xenial-proposed/newton',
109}117}
110118
111# The order of this list is very important. Handlers should be listed in from119# The order of this list is very important. Handlers should be listed in from

Subscribers

People subscribed via source and target branches

to all changes: