Merge ~barryprice/charm-nrpe/+git/nrpe-charm:barryprice into ~nrpe-charmers/charm-nrpe:master
- Git
- lp:~barryprice/charm-nrpe/+git/nrpe-charm
- barryprice
- Merge into master
Proposed by
Barry Price
Status: | Merged |
---|---|
Approved by: | Paul Gear |
Approved revision: | 0ac9aba714497c262e47a6f8c2516685b67e0bed |
Merged at revision: | 1eca699520ad627d89ba08fce9ca5094b4f1df17 |
Proposed branch: | ~barryprice/charm-nrpe/+git/nrpe-charm:barryprice |
Merge into: | ~nrpe-charmers/charm-nrpe:master |
Diff against target: |
884 lines (+202/-387) 8 files modified
hooks/charmhelpers/core/hookenv.py (+61/-0) hooks/charmhelpers/core/host.py (+75/-64) hooks/charmhelpers/core/kernel.py (+21/-15) hooks/charmhelpers/fetch/__init__.py (+27/-297) hooks/charmhelpers/fetch/bzrurl.py (+4/-3) hooks/charmhelpers/fetch/giturl.py (+4/-3) tests/charmhelpers/contrib/amulet/deployment.py (+7/-3) tests/charmhelpers/contrib/amulet/utils.py (+3/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Gear (community) | Approve | ||
Review via email:
|
Commit message
Charmhelpers sync
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py | |||
2 | index 48b2b9d..e44e22b 100644 | |||
3 | --- a/hooks/charmhelpers/core/hookenv.py | |||
4 | +++ b/hooks/charmhelpers/core/hookenv.py | |||
5 | @@ -332,6 +332,8 @@ def config(scope=None): | |||
6 | 332 | config_cmd_line = ['config-get'] | 332 | config_cmd_line = ['config-get'] |
7 | 333 | if scope is not None: | 333 | if scope is not None: |
8 | 334 | config_cmd_line.append(scope) | 334 | config_cmd_line.append(scope) |
9 | 335 | else: | ||
10 | 336 | config_cmd_line.append('--all') | ||
11 | 335 | config_cmd_line.append('--format=json') | 337 | config_cmd_line.append('--format=json') |
12 | 336 | try: | 338 | try: |
13 | 337 | config_data = json.loads( | 339 | config_data = json.loads( |
14 | @@ -614,6 +616,20 @@ def close_port(port, protocol="TCP"): | |||
15 | 614 | subprocess.check_call(_args) | 616 | subprocess.check_call(_args) |
16 | 615 | 617 | ||
17 | 616 | 618 | ||
18 | 619 | def open_ports(start, end, protocol="TCP"): | ||
19 | 620 | """Opens a range of service network ports""" | ||
20 | 621 | _args = ['open-port'] | ||
21 | 622 | _args.append('{}-{}/{}'.format(start, end, protocol)) | ||
22 | 623 | subprocess.check_call(_args) | ||
23 | 624 | |||
24 | 625 | |||
25 | 626 | def close_ports(start, end, protocol="TCP"): | ||
26 | 627 | """Close a range of service network ports""" | ||
27 | 628 | _args = ['close-port'] | ||
28 | 629 | _args.append('{}-{}/{}'.format(start, end, protocol)) | ||
29 | 630 | subprocess.check_call(_args) | ||
30 | 631 | |||
31 | 632 | |||
32 | 617 | @cached | 633 | @cached |
33 | 618 | def unit_get(attribute): | 634 | def unit_get(attribute): |
34 | 619 | """Get the unit ID for the remote unit""" | 635 | """Get the unit ID for the remote unit""" |
35 | @@ -843,6 +859,20 @@ def translate_exc(from_exc, to_exc): | |||
36 | 843 | return inner_translate_exc1 | 859 | return inner_translate_exc1 |
37 | 844 | 860 | ||
38 | 845 | 861 | ||
39 | 862 | def application_version_set(version): | ||
40 | 863 | """Charm authors may trigger this command from any hook to output what | ||
41 | 864 | version of the application is running. This could be a package version, | ||
42 | 865 | for instance postgres version 9.5. It could also be a build number or | ||
43 | 866 | version control revision identifier, for instance git sha 6fb7ba68. """ | ||
44 | 867 | |||
45 | 868 | cmd = ['application-version-set'] | ||
46 | 869 | cmd.append(version) | ||
47 | 870 | try: | ||
48 | 871 | subprocess.check_call(cmd) | ||
49 | 872 | except OSError: | ||
50 | 873 | log("Application Version: {}".format(version)) | ||
51 | 874 | |||
52 | 875 | |||
53 | 846 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) | 876 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
54 | 847 | def is_leader(): | 877 | def is_leader(): |
55 | 848 | """Does the current unit hold the juju leadership | 878 | """Does the current unit hold the juju leadership |
56 | @@ -1005,3 +1035,34 @@ def network_get_primary_address(binding): | |||
57 | 1005 | ''' | 1035 | ''' |
58 | 1006 | cmd = ['network-get', '--primary-address', binding] | 1036 | cmd = ['network-get', '--primary-address', binding] |
59 | 1007 | return subprocess.check_output(cmd).decode('UTF-8').strip() | 1037 | return subprocess.check_output(cmd).decode('UTF-8').strip() |
60 | 1038 | |||
61 | 1039 | |||
62 | 1040 | def add_metric(*args, **kwargs): | ||
63 | 1041 | """Add metric values. Values may be expressed with keyword arguments. For | ||
64 | 1042 | metric names containing dashes, these may be expressed as one or more | ||
65 | 1043 | 'key=value' positional arguments. May only be called from the collect-metrics | ||
66 | 1044 | hook.""" | ||
67 | 1045 | _args = ['add-metric'] | ||
68 | 1046 | _kvpairs = [] | ||
69 | 1047 | _kvpairs.extend(args) | ||
70 | 1048 | _kvpairs.extend(['{}={}'.format(k, v) for k, v in kwargs.items()]) | ||
71 | 1049 | _args.extend(sorted(_kvpairs)) | ||
72 | 1050 | try: | ||
73 | 1051 | subprocess.check_call(_args) | ||
74 | 1052 | return | ||
75 | 1053 | except EnvironmentError as e: | ||
76 | 1054 | if e.errno != errno.ENOENT: | ||
77 | 1055 | raise | ||
78 | 1056 | log_message = 'add-metric failed: {}'.format(' '.join(_kvpairs)) | ||
79 | 1057 | log(log_message, level='INFO') | ||
80 | 1058 | |||
81 | 1059 | |||
82 | 1060 | def meter_status(): | ||
83 | 1061 | """Get the meter status, if running in the meter-status-changed hook.""" | ||
84 | 1062 | return os.environ.get('JUJU_METER_STATUS') | ||
85 | 1063 | |||
86 | 1064 | |||
87 | 1065 | def meter_info(): | ||
88 | 1066 | """Get the meter status information, if running in the meter-status-changed | ||
89 | 1067 | hook.""" | ||
90 | 1068 | return os.environ.get('JUJU_METER_INFO') | ||
91 | diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py | |||
92 | index 5306859..3638e65 100644 | |||
93 | --- a/hooks/charmhelpers/core/host.py | |||
94 | +++ b/hooks/charmhelpers/core/host.py | |||
95 | @@ -30,14 +30,31 @@ import subprocess | |||
96 | 30 | import hashlib | 30 | import hashlib |
97 | 31 | import functools | 31 | import functools |
98 | 32 | import itertools | 32 | import itertools |
99 | 33 | from contextlib import contextmanager | ||
100 | 34 | from collections import OrderedDict | ||
101 | 35 | |||
102 | 36 | import six | 33 | import six |
103 | 37 | 34 | ||
104 | 35 | from contextlib import contextmanager | ||
105 | 36 | from collections import OrderedDict | ||
106 | 38 | from .hookenv import log | 37 | from .hookenv import log |
107 | 39 | from .fstab import Fstab | 38 | from .fstab import Fstab |
109 | 40 | 39 | from charmhelpers.osplatform import get_platform | |
110 | 40 | |||
111 | 41 | __platform__ = get_platform() | ||
112 | 42 | if __platform__ == "ubuntu": | ||
113 | 43 | from charmhelpers.core.host_factory.ubuntu import ( | ||
114 | 44 | service_available, | ||
115 | 45 | add_new_group, | ||
116 | 46 | lsb_release, | ||
117 | 47 | cmp_pkgrevno, | ||
118 | 48 | ) # flake8: noqa -- ignore F401 for this import | ||
119 | 49 | elif __platform__ == "centos": | ||
120 | 50 | from charmhelpers.core.host_factory.centos import ( | ||
121 | 51 | service_available, | ||
122 | 52 | add_new_group, | ||
123 | 53 | lsb_release, | ||
124 | 54 | cmp_pkgrevno, | ||
125 | 55 | ) # flake8: noqa -- ignore F401 for this import | ||
126 | 56 | |||
127 | 57 | UPDATEDB_PATH = '/etc/updatedb.conf' | ||
128 | 41 | 58 | ||
129 | 42 | def service_start(service_name): | 59 | def service_start(service_name): |
130 | 43 | """Start a system service""" | 60 | """Start a system service""" |
131 | @@ -144,8 +161,11 @@ def service_running(service_name): | |||
132 | 144 | return False | 161 | return False |
133 | 145 | else: | 162 | else: |
134 | 146 | # This works for upstart scripts where the 'service' command | 163 | # This works for upstart scripts where the 'service' command |
137 | 147 | # returns a consistent string to represent running 'start/running' | 164 | # returns a consistent string to represent running |
138 | 148 | if "start/running" in output: | 165 | # 'start/running' |
139 | 166 | if ("start/running" in output or | ||
140 | 167 | "is running" in output or | ||
141 | 168 | "up and running" in output): | ||
142 | 149 | return True | 169 | return True |
143 | 150 | elif os.path.exists(_INIT_D_CONF.format(service_name)): | 170 | elif os.path.exists(_INIT_D_CONF.format(service_name)): |
144 | 151 | # Check System V scripts init script return codes | 171 | # Check System V scripts init script return codes |
145 | @@ -153,18 +173,6 @@ def service_running(service_name): | |||
146 | 153 | return False | 173 | return False |
147 | 154 | 174 | ||
148 | 155 | 175 | ||
149 | 156 | def service_available(service_name): | ||
150 | 157 | """Determine whether a system service is available""" | ||
151 | 158 | try: | ||
152 | 159 | subprocess.check_output( | ||
153 | 160 | ['service', service_name, 'status'], | ||
154 | 161 | stderr=subprocess.STDOUT).decode('UTF-8') | ||
155 | 162 | except subprocess.CalledProcessError as e: | ||
156 | 163 | return b'unrecognized service' not in e.output | ||
157 | 164 | else: | ||
158 | 165 | return True | ||
159 | 166 | |||
160 | 167 | |||
161 | 168 | SYSTEMD_SYSTEM = '/run/systemd/system' | 176 | SYSTEMD_SYSTEM = '/run/systemd/system' |
162 | 169 | 177 | ||
163 | 170 | 178 | ||
164 | @@ -173,8 +181,9 @@ def init_is_systemd(): | |||
165 | 173 | return os.path.isdir(SYSTEMD_SYSTEM) | 181 | return os.path.isdir(SYSTEMD_SYSTEM) |
166 | 174 | 182 | ||
167 | 175 | 183 | ||
170 | 176 | def adduser(username, password=None, shell='/bin/bash', system_user=False, | 184 | def adduser(username, password=None, shell='/bin/bash', |
171 | 177 | primary_group=None, secondary_groups=None, uid=None, home_dir=None): | 185 | system_user=False, primary_group=None, |
172 | 186 | secondary_groups=None, uid=None, home_dir=None): | ||
173 | 178 | """Add a user to the system. | 187 | """Add a user to the system. |
174 | 179 | 188 | ||
175 | 180 | Will log but otherwise succeed if the user already exists. | 189 | Will log but otherwise succeed if the user already exists. |
176 | @@ -286,17 +295,7 @@ def add_group(group_name, system_group=False, gid=None): | |||
177 | 286 | log('group with gid {0} already exists!'.format(gid)) | 295 | log('group with gid {0} already exists!'.format(gid)) |
178 | 287 | except KeyError: | 296 | except KeyError: |
179 | 288 | log('creating group {0}'.format(group_name)) | 297 | log('creating group {0}'.format(group_name)) |
191 | 289 | cmd = ['addgroup'] | 298 | add_new_group(group_name, system_group, gid) |
181 | 290 | if gid: | ||
182 | 291 | cmd.extend(['--gid', str(gid)]) | ||
183 | 292 | if system_group: | ||
184 | 293 | cmd.append('--system') | ||
185 | 294 | else: | ||
186 | 295 | cmd.extend([ | ||
187 | 296 | '--group', | ||
188 | 297 | ]) | ||
189 | 298 | cmd.append(group_name) | ||
190 | 299 | subprocess.check_call(cmd) | ||
192 | 300 | group_info = grp.getgrnam(group_name) | 299 | group_info = grp.getgrnam(group_name) |
193 | 301 | return group_info | 300 | return group_info |
194 | 302 | 301 | ||
195 | @@ -308,15 +307,17 @@ def add_user_to_group(username, group): | |||
196 | 308 | subprocess.check_call(cmd) | 307 | subprocess.check_call(cmd) |
197 | 309 | 308 | ||
198 | 310 | 309 | ||
200 | 311 | def rsync(from_path, to_path, flags='-r', options=None): | 310 | def rsync(from_path, to_path, flags='-r', options=None, timeout=None): |
201 | 312 | """Replicate the contents of a path""" | 311 | """Replicate the contents of a path""" |
202 | 313 | options = options or ['--delete', '--executability'] | 312 | options = options or ['--delete', '--executability'] |
203 | 314 | cmd = ['/usr/bin/rsync', flags] | 313 | cmd = ['/usr/bin/rsync', flags] |
204 | 314 | if timeout: | ||
205 | 315 | cmd = ['timeout', str(timeout)] + cmd | ||
206 | 315 | cmd.extend(options) | 316 | cmd.extend(options) |
207 | 316 | cmd.append(from_path) | 317 | cmd.append(from_path) |
208 | 317 | cmd.append(to_path) | 318 | cmd.append(to_path) |
209 | 318 | log(" ".join(cmd)) | 319 | log(" ".join(cmd)) |
211 | 319 | return subprocess.check_output(cmd).decode('UTF-8').strip() | 320 | return subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('UTF-8').strip() |
212 | 320 | 321 | ||
213 | 321 | 322 | ||
214 | 322 | def symlink(source, destination): | 323 | def symlink(source, destination): |
215 | @@ -541,16 +542,6 @@ def restart_on_change_helper(lambda_f, restart_map, stopstart=False, | |||
216 | 541 | return r | 542 | return r |
217 | 542 | 543 | ||
218 | 543 | 544 | ||
219 | 544 | def lsb_release(): | ||
220 | 545 | """Return /etc/lsb-release in a dict""" | ||
221 | 546 | d = {} | ||
222 | 547 | with open('/etc/lsb-release', 'r') as lsb: | ||
223 | 548 | for l in lsb: | ||
224 | 549 | k, v = l.split('=') | ||
225 | 550 | d[k.strip()] = v.strip() | ||
226 | 551 | return d | ||
227 | 552 | |||
228 | 553 | |||
229 | 554 | def pwgen(length=None): | 545 | def pwgen(length=None): |
230 | 555 | """Generate a random pasword.""" | 546 | """Generate a random pasword.""" |
231 | 556 | if length is None: | 547 | if length is None: |
232 | @@ -674,25 +665,6 @@ def get_nic_hwaddr(nic): | |||
233 | 674 | return hwaddr | 665 | return hwaddr |
234 | 675 | 666 | ||
235 | 676 | 667 | ||
236 | 677 | def cmp_pkgrevno(package, revno, pkgcache=None): | ||
237 | 678 | """Compare supplied revno with the revno of the installed package | ||
238 | 679 | |||
239 | 680 | * 1 => Installed revno is greater than supplied arg | ||
240 | 681 | * 0 => Installed revno is the same as supplied arg | ||
241 | 682 | * -1 => Installed revno is less than supplied arg | ||
242 | 683 | |||
243 | 684 | This function imports apt_cache function from charmhelpers.fetch if | ||
244 | 685 | the pkgcache argument is None. Be sure to add charmhelpers.fetch if | ||
245 | 686 | you call this function, or pass an apt_pkg.Cache() instance. | ||
246 | 687 | """ | ||
247 | 688 | import apt_pkg | ||
248 | 689 | if not pkgcache: | ||
249 | 690 | from charmhelpers.fetch import apt_cache | ||
250 | 691 | pkgcache = apt_cache() | ||
251 | 692 | pkg = pkgcache[package] | ||
252 | 693 | return apt_pkg.version_compare(pkg.current_ver.ver_str, revno) | ||
253 | 694 | |||
254 | 695 | |||
255 | 696 | @contextmanager | 668 | @contextmanager |
256 | 697 | def chdir(directory): | 669 | def chdir(directory): |
257 | 698 | """Change the current working directory to a different directory for a code | 670 | """Change the current working directory to a different directory for a code |
258 | @@ -715,7 +687,7 @@ def chownr(path, owner, group, follow_links=True, chowntopdir=False): | |||
259 | 715 | :param str path: The string path to start changing ownership. | 687 | :param str path: The string path to start changing ownership. |
260 | 716 | :param str owner: The owner string to use when looking up the uid. | 688 | :param str owner: The owner string to use when looking up the uid. |
261 | 717 | :param str group: The group string to use when looking up the gid. | 689 | :param str group: The group string to use when looking up the gid. |
263 | 718 | :param bool follow_links: Also Chown links if True | 690 | :param bool follow_links: Also follow and chown links if True |
264 | 719 | :param bool chowntopdir: Also chown path itself if True | 691 | :param bool chowntopdir: Also chown path itself if True |
265 | 720 | """ | 692 | """ |
266 | 721 | uid = pwd.getpwnam(owner).pw_uid | 693 | uid = pwd.getpwnam(owner).pw_uid |
267 | @@ -729,7 +701,7 @@ def chownr(path, owner, group, follow_links=True, chowntopdir=False): | |||
268 | 729 | broken_symlink = os.path.lexists(path) and not os.path.exists(path) | 701 | broken_symlink = os.path.lexists(path) and not os.path.exists(path) |
269 | 730 | if not broken_symlink: | 702 | if not broken_symlink: |
270 | 731 | chown(path, uid, gid) | 703 | chown(path, uid, gid) |
272 | 732 | for root, dirs, files in os.walk(path): | 704 | for root, dirs, files in os.walk(path, followlinks=follow_links): |
273 | 733 | for name in dirs + files: | 705 | for name in dirs + files: |
274 | 734 | full = os.path.join(root, name) | 706 | full = os.path.join(root, name) |
275 | 735 | broken_symlink = os.path.lexists(full) and not os.path.exists(full) | 707 | broken_symlink = os.path.lexists(full) and not os.path.exists(full) |
276 | @@ -763,3 +735,42 @@ def get_total_ram(): | |||
277 | 763 | assert unit == 'kB', 'Unknown unit' | 735 | assert unit == 'kB', 'Unknown unit' |
278 | 764 | return int(value) * 1024 # Classic, not KiB. | 736 | return int(value) * 1024 # Classic, not KiB. |
279 | 765 | raise NotImplementedError() | 737 | raise NotImplementedError() |
280 | 738 | |||
281 | 739 | |||
282 | 740 | UPSTART_CONTAINER_TYPE = '/run/container_type' | ||
283 | 741 | |||
284 | 742 | |||
285 | 743 | def is_container(): | ||
286 | 744 | """Determine whether unit is running in a container | ||
287 | 745 | |||
288 | 746 | @return: boolean indicating if unit is in a container | ||
289 | 747 | """ | ||
290 | 748 | if init_is_systemd(): | ||
291 | 749 | # Detect using systemd-detect-virt | ||
292 | 750 | return subprocess.call(['systemd-detect-virt', | ||
293 | 751 | '--container']) == 0 | ||
294 | 752 | else: | ||
295 | 753 | # Detect using upstart container file marker | ||
296 | 754 | return os.path.exists(UPSTART_CONTAINER_TYPE) | ||
297 | 755 | |||
298 | 756 | |||
299 | 757 | def add_to_updatedb_prunepath(path, updatedb_path=UPDATEDB_PATH): | ||
300 | 758 | with open(updatedb_path, 'r+') as f_id: | ||
301 | 759 | updatedb_text = f_id.read() | ||
302 | 760 | output = updatedb(updatedb_text, path) | ||
303 | 761 | f_id.seek(0) | ||
304 | 762 | f_id.write(output) | ||
305 | 763 | f_id.truncate() | ||
306 | 764 | |||
307 | 765 | |||
308 | 766 | def updatedb(updatedb_text, new_path): | ||
309 | 767 | lines = [line for line in updatedb_text.split("\n")] | ||
310 | 768 | for i, line in enumerate(lines): | ||
311 | 769 | if line.startswith("PRUNEPATHS="): | ||
312 | 770 | paths_line = line.split("=")[1].replace('"', '') | ||
313 | 771 | paths = paths_line.split(" ") | ||
314 | 772 | if new_path not in paths: | ||
315 | 773 | paths.append(new_path) | ||
316 | 774 | lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths)) | ||
317 | 775 | output = "\n".join(lines) | ||
318 | 776 | return output | ||
319 | diff --git a/hooks/charmhelpers/core/kernel.py b/hooks/charmhelpers/core/kernel.py | |||
320 | index b166efe..2d40452 100644 | |||
321 | --- a/hooks/charmhelpers/core/kernel.py | |||
322 | +++ b/hooks/charmhelpers/core/kernel.py | |||
323 | @@ -15,15 +15,28 @@ | |||
324 | 15 | # See the License for the specific language governing permissions and | 15 | # See the License for the specific language governing permissions and |
325 | 16 | # limitations under the License. | 16 | # limitations under the License. |
326 | 17 | 17 | ||
328 | 18 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | 18 | import re |
329 | 19 | import subprocess | ||
330 | 19 | 20 | ||
331 | 21 | from charmhelpers.osplatform import get_platform | ||
332 | 20 | from charmhelpers.core.hookenv import ( | 22 | from charmhelpers.core.hookenv import ( |
333 | 21 | log, | 23 | log, |
334 | 22 | INFO | 24 | INFO |
335 | 23 | ) | 25 | ) |
336 | 24 | 26 | ||
339 | 25 | from subprocess import check_call, check_output | 27 | __platform__ = get_platform() |
340 | 26 | import re | 28 | if __platform__ == "ubuntu": |
341 | 29 | from charmhelpers.core.kernel_factory.ubuntu import ( | ||
342 | 30 | persistent_modprobe, | ||
343 | 31 | update_initramfs, | ||
344 | 32 | ) # flake8: noqa -- ignore F401 for this import | ||
345 | 33 | elif __platform__ == "centos": | ||
346 | 34 | from charmhelpers.core.kernel_factory.centos import ( | ||
347 | 35 | persistent_modprobe, | ||
348 | 36 | update_initramfs, | ||
349 | 37 | ) # flake8: noqa -- ignore F401 for this import | ||
350 | 38 | |||
351 | 39 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
352 | 27 | 40 | ||
353 | 28 | 41 | ||
354 | 29 | def modprobe(module, persist=True): | 42 | def modprobe(module, persist=True): |
355 | @@ -32,11 +45,9 @@ def modprobe(module, persist=True): | |||
356 | 32 | 45 | ||
357 | 33 | log('Loading kernel module %s' % module, level=INFO) | 46 | log('Loading kernel module %s' % module, level=INFO) |
358 | 34 | 47 | ||
360 | 35 | check_call(cmd) | 48 | subprocess.check_call(cmd) |
361 | 36 | if persist: | 49 | if persist: |
365 | 37 | with open('/etc/modules', 'r+') as modules: | 50 | persistent_modprobe(module) |
363 | 38 | if module not in modules.read(): | ||
364 | 39 | modules.write(module) | ||
366 | 40 | 51 | ||
367 | 41 | 52 | ||
368 | 42 | def rmmod(module, force=False): | 53 | def rmmod(module, force=False): |
369 | @@ -46,21 +57,16 @@ def rmmod(module, force=False): | |||
370 | 46 | cmd.append('-f') | 57 | cmd.append('-f') |
371 | 47 | cmd.append(module) | 58 | cmd.append(module) |
372 | 48 | log('Removing kernel module %s' % module, level=INFO) | 59 | log('Removing kernel module %s' % module, level=INFO) |
374 | 49 | return check_call(cmd) | 60 | return subprocess.check_call(cmd) |
375 | 50 | 61 | ||
376 | 51 | 62 | ||
377 | 52 | def lsmod(): | 63 | def lsmod(): |
378 | 53 | """Shows what kernel modules are currently loaded""" | 64 | """Shows what kernel modules are currently loaded""" |
381 | 54 | return check_output(['lsmod'], | 65 | return subprocess.check_output(['lsmod'], |
382 | 55 | universal_newlines=True) | 66 | universal_newlines=True) |
383 | 56 | 67 | ||
384 | 57 | 68 | ||
385 | 58 | def is_module_loaded(module): | 69 | def is_module_loaded(module): |
386 | 59 | """Checks if a kernel module is already loaded""" | 70 | """Checks if a kernel module is already loaded""" |
387 | 60 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) | 71 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) |
388 | 61 | return len(matches) > 0 | 72 | return len(matches) > 0 |
389 | 62 | |||
390 | 63 | |||
391 | 64 | def update_initramfs(version='all'): | ||
392 | 65 | """Updates an initramfs image""" | ||
393 | 66 | return check_call(["update-initramfs", "-k", version, "-u"]) | ||
394 | diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py | |||
395 | index 52eaf82..ec5e0fe 100644 | |||
396 | --- a/hooks/charmhelpers/fetch/__init__.py | |||
397 | +++ b/hooks/charmhelpers/fetch/__init__.py | |||
398 | @@ -13,18 +13,12 @@ | |||
399 | 13 | # limitations under the License. | 13 | # limitations under the License. |
400 | 14 | 14 | ||
401 | 15 | import importlib | 15 | import importlib |
404 | 16 | from tempfile import NamedTemporaryFile | 16 | from charmhelpers.osplatform import get_platform |
403 | 17 | import time | ||
405 | 18 | from yaml import safe_load | 17 | from yaml import safe_load |
406 | 19 | from charmhelpers.core.host import ( | ||
407 | 20 | lsb_release | ||
408 | 21 | ) | ||
409 | 22 | import subprocess | ||
410 | 23 | from charmhelpers.core.hookenv import ( | 18 | from charmhelpers.core.hookenv import ( |
411 | 24 | config, | 19 | config, |
412 | 25 | log, | 20 | log, |
413 | 26 | ) | 21 | ) |
414 | 27 | import os | ||
415 | 28 | 22 | ||
416 | 29 | import six | 23 | import six |
417 | 30 | if six.PY3: | 24 | if six.PY3: |
418 | @@ -33,87 +27,6 @@ else: | |||
419 | 33 | from urlparse import urlparse, urlunparse | 27 | from urlparse import urlparse, urlunparse |
420 | 34 | 28 | ||
421 | 35 | 29 | ||
422 | 36 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive | ||
423 | 37 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main | ||
424 | 38 | """ | ||
425 | 39 | PROPOSED_POCKET = """# Proposed | ||
426 | 40 | deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted | ||
427 | 41 | """ | ||
428 | 42 | CLOUD_ARCHIVE_POCKETS = { | ||
429 | 43 | # Folsom | ||
430 | 44 | 'folsom': 'precise-updates/folsom', | ||
431 | 45 | 'precise-folsom': 'precise-updates/folsom', | ||
432 | 46 | 'precise-folsom/updates': 'precise-updates/folsom', | ||
433 | 47 | 'precise-updates/folsom': 'precise-updates/folsom', | ||
434 | 48 | 'folsom/proposed': 'precise-proposed/folsom', | ||
435 | 49 | 'precise-folsom/proposed': 'precise-proposed/folsom', | ||
436 | 50 | 'precise-proposed/folsom': 'precise-proposed/folsom', | ||
437 | 51 | # Grizzly | ||
438 | 52 | 'grizzly': 'precise-updates/grizzly', | ||
439 | 53 | 'precise-grizzly': 'precise-updates/grizzly', | ||
440 | 54 | 'precise-grizzly/updates': 'precise-updates/grizzly', | ||
441 | 55 | 'precise-updates/grizzly': 'precise-updates/grizzly', | ||
442 | 56 | 'grizzly/proposed': 'precise-proposed/grizzly', | ||
443 | 57 | 'precise-grizzly/proposed': 'precise-proposed/grizzly', | ||
444 | 58 | 'precise-proposed/grizzly': 'precise-proposed/grizzly', | ||
445 | 59 | # Havana | ||
446 | 60 | 'havana': 'precise-updates/havana', | ||
447 | 61 | 'precise-havana': 'precise-updates/havana', | ||
448 | 62 | 'precise-havana/updates': 'precise-updates/havana', | ||
449 | 63 | 'precise-updates/havana': 'precise-updates/havana', | ||
450 | 64 | 'havana/proposed': 'precise-proposed/havana', | ||
451 | 65 | 'precise-havana/proposed': 'precise-proposed/havana', | ||
452 | 66 | 'precise-proposed/havana': 'precise-proposed/havana', | ||
453 | 67 | # Icehouse | ||
454 | 68 | 'icehouse': 'precise-updates/icehouse', | ||
455 | 69 | 'precise-icehouse': 'precise-updates/icehouse', | ||
456 | 70 | 'precise-icehouse/updates': 'precise-updates/icehouse', | ||
457 | 71 | 'precise-updates/icehouse': 'precise-updates/icehouse', | ||
458 | 72 | 'icehouse/proposed': 'precise-proposed/icehouse', | ||
459 | 73 | 'precise-icehouse/proposed': 'precise-proposed/icehouse', | ||
460 | 74 | 'precise-proposed/icehouse': 'precise-proposed/icehouse', | ||
461 | 75 | # Juno | ||
462 | 76 | 'juno': 'trusty-updates/juno', | ||
463 | 77 | 'trusty-juno': 'trusty-updates/juno', | ||
464 | 78 | 'trusty-juno/updates': 'trusty-updates/juno', | ||
465 | 79 | 'trusty-updates/juno': 'trusty-updates/juno', | ||
466 | 80 | 'juno/proposed': 'trusty-proposed/juno', | ||
467 | 81 | 'trusty-juno/proposed': 'trusty-proposed/juno', | ||
468 | 82 | 'trusty-proposed/juno': 'trusty-proposed/juno', | ||
469 | 83 | # Kilo | ||
470 | 84 | 'kilo': 'trusty-updates/kilo', | ||
471 | 85 | 'trusty-kilo': 'trusty-updates/kilo', | ||
472 | 86 | 'trusty-kilo/updates': 'trusty-updates/kilo', | ||
473 | 87 | 'trusty-updates/kilo': 'trusty-updates/kilo', | ||
474 | 88 | 'kilo/proposed': 'trusty-proposed/kilo', | ||
475 | 89 | 'trusty-kilo/proposed': 'trusty-proposed/kilo', | ||
476 | 90 | 'trusty-proposed/kilo': 'trusty-proposed/kilo', | ||
477 | 91 | # Liberty | ||
478 | 92 | 'liberty': 'trusty-updates/liberty', | ||
479 | 93 | 'trusty-liberty': 'trusty-updates/liberty', | ||
480 | 94 | 'trusty-liberty/updates': 'trusty-updates/liberty', | ||
481 | 95 | 'trusty-updates/liberty': 'trusty-updates/liberty', | ||
482 | 96 | 'liberty/proposed': 'trusty-proposed/liberty', | ||
483 | 97 | 'trusty-liberty/proposed': 'trusty-proposed/liberty', | ||
484 | 98 | 'trusty-proposed/liberty': 'trusty-proposed/liberty', | ||
485 | 99 | # Mitaka | ||
486 | 100 | 'mitaka': 'trusty-updates/mitaka', | ||
487 | 101 | 'trusty-mitaka': 'trusty-updates/mitaka', | ||
488 | 102 | 'trusty-mitaka/updates': 'trusty-updates/mitaka', | ||
489 | 103 | 'trusty-updates/mitaka': 'trusty-updates/mitaka', | ||
490 | 104 | 'mitaka/proposed': 'trusty-proposed/mitaka', | ||
491 | 105 | 'trusty-mitaka/proposed': 'trusty-proposed/mitaka', | ||
492 | 106 | 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', | ||
493 | 107 | # Newton | ||
494 | 108 | 'newton': 'xenial-updates/newton', | ||
495 | 109 | 'xenial-newton': 'xenial-updates/newton', | ||
496 | 110 | 'xenial-newton/updates': 'xenial-updates/newton', | ||
497 | 111 | 'xenial-updates/newton': 'xenial-updates/newton', | ||
498 | 112 | 'newton/proposed': 'xenial-proposed/newton', | ||
499 | 113 | 'xenial-newton/proposed': 'xenial-proposed/newton', | ||
500 | 114 | 'xenial-proposed/newton': 'xenial-proposed/newton', | ||
501 | 115 | } | ||
502 | 116 | |||
503 | 117 | # The order of this list is very important. Handlers should be listed in from | 30 | # The order of this list is very important. Handlers should be listed in from |
504 | 118 | # least- to most-specific URL matching. | 31 | # least- to most-specific URL matching. |
505 | 119 | FETCH_HANDLERS = ( | 32 | FETCH_HANDLERS = ( |
506 | @@ -122,10 +35,6 @@ FETCH_HANDLERS = ( | |||
507 | 122 | 'charmhelpers.fetch.giturl.GitUrlFetchHandler', | 35 | 'charmhelpers.fetch.giturl.GitUrlFetchHandler', |
508 | 123 | ) | 36 | ) |
509 | 124 | 37 | ||
510 | 125 | APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. | ||
511 | 126 | APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. | ||
512 | 127 | APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. | ||
513 | 128 | |||
514 | 129 | 38 | ||
515 | 130 | class SourceConfigError(Exception): | 39 | class SourceConfigError(Exception): |
516 | 131 | pass | 40 | pass |
517 | @@ -163,180 +72,38 @@ class BaseFetchHandler(object): | |||
518 | 163 | return urlunparse(parts) | 72 | return urlunparse(parts) |
519 | 164 | 73 | ||
520 | 165 | 74 | ||
617 | 166 | def filter_installed_packages(packages): | 75 | __platform__ = get_platform() |
618 | 167 | """Returns a list of packages that require installation""" | 76 | module = "charmhelpers.fetch.%s" % __platform__ |
619 | 168 | cache = apt_cache() | 77 | fetch = importlib.import_module(module) |
524 | 169 | _pkgs = [] | ||
525 | 170 | for package in packages: | ||
526 | 171 | try: | ||
527 | 172 | p = cache[package] | ||
528 | 173 | p.current_ver or _pkgs.append(package) | ||
529 | 174 | except KeyError: | ||
530 | 175 | log('Package {} has no installation candidate.'.format(package), | ||
531 | 176 | level='WARNING') | ||
532 | 177 | _pkgs.append(package) | ||
533 | 178 | return _pkgs | ||
534 | 179 | |||
535 | 180 | |||
536 | 181 | def apt_cache(in_memory=True, progress=None): | ||
537 | 182 | """Build and return an apt cache""" | ||
538 | 183 | from apt import apt_pkg | ||
539 | 184 | apt_pkg.init() | ||
540 | 185 | if in_memory: | ||
541 | 186 | apt_pkg.config.set("Dir::Cache::pkgcache", "") | ||
542 | 187 | apt_pkg.config.set("Dir::Cache::srcpkgcache", "") | ||
543 | 188 | return apt_pkg.Cache(progress) | ||
544 | 189 | |||
545 | 190 | |||
546 | 191 | def apt_install(packages, options=None, fatal=False): | ||
547 | 192 | """Install one or more packages""" | ||
548 | 193 | if options is None: | ||
549 | 194 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
550 | 195 | |||
551 | 196 | cmd = ['apt-get', '--assume-yes'] | ||
552 | 197 | cmd.extend(options) | ||
553 | 198 | cmd.append('install') | ||
554 | 199 | if isinstance(packages, six.string_types): | ||
555 | 200 | cmd.append(packages) | ||
556 | 201 | else: | ||
557 | 202 | cmd.extend(packages) | ||
558 | 203 | log("Installing {} with options: {}".format(packages, | ||
559 | 204 | options)) | ||
560 | 205 | _run_apt_command(cmd, fatal) | ||
561 | 206 | |||
562 | 207 | |||
563 | 208 | def apt_upgrade(options=None, fatal=False, dist=False): | ||
564 | 209 | """Upgrade all packages""" | ||
565 | 210 | if options is None: | ||
566 | 211 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
567 | 212 | |||
568 | 213 | cmd = ['apt-get', '--assume-yes'] | ||
569 | 214 | cmd.extend(options) | ||
570 | 215 | if dist: | ||
571 | 216 | cmd.append('dist-upgrade') | ||
572 | 217 | else: | ||
573 | 218 | cmd.append('upgrade') | ||
574 | 219 | log("Upgrading with options: {}".format(options)) | ||
575 | 220 | _run_apt_command(cmd, fatal) | ||
576 | 221 | |||
577 | 222 | |||
578 | 223 | def apt_update(fatal=False): | ||
579 | 224 | """Update local apt cache""" | ||
580 | 225 | cmd = ['apt-get', 'update'] | ||
581 | 226 | _run_apt_command(cmd, fatal) | ||
582 | 227 | |||
583 | 228 | |||
584 | 229 | def apt_purge(packages, fatal=False): | ||
585 | 230 | """Purge one or more packages""" | ||
586 | 231 | cmd = ['apt-get', '--assume-yes', 'purge'] | ||
587 | 232 | if isinstance(packages, six.string_types): | ||
588 | 233 | cmd.append(packages) | ||
589 | 234 | else: | ||
590 | 235 | cmd.extend(packages) | ||
591 | 236 | log("Purging {}".format(packages)) | ||
592 | 237 | _run_apt_command(cmd, fatal) | ||
593 | 238 | |||
594 | 239 | |||
595 | 240 | def apt_mark(packages, mark, fatal=False): | ||
596 | 241 | """Flag one or more packages using apt-mark""" | ||
597 | 242 | log("Marking {} as {}".format(packages, mark)) | ||
598 | 243 | cmd = ['apt-mark', mark] | ||
599 | 244 | if isinstance(packages, six.string_types): | ||
600 | 245 | cmd.append(packages) | ||
601 | 246 | else: | ||
602 | 247 | cmd.extend(packages) | ||
603 | 248 | |||
604 | 249 | if fatal: | ||
605 | 250 | subprocess.check_call(cmd, universal_newlines=True) | ||
606 | 251 | else: | ||
607 | 252 | subprocess.call(cmd, universal_newlines=True) | ||
608 | 253 | |||
609 | 254 | |||
610 | 255 | def apt_hold(packages, fatal=False): | ||
611 | 256 | return apt_mark(packages, 'hold', fatal=fatal) | ||
612 | 257 | |||
613 | 258 | |||
614 | 259 | def apt_unhold(packages, fatal=False): | ||
615 | 260 | return apt_mark(packages, 'unhold', fatal=fatal) | ||
616 | 261 | |||
620 | 262 | 78 | ||
623 | 263 | def add_source(source, key=None): | 79 | filter_installed_packages = fetch.filter_installed_packages |
624 | 264 | """Add a package source to this system. | 80 | install = fetch.install |
625 | 81 | upgrade = fetch.upgrade | ||
626 | 82 | update = fetch.update | ||
627 | 83 | purge = fetch.purge | ||
628 | 84 | add_source = fetch.add_source | ||
629 | 265 | 85 | ||
694 | 266 | @param source: a URL or sources.list entry, as supported by | 86 | if __platform__ == "ubuntu": |
695 | 267 | add-apt-repository(1). Examples:: | 87 | apt_cache = fetch.apt_cache |
696 | 268 | 88 | apt_install = fetch.install | |
697 | 269 | ppa:charmers/example | 89 | apt_update = fetch.update |
698 | 270 | deb https://stub:key@private.example.com/ubuntu trusty main | 90 | apt_upgrade = fetch.upgrade |
699 | 271 | 91 | apt_purge = fetch.purge | |
700 | 272 | In addition: | 92 | apt_mark = fetch.apt_mark |
701 | 273 | 'proposed:' may be used to enable the standard 'proposed' | 93 | apt_hold = fetch.apt_hold |
702 | 274 | pocket for the release. | 94 | apt_unhold = fetch.apt_unhold |
703 | 275 | 'cloud:' may be used to activate official cloud archive pockets, | 95 | get_upstream_version = fetch.get_upstream_version |
704 | 276 | such as 'cloud:icehouse' | 96 | elif __platform__ == "centos": |
705 | 277 | 'distro' may be used as a noop | 97 | yum_search = fetch.yum_search |
642 | 278 | |||
643 | 279 | @param key: A key to be added to the system's APT keyring and used | ||
644 | 280 | to verify the signatures on packages. Ideally, this should be an | ||
645 | 281 | ASCII format GPG public key including the block headers. A GPG key | ||
646 | 282 | id may also be used, but be aware that only insecure protocols are | ||
647 | 283 | available to retrieve the actual public key from a public keyserver | ||
648 | 284 | placing your Juju environment at risk. ppa and cloud archive keys | ||
649 | 285 | are securely added automtically, so sould not be provided. | ||
650 | 286 | """ | ||
651 | 287 | if source is None: | ||
652 | 288 | log('Source is not present. Skipping') | ||
653 | 289 | return | ||
654 | 290 | |||
655 | 291 | if (source.startswith('ppa:') or | ||
656 | 292 | source.startswith('http') or | ||
657 | 293 | source.startswith('deb ') or | ||
658 | 294 | source.startswith('cloud-archive:')): | ||
659 | 295 | subprocess.check_call(['add-apt-repository', '--yes', source]) | ||
660 | 296 | elif source.startswith('cloud:'): | ||
661 | 297 | apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), | ||
662 | 298 | fatal=True) | ||
663 | 299 | pocket = source.split(':')[-1] | ||
664 | 300 | if pocket not in CLOUD_ARCHIVE_POCKETS: | ||
665 | 301 | raise SourceConfigError( | ||
666 | 302 | 'Unsupported cloud: source option %s' % | ||
667 | 303 | pocket) | ||
668 | 304 | actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] | ||
669 | 305 | with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: | ||
670 | 306 | apt.write(CLOUD_ARCHIVE.format(actual_pocket)) | ||
671 | 307 | elif source == 'proposed': | ||
672 | 308 | release = lsb_release()['DISTRIB_CODENAME'] | ||
673 | 309 | with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: | ||
674 | 310 | apt.write(PROPOSED_POCKET.format(release)) | ||
675 | 311 | elif source == 'distro': | ||
676 | 312 | pass | ||
677 | 313 | else: | ||
678 | 314 | log("Unknown source: {!r}".format(source)) | ||
679 | 315 | |||
680 | 316 | if key: | ||
681 | 317 | if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: | ||
682 | 318 | with NamedTemporaryFile('w+') as key_file: | ||
683 | 319 | key_file.write(key) | ||
684 | 320 | key_file.flush() | ||
685 | 321 | key_file.seek(0) | ||
686 | 322 | subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file) | ||
687 | 323 | else: | ||
688 | 324 | # Note that hkp: is in no way a secure protocol. Using a | ||
689 | 325 | # GPG key id is pointless from a security POV unless you | ||
690 | 326 | # absolutely trust your network and DNS. | ||
691 | 327 | subprocess.check_call(['apt-key', 'adv', '--keyserver', | ||
692 | 328 | 'hkp://keyserver.ubuntu.com:80', '--recv', | ||
693 | 329 | key]) | ||
706 | 330 | 98 | ||
707 | 331 | 99 | ||
708 | 332 | def configure_sources(update=False, | 100 | def configure_sources(update=False, |
709 | 333 | sources_var='install_sources', | 101 | sources_var='install_sources', |
710 | 334 | keys_var='install_keys'): | 102 | keys_var='install_keys'): |
713 | 335 | """ | 103 | """Configure multiple sources from charm configuration. |
712 | 336 | Configure multiple sources from charm configuration. | ||
714 | 337 | 104 | ||
715 | 338 | The lists are encoded as yaml fragments in the configuration. | 105 | The lists are encoded as yaml fragments in the configuration. |
717 | 339 | The frament needs to be included as a string. Sources and their | 106 | The fragment needs to be included as a string. Sources and their |
718 | 340 | corresponding keys are of the types supported by add_source(). | 107 | corresponding keys are of the types supported by add_source(). |
719 | 341 | 108 | ||
720 | 342 | Example config: | 109 | Example config: |
721 | @@ -368,12 +135,11 @@ def configure_sources(update=False, | |||
722 | 368 | for source, key in zip(sources, keys): | 135 | for source, key in zip(sources, keys): |
723 | 369 | add_source(source, key) | 136 | add_source(source, key) |
724 | 370 | if update: | 137 | if update: |
726 | 371 | apt_update(fatal=True) | 138 | fetch.update(fatal=True) |
727 | 372 | 139 | ||
728 | 373 | 140 | ||
729 | 374 | def install_remote(source, *args, **kwargs): | 141 | def install_remote(source, *args, **kwargs): |
732 | 375 | """ | 142 | """Install a file tree from a remote source. |
731 | 376 | Install a file tree from a remote source | ||
733 | 377 | 143 | ||
734 | 378 | The specified source should be a url of the form: | 144 | The specified source should be a url of the form: |
735 | 379 | scheme://[host]/path[#[option=value][&...]] | 145 | scheme://[host]/path[#[option=value][&...]] |
736 | @@ -406,6 +172,7 @@ def install_remote(source, *args, **kwargs): | |||
737 | 406 | 172 | ||
738 | 407 | 173 | ||
739 | 408 | def install_from_config(config_var_name): | 174 | def install_from_config(config_var_name): |
740 | 175 | """Install a file from config.""" | ||
741 | 409 | charm_config = config() | 176 | charm_config = config() |
742 | 410 | source = charm_config[config_var_name] | 177 | source = charm_config[config_var_name] |
743 | 411 | return install_remote(source) | 178 | return install_remote(source) |
744 | @@ -428,40 +195,3 @@ def plugins(fetch_handlers=None): | |||
745 | 428 | log("FetchHandler {} not found, skipping plugin".format( | 195 | log("FetchHandler {} not found, skipping plugin".format( |
746 | 429 | handler_name)) | 196 | handler_name)) |
747 | 430 | return plugin_list | 197 | return plugin_list |
748 | 431 | |||
749 | 432 | |||
750 | 433 | def _run_apt_command(cmd, fatal=False): | ||
751 | 434 | """ | ||
752 | 435 | Run an APT command, checking output and retrying if the fatal flag is set | ||
753 | 436 | to True. | ||
754 | 437 | |||
755 | 438 | :param: cmd: str: The apt command to run. | ||
756 | 439 | :param: fatal: bool: Whether the command's output should be checked and | ||
757 | 440 | retried. | ||
758 | 441 | """ | ||
759 | 442 | env = os.environ.copy() | ||
760 | 443 | |||
761 | 444 | if 'DEBIAN_FRONTEND' not in env: | ||
762 | 445 | env['DEBIAN_FRONTEND'] = 'noninteractive' | ||
763 | 446 | |||
764 | 447 | if fatal: | ||
765 | 448 | retry_count = 0 | ||
766 | 449 | result = None | ||
767 | 450 | |||
768 | 451 | # If the command is considered "fatal", we need to retry if the apt | ||
769 | 452 | # lock was not acquired. | ||
770 | 453 | |||
771 | 454 | while result is None or result == APT_NO_LOCK: | ||
772 | 455 | try: | ||
773 | 456 | result = subprocess.check_call(cmd, env=env) | ||
774 | 457 | except subprocess.CalledProcessError as e: | ||
775 | 458 | retry_count = retry_count + 1 | ||
776 | 459 | if retry_count > APT_NO_LOCK_RETRY_COUNT: | ||
777 | 460 | raise | ||
778 | 461 | result = e.returncode | ||
779 | 462 | log("Couldn't acquire DPKG lock. Will retry in {} seconds." | ||
780 | 463 | "".format(APT_NO_LOCK_RETRY_DELAY)) | ||
781 | 464 | time.sleep(APT_NO_LOCK_RETRY_DELAY) | ||
782 | 465 | |||
783 | 466 | else: | ||
784 | 467 | subprocess.call(cmd, env=env) | ||
785 | diff --git a/hooks/charmhelpers/fetch/bzrurl.py b/hooks/charmhelpers/fetch/bzrurl.py | |||
786 | index b3404d8..07cd029 100644 | |||
787 | --- a/hooks/charmhelpers/fetch/bzrurl.py | |||
788 | +++ b/hooks/charmhelpers/fetch/bzrurl.py | |||
789 | @@ -18,19 +18,20 @@ from charmhelpers.fetch import ( | |||
790 | 18 | BaseFetchHandler, | 18 | BaseFetchHandler, |
791 | 19 | UnhandledSource, | 19 | UnhandledSource, |
792 | 20 | filter_installed_packages, | 20 | filter_installed_packages, |
794 | 21 | apt_install, | 21 | install, |
795 | 22 | ) | 22 | ) |
796 | 23 | from charmhelpers.core.host import mkdir | 23 | from charmhelpers.core.host import mkdir |
797 | 24 | 24 | ||
798 | 25 | 25 | ||
799 | 26 | if filter_installed_packages(['bzr']) != []: | 26 | if filter_installed_packages(['bzr']) != []: |
801 | 27 | apt_install(['bzr']) | 27 | install(['bzr']) |
802 | 28 | if filter_installed_packages(['bzr']) != []: | 28 | if filter_installed_packages(['bzr']) != []: |
803 | 29 | raise NotImplementedError('Unable to install bzr') | 29 | raise NotImplementedError('Unable to install bzr') |
804 | 30 | 30 | ||
805 | 31 | 31 | ||
806 | 32 | class BzrUrlFetchHandler(BaseFetchHandler): | 32 | class BzrUrlFetchHandler(BaseFetchHandler): |
808 | 33 | """Handler for bazaar branches via generic and lp URLs""" | 33 | """Handler for bazaar branches via generic and lp URLs.""" |
809 | 34 | |||
810 | 34 | def can_handle(self, source): | 35 | def can_handle(self, source): |
811 | 35 | url_parts = self.parse_url(source) | 36 | url_parts = self.parse_url(source) |
812 | 36 | if url_parts.scheme not in ('bzr+ssh', 'lp', ''): | 37 | if url_parts.scheme not in ('bzr+ssh', 'lp', ''): |
813 | diff --git a/hooks/charmhelpers/fetch/giturl.py b/hooks/charmhelpers/fetch/giturl.py | |||
814 | index f708d1e..4cf21bc 100644 | |||
815 | --- a/hooks/charmhelpers/fetch/giturl.py | |||
816 | +++ b/hooks/charmhelpers/fetch/giturl.py | |||
817 | @@ -18,17 +18,18 @@ from charmhelpers.fetch import ( | |||
818 | 18 | BaseFetchHandler, | 18 | BaseFetchHandler, |
819 | 19 | UnhandledSource, | 19 | UnhandledSource, |
820 | 20 | filter_installed_packages, | 20 | filter_installed_packages, |
822 | 21 | apt_install, | 21 | install, |
823 | 22 | ) | 22 | ) |
824 | 23 | 23 | ||
825 | 24 | if filter_installed_packages(['git']) != []: | 24 | if filter_installed_packages(['git']) != []: |
827 | 25 | apt_install(['git']) | 25 | install(['git']) |
828 | 26 | if filter_installed_packages(['git']) != []: | 26 | if filter_installed_packages(['git']) != []: |
829 | 27 | raise NotImplementedError('Unable to install git') | 27 | raise NotImplementedError('Unable to install git') |
830 | 28 | 28 | ||
831 | 29 | 29 | ||
832 | 30 | class GitUrlFetchHandler(BaseFetchHandler): | 30 | class GitUrlFetchHandler(BaseFetchHandler): |
834 | 31 | """Handler for git branches via generic and github URLs""" | 31 | """Handler for git branches via generic and github URLs.""" |
835 | 32 | |||
836 | 32 | def can_handle(self, source): | 33 | def can_handle(self, source): |
837 | 33 | url_parts = self.parse_url(source) | 34 | url_parts = self.parse_url(source) |
838 | 34 | # TODO (mattyw) no support for ssh git@ yet | 35 | # TODO (mattyw) no support for ssh git@ yet |
839 | diff --git a/tests/charmhelpers/contrib/amulet/deployment.py b/tests/charmhelpers/contrib/amulet/deployment.py | |||
840 | index 0146236..9c65518 100644 | |||
841 | --- a/tests/charmhelpers/contrib/amulet/deployment.py | |||
842 | +++ b/tests/charmhelpers/contrib/amulet/deployment.py | |||
843 | @@ -78,11 +78,15 @@ class AmuletDeployment(object): | |||
844 | 78 | 78 | ||
845 | 79 | def _deploy(self): | 79 | def _deploy(self): |
846 | 80 | """Deploy environment and wait for all hooks to finish executing.""" | 80 | """Deploy environment and wait for all hooks to finish executing.""" |
847 | 81 | timeout = int(os.environ.get('AMULET_SETUP_TIMEOUT', 900)) | ||
848 | 81 | try: | 82 | try: |
851 | 82 | self.d.setup(timeout=900) | 83 | self.d.setup(timeout=timeout) |
852 | 83 | self.d.sentry.wait(timeout=900) | 84 | self.d.sentry.wait(timeout=timeout) |
853 | 84 | except amulet.helpers.TimeoutError: | 85 | except amulet.helpers.TimeoutError: |
855 | 85 | amulet.raise_status(amulet.FAIL, msg="Deployment timed out") | 86 | amulet.raise_status( |
856 | 87 | amulet.FAIL, | ||
857 | 88 | msg="Deployment timed out ({}s)".format(timeout) | ||
858 | 89 | ) | ||
859 | 86 | except Exception: | 90 | except Exception: |
860 | 87 | raise | 91 | raise |
861 | 88 | 92 | ||
862 | diff --git a/tests/charmhelpers/contrib/amulet/utils.py b/tests/charmhelpers/contrib/amulet/utils.py | |||
863 | index a39ed4c..f9e4c3a 100644 | |||
864 | --- a/tests/charmhelpers/contrib/amulet/utils.py | |||
865 | +++ b/tests/charmhelpers/contrib/amulet/utils.py | |||
866 | @@ -148,7 +148,8 @@ class AmuletUtils(object): | |||
867 | 148 | 148 | ||
868 | 149 | for service_name in services_list: | 149 | for service_name in services_list: |
869 | 150 | if (self.ubuntu_releases.index(release) >= systemd_switch or | 150 | if (self.ubuntu_releases.index(release) >= systemd_switch or |
871 | 151 | service_name in ['rabbitmq-server', 'apache2']): | 151 | service_name in ['rabbitmq-server', 'apache2', |
872 | 152 | 'memcached']): | ||
873 | 152 | # init is systemd (or regular sysv) | 153 | # init is systemd (or regular sysv) |
874 | 153 | cmd = 'sudo service {} status'.format(service_name) | 154 | cmd = 'sudo service {} status'.format(service_name) |
875 | 154 | output, code = sentry_unit.run(cmd) | 155 | output, code = sentry_unit.run(cmd) |
876 | @@ -546,7 +547,7 @@ class AmuletUtils(object): | |||
877 | 546 | raise if it is present. | 547 | raise if it is present. |
878 | 547 | :returns: List of process IDs | 548 | :returns: List of process IDs |
879 | 548 | """ | 549 | """ |
881 | 549 | cmd = 'pidof -x {}'.format(process_name) | 550 | cmd = 'pidof -x "{}"'.format(process_name) |
882 | 550 | if not expect_success: | 551 | if not expect_success: |
883 | 551 | cmd += " || exit 0 && exit 1" | 552 | cmd += " || exit 0 && exit 1" |
884 | 552 | output, code = sentry_unit.run(cmd) | 553 | output, code = sentry_unit.run(cmd) |
LGTM