Move the last_bytes util method to libvirt

The only user of this util method is libvirt, so it should live there.
This is a precursor to re-writing its single caller to use privsep to
read bytes instead of chown'ing files.

Change-Id: Ice5cc70eeef3fbeee9a1a0ec0daa801c6f74480c
This commit is contained in:
Michael Still 2017-06-08 14:57:49 +10:00 committed by Tony Breeds
parent 24a96f4cbe
commit ddfd93a38c
6 changed files with 59 additions and 53 deletions

View File

@ -779,33 +779,6 @@ class MkfsTestCase(test.NoDBTestCase):
'/my/swap/block/dev', run_as_root=False)
class LastBytesTestCase(test.NoDBTestCase):
"""Test the last_bytes() utility method."""
def setUp(self):
super(LastBytesTestCase, self).setUp()
self.f = six.BytesIO(b'1234567890')
def test_truncated(self):
self.f.seek(0, os.SEEK_SET)
out, remaining = utils.last_bytes(self.f, 5)
self.assertEqual(out, b'67890')
self.assertGreater(remaining, 0)
def test_read_all(self):
self.f.seek(0, os.SEEK_SET)
out, remaining = utils.last_bytes(self.f, 1000)
self.assertEqual(out, b'1234567890')
self.assertFalse(remaining > 0)
def test_seek_too_far_real_file(self):
# StringIO doesn't raise IOError if you see past the start of the file.
with tempfile.TemporaryFile() as flo:
content = b'1234567890'
flo.write(content)
self.assertEqual((content, 0), utils.last_bytes(flo, 1000))
class MetadataToDictTestCase(test.NoDBTestCase):
def test_metadata_to_dict(self):
self.assertEqual(utils.metadata_to_dict(

View File

@ -177,3 +177,7 @@ def chown_for_id_maps(path, id_maps):
def get_arch(image_meta):
return libvirt_utils.get_arch(image_meta)
def last_bytes(file_like_object, num):
return libvirt_utils.last_bytes(file_like_object, num)

View File

@ -939,3 +939,32 @@ sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
disk_path, format = libvirt_utils.find_disk(guest)
self.assertEqual('/test/disk', disk_path)
self.assertEqual('ploop', format)
class LastBytesTestCase(test.NoDBTestCase):
"""Test the last_bytes() utility method."""
def setUp(self):
super(LastBytesTestCase, self).setUp()
self.f = six.BytesIO(b'1234567890')
def test_truncated(self):
self.f.seek(0, os.SEEK_SET)
out, remaining = libvirt_utils.last_bytes(self.f, 5)
self.assertEqual(out, b'67890')
self.assertGreater(remaining, 0)
def test_read_all(self):
self.f.seek(0, os.SEEK_SET)
out, remaining = libvirt_utils.last_bytes(self.f, 1000)
self.assertEqual(out, b'1234567890')
self.assertFalse(remaining > 0)
def test_seek_too_far_real_file(self):
# StringIO doesn't raise IOError if you see past the start of the file.
with tempfile.TemporaryFile() as flo:
content = b'1234567890'
flo.write(content)
self.assertEqual(
(content, 0),
libvirt_utils.last_bytes(flo, 1000))

View File

@ -20,7 +20,6 @@
import contextlib
import copy
import datetime
import errno
import functools
import hashlib
import inspect
@ -837,30 +836,6 @@ def mkfs(fs, path, label=None, run_as_root=False):
execute(*args, run_as_root=run_as_root)
def last_bytes(file_like_object, num):
"""Return num bytes from the end of the file, and remaining byte count.
:param file_like_object: The file to read
:param num: The number of bytes to return
:returns: (data, remaining)
"""
try:
file_like_object.seek(-num, os.SEEK_END)
except IOError as e:
# seek() fails with EINVAL when trying to go before the start of the
# file. It means that num is larger than the file size, so just
# go to the start.
if e.errno == errno.EINVAL:
file_like_object.seek(0, os.SEEK_SET)
else:
raise
remaining = file_like_object.tell()
return (file_like_object.read(), remaining)
def metadata_to_dict(metadata, include_deleted=False):
result = {}
for item in metadata:

View File

@ -2839,7 +2839,8 @@ class LibvirtDriver(driver.ComputeDriver):
while bytes_to_read > 0 and os.path.exists(path):
libvirt_utils.chown(path, os.getuid())
with libvirt_utils.file_open(path, 'rb') as fp:
read_log_data, remaining = utils.last_bytes(fp, bytes_to_read)
read_log_data, remaining = libvirt_utils.last_bytes(
fp, bytes_to_read)
# We need the log file content in chronological order,
# that's why we *prepend* the log data.
log_data = read_log_data + log_data

View File

@ -532,3 +532,27 @@ def is_mounted(mount_path, source=None):
def is_valid_hostname(hostname):
return re.match(r"^[\w\-\.:]+$", hostname)
def last_bytes(file_like_object, num):
"""Return num bytes from the end of the file, and remaining byte count.
:param file_like_object: The file to read
:param num: The number of bytes to return
:returns: (data, remaining)
"""
try:
file_like_object.seek(-num, os.SEEK_END)
except IOError as e:
# seek() fails with EINVAL when trying to go before the start of
# the file. It means that num is larger than the file size, so
# just go to the start.
if e.errno == errno.EINVAL:
file_like_object.seek(0, os.SEEK_SET)
else:
raise
remaining = file_like_object.tell()
return (file_like_object.read(), remaining)