summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.zuul.yaml3
-rwxr-xr-x[l---------]actions/create-cache-tier42
-rwxr-xr-xactions/create-cache-tier.py16
-rwxr-xr-xactions/create-erasure-profile24
-rwxr-xr-xactions/create-pool18
-rwxr-xr-xactions/delete-erasure-profile15
-rwxr-xr-xactions/delete-pool19
-rwxr-xr-xactions/get-erasure-profile15
-rwxr-xr-xactions/list-erasure-profiles21
-rwxr-xr-xactions/list-pools21
-rwxr-xr-xactions/pool-get21
-rwxr-xr-xactions/pool-set19
-rwxr-xr-xactions/pool-statistics20
-rwxr-xr-x[l---------]actions/remove-cache-tier42
-rwxr-xr-xactions/remove-cache-tier.py19
-rwxr-xr-xactions/remove-pool-snapshot20
-rwxr-xr-xactions/rename-pool20
-rwxr-xr-xactions/set-pool-max-bytes20
-rwxr-xr-xactions/snapshot-pool20
-rw-r--r--charm-helpers-hooks.yaml2
-rw-r--r--charmhelpers/__init__.py (renamed from hooks/charmhelpers/__init__.py)0
-rw-r--r--charmhelpers/cli/__init__.py (renamed from hooks/charmhelpers/cli/__init__.py)0
-rw-r--r--charmhelpers/cli/benchmark.py (renamed from hooks/charmhelpers/cli/benchmark.py)0
-rw-r--r--charmhelpers/cli/commands.py (renamed from hooks/charmhelpers/cli/commands.py)0
-rw-r--r--charmhelpers/cli/hookenv.py (renamed from hooks/charmhelpers/cli/hookenv.py)0
-rw-r--r--charmhelpers/cli/host.py (renamed from hooks/charmhelpers/cli/host.py)0
-rw-r--r--charmhelpers/cli/unitdata.py (renamed from hooks/charmhelpers/cli/unitdata.py)9
-rw-r--r--charmhelpers/contrib/__init__.py (renamed from hooks/charmhelpers/contrib/__init__.py)0
-rw-r--r--charmhelpers/contrib/charmsupport/__init__.py (renamed from hooks/charmhelpers/contrib/charmsupport/__init__.py)0
-rw-r--r--charmhelpers/contrib/charmsupport/nrpe.py (renamed from hooks/charmhelpers/contrib/charmsupport/nrpe.py)0
-rw-r--r--charmhelpers/contrib/charmsupport/volumes.py (renamed from hooks/charmhelpers/contrib/charmsupport/volumes.py)0
-rw-r--r--charmhelpers/contrib/hardening/README.hardening.md (renamed from hooks/charmhelpers/contrib/hardening/README.hardening.md)0
-rw-r--r--charmhelpers/contrib/hardening/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/apache/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/apache/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/apache/checks/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/apache/checks/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/apache/checks/config.py (renamed from hooks/charmhelpers/contrib/hardening/apache/checks/config.py)0
-rw-r--r--charmhelpers/contrib/hardening/apache/templates/99-hardening.conf (renamed from hooks/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf)0
-rw-r--r--charmhelpers/contrib/hardening/apache/templates/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/apache/templates/alias.conf (renamed from hooks/charmhelpers/contrib/hardening/apache/templates/alias.conf)0
-rw-r--r--charmhelpers/contrib/hardening/audits/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/audits/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/audits/apache.py (renamed from hooks/charmhelpers/contrib/hardening/audits/apache.py)0
-rw-r--r--charmhelpers/contrib/hardening/audits/apt.py (renamed from hooks/charmhelpers/contrib/hardening/audits/apt.py)0
-rw-r--r--charmhelpers/contrib/hardening/audits/file.py (renamed from hooks/charmhelpers/contrib/hardening/audits/file.py)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/defaults/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/apache.yaml (renamed from hooks/charmhelpers/contrib/hardening/defaults/apache.yaml)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/apache.yaml.schema (renamed from hooks/charmhelpers/contrib/hardening/defaults/apache.yaml.schema)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/mysql.yaml (renamed from hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/mysql.yaml.schema (renamed from hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/os.yaml (renamed from hooks/charmhelpers/contrib/hardening/defaults/os.yaml)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/os.yaml.schema (renamed from hooks/charmhelpers/contrib/hardening/defaults/os.yaml.schema)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/ssh.yaml (renamed from hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml)0
-rw-r--r--charmhelpers/contrib/hardening/defaults/ssh.yaml.schema (renamed from hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema)0
-rw-r--r--charmhelpers/contrib/hardening/harden.py (renamed from hooks/charmhelpers/contrib/hardening/harden.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/host/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/apt.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/apt.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/limits.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/limits.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/login.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/login.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/minimize_access.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/minimize_access.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/pam.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/pam.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/profile.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/profile.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/securetty.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/securetty.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/suid_sgid.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/suid_sgid.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/checks/sysctl.py (renamed from hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/10.hardcore.conf (renamed from hooks/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/99-hardening.sh (renamed from hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf (renamed from hooks/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/host/templates/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/login.defs (renamed from hooks/charmhelpers/contrib/hardening/host/templates/login.defs)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/modules (renamed from hooks/charmhelpers/contrib/hardening/host/templates/modules)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/passwdqc.conf (renamed from hooks/charmhelpers/contrib/hardening/host/templates/passwdqc.conf)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh (renamed from hooks/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/securetty (renamed from hooks/charmhelpers/contrib/hardening/host/templates/securetty)0
-rw-r--r--charmhelpers/contrib/hardening/host/templates/tally2 (renamed from hooks/charmhelpers/contrib/hardening/host/templates/tally2)0
-rw-r--r--charmhelpers/contrib/hardening/mysql/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/mysql/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/mysql/checks/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/mysql/checks/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/mysql/checks/config.py (renamed from hooks/charmhelpers/contrib/hardening/mysql/checks/config.py)0
-rw-r--r--charmhelpers/contrib/hardening/mysql/templates/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/mysql/templates/hardening.cnf (renamed from hooks/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/ssh/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/checks/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/ssh/checks/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/checks/config.py (renamed from hooks/charmhelpers/contrib/hardening/ssh/checks/config.py)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/templates/__init__.py (renamed from hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/templates/ssh_config (renamed from hooks/charmhelpers/contrib/hardening/ssh/templates/ssh_config)0
-rw-r--r--charmhelpers/contrib/hardening/ssh/templates/sshd_config (renamed from hooks/charmhelpers/contrib/hardening/ssh/templates/sshd_config)0
-rw-r--r--charmhelpers/contrib/hardening/templating.py (renamed from hooks/charmhelpers/contrib/hardening/templating.py)0
-rw-r--r--charmhelpers/contrib/hardening/utils.py (renamed from hooks/charmhelpers/contrib/hardening/utils.py)0
-rw-r--r--charmhelpers/contrib/network/__init__.py (renamed from hooks/charmhelpers/contrib/network/__init__.py)0
-rw-r--r--charmhelpers/contrib/network/ip.py (renamed from hooks/charmhelpers/contrib/network/ip.py)0
-rw-r--r--charmhelpers/contrib/openstack/__init__.py (renamed from hooks/charmhelpers/contrib/openstack/__init__.py)0
-rw-r--r--charmhelpers/contrib/openstack/alternatives.py (renamed from hooks/charmhelpers/contrib/openstack/alternatives.py)0
-rw-r--r--charmhelpers/contrib/openstack/exceptions.py (renamed from hooks/charmhelpers/contrib/openstack/exceptions.py)0
-rw-r--r--charmhelpers/contrib/openstack/utils.py (renamed from hooks/charmhelpers/contrib/openstack/utils.py)4
-rw-r--r--charmhelpers/contrib/python.py (renamed from hooks/charmhelpers/contrib/python.py)0
-rw-r--r--charmhelpers/contrib/storage/__init__.py (renamed from hooks/charmhelpers/contrib/storage/__init__.py)0
-rw-r--r--charmhelpers/contrib/storage/linux/__init__.py (renamed from hooks/charmhelpers/contrib/storage/linux/__init__.py)0
-rw-r--r--charmhelpers/contrib/storage/linux/ceph.py (renamed from hooks/charmhelpers/contrib/storage/linux/ceph.py)191
-rw-r--r--charmhelpers/contrib/storage/linux/loopback.py (renamed from hooks/charmhelpers/contrib/storage/linux/loopback.py)0
-rw-r--r--charmhelpers/contrib/storage/linux/lvm.py (renamed from hooks/charmhelpers/contrib/storage/linux/lvm.py)0
-rw-r--r--charmhelpers/contrib/storage/linux/utils.py (renamed from hooks/charmhelpers/contrib/storage/linux/utils.py)0
-rw-r--r--charmhelpers/core/__init__.py (renamed from hooks/charmhelpers/core/__init__.py)0
-rw-r--r--charmhelpers/core/decorators.py (renamed from hooks/charmhelpers/core/decorators.py)0
-rw-r--r--charmhelpers/core/files.py (renamed from hooks/charmhelpers/core/files.py)0
-rw-r--r--charmhelpers/core/fstab.py (renamed from hooks/charmhelpers/core/fstab.py)0
-rw-r--r--charmhelpers/core/hookenv.py (renamed from hooks/charmhelpers/core/hookenv.py)74
-rw-r--r--charmhelpers/core/host.py (renamed from hooks/charmhelpers/core/host.py)0
-rw-r--r--charmhelpers/core/host_factory/__init__.py (renamed from hooks/charmhelpers/core/host_factory/__init__.py)0
-rw-r--r--charmhelpers/core/host_factory/centos.py (renamed from hooks/charmhelpers/core/host_factory/centos.py)0
-rw-r--r--charmhelpers/core/host_factory/ubuntu.py (renamed from hooks/charmhelpers/core/host_factory/ubuntu.py)0
-rw-r--r--charmhelpers/core/hugepage.py (renamed from hooks/charmhelpers/core/hugepage.py)0
-rw-r--r--charmhelpers/core/kernel.py (renamed from hooks/charmhelpers/core/kernel.py)0
-rw-r--r--charmhelpers/core/kernel_factory/__init__.py (renamed from hooks/charmhelpers/core/kernel_factory/__init__.py)0
-rw-r--r--charmhelpers/core/kernel_factory/centos.py (renamed from hooks/charmhelpers/core/kernel_factory/centos.py)0
-rw-r--r--charmhelpers/core/kernel_factory/ubuntu.py (renamed from hooks/charmhelpers/core/kernel_factory/ubuntu.py)0
-rw-r--r--charmhelpers/core/services/__init__.py (renamed from hooks/charmhelpers/core/services/__init__.py)0
-rw-r--r--charmhelpers/core/services/base.py (renamed from hooks/charmhelpers/core/services/base.py)0
-rw-r--r--charmhelpers/core/services/helpers.py (renamed from hooks/charmhelpers/core/services/helpers.py)0
-rw-r--r--charmhelpers/core/strutils.py (renamed from hooks/charmhelpers/core/strutils.py)0
-rw-r--r--charmhelpers/core/sysctl.py (renamed from hooks/charmhelpers/core/sysctl.py)0
-rw-r--r--charmhelpers/core/templating.py (renamed from hooks/charmhelpers/core/templating.py)0
-rw-r--r--charmhelpers/core/unitdata.py (renamed from hooks/charmhelpers/core/unitdata.py)0
-rw-r--r--charmhelpers/fetch/__init__.py (renamed from hooks/charmhelpers/fetch/__init__.py)0
-rw-r--r--charmhelpers/fetch/archiveurl.py (renamed from hooks/charmhelpers/fetch/archiveurl.py)0
-rw-r--r--charmhelpers/fetch/bzrurl.py (renamed from hooks/charmhelpers/fetch/bzrurl.py)0
-rw-r--r--charmhelpers/fetch/centos.py (renamed from hooks/charmhelpers/fetch/centos.py)0
-rw-r--r--charmhelpers/fetch/giturl.py (renamed from hooks/charmhelpers/fetch/giturl.py)0
-rw-r--r--charmhelpers/fetch/python/__init__.py (renamed from hooks/charmhelpers/fetch/python/__init__.py)0
-rw-r--r--charmhelpers/fetch/python/debug.py (renamed from hooks/charmhelpers/fetch/python/debug.py)0
-rw-r--r--charmhelpers/fetch/python/packages.py (renamed from hooks/charmhelpers/fetch/python/packages.py)0
-rw-r--r--charmhelpers/fetch/python/rpdb.py (renamed from hooks/charmhelpers/fetch/python/rpdb.py)0
-rw-r--r--charmhelpers/fetch/python/version.py (renamed from hooks/charmhelpers/fetch/python/version.py)0
-rw-r--r--charmhelpers/fetch/snap.py (renamed from hooks/charmhelpers/fetch/snap.py)0
-rw-r--r--charmhelpers/fetch/ubuntu.py (renamed from hooks/charmhelpers/fetch/ubuntu.py)183
-rw-r--r--charmhelpers/osplatform.py (renamed from hooks/charmhelpers/osplatform.py)0
-rw-r--r--charmhelpers/payload/__init__.py (renamed from hooks/charmhelpers/payload/__init__.py)0
-rw-r--r--charmhelpers/payload/execd.py (renamed from hooks/charmhelpers/payload/execd.py)0
-rw-r--r--hooks/ceph.py19
-rwxr-xr-xhooks/ceph_hooks.py14
-rwxr-xr-xhooks/install2
-rw-r--r--hooks/utils.py4
-rw-r--r--tox.ini3
-rw-r--r--unit_tests/__init__.py19
-rw-r--r--unit_tests/test_ceph.py10
-rw-r--r--unit_tests/test_ceph_hooks.py4
-rw-r--r--unit_tests/test_utils.py2
145 files changed, 748 insertions, 187 deletions
diff --git a/.zuul.yaml b/.zuul.yaml
index aa9c508..7051aee 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,4 +1,3 @@
1- project: 1- project:
2 templates: 2 templates:
3 - python-charm-jobs 3 - python35-charm-jobs
4 - openstack-python35-jobs-nonvoting
diff --git a/actions/create-cache-tier b/actions/create-cache-tier
index 2a7e434..e8170cf 120000..100755
--- a/actions/create-cache-tier
+++ b/actions/create-cache-tier
@@ -1 +1,41 @@
1create-cache-tier.py \ No newline at end of file 1#!/usr/bin/python
2__author__ = 'chris'
3from subprocess import CalledProcessError
4import sys
5
6sys.path.append('hooks')
7
8from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists
9from charmhelpers.core.hookenv import action_get, log, action_fail
10
11
12def make_cache_tier():
13 backer_pool = action_get("backer-pool")
14 cache_pool = action_get("cache-pool")
15 cache_mode = action_get("cache-mode")
16
17 # Pre flight checks
18 if not pool_exists('admin', backer_pool):
19 log("Please create {} pool before calling create-cache-tier".format(
20 backer_pool))
21 action_fail("create-cache-tier failed. Backer pool {} must exist "
22 "before calling this".format(backer_pool))
23
24 if not pool_exists('admin', cache_pool):
25 log("Please create {} pool before calling create-cache-tier".format(
26 cache_pool))
27 action_fail("create-cache-tier failed. Cache pool {} must exist "
28 "before calling this".format(cache_pool))
29
30 pool = Pool(service='admin', name=backer_pool)
31 try:
32 pool.add_cache_tier(cache_pool=cache_pool, mode=cache_mode)
33 except CalledProcessError as err:
34 log("Add cache tier failed with message: {}".format(
35 err.message))
36 action_fail("create-cache-tier failed. Add cache tier failed with "
37 "message: {}".format(err.message))
38
39
40if __name__ == '__main__':
41 make_cache_tier()
diff --git a/actions/create-cache-tier.py b/actions/create-cache-tier.py
index e8170cf..928e941 100755
--- a/actions/create-cache-tier.py
+++ b/actions/create-cache-tier.py
@@ -1,9 +1,21 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2__author__ = 'chris' 2__author__ = 'chris'
3import os
3from subprocess import CalledProcessError 4from subprocess import CalledProcessError
4import sys 5import sys
5 6
6sys.path.append('hooks') 7_path = os.path.dirname(os.path.realpath(__file__))
8_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
9_root = os.path.abspath(os.path.join(_path, '..'))
10
11
12def _add_path(path):
13 if path not in sys.path:
14 sys.path.insert(1, path)
15
16_add_path(_hooks)
17_add_path(_root)
18
7 19
8from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists 20from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists
9from charmhelpers.core.hookenv import action_get, log, action_fail 21from charmhelpers.core.hookenv import action_get, log, action_fail
diff --git a/actions/create-erasure-profile b/actions/create-erasure-profile
index 2b00b58..7400ccd 100755
--- a/actions/create-erasure-profile
+++ b/actions/create-erasure-profile
@@ -1,8 +1,20 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2from subprocess import CalledProcessError 3from subprocess import CalledProcessError
3import sys 4import sys
4 5
5sys.path.append('hooks') 6_path = os.path.dirname(os.path.realpath(__file__))
7_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
8_root = os.path.abspath(os.path.join(_path, '..'))
9
10
11def _add_path(path):
12 if path not in sys.path:
13 sys.path.insert(1, path)
14
15_add_path(_hooks)
16_add_path(_root)
17
6 18
7from charmhelpers.contrib.storage.linux.ceph import create_erasure_profile 19from charmhelpers.contrib.storage.linux.ceph import create_erasure_profile
8from charmhelpers.core.hookenv import action_get, log, action_fail 20from charmhelpers.core.hookenv import action_get, log, action_fail
@@ -29,7 +41,7 @@ def make_erasure_profile():
29 coding_chunks=m, 41 coding_chunks=m,
30 failure_domain=failure_domain) 42 failure_domain=failure_domain)
31 except CalledProcessError as e: 43 except CalledProcessError as e:
32 log(e) 44 log(str(e))
33 action_fail("Create erasure profile failed with " 45 action_fail("Create erasure profile failed with "
34 "message: {}".format(e.message)) 46 "message: {}".format(e.message))
35 elif plugin == "isa": 47 elif plugin == "isa":
@@ -43,7 +55,7 @@ def make_erasure_profile():
43 coding_chunks=m, 55 coding_chunks=m,
44 failure_domain=failure_domain) 56 failure_domain=failure_domain)
45 except CalledProcessError as e: 57 except CalledProcessError as e:
46 log(e) 58 log(str(e))
47 action_fail("Create erasure profile failed with " 59 action_fail("Create erasure profile failed with "
48 "message: {}".format(e.message)) 60 "message: {}".format(e.message))
49 elif plugin == "local": 61 elif plugin == "local":
@@ -59,7 +71,7 @@ def make_erasure_profile():
59 locality=l, 71 locality=l,
60 failure_domain=failure_domain) 72 failure_domain=failure_domain)
61 except CalledProcessError as e: 73 except CalledProcessError as e:
62 log(e) 74 log(str(e))
63 action_fail("Create erasure profile failed with " 75 action_fail("Create erasure profile failed with "
64 "message: {}".format(e.message)) 76 "message: {}".format(e.message))
65 elif plugin == "shec": 77 elif plugin == "shec":
@@ -75,7 +87,7 @@ def make_erasure_profile():
75 durability_estimator=c, 87 durability_estimator=c,
76 failure_domain=failure_domain) 88 failure_domain=failure_domain)
77 except CalledProcessError as e: 89 except CalledProcessError as e:
78 log(e) 90 log(str(e))
79 action_fail("Create erasure profile failed with " 91 action_fail("Create erasure profile failed with "
80 "message: {}".format(e.message)) 92 "message: {}".format(e.message))
81 else: 93 else:
diff --git a/actions/create-pool b/actions/create-pool
index 4d1d214..0dd0be3 100755
--- a/actions/create-pool
+++ b/actions/create-pool
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import CalledProcessError 17from subprocess import CalledProcessError
6from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
7from charmhelpers.contrib.storage.linux.ceph import ErasurePool, ReplicatedPool 19from charmhelpers.contrib.storage.linux.ceph import ErasurePool, ReplicatedPool
@@ -31,7 +43,7 @@ def create_pool():
31 "is allowed".format(pool_type)) 43 "is allowed".format(pool_type))
32 except CalledProcessError as e: 44 except CalledProcessError as e:
33 action_fail("Pool creation failed because of a failed process. " 45 action_fail("Pool creation failed because of a failed process. "
34 "Ret Code: {} Message: {}".format(e.returncode, e.message)) 46 "Ret Code: {} Message: {}".format(e.returncode, str(e)))
35 47
36 48
37if __name__ == '__main__': 49if __name__ == '__main__':
diff --git a/actions/delete-erasure-profile b/actions/delete-erasure-profile
index 075c410..8651d07 100755
--- a/actions/delete-erasure-profile
+++ b/actions/delete-erasure-profile
@@ -1,10 +1,21 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2from subprocess import CalledProcessError 2from subprocess import CalledProcessError
3 3
4__author__ = 'chris' 4__author__ = 'chris'
5import os
5import sys 6import sys
6 7
7sys.path.append('hooks') 8_path = os.path.dirname(os.path.realpath(__file__))
9_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
10_root = os.path.abspath(os.path.join(_path, '..'))
11
12
13def _add_path(path):
14 if path not in sys.path:
15 sys.path.insert(1, path)
16
17_add_path(_hooks)
18_add_path(_root)
8 19
9from charmhelpers.contrib.storage.linux.ceph import remove_erasure_profile 20from charmhelpers.contrib.storage.linux.ceph import remove_erasure_profile
10from charmhelpers.core.hookenv import action_get, log, action_fail 21from charmhelpers.core.hookenv import action_get, log, action_fail
diff --git a/actions/delete-pool b/actions/delete-pool
index 3d65507..68b89b2 100755
--- a/actions/delete-pool
+++ b/actions/delete-pool
@@ -1,7 +1,18 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
5 16
6import rados 17import rados
7from ceph_ops import connect 18from ceph_ops import connect
@@ -20,8 +31,8 @@ def remove_pool():
20 rados.NoData, 31 rados.NoData,
21 rados.NoSpace, 32 rados.NoSpace,
22 rados.PermissionError) as e: 33 rados.PermissionError) as e:
23 log(e) 34 log(str(e))
24 action_fail(e) 35 action_fail(str(e))
25 36
26 37
27if __name__ == '__main__': 38if __name__ == '__main__':
diff --git a/actions/get-erasure-profile b/actions/get-erasure-profile
index 29ece59..39947bb 100755
--- a/actions/get-erasure-profile
+++ b/actions/get-erasure-profile
@@ -1,8 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2__author__ = 'chris' 2__author__ = 'chris'
3import os
3import sys 4import sys
4 5
5sys.path.append('hooks') 6_path = os.path.dirname(os.path.realpath(__file__))
7_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
8_root = os.path.abspath(os.path.join(_path, '..'))
9
10
11def _add_path(path):
12 if path not in sys.path:
13 sys.path.insert(1, path)
14
15_add_path(_hooks)
16_add_path(_root)
6 17
7from charmhelpers.contrib.storage.linux.ceph import get_erasure_profile 18from charmhelpers.contrib.storage.linux.ceph import get_erasure_profile
8from charmhelpers.core.hookenv import action_get, action_set 19from charmhelpers.core.hookenv import action_get, action_set
diff --git a/actions/list-erasure-profiles b/actions/list-erasure-profiles
index cf6dfa0..fd0586f 100755
--- a/actions/list-erasure-profiles
+++ b/actions/list-erasure-profiles
@@ -1,9 +1,20 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2__author__ = 'chris' 2__author__ = 'chris'
3import sys 3import os
4from subprocess import check_output, CalledProcessError 4from subprocess import check_output, CalledProcessError
5import sys
6
7_path = os.path.dirname(os.path.realpath(__file__))
8_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
9_root = os.path.abspath(os.path.join(_path, '..'))
10
11
12def _add_path(path):
13 if path not in sys.path:
14 sys.path.insert(1, path)
5 15
6sys.path.append('hooks') 16_add_path(_hooks)
17_add_path(_root)
7 18
8from charmhelpers.core.hookenv import action_get, log, action_set, action_fail 19from charmhelpers.core.hookenv import action_get, log, action_set, action_fail
9 20
@@ -17,6 +28,6 @@ if __name__ == '__main__':
17 'ls']).decode('UTF-8') 28 'ls']).decode('UTF-8')
18 action_set({'message': out}) 29 action_set({'message': out})
19 except CalledProcessError as e: 30 except CalledProcessError as e:
20 log(e) 31 log(str(e))
21 action_fail("Listing erasure profiles failed with error: {}".format( 32 action_fail("Listing erasure profiles failed with error: {}".format(
22 e.message)) 33 str(e)))
diff --git a/actions/list-pools b/actions/list-pools
index 102667c..67c1aed 100755
--- a/actions/list-pools
+++ b/actions/list-pools
@@ -1,9 +1,20 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2__author__ = 'chris' 2__author__ = 'chris'
3import sys 3import os
4from subprocess import check_output, CalledProcessError 4from subprocess import check_output, CalledProcessError
5import sys
6
7_path = os.path.dirname(os.path.realpath(__file__))
8_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
9_root = os.path.abspath(os.path.join(_path, '..'))
10
11
12def _add_path(path):
13 if path not in sys.path:
14 sys.path.insert(1, path)
5 15
6sys.path.append('hooks') 16_add_path(_hooks)
17_add_path(_root)
7 18
8from charmhelpers.core.hookenv import log, action_set, action_fail 19from charmhelpers.core.hookenv import log, action_set, action_fail
9 20
@@ -13,5 +24,5 @@ if __name__ == '__main__':
13 'osd', 'lspools']).decode('UTF-8') 24 'osd', 'lspools']).decode('UTF-8')
14 action_set({'message': out}) 25 action_set({'message': out})
15 except CalledProcessError as e: 26 except CalledProcessError as e:
16 log(e) 27 log(str(e))
17 action_fail("List pools failed with error: {}".format(e.message)) 28 action_fail("List pools failed with error: {}".format(str(e)))
diff --git a/actions/pool-get b/actions/pool-get
index e4f924b..3a42ab4 100755
--- a/actions/pool-get
+++ b/actions/pool-get
@@ -1,9 +1,20 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2__author__ = 'chris' 2__author__ = 'chris'
3import sys 3import os
4from subprocess import check_output, CalledProcessError 4from subprocess import check_output, CalledProcessError
5import sys
6
7_path = os.path.dirname(os.path.realpath(__file__))
8_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
9_root = os.path.abspath(os.path.join(_path, '..'))
10
11
12def _add_path(path):
13 if path not in sys.path:
14 sys.path.insert(1, path)
5 15
6sys.path.append('hooks') 16_add_path(_hooks)
17_add_path(_root)
7 18
8from charmhelpers.core.hookenv import log, action_set, action_get, action_fail 19from charmhelpers.core.hookenv import log, action_set, action_get, action_fail
9 20
@@ -15,5 +26,5 @@ if __name__ == '__main__':
15 'osd', 'pool', 'get', name, key]).decode('UTF-8') 26 'osd', 'pool', 'get', name, key]).decode('UTF-8')
16 action_set({'message': out}) 27 action_set({'message': out})
17 except CalledProcessError as e: 28 except CalledProcessError as e:
18 log(e) 29 log(str(e))
19 action_fail("Pool get failed with message: {}".format(e.message)) 30 action_fail("Pool get failed with message: {}".format(str(e)))
diff --git a/actions/pool-set b/actions/pool-set
index 1f6e13b..8963c90 100755
--- a/actions/pool-set
+++ b/actions/pool-set
@@ -1,8 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2from subprocess import CalledProcessError 3from subprocess import CalledProcessError
3import sys 4import sys
4 5
5sys.path.append('hooks') 6_path = os.path.dirname(os.path.realpath(__file__))
7_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
8_root = os.path.abspath(os.path.join(_path, '..'))
9
10
11def _add_path(path):
12 if path not in sys.path:
13 sys.path.insert(1, path)
14
15_add_path(_hooks)
16_add_path(_root)
6 17
7from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
8from ceph_broker import handle_set_pool_value 19from ceph_broker import handle_set_pool_value
@@ -18,6 +29,6 @@ if __name__ == '__main__':
18 try: 29 try:
19 handle_set_pool_value(service='admin', request=request) 30 handle_set_pool_value(service='admin', request=request)
20 except CalledProcessError as e: 31 except CalledProcessError as e:
21 log(e.message) 32 log(str(e))
22 action_fail("Setting pool key: {} and value: {} failed with " 33 action_fail("Setting pool key: {} and value: {} failed with "
23 "message: {}".format(key, value, e.message)) 34 "message: {}".format(key, value, str(e)))
diff --git a/actions/pool-statistics b/actions/pool-statistics
index 536c889..403267f 100755
--- a/actions/pool-statistics
+++ b/actions/pool-statistics
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import check_output, CalledProcessError 17from subprocess import check_output, CalledProcessError
6from charmhelpers.core.hookenv import log, action_set, action_fail 18from charmhelpers.core.hookenv import log, action_set, action_fail
7 19
@@ -11,5 +23,5 @@ if __name__ == '__main__':
11 'df']).decode('UTF-8') 23 'df']).decode('UTF-8')
12 action_set({'message': out}) 24 action_set({'message': out})
13 except CalledProcessError as e: 25 except CalledProcessError as e:
14 log(e) 26 log(str(e))
15 action_fail("ceph df failed with message: {}".format(e.message)) 27 action_fail("ceph df failed with message: {}".format(str(e)))
diff --git a/actions/remove-cache-tier b/actions/remove-cache-tier
index 136c0f0..79db9cf 120000..100755
--- a/actions/remove-cache-tier
+++ b/actions/remove-cache-tier
@@ -1 +1,41 @@
1remove-cache-tier.py \ No newline at end of file 1#!/usr/bin/python
2from subprocess import CalledProcessError
3import sys
4
5sys.path.append('hooks')
6
7from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists
8from charmhelpers.core.hookenv import action_get, log, action_fail
9
10__author__ = 'chris'
11
12
13def delete_cache_tier():
14 backer_pool = action_get("backer-pool")
15 cache_pool = action_get("cache-pool")
16
17 # Pre flight checks
18 if not pool_exists('admin', backer_pool):
19 log("Backer pool {} must exist before calling this".format(
20 backer_pool))
21 action_fail("remove-cache-tier failed. Backer pool {} must exist "
22 "before calling this".format(backer_pool))
23
24 if not pool_exists('admin', cache_pool):
25 log("Cache pool {} must exist before calling this".format(
26 cache_pool))
27 action_fail("remove-cache-tier failed. Cache pool {} must exist "
28 "before calling this".format(cache_pool))
29
30 pool = Pool(service='admin', name=backer_pool)
31 try:
32 pool.remove_cache_tier(cache_pool=cache_pool)
33 except CalledProcessError as err:
34 log("Removing the cache tier failed with message: {}".format(
35 err.message))
36 action_fail("remove-cache-tier failed. Removing the cache tier failed "
37 "with message: {}".format(err.message))
38
39
40if __name__ == '__main__':
41 delete_cache_tier()
diff --git a/actions/remove-cache-tier.py b/actions/remove-cache-tier.py
index 79db9cf..8c9b937 100755
--- a/actions/remove-cache-tier.py
+++ b/actions/remove-cache-tier.py
@@ -1,8 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2from subprocess import CalledProcessError 3from subprocess import CalledProcessError
3import sys 4import sys
4 5
5sys.path.append('hooks') 6_path = os.path.dirname(os.path.realpath(__file__))
7_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
8_root = os.path.abspath(os.path.join(_path, '..'))
9
10
11def _add_path(path):
12 if path not in sys.path:
13 sys.path.insert(1, path)
14
15_add_path(_hooks)
16_add_path(_root)
6 17
7from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists 18from charmhelpers.contrib.storage.linux.ceph import Pool, pool_exists
8from charmhelpers.core.hookenv import action_get, log, action_fail 19from charmhelpers.core.hookenv import action_get, log, action_fail
@@ -32,9 +43,9 @@ def delete_cache_tier():
32 pool.remove_cache_tier(cache_pool=cache_pool) 43 pool.remove_cache_tier(cache_pool=cache_pool)
33 except CalledProcessError as err: 44 except CalledProcessError as err:
34 log("Removing the cache tier failed with message: {}".format( 45 log("Removing the cache tier failed with message: {}".format(
35 err.message)) 46 str(err)))
36 action_fail("remove-cache-tier failed. Removing the cache tier failed " 47 action_fail("remove-cache-tier failed. Removing the cache tier failed "
37 "with message: {}".format(err.message)) 48 "with message: {}".format(str(err)))
38 49
39 50
40if __name__ == '__main__': 51if __name__ == '__main__':
diff --git a/actions/remove-pool-snapshot b/actions/remove-pool-snapshot
index 387849e..645ff07 100755
--- a/actions/remove-pool-snapshot
+++ b/actions/remove-pool-snapshot
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import CalledProcessError 17from subprocess import CalledProcessError
6from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
7from charmhelpers.contrib.storage.linux.ceph import remove_pool_snapshot 19from charmhelpers.contrib.storage.linux.ceph import remove_pool_snapshot
@@ -14,6 +26,6 @@ if __name__ == '__main__':
14 pool_name=name, 26 pool_name=name,
15 snapshot_name=snapname) 27 snapshot_name=snapname)
16 except CalledProcessError as e: 28 except CalledProcessError as e:
17 log(e) 29 log(str(e))
18 action_fail("Remove pool snapshot failed with message: {}".format( 30 action_fail("Remove pool snapshot failed with message: {}".format(
19 e.message)) 31 str(e)))
diff --git a/actions/rename-pool b/actions/rename-pool
index 6fe088e..3301830 100755
--- a/actions/rename-pool
+++ b/actions/rename-pool
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import CalledProcessError 17from subprocess import CalledProcessError
6from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
7from charmhelpers.contrib.storage.linux.ceph import rename_pool 19from charmhelpers.contrib.storage.linux.ceph import rename_pool
@@ -12,5 +24,5 @@ if __name__ == '__main__':
12 try: 24 try:
13 rename_pool(service='admin', old_name=name, new_name=new_name) 25 rename_pool(service='admin', old_name=name, new_name=new_name)
14 except CalledProcessError as e: 26 except CalledProcessError as e:
15 log(e) 27 log(str(e))
16 action_fail("Renaming pool failed with message: {}".format(e.message)) 28 action_fail("Renaming pool failed with message: {}".format(str(e)))
diff --git a/actions/set-pool-max-bytes b/actions/set-pool-max-bytes
index 8636088..c1550d4 100755
--- a/actions/set-pool-max-bytes
+++ b/actions/set-pool-max-bytes
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import CalledProcessError 17from subprocess import CalledProcessError
6from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
7from charmhelpers.contrib.storage.linux.ceph import set_pool_quota 19from charmhelpers.contrib.storage.linux.ceph import set_pool_quota
@@ -12,5 +24,5 @@ if __name__ == '__main__':
12 try: 24 try:
13 set_pool_quota(service='admin', pool_name=name, max_bytes=max_bytes) 25 set_pool_quota(service='admin', pool_name=name, max_bytes=max_bytes)
14 except CalledProcessError as e: 26 except CalledProcessError as e:
15 log(e) 27 log(str(e))
16 action_fail("Set pool quota failed with message: {}".format(e.message)) 28 action_fail("Set pool quota failed with message: {}".format(str(e)))
diff --git a/actions/snapshot-pool b/actions/snapshot-pool
index a02619b..0191bcc 100755
--- a/actions/snapshot-pool
+++ b/actions/snapshot-pool
@@ -1,7 +1,19 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2import os
2import sys 3import sys
3 4
4sys.path.append('hooks') 5_path = os.path.dirname(os.path.realpath(__file__))
6_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
7_root = os.path.abspath(os.path.join(_path, '..'))
8
9
10def _add_path(path):
11 if path not in sys.path:
12 sys.path.insert(1, path)
13
14_add_path(_hooks)
15_add_path(_root)
16
5from subprocess import CalledProcessError 17from subprocess import CalledProcessError
6from charmhelpers.core.hookenv import action_get, log, action_fail 18from charmhelpers.core.hookenv import action_get, log, action_fail
7from charmhelpers.contrib.storage.linux.ceph import snapshot_pool 19from charmhelpers.contrib.storage.linux.ceph import snapshot_pool
@@ -14,5 +26,5 @@ if __name__ == '__main__':
14 pool_name=name, 26 pool_name=name,
15 snapshot_name=snapname) 27 snapshot_name=snapname)
16 except CalledProcessError as e: 28 except CalledProcessError as e:
17 log(e) 29 log(str(e))
18 action_fail("Snapshot pool failed with message: {}".format(e.message)) 30 action_fail("Snapshot pool failed with message: {}".format(str(e)))
diff --git a/charm-helpers-hooks.yaml b/charm-helpers-hooks.yaml
index e4767c5..8f484eb 100644
--- a/charm-helpers-hooks.yaml
+++ b/charm-helpers-hooks.yaml
@@ -1,5 +1,5 @@
1repo: https://github.com/juju/charm-helpers 1repo: https://github.com/juju/charm-helpers
2destination: hooks/charmhelpers 2destination: charmhelpers
3include: 3include:
4 - core 4 - core
5 - cli 5 - cli
diff --git a/hooks/charmhelpers/__init__.py b/charmhelpers/__init__.py
index 61ef907..61ef907 100644
--- a/hooks/charmhelpers/__init__.py
+++ b/charmhelpers/__init__.py
diff --git a/hooks/charmhelpers/cli/__init__.py b/charmhelpers/cli/__init__.py
index 389b490..389b490 100644
--- a/hooks/charmhelpers/cli/__init__.py
+++ b/charmhelpers/cli/__init__.py
diff --git a/hooks/charmhelpers/cli/benchmark.py b/charmhelpers/cli/benchmark.py
index 303af14..303af14 100644
--- a/hooks/charmhelpers/cli/benchmark.py
+++ b/charmhelpers/cli/benchmark.py
diff --git a/hooks/charmhelpers/cli/commands.py b/charmhelpers/cli/commands.py
index b931056..b931056 100644
--- a/hooks/charmhelpers/cli/commands.py
+++ b/charmhelpers/cli/commands.py
diff --git a/hooks/charmhelpers/cli/hookenv.py b/charmhelpers/cli/hookenv.py
index bd72f44..bd72f44 100644
--- a/hooks/charmhelpers/cli/hookenv.py
+++ b/charmhelpers/cli/hookenv.py
diff --git a/hooks/charmhelpers/cli/host.py b/charmhelpers/cli/host.py
index 4039684..4039684 100644
--- a/hooks/charmhelpers/cli/host.py
+++ b/charmhelpers/cli/host.py
diff --git a/hooks/charmhelpers/cli/unitdata.py b/charmhelpers/cli/unitdata.py
index c572858..acce846 100644
--- a/hooks/charmhelpers/cli/unitdata.py
+++ b/charmhelpers/cli/unitdata.py
@@ -19,9 +19,16 @@ from charmhelpers.core import unitdata
19@cmdline.subcommand_builder('unitdata', description="Store and retrieve data") 19@cmdline.subcommand_builder('unitdata', description="Store and retrieve data")
20def unitdata_cmd(subparser): 20def unitdata_cmd(subparser):
21 nested = subparser.add_subparsers() 21 nested = subparser.add_subparsers()
22
22 get_cmd = nested.add_parser('get', help='Retrieve data') 23 get_cmd = nested.add_parser('get', help='Retrieve data')
23 get_cmd.add_argument('key', help='Key to retrieve the value of') 24 get_cmd.add_argument('key', help='Key to retrieve the value of')
24 get_cmd.set_defaults(action='get', value=None) 25 get_cmd.set_defaults(action='get', value=None)
26
27 getrange_cmd = nested.add_parser('getrange', help='Retrieve multiple data')
28 getrange_cmd.add_argument('key', metavar='prefix',
29 help='Prefix of the keys to retrieve')
30 getrange_cmd.set_defaults(action='getrange', value=None)
31
25 set_cmd = nested.add_parser('set', help='Store data') 32 set_cmd = nested.add_parser('set', help='Store data')
26 set_cmd.add_argument('key', help='Key to set') 33 set_cmd.add_argument('key', help='Key to set')
27 set_cmd.add_argument('value', help='Value to store') 34 set_cmd.add_argument('value', help='Value to store')
@@ -30,6 +37,8 @@ def unitdata_cmd(subparser):
30 def _unitdata_cmd(action, key, value): 37 def _unitdata_cmd(action, key, value):
31 if action == 'get': 38 if action == 'get':
32 return unitdata.kv().get(key) 39 return unitdata.kv().get(key)
40 elif action == 'getrange':
41 return unitdata.kv().getrange(key)
33 elif action == 'set': 42 elif action == 'set':
34 unitdata.kv().set(key, value) 43 unitdata.kv().set(key, value)
35 unitdata.kv().flush() 44 unitdata.kv().flush()
diff --git a/hooks/charmhelpers/contrib/__init__.py b/charmhelpers/contrib/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/__init__.py
+++ b/charmhelpers/contrib/__init__.py
diff --git a/hooks/charmhelpers/contrib/charmsupport/__init__.py b/charmhelpers/contrib/charmsupport/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/charmsupport/__init__.py
+++ b/charmhelpers/contrib/charmsupport/__init__.py
diff --git a/hooks/charmhelpers/contrib/charmsupport/nrpe.py b/charmhelpers/contrib/charmsupport/nrpe.py
index 0626b32..0626b32 100644
--- a/hooks/charmhelpers/contrib/charmsupport/nrpe.py
+++ b/charmhelpers/contrib/charmsupport/nrpe.py
diff --git a/hooks/charmhelpers/contrib/charmsupport/volumes.py b/charmhelpers/contrib/charmsupport/volumes.py
index 7ea43f0..7ea43f0 100644
--- a/hooks/charmhelpers/contrib/charmsupport/volumes.py
+++ b/charmhelpers/contrib/charmsupport/volumes.py
diff --git a/hooks/charmhelpers/contrib/hardening/README.hardening.md b/charmhelpers/contrib/hardening/README.hardening.md
index 91280c0..91280c0 100644
--- a/hooks/charmhelpers/contrib/hardening/README.hardening.md
+++ b/charmhelpers/contrib/hardening/README.hardening.md
diff --git a/hooks/charmhelpers/contrib/hardening/__init__.py b/charmhelpers/contrib/hardening/__init__.py
index 30a3e94..30a3e94 100644
--- a/hooks/charmhelpers/contrib/hardening/__init__.py
+++ b/charmhelpers/contrib/hardening/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/apache/__init__.py b/charmhelpers/contrib/hardening/apache/__init__.py
index 58bebd8..58bebd8 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/__init__.py
+++ b/charmhelpers/contrib/hardening/apache/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/apache/checks/__init__.py b/charmhelpers/contrib/hardening/apache/checks/__init__.py
index 3bc2ebd..3bc2ebd 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/checks/__init__.py
+++ b/charmhelpers/contrib/hardening/apache/checks/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/apache/checks/config.py b/charmhelpers/contrib/hardening/apache/checks/config.py
index 341da9e..341da9e 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/checks/config.py
+++ b/charmhelpers/contrib/hardening/apache/checks/config.py
diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf b/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf
index 22b6804..22b6804 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf
+++ b/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf
diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py b/charmhelpers/contrib/hardening/apache/templates/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py
+++ b/charmhelpers/contrib/hardening/apache/templates/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/apache/templates/alias.conf b/charmhelpers/contrib/hardening/apache/templates/alias.conf
index e46a58a..e46a58a 100644
--- a/hooks/charmhelpers/contrib/hardening/apache/templates/alias.conf
+++ b/charmhelpers/contrib/hardening/apache/templates/alias.conf
diff --git a/hooks/charmhelpers/contrib/hardening/audits/__init__.py b/charmhelpers/contrib/hardening/audits/__init__.py
index 6dd5b05..6dd5b05 100644
--- a/hooks/charmhelpers/contrib/hardening/audits/__init__.py
+++ b/charmhelpers/contrib/hardening/audits/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/audits/apache.py b/charmhelpers/contrib/hardening/audits/apache.py
index 04825f5..04825f5 100644
--- a/hooks/charmhelpers/contrib/hardening/audits/apache.py
+++ b/charmhelpers/contrib/hardening/audits/apache.py
diff --git a/hooks/charmhelpers/contrib/hardening/audits/apt.py b/charmhelpers/contrib/hardening/audits/apt.py
index 3dc14e3..3dc14e3 100644
--- a/hooks/charmhelpers/contrib/hardening/audits/apt.py
+++ b/charmhelpers/contrib/hardening/audits/apt.py
diff --git a/hooks/charmhelpers/contrib/hardening/audits/file.py b/charmhelpers/contrib/hardening/audits/file.py
index 257c635..257c635 100644
--- a/hooks/charmhelpers/contrib/hardening/audits/file.py
+++ b/charmhelpers/contrib/hardening/audits/file.py
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/__init__.py b/charmhelpers/contrib/hardening/defaults/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/__init__.py
+++ b/charmhelpers/contrib/hardening/defaults/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml b/charmhelpers/contrib/hardening/defaults/apache.yaml
index 0f940d4..0f940d4 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml
+++ b/charmhelpers/contrib/hardening/defaults/apache.yaml
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml.schema b/charmhelpers/contrib/hardening/defaults/apache.yaml.schema
index c112137..c112137 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/apache.yaml.schema
+++ b/charmhelpers/contrib/hardening/defaults/apache.yaml.schema
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml b/charmhelpers/contrib/hardening/defaults/mysql.yaml
index 682d22b..682d22b 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml
+++ b/charmhelpers/contrib/hardening/defaults/mysql.yaml
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema b/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema
index 2edf325..2edf325 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema
+++ b/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml b/charmhelpers/contrib/hardening/defaults/os.yaml
index 9a8627b..9a8627b 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml
+++ b/charmhelpers/contrib/hardening/defaults/os.yaml
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml.schema b/charmhelpers/contrib/hardening/defaults/os.yaml.schema
index cc3b9c2..cc3b9c2 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/os.yaml.schema
+++ b/charmhelpers/contrib/hardening/defaults/os.yaml.schema
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml b/charmhelpers/contrib/hardening/defaults/ssh.yaml
index cd529bc..cd529bc 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml
+++ b/charmhelpers/contrib/hardening/defaults/ssh.yaml
diff --git a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema b/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema
index d05e054..d05e054 100644
--- a/hooks/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema
+++ b/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema
diff --git a/hooks/charmhelpers/contrib/hardening/harden.py b/charmhelpers/contrib/hardening/harden.py
index 63f21b9..63f21b9 100644
--- a/hooks/charmhelpers/contrib/hardening/harden.py
+++ b/charmhelpers/contrib/hardening/harden.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/__init__.py b/charmhelpers/contrib/hardening/host/__init__.py
index 58bebd8..58bebd8 100644
--- a/hooks/charmhelpers/contrib/hardening/host/__init__.py
+++ b/charmhelpers/contrib/hardening/host/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/__init__.py b/charmhelpers/contrib/hardening/host/checks/__init__.py
index 0e7e409..0e7e409 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/__init__.py
+++ b/charmhelpers/contrib/hardening/host/checks/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/apt.py b/charmhelpers/contrib/hardening/host/checks/apt.py
index 7ce41b0..7ce41b0 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/apt.py
+++ b/charmhelpers/contrib/hardening/host/checks/apt.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/limits.py b/charmhelpers/contrib/hardening/host/checks/limits.py
index e94f5eb..e94f5eb 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/limits.py
+++ b/charmhelpers/contrib/hardening/host/checks/limits.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/login.py b/charmhelpers/contrib/hardening/host/checks/login.py
index fe2bc6e..fe2bc6e 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/login.py
+++ b/charmhelpers/contrib/hardening/host/checks/login.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/minimize_access.py b/charmhelpers/contrib/hardening/host/checks/minimize_access.py
index 6e64be0..6e64be0 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/minimize_access.py
+++ b/charmhelpers/contrib/hardening/host/checks/minimize_access.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/pam.py b/charmhelpers/contrib/hardening/host/checks/pam.py
index 9b38d5f..9b38d5f 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/pam.py
+++ b/charmhelpers/contrib/hardening/host/checks/pam.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/profile.py b/charmhelpers/contrib/hardening/host/checks/profile.py
index 2727428..2727428 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/profile.py
+++ b/charmhelpers/contrib/hardening/host/checks/profile.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/securetty.py b/charmhelpers/contrib/hardening/host/checks/securetty.py
index 34cd021..34cd021 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/securetty.py
+++ b/charmhelpers/contrib/hardening/host/checks/securetty.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/suid_sgid.py b/charmhelpers/contrib/hardening/host/checks/suid_sgid.py
index bcbe3fd..bcbe3fd 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/suid_sgid.py
+++ b/charmhelpers/contrib/hardening/host/checks/suid_sgid.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py b/charmhelpers/contrib/hardening/host/checks/sysctl.py
index f1ea581..f1ea581 100644
--- a/hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py
+++ b/charmhelpers/contrib/hardening/host/checks/sysctl.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf b/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf
index 0014191..0014191 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf
+++ b/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh b/charmhelpers/contrib/hardening/host/templates/99-hardening.sh
index 616cef4..616cef4 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh
+++ b/charmhelpers/contrib/hardening/host/templates/99-hardening.sh
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf b/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf
index 101f1e1..101f1e1 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf
+++ b/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/__init__.py b/charmhelpers/contrib/hardening/host/templates/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/__init__.py
+++ b/charmhelpers/contrib/hardening/host/templates/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/login.defs b/charmhelpers/contrib/hardening/host/templates/login.defs
index db137d6..db137d6 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/login.defs
+++ b/charmhelpers/contrib/hardening/host/templates/login.defs
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/modules b/charmhelpers/contrib/hardening/host/templates/modules
index ef0354e..ef0354e 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/modules
+++ b/charmhelpers/contrib/hardening/host/templates/modules
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/passwdqc.conf b/charmhelpers/contrib/hardening/host/templates/passwdqc.conf
index f98d14e..f98d14e 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/passwdqc.conf
+++ b/charmhelpers/contrib/hardening/host/templates/passwdqc.conf
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh b/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh
index fd2de79..fd2de79 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh
+++ b/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/securetty b/charmhelpers/contrib/hardening/host/templates/securetty
index 15b18d4..15b18d4 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/securetty
+++ b/charmhelpers/contrib/hardening/host/templates/securetty
diff --git a/hooks/charmhelpers/contrib/hardening/host/templates/tally2 b/charmhelpers/contrib/hardening/host/templates/tally2
index d962029..d962029 100644
--- a/hooks/charmhelpers/contrib/hardening/host/templates/tally2
+++ b/charmhelpers/contrib/hardening/host/templates/tally2
diff --git a/hooks/charmhelpers/contrib/hardening/mysql/__init__.py b/charmhelpers/contrib/hardening/mysql/__init__.py
index 58bebd8..58bebd8 100644
--- a/hooks/charmhelpers/contrib/hardening/mysql/__init__.py
+++ b/charmhelpers/contrib/hardening/mysql/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/mysql/checks/__init__.py b/charmhelpers/contrib/hardening/mysql/checks/__init__.py
index 1990d85..1990d85 100644
--- a/hooks/charmhelpers/contrib/hardening/mysql/checks/__init__.py
+++ b/charmhelpers/contrib/hardening/mysql/checks/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/mysql/checks/config.py b/charmhelpers/contrib/hardening/mysql/checks/config.py
index a79f33b..a79f33b 100644
--- a/hooks/charmhelpers/contrib/hardening/mysql/checks/config.py
+++ b/charmhelpers/contrib/hardening/mysql/checks/config.py
diff --git a/hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py b/charmhelpers/contrib/hardening/mysql/templates/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py
+++ b/charmhelpers/contrib/hardening/mysql/templates/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf b/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf
index 8242586..8242586 100644
--- a/hooks/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf
+++ b/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/__init__.py b/charmhelpers/contrib/hardening/ssh/__init__.py
index 58bebd8..58bebd8 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/__init__.py
+++ b/charmhelpers/contrib/hardening/ssh/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/checks/__init__.py b/charmhelpers/contrib/hardening/ssh/checks/__init__.py
index edaf484..edaf484 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/checks/__init__.py
+++ b/charmhelpers/contrib/hardening/ssh/checks/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/checks/config.py b/charmhelpers/contrib/hardening/ssh/checks/config.py
index 41bed2d..41bed2d 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/checks/config.py
+++ b/charmhelpers/contrib/hardening/ssh/checks/config.py
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py b/charmhelpers/contrib/hardening/ssh/templates/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py
+++ b/charmhelpers/contrib/hardening/ssh/templates/__init__.py
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/ssh_config b/charmhelpers/contrib/hardening/ssh/templates/ssh_config
index 9742d8e..9742d8e 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/templates/ssh_config
+++ b/charmhelpers/contrib/hardening/ssh/templates/ssh_config
diff --git a/hooks/charmhelpers/contrib/hardening/ssh/templates/sshd_config b/charmhelpers/contrib/hardening/ssh/templates/sshd_config
index 5f87298..5f87298 100644
--- a/hooks/charmhelpers/contrib/hardening/ssh/templates/sshd_config
+++ b/charmhelpers/contrib/hardening/ssh/templates/sshd_config
diff --git a/hooks/charmhelpers/contrib/hardening/templating.py b/charmhelpers/contrib/hardening/templating.py
index 5b6765f..5b6765f 100644
--- a/hooks/charmhelpers/contrib/hardening/templating.py
+++ b/charmhelpers/contrib/hardening/templating.py
diff --git a/hooks/charmhelpers/contrib/hardening/utils.py b/charmhelpers/contrib/hardening/utils.py
index ff7485c..ff7485c 100644
--- a/hooks/charmhelpers/contrib/hardening/utils.py
+++ b/charmhelpers/contrib/hardening/utils.py
diff --git a/hooks/charmhelpers/contrib/network/__init__.py b/charmhelpers/contrib/network/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/network/__init__.py
+++ b/charmhelpers/contrib/network/__init__.py
diff --git a/hooks/charmhelpers/contrib/network/ip.py b/charmhelpers/contrib/network/ip.py
index b13277b..b13277b 100644
--- a/hooks/charmhelpers/contrib/network/ip.py
+++ b/charmhelpers/contrib/network/ip.py
diff --git a/hooks/charmhelpers/contrib/openstack/__init__.py b/charmhelpers/contrib/openstack/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/openstack/__init__.py
+++ b/charmhelpers/contrib/openstack/__init__.py
diff --git a/hooks/charmhelpers/contrib/openstack/alternatives.py b/charmhelpers/contrib/openstack/alternatives.py
index 547de09..547de09 100644
--- a/hooks/charmhelpers/contrib/openstack/alternatives.py
+++ b/charmhelpers/contrib/openstack/alternatives.py
diff --git a/hooks/charmhelpers/contrib/openstack/exceptions.py b/charmhelpers/contrib/openstack/exceptions.py
index f85ae4f..f85ae4f 100644
--- a/hooks/charmhelpers/contrib/openstack/exceptions.py
+++ b/charmhelpers/contrib/openstack/exceptions.py
diff --git a/hooks/charmhelpers/contrib/openstack/utils.py b/charmhelpers/contrib/openstack/utils.py
index 86b011b..e5e2536 100644
--- a/hooks/charmhelpers/contrib/openstack/utils.py
+++ b/charmhelpers/contrib/openstack/utils.py
@@ -194,7 +194,7 @@ SWIFT_CODENAMES = OrderedDict([
194 ('rocky', 194 ('rocky',
195 ['2.18.0', '2.19.0']), 195 ['2.18.0', '2.19.0']),
196 ('stein', 196 ('stein',
197 ['2.19.0']), 197 ['2.20.0']),
198]) 198])
199 199
200# >= Liberty version->codename mapping 200# >= Liberty version->codename mapping
@@ -656,7 +656,7 @@ def openstack_upgrade_available(package):
656 else: 656 else:
657 avail_vers = get_os_version_install_source(src) 657 avail_vers = get_os_version_install_source(src)
658 apt.init() 658 apt.init()
659 return apt.version_compare(avail_vers, cur_vers) == 1 659 return apt.version_compare(avail_vers, cur_vers) >= 1
660 660
661 661
662def ensure_block_device(block_device): 662def ensure_block_device(block_device):
diff --git a/hooks/charmhelpers/contrib/python.py b/charmhelpers/contrib/python.py
index 84cba8c..84cba8c 100644
--- a/hooks/charmhelpers/contrib/python.py
+++ b/charmhelpers/contrib/python.py
diff --git a/hooks/charmhelpers/contrib/storage/__init__.py b/charmhelpers/contrib/storage/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/storage/__init__.py
+++ b/charmhelpers/contrib/storage/__init__.py
diff --git a/hooks/charmhelpers/contrib/storage/linux/__init__.py b/charmhelpers/contrib/storage/linux/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/contrib/storage/linux/__init__.py
+++ b/charmhelpers/contrib/storage/linux/__init__.py
diff --git a/hooks/charmhelpers/contrib/storage/linux/ceph.py b/charmhelpers/contrib/storage/linux/ceph.py
index 63c9304..2c62092 100644
--- a/hooks/charmhelpers/contrib/storage/linux/ceph.py
+++ b/charmhelpers/contrib/storage/linux/ceph.py
@@ -59,6 +59,7 @@ from charmhelpers.core.host import (
59 service_stop, 59 service_stop,
60 service_running, 60 service_running,
61 umount, 61 umount,
62 cmp_pkgrevno,
62) 63)
63from charmhelpers.fetch import ( 64from charmhelpers.fetch import (
64 apt_install, 65 apt_install,
@@ -178,7 +179,6 @@ class Pool(object):
178 """ 179 """
179 # read-only is easy, writeback is much harder 180 # read-only is easy, writeback is much harder
180 mode = get_cache_mode(self.service, cache_pool) 181 mode = get_cache_mode(self.service, cache_pool)
181 version = ceph_version()
182 if mode == 'readonly': 182 if mode == 'readonly':
183 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'cache-mode', cache_pool, 'none']) 183 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'cache-mode', cache_pool, 'none'])
184 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove', self.name, cache_pool]) 184 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove', self.name, cache_pool])
@@ -186,7 +186,7 @@ class Pool(object):
186 elif mode == 'writeback': 186 elif mode == 'writeback':
187 pool_forward_cmd = ['ceph', '--id', self.service, 'osd', 'tier', 187 pool_forward_cmd = ['ceph', '--id', self.service, 'osd', 'tier',
188 'cache-mode', cache_pool, 'forward'] 188 'cache-mode', cache_pool, 'forward']
189 if version >= '10.1': 189 if cmp_pkgrevno('ceph-common', '10.1') >= 0:
190 # Jewel added a mandatory flag 190 # Jewel added a mandatory flag
191 pool_forward_cmd.append('--yes-i-really-mean-it') 191 pool_forward_cmd.append('--yes-i-really-mean-it')
192 192
@@ -196,7 +196,8 @@ class Pool(object):
196 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove-overlay', self.name]) 196 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove-overlay', self.name])
197 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove', self.name, cache_pool]) 197 check_call(['ceph', '--id', self.service, 'osd', 'tier', 'remove', self.name, cache_pool])
198 198
199 def get_pgs(self, pool_size, percent_data=DEFAULT_POOL_WEIGHT): 199 def get_pgs(self, pool_size, percent_data=DEFAULT_POOL_WEIGHT,
200 device_class=None):
200 """Return the number of placement groups to use when creating the pool. 201 """Return the number of placement groups to use when creating the pool.
201 202
202 Returns the number of placement groups which should be specified when 203 Returns the number of placement groups which should be specified when
@@ -229,6 +230,9 @@ class Pool(object):
229 increased. NOTE: the default is primarily to handle the scenario 230 increased. NOTE: the default is primarily to handle the scenario
230 where related charms requiring pools has not been upgraded to 231 where related charms requiring pools has not been upgraded to
231 include an update to indicate their relative usage of the pools. 232 include an update to indicate their relative usage of the pools.
233 :param device_class: str. class of storage to use for basis of pgs
234 calculation; ceph supports nvme, ssd and hdd by default based
235 on presence of devices of each type in the deployment.
232 :return: int. The number of pgs to use. 236 :return: int. The number of pgs to use.
233 """ 237 """
234 238
@@ -243,17 +247,20 @@ class Pool(object):
243 247
244 # If the expected-osd-count is specified, then use the max between 248 # If the expected-osd-count is specified, then use the max between
245 # the expected-osd-count and the actual osd_count 249 # the expected-osd-count and the actual osd_count
246 osd_list = get_osds(self.service) 250 osd_list = get_osds(self.service, device_class)
247 expected = config('expected-osd-count') or 0 251 expected = config('expected-osd-count') or 0
248 252
249 if osd_list: 253 if osd_list:
250 osd_count = max(expected, len(osd_list)) 254 if device_class:
255 osd_count = len(osd_list)
256 else:
257 osd_count = max(expected, len(osd_list))
251 258
252 # Log a message to provide some insight if the calculations claim 259 # Log a message to provide some insight if the calculations claim
253 # to be off because someone is setting the expected count and 260 # to be off because someone is setting the expected count and
254 # there are more OSDs in reality. Try to make a proper guess 261 # there are more OSDs in reality. Try to make a proper guess
255 # based upon the cluster itself. 262 # based upon the cluster itself.
256 if expected and osd_count != expected: 263 if not device_class and expected and osd_count != expected:
257 log("Found more OSDs than provided expected count. " 264 log("Found more OSDs than provided expected count. "
258 "Using the actual count instead", INFO) 265 "Using the actual count instead", INFO)
259 elif expected: 266 elif expected:
@@ -575,21 +582,24 @@ def remove_pool_snapshot(service, pool_name, snapshot_name):
575 raise 582 raise
576 583
577 584
578# max_bytes should be an int or long 585def set_pool_quota(service, pool_name, max_bytes=None, max_objects=None):
579def set_pool_quota(service, pool_name, max_bytes):
580 """ 586 """
581 :param service: six.string_types. The Ceph user name to run the command under 587 :param service: The Ceph user name to run the command under
582 :param pool_name: six.string_types 588 :type service: str
583 :param max_bytes: int or long 589 :param pool_name: Name of pool
584 :return: None. Can raise CalledProcessError 590 :type pool_name: str
591 :param max_bytes: Maximum bytes quota to apply
592 :type max_bytes: int
593 :param max_objects: Maximum objects quota to apply
594 :type max_objects: int
595 :raises: subprocess.CalledProcessError
585 """ 596 """
586 # Set a byte quota on a RADOS pool in ceph. 597 cmd = ['ceph', '--id', service, 'osd', 'pool', 'set-quota', pool_name]
587 cmd = ['ceph', '--id', service, 'osd', 'pool', 'set-quota', pool_name, 598 if max_bytes:
588 'max_bytes', str(max_bytes)] 599 cmd = cmd + ['max_bytes', str(max_bytes)]
589 try: 600 if max_objects:
590 check_call(cmd) 601 cmd = cmd + ['max_objects', str(max_objects)]
591 except CalledProcessError: 602 check_call(cmd)
592 raise
593 603
594 604
595def remove_pool_quota(service, pool_name): 605def remove_pool_quota(service, pool_name):
@@ -626,7 +636,8 @@ def remove_erasure_profile(service, profile_name):
626def create_erasure_profile(service, profile_name, erasure_plugin_name='jerasure', 636def create_erasure_profile(service, profile_name, erasure_plugin_name='jerasure',
627 failure_domain='host', 637 failure_domain='host',
628 data_chunks=2, coding_chunks=1, 638 data_chunks=2, coding_chunks=1,
629 locality=None, durability_estimator=None): 639 locality=None, durability_estimator=None,
640 device_class=None):
630 """ 641 """
631 Create a new erasure code profile if one does not already exist for it. Updates 642 Create a new erasure code profile if one does not already exist for it. Updates
632 the profile if it exists. Please see http://docs.ceph.com/docs/master/rados/operations/erasure-code-profile/ 643 the profile if it exists. Please see http://docs.ceph.com/docs/master/rados/operations/erasure-code-profile/
@@ -640,10 +651,9 @@ def create_erasure_profile(service, profile_name, erasure_plugin_name='jerasure'
640 :param coding_chunks: int 651 :param coding_chunks: int
641 :param locality: int 652 :param locality: int
642 :param durability_estimator: int 653 :param durability_estimator: int
654 :param device_class: six.string_types
643 :return: None. Can raise CalledProcessError 655 :return: None. Can raise CalledProcessError
644 """ 656 """
645 version = ceph_version()
646
647 # Ensure this failure_domain is allowed by Ceph 657 # Ensure this failure_domain is allowed by Ceph
648 validator(failure_domain, six.string_types, 658 validator(failure_domain, six.string_types,
649 ['chassis', 'datacenter', 'host', 'osd', 'pdu', 'pod', 'rack', 'region', 'room', 'root', 'row']) 659 ['chassis', 'datacenter', 'host', 'osd', 'pdu', 'pod', 'rack', 'region', 'room', 'root', 'row'])
@@ -654,12 +664,20 @@ def create_erasure_profile(service, profile_name, erasure_plugin_name='jerasure'
654 if locality is not None and durability_estimator is not None: 664 if locality is not None and durability_estimator is not None:
655 raise ValueError("create_erasure_profile should be called with k, m and one of l or c but not both.") 665 raise ValueError("create_erasure_profile should be called with k, m and one of l or c but not both.")
656 666
667 luminous_or_later = cmp_pkgrevno('ceph-common', '12.0.0') >= 0
657 # failure_domain changed in luminous 668 # failure_domain changed in luminous
658 if version and version >= '12.0.0': 669 if luminous_or_later:
659 cmd.append('crush-failure-domain=' + failure_domain) 670 cmd.append('crush-failure-domain=' + failure_domain)
660 else: 671 else:
661 cmd.append('ruleset-failure-domain=' + failure_domain) 672 cmd.append('ruleset-failure-domain=' + failure_domain)
662 673
674 # device class new in luminous
675 if luminous_or_later and device_class:
676 cmd.append('crush-device-class={}'.format(device_class))
677 else:
678 log('Skipping device class configuration (ceph < 12.0.0)',
679 level=DEBUG)
680
663 # Add plugin specific information 681 # Add plugin specific information
664 if locality is not None: 682 if locality is not None:
665 # For local erasure codes 683 # For local erasure codes
@@ -744,20 +762,26 @@ def pool_exists(service, name):
744 return name in out.split() 762 return name in out.split()
745 763
746 764
747def get_osds(service): 765def get_osds(service, device_class=None):
748 """Return a list of all Ceph Object Storage Daemons currently in the 766 """Return a list of all Ceph Object Storage Daemons currently in the
749 cluster. 767 cluster (optionally filtered by storage device class).
768
769 :param device_class: Class of storage device for OSD's
770 :type device_class: str
750 """ 771 """
751 version = ceph_version() 772 luminous_or_later = cmp_pkgrevno('ceph-common', '12.0.0') >= 0
752 if version and version >= '0.56': 773 if luminous_or_later and device_class:
774 out = check_output(['ceph', '--id', service,
775 'osd', 'crush', 'class',
776 'ls-osd', device_class,
777 '--format=json'])
778 else:
753 out = check_output(['ceph', '--id', service, 779 out = check_output(['ceph', '--id', service,
754 'osd', 'ls', 780 'osd', 'ls',
755 '--format=json']) 781 '--format=json'])
756 if six.PY3: 782 if six.PY3:
757 out = out.decode('UTF-8') 783 out = out.decode('UTF-8')
758 return json.loads(out) 784 return json.loads(out)
759
760 return None
761 785
762 786
763def install(): 787def install():
@@ -811,7 +835,7 @@ def set_app_name_for_pool(client, pool, name):
811 835
812 :raises: CalledProcessError if ceph call fails 836 :raises: CalledProcessError if ceph call fails
813 """ 837 """
814 if ceph_version() >= '12.0.0': 838 if cmp_pkgrevno('ceph-common', '12.0.0') >= 0:
815 cmd = ['ceph', '--id', client, 'osd', 'pool', 839 cmd = ['ceph', '--id', client, 'osd', 'pool',
816 'application', 'enable', pool, name] 840 'application', 'enable', pool, name]
817 check_call(cmd) 841 check_call(cmd)
@@ -1091,22 +1115,6 @@ def ensure_ceph_keyring(service, user=None, group=None,
1091 return True 1115 return True
1092 1116
1093 1117
1094def ceph_version():
1095 """Retrieve the local version of ceph."""
1096 if os.path.exists('/usr/bin/ceph'):
1097 cmd = ['ceph', '-v']
1098 output = check_output(cmd)
1099 if six.PY3:
1100 output = output.decode('UTF-8')
1101 output = output.split()
1102 if len(output) > 3:
1103 return output[2]
1104 else:
1105 return None
1106 else:
1107 return None
1108
1109
1110class CephBrokerRq(object): 1118class CephBrokerRq(object):
1111 """Ceph broker request. 1119 """Ceph broker request.
1112 1120
@@ -1147,14 +1155,47 @@ class CephBrokerRq(object):
1147 'object-prefix-permissions': object_prefix_permissions}) 1155 'object-prefix-permissions': object_prefix_permissions})
1148 1156
1149 def add_op_create_pool(self, name, replica_count=3, pg_num=None, 1157 def add_op_create_pool(self, name, replica_count=3, pg_num=None,
1150 weight=None, group=None, namespace=None): 1158 weight=None, group=None, namespace=None,
1151 """Adds an operation to create a pool. 1159 app_name=None, max_bytes=None, max_objects=None):
1152 1160 """DEPRECATED: Use ``add_op_create_replicated_pool()`` or
1153 @param pg_num setting: optional setting. If not provided, this value 1161 ``add_op_create_erasure_pool()`` instead.
1154 will be calculated by the broker based on how many OSDs are in the 1162 """
1155 cluster at the time of creation. Note that, if provided, this value 1163 return self.add_op_create_replicated_pool(
1156 will be capped at the current available maximum. 1164 name, replica_count=replica_count, pg_num=pg_num, weight=weight,
1157 @param weight: the percentage of data the pool makes up 1165 group=group, namespace=namespace, app_name=app_name,
1166 max_bytes=max_bytes, max_objects=max_objects)
1167
1168 def add_op_create_replicated_pool(self, name, replica_count=3, pg_num=None,
1169 weight=None, group=None, namespace=None,
1170 app_name=None, max_bytes=None,
1171 max_objects=None):
1172 """Adds an operation to create a replicated pool.
1173
1174 :param name: Name of pool to create
1175 :type name: str
1176 :param replica_count: Number of copies Ceph should keep of your data.
1177 :type replica_count: int
1178 :param pg_num: Request specific number of Placement Groups to create
1179 for pool.
1180 :type pg_num: int
1181 :param weight: The percentage of data that is expected to be contained
1182 in the pool from the total available space on the OSDs.
1183 Used to calculate number of Placement Groups to create
1184 for pool.
1185 :type weight: float
1186 :param group: Group to add pool to
1187 :type group: str
1188 :param namespace: Group namespace
1189 :type namespace: str
1190 :param app_name: (Optional) Tag pool with application name. Note that
1191 there is certain protocols emerging upstream with
1192 regard to meaningful application names to use.
1193 Examples are ``rbd`` and ``rgw``.
1194 :type app_name: str
1195 :param max_bytes: Maximum bytes quota to apply
1196 :type max_bytes: int
1197 :param max_objects: Maximum objects quota to apply
1198 :type max_objects: int
1158 """ 1199 """
1159 if pg_num and weight: 1200 if pg_num and weight:
1160 raise ValueError('pg_num and weight are mutually exclusive') 1201 raise ValueError('pg_num and weight are mutually exclusive')
@@ -1162,7 +1203,41 @@ class CephBrokerRq(object):
1162 self.ops.append({'op': 'create-pool', 'name': name, 1203 self.ops.append({'op': 'create-pool', 'name': name,
1163 'replicas': replica_count, 'pg_num': pg_num, 1204 'replicas': replica_count, 'pg_num': pg_num,
1164 'weight': weight, 'group': group, 1205 'weight': weight, 'group': group,
1165 'group-namespace': namespace}) 1206 'group-namespace': namespace, 'app-name': app_name,
1207 'max-bytes': max_bytes, 'max-objects': max_objects})
1208
1209 def add_op_create_erasure_pool(self, name, erasure_profile=None,
1210 weight=None, group=None, app_name=None,
1211 max_bytes=None, max_objects=None):
1212 """Adds an operation to create a erasure coded pool.
1213
1214 :param name: Name of pool to create
1215 :type name: str
1216 :param erasure_profile: Name of erasure code profile to use. If not
1217 set the ceph-mon unit handling the broker
1218 request will set its default value.
1219 :type erasure_profile: str
1220 :param weight: The percentage of data that is expected to be contained
1221 in the pool from the total available space on the OSDs.
1222 :type weight: float
1223 :param group: Group to add pool to
1224 :type group: str
1225 :param app_name: (Optional) Tag pool with application name. Note that
1226 there is certain protocols emerging upstream with
1227 regard to meaningful application names to use.
1228 Examples are ``rbd`` and ``rgw``.
1229 :type app_name: str
1230 :param max_bytes: Maximum bytes quota to apply
1231 :type max_bytes: int
1232 :param max_objects: Maximum objects quota to apply
1233 :type max_objects: int
1234 """
1235 self.ops.append({'op': 'create-pool', 'name': name,
1236 'pool-type': 'erasure',
1237 'erasure-profile': erasure_profile,
1238 'weight': weight,
1239 'group': group, 'app-name': app_name,
1240 'max-bytes': max_bytes, 'max-objects': max_objects})
1166 1241
1167 def set_ops(self, ops): 1242 def set_ops(self, ops):
1168 """Set request ops to provided value. 1243 """Set request ops to provided value.
diff --git a/hooks/charmhelpers/contrib/storage/linux/loopback.py b/charmhelpers/contrib/storage/linux/loopback.py
index 82472ff..82472ff 100644
--- a/hooks/charmhelpers/contrib/storage/linux/loopback.py
+++ b/charmhelpers/contrib/storage/linux/loopback.py
diff --git a/hooks/charmhelpers/contrib/storage/linux/lvm.py b/charmhelpers/contrib/storage/linux/lvm.py
index c8bde69..c8bde69 100644
--- a/hooks/charmhelpers/contrib/storage/linux/lvm.py
+++ b/charmhelpers/contrib/storage/linux/lvm.py
diff --git a/hooks/charmhelpers/contrib/storage/linux/utils.py b/charmhelpers/contrib/storage/linux/utils.py
index 6f846b0..6f846b0 100644
--- a/hooks/charmhelpers/contrib/storage/linux/utils.py
+++ b/charmhelpers/contrib/storage/linux/utils.py
diff --git a/hooks/charmhelpers/core/__init__.py b/charmhelpers/core/__init__.py
index d7567b8..d7567b8 100644
--- a/hooks/charmhelpers/core/__init__.py
+++ b/charmhelpers/core/__init__.py
diff --git a/hooks/charmhelpers/core/decorators.py b/charmhelpers/core/decorators.py
index 6ad41ee..6ad41ee 100644
--- a/hooks/charmhelpers/core/decorators.py
+++ b/charmhelpers/core/decorators.py
diff --git a/hooks/charmhelpers/core/files.py b/charmhelpers/core/files.py
index fdd82b7..fdd82b7 100644
--- a/hooks/charmhelpers/core/files.py
+++ b/charmhelpers/core/files.py
diff --git a/hooks/charmhelpers/core/fstab.py b/charmhelpers/core/fstab.py
index d9fa915..d9fa915 100644
--- a/hooks/charmhelpers/core/fstab.py
+++ b/charmhelpers/core/fstab.py
diff --git a/hooks/charmhelpers/core/hookenv.py b/charmhelpers/core/hookenv.py
index 2e28765..4744eb4 100644
--- a/hooks/charmhelpers/core/hookenv.py
+++ b/charmhelpers/core/hookenv.py
@@ -50,6 +50,11 @@ TRACE = "TRACE"
50MARKER = object() 50MARKER = object()
51SH_MAX_ARG = 131071 51SH_MAX_ARG = 131071
52 52
53
54RANGE_WARNING = ('Passing NO_PROXY string that includes a cidr. '
55 'This may not be compatible with software you are '
56 'running in your shell.')
57
53cache = {} 58cache = {}
54 59
55 60
@@ -1414,3 +1419,72 @@ def unit_doomed(unit=None):
1414 # I don't think 'dead' units ever show up in the goal-state, but 1419 # I don't think 'dead' units ever show up in the goal-state, but
1415 # check anyway in addition to 'dying'. 1420 # check anyway in addition to 'dying'.
1416 return units[unit]['status'] in ('dying', 'dead') 1421 return units[unit]['status'] in ('dying', 'dead')
1422
1423
1424def env_proxy_settings(selected_settings=None):
1425 """Get proxy settings from process environment variables.
1426
1427 Get charm proxy settings from environment variables that correspond to
1428 juju-http-proxy, juju-https-proxy and juju-no-proxy (available as of 2.4.2,
1429 see lp:1782236) in a format suitable for passing to an application that
1430 reacts to proxy settings passed as environment variables. Some applications
1431 support lowercase or uppercase notation (e.g. curl), some support only
1432 lowercase (e.g. wget), there are also subjectively rare cases of only
1433 uppercase notation support. no_proxy CIDR and wildcard support also varies
1434 between runtimes and applications as there is no enforced standard.
1435
1436 Some applications may connect to multiple destinations and expose config
1437 options that would affect only proxy settings for a specific destination
1438 these should be handled in charms in an application-specific manner.
1439
1440 :param selected_settings: format only a subset of possible settings
1441 :type selected_settings: list
1442 :rtype: Option(None, dict[str, str])
1443 """
1444 SUPPORTED_SETTINGS = {
1445 'http': 'HTTP_PROXY',
1446 'https': 'HTTPS_PROXY',
1447 'no_proxy': 'NO_PROXY',
1448 'ftp': 'FTP_PROXY'
1449 }
1450 if selected_settings is None:
1451 selected_settings = SUPPORTED_SETTINGS
1452
1453 selected_vars = [v for k, v in SUPPORTED_SETTINGS.items()
1454 if k in selected_settings]
1455 proxy_settings = {}
1456 for var in selected_vars:
1457 var_val = os.getenv(var)
1458 if var_val:
1459 proxy_settings[var] = var_val
1460 proxy_settings[var.lower()] = var_val
1461 # Now handle juju-prefixed environment variables. The legacy vs new
1462 # environment variable usage is mutually exclusive
1463 charm_var_val = os.getenv('JUJU_CHARM_{}'.format(var))
1464 if charm_var_val:
1465 proxy_settings[var] = charm_var_val
1466 proxy_settings[var.lower()] = charm_var_val
1467 if 'no_proxy' in proxy_settings:
1468 if _contains_range(proxy_settings['no_proxy']):
1469 log(RANGE_WARNING, level=WARNING)
1470 return proxy_settings if proxy_settings else None
1471
1472
1473def _contains_range(addresses):
1474 """Check for cidr or wildcard domain in a string.
1475
1476 Given a string comprising a comma seperated list of ip addresses
1477 and domain names, determine whether the string contains IP ranges
1478 or wildcard domains.
1479
1480 :param addresses: comma seperated list of domains and ip addresses.
1481 :type addresses: str
1482 """
1483 return (
1484 # Test for cidr (e.g. 10.20.20.0/24)
1485 "/" in addresses or
1486 # Test for wildcard domains (*.foo.com or .foo.com)
1487 "*" in addresses or
1488 addresses.startswith(".") or
1489 ",." in addresses or
1490 " ." in addresses)
diff --git a/hooks/charmhelpers/core/host.py b/charmhelpers/core/host.py
index 47c1fc3..47c1fc3 100644
--- a/hooks/charmhelpers/core/host.py
+++ b/charmhelpers/core/host.py
diff --git a/hooks/charmhelpers/core/host_factory/__init__.py b/charmhelpers/core/host_factory/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/core/host_factory/__init__.py
+++ b/charmhelpers/core/host_factory/__init__.py
diff --git a/hooks/charmhelpers/core/host_factory/centos.py b/charmhelpers/core/host_factory/centos.py
index 7781a39..7781a39 100644
--- a/hooks/charmhelpers/core/host_factory/centos.py
+++ b/charmhelpers/core/host_factory/centos.py
diff --git a/hooks/charmhelpers/core/host_factory/ubuntu.py b/charmhelpers/core/host_factory/ubuntu.py
index d7e920e..d7e920e 100644
--- a/hooks/charmhelpers/core/host_factory/ubuntu.py
+++ b/charmhelpers/core/host_factory/ubuntu.py
diff --git a/hooks/charmhelpers/core/hugepage.py b/charmhelpers/core/hugepage.py
index 54b5b5e..54b5b5e 100644
--- a/hooks/charmhelpers/core/hugepage.py
+++ b/charmhelpers/core/hugepage.py
diff --git a/hooks/charmhelpers/core/kernel.py b/charmhelpers/core/kernel.py
index e01f4f8..e01f4f8 100644
--- a/hooks/charmhelpers/core/kernel.py
+++ b/charmhelpers/core/kernel.py
diff --git a/hooks/charmhelpers/core/kernel_factory/__init__.py b/charmhelpers/core/kernel_factory/__init__.py
index e69de29..e69de29 100644
--- a/hooks/charmhelpers/core/kernel_factory/__init__.py
+++ b/charmhelpers/core/kernel_factory/__init__.py
diff --git a/hooks/charmhelpers/core/kernel_factory/centos.py b/charmhelpers/core/kernel_factory/centos.py
index 1c402c1..1c402c1 100644
--- a/hooks/charmhelpers/core/kernel_factory/centos.py
+++ b/charmhelpers/core/kernel_factory/centos.py
diff --git a/hooks/charmhelpers/core/kernel_factory/ubuntu.py b/charmhelpers/core/kernel_factory/ubuntu.py
index 3de372f..3de372f 100644
--- a/hooks/charmhelpers/core/kernel_factory/ubuntu.py
+++ b/charmhelpers/core/kernel_factory/ubuntu.py
diff --git a/hooks/charmhelpers/core/services/__init__.py b/charmhelpers/core/services/__init__.py
index 61fd074..61fd074 100644
--- a/hooks/charmhelpers/core/services/__init__.py
+++ b/charmhelpers/core/services/__init__.py
diff --git a/hooks/charmhelpers/core/services/base.py b/charmhelpers/core/services/base.py
index 179ad4f..179ad4f 100644
--- a/hooks/charmhelpers/core/services/base.py
+++ b/charmhelpers/core/services/base.py
diff --git a/hooks/charmhelpers/core/services/helpers.py b/charmhelpers/core/services/helpers.py
index 3e6e30d..3e6e30d 100644
--- a/hooks/charmhelpers/core/services/helpers.py
+++ b/charmhelpers/core/services/helpers.py
diff --git a/hooks/charmhelpers/core/strutils.py b/charmhelpers/core/strutils.py
index e8df045..e8df045 100644
--- a/hooks/charmhelpers/core/strutils.py
+++ b/charmhelpers/core/strutils.py
diff --git a/hooks/charmhelpers/core/sysctl.py b/charmhelpers/core/sysctl.py
index 1f188d8..1f188d8 100644
--- a/hooks/charmhelpers/core/sysctl.py
+++ b/charmhelpers/core/sysctl.py
diff --git a/hooks/charmhelpers/core/templating.py b/charmhelpers/core/templating.py
index 9014015..9014015 100644
--- a/hooks/charmhelpers/core/templating.py
+++ b/charmhelpers/core/templating.py
diff --git a/hooks/charmhelpers/core/unitdata.py b/charmhelpers/core/unitdata.py
index ab55432..ab55432 100644
--- a/hooks/charmhelpers/core/unitdata.py
+++ b/charmhelpers/core/unitdata.py
diff --git a/hooks/charmhelpers/fetch/__init__.py b/charmhelpers/fetch/__init__.py
index 8572d34..8572d34 100644
--- a/hooks/charmhelpers/fetch/__init__.py
+++ b/charmhelpers/fetch/__init__.py
diff --git a/hooks/charmhelpers/fetch/archiveurl.py b/charmhelpers/fetch/archiveurl.py
index d25587a..d25587a 100644
--- a/hooks/charmhelpers/fetch/archiveurl.py
+++ b/charmhelpers/fetch/archiveurl.py
diff --git a/hooks/charmhelpers/fetch/bzrurl.py b/charmhelpers/fetch/bzrurl.py
index c4ab3ff..c4ab3ff 100644
--- a/hooks/charmhelpers/fetch/bzrurl.py
+++ b/charmhelpers/fetch/bzrurl.py
diff --git a/hooks/charmhelpers/fetch/centos.py b/charmhelpers/fetch/centos.py
index a91dcff..a91dcff 100644
--- a/hooks/charmhelpers/fetch/centos.py
+++ b/charmhelpers/fetch/centos.py
diff --git a/hooks/charmhelpers/fetch/giturl.py b/charmhelpers/fetch/giturl.py
index 070ca9b..070ca9b 100644
--- a/hooks/charmhelpers/fetch/giturl.py
+++ b/charmhelpers/fetch/giturl.py
diff --git a/hooks/charmhelpers/fetch/python/__init__.py b/charmhelpers/fetch/python/__init__.py
index bff99dc..bff99dc 100644
--- a/hooks/charmhelpers/fetch/python/__init__.py
+++ b/charmhelpers/fetch/python/__init__.py
diff --git a/hooks/charmhelpers/fetch/python/debug.py b/charmhelpers/fetch/python/debug.py
index 757135e..757135e 100644
--- a/hooks/charmhelpers/fetch/python/debug.py
+++ b/charmhelpers/fetch/python/debug.py
diff --git a/hooks/charmhelpers/fetch/python/packages.py b/charmhelpers/fetch/python/packages.py
index 6e95028..6e95028 100644
--- a/hooks/charmhelpers/fetch/python/packages.py
+++ b/charmhelpers/fetch/python/packages.py
diff --git a/hooks/charmhelpers/fetch/python/rpdb.py b/charmhelpers/fetch/python/rpdb.py
index 9b31610..9b31610 100644
--- a/hooks/charmhelpers/fetch/python/rpdb.py
+++ b/charmhelpers/fetch/python/rpdb.py
diff --git a/hooks/charmhelpers/fetch/python/version.py b/charmhelpers/fetch/python/version.py
index 3eb4210..3eb4210 100644
--- a/hooks/charmhelpers/fetch/python/version.py
+++ b/charmhelpers/fetch/python/version.py
diff --git a/hooks/charmhelpers/fetch/snap.py b/charmhelpers/fetch/snap.py
index 395836c..395836c 100644
--- a/hooks/charmhelpers/fetch/snap.py
+++ b/charmhelpers/fetch/snap.py
diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/charmhelpers/fetch/ubuntu.py
index 8a5cadf..c6d9341 100644
--- a/hooks/charmhelpers/fetch/ubuntu.py
+++ b/charmhelpers/fetch/ubuntu.py
@@ -19,15 +19,14 @@ import re
19import six 19import six
20import time 20import time
21import subprocess 21import subprocess
22from tempfile import NamedTemporaryFile
23 22
24from charmhelpers.core.host import ( 23from charmhelpers.core.host import get_distrib_codename
25 lsb_release 24
26)
27from charmhelpers.core.hookenv import ( 25from charmhelpers.core.hookenv import (
28 log, 26 log,
29 DEBUG, 27 DEBUG,
30 WARNING, 28 WARNING,
29 env_proxy_settings,
31) 30)
32from charmhelpers.fetch import SourceConfigError, GPGKeyError 31from charmhelpers.fetch import SourceConfigError, GPGKeyError
33 32
@@ -303,12 +302,17 @@ def import_key(key):
303 """Import an ASCII Armor key. 302 """Import an ASCII Armor key.
304 303
305 A Radix64 format keyid is also supported for backwards 304 A Radix64 format keyid is also supported for backwards
306 compatibility, but should never be used; the key retrieval 305 compatibility. In this case Ubuntu keyserver will be
307 mechanism is insecure and subject to man-in-the-middle attacks 306 queried for a key via HTTPS by its keyid. This method
308 voiding all signature checks using that key. 307 is less preferrable because https proxy servers may
309 308 require traffic decryption which is equivalent to a
310 :param keyid: The key in ASCII armor format, 309 man-in-the-middle attack (a proxy server impersonates
311 including BEGIN and END markers. 310 keyserver TLS certificates and has to be explicitly
311 trusted by the system).
312
313 :param key: A GPG key in ASCII armor format,
314 including BEGIN and END markers or a keyid.
315 :type key: (bytes, str)
312 :raises: GPGKeyError if the key could not be imported 316 :raises: GPGKeyError if the key could not be imported
313 """ 317 """
314 key = key.strip() 318 key = key.strip()
@@ -319,35 +323,131 @@ def import_key(key):
319 log("PGP key found (looks like ASCII Armor format)", level=DEBUG) 323 log("PGP key found (looks like ASCII Armor format)", level=DEBUG)
320 if ('-----BEGIN PGP PUBLIC KEY BLOCK-----' in key and 324 if ('-----BEGIN PGP PUBLIC KEY BLOCK-----' in key and
321 '-----END PGP PUBLIC KEY BLOCK-----' in key): 325 '-----END PGP PUBLIC KEY BLOCK-----' in key):
322 log("Importing ASCII Armor PGP key", level=DEBUG) 326 log("Writing provided PGP key in the binary format", level=DEBUG)
323 with NamedTemporaryFile() as keyfile: 327 if six.PY3:
324 with open(keyfile.name, 'w') as fd: 328 key_bytes = key.encode('utf-8')
325 fd.write(key) 329 else:
326 fd.write("\n") 330 key_bytes = key
327 cmd = ['apt-key', 'add', keyfile.name] 331 key_name = _get_keyid_by_gpg_key(key_bytes)
328 try: 332 key_gpg = _dearmor_gpg_key(key_bytes)
329 subprocess.check_call(cmd) 333 _write_apt_gpg_keyfile(key_name=key_name, key_material=key_gpg)
330 except subprocess.CalledProcessError:
331 error = "Error importing PGP key '{}'".format(key)
332 log(error)
333 raise GPGKeyError(error)
334 else: 334 else:
335 raise GPGKeyError("ASCII armor markers missing from GPG key") 335 raise GPGKeyError("ASCII armor markers missing from GPG key")
336 else: 336 else:
337 # We should only send things obviously not a keyid offsite
338 # via this unsecured protocol, as it may be a secret or part
339 # of one.
340 log("PGP key found (looks like Radix64 format)", level=WARNING) 337 log("PGP key found (looks like Radix64 format)", level=WARNING)
341 log("INSECURLY importing PGP key from keyserver; " 338 log("SECURELY importing PGP key from keyserver; "
342 "full key not provided.", level=WARNING) 339 "full key not provided.", level=WARNING)
343 cmd = ['apt-key', 'adv', '--keyserver', 340 # as of bionic add-apt-repository uses curl with an HTTPS keyserver URL
344 'hkp://keyserver.ubuntu.com:80', '--recv-keys', key] 341 # to retrieve GPG keys. `apt-key adv` command is deprecated as is
345 try: 342 # apt-key in general as noted in its manpage. See lp:1433761 for more
346 _run_with_retries(cmd) 343 # history. Instead, /etc/apt/trusted.gpg.d is used directly to drop
347 except subprocess.CalledProcessError: 344 # gpg
348 error = "Error importing PGP key '{}'".format(key) 345 key_asc = _get_key_by_keyid(key)
349 log(error) 346 # write the key in GPG format so that apt-key list shows it
350 raise GPGKeyError(error) 347 key_gpg = _dearmor_gpg_key(key_asc)
348 _write_apt_gpg_keyfile(key_name=key, key_material=key_gpg)
349
350
351def _get_keyid_by_gpg_key(key_material):
352 """Get a GPG key fingerprint by GPG key material.
353 Gets a GPG key fingerprint (40-digit, 160-bit) by the ASCII armor-encoded
354 or binary GPG key material. Can be used, for example, to generate file
355 names for keys passed via charm options.
356
357 :param key_material: ASCII armor-encoded or binary GPG key material
358 :type key_material: bytes
359 :raises: GPGKeyError if invalid key material has been provided
360 :returns: A GPG key fingerprint
361 :rtype: str
362 """
363 # Use the same gpg command for both Xenial and Bionic
364 cmd = 'gpg --with-colons --with-fingerprint'
365 ps = subprocess.Popen(cmd.split(),
366 stdout=subprocess.PIPE,
367 stderr=subprocess.PIPE,
368 stdin=subprocess.PIPE)
369 out, err = ps.communicate(input=key_material)
370 if six.PY3:
371 out = out.decode('utf-8')
372 err = err.decode('utf-8')
373 if 'gpg: no valid OpenPGP data found.' in err:
374 raise GPGKeyError('Invalid GPG key material provided')
375 # from gnupg2 docs: fpr :: Fingerprint (fingerprint is in field 10)
376 return re.search(r"^fpr:{9}([0-9A-F]{40}):$", out, re.MULTILINE).group(1)
377
378
379def _get_key_by_keyid(keyid):
380 """Get a key via HTTPS from the Ubuntu keyserver.
381 Different key ID formats are supported by SKS keyservers (the longer ones
382 are more secure, see "dead beef attack" and https://evil32.com/). Since
383 HTTPS is used, if SSLBump-like HTTPS proxies are in place, they will
384 impersonate keyserver.ubuntu.com and generate a certificate with
385 keyserver.ubuntu.com in the CN field or in SubjAltName fields of a
386 certificate. If such proxy behavior is expected it is necessary to add the
387 CA certificate chain containing the intermediate CA of the SSLBump proxy to
388 every machine that this code runs on via ca-certs cloud-init directive (via
389 cloudinit-userdata model-config) or via other means (such as through a
390 custom charm option). Also note that DNS resolution for the hostname in a
391 URL is done at a proxy server - not at the client side.
392
393 8-digit (32 bit) key ID
394 https://keyserver.ubuntu.com/pks/lookup?search=0x4652B4E6
395 16-digit (64 bit) key ID
396 https://keyserver.ubuntu.com/pks/lookup?search=0x6E85A86E4652B4E6
397 40-digit key ID:
398 https://keyserver.ubuntu.com/pks/lookup?search=0x35F77D63B5CEC106C577ED856E85A86E4652B4E6
399
400 :param keyid: An 8, 16 or 40 hex digit keyid to find a key for
401 :type keyid: (bytes, str)
402 :returns: A key material for the specified GPG key id
403 :rtype: (str, bytes)
404 :raises: subprocess.CalledProcessError
405 """
406 # options=mr - machine-readable output (disables html wrappers)
407 keyserver_url = ('https://keyserver.ubuntu.com'
408 '/pks/lookup?op=get&options=mr&exact=on&search=0x{}')
409 curl_cmd = ['curl', keyserver_url.format(keyid)]
410 # use proxy server settings in order to retrieve the key
411 return subprocess.check_output(curl_cmd,
412 env=env_proxy_settings(['https']))
413
414
415def _dearmor_gpg_key(key_asc):
416 """Converts a GPG key in the ASCII armor format to the binary format.
417
418 :param key_asc: A GPG key in ASCII armor format.
419 :type key_asc: (str, bytes)
420 :returns: A GPG key in binary format
421 :rtype: (str, bytes)
422 :raises: GPGKeyError
423 """
424 ps = subprocess.Popen(['gpg', '--dearmor'],
425 stdout=subprocess.PIPE,
426 stderr=subprocess.PIPE,
427 stdin=subprocess.PIPE)
428 out, err = ps.communicate(input=key_asc)
429 # no need to decode output as it is binary (invalid utf-8), only error
430 if six.PY3:
431 err = err.decode('utf-8')
432 if 'gpg: no valid OpenPGP data found.' in err:
433 raise GPGKeyError('Invalid GPG key material. Check your network setup'
434 ' (MTU, routing, DNS) and/or proxy server settings'
435 ' as well as destination keyserver status.')
436 else:
437 return out
438
439
440def _write_apt_gpg_keyfile(key_name, key_material):
441 """Writes GPG key material into a file at a provided path.
442
443 :param key_name: A key name to use for a key file (could be a fingerprint)
444 :type key_name: str
445 :param key_material: A GPG key material (binary)
446 :type key_material: (str, bytes)
447 """
448 with open('/etc/apt/trusted.gpg.d/{}.gpg'.format(key_name),
449 'wb') as keyf:
450 keyf.write(key_material)
351 451
352 452
353def add_source(source, key=None, fail_invalid=False): 453def add_source(source, key=None, fail_invalid=False):
@@ -442,13 +542,13 @@ def add_source(source, key=None, fail_invalid=False):
442def _add_proposed(): 542def _add_proposed():
443 """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list 543 """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list
444 544
445 Uses lsb_release()['DISTRIB_CODENAME'] to determine the correct staza for 545 Uses get_distrib_codename to determine the correct stanza for
446 the deb line. 546 the deb line.
447 547
448 For intel architecutres PROPOSED_POCKET is used for the release, but for 548 For intel architecutres PROPOSED_POCKET is used for the release, but for
449 other architectures PROPOSED_PORTS_POCKET is used for the release. 549 other architectures PROPOSED_PORTS_POCKET is used for the release.
450 """ 550 """
451 release = lsb_release()['DISTRIB_CODENAME'] 551 release = get_distrib_codename()
452 arch = platform.machine() 552 arch = platform.machine()
453 if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET): 553 if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET):
454 raise SourceConfigError("Arch {} not supported for (distro-)proposed" 554 raise SourceConfigError("Arch {} not supported for (distro-)proposed"
@@ -461,11 +561,16 @@ def _add_apt_repository(spec):
461 """Add the spec using add_apt_repository 561 """Add the spec using add_apt_repository
462 562
463 :param spec: the parameter to pass to add_apt_repository 563 :param spec: the parameter to pass to add_apt_repository
564 :type spec: str
464 """ 565 """
465 if '{series}' in spec: 566 if '{series}' in spec:
466 series = lsb_release()['DISTRIB_CODENAME'] 567 series = get_distrib_codename()
467 spec = spec.replace('{series}', series) 568 spec = spec.replace('{series}', series)
468 _run_with_retries(['add-apt-repository', '--yes', spec]) 569 # software-properties package for bionic properly reacts to proxy settings
570 # passed as environment variables (See lp:1433761). This is not the case
571 # LTS and non-LTS releases below bionic.
572 _run_with_retries(['add-apt-repository', '--yes', spec],
573 cmd_env=env_proxy_settings(['https']))
469 574
470 575
471def _add_cloud_pocket(pocket): 576def _add_cloud_pocket(pocket):
@@ -534,7 +639,7 @@ def _verify_is_ubuntu_rel(release, os_release):
534 :raises: SourceConfigError if the release is not the same as the ubuntu 639 :raises: SourceConfigError if the release is not the same as the ubuntu
535 release. 640 release.
536 """ 641 """
537 ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] 642 ubuntu_rel = get_distrib_codename()
538 if release != ubuntu_rel: 643 if release != ubuntu_rel:
539 raise SourceConfigError( 644 raise SourceConfigError(
540 'Invalid Cloud Archive release specified: {}-{} on this Ubuntu' 645 'Invalid Cloud Archive release specified: {}-{} on this Ubuntu'
diff --git a/hooks/charmhelpers/osplatform.py b/charmhelpers/osplatform.py
index d9a4d5c..d9a4d5c 100644
--- a/hooks/charmhelpers/osplatform.py
+++ b/charmhelpers/osplatform.py
diff --git a/hooks/charmhelpers/payload/__init__.py b/charmhelpers/payload/__init__.py
index ee55cb3..ee55cb3 100644
--- a/hooks/charmhelpers/payload/__init__.py
+++ b/charmhelpers/payload/__init__.py
diff --git a/hooks/charmhelpers/payload/execd.py b/charmhelpers/payload/execd.py
index 1502aa0..1502aa0 100644
--- a/hooks/charmhelpers/payload/execd.py
+++ b/charmhelpers/payload/execd.py
diff --git a/hooks/ceph.py b/hooks/ceph.py
index de1e48e..8000a00 100644
--- a/hooks/ceph.py
+++ b/hooks/ceph.py
@@ -128,7 +128,7 @@ def is_quorum():
128 ] 128 ]
129 if os.path.exists(asok): 129 if os.path.exists(asok):
130 try: 130 try:
131 result = json.loads(subprocess.check_output(cmd)) 131 result = json.loads(subprocess.check_output(cmd).decode('utf-8'))
132 except subprocess.CalledProcessError: 132 except subprocess.CalledProcessError:
133 return False 133 return False
134 except ValueError: 134 except ValueError:
@@ -155,7 +155,7 @@ def is_leader():
155 ] 155 ]
156 if os.path.exists(asok): 156 if os.path.exists(asok):
157 try: 157 try:
158 result = json.loads(subprocess.check_output(cmd)) 158 result = json.loads(subprocess.check_output(cmd).decode('utf-8'))
159 except subprocess.CalledProcessError: 159 except subprocess.CalledProcessError:
160 return False 160 return False
161 except ValueError: 161 except ValueError:
@@ -201,7 +201,9 @@ DISK_FORMATS = [
201 201
202def is_osd_disk(dev): 202def is_osd_disk(dev):
203 try: 203 try:
204 info = subprocess.check_output(['sgdisk', '-i', '1', dev]) 204 info = (subprocess
205 .check_output(['sgdisk', '-i', '1', dev])
206 .decode('utf-8'))
205 info = info.split("\n") # IGNORE:E1103 207 info = info.split("\n") # IGNORE:E1103
206 for line in info: 208 for line in info:
207 if line.startswith( 209 if line.startswith(
@@ -266,7 +268,7 @@ def generate_monitor_secret():
266 '--name=mon.', 268 '--name=mon.',
267 '--gen-key' 269 '--gen-key'
268 ] 270 ]
269 res = subprocess.check_output(cmd) 271 res = subprocess.check_output(cmd).decode('utf-8')
270 272
271 return "{}==".format(res.split('=')[1].strip()) 273 return "{}==".format(res.split('=')[1].strip())
272 274
@@ -403,12 +405,15 @@ def get_named_key(name, caps=None):
403 'auth', 'get-or-create', 'client.{}'.format(name), 405 'auth', 'get-or-create', 'client.{}'.format(name),
404 ] 406 ]
405 # Add capabilities 407 # Add capabilities
406 for subsystem, subcaps in caps.iteritems(): 408 for subsystem, subcaps in caps.items():
407 cmd.extend([ 409 cmd.extend([
408 subsystem, 410 subsystem,
409 '; '.join(subcaps), 411 '; '.join(subcaps),
410 ]) 412 ])
411 return parse_key(subprocess.check_output(cmd).strip()) # IGNORE:E1103 413 return parse_key(subprocess
414 .check_output(cmd)
415 .decode('utf-8')
416 .strip()) # IGNORE:E1103
412 417
413 418
414def upgrade_key_caps(key, caps): 419def upgrade_key_caps(key, caps):
@@ -419,7 +424,7 @@ def upgrade_key_caps(key, caps):
419 cmd = [ 424 cmd = [
420 "sudo", "-u", ceph_user(), 'ceph', 'auth', 'caps', key 425 "sudo", "-u", ceph_user(), 'ceph', 'auth', 'caps', key
421 ] 426 ]
422 for subsystem, subcaps in caps.iteritems(): 427 for subsystem, subcaps in caps.items():
423 cmd.extend([subsystem, '; '.join(subcaps)]) 428 cmd.extend([subsystem, '; '.join(subcaps)])
424 subprocess.check_call(cmd) 429 subprocess.check_call(cmd)
425 430
diff --git a/hooks/ceph_hooks.py b/hooks/ceph_hooks.py
index 6347e0e..322ccb0 100755
--- a/hooks/ceph_hooks.py
+++ b/hooks/ceph_hooks.py
@@ -1,4 +1,4 @@
1#!/usr/bin/python 1#!/usr/bin/env python3
2 2
3# 3#
4# Copyright 2012 Canonical Ltd. 4# Copyright 2012 Canonical Ltd.
@@ -13,6 +13,18 @@ import os
13import shutil 13import shutil
14import sys 14import sys
15 15
16
17_path = os.path.dirname(os.path.realpath(__file__))
18_root = os.path.abspath(os.path.join(_path, '..'))
19
20
21def _add_path(path):
22 if path not in sys.path:
23 sys.path.insert(1, path)
24
25
26_add_path(_root)
27
16import ceph 28import ceph
17from charmhelpers.core.hookenv import ( 29from charmhelpers.core.hookenv import (
18 log, 30 log,
diff --git a/hooks/install b/hooks/install
index 86d4885..eb05824 100755
--- a/hooks/install
+++ b/hooks/install
@@ -11,7 +11,7 @@ check_and_install() {
11 fi 11 fi
12} 12}
13 13
14PYTHON="python" 14PYTHON="python3"
15 15
16for dep in ${DEPS[@]}; do 16for dep in ${DEPS[@]}; do
17 check_and_install ${PYTHON} ${dep} 17 check_and_install ${PYTHON} ${dep}
diff --git a/hooks/utils.py b/hooks/utils.py
index 5b68a1e..d1cf500 100644
--- a/hooks/utils.py
+++ b/hooks/utils.py
@@ -43,9 +43,9 @@ except ImportError:
43 43
44def enable_pocket(pocket): 44def enable_pocket(pocket):
45 apt_sources = "/etc/apt/sources.list" 45 apt_sources = "/etc/apt/sources.list"
46 with open(apt_sources, "r") as sources: 46 with open(apt_sources, "rt") as sources:
47 lines = sources.readlines() 47 lines = sources.readlines()
48 with open(apt_sources, "w") as sources: 48 with open(apt_sources, "wt") as sources:
49 for line in lines: 49 for line in lines:
50 if pocket in line: 50 if pocket in line:
51 sources.write(re.sub('^# deb', 'deb', line)) 51 sources.write(re.sub('^# deb', 'deb', line))
diff --git a/tox.ini b/tox.ini
index 68ab28e..3a8edbf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,7 +2,7 @@
2# This file is managed centrally by release-tools and should not be modified 2# This file is managed centrally by release-tools and should not be modified
3# within individual charm repos. 3# within individual charm repos.
4[tox] 4[tox]
5envlist = pep8,py27 5envlist = pep8,py3{5,6}
6skipsdist = True 6skipsdist = True
7 7
8[testenv] 8[testenv]
@@ -20,6 +20,7 @@ passenv = HOME TERM AMULET_* CS_API_*
20basepython = python2.7 20basepython = python2.7
21deps = -r{toxinidir}/requirements.txt 21deps = -r{toxinidir}/requirements.txt
22 -r{toxinidir}/test-requirements.txt 22 -r{toxinidir}/test-requirements.txt
23commands = /bin/true
23 24
24[testenv:py35] 25[testenv:py35]
25basepython = python3.5 26basepython = python3.5
diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py
index f80aab3..ba8fe96 100644
--- a/unit_tests/__init__.py
+++ b/unit_tests/__init__.py
@@ -1,2 +1,19 @@
1import os
1import sys 2import sys
2sys.path.append('hooks') 3
4
5_path = os.path.dirname(os.path.realpath(__file__))
6_actions = os.path.abspath(os.path.join(_path, '../actions'))
7_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
8_charmhelpers = os.path.abspath(os.path.join(_path, '../charmhelpers'))
9_unit_tests = os.path.abspath(os.path.join(_path, '../unit_tests'))
10
11
12def _add_path(path):
13 if path not in sys.path:
14 sys.path.insert(1, path)
15
16_add_path(_actions)
17_add_path(_hooks)
18_add_path(_charmhelpers)
19_add_path(_unit_tests)
diff --git a/unit_tests/test_ceph.py b/unit_tests/test_ceph.py
index 9ed36c0..4cb55b2 100644
--- a/unit_tests/test_ceph.py
+++ b/unit_tests/test_ceph.py
@@ -1,3 +1,4 @@
1import collections
1import unittest 2import unittest
2 3
3import mock 4import mock
@@ -70,10 +71,8 @@ class CephTestCase(unittest.TestCase):
70 expected_key = 'AQCnjmtbuEACMxAA7joUmgLIGI4/3LKkPzUy8g==' 71 expected_key = 'AQCnjmtbuEACMxAA7joUmgLIGI4/3LKkPzUy8g=='
71 expected_output = ('[client.testuser]\n key = {}' 72 expected_output = ('[client.testuser]\n key = {}'
72 .format(expected_key)) 73 .format(expected_key))
73 caps = { 74 caps = collections.OrderedDict([('mon', ['allow rw']),
74 'mon': ['allow rw'], 75 ('osd', ['allow rwx'])])
75 'osd': ['allow rwx']
76 }
77 ceph_user = 'ceph' 76 ceph_user = 'ceph'
78 ceph_proxy_host = 'cephproxy' 77 ceph_proxy_host = 'cephproxy'
79 mock_get_unit_hostname.return_value = ceph_proxy_host 78 mock_get_unit_hostname.return_value = ceph_proxy_host
@@ -86,7 +85,8 @@ class CephTestCase(unittest.TestCase):
86 '/var/lib/ceph/mon/ceph-{}/keyring'.format( 85 '/var/lib/ceph/mon/ceph-{}/keyring'.format(
87 ceph_proxy_host), 86 ceph_proxy_host),
88 'auth', 'get-or-create', user_spec, 'mon', 87 'auth', 'get-or-create', user_spec, 'mon',
89 'allow rw', 'osd', 'allow rwx']): expected_output 88 'allow rw', 'osd', 'allow rwx']): (expected_output
89 .encode('utf-8'))
90 }[' '.join(cmd)] 90 }[' '.join(cmd)]
91 mock_check_output.side_effect = check_output_side_effect 91 mock_check_output.side_effect = check_output_side_effect
92 mock_config.side_effect = self.empty_config_side_effect 92 mock_config.side_effect = self.empty_config_side_effect
diff --git a/unit_tests/test_ceph_hooks.py b/unit_tests/test_ceph_hooks.py
index 0b394cf..1bf97df 100644
--- a/unit_tests/test_ceph_hooks.py
+++ b/unit_tests/test_ceph_hooks.py
@@ -58,7 +58,7 @@ class TestHooks(test_utils.CharmTestCase):
58 'auth': 'cephx', 58 'auth': 'cephx',
59 'fsid': 'some-fsid'} 59 'fsid': 'some-fsid'}
60 60
61 mock_check_output.return_value = CEPH_GET_KEY 61 mock_check_output.return_value = CEPH_GET_KEY.encode()
62 self.relation_get.return_value = {} 62 self.relation_get.return_value = {}
63 self.test_config.set('monitor-hosts', settings['ceph-public-address']) 63 self.test_config.set('monitor-hosts', settings['ceph-public-address'])
64 self.test_config.set('fsid', settings['fsid']) 64 self.test_config.set('fsid', settings['fsid'])
@@ -123,7 +123,7 @@ class TestHooks(test_utils.CharmTestCase):
123 123
124 @mock.patch('subprocess.check_output') 124 @mock.patch('subprocess.check_output')
125 def test_client_relation_joined(self, mock_check_output): 125 def test_client_relation_joined(self, mock_check_output):
126 mock_check_output.return_value = CEPH_GET_KEY 126 mock_check_output.return_value = CEPH_GET_KEY.encode()
127 self.test_config.set('monitor-hosts', '127.0.0.1:1234') 127 self.test_config.set('monitor-hosts', '127.0.0.1:1234')
128 self.test_config.set('fsid', 'abc123') 128 self.test_config.set('fsid', 'abc123')
129 self.test_config.set('admin-key', 'some-admin-key') 129 self.test_config.set('admin-key', 'some-admin-key')
diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py
index 0a77482..ed0e7a1 100644
--- a/unit_tests/test_utils.py
+++ b/unit_tests/test_utils.py
@@ -36,7 +36,7 @@ def get_default_config():
36 ''' 36 '''
37 default_config = {} 37 default_config = {}
38 config = load_config() 38 config = load_config()
39 for k, v in config.iteritems(): 39 for k, v in config.items():
40 if 'default' in v: 40 if 'default' in v:
41 default_config[k] = v['default'] 41 default_config[k] = v['default']
42 else: 42 else: