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