Merge lp:~hopem/charm-helpers/lp1522375 into lp:charm-helpers

Proposed by Edward Hope-Morley
Status: Merged
Merged at revision: 577
Proposed branch: lp:~hopem/charm-helpers/lp1522375
Merge into: lp:charm-helpers
Diff against target: 321 lines (+143/-83)
5 files modified
charmhelpers/contrib/openstack/context.py (+5/-83)
charmhelpers/contrib/openstack/exceptions.py (+6/-0)
charmhelpers/contrib/openstack/utils.py (+81/-0)
charmhelpers/contrib/storage/linux/ceph.py (+41/-0)
tests/contrib/storage/test_linux_ceph.py (+10/-0)
To merge this branch: bzr merge lp:~hopem/charm-helpers/lp1522375
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+295612@code.launchpad.net
To post a comment you must log in.
Revision history for this message
James Page (james-page) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/openstack/context.py'
--- charmhelpers/contrib/openstack/context.py 2016-04-04 18:06:59 +0000
+++ charmhelpers/contrib/openstack/context.py 2016-05-25 12:22:56 +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 'charmhelpers/contrib/openstack/exceptions.py'
--- charmhelpers/contrib/openstack/exceptions.py 1970-01-01 00:00:00 +0000
+++ charmhelpers/contrib/openstack/exceptions.py 2016-05-25 12:22:56 +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 'charmhelpers/contrib/openstack/utils.py'
--- charmhelpers/contrib/openstack/utils.py 2016-05-20 18:40:40 +0000
+++ charmhelpers/contrib/openstack/utils.py 2016-05-25 12:22:56 +0000
@@ -47,6 +47,7 @@
47 charm_dir,47 charm_dir,
48 DEBUG,48 DEBUG,
49 INFO,49 INFO,
50 ERROR,
50 related_units,51 related_units,
51 relation_ids,52 relation_ids,
52 relation_set,53 relation_set,
@@ -83,6 +84,7 @@
83from charmhelpers.fetch import apt_install, apt_cache, install_remote84from charmhelpers.fetch import apt_install, apt_cache, install_remote
84from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk85from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
85from 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
8688
87CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"89CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
88CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'90CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
@@ -1616,3 +1618,82 @@
1616 restart_functions)1618 restart_functions)
1617 return wrapped_f1619 return wrapped_f
1618 return wrap1620 return wrap
1621
1622
1623def config_flags_parser(config_flags):
1624 """Parses config flags string into dict.
1625
1626 This parsing method supports a few different formats for the config
1627 flag values to be parsed:
1628
1629 1. A string in the simple format of key=value pairs, with the possibility
1630 of specifying multiple key value pairs within the same string. For
1631 example, a string in the format of 'key1=value1, key2=value2' will
1632 return a dict of:
1633
1634 {'key1': 'value1',
1635 'key2': 'value2'}.
1636
1637 2. A string in the above format, but supporting a comma-delimited list
1638 of values for the same key. For example, a string in the format of
1639 'key1=value1, key2=value3,value4,value5' will return a dict of:
1640
1641 {'key1', 'value1',
1642 'key2', 'value2,value3,value4'}
1643
1644 3. A string containing a colon character (:) prior to an equal
1645 character (=) will be treated as yaml and parsed as such. This can be
1646 used to specify more complex key value pairs. For example,
1647 a string in the format of 'key1: subkey1=value1, subkey2=value2' will
1648 return a dict of:
1649
1650 {'key1', 'subkey1=value1, subkey2=value2'}
1651
1652 The provided config_flags string may be a list of comma-separated values
1653 which themselves may be comma-separated list of values.
1654 """
1655 # If we find a colon before an equals sign then treat it as yaml.
1656 # Note: limit it to finding the colon first since this indicates assignment
1657 # for inline yaml.
1658 colon = config_flags.find(':')
1659 equals = config_flags.find('=')
1660 if colon > 0:
1661 if colon < equals or equals < 0:
1662 return yaml.safe_load(config_flags)
1663
1664 if config_flags.find('==') >= 0:
1665 juju_log("config_flags is not in expected format (key=value)",
1666 level=ERROR)
1667 raise OSContextError
1668
1669 # strip the following from each value.
1670 post_strippers = ' ,'
1671 # we strip any leading/trailing '=' or ' ' from the string then
1672 # split on '='.
1673 split = config_flags.strip(' =').split('=')
1674 limit = len(split)
1675 flags = {}
1676 for i in range(0, limit - 1):
1677 current = split[i]
1678 next = split[i + 1]
1679 vindex = next.rfind(',')
1680 if (i == limit - 2) or (vindex < 0):
1681 value = next
1682 else:
1683 value = next[:vindex]
1684
1685 if i == 0:
1686 key = current
1687 else:
1688 # if this not the first entry, expect an embedded key.
1689 index = current.rfind(',')
1690 if index < 0:
1691 juju_log("Invalid config value(s) at index %s" % (i),
1692 level=ERROR)
1693 raise OSContextError
1694 key = current[index + 1:]
1695
1696 # Add to collection.
1697 flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
1698
1699 return flags
16191700
=== modified file 'charmhelpers/contrib/storage/linux/ceph.py'
--- charmhelpers/contrib/storage/linux/ceph.py 2016-04-20 10:51:23 +0000
+++ charmhelpers/contrib/storage/linux/ceph.py 2016-05-25 12:22:56 +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()).symmetric_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 'tests/contrib/storage/test_linux_ceph.py'
--- tests/contrib/storage/test_linux_ceph.py 2016-04-20 10:51:23 +0000
+++ tests/contrib/storage/test_linux_ceph.py 2016-05-25 12:22:56 +0000
@@ -1298,3 +1298,13 @@
1298 self.assertEqual(actual['ops'][0]['replicas'], 4)1298 self.assertEqual(actual['ops'][0]['replicas'], 4)
1299 self.assertEqual(actual['ops'][0]['op'], 'create-pool')1299 self.assertEqual(actual['ops'][0]['op'], 'create-pool')
1300 self.assertEqual(actual['ops'][0]['name'], 'glance')1300 self.assertEqual(actual['ops'][0]['name'], 'glance')
1301
1302 @patch.object(ceph_utils, 'config')
1303 def test_ceph_conf_context(self, mock_config):
1304 mock_config.return_value = "{'osd': {'foo': 1}}"
1305 ctxt = ceph_utils.CephConfContext()()
1306 self.assertEqual({'osd': {'foo': 1}}, ctxt)
1307 ctxt = ceph_utils.CephConfContext(['osd', 'mon'])()
1308 mock_config.return_value = ("{'osd': {'foo': 1},"
1309 "'unknown': {'blah': 1}}")
1310 self.assertEqual({'osd': {'foo': 1}}, ctxt)

Subscribers

People subscribed via source and target branches