Merge lp:~stub/charm-helpers/py3-2 into lp:charm-helpers

Proposed by Stuart Bishop
Status: Merged
Merged at revision: 258
Proposed branch: lp:~stub/charm-helpers/py3-2
Merge into: lp:charm-helpers
Diff against target: 3281 lines (+625/-393)
62 files modified
.bzrignore (+2/-0)
Makefile (+30/-7)
charmhelpers/__init__.py (+22/-0)
charmhelpers/cli/__init__.py (+3/-2)
charmhelpers/contrib/amulet/deployment.py (+3/-3)
charmhelpers/contrib/amulet/utils.py (+6/-4)
charmhelpers/contrib/charmhelpers/__init__.py (+26/-19)
charmhelpers/contrib/hahelpers/cluster.py (+4/-3)
charmhelpers/contrib/network/ip.py (+2/-2)
charmhelpers/contrib/openstack/amulet/deployment.py (+2/-1)
charmhelpers/contrib/openstack/amulet/utils.py (+3/-1)
charmhelpers/contrib/openstack/context.py (+14/-13)
charmhelpers/contrib/openstack/neutron.py (+2/-2)
charmhelpers/contrib/openstack/templates/haproxy.cfg (+2/-2)
charmhelpers/contrib/openstack/templating.py (+5/-5)
charmhelpers/contrib/openstack/utils.py (+8/-7)
charmhelpers/contrib/peerstorage/__init__.py (+4/-3)
charmhelpers/contrib/python/packages.py (+1/-1)
charmhelpers/contrib/ssl/__init__.py (+1/-1)
charmhelpers/contrib/storage/linux/ceph.py (+9/-6)
charmhelpers/contrib/storage/linux/loopback.py (+4/-4)
charmhelpers/contrib/storage/linux/lvm.py (+1/-0)
charmhelpers/contrib/storage/linux/utils.py (+3/-2)
charmhelpers/contrib/templating/contexts.py (+5/-3)
charmhelpers/core/fstab.py (+10/-8)
charmhelpers/core/hookenv.py (+20/-12)
charmhelpers/core/host.py (+22/-17)
charmhelpers/core/services/helpers.py (+9/-5)
charmhelpers/core/templating.py (+2/-1)
charmhelpers/fetch/__init__.py (+13/-11)
charmhelpers/fetch/archiveurl.py (+53/-16)
charmhelpers/fetch/bzrurl.py (+5/-1)
charmhelpers/fetch/giturl.py (+6/-2)
test_requirements.txt (+17/-18)
tests/cli/test_cmdline.py (+7/-7)
tests/cli/test_function_signature_analysis.py (+2/-2)
tests/contrib/charmhelpers/test_charmhelpers.py (+6/-6)
tests/contrib/hahelpers/test_cluster_utils.py (+2/-2)
tests/contrib/network/test_ip.py (+11/-10)
tests/contrib/openstack/test_neutron_utils.py (+2/-2)
tests/contrib/openstack/test_openstack_utils.py (+33/-24)
tests/contrib/openstack/test_os_contexts.py (+28/-27)
tests/contrib/openstack/test_os_templating.py (+9/-3)
tests/contrib/storage/test_linux_ceph.py (+8/-8)
tests/contrib/storage/test_linux_storage_lvm.py (+2/-2)
tests/contrib/storage/test_linux_storage_utils.py (+8/-8)
tests/contrib/templating/test_contexts.py (+9/-6)
tests/contrib/unison/test_unison.py (+2/-2)
tests/core/test_fstab.py (+1/-1)
tests/core/test_hookenv.py (+34/-32)
tests/core/test_host.py (+19/-20)
tests/core/test_services.py (+4/-4)
tests/core/test_sysctl.py (+10/-3)
tests/fetch/test_archiveurl.py (+12/-5)
tests/fetch/test_bzrurl.py (+25/-8)
tests/fetch/test_fetch.py (+13/-3)
tests/fetch/test_giturl.py (+24/-8)
tests/helpers.py (+15/-6)
tests/payload/test_archive.py (+1/-1)
tests/payload/test_execd.py (+1/-1)
tests/tools/test_charm_helper_sync.py (+13/-6)
tools/charm_helpers_sync/charm_helpers_sync.py (+5/-4)
To merge this branch: bzr merge lp:~stub/charm-helpers/py3-2
Reviewer Review Type Date Requested Status
Tim Van Steenburgh Approve
Jorge Niedbalski (community) Approve
Jorge Niedbalski Pending
Review via email: mp+242653@code.launchpad.net

Description of the change

Per https://code.launchpad.net/~stub/charm-helpers/py3/+merge/242639,
precise charms attempting to use the Python 3 updated charm-helperers generally will not work, as the version of six they will have installed is very, very old.

This branch backports charm-helpers to use six 1.1, rather than six 1.18.

I believe this version will be good enough until precise charms stop being maintained by maintained, or until the charm-helpers distribution story is improved (if we distribute charm-helpers as a package or similar, we can distribute modern six with it).

An alternative approach I looked at on the previous MP was embedding a copy of six. This worked fine, but would have required people to sync six.py into their charm as well as the rest of charm-helpers.

To post a comment you must log in.
lp:~stub/charm-helpers/py3-2 updated
196. By Stuart Bishop

Bootstrap six on import

197. By Stuart Bishop

Bootstrap yaml package

198. By Stuart Bishop

Fix reliance on implicit casts, such as treating subprocess.check_output result as text instead of bytes

199. By Stuart Bishop

Revert Py3 syntax hidden by mocking

200. By Stuart Bishop

antique six

201. By Stuart Bishop

Need modern distribute under precise

Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

Running make test in a clean precise lxc:

Checking for Python syntax...
Py2 OK
charmhelpers/contrib/templating/contexts.py:99:54: E901 SyntaxError: invalid syntax
tests/contrib/templating/test_contexts.py:137:31: E901 SyntaxError: invalid syntax
tests/core/test_hookenv.py:132:40: E901 SyntaxError: invalid syntax
make: *** [lint] Error 1

Also, running apt-get as an import side-effect makes me grimace.

review: Needs Fixing
lp:~stub/charm-helpers/py3-2 updated
202. By Stuart Bishop

Python3.2 backport for precise

203. By Stuart Bishop

Don't need simplejson

Revision history for this message
Jorge Niedbalski (niedbalski) wrote :

My preliminary review shows everything OK.

- Trusty tests OK [python2.7, python3.4]
   - No import errors

- Precise tests OK [python2.7, python3.2]
   - No import errors

---------------------------------------------------------------------------
TOTAL 3683 236 94%
----------------------------------------------------------------------
Ran 773 tests in 7.484s

OK (SKIP=7)
OK

ubuntu@precise:~/charm-helpers$ sudo python2.7
Python 2.7.3 (default, Feb 27 2014, 19:58:35)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import charmhelpers
>>>

Python 3.2.3 (default, Feb 27 2014, 21:31:18)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import charmhelpers
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  python3-six
0 upgraded, 1 newly installed, 0 to remove and 54 not upgraded.
Need to get 5,894 B of archives.
After this operation, 50.2 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ precise/universe python3-six all 1.1.0-2 [5,894 B]
Fetched 5,894 B in 0s (16.7 kB/s)
Selecting previously unselected package python3-six.
(Reading database ... 58662 files and directories currently installed.)
Unpacking python3-six (from .../python3-six_1.1.0-2_all.deb) ...
Setting up python3-six (1.1.0-2) ...
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  python3-yaml
0 upgraded, 1 newly installed, 0 to remove and 54 not upgraded.
Need to get 111 kB of archives.
After this operation, 442 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ precise/main python3-yaml amd64 3.10-2 [111 kB]
Fetched 111 kB in 0s (120 kB/s)
Selecting previously unselected package python3-yaml.
(Reading database ... 58667 files and directories currently installed.)
Unpacking python3-yaml (from .../python3-yaml_3.10-2_amd64.deb) ...
Setting up python3-yaml (3.10-2) ...
>>>

lp:~stub/charm-helpers/py3-2 updated
204. By Stuart Bishop

merge trunk, revert reversion

Revision history for this message
Jorge Niedbalski (niedbalski) wrote :

After the latest merge, conflicts resolved. LGTM +1

review: Approve
Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

> Running make test in a clean precise lxc:
>
> Checking for Python syntax...
> Py2 OK
> charmhelpers/contrib/templating/contexts.py:99:54: E901 SyntaxError: invalid
> syntax
> tests/contrib/templating/test_contexts.py:137:31: E901 SyntaxError: invalid
> syntax
> tests/core/test_hookenv.py:132:40: E901 SyntaxError: invalid syntax
> make: *** [lint] Error 1
>
>
> Also, running apt-get as an import side-effect makes me grimace.

Retested on precise and tests pass. Sorry for the noise, not sure what happened the first time.

Revision history for this message
Tim Van Steenburgh (tvansteenburgh) wrote :

