Merge lp:~gocept/landscape-client/py3-network into lp:~landscape/landscape-client/trunk

Proposed by Steffen Allner
Status: Superseded
Proposed branch: lp:~gocept/landscape-client/py3-network
Merge into: lp:~landscape/landscape-client/trunk
Prerequisite: lp:~gocept/landscape-client/python3
Diff against target: 522 lines (+139/-81)
9 files modified
landscape/broker/store.py (+1/-1)
landscape/broker/tests/test_store.py (+2/-2)
landscape/lib/fs.py (+38/-20)
landscape/lib/juju.py (+1/-1)
landscape/lib/network.py (+27/-15)
landscape/lib/tests/test_fs.py (+27/-5)
landscape/lib/tests/test_network.py (+34/-31)
landscape/lib/tests/test_process.py (+5/-3)
landscape/lib/vm_info.py (+4/-3)
To merge this branch: bzr merge lp:~gocept/landscape-client/py3-network
Reviewer Review Type Date Requested Status
🤖 Landscape Builder test results Needs Fixing
Данило Шеган (community) Needs Fixing
Gocept Pending
Landscape Pending
Review via email: mp+319426@code.launchpad.net

This proposal has been superseded by a proposal from 2017-03-13.

Description of the change

We are on the journey to get the Bytes / String story solved for Python 2/3 compatibility.

This MP considers code in the landscape.lib.network module. This module mostly consists of methods used in this model to retrieve information about network devices. To achieve this, calls to socket methods are used, which require bytes as input. To maintain compatibility with the code using `get_active_device_info` the result of this function still consists of strings in Python 3 but the internal methods now explicitly work on bytes. The `skipped_interfaces` are still a tuple of strings to maintain signature stability.

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: 961
Branch: lp:~gocept/landscape-client/py3-network
Jenkins: https://ci.lscape.net/job/latch-test-precise/899/

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) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 961
Branch: lp:~gocept/landscape-client/py3-network
Jenkins: https://ci.lscape.net/job/latch-test-xenial/1463/

review: Needs Fixing (test results)
962. By Steffen Allner

Only decode with Python 3, otherwise a landscape.schema violation occurs.

963. By Steffen Allner

Opening file in binary mode allows to write and load bpickled data.

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

Looks good but there are a few nits here.

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: Success
Revno: 963
Branch: lp:~gocept/landscape-client/py3-network
Jenkins: https://ci.lscape.net/job/latch-test-xenial/3600/

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

I merged the current trunk and the py3-fs branch and will resubmit the proposal with a new prerequisite branch.

964. By Steffen Allner

Backmerge from trunk.

965. By Steffen Allner

Merge landscape.lib.fs with optional encoding.

966. By Steffen Allner

Improve documentation.

967. By Steffen Allner

Always ensure ascii characters for interface name.

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: 967
Branch: lp:~gocept/landscape-client/py3-network
Jenkins: https://ci.lscape.net/job/latch-test-xenial/3608/

review: Needs Fixing (test results)
968. By Steffen Allner

Improve compliance with coding style.

969. By Steffen Allner

Merge in current l.l.fs

970. By Steffen Allner

