Merge lp:~gocept/landscape-client/python3 into lp:~landscape/landscape-client/trunk

Proposed by Daniel Havlik
Status: Merged
Approved by: Данило Шеган
Approved revision: 974
Merged at revision: 941
Proposed branch: lp:~gocept/landscape-client/python3
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 4348 lines (+897/-643)
97 files modified
.bzrignore (+1/-0)
Makefile (+16/-4)
debian/control (+1/-0)
display_py2_testresults (+13/-0)
landscape/broker/amp.py (+4/-3)
landscape/broker/ping.py (+2/-2)
landscape/broker/store.py (+7/-5)
landscape/broker/tests/test_ping.py (+2/-2)
landscape/broker/tests/test_transport.py (+1/-1)
landscape/broker/transport.py (+3/-1)
landscape/compat.py (+62/-0)
landscape/configuration.py (+5/-5)
landscape/deployment.py (+5/-3)
landscape/diff.py (+2/-2)
landscape/lib/amp.py (+20/-19)
landscape/lib/bpickle.py (+0/-1)
landscape/lib/bpickle3.py (+171/-0)
landscape/lib/cloud.py (+4/-1)
landscape/lib/disk.py (+10/-5)
landscape/lib/encoding.py (+2/-1)
landscape/lib/fetch.py (+8/-6)
landscape/lib/gpg.py (+2/-1)
landscape/lib/hashlib.py (+0/-1)
landscape/lib/md5crypt.py (+0/-159)
landscape/lib/network.py (+2/-0)
landscape/lib/persist.py (+4/-4)
landscape/lib/scriptcontent.py (+2/-2)
landscape/lib/sequenceranges.py (+1/-0)
landscape/lib/sysstats.py (+2/-1)
landscape/lib/tests/test_amp.py (+7/-0)
landscape/lib/tests/test_bootstrap.py (+6/-6)
landscape/lib/tests/test_bpickle.py (+1/-1)
landscape/lib/tests/test_disk.py (+6/-3)
landscape/lib/tests/test_fetch.py (+4/-3)
landscape/lib/tests/test_gpg.py (+2/-2)
landscape/lib/tests/test_network.py (+2/-3)
landscape/lib/tests/test_process.py (+3/-2)
landscape/lib/tests/test_sysstats.py (+1/-1)
landscape/lib/tests/test_twisted_util.py (+21/-10)
landscape/lib/twisted_util.py (+5/-4)
landscape/manager/aptsources.py (+3/-4)
landscape/manager/customgraph.py (+9/-7)
landscape/manager/keystonetoken.py (+1/-1)
landscape/manager/plugin.py (+2/-1)
landscape/manager/scriptexecution.py (+17/-13)
landscape/manager/tests/test_aptsources.py (+19/-13)
landscape/manager/tests/test_customgraph.py (+10/-25)
landscape/manager/tests/test_packagemanager.py (+7/-7)
landscape/manager/tests/test_scriptexecution.py (+18/-3)
landscape/manager/usermanager.py (+1/-1)
landscape/monitor/activeprocessinfo.py (+5/-3)
landscape/monitor/aptpreferences.py (+3/-1)
landscape/monitor/cephusage.py (+1/-0)
landscape/monitor/computeruptime.py (+17/-15)
landscape/monitor/mountinfo.py (+3/-2)
landscape/monitor/rebootrequired.py (+1/-1)
landscape/monitor/tests/test_aptpreferences.py (+2/-0)
landscape/monitor/tests/test_cephusage.py (+1/-1)
landscape/monitor/tests/test_mountinfo.py (+50/-58)
landscape/monitor/tests/test_packagemonitor.py (+5/-5)
landscape/monitor/updatemanager.py (+2/-2)
landscape/package/changer.py (+2/-2)
landscape/package/facade.py (+8/-5)
landscape/package/releaseupgrader.py (+4/-3)
landscape/package/reporter.py (+9/-5)
landscape/package/skeleton.py (+3/-1)
landscape/package/store.py (+16/-11)
landscape/package/taskhandler.py (+3/-3)
landscape/package/tests/helpers.py (+3/-3)
landscape/package/tests/test_facade.py (+2/-0)
landscape/package/tests/test_releaseupgrader.py (+17/-9)
landscape/package/tests/test_reporter.py (+22/-12)
landscape/package/tests/test_skeleton.py (+2/-0)
landscape/package/tests/test_store.py (+2/-2)
landscape/package/tests/test_taskhandler.py (+3/-3)
landscape/reactor.py (+3/-3)
landscape/schema.py (+6/-5)
landscape/sysinfo/deployment.py (+3/-3)
landscape/sysinfo/tests/test_disk.py (+5/-3)
landscape/sysinfo/tests/test_sysinfo.py (+2/-2)
landscape/tests/clock.py (+7/-2)
landscape/tests/helpers.py (+16/-15)
landscape/tests/subunit.py (+3/-2)
landscape/tests/test_configuration.py (+17/-16)
landscape/tests/test_deployment.py (+4/-4)
landscape/tests/test_reactor.py (+5/-2)
landscape/tests/test_schema.py (+6/-2)
landscape/tests/test_sysvconfig.py (+6/-2)
landscape/tests/test_watchdog.py (+23/-19)
landscape/user/changes.py (+8/-6)
landscape/user/management.py (+2/-4)
landscape/user/provider.py (+35/-28)
landscape/user/tests/helpers.py (+5/-3)
landscape/user/tests/test_management.py (+2/-2)
landscape/user/tests/test_provider.py (+28/-17)
landscape/watchdog.py (+17/-17)
trial5 (+11/-0)
To merge this branch: bzr merge lp:~gocept/landscape-client/python3
Reviewer Review Type Date Requested Status
Данило Шеган (community) Approve
🤖 Landscape Builder test results Approve
Free Ekanayaka (community) Approve
Adam Collard (community) Abstain
Steffen Allner (community) Approve
Daniel Havlik (community) Approve
Review via email: mp+315096@code.launchpad.net

Commit message

* Changed a lot of files to be python 3.5 syntax compatible
* Introduced landscape.compat containing some helpers
* Used twisted.python.compat for python 3 detection and some helpers from there
* Almost all tests run with python 3.5 (2163 of 2178 - run them with make check3)

* Everything should still work with python 2 as before.

Description of the change

* Changed a lot of files to be python 3.5 syntax compatible
* Introduced landscape.compat containing some helpers
* Used twisted.python.compat for python 3 detection and some helpers from there
* Almost all tests run with python 3.5 (2163 of 2178 - run them with make check3)

* Everything should still work with python 2 as before.

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 953
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/857/

review: Needs Fixing (test results)
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Needs Fixing
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 953
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/859/

review: Needs Fixing (test results)
Revision history for this message
Adam Collard (adam-collard) wrote :

The failure messages from that Jenkins run are: http://paste.ubuntu.com/23827074/

Revision history for this message
Adam Collard (adam-collard) wrote :

Looking pretty good! Have added some initial comments inline.

lp:~gocept/landscape-client/python3 updated
954. By Daniel Havlik

use passlib provided salt generation (as passlib limits the characters for salt to ./a-zA-Z0-9)

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 954
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/860/

review: Approve (test results)
Revision history for this message
Steffen Allner (sallner) wrote :

I addressed the comments from r953 at https://code.launchpad.net/~sallner/landscape-client/fixing-mp-1/+merge/315349. Adam, did you already had a look at the rest of the MP?

Revision history for this message
Adam Collard (adam-collard) wrote :

> I addressed the comments from r953 at https://code.launchpad.net/~sallner
> /landscape-client/fixing-mp-1/+merge/315349.

Hmm, this is arguably the Wrong Way To Do It™.

This will require Daniel to merge your branch into his. Ideally Daniel would have made these fixes and just "bzr push"'d the fixes (only Daniel has write permission to his branches).

Alternatively, you can create a team in Launchpad (e.g. ~gocept) then push the branch to that team's namespace (e.g. lp:~gocept/landscape-client/python3) and everyone in that team will have write permission. That gives you flexibility on your side on who does the work.

> Adam, did you already had a look
> at the rest of the MP?

I don't have further comments to make at this stage, but would like to take a fresh look with the fixes I suggested.

lp:~gocept/landscape-client/python3 updated
955. By Daniel Havlik

merge fixes by sallner

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 955
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/861/

review: Approve (test results)
Revision history for this message
Daniel Havlik (nilo) :
review: Approve
Revision history for this message
Steffen Allner (sallner) wrote :

LGTM

review: Approve
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 955
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/864/

review: Approve (test results)
Revision history for this message
Adam Collard (adam-collard) wrote :

Sorry this took so long to review. Please see inline comments about bpickle (and a couple of nit-picks)

review: Needs Information
lp:~gocept/landscape-client/python3 updated
956. By Daniel Havlik

remove dead code

957. By Daniel Havlik

revert compatibility changes on bpickle, use "new" bpickle3 instead / add compat import for cpickle

958. By Daniel Havlik

backmerge from trunk

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 958
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-precise/886/

review: Approve (test results)
Revision history for this message
Steffen Allner (sallner) wrote :

Adam, we have addressed your comments and also merged the master into this branch. It would be good to have this branch merged into master, as it would be easier to directly depend on the master instead of this branch for further development.

Revision history for this message
Adam Collard (adam-collard) :
review: Abstain
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 958
Branch: lp:~nilo/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-xenial/1456/

review: Approve (test results)
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

This branch looks mostly good to me.

The main concern I have is about the test skips being added. Assuming those are temporary, and will be removed in follow up branches, I'm +1 for landing. If that's not the case, please let me know.

review: Approve
lp:~gocept/landscape-client/python3 updated
959. By Daniel Havlik

Insert test for `long` and make them work under Python 3.

Revision history for this message
Steffen Allner (sallner) wrote :

> This branch looks mostly good to me.
>
> The main concern I have is about the test skips being added. Assuming those
> are temporary, and will be removed in follow up branches, I'm +1 for landing.
> If that's not the case, please let me know.

The tests have been only skipped to allow a run of the testsuite in a reasonable time, as some got stuck or had a long timeout. I could already activate some in the MP to `landscape.lib.amp` https://code.launchpad.net/~gocept/landscape-client/py3-amp/+merge/319452

Revision history for this message
Данило Шеган (danilo) wrote :

I have to step out for a bit again: here're the comments so far (some are nitpicking trivials, sorry).

Please reply inline using the "reply" link next to each of my comments: that makes it so much easier to understand what you've done to address a comment.

I'll continue with this branch later.

review: Needs Fixing
Revision history for this message
Steffen Allner (sallner) wrote :

Oh, mea culpa. I just realised how to save inline comments. So here are the now already old ones addressing Free's comments.

Revision history for this message
Данило Шеган (danilo) wrote :

Not at all: that's not the most obvious part of Launchpad reviews. :-)

Here are a few more warnings: when you click on "Show diff comments" next to someone's comment, it will refresh the diff and show the revision they made the comments on. If you reply there and "save comment", those comments will still go against that older diff. If you, however, switch to a different revision, those comments will not show and might not get saved: you have to "save comment" while looking at the diff you made the comments on.

Revision history for this message
Данило Шеган (danilo) wrote :

Btw, now there's the conflict in the Makefile I warned about in my last comments (click on "Show diff comments" next to my previous vote to see them again): test runner won't run until the conflict is resolved.

Revision history for this message
Данило Шеган (danilo) wrote :

Ok, I've gone through the whole changeset: thanks for the great changes, and for a few lint fixes here and there (yes, I noticed :-)). A few nitpicks here, but there is one overarching theme: coerce_unicode() is a huge hammer that is rarely appropriate.

Basically, I would like you to do away with it and do exactly what's appropriate in each individual case. Some of the cases are pure bugs and changes in behaviour, some are unneeded, and others just indicate where code should have the decode moved elsewhere. In the few cases where a simple "decode_if_python2" logic _is_ appropriate, you might just as well inline the logic.

Another thing I'd like fixed is switch from FakeStatvfsResult to the real os.statvfs_result, but to speed up getting this landed, I am happy to have that done in a separate branch later.

Also, make sure to resolve conflicts.

review: Needs Fixing
Revision history for this message
Данило Шеган (danilo) wrote :

Darn, most of my comments got lost when the branch moved from ~nilo to ~gocept. Resubmitting the rest.

(Note that you should go through my previous set of comments as well, which cover the first ~1000 lines of the diff, but some of them are now obsolete since I want you to drop coerce_unicode entirely)

Revision history for this message
Steffen Allner (sallner) wrote :

I also got my comments deleted during the switch. This is the response to the very first batch. I am working on your other comments and agree with you on the removal of coerce_unicode(). It was introduced as a helper to get rid of the encoding/decoding errors and to run the tests with Python 3. But as you put so much effort into it already, we can remove it already at this point.

Revision history for this message
Данило Шеган (danilo) wrote :

Thanks for addressing my comments so far.  Feel free to say that
something is coming up in a follow up branch or two: you can also
highlight bits that you know are imperfect and which will be fixed
later and we can ignore them in reviews.

Also, I might sometimes misread a bit of code or diff, so if you think
I am wrong, shout! :)

I'll wait for the next round of changes to go over them next.  Thanks
for bearing with me :-)

Revision history for this message
Данило Шеган (danilo) wrote :

Btw, there's still a conflict in the Makefile "check" rule.

Revision history for this message
Steffen Allner (sallner) wrote :

So I integrated the second batch of changes and will push it now. I should be able to have a look at the third batch today, so that maybe we can get a merge at the beginning of the week.

lp:~gocept/landscape-client/python3 updated
960. By Steffen Allner

Backmerge from trunk.

961. By Steffen Allner

Fix PEP-8 and extend output.

962. By Steffen Allner

Remove double import and improve documentation.

963. By Steffen Allner

Handle decoding explicitly here, instead of using the helper function.

964. By Steffen Allner

Restore correct docstring.

965. By Steffen Allner

Remove `landscape.md5crypt` completely and use `passlib` instead.

966. By Steffen Allner

Get rid of coerce_unicode and move encoding of script.

967. By Steffen Allner

Improve readability and PEP-8

968. By Steffen Allner

`read_file()` should do the decoding. That is coming with a future MP.

969. By Steffen Allner

Remove coerce_unicode().

970. By Steffen Allner

Formatting for readability.

971. By Steffen Allner

Remove coerce_unicode()

972. By Steffen Allner

Remove coerce_unicode() and move the decoding to the underlying function. We have to teach the makeFile() helper to write binary files for a broken encoding test.

973. By Steffen Allner

Use `os.statsvfs_result()` instead of the fake one and use a fixture in tests.

974. By Steffen Allner

Remove coerce_unicode().

Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
Steffen Allner (sallner) wrote :

Oh, all your diff comments were shown combined, so I should be finished with it now. It is your turn again. The conflict in check should be solved with http://bazaar.launchpad.net/~gocept/landscape-client/python3/revision/960 .

Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 974
Branch: lp:~gocept/landscape-client/python3
Jenkins: https://ci.lscape.net/job/latch-test-xenial/2039/

review: Approve (test results)
Revision history for this message
Данило Шеган (danilo) wrote :

(Save a few replies on the older rev, nothing to act on)

Revision history for this message
Данило Шеган (danilo) wrote :

Right, all comments on a single revision will show combined: only when a code change is pushed after one set of comments they might be separate (though, it is still possible to make comments on earlier revisions, I prefer to do it on the latest one).

Anyway, this looks great now: thanks for all the extra work, and for addressing review comments in individual commits — allows me to easily see just the relevant change.

