Merge lp:~stub/charm-helpers/py3-2 into lp:charm-helpers
- py3-2
- Merge into devel
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 | ||||
Related bugs: |
|
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 |
Commit message
Description of the change
Per https:/
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.
- 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
Tim Van Steenburgh (tvansteenburgh) wrote : | # |
- 202. By Stuart Bishop
-
Python3.2 backport for precise
- 203. By Stuart Bishop
-
Don't need simplejson
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@
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://
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-
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://
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-
Setting up python3-yaml (3.10-2) ...
>>>
- 204. By Stuart Bishop
-
merge trunk, revert reversion
Jorge Niedbalski (niedbalski) wrote : | # |
After the latest merge, conflicts resolved. LGTM +1
Tim Van Steenburgh (tvansteenburgh) wrote : | # |
> Running make test in a clean precise lxc:
>
> Checking for Python syntax...
> Py2 OK
> charmhelpers/
> syntax
> tests/contrib/
> syntax
> tests/core/
> 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.
Tim Van Steenburgh (tvansteenburgh) wrote : | # |
+1 LGTM
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2014-11-25 14:35:22 +0000 |
3 | +++ .bzrignore 2014-11-25 15:09:14 +0000 |
4 | @@ -7,3 +7,5 @@ |
5 | .env/ |
6 | coverage.xml |
7 | docs/_build |
8 | +.venv |
9 | +.venv3 |
10 | |
11 | === modified file 'Makefile' |
12 | --- Makefile 2014-11-25 14:35:22 +0000 |
13 | +++ Makefile 2014-11-25 15:09:14 +0000 |
14 | @@ -23,11 +23,12 @@ |
15 | python setup.py sdist |
16 | |
17 | clean: |
18 | - python setup.py clean |
19 | + -python setup.py clean |
20 | rm -rf build/ MANIFEST |
21 | find . -name '*.pyc' -delete |
22 | rm -rf dist/* |
23 | rm -rf .venv |
24 | + rm -rf .venv3 |
25 | (which dh_clean && dh_clean) || true |
26 | |
27 | userinstall: |
28 | @@ -38,19 +39,41 @@ |
29 | sudo apt-get install -y gcc python-dev python-virtualenv python-apt |
30 | virtualenv .venv --system-site-packages |
31 | .venv/bin/pip install -U pip |
32 | + .venv/bin/pip install -U distribute |
33 | .venv/bin/pip install -I -r test_requirements.txt |
34 | - |
35 | -test: .venv |
36 | - @echo Starting tests... |
37 | + .venv/bin/pip install bzr |
38 | + .venv/bin/pip install GitPython |
39 | + |
40 | +.venv3: |
41 | + sudo apt-get install -y gcc python3-dev python-virtualenv python3-apt |
42 | + virtualenv .venv3 --python=python3 --system-site-packages |
43 | + .venv3/bin/pip install -U pip |
44 | + .venv3/bin/pip install -U distribute |
45 | + .venv3/bin/pip install -I -r test_requirements.txt |
46 | + |
47 | +# Note we don't even attempt to run tests if lint isn't passing. |
48 | +test: lint test2 test3 |
49 | + @echo OK |
50 | + |
51 | +test2: |
52 | + @echo Starting Py2 tests... |
53 | .venv/bin/nosetests -s --nologcapture tests/ |
54 | |
55 | -ftest: .venv |
56 | +test3: |
57 | + @echo Starting Py3 tests... |
58 | + .venv3/bin/nosetests -s --nologcapture tests/ |
59 | + |
60 | +ftest: lint |
61 | @echo Starting fast tests... |
62 | .venv/bin/nosetests --attr '!slow' --nologcapture tests/ |
63 | + .venv3/bin/nosetests --attr '!slow' --nologcapture tests/ |
64 | |
65 | -lint: |
66 | +lint: .venv .venv3 |
67 | @echo Checking for Python syntax... |
68 | - @flake8 --ignore=E123,E501 $(PROJECT) $(TESTS) && echo OK |
69 | + @.venv/bin/flake8 --ignore=E501 $(PROJECT) $(TESTS) tools/ \ |
70 | + && echo Py2 OK |
71 | + @.venv3/bin/flake8 --ignore=E501 $(PROJECT) $(TESTS) tools/ \ |
72 | + && echo Py3 OK |
73 | |
74 | docs: |
75 | - [ -z "`dpkg -l | grep python-sphinx`" ] && sudo apt-get install python-sphinx -y |
76 | |
77 | === modified file 'charmhelpers/__init__.py' |
78 | --- charmhelpers/__init__.py 2013-05-11 19:55:58 +0000 |
79 | +++ charmhelpers/__init__.py 2014-11-25 15:09:14 +0000 |
80 | @@ -0,0 +1,22 @@ |
81 | +# Bootstrap charm-helpers, installing its dependencies if necessary using |
82 | +# only standard libraries. |
83 | +import subprocess |
84 | +import sys |
85 | + |
86 | +try: |
87 | + import six # flake8: noqa |
88 | +except ImportError: |
89 | + if sys.version_info.major == 2: |
90 | + subprocess.check_call(['apt-get', 'install', '-y', 'python-six']) |
91 | + else: |
92 | + subprocess.check_call(['apt-get', 'install', '-y', 'python3-six']) |
93 | + import six # flake8: noqa |
94 | + |
95 | +try: |
96 | + import yaml # flake8: noqa |
97 | +except ImportError: |
98 | + if sys.version_info.major == 2: |
99 | + subprocess.check_call(['apt-get', 'install', '-y', 'python-yaml']) |
100 | + else: |
101 | + subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml']) |
102 | + import yaml # flake8: noqa |
103 | |
104 | === modified file 'charmhelpers/cli/__init__.py' |
105 | --- charmhelpers/cli/__init__.py 2014-11-25 14:35:22 +0000 |
106 | +++ charmhelpers/cli/__init__.py 2014-11-25 15:09:14 +0000 |
107 | @@ -1,8 +1,9 @@ |
108 | import inspect |
109 | -import itertools |
110 | import argparse |
111 | import sys |
112 | |
113 | +from six.moves import zip |
114 | + |
115 | |
116 | class OutputFormatter(object): |
117 | def __init__(self, outfile=sys.stdout): |
118 | @@ -136,7 +137,7 @@ |
119 | if argspec.defaults: |
120 | positional_args = argspec.args[:-len(argspec.defaults)] |
121 | keyword_names = argspec.args[-len(argspec.defaults):] |
122 | - for arg, default in itertools.izip(keyword_names, argspec.defaults): |
123 | + for arg, default in zip(keyword_names, argspec.defaults): |
124 | yield ('--{}'.format(arg),), {'default': default} |
125 | else: |
126 | positional_args = argspec.args |
127 | |
128 | === modified file 'charmhelpers/contrib/amulet/deployment.py' |
129 | --- charmhelpers/contrib/amulet/deployment.py 2014-11-25 14:35:22 +0000 |
130 | +++ charmhelpers/contrib/amulet/deployment.py 2014-11-25 15:09:14 +0000 |
131 | @@ -1,6 +1,6 @@ |
132 | import amulet |
133 | - |
134 | import os |
135 | +import six |
136 | |
137 | |
138 | class AmuletDeployment(object): |
139 | @@ -52,12 +52,12 @@ |
140 | |
141 | def _add_relations(self, relations): |
142 | """Add all of the relations for the services.""" |
143 | - for k, v in relations.iteritems(): |
144 | + for k, v in six.iteritems(relations): |
145 | self.d.relate(k, v) |
146 | |
147 | def _configure_services(self, configs): |
148 | """Configure all of the services.""" |
149 | - for service, config in configs.iteritems(): |
150 | + for service, config in six.iteritems(configs): |
151 | self.d.configure(service, config) |
152 | |
153 | def _deploy(self): |
154 | |
155 | === modified file 'charmhelpers/contrib/amulet/utils.py' |
156 | --- charmhelpers/contrib/amulet/utils.py 2014-11-25 14:35:22 +0000 |
157 | +++ charmhelpers/contrib/amulet/utils.py 2014-11-25 15:09:14 +0000 |
158 | @@ -5,6 +5,8 @@ |
159 | import sys |
160 | import time |
161 | |
162 | +import six |
163 | + |
164 | |
165 | class AmuletUtils(object): |
166 | """Amulet utilities. |
167 | @@ -58,7 +60,7 @@ |
168 | Verify the specified services are running on the corresponding |
169 | service units. |
170 | """ |
171 | - for k, v in commands.iteritems(): |
172 | + for k, v in six.iteritems(commands): |
173 | for cmd in v: |
174 | output, code = k.run(cmd) |
175 | if code != 0: |
176 | @@ -100,11 +102,11 @@ |
177 | longs, or can be a function that evaluate a variable and returns a |
178 | bool. |
179 | """ |
180 | - for k, v in expected.iteritems(): |
181 | + for k, v in six.iteritems(expected): |
182 | if k in actual: |
183 | - if (isinstance(v, basestring) or |
184 | + if (isinstance(v, six.string_types) or |
185 | isinstance(v, bool) or |
186 | - isinstance(v, (int, long))): |
187 | + isinstance(v, six.integer_types)): |
188 | if v != actual[k]: |
189 | return "{}:{}".format(k, actual[k]) |
190 | elif not v(actual[k]): |
191 | |
192 | === modified file 'charmhelpers/contrib/charmhelpers/__init__.py' |
193 | --- charmhelpers/contrib/charmhelpers/__init__.py 2014-11-25 14:35:22 +0000 |
194 | +++ charmhelpers/contrib/charmhelpers/__init__.py 2014-11-25 15:09:14 +0000 |
195 | @@ -8,19 +8,19 @@ |
196 | |
197 | __metaclass__ = type |
198 | __all__ = [ |
199 | - #'get_config', # core.hookenv.config() |
200 | - #'log', # core.hookenv.log() |
201 | - #'log_entry', # core.hookenv.log() |
202 | - #'log_exit', # core.hookenv.log() |
203 | - #'relation_get', # core.hookenv.relation_get() |
204 | - #'relation_set', # core.hookenv.relation_set() |
205 | - #'relation_ids', # core.hookenv.relation_ids() |
206 | - #'relation_list', # core.hookenv.relation_units() |
207 | - #'config_get', # core.hookenv.config() |
208 | - #'unit_get', # core.hookenv.unit_get() |
209 | - #'open_port', # core.hookenv.open_port() |
210 | - #'close_port', # core.hookenv.close_port() |
211 | - #'service_control', # core.host.service() |
212 | + # 'get_config', # core.hookenv.config() |
213 | + # 'log', # core.hookenv.log() |
214 | + # 'log_entry', # core.hookenv.log() |
215 | + # 'log_exit', # core.hookenv.log() |
216 | + # 'relation_get', # core.hookenv.relation_get() |
217 | + # 'relation_set', # core.hookenv.relation_set() |
218 | + # 'relation_ids', # core.hookenv.relation_ids() |
219 | + # 'relation_list', # core.hookenv.relation_units() |
220 | + # 'config_get', # core.hookenv.config() |
221 | + # 'unit_get', # core.hookenv.unit_get() |
222 | + # 'open_port', # core.hookenv.open_port() |
223 | + # 'close_port', # core.hookenv.close_port() |
224 | + # 'service_control', # core.host.service() |
225 | 'unit_info', # client-side, NOT IMPLEMENTED |
226 | 'wait_for_machine', # client-side, NOT IMPLEMENTED |
227 | 'wait_for_page_contents', # client-side, NOT IMPLEMENTED |
228 | @@ -31,17 +31,24 @@ |
229 | import operator |
230 | import tempfile |
231 | import time |
232 | -import urllib2 |
233 | import yaml |
234 | import subprocess |
235 | |
236 | +import six |
237 | +if six.PY3: |
238 | + from urllib.request import urlopen |
239 | + from urllib.error import (HTTPError, URLError) |
240 | +else: |
241 | + from urllib2 import (urlopen, HTTPError, URLError) |
242 | + |
243 | + |
244 | SLEEP_AMOUNT = 0.1 |
245 | # We create a juju_status Command here because it makes testing much, |
246 | # much easier. |
247 | juju_status = lambda: subprocess.check_call(['juju', 'status']) |
248 | |
249 | # re-implemented as charmhelpers.fetch.configure_sources() |
250 | -#def configure_source(update=False): |
251 | +# def configure_source(update=False): |
252 | # source = config_get('source') |
253 | # if ((source.startswith('ppa:') or |
254 | # source.startswith('cloud:') or |
255 | @@ -55,7 +62,7 @@ |
256 | |
257 | # DEPRECATED: client-side only |
258 | def make_charm_config_file(charm_config): |
259 | - charm_config_file = tempfile.NamedTemporaryFile() |
260 | + charm_config_file = tempfile.NamedTemporaryFile(mode='w+') |
261 | charm_config_file.write(yaml.dump(charm_config)) |
262 | charm_config_file.flush() |
263 | # The NamedTemporaryFile instance is returned instead of just the name |
264 | @@ -119,7 +126,7 @@ |
265 | # we're in LXC. |
266 | machine_data = get_machine_data() |
267 | non_zookeeper_machines = [ |
268 | - machine_data[key] for key in machine_data.keys()[1:]] |
269 | + machine_data[key] for key in list(machine_data.keys())[1:]] |
270 | if len(non_zookeeper_machines) >= num_machines: |
271 | all_machines_running = True |
272 | for machine in non_zookeeper_machines: |
273 | @@ -170,8 +177,8 @@ |
274 | start_time = time.time() |
275 | while True: |
276 | try: |
277 | - stream = urllib2.urlopen(url) |
278 | - except (urllib2.HTTPError, urllib2.URLError): |
279 | + stream = urlopen(url) |
280 | + except (HTTPError, URLError): |
281 | pass |
282 | else: |
283 | page = stream.read() |
284 | |
285 | === modified file 'charmhelpers/contrib/hahelpers/cluster.py' |
286 | --- charmhelpers/contrib/hahelpers/cluster.py 2014-11-25 14:35:22 +0000 |
287 | +++ charmhelpers/contrib/hahelpers/cluster.py 2014-11-25 15:09:14 +0000 |
288 | @@ -13,9 +13,10 @@ |
289 | |
290 | import subprocess |
291 | import os |
292 | - |
293 | from socket import gethostname as get_unit_hostname |
294 | |
295 | +import six |
296 | + |
297 | from charmhelpers.core.hookenv import ( |
298 | log, |
299 | relation_ids, |
300 | @@ -77,7 +78,7 @@ |
301 | "show", resource |
302 | ] |
303 | try: |
304 | - status = subprocess.check_output(cmd) |
305 | + status = subprocess.check_output(cmd).decode('UTF-8') |
306 | except subprocess.CalledProcessError: |
307 | return False |
308 | else: |
309 | @@ -197,7 +198,7 @@ |
310 | for setting in settings: |
311 | conf[setting] = config_get(setting) |
312 | missing = [] |
313 | - [missing.append(s) for s, v in conf.iteritems() if v is None] |
314 | + [missing.append(s) for s, v in six.iteritems(conf) if v is None] |
315 | if missing: |
316 | log('Insufficient config data to configure hacluster.', level=ERROR) |
317 | raise HAIncompleteConfig |
318 | |
319 | === modified file 'charmhelpers/contrib/network/ip.py' |
320 | --- charmhelpers/contrib/network/ip.py 2014-11-25 14:35:22 +0000 |
321 | +++ charmhelpers/contrib/network/ip.py 2014-11-25 15:09:14 +0000 |
322 | @@ -228,7 +228,7 @@ |
323 | raise Exception("Interface '%s' doesn't have any %s addresses." % |
324 | (iface, inet_type)) |
325 | |
326 | - return addresses |
327 | + return sorted(addresses) |
328 | |
329 | |
330 | get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET') |
331 | @@ -302,7 +302,7 @@ |
332 | if global_addrs: |
333 | # Make sure any found global addresses are not temporary |
334 | cmd = ['ip', 'addr', 'show', iface] |
335 | - out = subprocess.check_output(cmd) |
336 | + out = subprocess.check_output(cmd).decode('UTF-8') |
337 | if dynamic_only: |
338 | key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*") |
339 | else: |
340 | |
341 | === modified file 'charmhelpers/contrib/openstack/amulet/deployment.py' |
342 | --- charmhelpers/contrib/openstack/amulet/deployment.py 2014-11-25 14:35:22 +0000 |
343 | +++ charmhelpers/contrib/openstack/amulet/deployment.py 2014-11-25 15:09:14 +0000 |
344 | @@ -1,3 +1,4 @@ |
345 | +import six |
346 | from charmhelpers.contrib.amulet.deployment import ( |
347 | AmuletDeployment |
348 | ) |
349 | @@ -69,7 +70,7 @@ |
350 | |
351 | def _configure_services(self, configs): |
352 | """Configure all of the services.""" |
353 | - for service, config in configs.iteritems(): |
354 | + for service, config in six.iteritems(configs): |
355 | self.d.configure(service, config) |
356 | |
357 | def _get_openstack_release(self): |
358 | |
359 | === modified file 'charmhelpers/contrib/openstack/amulet/utils.py' |
360 | --- charmhelpers/contrib/openstack/amulet/utils.py 2014-11-25 14:35:22 +0000 |
361 | +++ charmhelpers/contrib/openstack/amulet/utils.py 2014-11-25 15:09:14 +0000 |
362 | @@ -7,6 +7,8 @@ |
363 | import keystoneclient.v2_0 as keystone_client |
364 | import novaclient.v1_1.client as nova_client |
365 | |
366 | +import six |
367 | + |
368 | from charmhelpers.contrib.amulet.utils import ( |
369 | AmuletUtils |
370 | ) |
371 | @@ -60,7 +62,7 @@ |
372 | expected service catalog endpoints. |
373 | """ |
374 | self.log.debug('actual: {}'.format(repr(actual))) |
375 | - for k, v in expected.iteritems(): |
376 | + for k, v in six.iteritems(expected): |
377 | if k in actual: |
378 | ret = self._validate_dict_data(expected[k][0], actual[k][0]) |
379 | if ret: |
380 | |
381 | === modified file 'charmhelpers/contrib/openstack/context.py' |
382 | --- charmhelpers/contrib/openstack/context.py 2014-11-25 14:35:22 +0000 |
383 | +++ charmhelpers/contrib/openstack/context.py 2014-11-25 15:09:14 +0000 |
384 | @@ -1,10 +1,11 @@ |
385 | import json |
386 | import os |
387 | import time |
388 | - |
389 | from base64 import b64decode |
390 | from subprocess import check_call |
391 | |
392 | +import six |
393 | + |
394 | from charmhelpers.fetch import ( |
395 | apt_install, |
396 | filter_installed_packages, |
397 | @@ -69,7 +70,7 @@ |
398 | |
399 | def context_complete(ctxt): |
400 | _missing = [] |
401 | - for k, v in ctxt.iteritems(): |
402 | + for k, v in six.iteritems(ctxt): |
403 | if v is None or v == '': |
404 | _missing.append(k) |
405 | |
406 | @@ -97,7 +98,7 @@ |
407 | split = config_flags.strip(' =').split('=') |
408 | limit = len(split) |
409 | flags = {} |
410 | - for i in xrange(0, limit - 1): |
411 | + for i in range(0, limit - 1): |
412 | current = split[i] |
413 | next = split[i + 1] |
414 | vindex = next.rfind(',') |
415 | @@ -375,7 +376,7 @@ |
416 | host = format_ipv6_addr(host) or host |
417 | rabbitmq_hosts.append(host) |
418 | |
419 | - ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts) |
420 | + ctxt['rabbitmq_hosts'] = ','.join(sorted(rabbitmq_hosts)) |
421 | |
422 | if not context_complete(ctxt): |
423 | return {} |
424 | @@ -408,7 +409,7 @@ |
425 | ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr |
426 | mon_hosts.append(ceph_addr) |
427 | |
428 | - ctxt = {'mon_hosts': ' '.join(mon_hosts), |
429 | + ctxt = {'mon_hosts': ' '.join(sorted(mon_hosts)), |
430 | 'auth': auth, |
431 | 'key': key, |
432 | 'use_syslog': use_syslog} |
433 | @@ -587,7 +588,7 @@ |
434 | if k.startswith('ssl_key_'): |
435 | cns.append(k.lstrip('ssl_key_')) |
436 | |
437 | - return list(set(cns)) |
438 | + return sorted(list(set(cns))) |
439 | |
440 | def get_network_addresses(self): |
441 | """For each network configured, return corresponding address and vip |
442 | @@ -631,10 +632,10 @@ |
443 | else: |
444 | addresses.append((addr, addr)) |
445 | |
446 | - return addresses |
447 | + return sorted(addresses) |
448 | |
449 | def __call__(self): |
450 | - if isinstance(self.external_ports, basestring): |
451 | + if isinstance(self.external_ports, six.string_types): |
452 | self.external_ports = [self.external_ports] |
453 | |
454 | if not self.external_ports or not https(): |
455 | @@ -651,7 +652,7 @@ |
456 | self.configure_cert(cn) |
457 | |
458 | addresses = self.get_network_addresses() |
459 | - for address, endpoint in set(addresses): |
460 | + for address, endpoint in sorted(set(addresses)): |
461 | for api_port in self.external_ports: |
462 | ext_port = determine_apache_port(api_port) |
463 | int_port = determine_api_port(api_port) |
464 | @@ -659,7 +660,7 @@ |
465 | ctxt['endpoints'].append(portmap) |
466 | ctxt['ext_ports'].append(int(ext_port)) |
467 | |
468 | - ctxt['ext_ports'] = list(set(ctxt['ext_ports'])) |
469 | + ctxt['ext_ports'] = sorted(list(set(ctxt['ext_ports']))) |
470 | return ctxt |
471 | |
472 | |
473 | @@ -921,10 +922,10 @@ |
474 | continue |
475 | |
476 | sub_config = sub_config[self.config_file] |
477 | - for k, v in sub_config.iteritems(): |
478 | + for k, v in six.iteritems(sub_config): |
479 | if k == 'sections': |
480 | - for section, config_dict in v.iteritems(): |
481 | - log("Adding section '%s'" % (section), |
482 | + for section, config_dict in six.iteritems(v): |
483 | + log("adding section '%s'" % (section), |
484 | level=DEBUG) |
485 | ctxt[k][section] = config_dict |
486 | else: |
487 | |
488 | === modified file 'charmhelpers/contrib/openstack/neutron.py' |
489 | --- charmhelpers/contrib/openstack/neutron.py 2014-11-17 15:15:52 +0000 |
490 | +++ charmhelpers/contrib/openstack/neutron.py 2014-11-25 15:09:14 +0000 |
491 | @@ -14,7 +14,7 @@ |
492 | def headers_package(): |
493 | """Ensures correct linux-headers for running kernel are installed, |
494 | for building DKMS package""" |
495 | - kver = check_output(['uname', '-r']).strip() |
496 | + kver = check_output(['uname', '-r']).decode('UTF-8').strip() |
497 | return 'linux-headers-%s' % kver |
498 | |
499 | QUANTUM_CONF_DIR = '/etc/quantum' |
500 | @@ -22,7 +22,7 @@ |
501 | |
502 | def kernel_version(): |
503 | """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """ |
504 | - kver = check_output(['uname', '-r']).strip() |
505 | + kver = check_output(['uname', '-r']).decode('UTF-8').strip() |
506 | kver = kver.split('.') |
507 | return (int(kver[0]), int(kver[1])) |
508 | |
509 | |
510 | === modified file 'charmhelpers/contrib/openstack/templates/haproxy.cfg' |
511 | --- charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-11-25 14:35:22 +0000 |
512 | +++ charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-11-25 15:09:14 +0000 |
513 | @@ -35,7 +35,7 @@ |
514 | stats auth admin:password |
515 | |
516 | {% if frontends -%} |
517 | -{% for service, ports in service_ports.iteritems() -%} |
518 | +{% for service, ports in service_ports.items() -%} |
519 | frontend tcp-in_{{ service }} |
520 | bind *:{{ ports[0] }} |
521 | bind :::{{ ports[0] }} |
522 | @@ -46,7 +46,7 @@ |
523 | {% for frontend in frontends -%} |
524 | backend {{ service }}_{{ frontend }} |
525 | balance leastconn |
526 | - {% for unit, address in frontends[frontend]['backends'].iteritems() -%} |
527 | + {% for unit, address in frontends[frontend]['backends'].items() -%} |
528 | server {{ unit }} {{ address }}:{{ ports[1] }} check |
529 | {% endfor %} |
530 | {% endfor -%} |
531 | |
532 | === modified file 'charmhelpers/contrib/openstack/templating.py' |
533 | --- charmhelpers/contrib/openstack/templating.py 2014-11-25 14:35:22 +0000 |
534 | +++ charmhelpers/contrib/openstack/templating.py 2014-11-25 15:09:14 +0000 |
535 | @@ -1,13 +1,13 @@ |
536 | import os |
537 | |
538 | +import six |
539 | + |
540 | from charmhelpers.fetch import apt_install |
541 | - |
542 | from charmhelpers.core.hookenv import ( |
543 | log, |
544 | ERROR, |
545 | INFO |
546 | ) |
547 | - |
548 | from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES |
549 | |
550 | try: |
551 | @@ -43,7 +43,7 @@ |
552 | order by OpenStack release. |
553 | """ |
554 | tmpl_dirs = [(rel, os.path.join(templates_dir, rel)) |
555 | - for rel in OPENSTACK_CODENAMES.itervalues()] |
556 | + for rel in six.itervalues(OPENSTACK_CODENAMES)] |
557 | |
558 | if not os.path.isdir(templates_dir): |
559 | log('Templates directory not found @ %s.' % templates_dir, |
560 | @@ -258,7 +258,7 @@ |
561 | """ |
562 | Write out all registered config files. |
563 | """ |
564 | - [self.write(k) for k in self.templates.iterkeys()] |
565 | + [self.write(k) for k in six.iterkeys(self.templates)] |
566 | |
567 | def set_release(self, openstack_release): |
568 | """ |
569 | @@ -275,5 +275,5 @@ |
570 | ''' |
571 | interfaces = [] |
572 | [interfaces.extend(i.complete_contexts()) |
573 | - for i in self.templates.itervalues()] |
574 | + for i in six.itervalues(self.templates)] |
575 | return interfaces |
576 | |
577 | === modified file 'charmhelpers/contrib/openstack/utils.py' |
578 | --- charmhelpers/contrib/openstack/utils.py 2014-11-25 14:35:22 +0000 |
579 | +++ charmhelpers/contrib/openstack/utils.py 2014-11-25 15:09:14 +0000 |
580 | @@ -10,6 +10,8 @@ |
581 | import socket |
582 | import sys |
583 | |
584 | +import six |
585 | + |
586 | from charmhelpers.core.hookenv import ( |
587 | config, |
588 | log as juju_log, |
589 | @@ -113,7 +115,7 @@ |
590 | |
591 | # Best guess match based on deb string provided |
592 | if src.startswith('deb') or src.startswith('ppa'): |
593 | - for k, v in OPENSTACK_CODENAMES.iteritems(): |
594 | + for k, v in six.iteritems(OPENSTACK_CODENAMES): |
595 | if v in src: |
596 | return v |
597 | |
598 | @@ -134,7 +136,7 @@ |
599 | |
600 | def get_os_version_codename(codename): |
601 | '''Determine OpenStack version number from codename.''' |
602 | - for k, v in OPENSTACK_CODENAMES.iteritems(): |
603 | + for k, v in six.iteritems(OPENSTACK_CODENAMES): |
604 | if v == codename: |
605 | return k |
606 | e = 'Could not derive OpenStack version for '\ |
607 | @@ -194,7 +196,7 @@ |
608 | else: |
609 | vers_map = OPENSTACK_CODENAMES |
610 | |
611 | - for version, cname in vers_map.iteritems(): |
612 | + for version, cname in six.iteritems(vers_map): |
613 | if cname == codename: |
614 | return version |
615 | # e = "Could not determine OpenStack version for package: %s" % pkg |
616 | @@ -318,7 +320,7 @@ |
617 | rc_script.write( |
618 | "#!/bin/bash\n") |
619 | [rc_script.write('export %s=%s\n' % (u, p)) |
620 | - for u, p in env_vars.iteritems() if u != "script_path"] |
621 | + for u, p in six.iteritems(env_vars) if u != "script_path"] |
622 | |
623 | |
624 | def openstack_upgrade_available(package): |
625 | @@ -418,7 +420,7 @@ |
626 | |
627 | if isinstance(address, dns.name.Name): |
628 | rtype = 'PTR' |
629 | - elif isinstance(address, basestring): |
630 | + elif isinstance(address, six.string_types): |
631 | rtype = 'A' |
632 | else: |
633 | return None |
634 | @@ -486,8 +488,7 @@ |
635 | 'hostname': json.dumps(hosts)} |
636 | |
637 | if relation_prefix: |
638 | - keys = kwargs.keys() |
639 | - for key in keys: |
640 | + for key in list(kwargs.keys()): |
641 | kwargs["%s_%s" % (relation_prefix, key)] = kwargs[key] |
642 | del kwargs[key] |
643 | |
644 | |
645 | === modified file 'charmhelpers/contrib/peerstorage/__init__.py' |
646 | --- charmhelpers/contrib/peerstorage/__init__.py 2014-11-25 14:35:22 +0000 |
647 | +++ charmhelpers/contrib/peerstorage/__init__.py 2014-11-25 15:09:14 +0000 |
648 | @@ -1,3 +1,4 @@ |
649 | +import six |
650 | from charmhelpers.core.hookenv import relation_id as current_relation_id |
651 | from charmhelpers.core.hookenv import ( |
652 | is_relation_made, |
653 | @@ -93,7 +94,7 @@ |
654 | if ex in echo_data: |
655 | echo_data.pop(ex) |
656 | else: |
657 | - for attribute, value in rdata.iteritems(): |
658 | + for attribute, value in six.iteritems(rdata): |
659 | for include in includes: |
660 | if include in attribute: |
661 | echo_data[attribute] = value |
662 | @@ -119,8 +120,8 @@ |
663 | relation_settings=relation_settings, |
664 | **kwargs) |
665 | if is_relation_made(peer_relation_name): |
666 | - for key, value in dict(kwargs.items() + |
667 | - relation_settings.items()).iteritems(): |
668 | + for key, value in six.iteritems(dict(list(kwargs.items()) + |
669 | + list(relation_settings.items()))): |
670 | key_prefix = relation_id or current_relation_id() |
671 | peer_store(key_prefix + delimiter + key, |
672 | value, |
673 | |
674 | === modified file 'charmhelpers/contrib/python/packages.py' |
675 | --- charmhelpers/contrib/python/packages.py 2014-11-25 14:35:22 +0000 |
676 | +++ charmhelpers/contrib/python/packages.py 2014-11-25 15:09:14 +0000 |
677 | @@ -15,7 +15,7 @@ |
678 | |
679 | def parse_options(given, available): |
680 | """Given a set of options, check if available""" |
681 | - for key, value in given.items(): |
682 | + for key, value in sorted(given.items()): |
683 | if key in available: |
684 | yield "--{0}={1}".format(key, value) |
685 | |
686 | |
687 | === modified file 'charmhelpers/contrib/ssl/__init__.py' |
688 | --- charmhelpers/contrib/ssl/__init__.py 2014-11-25 14:35:22 +0000 |
689 | +++ charmhelpers/contrib/ssl/__init__.py 2014-11-25 15:09:14 +0000 |
690 | @@ -74,5 +74,5 @@ |
691 | subprocess.check_call(cmd) |
692 | return True |
693 | except Exception as e: |
694 | - print "Execution of openssl command failed:\n{}".format(e) |
695 | + print("Execution of openssl command failed:\n{}".format(e)) |
696 | return False |
697 | |
698 | === modified file 'charmhelpers/contrib/storage/linux/ceph.py' |
699 | --- charmhelpers/contrib/storage/linux/ceph.py 2014-11-25 14:35:22 +0000 |
700 | +++ charmhelpers/contrib/storage/linux/ceph.py 2014-11-25 15:09:14 +0000 |
701 | @@ -65,7 +65,8 @@ |
702 | def rbd_exists(service, pool, rbd_img): |
703 | """Check to see if a RADOS block device exists.""" |
704 | try: |
705 | - out = check_output(['rbd', 'list', '--id', service, '--pool', pool]) |
706 | + out = check_output(['rbd', 'list', '--id', |
707 | + service, '--pool', pool]).decode('UTF-8') |
708 | except CalledProcessError: |
709 | return False |
710 | |
711 | @@ -82,7 +83,8 @@ |
712 | def pool_exists(service, name): |
713 | """Check to see if a RADOS pool already exists.""" |
714 | try: |
715 | - out = check_output(['rados', '--id', service, 'lspools']) |
716 | + out = check_output(['rados', '--id', service, |
717 | + 'lspools']).decode('UTF-8') |
718 | except CalledProcessError: |
719 | return False |
720 | |
721 | @@ -96,7 +98,8 @@ |
722 | version = ceph_version() |
723 | if version and version >= '0.56': |
724 | return json.loads(check_output(['ceph', '--id', service, |
725 | - 'osd', 'ls', '--format=json'])) |
726 | + 'osd', 'ls', |
727 | + '--format=json']).decode('UTF-8')) |
728 | |
729 | return None |
730 | |
731 | @@ -112,7 +115,7 @@ |
732 | # on upstream recommended best practices. |
733 | osds = get_osds(service) |
734 | if osds: |
735 | - pgnum = (len(osds) * 100 / replicas) |
736 | + pgnum = (len(osds) * 100 // replicas) |
737 | else: |
738 | # NOTE(james-page): Default to 200 for older ceph versions |
739 | # which don't support OSD query from cli |
740 | @@ -193,7 +196,7 @@ |
741 | def image_mapped(name): |
742 | """Determine whether a RADOS block device is mapped locally.""" |
743 | try: |
744 | - out = check_output(['rbd', 'showmapped']) |
745 | + out = check_output(['rbd', 'showmapped']).decode('UTF-8') |
746 | except CalledProcessError: |
747 | return False |
748 | |
749 | @@ -361,7 +364,7 @@ |
750 | """Retrieve the local version of ceph.""" |
751 | if os.path.exists('/usr/bin/ceph'): |
752 | cmd = ['ceph', '-v'] |
753 | - output = check_output(cmd) |
754 | + output = check_output(cmd).decode('US-ASCII') |
755 | output = output.split() |
756 | if len(output) > 3: |
757 | return output[2] |
758 | |
759 | === modified file 'charmhelpers/contrib/storage/linux/loopback.py' |
760 | --- charmhelpers/contrib/storage/linux/loopback.py 2014-11-25 14:35:22 +0000 |
761 | +++ charmhelpers/contrib/storage/linux/loopback.py 2014-11-25 15:09:14 +0000 |
762 | @@ -1,12 +1,12 @@ |
763 | - |
764 | import os |
765 | import re |
766 | - |
767 | from subprocess import ( |
768 | check_call, |
769 | check_output, |
770 | ) |
771 | |
772 | +import six |
773 | + |
774 | |
775 | ################################################## |
776 | # loopback device helpers. |
777 | @@ -37,7 +37,7 @@ |
778 | ''' |
779 | file_path = os.path.abspath(file_path) |
780 | check_call(['losetup', '--find', file_path]) |
781 | - for d, f in loopback_devices().iteritems(): |
782 | + for d, f in six.iteritems(loopback_devices()): |
783 | if f == file_path: |
784 | return d |
785 | |
786 | @@ -51,7 +51,7 @@ |
787 | |
788 | :returns: str: Full path to the ensured loopback device (eg, /dev/loop0) |
789 | ''' |
790 | - for d, f in loopback_devices().iteritems(): |
791 | + for d, f in six.iteritems(loopback_devices()): |
792 | if f == path: |
793 | return d |
794 | |
795 | |
796 | === modified file 'charmhelpers/contrib/storage/linux/lvm.py' |
797 | --- charmhelpers/contrib/storage/linux/lvm.py 2014-05-10 19:58:31 +0000 |
798 | +++ charmhelpers/contrib/storage/linux/lvm.py 2014-11-25 15:09:14 +0000 |
799 | @@ -61,6 +61,7 @@ |
800 | vg = None |
801 | pvd = check_output(['pvdisplay', block_device]).splitlines() |
802 | for l in pvd: |
803 | + l = l.decode('UTF-8') |
804 | if l.strip().startswith('VG Name'): |
805 | vg = ' '.join(l.strip().split()[2:]) |
806 | return vg |
807 | |
808 | === modified file 'charmhelpers/contrib/storage/linux/utils.py' |
809 | --- charmhelpers/contrib/storage/linux/utils.py 2014-07-31 08:17:42 +0000 |
810 | +++ charmhelpers/contrib/storage/linux/utils.py 2014-11-25 15:09:14 +0000 |
811 | @@ -30,7 +30,8 @@ |
812 | # sometimes sgdisk exits non-zero; this is OK, dd will clean up |
813 | call(['sgdisk', '--zap-all', '--mbrtogpt', |
814 | '--clear', block_device]) |
815 | - dev_end = check_output(['blockdev', '--getsz', block_device]) |
816 | + dev_end = check_output(['blockdev', '--getsz', |
817 | + block_device]).decode('UTF-8') |
818 | gpt_end = int(dev_end.split()[0]) - 100 |
819 | check_call(['dd', 'if=/dev/zero', 'of=%s' % (block_device), |
820 | 'bs=1M', 'count=1']) |
821 | @@ -47,7 +48,7 @@ |
822 | it doesn't. |
823 | ''' |
824 | is_partition = bool(re.search(r".*[0-9]+\b", device)) |
825 | - out = check_output(['mount']) |
826 | + out = check_output(['mount']).decode('UTF-8') |
827 | if is_partition: |
828 | return bool(re.search(device + r"\b", out)) |
829 | return bool(re.search(device + r"[0-9]+\b", out)) |
830 | |
831 | === modified file 'charmhelpers/contrib/templating/contexts.py' |
832 | --- charmhelpers/contrib/templating/contexts.py 2014-11-25 14:35:22 +0000 |
833 | +++ charmhelpers/contrib/templating/contexts.py 2014-11-25 15:09:14 +0000 |
834 | @@ -6,6 +6,8 @@ |
835 | import os |
836 | import yaml |
837 | |
838 | +import six |
839 | + |
840 | import charmhelpers.core.hookenv |
841 | |
842 | |
843 | @@ -92,9 +94,9 @@ |
844 | |
845 | # Don't use non-standard tags for unicode which will not |
846 | # work when salt uses yaml.load_safe. |
847 | - yaml.add_representer(unicode, lambda dumper, |
848 | - value: dumper.represent_scalar( |
849 | - u'tag:yaml.org,2002:str', value)) |
850 | + yaml.add_representer(six.text_type, |
851 | + lambda dumper, value: dumper.represent_scalar( |
852 | + six.u('tag:yaml.org,2002:str'), value)) |
853 | |
854 | yaml_dir = os.path.dirname(yaml_path) |
855 | if not os.path.exists(yaml_dir): |
856 | |
857 | === modified file 'charmhelpers/core/fstab.py' |
858 | --- charmhelpers/core/fstab.py 2014-11-25 14:35:22 +0000 |
859 | +++ charmhelpers/core/fstab.py 2014-11-25 15:09:14 +0000 |
860 | @@ -3,10 +3,11 @@ |
861 | |
862 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
863 | |
864 | +import io |
865 | import os |
866 | |
867 | |
868 | -class Fstab(file): |
869 | +class Fstab(io.FileIO): |
870 | """This class extends file in order to implement a file reader/writer |
871 | for file `/etc/fstab` |
872 | """ |
873 | @@ -24,8 +25,8 @@ |
874 | options = "defaults" |
875 | |
876 | self.options = options |
877 | - self.d = d |
878 | - self.p = p |
879 | + self.d = int(d) |
880 | + self.p = int(p) |
881 | |
882 | def __eq__(self, o): |
883 | return str(self) == str(o) |
884 | @@ -45,7 +46,7 @@ |
885 | self._path = path |
886 | else: |
887 | self._path = self.DEFAULT_PATH |
888 | - file.__init__(self, self._path, 'r+') |
889 | + super(Fstab, self).__init__(self._path, 'rb+') |
890 | |
891 | def _hydrate_entry(self, line): |
892 | # NOTE: use split with no arguments to split on any |
893 | @@ -58,8 +59,9 @@ |
894 | def entries(self): |
895 | self.seek(0) |
896 | for line in self.readlines(): |
897 | + line = line.decode('us-ascii') |
898 | try: |
899 | - if not line.startswith("#"): |
900 | + if line.strip() and not line.startswith("#"): |
901 | yield self._hydrate_entry(line) |
902 | except ValueError: |
903 | pass |
904 | @@ -75,14 +77,14 @@ |
905 | if self.get_entry_by_attr('device', entry.device): |
906 | return False |
907 | |
908 | - self.write(str(entry) + '\n') |
909 | + self.write((str(entry) + '\n').encode('us-ascii')) |
910 | self.truncate() |
911 | return entry |
912 | |
913 | def remove_entry(self, entry): |
914 | self.seek(0) |
915 | |
916 | - lines = self.readlines() |
917 | + lines = [l.decode('us-ascii') for l in self.readlines()] |
918 | |
919 | found = False |
920 | for index, line in enumerate(lines): |
921 | @@ -97,7 +99,7 @@ |
922 | lines.remove(line) |
923 | |
924 | self.seek(0) |
925 | - self.write(''.join(lines)) |
926 | + self.write(''.join(lines).encode('us-ascii')) |
927 | self.truncate() |
928 | return True |
929 | |
930 | |
931 | === modified file 'charmhelpers/core/hookenv.py' |
932 | --- charmhelpers/core/hookenv.py 2014-11-25 14:35:22 +0000 |
933 | +++ charmhelpers/core/hookenv.py 2014-11-25 15:09:14 +0000 |
934 | @@ -9,9 +9,14 @@ |
935 | import yaml |
936 | import subprocess |
937 | import sys |
938 | -import UserDict |
939 | from subprocess import CalledProcessError |
940 | |
941 | +import six |
942 | +if not six.PY3: |
943 | + from UserDict import UserDict |
944 | +else: |
945 | + from collections import UserDict |
946 | + |
947 | CRITICAL = "CRITICAL" |
948 | ERROR = "ERROR" |
949 | WARNING = "WARNING" |
950 | @@ -67,12 +72,12 @@ |
951 | subprocess.call(command) |
952 | |
953 | |
954 | -class Serializable(UserDict.IterableUserDict): |
955 | +class Serializable(UserDict): |
956 | """Wrapper, an object that can be serialized to yaml or json""" |
957 | |
958 | def __init__(self, obj): |
959 | # wrap the object |
960 | - UserDict.IterableUserDict.__init__(self) |
961 | + UserDict.__init__(self) |
962 | self.data = obj |
963 | |
964 | def __getattr__(self, attr): |
965 | @@ -218,7 +223,7 @@ |
966 | prev_keys = [] |
967 | if self._prev_dict is not None: |
968 | prev_keys = self._prev_dict.keys() |
969 | - return list(set(prev_keys + dict.keys(self))) |
970 | + return list(set(prev_keys + list(dict.keys(self)))) |
971 | |
972 | def load_previous(self, path=None): |
973 | """Load previous copy of config from disk. |
974 | @@ -269,7 +274,7 @@ |
975 | |
976 | """ |
977 | if self._prev_dict: |
978 | - for k, v in self._prev_dict.iteritems(): |
979 | + for k, v in six.iteritems(self._prev_dict): |
980 | if k not in self: |
981 | self[k] = v |
982 | with open(self.path, 'w') as f: |
983 | @@ -284,7 +289,8 @@ |
984 | config_cmd_line.append(scope) |
985 | config_cmd_line.append('--format=json') |
986 | try: |
987 | - config_data = json.loads(subprocess.check_output(config_cmd_line)) |
988 | + config_data = json.loads( |
989 | + subprocess.check_output(config_cmd_line).decode('UTF-8')) |
990 | if scope is not None: |
991 | return config_data |
992 | return Config(config_data) |
993 | @@ -303,10 +309,10 @@ |
994 | if unit: |
995 | _args.append(unit) |
996 | try: |
997 | - return json.loads(subprocess.check_output(_args)) |
998 | + return json.loads(subprocess.check_output(_args).decode('UTF-8')) |
999 | except ValueError: |
1000 | return None |
1001 | - except CalledProcessError, e: |
1002 | + except CalledProcessError as e: |
1003 | if e.returncode == 2: |
1004 | return None |
1005 | raise |
1006 | @@ -318,7 +324,7 @@ |
1007 | relation_cmd_line = ['relation-set'] |
1008 | if relation_id is not None: |
1009 | relation_cmd_line.extend(('-r', relation_id)) |
1010 | - for k, v in (relation_settings.items() + kwargs.items()): |
1011 | + for k, v in (list(relation_settings.items()) + list(kwargs.items())): |
1012 | if v is None: |
1013 | relation_cmd_line.append('{}='.format(k)) |
1014 | else: |
1015 | @@ -335,7 +341,8 @@ |
1016 | relid_cmd_line = ['relation-ids', '--format=json'] |
1017 | if reltype is not None: |
1018 | relid_cmd_line.append(reltype) |
1019 | - return json.loads(subprocess.check_output(relid_cmd_line)) or [] |
1020 | + return json.loads( |
1021 | + subprocess.check_output(relid_cmd_line).decode('UTF-8')) or [] |
1022 | return [] |
1023 | |
1024 | |
1025 | @@ -346,7 +353,8 @@ |
1026 | units_cmd_line = ['relation-list', '--format=json'] |
1027 | if relid is not None: |
1028 | units_cmd_line.extend(('-r', relid)) |
1029 | - return json.loads(subprocess.check_output(units_cmd_line)) or [] |
1030 | + return json.loads( |
1031 | + subprocess.check_output(units_cmd_line).decode('UTF-8')) or [] |
1032 | |
1033 | |
1034 | @cached |
1035 | @@ -455,7 +463,7 @@ |
1036 | """Get the unit ID for the remote unit""" |
1037 | _args = ['unit-get', '--format=json', attribute] |
1038 | try: |
1039 | - return json.loads(subprocess.check_output(_args)) |
1040 | + return json.loads(subprocess.check_output(_args).decode('UTF-8')) |
1041 | except ValueError: |
1042 | return None |
1043 | |
1044 | |
1045 | === modified file 'charmhelpers/core/host.py' |
1046 | --- charmhelpers/core/host.py 2014-11-25 14:35:22 +0000 |
1047 | +++ charmhelpers/core/host.py 2014-11-25 15:09:14 +0000 |
1048 | @@ -14,11 +14,12 @@ |
1049 | import subprocess |
1050 | import hashlib |
1051 | from contextlib import contextmanager |
1052 | - |
1053 | from collections import OrderedDict |
1054 | |
1055 | -from hookenv import log |
1056 | -from fstab import Fstab |
1057 | +import six |
1058 | + |
1059 | +from .hookenv import log |
1060 | +from .fstab import Fstab |
1061 | |
1062 | |
1063 | def service_start(service_name): |
1064 | @@ -54,7 +55,9 @@ |
1065 | def service_running(service): |
1066 | """Determine whether a system service is running""" |
1067 | try: |
1068 | - output = subprocess.check_output(['service', service, 'status'], stderr=subprocess.STDOUT) |
1069 | + output = subprocess.check_output( |
1070 | + ['service', service, 'status'], |
1071 | + stderr=subprocess.STDOUT).decode('UTF-8') |
1072 | except subprocess.CalledProcessError: |
1073 | return False |
1074 | else: |
1075 | @@ -67,7 +70,9 @@ |
1076 | def service_available(service_name): |
1077 | """Determine whether a system service is available""" |
1078 | try: |
1079 | - subprocess.check_output(['service', service_name, 'status'], stderr=subprocess.STDOUT) |
1080 | + subprocess.check_output( |
1081 | + ['service', service_name, 'status'], |
1082 | + stderr=subprocess.STDOUT).decode('UTF-8') |
1083 | except subprocess.CalledProcessError as e: |
1084 | return 'unrecognized service' not in e.output |
1085 | else: |
1086 | @@ -115,7 +120,7 @@ |
1087 | cmd.append(from_path) |
1088 | cmd.append(to_path) |
1089 | log(" ".join(cmd)) |
1090 | - return subprocess.check_output(cmd).strip() |
1091 | + return subprocess.check_output(cmd).decode('UTF-8').strip() |
1092 | |
1093 | |
1094 | def symlink(source, destination): |
1095 | @@ -130,7 +135,7 @@ |
1096 | subprocess.check_call(cmd) |
1097 | |
1098 | |
1099 | -def mkdir(path, owner='root', group='root', perms=0555, force=False): |
1100 | +def mkdir(path, owner='root', group='root', perms=0o555, force=False): |
1101 | """Create a directory""" |
1102 | log("Making dir {} {}:{} {:o}".format(path, owner, group, |
1103 | perms)) |
1104 | @@ -146,7 +151,7 @@ |
1105 | os.chown(realpath, uid, gid) |
1106 | |
1107 | |
1108 | -def write_file(path, content, owner='root', group='root', perms=0444): |
1109 | +def write_file(path, content, owner='root', group='root', perms=0o444): |
1110 | """Create or overwrite a file with the contents of a string""" |
1111 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) |
1112 | uid = pwd.getpwnam(owner).pw_uid |
1113 | @@ -177,7 +182,7 @@ |
1114 | cmd_args.extend([device, mountpoint]) |
1115 | try: |
1116 | subprocess.check_output(cmd_args) |
1117 | - except subprocess.CalledProcessError, e: |
1118 | + except subprocess.CalledProcessError as e: |
1119 | log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output)) |
1120 | return False |
1121 | |
1122 | @@ -191,7 +196,7 @@ |
1123 | cmd_args = ['umount', mountpoint] |
1124 | try: |
1125 | subprocess.check_output(cmd_args) |
1126 | - except subprocess.CalledProcessError, e: |
1127 | + except subprocess.CalledProcessError as e: |
1128 | log('Error unmounting {}\n{}'.format(mountpoint, e.output)) |
1129 | return False |
1130 | |
1131 | @@ -218,8 +223,8 @@ |
1132 | """ |
1133 | if os.path.exists(path): |
1134 | h = getattr(hashlib, hash_type)() |
1135 | - with open(path, 'r') as source: |
1136 | - h.update(source.read()) # IGNORE:E1101 - it does have update |
1137 | + with open(path, 'rb') as source: |
1138 | + h.update(source.read()) |
1139 | return h.hexdigest() |
1140 | else: |
1141 | return None |
1142 | @@ -297,7 +302,7 @@ |
1143 | if length is None: |
1144 | length = random.choice(range(35, 45)) |
1145 | alphanumeric_chars = [ |
1146 | - l for l in (string.letters + string.digits) |
1147 | + l for l in (string.ascii_letters + string.digits) |
1148 | if l not in 'l0QD1vAEIOUaeiou'] |
1149 | random_chars = [ |
1150 | random.choice(alphanumeric_chars) for _ in range(length)] |
1151 | @@ -306,14 +311,14 @@ |
1152 | |
1153 | def list_nics(nic_type): |
1154 | '''Return a list of nics of given type(s)''' |
1155 | - if isinstance(nic_type, basestring): |
1156 | + if isinstance(nic_type, six.string_types): |
1157 | int_types = [nic_type] |
1158 | else: |
1159 | int_types = nic_type |
1160 | interfaces = [] |
1161 | for int_type in int_types: |
1162 | cmd = ['ip', 'addr', 'show', 'label', int_type + '*'] |
1163 | - ip_output = subprocess.check_output(cmd).split('\n') |
1164 | + ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n') |
1165 | ip_output = (line for line in ip_output if line) |
1166 | for line in ip_output: |
1167 | if line.split()[1].startswith(int_type): |
1168 | @@ -335,7 +340,7 @@ |
1169 | |
1170 | def get_nic_mtu(nic): |
1171 | cmd = ['ip', 'addr', 'show', nic] |
1172 | - ip_output = subprocess.check_output(cmd).split('\n') |
1173 | + ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n') |
1174 | mtu = "" |
1175 | for line in ip_output: |
1176 | words = line.split() |
1177 | @@ -346,7 +351,7 @@ |
1178 | |
1179 | def get_nic_hwaddr(nic): |
1180 | cmd = ['ip', '-o', '-0', 'addr', 'show', nic] |
1181 | - ip_output = subprocess.check_output(cmd) |
1182 | + ip_output = subprocess.check_output(cmd).decode('UTF-8') |
1183 | hwaddr = "" |
1184 | words = ip_output.split() |
1185 | if 'link/ether' in words: |
1186 | |
1187 | === modified file 'charmhelpers/core/services/helpers.py' |
1188 | --- charmhelpers/core/services/helpers.py 2014-11-25 14:35:22 +0000 |
1189 | +++ charmhelpers/core/services/helpers.py 2014-11-25 15:09:14 +0000 |
1190 | @@ -196,7 +196,7 @@ |
1191 | if not os.path.isabs(file_name): |
1192 | file_name = os.path.join(hookenv.charm_dir(), file_name) |
1193 | with open(file_name, 'w') as file_stream: |
1194 | - os.fchmod(file_stream.fileno(), 0600) |
1195 | + os.fchmod(file_stream.fileno(), 0o600) |
1196 | yaml.dump(config_data, file_stream) |
1197 | |
1198 | def read_context(self, file_name): |
1199 | @@ -211,15 +211,19 @@ |
1200 | |
1201 | class TemplateCallback(ManagerCallback): |
1202 | """ |
1203 | - Callback class that will render a Jinja2 template, for use as a ready action. |
1204 | - |
1205 | - :param str source: The template source file, relative to `$CHARM_DIR/templates` |
1206 | + Callback class that will render a Jinja2 template, for use as a ready |
1207 | + action. |
1208 | + |
1209 | + :param str source: The template source file, relative to |
1210 | + `$CHARM_DIR/templates` |
1211 | + |
1212 | :param str target: The target to write the rendered template to |
1213 | :param str owner: The owner of the rendered file |
1214 | :param str group: The group of the rendered file |
1215 | :param int perms: The permissions of the rendered file |
1216 | """ |
1217 | - def __init__(self, source, target, owner='root', group='root', perms=0444): |
1218 | + def __init__(self, source, target, |
1219 | + owner='root', group='root', perms=0o444): |
1220 | self.source = source |
1221 | self.target = target |
1222 | self.owner = owner |
1223 | |
1224 | === modified file 'charmhelpers/core/templating.py' |
1225 | --- charmhelpers/core/templating.py 2014-11-25 14:35:22 +0000 |
1226 | +++ charmhelpers/core/templating.py 2014-11-25 15:09:14 +0000 |
1227 | @@ -4,7 +4,8 @@ |
1228 | from charmhelpers.core import hookenv |
1229 | |
1230 | |
1231 | -def render(source, target, context, owner='root', group='root', perms=0444, templates_dir=None): |
1232 | +def render(source, target, context, owner='root', group='root', |
1233 | + perms=0o444, templates_dir=None): |
1234 | """ |
1235 | Render a template. |
1236 | |
1237 | |
1238 | === modified file 'charmhelpers/fetch/__init__.py' |
1239 | --- charmhelpers/fetch/__init__.py 2014-11-25 14:35:22 +0000 |
1240 | +++ charmhelpers/fetch/__init__.py 2014-11-25 15:09:14 +0000 |
1241 | @@ -5,10 +5,6 @@ |
1242 | from charmhelpers.core.host import ( |
1243 | lsb_release |
1244 | ) |
1245 | -from urlparse import ( |
1246 | - urlparse, |
1247 | - urlunparse, |
1248 | -) |
1249 | import subprocess |
1250 | from charmhelpers.core.hookenv import ( |
1251 | config, |
1252 | @@ -16,6 +12,12 @@ |
1253 | ) |
1254 | import os |
1255 | |
1256 | +import six |
1257 | +if six.PY3: |
1258 | + from urllib.parse import urlparse, urlunparse |
1259 | +else: |
1260 | + from urlparse import urlparse, urlunparse |
1261 | + |
1262 | |
1263 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive |
1264 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
1265 | @@ -149,7 +151,7 @@ |
1266 | cmd = ['apt-get', '--assume-yes'] |
1267 | cmd.extend(options) |
1268 | cmd.append('install') |
1269 | - if isinstance(packages, basestring): |
1270 | + if isinstance(packages, six.string_types): |
1271 | cmd.append(packages) |
1272 | else: |
1273 | cmd.extend(packages) |
1274 | @@ -182,7 +184,7 @@ |
1275 | def apt_purge(packages, fatal=False): |
1276 | """Purge one or more packages""" |
1277 | cmd = ['apt-get', '--assume-yes', 'purge'] |
1278 | - if isinstance(packages, basestring): |
1279 | + if isinstance(packages, six.string_types): |
1280 | cmd.append(packages) |
1281 | else: |
1282 | cmd.extend(packages) |
1283 | @@ -193,7 +195,7 @@ |
1284 | def apt_hold(packages, fatal=False): |
1285 | """Hold one or more packages""" |
1286 | cmd = ['apt-mark', 'hold'] |
1287 | - if isinstance(packages, basestring): |
1288 | + if isinstance(packages, six.string_types): |
1289 | cmd.append(packages) |
1290 | else: |
1291 | cmd.extend(packages) |
1292 | @@ -260,7 +262,7 @@ |
1293 | |
1294 | if key: |
1295 | if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: |
1296 | - with NamedTemporaryFile() as key_file: |
1297 | + with NamedTemporaryFile('w+') as key_file: |
1298 | key_file.write(key) |
1299 | key_file.flush() |
1300 | key_file.seek(0) |
1301 | @@ -297,14 +299,14 @@ |
1302 | sources = safe_load((config(sources_var) or '').strip()) or [] |
1303 | keys = safe_load((config(keys_var) or '').strip()) or None |
1304 | |
1305 | - if isinstance(sources, basestring): |
1306 | + if isinstance(sources, six.string_types): |
1307 | sources = [sources] |
1308 | |
1309 | if keys is None: |
1310 | for source in sources: |
1311 | add_source(source, None) |
1312 | else: |
1313 | - if isinstance(keys, basestring): |
1314 | + if isinstance(keys, six.string_types): |
1315 | keys = [keys] |
1316 | |
1317 | if len(sources) != len(keys): |
1318 | @@ -401,7 +403,7 @@ |
1319 | while result is None or result == APT_NO_LOCK: |
1320 | try: |
1321 | result = subprocess.check_call(cmd, env=env) |
1322 | - except subprocess.CalledProcessError, e: |
1323 | + except subprocess.CalledProcessError as e: |
1324 | retry_count = retry_count + 1 |
1325 | if retry_count > APT_NO_LOCK_RETRY_COUNT: |
1326 | raise |
1327 | |
1328 | === modified file 'charmhelpers/fetch/archiveurl.py' |
1329 | --- charmhelpers/fetch/archiveurl.py 2014-11-25 14:35:22 +0000 |
1330 | +++ charmhelpers/fetch/archiveurl.py 2014-11-25 15:09:14 +0000 |
1331 | @@ -1,8 +1,23 @@ |
1332 | import os |
1333 | -import urllib2 |
1334 | -from urllib import urlretrieve |
1335 | -import urlparse |
1336 | import hashlib |
1337 | +import re |
1338 | + |
1339 | +import six |
1340 | +if six.PY3: |
1341 | + from urllib.request import ( |
1342 | + build_opener, install_opener, urlopen, urlretrieve, |
1343 | + HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, |
1344 | + ) |
1345 | + from urllib.parse import urlparse, urlunparse, parse_qs |
1346 | + from urllib.error import URLError |
1347 | +else: |
1348 | + from urllib import urlretrieve |
1349 | + from urllib2 import ( |
1350 | + build_opener, install_opener, urlopen, |
1351 | + HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, |
1352 | + URLError |
1353 | + ) |
1354 | + from urlparse import urlparse, urlunparse, parse_qs |
1355 | |
1356 | from charmhelpers.fetch import ( |
1357 | BaseFetchHandler, |
1358 | @@ -15,6 +30,24 @@ |
1359 | from charmhelpers.core.host import mkdir, check_hash |
1360 | |
1361 | |
1362 | +def splituser(host): |
1363 | + '''urllib.splituser(), but six's support of this seems broken''' |
1364 | + _userprog = re.compile('^(.*)@(.*)$') |
1365 | + match = _userprog.match(host) |
1366 | + if match: |
1367 | + return match.group(1, 2) |
1368 | + return None, host |
1369 | + |
1370 | + |
1371 | +def splitpasswd(user): |
1372 | + '''urllib.splitpasswd(), but six's support of this is missing''' |
1373 | + _passwdprog = re.compile('^([^:]*):(.*)$', re.S) |
1374 | + match = _passwdprog.match(user) |
1375 | + if match: |
1376 | + return match.group(1, 2) |
1377 | + return user, None |
1378 | + |
1379 | + |
1380 | class ArchiveUrlFetchHandler(BaseFetchHandler): |
1381 | """ |
1382 | Handler to download archive files from arbitrary URLs. |
1383 | @@ -42,20 +75,20 @@ |
1384 | """ |
1385 | # propogate all exceptions |
1386 | # URLError, OSError, etc |
1387 | - proto, netloc, path, params, query, fragment = urlparse.urlparse(source) |
1388 | + proto, netloc, path, params, query, fragment = urlparse(source) |
1389 | if proto in ('http', 'https'): |
1390 | - auth, barehost = urllib2.splituser(netloc) |
1391 | + auth, barehost = splituser(netloc) |
1392 | if auth is not None: |
1393 | - source = urlparse.urlunparse((proto, barehost, path, params, query, fragment)) |
1394 | - username, password = urllib2.splitpasswd(auth) |
1395 | - passman = urllib2.HTTPPasswordMgrWithDefaultRealm() |
1396 | + source = urlunparse((proto, barehost, path, params, query, fragment)) |
1397 | + username, password = splitpasswd(auth) |
1398 | + passman = HTTPPasswordMgrWithDefaultRealm() |
1399 | # Realm is set to None in add_password to force the username and password |
1400 | # to be used whatever the realm |
1401 | passman.add_password(None, source, username, password) |
1402 | - authhandler = urllib2.HTTPBasicAuthHandler(passman) |
1403 | - opener = urllib2.build_opener(authhandler) |
1404 | - urllib2.install_opener(opener) |
1405 | - response = urllib2.urlopen(source) |
1406 | + authhandler = HTTPBasicAuthHandler(passman) |
1407 | + opener = build_opener(authhandler) |
1408 | + install_opener(opener) |
1409 | + response = urlopen(source) |
1410 | try: |
1411 | with open(dest, 'w') as dest_file: |
1412 | dest_file.write(response.read()) |
1413 | @@ -91,17 +124,21 @@ |
1414 | url_parts = self.parse_url(source) |
1415 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), 'fetched') |
1416 | if not os.path.exists(dest_dir): |
1417 | - mkdir(dest_dir, perms=0755) |
1418 | + mkdir(dest_dir, perms=0o755) |
1419 | dld_file = os.path.join(dest_dir, os.path.basename(url_parts.path)) |
1420 | try: |
1421 | self.download(source, dld_file) |
1422 | - except urllib2.URLError as e: |
1423 | + except URLError as e: |
1424 | raise UnhandledSource(e.reason) |
1425 | except OSError as e: |
1426 | raise UnhandledSource(e.strerror) |
1427 | - options = urlparse.parse_qs(url_parts.fragment) |
1428 | + options = parse_qs(url_parts.fragment) |
1429 | for key, value in options.items(): |
1430 | - if key in hashlib.algorithms: |
1431 | + if not six.PY3: |
1432 | + algorithms = hashlib.algorithms |
1433 | + else: |
1434 | + algorithms = hashlib.algorithms_available |
1435 | + if key in algorithms: |
1436 | check_hash(dld_file, value, key) |
1437 | if checksum: |
1438 | check_hash(dld_file, checksum, hash_type) |
1439 | |
1440 | === modified file 'charmhelpers/fetch/bzrurl.py' |
1441 | --- charmhelpers/fetch/bzrurl.py 2014-11-25 14:35:22 +0000 |
1442 | +++ charmhelpers/fetch/bzrurl.py 2014-11-25 15:09:14 +0000 |
1443 | @@ -5,6 +5,10 @@ |
1444 | ) |
1445 | from charmhelpers.core.host import mkdir |
1446 | |
1447 | +import six |
1448 | +if six.PY3: |
1449 | + raise ImportError('bzrlib does not support Python3') |
1450 | + |
1451 | try: |
1452 | from bzrlib.branch import Branch |
1453 | except ImportError: |
1454 | @@ -42,7 +46,7 @@ |
1455 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
1456 | branch_name) |
1457 | if not os.path.exists(dest_dir): |
1458 | - mkdir(dest_dir, perms=0755) |
1459 | + mkdir(dest_dir, perms=0o755) |
1460 | try: |
1461 | self.branch(source, dest_dir) |
1462 | except OSError as e: |
1463 | |
1464 | === modified file 'charmhelpers/fetch/giturl.py' |
1465 | --- charmhelpers/fetch/giturl.py 2014-11-25 14:35:22 +0000 |
1466 | +++ charmhelpers/fetch/giturl.py 2014-11-25 15:09:14 +0000 |
1467 | @@ -5,6 +5,10 @@ |
1468 | ) |
1469 | from charmhelpers.core.host import mkdir |
1470 | |
1471 | +import six |
1472 | +if six.PY3: |
1473 | + raise ImportError('GitPython does not support Python 3') |
1474 | + |
1475 | try: |
1476 | from git import Repo |
1477 | except ImportError: |
1478 | @@ -17,7 +21,7 @@ |
1479 | """Handler for git branches via generic and github URLs""" |
1480 | def can_handle(self, source): |
1481 | url_parts = self.parse_url(source) |
1482 | - #TODO (mattyw) no support for ssh git@ yet |
1483 | + # TODO (mattyw) no support for ssh git@ yet |
1484 | if url_parts.scheme not in ('http', 'https', 'git'): |
1485 | return False |
1486 | else: |
1487 | @@ -36,7 +40,7 @@ |
1488 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
1489 | branch_name) |
1490 | if not os.path.exists(dest_dir): |
1491 | - mkdir(dest_dir, perms=0755) |
1492 | + mkdir(dest_dir, perms=0o755) |
1493 | try: |
1494 | self.clone(source, dest_dir, branch) |
1495 | except OSError as e: |
1496 | |
1497 | === modified file 'test_requirements.txt' |
1498 | --- test_requirements.txt 2014-11-25 14:35:22 +0000 |
1499 | +++ test_requirements.txt 2014-11-25 15:09:14 +0000 |
1500 | @@ -1,18 +1,17 @@ |
1501 | -coverage==3.6 |
1502 | -launchpadlib==1.10.2 |
1503 | ---allow-external launchpadlib |
1504 | ---allow-unverified launchpadlib |
1505 | -mock==1.0.1 |
1506 | -netaddr |
1507 | -nose==1.3.1 |
1508 | -PyYAML==3.10 |
1509 | -simplejson==3.3.0 |
1510 | -testtools |
1511 | -Tempita==0.5.1 |
1512 | -bzr+http://bazaar.launchpad.net/~yellow/python-shelltoolbox/trunk@17#egg=shelltoolbox |
1513 | -http://alastairs-place.net/projects/netifaces/netifaces-0.6.tar.gz |
1514 | -bzr==2.6.0 |
1515 | -GitPython>=0.3.2.RC1 |
1516 | -Jinja2==2.7.2 |
1517 | ---allow-external lazr.authentication |
1518 | ---allow-unverified lazr.authentication |
1519 | +# Test-only dependencies are unpinned. |
1520 | +# |
1521 | +pip |
1522 | +distribute |
1523 | +coverage>=3.6 |
1524 | +mock>=1.0.1 |
1525 | +nose>=1.3.1 |
1526 | +flake8 |
1527 | +testtools==0.9.14 # Before dependent on modern 'six' |
1528 | +# |
1529 | +# Specify precise versions of runtime dependencies where possible. |
1530 | +netaddr==0.7.10 # trusty. precise is 0.7.5, but not in pypi. |
1531 | +PyYAML==3.10 # precise |
1532 | +Tempita==0.5.1 # precise |
1533 | +netifaces==0.10 # trusty is 0.8, but using py3 compatible version for tests. |
1534 | +Jinja2==2.6 # precise |
1535 | +six==1.1 # precise |
1536 | |
1537 | === modified file 'tests/cli/test_cmdline.py' |
1538 | --- tests/cli/test_cmdline.py 2014-11-25 14:35:22 +0000 |
1539 | +++ tests/cli/test_cmdline.py 2014-11-25 15:09:14 +0000 |
1540 | @@ -6,15 +6,13 @@ |
1541 | patch, |
1542 | MagicMock, |
1543 | ) |
1544 | -try: |
1545 | - from cStringIO import StringIO |
1546 | -except ImportError: |
1547 | - from StringIO import StringIO |
1548 | import json |
1549 | from pprint import pformat |
1550 | import yaml |
1551 | import csv |
1552 | |
1553 | +from six import StringIO |
1554 | + |
1555 | from charmhelpers import cli |
1556 | |
1557 | |
1558 | @@ -116,7 +114,8 @@ |
1559 | self.output_data = {"this": "is", "some": 1, "data": dict()} |
1560 | |
1561 | def test_supports_formats(self): |
1562 | - self.assertItemsEqual(self.expected_formats, self.of.supported_formats) |
1563 | + self.assertEqual(sorted(self.expected_formats), |
1564 | + sorted(self.of.supported_formats)) |
1565 | |
1566 | def test_adds_arguments(self): |
1567 | ap = MagicMock() |
1568 | @@ -130,11 +129,12 @@ |
1569 | |
1570 | for call_args in add_arg.call_args_list: |
1571 | if "--format" in call_args[0]: |
1572 | - self.assertItemsEqual(call_args[1]['choices'], self.expected_formats) |
1573 | + self.assertEqual(sorted(call_args[1]['choices']), |
1574 | + sorted(self.expected_formats)) |
1575 | self.assertEqual(call_args[1]['default'], 'raw') |
1576 | break |
1577 | else: |
1578 | - print arg_group.call_args_list |
1579 | + print(arg_group.call_args_list) |
1580 | self.fail("No --format argument was created") |
1581 | |
1582 | all_args = [c[0][0] for c in add_arg.call_args_list] |
1583 | |
1584 | === modified file 'tests/cli/test_function_signature_analysis.py' |
1585 | --- tests/cli/test_function_signature_analysis.py 2013-08-21 21:40:07 +0000 |
1586 | +++ tests/cli/test_function_signature_analysis.py 2014-11-25 15:09:14 +0000 |
1587 | @@ -1,7 +1,7 @@ |
1588 | """Tests for the commandant code that analyzes a function signature to |
1589 | determine the parameters to argparse.""" |
1590 | |
1591 | -from testtools import TestCase, matchers |
1592 | +from testtools import TestCase |
1593 | |
1594 | from charmhelpers import cli |
1595 | |
1596 | @@ -42,5 +42,5 @@ |
1597 | args = cli.describe_arguments(lambda x, y=3, *z, **missing: False) |
1598 | for opts, _ in args: |
1599 | # opts should be ('varname',) at this point |
1600 | - self.assertThat(opts, matchers.HasLength(1)) |
1601 | + self.assertTrue(len(opts) == 1) |
1602 | self.assertNotIn('missing', opts) |
1603 | |
1604 | === modified file 'tests/contrib/charmhelpers/test_charmhelpers.py' |
1605 | --- tests/contrib/charmhelpers/test_charmhelpers.py 2014-11-25 14:35:22 +0000 |
1606 | +++ tests/contrib/charmhelpers/test_charmhelpers.py 2014-11-25 15:09:14 +0000 |
1607 | @@ -2,10 +2,10 @@ |
1608 | |
1609 | import unittest |
1610 | import yaml |
1611 | - |
1612 | -from StringIO import StringIO |
1613 | from testtools import TestCase |
1614 | |
1615 | +from six import StringIO |
1616 | + |
1617 | import sys |
1618 | # Path hack to ensure we test the local code, not a version installed in |
1619 | # /usr/local/lib. This is necessary since /usr/local/lib is prepended before |
1620 | @@ -265,11 +265,11 @@ |
1621 | # wait_for_page_contents() will wait until a given string is |
1622 | # contained within the results of a given url and will return |
1623 | # once it does. |
1624 | - # We need to patch the charmhelpers instance of urllib2 so that |
1625 | + # We need to patch the charmhelpers instance of urlopen so that |
1626 | # it doesn't try to connect out. |
1627 | test_content = "Hello, world." |
1628 | new_urlopen = lambda *args: StringIO(test_content) |
1629 | - self.patch(charmhelpers.urllib2, 'urlopen', new_urlopen) |
1630 | + self.patch(charmhelpers, 'urlopen', new_urlopen) |
1631 | charmhelpers.wait_for_page_contents( |
1632 | 'http://example.com', test_content, timeout=0) |
1633 | |
1634 | @@ -277,10 +277,10 @@ |
1635 | # If the desired contents do not appear within the page before |
1636 | # the specified timeout, wait_for_page_contents() will raise a |
1637 | # RuntimeError. |
1638 | - # We need to patch the charmhelpers instance of urllib2 so that |
1639 | + # We need to patch the charmhelpers instance of urlopen so that |
1640 | # it doesn't try to connect out. |
1641 | new_urlopen = lambda *args: StringIO("This won't work.") |
1642 | - self.patch(charmhelpers.urllib2, 'urlopen', new_urlopen) |
1643 | + self.patch(charmhelpers, 'urlopen', new_urlopen) |
1644 | self.assertRaises( |
1645 | RuntimeError, charmhelpers.wait_for_page_contents, |
1646 | 'http://example.com', "This will error", timeout=0) |
1647 | |
1648 | === modified file 'tests/contrib/hahelpers/test_cluster_utils.py' |
1649 | --- tests/contrib/hahelpers/test_cluster_utils.py 2014-09-24 09:42:52 +0000 |
1650 | +++ tests/contrib/hahelpers/test_cluster_utils.py 2014-11-25 15:09:14 +0000 |
1651 | @@ -44,7 +44,7 @@ |
1652 | def test_is_crm_leader(self, check_output): |
1653 | '''It determines its unit is leader''' |
1654 | self.get_unit_hostname.return_value = 'node1' |
1655 | - crm = 'resource vip is running on: node1' |
1656 | + crm = b'resource vip is running on: node1' |
1657 | check_output.return_value = crm |
1658 | self.assertTrue(cluster_utils.is_crm_leader('vip')) |
1659 | |
1660 | @@ -52,7 +52,7 @@ |
1661 | def test_is_not_leader(self, check_output): |
1662 | '''It determines its unit is not leader''' |
1663 | self.get_unit_hostname.return_value = 'node1' |
1664 | - crm = 'resource vip is running on: node2' |
1665 | + crm = b'resource vip is running on: node2' |
1666 | check_output.return_value = crm |
1667 | self.assertFalse(cluster_utils.is_crm_leader('some_resource')) |
1668 | |
1669 | |
1670 | === modified file 'tests/contrib/network/test_ip.py' |
1671 | --- tests/contrib/network/test_ip.py 2014-11-25 14:35:22 +0000 |
1672 | +++ tests/contrib/network/test_ip.py 2014-11-25 15:09:14 +0000 |
1673 | @@ -59,7 +59,7 @@ |
1674 | }, |
1675 | } |
1676 | |
1677 | -IP_OUTPUT = """link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff |
1678 | +IP_OUTPUT = b"""link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff |
1679 | inet 10.5.16.93/16 brd 10.5.255.255 scope global eth0 |
1680 | valid_lft forever preferred_lft forever |
1681 | inet6 2001:db8:1:0:d0cf:528c:23eb:6000/64 scope global |
1682 | @@ -72,7 +72,7 @@ |
1683 | valid_lft forever preferred_lft forever |
1684 | """ |
1685 | |
1686 | -IP_OUTPUT_NO_VALID = """link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff |
1687 | +IP_OUTPUT_NO_VALID = b"""link/ether fa:16:3e:2a:cc:ce brd ff:ff:ff:ff:ff:ff |
1688 | inet 10.5.16.93/16 brd 10.5.255.255 scope global eth0 |
1689 | valid_lft forever preferred_lft forever |
1690 | inet6 2001:db8:1:0:2918:3444:852:5b8a/64 scope global temporary dynamic |
1691 | @@ -100,13 +100,14 @@ |
1692 | return DUMMY_ADDRESSES[iface] |
1693 | |
1694 | with mock.patch.object(netifaces, 'interfaces') as interfaces: |
1695 | - interfaces.return_value = DUMMY_ADDRESSES.keys() |
1696 | + interfaces.return_value = sorted(DUMMY_ADDRESSES.keys()) |
1697 | with mock.patch.object(netifaces, 'ifaddresses') as ifaddresses: |
1698 | ifaddresses.side_effect = side_effect |
1699 | if not fatal: |
1700 | self.assertEqual(expect_ip_addr, |
1701 | - net_ip.get_address_in_network( |
1702 | - network, fallback, fatal)) |
1703 | + net_ip.get_address_in_network(network, |
1704 | + fallback, |
1705 | + fatal)) |
1706 | else: |
1707 | net_ip.get_address_in_network(network, fallback, fatal) |
1708 | |
1709 | @@ -122,7 +123,7 @@ |
1710 | None, None, fatal=True) |
1711 | |
1712 | def test_get_address_in_network_ipv4(self): |
1713 | - self._test_get_address_in_network('192.168.1.56', '192.168.1.0/24') |
1714 | + self._test_get_address_in_network('192.168.1.55', '192.168.1.0/24') |
1715 | |
1716 | def test_get_address_in_network_ipv6(self): |
1717 | self._test_get_address_in_network('2a01:348:2f4:0:685e:5748:ae62:209f', |
1718 | @@ -260,7 +261,7 @@ |
1719 | mock_get_iface_from_addr): |
1720 | mock_get_iface_from_addr.return_value = 'eth0' |
1721 | mock_check_out.return_value = \ |
1722 | - "inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic" |
1723 | + b"inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic" |
1724 | _interfaces.return_value = DUMMY_ADDRESSES.keys() |
1725 | _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__ |
1726 | result = net_ip.get_ipv6_addr(dynamic_only=False) |
1727 | @@ -275,7 +276,7 @@ |
1728 | mock_get_iface_from_addr): |
1729 | mock_get_iface_from_addr.return_value = 'eth0' |
1730 | mock_check_out.return_value = \ |
1731 | - "inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic" |
1732 | + b"inet6 2a01:348:2f4:0:685e:5748:ae62:209f/64 scope global dynamic" |
1733 | _interfaces.return_value = DUMMY_ADDRESSES.keys() |
1734 | _ifaddresses.side_effect = DUMMY_ADDRESSES.__getitem__ |
1735 | result = net_ip.get_ipv6_addr(dynamic_only=False) |
1736 | @@ -494,9 +495,9 @@ |
1737 | def test_get_iface_from_addr(self, mock_log, mock_ifaddresses, |
1738 | mock_interfaces): |
1739 | mock_ifaddresses.side_effect = lambda iface: DUMMY_ADDRESSES[iface] |
1740 | - mock_interfaces.return_value = DUMMY_ADDRESSES.keys() |
1741 | + mock_interfaces.return_value = sorted(DUMMY_ADDRESSES.keys()) |
1742 | addr = 'fe80::3e97:eff:fe8b:1cf7' |
1743 | - self.assertEqual(net_ip.get_iface_from_addr(addr), 'eth1') |
1744 | + self.assertEqual(net_ip.get_iface_from_addr(addr), 'eth0') |
1745 | |
1746 | with nose.tools.assert_raises(Exception): |
1747 | net_ip.get_iface_from_addr('1.2.3.4') |
1748 | |
1749 | === modified file 'tests/contrib/openstack/test_neutron_utils.py' |
1750 | --- tests/contrib/openstack/test_neutron_utils.py 2014-11-07 09:54:33 +0000 |
1751 | +++ tests/contrib/openstack/test_neutron_utils.py 2014-11-25 15:09:14 +0000 |
1752 | @@ -23,12 +23,12 @@ |
1753 | return mock |
1754 | |
1755 | def test_headers_package(self): |
1756 | - self.check_output.return_value = '3.13.0-19-generic' |
1757 | + self.check_output.return_value = b'3.13.0-19-generic' |
1758 | kname = neutron.headers_package() |
1759 | self.assertEquals(kname, 'linux-headers-3.13.0-19-generic') |
1760 | |
1761 | def test_kernel_version(self): |
1762 | - self.check_output.return_value = '3.13.0-19-generic' |
1763 | + self.check_output.return_value = b'3.13.0-19-generic' |
1764 | kver_maj, kver_min = neutron.kernel_version() |
1765 | self.assertEquals((kver_maj, kver_min), (3, 13)) |
1766 | |
1767 | |
1768 | === modified file 'tests/contrib/openstack/test_openstack_utils.py' |
1769 | --- tests/contrib/openstack/test_openstack_utils.py 2014-11-25 14:35:22 +0000 |
1770 | +++ tests/contrib/openstack/test_openstack_utils.py 2014-11-25 15:09:14 +0000 |
1771 | @@ -1,13 +1,22 @@ |
1772 | +import io |
1773 | import os |
1774 | import subprocess |
1775 | import unittest |
1776 | - |
1777 | from copy import copy |
1778 | from testtools import TestCase |
1779 | from mock import MagicMock, patch, call |
1780 | |
1781 | import charmhelpers.contrib.openstack.utils as openstack |
1782 | |
1783 | +import six |
1784 | + |
1785 | +if not six.PY3: |
1786 | + builtin_open = '__builtin__.open' |
1787 | + builtin_import = '__builtin__.__import__' |
1788 | +else: |
1789 | + builtin_open = 'builtins.open' |
1790 | + builtin_import = 'builtins.__import__' |
1791 | + |
1792 | # mocked return of openstack.lsb_release() |
1793 | FAKE_RELEASE = { |
1794 | 'DISTRIB_CODENAME': 'precise', |
1795 | @@ -222,7 +231,7 @@ |
1796 | '''Test deriving OpenStack codename from an installed package''' |
1797 | with patch('apt_pkg.Cache') as cache: |
1798 | cache.return_value = self._apt_cache() |
1799 | - for pkg, vers in FAKE_REPO.iteritems(): |
1800 | + for pkg, vers in six.iteritems(FAKE_REPO): |
1801 | # test fake repo for all "installed" packages |
1802 | if pkg.startswith('bad-'): |
1803 | continue |
1804 | @@ -291,7 +300,7 @@ |
1805 | '''Test deriving OpenStack version from an installed package''' |
1806 | with patch('apt_pkg.Cache') as cache: |
1807 | cache.return_value = self._apt_cache() |
1808 | - for pkg, vers in FAKE_REPO.iteritems(): |
1809 | + for pkg, vers in six.iteritems(FAKE_REPO): |
1810 | if pkg.startswith('bad-'): |
1811 | continue |
1812 | if 'pkg_vers' not in vers: |
1813 | @@ -354,12 +363,12 @@ |
1814 | ex_cmd = ['add-apt-repository', '-y', 'ppa:gandelman-a/openstack'] |
1815 | mock.assert_called_with(ex_cmd) |
1816 | |
1817 | - @patch('__builtin__.open') |
1818 | + @patch(builtin_open) |
1819 | @patch('charmhelpers.contrib.openstack.utils.juju_log') |
1820 | @patch('charmhelpers.contrib.openstack.utils.import_key') |
1821 | def test_configure_install_source_deb_url(self, _import, _log, _open): |
1822 | '''Test configuring installation source from deb repo url''' |
1823 | - _file = MagicMock(spec=file) |
1824 | + _file = MagicMock(spec=io.FileIO) |
1825 | _open.return_value = _file |
1826 | src = ('deb http://ubuntu-cloud.archive.canonical.com/ubuntu ' |
1827 | 'precise-havana main|KEYID') |
1828 | @@ -372,12 +381,12 @@ |
1829 | _file.__enter__().write.assert_called_with(src) |
1830 | |
1831 | @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1832 | - @patch('__builtin__.open') |
1833 | + @patch(builtin_open) |
1834 | @patch('charmhelpers.contrib.openstack.utils.juju_log') |
1835 | def test_configure_install_source_distro_proposed(self, _log, _open, _lsb): |
1836 | '''Test configuring installation source from deb repo url''' |
1837 | _lsb.return_value = FAKE_RELEASE |
1838 | - _file = MagicMock(spec=file) |
1839 | + _file = MagicMock(spec=io.FileIO) |
1840 | _open.return_value = _file |
1841 | openstack.configure_installation_source('distro-proposed') |
1842 | src = ('deb http://archive.ubuntu.com/ubuntu/ precise-proposed ' |
1843 | @@ -402,13 +411,13 @@ |
1844 | 'ppa:ubuntu-cloud-archive/folsom-staging'] |
1845 | _subp.assert_called_with(cmd) |
1846 | |
1847 | - @patch('__builtin__.open') |
1848 | + @patch(builtin_open) |
1849 | @patch('charmhelpers.contrib.openstack.utils.apt_install') |
1850 | @patch('charmhelpers.contrib.openstack.utils.lsb_release') |
1851 | def test_configure_install_source_uca_repos(self, _lsb, _install, _open): |
1852 | '''Test configuring installation source from UCA sources''' |
1853 | _lsb.return_value = FAKE_RELEASE |
1854 | - _file = MagicMock(spec=file) |
1855 | + _file = MagicMock(spec=io.FileIO) |
1856 | _open.return_value = _file |
1857 | for src, url in UCA_SOURCES: |
1858 | openstack.configure_installation_source(src) |
1859 | @@ -455,13 +464,13 @@ |
1860 | @patch('os.mkdir') |
1861 | @patch('os.path.exists') |
1862 | @patch('charmhelpers.contrib.openstack.utils.charm_dir') |
1863 | - @patch('__builtin__.open') |
1864 | + @patch(builtin_open) |
1865 | def test_save_scriptrc(self, _open, _charm_dir, _exists, _mkdir): |
1866 | '''Test generation of scriptrc from environment''' |
1867 | scriptrc = ['#!/bin/bash\n', |
1868 | 'export setting1=foo\n', |
1869 | 'export setting2=bar\n'] |
1870 | - _file = MagicMock(spec=file) |
1871 | + _file = MagicMock(spec=io.FileIO) |
1872 | _open.return_value = _file |
1873 | _charm_dir.return_value = '/var/lib/juju/units/testing-foo-0/charm' |
1874 | _exists.return_value = False |
1875 | @@ -583,21 +592,21 @@ |
1876 | @patch.object(openstack, 'apt_install') |
1877 | def test_get_host_ip_with_hostname(self, apt_install): |
1878 | fake_dns = FakeDNS('10.0.0.1') |
1879 | - with patch('__builtin__.__import__', side_effect=[fake_dns]): |
1880 | + with patch(builtin_import, side_effect=[fake_dns]): |
1881 | ip = openstack.get_host_ip('www.ubuntu.com') |
1882 | self.assertEquals(ip, '10.0.0.1') |
1883 | |
1884 | @patch.object(openstack, 'apt_install') |
1885 | def test_get_host_ip_with_ip(self, apt_install): |
1886 | fake_dns = FakeDNS('5.5.5.5') |
1887 | - with patch('__builtin__.__import__', side_effect=[fake_dns]): |
1888 | + with patch(builtin_import, side_effect=[fake_dns]): |
1889 | ip = openstack.get_host_ip('4.2.2.1') |
1890 | self.assertEquals(ip, '4.2.2.1') |
1891 | |
1892 | @patch.object(openstack, 'apt_install') |
1893 | def test_ns_query_trigger_apt_install(self, apt_install): |
1894 | fake_dns = FakeDNS('5.5.5.5') |
1895 | - with patch('__builtin__.__import__', side_effect=[ImportError, fake_dns]): |
1896 | + with patch(builtin_import, side_effect=[ImportError, fake_dns]): |
1897 | nsq = openstack.ns_query('5.5.5.5') |
1898 | apt_install.assert_called_with('python-dnspython') |
1899 | self.assertEquals(nsq, '5.5.5.5') |
1900 | @@ -605,7 +614,7 @@ |
1901 | @patch.object(openstack, 'apt_install') |
1902 | def test_ns_query_ptr_record(self, apt_install): |
1903 | fake_dns = FakeDNS('127.0.0.1') |
1904 | - with patch('__builtin__.__import__', side_effect=[fake_dns]): |
1905 | + with patch(builtin_import, side_effect=[fake_dns]): |
1906 | nsq = openstack.ns_query('127.0.0.1') |
1907 | self.assertEquals(nsq, '127.0.0.1') |
1908 | |
1909 | @@ -613,35 +622,35 @@ |
1910 | def test_ns_query_a_record(self, apt_install): |
1911 | fake_dns = FakeDNS('127.0.0.1') |
1912 | fake_dns_name = FakeDNSName('www.somedomain.tld') |
1913 | - with patch('__builtin__.__import__', side_effect=[fake_dns]): |
1914 | + with patch(builtin_import, side_effect=[fake_dns]): |
1915 | nsq = openstack.ns_query(fake_dns_name) |
1916 | self.assertEquals(nsq, '127.0.0.1') |
1917 | |
1918 | @patch.object(openstack, 'apt_install') |
1919 | def test_ns_query_blank_record(self, apt_install): |
1920 | fake_dns = FakeDNS(None) |
1921 | - with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]): |
1922 | + with patch(builtin_import, side_effect=[fake_dns, fake_dns]): |
1923 | nsq = openstack.ns_query(None) |
1924 | self.assertEquals(nsq, None) |
1925 | |
1926 | @patch.object(openstack, 'apt_install') |
1927 | def test_ns_query_lookup_fail(self, apt_install): |
1928 | fake_dns = FakeDNS('') |
1929 | - with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]): |
1930 | + with patch(builtin_import, side_effect=[fake_dns, fake_dns]): |
1931 | nsq = openstack.ns_query('nonexistant') |
1932 | self.assertEquals(nsq, None) |
1933 | |
1934 | @patch.object(openstack, 'apt_install') |
1935 | def test_get_hostname_with_ip(self, apt_install): |
1936 | fake_dns = FakeDNS('www.ubuntu.com') |
1937 | - with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]): |
1938 | + with patch(builtin_import, side_effect=[fake_dns, fake_dns]): |
1939 | hn = openstack.get_hostname('4.2.2.1') |
1940 | self.assertEquals(hn, 'www.ubuntu.com') |
1941 | |
1942 | @patch.object(openstack, 'apt_install') |
1943 | def test_get_hostname_with_ip_not_fqdn(self, apt_install): |
1944 | fake_dns = FakeDNS('packages.ubuntu.com') |
1945 | - with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]): |
1946 | + with patch(builtin_import, side_effect=[fake_dns, fake_dns]): |
1947 | hn = openstack.get_hostname('4.2.2.1', fqdn=False) |
1948 | self.assertEquals(hn, 'packages') |
1949 | |
1950 | @@ -663,7 +672,7 @@ |
1951 | @patch.object(openstack, 'apt_install') |
1952 | def test_get_hostname_trigger_apt_install(self, apt_install): |
1953 | fake_dns = FakeDNS('www.ubuntu.com') |
1954 | - with patch('__builtin__.__import__', side_effect=[ImportError, fake_dns, fake_dns]): |
1955 | + with patch(builtin_import, side_effect=[ImportError, fake_dns, fake_dns]): |
1956 | hn = openstack.get_hostname('4.2.2.1') |
1957 | apt_install.assert_called_with('python-dnspython') |
1958 | self.assertEquals(hn, 'www.ubuntu.com') |
1959 | @@ -673,12 +682,12 @@ |
1960 | def test_get_hostname_lookup_fail(self, apt_install, ns_query): |
1961 | fake_dns = FakeDNS('www.ubuntu.com') |
1962 | ns_query.return_value = [] |
1963 | - with patch('__builtin__.__import__', side_effect=[fake_dns, fake_dns]): |
1964 | + with patch(builtin_import, side_effect=[fake_dns, fake_dns]): |
1965 | hn = openstack.get_hostname('4.2.2.1') |
1966 | self.assertEquals(hn, None) |
1967 | |
1968 | @patch('os.path.isfile') |
1969 | - @patch('__builtin__.open') |
1970 | + @patch(builtin_open) |
1971 | def test_get_matchmaker_map(self, _open, _isfile): |
1972 | _isfile.return_value = True |
1973 | mm_data = """ |
1974 | @@ -696,7 +705,7 @@ |
1975 | ) |
1976 | |
1977 | @patch('os.path.isfile') |
1978 | - @patch('__builtin__.open') |
1979 | + @patch(builtin_open) |
1980 | def test_get_matchmaker_map_nofile(self, _open, _isfile): |
1981 | _isfile.return_value = False |
1982 | self.assertEqual( |
1983 | |
1984 | === modified file 'tests/contrib/openstack/test_os_contexts.py' |
1985 | --- tests/contrib/openstack/test_os_contexts.py 2014-11-25 14:35:22 +0000 |
1986 | +++ tests/contrib/openstack/test_os_contexts.py 2014-11-25 15:09:14 +0000 |
1987 | @@ -11,6 +11,13 @@ |
1988 | ) |
1989 | from tests.helpers import patch_open |
1990 | |
1991 | +import six |
1992 | + |
1993 | +if not six.PY3: |
1994 | + open_builtin = '__builtin__.open' |
1995 | +else: |
1996 | + open_builtin = 'builtins.open' |
1997 | + |
1998 | |
1999 | class FakeRelation(object): |
2000 | |
2001 | @@ -472,7 +479,7 @@ |
2002 | self.assertEquals(result, expected) |
2003 | |
2004 | @patch('os.path.exists') |
2005 | - @patch('__builtin__.open') |
2006 | + @patch(open_builtin) |
2007 | def test_db_ssl(self, _open, osexists): |
2008 | osexists.return_value = False |
2009 | ssl_dir = '/etc/dbssl' |
2010 | @@ -725,7 +732,7 @@ |
2011 | } |
2012 | self.assertEquals(result, expected) |
2013 | |
2014 | - @patch('__builtin__.open') |
2015 | + @patch(open_builtin) |
2016 | def test_amqp_context_with_data_ssl(self, _open): |
2017 | '''Test amqp context with all required data and ssl''' |
2018 | relation = FakeRelation(relation_data=AMQP_RELATION_WITH_SSL) |
2019 | @@ -799,7 +806,7 @@ |
2020 | 'rabbitmq_password': 'foobar', |
2021 | 'rabbitmq_user': 'adam', |
2022 | 'rabbitmq_virtual_host': 'foo', |
2023 | - 'rabbitmq_hosts': 'rabbithost2,rabbithost1', |
2024 | + 'rabbitmq_hosts': 'rabbithost1,rabbithost2', |
2025 | } |
2026 | self.assertEquals(result, expected) |
2027 | |
2028 | @@ -868,7 +875,7 @@ |
2029 | ceph = context.CephContext() |
2030 | result = ceph() |
2031 | expected = { |
2032 | - 'mon_hosts': 'ceph_node2 ceph_node1', |
2033 | + 'mon_hosts': 'ceph_node1 ceph_node2', |
2034 | 'auth': 'foo', |
2035 | 'key': 'bar', |
2036 | 'use_syslog': 'true' |
2037 | @@ -882,8 +889,8 @@ |
2038 | def test_ceph_context_with_missing_data(self, ensure_packages, mkdir): |
2039 | '''Test ceph context with missing relation data''' |
2040 | relation = copy(CEPH_RELATION) |
2041 | - for k, v in relation.iteritems(): |
2042 | - for u in v.iterkeys(): |
2043 | + for k, v in six.iteritems(relation): |
2044 | + for u in six.iterkeys(v): |
2045 | del relation[k][u]['auth'] |
2046 | relation = FakeRelation(relation_data=relation) |
2047 | self.relation_get.side_effect = relation.get |
2048 | @@ -911,7 +918,7 @@ |
2049 | ceph = context.CephContext() |
2050 | result = ceph() |
2051 | expected = { |
2052 | - 'mon_hosts': '192.168.1.11 192.168.1.10', |
2053 | + 'mon_hosts': '192.168.1.10 192.168.1.11', |
2054 | 'auth': 'foo', |
2055 | 'key': 'bar', |
2056 | 'use_syslog': 'true', |
2057 | @@ -1264,12 +1271,9 @@ |
2058 | if len(vips) > 1: |
2059 | ex = { |
2060 | 'namespace': 'cinder', |
2061 | - 'endpoints': [('10.5.1.100', '10.5.1.1', |
2062 | - 8766, 8756), |
2063 | - ('10.5.2.100', '10.5.2.1', |
2064 | - 8766, 8756), |
2065 | - ('10.5.3.100', '10.5.3.1', |
2066 | - 8766, 8756)], |
2067 | + 'endpoints': [('10.5.1.100', '10.5.1.1', 8766, 8756), |
2068 | + ('10.5.2.100', '10.5.2.1', 8766, 8756), |
2069 | + ('10.5.3.100', '10.5.3.1', 8766, 8756)], |
2070 | 'ext_ports': [8766] |
2071 | } |
2072 | else: |
2073 | @@ -1283,12 +1287,10 @@ |
2074 | if multinet: |
2075 | ex = { |
2076 | 'namespace': 'cinder', |
2077 | - 'endpoints': [('10.5.3.100', '10.5.3.100', |
2078 | - 8776, 8766), |
2079 | - ('10.5.2.100', '10.5.2.100', |
2080 | - 8776, 8766), |
2081 | - ('10.5.1.100', '10.5.1.100', |
2082 | - 8776, 8766)], |
2083 | + 'endpoints': sorted([ |
2084 | + ('10.5.3.100', '10.5.3.100', 8776, 8766), |
2085 | + ('10.5.2.100', '10.5.2.100', 8776, 8766), |
2086 | + ('10.5.1.100', '10.5.1.100', 8776, 8766)]), |
2087 | 'ext_ports': [8776] |
2088 | } |
2089 | else: |
2090 | @@ -1391,9 +1393,10 @@ |
2091 | apache = context.ApacheSSLContext() |
2092 | self.assertEquals(apache.canonical_names(), ['cinderhost1']) |
2093 | rel.relation_data = IDENTITY_RELATION_MULTIPLE_CERT |
2094 | - self.assertEquals(apache.canonical_names(), ['cinderhost1-adm-network', |
2095 | - 'cinderhost1-int-network', |
2096 | - 'cinderhost1-pub-network']) |
2097 | + self.assertEquals(apache.canonical_names(), |
2098 | + sorted(['cinderhost1-adm-network', |
2099 | + 'cinderhost1-int-network', |
2100 | + 'cinderhost1-pub-network'])) |
2101 | rel.relation_data = IDENTITY_RELATION_NO_CERT |
2102 | self.assertEquals(apache.canonical_names(), []) |
2103 | |
2104 | @@ -1903,17 +1906,15 @@ |
2105 | |
2106 | def test_workerconfig_context_noconfig(self): |
2107 | self.config.return_value = None |
2108 | - with patch.object(context.WorkerConfigContext, 'num_cpus') as cpus: |
2109 | - cpus.__get__ = Mock(return_value=2) |
2110 | + with patch.object(context.WorkerConfigContext, 'num_cpus', 2): |
2111 | worker = context.WorkerConfigContext() |
2112 | - self.assertEqual({'workers': 2}, worker()) |
2113 | + self.assertEqual({'workers': 0}, worker()) |
2114 | |
2115 | def test_workerconfig_context_withconfig(self): |
2116 | self.config.side_effect = fake_config({ |
2117 | 'worker-multiplier': 4, |
2118 | }) |
2119 | - with patch.object(context.WorkerConfigContext, 'num_cpus') as cpus: |
2120 | - cpus.__get__ = Mock(return_value=2) |
2121 | + with patch.object(context.WorkerConfigContext, 'num_cpus', 2): |
2122 | worker = context.WorkerConfigContext() |
2123 | self.assertEqual({'workers': 8}, worker()) |
2124 | |
2125 | |
2126 | === modified file 'tests/contrib/openstack/test_os_templating.py' |
2127 | --- tests/contrib/openstack/test_os_templating.py 2014-11-25 14:35:22 +0000 |
2128 | +++ tests/contrib/openstack/test_os_templating.py 2014-11-25 15:09:14 +0000 |
2129 | @@ -1,7 +1,13 @@ |
2130 | |
2131 | import os |
2132 | - |
2133 | import unittest |
2134 | + |
2135 | +import six |
2136 | +if not six.PY3: |
2137 | + builtin_open = '__builtin__.open' |
2138 | +else: |
2139 | + builtin_open = 'builtins.open' |
2140 | + |
2141 | from mock import patch, call, MagicMock |
2142 | |
2143 | import charmhelpers.contrib.openstack.templating as templating |
2144 | @@ -160,7 +166,7 @@ |
2145 | def test_render_template_by_basename(self): |
2146 | '''It renders template if it finds it by config file basename''' |
2147 | |
2148 | - @patch('__builtin__.open') |
2149 | + @patch(builtin_open) |
2150 | @patch.object(templating, 'get_loader') |
2151 | def test_write_out_config(self, loader, _open): |
2152 | '''It writes a templated config when provided a complete context''' |
2153 | @@ -183,7 +189,7 @@ |
2154 | ] |
2155 | with patch.object(self.renderer, 'write') as _write: |
2156 | self.renderer.write_all() |
2157 | - self.assertEquals(ex_calls, _write.call_args_list) |
2158 | + self.assertEquals(sorted(ex_calls), sorted(_write.call_args_list)) |
2159 | pass |
2160 | |
2161 | @patch.object(templating, 'get_loader') |
2162 | |
2163 | === modified file 'tests/contrib/storage/test_linux_ceph.py' |
2164 | --- tests/contrib/storage/test_linux_ceph.py 2014-11-19 22:27:26 +0000 |
2165 | +++ tests/contrib/storage/test_linux_ceph.py 2014-11-25 15:09:14 +0000 |
2166 | @@ -14,19 +14,19 @@ |
2167 | import time |
2168 | |
2169 | |
2170 | -LS_POOLS = """ |
2171 | +LS_POOLS = b""" |
2172 | images |
2173 | volumes |
2174 | rbd |
2175 | """ |
2176 | |
2177 | -LS_RBDS = """ |
2178 | +LS_RBDS = b""" |
2179 | rbd1 |
2180 | rbd2 |
2181 | rbd3 |
2182 | """ |
2183 | |
2184 | -IMG_MAP = """ |
2185 | +IMG_MAP = b""" |
2186 | bar |
2187 | baz |
2188 | """ |
2189 | @@ -93,7 +93,7 @@ |
2190 | @patch.object(ceph_utils, 'ceph_version') |
2191 | def test_get_osds(self, version): |
2192 | version.return_value = '0.56.2' |
2193 | - self.check_output.return_value = json.dumps([1, 2, 3]) |
2194 | + self.check_output.return_value = json.dumps([1, 2, 3]).encode('UTF-8') |
2195 | self.assertEquals(ceph_utils.get_osds('test'), [1, 2, 3]) |
2196 | |
2197 | @patch.object(ceph_utils, 'ceph_version') |
2198 | @@ -104,7 +104,7 @@ |
2199 | @patch.object(ceph_utils, 'ceph_version') |
2200 | def test_get_osds_none(self, version): |
2201 | version.return_value = '0.56.2' |
2202 | - self.check_output.return_value = json.dumps(None) |
2203 | + self.check_output.return_value = json.dumps(None).encode('UTF-8') |
2204 | self.assertEquals(ceph_utils.get_osds('test'), None) |
2205 | |
2206 | @patch.object(ceph_utils, 'get_osds') |
2207 | @@ -534,13 +534,13 @@ |
2208 | @patch('os.path.exists') |
2209 | def test_ceph_version_error(self, path, output): |
2210 | path.return_value = True |
2211 | - output.return_value = '' |
2212 | + output.return_value = b'' |
2213 | self.assertEquals(ceph_utils.ceph_version(), None) |
2214 | |
2215 | @patch.object(ceph_utils, 'check_output') |
2216 | @patch('os.path.exists') |
2217 | def test_ceph_version_ok(self, path, output): |
2218 | path.return_value = True |
2219 | - output.return_value = 'ceph version 0.67.4'\ |
2220 | - ' (ad85b8bfafea6232d64cb7ba76a8b6e8252fa0c7)' |
2221 | + output.return_value = \ |
2222 | + b'ceph version 0.67.4 (ad85b8bfafea6232d64cb7ba76a8b6e8252fa0c7)' |
2223 | self.assertEquals(ceph_utils.ceph_version(), '0.67.4') |
2224 | |
2225 | === modified file 'tests/contrib/storage/test_linux_storage_lvm.py' |
2226 | --- tests/contrib/storage/test_linux_storage_lvm.py 2014-07-03 12:36:50 +0000 |
2227 | +++ tests/contrib/storage/test_linux_storage_lvm.py 2014-11-25 15:09:14 +0000 |
2228 | @@ -5,7 +5,7 @@ |
2229 | |
2230 | import charmhelpers.contrib.storage.linux.lvm as lvm |
2231 | |
2232 | -PVDISPLAY = """ |
2233 | +PVDISPLAY = b""" |
2234 | --- Physical volume --- |
2235 | PV Name /dev/loop0 |
2236 | VG Name foo |
2237 | @@ -19,7 +19,7 @@ |
2238 | |
2239 | """ |
2240 | |
2241 | -EMPTY_VG_IN_PVDISPLAY = """ |
2242 | +EMPTY_VG_IN_PVDISPLAY = b""" |
2243 | --- Physical volume --- |
2244 | PV Name /dev/loop0 |
2245 | VG Name |
2246 | |
2247 | === modified file 'tests/contrib/storage/test_linux_storage_utils.py' |
2248 | --- tests/contrib/storage/test_linux_storage_utils.py 2014-07-31 08:55:15 +0000 |
2249 | +++ tests/contrib/storage/test_linux_storage_utils.py 2014-11-25 15:09:14 +0000 |
2250 | @@ -14,7 +14,7 @@ |
2251 | @patch(STORAGE_LINUX_UTILS + '.check_call') |
2252 | def test_zap_disk(self, check_call, call, check_output): |
2253 | '''It calls sgdisk correctly to zap disk''' |
2254 | - check_output.return_value = '200\n' |
2255 | + check_output.return_value = b'200\n' |
2256 | storage_utils.zap_disk('/dev/foo') |
2257 | call.assert_any_call(['sgdisk', '--zap-all', '--mbrtogpt', |
2258 | '--clear', '/dev/foo']) |
2259 | @@ -50,7 +50,7 @@ |
2260 | def test_is_device_mounted(self, check_output): |
2261 | '''It detects mounted devices as mounted.''' |
2262 | check_output.return_value = ( |
2263 | - "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2264 | + b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2265 | result = storage_utils.is_device_mounted('/dev/sda') |
2266 | self.assertTrue(result) |
2267 | |
2268 | @@ -58,7 +58,7 @@ |
2269 | def test_is_device_mounted_partition(self, check_output): |
2270 | '''It detects mounted partitions as mounted.''' |
2271 | check_output.return_value = ( |
2272 | - "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2273 | + b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2274 | result = storage_utils.is_device_mounted('/dev/sda1') |
2275 | self.assertTrue(result) |
2276 | |
2277 | @@ -67,7 +67,7 @@ |
2278 | '''It detects mounted devices as mounted if "mount" shows only a |
2279 | partition as mounted.''' |
2280 | check_output.return_value = ( |
2281 | - "/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2282 | + b"/dev/sda1 on / type ext4 (rw,errors=remount-ro)\n") |
2283 | result = storage_utils.is_device_mounted('/dev/sda') |
2284 | self.assertTrue(result) |
2285 | |
2286 | @@ -75,7 +75,7 @@ |
2287 | def test_is_device_mounted_not_mounted(self, check_output): |
2288 | '''It detects unmounted devices as not mounted.''' |
2289 | check_output.return_value = ( |
2290 | - "/dev/foo on / type ext4 (rw,errors=remount-ro)\n") |
2291 | + b"/dev/foo on / type ext4 (rw,errors=remount-ro)\n") |
2292 | result = storage_utils.is_device_mounted('/dev/sda') |
2293 | self.assertFalse(result) |
2294 | |
2295 | @@ -83,7 +83,7 @@ |
2296 | def test_is_device_mounted_not_mounted_partition(self, check_output): |
2297 | '''It detects unmounted partitions as not mounted.''' |
2298 | check_output.return_value = ( |
2299 | - "/dev/foo on / type ext4 (rw,errors=remount-ro)\n") |
2300 | + b"/dev/foo on / type ext4 (rw,errors=remount-ro)\n") |
2301 | result = storage_utils.is_device_mounted('/dev/sda1') |
2302 | self.assertFalse(result) |
2303 | |
2304 | @@ -91,7 +91,7 @@ |
2305 | def test_is_device_mounted_cciss(self, check_output): |
2306 | '''It detects mounted cciss partitions as mounted.''' |
2307 | check_output.return_value = ( |
2308 | - "/dev/cciss/c0d0 on / type ext4 (rw,errors=remount-ro)\n") |
2309 | + b"/dev/cciss/c0d0 on / type ext4 (rw,errors=remount-ro)\n") |
2310 | result = storage_utils.is_device_mounted('/dev/cciss/c0d0') |
2311 | self.assertTrue(result) |
2312 | |
2313 | @@ -99,6 +99,6 @@ |
2314 | def test_is_device_mounted_cciss_not_mounted(self, check_output): |
2315 | '''It detects unmounted cciss partitions as not mounted.''' |
2316 | check_output.return_value = ( |
2317 | - "/dev/cciss/c0d1 on / type ext4 (rw,errors=remount-ro)\n") |
2318 | + b"/dev/cciss/c0d1 on / type ext4 (rw,errors=remount-ro)\n") |
2319 | result = storage_utils.is_device_mounted('/dev/cciss/c0d0') |
2320 | self.assertFalse(result) |
2321 | |
2322 | === modified file 'tests/contrib/templating/test_contexts.py' |
2323 | --- tests/contrib/templating/test_contexts.py 2014-08-13 20:52:09 +0000 |
2324 | +++ tests/contrib/templating/test_contexts.py 2014-11-25 15:09:14 +0000 |
2325 | @@ -9,6 +9,8 @@ |
2326 | import unittest |
2327 | import yaml |
2328 | |
2329 | +import six |
2330 | + |
2331 | import charmhelpers.contrib.templating.contexts |
2332 | |
2333 | |
2334 | @@ -134,12 +136,12 @@ |
2335 | } |
2336 | self.mock_relations.return_value = { |
2337 | 'wsgi-file': { |
2338 | - u'wsgi-file:0': { |
2339 | - u'gunicorn/1': { |
2340 | - u'private-address': u'10.0.3.99', |
2341 | + six.u('wsgi-file:0'): { |
2342 | + six.u('gunicorn/1'): { |
2343 | + six.u('private-address'): six.u('10.0.3.99'), |
2344 | }, |
2345 | 'click-index/3': { |
2346 | - u'wsgi_group': u'ubunet', |
2347 | + six.u('wsgi_group'): six.u('ubunet'), |
2348 | }, |
2349 | }, |
2350 | }, |
2351 | @@ -162,8 +164,9 @@ |
2352 | expected["wsgi_file:relation_key2"] = "relation_value2" |
2353 | expected["relations_full"]['wsgi-file'] = { |
2354 | 'wsgi-file:0': { |
2355 | - 'gunicorn/1': {u'private-address': u'10.0.3.99'}, |
2356 | - 'click-index/3': {u'wsgi_group': u'ubunet'}, |
2357 | + 'gunicorn/1': { |
2358 | + six.u('private-address'): six.u('10.0.3.99')}, |
2359 | + 'click-index/3': {six.u('wsgi_group'): six.u('ubunet')}, |
2360 | }, |
2361 | } |
2362 | expected["relations"]["wsgi-file"] = [ |
2363 | |
2364 | === modified file 'tests/contrib/unison/test_unison.py' |
2365 | --- tests/contrib/unison/test_unison.py 2014-10-22 06:17:55 +0000 |
2366 | +++ tests/contrib/unison/test_unison.py 2014-11-25 15:09:14 +0000 |
2367 | @@ -110,13 +110,13 @@ |
2368 | |
2369 | isfile.return_value = False |
2370 | with patch_open() as (_open, _file): |
2371 | - self.check_output.return_value = 'fookey' |
2372 | + self.check_output.return_value = b'fookey' |
2373 | unison.create_public_key( |
2374 | user='foo', priv_key_path='/home/foo/.ssh/id_rsa', |
2375 | pub_key_path='/home/foo/.ssh/id_rsa.pub') |
2376 | self.assertIn(call(create_cmd), self.check_output.call_args_list) |
2377 | _open.assert_called_with('/home/foo/.ssh/id_rsa.pub', 'wb') |
2378 | - _file.write.assert_called_with('fookey') |
2379 | + _file.write.assert_called_with(b'fookey') |
2380 | |
2381 | @patch('os.mkdir') |
2382 | @patch('os.path.isdir') |
2383 | |
2384 | === modified file 'tests/core/test_fstab.py' |
2385 | --- tests/core/test_fstab.py 2014-11-25 14:35:22 +0000 |
2386 | +++ tests/core/test_fstab.py 2014-11-25 15:09:14 +0000 |
2387 | @@ -24,7 +24,7 @@ |
2388 | class FstabTest(unittest.TestCase): |
2389 | |
2390 | def setUp(self): |
2391 | - self.tempfile = tempfile.NamedTemporaryFile(delete=False) |
2392 | + self.tempfile = tempfile.NamedTemporaryFile('w+', delete=False) |
2393 | self.tempfile.write(DEFAULT_FSTAB_FILE) |
2394 | self.tempfile.close() |
2395 | self.fstab = Fstab(path=self.tempfile.name) |
2396 | |
2397 | === modified file 'tests/core/test_hookenv.py' |
2398 | --- tests/core/test_hookenv.py 2014-11-25 14:35:22 +0000 |
2399 | +++ tests/core/test_hookenv.py 2014-11-25 15:09:14 +0000 |
2400 | @@ -3,14 +3,18 @@ |
2401 | from subprocess import CalledProcessError |
2402 | import shutil |
2403 | import tempfile |
2404 | - |
2405 | -import cPickle as pickle |
2406 | from mock import patch, call, mock_open |
2407 | -from StringIO import StringIO |
2408 | from mock import MagicMock |
2409 | from testtools import TestCase |
2410 | import yaml |
2411 | |
2412 | +import six |
2413 | +from six.moves import StringIO |
2414 | +if six.PY3: |
2415 | + import pickle |
2416 | +else: |
2417 | + import cPickle as pickle |
2418 | + |
2419 | from charmhelpers.core import hookenv |
2420 | |
2421 | CHARM_METADATA = """name: testmock |
2422 | @@ -125,7 +129,7 @@ |
2423 | def test_keys(self): |
2424 | c = hookenv.Config(dict(foo='bar')) |
2425 | c["baz"] = "bar" |
2426 | - self.assertEqual([u"foo", "baz"], c.keys()) |
2427 | + self.assertEqual(sorted([six.u("foo"), "baz"]), sorted(c.keys())) |
2428 | |
2429 | |
2430 | class SerializableTest(TestCase): |
2431 | @@ -165,11 +169,9 @@ |
2432 | } |
2433 | wrapped = hookenv.Serializable(foo) |
2434 | for meth in ('keys', 'values', 'items'): |
2435 | - self.assertEqual(getattr(wrapped, meth)(), getattr(foo, meth)()) |
2436 | + self.assertEqual(sorted(list(getattr(wrapped, meth)())), |
2437 | + sorted(list(getattr(foo, meth)()))) |
2438 | |
2439 | - for meth in ('iterkeys', 'itervalues', 'iteritems'): |
2440 | - self.assertEqual(list(getattr(wrapped, meth)()), |
2441 | - list(getattr(foo, meth)())) |
2442 | self.assertEqual(wrapped.get('bar'), foo.get('bar')) |
2443 | self.assertEqual(wrapped.get('baz', 42), foo.get('baz', 42)) |
2444 | self.assertIn('bar', wrapped) |
2445 | @@ -246,7 +248,7 @@ |
2446 | @patch('subprocess.check_output') |
2447 | def test_gets_charm_config_with_scope(self, check_output): |
2448 | config_data = 'bar' |
2449 | - check_output.return_value = json.dumps(config_data) |
2450 | + check_output.return_value = json.dumps(config_data).encode('UTF-8') |
2451 | |
2452 | result = hookenv.config(scope='baz') |
2453 | |
2454 | @@ -257,11 +259,11 @@ |
2455 | self.assertEqual(result[1], 'a') |
2456 | |
2457 | # ... because the result is actually a string |
2458 | - self.assert_(isinstance(result, basestring)) |
2459 | + self.assert_(isinstance(result, six.string_types)) |
2460 | |
2461 | @patch('subprocess.check_output') |
2462 | def test_gets_missing_charm_config_with_scope(self, check_output): |
2463 | - check_output.return_value = '' |
2464 | + check_output.return_value = b'' |
2465 | |
2466 | result = hookenv.config(scope='baz') |
2467 | |
2468 | @@ -271,7 +273,7 @@ |
2469 | @patch('charmhelpers.core.hookenv.charm_dir') |
2470 | @patch('subprocess.check_output') |
2471 | def test_gets_config_without_scope(self, check_output, charm_dir): |
2472 | - check_output.return_value = json.dumps(dict(foo='bar')) |
2473 | + check_output.return_value = json.dumps(dict(foo='bar')).encode('UTF-8') |
2474 | charm_dir.side_effect = tempfile.mkdtemp |
2475 | |
2476 | result = hookenv.config() |
2477 | @@ -327,7 +329,7 @@ |
2478 | @patch('charmhelpers.core.hookenv.relation_type') |
2479 | def test_gets_relation_ids(self, relation_type, check_output): |
2480 | ids = [1, 2, 3] |
2481 | - check_output.return_value = json.dumps(ids) |
2482 | + check_output.return_value = json.dumps(ids).encode('UTF-8') |
2483 | reltype = 'foo' |
2484 | relation_type.return_value = reltype |
2485 | |
2486 | @@ -341,7 +343,7 @@ |
2487 | @patch('charmhelpers.core.hookenv.relation_type') |
2488 | def test_gets_relation_ids_empty_array(self, relation_type, check_output): |
2489 | ids = [] |
2490 | - check_output.return_value = json.dumps(None) |
2491 | + check_output.return_value = json.dumps(None).encode('UTF-8') |
2492 | reltype = 'foo' |
2493 | relation_type.return_value = reltype |
2494 | |
2495 | @@ -355,7 +357,7 @@ |
2496 | @patch('charmhelpers.core.hookenv.relation_type') |
2497 | def test_relation_ids_no_relation_type(self, relation_type, check_output): |
2498 | ids = [1, 2, 3] |
2499 | - check_output.return_value = json.dumps(ids) |
2500 | + check_output.return_value = json.dumps(ids).encode('UTF-8') |
2501 | relation_type.return_value = None |
2502 | |
2503 | result = hookenv.relation_ids() |
2504 | @@ -366,7 +368,7 @@ |
2505 | @patch('charmhelpers.core.hookenv.relation_type') |
2506 | def test_gets_relation_ids_for_type(self, relation_type, check_output): |
2507 | ids = [1, 2, 3] |
2508 | - check_output.return_value = json.dumps(ids) |
2509 | + check_output.return_value = json.dumps(ids).encode('UTF-8') |
2510 | reltype = 'foo' |
2511 | |
2512 | result = hookenv.relation_ids(reltype) |
2513 | @@ -382,7 +384,7 @@ |
2514 | relid = 123 |
2515 | units = ['foo', 'bar'] |
2516 | relation_id.return_value = relid |
2517 | - check_output.return_value = json.dumps(units) |
2518 | + check_output.return_value = json.dumps(units).encode('UTF-8') |
2519 | |
2520 | result = hookenv.related_units() |
2521 | |
2522 | @@ -393,10 +395,10 @@ |
2523 | @patch('subprocess.check_output') |
2524 | @patch('charmhelpers.core.hookenv.relation_id') |
2525 | def test_gets_related_units_empty_array(self, relation_id, check_output): |
2526 | - relid = 123 |
2527 | + relid = str(123) |
2528 | units = [] |
2529 | relation_id.return_value = relid |
2530 | - check_output.return_value = json.dumps(None) |
2531 | + check_output.return_value = json.dumps(None).encode('UTF-8') |
2532 | |
2533 | result = hookenv.related_units() |
2534 | |
2535 | @@ -409,7 +411,7 @@ |
2536 | def test_related_units_no_relation(self, relation_id, check_output): |
2537 | units = ['foo', 'bar'] |
2538 | relation_id.return_value = None |
2539 | - check_output.return_value = json.dumps(units) |
2540 | + check_output.return_value = json.dumps(units).encode('UTF-8') |
2541 | |
2542 | result = hookenv.related_units() |
2543 | |
2544 | @@ -421,7 +423,7 @@ |
2545 | def test_gets_related_units_for_id(self, relation_id, check_output): |
2546 | relid = 123 |
2547 | units = ['foo', 'bar'] |
2548 | - check_output.return_value = json.dumps(units) |
2549 | + check_output.return_value = json.dumps(units).encode('UTF-8') |
2550 | |
2551 | result = hookenv.related_units(relid) |
2552 | |
2553 | @@ -765,7 +767,7 @@ |
2554 | @patch('subprocess.check_output') |
2555 | def test_gets_relation(self, check_output): |
2556 | data = {"foo": "BAR"} |
2557 | - check_output.return_value = json.dumps(data) |
2558 | + check_output.return_value = json.dumps(data).encode('UTF-8') |
2559 | result = hookenv.relation_get() |
2560 | |
2561 | self.assertEqual(result['foo'], 'BAR') |
2562 | @@ -773,7 +775,7 @@ |
2563 | |
2564 | @patch('charmhelpers.core.hookenv.subprocess') |
2565 | def test_relation_get_none(self, mock_subprocess): |
2566 | - mock_subprocess.check_output.return_value = 'null' |
2567 | + mock_subprocess.check_output.return_value = b'null' |
2568 | |
2569 | result = hookenv.relation_get() |
2570 | |
2571 | @@ -801,7 +803,7 @@ |
2572 | |
2573 | @patch('subprocess.check_output') |
2574 | def test_gets_relation_with_scope(self, check_output): |
2575 | - check_output.return_value = json.dumps('bar') |
2576 | + check_output.return_value = json.dumps('bar').encode('UTF-8') |
2577 | |
2578 | result = hookenv.relation_get(attribute='baz-scope') |
2579 | |
2580 | @@ -811,7 +813,7 @@ |
2581 | |
2582 | @patch('subprocess.check_output') |
2583 | def test_gets_missing_relation_with_scope(self, check_output): |
2584 | - check_output.return_value = "" |
2585 | + check_output.return_value = b"" |
2586 | |
2587 | result = hookenv.relation_get(attribute='baz-scope') |
2588 | |
2589 | @@ -821,7 +823,7 @@ |
2590 | |
2591 | @patch('subprocess.check_output') |
2592 | def test_gets_relation_with_unit_name(self, check_output): |
2593 | - check_output.return_value = json.dumps('BAR') |
2594 | + check_output.return_value = json.dumps('BAR').encode('UTF-8') |
2595 | |
2596 | result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit') |
2597 | |
2598 | @@ -834,7 +836,7 @@ |
2599 | @patch('subprocess.check_output') |
2600 | def test_relation_set_flushes_local_unit_cache(self, check_output, |
2601 | check_call, local_unit): |
2602 | - check_output.return_value = json.dumps('BAR') |
2603 | + check_output.return_value = json.dumps('BAR').encode('UTF-8') |
2604 | local_unit.return_value = 'baz_unit' |
2605 | hookenv.relation_get(attribute='baz_scope', unit='baz_unit') |
2606 | hookenv.relation_get(attribute='bar_scope') |
2607 | @@ -845,7 +847,7 @@ |
2608 | |
2609 | @patch('subprocess.check_output') |
2610 | def test_gets_relation_with_relation_id(self, check_output): |
2611 | - check_output.return_value = json.dumps('BAR') |
2612 | + check_output.return_value = json.dumps('BAR').encode('UTF-8') |
2613 | |
2614 | result = hookenv.relation_get(attribute='baz-scope', unit='baz-unit', |
2615 | rid=123) |
2616 | @@ -910,13 +912,13 @@ |
2617 | |
2618 | @patch('subprocess.check_output') |
2619 | def test_gets_unit_attribute(self, check_output_): |
2620 | - check_output_.return_value = json.dumps('bar') |
2621 | + check_output_.return_value = json.dumps('bar').encode('UTF-8') |
2622 | self.assertEqual(hookenv.unit_get('foo'), 'bar') |
2623 | check_output_.assert_called_with(['unit-get', '--format=json', 'foo']) |
2624 | |
2625 | @patch('subprocess.check_output') |
2626 | def test_gets_missing_unit_attribute(self, check_output_): |
2627 | - check_output_.return_value = "" |
2628 | + check_output_.return_value = b"" |
2629 | self.assertEqual(hookenv.unit_get('foo'), None) |
2630 | check_output_.assert_called_with(['unit-get', '--format=json', 'foo']) |
2631 | |
2632 | @@ -925,8 +927,8 @@ |
2633 | values = { |
2634 | 'hello': 'world', |
2635 | 'foo': 'bar', |
2636 | - 'baz': None |
2637 | - } |
2638 | + 'baz': None, |
2639 | + } |
2640 | |
2641 | @hookenv.cached |
2642 | def cache_function(attribute): |
2643 | |
2644 | === modified file 'tests/core/test_host.py' |
2645 | --- tests/core/test_host.py 2014-11-25 14:35:22 +0000 |
2646 | +++ tests/core/test_host.py 2014-11-25 15:09:14 +0000 |
2647 | @@ -19,30 +19,30 @@ |
2648 | """rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0 |
2649 | """).strip().split('\n') |
2650 | |
2651 | -LSB_RELEASE = u'''DISTRIB_ID=Ubuntu |
2652 | +LSB_RELEASE = '''DISTRIB_ID=Ubuntu |
2653 | DISTRIB_RELEASE=13.10 |
2654 | DISTRIB_CODENAME=saucy |
2655 | DISTRIB_DESCRIPTION="Ubuntu Saucy Salamander (development branch)" |
2656 | ''' |
2657 | |
2658 | -IP_LINE_ETH0 = (""" |
2659 | +IP_LINE_ETH0 = b""" |
2660 | 2: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP qlen 1000 |
2661 | link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff |
2662 | -""") |
2663 | +""" |
2664 | |
2665 | -IP_LINE_ETH1 = (""" |
2666 | +IP_LINE_ETH1 = b""" |
2667 | 3: eth1: <BROADCAST,MULTICAST> mtu 1546 qdisc noop state DOWN qlen 1000 |
2668 | link/ether e4:11:5b:ab:a7:3c brd ff:ff:ff:ff:ff:ff |
2669 | -""") |
2670 | +""" |
2671 | |
2672 | -IP_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""") |
2673 | +IP_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""" |
2674 | |
2675 | IP_LINES = IP_LINE_ETH0 + IP_LINE_ETH1 |
2676 | |
2677 | -IP_LINE_BONDS = (""" |
2678 | +IP_LINE_BONDS = b""" |
2679 | 6: bond0.10@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default |
2680 | link/ether 08:00:27:16:b9:5f brd ff:ff:ff:ff:ff:ff |
2681 | -""") |
2682 | +""" |
2683 | |
2684 | |
2685 | class HelpersTest(TestCase): |
2686 | @@ -166,12 +166,12 @@ |
2687 | |
2688 | @patch('subprocess.check_output') |
2689 | def test_service_running_on_stopped_service(self, check_output): |
2690 | - check_output.return_value = 'foo stop/waiting' |
2691 | + check_output.return_value = b'foo stop/waiting' |
2692 | self.assertFalse(host.service_running('foo')) |
2693 | |
2694 | @patch('subprocess.check_output') |
2695 | def test_service_running_on_running_service(self, check_output): |
2696 | - check_output.return_value = 'foo start/running, process 23871' |
2697 | + check_output.return_value = b'foo start/running, process 23871' |
2698 | self.assertTrue(host.service_running('foo')) |
2699 | |
2700 | @patch('subprocess.check_output') |
2701 | @@ -284,7 +284,7 @@ |
2702 | def test_rsyncs_a_path(self, log, check_output): |
2703 | from_path = '/from/this/path/foo' |
2704 | to_path = '/to/this/path/bar' |
2705 | - check_output.return_value = ' some output ' |
2706 | + check_output.return_value = b' some output ' # Spaces will be stripped |
2707 | |
2708 | result = host.rsync(from_path, to_path) |
2709 | |
2710 | @@ -319,7 +319,7 @@ |
2711 | path = '/some/other/path/from/link' |
2712 | realpath = '/some/path' |
2713 | path_exists = False |
2714 | - perms = 0644 |
2715 | + perms = 0o644 |
2716 | |
2717 | getpwnam.return_value.pw_uid = uid |
2718 | getgrnam.return_value.gr_gid = gid |
2719 | @@ -343,7 +343,7 @@ |
2720 | path = '/some/other/path/from/link' |
2721 | realpath = '/some/path' |
2722 | path_exists = False |
2723 | - perms = 0555 |
2724 | + perms = 0o555 |
2725 | |
2726 | os_.path.abspath.return_value = realpath |
2727 | os_.path.exists.return_value = path_exists |
2728 | @@ -370,7 +370,7 @@ |
2729 | path_exists = True |
2730 | force = True |
2731 | is_dir = False |
2732 | - perms = 0644 |
2733 | + perms = 0o644 |
2734 | |
2735 | getpwnam.return_value.pw_uid = uid |
2736 | getgrnam.return_value.gr_gid = gid |
2737 | @@ -402,7 +402,7 @@ |
2738 | group = 'some-group-{bar}' |
2739 | path = '/some/path/{baz}' |
2740 | contents = 'what is {juju}' |
2741 | - perms = 0644 |
2742 | + perms = 0o644 |
2743 | fileno = 'some-fileno' |
2744 | |
2745 | getpwnam.return_value.pw_uid = uid |
2746 | @@ -428,7 +428,7 @@ |
2747 | gid = 0 |
2748 | path = '/some/path/{baz}' |
2749 | fmtstr = 'what is {juju}' |
2750 | - perms = 0444 |
2751 | + perms = 0o444 |
2752 | fileno = 'some-fileno' |
2753 | |
2754 | with patch_open() as (mock_open, mock_file): |
2755 | @@ -636,7 +636,7 @@ |
2756 | |
2757 | @host.restart_on_change(restart_map) |
2758 | def make_some_changes(mock_file): |
2759 | - mock_file.read.return_value = "newstuff" |
2760 | + mock_file.read.return_value = b"newstuff" |
2761 | |
2762 | with patch_open() as (mock_open, mock_file): |
2763 | make_some_changes(mock_file) |
2764 | @@ -664,7 +664,7 @@ |
2765 | pass |
2766 | |
2767 | with patch_open() as (mock_open, mock_file): |
2768 | - mock_file.read.side_effect = ['exists', 'missing', 'exists2'] |
2769 | + mock_file.read.side_effect = [b'exists', b'missing', b'exists2'] |
2770 | make_some_changes() |
2771 | |
2772 | # Restart should only happen once per service |
2773 | @@ -691,7 +691,7 @@ |
2774 | pass |
2775 | |
2776 | with patch_open() as (mock_open, mock_file): |
2777 | - mock_file.read.side_effect = ['exists', 'missing', 'exists2'] |
2778 | + mock_file.read.side_effect = [b'exists', b'missing', b'exists2'] |
2779 | make_some_changes() |
2780 | |
2781 | # Restarts should happen in the order they are described in the |
2782 | @@ -713,7 +713,6 @@ |
2783 | with mocked_open('/etc/lsb-release', LSB_RELEASE): |
2784 | lsb_release = host.lsb_release() |
2785 | for key in result: |
2786 | - print lsb_release |
2787 | self.assertEqual(result[key], lsb_release[key]) |
2788 | |
2789 | def test_pwgen(self): |
2790 | |
2791 | === modified file 'tests/core/test_services.py' |
2792 | --- tests/core/test_services.py 2014-11-25 14:35:22 +0000 |
2793 | +++ tests/core/test_services.py 2014-11-25 15:09:14 +0000 |
2794 | @@ -573,7 +573,7 @@ |
2795 | with mock.patch.object(services.helpers, 'open', mopen, create=True): |
2796 | services.helpers.StoredContext('foo.yaml', {'key': 'val'}) |
2797 | mopen.assert_called_once_with('charm_dir/foo.yaml', 'w') |
2798 | - fchmod.assert_called_once_with(mopen.return_value.fileno(), 0600) |
2799 | + fchmod.assert_called_once_with(mopen.return_value.fileno(), 0o600) |
2800 | yaml.dump.assert_called_once_with({'key': 'val'}, mopen.return_value) |
2801 | |
2802 | @mock.patch.object(hookenv, 'charm_dir', lambda: 'charm_dir') |
2803 | @@ -637,7 +637,7 @@ |
2804 | callback(manager, 'test', 'event') |
2805 | mtemplating.render.assert_called_once_with( |
2806 | 'foo.yml', 'bar.yml', {'foo': 'bar'}, |
2807 | - 'root', 'root', 0444) |
2808 | + 'root', 'root', 0o444) |
2809 | |
2810 | @mock.patch.object(services.helpers, 'templating') |
2811 | def test_template_explicit(self, mtemplating): |
2812 | @@ -645,14 +645,14 @@ |
2813 | 'required_data': [{'foo': 'bar'}]}}) |
2814 | callback = services.template( |
2815 | source='foo.yml', target='bar.yml', |
2816 | - owner='user', group='group', perms=0555 |
2817 | + owner='user', group='group', perms=0o555 |
2818 | ) |
2819 | assert isinstance(callback, services.ManagerCallback) |
2820 | assert not mtemplating.render.called |
2821 | callback(manager, 'test', 'event') |
2822 | mtemplating.render.assert_called_once_with( |
2823 | 'foo.yml', 'bar.yml', {'foo': 'bar'}, |
2824 | - 'user', 'group', 0555) |
2825 | + 'user', 'group', 0o555) |
2826 | |
2827 | |
2828 | class TestPortsCallback(unittest.TestCase): |
2829 | |
2830 | === modified file 'tests/core/test_sysctl.py' |
2831 | --- tests/core/test_sysctl.py 2014-11-25 14:35:22 +0000 |
2832 | +++ tests/core/test_sysctl.py 2014-11-25 15:09:14 +0000 |
2833 | @@ -4,11 +4,18 @@ |
2834 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
2835 | |
2836 | from charmhelpers.core.sysctl import create |
2837 | +import io |
2838 | from mock import patch, MagicMock |
2839 | - |
2840 | import unittest |
2841 | import tempfile |
2842 | |
2843 | +import six |
2844 | +if not six.PY3: |
2845 | + builtin_open = '__builtin__.open' |
2846 | +else: |
2847 | + builtin_open = 'builtins.open' |
2848 | + |
2849 | + |
2850 | TO_PATCH = [ |
2851 | 'log', |
2852 | 'check_call', |
2853 | @@ -27,10 +34,10 @@ |
2854 | self.addCleanup(_m.stop) |
2855 | return mock |
2856 | |
2857 | - @patch('__builtin__.open') |
2858 | + @patch(builtin_open) |
2859 | def test_create(self, mock_open): |
2860 | """Test create sysctl method""" |
2861 | - _file = MagicMock(spec=file) |
2862 | + _file = MagicMock(spec=io.FileIO) |
2863 | mock_open.return_value = _file |
2864 | |
2865 | create('{"kernel.max_pid": 1337}', "/etc/sysctl.d/test-sysctl.conf") |
2866 | |
2867 | === modified file 'tests/fetch/test_archiveurl.py' |
2868 | --- tests/fetch/test_archiveurl.py 2014-11-25 14:35:22 +0000 |
2869 | +++ tests/fetch/test_archiveurl.py 2014-11-25 15:09:14 +0000 |
2870 | @@ -1,6 +1,14 @@ |
2871 | import os |
2872 | -from testtools import TestCase |
2873 | -from urlparse import urlparse |
2874 | + |
2875 | +import six |
2876 | +if six.PY3: |
2877 | + from urllib.parse import urlparse |
2878 | + from urllib.error import URLError |
2879 | +else: |
2880 | + from urllib2 import URLError |
2881 | + from urlparse import urlparse |
2882 | + |
2883 | +from unittest import TestCase |
2884 | from mock import ( |
2885 | MagicMock, |
2886 | patch, |
2887 | @@ -11,7 +19,6 @@ |
2888 | archiveurl, |
2889 | UnhandledSource, |
2890 | ) |
2891 | -import urllib2 |
2892 | |
2893 | |
2894 | class ArchiveUrlFetchHandlerTest(TestCase): |
2895 | @@ -50,7 +57,7 @@ |
2896 | result = self.fh.can_handle(url) |
2897 | self.assertNotEqual(result, True, url) |
2898 | |
2899 | - @patch('urllib2.urlopen') |
2900 | + @patch('charmhelpers.fetch.archiveurl.urlopen') |
2901 | def test_downloads(self, _urlopen): |
2902 | for url in self.valid_urls: |
2903 | response = MagicMock() |
2904 | @@ -87,7 +94,7 @@ |
2905 | |
2906 | url = "http://www.example.com/archive.tar.gz" |
2907 | |
2908 | - self.fh.download.side_effect = urllib2.URLError('fail') |
2909 | + self.fh.download.side_effect = URLError('fail') |
2910 | with patch.dict('os.environ', {'CHARM_DIR': 'foo'}): |
2911 | self.assertRaises(UnhandledSource, self.fh.install, url) |
2912 | |
2913 | |
2914 | === modified file 'tests/fetch/test_bzrurl.py' |
2915 | --- tests/fetch/test_bzrurl.py 2014-11-25 14:35:22 +0000 |
2916 | +++ tests/fetch/test_bzrurl.py 2014-11-25 15:09:14 +0000 |
2917 | @@ -1,20 +1,34 @@ |
2918 | import os |
2919 | from testtools import TestCase |
2920 | -from urlparse import urlparse |
2921 | from mock import ( |
2922 | MagicMock, |
2923 | patch, |
2924 | ) |
2925 | -from charmhelpers.fetch import ( |
2926 | - bzrurl, |
2927 | - UnhandledSource, |
2928 | -) |
2929 | - |
2930 | - |
2931 | +import unittest |
2932 | + |
2933 | +import six |
2934 | +if six.PY3: |
2935 | + from urllib.parse import urlparse |
2936 | +else: |
2937 | + from urlparse import urlparse |
2938 | + |
2939 | +try: |
2940 | + from charmhelpers.fetch import ( |
2941 | + bzrurl, |
2942 | + UnhandledSource, |
2943 | + ) |
2944 | +except ImportError: |
2945 | + bzrurl = None |
2946 | + UnhandledSource = None |
2947 | + |
2948 | + |
2949 | +@unittest.skipIf(six.PY3, 'bzr does not support Python 3') |
2950 | class BzrUrlFetchHandlerTest(TestCase): |
2951 | |
2952 | def setUp(self): |
2953 | super(BzrUrlFetchHandlerTest, self).setUp() |
2954 | + if six.PY3: |
2955 | + return |
2956 | self.valid_urls = ( |
2957 | "bzr+ssh://example.com/branch-name", |
2958 | "bzr+ssh://example.com/branch-name/", |
2959 | @@ -41,6 +55,7 @@ |
2960 | ) |
2961 | self.fh = bzrurl.BzrUrlFetchHandler() |
2962 | |
2963 | + @unittest.skipIf(six.PY3, 'bzr does not support Python 3') |
2964 | def test_handles_bzr_urls(self): |
2965 | for url in self.valid_urls: |
2966 | result = self.fh.can_handle(url) |
2967 | @@ -49,6 +64,7 @@ |
2968 | result = self.fh.can_handle(url) |
2969 | self.assertNotEqual(result, True, url) |
2970 | |
2971 | + @unittest.skipIf(six.PY3, 'bzr does not support Python 3') |
2972 | @patch('bzrlib.branch.Branch.open') |
2973 | def test_branch(self, _open): |
2974 | dest_path = "/destination/path" |
2975 | @@ -63,6 +79,7 @@ |
2976 | with patch.dict('os.environ', {'CHARM_DIR': 'foo'}): |
2977 | self.assertRaises(UnhandledSource, self.fh.branch, url, dest_path) |
2978 | |
2979 | + @unittest.skipIf(six.PY3, 'bzr does not support Python 3') |
2980 | @patch('charmhelpers.fetch.bzrurl.mkdir') |
2981 | def test_installs(self, _mkdir): |
2982 | self.fh.branch = MagicMock() |
2983 | @@ -73,4 +90,4 @@ |
2984 | with patch.dict('os.environ', {'CHARM_DIR': 'foo'}): |
2985 | where = self.fh.install(url) |
2986 | self.assertEqual(where, dest) |
2987 | - _mkdir.assert_called_with(where, perms=0755) |
2988 | + _mkdir.assert_called_with(where, perms=0o755) |
2989 | |
2990 | === modified file 'tests/fetch/test_fetch.py' |
2991 | --- tests/fetch/test_fetch.py 2014-11-25 14:35:22 +0000 |
2992 | +++ tests/fetch/test_fetch.py 2014-11-25 15:09:14 +0000 |
2993 | @@ -1,4 +1,3 @@ |
2994 | -from cStringIO import StringIO |
2995 | import subprocess |
2996 | |
2997 | from tests.helpers import patch_open |
2998 | @@ -8,11 +7,18 @@ |
2999 | MagicMock, |
3000 | call, |
3001 | ) |
3002 | -from urlparse import urlparse |
3003 | from charmhelpers import fetch |
3004 | import os |
3005 | import yaml |
3006 | |
3007 | +import six |
3008 | +from six.moves import StringIO |
3009 | +if six.PY3: |
3010 | + from urllib.parse import urlparse |
3011 | +else: |
3012 | + from urlparse import urlparse |
3013 | + |
3014 | + |
3015 | FAKE_APT_CACHE = { |
3016 | # an installed package |
3017 | 'vim': { |
3018 | @@ -404,7 +410,11 @@ |
3019 | @patch('charmhelpers.fetch.log') |
3020 | def test_plugins_are_valid(self, log_): |
3021 | plugins = fetch.plugins() |
3022 | - self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins)) |
3023 | + if not six.PY3: |
3024 | + self.assertEqual(len(fetch.FETCH_HANDLERS), len(plugins)) |
3025 | + else: |
3026 | + # No bzr or git libraries for Python3. |
3027 | + self.assertEqual(len(fetch.FETCH_HANDLERS) - 2, len(plugins)) |
3028 | |
3029 | |
3030 | class BaseFetchHandlerTest(TestCase): |
3031 | |
3032 | === modified file 'tests/fetch/test_giturl.py' |
3033 | --- tests/fetch/test_giturl.py 2014-11-25 14:35:22 +0000 |
3034 | +++ tests/fetch/test_giturl.py 2014-11-25 15:09:14 +0000 |
3035 | @@ -1,18 +1,31 @@ |
3036 | import os |
3037 | from testtools import TestCase |
3038 | -from urlparse import urlparse |
3039 | from mock import ( |
3040 | MagicMock, |
3041 | patch, |
3042 | ) |
3043 | -from charmhelpers.fetch import ( |
3044 | - giturl, |
3045 | - UnhandledSource, |
3046 | -) |
3047 | - |
3048 | - |
3049 | +import unittest |
3050 | + |
3051 | +import six |
3052 | +if six.PY3: |
3053 | + from urllib.parse import urlparse |
3054 | +else: |
3055 | + from urlparse import urlparse |
3056 | + |
3057 | +try: |
3058 | + from charmhelpers.fetch import ( |
3059 | + giturl, |
3060 | + UnhandledSource, |
3061 | + ) |
3062 | +except ImportError: |
3063 | + giturl = None |
3064 | + UnhandledSource = None |
3065 | + |
3066 | + |
3067 | +@unittest.skipIf(six.PY3, 'git does not support Python 3') |
3068 | class GitUrlFetchHandlerTest(TestCase): |
3069 | |
3070 | + @unittest.skipIf(six.PY3, 'git does not support Python 3') |
3071 | def setUp(self): |
3072 | super(GitUrlFetchHandlerTest, self).setUp() |
3073 | self.valid_urls = ( |
3074 | @@ -27,6 +40,7 @@ |
3075 | ) |
3076 | self.fh = giturl.GitUrlFetchHandler() |
3077 | |
3078 | + @unittest.skipIf(six.PY3, 'git does not support Python 3') |
3079 | def test_handles_git_urls(self): |
3080 | for url in self.valid_urls: |
3081 | result = self.fh.can_handle(url) |
3082 | @@ -35,6 +49,7 @@ |
3083 | result = self.fh.can_handle(url) |
3084 | self.assertNotEqual(result, True, url) |
3085 | |
3086 | + @unittest.skipIf(six.PY3, 'git does not support Python 3') |
3087 | @patch('git.Repo.clone_from') |
3088 | def test_branch(self, _clone_from): |
3089 | dest_path = "/destination/path" |
3090 | @@ -52,6 +67,7 @@ |
3091 | dest_path, |
3092 | branch) |
3093 | |
3094 | + @unittest.skipIf(six.PY3, 'git does not support Python 3') |
3095 | @patch('charmhelpers.fetch.giturl.mkdir') |
3096 | def test_installs(self, _mkdir): |
3097 | self.fh.clone = MagicMock() |
3098 | @@ -63,4 +79,4 @@ |
3099 | with patch.dict('os.environ', {'CHARM_DIR': 'foo'}): |
3100 | where = self.fh.install(url) |
3101 | self.assertEqual(where, dest) |
3102 | - _mkdir.assert_called_with(where, perms=0755) |
3103 | + _mkdir.assert_called_with(where, perms=0o755) |
3104 | |
3105 | === modified file 'tests/helpers.py' |
3106 | --- tests/helpers.py 2014-11-25 14:35:22 +0000 |
3107 | +++ tests/helpers.py 2014-11-25 15:09:14 +0000 |
3108 | @@ -3,6 +3,12 @@ |
3109 | from mock import patch, MagicMock |
3110 | import io |
3111 | |
3112 | +import six |
3113 | +if not six.PY3: |
3114 | + builtin_open = '__builtin__.open' |
3115 | +else: |
3116 | + builtin_open = 'builtins.open' |
3117 | + |
3118 | |
3119 | @contextmanager |
3120 | def patch_open(): |
3121 | @@ -11,26 +17,29 @@ |
3122 | |
3123 | Yields the mock for "open" and "file", respectively.''' |
3124 | mock_open = MagicMock(spec=open) |
3125 | - mock_file = MagicMock(spec=file) |
3126 | + mock_file = MagicMock(spec=io.FileIO) |
3127 | |
3128 | @contextmanager |
3129 | def stub_open(*args, **kwargs): |
3130 | mock_open(*args, **kwargs) |
3131 | yield mock_file |
3132 | |
3133 | - with patch('__builtin__.open', stub_open): |
3134 | + with patch(builtin_open, stub_open): |
3135 | yield mock_open, mock_file |
3136 | |
3137 | |
3138 | @contextmanager |
3139 | def mock_open(filename, contents=None): |
3140 | ''' Slightly simpler mock of open to return contents for filename ''' |
3141 | - def mock_file(*args): |
3142 | - if args[0] == filename: |
3143 | + def mock_file(name, mode='r', buffering=-1): # Python 2 signature. |
3144 | + if name == filename: |
3145 | + if (not six.PY3) or 'b' in mode: |
3146 | + return io.BytesIO(contents) |
3147 | return io.StringIO(contents) |
3148 | else: |
3149 | - return open(*args) |
3150 | - with patch('__builtin__.open', mock_file): |
3151 | + return open(name, mode, buffering) |
3152 | + |
3153 | + with patch(builtin_open, mock_file): |
3154 | yield |
3155 | |
3156 | |
3157 | |
3158 | === modified file 'tests/payload/test_archive.py' |
3159 | --- tests/payload/test_archive.py 2014-11-25 14:35:22 +0000 |
3160 | +++ tests/payload/test_archive.py 2014-11-25 15:09:14 +0000 |
3161 | @@ -84,7 +84,7 @@ |
3162 | self.addCleanup(rmtree, destdir) |
3163 | try: |
3164 | zip_file, contents = self.create_archive("zip") |
3165 | - except subprocess.CalledProcessError, e: |
3166 | + except subprocess.CalledProcessError as e: |
3167 | if e.returncode == 127: |
3168 | self.skip("Skipping - zip is not installed") |
3169 | else: |
3170 | |
3171 | === modified file 'tests/payload/test_execd.py' |
3172 | --- tests/payload/test_execd.py 2013-07-11 08:31:49 +0000 |
3173 | +++ tests/payload/test_execd.py 2014-11-25 15:09:14 +0000 |
3174 | @@ -111,7 +111,7 @@ |
3175 | |
3176 | expected = [os.path.join(self.test_charm_dir, 'exec.d', mod, |
3177 | 'charm-pre-install') for mod in modules] |
3178 | - self.assertItemsEqual(submodules, expected) |
3179 | + self.assertEqual(sorted(submodules), sorted(expected)) |
3180 | |
3181 | def test_execd_run(self): |
3182 | modules = ['basenode', 'mod2', 'c'] |
3183 | |
3184 | === modified file 'tests/tools/test_charm_helper_sync.py' |
3185 | --- tests/tools/test_charm_helper_sync.py 2014-11-25 14:35:22 +0000 |
3186 | +++ tests/tools/test_charm_helper_sync.py 2014-11-25 15:09:14 +0000 |
3187 | @@ -2,6 +2,13 @@ |
3188 | from mock import call, patch |
3189 | import yaml |
3190 | |
3191 | +import six |
3192 | +if not six.PY3: |
3193 | + builtin_open = '__builtin__.open' |
3194 | +else: |
3195 | + builtin_open = 'builtins.open' |
3196 | + |
3197 | + |
3198 | import tools.charm_helpers_sync.charm_helpers_sync as sync |
3199 | |
3200 | INCLUDE = """ |
3201 | @@ -46,7 +53,7 @@ |
3202 | self.assertEquals('/tmp/mycharm/hooks/charmhelpers/contrib/openstack', |
3203 | path) |
3204 | |
3205 | - @patch('__builtin__.open') |
3206 | + @patch(builtin_open) |
3207 | @patch('os.path.exists') |
3208 | @patch('os.walk') |
3209 | def test_ensure_init(self, walk, exists, _open): |
3210 | @@ -114,7 +121,7 @@ |
3211 | isfile.side_effect = _isfile |
3212 | isdir.side_effect = _isdir |
3213 | result = sync.get_filter(opts)(dir='/tmp/charm-helpers/core', |
3214 | - ls=files.iterkeys()) |
3215 | + ls=six.iterkeys(files)) |
3216 | return result |
3217 | |
3218 | @patch('os.path.isdir') |
3219 | @@ -123,15 +130,15 @@ |
3220 | '''It filters out all non-py files by default''' |
3221 | result = self._test_filter_dir(opts=None, isfile=isfile, isdir=isdir) |
3222 | ex = ['bad_file.bin', 'bad_file.img', 'some_dir'] |
3223 | - self.assertEquals(ex, result) |
3224 | + self.assertEquals(sorted(ex), sorted(result)) |
3225 | |
3226 | @patch('os.path.isdir') |
3227 | @patch('os.path.isfile') |
3228 | def test_filter_dir_with_include(self, isfile, isdir): |
3229 | '''It includes non-py files if specified as an include opt''' |
3230 | - result = self._test_filter_dir(opts=['inc=*.img'], |
3231 | - isfile=isfile, isdir=isdir) |
3232 | - ex = ['bad_file.bin', 'some_dir'] |
3233 | + result = sorted(self._test_filter_dir(opts=['inc=*.img'], |
3234 | + isfile=isfile, isdir=isdir)) |
3235 | + ex = sorted(['bad_file.bin', 'some_dir']) |
3236 | self.assertEquals(ex, result) |
3237 | |
3238 | @patch('os.path.isdir') |
3239 | |
3240 | === modified file 'tools/charm_helpers_sync/charm_helpers_sync.py' |
3241 | --- tools/charm_helpers_sync/charm_helpers_sync.py 2014-11-25 14:35:22 +0000 |
3242 | +++ tools/charm_helpers_sync/charm_helpers_sync.py 2014-11-25 15:09:14 +0000 |
3243 | @@ -14,9 +14,10 @@ |
3244 | import sys |
3245 | import tempfile |
3246 | import yaml |
3247 | - |
3248 | from fnmatch import fnmatch |
3249 | |
3250 | +import six |
3251 | + |
3252 | CHARM_HELPERS_BRANCH = 'lp:charm-helpers' |
3253 | |
3254 | |
3255 | @@ -139,7 +140,7 @@ |
3256 | |
3257 | def extract_options(inc, global_options=None): |
3258 | global_options = global_options or [] |
3259 | - if global_options and isinstance(global_options, basestring): |
3260 | + if global_options and isinstance(global_options, six.string_types): |
3261 | global_options = [global_options] |
3262 | if '|' not in inc: |
3263 | return (inc, global_options) |
3264 | @@ -159,7 +160,7 @@ |
3265 | sync(src, dest, inc, opts) |
3266 | elif isinstance(inc, dict): |
3267 | # could also do nested dicts here. |
3268 | - for k, v in inc.iteritems(): |
3269 | + for k, v in six.iteritems(inc): |
3270 | if isinstance(v, list): |
3271 | for m in v: |
3272 | inc, opts = extract_options(m, global_options) |
3273 | @@ -217,7 +218,7 @@ |
3274 | checkout = clone_helpers(tmpd, config['branch']) |
3275 | sync_helpers(config['include'], checkout, config['destination'], |
3276 | options=sync_options) |
3277 | - except Exception, e: |
3278 | + except Exception as e: |
3279 | logging.error("Could not sync: %s" % e) |
3280 | raise e |
3281 | finally: |
Running make test in a clean precise lxc:
Checking for Python syntax... contrib/ templating/ contexts. py:99:54: E901 SyntaxError: invalid syntax templating/ test_contexts. py:137: 31: E901 SyntaxError: invalid syntax test_hookenv. py:132: 40: E901 SyntaxError: invalid syntax
Py2 OK
charmhelpers/
tests/contrib/
tests/core/
make: *** [lint] Error 1
Also, running apt-get as an import side-effect makes me grimace.