Merge ~barryprice/charm-nrpe/+git/nrpe-charm:master into ~nrpe-charmers/charm-nrpe:master
- Git
- lp:~barryprice/charm-nrpe/+git/nrpe-charm
- master
- Merge into master
Proposed by
Barry Price
Status: | Superseded |
---|---|
Proposed branch: | ~barryprice/charm-nrpe/+git/nrpe-charm:master |
Merge into: | ~nrpe-charmers/charm-nrpe:master |
Diff against target: |
544 lines (+227/-58) 11 files modified
bin/charm_helpers_sync.py (+27/-22) hooks/charmhelpers/core/hookenv.py (+69/-2) hooks/charmhelpers/core/host.py (+74/-1) hooks/charmhelpers/core/host_factory/ubuntu.py (+1/-0) hooks/charmhelpers/core/services/base.py (+6/-15) hooks/charmhelpers/core/strutils.py (+11/-5) hooks/charmhelpers/core/templating.py (+18/-9) hooks/charmhelpers/core/unitdata.py (+3/-1) hooks/charmhelpers/fetch/snap.py (+16/-0) hooks/charmhelpers/fetch/ubuntu.py (+1/-1) metadata.yaml (+1/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Junien F | Approve | ||
Review via email:
|
Commit message
Freshen charm_helpers_
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision f2747e0ca46b6ac
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/bin/charm_helpers_sync.py b/bin/charm_helpers_sync.py | |||
2 | index f67fdb9..e3f0e74 100644 | |||
3 | --- a/bin/charm_helpers_sync.py | |||
4 | +++ b/bin/charm_helpers_sync.py | |||
5 | @@ -2,19 +2,17 @@ | |||
6 | 2 | 2 | ||
7 | 3 | # Copyright 2014-2015 Canonical Limited. | 3 | # Copyright 2014-2015 Canonical Limited. |
8 | 4 | # | 4 | # |
10 | 5 | # This file is part of charm-helpers. | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
11 | 6 | # you may not use this file except in compliance with the License. | ||
12 | 7 | # You may obtain a copy of the License at | ||
13 | 6 | # | 8 | # |
17 | 7 | # charm-helpers is free software: you can redistribute it and/or modify | 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
15 | 8 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
16 | 9 | # published by the Free Software Foundation. | ||
18 | 10 | # | 10 | # |
26 | 11 | # charm-helpers is distributed in the hope that it will be useful, | 11 | # Unless required by applicable law or agreed to in writing, software |
27 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
28 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
29 | 14 | # GNU Lesser General Public License for more details. | 14 | # See the License for the specific language governing permissions and |
30 | 15 | # | 15 | # limitations under the License. |
24 | 16 | # You should have received a copy of the GNU Lesser General Public License | ||
25 | 17 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
31 | 18 | 16 | ||
32 | 19 | # Authors: | 17 | # Authors: |
33 | 20 | # Adam Gandelman <adamg@ubuntu.com> | 18 | # Adam Gandelman <adamg@ubuntu.com> |
34 | @@ -31,7 +29,7 @@ from fnmatch import fnmatch | |||
35 | 31 | 29 | ||
36 | 32 | import six | 30 | import six |
37 | 33 | 31 | ||
39 | 34 | CHARM_HELPERS_BRANCH = 'lp:charm-helpers' | 32 | CHARM_HELPERS_REPO = 'https://github.com/juju/charm-helpers' |
40 | 35 | 33 | ||
41 | 36 | 34 | ||
42 | 37 | def parse_config(conf_file): | 35 | def parse_config(conf_file): |
43 | @@ -41,10 +39,16 @@ def parse_config(conf_file): | |||
44 | 41 | return yaml.load(open(conf_file).read()) | 39 | return yaml.load(open(conf_file).read()) |
45 | 42 | 40 | ||
46 | 43 | 41 | ||
48 | 44 | def clone_helpers(work_dir, branch): | 42 | def clone_helpers(work_dir, repo): |
49 | 45 | dest = os.path.join(work_dir, 'charm-helpers') | 43 | dest = os.path.join(work_dir, 'charm-helpers') |
52 | 46 | logging.info('Checking out %s to %s.' % (branch, dest)) | 44 | logging.info('Cloning out %s to %s.' % (repo, dest)) |
53 | 47 | cmd = ['bzr', 'checkout', '--lightweight', branch, dest] | 45 | branch = None |
54 | 46 | if '@' in repo: | ||
55 | 47 | repo, branch = repo.split('@', 1) | ||
56 | 48 | cmd = ['git', 'clone', '--depth=1'] | ||
57 | 49 | if branch is not None: | ||
58 | 50 | cmd += ['--branch', branch] | ||
59 | 51 | cmd += [repo, dest] | ||
60 | 48 | subprocess.check_call(cmd) | 52 | subprocess.check_call(cmd) |
61 | 49 | return dest | 53 | return dest |
62 | 50 | 54 | ||
63 | @@ -193,14 +197,15 @@ def sync_helpers(include, src, dest, options=None): | |||
64 | 193 | inc, opts = extract_options(m, global_options) | 197 | inc, opts = extract_options(m, global_options) |
65 | 194 | sync(src, dest, '%s.%s' % (k, inc), opts) | 198 | sync(src, dest, '%s.%s' % (k, inc), opts) |
66 | 195 | 199 | ||
67 | 200 | |||
68 | 196 | if __name__ == '__main__': | 201 | if __name__ == '__main__': |
69 | 197 | parser = optparse.OptionParser() | 202 | parser = optparse.OptionParser() |
70 | 198 | parser.add_option('-c', '--config', action='store', dest='config', | 203 | parser.add_option('-c', '--config', action='store', dest='config', |
71 | 199 | default=None, help='helper config file') | 204 | default=None, help='helper config file') |
72 | 200 | parser.add_option('-D', '--debug', action='store_true', dest='debug', | 205 | parser.add_option('-D', '--debug', action='store_true', dest='debug', |
73 | 201 | default=False, help='debug') | 206 | default=False, help='debug') |
76 | 202 | parser.add_option('-b', '--branch', action='store', dest='branch', | 207 | parser.add_option('-r', '--repository', action='store', dest='repo', |
77 | 203 | help='charm-helpers bzr branch (overrides config)') | 208 | help='charm-helpers git repository (overrides config)') |
78 | 204 | parser.add_option('-d', '--destination', action='store', dest='dest_dir', | 209 | parser.add_option('-d', '--destination', action='store', dest='dest_dir', |
79 | 205 | help='sync destination dir (overrides config)') | 210 | help='sync destination dir (overrides config)') |
80 | 206 | (opts, args) = parser.parse_args() | 211 | (opts, args) = parser.parse_args() |
81 | @@ -219,10 +224,10 @@ if __name__ == '__main__': | |||
82 | 219 | else: | 224 | else: |
83 | 220 | config = {} | 225 | config = {} |
84 | 221 | 226 | ||
89 | 222 | if 'branch' not in config: | 227 | if 'repo' not in config: |
90 | 223 | config['branch'] = CHARM_HELPERS_BRANCH | 228 | config['repo'] = CHARM_HELPERS_REPO |
91 | 224 | if opts.branch: | 229 | if opts.repo: |
92 | 225 | config['branch'] = opts.branch | 230 | config['repo'] = opts.repo |
93 | 226 | if opts.dest_dir: | 231 | if opts.dest_dir: |
94 | 227 | config['destination'] = opts.dest_dir | 232 | config['destination'] = opts.dest_dir |
95 | 228 | 233 | ||
96 | @@ -242,7 +247,7 @@ if __name__ == '__main__': | |||
97 | 242 | sync_options = config['options'] | 247 | sync_options = config['options'] |
98 | 243 | tmpd = tempfile.mkdtemp() | 248 | tmpd = tempfile.mkdtemp() |
99 | 244 | try: | 249 | try: |
101 | 245 | checkout = clone_helpers(tmpd, config['branch']) | 250 | checkout = clone_helpers(tmpd, config['repo']) |
102 | 246 | sync_helpers(config['include'], checkout, config['destination'], | 251 | sync_helpers(config['include'], checkout, config['destination'], |
103 | 247 | options=sync_options) | 252 | options=sync_options) |
104 | 248 | except Exception as e: | 253 | except Exception as e: |
105 | diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py | |||
106 | index c7feeaf..7ed1cc4 100644 | |||
107 | --- a/hooks/charmhelpers/core/hookenv.py | |||
108 | +++ b/hooks/charmhelpers/core/hookenv.py | |||
109 | @@ -22,6 +22,7 @@ from __future__ import print_function | |||
110 | 22 | import copy | 22 | import copy |
111 | 23 | from distutils.version import LooseVersion | 23 | from distutils.version import LooseVersion |
112 | 24 | from functools import wraps | 24 | from functools import wraps |
113 | 25 | from collections import namedtuple | ||
114 | 25 | import glob | 26 | import glob |
115 | 26 | import os | 27 | import os |
116 | 27 | import json | 28 | import json |
117 | @@ -38,6 +39,7 @@ if not six.PY3: | |||
118 | 38 | else: | 39 | else: |
119 | 39 | from collections import UserDict | 40 | from collections import UserDict |
120 | 40 | 41 | ||
121 | 42 | |||
122 | 41 | CRITICAL = "CRITICAL" | 43 | CRITICAL = "CRITICAL" |
123 | 42 | ERROR = "ERROR" | 44 | ERROR = "ERROR" |
124 | 43 | WARNING = "WARNING" | 45 | WARNING = "WARNING" |
125 | @@ -343,6 +345,7 @@ class Config(dict): | |||
126 | 343 | 345 | ||
127 | 344 | """ | 346 | """ |
128 | 345 | with open(self.path, 'w') as f: | 347 | with open(self.path, 'w') as f: |
129 | 348 | os.fchmod(f.fileno(), 0o600) | ||
130 | 346 | json.dump(self, f) | 349 | json.dump(self, f) |
131 | 347 | 350 | ||
132 | 348 | def _implicit_save(self): | 351 | def _implicit_save(self): |
133 | @@ -654,7 +657,7 @@ def _port_op(op_name, port, protocol="TCP"): | |||
134 | 654 | _args.append('{}/{}'.format(port, protocol)) | 657 | _args.append('{}/{}'.format(port, protocol)) |
135 | 655 | try: | 658 | try: |
136 | 656 | subprocess.check_call(_args) | 659 | subprocess.check_call(_args) |
138 | 657 | except: | 660 | except subprocess.CalledProcessError: |
139 | 658 | # Older Juju pre 2.3 doesn't support ICMP | 661 | # Older Juju pre 2.3 doesn't support ICMP |
140 | 659 | # so treat it as a no-op if it fails. | 662 | # so treat it as a no-op if it fails. |
141 | 660 | if not icmp: | 663 | if not icmp: |
142 | @@ -685,6 +688,17 @@ def close_ports(start, end, protocol="TCP"): | |||
143 | 685 | subprocess.check_call(_args) | 688 | subprocess.check_call(_args) |
144 | 686 | 689 | ||
145 | 687 | 690 | ||
146 | 691 | def opened_ports(): | ||
147 | 692 | """Get the opened ports | ||
148 | 693 | |||
149 | 694 | *Note that this will only show ports opened in a previous hook* | ||
150 | 695 | |||
151 | 696 | :returns: Opened ports as a list of strings: ``['8080/tcp', '8081-8083/tcp']`` | ||
152 | 697 | """ | ||
153 | 698 | _args = ['opened-ports', '--format=json'] | ||
154 | 699 | return json.loads(subprocess.check_output(_args).decode('UTF-8')) | ||
155 | 700 | |||
156 | 701 | |||
157 | 688 | @cached | 702 | @cached |
158 | 689 | def unit_get(attribute): | 703 | def unit_get(attribute): |
159 | 690 | """Get the unit ID for the remote unit""" | 704 | """Get the unit ID for the remote unit""" |
160 | @@ -806,6 +820,10 @@ class Hooks(object): | |||
161 | 806 | return wrapper | 820 | return wrapper |
162 | 807 | 821 | ||
163 | 808 | 822 | ||
164 | 823 | class NoNetworkBinding(Exception): | ||
165 | 824 | pass | ||
166 | 825 | |||
167 | 826 | |||
168 | 809 | def charm_dir(): | 827 | def charm_dir(): |
169 | 810 | """Return the root directory of the current charm""" | 828 | """Return the root directory of the current charm""" |
170 | 811 | d = os.environ.get('JUJU_CHARM_DIR') | 829 | d = os.environ.get('JUJU_CHARM_DIR') |
171 | @@ -1092,7 +1110,17 @@ def network_get_primary_address(binding): | |||
172 | 1092 | :raise: NotImplementedError if run on Juju < 2.0 | 1110 | :raise: NotImplementedError if run on Juju < 2.0 |
173 | 1093 | ''' | 1111 | ''' |
174 | 1094 | cmd = ['network-get', '--primary-address', binding] | 1112 | cmd = ['network-get', '--primary-address', binding] |
176 | 1095 | return subprocess.check_output(cmd).decode('UTF-8').strip() | 1113 | try: |
177 | 1114 | response = subprocess.check_output( | ||
178 | 1115 | cmd, | ||
179 | 1116 | stderr=subprocess.STDOUT).decode('UTF-8').strip() | ||
180 | 1117 | except CalledProcessError as e: | ||
181 | 1118 | if 'no network config found for binding' in e.output.decode('UTF-8'): | ||
182 | 1119 | raise NoNetworkBinding("No network binding for {}" | ||
183 | 1120 | .format(binding)) | ||
184 | 1121 | else: | ||
185 | 1122 | raise | ||
186 | 1123 | return response | ||
187 | 1096 | 1124 | ||
188 | 1097 | 1125 | ||
189 | 1098 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) | 1126 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
190 | @@ -1153,3 +1181,42 @@ def meter_info(): | |||
191 | 1153 | """Get the meter status information, if running in the meter-status-changed | 1181 | """Get the meter status information, if running in the meter-status-changed |
192 | 1154 | hook.""" | 1182 | hook.""" |
193 | 1155 | return os.environ.get('JUJU_METER_INFO') | 1183 | return os.environ.get('JUJU_METER_INFO') |
194 | 1184 | |||
195 | 1185 | |||
196 | 1186 | def iter_units_for_relation_name(relation_name): | ||
197 | 1187 | """Iterate through all units in a relation | ||
198 | 1188 | |||
199 | 1189 | Generator that iterates through all the units in a relation and yields | ||
200 | 1190 | a named tuple with rid and unit field names. | ||
201 | 1191 | |||
202 | 1192 | Usage: | ||
203 | 1193 | data = [(u.rid, u.unit) | ||
204 | 1194 | for u in iter_units_for_relation_name(relation_name)] | ||
205 | 1195 | |||
206 | 1196 | :param relation_name: string relation name | ||
207 | 1197 | :yield: Named Tuple with rid and unit field names | ||
208 | 1198 | """ | ||
209 | 1199 | RelatedUnit = namedtuple('RelatedUnit', 'rid, unit') | ||
210 | 1200 | for rid in relation_ids(relation_name): | ||
211 | 1201 | for unit in related_units(rid): | ||
212 | 1202 | yield RelatedUnit(rid, unit) | ||
213 | 1203 | |||
214 | 1204 | |||
215 | 1205 | def ingress_address(rid=None, unit=None): | ||
216 | 1206 | """ | ||
217 | 1207 | Retrieve the ingress-address from a relation when available. Otherwise, | ||
218 | 1208 | return the private-address. This function is to be used on the consuming | ||
219 | 1209 | side of the relation. | ||
220 | 1210 | |||
221 | 1211 | Usage: | ||
222 | 1212 | addresses = [ingress_address(rid=u.rid, unit=u.unit) | ||
223 | 1213 | for u in iter_units_for_relation_name(relation_name)] | ||
224 | 1214 | |||
225 | 1215 | :param rid: string relation id | ||
226 | 1216 | :param unit: string unit name | ||
227 | 1217 | :side effect: calls relation_get | ||
228 | 1218 | :return: string IP address | ||
229 | 1219 | """ | ||
230 | 1220 | settings = relation_get(rid=rid, unit=unit) | ||
231 | 1221 | return (settings.get('ingress-address') or | ||
232 | 1222 | settings.get('private-address')) | ||
233 | diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py | |||
234 | index 5656e2f..fd14d60 100644 | |||
235 | --- a/hooks/charmhelpers/core/host.py | |||
236 | +++ b/hooks/charmhelpers/core/host.py | |||
237 | @@ -34,7 +34,7 @@ import six | |||
238 | 34 | 34 | ||
239 | 35 | from contextlib import contextmanager | 35 | from contextlib import contextmanager |
240 | 36 | from collections import OrderedDict | 36 | from collections import OrderedDict |
242 | 37 | from .hookenv import log, DEBUG | 37 | from .hookenv import log, DEBUG, local_unit |
243 | 38 | from .fstab import Fstab | 38 | from .fstab import Fstab |
244 | 39 | from charmhelpers.osplatform import get_platform | 39 | from charmhelpers.osplatform import get_platform |
245 | 40 | 40 | ||
246 | @@ -441,6 +441,49 @@ def add_user_to_group(username, group): | |||
247 | 441 | subprocess.check_call(cmd) | 441 | subprocess.check_call(cmd) |
248 | 442 | 442 | ||
249 | 443 | 443 | ||
250 | 444 | def chage(username, lastday=None, expiredate=None, inactive=None, | ||
251 | 445 | mindays=None, maxdays=None, root=None, warndays=None): | ||
252 | 446 | """Change user password expiry information | ||
253 | 447 | |||
254 | 448 | :param str username: User to update | ||
255 | 449 | :param str lastday: Set when password was changed in YYYY-MM-DD format | ||
256 | 450 | :param str expiredate: Set when user's account will no longer be | ||
257 | 451 | accessible in YYYY-MM-DD format. | ||
258 | 452 | -1 will remove an account expiration date. | ||
259 | 453 | :param str inactive: Set the number of days of inactivity after a password | ||
260 | 454 | has expired before the account is locked. | ||
261 | 455 | -1 will remove an account's inactivity. | ||
262 | 456 | :param str mindays: Set the minimum number of days between password | ||
263 | 457 | changes to MIN_DAYS. | ||
264 | 458 | 0 indicates the password can be changed anytime. | ||
265 | 459 | :param str maxdays: Set the maximum number of days during which a | ||
266 | 460 | password is valid. | ||
267 | 461 | -1 as MAX_DAYS will remove checking maxdays | ||
268 | 462 | :param str root: Apply changes in the CHROOT_DIR directory | ||
269 | 463 | :param str warndays: Set the number of days of warning before a password | ||
270 | 464 | change is required | ||
271 | 465 | :raises subprocess.CalledProcessError: if call to chage fails | ||
272 | 466 | """ | ||
273 | 467 | cmd = ['chage'] | ||
274 | 468 | if root: | ||
275 | 469 | cmd.extend(['--root', root]) | ||
276 | 470 | if lastday: | ||
277 | 471 | cmd.extend(['--lastday', lastday]) | ||
278 | 472 | if expiredate: | ||
279 | 473 | cmd.extend(['--expiredate', expiredate]) | ||
280 | 474 | if inactive: | ||
281 | 475 | cmd.extend(['--inactive', inactive]) | ||
282 | 476 | if mindays: | ||
283 | 477 | cmd.extend(['--mindays', mindays]) | ||
284 | 478 | if maxdays: | ||
285 | 479 | cmd.extend(['--maxdays', maxdays]) | ||
286 | 480 | if warndays: | ||
287 | 481 | cmd.extend(['--warndays', warndays]) | ||
288 | 482 | cmd.append(username) | ||
289 | 483 | subprocess.check_call(cmd) | ||
290 | 484 | |||
291 | 485 | remove_password_expiry = functools.partial(chage, expiredate='-1', inactive='-1', mindays='0', maxdays='-1') | ||
292 | 486 | |||
293 | 444 | def rsync(from_path, to_path, flags='-r', options=None, timeout=None): | 487 | def rsync(from_path, to_path, flags='-r', options=None, timeout=None): |
294 | 445 | """Replicate the contents of a path""" | 488 | """Replicate the contents of a path""" |
295 | 446 | options = options or ['--delete', '--executability'] | 489 | options = options or ['--delete', '--executability'] |
296 | @@ -506,6 +549,8 @@ def write_file(path, content, owner='root', group='root', perms=0o444): | |||
297 | 506 | with open(path, 'wb') as target: | 549 | with open(path, 'wb') as target: |
298 | 507 | os.fchown(target.fileno(), uid, gid) | 550 | os.fchown(target.fileno(), uid, gid) |
299 | 508 | os.fchmod(target.fileno(), perms) | 551 | os.fchmod(target.fileno(), perms) |
300 | 552 | if six.PY3 and isinstance(content, six.string_types): | ||
301 | 553 | content = content.encode('UTF-8') | ||
302 | 509 | target.write(content) | 554 | target.write(content) |
303 | 510 | return | 555 | return |
304 | 511 | # the contents were the same, but we might still need to change the | 556 | # the contents were the same, but we might still need to change the |
305 | @@ -946,3 +991,31 @@ def updatedb(updatedb_text, new_path): | |||
306 | 946 | lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths)) | 991 | lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths)) |
307 | 947 | output = "\n".join(lines) | 992 | output = "\n".join(lines) |
308 | 948 | return output | 993 | return output |
309 | 994 | |||
310 | 995 | |||
311 | 996 | def modulo_distribution(modulo=3, wait=30): | ||
312 | 997 | """ Modulo distribution | ||
313 | 998 | |||
314 | 999 | This helper uses the unit number, a modulo value and a constant wait time | ||
315 | 1000 | to produce a calculated wait time distribution. This is useful in large | ||
316 | 1001 | scale deployments to distribute load during an expensive operation such as | ||
317 | 1002 | service restarts. | ||
318 | 1003 | |||
319 | 1004 | If you have 1000 nodes that need to restart 100 at a time 1 minute at a | ||
320 | 1005 | time: | ||
321 | 1006 | |||
322 | 1007 | time.wait(modulo_distribution(modulo=100, wait=60)) | ||
323 | 1008 | restart() | ||
324 | 1009 | |||
325 | 1010 | If you need restarts to happen serially set modulo to the exact number of | ||
326 | 1011 | nodes and set a high constant wait time: | ||
327 | 1012 | |||
328 | 1013 | time.wait(modulo_distribution(modulo=10, wait=120)) | ||
329 | 1014 | restart() | ||
330 | 1015 | |||
331 | 1016 | @param modulo: int The modulo number creates the group distribution | ||
332 | 1017 | @param wait: int The constant time wait value | ||
333 | 1018 | @return: int Calculated time to wait for unit operation | ||
334 | 1019 | """ | ||
335 | 1020 | unit_number = int(local_unit().split('/')[1]) | ||
336 | 1021 | return (unit_number % modulo) * wait | ||
337 | diff --git a/hooks/charmhelpers/core/host_factory/ubuntu.py b/hooks/charmhelpers/core/host_factory/ubuntu.py | |||
338 | index d8dc378..99451b5 100644 | |||
339 | --- a/hooks/charmhelpers/core/host_factory/ubuntu.py | |||
340 | +++ b/hooks/charmhelpers/core/host_factory/ubuntu.py | |||
341 | @@ -20,6 +20,7 @@ UBUNTU_RELEASES = ( | |||
342 | 20 | 'yakkety', | 20 | 'yakkety', |
343 | 21 | 'zesty', | 21 | 'zesty', |
344 | 22 | 'artful', | 22 | 'artful', |
345 | 23 | 'bionic', | ||
346 | 23 | ) | 24 | ) |
347 | 24 | 25 | ||
348 | 25 | 26 | ||
349 | diff --git a/hooks/charmhelpers/core/services/base.py b/hooks/charmhelpers/core/services/base.py | |||
350 | index 345b60d..ca9dc99 100644 | |||
351 | --- a/hooks/charmhelpers/core/services/base.py | |||
352 | +++ b/hooks/charmhelpers/core/services/base.py | |||
353 | @@ -313,26 +313,17 @@ class PortManagerCallback(ManagerCallback): | |||
354 | 313 | with open(port_file) as fp: | 313 | with open(port_file) as fp: |
355 | 314 | old_ports = fp.read().split(',') | 314 | old_ports = fp.read().split(',') |
356 | 315 | for old_port in old_ports: | 315 | for old_port in old_ports: |
359 | 316 | if bool(old_port) and not self.ports_contains(old_port, new_ports): | 316 | if bool(old_port): |
360 | 317 | hookenv.close_port(old_port) | 317 | old_port = int(old_port) |
361 | 318 | if old_port not in new_ports: | ||
362 | 319 | hookenv.close_port(old_port) | ||
363 | 318 | with open(port_file, 'w') as fp: | 320 | with open(port_file, 'w') as fp: |
364 | 319 | fp.write(','.join(str(port) for port in new_ports)) | 321 | fp.write(','.join(str(port) for port in new_ports)) |
365 | 320 | for port in new_ports: | 322 | for port in new_ports: |
366 | 321 | # A port is either a number or 'ICMP' | ||
367 | 322 | protocol = 'TCP' | ||
368 | 323 | if str(port).upper() == 'ICMP': | ||
369 | 324 | protocol = 'ICMP' | ||
370 | 325 | if event_name == 'start': | 323 | if event_name == 'start': |
372 | 326 | hookenv.open_port(port, protocol) | 324 | hookenv.open_port(port) |
373 | 327 | elif event_name == 'stop': | 325 | elif event_name == 'stop': |
382 | 328 | hookenv.close_port(port, protocol) | 326 | hookenv.close_port(port) |
375 | 329 | |||
376 | 330 | def ports_contains(self, port, ports): | ||
377 | 331 | if not bool(port): | ||
378 | 332 | return False | ||
379 | 333 | if str(port).upper() != 'ICMP': | ||
380 | 334 | port = int(port) | ||
381 | 335 | return port in ports | ||
383 | 336 | 327 | ||
384 | 337 | 328 | ||
385 | 338 | def service_stop(service_name): | 329 | def service_stop(service_name): |
386 | diff --git a/hooks/charmhelpers/core/strutils.py b/hooks/charmhelpers/core/strutils.py | |||
387 | index 685dabd..e8df045 100644 | |||
388 | --- a/hooks/charmhelpers/core/strutils.py | |||
389 | +++ b/hooks/charmhelpers/core/strutils.py | |||
390 | @@ -61,13 +61,19 @@ def bytes_from_string(value): | |||
391 | 61 | if isinstance(value, six.string_types): | 61 | if isinstance(value, six.string_types): |
392 | 62 | value = six.text_type(value) | 62 | value = six.text_type(value) |
393 | 63 | else: | 63 | else: |
395 | 64 | msg = "Unable to interpret non-string value '%s' as boolean" % (value) | 64 | msg = "Unable to interpret non-string value '%s' as bytes" % (value) |
396 | 65 | raise ValueError(msg) | 65 | raise ValueError(msg) |
397 | 66 | matches = re.match("([0-9]+)([a-zA-Z]+)", value) | 66 | matches = re.match("([0-9]+)([a-zA-Z]+)", value) |
402 | 67 | if not matches: | 67 | if matches: |
403 | 68 | msg = "Unable to interpret string value '%s' as bytes" % (value) | 68 | size = int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)]) |
404 | 69 | raise ValueError(msg) | 69 | else: |
405 | 70 | return int(matches.group(1)) * (1024 ** BYTE_POWER[matches.group(2)]) | 70 | # Assume that value passed in is bytes |
406 | 71 | try: | ||
407 | 72 | size = int(value) | ||
408 | 73 | except ValueError: | ||
409 | 74 | msg = "Unable to interpret string value '%s' as bytes" % (value) | ||
410 | 75 | raise ValueError(msg) | ||
411 | 76 | return size | ||
412 | 71 | 77 | ||
413 | 72 | 78 | ||
414 | 73 | class BasicStringComparator(object): | 79 | class BasicStringComparator(object): |
415 | diff --git a/hooks/charmhelpers/core/templating.py b/hooks/charmhelpers/core/templating.py | |||
416 | index 7b801a3..9014015 100644 | |||
417 | --- a/hooks/charmhelpers/core/templating.py | |||
418 | +++ b/hooks/charmhelpers/core/templating.py | |||
419 | @@ -20,7 +20,8 @@ from charmhelpers.core import hookenv | |||
420 | 20 | 20 | ||
421 | 21 | 21 | ||
422 | 22 | def render(source, target, context, owner='root', group='root', | 22 | def render(source, target, context, owner='root', group='root', |
424 | 23 | perms=0o444, templates_dir=None, encoding='UTF-8', template_loader=None): | 23 | perms=0o444, templates_dir=None, encoding='UTF-8', |
425 | 24 | template_loader=None, config_template=None): | ||
426 | 24 | """ | 25 | """ |
427 | 25 | Render a template. | 26 | Render a template. |
428 | 26 | 27 | ||
429 | @@ -32,6 +33,9 @@ def render(source, target, context, owner='root', group='root', | |||
430 | 32 | The context should be a dict containing the values to be replaced in the | 33 | The context should be a dict containing the values to be replaced in the |
431 | 33 | template. | 34 | template. |
432 | 34 | 35 | ||
433 | 36 | config_template may be provided to render from a provided template instead | ||
434 | 37 | of loading from a file. | ||
435 | 38 | |||
436 | 35 | The `owner`, `group`, and `perms` options will be passed to `write_file`. | 39 | The `owner`, `group`, and `perms` options will be passed to `write_file`. |
437 | 36 | 40 | ||
438 | 37 | If omitted, `templates_dir` defaults to the `templates` folder in the charm. | 41 | If omitted, `templates_dir` defaults to the `templates` folder in the charm. |
439 | @@ -65,14 +69,19 @@ def render(source, target, context, owner='root', group='root', | |||
440 | 65 | if templates_dir is None: | 69 | if templates_dir is None: |
441 | 66 | templates_dir = os.path.join(hookenv.charm_dir(), 'templates') | 70 | templates_dir = os.path.join(hookenv.charm_dir(), 'templates') |
442 | 67 | template_env = Environment(loader=FileSystemLoader(templates_dir)) | 71 | template_env = Environment(loader=FileSystemLoader(templates_dir)) |
451 | 68 | try: | 72 | |
452 | 69 | source = source | 73 | # load from a string if provided explicitly |
453 | 70 | template = template_env.get_template(source) | 74 | if config_template is not None: |
454 | 71 | except exceptions.TemplateNotFound as e: | 75 | template = template_env.from_string(config_template) |
455 | 72 | hookenv.log('Could not load template %s from %s.' % | 76 | else: |
456 | 73 | (source, templates_dir), | 77 | try: |
457 | 74 | level=hookenv.ERROR) | 78 | source = source |
458 | 75 | raise e | 79 | template = template_env.get_template(source) |
459 | 80 | except exceptions.TemplateNotFound as e: | ||
460 | 81 | hookenv.log('Could not load template %s from %s.' % | ||
461 | 82 | (source, templates_dir), | ||
462 | 83 | level=hookenv.ERROR) | ||
463 | 84 | raise e | ||
464 | 76 | content = template.render(context) | 85 | content = template.render(context) |
465 | 77 | if target is not None: | 86 | if target is not None: |
466 | 78 | target_dir = os.path.dirname(target) | 87 | target_dir = os.path.dirname(target) |
467 | diff --git a/hooks/charmhelpers/core/unitdata.py b/hooks/charmhelpers/core/unitdata.py | |||
468 | index 54ec969..6d7b494 100644 | |||
469 | --- a/hooks/charmhelpers/core/unitdata.py | |||
470 | +++ b/hooks/charmhelpers/core/unitdata.py | |||
471 | @@ -175,6 +175,8 @@ class Storage(object): | |||
472 | 175 | else: | 175 | else: |
473 | 176 | self.db_path = os.path.join( | 176 | self.db_path = os.path.join( |
474 | 177 | os.environ.get('CHARM_DIR', ''), '.unit-state.db') | 177 | os.environ.get('CHARM_DIR', ''), '.unit-state.db') |
475 | 178 | with open(self.db_path, 'a') as f: | ||
476 | 179 | os.fchmod(f.fileno(), 0o600) | ||
477 | 178 | self.conn = sqlite3.connect('%s' % self.db_path) | 180 | self.conn = sqlite3.connect('%s' % self.db_path) |
478 | 179 | self.cursor = self.conn.cursor() | 181 | self.cursor = self.conn.cursor() |
479 | 180 | self.revision = None | 182 | self.revision = None |
480 | @@ -358,7 +360,7 @@ class Storage(object): | |||
481 | 358 | try: | 360 | try: |
482 | 359 | yield self.revision | 361 | yield self.revision |
483 | 360 | self.revision = None | 362 | self.revision = None |
485 | 361 | except: | 363 | except Exception: |
486 | 362 | self.flush(False) | 364 | self.flush(False) |
487 | 363 | self.revision = None | 365 | self.revision = None |
488 | 364 | raise | 366 | raise |
489 | diff --git a/hooks/charmhelpers/fetch/snap.py b/hooks/charmhelpers/fetch/snap.py | |||
490 | index 112a54c..395836c 100644 | |||
491 | --- a/hooks/charmhelpers/fetch/snap.py | |||
492 | +++ b/hooks/charmhelpers/fetch/snap.py | |||
493 | @@ -41,6 +41,10 @@ class CouldNotAcquireLockException(Exception): | |||
494 | 41 | pass | 41 | pass |
495 | 42 | 42 | ||
496 | 43 | 43 | ||
497 | 44 | class InvalidSnapChannel(Exception): | ||
498 | 45 | pass | ||
499 | 46 | |||
500 | 47 | |||
501 | 44 | def _snap_exec(commands): | 48 | def _snap_exec(commands): |
502 | 45 | """ | 49 | """ |
503 | 46 | Execute snap commands. | 50 | Execute snap commands. |
504 | @@ -132,3 +136,15 @@ def snap_refresh(packages, *flags): | |||
505 | 132 | 136 | ||
506 | 133 | log(message, level='INFO') | 137 | log(message, level='INFO') |
507 | 134 | return _snap_exec(['refresh'] + flags + packages) | 138 | return _snap_exec(['refresh'] + flags + packages) |
508 | 139 | |||
509 | 140 | |||
510 | 141 | def valid_snap_channel(channel): | ||
511 | 142 | """ Validate snap channel exists | ||
512 | 143 | |||
513 | 144 | :raises InvalidSnapChannel: When channel does not exist | ||
514 | 145 | :return: Boolean | ||
515 | 146 | """ | ||
516 | 147 | if channel.lower() in SNAP_CHANNELS: | ||
517 | 148 | return True | ||
518 | 149 | else: | ||
519 | 150 | raise InvalidSnapChannel("Invalid Snap Channel: {}".format(channel)) | ||
520 | diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/hooks/charmhelpers/fetch/ubuntu.py | |||
521 | index 40e1cb5..910e96a 100644 | |||
522 | --- a/hooks/charmhelpers/fetch/ubuntu.py | |||
523 | +++ b/hooks/charmhelpers/fetch/ubuntu.py | |||
524 | @@ -572,7 +572,7 @@ def get_upstream_version(package): | |||
525 | 572 | cache = apt_cache() | 572 | cache = apt_cache() |
526 | 573 | try: | 573 | try: |
527 | 574 | pkg = cache[package] | 574 | pkg = cache[package] |
529 | 575 | except: | 575 | except Exception: |
530 | 576 | # the package is unknown to the current apt cache. | 576 | # the package is unknown to the current apt cache. |
531 | 577 | return None | 577 | return None |
532 | 578 | 578 | ||
533 | diff --git a/metadata.yaml b/metadata.yaml | |||
534 | index d0f196f..ace705f 100644 | |||
535 | --- a/metadata.yaml | |||
536 | +++ b/metadata.yaml | |||
537 | @@ -29,6 +29,5 @@ requires: | |||
538 | 29 | series: | 29 | series: |
539 | 30 | - xenial | 30 | - xenial |
540 | 31 | - trusty | 31 | - trusty |
541 | 32 | - zesty | ||
542 | 33 | - yakkety | ||
543 | 34 | - artful | 32 | - artful |
544 | 33 | - bionic |
+1