Backout former revision 967 as we need a byte string for Python 2 to satisfy the schema.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/broker/store.py'
2--- landscape/broker/store.py 2017-03-06 10:38:07 +0000
3+++ landscape/broker/store.py 2017-03-13 13:20:43 +0000
4@@ -437,7 +437,7 @@
5 return os.path.join(self._directory, *args)
6
7 def _get_content(self, filename):
8- file = open(filename)
9+ file = open(filename, 'rb')
10 try:
11 return file.read()
12 finally:
13
14=== modified file 'landscape/broker/tests/test_store.py'
15--- landscape/broker/tests/test_store.py 2016-08-17 20:31:48 +0000
16+++ landscape/broker/tests/test_store.py 2017-03-13 13:20:43 +0000
17@@ -280,7 +280,7 @@
18 self.store.add({"type": "data", "data": 1})
19 # We simulate it by creating a fake file which raises halfway through
20 # writing a file.
21- with mock.patch("__builtin__.open") as mock_open:
22+ with mock.patch("landscape.lib.fs.open") as mock_open:
23 mocked_file = mock_open.return_value
24 mocked_file.write.side_effect = IOError("Sorry, pal!")
25 # This kind of ensures that raising an exception is somewhat
26@@ -288,7 +288,7 @@
27 # on special exception-handling in the file-writing code.
28 self.assertRaises(
29 IOError, self.store.add, {"type": "data", "data": 2})
30- mock_open.assert_called_with(mock.ANY, "w")
31+ mock_open.assert_called_with(mock.ANY, "wb")
32 mocked_file.write.assert_called_once_with(mock.ANY)
33 self.assertEqual(self.store.get_pending_messages(),
34 [{"type": "data", "data": 1, "api": "3.2"}])
35
36=== modified file 'landscape/lib/fs.py'
37--- landscape/lib/fs.py 2013-05-28 08:48:31 +0000
38+++ landscape/lib/fs.py 2017-03-13 13:20:43 +0000
39@@ -1,50 +1,68 @@
40 """File-system utils"""
41
42+import codecs
43 import os
44 import time
45
46
47-def create_file(path, content):
48+from twisted.python.compat import long
49+
50+
51+def create_file(path, content, encoding=None):
52 """Create a file with the given content.
53
54 @param path: The path to the file.
55 @param content: The content to be written in the file.
56+ @param encoding: An optional encoding. If set, the content will be encoded
57+ with C{encoding} before writing to the file.
58 """
59- fd = open(path, "w")
60- fd.write(content)
61- fd.close()
62+ if encoding:
63+ content = codecs.encode(content, encoding)
64+ # XXX: Due to a very specific mock of `open()` in landscape.broker.tests.\
65+ # test_store.MessageStoreTest.test_atomic_message_writing it is hard to
66+ # write this file opening as context manager.
67+ fd = open(path, "wb")
68+ try:
69+ fd.write(content)
70+ finally:
71+ fd.close()
72
73
74 def append_file(path, content):
75- """Append a file with the given content.
76+ """Append a file with the given binary content.
77
78 The file is created, if it doesn't exist already.
79
80 @param path: The path to the file.
81 @param content: The content to be written in the file at the end.
82 """
83- fd = open(path, "a")
84- fd.write(content)
85- fd.close()
86-
87-
88-def read_file(path, limit=None):
89+ with open(path, "a") as fd:
90+ fd.write(content)
91+
92+
93+def read_file(path, limit=None, encoding=None):
94 """Return the content of the given file.
95
96 @param path: The path to the file.
97 @param limit: An optional read limit. If positive, read up to that number
98 of bytes from the beginning of the file. If negative, read up to that
99 number of bytes from the end of the file.
100- @return content: The content of the file, possibly trimmed to C{limit}.
101+ @param encoding: An optional encoding. If set, the content will be returned
102+ decoded with C{encoding}.
103+ @return content: The content of the file as bytes or unicode string
104+ (depending on C{encoding}), possibly trimmed to C{limit}.
105 """
106- fd = open(path, "r")
107- if limit and os.path.getsize(path) > abs(limit):
108- whence = 0
109- if limit < 0:
110- whence = 2
111- fd.seek(limit, whence)
112- content = fd.read()
113- fd.close()
114+ # Use binary mode since opening a file in text mode in Python 3 does not
115+ # allow non-zero offset seek from the end of the file.
116+ with open(path, "rb") as fd:
117+ if limit and os.path.getsize(path) > abs(limit):
118+ whence = 0
119+ if limit < 0:
120+ whence = 2
121+ fd.seek(limit, whence)
122+ content = fd.read()
123+ if encoding:
124+ content = codecs.decode(content, encoding)
125 return content
126
127
128
129=== modified file 'landscape/lib/juju.py'
130--- landscape/lib/juju.py 2014-09-24 08:09:55 +0000
131+++ landscape/lib/juju.py 2017-03-13 13:20:43 +0000
132@@ -13,7 +13,7 @@
133 if not os.path.exists(config.juju_filename):
134 return
135
136- json_contents = read_file(config.juju_filename)
137+ json_contents = read_file(config.juju_filename, encoding="utf-8")
138 try:
139 juju_info = json.loads(json_contents)
140 # Catch any error the json lib could throw, because we don't know or
141
142=== modified file 'landscape/lib/network.py'
143--- landscape/lib/network.py 2017-01-10 16:02:02 +0000
144+++ landscape/lib/network.py 2017-03-13 13:20:43 +0000
145@@ -8,7 +8,7 @@
146 import errno
147 import logging
148
149-from twisted.python.compat import long
150+from twisted.python.compat import long, _PY3
151
152 __all__ = ["get_active_device_info", "get_network_traffic", "is_64"]
153
154@@ -55,7 +55,7 @@
155 max_interfaces = 128
156
157 # Setup an array to hold our response, and initialized to null strings.
158- interfaces = array.array("B", "\0" * max_interfaces * IF_STRUCT_SIZE)
159+ interfaces = array.array("B", b"\0" * max_interfaces * IF_STRUCT_SIZE)
160 buffer_size = interfaces.buffer_info()[0]
161 packed_bytes = struct.pack(
162 "iL", max_interfaces * IF_STRUCT_SIZE, buffer_size)
163@@ -69,7 +69,7 @@
164 already_found = set()
165 for index in range(0, byte_length, IF_STRUCT_SIZE):
166 ifreq_struct = result[index:index + IF_STRUCT_SIZE]
167- interface_name = ifreq_struct[:ifreq_struct.index("\0")]
168+ interface_name = ifreq_struct[:ifreq_struct.index(b"\0")]
169 if interface_name not in already_found:
170 already_found.add(interface_name)
171 yield interface_name
172@@ -121,7 +121,16 @@
173 """
174 mac_address = fcntl.ioctl(
175 sock.fileno(), SIOCGIFHWADDR, struct.pack("256s", interface[:15]))
176- return "".join(["%02x:" % ord(char) for char in mac_address[18:24]])[:-1]
177+ if _PY3:
178+ def to_int(char):
179+ # We have already bytes, so we do nothing
180+ return char
181+ else:
182+ def to_int(char):
183+ # We have a string in python 2 and need the int here.
184+ return ord(char)
185+ return "".join(["%02x:" % to_int(char)
186+ for char in mac_address[18:24]])[:-1]
187
188
189 def get_flags(sock, interface):
190@@ -147,13 +156,17 @@
191 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
192 socket.IPPROTO_IP)
193 for interface in get_active_interfaces(sock):
194- if interface in skipped_interfaces:
195- continue
196- if skip_vlan and "." in interface:
197- continue
198- if skip_alias and ":" in interface:
199- continue
200- interface_info = {"interface": interface}
201+ interface_string = interface.decode("ascii")
202+ if interface_string in skipped_interfaces:
203+ continue
204+ if skip_vlan and b"." in interface:
205+ continue
206+ if skip_alias and b":" in interface:
207+ continue
208+ # We keep values as byte strings for use in struct.pack() in
209+ # different getter methods, and only use the decoded value of the
210+ # interface name here.
211+ interface_info = {"interface": interface_string}
212 interface_info["ip_address"] = get_ip_address(sock, interface)
213 interface_info["mac_address"] = get_mac_address(sock, interface)
214 interface_info["broadcast_address"] = get_broadcast_address(
215@@ -175,9 +188,8 @@
216 Retrieves an array of information regarding the network activity per
217 network interface.
218 """
219- netdev = open(source_file, "r")
220- lines = netdev.readlines()
221- netdev.close()
222+ with open(source_file, "r") as netdev:
223+ lines = netdev.readlines()
224
225 # Parse out the column headers as keys.
226 _, receive_columns, transmit_columns = lines[1].split("|")
227@@ -227,7 +239,7 @@
228 * 0: The cable is not connected to the interface. We cannot measure
229 interface speed, but could if it was plugged in.
230 """
231- cmd_struct = struct.pack('I39s', ETHTOOL_GSET, '\x00' * 39)
232+ cmd_struct = struct.pack('I39s', ETHTOOL_GSET, b'\x00' * 39)
233 status_cmd = array.array('B', cmd_struct)
234 packed = struct.pack('16sP', interface_name, status_cmd.buffer_info()[0])
235
236
237=== modified file 'landscape/lib/tests/test_fs.py'
238--- landscape/lib/tests/test_fs.py 2016-06-15 16:42:17 +0000
239+++ landscape/lib/tests/test_fs.py 2017-03-13 13:20:43 +0000
240@@ -1,7 +1,11 @@
241+# -*- coding: utf-8 -*-
242+import codecs
243 import os
244 from mock import patch
245 import time
246
247+from twisted.python.compat import long
248+
249 from landscape.tests.helpers import LandscapeTest
250
251 from landscape.lib.fs import append_file, read_file, touch_file
252@@ -14,7 +18,16 @@
253 With no options L{read_file} reads the whole file passed as argument.
254 """
255 path = self.makeFile("foo")
256- self.assertEqual(read_file(path), "foo")
257+ self.assertEqual(read_file(path), b"foo")
258+
259+ def test_read_file_encoding(self):
260+ """
261+ With encoding L{read_file} reads the whole file passed as argument and
262+ decodes it.
263+ """
264+ utf8_content = codecs.encode(u"foo \N{SNOWMAN}", "utf-8")
265+ path = self.makeFile(utf8_content, mode="wb")
266+ self.assertEqual(read_file(path, encoding="utf-8"), u"foo ☃")
267
268 def test_read_file_with_limit(self):
269 """
270@@ -22,14 +35,23 @@
271 given limit.
272 """
273 path = self.makeFile("foo bar")
274- self.assertEqual(read_file(path, limit=3), " bar")
275+ self.assertEqual(read_file(path, limit=3), b" bar")
276+
277+ def test_read_file_with_limit_encoding(self):
278+ """
279+ With a positive limit L{read_file} reads only the bytes after the
280+ given limit.
281+ """
282+ utf8_content = codecs.encode(u"foo \N{SNOWMAN}", "utf-8")
283+ path = self.makeFile(utf8_content, mode="wb")
284+ self.assertEqual(read_file(path, limit=3, encoding="utf-8"), u" ☃")
285
286 def test_read_file_with_negative_limit(self):
287 """
288 With a negative limit L{read_file} reads only the tail of the file.
289 """
290 path = self.makeFile("foo bar from end")
291- self.assertEqual(read_file(path, limit=-3), "end")
292+ self.assertEqual(read_file(path, limit=-3), b"end")
293
294 def test_read_file_with_limit_bigger_than_file(self):
295 """
296@@ -37,8 +59,8 @@
297 file.
298 """
299 path = self.makeFile("foo bar from end")
300- self.assertEqual(read_file(path, limit=100), "foo bar from end")
301- self.assertEqual(read_file(path, limit=-100), "foo bar from end")
302+ self.assertEqual(read_file(path, limit=100), b"foo bar from end")
303+ self.assertEqual(read_file(path, limit=-100), b"foo bar from end")
304
305
306 class TouchFileTest(LandscapeTest):
307
308=== modified file 'landscape/lib/tests/test_network.py'
309--- landscape/lib/tests/test_network.py 2017-01-23 12:14:33 +0000
310+++ landscape/lib/tests/test_network.py 2017-03-13 13:20:43 +0000
311@@ -1,14 +1,13 @@
312 import array
313 import socket
314
315-from mock import patch, ANY
316+from mock import patch, ANY, mock_open
317 from subprocess import Popen, PIPE
318
319 from landscape.tests.helpers import LandscapeTest
320 from landscape.lib.network import (
321 get_network_traffic, get_active_device_info, get_active_interfaces,
322 get_fqdn, get_network_interface_speed)
323-from landscape.compat import StringIO
324
325
326 class NetworkInfoTest(LandscapeTest):
327@@ -24,6 +23,7 @@
328
329 device_info = get_active_device_info()
330 result = Popen(["/sbin/ifconfig"], stdout=PIPE).communicate()[0]
331+ result = result.decode('ascii')
332 interface_blocks = dict(
333 [(block.split()[0], block.upper()) for block in
334 filter(None, result.split("\n\n"))])
335@@ -62,7 +62,7 @@
336 def test_skip_vlan(self, mock_get_active_interfaces):
337 """VLAN interfaces are not reported by L{get_active_device_info}."""
338 mock_get_active_interfaces.side_effect = lambda sock: (
339- list(get_active_interfaces(sock)) + ["eth0.1"])
340+ list(get_active_interfaces(sock)) + [b"eth0.1"])
341 device_info = get_active_device_info()
342 mock_get_active_interfaces.assert_called_with(ANY)
343 interfaces = [i["interface"] for i in device_info]
344@@ -72,7 +72,7 @@
345 def test_skip_alias(self, mock_get_active_interfaces):
346 """Interface aliases are not reported by L{get_active_device_info}."""
347 mock_get_active_interfaces.side_effect = lambda sock: (
348- list(get_active_interfaces(sock)) + ["eth0:foo"])
349+ list(get_active_interfaces(sock)) + [b"eth0:foo"])
350 device_info = get_active_device_info()
351 interfaces = [i["interface"] for i in device_info]
352 self.assertNotIn("eth0:foo", interfaces)
353@@ -94,23 +94,25 @@
354 # This is a fake response observed to return the same interface several
355 # times (here, br1:priv)
356 response = (
357- "lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
358- "\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
359- "\x00\x00\x00\x00\x00\x00\x00eth1:pub\x00\x00\x00\x00\x00\x00\x00"
360- "\x00\x02\x00\x00\x00\xc8\xb4\xc4.\x00\x00\x00\x00\x00\x00\x00\x00"
361- "\x00\x00\x00\x00\x00\x00\x00\x00br1:metadata\x00\x00\x00\x00\x02"
362- "\x00\x00\x00\xa9\xfe\xa9\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00"
363- "\x00\x00\x00\x00\x00\x00\x00br1:0\x00\x00\x00\x00\x00\x00\x00\x00"
364- "\x00\x00\x00\x02\x00\x00\x00\xc9\x19\x1f\x1d\x00\x00\x00\x00\x00"
365- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00br1\x00\x00\x00\x00"
366- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\xc0\xa8d"
367- "\x1d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
368- "\x00br1:priv\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\xac"
369- "\x13\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
370- "\x00\x00\x00br1:priv\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00"
371- "\x00\xac\x13\x02A")
372+ b"lo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
373+ b"\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
374+ b"\x00\x00\x00\x00\x00\x00\x00eth1:pub\x00\x00\x00\x00\x00\x00\x00"
375+ b"\x00\x02\x00\x00\x00\xc8\xb4\xc4.\x00\x00\x00\x00\x00\x00\x00"
376+ b"\x00"
377+ b"\x00\x00\x00\x00\x00\x00\x00\x00br1:metadata\x00\x00\x00\x00\x02"
378+ b"\x00\x00\x00\xa9\xfe\xa9\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00"
379+ b"\x00\x00\x00\x00\x00\x00\x00br1:0\x00\x00\x00\x00\x00\x00\x00"
380+ b"\x00"
381+ b"\x00\x00\x00\x02\x00\x00\x00\xc9\x19\x1f\x1d\x00\x00\x00\x00\x00"
382+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00br1\x00\x00\x00\x00"
383+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\xc0\xa8d"
384+ b"\x1d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
385+ b"\x00br1:priv\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\xac"
386+ b"\x13\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
387+ b"\x00\x00\x00br1:priv\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00"
388+ b"\x00\xac\x13\x02A")
389
390- fake_array = array.array("B", response + "\0" * 4855)
391+ fake_array = array.array("B", response + b"\0" * 4855)
392
393 with patch("array.array") as mock_array:
394 mock_array.return_value = fake_array
395@@ -126,18 +128,19 @@
396 mock_unpack.assert_called_with("iL", ANY)
397
398 self.assertEqual(
399- ["lo", "eth1:pub", "br1:metadata", "br1:0", "br1", "br1:priv"],
400+ [b"lo", b"eth1:pub", b"br1:metadata",
401+ b"br1:0", b"br1", b"br1:priv"],
402 interfaces)
403
404- @patch("__builtin__.open")
405- def test_get_network_traffic(self, mock_open):
406+ def test_get_network_traffic(self):
407 """
408 Network traffic is assessed via reading /proc/net/dev, verify
409 the parsed output against a known sample.
410 """
411- mock_open.return_value = StringIO(test_proc_net_dev_output)
412- traffic = get_network_traffic()
413- mock_open.assert_called_with("/proc/net/dev", "r")
414+ m = mock_open(read_data=test_proc_net_dev_output)
415+ with patch('landscape.lib.network.open', m):
416+ traffic = get_network_traffic()
417+ m.assert_called_with("/proc/net/dev", "r")
418 self.assertEqual(traffic, test_proc_net_dev_parsed)
419
420
421@@ -221,7 +224,7 @@
422 # ioctl always succeeds
423 mock_unpack.return_value = (100, False)
424
425- result = get_network_interface_speed(sock, "eth0")
426+ result = get_network_interface_speed(sock, b"eth0")
427
428 mock_ioctl.assert_called_with(ANY, ANY, ANY)
429 mock_unpack.assert_called_with("12xHB28x", ANY)
430@@ -240,7 +243,7 @@
431 # ioctl always succeeds
432 mock_unpack.return_value = (65535, False)
433
434- result = get_network_interface_speed(sock, "eth0")
435+ result = get_network_interface_speed(sock, b"eth0")
436
437 mock_ioctl.assert_called_with(ANY, ANY, ANY)
438 mock_unpack.assert_called_with("12xHB28x", ANY)
439@@ -263,7 +266,7 @@
440 # ioctl always raises
441 mock_ioctl.side_effect = theerror
442
443- result = get_network_interface_speed(sock, "eth0")
444+ result = get_network_interface_speed(sock, b"eth0")
445
446 mock_ioctl.assert_called_with(ANY, ANY, ANY)
447
448@@ -285,7 +288,7 @@
449 # ioctl always raises
450 mock_ioctl.side_effect = theerror
451
452- result = get_network_interface_speed(sock, "eth0")
453+ result = get_network_interface_speed(sock, b"eth0")
454
455 mock_ioctl.assert_called_with(ANY, ANY, ANY)
456
457@@ -306,4 +309,4 @@
458 # ioctl always raises
459 mock_ioctl.side_effect = theerror
460
461- self.assertRaises(IOError, get_network_interface_speed, sock, "eth0")
462+ self.assertRaises(IOError, get_network_interface_speed, sock, b"eth0")
463
464=== modified file 'landscape/lib/tests/test_process.py'
465--- landscape/lib/tests/test_process.py 2017-03-09 10:13:30 +0000
466+++ landscape/lib/tests/test_process.py 2017-03-13 13:20:43 +0000
467@@ -25,7 +25,8 @@
468 os.mkdir(process_dir)
469
470 cmd_line = "/usr/bin/foo"
471- create_file(os.path.join(process_dir, "cmdline"), cmd_line)
472+ create_file(
473+ os.path.join(process_dir, "cmdline"), cmd_line, encoding="utf-8")
474
475 status = "\n".join([
476 "Name: foo",
477@@ -34,11 +35,12 @@
478 "Gid: 2000",
479 "VmSize: 3000",
480 "Ignored: value"])
481- create_file(os.path.join(process_dir, "status"), status)
482+ create_file(
483+ os.path.join(process_dir, "status"), status, encoding="utf-8")
484
485 stat_array = [str(index) for index in range(44)]
486 stat = " ".join(stat_array)
487- create_file(os.path.join(process_dir, "stat"), stat)
488+ create_file(os.path.join(process_dir, "stat"), stat, encoding="utf-8")
489
490 @mock.patch("landscape.lib.process.detect_jiffies", return_value=1)
491 @mock.patch("os.listdir")
492
493=== modified file 'landscape/lib/vm_info.py'
494--- landscape/lib/vm_info.py 2016-09-23 15:43:36 +0000
495+++ landscape/lib/vm_info.py 2017-03-13 13:20:43 +0000
496@@ -34,7 +34,7 @@
497 for filename in ("container_type", "systemd/container"):
498 path = os.path.join(run_path, filename)
499 if os.path.exists(path):
500- return read_file(path).strip()
501+ return read_file(path, encoding="utf-8").strip()
502 return ""
503
504
505@@ -52,7 +52,7 @@
506
507 def _get_vm_by_vendor(sys_vendor_path):
508 """Return the VM type string (possibly empty) based on the vendor."""
509- vendor = read_file(sys_vendor_path).lower()
510+ vendor = read_file(sys_vendor_path, encoding="utf-8").lower()
511 # Use lower-key string for vendors, since we do case-insentive match.
512 content_vendors_map = (
513 ("bochs", "kvm"),
514@@ -72,7 +72,8 @@
515 def _get_vm_legacy(root_path):
516 """Check if the host is virtualized looking at /proc/cpuinfo content."""
517 try:
518- cpuinfo = read_file(os.path.join(root_path, "proc/cpuinfo"))
519+ cpuinfo = read_file(
520+ os.path.join(root_path, "proc/cpuinfo"), encoding="utf-8")
521 except (IOError, OSError):
522 return ""
523

Subscribers

People subscribed via source and target branches

to all changes: