summaryrefslogtreecommitdiff
path: root/hooks/charmhelpers
diff options
authorMario Splivalo <mario.splivalo@canonical.com>2015-01-19 20:57:11 +0100
committerMario Splivalo <mario.splivalo@canonical.com>2015-01-19 20:57:11 +0100
commitbe4fc1f8a4281310e7b47d1064c639d80b3c5680 (patch)
treef8e009147f0056d761e90db3fb360babc94ca3b1 /hooks/charmhelpers
parent16da59c186121531865aa7eecc4cf88dabe1ec6f (diff)
Added charmhelpers.contrib.python.packages.
Diffstat (limited to 'hooks/charmhelpers')
-rw-r--r--hooks/charmhelpers/contrib/hahelpers/cluster.py38
-rw-r--r--hooks/charmhelpers/contrib/python/__init__.py0
-rw-r--r--hooks/charmhelpers/contrib/python/packages.py80
-rw-r--r--hooks/charmhelpers/core/decorators.py41
-rw-r--r--hooks/charmhelpers/core/host.py11
-rw-r--r--hooks/charmhelpers/core/templating.py2
-rw-r--r--hooks/charmhelpers/fetch/__init__.py9
7 files changed, 163 insertions, 18 deletions
diff --git a/hooks/charmhelpers/contrib/hahelpers/cluster.py b/hooks/charmhelpers/contrib/hahelpers/cluster.py
index 52ce4b7..912b2fe 100644
--- a/hooks/charmhelpers/contrib/hahelpers/cluster.py
+++ b/hooks/charmhelpers/contrib/hahelpers/cluster.py
@@ -13,6 +13,7 @@ clustering-related helpers.
import subprocess
import os
+
from socket import gethostname as get_unit_hostname
import six
@@ -28,12 +29,19 @@ from charmhelpers.core.hookenv import (
WARNING,
unit_get,
)
+from charmhelpers.core.decorators import (
+ retry_on_exception,
+)
class HAIncompleteConfig(Exception):
pass
+class CRMResourceNotFound(Exception):
+ pass
+
+
def is_elected_leader(resource):
"""
Returns True if the charm executing this is the elected cluster leader.
@@ -68,24 +76,30 @@ def is_clustered():
return False
-def is_crm_leader(resource):
+@retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound)
+def is_crm_leader(resource, retry=False):
"""
Returns True if the charm calling this is the elected corosync leader,
as returned by calling the external "crm" command.
+
+ We allow this operation to be retried to avoid the possibility of getting a
+ false negative. See LP #1396246 for more info.
"""
- cmd = [
- "crm", "resource",
- "show", resource
- ]
+ cmd = ['crm', 'resource', 'show', resource]
try:
- status = subprocess.check_output(cmd).decode('UTF-8')
+ status = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ if not isinstance(status, six.text_type):
+ status = six.text_type(status, "utf-8")
except subprocess.CalledProcessError:
- return False
- else:
- if get_unit_hostname() in status:
- return True
- else:
- return False
+ status = None
+
+ if status and get_unit_hostname() in status:
+ return True
+
+ if status and "resource %s is NOT running" % (resource) in status:
+ raise CRMResourceNotFound("CRM resource %s not found" % (resource))
+
+ return False
def is_leader(resource):
diff --git a/hooks/charmhelpers/contrib/python/__init__.py b/hooks/charmhelpers/contrib/python/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/hooks/charmhelpers/contrib/python/__init__.py
diff --git a/hooks/charmhelpers/contrib/python/packages.py b/hooks/charmhelpers/contrib/python/packages.py
new file mode 100644
index 0000000..aebeab0
--- /dev/null
+++ b/hooks/charmhelpers/contrib/python/packages.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>"
+
+from charmhelpers.fetch import apt_install, apt_update
+from charmhelpers.core.hookenv import log
+
+try:
+ from pip import main as pip_execute
+except ImportError:
+ apt_update()
+ apt_install('python-pip')
+ from pip import main as pip_execute
+
+
+def parse_options(given, available):
+ """Given a set of options, check if available"""
+ for key, value in sorted(given.items()):
+ if key in available:
+ yield "--{0}={1}".format(key, value)
+
+
+def pip_install_requirements(requirements, **options):
+ """Install a requirements file """
+ command = ["install"]
+
+ available_options = ('proxy', 'src', 'log', )
+ for option in parse_options(options, available_options):
+ command.append(option)
+
+ command.append("-r {0}".format(requirements))
+ log("Installing from file: {} with options: {}".format(requirements,
+ command))
+ pip_execute(command)
+
+
+def pip_install(package, fatal=False, upgrade=False, **options):
+ """Install a python package"""
+ command = ["install"]
+
+ available_options = ('proxy', 'src', 'log', "index-url", )
+ for option in parse_options(options, available_options):
+ command.append(option)
+
+ if upgrade:
+ command.append('--upgrade')
+
+ if isinstance(package, list):
+ command.extend(package)
+ else:
+ command.append(package)
+
+ log("Installing {} package with options: {}".format(package,
+ command))
+ pip_execute(command)
+
+
+def pip_uninstall(package, **options):
+ """Uninstall a python package"""
+ command = ["uninstall", "-q", "-y"]
+
+ available_options = ('proxy', 'log', )
+ for option in parse_options(options, available_options):
+ command.append(option)
+
+ if isinstance(package, list):
+ command.extend(package)
+ else:
+ command.append(package)
+
+ log("Uninstalling {} package with options: {}".format(package,
+ command))
+ pip_execute(command)
+
+
+def pip_list():
+ """Returns the list of current python installed packages
+ """
+ return pip_execute(["list"])
diff --git a/hooks/charmhelpers/core/decorators.py b/hooks/charmhelpers/core/decorators.py
new file mode 100644
index 0000000..029a4ef
--- /dev/null
+++ b/hooks/charmhelpers/core/decorators.py
@@ -0,0 +1,41 @@
+#
+# Copyright 2014 Canonical Ltd.
+#
+# Authors:
+# Edward Hope-Morley <opentastic@gmail.com>
+#
+
+import time
+
+from charmhelpers.core.hookenv import (
+ log,
+ INFO,
+)
+
+
+def retry_on_exception(num_retries, base_delay=0, exc_type=Exception):
+ """If the decorated function raises exception exc_type, allow num_retries
+ retry attempts before raise the exception.
+ """
+ def _retry_on_exception_inner_1(f):
+ def _retry_on_exception_inner_2(*args, **kwargs):
+ retries = num_retries
+ multiplier = 1
+ while True:
+ try:
+ return f(*args, **kwargs)
+ except exc_type:
+ if not retries:
+ raise
+
+ delay = base_delay * multiplier
+ multiplier += 1
+ log("Retrying '%s' %d more times (delay=%s)" %
+ (f.__name__, retries, delay), level=INFO)
+ retries -= 1
+ if delay:
+ time.sleep(delay)
+
+ return _retry_on_exception_inner_2
+
+ return _retry_on_exception_inner_1
diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py
index c6f1680..5221120 100644
--- a/hooks/charmhelpers/core/host.py
+++ b/hooks/charmhelpers/core/host.py
@@ -162,13 +162,16 @@ def mkdir(path, owner='root', group='root', perms=0o555, force=False):
uid = pwd.getpwnam(owner).pw_uid
gid = grp.getgrnam(group).gr_gid
realpath = os.path.abspath(path)
- if os.path.exists(realpath):
- if force and not os.path.isdir(realpath):
+ path_exists = os.path.exists(realpath)
+ if path_exists and force:
+ if not os.path.isdir(realpath):
log("Removing non-directory file {} prior to mkdir()".format(path))
os.unlink(realpath)
- else:
+ os.makedirs(realpath, perms)
+ os.chown(realpath, uid, gid)
+ elif not path_exists:
os.makedirs(realpath, perms)
- os.chown(realpath, uid, gid)
+ os.chown(realpath, uid, gid)
def write_file(path, content, owner='root', group='root', perms=0o444):
diff --git a/hooks/charmhelpers/core/templating.py b/hooks/charmhelpers/core/templating.py
index 83133fa..569eaed 100644
--- a/hooks/charmhelpers/core/templating.py
+++ b/hooks/charmhelpers/core/templating.py
@@ -48,5 +48,5 @@ def render(source, target, context, owner='root', group='root',
level=hookenv.ERROR)
raise e
content = template.render(context)
- host.mkdir(os.path.dirname(target))
+ host.mkdir(os.path.dirname(target), owner, group)
host.write_file(target, content, owner, group, perms)
diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py
index 0a126fc..aceadea 100644
--- a/hooks/charmhelpers/fetch/__init__.py
+++ b/hooks/charmhelpers/fetch/__init__.py
@@ -64,9 +64,16 @@ CLOUD_ARCHIVE_POCKETS = {
'trusty-juno/updates': 'trusty-updates/juno',
'trusty-updates/juno': 'trusty-updates/juno',
'juno/proposed': 'trusty-proposed/juno',
- 'juno/proposed': 'trusty-proposed/juno',
'trusty-juno/proposed': 'trusty-proposed/juno',
'trusty-proposed/juno': 'trusty-proposed/juno',
+ # Kilo
+ 'kilo': 'trusty-updates/kilo',
+ 'trusty-kilo': 'trusty-updates/kilo',
+ 'trusty-kilo/updates': 'trusty-updates/kilo',
+ 'trusty-updates/kilo': 'trusty-updates/kilo',
+ 'kilo/proposed': 'trusty-proposed/kilo',
+ 'trusty-kilo/proposed': 'trusty-proposed/kilo',
+ 'trusty-proposed/kilo': 'trusty-proposed/kilo',
}
# The order of this list is very important. Handlers should be listed in from