+1 LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2014-11-25 14:35:22 +0000
+++ .bzrignore 2014-11-25 15:09:14 +0000
@@ -7,3 +7,5 @@
7.env/7.env/
8coverage.xml8coverage.xml
9docs/_build9docs/_build
10.venv
11.venv3
1012
=== modified file 'Makefile'
--- Makefile 2014-11-25 14:35:22 +0000
+++ Makefile 2014-11-25 15:09:14 +0000
@@ -23,11 +23,12 @@
23 python setup.py sdist23 python setup.py sdist
2424
25clean:25clean:
26 python setup.py clean26 -python setup.py clean
27 rm -rf build/ MANIFEST27 rm -rf build/ MANIFEST
28 find . -name '*.pyc' -delete28 find . -name '*.pyc' -delete
29 rm -rf dist/*29 rm -rf dist/*
30 rm -rf .venv30 rm -rf .venv
31 rm -rf .venv3
31 (which dh_clean && dh_clean) || true32 (which dh_clean && dh_clean) || true
3233
33userinstall:34userinstall:
@@ -38,19 +39,41 @@
38 sudo apt-get install -y gcc python-dev python-virtualenv python-apt39 sudo apt-get install -y gcc python-dev python-virtualenv python-apt
39 virtualenv .venv --system-site-packages40 virtualenv .venv --system-site-packages
40 .venv/bin/pip install -U pip41 .venv/bin/pip install -U pip
42 .venv/bin/pip install -U distribute
41 .venv/bin/pip install -I -r test_requirements.txt43 .venv/bin/pip install -I -r test_requirements.txt
4244 .venv/bin/pip install bzr
43test: .venv45 .venv/bin/pip install GitPython
44 @echo Starting tests...46
47.venv3:
48 sudo apt-get install -y gcc python3-dev python-virtualenv python3-apt
49 virtualenv .venv3 --python=python3 --system-site-packages
50 .venv3/bin/pip install -U pip
51 .venv3/bin/pip install -U distribute
52 .venv3/bin/pip install -I -r test_requirements.txt
53
54# Note we don't even attempt to run tests if lint isn't passing.
55test: lint test2 test3
56 @echo OK
57
58test2:
59 @echo Starting Py2 tests...
45 .venv/bin/nosetests -s --nologcapture tests/60 .venv/bin/nosetests -s --nologcapture tests/
4661
47ftest: .venv62test3:
63 @echo Starting Py3 tests...
64 .venv3/bin/nosetests -s --nologcapture tests/
65
66ftest: lint
48 @echo Starting fast tests...67 @echo Starting fast tests...
49 .venv/bin/nosetests --attr '!slow' --nologcapture tests/68 .venv/bin/nosetests --attr '!slow' --nologcapture tests/
69 .venv3/bin/nosetests --attr '!slow' --nologcapture tests/
5070
51lint:71lint: .venv .venv3
52 @echo Checking for Python syntax...72 @echo Checking for Python syntax...
53 @flake8 --ignore=E123,E501 $(PROJECT) $(TESTS) && echo OK73 @.venv/bin/flake8 --ignore=E501 $(PROJECT) $(TESTS) tools/ \
74 && echo Py2 OK
75 @.venv3/bin/flake8 --ignore=E501 $(PROJECT) $(TESTS) tools/ \
76 && echo Py3 OK
5477
55docs:78docs:
56 - [ -z "`dpkg -l | grep python-sphinx`" ] && sudo apt-get install python-sphinx -y79 - [ -z "`dpkg -l | grep python-sphinx`" ] && sudo apt-get install python-sphinx -y
5780
=== modified file 'charmhelpers/__init__.py'
--- charmhelpers/__init__.py 2013-05-11 19:55:58 +0000
+++ charmhelpers/__init__.py 2014-11-25 15:09:14 +0000
@@ -0,0 +1,22 @@
1# Bootstrap charm-helpers, installing its dependencies if necessary using
2# only standard libraries.
3import subprocess
4import sys
5
6try:
7 import six # flake8: noqa
8except ImportError:
9 if sys.version_info.major == 2:
10 subprocess.check_call(['apt-get', 'install', '-y', 'python-six'])
11 else:
12 subprocess.check_call(['apt-get', 'install', '-y', 'python3-six'])
13 import six # flake8: noqa
14
15try:
16 import yaml # flake8: noqa
17except ImportError:
18 if sys.version_info.major == 2:
19 subprocess.check_call(['apt-get', 'install', '-y', 'python-yaml'])
20 else:
21 subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml'])
22 import yaml # flake8: noqa
023
=== modified file 'charmhelpers/cli/__init__.py'
--- charmhelpers/cli/__init__.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/cli/__init__.py 2014-11-25 15:09:14 +0000
@@ -1,8 +1,9 @@
1import inspect1import inspect
2import itertools
3import argparse2import argparse
4import sys3import sys
54
5from six.moves import zip
6
67
7class OutputFormatter(object):8class OutputFormatter(object):
8 def __init__(self, outfile=sys.stdout):9 def __init__(self, outfile=sys.stdout):
@@ -136,7 +137,7 @@
136 if argspec.defaults:137 if argspec.defaults:
137 positional_args = argspec.args[:-len(argspec.defaults)]138 positional_args = argspec.args[:-len(argspec.defaults)]
138 keyword_names = argspec.args[-len(argspec.defaults):]139 keyword_names = argspec.args[-len(argspec.defaults):]
139 for arg, default in itertools.izip(keyword_names, argspec.defaults):140 for arg, default in zip(keyword_names, argspec.defaults):
140 yield ('--{}'.format(arg),), {'default': default}141 yield ('--{}'.format(arg),), {'default': default}
141 else:142 else:
142 positional_args = argspec.args143 positional_args = argspec.args
143144
=== modified file 'charmhelpers/contrib/amulet/deployment.py'
--- charmhelpers/contrib/amulet/deployment.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/amulet/deployment.py 2014-11-25 15:09:14 +0000
@@ -1,6 +1,6 @@
1import amulet1import amulet
2
3import os2import os
3import six
44
55
6class AmuletDeployment(object):6class AmuletDeployment(object):
@@ -52,12 +52,12 @@
5252
53 def _add_relations(self, relations):53 def _add_relations(self, relations):
54 """Add all of the relations for the services."""54 """Add all of the relations for the services."""
55 for k, v in relations.iteritems():55 for k, v in six.iteritems(relations):
56 self.d.relate(k, v)56 self.d.relate(k, v)
5757
58 def _configure_services(self, configs):58 def _configure_services(self, configs):
59 """Configure all of the services."""59 """Configure all of the services."""
60 for service, config in configs.iteritems():60 for service, config in six.iteritems(configs):
61 self.d.configure(service, config)61 self.d.configure(service, config)
6262
63 def _deploy(self):63 def _deploy(self):
6464
=== modified file 'charmhelpers/contrib/amulet/utils.py'
--- charmhelpers/contrib/amulet/utils.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/amulet/utils.py 2014-11-25 15:09:14 +0000
@@ -5,6 +5,8 @@
5import sys5import sys
6import time6import time
77
8import six
9
810
9class AmuletUtils(object):11class AmuletUtils(object):
10 """Amulet utilities.12 """Amulet utilities.
@@ -58,7 +60,7 @@
58 Verify the specified services are running on the corresponding60 Verify the specified services are running on the corresponding
59 service units.61 service units.
60 """62 """
61 for k, v in commands.iteritems():63 for k, v in six.iteritems(commands):
62 for cmd in v:64 for cmd in v:
63 output, code = k.run(cmd)65 output, code = k.run(cmd)
64 if code != 0:66 if code != 0:
@@ -100,11 +102,11 @@
100 longs, or can be a function that evaluate a variable and returns a102 longs, or can be a function that evaluate a variable and returns a
101 bool.103 bool.
102 """104 """
103 for k, v in expected.iteritems():105 for k, v in six.iteritems(expected):
104 if k in actual:106 if k in actual:
105 if (isinstance(v, basestring) or107 if (isinstance(v, six.string_types) or
106 isinstance(v, bool) or108 isinstance(v, bool) or
107 isinstance(v, (int, long))):109 isinstance(v, six.integer_types)):
108 if v != actual[k]:110 if v != actual[k]:
109 return "{}:{}".format(k, actual[k])111 return "{}:{}".format(k, actual[k])
110 elif not v(actual[k]):112 elif not v(actual[k]):
111113
=== modified file 'charmhelpers/contrib/charmhelpers/__init__.py'
--- charmhelpers/contrib/charmhelpers/__init__.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/charmhelpers/__init__.py 2014-11-25 15:09:14 +0000
@@ -8,19 +8,19 @@
88
9__metaclass__ = type9__metaclass__ = type
10__all__ = [10__all__ = [
11 #'get_config', # core.hookenv.config()11 # 'get_config', # core.hookenv.config()
12 #'log', # core.hookenv.log()12 # 'log', # core.hookenv.log()
13 #'log_entry', # core.hookenv.log()13 # 'log_entry', # core.hookenv.log()
14 #'log_exit', # core.hookenv.log()14 # 'log_exit', # core.hookenv.log()
15 #'relation_get', # core.hookenv.relation_get()15 # 'relation_get', # core.hookenv.relation_get()
16 #'relation_set', # core.hookenv.relation_set()16 # 'relation_set', # core.hookenv.relation_set()
17 #'relation_ids', # core.hookenv.relation_ids()17 # 'relation_ids', # core.hookenv.relation_ids()
18 #'relation_list', # core.hookenv.relation_units()18 # 'relation_list', # core.hookenv.relation_units()
19 #'config_get', # core.hookenv.config()19 # 'config_get', # core.hookenv.config()
20 #'unit_get', # core.hookenv.unit_get()20 # 'unit_get', # core.hookenv.unit_get()
21 #'open_port', # core.hookenv.open_port()21 # 'open_port', # core.hookenv.open_port()
22 #'close_port', # core.hookenv.close_port()22 # 'close_port', # core.hookenv.close_port()
23 #'service_control', # core.host.service()23 # 'service_control', # core.host.service()
24 'unit_info', # client-side, NOT IMPLEMENTED24 'unit_info', # client-side, NOT IMPLEMENTED
25 'wait_for_machine', # client-side, NOT IMPLEMENTED25 'wait_for_machine', # client-side, NOT IMPLEMENTED
26 'wait_for_page_contents', # client-side, NOT IMPLEMENTED26 'wait_for_page_contents', # client-side, NOT IMPLEMENTED
@@ -31,17 +31,24 @@
31import operator31import operator
32import tempfile32import tempfile
33import time33import time
34import urllib2
35import yaml34import yaml
36import subprocess35import subprocess
3736
37import six
38if six.PY3:
39 from urllib.request import urlopen
40 from urllib.error import (HTTPError, URLError)
41else:
42 from urllib2 import (urlopen, HTTPError, URLError)
43
44
38SLEEP_AMOUNT = 0.145SLEEP_AMOUNT = 0.1
39# We create a juju_status Command here because it makes testing much,46# We create a juju_status Command here because it makes testing much,
40# much easier.47# much easier.
41juju_status = lambda: subprocess.check_call(['juju', 'status'])48juju_status = lambda: subprocess.check_call(['juju', 'status'])
4249
43# re-implemented as charmhelpers.fetch.configure_sources()50# re-implemented as charmhelpers.fetch.configure_sources()
44#def configure_source(update=False):51# def configure_source(update=False):
45# source = config_get('source')52# source = config_get('source')
46# if ((source.startswith('ppa:') or53# if ((source.startswith('ppa:') or
47# source.startswith('cloud:') or54# source.startswith('cloud:') or
@@ -55,7 +62,7 @@
5562
56# DEPRECATED: client-side only63# DEPRECATED: client-side only
57def make_charm_config_file(charm_config):64def make_charm_config_file(charm_config):
58 charm_config_file = tempfile.NamedTemporaryFile()65 charm_config_file = tempfile.NamedTemporaryFile(mode='w+')
59 charm_config_file.write(yaml.dump(charm_config))66 charm_config_file.write(yaml.dump(charm_config))
60 charm_config_file.flush()67 charm_config_file.flush()
61 # The NamedTemporaryFile instance is returned instead of just the name68 # The NamedTemporaryFile instance is returned instead of just the name
@@ -119,7 +126,7 @@
119 # we're in LXC.126 # we're in LXC.
120 machine_data = get_machine_data()127 machine_data = get_machine_data()
121 non_zookeeper_machines = [128 non_zookeeper_machines = [
122 machine_data[key] for key in machine_data.keys()[1:]]129 machine_data[key] for key in list(machine_data.keys())[1:]]
123 if len(non_zookeeper_machines) >= num_machines:130 if len(non_zookeeper_machines) >= num_machines:
124 all_machines_running = True131 all_machines_running = True
125 for machine in non_zookeeper_machines:132 for machine in non_zookeeper_machines:
@@ -170,8 +177,8 @@
170 start_time = time.time()177 start_time = time.time()
171 while True:178 while True:
172 try:179 try:
173 stream = urllib2.urlopen(url)180 stream = urlopen(url)
174 except (urllib2.HTTPError, urllib2.URLError):181 except (HTTPError, URLError):
175 pass182 pass
176 else:183 else:
177 page = stream.read()184 page = stream.read()
178185
=== modified file 'charmhelpers/contrib/hahelpers/cluster.py'
--- charmhelpers/contrib/hahelpers/cluster.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/hahelpers/cluster.py 2014-11-25 15:09:14 +0000
@@ -13,9 +13,10 @@
1313
14import subprocess14import subprocess
15import os15import os
16
17from socket import gethostname as get_unit_hostname16from socket import gethostname as get_unit_hostname
1817
18import six
19
19from charmhelpers.core.hookenv import (20from charmhelpers.core.hookenv import (
20 log,21 log,
21 relation_ids,22 relation_ids,
@@ -77,7 +78,7 @@
77 "show", resource78 "show", resource
78 ]79 ]
79 try:80 try:
80 status = subprocess.check_output(cmd)81 status = subprocess.check_output(cmd).decode('UTF-8')
81 except subprocess.CalledProcessError:82 except subprocess.CalledProcessError:
82 return False83 return False
83 else:84 else:
@@ -197,7 +198,7 @@
197 for setting in settings:198 for setting in settings:
198 conf[setting] = config_get(setting)199 conf[setting] = config_get(setting)
199 missing = []200 missing = []
200 [missing.append(s) for s, v in conf.iteritems() if v is None]201 [missing.append(s) for s, v in six.iteritems(conf) if v is None]
201 if missing:202 if missing:
202 log('Insufficient config data to configure hacluster.', level=ERROR)203 log('Insufficient config data to configure hacluster.', level=ERROR)
203 raise HAIncompleteConfig204 raise HAIncompleteConfig
204205
=== modified file 'charmhelpers/contrib/network/ip.py'
--- charmhelpers/contrib/network/ip.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/network/ip.py 2014-11-25 15:09:14 +0000
@@ -228,7 +228,7 @@
228 raise Exception("Interface '%s' doesn't have any %s addresses." %228 raise Exception("Interface '%s' doesn't have any %s addresses." %
229 (iface, inet_type))229 (iface, inet_type))
230230
231 return addresses231 return sorted(addresses)
232232
233233
234get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET')234get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET')
@@ -302,7 +302,7 @@
302 if global_addrs:302 if global_addrs:
303 # Make sure any found global addresses are not temporary303 # Make sure any found global addresses are not temporary
304 cmd = ['ip', 'addr', 'show', iface]304 cmd = ['ip', 'addr', 'show', iface]
305 out = subprocess.check_output(cmd)305 out = subprocess.check_output(cmd).decode('UTF-8')
306 if dynamic_only:306 if dynamic_only:
307 key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")307 key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")
308 else:308 else:
309309
=== modified file 'charmhelpers/contrib/openstack/amulet/deployment.py'
--- charmhelpers/contrib/openstack/amulet/deployment.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/amulet/deployment.py 2014-11-25 15:09:14 +0000
@@ -1,3 +1,4 @@
1import six
1from charmhelpers.contrib.amulet.deployment import (2from charmhelpers.contrib.amulet.deployment import (
2 AmuletDeployment3 AmuletDeployment
3)4)
@@ -69,7 +70,7 @@
6970
70 def _configure_services(self, configs):71 def _configure_services(self, configs):
71 """Configure all of the services."""72 """Configure all of the services."""
72 for service, config in configs.iteritems():73 for service, config in six.iteritems(configs):
73 self.d.configure(service, config)74 self.d.configure(service, config)
7475
75 def _get_openstack_release(self):76 def _get_openstack_release(self):
7677
=== modified file 'charmhelpers/contrib/openstack/amulet/utils.py'
--- charmhelpers/contrib/openstack/amulet/utils.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/amulet/utils.py 2014-11-25 15:09:14 +0000
@@ -7,6 +7,8 @@
7import keystoneclient.v2_0 as keystone_client7import keystoneclient.v2_0 as keystone_client
8import novaclient.v1_1.client as nova_client8import novaclient.v1_1.client as nova_client
99
10import six
11
10from charmhelpers.contrib.amulet.utils import (12from charmhelpers.contrib.amulet.utils import (
11 AmuletUtils13 AmuletUtils
12)14)
@@ -60,7 +62,7 @@
60 expected service catalog endpoints.62 expected service catalog endpoints.
61 """63 """
62 self.log.debug('actual: {}'.format(repr(actual)))64 self.log.debug('actual: {}'.format(repr(actual)))
63 for k, v in expected.iteritems():65 for k, v in six.iteritems(expected):
64 if k in actual:66 if k in actual:
65 ret = self._validate_dict_data(expected[k][0], actual[k][0])67 ret = self._validate_dict_data(expected[k][0], actual[k][0])
66 if ret:68 if ret:
6769
=== modified file 'charmhelpers/contrib/openstack/context.py'
--- charmhelpers/contrib/openstack/context.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/context.py 2014-11-25 15:09:14 +0000
@@ -1,10 +1,11 @@
1import json1import json
2import os2import os
3import time3import time
4
5from base64 import b64decode4from base64 import b64decode
6from subprocess import check_call5from subprocess import check_call
76
7import six
8
8from charmhelpers.fetch import (9from charmhelpers.fetch import (
9 apt_install,10 apt_install,
10 filter_installed_packages,11 filter_installed_packages,
@@ -69,7 +70,7 @@
6970
70def context_complete(ctxt):71def context_complete(ctxt):
71 _missing = []72 _missing = []
72 for k, v in ctxt.iteritems():73 for k, v in six.iteritems(ctxt):
73 if v is None or v == '':74 if v is None or v == '':
74 _missing.append(k)75 _missing.append(k)
7576
@@ -97,7 +98,7 @@
97 split = config_flags.strip(' =').split('=')98 split = config_flags.strip(' =').split('=')
98 limit = len(split)99 limit = len(split)
99 flags = {}100 flags = {}
100 for i in xrange(0, limit - 1):101 for i in range(0, limit - 1):
101 current = split[i]102 current = split[i]
102 next = split[i + 1]103 next = split[i + 1]
103 vindex = next.rfind(',')104 vindex = next.rfind(',')
@@ -375,7 +376,7 @@
375 host = format_ipv6_addr(host) or host376 host = format_ipv6_addr(host) or host
376 rabbitmq_hosts.append(host)377 rabbitmq_hosts.append(host)
377378
378 ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts)379 ctxt['rabbitmq_hosts'] = ','.join(sorted(rabbitmq_hosts))
379380
380 if not context_complete(ctxt):381 if not context_complete(ctxt):
381 return {}382 return {}
@@ -408,7 +409,7 @@
408 ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr409 ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr
409 mon_hosts.append(ceph_addr)410 mon_hosts.append(ceph_addr)
410411
411 ctxt = {'mon_hosts': ' '.join(mon_hosts),412 ctxt = {'mon_hosts': ' '.join(sorted(mon_hosts)),
412 'auth': auth,413 'auth': auth,
413 'key': key,414 'key': key,
414 'use_syslog': use_syslog}415 'use_syslog': use_syslog}
@@ -587,7 +588,7 @@
587 if k.startswith('ssl_key_'):588 if k.startswith('ssl_key_'):
588 cns.append(k.lstrip('ssl_key_'))589 cns.append(k.lstrip('ssl_key_'))
589590
590 return list(set(cns))591 return sorted(list(set(cns)))
591592
592 def get_network_addresses(self):593 def get_network_addresses(self):
593 """For each network configured, return corresponding address and vip594 """For each network configured, return corresponding address and vip
@@ -631,10 +632,10 @@
631 else:632 else:
632 addresses.append((addr, addr))633 addresses.append((addr, addr))
633634
634 return addresses635 return sorted(addresses)
635636
636 def __call__(self):637 def __call__(self):
637 if isinstance(self.external_ports, basestring):638 if isinstance(self.external_ports, six.string_types):
638 self.external_ports = [self.external_ports]639 self.external_ports = [self.external_ports]
639640
640 if not self.external_ports or not https():641 if not self.external_ports or not https():
@@ -651,7 +652,7 @@
651 self.configure_cert(cn)652 self.configure_cert(cn)
652653
653 addresses = self.get_network_addresses()654 addresses = self.get_network_addresses()
654 for address, endpoint in set(addresses):655 for address, endpoint in sorted(set(addresses)):
655 for api_port in self.external_ports:656 for api_port in self.external_ports:
656 ext_port = determine_apache_port(api_port)657 ext_port = determine_apache_port(api_port)
657 int_port = determine_api_port(api_port)658 int_port = determine_api_port(api_port)
@@ -659,7 +660,7 @@
659 ctxt['endpoints'].append(portmap)660 ctxt['endpoints'].append(portmap)
660 ctxt['ext_ports'].append(int(ext_port))661 ctxt['ext_ports'].append(int(ext_port))
661662
662 ctxt['ext_ports'] = list(set(ctxt['ext_ports']))663 ctxt['ext_ports'] = sorted(list(set(ctxt['ext_ports'])))
663 return ctxt664 return ctxt
664665
665666
@@ -921,10 +922,10 @@
921 continue922 continue
922923
923 sub_config = sub_config[self.config_file]924 sub_config = sub_config[self.config_file]
924 for k, v in sub_config.iteritems():925 for k, v in six.iteritems(sub_config):
925 if k == 'sections':926 if k == 'sections':
926 for section, config_dict in v.iteritems():927 for section, config_dict in six.iteritems(v):
927 log("Adding section '%s'" % (section),928 log("adding section '%s'" % (section),
928 level=DEBUG)929 level=DEBUG)
929 ctxt[k][section] = config_dict930 ctxt[k][section] = config_dict
930 else:931 else:
931932
=== modified file 'charmhelpers/contrib/openstack/neutron.py'
--- charmhelpers/contrib/openstack/neutron.py 2014-11-17 15:15:52 +0000
+++ charmhelpers/contrib/openstack/neutron.py 2014-11-25 15:09:14 +0000
@@ -14,7 +14,7 @@
14def headers_package():14def headers_package():
15 """Ensures correct linux-headers for running kernel are installed,15 """Ensures correct linux-headers for running kernel are installed,
16 for building DKMS package"""16 for building DKMS package"""
17 kver = check_output(['uname', '-r']).strip()17 kver = check_output(['uname', '-r']).decode('UTF-8').strip()
18 return 'linux-headers-%s' % kver18 return 'linux-headers-%s' % kver
1919
20QUANTUM_CONF_DIR = '/etc/quantum'20QUANTUM_CONF_DIR = '/etc/quantum'
@@ -22,7 +22,7 @@
2222
23def kernel_version():23def kernel_version():
24 """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """24 """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """
25 kver = check_output(['uname', '-r']).strip()25 kver = check_output(['uname', '-r']).decode('UTF-8').strip()
26 kver = kver.split('.')26 kver = kver.split('.')
27 return (int(kver[0]), int(kver[1]))27 return (int(kver[0]), int(kver[1]))
2828
2929
=== modified file 'charmhelpers/contrib/openstack/templates/haproxy.cfg'
--- charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-11-25 15:09:14 +0000
@@ -35,7 +35,7 @@
35 stats auth admin:password35 stats auth admin:password
3636
37{% if frontends -%}37{% if frontends -%}
38{% for service, ports in service_ports.iteritems() -%}38{% for service, ports in service_ports.items() -%}
39frontend tcp-in_{{ service }}39frontend tcp-in_{{ service }}
40 bind *:{{ ports[0] }}40 bind *:{{ ports[0] }}
41 bind :::{{ ports[0] }}41 bind :::{{ ports[0] }}
@@ -46,7 +46,7 @@
46{% for frontend in frontends -%}46{% for frontend in frontends -%}
47backend {{ service }}_{{ frontend }}47backend {{ service }}_{{ frontend }}
48 balance leastconn48 balance leastconn
49 {% for unit, address in frontends[frontend]['backends'].iteritems() -%}49 {% for unit, address in frontends[frontend]['backends'].items() -%}
50 server {{ unit }} {{ address }}:{{ ports[1] }} check50 server {{ unit }} {{ address }}:{{ ports[1] }} check
51 {% endfor %}51 {% endfor %}
52{% endfor -%}52{% endfor -%}
5353
=== modified file 'charmhelpers/contrib/openstack/templating.py'
--- charmhelpers/contrib/openstack/templating.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/templating.py 2014-11-25 15:09:14 +0000
@@ -1,13 +1,13 @@
1import os1import os
22
3import six
4
3from charmhelpers.fetch import apt_install5from charmhelpers.fetch import apt_install
4
5from charmhelpers.core.hookenv import (6from charmhelpers.core.hookenv import (
6 log,7 log,
7 ERROR,8 ERROR,
8 INFO9 INFO
9)10)
10
11from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES11from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES
1212
13try:13try:
@@ -43,7 +43,7 @@
43 order by OpenStack release.43 order by OpenStack release.
44 """44 """
45 tmpl_dirs = [(rel, os.path.join(templates_dir, rel))45 tmpl_dirs = [(rel, os.path.join(templates_dir, rel))
46 for rel in OPENSTACK_CODENAMES.itervalues()]46 for rel in six.itervalues(OPENSTACK_CODENAMES)]
4747
48 if not os.path.isdir(templates_dir):48 if not os.path.isdir(templates_dir):
49 log('Templates directory not found @ %s.' % templates_dir,49 log('Templates directory not found @ %s.' % templates_dir,
@@ -258,7 +258,7 @@
258 """258 """
259 Write out all registered config files.259 Write out all registered config files.
260 """260 """
261 [self.write(k) for k in self.templates.iterkeys()]261 [self.write(k) for k in six.iterkeys(self.templates)]
262262
263 def set_release(self, openstack_release):263 def set_release(self, openstack_release):
264 """264 """
@@ -275,5 +275,5 @@
275 '''275 '''
276 interfaces = []276 interfaces = []
277 [interfaces.extend(i.complete_contexts())277 [interfaces.extend(i.complete_contexts())
278 for i in self.templates.itervalues()]278 for i in six.itervalues(self.templates)]
279 return interfaces279 return interfaces
280280
=== modified file 'charmhelpers/contrib/openstack/utils.py'
--- charmhelpers/contrib/openstack/utils.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/openstack/utils.py 2014-11-25 15:09:14 +0000
@@ -10,6 +10,8 @@
10import socket10import socket
11import sys11import sys
1212
13import six
14
13from charmhelpers.core.hookenv import (15from charmhelpers.core.hookenv import (
14 config,16 config,
15 log as juju_log,17 log as juju_log,
@@ -113,7 +115,7 @@
113115
114 # Best guess match based on deb string provided116 # Best guess match based on deb string provided
115 if src.startswith('deb') or src.startswith('ppa'):117 if src.startswith('deb') or src.startswith('ppa'):
116 for k, v in OPENSTACK_CODENAMES.iteritems():118 for k, v in six.iteritems(OPENSTACK_CODENAMES):
117 if v in src:119 if v in src:
118 return v120 return v
119121
@@ -134,7 +136,7 @@
134136
135def get_os_version_codename(codename):137def get_os_version_codename(codename):
136 '''Determine OpenStack version number from codename.'''138 '''Determine OpenStack version number from codename.'''
137 for k, v in OPENSTACK_CODENAMES.iteritems():139 for k, v in six.iteritems(OPENSTACK_CODENAMES):
138 if v == codename:140 if v == codename:
139 return k141 return k
140 e = 'Could not derive OpenStack version for '\142 e = 'Could not derive OpenStack version for '\
@@ -194,7 +196,7 @@
194 else:196 else:
195 vers_map = OPENSTACK_CODENAMES197 vers_map = OPENSTACK_CODENAMES
196198
197 for version, cname in vers_map.iteritems():199 for version, cname in six.iteritems(vers_map):
198 if cname == codename:200 if cname == codename:
199 return version201 return version
200 # e = "Could not determine OpenStack version for package: %s" % pkg202 # e = "Could not determine OpenStack version for package: %s" % pkg
@@ -318,7 +320,7 @@
318 rc_script.write(320 rc_script.write(
319 "#!/bin/bash\n")321 "#!/bin/bash\n")
320 [rc_script.write('export %s=%s\n' % (u, p))322 [rc_script.write('export %s=%s\n' % (u, p))
321 for u, p in env_vars.iteritems() if u != "script_path"]323 for u, p in six.iteritems(env_vars) if u != "script_path"]
322324
323325
324def openstack_upgrade_available(package):326def openstack_upgrade_available(package):
@@ -418,7 +420,7 @@
418420
419 if isinstance(address, dns.name.Name):421 if isinstance(address, dns.name.Name):
420 rtype = 'PTR'422 rtype = 'PTR'
421 elif isinstance(address, basestring):423 elif isinstance(address, six.string_types):
422 rtype = 'A'424 rtype = 'A'
423 else:425 else:
424 return None426 return None
@@ -486,8 +488,7 @@
486 'hostname': json.dumps(hosts)}488 'hostname': json.dumps(hosts)}
487489
488 if relation_prefix:490 if relation_prefix:
489 keys = kwargs.keys()491 for key in list(kwargs.keys()):
490 for key in keys:
491 kwargs["%s_%s" % (relation_prefix, key)] = kwargs[key]492 kwargs["%s_%s" % (relation_prefix, key)] = kwargs[key]
492 del kwargs[key]493 del kwargs[key]
493494
494495
=== modified file 'charmhelpers/contrib/peerstorage/__init__.py'
--- charmhelpers/contrib/peerstorage/__init__.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/peerstorage/__init__.py 2014-11-25 15:09:14 +0000
@@ -1,3 +1,4 @@
1import six
1from charmhelpers.core.hookenv import relation_id as current_relation_id2from charmhelpers.core.hookenv import relation_id as current_relation_id
2from charmhelpers.core.hookenv import (3from charmhelpers.core.hookenv import (
3 is_relation_made,4 is_relation_made,
@@ -93,7 +94,7 @@
93 if ex in echo_data:94 if ex in echo_data:
94 echo_data.pop(ex)95 echo_data.pop(ex)
95 else:96 else:
96 for attribute, value in rdata.iteritems():97 for attribute, value in six.iteritems(rdata):
97 for include in includes:98 for include in includes:
98 if include in attribute:99 if include in attribute:
99 echo_data[attribute] = value100 echo_data[attribute] = value
@@ -119,8 +120,8 @@
119 relation_settings=relation_settings,120 relation_settings=relation_settings,
120 **kwargs)121 **kwargs)
121 if is_relation_made(peer_relation_name):122 if is_relation_made(peer_relation_name):
122 for key, value in dict(kwargs.items() +123 for key, value in six.iteritems(dict(list(kwargs.items()) +
123 relation_settings.items()).iteritems():124 list(relation_settings.items()))):
124 key_prefix = relation_id or current_relation_id()125 key_prefix = relation_id or current_relation_id()
125 peer_store(key_prefix + delimiter + key,126 peer_store(key_prefix + delimiter + key,
126 value,127 value,
127128
=== modified file 'charmhelpers/contrib/python/packages.py'
--- charmhelpers/contrib/python/packages.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/python/packages.py 2014-11-25 15:09:14 +0000
@@ -15,7 +15,7 @@
1515
16def parse_options(given, available):16def parse_options(given, available):
17 """Given a set of options, check if available"""17 """Given a set of options, check if available"""
18 for key, value in given.items():18 for key, value in sorted(given.items()):
19 if key in available:19 if key in available:
20 yield "--{0}={1}".format(key, value)20 yield "--{0}={1}".format(key, value)
2121
2222
=== modified file 'charmhelpers/contrib/ssl/__init__.py'
--- charmhelpers/contrib/ssl/__init__.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/ssl/__init__.py 2014-11-25 15:09:14 +0000
@@ -74,5 +74,5 @@
74 subprocess.check_call(cmd)74 subprocess.check_call(cmd)
75 return True75 return True
76 except Exception as e:76 except Exception as e:
77 print "Execution of openssl command failed:\n{}".format(e)77 print("Execution of openssl command failed:\n{}".format(e))
78 return False78 return False
7979
=== modified file 'charmhelpers/contrib/storage/linux/ceph.py'
--- charmhelpers/contrib/storage/linux/ceph.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/storage/linux/ceph.py 2014-11-25 15:09:14 +0000
@@ -65,7 +65,8 @@
65def rbd_exists(service, pool, rbd_img):65def rbd_exists(service, pool, rbd_img):
66 """Check to see if a RADOS block device exists."""66 """Check to see if a RADOS block device exists."""
67 try:67 try:
68 out = check_output(['rbd', 'list', '--id', service, '--pool', pool])68 out = check_output(['rbd', 'list', '--id',
69 service, '--pool', pool]).decode('UTF-8')
69 except CalledProcessError:70 except CalledProcessError:
70 return False71 return False
7172
@@ -82,7 +83,8 @@
82def pool_exists(service, name):83def pool_exists(service, name):
83 """Check to see if a RADOS pool already exists."""84 """Check to see if a RADOS pool already exists."""
84 try:85 try:
85 out = check_output(['rados', '--id', service, 'lspools'])86 out = check_output(['rados', '--id', service,
87 'lspools']).decode('UTF-8')
86 except CalledProcessError:88 except CalledProcessError:
87 return False89 return False
8890
@@ -96,7 +98,8 @@
96 version = ceph_version()98 version = ceph_version()
97 if version and version >= '0.56':99 if version and version >= '0.56':
98 return json.loads(check_output(['ceph', '--id', service,100 return json.loads(check_output(['ceph', '--id', service,
99 'osd', 'ls', '--format=json']))101 'osd', 'ls',
102 '--format=json']).decode('UTF-8'))
100103
101 return None104 return None
102105
@@ -112,7 +115,7 @@
112 # on upstream recommended best practices.115 # on upstream recommended best practices.
113 osds = get_osds(service)116 osds = get_osds(service)
114 if osds:117 if osds:
115 pgnum = (len(osds) * 100 / replicas)118 pgnum = (len(osds) * 100 // replicas)
116 else:119 else:
117 # NOTE(james-page): Default to 200 for older ceph versions120 # NOTE(james-page): Default to 200 for older ceph versions
118 # which don't support OSD query from cli121 # which don't support OSD query from cli
@@ -193,7 +196,7 @@
193def image_mapped(name):196def image_mapped(name):
194 """Determine whether a RADOS block device is mapped locally."""197 """Determine whether a RADOS block device is mapped locally."""
195 try:198 try:
196 out = check_output(['rbd', 'showmapped'])199 out = check_output(['rbd', 'showmapped']).decode('UTF-8')
197 except CalledProcessError:200 except CalledProcessError:
198 return False201 return False
199202
@@ -361,7 +364,7 @@
361 """Retrieve the local version of ceph."""364 """Retrieve the local version of ceph."""
362 if os.path.exists('/usr/bin/ceph'):365 if os.path.exists('/usr/bin/ceph'):
363 cmd = ['ceph', '-v']366 cmd = ['ceph', '-v']
364 output = check_output(cmd)367 output = check_output(cmd).decode('US-ASCII')
365 output = output.split()368 output = output.split()
366 if len(output) > 3:369 if len(output) > 3:
367 return output[2]370 return output[2]
368371
=== modified file 'charmhelpers/contrib/storage/linux/loopback.py'
--- charmhelpers/contrib/storage/linux/loopback.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/storage/linux/loopback.py 2014-11-25 15:09:14 +0000
@@ -1,12 +1,12 @@
1
2import os1import os
3import re2import re
4
5from subprocess import (3from subprocess import (
6 check_call,4 check_call,
7 check_output,5 check_output,
8)6)
97
8import six
9
1010
11##################################################11##################################################
12# loopback device helpers.12# loopback device helpers.
@@ -37,7 +37,7 @@
37 '''37 '''
38 file_path = os.path.abspath(file_path)38 file_path = os.path.abspath(file_path)
39 check_call(['losetup', '--find', file_path])39 check_call(['losetup', '--find', file_path])
40 for d, f in loopback_devices().iteritems():40 for d, f in six.iteritems(loopback_devices()):
41 if f == file_path:41 if f == file_path:
42 return d42 return d
4343
@@ -51,7 +51,7 @@
5151
52 :returns: str: Full path to the ensured loopback device (eg, /dev/loop0)52 :returns: str: Full path to the ensured loopback device (eg, /dev/loop0)
53 '''53 '''
54 for d, f in loopback_devices().iteritems():54 for d, f in six.iteritems(loopback_devices()):
55 if f == path:55 if f == path:
56 return d56 return d
5757
5858
=== modified file 'charmhelpers/contrib/storage/linux/lvm.py'
--- charmhelpers/contrib/storage/linux/lvm.py 2014-05-10 19:58:31 +0000
+++ charmhelpers/contrib/storage/linux/lvm.py 2014-11-25 15:09:14 +0000
@@ -61,6 +61,7 @@
61 vg = None61 vg = None
62 pvd = check_output(['pvdisplay', block_device]).splitlines()62 pvd = check_output(['pvdisplay', block_device]).splitlines()
63 for l in pvd:63 for l in pvd:
64 l = l.decode('UTF-8')
64 if l.strip().startswith('VG Name'):65 if l.strip().startswith('VG Name'):
65 vg = ' '.join(l.strip().split()[2:])66 vg = ' '.join(l.strip().split()[2:])
66 return vg67 return vg
6768
=== modified file 'charmhelpers/contrib/storage/linux/utils.py'
--- charmhelpers/contrib/storage/linux/utils.py 2014-07-31 08:17:42 +0000
+++ charmhelpers/contrib/storage/linux/utils.py 2014-11-25 15:09:14 +0000
@@ -30,7 +30,8 @@
30 # sometimes sgdisk exits non-zero; this is OK, dd will clean up30 # sometimes sgdisk exits non-zero; this is OK, dd will clean up
31 call(['sgdisk', '--zap-all', '--mbrtogpt',31 call(['sgdisk', '--zap-all', '--mbrtogpt',
32 '--clear', block_device])32 '--clear', block_device])
33 dev_end = check_output(['blockdev', '--getsz', block_device])33 dev_end = check_output(['blockdev', '--getsz',
34 block_device]).decode('UTF-8')
34 gpt_end = int(dev_end.split()[0]) - 10035 gpt_end = int(dev_end.split()[0]) - 100
35 check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device),36 check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device),
36 'bs=1M', 'count=1'])37 'bs=1M', 'count=1'])
@@ -47,7 +48,7 @@
47 it doesn't.48 it doesn't.
48 '''49 '''
49 is_partition = bool(re.search(r".*[0-9]+\b", device))50 is_partition = bool(re.search(r".*[0-9]+\b", device))
50 out = check_output(['mount'])51 out = check_output(['mount']).decode('UTF-8')
51 if is_partition:52 if is_partition:
52 return bool(re.search(device + r"\b", out))53 return bool(re.search(device + r"\b", out))
53 return bool(re.search(device + r"[0-9]+\b", out))54 return bool(re.search(device + r"[0-9]+\b", out))
5455
=== modified file 'charmhelpers/contrib/templating/contexts.py'
--- charmhelpers/contrib/templating/contexts.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/contrib/templating/contexts.py 2014-11-25 15:09:14 +0000
@@ -6,6 +6,8 @@
6import os6import os
7import yaml7import yaml
88
9import six
10
9import charmhelpers.core.hookenv11import charmhelpers.core.hookenv
1012
1113
@@ -92,9 +94,9 @@
9294
93 # Don't use non-standard tags for unicode which will not95 # Don't use non-standard tags for unicode which will not
94 # work when salt uses yaml.load_safe.96 # work when salt uses yaml.load_safe.
95 yaml.add_representer(unicode, lambda dumper,97 yaml.add_representer(six.text_type,
96 value: dumper.represent_scalar(98 lambda dumper, value: dumper.represent_scalar(
97 u'tag:yaml.org,2002:str', value))99 six.u('tag:yaml.org,2002:str'), value))
98100
99 yaml_dir = os.path.dirname(yaml_path)101 yaml_dir = os.path.dirname(yaml_path)
100 if not os.path.exists(yaml_dir):102 if not os.path.exists(yaml_dir):
101103
=== modified file 'charmhelpers/core/fstab.py'
--- charmhelpers/core/fstab.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/core/fstab.py 2014-11-25 15:09:14 +0000
@@ -3,10 +3,11 @@
33
4__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'4__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
55
6import io
6import os7import os
78
89
9class Fstab(file):10class Fstab(io.FileIO):
10 """This class extends file in order to implement a file reader/writer11 """This class extends file in order to implement a file reader/writer
11 for file `/etc/fstab`12 for file `/etc/fstab`
12 """13 """
@@ -24,8 +25,8 @@
24 options = "defaults"25 options = "defaults"
2526
26 self.options = options27 self.options = options
27 self.d = d28 self.d = int(d)
28 self.p = p29 self.p = int(p)
2930
30 def __eq__(self, o):31 def __eq__(self, o):
31 return str(self) == str(o)32 return str(self) == str(o)
@@ -45,7 +46,7 @@
45 self._path = path46 self._path = path
46 else:47 else:
47 self._path = self.DEFAULT_PATH48 self._path = self.DEFAULT_PATH
48 file.__init__(self, self._path, 'r+')49 super(Fstab, self).__init__(self._path, 'rb+')
4950
50 def _hydrate_entry(self, line):51 def _hydrate_entry(self, line):
51 # NOTE: use split with no arguments to split on any52 # NOTE: use split with no arguments to split on any
@@ -58,8 +59,9 @@
58 def entries(self):59 def entries(self):
59 self.seek(0)60 self.seek(0)
60 for line in self.readlines():61 for line in self.readlines():
62 line = line.decode('us-ascii')
61 try:63 try:
62 if not line.startswith("#"):64 if line.strip() and not line.startswith("#"):
63 yield self._hydrate_entry(line)65 yield self._hydrate_entry(line)
64 except ValueError:66 except ValueError:
65 pass67 pass
@@ -75,14 +77,14 @@
75 if self.get_entry_by_attr('device', entry.device):77 if self.get_entry_by_attr('device', entry.device):
76 return False78 return False
7779
78 self.write(str(entry) + '\n')80 self.write((str(entry) + '\n').encode('us-ascii'))
79 self.truncate()81 self.truncate()
80 return entry82 return entry
8183
82 def remove_entry(self, entry):84 def remove_entry(self, entry):
83 self.seek(0)85 self.seek(0)
8486
85 lines = self.readlines()87 lines = [l.decode('us-ascii') for l in self.readlines()]
8688
87 found = False89 found = False
88 for index, line in enumerate(lines):90 for index, line in enumerate(lines):
@@ -97,7 +99,7 @@
97 lines.remove(line)99 lines.remove(line)
98100
99 self.seek(0)101 self.seek(0)
100 self.write(''.join(lines))102 self.write(''.join(lines).encode('us-ascii'))
101 self.truncate()103 self.truncate()
102 return True104 return True
103105
104106
=== modified file 'charmhelpers/core/hookenv.py'
--- charmhelpers/core/hookenv.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/core/hookenv.py 2014-11-25 15:09:14 +0000
@@ -9,9 +9,14 @@
9import yaml9import yaml
10import subprocess10import subprocess
11import sys11import sys
12import UserDict
13from subprocess import CalledProcessError12from subprocess import CalledProcessError
1413
14import six
15if not six.PY3:
16 from UserDict import UserDict
17else:
18 from collections import UserDict
19
15CRITICAL = "CRITICAL"20CRITICAL = "CRITICAL"
16ERROR = "ERROR"21ERROR = "ERROR"
17WARNING = "WARNING"22WARNING = "WARNING"
@@ -67,12 +72,12 @@
67 subprocess.call(command)72 subprocess.call(command)
6873
6974
70class Serializable(UserDict.IterableUserDict):75class Serializable(UserDict):
71 """Wrapper, an object that can be serialized to yaml or json"""76 """Wrapper, an object that can be serialized to yaml or json"""
7277
73 def __init__(self, obj):78 def __init__(self, obj):
74 # wrap the object79 # wrap the object
75 UserDict.IterableUserDict.__init__(self)80 UserDict.__init__(self)
76 self.data = obj81 self.data = obj
7782
78 def __getattr__(self, attr):83 def __getattr__(self, attr):
@@ -218,7 +223,7 @@
218 prev_keys = []223 prev_keys = []
219 if self._prev_dict is not None:224 if self._prev_dict is not None:
220 prev_keys = self._prev_dict.keys()225 prev_keys = self._prev_dict.keys()
221 return list(set(prev_keys + dict.keys(self)))226 return list(set(prev_keys + list(dict.keys(self))))
222227
223 def load_previous(self, path=None):228 def load_previous(self, path=None):
224 """Load previous copy of config from disk.229 """Load previous copy of config from disk.
@@ -269,7 +274,7 @@
269274
270 """275 """
271 if self._prev_dict:276 if self._prev_dict:
272 for k, v in self._prev_dict.iteritems():277 for k, v in six.iteritems(self._prev_dict):
273 if k not in self:278 if k not in self:
274 self[k] = v279 self[k] = v
275 with open(self.path, 'w') as f:280 with open(self.path, 'w') as f:
@@ -284,7 +289,8 @@
284 config_cmd_line.append(scope)289 config_cmd_line.append(scope)
285 config_cmd_line.append('--format=json')290 config_cmd_line.append('--format=json')
286 try:291 try:
287 config_data = json.loads(subprocess.check_output(config_cmd_line))292 config_data = json.loads(
293 subprocess.check_output(config_cmd_line).decode('UTF-8'))
288 if scope is not None:294 if scope is not None:
289 return config_data295 return config_data
290 return Config(config_data)296 return Config(config_data)
@@ -303,10 +309,10 @@
303 if unit:309 if unit:
304 _args.append(unit)310 _args.append(unit)
305 try:311 try:
306 return json.loads(subprocess.check_output(_args))312 return json.loads(subprocess.check_output(_args).decode('UTF-8'))
307 except ValueError:313 except ValueError:
308 return None314 return None
309 except CalledProcessError, e:315 except CalledProcessError as e:
310 if e.returncode == 2:316 if e.returncode == 2:
311 return None317 return None
312 raise318 raise
@@ -318,7 +324,7 @@
318 relation_cmd_line = ['relation-set']324 relation_cmd_line = ['relation-set']
319 if relation_id is not None:325 if relation_id is not None:
320 relation_cmd_line.extend(('-r', relation_id))326 relation_cmd_line.extend(('-r', relation_id))
321 for k, v in (relation_settings.items() + kwargs.items()):327 for k, v in (list(relation_settings.items()) + list(kwargs.items())):
322 if v is None:328 if v is None:
323 relation_cmd_line.append('{}='.format(k))329 relation_cmd_line.append('{}='.format(k))
324 else:330 else:
@@ -335,7 +341,8 @@
335 relid_cmd_line = ['relation-ids', '--format=json']341 relid_cmd_line = ['relation-ids', '--format=json']
336 if reltype is not None:342 if reltype is not None:
337 relid_cmd_line.append(reltype)343 relid_cmd_line.append(reltype)
338 return json.loads(subprocess.check_output(relid_cmd_line)) or []344 return json.loads(
345 subprocess.check_output(relid_cmd_line).decode('UTF-8')) or []
339 return []346 return []
340347
341348
@@ -346,7 +353,8 @@
346 units_cmd_line = ['relation-list', '--format=json']353 units_cmd_line = ['relation-list', '--format=json']
347 if relid is not None:354 if relid is not None:
348 units_cmd_line.extend(('-r', relid))355 units_cmd_line.extend(('-r', relid))
349 return json.loads(subprocess.check_output(units_cmd_line)) or []356 return json.loads(
357 subprocess.check_output(units_cmd_line).decode('UTF-8')) or []
350358
351359
352@cached360@cached
@@ -455,7 +463,7 @@
455 """Get the unit ID for the remote unit"""463 """Get the unit ID for the remote unit"""
456 _args = ['unit-get', '--format=json', attribute]464 _args = ['unit-get', '--format=json', attribute]
457 try:465 try:
458 return json.loads(subprocess.check_output(_args))466 return json.loads(subprocess.check_output(_args).decode('UTF-8'))
459 except ValueError:467 except ValueError:
460 return None468 return None
461469
462470
=== modified file 'charmhelpers/core/host.py'
--- charmhelpers/core/host.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/core/host.py 2014-11-25 15:09:14 +0000
@@ -14,11 +14,12 @@
14import subprocess14import subprocess
15import hashlib15import hashlib
16from contextlib import contextmanager16from contextlib import contextmanager
17
18from collections import OrderedDict17from collections import OrderedDict
1918
20from hookenv import log19import six
21from fstab import Fstab20
21from .hookenv import log
22from .fstab import Fstab
2223
2324
24def service_start(service_name):25def service_start(service_name):
@@ -54,7 +55,9 @@
54def service_running(service):55def service_running(service):
55 """Determine whether a system service is running"""56 """Determine whether a system service is running"""
56 try:57 try:
57 output = subprocess.check_output(['service', service, 'status'], stderr=subprocess.STDOUT)58 output = subprocess.check_output(
59 ['service', service, 'status'],
60 stderr=subprocess.STDOUT).decode('UTF-8')
58 except subprocess.CalledProcessError:61 except subprocess.CalledProcessError:
59 return False62 return False
60 else:63 else:
@@ -67,7 +70,9 @@
67def service_available(service_name):70def service_available(service_name):
68 """Determine whether a system service is available"""71 """Determine whether a system service is available"""
69 try:72 try:
70 subprocess.check_output(['service', service_name, 'status'], stderr=subprocess.STDOUT)73 subprocess.check_output(
74 ['service', service_name, 'status'],
75 stderr=subprocess.STDOUT).decode('UTF-8')
71 except subprocess.CalledProcessError as e:76 except subprocess.CalledProcessError as e:
72 return 'unrecognized service' not in e.output77 return 'unrecognized service' not in e.output
73 else:78 else:
@@ -115,7 +120,7 @@
115 cmd.append(from_path)120 cmd.append(from_path)
116 cmd.append(to_path)121 cmd.append(to_path)
117 log(" ".join(cmd))122 log(" ".join(cmd))
118 return subprocess.check_output(cmd).strip()123 return subprocess.check_output(cmd).decode('UTF-8').strip()
119124
120125
121def symlink(source, destination):126def symlink(source, destination):
@@ -130,7 +135,7 @@
130 subprocess.check_call(cmd)135 subprocess.check_call(cmd)
131136
132137
133def mkdir(path, owner='root', group='root', perms=0555, force=False):138def mkdir(path, owner='root', group='root', perms=0o555, force=False):
134 """Create a directory"""139 """Create a directory"""
135 log("Making dir {} {}:{} {:o}".format(path, owner, group,140 log("Making dir {} {}:{} {:o}".format(path, owner, group,
136 perms))141 perms))
@@ -146,7 +151,7 @@
146 os.chown(realpath, uid, gid)151 os.chown(realpath, uid, gid)
147152
148153
149def write_file(path, content, owner='root', group='root', perms=0444):154def write_file(path, content, owner='root', group='root', perms=0o444):
150 """Create or overwrite a file with the contents of a string"""155 """Create or overwrite a file with the contents of a string"""
151 log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))156 log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
152 uid = pwd.getpwnam(owner).pw_uid157 uid = pwd.getpwnam(owner).pw_uid
@@ -177,7 +182,7 @@
177 cmd_args.extend([device, mountpoint])182 cmd_args.extend([device, mountpoint])
178 try:183 try:
179 subprocess.check_output(cmd_args)184 subprocess.check_output(cmd_args)
180 except subprocess.CalledProcessError, e:185 except subprocess.CalledProcessError as e:
181 log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))186 log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
182 return False187 return False
183188
@@ -191,7 +196,7 @@
191 cmd_args = ['umount', mountpoint]196 cmd_args = ['umount', mountpoint]
192 try:197 try:
193 subprocess.check_output(cmd_args)198 subprocess.check_output(cmd_args)
194 except subprocess.CalledProcessError, e:199 except subprocess.CalledProcessError as e:
195 log('Error unmounting {}\n{}'.format(mountpoint, e.output))200 log('Error unmounting {}\n{}'.format(mountpoint, e.output))
196 return False201 return False
197202
@@ -218,8 +223,8 @@
218 """223 """
219 if os.path.exists(path):224 if os.path.exists(path):
220 h = getattr(hashlib, hash_type)()225 h = getattr(hashlib, hash_type)()
221 with open(path, 'r') as source:226 with open(path, 'rb') as source:
222 h.update(source.read()) # IGNORE:E1101 - it does have update227 h.update(source.read())
223 return h.hexdigest()228 return h.hexdigest()
224 else:229 else:
225 return None230 return None
@@ -297,7 +302,7 @@
297 if length is None:302 if length is None:
298 length = random.choice(range(35, 45))303 length = random.choice(range(35, 45))
299 alphanumeric_chars = [304 alphanumeric_chars = [
300 l for l in (string.letters + string.digits)305 l for l in (string.ascii_letters + string.digits)
301 if l not in 'l0QD1vAEIOUaeiou']306 if l not in 'l0QD1vAEIOUaeiou']
302 random_chars = [307 random_chars = [
303 random.choice(alphanumeric_chars) for _ in range(length)]308 random.choice(alphanumeric_chars) for _ in range(length)]
@@ -306,14 +311,14 @@
306311
307def list_nics(nic_type):312def list_nics(nic_type):
308 '''Return a list of nics of given type(s)'''313 '''Return a list of nics of given type(s)'''
309 if isinstance(nic_type, basestring):314 if isinstance(nic_type, six.string_types):
310 int_types = [nic_type]315 int_types = [nic_type]
311 else:316 else:
312 int_types = nic_type317 int_types = nic_type
313 interfaces = []318 interfaces = []
314 for int_type in int_types:319 for int_type in int_types:
315 cmd = ['ip', 'addr', 'show', 'label', int_type + '*']320 cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
316 ip_output = subprocess.check_output(cmd).split('\n')321 ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
317 ip_output = (line for line in ip_output if line)322 ip_output = (line for line in ip_output if line)
318 for line in ip_output:323 for line in ip_output:
319 if line.split()[1].startswith(int_type):324 if line.split()[1].startswith(int_type):
@@ -335,7 +340,7 @@
335340
336def get_nic_mtu(nic):341def get_nic_mtu(nic):
337 cmd = ['ip', 'addr', 'show', nic]342 cmd = ['ip', 'addr', 'show', nic]
338 ip_output = subprocess.check_output(cmd).split('\n')343 ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
339 mtu = ""344 mtu = ""
340 for line in ip_output:345 for line in ip_output:
341 words = line.split()346 words = line.split()
@@ -346,7 +351,7 @@
346351
347def get_nic_hwaddr(nic):352def get_nic_hwaddr(nic):
348 cmd = ['ip', '-o', '-0', 'addr', 'show', nic]353 cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
349 ip_output = subprocess.check_output(cmd)354 ip_output = subprocess.check_output(cmd).decode('UTF-8')
350 hwaddr = ""355 hwaddr = ""
351 words = ip_output.split()356 words = ip_output.split()
352 if 'link/ether' in words:357 if 'link/ether' in words:
353358
=== modified file 'charmhelpers/core/services/helpers.py'
--- charmhelpers/core/services/helpers.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/core/services/helpers.py 2014-11-25 15:09:14 +0000
@@ -196,7 +196,7 @@
196 if not os.path.isabs(file_name):196 if not os.path.isabs(file_name):
197 file_name = os.path.join(hookenv.charm_dir(), file_name)197 file_name = os.path.join(hookenv.charm_dir(), file_name)
198 with open(file_name, 'w') as file_stream:198 with open(file_name, 'w') as file_stream:
199 os.fchmod(file_stream.fileno(), 0600)199 os.fchmod(file_stream.fileno(), 0o600)
200 yaml.dump(config_data, file_stream)200 yaml.dump(config_data, file_stream)
201201
202 def read_context(self, file_name):202 def read_context(self, file_name):
@@ -211,15 +211,19 @@
211211
212class TemplateCallback(ManagerCallback):212class TemplateCallback(ManagerCallback):
213 """213 """
214 Callback class that will render a Jinja2 template, for use as a ready action.214 Callback class that will render a Jinja2 template, for use as a ready
215215 action.
216 :param str source: The template source file, relative to `$CHARM_DIR/templates`216
217 :param str source: The template source file, relative to
218 `$CHARM_DIR/templates`
219
217 :param str target: The target to write the rendered template to220 :param str target: The target to write the rendered template to
218 :param str owner: The owner of the rendered file221 :param str owner: The owner of the rendered file
219 :param str group: The group of the rendered file222 :param str group: The group of the rendered file
220 :param int perms: The permissions of the rendered file223 :param int perms: The permissions of the rendered file
221 """224 """
222 def __init__(self, source, target, owner='root', group='root', perms=0444):225 def __init__(self, source, target,
226 owner='root', group='root', perms=0o444):
223 self.source = source227 self.source = source
224 self.target = target228 self.target = target
225 self.owner = owner229 self.owner = owner
226230
=== modified file 'charmhelpers/core/templating.py'
--- charmhelpers/core/templating.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/core/templating.py 2014-11-25 15:09:14 +0000
@@ -4,7 +4,8 @@
4from charmhelpers.core import hookenv4from charmhelpers.core import hookenv
55
66
7def render(source, target, context, owner='root', group='root', perms=0444, templates_dir=None):7def render(source, target, context, owner='root', group='root',
8 perms=0o444, templates_dir=None):
8 """9 """
9 Render a template.10 Render a template.
1011
1112
=== modified file 'charmhelpers/fetch/__init__.py'
--- charmhelpers/fetch/__init__.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/fetch/__init__.py 2014-11-25 15:09:14 +0000
@@ -5,10 +5,6 @@
5from charmhelpers.core.host import (5from charmhelpers.core.host import (
6 lsb_release6 lsb_release
7)7)
8from urlparse import (
9 urlparse,
10 urlunparse,
11)
12import subprocess8import subprocess
13from charmhelpers.core.hookenv import (9from charmhelpers.core.hookenv import (
14 config,10 config,
@@ -16,6 +12,12 @@
16)12)
17import os13import os
1814
15import six
16if six.PY3:
17 from urllib.parse import urlparse, urlunparse
18else:
19 from urlparse import urlparse, urlunparse
20
1921
20CLOUD_ARCHIVE = """# Ubuntu Cloud Archive22CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
21deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main23deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
@@ -149,7 +151,7 @@
149 cmd = ['apt-get', '--assume-yes']151 cmd = ['apt-get', '--assume-yes']
150 cmd.extend(options)152 cmd.extend(options)
151 cmd.append('install')153 cmd.append('install')
152 if isinstance(packages, basestring):154 if isinstance(packages, six.string_types):
153 cmd.append(packages)155 cmd.append(packages)
154 else:156 else:
155 cmd.extend(packages)157 cmd.extend(packages)
@@ -182,7 +184,7 @@
182def apt_purge(packages, fatal=False):184def apt_purge(packages, fatal=False):
183 """Purge one or more packages"""185 """Purge one or more packages"""
184 cmd = ['apt-get', '--assume-yes', 'purge']186 cmd = ['apt-get', '--assume-yes', 'purge']
185 if isinstance(packages, basestring):187 if isinstance(packages, six.string_types):
186 cmd.append(packages)188 cmd.append(packages)
187 else:189 else:
188 cmd.extend(packages)190 cmd.extend(packages)
@@ -193,7 +195,7 @@
193def apt_hold(packages, fatal=False):195def apt_hold(packages, fatal=False):
194 """Hold one or more packages"""196 """Hold one or more packages"""
195 cmd = ['apt-mark', 'hold']197 cmd = ['apt-mark', 'hold']
196 if isinstance(packages, basestring):198 if isinstance(packages, six.string_types):
197 cmd.append(packages)199 cmd.append(packages)
198 else:200 else:
199 cmd.extend(packages)201 cmd.extend(packages)
@@ -260,7 +262,7 @@
260262
261 if key:263 if key:
262 if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:264 if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
263 with NamedTemporaryFile() as key_file:265 with NamedTemporaryFile('w+') as key_file:
264 key_file.write(key)266 key_file.write(key)
265 key_file.flush()267 key_file.flush()
266 key_file.seek(0)268 key_file.seek(0)
@@ -297,14 +299,14 @@
297 sources = safe_load((config(sources_var) or '').strip()) or []299 sources = safe_load((config(sources_var) or '').strip()) or []
298 keys = safe_load((config(keys_var) or '').strip()) or None300 keys = safe_load((config(keys_var) or '').strip()) or None
299301
300 if isinstance(sources, basestring):302 if isinstance(sources, six.string_types):
301 sources = [sources]303 sources = [sources]
302304
303 if keys is None:305 if keys is None:
304 for source in sources:306 for source in sources:
305 add_source(source, None)307 add_source(source, None)
306 else:308 else:
307 if isinstance(keys, basestring):309 if isinstance(keys, six.string_types):
308 keys = [keys]310 keys = [keys]
309311
310 if len(sources) != len(keys):312 if len(sources) != len(keys):
@@ -401,7 +403,7 @@
401 while result is None or result == APT_NO_LOCK:403 while result is None or result == APT_NO_LOCK:
402 try:404 try:
403 result = subprocess.check_call(cmd, env=env)405 result = subprocess.check_call(cmd, env=env)
404 except subprocess.CalledProcessError, e:406 except subprocess.CalledProcessError as e:
405 retry_count = retry_count + 1407 retry_count = retry_count + 1
406 if retry_count > APT_NO_LOCK_RETRY_COUNT:408 if retry_count > APT_NO_LOCK_RETRY_COUNT:
407 raise409 raise
408410
=== modified file 'charmhelpers/fetch/archiveurl.py'
--- charmhelpers/fetch/archiveurl.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/fetch/archiveurl.py 2014-11-25 15:09:14 +0000
@@ -1,8 +1,23 @@
1import os1import os
2import urllib2
3from urllib import urlretrieve
4import urlparse
5import hashlib2import hashlib
3import re
4
5import six
6if six.PY3:
7 from urllib.request import (
8 build_opener, install_opener, urlopen, urlretrieve,
9 HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,
10 )
11 from urllib.parse import urlparse, urlunparse, parse_qs
12 from urllib.error import URLError
13else:
14 from urllib import urlretrieve
15 from urllib2 import (
16 build_opener, install_opener, urlopen,
17 HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler,
18 URLError
19 )
20 from urlparse import urlparse, urlunparse, parse_qs
621
7from charmhelpers.fetch import (22from charmhelpers.fetch import (
8 BaseFetchHandler,23 BaseFetchHandler,
@@ -15,6 +30,24 @@
15from charmhelpers.core.host import mkdir, check_hash30from charmhelpers.core.host import mkdir, check_hash
1631
1732
33def splituser(host):
34 '''urllib.splituser(), but six's support of this seems broken'''
35 _userprog = re.compile('^(.*)@(.*)$')
36 match = _userprog.match(host)
37 if match:
38 return match.group(1, 2)
39 return None, host
40
41
42def splitpasswd(user):
43 '''urllib.splitpasswd(), but six's support of this is missing'''
44 _passwdprog = re.compile('^([^:]*):(.*)$', re.S)
45 match = _passwdprog.match(user)
46 if match:
47 return match.group(1, 2)
48 return user, None
49
50
18class ArchiveUrlFetchHandler(BaseFetchHandler):51class ArchiveUrlFetchHandler(BaseFetchHandler):
19 """52 """
20 Handler to download archive files from arbitrary URLs.53 Handler to download archive files from arbitrary URLs.
@@ -42,20 +75,20 @@
42 """75 """
43 # propogate all exceptions76 # propogate all exceptions
44 # URLError, OSError, etc77 # URLError, OSError, etc
45 proto, netloc, path, params, query, fragment = urlparse.urlparse(source)78 proto, netloc, path, params, query, fragment = urlparse(source)
46 if proto in ('http', 'https'):79 if proto in ('http', 'https'):
47 auth, barehost = urllib2.splituser(netloc)80 auth, barehost = splituser(netloc)
48 if auth is not None:81 if auth is not None:
49 source = urlparse.urlunparse((proto, barehost, path, params, query, fragment))82 source = urlunparse((proto, barehost, path, params, query, fragment))
50 username, password = urllib2.splitpasswd(auth)83 username, password = splitpasswd(auth)
51 passman = urllib2.HTTPPasswordMgrWithDefaultRealm()84 passman = HTTPPasswordMgrWithDefaultRealm()
52 # Realm is set to None in add_password to force the username and password85 # Realm is set to None in add_password to force the username and password
53 # to be used whatever the realm86 # to be used whatever the realm
54 passman.add_password(None, source, username, password)87 passman.add_password(None, source, username, password)
55 authhandler = urllib2.HTTPBasicAuthHandler(passman)88 authhandler = HTTPBasicAuthHandler(passman)
56 opener = urllib2.build_opener(authhandler)89 opener = build_opener(authhandler)
57 urllib2.install_opener(opener)90 install_opener(opener)
58 response = urllib2.urlopen(source)91 response = urlopen(source)
59 try:92 try:
60 with open(dest, 'w') as dest_file:93 with open(dest, 'w') as dest_file:
61 dest_file.write(response.read())94 dest_file.write(response.read())
@@ -91,17 +124,21 @@
91 url_parts = self.parse_url(source)124 url_parts = self.parse_url(source)
92 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched')125 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched')
93 if not os.path.exists(dest_dir):126 if not os.path.exists(dest_dir):
94 mkdir(dest_dir, perms=0755)127 mkdir(dest_dir, perms=0o755)
95 dld_file = os.path.join(dest_dir, os.path.basename(url_parts.path))128 dld_file = os.path.join(dest_dir, os.path.basename(url_parts.path))
96 try:129 try:
97 self.download(source, dld_file)130 self.download(source, dld_file)
98 except urllib2.URLError as e:131 except URLError as e:
99 raise UnhandledSource(e.reason)132 raise UnhandledSource(e.reason)
100 except OSError as e:133 except OSError as e:
101 raise UnhandledSource(e.strerror)134 raise UnhandledSource(e.strerror)
102 options = urlparse.parse_qs(url_parts.fragment)135 options = parse_qs(url_parts.fragment)
103 for key, value in options.items():136 for key, value in options.items():
104 if key in hashlib.algorithms:137 if not six.PY3:
138 algorithms = hashlib.algorithms
139 else:
140 algorithms = hashlib.algorithms_available
141 if key in algorithms:
105 check_hash(dld_file, value, key)142 check_hash(dld_file, value, key)
106 if checksum:143 if checksum:
107 check_hash(dld_file, checksum, hash_type)144 check_hash(dld_file, checksum, hash_type)
108145
=== modified file 'charmhelpers/fetch/bzrurl.py'
--- charmhelpers/fetch/bzrurl.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/fetch/bzrurl.py 2014-11-25 15:09:14 +0000
@@ -5,6 +5,10 @@
5)5)
6from charmhelpers.core.host import mkdir6from charmhelpers.core.host import mkdir
77
8import six
9if six.PY3:
10 raise ImportError('bzrlib does not support Python3')
11
8try:12try:
9 from bzrlib.branch import Branch13 from bzrlib.branch import Branch
10except ImportError:14except ImportError:
@@ -42,7 +46,7 @@
42 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",46 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",
43 branch_name)47 branch_name)
44 if not os.path.exists(dest_dir):48 if not os.path.exists(dest_dir):
45 mkdir(dest_dir, perms=0755)49 mkdir(dest_dir, perms=0o755)
46 try:50 try:
47 self.branch(source, dest_dir)51 self.branch(source, dest_dir)
48 except OSError as e:52 except OSError as e:
4953
=== modified file 'charmhelpers/fetch/giturl.py'
--- charmhelpers/fetch/giturl.py 2014-11-25 14:35:22 +0000
+++ charmhelpers/fetch/giturl.py 2014-11-25 15:09:14 +0000
@@ -5,6 +5,10 @@
5)5)
6from charmhelpers.core.host import mkdir6from charmhelpers.core.host import mkdir
77
8import six
9if six.PY3:
10 raise ImportError('GitPython does not support Python 3')
11
8try:12try:
9 from git import Repo13 from git import Repo
10except ImportError:14except ImportError:
@@ -17,7 +21,7 @@
17 """Handler for git branches via generic and github URLs"""21 """Handler for git branches via generic and github URLs"""
18 def can_handle(self, source):22 def can_handle(self, source):
19 url_parts = self.parse_url(source)23 url_parts = self.parse_url(source)
20 #TODO (mattyw) no support for ssh git@ yet24 # TODO (mattyw) no support for ssh git@ yet
21 if url_parts.scheme not in ('http', 'https', 'git'):25 if url_parts.scheme not in ('http', 'https', 'git'):
22 return False26 return False
23 else:27 else:
@@ -36,7 +40,7 @@
36 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",40 dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched",
37 branch_name)41 branch_name)
38 if not os.path.exists(dest_dir):42 if not os.path.exists(dest_dir):
39 mkdir(dest_dir, perms=0755)43 mkdir(dest_dir, perms=0o755)
40 try:44 try:
41 self.clone(source, dest_dir, branch)45 self.clone(source, dest_dir, branch)
42 except OSError as e:46 except OSError as e:
4347
=== modified file 'test_requirements.txt'
--- test_requirements.txt 2014-11-25 14:35:22 +0000
+++ test_requirements.txt 2014-11-25 15:09:14 +0000
@@ -1,18 +1,17 @@
1coverage==3.61# Test-only dependencies are unpinned.
2launchpadlib==1.10.22#
3--allow-external launchpadlib3pip
4--allow-unverified launchpadlib4distribute
5mock==1.0.15coverage>=3.6
6netaddr6mock>=1.0.1
7nose==1.3.17nose>=1.3.1
8PyYAML==3.108flake8
9simplejson==3.3.09testtools==0.9.14 # Before dependent on modern 'six'
10testtools10#
11Tempita==0.5.111# Specify precise versions of runtime dependencies where possible.
12bzr+http://bazaar.launchpad.net/~yellow/python-shelltoolbox/trunk@17#egg=shelltoolbox12netaddr==0.7.10 # trusty. precise is 0.7.5, but not in pypi.
13http://alastairs-place.net/projects/netifaces/netifaces-0.6.tar.gz13PyYAML==3.10 # precise
14bzr==2.6.014Tempita==0.5.1 # precise
15GitPython>=0.3.2.RC115netifaces==0.10 # trusty is 0.8, but using py3 compatible version for tests.
16Jinja2==2.7.216Jinja2==2.6 # precise
17--allow-external lazr.authentication17six==1.1 # precise
18--allow-unverified lazr.authentication
1918
=== modified file 'tests/cli/test_cmdline.py'
--- tests/cli/test_cmdline.py 2014-11-25 14:35:22 +0000
+++ tests/cli/test_cmdline.py 2014-11-25 15:09:14 +0000
@@ -6,15 +6,13 @@
6 patch,6 patch,
7 MagicMock,7 MagicMock,
8)8)
9try:
10 from cStringIO import StringIO
11except ImportError:
12 from StringIO import StringIO
13import json9import json
14from pprint import pformat10from pprint import pformat
15import yaml11import yaml
16import csv12import csv
1713
14from six import StringIO
15
18from charmhelpers import cli16from charmhelpers import cli
1917
2018
@@ -116,7 +114,8 @@
116 self.output_data = {"this": "is", "some": 1, "data": dict()}114 self.output_data = {"this": "is", "some": 1, "data": dict()}
117115
118 def test_supports_formats(self):116 def test_supports_formats(self):
119 self.assertItemsEqual(self.expected_formats, self.of.supported_formats)117 self.assertEqual(sorted(self.expected_formats),
118 sorted(self.of.supported_formats))
120119
121 def test_adds_arguments(self):120 def test_adds_arguments(self):
122 ap = MagicMock()121 ap = MagicMock()
@@ -130,11 +129,12 @@
130129
131 for call_args in add_arg.call_args_list:130 for call_args in add_arg.call_args_list:
132 if "--format" in call_args[0]:131 if "--format" in call_args[0]:
133 self.assertItemsEqual(call_args[1]['choices'], self.expected_formats)132 self.assertEqual(sorted(call_args[1]['choices']),
133 sorted(self.expected_formats))
134 self.assertEqual(call_args[1]['default'], 'raw')134 self.assertEqual(call_args[1]['default'], 'raw')
135 break135 break
136 else:136 else:
137 print arg_group.call_args_list137 print(arg_group.call_args_list)
138 self.fail("No --format argument was created")138 self.fail("No --format argument was created")
139139
140 all_args = [c[0][0] for c in add_arg.call_args_list]140 all_args = [c[0][0] for c in add_arg.call_args_list]
141141
=== modified file 'tests/cli/test_function_signature_analysis.py'
--- tests/cli/test_function_signature_analysis.py 2013-08-21 21:40:07 +0000
+++ tests/cli/test_function_signature_analysis.py 2014-11-25 15:09:14 +0000
@@ -1,7 +1,7 @@
1"""Tests for the commandant code that analyzes a function signature to1"""Tests for the commandant code that analyzes a function signature to
2determine the parameters to argparse."""2determine the parameters to argparse."""
33
4from testtools import TestCase, matchers4from testtools import TestCase
55
6from charmhelpers import cli6from charmhelpers import cli
77
@@ -42,5 +42,5 @@
42 args = cli.describe_arguments(lambda x, y=3, *z, **missing: False)42 args = cli.describe_arguments(lambda x, y=3, *z, **missing: False)
43 for opts, _ in args:43 for opts, _ in args:
44 # opts should be ('varname',) at this point44 # opts should be ('varname',) at this point
45 self.assertThat(opts, matchers.HasLength(1))45 self.assertTrue(len(opts) == 1)
46 self.assertNotIn('missing', opts)46 self.assertNotIn('missing', opts)
4747
=== modified file 'tests/contrib/charmhelpers/test_charmhelpers.py'
--- tests/contrib/charmhelpers/test_charmhelpers.py 2014-11-25 14:35:22 +0000
+++ tests/contrib/charmhelpers/test_charmhelpers.py 2014-11-25 15:09:14 +0000
@@ -2,10 +2,10 @@
22
3import unittest3import unittest
4import yaml4import yaml
5
6from StringIO import StringIO
7from testtools import TestCase5from testtools import TestCase
86
7from six import StringIO
8
9import sys9import sys
10# Path hack to ensure we test the local code, not a version installed in10# Path hack to ensure we test the local code, not a version installed in
11# /usr/local/lib. This is necessary since /usr/local/lib is prepended before11# /usr/local/lib. This is necessary since /usr/local/lib is prepended before
@@ -265,11 +265,11 @@
265 # wait_for_page_contents() will wait until a given string is265 # wait_for_page_contents() will wait until a given string is
266 # contained within the results of a given url and will return266 # contained within the results of a given url and will return
267 # once it does.267 # once it does.
268 # We need to patch the charmhelpers instance of urllib2 so that268 # We need to patch the charmhelpers instance of urlopen so that
269 # it doesn't try to connect out.269 # it doesn't try to connect out.
270 test_content = "Hello, world."270 test_content = "Hello, world."
271 new_urlopen = lambda *args: StringIO(test_content)271 new_urlopen = lambda *args: StringIO(test_content)
272 self.patch(charmhelpers.urllib2, 'urlopen', new_urlopen)272 self.patch(charmhelpers, 'urlopen', new_urlopen)
273 charmhelpers.wait_for_page_contents(273 charmhelpers.wait_for_page_contents(
274 'http://example.com', test_content, timeout=0)274 'http://example.com', test_content, timeout=0)
275275
@@ -277,10 +277,10 @@
277 # If the desired contents do not appear within the page before277 # If the desired contents do not appear within the page before
278 # the specified timeout, wait_for_page_contents() will raise a278 # the specified timeout, wait_for_page_contents() will raise a
279 # RuntimeError.279 # RuntimeError.
280 # We need to patch the charmhelpers instance of urllib2 so that280 # We need to patch the charmhelpers instance of urlopen so that
281 # it doesn't try to connect out.281 # it doesn't try to connect out.
282 new_urlopen = lambda *args: StringIO("This won't work.")282 new_urlopen = lambda *args: StringIO("This won't work.")
283 self.patch(charmhelpers.urllib2, 'urlopen', new_urlopen)283 self.patch(charmhelpers, 'urlopen', new_urlopen)
284 self.assertRaises(284 self.assertRaises(
285 RuntimeError, charmhelpers.wait_for_page_contents,285 RuntimeError, charmhelpers.wait_for_page_contents,
286 'http://example.com', "This will error", timeout=0)286 'http://example.com', "This will error", timeout=0)
287287
=== modified file 'tests/contrib/hahelpers/test_cluster_utils.py'
--- tests/contrib/hahelpers/test_cluster_utils.py 2014-09-24 09:42:52 +0000
+++ tests/contrib/hahelpers/test_cluster_utils.py 2014-11-25 15:09:14 +0000
@@ -44,7 +44,7 @@
44 def test_is_crm_leader(self, check_output):44 def test_is_crm_leader(self, check_output):
45 '''It determines its unit is leader'''45 '''It determines its unit is leader'''
46 self.get_unit_hostname.return_value = 'node1'46 self.get_unit_hostname.return_value = 'node1'
47 crm = 'resource vip is running on: node1'47 crm = b'resource vip is running on: node1'
48 check_output.return_value = crm48 check_output.return_value = crm
49 self.assertTrue(cluster_utils.is_crm_leader('vip'))49 self.assertTrue(cluster_utils.is_crm_leader('vip'))
5050
@@ -52,7 +52,7 @@
52 def test_is_not_leader(self, check_output):52 def test_is_not_leader(self, check_output):
53 '''It determines its unit is not leader'''53 '''It determines its unit is not leader'''
54 self.get_unit_hostname.return_value = 'node1'54 self.get_unit_hostname.return_value = 'node1'
55 crm = 'resource vip is running on: node2'55 crm = b'resource vip is running on: node2'
56 check_output.return_value = crm56 check_output.return_value = crm
57 self.assertFalse(cluster_utils.is_crm_leader('some_resource'))57 self.assertFalse(cluster_utils.is_crm_leader('some_resource'))
5858
5959
=== modified file 'tests/contrib/network/test_ip.py'
--- tests/contrib/network/test_ip.py 2014-11-25 14:35:22 +0000
+++ tests/contrib/network/test_ip.py 2014-11-25 15:09:14 +0000
@@ -59,7 +59,7 @@
59 },59 },
60}60}
6161
62IP_OUTPUT = """link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff62IP_OUTPUT = b"""link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff
63 inet 10.5.16.93/16 brd 10.5.255.255 scope global eth063 inet 10.5.16.93/16 brd 10.5.255.255 scope global eth0
64 valid_lft forever preferred_lft forever64 valid_lft forever preferred_lft forever
65 inet6 2001:db8:1:0:d0cf:528c:23eb:6000/64 scope global65 inet6 2001:db8:1:0:d0cf:528c:23eb:6000/64 scope global
@@ -72,7 +72,7 @@
72 valid_lft forever preferred_lft forever72 valid_lft forever preferred_lft forever
73"""73"""
7474
75IP_OUTPUT_NO_VALID = """link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff75IP_OUTPUT_NO_VALID = b"""link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff
76 inet 10.5.16.93/16 brd 10.5.255.255 scope global eth076 inet 10.5.16.93/16 brd 10.5.255.255 scope global eth0
77 valid_lft forever preferred_lft forever77 valid_lft forever preferred_lft forever
78 inet6 2001:db8:1:0:2918:3444:852:5b8a/64 scope global temporary dynamic78 inet6 2001:db8:1:0:2918:3444:852:5b8a/64 scope global temporary dynamic
@@ -100,13 +100,14 @@
100 return DUMMY_ADDRESSES[iface]100 return DUMMY_ADDRESSES[iface]
101101
102 with mock.patch.object(netifaces, 'interfaces') as interfaces:102 with mock.patch.object(netifaces, 'interfaces') as interfaces:
103 interfaces.return_value = DUMMY_ADDRESSES.keys()103 interfaces.return_value = sorted(DUMMY_ADDRESSES.keys())
104 with mock.patch.object(netifaces, 'ifaddresses') as ifaddresses:104 with mock.patch.object(netifaces, 'ifaddresses') as ifaddresses:
105 ifaddresses.side_effect = side_effect105 ifaddresses.side_effect = side_effect
106 if not fatal:106 if not fatal:
107 self.assertEqual(expect_ip_addr,107 self.assertEqual(expect_ip_addr,
108 net_ip.get_address_in_network(108 net_ip.get_address_in_network(network,
109 network, fallback, fatal))109 fallback,
110 fatal))
110 else:111 else:
111 net_ip.get_address_in_network(network, fallback, fatal)112 net_ip.get_address_in_network(network, fallback, fatal)
112113
@@ -122,7 +123,7 @@
122 None, None, fatal=True)123 None, None, fatal=True)
123124
124 def test_get_address_in_network_ipv4(self):125 def test_get_address_in_network_ipv4(self):
125 self._test_get_address_in_network('192.168.1.56', '192.168.1.0/24')126 self._test_get_address_in_network('192.168.1.55', '192.168.1.0/24')
126127
127 def test_get_address_in_network_ipv6(self):128 def test_get_address_in_network_ipv6(self):
128 self._test_get_address_in_network('2a01:348:2f4:0:685e:5748:ae62:209f',129 self._test_get_address_in_network('2a01:348:2f4:0:685e:5748:ae62:209f',
@@ -260,7 +261,7 @@
260 mock_get_iface_from_addr):261 mock_get_iface_from_addr):
261 mock_get_iface_from_addr.return_value = 'eth0'262 mock_get_iface_from_addr.return_value = 'eth0'
262 mock_check_out.return_value = \263 mock_check_out.return_value = \
263 "inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic"264 b"inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic"
264 _interfaces.return_value = DUMMY_ADDRESSES.keys()265 _interfaces.return_value = DUMMY_ADDRESSES.keys()
265 _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__266 _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__
266 result = net_ip.get_ipv6_addr(dynamic_only=False)267 result = net_ip.get_ipv6_addr(dynamic_only=False)
@@ -275,7 +276,7 @@
275 mock_get_iface_from_addr):276 mock_get_iface_from_addr):
276 mock_get_iface_from_addr.return_value = 'eth0'277 mock_get_iface_from_addr.return_value = 'eth0'
277 mock_check_out.return_value = \278 mock_check_out.return_value = \
278 "inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic"279 b"inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic"
279 _interfaces.return_value = DUMMY_ADDRESSES.keys()280 _interfaces.return_value = DUMMY_ADDRESSES.keys()
280 _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__281 _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__
281 result = net_ip.get_ipv6_addr(dynamic_only=False)282 result = net_ip.get_ipv6_addr(dynamic_only=False)
@@ -494,9 +495,9 @@
494 def test_get_iface_from_addr(self, mock_log, mock_ifaddresses,495 def test_get_iface_from_addr(self, mock_log, mock_ifaddresses,
495 mock_interfaces):496 mock_interfaces):
496 mock_ifaddresses.side_effect = lambda iface: DUMMY_ADDRESSES[iface]497 mock_ifaddresses.side_effect = lambda iface: DUMMY_ADDRESSES[iface]
497 mock_interfaces.return_value = DUMMY_ADDRESSES.keys()498 mock_interfaces.return_value = sorted(DUMMY_ADDRESSES.keys())
498 addr = 'fe80::3e97:eff:fe8b:1cf7'499 addr = 'fe80::3e97:eff:fe8b:1cf7'
499 self.assertEqual(net_ip.get_iface_from_addr(addr), 'eth1')500 self.assertEqual(net_ip.get_iface_from_addr(addr), 'eth0')
500501
501 with nose.tools.assert_raises(Exception):502 with nose.tools.assert_raises(Exception):
502 net_ip.get_iface_from_addr('1.2.3.4')503 net_ip.get_iface_from_addr('1.2.3.4')
503504
=== modified file 'tests/contrib/openstack/test_neutron_utils.py'
--- tests/contrib/openstack/test_neutron_utils.py 2014-11-07 09:54:33 +0000
+++ tests/contrib/openstack/test_neutron_utils.py 2014-11-25 15:09:14 +0000
@@ -23,12 +23,12 @@
23 return mock23 return mock
2424
25 def test_headers_package(self):25 def test_headers_package(self):
26 self.check_output.return_value = '3.13.0-19-generic'26 self.check_output.return_value = b'3.13.0-19-generic'
27 kname = neutron.headers_package()27 kname = neutron.headers_package()
28 self.assertEquals(kname, 'linux-headers-3.13.0-19-generic')28 self.assertEquals(kname, 'linux-headers-3.13.0-19-generic')
2929
30 def test_kernel_version(self):30 def test_kernel_version(self):
31 self.check_output.return_value = '3.13.0-19-generic'31 self.check_output.return_value = b'3.13.0-19-generic'
32 kver_maj, kver_min = neutron.kernel_version()32 kver_maj, kver_min = neutron.kernel_version()
33 self.assertEquals((kver_maj, kver_min), (3, 13))33 self.assertEquals((kver_maj, kver_min), (3, 13))
3434
3535
=== modified file 'tests/contrib/openstack/test_openstack_utils.py'
--- tests/contrib/openstack/test_openstack_utils.py 2014-11-25 14:35:22 +0000
+++ tests/contrib/openstack/test_openstack_utils.py 2014-11-25 15:09:14 +0000
@@ -1,13 +1,22 @@
1import io
1import os2import os
2import subprocess3import subprocess
3import unittest4import unittest
4
5from copy import copy5from copy import copy
6from testtools import TestCase6from testtools import TestCase
7from mock import MagicMock, patch, call7from mock import MagicMock, patch, call
88
9import charmhelpers.contrib.openstack.utils as openstack9import charmhelpers.contrib.openstack.utils as openstack
1010
11import six
12
13if not six.PY3:
14 builtin_open = '__builtin__.open'
15 builtin_import = '__builtin__.__import__'
16else:
17 builtin_open = 'builtins.open'
18 builtin_import = 'builtins.__import__'
19
11# mocked return of openstack.lsb_release()20# mocked return of openstack.lsb_release()
12FAKE_RELEASE = {21FAKE_RELEASE = {
13 'DISTRIB_CODENAME': 'precise',22 'DISTRIB_CODENAME': 'precise',
@@ -222,7 +231,7 @@
222 '''Test deriving OpenStack codename from an installed package'''231 '''Test deriving OpenStack codename from an installed package'''
223 with patch('apt_pkg.Cache') as cache:232 with patch('apt_pkg.Cache') as cache:
224 cache.return_value = self._apt_cache()233 cache.return_value = self._apt_cache()
225 for pkg, vers in FAKE_REPO.iteritems():234 for pkg, vers in six.iteritems(FAKE_REPO):
226 # test fake repo for all "installed" packages235 # test fake repo for all "installed" packages
227 if pkg.startswith('bad-'):236 if pkg.startswith('bad-'):
228 continue237 continue
@@ -291,7 +300,7 @@
291 '''Test deriving OpenStack version from an installed package'''300 '''Test deriving OpenStack version from an installed package'''
292 with patch('apt_pkg.Cache') as cache:301 with patch('apt_pkg.Cache') as cache:
293 cache.return_value = self._apt_cache()302 cache.return_value = self._apt_cache()
294 for pkg, vers in FAKE_REPO.iteritems():303 for pkg, vers in six.iteritems(FAKE_REPO):
295 if pkg.startswith('bad-'):304 if pkg.startswith('bad-'):
296 continue305 continue
297 if 'pkg_vers' not in vers:306 if 'pkg_vers' not in vers:
@@ -354,12 +363,12 @@
354 ex_cmd = ['add-apt-repository', '-y', 'ppa:gandelman-a/openstack']363 ex_cmd = ['add-apt-repository', '-y', 'ppa:gandelman-a/openstack']
355 mock.assert_called_with(ex_cmd)364 mock.assert_called_with(ex_cmd)
356365
357 @patch('__builtin__.open')366 @patch(builtin_open)
358 @patch('charmhelpers.contrib.openstack.utils.juju_log')367 @patch('charmhelpers.contrib.openstack.utils.juju_log')
359 @patch('charmhelpers.contrib.openstack.utils.import_key')368 @patch('charmhelpers.contrib.openstack.utils.import_key')
360 def test_configure_install_source_deb_url(self, _import, _log, _open):369 def test_configure_install_source_deb_url(self, _import, _log, _open):
361 '''Test configuring installation source from deb repo url'''370 '''Test configuring installation source from deb repo url'''
362 _file = MagicMock(spec=file)371 _file = MagicMock(spec=io.FileIO)
363 _open.return_value = _file372 _open.return_value = _file
364 src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu '373 src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu '
365 'precise-havana main|KEYID')374 'precise-havana main|KEYID')
@@ -372,12 +381,12 @@
372 _file.__enter__().write.assert_called_with(src)381 _file.__enter__().write.assert_called_with(src)
373382
374 @patch('charmhelpers.contrib.openstack.utils.lsb_release')383 @patch('charmhelpers.contrib.openstack.utils.lsb_release')
375 @patch('__builtin__.open')384 @patch(builtin_open)
376 @patch('charmhelpers.contrib.openstack.utils.juju_log')385 @patch('charmhelpers.contrib.openstack.utils.juju_log')
377 def test_configure_install_source_distro_proposed(self, _log, _open, _lsb):386 def test_configure_install_source_distro_proposed(self, _log, _open, _lsb):
378 '''Test configuring installation source from deb repo url'''387 '''Test configuring installation source from deb repo url'''
379 _lsb.return_value = FAKE_RELEASE388 _lsb.return_value = FAKE_RELEASE
380 _file = MagicMock(spec=file)389 _file = MagicMock(spec=io.FileIO)
381 _open.return_value = _file390 _open.return_value = _file
382 openstack.configure_installation_source('distro-proposed')391 openstack.configure_installation_source('distro-proposed')
383 src = ('deb http://archive.ubuntu.com/ubuntu/ precise-proposed '392 src = ('deb http://archive.ubuntu.com/ubuntu/ precise-proposed '
@@ -402,13 +411,13 @@
402 'ppa:ubuntu-cloud-archive/folsom-staging']411 'ppa:ubuntu-cloud-archive/folsom-staging']
403 _subp.assert_called_with(cmd)412 _subp.assert_called_with(cmd)
404413
405 @patch('__builtin__.open')414 @patch(builtin_open)
406 @patch('charmhelpers.contrib.openstack.utils.apt_install')415 @patch('charmhelpers.contrib.openstack.utils.apt_install')
407 @patch('charmhelpers.contrib.openstack.utils.lsb_release')416 @patch('charmhelpers.contrib.openstack.utils.lsb_release')
408 def test_configure_install_source_uca_repos(self, _lsb, _install, _open):417 def test_configure_install_source_uca_repos(self, _lsb, _install, _open):
409 '''Test configuring installation source from UCA sources'''418 '''Test configuring installation source from UCA sources'''
410 _lsb.return_value = FAKE_RELEASE419 _lsb.return_value = FAKE_RELEASE
411 _file = MagicMock(spec=file)420 _file = MagicMock(spec=io.FileIO)
412 _open.return_value = _file421 _open.return_value = _file
413 for src, url in UCA_SOURCES:422 for src, url in UCA_SOURCES:
414 openstack.configure_installation_source(src)423 openstack.configure_installation_source(src)
@@ -455,13 +464,13 @@
455 @patch('os.mkdir')464 @patch('os.mkdir')
456 @patch('os.path.exists')465 @patch('os.path.exists')
457 @patch('charmhelpers.contrib.openstack.utils.charm_dir')466 @patch('charmhelpers.contrib.openstack.utils.charm_dir')
458 @patch('__builtin__.open')467 @patch(builtin_open)
459 def test_save_scriptrc(self, _open, _charm_dir, _exists, _mkdir):468 def test_save_scriptrc(self, _open, _charm_dir, _exists, _mkdir):
460 '''Test generation of scriptrc from environment'''469 '''Test generation of scriptrc from environment'''
461 scriptrc = ['#!/bin/bash\n',470 scriptrc = ['#!/bin/bash\n',
462 'export setting1=foo\n',471 'export setting1=foo\n',
463 'export setting2=bar\n']472 'export setting2=bar\n']
464 _file = MagicMock(spec=file)473 _file = MagicMock(spec=io.FileIO)
465 _open.return_value = _file474 _open.return_value = _file
466 _charm_dir.return_value = '/var/lib/juju/units/testing-foo-0/charm'475 _charm_dir.return_value = '/var/lib/juju/units/testing-foo-0/charm'
467 _exists.return_value = False476 _exists.return_value = False
@@ -583,21 +592,21 @@
583 @patch.object(openstack, 'apt_install')592 @patch.object(openstack, 'apt_install')
584 def test_get_host_ip_with_hostname(self, apt_install):593 def test_get_host_ip_with_hostname(self, apt_install):
585 fake_dns = FakeDNS('10.0.0.1')594 fake_dns = FakeDNS('10.0.0.1')
586 with patch('__builtin__.__import__', side_effect=[fake_dns]):595 with patch(builtin_import, side_effect=[fake_dns]):
587 ip = openstack.get_host_ip('www.ubuntu.com')596 ip = openstack.get_host_ip('www.ubuntu.com')
588 self.assertEquals(ip, '10.0.0.1')597 self.assertEquals(ip, '10.0.0.1')
589598
590 @patch.object(openstack, 'apt_install')599 @patch.object(openstack, 'apt_install')
591 def test_get_host_ip_with_ip(self, apt_install):600 def test_get_host_ip_with_ip(self, apt_install):
592 fake_dns = FakeDNS('5.5.5.5')601 fake_dns = FakeDNS('5.5.5.5')
593 with patch('__builtin__.__import__', side_effect=[fake_dns]):602 with patch(builtin_import, side_effect=[fake_dns]):
594 ip = openstack.get_host_ip('4.2.2.1')603 ip = openstack.get_host_ip('4.2.2.1')
595 self.assertEquals(ip, '4.2.2.1')604 self.assertEquals(ip, '4.2.2.1')
596605
597 @patch.object(openstack, 'apt_install')606 @patch.object(openstack, 'apt_install')
598 def test_ns_query_trigger_apt_install(self, apt_install):607 def test_ns_query_trigger_apt_install(self, apt_install):
599 fake_dns = FakeDNS('5.5.5.5')608 fake_dns = FakeDNS('5.5.5.5')
600 with patch('__builtin__.__import__', side_effect=[ImportError, fake_dns]):609 with patch(builtin_import, side_effect=[ImportError, fake_dns]):
601 nsq = openstack.ns_query('5.5.5.5')610 nsq = openstack.ns_query('5.5.5.5')
602 apt_install.assert_called_with('python-dnspython')611 apt_install.assert_called_with('python-dnspython')
603 self.assertEquals(nsq, '5.5.5.5')612 self.assertEquals(nsq, '5.5.5.5')
@@ -605,7 +614,7 @@
605 @patch.object(openstack, 'apt_install')614 @patch.object(openstack, 'apt_install')
606 def test_ns_query_ptr_record(self, apt_install):615 def test_ns_query_ptr_record(self, apt_install):
607 fake_dns = FakeDNS('127.0.0.1')616 fake_dns = FakeDNS('127.0.0.1')
608 with patch('__builtin__.__import__', side_effect=[fake_dns]):617 with patch(builtin_import, side_effect=[fake_dns]):
609 nsq = openstack.ns_query('127.0.0.1')618 nsq = openstack.ns_query('127.0.0.1')
610 self.assertEquals(nsq, '127.0.0.1')619 self.assertEquals(nsq, '127.0.0.1')
611620
@@ -613,35 +622,35 @@
613 def test_ns_query_a_record(self, apt_install):622 def test_ns_query_a_record(self, apt_install):
614 fake_dns = FakeDNS('127.0.0.1')623 fake_dns = FakeDNS('127.0.0.1')
615 fake_dns_name = FakeDNSName('www.somedomain.tld')624 fake_dns_name = FakeDNSName('www.somedomain.tld')
616 with patch('__builtin__.__import__', side_effect=[fake_dns]):625 with patch(builtin_import, side_effect=[fake_dns]):
617 nsq = openstack.ns_query(fake_dns_name)626 nsq = openstack.ns_query(fake_dns_name)
618 self.assertEquals(nsq, '127.0.0.1')627 self.assertEquals(nsq, '127.0.0.1')
619628
620 @patch.object(openstack, 'apt_install')629 @patch.object(openstack, 'apt_install')
621 def test_ns_query_blank_record(self, apt_install):630 def test_ns_query_blank_record(self, apt_install):
622 fake_dns = FakeDNS(None)631 fake_dns = FakeDNS(None)
623 with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]):632 with patch(builtin_import, side_effect=[fake_dns, fake_dns]):
624 nsq = openstack.ns_query(None)633 nsq = openstack.ns_query(None)
625 self.assertEquals(nsq, None)634 self.assertEquals(nsq, None)
626635
627 @patch.object(openstack, 'apt_install')636 @patch.object(openstack, 'apt_install')
628 def test_ns_query_lookup_fail(self, apt_install):637 def test_ns_query_lookup_fail(self, apt_install):
629 fake_dns = FakeDNS('')638 fake_dns = FakeDNS('')
630 with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]):639 with patch(builtin_import, side_effect=[fake_dns, fake_dns]):
631 nsq = openstack.ns_query('nonexistant')640 nsq = openstack.ns_query('nonexistant')
632 self.assertEquals(nsq, None)641 self.assertEquals(nsq, None)
633642
634 @patch.object(openstack, 'apt_install')643 @patch.object(openstack, 'apt_install')
635 def test_get_hostname_with_ip(self, apt_install):644 def test_get_hostname_with_ip(self, apt_install):
636 fake_dns = FakeDNS('www.ubuntu.com')645 fake_dns = FakeDNS('www.ubuntu.com')
637 with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]):646 with patch(builtin_import, side_effect=[fake_dns, fake_dns]):
638 hn = openstack.get_hostname('4.2.2.1')647 hn = openstack.get_hostname('4.2.2.1')
639 self.assertEquals(hn, 'www.ubuntu.com')648 self.assertEquals(hn, 'www.ubuntu.com')
640649
641 @patch.object(openstack, 'apt_install')650 @patch.object(openstack, 'apt_install')
642 def test_get_hostname_with_ip_not_fqdn(self, apt_install):651 def test_get_hostname_with_ip_not_fqdn(self, apt_install):
643 fake_dns = FakeDNS('packages.ubuntu.com')652 fake_dns = FakeDNS('packages.ubuntu.com')
644 with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]):653 with patch(builtin_import, side_effect=[fake_dns, fake_dns]):
645 hn = openstack.get_hostname('4.2.2.1', fqdn=False)654 hn = openstack.get_hostname('4.2.2.1', fqdn=False)
646 self.assertEquals(hn, 'packages')655 self.assertEquals(hn, 'packages')
647656
@@ -663,7 +672,7 @@
663 @patch.object(openstack, 'apt_install')672 @patch.object(openstack, 'apt_install')
664 def test_get_hostname_trigger_apt_install(self, apt_install):673 def test_get_hostname_trigger_apt_install(self, apt_install):
665 fake_dns = FakeDNS('www.ubuntu.com')674 fake_dns = FakeDNS('www.ubuntu.com')
666 with patch('__builtin__.__import__', side_effect=[ImportError, fake_dns, fake_dns]):675 with patch(builtin_import, side_effect=[ImportError, fake_dns, fake_dns]):
667 hn = openstack.get_hostname('4.2.2.1')676 hn = openstack.get_hostname('4.2.2.1')
668 apt_install.assert_called_with('python-dnspython')677 apt_install.assert_called_with('python-dnspython')
669 self.assertEquals(hn, 'www.ubuntu.com')678 self.assertEquals(hn, 'www.ubuntu.com')
@@ -673,12 +682,12 @@
673 def test_get_hostname_lookup_fail(self, apt_install, ns_query):682 def test_get_hostname_lookup_fail(self, apt_install, ns_query):
674 fake_dns = FakeDNS('www.ubuntu.com')683 fake_dns = FakeDNS('www.ubuntu.com')
675 ns_query.return_value = []684 ns_query.return_value = []
676 with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]):685 with patch(builtin_import, side_effect=[fake_dns, fake_dns]):
677 hn = openstack.get_hostname('4.2.2.1')686 hn = openstack.get_hostname('4.2.2.1')
678 self.assertEquals(hn, None)687 self.assertEquals(hn, None)
679688
680 @patch('os.path.isfile')689 @patch('os.path.isfile')
681 @patch('__builtin__.open')690 @patch(builtin_open)
682 def test_get_matchmaker_map(self, _open, _isfile):691 def test_get_matchmaker_map(self, _open, _isfile):
683 _isfile.return_value = True692 _isfile.return_value = True
684 mm_data = """693 mm_data = """
@@ -696,7 +705,7 @@
696 )705 )
697706
698 @patch('os.path.isfile')707 @patch('os.path.isfile')
699 @patch('__builtin__.open')708 @patch(builtin_open)
700 def test_get_matchmaker_map_nofile(self, _open, _isfile):709 def test_get_matchmaker_map_nofile(self, _open, _isfile):
701 _isfile.return_value = False710 _isfile.return_value = False
702 self.assertEqual(711 self.assertEqual(
703712
=== modified file 'tests/contrib/openstack/test_os_contexts.py'
--- tests/contrib/openstack/test_os_contexts.py 2014-11-25 14:35:22 +0000
+++ tests/contrib/openstack/test_os_contexts.py 2014-11-25 15:09:14 +0000
@@ -11,6 +11,13 @@
11)11)
12from tests.helpers import patch_open12from tests.helpers import patch_open
1313
14import six
15
16if not six.PY3:
17 open_builtin = '__builtin__.open'
18else:
19 open_builtin = 'builtins.open'
20
1421
15class FakeRelation(object):22class FakeRelation(object):
1623
@@ -472,7 +479,7 @@
472 self.assertEquals(result, expected)479 self.assertEquals(result, expected)
473480
474 @patch('os.path.exists')481 @patch('os.path.exists')
475 @patch('__builtin__.open')482 @patch(open_builtin)
476 def test_db_ssl(self, _open, osexists):483 def test_db_ssl(self, _open, osexists):
477 osexists.return_value = False484 osexists.return_value = False
478 ssl_dir = '/etc/dbssl'485 ssl_dir = '/etc/dbssl'
@@ -725,7 +732,7 @@
725 }732 }
726 self.assertEquals(result, expected)733 self.assertEquals(result, expected)
727734
728 @patch('__builtin__.open')735 @patch(open_builtin)
729 def test_amqp_context_with_data_ssl(self, _open):736 def test_amqp_context_with_data_ssl(self, _open):
730 '''Test amqp context with all required data and ssl'''737 '''Test amqp context with all required data and ssl'''
731 relation = FakeRelation(relation_data=AMQP_RELATION_WITH_SSL)738 relation = FakeRelation(relation_data=AMQP_RELATION_WITH_SSL)
@@ -799,7 +806,7 @@
799 'rabbitmq_password': 'foobar',806 'rabbitmq_password': 'foobar',
800 'rabbitmq_user': 'adam',807 'rabbitmq_user': 'adam',
801 'rabbitmq_virtual_host': 'foo',808 'rabbitmq_virtual_host': 'foo',
802 'rabbitmq_hosts': 'rabbithost2,rabbithost1',809 'rabbitmq_hosts': 'rabbithost1,rabbithost2',
803 }810 }
804 self.assertEquals(result, expected)811 self.assertEquals(result, expected)
805812
@@ -868,7 +875,7 @@
868 ceph = context.CephContext()875 ceph = context.CephContext()
869 result = ceph()876 result = ceph()
870 expected = {877 expected = {
871 'mon_hosts': 'ceph_node2 ceph_node1',878 'mon_hosts': 'ceph_node1 ceph_node2',
872 'auth': 'foo',879 'auth': 'foo',
873 'key': 'bar',880 'key': 'bar',
874 'use_syslog': 'true'881 'use_syslog': 'true'
@@ -882,8 +889,8 @@
882 def test_ceph_context_with_missing_data(self, ensure_packages, mkdir):889 def test_ceph_context_with_missing_data(self, ensure_packages, mkdir):
883 '''Test ceph context with missing relation data'''890 '''Test ceph context with missing relation data'''
884 relation = copy(CEPH_RELATION)891 relation = copy(CEPH_RELATION)
885 for k, v in relation.iteritems():892 for k, v in six.iteritems(relation):
886 for u in v.iterkeys():893 for u in six.iterkeys(v):
887 del relation[k][u]['auth']894 del relation[k][u]['auth']
888 relation = FakeRelation(relation_data=relation)895 relation = FakeRelation(relation_data=relation)
889 self.relation_get.side_effect = relation.get896 self.relation_get.side_effect = relation.get
@@ -911,7 +918,7 @@
911 ceph = context.CephContext()918 ceph = context.CephContext()
912 result = ceph()919 result = ceph()
913 expected = {920 expected = {
914 'mon_hosts': '192.168.1.11 192.168.1.10',921 'mon_hosts': '192.168.1.10 192.168.1.11',
915 'auth': 'foo',922 'auth': 'foo',
916 'key': 'bar',923 'key': 'bar',
917 'use_syslog': 'true',924 'use_syslog': 'true',
@@ -1264,12 +1271,9 @@
1264 if len(vips) > 1:1271 if len(vips) > 1:
1265 ex = {1272 ex = {
1266 'namespace': 'cinder',1273 'namespace': 'cinder',
1267 'endpoints': [('10.5.1.100', '10.5.1.1',1274 'endpoints': [('10.5.1.100', '10.5.1.1', 8766, 8756),
1268 8766, 8756),1275 ('10.5.2.100', '10.5.2.1', 8766, 8756),
1269 ('10.5.2.100', '10.5.2.1',1276 ('10.5.3.100', '10.5.3.1', 8766, 8756)],
1270 8766, 8756),
1271 ('10.5.3.100', '10.5.3.1',
1272 8766, 8756)],
1273 'ext_ports': [8766]1277 'ext_ports': [8766]
1274 }1278 }
1275 else:1279 else:
@@ -1283,12 +1287,10 @@
1283 if multinet:1287 if multinet:
1284 ex = {1288 ex = {
1285 'namespace': 'cinder',1289 'namespace': 'cinder',
1286 'endpoints': [('10.5.3.100', '10.5.3.100',1290 'endpoints': sorted([
1287 8776, 8766),1291 ('10.5.3.100', '10.5.3.100', 8776, 8766),
1288 ('10.5.2.100', '10.5.2.100',1292 ('10.5.2.100', '10.5.2.100', 8776, 8766),
1289 8776, 8766),1293 ('10.5.1.100', '10.5.1.100', 8776, 8766)]),
1290 ('10.5.1.100', '10.5.1.100',
1291 8776, 8766)],
1292 'ext_ports': [8776]1294 'ext_ports': [8776]
1293 }1295 }
1294 else:1296 else:
@@ -1391,9 +1393,10 @@
1391 apache = context.ApacheSSLContext()1393 apache = context.ApacheSSLContext()
1392 self.assertEquals(apache.canonical_names(), ['cinderhost1'])1394 self.assertEquals(apache.canonical_names(), ['cinderhost1'])
1393 rel.relation_data = IDENTITY_RELATION_MULTIPLE_CERT1395 rel.relation_data = IDENTITY_RELATION_MULTIPLE_CERT
1394 self.assertEquals(apache.canonical_names(), ['cinderhost1-adm-network',1396 self.assertEquals(apache.canonical_names(),
1395 'cinderhost1-int-network',1397 sorted(['cinderhost1-adm-network',
1396 'cinderhost1-pub-network'])1398 'cinderhost1-int-network',
1399 'cinderhost1-pub-network']))
1397 rel.relation_data = IDENTITY_RELATION_NO_CERT1400 rel.relation_data = IDENTITY_RELATION_NO_CERT
1398 self.assertEquals(apache.canonical_names(), [])1401 self.assertEquals(apache.canonical_names(), [])
13991402
@@ -1903,17 +1906,15 @@
19031906
1904 def test_workerconfig_context_noconfig(self):1907 def test_workerconfig_context_noconfig(self):
1905 self.config.return_value = None1908 self.config.return_value = None
1906 with patch.object(context.WorkerConfigContext, 'num_cpus') as cpus:1909 with patch.object(context.WorkerConfigContext, 'num_cpus', 2):
1907 cpus.__get__ = Mock(return_value=2)
1908 worker = context.WorkerConfigContext()1910 worker = context.WorkerConfigContext()
1909 self.assertEqual({'workers': 2}, worker())1911 self.assertEqual({'workers': 0}, worker())
19101912
1911 def test_workerconfig_context_withconfig(self):1913 def test_workerconfig_context_withconfig(self):
1912 self.config.side_effect = fake_config({1914 self.config.side_effect = fake_config({
1913 'worker-multiplier': 4,1915 'worker-multiplier': 4,
1914 })1916 })
1915 with patch.object(context.WorkerConfigContext, 'num_cpus') as cpus:1917 with patch.object(context.WorkerConfigContext, 'num_cpus', 2):
1916 cpus.__get__ = Mock(return_value=2)
1917 worker = context.WorkerConfigContext()1918 worker = context.WorkerConfigContext()
1918 self.assertEqual({'workers': 8}, worker())1919 self.assertEqual({'workers': 8}, worker())
19191920
19201921
=== modified file 'tests/contrib/openstack/test_os_templating.py'
--- tests/contrib/openstack/test_os_templating.py 2014-11-25 14:35:22 +0000
+++ tests/contrib/openstack/test_os_templating.py 2014-11-25 15:09:14 +0000
@@ -1,7 +1,13 @@
11
2import os2import os
3
4import unittest3import unittest
4
5import six
6if not six.PY3:
7 builtin_open = '__builtin__.open'
8else:
9 builtin_open = 'builtins.open'
10
5from mock import patch, call, MagicMock11from mock import patch, call, MagicMock
612
7import charmhelpers.contrib.openstack.templating as templating13import charmhelpers.contrib.openstack.templating as templating
@@ -160,7 +166,7 @@
160 def test_render_template_by_basename(self):166 def test_render_template_by_basename(self):
161 '''It renders template if it finds it by config file basename'''167 '''It renders template if it finds it by config file basename'''
162168
163 @patch('__builtin__.open')169 @patch(builtin_open)
164 @patch.object(templating, 'get_loader')170 @patch.object(templating, 'get_loader')
165 def test_write_out_config(self, loader, _open):171 def test_write_out_config(self, loader, _open):
166 '''It writes a templated config when provided a complete context'''172 '''It writes a templated config when provided a complete context'''
@@ -183,7 +189,7 @@
183 ]189 ]
184 with patch.object(self.renderer, 'write') as _write:190 with patch.object(self.renderer, 'write') as _write:
185 self.renderer.write_all()191 self.renderer.write_all()
186 self.assertEquals(ex_calls, _write.call_args_list)192 self.assertEquals(sorted(ex_calls), sorted(_write.call_args_list))
187 pass193 pass
188194
189 @patch.object(templating, 'get_loader')195 @patch.object(templating, 'get_loader')
190196
=== modified file 'tests/contrib/storage/test_linux_ceph.py'
--- tests/contrib/storage/test_linux_ceph.py 2014-11-19 22:27:26 +0000
+++ tests/contrib/storage/test_linux_ceph.py 2014-11-25 15:09:14 +0000
@@ -14,19 +14,19 @@
14import time14import time
1515
1616
17LS_POOLS = """17LS_POOLS = b"""
18images18images
19volumes19volumes
20rbd20rbd
21"""21"""
2222
23LS_RBDS = """23LS_RBDS = b"""
24rbd124rbd1
25rbd225rbd2
26rbd326rbd3
27"""27"""
2828
29IMG_MAP = """29IMG_MAP = b"""
30bar30bar
31baz31baz
32"""32"""
@@ -93,7 +93,7 @@
93 @patch.object(ceph_utils, 'ceph_version')93 @patch.object(ceph_utils, 'ceph_version')
94 def test_get_osds(self, version):94 def test_get_osds(self, version):
95 version.return_value = '0.56.2'95 version.return_value = '0.56.2'
96 self.check_output.return_value = json.dumps([1, 2, 3])96 self.check_output.return_value = json.dumps([1, 2, 3]).encode('UTF-8')
97 self.assertEquals(ceph_utils.get_osds('test'), [1, 2, 3])97 self.assertEquals(ceph_utils.get_osds('test'), [1, 2, 3])
9898
99 @patch.object(ceph_utils, 'ceph_version')99 @patch.object(ceph_utils, 'ceph_version')
@@ -104,7 +104,7 @@
104 @patch.object(ceph_utils, 'ceph_version')104 @patch.object(ceph_utils, 'ceph_version')
105 def test_get_osds_none(self, version):105 def test_get_osds_none(self, version):
106 version.return_value = '0.56.2'106 version.return_value = '0.56.2'
107 self.check_output.return_value = json.dumps(None)107 self.check_output.return_value = json.dumps(None).encode('UTF-8')
108 self.assertEquals(ceph_utils.get_osds('test'), None)108 self.assertEquals(ceph_utils.get_osds('test'), None)
109109
110 @patch.object(ceph_utils, 'get_osds')110 @patch.object(ceph_utils, 'get_osds')
@@ -534,13 +534,13 @@
534 @patch('os.path.exists')534 @patch('os.path.exists')
535 def test_ceph_version_error(self, path, output):535 def test_ceph_version_error(self, path, output):
536 path.return_value = True536 path.return_value = True
537 output.return_value = ''537 output.return_value = b''
538 self.assertEquals(ceph_utils.ceph_version(), None)538 self.assertEquals(ceph_utils.ceph_version(), None)
539539
540 @patch.object(ceph_utils, 'check_output')540 @patch.object(ceph_utils, 'check_output')
541 @patch('os.path.exists')541 @patch('os.path.exists')
542 def test_ceph_version_ok(self, path, output):542 def test_ceph_version_ok(self, path, output):
543 path.return_value = True543 path.return_value = True
544 output.return_value = 'ceph version 0.67.4'\544 output.return_value = \
545 ' (ad85b8bfafea6232d64cb7ba76a8b6e8252fa0c7)'545 b'ceph version 0.67.4 (ad85b8bfafea6232d64cb7ba76a8b6e8252fa0c7)'
546 self.assertEquals(ceph_utils.ceph_version(), '0.67.4')546 self.assertEquals(ceph_utils.ceph_version(), '0.67.4')
547547
=== modified file 'tests/contrib/storage/test_linux_storage_lvm.py'
--- tests/contrib/storage/test_linux_storage_lvm.py 2014-07-03 12:36:50 +0000
+++ tests/contrib/storage/test_linux_storage_lvm.py 2014-11-25 15:09:14 +0000
@@ -5,7 +5,7 @@
55
6import charmhelpers.contrib.storage.linux.lvm as lvm6import charmhelpers.contrib.storage.linux.lvm as lvm
77
8PVDISPLAY = """8PVDISPLAY = b"""
9 --- Physical volume ---9 --- Physical volume ---
10 PV Name /dev/loop010 PV Name /dev/loop0
11 VG Name foo11 VG Name foo
@@ -19,7 +19,7 @@
1919
20"""20"""
2121
22EMPTY_VG_IN_PVDISPLAY = """22EMPTY_VG_IN_PVDISPLAY = b"""
23 --- Physical volume ---23 --- Physical volume ---
24 PV Name /dev/loop024 PV Name /dev/loop0
25 VG Name25 VG Name
2626
=== modified file 'tests/contrib/storage/test_linux_storage_utils.py'
--- tests/contrib/storage/test_linux_storage_utils.py 2014-07-31 08:55:15 +0000
+++ tests/contrib/storage/test_linux_storage_utils.py 2014-11-25 15:09:14 +0000
@@ -14,7 +14,7 @@
14 @patch(STORAGE_LINUX_UTILS + '.check_call')14 @patch(STORAGE_LINUX_UTILS + '.check_call')
15 def test_zap_disk(self, check_call, call, check_output):15 def test_zap_disk(self, check_call, call, check_output):
16 '''It calls sgdisk correctly to zap disk'''16 '''It calls sgdisk correctly to zap disk'''
17 check_output.return_value = '200\n'17 check_output.return_value = b'200\n'
18 storage_utils.zap_disk('/dev/foo')18 storage_utils.zap_disk('/dev/foo')
19 call.assert_any_call(['sgdisk', '--zap-all', '--mbrtogpt',19 call.assert_any_call(['sgdisk', '--zap-all', '--mbrtogpt',
20 '--clear', '/dev/foo'])20 '--clear', '/dev/foo'])
@@ -50,7 +50,7 @@
50 def test_is_device_mounted(self, check_output):50 def test_is_device_mounted(self, check_output):
51 '''It detects mounted devices as mounted.'''51 '''It detects mounted devices as mounted.'''
52 check_output.return_value = (52 check_output.return_value = (
53 "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")53 b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")
54 result = storage_utils.is_device_mounted('/dev/sda')54 result = storage_utils.is_device_mounted('/dev/sda')
55 self.assertTrue(result)55 self.assertTrue(result)
5656
@@ -58,7 +58,7 @@
58 def test_is_device_mounted_partition(self, check_output):58 def test_is_device_mounted_partition(self, check_output):
59 '''It detects mounted partitions as mounted.'''59 '''It detects mounted partitions as mounted.'''
60 check_output.return_value = (60 check_output.return_value = (
61 "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")61 b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")
62 result = storage_utils.is_device_mounted('/dev/sda1')62 result = storage_utils.is_device_mounted('/dev/sda1')
63 self.assertTrue(result)63 self.assertTrue(result)
6464
@@ -67,7 +67,7 @@
67 '''It detects mounted devices as mounted if "mount" shows only a67 '''It detects mounted devices as mounted if "mount" shows only a
68 partition as mounted.'''68 partition as mounted.'''
69 check_output.return_value = (69 check_output.return_value = (
70 "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")70 b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n")
71 result = storage_utils.is_device_mounted('/dev/sda')71 result = storage_utils.is_device_mounted('/dev/sda')
72 self.assertTrue(result)72 self.assertTrue(result)
7373
@@ -75,7 +75,7 @@
75 def test_is_device_mounted_not_mounted(self, check_output):75 def test_is_device_mounted_not_mounted(self, check_output):
76 '''It detects unmounted devices as not mounted.'''76 '''It detects unmounted devices as not mounted.'''
77 check_output.return_value = (77 check_output.return_value = (
78 "/dev/foo on / type ext4 (rw,errors=remount-ro)\n")78 b"/dev/foo on / type ext4 (rw,errors=remount-ro)\n")
79 result = storage_utils.is_device_mounted('/dev/sda')79 result = storage_utils.is_device_mounted('/dev/sda')
80 self.assertFalse(result)80 self.assertFalse(result)
8181
@@ -83,7 +83,7 @@
83 def test_is_device_mounted_not_mounted_partition(self, check_output):83 def test_is_device_mounted_not_mounted_partition(self, check_output):
84 '''It detects unmounted partitions as not mounted.'''84 '''It detects unmounted partitions as not mounted.'''
85 check_output.return_value = (85 check_output.return_value = (
86 "/dev/foo on / type ext4 (rw,errors=remount-ro)\n")86 b"/dev/foo on / type ext4 (rw,errors=remount-ro)\n")
87 result = storage_utils.is_device_mounted('/dev/sda1')87 result = storage_utils.is_device_mounted('/dev/sda1')
88 self.assertFalse(result)88 self.assertFalse(result)
8989
@@ -91,7 +91,7 @@
91 def test_is_device_mounted_cciss(self, check_output):91 def test_is_device_mounted_cciss(self, check_output):
92 '''It detects mounted cciss partitions as mounted.'''92 '''It detects mounted cciss partitions as mounted.'''
93 check_output.return_value = (93 check_output.return_value = (
94 "/dev/cciss/c0d0 on / type ext4 (rw,errors=remount-ro)\n")94 b"/dev/cciss/c0d0 on / type ext4 (rw,errors=remount-ro)\n")
95 result = storage_utils.is_device_mounted('/dev/cciss/c0d0')95 result = storage_utils.is_device_mounted('/dev/cciss/c0d0')
96 self.assertTrue(result)96 self.assertTrue(result)
9797
@@ -99,6 +99,6 @@
99 def test_is_device_mounted_cciss_not_mounted(self, check_output):99 def test_is_device_mounted_cciss_not_mounted(self, check_output):
100 '''It detects unmounted cciss partitions as not mounted.'''100 '''It detects unmounted cciss partitions as not mounted.'''
101 check_output.return_value = (101 check_output.return_value = (
102 "/dev/cciss/c0d1 on / type ext4 (rw,errors=remount-ro)\n")102 b"/dev/cciss/c0d1 on / type ext4 (rw,errors=remount-ro)\n")
103 result = storage_utils.is_device_mounted('/dev/cciss/c0d0')103 result = storage_utils.is_device_mounted('/dev/cciss/c0d0')
104 self.assertFalse(result)104 self.assertFalse(result)
105105
=== modified file 'tests/contrib/templating/test_contexts.py'
--- tests/contrib/templating/test_contexts.py 2014-08-13 20:52:09 +0000
+++ tests/contrib/templating/test_contexts.py 2014-11-25 15:09:14 +0000
@@ -9,6 +9,8 @@
9import unittest9import unittest
10import yaml10import yaml
1111
12import six
13
12import charmhelpers.contrib.templating.contexts14import charmhelpers.contrib.templating.contexts
1315
1416
@@ -134,12 +136,12 @@
134 }136 }
135 self.mock_relations.return_value = {137 self.mock_relations.return_value = {
136 'wsgi-file': {138 'wsgi-file': {
137 u'wsgi-file:0': {139 six.u('wsgi-file:0'): {
138 u'gunicorn/1': {140 six.u('gunicorn/1'): {
139 u'private-address': u'10.0.3.99',141 six.u('private-address'): six.u('10.0.3.99'),
140 },142 },
141 'click-index/3': {143 'click-index/3': {
142 u'wsgi_group': u'ubunet',144 six.u('wsgi_group'): six.u('ubunet'),
143 },145 },
144 },146 },
145 },147 },
@@ -162,8 +164,9 @@
162 expected["wsgi_file:relation_key2"] = "relation_value2"164 expected["wsgi_file:relation_key2"] = "relation_value2"
163 expected["relations_full"]['wsgi-file'] = {165 expected["relations_full"]['wsgi-file'] = {
164 'wsgi-file:0': {166 'wsgi-file:0': {
165 'gunicorn/1': {u'private-address': u'10.0.3.99'},167 'gunicorn/1': {
166 'click-index/3': {u'wsgi_group': u'ubunet'},168 six.u('private-address'): six.u('10.0.3.99')},
169 'click-index/3': {six.u('wsgi_group'): six.u('ubunet')},
167 },170 },
168 }171 }
169 expected["relations"]["wsgi-file"] = [172 expected["relations"]["wsgi-file"] = [
170173
=== modified file 'tests/contrib/unison/test_unison.py'
--- tests/contrib/unison/test_unison.py 2014-10-22 06:17:55 +0000
+++ tests/contrib/unison/test_unison.py 2014-11-25 15:09:14 +0000
@@ -110,13 +110,13 @@
110110
111 isfile.return_value = False111 isfile.return_value = False
112 with patch_open() as (_open, _file):112 with patch_open() as (_open, _file):
113 self.check_output.return_value = 'fookey'113 self.check_output.return_value = b'fookey'
114 unison.create_public_key(114 unison.create_public_key(
115 user='foo', priv_key_path='/home/foo/.ssh/id_rsa',115 user='foo', priv_key_path='/home/foo/.ssh/id_rsa',
116 pub_key_path='/home/foo/.ssh/id_rsa.pub')116 pub_key_path='/home/foo/.ssh/id_rsa.pub')
117 self.assertIn(call(create_cmd), self.check_output.call_args_list)117 self.assertIn(call(create_cmd), self.check_output.call_args_list)
118 _open.assert_called_with('/home/foo/.ssh/id_rsa.pub', 'wb')118 _open.assert_called_with('/home/foo/.ssh/id_rsa.pub', 'wb')
119 _file.write.assert_called_with('fookey')119 _file.write.assert_called_with(b'fookey')
120120
121 @patch('os.mkdir')121 @patch('os.mkdir')
122 @patch('os.path.isdir')122 @patch('os.path.isdir')
123123
=== modified file 'tests/core/test_fstab.py'
--- tests/core/test_fstab.py 2014-11-25 14:35:22 +0000
+++ tests/core/test_fstab.py 2014-11-25 15:09:14 +0000
@@ -24,7 +24,7 @@
24class FstabTest(unittest.TestCase):24class FstabTest(unittest.TestCase):
2525
26 def setUp(self):26 def setUp(self):
27 self.tempfile = tempfile.NamedTemporaryFile(delete=False)27 self.tempfile = tempfile.NamedTemporaryFile('w+', delete=False)
28 self.tempfile.write(DEFAULT_FSTAB_FILE)28 self.tempfile.write(DEFAULT_FSTAB_FILE)
29 self.tempfile.close()29 self.tempfile.close()
30 self.fstab = Fstab(path=self.tempfile.name)30 self.fstab = Fstab(path=self.tempfile.name)
3131
=== modified file 'tests/core/test_hookenv.py'
--- tests/core/test_hookenv.py 2014-11-25 14:35:22 +0000
+++ tests/core/test_hookenv.py 2014-11-25 15:09:14 +0000
@@ -3,14 +3,18 @@
3from subprocess import CalledProcessError3from subprocess import CalledProcessError
4import shutil4import shutil
5import tempfile5import tempfile
6
7import cPickle as pickle
8from mock import patch, call, mock_open6from mock import patch, call, mock_open
9from StringIO import StringIO
10from mock import MagicMock7from mock import MagicMock
11from testtools import TestCase8from testtools import TestCase
12import yaml9import yaml
1310
11import six
12from six.moves import StringIO
13if six.PY3:
14 import pickle
15else:
16 import cPickle as pickle
17
14from charmhelpers.core import hookenv18from charmhelpers.core import hookenv
1519
16CHARM_METADATA = """name: testmock20CHARM_METADATA = """name: testmock
@@ -125,7 +129,7 @@
125 def test_keys(self):129 def test_keys(self):
126 c = hookenv.Config(dict(foo='bar'))130 c = hookenv.Config(dict(foo='bar'))
127 c["baz"] = "bar"131 c["baz"] = "bar"
128 self.assertEqual([u"foo", "baz"], c.keys())132 self.assertEqual(sorted([six.u("foo"), "baz"]), sorted(c.keys()))
129133
130134
131class SerializableTest(TestCase):135class SerializableTest(TestCase):
@@ -165,11 +169,9 @@
165 }169 }
166 wrapped = hookenv.Serializable(foo)170 wrapped = hookenv.Serializable(foo)
167 for meth in ('keys', 'values', 'items'):171 for meth in ('keys', 'values', 'items'):
168 self.assertEqual(getattr(wrapped, meth)(), getattr(foo, meth)())172 self.assertEqual(sorted(list(getattr(wrapped, meth)())),
173 sorted(list(getattr(foo, meth)())))
169174
170 for meth in ('iterkeys', 'itervalues', 'iteritems'):
171 self.assertEqual(list(getattr(wrapped, meth)()),
172 list(getattr(foo, meth)()))
173 self.assertEqual(wrapped.get('bar'), foo.get('bar'))175 self.assertEqual(wrapped.get('bar'), foo.get('bar'))
174 self.assertEqual(wrapped.get('baz', 42), foo.get('baz', 42))176 self.assertEqual(wrapped.get('baz', 42), foo.get('baz', 42))
175 self.assertIn('bar', wrapped)177 self.assertIn('bar', wrapped)
@@ -246,7 +248,7 @@
246 @patch('subprocess.check_output')248 @patch('subprocess.check_output')
247 def test_gets_charm_config_with_scope(self, check_output):249 def test_gets_charm_config_with_scope(self, check_output):
248 config_data = 'bar'250 config_data = 'bar'
249 check_output.return_value = json.dumps(config_data)251 check_output.return_value = json.dumps(config_data).encode('UTF-8')
250252
251 result = hookenv.config(scope='baz')253 result = hookenv.config(scope='baz')
252254
@@ -257,11 +259,11 @@
257 self.assertEqual(result[1], 'a')259 self.assertEqual(result[1], 'a')
258260
259 # ... because the result is actually a string261 # ... because the result is actually a string
260 self.assert_(isinstance(result, basestring))262 self.assert_(isinstance(result, six.string_types))
261263
262 @patch('subprocess.check_output')264 @patch('subprocess.check_output')
263 def test_gets_missing_charm_config_with_scope(self, check_output):265 def test_gets_missing_charm_config_with_scope(self, check_output):
264 check_output.return_value = ''266 check_output.return_value = b''
265267
266 result = hookenv.config(scope='baz')268 result = hookenv.config(scope='baz')
267269
@@ -271,7 +273,7 @@
271 @patch('charmhelpers.core.hookenv.charm_dir')273 @patch('charmhelpers.core.hookenv.charm_dir')
272 @patch('subprocess.check_output')274 @patch('subprocess.check_output')
273 def test_gets_config_without_scope(self, check_output, charm_dir):275 def test_gets_config_without_scope(self, check_output, charm_dir):
274 check_output.return_value = json.dumps(dict(foo='bar'))276 check_output.return_value = json.dumps(dict(foo='bar')).encode('UTF-8')
275 charm_dir.side_effect = tempfile.mkdtemp277 charm_dir.side_effect = tempfile.mkdtemp
276278
277 result = hookenv.config()279 result = hookenv.config()
@@ -327,7 +329,7 @@
327 @patch('charmhelpers.core.hookenv.relation_type')329 @patch('charmhelpers.core.hookenv.relation_type')
328 def test_gets_relation_ids(self, relation_type, check_output):330 def test_gets_relation_ids(self, relation_type, check_output):
329 ids = [1, 2, 3]331 ids = [1, 2, 3]
330 check_output.return_value = json.dumps(ids)332 check_output.return_value = json.dumps(ids).encode('UTF-8')
331 reltype = 'foo'333 reltype = 'foo'
332 relation_type.return_value = reltype334 relation_type.return_value = reltype
333335
@@ -341,7 +343,7 @@
341 @patch('charmhelpers.core.hookenv.relation_type')343 @patch('charmhelpers.core.hookenv.relation_type')
342 def test_gets_relation_ids_empty_array(self, relation_type, check_output):344 def test_gets_relation_ids_empty_array(self, relation_type, check_output):
343 ids = []345 ids = []
344 check_output.return_value = json.dumps(None)346 check_output.return_value = json.dumps(None).encode('UTF-8')
345 reltype = 'foo'347 reltype = 'foo'
346 relation_type.return_value = reltype348 relation_type.return_value = reltype
347349
@@ -355,7 +357,7 @@
355 @patch('charmhelpers.core.hookenv.relation_type')357 @patch('charmhelpers.core.hookenv.relation_type')
356 def test_relation_ids_no_relation_type(self, relation_type, check_output):358 def test_relation_ids_no_relation_type(self, relation_type, check_output):
357 ids = [1, 2, 3]359 ids = [1, 2, 3]
358 check_output.return_value = json.dumps(ids)360 check_output.return_value = json.dumps(ids).encode('UTF-8')
359 relation_type.return_value = None361 relation_type.return_value = None
360362
361 result = hookenv.relation_ids()363 result = hookenv.relation_ids()
@@ -366,7 +368,7 @@
366 @patch('charmhelpers.core.hookenv.relation_type')368 @patch('charmhelpers.core.hookenv.relation_type')
367 def test_gets_relation_ids_for_type(self, relation_type, check_output):369 def test_gets_relation_ids_for_type(self, relation_type, check_output):
368 ids = [1, 2, 3]370 ids = [1, 2, 3]
369 check_output.return_value = json.dumps(ids)371 check_output.return_value = json.dumps(ids).encode('UTF-8')
370 reltype = 'foo'372 reltype = 'foo'
371373
372 result = hookenv.relation_ids(reltype)374 result = hookenv.relation_ids(reltype)
@@ -382,7 +384,7 @@
382 relid = 123384 relid = 123
383 units = ['foo', 'bar']385 units = ['foo', 'bar']
384 relation_id.return_value = relid386 relation_id.return_value = relid
385 check_output.return_value = json.dumps(units)387 check_output.return_value = json.dumps(units).encode('UTF-8')
386388
387 result = hookenv.related_units()389 result = hookenv.related_units()
388390
@@ -393,10 +395,10 @@
393 @patch('subprocess.check_output')395 @patch('subprocess.check_output')
394 @patch('charmhelpers.core.hookenv.relation_id')396 @patch('charmhelpers.core.hookenv.relation_id')
395 def test_gets_related_units_empty_array(self, relation_id, check_output):397 def test_gets_related_units_empty_array(self, relation_id, check_output):
396 relid = 123398 relid = str(123)
397 units = []399 units = []
398 relation_id.return_value = relid400 relation_id.return_value = relid
399 check_output.return_value = json.dumps(None)401 check_output.return_value = json.dumps(None).encode('UTF-8')
400402
401 result = hookenv.related_units()403 result = hookenv.related_units()
402404
@@ -409,7 +411,7 @@
409 def test_related_units_no_relation(self, relation_id, check_output):411 def test_related_units_no_relation(self, relation_id, check_output):
410 units = ['foo', 'bar']412 units = ['foo', 'bar']
411 relation_id.return_value = None413 relation_id.return_value = None
412 check_output.return_value = json.dumps(units)414 check_output.return_value = json.dumps(units).encode('UTF-8')
413415
414 result = hookenv.related_units()416 result = hookenv.related_units()
415417
@@ -421,7 +423,7 @@
421 def test_gets_related_units_for_id(self, relation_id, check_output):423 def test_gets_related_units_for_id(self, relation_id, check_output):
422 relid = 123424 relid = 123
423 units = ['foo', 'bar']425 units = ['foo', 'bar']
424 check_output.return_value = json.dumps(units)426 check_output.return_value = json.dumps(units).encode('UTF-8')
425427
426 result = hookenv.related_units(relid)428 result = hookenv.related_units(relid)
427429
@@ -765,7 +767,7 @@
765 @patch('subprocess.check_output')767 @patch('subprocess.check_output')
766 def test_gets_relation(self, check_output):768 def test_gets_relation(self, check_output):
767 data = {"foo": "BAR"}769 data = {"foo": "BAR"}
768 check_output.return_value = json.dumps(data)770 check_output.return_value = json.dumps(data).encode('UTF-8')
769 result = hookenv.relation_get()771 result = hookenv.relation_get()
770772
771 self.assertEqual(result['foo'], 'BAR')773 self.assertEqual(result['foo'], 'BAR')
@@ -773,7 +775,7 @@
773775
774 @patch('charmhelpers.core.hookenv.subprocess')776 @patch('charmhelpers.core.hookenv.subprocess')
775 def test_relation_get_none(self, mock_subprocess):777 def test_relation_get_none(self, mock_subprocess):
776 mock_subprocess.check_output.return_value = 'null'778 mock_subprocess.check_output.return_value = b'null'
777779
778 result = hookenv.relation_get()780 result = hookenv.relation_get()
779781
@@ -801,7 +803,7 @@
801803
802 @patch('subprocess.check_output')804 @patch('subprocess.check_output')
803 def test_gets_relation_with_scope(self, check_output):805 def test_gets_relation_with_scope(self, check_output):
804 check_output.return_value = json.dumps('bar')806 check_output.return_value = json.dumps('bar').encode('UTF-8')
805807
806 result = hookenv.relation_get(attribute='baz-scope')808 result = hookenv.relation_get(attribute='baz-scope')
807809
@@ -811,7 +813,7 @@
811813
812 @patch('subprocess.check_output')814 @patch('subprocess.check_output')
813 def test_gets_missing_relation_with_scope(self, check_output):815 def test_gets_missing_relation_with_scope(self, check_output):
814 check_output.return_value = ""816 check_output.return_value = b""
815817
816 result = hookenv.relation_get(attribute='baz-scope')818 result = hookenv.relation_get(attribute='baz-scope')
817819
@@ -821,7 +823,7 @@
821823
822 @patch('subprocess.check_output')824 @patch('subprocess.check_output')
823 def test_gets_relation_with_unit_name(self, check_output):825 def test_gets_relation_with_unit_name(self, check_output):
824 check_output.return_value = json.dumps('BAR')826 check_output.return_value = json.dumps('BAR').encode('UTF-8')
825827
826 result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit')828 result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit')
827829
@@ -834,7 +836,7 @@
834 @patch('subprocess.check_output')836 @patch('subprocess.check_output')
835 def test_relation_set_flushes_local_unit_cache(self, check_output,837 def test_relation_set_flushes_local_unit_cache(self, check_output,
836 check_call, local_unit):838 check_call, local_unit):
837 check_output.return_value = json.dumps('BAR')839 check_output.return_value = json.dumps('BAR').encode('UTF-8')
838 local_unit.return_value = 'baz_unit'840 local_unit.return_value = 'baz_unit'
839 hookenv.relation_get(attribute='baz_scope', unit='baz_unit')841 hookenv.relation_get(attribute='baz_scope', unit='baz_unit')
840 hookenv.relation_get(attribute='bar_scope')842 hookenv.relation_get(attribute='bar_scope')
@@ -845,7 +847,7 @@
845847
846 @patch('subprocess.check_output')848 @patch('subprocess.check_output')
847 def test_gets_relation_with_relation_id(self, check_output):849 def test_gets_relation_with_relation_id(self, check_output):
848 check_output.return_value = json.dumps('BAR')850 check_output.return_value = json.dumps('BAR').encode('UTF-8')
849851
850 result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit',852 result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit',
851 rid=123)853 rid=123)
@@ -910,13 +912,13 @@
910912
911 @patch('subprocess.check_output')913 @patch('subprocess.check_output')
912 def test_gets_unit_attribute(self, check_output_):914 def test_gets_unit_attribute(self, check_output_):
913 check_output_.return_value = json.dumps('bar')915 check_output_.return_value = json.dumps('bar').encode('UTF-8')
914 self.assertEqual(hookenv.unit_get('foo'), 'bar')916 self.assertEqual(hookenv.unit_get('foo'), 'bar')
915 check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])917 check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])
916918
917 @patch('subprocess.check_output')919 @patch('subprocess.check_output')
918 def test_gets_missing_unit_attribute(self, check_output_):920 def test_gets_missing_unit_attribute(self, check_output_):
919 check_output_.return_value = ""921 check_output_.return_value = b""
920 self.assertEqual(hookenv.unit_get('foo'), None)922 self.assertEqual(hookenv.unit_get('foo'), None)
921 check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])923 check_output_.assert_called_with(['unit-get', '--format=json', 'foo'])
922924
@@ -925,8 +927,8 @@
925 values = {927 values = {
926 'hello': 'world',928 'hello': 'world',
927 'foo': 'bar',929 'foo': 'bar',
928 'baz': None930 'baz': None,
929 }931 }
930932
931 @hookenv.cached933 @hookenv.cached
932 def cache_function(attribute):934 def cache_function(attribute):
933935
=== modified file 'tests/core/test_host.py'
--- tests/core/test_host.py 2014-11-25 14:35:22 +0000
+++ tests/core/test_host.py 2014-11-25 15:09:14 +0000
@@ -19,30 +19,30 @@
19 """rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 019 """rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
20""").strip().split('\n')20""").strip().split('\n')
2121
22LSB_RELEASE = u'''DISTRIB_ID=Ubuntu22LSB_RELEASE = '''DISTRIB_ID=Ubuntu
23DISTRIB_RELEASE=13.1023DISTRIB_RELEASE=13.10
24DISTRIB_CODENAME=saucy24DISTRIB_CODENAME=saucy
25DISTRIB_DESCRIPTION="Ubuntu Saucy Salamander (development branch)"25DISTRIB_DESCRIPTION="Ubuntu Saucy Salamander (development branch)"
26'''26'''
2727
28IP_LINE_ETH0 = ("""28IP_LINE_ETH0 = b"""
292: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP qlen 1000292: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP qlen 1000
30 link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff30 link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff
31""")31"""
3232
33IP_LINE_ETH1 = ("""33IP_LINE_ETH1 = b"""
343: eth1: <BROADCAST,MULTICAST> mtu 1546 qdisc noop state DOWN qlen 1000343: eth1: <BROADCAST,MULTICAST> mtu 1546 qdisc noop state DOWN qlen 1000
35 link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff35 link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff
36""")36"""
3737
38IP_LINE_HWADDR = ("""2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff""")38IP_LINE_HWADDR = b"""2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000\ link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff"""
3939
40IP_LINES = IP_LINE_ETH0 + IP_LINE_ETH140IP_LINES = IP_LINE_ETH0 + IP_LINE_ETH1
4141
42IP_LINE_BONDS = ("""42IP_LINE_BONDS = b"""
436: bond0.10@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default436: bond0.10@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
44link/ether 08:00:27:16:b9:5f brd ff:ff:ff:ff:ff:ff44link/ether 08:00:27:16:b9:5f brd ff:ff:ff:ff:ff:ff
45""")45"""
4646
4747
48class HelpersTest(TestCase):48class HelpersTest(TestCase):
@@ -166,12 +166,12 @@
166166
167 @patch('subprocess.check_output')167 @patch('subprocess.check_output')
168 def test_service_running_on_stopped_service(self, check_output):168 def test_service_running_on_stopped_service(self, check_output):
169 check_output.return_value = 'foo stop/waiting'169 check_output.return_value = b'foo stop/waiting'
170 self.assertFalse(host.service_running('foo'))170 self.assertFalse(host.service_running('foo'))
171171
172 @patch('subprocess.check_output')172 @patch('subprocess.check_output')
173 def test_service_running_on_running_service(self, check_output):173 def test_service_running_on_running_service(self, check_output):
174 check_output.return_value = 'foo start/running, process 23871'174 check_output.return_value = b'foo start/running, process 23871'
175 self.assertTrue(host.service_running('foo'))175 self.assertTrue(host.service_running('foo'))
176176
177 @patch('subprocess.check_output')177 @patch('subprocess.check_output')
@@ -284,7 +284,7 @@
284 def test_rsyncs_a_path(self, log, check_output):284 def test_rsyncs_a_path(self, log, check_output):
285 from_path = '/from/this/path/foo'285 from_path = '/from/this/path/foo'
286 to_path = '/to/this/path/bar'286 to_path = '/to/this/path/bar'
287 check_output.return_value = ' some output '287 check_output.return_value = b' some output ' # Spaces will be stripped
288288
289 result = host.rsync(from_path, to_path)289 result = host.rsync(from_path, to_path)
290290
@@ -319,7 +319,7 @@
319 path = '/some/other/path/from/link'319 path = '/some/other/path/from/link'
320 realpath = '/some/path'320 realpath = '/some/path'
321 path_exists = False321 path_exists = False
322 perms = 0644322 perms = 0o644
323323
324 getpwnam.return_value.pw_uid = uid324 getpwnam.return_value.pw_uid = uid
325 getgrnam.return_value.gr_gid = gid325 getgrnam.return_value.gr_gid = gid
@@ -343,7 +343,7 @@
343 path = '/some/other/path/from/link'343 path = '/some/other/path/from/link'
344 realpath = '/some/path'344 realpath = '/some/path'
345 path_exists = False345 path_exists = False
346 perms = 0555346 perms = 0o555
347347
348 os_.path.abspath.return_value = realpath348 os_.path.abspath.return_value = realpath
349 os_.path.exists.return_value = path_exists349 os_.path.exists.return_value = path_exists
@@ -370,7 +370,7 @@
370 path_exists = True370 path_exists = True
371 force = True371 force = True
372 is_dir = False372 is_dir = False
373 perms = 0644373 perms = 0o644
374374
375 getpwnam.return_value.pw_uid = uid375 getpwnam.return_value.pw_uid = uid
376 getgrnam.return_value.gr_gid = gid376 getgrnam.return_value.gr_gid = gid
@@ -402,7 +402,7 @@
402 group = 'some-group-{bar}'402 group = 'some-group-{bar}'
403 path = '/some/path/{baz}'403 path = '/some/path/{baz}'
404 contents = 'what is {juju}'404 contents = 'what is {juju}'
405 perms = 0644405 perms = 0o644
406 fileno = 'some-fileno'406 fileno = 'some-fileno'
407407
408 getpwnam.return_value.pw_uid = uid408 getpwnam.return_value.pw_uid = uid
@@ -428,7 +428,7 @@
428 gid = 0428 gid = 0
429 path = '/some/path/{baz}'429 path = '/some/path/{baz}'
430 fmtstr = 'what is {juju}'430 fmtstr = 'what is {juju}'
431 perms = 0444431 perms = 0o444
432 fileno = 'some-fileno'432 fileno = 'some-fileno'
433433
434 with patch_open() as (mock_open, mock_file):434 with patch_open() as (mock_open, mock_file):
@@ -636,7 +636,7 @@
636636
637 @host.restart_on_change(restart_map)637 @host.restart_on_change(restart_map)
638 def make_some_changes(mock_file):638 def make_some_changes(mock_file):
639 mock_file.read.return_value = "newstuff"639 mock_file.read.return_value = b"newstuff"
640640
641 with patch_open() as (mock_open, mock_file):641 with patch_open() as (mock_open, mock_file):
642 make_some_changes(mock_file)642 make_some_changes(mock_file)
@@ -664,7 +664,7 @@
664 pass664 pass
665665
666 with patch_open() as (mock_open, mock_file):666 with patch_open() as (mock_open, mock_file):
667 mock_file.read.side_effect = ['exists', 'missing', 'exists2']667 mock_file.read.side_effect = [b'exists', b'missing', b'exists2']
668 make_some_changes()668 make_some_changes()
669669
670 # Restart should only happen once per service670 # Restart should only happen once per service
@@ -691,7 +691,7 @@
691 pass691 pass
692692
693 with patch_open() as (mock_open, mock_file):693 with patch_open() as (mock_open, mock_file):
694 mock_file.read.side_effect = ['exists', 'missing', 'exists2']694 mock_file.read.side_effect = [b'exists', b'missing', b'exists2']
695 make_some_changes()695 make_some_changes()
696696
697 # Restarts should happen in the order they are described in the697 # Restarts should happen in the order they are described in the
@@ -713,7 +713,6 @@
713 with mocked_open('/etc/lsb-release', LSB_RELEASE):713 with mocked_open('/etc/lsb-release', LSB_RELEASE):
714 lsb_release = host.lsb_release()714 lsb_release = host.lsb_release()
715 for key in result:715 for key in result:
716 print lsb_release
717 self.assertEqual(result[key], lsb_release[key])716 self.assertEqual(result[key], lsb_release[key])
718717
719 def test_pwgen(self):718 def test_pwgen(self):
720719
=== modified file 'tests/core/test_services.py'
--- tests/core/test_services.py 2014-11-25 14:35:22 +0000
+++ tests/core/test_services.py 2014-11-25 15:09:14 +0000
@@ -573,7 +573,7 @@
573 with mock.patch.object(services.helpers, 'open', mopen, create=True):573 with mock.patch.object(services.helpers, 'open', mopen, create=True):
574 services.helpers.StoredContext('foo.yaml', {'key': 'val'})574 services.helpers.StoredContext('foo.yaml', {'key': 'val'})
575 mopen.assert_called_once_with('charm_dir/foo.yaml', 'w')575 mopen.assert_called_once_with('charm_dir/foo.yaml', 'w')
576 fchmod.assert_called_once_with(mopen.return_value.fileno(), 0600)576 fchmod.assert_called_once_with(mopen.return_value.fileno(), 0o600)
577 yaml.dump.assert_called_once_with({'key': 'val'}, mopen.return_value)577 yaml.dump.assert_called_once_with({'key': 'val'}, mopen.return_value)
578578
579 @mock.patch.object(hookenv, 'charm_dir', lambda: 'charm_dir')579 @mock.patch.object(hookenv, 'charm_dir', lambda: 'charm_dir')
@@ -637,7 +637,7 @@
637 callback(manager, 'test', 'event')637 callback(manager, 'test', 'event')
638 mtemplating.render.assert_called_once_with(638 mtemplating.render.assert_called_once_with(
639 'foo.yml', 'bar.yml', {'foo': 'bar'},639 'foo.yml', 'bar.yml', {'foo': 'bar'},
640 'root', 'root', 0444)640 'root', 'root', 0o444)
641641
642 @mock.patch.object(services.helpers, 'templating')642 @mock.patch.object(services.helpers, 'templating')
643 def test_template_explicit(self, mtemplating):643 def test_template_explicit(self, mtemplating):
@@ -645,14 +645,14 @@
645 'required_data': [{'foo': 'bar'}]}})645 'required_data': [{'foo': 'bar'}]}})
646 callback = services.template(646 callback = services.template(
647 source='foo.yml', target='bar.yml',647 source='foo.yml', target='bar.yml',
648 owner='user', group='group', perms=0555648 owner='user', group='group', perms=0o555
649 )649 )
650 assert isinstance(callback, services.ManagerCallback)650 assert isinstance(callback, services.ManagerCallback)
651 assert not mtemplating.render.called651 assert not mtemplating.render.called
652 callback(manager, 'test', 'event')652 callback(manager, 'test', 'event')
653 mtemplating.render.assert_called_once_with(653 mtemplating.render.assert_called_once_with(
654 'foo.yml', 'bar.yml', {'foo': 'bar'},654 'foo.yml', 'bar.yml', {'foo': 'bar'},
655 'user', 'group', 0555)655 'user', 'group', 0o555)
656656
657657
658class TestPortsCallback(unittest.TestCase):658class TestPortsCallback(unittest.TestCase):
659659
=== modified file 'tests/core/test_sysctl.py'
--- tests/core/test_sysctl.py 2014-11-25 14:35:22 +0000
+++ tests/core/test_sysctl.py 2014-11-25 15:09:14 +0000
@@ -4,11 +4,18 @@
4__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'4__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
55
6from charmhelpers.core.sysctl import create6from charmhelpers.core.sysctl import create
7import io
7from mock import patch, MagicMock8from mock import patch, MagicMock
8
9import unittest9import unittest
10import tempfile10import tempfile
1111
12import six
13if not six.PY3:
14 builtin_open = '__builtin__.open'
15else:
16 builtin_open = 'builtins.open'
17
18
12TO_PATCH = [19TO_PATCH = [
13 'log',20 'log',
14 'check_call',21 'check_call',
@@ -27,10 +34,10 @@
27 self.addCleanup(_m.stop)34 self.addCleanup(_m.stop)
28 return mock35 return mock
2936
30 @patch('__builtin__.open')37 @patch(builtin_open)
31 def test_create(self, mock_open):38 def test_create(self, mock_open):
32 """Test create sysctl method"""39 """Test create sysctl method"""
33 _file = MagicMock(spec=file)40 _file = MagicMock(spec=io.FileIO)
34 mock_open.return_value = _file41 mock_open.return_value = _file
3542
36 create('{"kernel.max_pid": 1337}', "/etc/sysctl.d/test-sysctl.conf")43 create('{"kernel.max_pid": 1337}', "/etc/sysctl.d/test-sysctl.conf")
3744
=== modified file 'tests/fetch/test_archiveurl.py'
--- tests/fetch/test_archiveurl.py 2014-11-25 14:35:22 +0000
+++ tests/fetch/test_archiveurl.py 2014-11-25 15:09:14 +0000
@@ -1,6 +1,14 @@
1import os1import os
2from testtools import TestCase2
3from urlparse import urlparse3import six
4if six.PY3:
5 from urllib.parse import urlparse
6 from urllib.error import URLError
7else:
8 from urllib2 import URLError
9 from urlparse import urlparse
10
11from unittest import TestCase
4from mock import (12from mock import (
5 MagicMock,13 MagicMock,
6 patch,14 patch,
@@ -11,7 +19,6 @@
11 archiveurl,19 archiveurl,
12 UnhandledSource,20 UnhandledSource,
13)21)
14import urllib2
1522
1623
17class ArchiveUrlFetchHandlerTest(TestCase):24class ArchiveUrlFetchHandlerTest(TestCase):
@@ -50,7 +57,7 @@
50 result = self.fh.can_handle(url)57 result = self.fh.can_handle(url)
51 self.assertNotEqual(result, True, url)58 self.assertNotEqual(result, True, url)
5259
53 @patch('urllib2.urlopen')60 @patch('charmhelpers.fetch.archiveurl.urlopen')
54 def test_downloads(self, _urlopen):61 def test_downloads(self, _urlopen):
55 for url in self.valid_urls:62 for url in self.valid_urls:
56 response = MagicMock()63 response = MagicMock()
@@ -87,7 +94,7 @@
8794
88 url = "http://www.example.com/archive.tar.gz"95 url = "http://www.example.com/archive.tar.gz"
8996
90 self.fh.download.side_effect = urllib2.URLError('fail')97 self.fh.download.side_effect = URLError('fail')
91 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):98 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
92 self.assertRaises(UnhandledSource, self.fh.install, url)99 self.assertRaises(UnhandledSource, self.fh.install, url)
93100
94101
=== modified file 'tests/fetch/test_bzrurl.py'
--- tests/fetch/test_bzrurl.py 2014-11-25 14:35:22 +0000
+++ tests/fetch/test_bzrurl.py 2014-11-25 15:09:14 +0000
@@ -1,20 +1,34 @@
1import os1import os
2from testtools import TestCase2from testtools import TestCase
3from urlparse import urlparse
4from mock import (3from mock import (
5 MagicMock,4 MagicMock,
6 patch,5 patch,
7)6)
8from charmhelpers.fetch import (7import unittest
9 bzrurl,8
10 UnhandledSource,9import six
11)10if six.PY3:
1211 from urllib.parse import urlparse
1312else:
13 from urlparse import urlparse
14
15try:
16 from charmhelpers.fetch import (
17 bzrurl,
18 UnhandledSource,
19 )
20except ImportError:
21 bzrurl = None
22 UnhandledSource = None
23
24
25@unittest.skipIf(six.PY3, 'bzr does not support Python 3')
14class BzrUrlFetchHandlerTest(TestCase):26class BzrUrlFetchHandlerTest(TestCase):
1527
16 def setUp(self):28 def setUp(self):
17 super(BzrUrlFetchHandlerTest, self).setUp()29 super(BzrUrlFetchHandlerTest, self).setUp()
30 if six.PY3:
31 return
18 self.valid_urls = (32 self.valid_urls = (
19 "bzr+ssh://example.com/branch-name",33 "bzr+ssh://example.com/branch-name",
20 "bzr+ssh://example.com/branch-name/",34 "bzr+ssh://example.com/branch-name/",
@@ -41,6 +55,7 @@
41 )55 )
42 self.fh = bzrurl.BzrUrlFetchHandler()56 self.fh = bzrurl.BzrUrlFetchHandler()
4357
58 @unittest.skipIf(six.PY3, 'bzr does not support Python 3')
44 def test_handles_bzr_urls(self):59 def test_handles_bzr_urls(self):
45 for url in self.valid_urls:60 for url in self.valid_urls:
46 result = self.fh.can_handle(url)61 result = self.fh.can_handle(url)
@@ -49,6 +64,7 @@
49 result = self.fh.can_handle(url)64 result = self.fh.can_handle(url)
50 self.assertNotEqual(result, True, url)65 self.assertNotEqual(result, True, url)
5166
67 @unittest.skipIf(six.PY3, 'bzr does not support Python 3')
52 @patch('bzrlib.branch.Branch.open')68 @patch('bzrlib.branch.Branch.open')
53 def test_branch(self, _open):69 def test_branch(self, _open):
54 dest_path = "/destination/path"70 dest_path = "/destination/path"
@@ -63,6 +79,7 @@
63 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):79 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
64 self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path)80 self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path)
6581
82 @unittest.skipIf(six.PY3, 'bzr does not support Python 3')
66 @patch('charmhelpers.fetch.bzrurl.mkdir')83 @patch('charmhelpers.fetch.bzrurl.mkdir')
67 def test_installs(self, _mkdir):84 def test_installs(self, _mkdir):
68 self.fh.branch = MagicMock()85 self.fh.branch = MagicMock()
@@ -73,4 +90,4 @@
73 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):90 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
74 where = self.fh.install(url)91 where = self.fh.install(url)
75 self.assertEqual(where, dest)92 self.assertEqual(where, dest)
76 _mkdir.assert_called_with(where, perms=0755)93 _mkdir.assert_called_with(where, perms=0o755)
7794
=== modified file 'tests/fetch/test_fetch.py'
--- tests/fetch/test_fetch.py 2014-11-25 14:35:22 +0000
+++ tests/fetch/test_fetch.py 2014-11-25 15:09:14 +0000
@@ -1,4 +1,3 @@
1from cStringIO import StringIO
2import subprocess1import subprocess
32
4from tests.helpers import patch_open3from tests.helpers import patch_open
@@ -8,11 +7,18 @@
8 MagicMock,7 MagicMock,
9 call,8 call,
10)9)
11from urlparse import urlparse
12from charmhelpers import fetch10from charmhelpers import fetch
13import os11import os
14import yaml12import yaml
1513
14import six
15from six.moves import StringIO
16if six.PY3:
17 from urllib.parse import urlparse
18else:
19 from urlparse import urlparse
20
21
16FAKE_APT_CACHE = {22FAKE_APT_CACHE = {
17 # an installed package23 # an installed package
18 'vim': {24 'vim': {
@@ -404,7 +410,11 @@
404 @patch('charmhelpers.fetch.log')410 @patch('charmhelpers.fetch.log')
405 def test_plugins_are_valid(self, log_):411 def test_plugins_are_valid(self, log_):
406 plugins = fetch.plugins()412 plugins = fetch.plugins()
407 self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins))413 if not six.PY3:
414 self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins))
415 else:
416 # No bzr or git libraries for Python3.
417 self.assertEqual(len(fetch.FETCH_HANDLERS) - 2, len(plugins))
408418
409419
410class BaseFetchHandlerTest(TestCase):420class BaseFetchHandlerTest(TestCase):
411421
=== modified file 'tests/fetch/test_giturl.py'
--- tests/fetch/test_giturl.py 2014-11-25 14:35:22 +0000
+++ tests/fetch/test_giturl.py 2014-11-25 15:09:14 +0000
@@ -1,18 +1,31 @@
1import os1import os
2from testtools import TestCase2from testtools import TestCase
3from urlparse import urlparse
4from mock import (3from mock import (
5 MagicMock,4 MagicMock,
6 patch,5 patch,
7)6)
8from charmhelpers.fetch import (7import unittest
9 giturl,8
10 UnhandledSource,9import six
11)10if six.PY3:
1211 from urllib.parse import urlparse
1312else:
13 from urlparse import urlparse
14
15try:
16 from charmhelpers.fetch import (
17 giturl,
18 UnhandledSource,
19 )
20except ImportError:
21 giturl = None
22 UnhandledSource = None
23
24
25@unittest.skipIf(six.PY3, 'git does not support Python 3')
14class GitUrlFetchHandlerTest(TestCase):26class GitUrlFetchHandlerTest(TestCase):
1527
28 @unittest.skipIf(six.PY3, 'git does not support Python 3')
16 def setUp(self):29 def setUp(self):
17 super(GitUrlFetchHandlerTest, self).setUp()30 super(GitUrlFetchHandlerTest, self).setUp()
18 self.valid_urls = (31 self.valid_urls = (
@@ -27,6 +40,7 @@
27 )40 )
28 self.fh = giturl.GitUrlFetchHandler()41 self.fh = giturl.GitUrlFetchHandler()
2942
43 @unittest.skipIf(six.PY3, 'git does not support Python 3')
30 def test_handles_git_urls(self):44 def test_handles_git_urls(self):
31 for url in self.valid_urls:45 for url in self.valid_urls:
32 result = self.fh.can_handle(url)46 result = self.fh.can_handle(url)
@@ -35,6 +49,7 @@
35 result = self.fh.can_handle(url)49 result = self.fh.can_handle(url)
36 self.assertNotEqual(result, True, url)50 self.assertNotEqual(result, True, url)
3751
52 @unittest.skipIf(six.PY3, 'git does not support Python 3')
38 @patch('git.Repo.clone_from')53 @patch('git.Repo.clone_from')
39 def test_branch(self, _clone_from):54 def test_branch(self, _clone_from):
40 dest_path = "/destination/path"55 dest_path = "/destination/path"
@@ -52,6 +67,7 @@
52 dest_path,67 dest_path,
53 branch)68 branch)
5469
70 @unittest.skipIf(six.PY3, 'git does not support Python 3')
55 @patch('charmhelpers.fetch.giturl.mkdir')71 @patch('charmhelpers.fetch.giturl.mkdir')
56 def test_installs(self, _mkdir):72 def test_installs(self, _mkdir):
57 self.fh.clone = MagicMock()73 self.fh.clone = MagicMock()
@@ -63,4 +79,4 @@
63 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):79 with patch.dict('os.environ', {'CHARM_DIR': 'foo'}):
64 where = self.fh.install(url)80 where = self.fh.install(url)
65 self.assertEqual(where, dest)81 self.assertEqual(where, dest)
66 _mkdir.assert_called_with(where, perms=0755)82 _mkdir.assert_called_with(where, perms=0o755)
6783
=== modified file 'tests/helpers.py'
--- tests/helpers.py 2014-11-25 14:35:22 +0000
+++ tests/helpers.py 2014-11-25 15:09:14 +0000
@@ -3,6 +3,12 @@
3from mock import patch, MagicMock3from mock import patch, MagicMock
4import io4import io
55
6import six
7if not six.PY3:
8 builtin_open = '__builtin__.open'
9else:
10 builtin_open = 'builtins.open'
11
612
7@contextmanager13@contextmanager
8def patch_open():14def patch_open():
@@ -11,26 +17,29 @@
1117
12 Yields the mock for "open" and "file", respectively.'''18 Yields the mock for "open" and "file", respectively.'''
13 mock_open = MagicMock(spec=open)19 mock_open = MagicMock(spec=open)
14 mock_file = MagicMock(spec=file)20 mock_file = MagicMock(spec=io.FileIO)
1521
16 @contextmanager22 @contextmanager
17 def stub_open(*args, **kwargs):23 def stub_open(*args, **kwargs):
18 mock_open(*args, **kwargs)24 mock_open(*args, **kwargs)
19 yield mock_file25 yield mock_file
2026
21 with patch('__builtin__.open', stub_open):27 with patch(builtin_open, stub_open):
22 yield mock_open, mock_file28 yield mock_open, mock_file
2329
2430
25@contextmanager31@contextmanager
26def mock_open(filename, contents=None):32def mock_open(filename, contents=None):
27 ''' Slightly simpler mock of open to return contents for filename '''33 ''' Slightly simpler mock of open to return contents for filename '''
28 def mock_file(*args):34 def mock_file(name, mode='r', buffering=-1): # Python 2 signature.
29 if args[0] == filename:35 if name == filename:
36 if (not six.PY3) or 'b' in mode:
37 return io.BytesIO(contents)
30 return io.StringIO(contents)38 return io.StringIO(contents)
31 else:39 else:
32 return open(*args)40 return open(name, mode, buffering)
33 with patch('__builtin__.open', mock_file):41
42 with patch(builtin_open, mock_file):
34 yield43 yield
3544
3645
3746
=== modified file 'tests/payload/test_archive.py'
--- tests/payload/test_archive.py 2014-11-25 14:35:22 +0000
+++ tests/payload/test_archive.py 2014-11-25 15:09:14 +0000
@@ -84,7 +84,7 @@
84 self.addCleanup(rmtree, destdir)84 self.addCleanup(rmtree, destdir)
85 try:85 try:
86 zip_file, contents = self.create_archive("zip")86 zip_file, contents = self.create_archive("zip")
87 except subprocess.CalledProcessError, e:87 except subprocess.CalledProcessError as e:
88 if e.returncode == 127:88 if e.returncode == 127:
89 self.skip("Skipping - zip is not installed")89 self.skip("Skipping - zip is not installed")
90 else:90 else:
9191
=== modified file 'tests/payload/test_execd.py'
--- tests/payload/test_execd.py 2013-07-11 08:31:49 +0000
+++ tests/payload/test_execd.py 2014-11-25 15:09:14 +0000
@@ -111,7 +111,7 @@
111111
112 expected = [os.path.join(self.test_charm_dir, 'exec.d', mod,112 expected = [os.path.join(self.test_charm_dir, 'exec.d', mod,
113 'charm-pre-install') for mod in modules]113 'charm-pre-install') for mod in modules]
114 self.assertItemsEqual(submodules, expected)114 self.assertEqual(sorted(submodules), sorted(expected))
115115
116 def test_execd_run(self):116 def test_execd_run(self):
117 modules = ['basenode', 'mod2', 'c']117 modules = ['basenode', 'mod2', 'c']
118118
=== modified file 'tests/tools/test_charm_helper_sync.py'
--- tests/tools/test_charm_helper_sync.py 2014-11-25 14:35:22 +0000
+++ tests/tools/test_charm_helper_sync.py 2014-11-25 15:09:14 +0000
@@ -2,6 +2,13 @@
2from mock import call, patch2from mock import call, patch
3import yaml3import yaml
44
5import six
6if not six.PY3:
7 builtin_open = '__builtin__.open'
8else:
9 builtin_open = 'builtins.open'
10
11
5import tools.charm_helpers_sync.charm_helpers_sync as sync12import tools.charm_helpers_sync.charm_helpers_sync as sync
613
7INCLUDE = """14INCLUDE = """
@@ -46,7 +53,7 @@
46 self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack',53 self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack',
47 path)54 path)
4855
49 @patch('__builtin__.open')56 @patch(builtin_open)
50 @patch('os.path.exists')57 @patch('os.path.exists')
51 @patch('os.walk')58 @patch('os.walk')
52 def test_ensure_init(self, walk, exists, _open):59 def test_ensure_init(self, walk, exists, _open):
@@ -114,7 +121,7 @@
114 isfile.side_effect = _isfile121 isfile.side_effect = _isfile
115 isdir.side_effect = _isdir122 isdir.side_effect = _isdir
116 result = sync.get_filter(opts)(dir='/tmp/charm-helpers/core',123 result = sync.get_filter(opts)(dir='/tmp/charm-helpers/core',
117 ls=files.iterkeys())124 ls=six.iterkeys(files))
118 return result125 return result
119126
120 @patch('os.path.isdir')127 @patch('os.path.isdir')
@@ -123,15 +130,15 @@
123 '''It filters out all non-py files by default'''130 '''It filters out all non-py files by default'''
124 result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir)131 result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir)
125 ex = ['bad_file.bin', 'bad_file.img', 'some_dir']132 ex = ['bad_file.bin', 'bad_file.img', 'some_dir']
126 self.assertEquals(ex, result)133 self.assertEquals(sorted(ex), sorted(result))
127134
128 @patch('os.path.isdir')135 @patch('os.path.isdir')
129 @patch('os.path.isfile')136 @patch('os.path.isfile')
130 def test_filter_dir_with_include(self, isfile, isdir):137 def test_filter_dir_with_include(self, isfile, isdir):
131 '''It includes non-py files if specified as an include opt'''138 '''It includes non-py files if specified as an include opt'''
132 result = self._test_filter_dir(opts=['inc=*.img'],139 result = sorted(self._test_filter_dir(opts=['inc=*.img'],
133 isfile=isfile, isdir=isdir)140 isfile=isfile, isdir=isdir))
134 ex = ['bad_file.bin', 'some_dir']141 ex = sorted(['bad_file.bin', 'some_dir'])
135 self.assertEquals(ex, result)142 self.assertEquals(ex, result)
136143
137 @patch('os.path.isdir')144 @patch('os.path.isdir')
138145
=== modified file 'tools/charm_helpers_sync/charm_helpers_sync.py'
--- tools/charm_helpers_sync/charm_helpers_sync.py 2014-11-25 14:35:22 +0000
+++ tools/charm_helpers_sync/charm_helpers_sync.py 2014-11-25 15:09:14 +0000
@@ -14,9 +14,10 @@
14import sys14import sys
15import tempfile15import tempfile
16import yaml16import yaml
17
18from fnmatch import fnmatch17from fnmatch import fnmatch
1918
19import six
20
20CHARM_HELPERS_BRANCH = 'lp:charm-helpers'21CHARM_HELPERS_BRANCH = 'lp:charm-helpers'
2122
2223
@@ -139,7 +140,7 @@
139140
140def extract_options(inc, global_options=None):141def extract_options(inc, global_options=None):
141 global_options = global_options or []142 global_options = global_options or []
142 if global_options and isinstance(global_options, basestring):143 if global_options and isinstance(global_options, six.string_types):
143 global_options = [global_options]144 global_options = [global_options]
144 if '|' not in inc:145 if '|' not in inc:
145 return (inc, global_options)146 return (inc, global_options)
@@ -159,7 +160,7 @@
159 sync(src, dest, inc, opts)160 sync(src, dest, inc, opts)
160 elif isinstance(inc, dict):161 elif isinstance(inc, dict):
161 # could also do nested dicts here.162 # could also do nested dicts here.
162 for k, v in inc.iteritems():163 for k, v in six.iteritems(inc):
163 if isinstance(v, list):164 if isinstance(v, list):
164 for m in v:165 for m in v:
165 inc, opts = extract_options(m, global_options)166 inc, opts = extract_options(m, global_options)
@@ -217,7 +218,7 @@
217 checkout = clone_helpers(tmpd, config['branch'])218 checkout = clone_helpers(tmpd, config['branch'])
218 sync_helpers(config['include'], checkout, config['destination'],219 sync_helpers(config['include'], checkout, config['destination'],
219 options=sync_options)220 options=sync_options)
220 except Exception, e:221 except Exception as e:
221 logging.error("Could not sync: %s" % e)222 logging.error("Could not sync: %s" % e)
222 raise e223 raise e
223 finally:224 finally:

Subscribers

People subscribed via source and target branches