Remove sendfile support

pysendfile[1] was added as an optional dependency but the library
hasn't been maintained and has got no release since 2014.

What is worse, the sendfile implementation is not actually working
since The SendFileIterator class was removed[2]. (Follow-up[3] removed
the remaining reference to the class). The broken implementation has
not been detected because the client is not currently used to upload
contents.

Remove the incomplete implementation to get rid of the dependency on
the unmaintained library.

[1] https://pypi.org/project/pysendfile/
[2] 76c3620c7e
[3] 0c151d7d7e

Closes-Bug: #2062573
Change-Id: Ia4784f59d16660e8d40c0e409f092ac4e46870b4
This commit is contained in:
Takashi Kajinami 2024-04-19 21:26:24 +09:00
parent 0bcd6cd71c
commit 205ca3336a
4 changed files with 4 additions and 82 deletions

View File

@ -23,9 +23,8 @@ External Requirements Affecting Glance
Like other OpenStack projects, Glance uses some external libraries for a subset Like other OpenStack projects, Glance uses some external libraries for a subset
of its features. Some examples include the ``qemu-img`` utility used by the of its features. Some examples include the ``qemu-img`` utility used by the
tasks feature, ``sendfile`` to utilize the "zero-copy" way of copying data tasks feature, ``pydev`` to debug using popular IDEs, ``python-xattr`` for
faster, ``pydev`` to debug using popular IDEs, ``python-xattr`` for Image Cache Image Cache using "xattr" driver.
using "xattr" driver.
On the other hand, if ``dnspython`` is installed in the environment, Glance On the other hand, if ``dnspython`` is installed in the environment, Glance
provides a workaround to make it work with IPV6. provides a workaround to make it work with IPV6.

View File

@ -19,7 +19,6 @@
import collections.abc import collections.abc
import copy import copy
import errno
import functools import functools
import http.client import http.client
import os import os
@ -35,12 +34,6 @@ except ImportError:
import osprofiler.web import osprofiler.web
try:
import sendfile # noqa
SENDFILE_SUPPORTED = True
except ImportError:
SENDFILE_SUPPORTED = False
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import netutils from oslo_utils import netutils
@ -446,12 +439,6 @@ class BaseClient(object):
def _filelike(body): def _filelike(body):
return hasattr(body, 'read') return hasattr(body, 'read')
def _sendbody(connection, iter):
connection.endheaders()
for sent in iter:
# iterator has done the heavy lifting
pass
def _chunkbody(connection, iter): def _chunkbody(connection, iter):
connection.putheader('Transfer-Encoding', 'chunked') connection.putheader('Transfer-Encoding', 'chunked')
connection.endheaders() connection.endheaders()
@ -469,22 +456,15 @@ class BaseClient(object):
elif _filelike(body) or self._iterable(body): elif _filelike(body) or self._iterable(body):
c.putrequest(method, path) c.putrequest(method, path)
use_sendfile = self._sendable(body)
# According to HTTP/1.1, Content-Length and Transfer-Encoding # According to HTTP/1.1, Content-Length and Transfer-Encoding
# conflict. # conflict.
for header, value in headers.items(): for header, value in headers.items():
if use_sendfile or header.lower() != 'content-length': if header.lower() != 'content-length':
c.putheader(header, str(value)) c.putheader(header, str(value))
iter = utils.chunkreadable(body) iter = utils.chunkreadable(body)
if use_sendfile: _chunkbody(c, iter)
# send actual file without copying into userspace
_sendbody(c, iter)
else:
# otherwise iterate and chunk
_chunkbody(c, iter)
else: else:
raise TypeError('Unsupported image type: %s' % body.__class__) raise TypeError('Unsupported image type: %s' % body.__class__)
@ -528,22 +508,6 @@ class BaseClient(object):
except (socket.error, IOError) as e: except (socket.error, IOError) as e:
raise exception.ClientConnectionError(e) raise exception.ClientConnectionError(e)
def _seekable(self, body):
# pipes are not seekable, avoids sendfile() failure on e.g.
# cat /path/to/image | glance add ...
# or where add command is launched via popen
try:
os.lseek(body.fileno(), 0, os.SEEK_CUR)
return True
except OSError as e:
return (e.errno != errno.ESPIPE)
def _sendable(self, body):
return (SENDFILE_SUPPORTED and
hasattr(body, 'fileno') and
self._seekable(body) and
not self.use_ssl)
def _iterable(self, body): def _iterable(self, body):
return isinstance(body, collections.abc.Iterable) return isinstance(body, collections.abc.Iterable)

View File

@ -15,23 +15,11 @@
"""Stubouts, mocks and fixtures for the test suite""" """Stubouts, mocks and fixtures for the test suite"""
import os
try:
import sendfile
SENDFILE_SUPPORTED = True
except ImportError:
SENDFILE_SUPPORTED = False
import routes import routes
import webob import webob
from glance.api.middleware import context from glance.api.middleware import context
from glance.api.v2 import router from glance.api.v2 import router
import glance.common.client
DEBUG = False
def stub_out_store_server(stubs, base_dir, **kwargs): def stub_out_store_server(stubs, base_dir, **kwargs):
@ -49,23 +37,10 @@ def stub_out_store_server(stubs, base_dir, **kwargs):
def fileno(self): def fileno(self):
return 42 return 42
class FakeSendFile(object):
def __init__(self, req):
self.req = req
def sendfile(self, o, i, offset, nbytes):
os.lseek(i, offset, os.SEEK_SET)
prev_len = len(self.req.body)
self.req.body += os.read(i, nbytes)
return len(self.req.body) - prev_len
class FakeGlanceConnection(object): class FakeGlanceConnection(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.sock = FakeSocket() self.sock = FakeSocket()
self.stub_force_sendfile = kwargs.get('stub_force_sendfile',
SENDFILE_SUPPORTED)
def connect(self): def connect(self):
return True return True
@ -75,9 +50,6 @@ def stub_out_store_server(stubs, base_dir, **kwargs):
def putrequest(self, method, url): def putrequest(self, method, url):
self.req = webob.Request.blank(url) self.req = webob.Request.blank(url)
if self.stub_force_sendfile:
fake_sendfile = FakeSendFile(self.req)
stubs.Set(sendfile, 'sendfile', fake_sendfile.sendfile)
self.req.method = method self.req.method = method
def putheader(self, key, value): def putheader(self, key, value):
@ -118,15 +90,3 @@ def stub_out_store_server(stubs, base_dir, **kwargs):
def fake_image_iter(self): def fake_image_iter(self):
for i in self.source.app_iter: for i in self.source.app_iter:
yield i yield i
def fake_sendable(self, body):
force = getattr(self, 'stub_force_sendfile', None)
if force is None:
return self._stub_orig_sendable(body)
else:
if force:
assert glance.common.client.SENDFILE_SUPPORTED
return force
setattr(glance.common.client.BaseClient, '_stub_orig_sendable',
glance.common.client.BaseClient._sendable)

View File

@ -20,7 +20,6 @@ boto3>=1.9.199 # Apache-2.0
# Optional packages that should be installed when testing # Optional packages that should be installed when testing
PyMySQL>=0.7.6 # MIT License PyMySQL>=0.7.6 # MIT License
psycopg2>=2.8.4 # LGPL/ZPL psycopg2>=2.8.4 # LGPL/ZPL
pysendfile>=2.0.0;sys_platform!='win32' # MIT
xattr>=0.9.2;sys_platform!='win32' # MIT xattr>=0.9.2;sys_platform!='win32' # MIT
python-swiftclient>=3.2.0 # Apache-2.0 python-swiftclient>=3.2.0 # Apache-2.0
python-cinderclient>=4.1.0 # Apache-2.0 python-cinderclient>=4.1.0 # Apache-2.0