Merge ~barryprice/charm-nrpe/+git/nrpe-charm:barryprice into ~nrpe-charmers/charm-nrpe: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)
Reviewer Review Type Date Requested Status
Paul Gear (community) Approve
Review via email: mp+316423@code.launchpad.net

Commit message

Charmhelpers sync

To post a comment you must log in.
Revision history for this message
Paul Gear (paulgear) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py
index 48b2b9d..e44e22b 100644
--- a/hooks/charmhelpers/core/hookenv.py
+++ b/hooks/charmhelpers/core/hookenv.py
@@ -332,6 +332,8 @@ def config(scope=None):
332 config_cmd_line = ['config-get']332 config_cmd_line = ['config-get']
333 if scope is not None:333 if scope is not None:
334 config_cmd_line.append(scope)334 config_cmd_line.append(scope)
335 else:
336 config_cmd_line.append('--all')
335 config_cmd_line.append('--format=json')337 config_cmd_line.append('--format=json')
336 try:338 try:
337 config_data = json.loads(339 config_data = json.loads(
@@ -614,6 +616,20 @@ def close_port(port, protocol="TCP"):
614 subprocess.check_call(_args)616 subprocess.check_call(_args)
615617
616618
619def open_ports(start, end, protocol="TCP"):
620 """Opens a range of service network ports"""
621 _args = ['open-port']
622 _args.append('{}-{}/{}'.format(start, end, protocol))
623 subprocess.check_call(_args)
624
625
626def close_ports(start, end, protocol="TCP"):
627 """Close a range of service network ports"""
628 _args = ['close-port']
629 _args.append('{}-{}/{}'.format(start, end, protocol))
630 subprocess.check_call(_args)
631
632
617@cached633@cached
618def unit_get(attribute):634def unit_get(attribute):
619 """Get the unit ID for the remote unit"""635 """Get the unit ID for the remote unit"""
@@ -843,6 +859,20 @@ def translate_exc(from_exc, to_exc):
843 return inner_translate_exc1859 return inner_translate_exc1
844860
845861
862def application_version_set(version):
863 """Charm authors may trigger this command from any hook to output what
864 version of the application is running. This could be a package version,
865 for instance postgres version 9.5. It could also be a build number or
866 version control revision identifier, for instance git sha 6fb7ba68. """
867
868 cmd = ['application-version-set']
869 cmd.append(version)
870 try:
871 subprocess.check_call(cmd)
872 except OSError:
873 log("Application Version: {}".format(version))
874
875
846@translate_exc(from_exc=OSError, to_exc=NotImplementedError)876@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
847def is_leader():877def is_leader():
848 """Does the current unit hold the juju leadership878 """Does the current unit hold the juju leadership
@@ -1005,3 +1035,34 @@ def network_get_primary_address(binding):
1005 '''1035 '''
1006 cmd = ['network-get', '--primary-address', binding]1036 cmd = ['network-get', '--primary-address', binding]
1007 return subprocess.check_output(cmd).decode('UTF-8').strip()1037 return subprocess.check_output(cmd).decode('UTF-8').strip()
1038
1039
1040def add_metric(*args, **kwargs):
1041 """Add metric values. Values may be expressed with keyword arguments. For
1042 metric names containing dashes, these may be expressed as one or more
1043 'key=value' positional arguments. May only be called from the collect-metrics
1044 hook."""
1045 _args = ['add-metric']
1046 _kvpairs = []
1047 _kvpairs.extend(args)
1048 _kvpairs.extend(['{}={}'.format(k, v) for k, v in kwargs.items()])
1049 _args.extend(sorted(_kvpairs))
1050 try:
1051 subprocess.check_call(_args)
1052 return
1053 except EnvironmentError as e:
1054 if e.errno != errno.ENOENT:
1055 raise
1056 log_message = 'add-metric failed: {}'.format(' '.join(_kvpairs))
1057 log(log_message, level='INFO')
1058
1059
1060def meter_status():
1061 """Get the meter status, if running in the meter-status-changed hook."""
1062 return os.environ.get('JUJU_METER_STATUS')
1063
1064
1065def meter_info():
1066 """Get the meter status information, if running in the meter-status-changed
1067 hook."""
1068 return os.environ.get('JUJU_METER_INFO')
diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py
index 5306859..3638e65 100644
--- a/hooks/charmhelpers/core/host.py
+++ b/hooks/charmhelpers/core/host.py
@@ -30,14 +30,31 @@ import subprocess
30import hashlib30import hashlib
31import functools31import functools
32import itertools32import itertools
33from contextlib import contextmanager
34from collections import OrderedDict
35
36import six33import six
3734
35from contextlib import contextmanager
36from collections import OrderedDict
38from .hookenv import log37from .hookenv import log
39from .fstab import Fstab38from .fstab import Fstab
4039from charmhelpers.osplatform import get_platform
40
41__platform__ = get_platform()
42if __platform__ == "ubuntu":
43 from charmhelpers.core.host_factory.ubuntu import (
44 service_available,
45 add_new_group,
46 lsb_release,
47 cmp_pkgrevno,
48 ) # flake8: noqa -- ignore F401 for this import
49elif __platform__ == "centos":
50 from charmhelpers.core.host_factory.centos import (
51 service_available,
52 add_new_group,
53 lsb_release,
54 cmp_pkgrevno,
55 ) # flake8: noqa -- ignore F401 for this import
56
57UPDATEDB_PATH = '/etc/updatedb.conf'
4158
42def service_start(service_name):59def service_start(service_name):
43 """Start a system service"""60 """Start a system service"""
@@ -144,8 +161,11 @@ def service_running(service_name):
144 return False161 return False
145 else:162 else:
146 # This works for upstart scripts where the 'service' command163 # This works for upstart scripts where the 'service' command
147 # returns a consistent string to represent running 'start/running'164 # returns a consistent string to represent running
148 if "start/running" in output:165 # 'start/running'
166 if ("start/running" in output or
167 "is running" in output or
168 "up and running" in output):
149 return True169 return True
150 elif os.path.exists(_INIT_D_CONF.format(service_name)):170 elif os.path.exists(_INIT_D_CONF.format(service_name)):
151 # Check System V scripts init script return codes171 # Check System V scripts init script return codes
@@ -153,18 +173,6 @@ def service_running(service_name):
153 return False173 return False
154174
155175
156def service_available(service_name):
157 """Determine whether a system service is available"""
158 try:
159 subprocess.check_output(
160 ['service', service_name, 'status'],
161 stderr=subprocess.STDOUT).decode('UTF-8')
162 except subprocess.CalledProcessError as e:
163 return b'unrecognized service' not in e.output
164 else:
165 return True
166
167
168SYSTEMD_SYSTEM = '/run/systemd/system'176SYSTEMD_SYSTEM = '/run/systemd/system'
169177
170178
@@ -173,8 +181,9 @@ def init_is_systemd():
173 return os.path.isdir(SYSTEMD_SYSTEM)181 return os.path.isdir(SYSTEMD_SYSTEM)
174182
175183
176def adduser(username, password=None, shell='/bin/bash', system_user=False,184def adduser(username, password=None, shell='/bin/bash',
177 primary_group=None, secondary_groups=None, uid=None, home_dir=None):185 system_user=False, primary_group=None,
186 secondary_groups=None, uid=None, home_dir=None):
178 """Add a user to the system.187 """Add a user to the system.
179188
180 Will log but otherwise succeed if the user already exists.189 Will log but otherwise succeed if the user already exists.
@@ -286,17 +295,7 @@ def add_group(group_name, system_group=False, gid=None):
286 log('group with gid {0} already exists!'.format(gid))295 log('group with gid {0} already exists!'.format(gid))
287 except KeyError:296 except KeyError:
288 log('creating group {0}'.format(group_name))297 log('creating group {0}'.format(group_name))
289 cmd = ['addgroup']298 add_new_group(group_name, system_group, gid)
290 if gid:
291 cmd.extend(['--gid', str(gid)])
292 if system_group:
293 cmd.append('--system')
294 else:
295 cmd.extend([
296 '--group',
297 ])
298 cmd.append(group_name)
299 subprocess.check_call(cmd)
300 group_info = grp.getgrnam(group_name)299 group_info = grp.getgrnam(group_name)
301 return group_info300 return group_info
302301
@@ -308,15 +307,17 @@ def add_user_to_group(username, group):
308 subprocess.check_call(cmd)307 subprocess.check_call(cmd)
309308
310309
311def rsync(from_path, to_path, flags='-r', options=None):310def rsync(from_path, to_path, flags='-r', options=None, timeout=None):
312 """Replicate the contents of a path"""311 """Replicate the contents of a path"""
313 options = options or ['--delete', '--executability']312 options = options or ['--delete', '--executability']
314 cmd = ['/usr/bin/rsync', flags]313 cmd = ['/usr/bin/rsync', flags]
314 if timeout:
315 cmd = ['timeout', str(timeout)] + cmd
315 cmd.extend(options)316 cmd.extend(options)
316 cmd.append(from_path)317 cmd.append(from_path)
317 cmd.append(to_path)318 cmd.append(to_path)
318 log(" ".join(cmd))319 log(" ".join(cmd))
319 return subprocess.check_output(cmd).decode('UTF-8').strip()320 return subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('UTF-8').strip()
320321
321322
322def symlink(source, destination):323def symlink(source, destination):
@@ -541,16 +542,6 @@ def restart_on_change_helper(lambda_f, restart_map, stopstart=False,
541 return r542 return r
542543
543544
544def lsb_release():
545 """Return /etc/lsb-release in a dict"""
546 d = {}
547 with open('/etc/lsb-release', 'r') as lsb:
548 for l in lsb:
549 k, v = l.split('=')
550 d[k.strip()] = v.strip()
551 return d
552
553
554def pwgen(length=None):545def pwgen(length=None):
555 """Generate a random pasword."""546 """Generate a random pasword."""
556 if length is None:547 if length is None:
@@ -674,25 +665,6 @@ def get_nic_hwaddr(nic):
674 return hwaddr665 return hwaddr
675666
676667
677def cmp_pkgrevno(package, revno, pkgcache=None):
678 """Compare supplied revno with the revno of the installed package
679
680 * 1 => Installed revno is greater than supplied arg
681 * 0 => Installed revno is the same as supplied arg
682 * -1 => Installed revno is less than supplied arg
683
684 This function imports apt_cache function from charmhelpers.fetch if
685 the pkgcache argument is None. Be sure to add charmhelpers.fetch if
686 you call this function, or pass an apt_pkg.Cache() instance.
687 """
688 import apt_pkg
689 if not pkgcache:
690 from charmhelpers.fetch import apt_cache
691 pkgcache = apt_cache()
692 pkg = pkgcache[package]
693 return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
694
695
696@contextmanager668@contextmanager
697def chdir(directory):669def chdir(directory):
698 """Change the current working directory to a different directory for a code670 """Change the current working directory to a different directory for a code
@@ -715,7 +687,7 @@ def chownr(path, owner, group, follow_links=True, chowntopdir=False):
715 :param str path: The string path to start changing ownership.687 :param str path: The string path to start changing ownership.
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.
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.
718 :param bool follow_links: Also Chown links if True690 :param bool follow_links: Also follow and chown links if True
719 :param bool chowntopdir: Also chown path itself if True691 :param bool chowntopdir: Also chown path itself if True
720 """692 """
721 uid = pwd.getpwnam(owner).pw_uid693 uid = pwd.getpwnam(owner).pw_uid
@@ -729,7 +701,7 @@ def chownr(path, owner, group, follow_links=True, chowntopdir=False):
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)
730 if not broken_symlink:702 if not broken_symlink:
731 chown(path, uid, gid)703 chown(path, uid, gid)
732 for root, dirs, files in os.walk(path):704 for root, dirs, files in os.walk(path, followlinks=follow_links):
733 for name in dirs + files:705 for name in dirs + files:
734 full = os.path.join(root, name)706 full = os.path.join(root, name)
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)
@@ -763,3 +735,42 @@ def get_total_ram():
763 assert unit == 'kB', 'Unknown unit'735 assert unit == 'kB', 'Unknown unit'
764 return int(value) * 1024 # Classic, not KiB.736 return int(value) * 1024 # Classic, not KiB.
765 raise NotImplementedError()737 raise NotImplementedError()
738
739
740UPSTART_CONTAINER_TYPE = '/run/container_type'
741
742
743def is_container():
744 """Determine whether unit is running in a container
745
746 @return: boolean indicating if unit is in a container
747 """
748 if init_is_systemd():
749 # Detect using systemd-detect-virt
750 return subprocess.call(['systemd-detect-virt',
751 '--container']) == 0
752 else:
753 # Detect using upstart container file marker
754 return os.path.exists(UPSTART_CONTAINER_TYPE)
755
756
757def add_to_updatedb_prunepath(path, updatedb_path=UPDATEDB_PATH):
758 with open(updatedb_path, 'r+') as f_id:
759 updatedb_text = f_id.read()
760 output = updatedb(updatedb_text, path)
761 f_id.seek(0)
762 f_id.write(output)
763 f_id.truncate()
764
765
766def updatedb(updatedb_text, new_path):
767 lines = [line for line in updatedb_text.split("\n")]
768 for i, line in enumerate(lines):
769 if line.startswith("PRUNEPATHS="):
770 paths_line = line.split("=")[1].replace('"', '')
771 paths = paths_line.split(" ")
772 if new_path not in paths:
773 paths.append(new_path)
774 lines[i] = 'PRUNEPATHS="{}"'.format(' '.join(paths))
775 output = "\n".join(lines)
776 return output
diff --git a/hooks/charmhelpers/core/kernel.py b/hooks/charmhelpers/core/kernel.py
index b166efe..2d40452 100644
--- a/hooks/charmhelpers/core/kernel.py
+++ b/hooks/charmhelpers/core/kernel.py
@@ -15,15 +15,28 @@
15# See the License for the specific language governing permissions and15# See the License for the specific language governing permissions and
16# limitations under the License.16# limitations under the License.
1717
18__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"18import re
19import subprocess
1920
21from charmhelpers.osplatform import get_platform
20from charmhelpers.core.hookenv import (22from charmhelpers.core.hookenv import (
21 log,23 log,
22 INFO24 INFO
23)25)
2426
25from subprocess import check_call, check_output27__platform__ = get_platform()
26import re28if __platform__ == "ubuntu":
29 from charmhelpers.core.kernel_factory.ubuntu import (
30 persistent_modprobe,
31 update_initramfs,
32 ) # flake8: noqa -- ignore F401 for this import
33elif __platform__ == "centos":
34 from charmhelpers.core.kernel_factory.centos import (
35 persistent_modprobe,
36 update_initramfs,
37 ) # flake8: noqa -- ignore F401 for this import
38
39__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"
2740
2841
29def modprobe(module, persist=True):42def modprobe(module, persist=True):
@@ -32,11 +45,9 @@ def modprobe(module, persist=True):
3245
33 log('Loading kernel module %s' % module, level=INFO)46 log('Loading kernel module %s' % module, level=INFO)
3447
35 check_call(cmd)48 subprocess.check_call(cmd)
36 if persist:49 if persist:
37 with open('/etc/modules', 'r+') as modules:50 persistent_modprobe(module)
38 if module not in modules.read():
39 modules.write(module)
4051
4152
42def rmmod(module, force=False):53def rmmod(module, force=False):
@@ -46,21 +57,16 @@ def rmmod(module, force=False):
46 cmd.append('-f')57 cmd.append('-f')
47 cmd.append(module)58 cmd.append(module)
48 log('Removing kernel module %s' % module, level=INFO)59 log('Removing kernel module %s' % module, level=INFO)
49 return check_call(cmd)60 return subprocess.check_call(cmd)
5061
5162
52def lsmod():63def lsmod():
53 """Shows what kernel modules are currently loaded"""64 """Shows what kernel modules are currently loaded"""
54 return check_output(['lsmod'],65 return subprocess.check_output(['lsmod'],
55 universal_newlines=True)66 universal_newlines=True)
5667
5768
58def is_module_loaded(module):69def is_module_loaded(module):
59 """Checks if a kernel module is already loaded"""70 """Checks if a kernel module is already loaded"""
60 matches = re.findall('^%s[ ]+' % module, lsmod(), re.M)71 matches = re.findall('^%s[ ]+' % module, lsmod(), re.M)
61 return len(matches) > 072 return len(matches) > 0
62
63
64def update_initramfs(version='all'):
65 """Updates an initramfs image"""
66 return check_call(["update-initramfs", "-k", version, "-u"])
diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py
index 52eaf82..ec5e0fe 100644
--- a/hooks/charmhelpers/fetch/__init__.py
+++ b/hooks/charmhelpers/fetch/__init__.py
@@ -13,18 +13,12 @@
13# limitations under the License.13# limitations under the License.
1414
15import importlib15import importlib
16from tempfile import NamedTemporaryFile16from charmhelpers.osplatform import get_platform
17import time
18from yaml import safe_load17from yaml import safe_load
19from charmhelpers.core.host import (
20 lsb_release
21)
22import subprocess
23from charmhelpers.core.hookenv import (18from charmhelpers.core.hookenv import (
24 config,19 config,
25 log,20 log,
26)21)
27import os
2822
29import six23import six
30if six.PY3:24if six.PY3:
@@ -33,87 +27,6 @@ else:
33 from urlparse import urlparse, urlunparse27 from urlparse import urlparse, urlunparse
3428
3529
36CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
37deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
38"""
39PROPOSED_POCKET = """# Proposed
40deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted
41"""
42CLOUD_ARCHIVE_POCKETS = {
43 # Folsom
44 'folsom': 'precise-updates/folsom',
45 'precise-folsom': 'precise-updates/folsom',
46 'precise-folsom/updates': 'precise-updates/folsom',
47 'precise-updates/folsom': 'precise-updates/folsom',
48 'folsom/proposed': 'precise-proposed/folsom',
49 'precise-folsom/proposed': 'precise-proposed/folsom',
50 'precise-proposed/folsom': 'precise-proposed/folsom',
51 # Grizzly
52 'grizzly': 'precise-updates/grizzly',
53 'precise-grizzly': 'precise-updates/grizzly',
54 'precise-grizzly/updates': 'precise-updates/grizzly',
55 'precise-updates/grizzly': 'precise-updates/grizzly',
56 'grizzly/proposed': 'precise-proposed/grizzly',
57 'precise-grizzly/proposed': 'precise-proposed/grizzly',
58 'precise-proposed/grizzly': 'precise-proposed/grizzly',
59 # Havana
60 'havana': 'precise-updates/havana',
61 'precise-havana': 'precise-updates/havana',
62 'precise-havana/updates': 'precise-updates/havana',
63 'precise-updates/havana': 'precise-updates/havana',
64 'havana/proposed': 'precise-proposed/havana',
65 'precise-havana/proposed': 'precise-proposed/havana',
66 'precise-proposed/havana': 'precise-proposed/havana',
67 # Icehouse
68 'icehouse': 'precise-updates/icehouse',
69 'precise-icehouse': 'precise-updates/icehouse',
70 'precise-icehouse/updates': 'precise-updates/icehouse',
71 'precise-updates/icehouse': 'precise-updates/icehouse',
72 'icehouse/proposed': 'precise-proposed/icehouse',
73 'precise-icehouse/proposed': 'precise-proposed/icehouse',
74 'precise-proposed/icehouse': 'precise-proposed/icehouse',
75 # Juno
76 'juno': 'trusty-updates/juno',
77 'trusty-juno': 'trusty-updates/juno',
78 'trusty-juno/updates': 'trusty-updates/juno',
79 'trusty-updates/juno': 'trusty-updates/juno',
80 'juno/proposed': 'trusty-proposed/juno',
81 'trusty-juno/proposed': 'trusty-proposed/juno',
82 'trusty-proposed/juno': 'trusty-proposed/juno',
83 # Kilo
84 'kilo': 'trusty-updates/kilo',
85 'trusty-kilo': 'trusty-updates/kilo',
86 'trusty-kilo/updates': 'trusty-updates/kilo',
87 'trusty-updates/kilo': 'trusty-updates/kilo',
88 'kilo/proposed': 'trusty-proposed/kilo',
89 'trusty-kilo/proposed': 'trusty-proposed/kilo',
90 'trusty-proposed/kilo': 'trusty-proposed/kilo',
91 # Liberty
92 'liberty': 'trusty-updates/liberty',
93 'trusty-liberty': 'trusty-updates/liberty',
94 'trusty-liberty/updates': 'trusty-updates/liberty',
95 'trusty-updates/liberty': 'trusty-updates/liberty',
96 'liberty/proposed': 'trusty-proposed/liberty',
97 'trusty-liberty/proposed': 'trusty-proposed/liberty',
98 'trusty-proposed/liberty': 'trusty-proposed/liberty',
99 # Mitaka
100 'mitaka': 'trusty-updates/mitaka',
101 'trusty-mitaka': 'trusty-updates/mitaka',
102 'trusty-mitaka/updates': 'trusty-updates/mitaka',
103 'trusty-updates/mitaka': 'trusty-updates/mitaka',
104 'mitaka/proposed': 'trusty-proposed/mitaka',
105 'trusty-mitaka/proposed': 'trusty-proposed/mitaka',
106 'trusty-proposed/mitaka': 'trusty-proposed/mitaka',
107 # Newton
108 'newton': 'xenial-updates/newton',
109 'xenial-newton': 'xenial-updates/newton',
110 'xenial-newton/updates': 'xenial-updates/newton',
111 'xenial-updates/newton': 'xenial-updates/newton',
112 'newton/proposed': 'xenial-proposed/newton',
113 'xenial-newton/proposed': 'xenial-proposed/newton',
114 'xenial-proposed/newton': 'xenial-proposed/newton',
115}
116
117# The order of this list is very important. Handlers should be listed in from30# The order of this list is very important. Handlers should be listed in from
118# least- to most-specific URL matching.31# least- to most-specific URL matching.
119FETCH_HANDLERS = (32FETCH_HANDLERS = (
@@ -122,10 +35,6 @@ FETCH_HANDLERS = (
122 'charmhelpers.fetch.giturl.GitUrlFetchHandler',35 'charmhelpers.fetch.giturl.GitUrlFetchHandler',
123)36)
12437
125APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT.
126APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks.
127APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times.
128
12938
130class SourceConfigError(Exception):39class SourceConfigError(Exception):
131 pass40 pass
@@ -163,180 +72,38 @@ class BaseFetchHandler(object):
163 return urlunparse(parts)72 return urlunparse(parts)
16473
16574
166def filter_installed_packages(packages):75__platform__ = get_platform()
167 """Returns a list of packages that require installation"""76module = "charmhelpers.fetch.%s" % __platform__
168 cache = apt_cache()77fetch = importlib.import_module(module)
169 _pkgs = []
170 for package in packages:
171 try:
172 p = cache[package]
173 p.current_ver or _pkgs.append(package)
174 except KeyError:
175 log('Package {} has no installation candidate.'.format(package),
176 level='WARNING')
177 _pkgs.append(package)
178 return _pkgs
179
180
181def apt_cache(in_memory=True, progress=None):
182 """Build and return an apt cache"""
183 from apt import apt_pkg
184 apt_pkg.init()
185 if in_memory:
186 apt_pkg.config.set("Dir::Cache::pkgcache", "")
187 apt_pkg.config.set("Dir::Cache::srcpkgcache", "")
188 return apt_pkg.Cache(progress)
189
190
191def apt_install(packages, options=None, fatal=False):
192 """Install one or more packages"""
193 if options is None:
194 options = ['--option=Dpkg::Options::=--force-confold']
195
196 cmd = ['apt-get', '--assume-yes']
197 cmd.extend(options)
198 cmd.append('install')
199 if isinstance(packages, six.string_types):
200 cmd.append(packages)
201 else:
202 cmd.extend(packages)
203 log("Installing {} with options: {}".format(packages,
204 options))
205 _run_apt_command(cmd, fatal)
206
207
208def apt_upgrade(options=None, fatal=False, dist=False):
209 """Upgrade all packages"""
210 if options is None:
211 options = ['--option=Dpkg::Options::=--force-confold']
212
213 cmd = ['apt-get', '--assume-yes']
214 cmd.extend(options)
215 if dist:
216 cmd.append('dist-upgrade')
217 else:
218 cmd.append('upgrade')
219 log("Upgrading with options: {}".format(options))
220 _run_apt_command(cmd, fatal)
221
222
223def apt_update(fatal=False):
224 """Update local apt cache"""
225 cmd = ['apt-get', 'update']
226 _run_apt_command(cmd, fatal)
227
228
229def apt_purge(packages, fatal=False):
230 """Purge one or more packages"""
231 cmd = ['apt-get', '--assume-yes', 'purge']
232 if isinstance(packages, six.string_types):
233 cmd.append(packages)
234 else:
235 cmd.extend(packages)
236 log("Purging {}".format(packages))
237 _run_apt_command(cmd, fatal)
238
239
240def apt_mark(packages, mark, fatal=False):
241 """Flag one or more packages using apt-mark"""
242 log("Marking {} as {}".format(packages, mark))
243 cmd = ['apt-mark', mark]
244 if isinstance(packages, six.string_types):
245 cmd.append(packages)
246 else:
247 cmd.extend(packages)
248
249 if fatal:
250 subprocess.check_call(cmd, universal_newlines=True)
251 else:
252 subprocess.call(cmd, universal_newlines=True)
253
254
255def apt_hold(packages, fatal=False):
256 return apt_mark(packages, 'hold', fatal=fatal)
257
258
259def apt_unhold(packages, fatal=False):
260 return apt_mark(packages, 'unhold', fatal=fatal)
261
26278
263def add_source(source, key=None):79filter_installed_packages = fetch.filter_installed_packages
264 """Add a package source to this system.80install = fetch.install
81upgrade = fetch.upgrade
82update = fetch.update
83purge = fetch.purge
84add_source = fetch.add_source
26585
266 @param source: a URL or sources.list entry, as supported by86if __platform__ == "ubuntu":
267 add-apt-repository(1). Examples::87 apt_cache = fetch.apt_cache
26888 apt_install = fetch.install
269 ppa:charmers/example89 apt_update = fetch.update
270 deb https://stub:key@private.example.com/ubuntu trusty main90 apt_upgrade = fetch.upgrade
27191 apt_purge = fetch.purge
272 In addition:92 apt_mark = fetch.apt_mark
273 'proposed:' may be used to enable the standard 'proposed'93 apt_hold = fetch.apt_hold
274 pocket for the release.94 apt_unhold = fetch.apt_unhold
275 'cloud:' may be used to activate official cloud archive pockets,95 get_upstream_version = fetch.get_upstream_version
276 such as 'cloud:icehouse'96elif __platform__ == "centos":
277 'distro' may be used as a noop97 yum_search = fetch.yum_search
278
279 @param key: A key to be added to the system's APT keyring and used
280 to verify the signatures on packages. Ideally, this should be an
281 ASCII format GPG public key including the block headers. A GPG key
282 id may also be used, but be aware that only insecure protocols are
283 available to retrieve the actual public key from a public keyserver
284 placing your Juju environment at risk. ppa and cloud archive keys
285 are securely added automtically, so sould not be provided.
286 """
287 if source is None:
288 log('Source is not present. Skipping')
289 return
290
291 if (source.startswith('ppa:') or
292 source.startswith('http') or
293 source.startswith('deb ') or
294 source.startswith('cloud-archive:')):
295 subprocess.check_call(['add-apt-repository', '--yes', source])
296 elif source.startswith('cloud:'):
297 apt_install(filter_installed_packages(['ubuntu-cloud-keyring']),
298 fatal=True)
299 pocket = source.split(':')[-1]
300 if pocket not in CLOUD_ARCHIVE_POCKETS:
301 raise SourceConfigError(
302 'Unsupported cloud: source option %s' %
303 pocket)
304 actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket]
305 with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt:
306 apt.write(CLOUD_ARCHIVE.format(actual_pocket))
307 elif source == 'proposed':
308 release = lsb_release()['DISTRIB_CODENAME']
309 with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
310 apt.write(PROPOSED_POCKET.format(release))
311 elif source == 'distro':
312 pass
313 else:
314 log("Unknown source: {!r}".format(source))
315
316 if key:
317 if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
318 with NamedTemporaryFile('w+') as key_file:
319 key_file.write(key)
320 key_file.flush()
321 key_file.seek(0)
322 subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file)
323 else:
324 # Note that hkp: is in no way a secure protocol. Using a
325 # GPG key id is pointless from a security POV unless you
326 # absolutely trust your network and DNS.
327 subprocess.check_call(['apt-key', 'adv', '--keyserver',
328 'hkp://keyserver.ubuntu.com:80', '--recv',
329 key])
33098
33199
332def configure_sources(update=False,100def configure_sources(update=False,
333 sources_var='install_sources',101 sources_var='install_sources',
334 keys_var='install_keys'):102 keys_var='install_keys'):
335 """103 """Configure multiple sources from charm configuration.
336 Configure multiple sources from charm configuration.
337104
338 The lists are encoded as yaml fragments in the configuration.105 The lists are encoded as yaml fragments in the configuration.
339 The frament needs to be included as a string. Sources and their106 The fragment needs to be included as a string. Sources and their
340 corresponding keys are of the types supported by add_source().107 corresponding keys are of the types supported by add_source().
341108
342 Example config:109 Example config:
@@ -368,12 +135,11 @@ def configure_sources(update=False,
368 for source, key in zip(sources, keys):135 for source, key in zip(sources, keys):
369 add_source(source, key)136 add_source(source, key)
370 if update:137 if update:
371 apt_update(fatal=True)138 fetch.update(fatal=True)
372139
373140
374def install_remote(source, *args, **kwargs):141def install_remote(source, *args, **kwargs):
375 """142 """Install a file tree from a remote source.
376 Install a file tree from a remote source
377143
378 The specified source should be a url of the form:144 The specified source should be a url of the form:
379 scheme://[host]/path[#[option=value][&...]]145 scheme://[host]/path[#[option=value][&...]]
@@ -406,6 +172,7 @@ def install_remote(source, *args, **kwargs):
406172
407173
408def install_from_config(config_var_name):174def install_from_config(config_var_name):
175 """Install a file from config."""
409 charm_config = config()176 charm_config = config()
410 source = charm_config[config_var_name]177 source = charm_config[config_var_name]
411 return install_remote(source)178 return install_remote(source)
@@ -428,40 +195,3 @@ def plugins(fetch_handlers=None):
428 log("FetchHandler {} not found, skipping plugin".format(195 log("FetchHandler {} not found, skipping plugin".format(
429 handler_name))196 handler_name))
430 return plugin_list197 return plugin_list
431
432
433def _run_apt_command(cmd, fatal=False):
434 """
435 Run an APT command, checking output and retrying if the fatal flag is set
436 to True.
437
438 :param: cmd: str: The apt command to run.
439 :param: fatal: bool: Whether the command's output should be checked and
440 retried.
441 """
442 env = os.environ.copy()
443
444 if 'DEBIAN_FRONTEND' not in env:
445 env['DEBIAN_FRONTEND'] = 'noninteractive'
446
447 if fatal:
448 retry_count = 0
449 result = None
450
451 # If the command is considered "fatal", we need to retry if the apt
452 # lock was not acquired.
453
454 while result is None or result == APT_NO_LOCK:
455 try:
456 result = subprocess.check_call(cmd, env=env)
457 except subprocess.CalledProcessError as e:
458 retry_count = retry_count + 1
459 if retry_count > APT_NO_LOCK_RETRY_COUNT:
460 raise
461 result = e.returncode
462 log("Couldn't acquire DPKG lock. Will retry in {} seconds."
463 "".format(APT_NO_LOCK_RETRY_DELAY))
464 time.sleep(APT_NO_LOCK_RETRY_DELAY)
465
466 else:
467 subprocess.call(cmd, env=env)
diff --git a/hooks/charmhelpers/fetch/bzrurl.py b/hooks/charmhelpers/fetch/bzrurl.py
index b3404d8..07cd029 100644
--- a/hooks/charmhelpers/fetch/bzrurl.py
+++ b/hooks/charmhelpers/fetch/bzrurl.py
@@ -18,19 +18,20 @@ from charmhelpers.fetch import (
18 BaseFetchHandler,18 BaseFetchHandler,
19 UnhandledSource,19 UnhandledSource,
20 filter_installed_packages,20 filter_installed_packages,
21 apt_install,21 install,
22)22)
23from charmhelpers.core.host import mkdir23from charmhelpers.core.host import mkdir
2424
2525
26if filter_installed_packages(['bzr']) != []:26if filter_installed_packages(['bzr']) != []:
27 apt_install(['bzr'])27 install(['bzr'])
28 if filter_installed_packages(['bzr']) != []:28 if filter_installed_packages(['bzr']) != []:
29 raise NotImplementedError('Unable to install bzr')29 raise NotImplementedError('Unable to install bzr')
3030
3131
32class BzrUrlFetchHandler(BaseFetchHandler):32class BzrUrlFetchHandler(BaseFetchHandler):
33 """Handler for bazaar branches via generic and lp URLs"""33 """Handler for bazaar branches via generic and lp URLs."""
34
34 def can_handle(self, source):35 def can_handle(self, source):
35 url_parts = self.parse_url(source)36 url_parts = self.parse_url(source)
36 if url_parts.scheme not in ('bzr+ssh', 'lp', ''):37 if url_parts.scheme not in ('bzr+ssh', 'lp', ''):
diff --git a/hooks/charmhelpers/fetch/giturl.py b/hooks/charmhelpers/fetch/giturl.py
index f708d1e..4cf21bc 100644
--- a/hooks/charmhelpers/fetch/giturl.py
+++ b/hooks/charmhelpers/fetch/giturl.py
@@ -18,17 +18,18 @@ from charmhelpers.fetch import (
18 BaseFetchHandler,18 BaseFetchHandler,
19 UnhandledSource,19 UnhandledSource,
20 filter_installed_packages,20 filter_installed_packages,
21 apt_install,21 install,
22)22)
2323
24if filter_installed_packages(['git']) != []:24if filter_installed_packages(['git']) != []:
25 apt_install(['git'])25 install(['git'])
26 if filter_installed_packages(['git']) != []:26 if filter_installed_packages(['git']) != []:
27 raise NotImplementedError('Unable to install git')27 raise NotImplementedError('Unable to install git')
2828
2929
30class GitUrlFetchHandler(BaseFetchHandler):30class GitUrlFetchHandler(BaseFetchHandler):
31 """Handler for git branches via generic and github URLs"""31 """Handler for git branches via generic and github URLs."""
32
32 def can_handle(self, source):33 def can_handle(self, source):
33 url_parts = self.parse_url(source)34 url_parts = self.parse_url(source)
34 # TODO (mattyw) no support for ssh git@ yet35 # TODO (mattyw) no support for ssh git@ yet
diff --git a/tests/charmhelpers/contrib/amulet/deployment.py b/tests/charmhelpers/contrib/amulet/deployment.py
index 0146236..9c65518 100644
--- a/tests/charmhelpers/contrib/amulet/deployment.py
+++ b/tests/charmhelpers/contrib/amulet/deployment.py
@@ -78,11 +78,15 @@ class AmuletDeployment(object):
7878
79 def _deploy(self):79 def _deploy(self):
80 """Deploy environment and wait for all hooks to finish executing."""80 """Deploy environment and wait for all hooks to finish executing."""
81 timeout = int(os.environ.get('AMULET_SETUP_TIMEOUT', 900))
81 try:82 try:
82 self.d.setup(timeout=900)83 self.d.setup(timeout=timeout)
83 self.d.sentry.wait(timeout=900)84 self.d.sentry.wait(timeout=timeout)
84 except amulet.helpers.TimeoutError:85 except amulet.helpers.TimeoutError:
85 amulet.raise_status(amulet.FAIL, msg="Deployment timed out")86 amulet.raise_status(
87 amulet.FAIL,
88 msg="Deployment timed out ({}s)".format(timeout)
89 )
86 except Exception:90 except Exception:
87 raise91 raise
8892
diff --git a/tests/charmhelpers/contrib/amulet/utils.py b/tests/charmhelpers/contrib/amulet/utils.py
index a39ed4c..f9e4c3a 100644
--- a/tests/charmhelpers/contrib/amulet/utils.py
+++ b/tests/charmhelpers/contrib/amulet/utils.py
@@ -148,7 +148,8 @@ class AmuletUtils(object):
148148
149 for service_name in services_list:149 for service_name in services_list:
150 if (self.ubuntu_releases.index(release) >= systemd_switch or150 if (self.ubuntu_releases.index(release) >= systemd_switch or
151 service_name in ['rabbitmq-server', 'apache2']):151 service_name in ['rabbitmq-server', 'apache2',
152 'memcached']):
152 # init is systemd (or regular sysv)153 # init is systemd (or regular sysv)
153 cmd = 'sudo service {} status'.format(service_name)154 cmd = 'sudo service {} status'.format(service_name)
154 output, code = sentry_unit.run(cmd)155 output, code = sentry_unit.run(cmd)
@@ -546,7 +547,7 @@ class AmuletUtils(object):
546 raise if it is present.547 raise if it is present.
547 :returns: List of process IDs548 :returns: List of process IDs
548 """549 """
549 cmd = 'pidof -x {}'.format(process_name)550 cmd = 'pidof -x "{}"'.format(process_name)
550 if not expect_success:551 if not expect_success:
551 cmd += " || exit 0 && exit 1"552 cmd += " || exit 0 && exit 1"
552 output, code = sentry_unit.run(cmd)553 output, code = sentry_unit.run(cmd)

Subscribers

People subscribed via source and target branches