I'll get this landed now. Note that all/some dependant branches might start seeing conflicts and you'll need to re-merge trunk once this gets in.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2016-01-07 10:45:00 +0000
3+++ .bzrignore 2017-03-10 15:29:37 +0000
4@@ -23,3 +23,4 @@
5 apt-update/apt-update
6 docs
7 tags
8+_last_py2_res
9
10=== modified file 'Makefile'
11--- Makefile 2017-03-09 09:18:28 +0000
12+++ Makefile 2017-03-10 15:29:37 +0000
13@@ -1,8 +1,9 @@
14 PYDOCTOR ?= pydoctor
15-TXT2MAN ?= txt2man
16+TXT2MAN ?= txt2man
17 PYTHON ?= python
18-TRIAL_ARGS ?=
19-TEST_COMMAND = trial --unclean-warnings $(TRIAL_ARGS) landscape
20+TRIAL_ARGS ?=
21+TEST_COMMAND_PY2 = trial --unclean-warnings $(TRIAL_ARGS) landscape
22+TEST_COMMAND_PY3 = trial3 --unclean-warnings $(TRIAL_ARGS) landscape
23 UBUNTU_RELEASE := $(shell lsb_release -cs)
24 # version in the code is authoritative
25 # Use := here, not =, it's really important, otherwise UPSTREAM_VERSION
26@@ -19,11 +20,22 @@
27
28 all: build
29
30+build3:
31+ python3 setup.py build_ext -i
32+
33 build:
34 $(PYTHON) setup.py build_ext -i
35
36+check5:
37+ -trial --unclean-warnings --reporter=summary landscape > _last_py2_res
38+ -trial3 --unclean-warnings landscape
39+ ./display_py2_testresults
40+
41+check3: build3
42+ LC_ALL=C $(TEST_COMMAND_PY3)
43+
44 check: build
45- LC_ALL=C $(TEST_COMMAND)
46+ LC_ALL=C $(TEST_COMMAND_PY2)
47
48 lint:
49 bzr ls-lint
50
51=== modified file 'debian/control'
52--- debian/control 2017-01-18 09:11:59 +0000
53+++ debian/control 2017-03-10 15:29:37 +0000
54@@ -14,6 +14,7 @@
55 python-twisted-core,
56 python-configobj,
57 python-apt,
58+ python-passlib,
59 ca-certificates,
60 python-gdbm,
61 lsb-release,
62
63=== added file 'display_py2_testresults'
64--- display_py2_testresults 1970-01-01 00:00:00 +0000
65+++ display_py2_testresults 2017-03-10 15:29:37 +0000
66@@ -0,0 +1,13 @@
67+#!/usr/bin/python
68+with open('_last_py2_res', 'r') as py2res:
69+ lines = py2res.readlines()
70+
71+lastline = lines[-1]
72+
73+time, total, total, err, fail, skip = lastline.split()
74+
75+if "".join((err, fail, skip)) != "000":
76+ print("Python 2: \033[91mFAILED\033[0m (skips={}, failures={}, "
77+ "errors={}, total={})".format(skip, fail, err, total))
78+else:
79+ print("Python 2: \033[92mOK\033[0m (total={})".format(total))
80
81=== modified file 'landscape/broker/amp.py'
82--- landscape/broker/amp.py 2015-03-04 14:22:17 +0000
83+++ landscape/broker/amp.py 2017-03-10 15:29:37 +0000
84@@ -1,4 +1,5 @@
85 from twisted.internet.defer import maybeDeferred, execute, succeed
86+from twisted.python.compat import iteritems
87
88 from landscape.lib.amp import RemoteObject, MethodCallArgument
89 from landscape.amp import ComponentConnector, get_remote_methods
90@@ -30,7 +31,7 @@
91 """
92 result = self.listen_events(handlers.keys())
93 return result.addCallback(
94- lambda (event_type, kwargs): handlers[event_type](**kwargs))
95+ lambda args: handlers[args[0]](**args[1]))
96
97
98 class FakeRemoteBroker(object):
99@@ -53,7 +54,7 @@
100 def method(*args, **kwargs):
101 for arg in args:
102 assert MethodCallArgument.check(arg)
103- for k, v in kwargs.iteritems():
104+ for k, v in iteritems(kwargs):
105 assert MethodCallArgument.check(v)
106 return execute(original, *args, **kwargs)
107 return method
108@@ -75,7 +76,7 @@
109 """
110 result = self.broker_server.listen_events(handlers.keys())
111 return result.addCallback(
112- lambda (event_type, kwargs): handlers[event_type](**kwargs))
113+ lambda args: handlers[args[0]](**args[1]))
114
115 def register(self):
116 return succeed(None)
117
118=== modified file 'landscape/broker/ping.py'
119--- landscape/broker/ping.py 2013-06-27 10:25:39 +0000
120+++ landscape/broker/ping.py 2017-03-10 15:29:37 +0000
121@@ -39,7 +39,7 @@
122 from twisted.python.failure import Failure
123 from twisted.internet import defer
124
125-from landscape.lib.bpickle import loads
126+from landscape.compat import bpickle
127 from landscape.lib.fetch import fetch
128 from landscape.lib.log import log_failure
129
130@@ -84,7 +84,7 @@
131 the response indicates that their are messages waiting for
132 this computer, False otherwise.
133 """
134- if loads(webtext) == {"messages": True}:
135+ if bpickle.loads(webtext) == {"messages": True}:
136 return True
137
138
139
140=== modified file 'landscape/broker/store.py'
141--- landscape/broker/store.py 2014-12-12 13:25:39 +0000
142+++ landscape/broker/store.py 2017-03-10 15:29:37 +0000
143@@ -97,8 +97,10 @@
144 import os
145 import uuid
146
147+from twisted.python.compat import iteritems
148+
149 from landscape import DEFAULT_SERVER_API
150-from landscape.lib import bpickle
151+from landscape.compat import bpickle
152 from landscape.lib.fs import create_file
153 from landscape.lib.versioning import sort_versions, is_version_higher
154
155@@ -261,7 +263,7 @@
156 data = self._get_content(self._message_dir(filename))
157 try:
158 message = bpickle.loads(data)
159- except ValueError, e:
160+ except ValueError as e:
161 logging.exception(e)
162 self._add_flags(filename, BROKEN)
163 else:
164@@ -453,7 +455,7 @@
165 flags = self._get_flags(old_filename)
166 try:
167 message = bpickle.loads(self._get_content(old_filename))
168- except ValueError, e:
169+ except ValueError as e:
170 logging.exception(e)
171 if HELD not in flags:
172 offset += 1
173@@ -500,7 +502,7 @@
174 information, limited by scope.
175 """
176 session_ids = self._persist.get("session-ids", {})
177- for session_id, stored_scope in session_ids.iteritems():
178+ for session_id, stored_scope in iteritems(session_ids):
179 # This loop should be relatively short as it's intent is to limit
180 # session-ids to one per scope. The or condition here is not
181 # strictly necessary, but we *should* do "is" comparisons when we
182@@ -524,7 +526,7 @@
183 new_session_ids = {}
184 if scopes:
185 session_ids = self._persist.get("session-ids", {})
186- for session_id, session_scope in session_ids.iteritems():
187+ for session_id, session_scope in iteritems(session_ids):
188 if session_scope not in scopes:
189 new_session_ids[session_id] = session_scope
190 self._persist.set("session-ids", new_session_ids)
191
192=== modified file 'landscape/broker/tests/test_ping.py'
193--- landscape/broker/tests/test_ping.py 2013-04-04 10:38:29 +0000
194+++ landscape/broker/tests/test_ping.py 2017-03-10 15:29:37 +0000
195@@ -2,7 +2,7 @@
196
197 from twisted.internet.defer import fail
198
199-from landscape.lib.bpickle import dumps
200+from landscape.compat import bpickle
201 from landscape.lib.fetch import fetch
202 from landscape.reactor import FakeReactor
203 from landscape.broker.ping import PingClient, Pinger
204@@ -25,7 +25,7 @@
205 data.
206 """
207 self.fetches.append((url, post, headers, data))
208- return dumps(self.response)
209+ return bpickle.dumps(self.response)
210
211 def failing_get_page(self, url, post, headers, data):
212 """
213
214=== modified file 'landscape/broker/tests/test_transport.py'
215--- landscape/broker/tests/test_transport.py 2016-03-04 08:57:40 +0000
216+++ landscape/broker/tests/test_transport.py 2017-03-10 15:29:37 +0000
217@@ -3,7 +3,7 @@
218 from landscape import VERSION
219 from landscape.broker.transport import HTTPTransport
220 from landscape.lib.fetch import PyCurlError
221-from landscape.lib import bpickle
222+from landscape.compat import bpickle
223
224 from landscape.tests.helpers import LandscapeTest, LogKeeperHelper
225
226
227=== modified file 'landscape/broker/transport.py'
228--- landscape/broker/transport.py 2014-10-03 10:06:24 +0000
229+++ landscape/broker/transport.py 2017-03-10 15:29:37 +0000
230@@ -6,8 +6,10 @@
231
232 import pycurl
233
234+from twisted.python.compat import unicode
235+
236+from landscape.compat import bpickle
237 from landscape.lib.fetch import fetch
238-from landscape.lib import bpickle
239 from landscape.log import format_delta
240 from landscape import SERVER_API, VERSION
241
242
243=== added file 'landscape/compat.py'
244--- landscape/compat.py 1970-01-01 00:00:00 +0000
245+++ landscape/compat.py 2017-03-10 15:29:37 +0000
246@@ -0,0 +1,62 @@
247+from twisted.python.compat import _PY3
248+
249+if _PY3:
250+ import _pickle as cPickle
251+ from landscape.lib import bpickle3 as bpickle
252+ from configparser import ConfigParser, NoOptionError
253+ SafeConfigParser = ConfigParser
254+
255+ import _thread as thread
256+
257+ from io import StringIO
258+ stringio = cstringio = StringIO
259+
260+else:
261+ import cPickle
262+ from landscape.lib import bpickle
263+ from ConfigParser import ConfigParser, NoOptionError, SafeConfigParser
264+
265+ import thread
266+
267+ from StringIO import StringIO
268+ stringio = StringIO
269+ from cStringIO import StringIO as cstringio
270+
271+
272+def coerce_unicode(s, encoding='ascii', errors='strict'):
273+ """
274+ Coerce byte strings into unicode for Python 2.
275+
276+ In Python 2, decodes a byte string L{s} into unicode using the L{encoding},
277+ returns unmodified if any other type. In Python 3, raises a L{TypeError}
278+ when passed a byte string in L{s}, returns unmodified otherwise.
279+
280+ @param s: The string to be converted to unicode.
281+ @type s: L{bytes} or L{unicode}
282+ @raise UnicodeError: The input L{bytes} is not decodable
283+ with given encoding.
284+ @raise TypeError: The input is L{bytes} on Python 3.
285+ """
286+ # In Python 2 C{unicode(b'bytes')} returns a unicode string C{'bytes'}. In
287+ # Python 3, the equivalent C{str(b'bytes')} will return C{"b'bytes'"}
288+ # instead.
289+ if isinstance(s, bytes):
290+ if _PY3:
291+ raise TypeError("Expected str not %r (bytes)" % (s,))
292+ else:
293+ return s.decode(encoding, errors)
294+ else:
295+ return s
296+
297+
298+def convert_buffer_to_string(mem_view):
299+ """
300+ Converts a buffer in Python 2 or a memoryview in Python 3 to str.
301+
302+ @param mem_view: The view to convert.
303+ """
304+ if _PY3:
305+ result = mem_view.decode('ascii')
306+ else:
307+ result = str(mem_view)
308+ return result
309
310=== modified file 'landscape/configuration.py'
311--- landscape/configuration.py 2017-03-03 13:52:57 +0000
312+++ landscape/configuration.py 2017-03-10 15:29:37 +0000
313@@ -13,7 +13,7 @@
314 import pwd
315 import sys
316
317-from StringIO import StringIO
318+from landscape.compat import StringIO
319
320 from landscape.lib.tag import is_valid_tag
321
322@@ -565,9 +565,9 @@
323 def bootstrap_tree(config):
324 """Create the client directories tree."""
325 bootstrap_list = [
326- BootstrapDirectory("$data_path", "landscape", "root", 0755),
327+ BootstrapDirectory("$data_path", "landscape", "root", 0o755),
328 BootstrapDirectory("$annotations_path", "landscape", "landscape",
329- 0755)]
330+ 0o755)]
331 BootstrapList(bootstrap_list).bootstrap(
332 data_path=config.data_path, annotations_path=config.annotations_path)
333
334@@ -756,7 +756,7 @@
335 config = LandscapeSetupConfiguration()
336 try:
337 config.load(args)
338- except ImportOptionError, error:
339+ except ImportOptionError as error:
340 print_text(str(error), error=True)
341 sys.exit(1)
342
343@@ -775,7 +775,7 @@
344 # Setup client configuration.
345 try:
346 setup(config)
347- except Exception, e:
348+ except Exception as e:
349 print_text(str(e))
350 sys.exit("Aborting Landscape configuration")
351
352
353=== modified file 'landscape/deployment.py'
354--- landscape/deployment.py 2014-09-24 08:09:55 +0000
355+++ landscape/deployment.py 2017-03-10 15:29:37 +0000
356@@ -13,6 +13,8 @@
357
358 from landscape.upgraders import UPGRADE_MANAGERS
359
360+from twisted.python.compat import StringType as basestring
361+
362
363 def init_logging(configuration, program_name):
364 """Given a basic configuration, set up logging."""
365@@ -40,8 +42,8 @@
366
367 def add_option(self, *args, **kwargs):
368 option = OptionParser.add_option(self, *args, **kwargs)
369- print dir(option)
370- print option.get_opt_string()
371+ print(dir(option))
372+ print(option.get_opt_string())
373 return option
374
375
376@@ -235,7 +237,7 @@
377 try:
378 config_obj = ConfigObj(config_source, list_values=False,
379 raise_errors=False, write_empty_values=True)
380- except ConfigObjError, e:
381+ except ConfigObjError as e:
382 logger = getLogger()
383 logger.warn(str(e))
384 # Good configuration values are recovered here
385
386=== modified file 'landscape/diff.py'
387--- landscape/diff.py 2008-06-10 10:56:01 +0000
388+++ landscape/diff.py 2017-03-10 15:29:37 +0000
389@@ -4,8 +4,8 @@
390 @return: A 3-tuple of dicts with the changes that would need to be
391 made to convert C{old} into C{new}: C{(creates, updates, deletes)}
392 """
393- new_keys = set(new.iterkeys())
394- old_keys = set(old.iterkeys())
395+ new_keys = set(new)
396+ old_keys = set(old)
397
398 creates = {}
399 for key in new_keys - old_keys:
400
401=== modified file 'landscape/lib/amp.py'
402--- landscape/lib/amp.py 2013-05-13 12:51:30 +0000
403+++ landscape/lib/amp.py 2017-03-10 15:29:37 +0000
404@@ -49,11 +49,12 @@
405 from twisted.internet.defer import Deferred, maybeDeferred, succeed
406 from twisted.internet.protocol import ServerFactory, ReconnectingClientFactory
407 from twisted.python.failure import Failure
408+from twisted.python.compat import xrange
409
410 from twisted.protocols.amp import (
411 Argument, String, Integer, Command, AMP, MAX_VALUE_LENGTH, CommandLocator)
412
413-from landscape.lib.bpickle import loads, dumps, dumps_table
414+from landscape.compat import bpickle
415
416
417 class MethodCallArgument(Argument):
418@@ -61,16 +62,16 @@
419
420 def toString(self, inObject):
421 """Serialize an argument."""
422- return dumps(inObject)
423+ return bpickle.dumps(inObject)
424
425 def fromString(self, inString):
426 """Unserialize an argument."""
427- return loads(inString)
428+ return bpickle.loads(inString)
429
430 @classmethod
431 def check(cls, inObject):
432 """Check if an argument is serializable."""
433- return type(inObject) in dumps_table
434+ return type(inObject) in bpickle.dumps_table
435
436
437 class MethodCallError(Exception):
438@@ -95,13 +96,13 @@
439 and C{kwargs} the keyword ones.
440 """
441
442- arguments = [("sequence", Integer()),
443- ("method", String()),
444- ("arguments", String())]
445-
446- response = [("result", MethodCallArgument())]
447-
448- errors = {MethodCallError: "METHOD_CALL_ERROR"}
449+ arguments = [(b"sequence", Integer()),
450+ (b"method", String()),
451+ (b"arguments", String())]
452+
453+ response = [(b"result", MethodCallArgument())]
454+
455+ errors = {MethodCallError: b"METHOD_CALL_ERROR"}
456
457
458 class MethodCallChunk(Command):
459@@ -119,12 +120,12 @@
460 being split and buffered.
461 """
462
463- arguments = [("sequence", Integer()),
464- ("chunk", String())]
465-
466- response = [("result", Integer())]
467-
468- errors = {MethodCallError: "METHOD_CALL_ERROR"}
469+ arguments = [(b"sequence", Integer()),
470+ (b"chunk", String())]
471+
472+ response = [(b"result", Integer())]
473+
474+ errors = {MethodCallError: b"METHOD_CALL_ERROR"}
475
476
477 class MethodCallReceiver(CommandLocator):
478@@ -164,7 +165,7 @@
479 chunks.append(arguments)
480 arguments = "".join(chunks)
481
482- args, kwargs = loads(arguments)
483+ args, kwargs = bpickle.loads(arguments)
484
485 if not method in self._methods:
486 raise MethodCallError("Forbidden method '%s'" % method)
487@@ -260,7 +261,7 @@
488 invoked on the remote object. If the remote method itself returns
489 a deferred, we fire with the callback value of such deferred.
490 """
491- arguments = dumps((args, kwargs))
492+ arguments = bpickle.dumps((args, kwargs))
493 sequence = uuid4().int
494
495 # Split the given arguments in one or more chunks
496
497=== modified file 'landscape/lib/bpickle.py'
498--- landscape/lib/bpickle.py 2008-06-10 10:56:01 +0000
499+++ landscape/lib/bpickle.py 2017-03-10 15:29:37 +0000
500@@ -158,4 +158,3 @@
501 "t": loads_tuple,
502 "d": loads_dict,
503 "n": loads_none })
504-
505
506=== added file 'landscape/lib/bpickle3.py'
507--- landscape/lib/bpickle3.py 1970-01-01 00:00:00 +0000
508+++ landscape/lib/bpickle3.py 2017-03-10 15:29:37 +0000
509@@ -0,0 +1,171 @@
510+"""Copyright (c) 2006, Gustavo Niemeyer <gustavo@niemeyer.net>
511+Port to python 3 was done by Chris Glass <chris.glass@canonical.com>
512+
513+All rights reserved.
514+
515+Redistribution and use in source and binary forms, with or without
516+modification, are permitted provided that the following conditions are met:
517+
518+ * Redistributions of source code must retain the above copyright notice,
519+ this list of conditions and the following disclaimer.
520+ * Redistributions in binary form must reproduce the above copyright notice,
521+ this list of conditions and the following disclaimer in the documentation
522+ and/or other materials provided with the distribution.
523+ * Neither the name of the copyright holder nor the names of its
524+ contributors may be used to endorse or promote products derived from
525+ this software without specific prior written permission.
526+
527+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
528+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
529+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
530+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
531+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
532+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
533+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
534+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
535+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
536+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
537+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
538+
539+This file is modified from the original to work with python3, but should be
540+wire compatible and behave the same way (bugs notwithstanding).
541+"""
542+
543+
544+dumps_table = {}
545+loads_table = {}
546+
547+
548+def dumps(obj, _dt=dumps_table):
549+ try:
550+ return _dt[type(obj)](obj).encode("UTF-8")
551+ except KeyError as e:
552+ raise ValueError("Unsupported type: %s" % e)
553+
554+
555+def loads(byte_string, _lt=loads_table):
556+ if not byte_string:
557+ raise ValueError("Can't load empty string")
558+ unicode_string = byte_string.decode()
559+ try:
560+ return _lt[unicode_string[0]](unicode_string, 0)[0]
561+ except KeyError as e:
562+ raise ValueError("Unknown type character: %s" % e)
563+ except IndexError: #pragma: nocover
564+ # NOTE: Not sure if reachable (chrisglass during py3 conversion)
565+ raise ValueError("Corrupted data")
566+
567+
568+def dumps_bool(obj):
569+ return "b%d" % int(obj)
570+
571+
572+def dumps_int(obj):
573+ return "i%s;" % obj
574+
575+
576+def dumps_float(obj):
577+ return "f%r;" % obj
578+
579+
580+def dumps_str(obj):
581+ return "s%s:%s" % (len(obj), obj)
582+
583+
584+def dumps_list(obj, _dt=dumps_table):
585+ return "l%s;" % "".join([_dt[type(val)](val) for val in obj])
586+
587+
588+def dumps_tuple(obj, _dt=dumps_table):
589+ return "t%s;" % "".join([_dt[type(val)](val) for val in obj])
590+
591+
592+def dumps_dict(obj, _dt=dumps_table):
593+ keys = list(obj.keys())
594+ keys.sort()
595+ res = []
596+ append = res.append
597+ for key in keys:
598+ val = obj[key]
599+ append(_dt[type(key)](key))
600+ append(_dt[type(val)](val))
601+ return "d%s;" % "".join(res)
602+
603+
604+def dumps_none(obj):
605+ return "n"
606+
607+
608+def loads_bool(str, pos):
609+ return bool(int(str[pos+1])), pos+2
610+
611+
612+def loads_int(str, pos):
613+ endpos = str.index(";", pos)
614+ return int(str[pos+1:endpos]), endpos+1
615+
616+
617+def loads_float(str, pos):
618+ endpos = str.index(";", pos)
619+ return float(str[pos+1:endpos]), endpos+1
620+
621+
622+def loads_str(str, pos):
623+ startpos = str.index(":", pos)+1
624+ endpos = startpos+int(str[pos+1:startpos-1])
625+ return str[startpos:endpos], endpos
626+
627+
628+def loads_list(str, pos, _lt=loads_table):
629+ pos += 1
630+ res = []
631+ append = res.append
632+ while str[pos] != ";":
633+ obj, pos = _lt[str[pos]](str, pos)
634+ append(obj)
635+ return res, pos+1
636+
637+
638+def loads_tuple(str, pos, _lt=loads_table):
639+ pos += 1
640+ res = []
641+ append = res.append
642+ while str[pos] != ";":
643+ obj, pos = _lt[str[pos]](str, pos)
644+ append(obj)
645+ return tuple(res), pos+1
646+
647+
648+def loads_dict(str, pos, _lt=loads_table):
649+ pos += 1
650+ res = {}
651+ while str[pos] != ";":
652+ key, pos = _lt[str[pos]](str, pos)
653+ val, pos = _lt[str[pos]](str, pos)
654+ res[key] = val
655+ return res, pos+1
656+
657+
658+def loads_none(str, pos):
659+ return None, pos+1
660+
661+
662+dumps_table.update({ bool: dumps_bool,
663+ int: dumps_int,
664+ float: dumps_float,
665+ str: dumps_str,
666+ list: dumps_list,
667+ tuple: dumps_tuple,
668+ dict: dumps_dict,
669+ type(None): dumps_none })
670+
671+loads_table.update({ "b": loads_bool,
672+ "i": loads_int,
673+ "f": loads_float,
674+ "s": loads_str,
675+ "u": loads_str,
676+ "l": loads_list,
677+ "t": loads_tuple,
678+ "d": loads_dict,
679+ "n": loads_none })
680+
681
682=== modified file 'landscape/lib/cloud.py'
683--- landscape/lib/cloud.py 2016-06-16 20:40:47 +0000
684+++ landscape/lib/cloud.py 2017-03-10 15:29:37 +0000
685@@ -1,5 +1,8 @@
686 from landscape.lib.fetch import fetch_async
687
688+from landscape.compat import coerce_unicode
689+
690+
691 EC2_HOST = "169.254.169.254"
692 EC2_API = "http://%s/latest" % (EC2_HOST,)
693 MAX_LENGTH = 64
694@@ -24,7 +27,7 @@
695
696 def _process_result(value):
697 if value is not None:
698- return value.decode("utf-8")[:MAX_LENGTH]
699+ return coerce_unicode(value, "utf-8")[:MAX_LENGTH]
700
701 (instance_id, instance_type, ami_id) = cloud_data
702 return {
703
704=== modified file 'landscape/lib/disk.py'
705--- landscape/lib/disk.py 2013-05-15 16:06:14 +0000
706+++ landscape/lib/disk.py 2017-03-10 15:29:37 +0000
707@@ -1,8 +1,10 @@
708 from __future__ import division
709
710 import os
711-import statvfs
712 import re
713+import codecs
714+
715+from twisted.python.compat import _PY3
716
717
718 # List of filesystem types authorized when generating disk use statistics.
719@@ -32,7 +34,10 @@
720 for line in open(mounts_file):
721 try:
722 device, mount_point, filesystem = line.split()[:3]
723- mount_point = mount_point.decode("string-escape")
724+ if _PY3:
725+ mount_point = codecs.decode(mount_point, "unicode_escape")
726+ else:
727+ mount_point = codecs.decode(mount_point, "string_escape")
728 except ValueError:
729 continue
730 if (filesystems_whitelist is not None and
731@@ -43,9 +48,9 @@
732 stats = statvfs_(mount_point)
733 except OSError:
734 continue
735- block_size = stats[statvfs.F_BSIZE]
736- total_space = (stats[statvfs.F_BLOCKS] * block_size) // megabytes
737- free_space = (stats[statvfs.F_BFREE] * block_size) // megabytes
738+ block_size = stats.f_bsize
739+ total_space = (stats.f_blocks * block_size) // megabytes
740+ free_space = (stats.f_bfree * block_size) // megabytes
741 yield {"device": device, "mount-point": mount_point,
742 "filesystem": filesystem, "total-space": total_space,
743 "free-space": free_space}
744
745=== modified file 'landscape/lib/encoding.py'
746--- landscape/lib/encoding.py 2013-03-19 14:58:59 +0000
747+++ landscape/lib/encoding.py 2017-03-10 15:29:37 +0000
748@@ -1,3 +1,4 @@
749+from twisted.python.compat import iteritems, unicode
750
751
752 def encode_if_needed(value):
753@@ -13,6 +14,6 @@
754 """
755 A wrapper taking a dict and passing each of the values to encode_if_needed.
756 """
757- for key, value in values.iteritems():
758+ for key, value in iteritems(values):
759 values[key] = encode_if_needed(value)
760 return values
761
762=== modified file 'landscape/lib/fetch.py'
763--- landscape/lib/fetch.py 2016-08-02 12:52:30 +0000
764+++ landscape/lib/fetch.py 2017-03-10 15:29:37 +0000
765@@ -2,10 +2,12 @@
766 import sys
767
768 from optparse import OptionParser
769-from StringIO import StringIO
770
771+from twisted.internet.defer import DeferredList
772 from twisted.internet.threads import deferToThread
773-from twisted.internet.defer import DeferredList
774+from twisted.python.compat import iteritems
775+
776+from landscape.compat import StringIO
777
778
779 class FetchError(Exception):
780@@ -84,7 +86,7 @@
781
782 if headers:
783 curl.setopt(pycurl.HTTPHEADER,
784- ["%s: %s" % pair for pair in sorted(headers.iteritems())])
785+ ["%s: %s" % pair for pair in sorted(iteritems(headers))])
786
787 if insecure:
788 curl.setopt(pycurl.SSL_VERIFYPEER, False)
789@@ -109,7 +111,7 @@
790
791 try:
792 curl.perform()
793- except pycurl.error, e:
794+ except pycurl.error as e:
795 raise PyCurlError(e.args[0], e.args[1])
796
797 body = input.getvalue()
798@@ -197,8 +199,8 @@
799 parser.add_option("--data", default="")
800 parser.add_option("--cainfo")
801 options, (url,) = parser.parse_args(args)
802- print fetch(url, post=options.post, data=options.data,
803- cainfo=options.cainfo)
804+ print(fetch(url, post=options.post, data=options.data,
805+ cainfo=options.cainfo))
806
807
808 if __name__ == "__main__":
809
810=== modified file 'landscape/lib/gpg.py'
811--- landscape/lib/gpg.py 2009-10-27 09:33:45 +0000
812+++ landscape/lib/gpg.py 2017-03-10 15:29:37 +0000
813@@ -22,7 +22,8 @@
814 shutil.rmtree(gpg_home)
815 return ignored
816
817- def check_gpg_exit_code((out, err, code)):
818+ def check_gpg_exit_code(args):
819+ out, err, code = args
820 if code != 0:
821 raise InvalidGPGSignature("%s failed (out='%s', err='%s', "
822 "code='%d')" % (gpg, out, err, code))
823
824=== modified file 'landscape/lib/hashlib.py'
825--- landscape/lib/hashlib.py 2013-01-03 07:09:48 +0000
826+++ landscape/lib/hashlib.py 2017-03-10 15:29:37 +0000
827@@ -1,6 +1,5 @@
828 """Provide backward compatible access to hashlib functions."""
829
830-
831 try:
832 _hashlib = __import__("hashlib")
833 except ImportError:
834
835=== removed file 'landscape/lib/md5crypt.py'
836--- landscape/lib/md5crypt.py 2009-08-23 07:05:22 +0000
837+++ landscape/lib/md5crypt.py 1970-01-01 00:00:00 +0000
838@@ -1,159 +0,0 @@
839-#########################################################
840-# md5crypt.py
841-#
842-# 0423.2000 by michal wallace http://www.sabren.com/
843-# based on perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
844-# based on /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
845-#
846-# MANY THANKS TO
847-#
848-# Carey Evans - http://home.clear.net.nz/pages/c.evans/
849-# Dennis Marti - http://users.starpower.net/marti1/
850-#
851-# For the patches that got this thing working!
852-#
853-#########################################################
854-"""md5crypt.py - Provides interoperable MD5-based crypt() function
855-
856-SYNOPSIS
857-
858- import md5crypt.py
859-
860- cryptedpassword = md5crypt.md5crypt(password, salt);
861-
862-DESCRIPTION
863-
864-unix_md5_crypt() provides a crypt()-compatible interface to the
865-rather new MD5-based crypt() function found in modern operating systems.
866-It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
867-contains the following license in it:
868-
869- "THE BEER-WARE LICENSE" (Revision 42):
870- <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
871- can do whatever you want with this stuff. If we meet some day, and you think
872- this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
873-
874-apache_md5_crypt() provides a function compatible with Apache's
875-.htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>.
876-
877-"""
878-
879-MAGIC = '$1$' # Magic string
880-ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
881-
882-from landscape.lib.hashlib import md5
883-
884-def to64 (v, n):
885- ret = ''
886- while (n - 1 >= 0):
887- n = n - 1
888- ret = ret + ITOA64[v & 0x3f]
889- v = v >> 6
890- return ret
891-
892-
893-def apache_md5_crypt (pw, salt):
894- # change the Magic string to match the one used by Apache
895- return unix_md5_crypt(pw, salt, '$apr1$')
896-
897-
898-def unix_md5_crypt(pw, salt, magic=None):
899-
900- if magic==None:
901- magic = MAGIC
902-
903- # Take care of the magic string if present
904- if salt[:len(magic)] == magic:
905- salt = salt[len(magic):]
906-
907-
908- # salt can have up to 8 characters:
909- import string
910- salt = string.split(salt, '$', 1)[0]
911- salt = salt[:8]
912-
913- ctx = pw + magic + salt
914-
915- final = md5(pw + salt + pw).digest()
916-
917- for pl in range(len(pw),0,-16):
918- if pl > 16:
919- ctx = ctx + final[:16]
920- else:
921- ctx = ctx + final[:pl]
922-
923-
924- # Now the 'weird' xform (??)
925-
926- i = len(pw)
927- while i:
928- if i & 1:
929- ctx = ctx + chr(0) #if ($i & 1) { $ctx->add(pack("C", 0)); }
930- else:
931- ctx = ctx + pw[0]
932- i = i >> 1
933-
934- final = md5(ctx).digest()
935-
936- # The following is supposed to make
937- # things run slower.
938-
939- # my question: WTF???
940-
941- for i in range(1000):
942- ctx1 = ''
943- if i & 1:
944- ctx1 = ctx1 + pw
945- else:
946- ctx1 = ctx1 + final[:16]
947-
948- if i % 3:
949- ctx1 = ctx1 + salt
950-
951- if i % 7:
952- ctx1 = ctx1 + pw
953-
954- if i & 1:
955- ctx1 = ctx1 + final[:16]
956- else:
957- ctx1 = ctx1 + pw
958-
959-
960- final = md5(ctx1).digest()
961-
962-
963- # Final xform
964-
965- passwd = ''
966-
967- passwd = passwd + to64((int(ord(final[0])) << 16)
968- |(int(ord(final[6])) << 8)
969- |(int(ord(final[12]))),4)
970-
971- passwd = passwd + to64((int(ord(final[1])) << 16)
972- |(int(ord(final[7])) << 8)
973- |(int(ord(final[13]))), 4)
974-
975- passwd = passwd + to64((int(ord(final[2])) << 16)
976- |(int(ord(final[8])) << 8)
977- |(int(ord(final[14]))), 4)
978-
979- passwd = passwd + to64((int(ord(final[3])) << 16)
980- |(int(ord(final[9])) << 8)
981- |(int(ord(final[15]))), 4)
982-
983- passwd = passwd + to64((int(ord(final[4])) << 16)
984- |(int(ord(final[10])) << 8)
985- |(int(ord(final[5]))), 4)
986-
987- passwd = passwd + to64((int(ord(final[11]))), 2)
988-
989-
990- return magic + salt + '$' + passwd
991-
992-
993-## assign a wrapper function:
994-md5crypt = unix_md5_crypt
995-
996-if __name__ == "__main__":
997- print unix_md5_crypt("cat", "hat")
998
999=== modified file 'landscape/lib/network.py'
1000--- landscape/lib/network.py 2013-05-20 08:22:52 +0000
1001+++ landscape/lib/network.py 2017-03-10 15:29:37 +0000
1002@@ -8,6 +8,8 @@
1003 import errno
1004 import logging
1005
1006+from twisted.python.compat import long
1007+
1008 __all__ = ["get_active_device_info", "get_network_traffic", "is_64"]
1009
1010 # from header /usr/include/bits/ioctls.h
1011
1012=== modified file 'landscape/lib/persist.py'
1013--- landscape/lib/persist.py 2013-11-21 08:32:12 +0000
1014+++ landscape/lib/persist.py 2017-03-10 15:29:37 +0000
1015@@ -590,7 +590,7 @@
1016 class PickleBackend(Backend):
1017
1018 def __init__(self):
1019- import cPickle
1020+ from landscape.compat import cPickle
1021 self._pickle = cPickle
1022
1023 def new(self):
1024@@ -614,21 +614,21 @@
1025 class BPickleBackend(Backend):
1026
1027 def __init__(self):
1028- from landscape.lib import bpickle
1029+ from landscape.compat import bpickle
1030 self._bpickle = bpickle
1031
1032 def new(self):
1033 return {}
1034
1035 def load(self, filepath):
1036- file = open(filepath)
1037+ file = open(filepath, "r")
1038 try:
1039 return self._bpickle.loads(file.read())
1040 finally:
1041 file.close()
1042
1043 def save(self, filepath, map):
1044- file = open(filepath, "w")
1045+ file = open(filepath, "wb")
1046 try:
1047 file.write(self._bpickle.dumps(map))
1048 finally:
1049
1050=== modified file 'landscape/lib/scriptcontent.py'
1051--- landscape/lib/scriptcontent.py 2009-08-23 07:05:22 +0000
1052+++ landscape/lib/scriptcontent.py 2017-03-10 15:29:37 +0000
1053@@ -1,11 +1,11 @@
1054 from landscape.lib.hashlib import md5
1055
1056+
1057 def build_script(interpreter, code):
1058 """
1059 Concatenates a interpreter and script into an executable script.
1060 """
1061- return "#!%s\n%s" % ((interpreter or u"").encode("utf-8"),
1062- (code or u"").encode("utf-8"))
1063+ return u"#!{}\n{}".format(interpreter or u"", code or u"")
1064
1065
1066 def generate_script_hash(script):
1067
1068=== modified file 'landscape/lib/sequenceranges.py'
1069--- landscape/lib/sequenceranges.py 2013-09-12 13:14:20 +0000
1070+++ landscape/lib/sequenceranges.py 2017-03-10 15:29:37 +0000
1071@@ -1,3 +1,4 @@
1072+from twisted.python.compat import xrange
1073
1074
1075 class SequenceError(Exception):
1076
1077=== modified file 'landscape/lib/sysstats.py'
1078--- landscape/lib/sysstats.py 2012-12-27 20:10:05 +0000
1079+++ landscape/lib/sysstats.py 2017-03-10 15:29:37 +0000
1080@@ -57,7 +57,8 @@
1081 def get_logged_in_users():
1082 result = getProcessOutputAndValue("who", ["-q"], env=os.environ)
1083
1084- def parse_output((stdout_data, stderr_data, status)):
1085+ def parse_output(args):
1086+ stdout_data, stderr_data, status = args
1087 if status != 0:
1088 raise CommandError(stderr_data)
1089 first_line = stdout_data.split("\n", 1)[0]
1090
1091=== modified file 'landscape/lib/tests/test_amp.py'
1092--- landscape/lib/tests/test_amp.py 2016-05-25 19:52:32 +0000
1093+++ landscape/lib/tests/test_amp.py 2017-03-10 15:29:37 +0000
1094@@ -10,6 +10,9 @@
1095 MethodCallSender)
1096 from landscape.tests.helpers import LandscapeTest
1097
1098+from unittest import skipIf
1099+from twisted.python.compat import _PY3
1100+
1101
1102 class FakeTransport(object):
1103 """Accumulate written data into a list."""
1104@@ -619,6 +622,7 @@
1105 self.client.stopTrying()
1106 connector.disconnect()
1107
1108+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
1109 @inlineCallbacks
1110 def test_retry(self):
1111 """
1112@@ -640,6 +644,7 @@
1113 self.client.stopTrying()
1114 connector.disconnect()
1115
1116+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
1117 @inlineCallbacks
1118 def test_retry_with_method_call_error(self):
1119 """
1120@@ -661,6 +666,7 @@
1121 self.client.stopTrying()
1122 connector.disconnect()
1123
1124+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
1125 @inlineCallbacks
1126 def test_wb_retry_with_while_still_disconnected(self):
1127 """
1128@@ -704,6 +710,7 @@
1129 self.client.stopTrying()
1130 connector.disconnect()
1131
1132+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
1133 @inlineCallbacks
1134 def test_retry_with_many_method_calls(self):
1135 """
1136
1137=== modified file 'landscape/lib/tests/test_bootstrap.py'
1138--- landscape/lib/tests/test_bootstrap.py 2016-06-15 17:49:11 +0000
1139+++ landscape/lib/tests/test_bootstrap.py 2017-03-10 15:29:37 +0000
1140@@ -52,10 +52,10 @@
1141
1142 @patch("os.chmod")
1143 def test_mode(self, chmod):
1144- file = self.bootstrap_class(self.path, mode=0644)
1145+ file = self.bootstrap_class(self.path, mode=0o644)
1146 file.bootstrap(my_var="my_var_value")
1147
1148- chmod.assert_called_with(self.real_path, 0644)
1149+ chmod.assert_called_with(self.real_path, 0o644)
1150
1151 @patch("os.chmod")
1152 @patch("os.chown")
1153@@ -71,25 +71,25 @@
1154 getgrnam.return_value = Mock()
1155 getgrnam.return_value.gr_gid = 5678
1156
1157- file = self.bootstrap_class(self.path, "username", "group", 0644)
1158+ file = self.bootstrap_class(self.path, "username", "group", 0o644)
1159 file.bootstrap(my_var="my_var_value")
1160
1161 getuid.assert_called_with()
1162 getpwnam.assert_called_with("username")
1163 getgrnam.assert_called_with("group")
1164 chown.assert_called_with(self.real_path, 1234, 5678)
1165- chmod.assert_called_with(self.real_path, 0644)
1166+ chmod.assert_called_with(self.real_path, 0o644)
1167
1168 @patch("os.chmod")
1169 @patch("os.getuid")
1170 def test_all_details_with_non_root(self, getuid, chmod):
1171 getuid.return_value = 1000
1172
1173- file = self.bootstrap_class(self.path, "username", "group", 0644)
1174+ file = self.bootstrap_class(self.path, "username", "group", 0o644)
1175 file.bootstrap(my_var="my_var_value")
1176
1177 getuid.assert_called_with()
1178- chmod.assert_called_with(self.real_path, 0644)
1179+ chmod.assert_called_with(self.real_path, 0o644)
1180
1181
1182 class BootstrapCreationTest(BootstrapPathTest):
1183
1184=== modified file 'landscape/lib/tests/test_bpickle.py'
1185--- landscape/lib/tests/test_bpickle.py 2011-07-05 05:09:11 +0000
1186+++ landscape/lib/tests/test_bpickle.py 2017-03-10 15:29:37 +0000
1187@@ -1,6 +1,6 @@
1188 import unittest
1189
1190-from landscape.lib import bpickle
1191+from landscape.compat import bpickle
1192
1193
1194 class BPickleTest(unittest.TestCase):
1195
1196=== modified file 'landscape/lib/tests/test_disk.py'
1197--- landscape/lib/tests/test_disk.py 2016-06-15 18:21:36 +0000
1198+++ landscape/lib/tests/test_disk.py 2017-03-10 15:29:37 +0000
1199@@ -42,7 +42,8 @@
1200 f.write(content)
1201 f.close()
1202 for point in points:
1203- self.stat_results[point] = (4096, 0, 1000, 500, 0, 0, 0, 0, 0)
1204+ self.stat_results[point] = os.statvfs_result(
1205+ (4096, 0, 1000, 500, 0, 0, 0, 0, 0, 0))
1206
1207 def test_get_filesystem_for_path(self):
1208 self.set_mount_points(["/"])
1209@@ -51,7 +52,8 @@
1210
1211 def test_get_filesystem_subpath(self):
1212 self.set_mount_points(["/"])
1213- self.stat_results["/"] = (4096, 0, 1000, 500, 0, 0, 0, 0, 0)
1214+ self.stat_results["/"] = os.statvfs_result(
1215+ (4096, 0, 1000, 500, 0, 0, 0, 0, 0, 0))
1216 info = get_filesystem_for_path("/home", self.mount_file, self.statvfs)
1217 self.assertEqual(info["mount-point"], "/")
1218
1219@@ -99,7 +101,8 @@
1220 f.write(content)
1221 f.close()
1222
1223- self.stat_results["/home"] = (4096, 0, 1000, 500, 0, 0, 0, 0, 0)
1224+ self.stat_results["/home"] = os.statvfs_result(
1225+ (4096, 0, 1000, 500, 0, 0, 0, 0, 0, 0))
1226
1227 result = [x for x in get_mount_info(self.mount_file, self.statvfs)]
1228 expected = {"device": "/dev/sda1", "mount-point": "/home",
1229
1230=== modified file 'landscape/lib/tests/test_fetch.py'
1231--- landscape/lib/tests/test_fetch.py 2016-08-04 16:13:11 +0000
1232+++ landscape/lib/tests/test_fetch.py 2017-03-10 15:29:37 +0000
1233@@ -4,6 +4,7 @@
1234 import pycurl
1235
1236 from twisted.internet.defer import FirstError
1237+from twisted.python.compat import itervalues, unicode
1238
1239 from landscape.lib.fetch import (
1240 fetch, fetch_async, fetch_many_async, fetch_to_files,
1241@@ -214,7 +215,7 @@
1242 curl = CurlStub("result", {pycurl.HTTP_CODE: 404})
1243 try:
1244 fetch("http://example.com", curl=curl)
1245- except HTTPCodeError, error:
1246+ except HTTPCodeError as error:
1247 self.assertEqual(error.http_code, 404)
1248 self.assertEqual(error.body, "result")
1249 else:
1250@@ -232,7 +233,7 @@
1251 curl = CurlStub(error=pycurl.error(60, "pycurl error"))
1252 try:
1253 fetch("http://example.com", curl=curl)
1254- except PyCurlError, error:
1255+ except PyCurlError as error:
1256 self.assertEqual(error.error_code, 60)
1257 self.assertEqual(error.message, "pycurl error")
1258 else:
1259@@ -418,7 +419,7 @@
1260 result = fetch_to_files(url_results.keys(), directory, curl=curl)
1261
1262 def check_files(ignored):
1263- for result in url_results.itervalues():
1264+ for result in itervalues(url_results):
1265 fd = open(os.path.join(directory, result))
1266 self.assertEqual(fd.read(), result)
1267 fd.close()
1268
1269=== modified file 'landscape/lib/tests/test_gpg.py'
1270--- landscape/lib/tests/test_gpg.py 2016-05-23 13:44:24 +0000
1271+++ landscape/lib/tests/test_gpg.py 2017-03-10 15:29:37 +0000
1272@@ -19,7 +19,7 @@
1273 gpg = self.makeFile("#!/bin/sh\n"
1274 "touch $3/trustdb.gpg\n"
1275 "echo -n $@ > %s\n" % gpg_options)
1276- os.chmod(gpg, 0755)
1277+ os.chmod(gpg, 0o755)
1278 gpg_home = self.makeDir()
1279 deferred = Deferred()
1280
1281@@ -48,7 +48,7 @@
1282 provided signature is not valid.
1283 """
1284 gpg = self.makeFile("#!/bin/sh\necho out; echo err >&2; exit 1\n")
1285- os.chmod(gpg, 0755)
1286+ os.chmod(gpg, 0o755)
1287 gpg_home = self.makeDir()
1288 deferred = Deferred()
1289
1290
1291=== modified file 'landscape/lib/tests/test_network.py'
1292--- landscape/lib/tests/test_network.py 2016-06-15 17:38:24 +0000
1293+++ landscape/lib/tests/test_network.py 2017-03-10 15:29:37 +0000
1294@@ -2,14 +2,13 @@
1295 import socket
1296
1297 from mock import patch, ANY
1298-
1299-from cStringIO import StringIO
1300 from subprocess import Popen, PIPE
1301+
1302 from landscape.tests.helpers import LandscapeTest
1303-
1304 from landscape.lib.network import (
1305 get_network_traffic, get_active_device_info, get_active_interfaces,
1306 get_fqdn, get_network_interface_speed)
1307+from landscape.compat import StringIO
1308
1309
1310 class NetworkInfoTest(LandscapeTest):
1311
1312=== modified file 'landscape/lib/tests/test_process.py'
1313--- landscape/lib/tests/test_process.py 2016-06-15 17:28:14 +0000
1314+++ landscape/lib/tests/test_process.py 2017-03-10 15:29:37 +0000
1315@@ -42,7 +42,8 @@
1316
1317 @mock.patch("landscape.lib.process.detect_jiffies", return_value=1)
1318 @mock.patch("os.listdir")
1319- def test_missing_process_race(self, list_dir_mock, jiffies_mock):
1320+ @mock.patch("landscape.monitor.computeruptime.get_uptime")
1321+ def test_missing_process_race(self, get_uptime_mock, list_dir_mock, jiffies_mock):
1322 """
1323 We use os.listdir("/proc") to get the list of active processes, if a
1324 process ends before we attempt to read the process' information, then
1325@@ -68,9 +69,9 @@
1326 self.closed = True
1327
1328 list_dir_mock.return_value = ["12345"]
1329+ get_uptime_mock.return_value = 1.0
1330 fakefile1 = FakeFile("test-binary")
1331 fakefile2 = FakeFile(None)
1332-
1333 with mock.patch("__builtin__.open", mock.mock_open()) as open_mock:
1334 # This means "return fakefile1, then fakefile2"
1335 open_mock.side_effect = [fakefile1, fakefile2]
1336
1337=== modified file 'landscape/lib/tests/test_sysstats.py'
1338--- landscape/lib/tests/test_sysstats.py 2012-12-27 20:10:05 +0000
1339+++ landscape/lib/tests/test_sysstats.py 2017-03-10 15:29:37 +0000
1340@@ -79,7 +79,7 @@
1341 who.write("echo '# users=%d'\n" % len(users.split()))
1342 who.close()
1343
1344- os.chmod(self.who_path, 0770)
1345+ os.chmod(self.who_path, 0o770)
1346
1347
1348 class LoggedInUsersTest(FakeWhoQTest):
1349
1350=== modified file 'landscape/lib/tests/test_twisted_util.py'
1351--- landscape/lib/tests/test_twisted_util.py 2011-08-04 16:05:10 +0000
1352+++ landscape/lib/tests/test_twisted_util.py 2017-03-10 15:29:37 +0000
1353@@ -1,8 +1,11 @@
1354 import os
1355
1356+from twisted.python.compat import _PY3
1357+from unittest import skipIf
1358+
1359+from landscape.lib.fs import create_file
1360+from landscape.lib.twisted_util import spawn_process
1361 from landscape.tests.helpers import LandscapeTest
1362-from landscape.lib.twisted_util import spawn_process
1363-from landscape.lib.fs import create_file
1364
1365
1366 class SpawnProcessTest(LandscapeTest):
1367@@ -10,7 +13,7 @@
1368 def setUp(self):
1369 super(SpawnProcessTest, self).setUp()
1370 self.command = self.makeFile("#!/bin/sh\necho -n $@")
1371- os.chmod(self.command, 0755)
1372+ os.chmod(self.command, 0o755)
1373
1374 def test_spawn_process_return_value(self):
1375 """
1376@@ -18,7 +21,8 @@
1377 """
1378 create_file(self.command, "#!/bin/sh\nexit 2")
1379
1380- def callback((out, err, code)):
1381+ def callback(args):
1382+ out, err, code = args
1383 self.assertEqual(out, "")
1384 self.assertEqual(err, "")
1385 self.assertEqual(code, 2)
1386@@ -31,7 +35,8 @@
1387 """
1388 The process returns the expected standard output.
1389 """
1390- def callback((out, err, code)):
1391+ def callback(args):
1392+ out, err, code = args
1393 self.assertEqual(out, "a b")
1394 self.assertEqual(err, "")
1395 self.assertEqual(code, 0)
1396@@ -46,7 +51,8 @@
1397 """
1398 create_file(self.command, "#!/bin/sh\necho -n $@ >&2")
1399
1400- def callback((out, err, code)):
1401+ def callback(args):
1402+ out, err, code = args
1403 self.assertEqual(out, "")
1404 self.assertEqual(err, "a b")
1405 self.assertEqual(code, 0)
1406@@ -68,7 +74,8 @@
1407 def line_received(line):
1408 lines.append(line)
1409
1410- def callback((out, err, code)):
1411+ def callback(args):
1412+ out, err, code = args
1413 self.assertEqual(expected, lines)
1414
1415 result = spawn_process(self.command, args=(param,),
1416@@ -88,7 +95,8 @@
1417 def line_received(line):
1418 lines.append(line)
1419
1420- def callback((out, err, code)):
1421+ def callback(args):
1422+ out, err, code = args
1423 self.assertEqual(expected, lines)
1424
1425 result = spawn_process(self.command, args=(param,),
1426@@ -109,7 +117,8 @@
1427 def line_received(line):
1428 lines.append(line)
1429
1430- def callback((out, err, code)):
1431+ def callback(args):
1432+ out, err, code = args
1433 self.assertEqual(expected, lines)
1434
1435 result = spawn_process(self.command, args=(param,),
1436@@ -117,13 +126,15 @@
1437 result.addCallback(callback)
1438 return result
1439
1440+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
1441 def test_spawn_process_with_stdin(self):
1442 """
1443 Optionally C{spawn_process} accepts a C{stdin} argument.
1444 """
1445 create_file(self.command, "#!/bin/sh\n/bin/cat")
1446
1447- def callback((out, err, code)):
1448+ def callback(args):
1449+ out, err, code = args
1450 self.assertEqual("hello", out)
1451
1452 result = spawn_process(self.command, stdin="hello")
1453
1454=== modified file 'landscape/lib/twisted_util.py'
1455--- landscape/lib/twisted_util.py 2013-07-19 09:12:16 +0000
1456+++ landscape/lib/twisted_util.py 2017-03-10 15:29:37 +0000
1457@@ -2,8 +2,9 @@
1458 from twisted.internet.protocol import ProcessProtocol
1459 from twisted.internet.process import Process, ProcessReader
1460 from twisted.internet import reactor
1461+from twisted.python.compat import itervalues
1462
1463-import cStringIO
1464+from landscape.compat import StringIO
1465
1466
1467 def gather_results(deferreds, consume_errors=False):
1468@@ -19,8 +20,8 @@
1469
1470 def __init__(self, deferred, stdin=None, line_received=None):
1471 self.deferred = deferred
1472- self.outBuf = cStringIO.StringIO()
1473- self.errBuf = cStringIO.StringIO()
1474+ self.outBuf = StringIO()
1475+ self.errBuf = StringIO()
1476 self.errReceived = self.errBuf.write
1477 self.stdin = stdin
1478 self.line_received = line_received
1479@@ -103,7 +104,7 @@
1480 be used in place of this workaround.
1481 """
1482 if process.pipes and not process.pid:
1483- for pipe in process.pipes.itervalues():
1484+ for pipe in itervalues(process.pipes):
1485 if isinstance(pipe, ProcessReader):
1486 # Read whatever is left
1487 pipe.doRead()
1488
1489=== modified file 'landscape/manager/aptsources.py'
1490--- landscape/manager/aptsources.py 2017-01-11 15:44:41 +0000
1491+++ landscape/manager/aptsources.py 2017-03-10 15:29:37 +0000
1492@@ -86,9 +86,8 @@
1493 for key in message["gpg-keys"]:
1494 fd, path = tempfile.mkstemp()
1495 os.close(fd)
1496- key_file = file(path, "w")
1497- key_file.write(key)
1498- key_file.close()
1499+ with open(path, "w") as key_file:
1500+ key_file.write(key)
1501 deferred.addCallback(
1502 lambda ignore, path=path:
1503 self._run_process("/usr/bin/apt-key", ["add", path]))
1504@@ -131,7 +130,7 @@
1505 "landscape-%s.list" % source["name"])
1506 with open(filename, "w") as sources_file:
1507 sources_file.write(source["content"])
1508- os.chmod(filename, 0644)
1509+ os.chmod(filename, 0o644)
1510 return self._run_reporter().addCallback(lambda ignored: None)
1511
1512 def _run_reporter(self):
1513
1514=== modified file 'landscape/manager/customgraph.py'
1515--- landscape/manager/customgraph.py 2013-05-21 14:18:29 +0000
1516+++ landscape/manager/customgraph.py 2017-03-10 15:29:37 +0000
1517@@ -3,7 +3,9 @@
1518 import logging
1519
1520 from twisted.internet.defer import fail, DeferredList, succeed
1521+from twisted.python.compat import iteritems
1522
1523+from landscape.compat import coerce_unicode
1524 from landscape.lib.scriptcontent import generate_script_hash
1525 from landscape.accumulate import Accumulator
1526 from landscape.manager.plugin import ManagerPlugin
1527@@ -129,7 +131,8 @@
1528 logging.error(u"Attempt to add graph with unknown user %s" %
1529 user)
1530 else:
1531- script_file = file(filename, "w")
1532+ script_file = open(filename, "w")
1533+ # file is closed in write_script_file
1534 self.write_script_file(
1535 script_file, filename, shell, code, uid, gid)
1536 if graph_id in self._data:
1537@@ -158,7 +161,7 @@
1538 message = {"type": self.message_type, "data": self._data}
1539
1540 new_data = {}
1541- for graph_id, item in self._data.iteritems():
1542+ for graph_id, item in iteritems(self._data):
1543 script_hash = item["script-hash"]
1544 new_data[graph_id] = {
1545 "values": [], "error": u"", "script-hash": script_hash}
1546@@ -186,7 +189,7 @@
1547 if graph_id not in self._data:
1548 return
1549 if failure.check(ProcessFailedError):
1550- failure_value = failure.value.data.decode("utf-8")
1551+ failure_value = coerce_unicode(failure.value.data, "utf-8")
1552 if failure.value.exit_code:
1553 failure_value = ("%s (process exited with code %d)" %
1554 (failure_value, failure.value.exit_code))
1555@@ -199,9 +202,8 @@
1556 failure.value)
1557
1558 def _get_script_hash(self, filename):
1559- file_object = file(filename)
1560- script_content = file_object.read()
1561- file_object.close()
1562+ with open(filename) as file_object:
1563+ script_content = file_object.read()
1564 return generate_script_hash(script_content)
1565
1566 def run(self):
1567@@ -235,7 +237,7 @@
1568 self._data[graph_id]["script-hash"] = script_hash
1569 try:
1570 uid, gid, path = get_user_info(user)
1571- except UnknownUserError, e:
1572+ except UnknownUserError as e:
1573 d = fail(e)
1574 d.addErrback(self._handle_error, graph_id)
1575 deferred_list.append(d)
1576
1577=== modified file 'landscape/manager/keystonetoken.py'
1578--- landscape/manager/keystonetoken.py 2013-07-11 20:27:10 +0000
1579+++ landscape/manager/keystonetoken.py 2017-03-10 15:29:37 +0000
1580@@ -1,7 +1,7 @@
1581 import os
1582 import logging
1583-from ConfigParser import ConfigParser, NoOptionError
1584
1585+from landscape.compat import ConfigParser, NoOptionError
1586 from landscape.monitor.plugin import DataWatcher
1587 from landscape.lib.persist import Persist
1588
1589
1590=== modified file 'landscape/manager/plugin.py'
1591--- landscape/manager/plugin.py 2013-05-08 22:18:47 +0000
1592+++ landscape/manager/plugin.py 2017-03-10 15:29:37 +0000
1593@@ -43,7 +43,8 @@
1594 log_failure(failure, msg=msg)
1595 return FAILED, text
1596
1597- def send((status, text)):
1598+ def send(args):
1599+ status, text = args
1600 result = {"type": "operation-result",
1601 "status": status,
1602 "operation-id": message["operation-id"]}
1603
1604=== modified file 'landscape/manager/scriptexecution.py'
1605--- landscape/manager/scriptexecution.py 2016-10-06 11:02:53 +0000
1606+++ landscape/manager/scriptexecution.py 2017-03-10 15:29:37 +0000
1607@@ -12,13 +12,14 @@
1608 from twisted.internet.defer import (
1609 Deferred, fail, inlineCallbacks, returnValue, succeed)
1610 from twisted.internet.error import ProcessDone
1611+from twisted.python.compat import unicode, _PY3
1612
1613 from landscape import VERSION
1614 from landscape.constants import UBUNTU_PATH
1615-from landscape.lib.scriptcontent import build_script
1616+from landscape.lib.encoding import encode_if_needed
1617 from landscape.lib.fetch import fetch_async, HTTPCodeError
1618 from landscape.lib.persist import Persist
1619-from landscape.lib.encoding import encode_if_needed
1620+from landscape.lib.scriptcontent import build_script
1621 from landscape.manager.plugin import ManagerPlugin, SUCCEEDED, FAILED
1622
1623
1624@@ -111,10 +112,14 @@
1625 # It would be nice to use fchown(2) and fchmod(2), but they're not
1626 # available in python and using it with ctypes is pretty tedious, not
1627 # to mention we can't get errno.
1628- os.chmod(filename, 0700)
1629+ os.chmod(filename, 0o700)
1630 if uid is not None:
1631 os.chown(filename, uid, gid)
1632- script_file.write(build_script(shell, code))
1633+
1634+ script = build_script(shell, code)
1635+ if not _PY3:
1636+ script = script.encode('utf-8')
1637+ script_file.write(script)
1638 script_file.close()
1639
1640 def _run_script(self, filename, uid, gid, path, env, time_limit):
1641@@ -178,7 +183,7 @@
1642 d.addCallback(self._respond_success, opid)
1643 d.addErrback(self._respond_failure, opid)
1644 return d
1645- except Exception, e:
1646+ except Exception as e:
1647 self._respond(FAILED, self._format_exception(e), opid)
1648 raise
1649
1650@@ -223,13 +228,12 @@
1651 cainfo=self.registry.config.ssl_public_key,
1652 headers=headers)
1653 full_filename = os.path.join(attachment_dir, filename)
1654- attachment = file(full_filename, "wb")
1655- os.chmod(full_filename, 0600)
1656- if uid is not None:
1657- os.chown(full_filename, uid, gid)
1658- attachment.write(data)
1659- attachment.close()
1660- os.chmod(attachment_dir, 0700)
1661+ with open(full_filename, "wb") as attachment:
1662+ os.chmod(full_filename, 0o600)
1663+ if uid is not None:
1664+ os.chown(full_filename, uid, gid)
1665+ attachment.write(data)
1666+ os.chmod(attachment_dir, 0o700)
1667 if uid is not None:
1668 os.chown(attachment_dir, uid, gid)
1669 returnValue(attachment_dir)
1670@@ -270,7 +274,7 @@
1671 env = {"PATH": UBUNTU_PATH, "USER": user or "", "HOME": path or ""}
1672 if server_supplied_env:
1673 env.update(server_supplied_env)
1674- old_umask = os.umask(0022)
1675+ old_umask = os.umask(0o022)
1676
1677 if attachments:
1678 persist = Persist(
1679
1680=== modified file 'landscape/manager/tests/test_aptsources.py'
1681--- landscape/manager/tests/test_aptsources.py 2017-01-13 15:49:40 +0000
1682+++ landscape/manager/tests/test_aptsources.py 2017-03-10 15:29:37 +0000
1683@@ -57,7 +57,7 @@
1684 "gpg-keys": [],
1685 "operation-id": 1})
1686
1687- with file(self.sourceslist.SOURCES_LIST) as sources:
1688+ with open(self.sourceslist.SOURCES_LIST) as sources:
1689 self.assertEqual(
1690 "# Landscape manages repositories for this computer\n"
1691 "# Original content of sources.list can be found in "
1692@@ -79,7 +79,7 @@
1693
1694 saved_sources_path = "{}.save".format(self.sourceslist.SOURCES_LIST)
1695 self.assertTrue(os.path.exists(saved_sources_path))
1696- with file(saved_sources_path) as saved_sources:
1697+ with open(saved_sources_path) as saved_sources:
1698 self.assertEqual("oki\n\ndoki\n#comment\n # other comment\n",
1699 saved_sources.read())
1700
1701@@ -102,7 +102,7 @@
1702 "operation-id": 1})
1703
1704 self.assertTrue(os.path.exists(saved_sources_path))
1705- with file(saved_sources_path) as saved_sources:
1706+ with open(saved_sources_path) as saved_sources:
1707 self.assertEqual("original content\n", saved_sources.read())
1708
1709 def test_restore_sources_list(self):
1710@@ -123,7 +123,7 @@
1711 "gpg-keys": [],
1712 "operation-id": 1})
1713
1714- with file(self.sourceslist.SOURCES_LIST) as sources:
1715+ with open(self.sourceslist.SOURCES_LIST) as sources:
1716 self.assertEqual("original content\n", sources.read())
1717
1718 def test_sources_list_permissions(self):
1719@@ -236,12 +236,16 @@
1720 dev_file = os.path.join(self.sourceslist.SOURCES_LIST_D,
1721 "landscape-dev.list")
1722 self.assertTrue(os.path.exists(dev_file))
1723- self.assertEqual("oki\n", file(dev_file).read())
1724+ with open(dev_file) as file:
1725+ result = file.read()
1726+ self.assertEqual("oki\n", result)
1727
1728 lucid_file = os.path.join(self.sourceslist.SOURCES_LIST_D,
1729 "landscape-lucid.list")
1730 self.assertTrue(os.path.exists(lucid_file))
1731- self.assertEqual("doki\n", file(lucid_file).read())
1732+ with open(lucid_file) as file:
1733+ result = file.read()
1734+ self.assertEqual("doki\n", result)
1735
1736 def test_import_gpg_keys(self):
1737 """
1738@@ -254,7 +258,9 @@
1739 self.assertEqual("/usr/bin/apt-key", command)
1740 self.assertEqual("add", args[0])
1741 filename = args[1]
1742- self.assertEqual("Some key content", file(filename).read())
1743+ with open(filename) as file:
1744+ result = file.read()
1745+ self.assertEqual("Some key content", result)
1746 deferred.callback(("ok", "", 0))
1747 return deferred
1748
1749@@ -376,17 +382,17 @@
1750
1751 self.sourceslist._run_process = _run_process
1752
1753- sources = file(self.sourceslist.SOURCES_LIST, "w")
1754- sources.write("oki\n\ndoki\n#comment\n")
1755- sources.close()
1756+ with open(self.sourceslist.SOURCES_LIST, "w") as sources:
1757+ sources.write("oki\n\ndoki\n#comment\n")
1758
1759 self.manager.dispatch_message(
1760 {"type": "apt-sources-replace", "sources": [], "gpg-keys": ["key"],
1761 "operation-id": 1})
1762
1763- self.assertEqual(
1764- "oki\n\ndoki\n#comment\n",
1765- file(self.sourceslist.SOURCES_LIST).read())
1766+ with open(self.sourceslist.SOURCES_LIST) as sources_list:
1767+ result = sources_list.read()
1768+
1769+ self.assertEqual("oki\n\ndoki\n#comment\n", result)
1770
1771 return deferred
1772
1773
1774=== modified file 'landscape/manager/tests/test_customgraph.py'
1775--- landscape/manager/tests/test_customgraph.py 2016-06-15 23:35:19 +0000
1776+++ landscape/manager/tests/test_customgraph.py 2017-03-10 15:29:37 +0000
1777@@ -105,7 +105,7 @@
1778 "bar")])
1779
1780 mock_chown.assert_called_with(mock.ANY, 1234, 5678)
1781- mock_chmod.assert_called_with(mock.ANY, 0700)
1782+ mock_chmod.assert_called_with(mock.ANY, 0o700)
1783 mock_getpwnam.assert_called_with("bar")
1784
1785 def test_remove_unknown_graph(self):
1786@@ -114,10 +114,7 @@
1787 "graph-id": 123})
1788
1789 def test_remove_graph(self):
1790- filename = self.makeFile()
1791- tempfile = file(filename, "w")
1792- tempfile.write("foo")
1793- tempfile.close()
1794+ filename = self.makeFile(content='foo')
1795 self.store.add_graph(123, filename, u"user")
1796 self.manager.dispatch_message(
1797 {"type": "custom-graph-remove",
1798@@ -125,11 +122,8 @@
1799 self.assertFalse(os.path.exists(filename))
1800
1801 def test_run(self):
1802- filename = self.makeFile()
1803- tempfile = file(filename, "w")
1804- tempfile.write("#!/bin/sh\necho 1")
1805- tempfile.close()
1806- os.chmod(filename, 0777)
1807+ filename = self.makeFile(content="#!/bin/sh\necho 1")
1808+ os.chmod(filename, 0o777)
1809 self.store.add_graph(123, filename, None)
1810
1811 def check(ignore):
1812@@ -144,18 +138,12 @@
1813 return self.graph_manager.run().addCallback(check)
1814
1815 def test_run_multiple(self):
1816- filename = self.makeFile()
1817- tempfile = file(filename, "w")
1818- tempfile.write("#!/bin/sh\necho 1")
1819- tempfile.close()
1820- os.chmod(filename, 0777)
1821+ filename = self.makeFile(content="#!/bin/sh\necho 1")
1822+ os.chmod(filename, 0o777)
1823 self.store.add_graph(123, filename, None)
1824
1825- filename = self.makeFile()
1826- tempfile = file(filename, "w")
1827- tempfile.write("#!/bin/sh\necho 2")
1828- tempfile.close()
1829- os.chmod(filename, 0777)
1830+ filename = self.makeFile(content="#!/bin/sh\necho 2")
1831+ os.chmod(filename, 0o777)
1832 self.store.add_graph(124, filename, None)
1833
1834 def check(ignore):
1835@@ -176,11 +164,8 @@
1836 return self.graph_manager.run().addCallback(check)
1837
1838 def test_run_with_nonzero_exit_code(self):
1839- filename = self.makeFile()
1840- tempfile = file(filename, "w")
1841- tempfile.write("#!/bin/sh\nexit 1")
1842- tempfile.close()
1843- os.chmod(filename, 0777)
1844+ filename = self.makeFile(content="#!/bin/sh\nexit 1")
1845+ os.chmod(filename, 0o777)
1846 self.store.add_graph(123, filename, None)
1847
1848 def check(ignore):
1849
1850=== modified file 'landscape/manager/tests/test_packagemanager.py'
1851--- landscape/manager/tests/test_packagemanager.py 2016-06-16 16:59:17 +0000
1852+++ landscape/manager/tests/test_packagemanager.py 2017-03-10 15:29:37 +0000
1853@@ -186,7 +186,7 @@
1854 when passed the L{PackageChanger} class as argument.
1855 """
1856 command = self.makeFile("#!/bin/sh\necho 'I am the changer!' >&2\n")
1857- os.chmod(command, 0755)
1858+ os.chmod(command, 0o755)
1859 find_command_mock.return_value = command
1860
1861 self.package_store.add_task("changer", "Do something!")
1862@@ -210,7 +210,7 @@
1863 when passed the L{ReleaseUpgrader} class as argument.
1864 """
1865 command = self.makeFile("#!/bin/sh\necho 'I am the upgrader!' >&2\n")
1866- os.chmod(command, 0755)
1867+ os.chmod(command, 0o755)
1868 find_command_mock.return_value = command
1869
1870 self.package_store.add_task("release-upgrader", "Do something!")
1871@@ -244,7 +244,7 @@
1872 @mock.patch("landscape.package.changer.find_changer_command")
1873 def test_spawn_handler_copies_environment(self, find_command_mock):
1874 command = self.makeFile("#!/bin/sh\necho VAR: $VAR\n")
1875- os.chmod(command, 0755)
1876+ os.chmod(command, 0o755)
1877 find_command_mock.return_value = command
1878
1879 self.manager.add(self.package_manager)
1880@@ -264,7 +264,7 @@
1881 @mock.patch("landscape.package.changer.find_changer_command")
1882 def test_spawn_handler_passes_quiet_option(self, find_command_mock):
1883 command = self.makeFile("#!/bin/sh\necho OPTIONS: $@\n")
1884- os.chmod(command, 0755)
1885+ os.chmod(command, 0o755)
1886 find_command_mock.return_value = command
1887
1888 self.manager.add(self.package_manager)
1889@@ -281,7 +281,7 @@
1890
1891 def test_spawn_handler_wont_run_without_tasks(self):
1892 command = self.makeFile("#!/bin/sh\necho RUN!\n")
1893- os.chmod(command, 0755)
1894+ os.chmod(command, 0o755)
1895
1896 self.manager.add(self.package_manager)
1897 result = self.package_manager.spawn_handler(PackageChanger)
1898@@ -295,7 +295,7 @@
1899 @mock.patch("landscape.package.changer.find_changer_command")
1900 def test_spawn_handler_doesnt_chdir(self, find_command_mock):
1901 command = self.makeFile("#!/bin/sh\necho RUN\n")
1902- os.chmod(command, 0755)
1903+ os.chmod(command, 0o755)
1904 cwd = os.getcwd()
1905 self.addCleanup(os.chdir, cwd)
1906 dir = self.makeDir()
1907@@ -313,7 +313,7 @@
1908 log = self.logfile.getvalue()
1909 self.assertIn("RUN", log)
1910 # restore permissions to the dir so tearDown can clean it up
1911- os.chmod(dir, 0766)
1912+ os.chmod(dir, 0o766)
1913
1914 return result.addCallback(got_result)
1915
1916
1917=== modified file 'landscape/manager/tests/test_scriptexecution.py'
1918--- landscape/manager/tests/test_scriptexecution.py 2016-10-24 10:59:47 +0000
1919+++ landscape/manager/tests/test_scriptexecution.py 2017-03-10 15:29:37 +0000
1920@@ -6,9 +6,12 @@
1921
1922 import mock
1923
1924+from unittest import skipIf
1925+
1926 from twisted.internet.defer import gatherResults, succeed, fail
1927 from twisted.internet.error import ProcessDone
1928 from twisted.python.failure import Failure
1929+from twisted.python.compat import _PY3
1930
1931 from landscape import VERSION
1932 from landscape.lib.fetch import HTTPCodeError
1933@@ -40,6 +43,7 @@
1934 self.plugin = ScriptExecutionPlugin()
1935 self.manager.add(self.plugin)
1936
1937+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1938 def test_basic_run(self):
1939 """
1940 The plugin returns a Deferred resulting in the output of basic
1941@@ -49,18 +53,21 @@
1942 result.addCallback(self.assertEqual, "hi\n")
1943 return result
1944
1945+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1946 def test_snap_path(self):
1947 """The bin path for snaps is included in the PATH."""
1948 deferred = self.plugin.run_script("/bin/sh", "echo $PATH")
1949 return deferred.addCallback(
1950 lambda result: self.assertIn("/snap/bin", result))
1951
1952+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1953 def test_other_interpreter(self):
1954 """Non-shell interpreters can be specified."""
1955 result = self.plugin.run_script("/usr/bin/python", "print 'hi'")
1956 result.addCallback(self.assertEqual, "hi\n")
1957 return result
1958
1959+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1960 def test_other_interpreter_env(self):
1961 """
1962 Non-shell interpreters don't have their paths set by the shell, so we
1963@@ -77,6 +84,7 @@
1964 result.addCallback(check_environment)
1965 return result
1966
1967+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1968 def test_server_supplied_env(self):
1969 """
1970 Server-supplied environment variables are merged with default
1971@@ -98,6 +106,7 @@
1972 result.addCallback(check_environment)
1973 return result
1974
1975+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1976 def test_server_supplied_env_overrides_client(self):
1977 """
1978 Server-supplied environment variables override client default
1979@@ -118,6 +127,7 @@
1980 result.addCallback(check_environment)
1981 return result
1982
1983+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1984 def test_concurrent(self):
1985 """
1986 Scripts run with the ScriptExecutionPlugin plugin are run concurrently.
1987@@ -134,6 +144,7 @@
1988 d2.addCallback(self.assertEqual, "")
1989 return gatherResults([d1, d2])
1990
1991+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
1992 def test_accented_run_in_code(self):
1993 """
1994 Scripts can contain accented data both in the code and in the
1995@@ -146,6 +157,7 @@
1996 self.assertEqual, "%s\n" % (accented_content.encode("utf-8"),))
1997 return result
1998
1999+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
2000 def test_accented_run_in_interpreter(self):
2001 """
2002 Scripts can also contain accents in the interpreter.
2003@@ -161,9 +173,10 @@
2004 result.addCallback(check)
2005 return result
2006
2007+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
2008 def test_set_umask_appropriately(self):
2009 """
2010- We should be setting the umask to 0022 before executing a script, and
2011+ We should be setting the umask to 0o022 before executing a script, and
2012 restoring it to the previous value when finishing.
2013 """
2014 # Get original umask.
2015@@ -183,6 +196,7 @@
2016 result.addCallback(check)
2017 return result.addCallback(lambda _: patch_umask.stop())
2018
2019+ @skipIf(_PY3, "mock does not get cleaned up, poisoning all other tests.")
2020 def test_restore_umask_in_event_of_error(self):
2021 """
2022 We set the umask before executing the script, in the event that there's
2023@@ -298,6 +312,7 @@
2024
2025 return result.addCallback(check).addBoth(cleanup)
2026
2027+ @skipIf(_PY3, 'Takes long with Python3, probably not an unclean Reactor')
2028 def test_self_remove_script(self):
2029 """
2030 If a script removes itself, it doesn't create an error when the script
2031@@ -416,9 +431,9 @@
2032 spawn = factory.spawns[0]
2033 self.assertIn("LANDSCAPE_ATTACHMENTS", spawn[3])
2034 attachment_dir = spawn[3]["LANDSCAPE_ATTACHMENTS"]
2035- self.assertEqual(stat.S_IMODE(os.stat(attachment_dir).st_mode), 0700)
2036+ self.assertEqual(stat.S_IMODE(os.stat(attachment_dir).st_mode), 0o700)
2037 filename = os.path.join(attachment_dir, "file 1")
2038- self.assertEqual(stat.S_IMODE(os.stat(filename).st_mode), 0600)
2039+ self.assertEqual(stat.S_IMODE(os.stat(filename).st_mode), 0o600)
2040
2041 protocol = spawn[0]
2042 protocol.childDataReceived(1, "foobar")
2043
2044=== modified file 'landscape/manager/usermanager.py'
2045--- landscape/manager/usermanager.py 2013-05-07 23:06:56 +0000
2046+++ landscape/manager/usermanager.py 2017-03-10 15:29:37 +0000
2047@@ -62,7 +62,7 @@
2048 if len(parts) > 1:
2049 if parts[1].startswith("!"):
2050 locked_users.append(parts[0].strip())
2051- except IOError, e:
2052+ except IOError as e:
2053 logging.error("Error reading shadow file. %s" % e)
2054 return locked_users
2055
2056
2057=== modified file 'landscape/monitor/activeprocessinfo.py'
2058--- landscape/monitor/activeprocessinfo.py 2013-07-12 09:49:47 +0000
2059+++ landscape/monitor/activeprocessinfo.py 2017-03-10 15:29:37 +0000
2060@@ -1,5 +1,7 @@
2061 import subprocess
2062
2063+from twisted.python.compat import itervalues
2064+
2065 from landscape.diff import diff
2066 from landscape.lib.process import ProcessInformation
2067 from landscape.lib.jiffies import detect_jiffies
2068@@ -67,11 +69,11 @@
2069 processes = self._get_processes()
2070 creates, updates, deletes = diff(self._persist_processes, processes)
2071 if creates:
2072- changes["add-processes"] = list(creates.itervalues())
2073+ changes["add-processes"] = list(itervalues(creates))
2074 if updates:
2075- changes["update-processes"] = list(updates.itervalues())
2076+ changes["update-processes"] = list(itervalues(updates))
2077 if deletes:
2078- changes["kill-processes"] = list(deletes.iterkeys())
2079+ changes["kill-processes"] = list(deletes)
2080
2081 # Update cached values for use on the next run.
2082 self._previous_processes = processes
2083
2084=== modified file 'landscape/monitor/aptpreferences.py'
2085--- landscape/monitor/aptpreferences.py 2013-07-05 12:52:40 +0000
2086+++ landscape/monitor/aptpreferences.py 2017-03-10 15:29:37 +0000
2087@@ -1,5 +1,7 @@
2088 import os
2089
2090+from twisted.python.compat import iteritems, unicode
2091+
2092 from landscape.lib.fs import read_file
2093 from landscape.constants import APT_PREFERENCES_SIZE_LIMIT
2094
2095@@ -46,7 +48,7 @@
2096 return None
2097
2098 item_size_limit = self.size_limit / len(data.keys())
2099- for filename, contents in data.iteritems():
2100+ for filename, contents in iteritems(data):
2101 if len(filename) + len(contents) > item_size_limit:
2102 truncated_contents_size = item_size_limit - len(filename)
2103 data[filename] = data[filename][0:truncated_contents_size]
2104
2105=== modified file 'landscape/monitor/cephusage.py'
2106--- landscape/monitor/cephusage.py 2014-05-20 14:20:21 +0000
2107+++ landscape/monitor/cephusage.py 2017-03-10 15:29:37 +0000
2108@@ -3,6 +3,7 @@
2109 import os
2110
2111 from twisted.internet import threads
2112+from twisted.python.compat import unicode
2113
2114 from landscape.accumulate import Accumulator
2115 from landscape.lib.monitor import CoverageMonitor
2116
2117=== modified file 'landscape/monitor/computeruptime.py'
2118--- landscape/monitor/computeruptime.py 2013-07-05 12:52:40 +0000
2119+++ landscape/monitor/computeruptime.py 2017-03-10 15:29:37 +0000
2120@@ -12,7 +12,8 @@
2121 This parses a file in /proc/uptime format and returns a floating point
2122 version of the first value (the actual uptime).
2123 """
2124- data = file(uptime_file, "r").readline()
2125+ with open(uptime_file, 'r') as ufile:
2126+ data = ufile.readline()
2127 up, idle = data.split()
2128 return float(up)
2129
2130@@ -86,21 +87,22 @@
2131 def get_times(self):
2132 reboot_times = []
2133 shutdown_times = []
2134- reader = LoginInfoReader(file(self._filename))
2135- self._last_boot = self._boots_newer_than
2136- self._last_shutdown = self._shutdowns_newer_than
2137+ with open(self._filename) as login_info_file:
2138+ reader = LoginInfoReader(login_info_file)
2139+ self._last_boot = self._boots_newer_than
2140+ self._last_shutdown = self._shutdowns_newer_than
2141
2142- for info in reader.login_info():
2143- if info.tty_device.startswith("~"):
2144- timestamp = to_timestamp(info.entry_time)
2145- if (info.username == "reboot"
2146- and timestamp > self._last_boot):
2147- reboot_times.append(timestamp)
2148- self._last_boot = timestamp
2149- elif (info.username == "shutdown"
2150- and timestamp > self._last_shutdown):
2151- shutdown_times.append(timestamp)
2152- self._last_shutdown = timestamp
2153+ for info in reader.login_info():
2154+ if info.tty_device.startswith("~"):
2155+ timestamp = to_timestamp(info.entry_time)
2156+ if (info.username == "reboot" and
2157+ timestamp > self._last_boot):
2158+ reboot_times.append(timestamp)
2159+ self._last_boot = timestamp
2160+ elif (info.username == "shutdown" and
2161+ timestamp > self._last_shutdown):
2162+ shutdown_times.append(timestamp)
2163+ self._last_shutdown = timestamp
2164 return reboot_times, shutdown_times
2165
2166 def get_last_boot_time(self):
2167
2168=== modified file 'landscape/monitor/mountinfo.py'
2169--- landscape/monitor/mountinfo.py 2013-07-05 12:52:40 +0000
2170+++ landscape/monitor/mountinfo.py 2017-03-10 15:29:37 +0000
2171@@ -1,9 +1,10 @@
2172+import codecs
2173 import time
2174 import os
2175
2176+from landscape.accumulate import Accumulator
2177 from landscape.lib.disk import get_mount_info, is_device_removable
2178 from landscape.lib.monitor import CoverageMonitor
2179-from landscape.accumulate import Accumulator
2180 from landscape.monitor.plugin import MonitorPlugin
2181
2182
2183@@ -134,7 +135,7 @@
2184 for line in file:
2185 try:
2186 device, mount_point, filesystem, options = line.split()[:4]
2187- mount_point = mount_point.decode("string-escape")
2188+ mount_point = codecs.decode(mount_point, "unicode_escape")
2189 except ValueError:
2190 continue
2191 if "bind" in options.split(","):
2192
2193=== modified file 'landscape/monitor/rebootrequired.py'
2194--- landscape/monitor/rebootrequired.py 2013-07-05 12:52:40 +0000
2195+++ landscape/monitor/rebootrequired.py 2017-03-10 15:29:37 +0000
2196@@ -35,7 +35,7 @@
2197 return []
2198
2199 lines = read_file(self._packages_filename).splitlines()
2200- packages = set(line.strip().decode("utf-8") for line in lines if line)
2201+ packages = set(line.strip() for line in lines if line)
2202 return sorted(packages)
2203
2204 def _create_message(self):
2205
2206=== modified file 'landscape/monitor/tests/test_aptpreferences.py'
2207--- landscape/monitor/tests/test_aptpreferences.py 2016-06-15 21:58:09 +0000
2208+++ landscape/monitor/tests/test_aptpreferences.py 2017-03-10 15:29:37 +0000
2209@@ -1,6 +1,8 @@
2210 import os
2211 import mock
2212
2213+from twisted.python.compat import unicode
2214+
2215 from landscape.monitor.aptpreferences import AptPreferences
2216 from landscape.tests.helpers import LandscapeTest
2217 from landscape.tests.helpers import MonitorHelper
2218
2219=== modified file 'landscape/monitor/tests/test_cephusage.py'
2220--- landscape/monitor/tests/test_cephusage.py 2016-08-17 20:33:47 +0000
2221+++ landscape/monitor/tests/test_cephusage.py 2017-03-10 15:29:37 +0000
2222@@ -141,7 +141,7 @@
2223 reached.
2224 """
2225 interval = 300
2226- stats = {"kb": 10240L, "kb_avail": 8192L, "kb_used": 2048L}
2227+ stats = {"kb": 10240, "kb_avail": 8192, "kb_used": 2048}
2228
2229 plugin = CephUsage(
2230 create_time=self.reactor.time, interval=interval,
2231
2232=== modified file 'landscape/monitor/tests/test_mountinfo.py'
2233--- landscape/monitor/tests/test_mountinfo.py 2017-02-22 10:55:15 +0000
2234+++ landscape/monitor/tests/test_mountinfo.py 2017-03-10 15:29:37 +0000
2235@@ -1,6 +1,10 @@
2236 import mock
2237+import os
2238 import tempfile
2239
2240+from twisted.python.compat import StringType as basestring
2241+from twisted.python.compat import long
2242+
2243 from landscape.monitor.mountinfo import MountInfo
2244 from landscape.tests.helpers import LandscapeTest, mock_counter, MonitorHelper
2245
2246@@ -8,6 +12,11 @@
2247 mb = lambda x: x * 1024 * 1024
2248
2249
2250+def statvfs_result_fixture(path):
2251+ """Fixture for a dummy statvfs_result."""
2252+ return os.statvfs_result((4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0, 0))
2253+
2254+
2255 class MountInfoTest(LandscapeTest):
2256 """Tests for mount-info plugin."""
2257
2258@@ -24,7 +33,8 @@
2259 if "mtab_file" not in kwargs:
2260 kwargs["mtab_file"] = self.makeFile("/dev/hda1 / ext3 rw 0 0\n")
2261 if "statvfs" not in kwargs:
2262- kwargs["statvfs"] = lambda path: (0,) * 1000
2263+ kwargs["statvfs"] = lambda path: os.statvfs_result(
2264+ (0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
2265 plugin = MountInfo(*args, **kwargs)
2266 # To make sure tests are isolated from the real system by default.
2267 plugin.is_device_removable = lambda x: False
2268@@ -65,9 +75,11 @@
2269 """
2270 def statvfs(path):
2271 if path == "/":
2272- return (4096, 0, mb(1000L), mb(100L), 0L, 0L, 0L, 0, 0)
2273+ return os.statvfs_result(
2274+ (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0, 0))
2275 else:
2276- return (4096, 0, mb(10000L), mb(1000L), 0L, 0L, 0L, 0, 0)
2277+ return os.statvfs_result(
2278+ (4096, 0, mb(10000), mb(1000), 0, 0, 0, 0, 0, 0))
2279
2280 filename = self.makeFile("""\
2281 rootfs / rootfs rw 0 0
2282@@ -138,7 +150,8 @@
2283 function which should cause it to queue new messages.
2284 """
2285 def statvfs(path, multiplier=mock_counter(1).next):
2286- return (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0)
2287+ return os.statvfs_result(
2288+ (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0, 0))
2289
2290 plugin = self.get_mount_info(
2291 statvfs=statvfs, create_time=self.reactor.time,
2292@@ -170,8 +183,10 @@
2293 """
2294 def statvfs(path, multiplier=mock_counter(1).next):
2295 if path == "/":
2296- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2297- return (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0)
2298+ return os.statvfs_result(
2299+ (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0, 0))
2300+ return os.statvfs_result(
2301+ (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0, 0))
2302
2303 filename = self.makeFile("""\
2304 /dev/hda1 / ext3 rw 0 0
2305@@ -213,11 +228,8 @@
2306 messages collected bewteen exchange periods should be
2307 delivered in a single message.
2308 """
2309- def statvfs(path):
2310- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2311-
2312 plugin = self.get_mount_info(
2313- statvfs=statvfs, create_time=self.reactor.time)
2314+ statvfs=statvfs_result_fixture, create_time=self.reactor.time)
2315 step_size = self.monitor.step_size
2316 self.monitor.add(plugin)
2317
2318@@ -244,11 +256,8 @@
2319 Duplicate message should never be created. If no data is
2320 available, None will be returned when messages are created.
2321 """
2322- def statvfs(path):
2323- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2324-
2325 plugin = self.get_mount_info(
2326- statvfs=statvfs, create_time=self.reactor.time)
2327+ statvfs=statvfs_result_fixture, create_time=self.reactor.time)
2328 self.monitor.add(plugin)
2329
2330 self.reactor.advance(self.monitor.step_size)
2331@@ -266,16 +275,14 @@
2332 really test anything since the current behaviour is to ignore
2333 any mount point for which the device doesn't start with /dev.
2334 """
2335- def statvfs(path):
2336- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2337-
2338 filename = self.makeFile("""\
2339 /dev/hdc4 /mm xfs rw 0 0
2340 /mm/ubuntu-mirror /home/dchroot/warty/mirror none bind 0 0
2341 /mm/ubuntu-mirror /home/dchroot/hoary/mirror none bind 0 0
2342 /mm/ubuntu-mirror /home/dchroot/breezy/mirror none bind 0 0
2343 """)
2344- plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
2345+ plugin = self.get_mount_info(mounts_file=filename,
2346+ statvfs=statvfs_result_fixture,
2347 create_time=self.reactor.time,
2348 mtab_file=filename)
2349 step_size = self.monitor.step_size
2350@@ -327,7 +334,8 @@
2351 def test_sample_free_space(self):
2352 """Test collecting information about free space."""
2353 def statvfs(path, multiplier=mock_counter(1).next):
2354- return (4096, 0, mb(1000), mb(multiplier() * 100), 0, 0, 0, 0, 0)
2355+ return os.statvfs_result(
2356+ (4096, 0, mb(1000), mb(multiplier() * 100), 0, 0, 0, 0, 0, 0))
2357
2358 plugin = self.get_mount_info(
2359 statvfs=statvfs, create_time=self.reactor.time)
2360@@ -362,13 +370,11 @@
2361 Test ensures all expected messages are created and contain the
2362 right datatypes.
2363 """
2364- def statvfs(path):
2365- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2366-
2367 filename = self.makeFile("""\
2368 /dev/hda2 / xfs rw 0 0
2369 """)
2370- plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
2371+ plugin = self.get_mount_info(mounts_file=filename,
2372+ statvfs=statvfs_result_fixture,
2373 create_time=self.reactor.time,
2374 mtab_file=filename)
2375 step_size = self.monitor.step_size
2376@@ -393,10 +399,8 @@
2377 On the reactor "resynchronize" event, new mount-info messages
2378 should be sent.
2379 """
2380- def statvfs(path):
2381- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2382 plugin = self.get_mount_info(
2383- create_time=self.reactor.time, statvfs=statvfs)
2384+ create_time=self.reactor.time, statvfs=statvfs_result_fixture)
2385 self.monitor.add(plugin)
2386
2387 plugin.run()
2388@@ -420,9 +424,6 @@
2389 shouldn't be listed, as they have the same free space/used space as the
2390 device they're bound to.
2391 """
2392- def statvfs(path):
2393- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2394-
2395 # From this test data, we expect only two mount points to be returned,
2396 # and the other two to be ignored (the rebound /dev/hda2 -> /mnt
2397 # mounting)
2398@@ -439,19 +440,20 @@
2399 /opt /mnt none rw,bind 0 0
2400 /opt /media/Boot\\040OSX none rw,bind 0 0
2401 """)
2402- plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
2403- statvfs=statvfs, mtab_file=mtab_filename)
2404+ plugin = MountInfo(
2405+ mounts_file=filename, create_time=self.reactor.time,
2406+ statvfs=statvfs_result_fixture, mtab_file=mtab_filename)
2407
2408 self.monitor.add(plugin)
2409 plugin.run()
2410 message = plugin.create_mount_info_message()
2411 self.assertEqual(message.get("mount-info"),
2412 [(0, {"device": "/dev/devices/by-uuid/12345567",
2413- "mount-point": "/", "total-space": 4096000L,
2414+ "mount-point": "/", "total-space": 4096000,
2415 "filesystem": "ext3"}),
2416 (0, {"device": "/dev/hda2",
2417 "mount-point": "/usr",
2418- "total-space": 4096000L,
2419+ "total-space": 4096000,
2420 "filesystem": "ext3"}),
2421 ])
2422
2423@@ -461,9 +463,6 @@
2424 bind mounted directories, so any filesystems in /proc/mounts will be
2425 reported.
2426 """
2427- def statvfs(path):
2428- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2429-
2430 # In this test, we expect all mount points to be returned, as we can't
2431 # identify any as bind mounts.
2432 filename = self.makeFile("""\
2433@@ -474,22 +473,23 @@
2434 # mktemp isn't normally secure, due to race conditions, but in this
2435 # case, we don't actually create the file at all.
2436 mtab_filename = tempfile.mktemp()
2437- plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
2438- statvfs=statvfs, mtab_file=mtab_filename)
2439+ plugin = MountInfo(
2440+ mounts_file=filename, create_time=self.reactor.time,
2441+ statvfs=statvfs_result_fixture, mtab_file=mtab_filename)
2442 self.monitor.add(plugin)
2443 plugin.run()
2444 message = plugin.create_mount_info_message()
2445 self.assertEqual(message.get("mount-info"),
2446 [(0, {"device": "/dev/devices/by-uuid/12345567",
2447- "mount-point": "/", "total-space": 4096000L,
2448+ "mount-point": "/", "total-space": 4096000,
2449 "filesystem": "ext3"}),
2450 (0, {"device": "/dev/hda2",
2451 "mount-point": "/usr",
2452- "total-space": 4096000L,
2453+ "total-space": 4096000,
2454 "filesystem": "ext3"}),
2455 (0, {"device": "/dev/devices/by-uuid/12345567",
2456 "mount-point": "/mnt",
2457- "total-space": 4096000L,
2458+ "total-space": 4096000,
2459 "filesystem": "ext3"})])
2460
2461 def test_no_message_if_not_accepted(self):
2462@@ -498,10 +498,6 @@
2463 accepting their type.
2464 """
2465 self.mstore.set_accepted_types([])
2466-
2467- def statvfs(path):
2468- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2469-
2470 # From this test data, we expect only two mount points to be returned,
2471 # and the third to be ignored (the rebound /dev/hda2 -> /mnt mounting)
2472 filename = self.makeFile("""\
2473@@ -516,8 +512,9 @@
2474 /opt /mnt none rw,bind 0 0
2475 """)
2476
2477- plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
2478- statvfs=statvfs, mtab_file=mtab_filename)
2479+ plugin = MountInfo(
2480+ mounts_file=filename, create_time=self.reactor.time,
2481+ statvfs=statvfs_result_fixture, mtab_file=mtab_filename)
2482 self.monitor.add(plugin)
2483 self.reactor.advance(self.monitor.step_size * 2)
2484 self.monitor.exchange()
2485@@ -547,14 +544,12 @@
2486 didn't get the mount info at all. This test ensures that mount info are
2487 only saved when exchange happens.
2488 """
2489- def statvfs(path):
2490- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2491-
2492 filename = self.makeFile("""\
2493 /dev/hda1 / ext3 rw 0 0
2494 """)
2495- plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
2496- statvfs=statvfs, mtab_file=filename)
2497+ plugin = MountInfo(
2498+ mounts_file=filename, create_time=self.reactor.time,
2499+ statvfs=statvfs_result_fixture, mtab_file=filename)
2500 self.monitor.add(plugin)
2501 plugin.run()
2502 message1 = plugin.create_mount_info_message()
2503@@ -563,7 +558,7 @@
2504 [(0, {"device": "/dev/hda1",
2505 "filesystem": "ext3",
2506 "mount-point": "/",
2507- "total-space": 4096000L})])
2508+ "total-space": 4096000})])
2509 plugin.run()
2510 message2 = plugin.create_mount_info_message()
2511 self.assertEqual(
2512@@ -571,7 +566,7 @@
2513 [(0, {"device": "/dev/hda1",
2514 "filesystem": "ext3",
2515 "mount-point": "/",
2516- "total-space": 4096000L})])
2517+ "total-space": 4096000})])
2518 # Run again, calling create_mount_info_message purge the information
2519 plugin.run()
2520 plugin.exchange()
2521@@ -584,11 +579,8 @@
2522 In order not to overload the server, the client should stagger the
2523 exchange of free-space messages.
2524 """
2525- def statvfs(path):
2526- return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
2527-
2528 plugin = self.get_mount_info(
2529- statvfs=statvfs, create_time=self.reactor.time)
2530+ statvfs=statvfs_result_fixture, create_time=self.reactor.time)
2531 # Limit the test exchange to 5 items.
2532 plugin.max_free_space_items_to_exchange = 5
2533 step_size = self.monitor.step_size
2534
2535=== modified file 'landscape/monitor/tests/test_packagemonitor.py'
2536--- landscape/monitor/tests/test_packagemonitor.py 2016-06-16 16:38:13 +0000
2537+++ landscape/monitor/tests/test_packagemonitor.py 2017-03-10 15:29:37 +0000
2538@@ -130,7 +130,7 @@
2539
2540 def test_spawn_reporter(self):
2541 command = self.makeFile("#!/bin/sh\necho 'I am the reporter!' >&2\n")
2542- os.chmod(command, 0755)
2543+ os.chmod(command, 0o755)
2544 find_command_mock_patcher = mock.patch(
2545 "landscape.monitor.packagemonitor.find_reporter_command",
2546 return_value=command)
2547@@ -167,7 +167,7 @@
2548
2549 def test_spawn_reporter_copies_environment(self):
2550 command = self.makeFile("#!/bin/sh\necho VAR: $VAR\n")
2551- os.chmod(command, 0755)
2552+ os.chmod(command, 0o755)
2553 find_command_mock_patcher = mock.patch(
2554 "landscape.monitor.packagemonitor.find_reporter_command",
2555 return_value=command)
2556@@ -190,7 +190,7 @@
2557
2558 def test_spawn_reporter_passes_quiet_option(self):
2559 command = self.makeFile("#!/bin/sh\necho OPTIONS: $@\n")
2560- os.chmod(command, 0755)
2561+ os.chmod(command, 0o755)
2562 find_command_mock_patcher = mock.patch(
2563 "landscape.monitor.packagemonitor.find_reporter_command",
2564 return_value=command)
2565@@ -277,7 +277,7 @@
2566
2567 def test_spawn_reporter_doesnt_chdir(self):
2568 command = self.makeFile("#!/bin/sh\necho RUN\n")
2569- os.chmod(command, 0755)
2570+ os.chmod(command, 0o755)
2571 cwd = os.getcwd()
2572 self.addCleanup(os.chdir, cwd)
2573 dir = self.makeDir()
2574@@ -298,7 +298,7 @@
2575 log = self.logfile.getvalue()
2576 self.assertIn("RUN", log)
2577 # restore permissions to the dir so tearDown can clean it up
2578- os.chmod(dir, 0766)
2579+ os.chmod(dir, 0o766)
2580 find_command_mock_patcher.stop()
2581
2582 return result.addCallback(got_result)
2583
2584=== modified file 'landscape/monitor/updatemanager.py'
2585--- landscape/monitor/updatemanager.py 2013-07-05 12:52:40 +0000
2586+++ landscape/monitor/updatemanager.py 2017-03-10 15:29:37 +0000
2587@@ -1,7 +1,7 @@
2588-import ConfigParser
2589 import os
2590 import logging
2591
2592+from landscape.compat import SafeConfigParser
2593 from landscape.monitor.plugin import MonitorPlugin
2594
2595
2596@@ -39,7 +39,7 @@
2597 # There is no config, so we just act as if it's set to 'normal'
2598 return "normal"
2599 config_file = open(self.update_manager_filename)
2600- parser = ConfigParser.SafeConfigParser()
2601+ parser = SafeConfigParser()
2602 parser.readfp(config_file)
2603 prompt = parser.get("DEFAULT", "Prompt")
2604 valid_prompts = ["lts", "never", "normal"]
2605
2606=== modified file 'landscape/package/changer.py'
2607--- landscape/package/changer.py 2015-01-08 18:36:25 +0000
2608+++ landscape/package/changer.py 2017-03-10 15:29:37 +0000
2609@@ -232,10 +232,10 @@
2610 count += 1
2611 try:
2612 result.text = self._facade.perform_changes()
2613- except TransactionError, exception:
2614+ except TransactionError as exception:
2615 result.code = ERROR_RESULT
2616 result.text = exception.args[0]
2617- except DependencyError, exception:
2618+ except DependencyError as exception:
2619 for package in exception.packages:
2620 hash = self._facade.get_package_hash(package)
2621 id = self._store.get_hash_id(hash)
2622
2623=== modified file 'landscape/package/facade.py'
2624--- landscape/package/facade.py 2016-01-07 10:45:00 +0000
2625+++ landscape/package/facade.py 2017-03-10 15:29:37 +0000
2626@@ -5,7 +5,7 @@
2627 import subprocess
2628 import sys
2629 import tempfile
2630-from cStringIO import StringIO
2631+
2632 from operator import attrgetter
2633
2634 # Importing apt throws a FutureWarning on hardy, that we don't want to
2635@@ -21,7 +21,10 @@
2636 from aptsources.sourceslist import SourcesList
2637 from apt.progress.text import AcquireProgress
2638 from apt.progress.base import InstallProgress
2639-
2640+from twisted.python.compat import itervalues
2641+
2642+
2643+from landscape.compat import StringIO
2644 from landscape.lib.fs import append_file, create_file, read_file, touch_file
2645 from landscape.package.skeleton import build_skeleton_apt
2646
2647@@ -168,7 +171,7 @@
2648
2649 def get_packages(self):
2650 """Get all the packages available in the channels."""
2651- return self._hash2pkg.itervalues()
2652+ return itervalues(self._hash2pkg)
2653
2654 def get_locked_packages(self):
2655 """Get all packages in the channels that are locked.
2656@@ -662,7 +665,7 @@
2657 install_progress=install_progress)
2658 if not install_progress.dpkg_exited:
2659 raise SystemError("dpkg didn't exit cleanly.")
2660- except (apt.cache.LockFailedException, SystemError), exception:
2661+ except (apt.cache.LockFailedException, SystemError) as exception:
2662 result_text = (fetch_output.getvalue()
2663 + read_file(install_output_path))
2664 error = TransactionError(exception.args[0] +
2665@@ -745,7 +748,7 @@
2666 if now_broken_packages != already_broken_packages:
2667 try:
2668 fixer.resolve(True)
2669- except SystemError, error:
2670+ except SystemError as error:
2671 raise TransactionError(error.args[0] + "\n" +
2672 self._get_unmet_dependency_info())
2673 else:
2674
2675=== modified file 'landscape/package/releaseupgrader.py'
2676--- landscape/package/releaseupgrader.py 2014-12-12 13:25:39 +0000
2677+++ landscape/package/releaseupgrader.py 2017-03-10 15:29:37 +0000
2678@@ -5,10 +5,10 @@
2679 import shutil
2680 import logging
2681 import tarfile
2682-import cStringIO
2683
2684 from twisted.internet.defer import succeed
2685
2686+from landscape.compat import StringIO
2687 from landscape.lib.fetch import url_to_filename, fetch_to_files
2688 from landscape.lib.lsb_release import parse_lsb_release, LSB_RELEASE_FILENAME
2689 from landscape.lib.gpg import gpg_verify
2690@@ -186,7 +186,7 @@
2691 @param err: The standard error of the upgrade-tool process.
2692 @return: A text aggregating the process output, error and log files.
2693 """
2694- buf = cStringIO.StringIO()
2695+ buf = StringIO()
2696
2697 for label, content in [("output", out), ("error", err)]:
2698 if content:
2699@@ -222,7 +222,8 @@
2700 result = spawn_process(upgrade_tool_filename, args=args, env=env,
2701 path=upgrade_tool_directory, wait_pipes=False)
2702
2703- def send_operation_result((out, err, code)):
2704+ def send_operation_result(args):
2705+ out, err, code = args
2706 if code == 0:
2707 status = SUCCEEDED
2708 else:
2709
2710=== modified file 'landscape/package/reporter.py'
2711--- landscape/package/reporter.py 2016-08-04 16:13:11 +0000
2712+++ landscape/package/reporter.py 2017-03-10 15:29:37 +0000
2713@@ -1,4 +1,8 @@
2714-import urlparse
2715+try:
2716+ import urlparse
2717+except ImportError:
2718+ import urllib.parse as urlparse
2719+
2720 import logging
2721 import time
2722 import sys
2723@@ -13,8 +17,8 @@
2724 from landscape.lib.twisted_util import gather_results, spawn_process
2725 from landscape.lib.fetch import fetch_async
2726 from landscape.lib.fs import touch_file
2727-from landscape.lib import bpickle
2728
2729+from landscape.compat import convert_buffer_to_string, bpickle
2730 from landscape.package.taskhandler import (
2731 PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler)
2732 from landscape.package.store import UnknownHashIDRequest, FakePackageStore
2733@@ -278,8 +282,8 @@
2734 env["https_proxy"] = self._config.https_proxy
2735 result = spawn_process(self.apt_update_filename, env=env)
2736
2737- def callback((out, err, code), deferred):
2738- return deferred.callback((out, err, code))
2739+ def callback(args, deferred):
2740+ return deferred.callback(args)
2741
2742 return result.addCallback(callback, deferred)
2743
2744@@ -703,7 +707,7 @@
2745 messages = global_store.get_messages_by_ids(not_sent)
2746 sent = []
2747 for message_id, message in messages:
2748- message = bpickle.loads(str(message))
2749+ message = bpickle.loads(convert_buffer_to_string(message))
2750 if message["type"] not in got_type:
2751 got_type.add(message["type"])
2752 sent.append(message_id)
2753
2754=== modified file 'landscape/package/skeleton.py'
2755--- landscape/package/skeleton.py 2017-01-11 10:48:09 +0000
2756+++ landscape/package/skeleton.py 2017-03-10 15:29:37 +0000
2757@@ -2,6 +2,8 @@
2758
2759 import apt_pkg
2760
2761+from twisted.python.compat import unicode, _PY3
2762+
2763
2764 PACKAGE = 1 << 0
2765 PROVIDES = 1 << 1
2766@@ -146,7 +148,7 @@
2767 skeleton.size = version.size
2768 if version.installed_size > 0:
2769 skeleton.installed_size = version.installed_size
2770- if with_unicode:
2771+ if with_unicode and not _PY3:
2772 skeleton.section = skeleton.section.decode("utf-8")
2773 skeleton.summary = skeleton.summary.decode("utf-8")
2774 # Avoid double-decoding package descriptions in build_skeleton_apt,
2775
2776=== modified file 'landscape/package/store.py'
2777--- landscape/package/store.py 2012-05-09 19:33:57 +0000
2778+++ landscape/package/store.py 2017-03-10 15:29:37 +0000
2779@@ -6,7 +6,10 @@
2780 except ImportError:
2781 from pysqlite2 import dbapi2 as sqlite3
2782
2783-from landscape.lib import bpickle
2784+from twisted.python.compat import iteritems, long
2785+from twisted.python.compat import StringType as basestring
2786+
2787+from landscape.compat import convert_buffer_to_string, bpickle
2788 from landscape.lib.store import with_cursor
2789
2790
2791@@ -40,14 +43,15 @@
2792
2793 @param hash_ids: a C{dict} of hash=>id mappings.
2794 """
2795- for hash, id in hash_ids.iteritems():
2796+ for hash, id in iteritems(hash_ids):
2797 cursor.execute("REPLACE INTO hash VALUES (?, ?)",
2798- (id, buffer(hash)))
2799+ (id, sqlite3.Binary(hash)))
2800
2801 @with_cursor
2802 def get_hash_id(self, cursor, hash):
2803 """Return the id associated to C{hash}, or C{None} if not available."""
2804- cursor.execute("SELECT id FROM hash WHERE hash=?", (buffer(hash),))
2805+ cursor.execute("SELECT id FROM hash WHERE hash=?",
2806+ (sqlite3.Binary(hash),))
2807 value = cursor.fetchone()
2808 if value:
2809 return value[0]
2810@@ -127,7 +131,7 @@
2811
2812 try:
2813 hash_id_store.check_sanity()
2814- except InvalidHashIdDb, e:
2815+ except InvalidHashIdDb as e:
2816 # propagate the error
2817 raise e
2818
2819@@ -253,7 +257,7 @@
2820 hashes = list(hashes)
2821 cursor.execute("INSERT INTO hash_id_request (hashes, timestamp)"
2822 " VALUES (?,?)",
2823- (buffer(bpickle.dumps(hashes)), time.time()))
2824+ (sqlite3.Binary(bpickle.dumps(hashes)), time.time()))
2825 return HashIDRequest(self._db, cursor.lastrowid)
2826
2827 @with_cursor
2828@@ -276,8 +280,9 @@
2829 @with_cursor
2830 def add_task(self, cursor, queue, data):
2831 data = bpickle.dumps(data)
2832- cursor.execute("INSERT INTO task (queue, timestamp, data) "
2833- "VALUES (?,?,?)", (queue, time.time(), buffer(data)))
2834+ cursor.execute(
2835+ "INSERT INTO task (queue, timestamp, data) VALUES (?,?,?)",
2836+ (queue, time.time(), sqlite3.Binary(data)))
2837 return PackageTask(self._db, cursor.lastrowid)
2838
2839 @with_cursor
2840@@ -307,7 +312,7 @@
2841 @with_cursor
2842 def save_message(self, cursor, message):
2843 cursor.execute("INSERT INTO message (data) VALUES (?)",
2844- (buffer(bpickle.dumps(message)),))
2845+ (sqlite3.Binary(bpickle.dumps(message)),))
2846
2847 @with_cursor
2848 def get_message_ids(self, cursor):
2849@@ -340,7 +345,7 @@
2850 def hashes(self, cursor):
2851 cursor.execute("SELECT hashes FROM hash_id_request WHERE id=?",
2852 (self.id,))
2853- return bpickle.loads(str(cursor.fetchone()[0]))
2854+ return bpickle.loads(convert_buffer_to_string(cursor.fetchone()[0]))
2855
2856 @with_cursor
2857 def _get_timestamp(self, cursor):
2858@@ -389,7 +394,7 @@
2859
2860 self.queue = row[0]
2861 self.timestamp = row[1]
2862- self.data = bpickle.loads(str(row[2]))
2863+ self.data = bpickle.loads(convert_buffer_to_string(row[2]))
2864
2865 @with_cursor
2866 def remove(self, cursor):
2867
2868=== modified file 'landscape/package/taskhandler.py'
2869--- landscape/package/taskhandler.py 2016-06-16 23:11:55 +0000
2870+++ landscape/package/taskhandler.py 2017-03-10 15:29:37 +0000
2871@@ -214,7 +214,7 @@
2872
2873 try:
2874 lsb_release_info = parse_lsb_release(self.lsb_release_filename)
2875- except IOError, error:
2876+ except IOError as error:
2877 logging.warning(warning % str(error))
2878 return None
2879 try:
2880@@ -276,8 +276,8 @@
2881 init_logging(config, "-".join(word.lower() for word in words))
2882
2883 # Setup our umask for Apt to use, this needs to setup file permissions to
2884- # 0644 so...
2885- os.umask(022)
2886+ # 0o644 so...
2887+ os.umask(0o022)
2888
2889 package_store = cls.package_store_class(config.store_filename)
2890 # Delay importing of the facades so that we don't
2891
2892=== modified file 'landscape/package/tests/helpers.py'
2893--- landscape/package/tests/helpers.py 2017-01-10 16:32:56 +0000
2894+++ landscape/package/tests/helpers.py 2017-03-10 15:29:37 +0000
2895@@ -301,9 +301,9 @@
2896 "GadWR2ltRC651hGpxw4et/u8phTny3Vdfvy2dgAAAAAAAAAAANjRE6Lr2rEAKAAACg==")
2897
2898
2899-HASH1 = base64.decodestring("/ezv4AefpJJ8DuYFSq4RiEHJYP4=")
2900-HASH2 = base64.decodestring("glP4DwWOfMULm0AkRXYsH/exehc=")
2901-HASH3 = base64.decodestring("NJM05mj86veaSInYxxqL1wahods=")
2902+HASH1 = base64.decodestring(b"/ezv4AefpJJ8DuYFSq4RiEHJYP4=")
2903+HASH2 = base64.decodestring(b"glP4DwWOfMULm0AkRXYsH/exehc=")
2904+HASH3 = base64.decodestring(b"NJM05mj86veaSInYxxqL1wahods=")
2905 HASH_MINIMAL = "6\xce\x8f\x1bM\x82MWZ\x1a\xffjAc(\xdb(\xa1\x0eG"
2906 HASH_SIMPLE_RELATIONS = (
2907 "'#\xab&k\xe6\xf5E\xcfB\x9b\xceO7\xe6\xec\xa9\xddY\xaa")
2908
2909=== modified file 'landscape/package/tests/test_facade.py'
2910--- landscape/package/tests/test_facade.py 2016-06-15 19:02:40 +0000
2911+++ landscape/package/tests/test_facade.py 2017-03-10 15:29:37 +0000
2912@@ -9,6 +9,8 @@
2913 from aptsources.sourceslist import SourcesList
2914 from apt.cache import LockFailedException
2915
2916+from twisted.python.compat import unicode
2917+
2918 from landscape.lib.fs import read_file, create_file
2919 from landscape.package.facade import (
2920 TransactionError, DependencyError, ChannelError, AptFacade,
2921
2922=== modified file 'landscape/package/tests/test_releaseupgrader.py'
2923--- landscape/package/tests/test_releaseupgrader.py 2016-06-16 16:26:32 +0000
2924+++ landscape/package/tests/test_releaseupgrader.py 2017-03-10 15:29:37 +0000
2925@@ -3,9 +3,11 @@
2926 import signal
2927 import tarfile
2928 import unittest
2929+from unittest import skipIf
2930
2931 from twisted.internet import reactor
2932 from twisted.internet.defer import succeed, fail, Deferred
2933+from twisted.python.compat import _PY3
2934
2935 from landscape.lib.gpg import InvalidGPGSignature
2936 from landscape.lib.fetch import HTTPCodeError
2937@@ -284,7 +286,8 @@
2938 "stderr\n\n"
2939 "=== main.log ===\n\n"
2940 "long log\n\n")
2941-
2942+
2943+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
2944 def test_upgrade(self):
2945 """
2946 The L{ReleaseUpgrader.upgrade} method spawns the appropropriate
2947@@ -300,7 +303,7 @@
2948 "echo PWD=$PWD\n"
2949 "echo out\n")
2950 fd.close()
2951- os.chmod(upgrade_tool_filename, 0755)
2952+ os.chmod(upgrade_tool_filename, 0o755)
2953 env_backup = os.environ.copy()
2954 os.environ.clear()
2955 os.environ.update({"FOO": "bar"})
2956@@ -336,6 +339,7 @@
2957
2958 return deferred.addBoth(cleanup)
2959
2960+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
2961 def test_upgrade_with_env_variables(self):
2962 """
2963 The L{ReleaseUpgrader.upgrade} method optionally sets environment
2964@@ -350,7 +354,7 @@
2965 "echo RELEASE_UPRADER_ALLOW_THIRD_PARTY="
2966 "$RELEASE_UPRADER_ALLOW_THIRD_PARTY\n")
2967 fd.close()
2968- os.chmod(upgrade_tool_filename, 0755)
2969+ os.chmod(upgrade_tool_filename, 0o755)
2970 env_backup = os.environ.copy()
2971 os.environ.clear()
2972 deferred = Deferred()
2973@@ -397,7 +401,7 @@
2974 "echo err >&2\n"
2975 "exit 3")
2976 fd.close()
2977- os.chmod(upgrade_tool_filename, 0755)
2978+ os.chmod(upgrade_tool_filename, 0o755)
2979
2980 deferred = Deferred()
2981
2982@@ -452,7 +456,7 @@
2983 " while True:\n"
2984 " time.sleep(2)\n" % child_pid_filename)
2985 fd.close()
2986- os.chmod(upgrade_tool_filename, 0755)
2987+ os.chmod(upgrade_tool_filename, 0o755)
2988 os.environ.clear()
2989 os.environ.update({"FOO": "bar"})
2990 deferred = Deferred()
2991@@ -495,6 +499,8 @@
2992
2993 return deferred.addBoth(cleanup)
2994
2995+
2996+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
2997 def test_finish(self):
2998 """
2999 The L{ReleaseUpgrader.finish} method wipes the upgrade-tool directory
3000@@ -508,7 +514,7 @@
3001 reporter_filename = self.makeFile("#!/bin/sh\n"
3002 "echo $@\n"
3003 "echo $(pwd)\n")
3004- os.chmod(reporter_filename, 0755)
3005+ os.chmod(reporter_filename, 0o755)
3006
3007 deferred = Deferred()
3008
3009@@ -518,7 +524,8 @@
3010 find_reporter_mock.return_value = reporter_filename
3011 result = self.upgrader.finish()
3012
3013- def check_result((out, err, code)):
3014+ def check_result(args):
3015+ out, err, code = args
3016 self.assertFalse(os.path.exists(upgrade_tool_directory))
3017 self.assertEqual(out, "--force-apt-update\n%s\n"
3018 % os.getcwd())
3019@@ -582,7 +589,7 @@
3020 configuration file the release-upgrader was called with.
3021 """
3022 reporter_filename = self.makeFile("#!/bin/sh\necho $@\n")
3023- os.chmod(reporter_filename, 0755)
3024+ os.chmod(reporter_filename, 0o755)
3025 self.config.config = "/some/config"
3026
3027 deferred = Deferred()
3028@@ -593,7 +600,8 @@
3029 find_reporter_mock.return_value = reporter_filename
3030 result = self.upgrader.finish()
3031
3032- def check_result((out, err, code)):
3033+ def check_result(args):
3034+ out, err, code = args
3035 self.assertEqual(out, "--force-apt-update "
3036 "--config=/some/config\n")
3037 self.assertEqual(err, "")
3038
3039=== modified file 'landscape/package/tests/test_reporter.py'
3040--- landscape/package/tests/test_reporter.py 2016-08-04 16:13:11 +0000
3041+++ landscape/package/tests/test_reporter.py 2017-03-10 15:29:37 +0000
3042@@ -10,7 +10,6 @@
3043
3044 from landscape.lib.fs import create_file, touch_file
3045 from landscape.lib.fetch import FetchError
3046-from landscape.lib import bpickle
3047 from landscape.package.store import (
3048 PackageStore, UnknownHashIDRequest, FakePackageStore)
3049 from landscape.package.reporter import (
3050@@ -25,6 +24,8 @@
3051 LandscapeTest, BrokerServiceHelper, EnvironSaverHelper)
3052 from landscape.reactor import FakeReactor
3053
3054+from landscape.compat import convert_buffer_to_string, bpickle
3055+
3056 SAMPLE_LSB_RELEASE = "DISTRIB_CODENAME=codename\n"
3057
3058
3059@@ -89,7 +90,7 @@
3060 "echo -n '%s'\n"
3061 "echo -n '%s' >&2\n"
3062 "exit %d" % (out, err, code))
3063- os.chmod(self.reporter.apt_update_filename, 0755)
3064+ os.chmod(self.reporter.apt_update_filename, 0o755)
3065
3066 def test_set_package_ids_with_all_known(self):
3067 self.store.add_hash_id_request(["hash1", "hash2"])
3068@@ -1123,7 +1124,8 @@
3069 def do_test():
3070 result = self.reporter.run_apt_update()
3071
3072- def callback((out, err, code)):
3073+ def callback(args):
3074+ out, err, code = args
3075 self.assertEqual("output", out)
3076 self.assertEqual("error", err)
3077 self.assertEqual(0, code)
3078@@ -1149,7 +1151,8 @@
3079
3080 result = self.reporter.run_apt_update()
3081
3082- def callback((out, err, code)):
3083+ def callback(args):
3084+ out, err, code = args
3085 self.assertEqual("output", out)
3086
3087 result.addCallback(callback)
3088@@ -1169,7 +1172,8 @@
3089
3090 result = self.reporter.run_apt_update()
3091
3092- def callback((out, err, code)):
3093+ def callback(args):
3094+ out, err, code = args
3095 self.assertEqual("output", out)
3096
3097 result.addCallback(callback)
3098@@ -1188,7 +1192,8 @@
3099
3100 result = self.reporter.run_apt_update()
3101
3102- def callback((out, err, code)):
3103+ def callback(args):
3104+ out, err, code = args
3105 self.assertEqual("output", out)
3106 self.assertEqual("error", err)
3107 self.assertEqual(2, code)
3108@@ -1219,7 +1224,8 @@
3109
3110 result = self.reporter.run_apt_update()
3111
3112- def callback((out, err, code)):
3113+ def callback(args):
3114+ out, err, code = args
3115 self.assertEqual("", out)
3116 self.assertEqual("", err)
3117 self.assertEqual(100, code)
3118@@ -1255,7 +1261,8 @@
3119
3120 result = self.reporter.run_apt_update()
3121
3122- def callback((out, err, code)):
3123+ def callback(args):
3124+ out, err, code = args
3125 self.assertEqual("output", out)
3126 self.assertEqual("error", err)
3127 self.assertEqual(0, code)
3128@@ -1390,7 +1397,8 @@
3129 def do_test():
3130 result = self.reporter.run_apt_update()
3131
3132- def callback((out, err, code)):
3133+ def callback(args):
3134+ out, err, code = args
3135 self.assertEqual("", out)
3136 self.assertEqual("", err)
3137 self.assertEqual(0, code)
3138@@ -1430,7 +1438,8 @@
3139 def do_test():
3140 result = self.reporter.run_apt_update()
3141
3142- def callback((out, err, code)):
3143+ def callback(args):
3144+ out, err, code = args
3145 self.assertEqual("", out)
3146 self.assertEqual("", err)
3147 self.assertEqual(0, code)
3148@@ -1728,7 +1737,7 @@
3149 message_store.set_accepted_types(["package-reporter-result"])
3150 self.reporter.apt_update_filename = self.makeFile(
3151 "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0")
3152- os.chmod(self.reporter.apt_update_filename, 0755)
3153+ os.chmod(self.reporter.apt_update_filename, 0o755)
3154 deferred = Deferred()
3155
3156 def do_test():
3157@@ -1745,7 +1754,8 @@
3158 "SELECT id, data FROM message").fetchall())
3159 self.assertEqual(1, len(stored))
3160 self.assertEqual(1, stored[0][0])
3161- self.assertEqual(message, bpickle.loads(str(stored[0][1])))
3162+ self.assertEqual(message,
3163+ bpickle.loads(convert_buffer_to_string(stored[0][1])))
3164 result.addCallback(callback)
3165 result.chainDeferred(deferred)
3166
3167
3168=== modified file 'landscape/package/tests/test_skeleton.py'
3169--- landscape/package/tests/test_skeleton.py 2017-01-12 08:01:31 +0000
3170+++ landscape/package/tests/test_skeleton.py 2017-03-10 15:29:37 +0000
3171@@ -13,6 +13,8 @@
3172 HASH_OR_RELATIONS)
3173 from landscape.tests.helpers import LandscapeTest
3174
3175+from twisted.python.compat import unicode
3176+
3177
3178 class SkeletonTestHelper(object):
3179 """A helper to set up a repository for the skeleton tests."""
3180
3181=== modified file 'landscape/package/tests/test_store.py'
3182--- landscape/package/tests/test_store.py 2016-06-16 00:08:40 +0000
3183+++ landscape/package/tests/test_store.py 2017-03-10 15:29:37 +0000
3184@@ -152,7 +152,7 @@
3185 store_filename = junk_db_factory()
3186 try:
3187 self.store1.add_hash_id_db(store_filename)
3188- except InvalidHashIdDb, e:
3189+ except InvalidHashIdDb as e:
3190 self.assertEqual(str(e), store_filename)
3191 else:
3192 self.fail()
3193@@ -547,7 +547,7 @@
3194 func2.store2 = PackageStore(self.filename)
3195 try:
3196 func2.store2.add_task("reporter", "data")
3197- except Exception, e:
3198+ except Exception as e:
3199 error.append(str(e))
3200
3201 for func in [func1, func2]:
3202
3203=== modified file 'landscape/package/tests/test_taskhandler.py'
3204--- landscape/package/tests/test_taskhandler.py 2016-06-16 20:33:26 +0000
3205+++ landscape/package/tests/test_taskhandler.py 2017-03-10 15:29:37 +0000
3206@@ -379,7 +379,7 @@
3207
3208 # We also expect the umask to be set appropriately before running the
3209 # commands
3210- umask.assert_called_once_with(022)
3211+ umask.assert_called_once_with(0o22)
3212
3213 return result.addCallback(assert_task_handler)
3214
3215@@ -389,7 +389,7 @@
3216
3217 try:
3218 run_task_handler(PackageTaskHandler, ["-c", self.config_filename])
3219- except SystemExit, e:
3220+ except SystemExit as e:
3221 self.assertIn("default is already running", str(e))
3222 else:
3223 self.fail("SystemExit not raised")
3224@@ -400,7 +400,7 @@
3225 try:
3226 run_task_handler(PackageTaskHandler,
3227 ["-c", self.config_filename, "--quiet"])
3228- except SystemExit, e:
3229+ except SystemExit as e:
3230 self.assertEqual(str(e), "")
3231 else:
3232 self.fail("SystemExit not raised")
3233
3234=== modified file 'landscape/reactor.py'
3235--- landscape/reactor.py 2013-07-15 15:25:08 +0000
3236+++ landscape/reactor.py 2017-03-10 15:29:37 +0000
3237@@ -399,14 +399,14 @@
3238 self._current_time = call[0]
3239 try:
3240 call[1](*call[2], **call[3])
3241- except Exception, e:
3242+ except Exception as e:
3243 logging.exception(e)
3244 self._current_time += seconds
3245
3246 def _in_thread(self, callback, errback, f, args, kwargs):
3247 try:
3248 result = f(*args, **kwargs)
3249- except Exception, e:
3250+ except Exception as e:
3251 exc_info = sys.exc_info()
3252 if errback is None:
3253 self.call_in_main(logging.error, e, exc_info=exc_info)
3254@@ -420,7 +420,7 @@
3255 while self._threaded_callbacks:
3256 try:
3257 self._threaded_callbacks.pop(0)()
3258- except Exception, e:
3259+ except Exception as e:
3260 logging.exception(e)
3261
3262 def _hook_threaded_callbacks(self):
3263
3264=== modified file 'landscape/schema.py'
3265--- landscape/schema.py 2014-07-16 17:15:13 +0000
3266+++ landscape/schema.py 2017-03-10 15:29:37 +0000
3267@@ -1,4 +1,5 @@
3268 """A schema system. Yes. Another one!"""
3269+from twisted.python.compat import iteritems, unicode, long
3270
3271
3272 class InvalidError(Exception):
3273@@ -83,10 +84,10 @@
3274 self.encoding = encoding
3275
3276 def coerce(self, value):
3277- if isinstance(value, str):
3278+ if isinstance(value, bytes):
3279 try:
3280 value = value.decode(self.encoding)
3281- except UnicodeDecodeError, e:
3282+ except UnicodeDecodeError as e:
3283 raise InvalidError("%r can't be decoded: %s" % (value, str(e)))
3284 if not isinstance(value, unicode):
3285 raise InvalidError("%r isn't a unicode" % (value,))
3286@@ -108,7 +109,7 @@
3287 for i, subvalue in enumerate(value):
3288 try:
3289 new_list[i] = self.schema.coerce(subvalue)
3290- except InvalidError, e:
3291+ except InvalidError as e:
3292 raise InvalidError(
3293 "%r could not coerce with %s: %s"
3294 % (subvalue, self.schema, e))
3295@@ -155,13 +156,13 @@
3296 new_dict = {}
3297 if not isinstance(value, dict):
3298 raise InvalidError("%r is not a dict." % (value,))
3299- for k, v in value.iteritems():
3300+ for k, v in iteritems(value):
3301 if k not in self.schema:
3302 raise InvalidError("%r is not a valid key as per %r"
3303 % (k, self.schema))
3304 try:
3305 new_dict[k] = self.schema[k].coerce(v)
3306- except InvalidError, e:
3307+ except InvalidError as e:
3308 raise InvalidError(
3309 "Value of %r key of dict %r could not coerce with %s: %s"
3310 % (k, value, self.schema[k], e))
3311
3312=== modified file 'landscape/sysinfo/deployment.py'
3313--- landscape/sysinfo/deployment.py 2014-03-26 12:33:14 +0000
3314+++ landscape/sysinfo/deployment.py 2017-03-10 15:29:37 +0000
3315@@ -94,7 +94,7 @@
3316 """
3317 try:
3318 setup_logging()
3319- except IOError, e:
3320+ except IOError as e:
3321 sys.exit("Unable to setup logging. %s" % e)
3322
3323 if sysinfo is None:
3324@@ -107,8 +107,8 @@
3325 sysinfo.add(plugin)
3326
3327 def show_output(result):
3328- print format_sysinfo(sysinfo.get_headers(), sysinfo.get_notes(),
3329- sysinfo.get_footnotes(), indent=" ")
3330+ print(format_sysinfo(sysinfo.get_headers(), sysinfo.get_notes(),
3331+ sysinfo.get_footnotes(), indent=" "))
3332
3333 def run_sysinfo():
3334 return sysinfo.run().addCallback(show_output)
3335
3336=== modified file 'landscape/sysinfo/tests/test_disk.py'
3337--- landscape/sysinfo/tests/test_disk.py 2011-07-05 05:09:11 +0000
3338+++ landscape/sysinfo/tests/test_disk.py 2017-03-10 15:29:37 +0000
3339@@ -1,7 +1,9 @@
3340+import os
3341+
3342 from twisted.internet.defer import Deferred
3343
3344+from landscape.sysinfo.disk import Disk, format_megabytes
3345 from landscape.sysinfo.sysinfo import SysInfoPluginRegistry
3346-from landscape.sysinfo.disk import Disk, format_megabytes
3347 from landscape.tests.helpers import LandscapeTest
3348
3349
3350@@ -21,8 +23,8 @@
3351 fs="ext3", device=None):
3352 if device is None:
3353 device = "/dev/" + point.replace("/", "_")
3354- self.stat_results[point] = (block_size, 0, capacity, unused,
3355- 0, 0, 0, 0, 0)
3356+ self.stat_results[point] = os.statvfs_result(
3357+ (block_size, 0, capacity, unused, 0, 0, 0, 0, 0, 0))
3358 f = open(self.mount_file, "a")
3359 f.write("/dev/%s %s %s rw 0 0\n" % (device, point, fs))
3360 f.close()
3361
3362=== modified file 'landscape/sysinfo/tests/test_sysinfo.py'
3363--- landscape/sysinfo/tests/test_sysinfo.py 2016-06-15 20:32:14 +0000
3364+++ landscape/sysinfo/tests/test_sysinfo.py 2017-03-10 15:29:37 +0000
3365@@ -1,12 +1,12 @@
3366-from cStringIO import StringIO
3367 from logging import getLogger, StreamHandler
3368 import mock
3369 import os
3370
3371 from twisted.internet.defer import Deferred, succeed, fail
3372
3373+from landscape.compat import StringIO
3374+from landscape.plugin import PluginRegistry
3375 from landscape.sysinfo.sysinfo import SysInfoPluginRegistry, format_sysinfo
3376-from landscape.plugin import PluginRegistry
3377 from landscape.tests.helpers import LandscapeTest
3378
3379
3380
3381=== modified file 'landscape/tests/clock.py'
3382--- landscape/tests/clock.py 2008-06-10 10:56:01 +0000
3383+++ landscape/tests/clock.py 2017-03-10 15:29:37 +0000
3384@@ -31,10 +31,13 @@
3385 C{seconds} argument until after Twisted 2.2.
3386 """
3387
3388+import functools
3389
3390 from twisted.internet import error
3391 from twisted.python.runtime import seconds as runtimeSeconds
3392 from twisted.python import reflect
3393+from twisted.python.compat import iteritems
3394+
3395 import traceback
3396
3397
3398@@ -71,7 +74,7 @@
3399 self.calls.remove,
3400 lambda c: None,
3401 self.seconds))
3402- self.calls.sort(lambda a, b: cmp(a.getTime(), b.getTime()))
3403+ self.calls.sort(key=lambda a: a.getTime())
3404 return self.calls[-1]
3405
3406
3407@@ -246,7 +249,9 @@
3408 if self.kw:
3409 L.append(", ")
3410 if self.kw:
3411- L.append(", ".join(['%s=%s' % (k, reflect.safe_repr(v)) for (k, v) in self.kw.iteritems()]))
3412+ L.append(", ".join([
3413+ '%s=%s' % (k, reflect.safe_repr(v))
3414+ for (k, v) in iteritems(self.kw)]))
3415 L.append(")")
3416
3417 if self.debug:
3418
3419=== modified file 'landscape/tests/helpers.py'
3420--- landscape/tests/helpers.py 2017-01-10 13:28:12 +0000
3421+++ landscape/tests/helpers.py 2017-03-10 15:29:37 +0000
3422@@ -1,5 +1,3 @@
3423-from cStringIO import StringIO
3424-from ConfigParser import ConfigParser
3425 import logging
3426 import shutil
3427 import pprint
3428@@ -12,9 +10,14 @@
3429
3430 from logging import Handler, ERROR, Formatter
3431 from twisted.trial.unittest import TestCase
3432+from twisted.python.compat import StringType as basestring
3433+from twisted.python.compat import _PY3
3434 from twisted.python.failure import Failure
3435 from twisted.internet.defer import Deferred
3436
3437+from landscape.compat import ConfigParser
3438+from landscape.compat import stringio, cstringio
3439+
3440 from landscape.tests.subunit import run_isolated
3441 from landscape.watchdog import bootstrap_list
3442
3443@@ -186,11 +189,11 @@
3444 and comments may be different but the actual parameters and sections
3445 must be the same.
3446 """
3447- first_fp = StringIO(first)
3448+ first_fp = cstringio(first)
3449 first_parser = ConfigParser()
3450 first_parser.readfp(first_fp)
3451
3452- second_fp = StringIO(second)
3453+ second_fp = cstringio(second)
3454 second_parser = ConfigParser()
3455 second_parser.readfp(second_fp)
3456
3457@@ -216,7 +219,7 @@
3458 return persist_filename
3459
3460 def makeFile(self, content=None, suffix="", prefix="tmp", basename=None,
3461- dirname=None, path=None):
3462+ dirname=None, path=None, mode="w"):
3463 """Create a temporary file and return the path to it.
3464
3465 @param content: Initial content for the file.
3466@@ -237,9 +240,8 @@
3467 if content is None:
3468 os.unlink(path)
3469 if content is not None:
3470- file = open(path, "w")
3471- file.write(content)
3472- file.close()
3473+ with open(path, mode) as file:
3474+ file.write(content)
3475 self.addCleanup(self._clean_file, path)
3476 return path
3477
3478@@ -324,7 +326,7 @@
3479 self.error_handler = ErrorHandler()
3480 test_case.log_helper = self
3481 test_case.logger = logger = logging.getLogger()
3482- test_case.logfile = StringIO()
3483+ test_case.logfile = cstringio()
3484 handler = logging.StreamHandler(test_case.logfile)
3485 format = ("%(levelname)8s: %(message)s")
3486 handler.setFormatter(logging.Formatter(format))
3487@@ -480,7 +482,7 @@
3488
3489 def __init__(self, output, return_codes=None):
3490 self.output = output
3491- self.stdout = StringIO(output)
3492+ self.stdout = cstringio(output)
3493 self.popen_inputs = []
3494 self.return_codes = return_codes
3495
3496@@ -500,13 +502,12 @@
3497 class StandardIOHelper(object):
3498
3499 def set_up(self, test_case):
3500- from StringIO import StringIO
3501-
3502 test_case.old_stdout = sys.stdout
3503 test_case.old_stdin = sys.stdin
3504- test_case.stdout = sys.stdout = StringIO()
3505- test_case.stdin = sys.stdin = StringIO()
3506- test_case.stdin.encoding = "UTF-8"
3507+ test_case.stdout = sys.stdout = stringio()
3508+ test_case.stdin = sys.stdin = stringio()
3509+ if not _PY3:
3510+ test_case.stdin.encoding = "UTF-8"
3511
3512 def tear_down(self, test_case):
3513 sys.stdout = test_case.old_stdout
3514
3515=== modified file 'landscape/tests/subunit.py'
3516--- landscape/tests/subunit.py 2009-11-13 15:47:51 +0000
3517+++ landscape/tests/subunit.py 2017-03-10 15:29:37 +0000
3518@@ -16,13 +16,14 @@
3519 # along with this program; if not, write to the Free Software
3520 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3521 #
3522-
3523 import os
3524-from StringIO import StringIO
3525 import subprocess
3526 import sys
3527 import unittest
3528
3529+from landscape.compat import StringIO
3530+
3531+
3532 def test_suite():
3533 import subunit.tests
3534 return subunit.tests.test_suite()
3535
3536=== modified file 'landscape/tests/test_configuration.py'
3537--- landscape/tests/test_configuration.py 2017-03-07 16:47:17 +0000
3538+++ landscape/tests/test_configuration.py 2017-03-10 15:29:37 +0000
3539@@ -1,15 +1,16 @@
3540 from __future__ import print_function
3541
3542+import mock
3543 from functools import partial
3544-from ConfigParser import ConfigParser
3545-from cStringIO import StringIO
3546 import os
3547 import sys
3548 import unittest
3549
3550-import mock
3551 from twisted.internet.defer import succeed, fail, Deferred
3552+from twisted.python.compat import iteritems
3553
3554+from landscape.compat import ConfigParser
3555+from landscape.compat import StringIO
3556 from landscape.broker.registration import RegistrationError
3557 from landscape.broker.tests.helpers import (
3558 RemoteBrokerHelper, BrokerConfigurationHelper)
3559@@ -776,8 +777,8 @@
3560
3561 config = self.get_config([], data_path=client_path)
3562 bootstrap_tree(config)
3563- mock_chmod.assert_any_call(client_path, 0755)
3564- mock_chmod.assert_called_with(annotations_path, 0755)
3565+ mock_chmod.assert_any_call(client_path, 0o755)
3566+ mock_chmod.assert_called_with(annotations_path, 0o755)
3567 self.assertTrue(os.path.isdir(client_path))
3568 self.assertTrue(os.path.isdir(annotations_path))
3569
3570@@ -840,7 +841,7 @@
3571 "Access group [webservers]: ": u"databases",
3572 "Tags [london, server]: ": u"glasgow, laptop",
3573 }
3574- for key, value in fixtures.iteritems():
3575+ for key, value in iteritems(fixtures):
3576 if key in prompt:
3577 return value
3578 raise KeyError("Couldn't find answer for {}".format(prompt))
3579@@ -848,7 +849,7 @@
3580 def side_effect_getpass(prompt):
3581 fixtures = {"Account registration key:": "New Password",
3582 "Please confirm:": "New Password"}
3583- for key, value in fixtures.iteritems():
3584+ for key, value in iteritems(fixtures):
3585 if key in prompt:
3586 return value
3587 raise KeyError("Couldn't find answer for {}".format(prompt))
3588@@ -1440,7 +1441,7 @@
3589 try:
3590 self.get_config(["--config", config_filename, "--silent",
3591 "--import", import_filename])
3592- except ImportOptionError, error:
3593+ except ImportOptionError as error:
3594 self.assertEqual(str(error),
3595 "Nothing to import at %s." % import_filename)
3596 else:
3597@@ -1454,7 +1455,7 @@
3598 try:
3599 self.get_config(["--config", config_filename, "--silent",
3600 "--import", import_filename])
3601- except ImportOptionError, error:
3602+ except ImportOptionError as error:
3603 self.assertEqual(str(error),
3604 "File %s doesn't exist." % import_filename)
3605 else:
3606@@ -1471,7 +1472,7 @@
3607 try:
3608 self.get_config(["--config", config_filename, "--silent",
3609 "--import", import_filename])
3610- except ImportOptionError, error:
3611+ except ImportOptionError as error:
3612 self.assertEqual(str(error),
3613 "Nothing to import at %s." % import_filename)
3614 else:
3615@@ -1486,7 +1487,7 @@
3616 try:
3617 self.get_config(["--config", config_filename, "--silent",
3618 "--import", import_filename])
3619- except ImportOptionError, error:
3620+ except ImportOptionError as error:
3621 self.assertIn("Nothing to import at %s" % import_filename,
3622 str(error))
3623 else:
3624@@ -1500,7 +1501,7 @@
3625 import_filename = self.makeFile(
3626 "[client]\nfoo=bar", basename="import_config")
3627 # Remove read permissions
3628- os.chmod(import_filename, os.stat(import_filename).st_mode - 0444)
3629+ os.chmod(import_filename, os.stat(import_filename).st_mode - 0o444)
3630 error = self.assertRaises(
3631 ImportOptionError, self.get_config, ["--import", import_filename])
3632 expected_message = ("Couldn't read configuration from %s." %
3633@@ -1635,7 +1636,7 @@
3634 try:
3635 self.get_config(["--config", config_filename, "--silent",
3636 "--import", "https://config.url"])
3637- except ImportOptionError, error:
3638+ except ImportOptionError as error:
3639 self.assertEqual(str(error),
3640 "Couldn't download configuration from "
3641 "https://config.url: Server "
3642@@ -1657,7 +1658,7 @@
3643 try:
3644 self.get_config(["--config", config_filename, "--silent",
3645 "--import", "https://config.url"])
3646- except ImportOptionError, error:
3647+ except ImportOptionError as error:
3648 self.assertEqual(str(error),
3649 "Couldn't download configuration from "
3650 "https://config.url: Error 60: pycurl message")
3651@@ -1674,7 +1675,7 @@
3652 # Use a command line option as well to test the precedence.
3653 try:
3654 self.get_config(["--silent", "--import", "https://config.url"])
3655- except ImportOptionError, error:
3656+ except ImportOptionError as error:
3657 self.assertEqual(str(error),
3658 "Nothing to import at https://config.url.")
3659 else:
3660@@ -1691,7 +1692,7 @@
3661 # Use a command line option as well to test the precedence.
3662 try:
3663 self.get_config(["--silent", "--import", "https://config.url"])
3664- except ImportOptionError, error:
3665+ except ImportOptionError as error:
3666 self.assertEqual("Nothing to import at https://config.url.",
3667 str(error))
3668 else:
3669
3670=== modified file 'landscape/tests/test_deployment.py'
3671--- landscape/tests/test_deployment.py 2016-06-15 16:35:06 +0000
3672+++ landscape/tests/test_deployment.py 2017-03-10 15:29:37 +0000
3673@@ -1,8 +1,9 @@
3674-import os
3675 from optparse import OptionParser
3676-from StringIO import StringIO
3677 from textwrap import dedent
3678+import mock
3679+import os
3680
3681+from landscape.compat import StringIO
3682 from landscape.lib.fs import read_file, create_file
3683
3684 from landscape.deployment import (
3685@@ -10,7 +11,6 @@
3686 from landscape.manager.config import ManagerConfiguration
3687
3688 from landscape.tests.helpers import LandscapeTest, LogKeeperHelper
3689-import mock
3690
3691
3692 class BabbleConfiguration(Configuration):
3693@@ -592,7 +592,7 @@
3694 default_filename2)
3695
3696 # If it is readable, than return the first default configuration file.
3697- os.chmod(default_filename1, 0644)
3698+ os.chmod(default_filename1, 0o644)
3699 self.assertEqual(self.config.get_config_filename(),
3700 default_filename1)
3701
3702
3703=== modified file 'landscape/tests/test_reactor.py'
3704--- landscape/tests/test_reactor.py 2013-07-10 09:17:14 +0000
3705+++ landscape/tests/test_reactor.py 2017-03-10 15:29:37 +0000
3706@@ -1,7 +1,7 @@
3707-import thread
3708 import types
3709 import time
3710
3711+from landscape.compat import thread
3712 from landscape.reactor import FakeReactor, LandscapeReactor
3713 from landscape.tests.helpers import LandscapeTest
3714
3715@@ -153,7 +153,10 @@
3716 def test_events_result(self):
3717 reactor = self.get_reactor()
3718
3719- generator = iter([1, 2, 3]).next
3720+ iterable = iter([1, 2, 3])
3721+
3722+ def generator():
3723+ return next(iterable)
3724
3725 reactor.call_on("foobar", generator)
3726 reactor.call_on("foobar", generator)
3727
3728=== modified file 'landscape/tests/test_schema.py'
3729--- landscape/tests/test_schema.py 2013-10-25 21:23:47 +0000
3730+++ landscape/tests/test_schema.py 2017-03-10 15:29:37 +0000
3731@@ -4,6 +4,8 @@
3732 InvalidError, Constant, Bool, Int, Float, Bytes, Unicode, List, KeyDict,
3733 Dict, Tuple, Any, Message)
3734
3735+from twisted.python.compat import long
3736+
3737
3738 class DummySchema(object):
3739 def coerce(self, value):
3740@@ -42,7 +44,8 @@
3741 self.assertEqual(Int().coerce(3), 3)
3742
3743 def test_int_accepts_long(self):
3744- self.assertEqual(Int().coerce(3L), 3)
3745+ # This test can be removed after dropping Python 2 support
3746+ self.assertEqual(Int().coerce(long(3)), 3)
3747
3748 def test_int_bad_str(self):
3749 self.assertRaises(InvalidError, Int().coerce, "3")
3750@@ -57,7 +60,8 @@
3751 self.assertEqual(Float().coerce(3), 3.0)
3752
3753 def test_float_accepts_long(self):
3754- self.assertEqual(Float().coerce(3L), 3.0)
3755+ # This test can be removed after dropping Python 2 support
3756+ self.assertEqual(Float().coerce(long(3)), 3.0)
3757
3758 def test_float_bad_str(self):
3759 self.assertRaises(InvalidError, Float().coerce, "3.0")
3760
3761=== modified file 'landscape/tests/test_sysvconfig.py'
3762--- landscape/tests/test_sysvconfig.py 2016-06-15 22:21:21 +0000
3763+++ landscape/tests/test_sysvconfig.py 2017-03-10 15:29:37 +0000
3764@@ -11,13 +11,17 @@
3765 filename = self.makeFile("RUN=0\n")
3766 sysvconfig = SysVConfig(filename)
3767 sysvconfig.set_start_on_boot(True)
3768- self.assertEqual(file(filename, "r").read(), "RUN=1\n")
3769+ with open(filename, "r") as res_file:
3770+ result = res_file.read()
3771+ self.assertEqual(result, "RUN=1\n")
3772
3773 def test_set_to_not_run_on_boot(self):
3774 filename = self.makeFile("RUN=1\n")
3775 sysvconfig = SysVConfig(filename)
3776 sysvconfig.set_start_on_boot(False)
3777- self.assertEqual(file(filename, "r").read(), "RUN=0\n")
3778+ with open(filename, "r") as res_file:
3779+ result = res_file.read()
3780+ self.assertEqual(result, "RUN=0\n")
3781
3782 def test_configured_to_run(self):
3783 filename = self.makeFile("RUN=1\n")
3784
3785=== modified file 'landscape/tests/test_watchdog.py'
3786--- landscape/tests/test_watchdog.py 2016-08-18 17:30:03 +0000
3787+++ landscape/tests/test_watchdog.py 2017-03-10 15:29:37 +0000
3788@@ -25,6 +25,9 @@
3789
3790 import landscape.watchdog
3791
3792+from unittest import skipIf
3793+from twisted.python.compat import _PY3
3794+
3795
3796 class StubDaemon(object):
3797 program = "program-name"
3798@@ -580,7 +583,7 @@
3799 output_filename = self.makeFile("NOT RUN")
3800 self.makeFile('#!/bin/sh\necho "RUN $@" > %s' % output_filename,
3801 path=self.exec_name)
3802- os.chmod(self.exec_name, 0755)
3803+ os.chmod(self.exec_name, 0o755)
3804
3805 waiter = FileChangeWaiter(output_filename)
3806
3807@@ -597,7 +600,7 @@
3808 output_filename = self.makeFile("NOT RUN")
3809 self.makeFile('#!/bin/sh\necho "RUN $@" > %s' % output_filename,
3810 path=self.exec_name)
3811- os.chmod(self.exec_name, 0755)
3812+ os.chmod(self.exec_name, 0o755)
3813
3814 waiter = FileChangeWaiter(output_filename)
3815
3816@@ -622,7 +625,7 @@
3817 "time.sleep(1000)\n"
3818 % (sys.executable, output_filename),
3819 path=self.exec_name)
3820- os.chmod(self.exec_name, 0755)
3821+ os.chmod(self.exec_name, 0o755)
3822
3823 waiter = FileChangeWaiter(output_filename)
3824 self.daemon.start()
3825@@ -646,7 +649,7 @@
3826 "os.kill(os.getpid(), signal.SIGSTOP)\n"
3827 % (sys.executable, output_filename),
3828 path=self.exec_name)
3829- os.chmod(self.exec_name, 0755)
3830+ os.chmod(self.exec_name, 0o755)
3831
3832 self.addCleanup(setattr, landscape.watchdog, "SIGKILL_DELAY",
3833 landscape.watchdog.SIGKILL_DELAY)
3834@@ -666,7 +669,7 @@
3835 output_filename = self.makeFile("NOT RUN")
3836 self.makeFile('#!/bin/sh\necho "RUN" > %s' % output_filename,
3837 path=self.exec_name)
3838- os.chmod(self.exec_name, 0755)
3839+ os.chmod(self.exec_name, 0o755)
3840
3841 self.daemon.start()
3842
3843@@ -682,7 +685,7 @@
3844 output_filename = self.makeFile("NOT RUN")
3845 self.makeFile('#!/bin/sh\necho "RUN" > %s' % output_filename,
3846 path=self.exec_name)
3847- os.chmod(self.exec_name, 0755)
3848+ os.chmod(self.exec_name, 0o755)
3849
3850 self.daemon.start()
3851
3852@@ -709,7 +712,7 @@
3853 """
3854 % {"exe": sys.executable, "out": output_filename},
3855 path=self.exec_name)
3856- os.chmod(self.exec_name, 0755)
3857+ os.chmod(self.exec_name, 0o755)
3858
3859 self.addCleanup(setattr, landscape.watchdog, "GRACEFUL_WAIT_PERIOD",
3860 landscape.watchdog.GRACEFUL_WAIT_PERIOD)
3861@@ -735,7 +738,7 @@
3862 "os.kill(os.getpid(), signal.SIGSTOP)\n"
3863 % (sys.executable, output_filename),
3864 path=self.exec_name)
3865- os.chmod(self.exec_name, 0755)
3866+ os.chmod(self.exec_name, 0o755)
3867
3868 self.addCleanup(setattr, landscape.watchdog, "SIGKILL_DELAY",
3869 landscape.watchdog.SIGKILL_DELAY)
3870@@ -789,7 +792,7 @@
3871
3872 self.makeFile("#!/bin/sh\necho RUN >> %s" % output_filename,
3873 path=self.exec_name)
3874- os.chmod(self.exec_name, 0755)
3875+ os.chmod(self.exec_name, 0o755)
3876
3877 def got_result(result):
3878 self.assertEqual(len(list(open(output_filename))),
3879@@ -832,7 +835,7 @@
3880
3881 self.makeFile("#!/bin/sh\necho RUN >> %s" % output_filename,
3882 path=self.exec_name)
3883- os.chmod(self.exec_name, 0755)
3884+ os.chmod(self.exec_name, 0o755)
3885
3886 def got_result(result):
3887 # Pay attention to the +1 bellow. It's the reason for this test.
3888@@ -947,6 +950,7 @@
3889 mock.ANY, mock.ANY, args=mock.ANY, env=mock.ANY, uid=None,
3890 gid=None)
3891
3892+ @skipIf(_PY3, 'Takes long with Python3, probably unclean Reactor')
3893 def test_request_exit(self):
3894 """The request_exit() method calls exit() on the broker process."""
3895
3896@@ -958,7 +962,7 @@
3897 "output_filename": output_filename,
3898 "socket": socket_filename})
3899
3900- os.chmod(broker_filename, 0755)
3901+ os.chmod(broker_filename, 0o755)
3902 process_result = getProcessOutput(broker_filename, env=os.environ,
3903 errortoo=True)
3904
3905@@ -1230,14 +1234,14 @@
3906 def mode(*suffix):
3907 return stat.S_IMODE(os.stat(path(*suffix)).st_mode)
3908
3909- self.assertEqual(mode(), 0755)
3910- self.assertEqual(mode("messages"), 0755)
3911- self.assertEqual(mode("package"), 0755)
3912- self.assertEqual(mode("package/hash-id"), 0755)
3913- self.assertEqual(mode("package/binaries"), 0755)
3914- self.assertEqual(mode("sockets"), 0750)
3915- self.assertEqual(mode("custom-graph-scripts"), 0755)
3916- self.assertEqual(mode("package/database"), 0644)
3917+ self.assertEqual(mode(), 0o755)
3918+ self.assertEqual(mode("messages"), 0o755)
3919+ self.assertEqual(mode("package"), 0o755)
3920+ self.assertEqual(mode("package/hash-id"), 0o755)
3921+ self.assertEqual(mode("package/binaries"), 0o755)
3922+ self.assertEqual(mode("sockets"), 0o750)
3923+ self.assertEqual(mode("custom-graph-scripts"), 0o755)
3924+ self.assertEqual(mode("package/database"), 0o644)
3925
3926 STUB_BROKER = """\
3927 #!%(executable)s
3928
3929=== modified file 'landscape/user/changes.py'
3930--- landscape/user/changes.py 2016-01-11 18:34:49 +0000
3931+++ landscape/user/changes.py 2017-03-10 15:29:37 +0000
3932@@ -1,3 +1,5 @@
3933+from twisted.python.compat import iteritems, itervalues
3934+
3935 from landscape.diff import diff
3936
3937
3938@@ -54,7 +56,7 @@
3939
3940 def create_diff(self):
3941 """Returns the changes since the last snapshot.
3942-
3943+
3944 See landscape.message_schemas.USERS schema for a description of the
3945 dictionary returned by this method.
3946 """
3947@@ -74,11 +76,11 @@
3948 changes = {}
3949 creates, updates, deletes = diff(self._old_users, self._new_users)
3950 if creates:
3951- changes["create-users"] = list(creates.itervalues())
3952+ changes["create-users"] = list(itervalues(creates))
3953 if updates:
3954- changes["update-users"] = list(updates.itervalues())
3955+ changes["update-users"] = list(itervalues(updates))
3956 if deletes:
3957- changes["delete-users"] = list(deletes.iterkeys())
3958+ changes["delete-users"] = list(deletes)
3959 return changes
3960
3961 def _detect_group_changes(self):
3962@@ -94,7 +96,7 @@
3963 if creates:
3964 groups = []
3965 create_members = {}
3966- for value in creates.itervalues():
3967+ for value in itervalues(creates):
3968 # Use a copy to avoid removing the 'members' element
3969 # from stored data.
3970 value = value.copy()
3971@@ -110,7 +112,7 @@
3972 remove_members = {}
3973 create_members = {}
3974 update_groups = []
3975- for groupname, new_data in updates.iteritems():
3976+ for groupname, new_data in iteritems(updates):
3977 old_data = self._old_groups[groupname]
3978 old_members = set(old_data["members"])
3979 new_members = set(new_data["members"])
3980
3981=== modified file 'landscape/user/management.py'
3982--- landscape/user/management.py 2013-09-18 07:52:46 +0000
3983+++ landscape/user/management.py 2017-03-10 15:29:37 +0000
3984@@ -4,11 +4,10 @@
3985 # subprocesses. liboobs (i.e. System Tools) is a possibility, and has
3986 # documentation now in the 2.17 series, but is not wrapped to Python.
3987
3988-import os
3989 import logging
3990 import subprocess
3991+from passlib.hash import md5_crypt
3992
3993-from landscape.lib import md5crypt
3994 from landscape.user.provider import UserManagementError, UserProvider
3995
3996
3997@@ -52,8 +51,7 @@
3998 # XXX temporary workaround? We're getting unicode here.
3999 username = username.encode("ascii")
4000 password = password.encode("ascii")
4001- salt = os.urandom(6).encode("base64")[:-1]
4002- crypted = md5crypt.md5crypt(password, salt)
4003+ crypted = md5_crypt.encrypt(password)
4004 result, output = self.call_popen(["usermod", "-p", crypted, username])
4005 if result != 0:
4006 raise UserManagementError("Error setting password for user "
4007
4008=== modified file 'landscape/user/provider.py'
4009--- landscape/user/provider.py 2008-06-12 17:46:35 +0000
4010+++ landscape/user/provider.py 2017-03-10 15:29:37 +0000
4011@@ -1,8 +1,10 @@
4012+from grp import struct_group
4013 from pwd import struct_passwd
4014-from grp import struct_group
4015 import csv
4016+import logging
4017 import subprocess
4018-import logging
4019+
4020+from twisted.python.compat import _PY3
4021
4022
4023 class UserManagementError(Exception):
4024@@ -31,7 +33,7 @@
4025
4026 Each user is represented as a dict with the keys: C{username},
4027 C{name}, C{uid}, C{enabled}, C{location}, C{work-phone} and
4028- C{home-phone}.
4029+ C{home-phone}.
4030 """
4031 users = []
4032 found_usernames = set()
4033@@ -40,8 +42,7 @@
4034 user = struct_passwd(user)
4035 if user.pw_name in found_usernames:
4036 continue
4037- gecos_data = [x.decode("utf-8", "replace") or None
4038- for x in user.pw_gecos.split(",")[:4]]
4039+ gecos_data = [x or None for x in user.pw_gecos.split(",")[:4]]
4040 while len(gecos_data) < 4:
4041 gecos_data.append(None)
4042 name, location, work_phone, home_phone = tuple(gecos_data)
4043@@ -70,7 +71,7 @@
4044 continue
4045 member_names = user_names.intersection(group.gr_mem)
4046 groups.append({"name": group.gr_name, "gid": group.gr_gid,
4047- "members": list(member_names)})
4048+ "members": sorted(list(member_names))})
4049 found_groupnames.add(group.gr_name)
4050 return groups
4051
4052@@ -96,6 +97,7 @@
4053 return data["gid"]
4054 raise GroupNotFoundError("Group not found for group %s." % groupname)
4055
4056+
4057 class UserProvider(UserProviderBase):
4058
4059 popen = subprocess.Popen
4060@@ -117,26 +119,32 @@
4061 directory, path to the user's shell)
4062 """
4063 user_data = []
4064- passwd_file = open(self._passwd_file, "r")
4065- reader = csv.DictReader(passwd_file, fieldnames=self.passwd_fields,
4066- delimiter=":", quoting=csv.QUOTE_NONE)
4067- current_line = 0
4068- for row in reader:
4069- current_line += 1
4070- # This skips the NIS user marker in the passwd file.
4071- if (row["username"].startswith("+") or
4072- row["username"].startswith("-")):
4073- continue
4074- try:
4075- user_data.append((row["username"], row["passwd"],
4076- int(row["uid"]), int(row["primary-gid"]),
4077- row["gecos"], row["home"], row["shell"]))
4078- except (ValueError, TypeError):
4079-
4080- logging.warn("passwd file %s is incorrectly formatted: "
4081- "line %d." % (self._passwd_file, current_line))
4082-
4083- passwd_file.close()
4084+ if _PY3:
4085+ open_params = dict(errors='replace')
4086+ else:
4087+ open_params = dict()
4088+ with open(self._passwd_file, "r", **open_params) as passwd_file:
4089+ reader = csv.DictReader(
4090+ passwd_file, fieldnames=self.passwd_fields, delimiter=":",
4091+ quoting=csv.QUOTE_NONE)
4092+ current_line = 0
4093+ for row in reader:
4094+ current_line += 1
4095+ # This skips the NIS user marker in the passwd file.
4096+ if (row["username"].startswith("+") or
4097+ row["username"].startswith("-")):
4098+ continue
4099+ gecos = row["gecos"]
4100+ if not _PY3 and gecos is not None:
4101+ gecos = gecos.decode("utf-8", "replace")
4102+ try:
4103+ user_data.append((row["username"], row["passwd"],
4104+ int(row["uid"]), int(row["primary-gid"]),
4105+ gecos, row["home"], row["shell"]))
4106+ except (ValueError, TypeError):
4107+ logging.warn(
4108+ "passwd file %s is incorrectly formatted: line %d."
4109+ % (self._passwd_file, current_line))
4110 return user_data
4111
4112 def get_group_data(self):
4113@@ -157,11 +165,10 @@
4114 row["name"].startswith("-")):
4115 continue
4116 try:
4117- group_data.append((row["name"], row["passwd"], int(row["gid"]),
4118+ group_data.append((row["name"], row["passwd"], int(row["gid"]),
4119 row["members"].split(",")))
4120 except (AttributeError, ValueError):
4121 logging.warn("group file %s is incorrectly formatted: "
4122 "line %d." % (self._group_file, current_line))
4123 group_file.close()
4124 return group_data
4125-
4126
4127=== modified file 'landscape/user/tests/helpers.py'
4128--- landscape/user/tests/helpers.py 2016-02-08 13:49:49 +0000
4129+++ landscape/user/tests/helpers.py 2017-03-10 15:29:37 +0000
4130@@ -1,3 +1,5 @@
4131+from twisted.python.compat import iteritems, itervalues
4132+
4133 from landscape.user.management import UserManagementError
4134 from landscape.user.provider import UserProviderBase
4135
4136@@ -29,7 +31,7 @@
4137 try:
4138 uid = 1000
4139 if self._users:
4140- uid = max([x["uid"] for x in self._users.itervalues()]) + 1
4141+ uid = max([x["uid"] for x in itervalues(self._users)]) + 1
4142 if self._groups:
4143 primary_gid = self.get_gid(primary_group_name)
4144 else:
4145@@ -108,7 +110,7 @@
4146 def add_group(self, name):
4147 gid = 1000
4148 if self._groups:
4149- gid = max([x["gid"] for x in self._groups.itervalues()]) + 1
4150+ gid = max([x["gid"] for x in itervalues(self._groups)]) + 1
4151 self._groups[name] = {"name": name, "gid": gid, "members": []}
4152 self.update_provider_from_groups()
4153 return "add_group succeeded"
4154@@ -144,7 +146,7 @@
4155
4156 def update_provider_from_groups(self):
4157 provider_list = []
4158- for k, v in self._groups.iteritems():
4159+ for k, v in iteritems(self._groups):
4160 provider_list.append((k, "x", v["gid"], v["members"]))
4161 self.provider.groups = provider_list
4162
4163
4164=== modified file 'landscape/user/tests/test_management.py'
4165--- landscape/user/tests/test_management.py 2011-07-05 05:09:11 +0000
4166+++ landscape/user/tests/test_management.py 2017-03-10 15:29:37 +0000
4167@@ -1,13 +1,13 @@
4168-from landscape.lib import md5crypt
4169 from landscape.user.management import UserManagement, UserManagementError
4170 from landscape.user.tests.helpers import FakeUserProvider
4171 from landscape.user.provider import UserNotFoundError, GroupNotFoundError
4172 from landscape.tests.helpers import LandscapeTest, MockPopen
4173+from passlib.hash import md5_crypt
4174
4175
4176 def guess_password(generated_password, plaintext_password):
4177 salt = generated_password[len("$1$"):generated_password.rfind("$")]
4178- crypted = md5crypt.md5crypt(plaintext_password, salt)
4179+ crypted = md5_crypt.encrypt(plaintext_password, salt=salt)
4180 return crypted
4181
4182
4183
4184=== modified file 'landscape/user/tests/test_provider.py'
4185--- landscape/user/tests/test_provider.py 2011-07-05 05:09:11 +0000
4186+++ landscape/user/tests/test_provider.py 2017-03-10 15:29:37 +0000
4187@@ -1,13 +1,14 @@
4188 import pwd
4189 import grp
4190
4191-from landscape.user.provider import (UserProvider, UserNotFoundError,
4192- GroupNotFoundError)
4193-
4194+from twisted.python.compat import _PY3
4195+
4196+from landscape.user.provider import (
4197+ UserProvider, UserNotFoundError, GroupNotFoundError)
4198+
4199+from landscape.tests.helpers import LandscapeTest
4200 from landscape.user.tests.helpers import FakeUserProvider
4201
4202-from landscape.tests.helpers import LandscapeTest
4203-
4204
4205 class ProviderTest(LandscapeTest):
4206
4207@@ -131,12 +132,19 @@
4208 name = u"Jos\N{LATIN SMALL LETTER E WITH ACUTE}"
4209 location = "F\N{LATIN SMALL LETTER I WITH DIAERESIS}nland"
4210 number = "N\N{LATIN SMALL LETTER AE}ver"
4211- gecos = "%s,%s,%s,%s," % (name.encode("utf-8"),
4212- location.encode("utf-8"),
4213- number.encode("utf-8"),
4214- number.encode("utf-8"))
4215- data = [("jdoe", "x", 1000, 1000, gecos, "/home/jdoe", "/bin/zsh")]
4216- provider = FakeUserProvider(users=data, shadow_file=self.shadow_file)
4217+ if _PY3:
4218+ gecos = "{},{},{},{},".format(name, location, number, number)
4219+ else:
4220+ gecos = "{},{},{},{},".format(name.encode("utf-8"),
4221+ location.encode("utf-8"),
4222+ number.encode("utf-8"),
4223+ number.encode("utf-8"))
4224+ passwd_file = self.makeFile("""\
4225+jdoe:x:1000:1000:{}:/home/jdoe:/bin/zsh
4226+root:x:0:0:root:/root:/bin/bash
4227+""".format(gecos))
4228+ provider = UserProvider(passwd_file=passwd_file,
4229+ group_file=self.group_file)
4230 users = provider.get_users()
4231 self.assertEqual(users[0]["name"], name)
4232 self.assertEqual(users[0]["location"], location)
4233@@ -148,10 +156,14 @@
4234 If a GECOS field contains non-UTF8 data, it should be replaced
4235 with question marks.
4236 """
4237+ passwd_file = self.makeFile(b"""\
4238+jdoe:x:1000:1000:\255,\255,\255,\255:/home/jdoe:/bin/zsh
4239+root:x:0:0:root:/root:/bin/bash
4240+""", mode="wb")
4241+ provider = UserProvider(passwd_file=passwd_file,
4242+ group_file=self.group_file)
4243 unicode_unknown = u'\N{REPLACEMENT CHARACTER}'
4244- data = [("jdoe", "x", 1000, 1000, "\255,\255,\255,\255", "/home/jdoe",
4245- "/bin/zsh")]
4246- provider = FakeUserProvider(users=data, shadow_file=self.shadow_file)
4247+ provider = UserProvider(passwd_file=passwd_file, group_file=None)
4248 users = provider.get_users()
4249 self.assertEqual(users[0]["name"], unicode_unknown)
4250 self.assertEqual(users[0]["location"], unicode_unknown)
4251@@ -180,8 +192,7 @@
4252 for user in users:
4253 if user["username"] == user_0.pw_name:
4254 self.assertEqual(user["uid"], 0)
4255- user_0_name = user_0.pw_gecos.split(",")[0].decode(
4256- "utf-8", "replace")
4257+ user_0_name = user_0.pw_gecos.split(",")[0]
4258 self.assertEqual(user["name"], user_0_name)
4259 break
4260 else:
4261@@ -390,7 +401,7 @@
4262 "members": []})
4263 self.assertEqual(groups[1], {"name": u"cdrom",
4264 "gid": 24,
4265- "members": [u"kevin", u"haldaemon"]})
4266+ "members": [u"haldaemon", u"kevin"]})
4267 self.assertEqual(groups[2], {"name": u"kevin",
4268 "gid": 1000,
4269 "members": []})
4270
4271=== modified file 'landscape/watchdog.py'
4272--- landscape/watchdog.py 2017-01-05 11:19:41 +0000
4273+++ landscape/watchdog.py 2017-03-10 15:29:37 +0000
4274@@ -480,12 +480,12 @@
4275 if os.fork(): # launch child and...
4276 os._exit(0) # kill off parent again.
4277 # some argue that this umask should be 0, but that's annoying.
4278- os.umask(077)
4279+ os.umask(0o077)
4280 null = os.open('/dev/null', os.O_RDWR)
4281 for i in range(3):
4282 try:
4283 os.dup2(null, i)
4284- except OSError, e:
4285+ except OSError as e:
4286 if e.errno != errno.EBADF:
4287 raise
4288 os.close(null)
4289@@ -576,20 +576,20 @@
4290
4291
4292 bootstrap_list = BootstrapList([
4293- BootstrapDirectory("$data_path", "landscape", "root", 0755),
4294- BootstrapDirectory("$data_path/package", "landscape", "root", 0755),
4295- BootstrapDirectory(
4296- "$data_path/package/hash-id", "landscape", "root", 0755),
4297- BootstrapDirectory(
4298- "$data_path/package/binaries", "landscape", "root", 0755),
4299- BootstrapDirectory(
4300- "$data_path/package/upgrade-tool", "landscape", "root", 0755),
4301- BootstrapDirectory("$data_path/messages", "landscape", "root", 0755),
4302- BootstrapDirectory("$data_path/sockets", "landscape", "root", 0750),
4303- BootstrapDirectory(
4304- "$data_path/custom-graph-scripts", "landscape", "root", 0755),
4305- BootstrapDirectory("$log_dir", "landscape", "root", 0755),
4306- BootstrapFile("$data_path/package/database", "landscape", "root", 0644)])
4307+ BootstrapDirectory("$data_path", "landscape", "root", 0o755),
4308+ BootstrapDirectory("$data_path/package", "landscape", "root", 0o755),
4309+ BootstrapDirectory(
4310+ "$data_path/package/hash-id", "landscape", "root", 0o755),
4311+ BootstrapDirectory(
4312+ "$data_path/package/binaries", "landscape", "root", 0o755),
4313+ BootstrapDirectory(
4314+ "$data_path/package/upgrade-tool", "landscape", "root", 0o755),
4315+ BootstrapDirectory("$data_path/messages", "landscape", "root", 0o755),
4316+ BootstrapDirectory("$data_path/sockets", "landscape", "root", 0o750),
4317+ BootstrapDirectory(
4318+ "$data_path/custom-graph-scripts", "landscape", "root", 0o755),
4319+ BootstrapDirectory("$log_dir", "landscape", "root", 0o755),
4320+ BootstrapFile("$data_path/package/database", "landscape", "root", 0o644)])
4321
4322
4323 def clean_environment():
4324@@ -600,7 +600,7 @@
4325 postinst script. Some environment variables may be set which would affect
4326 *other* maintainer scripts which landscape-client invokes.
4327 """
4328- for key in os.environ.keys():
4329+ for key in list(os.environ.keys()):
4330 if (key.startswith(("DEBIAN_", "DEBCONF_")) or
4331 key in ["LANDSCAPE_ATTACHMENTS", "MAIL"]):
4332 del os.environ[key]
4333
4334=== added file 'trial5'
4335--- trial5 1970-01-01 00:00:00 +0000
4336+++ trial5 2017-03-10 15:29:37 +0000
4337@@ -0,0 +1,11 @@
4338+echo -e "\e[1m\e[43m\e[34m \e[93m\e[44m \e[0m"
4339+echo -e "\e[1m\e[43m\e[34m PYTHON \e[93m\e[44m 2 \e[0m"
4340+echo -e "\e[1m\e[43m\e[34m \e[93m\e[44m \e[0m"
4341+echo "-------------------------------------------------------------------------------"
4342+trial --unclean-warnings $@
4343+echo -e "\e[1m\e[93m\e[44m \e[43m\e[34m \e[0m"
4344+echo -e "\e[1m\e[93m\e[44m PYTHON \e[43m\e[34m 3 \e[0m"
4345+echo -e "\e[1m\e[93m\e[44m \e[43m\e[34m \e[0m"
4346+echo "-------------------------------------------------------------------------------"
4347+trial3 --unclean-warnings $@
4348+

Subscribers

People subscribed via source and target branches

to all changes: