Bug 1694972 - Reading same image on block storage concurrently using vdsm HTTP server may fail one of the requests
Summary: Reading same image on block storage concurrently using vdsm HTTP server may f...
Keywords:
Status: CLOSED CURRENTRELEASE
Alias: None
Product: vdsm
Classification: oVirt
Component: Core
Version: 4.30.12
Hardware: Unspecified
OS: Unspecified
high
medium
Target Milestone: ovirt-4.4.1
: 4.40.21
Assignee: Nir Soffer
QA Contact: Nikolai Sednev
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2019-04-02 08:06 UTC by Kobi Hakimi
Modified: 2020-08-05 06:25 UTC (History)
6 users (show)

Fixed In Version: vdsm-4.40.21
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2020-08-05 06:25:19 UTC
oVirt Team: Storage
Embargoed:
sbonazzo: ovirt-4.5?


Attachments (Terms of Use)
vdsm log (6.74 MB, text/plain)
2019-04-02 08:16 UTC, Kobi Hakimi
no flags Details
engine log (1.32 MB, text/plain)
2019-04-02 08:17 UTC, Kobi Hakimi
no flags Details


Links
System ID Private Priority Status Summary Last Updated
oVirt gerrit 109721 0 master MERGED image: Allow single uploadImageToStream per image 2020-08-03 12:15:15 UTC
oVirt gerrit 109753 0 master MERGED download: Add stress test for vdsm http server 2020-08-03 12:15:15 UTC

Description Kobi Hakimi 2019-04-02 08:06:03 UTC
Description of problem:
seldom it fails to transfer a volume to the engine thus the second host deployed not as hosted engine

Version-Release number of selected component (if applicable):
ovirt-imageio-proxy-1.5.1-0.el7ev.noarch
ovirt-imageio-common-1.5.1-0.el7ev.x86_64
ovirt-imageio-proxy-setup-1.5.1-0.el7ev.noarch


How reproducible:
sometimes

Steps to Reproduce:
1. deploy hosted engine environment
2. add 2 secondary hosted engine hosts

Actual results:
first and second hosts will deploy as hosted engine but the third one will deploy as a regular host.

Expected results:
all should deploy as hosted engine.

Additional info:
vdsm.log:
2019-04-02 02:07:41,425+0300 INFO  (Thread-8) [vdsm.api] START uploadImageToStream(methodArgs={'length': 20480, 'fileObj': <socket._fileobject object at 0x7f78a43b9a50>}, callback=<function setCallback at 0x7f78c41080c8>, startEvent=<threading._Event object at 0x7f78a428d8d0>, spUUID='b780e05d-13a5-4b34-8ca6-dcd1e3db6e79', sdUUID='04be3824-bc78-4be0-bcdb-c9397e054cc0', imgUUID='5cdbfac5-25c7-4b3b-a57b-141fee6d728b', volUUID='b45c1a18-2701-4c0a-b1b3-9552792c9309') from=internal, task_id=4b79d567-f2e6-4d26-9c35-c7f9579e54fc (api:48)
2019-04-02 02:07:41,442+0300 INFO  (Thread-8) [vdsm.api] FINISH uploadImageToStream return=None from=internal, task_id=4b79d567-f2e6-4d26-9c35-c7f9579e54fc (api:54)
2019-04-02 02:07:41,448+0300 INFO  (tasks/4) [storage.StorageDomain] Removing image run directory '/var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b' (blockSD:1448)
2019-04-02 02:07:41,448+0300 INFO  (tasks/4) [storage.fileUtils] Removing directory: /var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b (fileUtils:180)
2019-04-02 02:07:41,493+0300 INFO  (tasks/0) [storage.ThreadPool.WorkerThread] START task 4b79d567-f2e6-4d26-9c35-c7f9579e54fc (cmd=<bound method Task.commit of <vdsm.storage.task.Task instance at 0x7f78a423cfc8>>, args=None) (threadPool:208)
2019-04-02 02:07:41,658+0300 INFO  (tasks/4) [storage.LVM] Deactivating lvs: vg=04be3824-bc78-4be0-bcdb-c9397e054cc0 lvs=['b45c1a18-2701-4c0a-b1b3-9552792c9309'] (lvm:1422)
2019-04-02 02:07:41,723+0300 INFO  (tasks/0) [storage.Image] chain=b45c1a18-2701-4c0a-b1b3-9552792c9309 (top)  (image:1353)
2019-04-02 02:07:41,723+0300 INFO  (tasks/0) [storage.LVM] Refreshing active lvs: vg=04be3824-bc78-4be0-bcdb-c9397e054cc0 lvs=['b45c1a18-2701-4c0a-b1b3-9552792c9309'] (lvm:1410)
2019-04-02 02:07:41,775+0300 INFO  (jsonrpc/7) [api.host] START getAllVmStats() from=::1,45232 (api:48)
2019-04-02 02:07:41,778+0300 INFO  (jsonrpc/7) [api.host] FINISH getAllVmStats return={'status': {'message': 'Done', 'code': 0}, 'statsList': (suppressed)} from=::1,45232 (api:54)
2019-04-02 02:07:41,779+0300 INFO  (jsonrpc/7) [jsonrpc.JsonRpcServer] RPC call Host.getAllVmStats succeeded in 0.00 seconds (__init__:312)
2019-04-02 02:07:41,787+0300 INFO  (jsonrpc/0) [api.host] START getAllVmIoTunePolicies() from=::1,45232 (api:48)
2019-04-02 02:07:41,787+0300 INFO  (jsonrpc/0) [api.host] FINISH getAllVmIoTunePolicies return={'status': {'message': 'Done', 'code': 0}, 'io_tune_policies_dict': {'11991d38-e07e-4883-81fa-43c91ea4b92e': {'policy': [], 'current_values': [{'ioTune': {'write_bytes_sec': 0L, 'total_iops_sec': 0L, 'read_iops_sec': 0L, 'read_bytes_sec': 0L, 'write_iops_sec': 0L, 'total_bytes_sec': 0L}, 'path': '/var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/1631df8d-c04f-4b76-b013-cda6cdbf45e7/79663f71-d452-4c7b-9834-5c21bc1053bb', 'name': 'vda'}]}}} from=::1,45232 (api:54)
2019-04-02 02:07:41,788+0300 INFO  (jsonrpc/0) [jsonrpc.JsonRpcServer] RPC call Host.getAllVmIoTunePolicies succeeded in 0.00 seconds (__init__:312)
2019-04-02 02:07:41,872+0300 INFO  (Thread-7) [vds.http.Server] Request handler for ::ffff:10.46.16.247:49588 stopped (http:312)
2019-04-02 02:07:41,922+0300 INFO  (tasks/0) [storage.StorageDomain] Creating image run directory '/var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b' (blockSD:1424)
2019-04-02 02:07:41,922+0300 INFO  (tasks/0) [storage.fileUtils] Creating directory: /var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b mode: None (fileUtils:199)
2019-04-02 02:07:41,922+0300 INFO  (tasks/0) [storage.StorageDomain] Creating symlink from /dev/04be3824-bc78-4be0-bcdb-c9397e054cc0/b45c1a18-2701-4c0a-b1b3-9552792c9309 to /var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b/b45c1a18-2701-4c0a-b1b3-9552792c9309 (blockSD:1429)
2019-04-02 02:07:41,935+0300 INFO  (tasks/4) [storage.ThreadPool.WorkerThread] FINISH task 2b23acec-57ac-4058-82fb-efb75524ad49 (threadPool:210)
2019-04-02 02:07:41,945+0300 ERROR (tasks/0) [storage.ImageSharing] partial data 0 from 20480 (imageSharing:122)
2019-04-02 02:07:41,946+0300 INFO  (tasks/0) [storage.StorageDomain] Removing image run directory '/var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b' (blockSD:1448)
2019-04-02 02:07:41,946+0300 INFO  (tasks/0) [storage.fileUtils] Removing directory: /var/run/vdsm/storage/04be3824-bc78-4be0-bcdb-c9397e054cc0/5cdbfac5-25c7-4b3b-a57b-141fee6d728b (fileUtils:180)
2019-04-02 02:07:42,154+0300 ERROR (tasks/0) [storage.TaskManager.Task] (Task='4b79d567-f2e6-4d26-9c35-c7f9579e54fc') Unexpected error (task:875)
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/vdsm/storage/task.py", line 882, in _run
    return fn(*args, **kargs)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/task.py", line 336, in run
    return self.cmd(*self.argslist, **self.argsdict)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/securable.py", line 79, in wrapper
    return method(self, *args, **kwargs)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/sp.py", line 1793, in uploadImageToStream
    return img.copyFromImage(methodArgs, sdUUID, imgUUID, volUUID)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/image.py", line 1389, in copyFromImage
    imageSharing.copyFromImage(vol.getVolumePath(), methodArgs)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/imageSharing.py", line 104, in copyFromImage
    _copyData(p.stdout, fileObj, bytes_left)
  File "/usr/lib/python2.7/site-packages/vdsm/storage/imageSharing.py", line 123, in _copyData
    raise se.MiscFileReadException(error)
MiscFileReadException: Internal file read failure: ('partial data 0 from 20480',)
2019-04-02 02:07:42,155+0300 INFO  (Thread-8) [vds.http.Server] Request handler for ::ffff:10.46.16.247:35434 stopped (http:312)
2019-04-02 02:07:42,229+0300 INFO  (tasks/0) [storage.ThreadPool.WorkerThread] FINISH task 4b79d567-f2e6-4d26-9c35-c7f9579e54fc (threadPool:210)

Comment 1 Kobi Hakimi 2019-04-02 08:16:29 UTC
Created attachment 1550916 [details]
vdsm log

Comment 2 Kobi Hakimi 2019-04-02 08:17:58 UTC
Created attachment 1550917 [details]
engine log

Comment 3 Kobi Hakimi 2019-04-02 08:19:28 UTC
ovirt-engine-4.3.3.1-0.1.el7.noarch
vdsm-4.30.12-1.el7ev.x86_64

Comment 4 Sandro Bonazzola 2019-04-03 06:50:11 UTC
I'm not sure I understood what's the flow here. How volume transfer to the engine and 3rd host Hosted engine deployment are related?

Comment 5 Sandro Bonazzola 2019-04-03 07:07:44 UTC
Seems like this is not easily reproducible and has a workaround of just re-trying to re-deploy the host. Keeping it for further investigations.

Comment 6 Kobi Hakimi 2019-04-14 08:44:08 UTC
reproduced in 4.2.8-10 with ge-6 and ge-7:
ovirt-imageio-proxy-1.4.5-0.el7ev.noarch
ovirt-imageio-proxy-setup-1.4.5-0.el7ev.noarch
ovirt-imageio-common-1.4.5-0.el7ev.x86_64

Comment 7 Sandro Bonazzola 2019-04-24 07:32:03 UTC
Nir, can you please look into this? Looks not strictly related to hosted engine, seems to be a storage operation failing

Comment 8 Fred Rolland 2019-04-29 13:29:48 UTC
Daniel,
Is it something that was fixed lately?

Comment 9 Daniel Erez 2019-05-07 11:07:36 UTC
(In reply to Fred Rolland from comment #8)
> Daniel,
> Is it something that was fixed lately?

Not sure, it's not related to imageio.. Seems like an issue on downloading an OVF that isn't exist yet.

Comment 10 Nir Soffer 2020-06-17 12:35:10 UTC
Can you reproduce with 4.4?

Comment 11 Kobi Hakimi 2020-06-17 18:50:44 UTC
we didn't see this issue lately with 4.4
please close it as not reproduced.
if we will see it again we can reopen it ;-)

Comment 12 Nir Soffer 2020-06-17 20:27:27 UTC
We can close this, but I think that this is an issue in hosted engine.

hosted engine is trying to read OVF disk when the disk is empty.

The reason the call failed is that the caller asked for:

   'length': 20480

Which do not exist on storage according to:

    MiscFileReadException: Internal file read failure: ('partial data 0 from 20480',)


In this case the client will get "500 Internal server error" and the agent
should retry again in the next check interval.

When engine uploads OVF, it updates the OVF disk description:
Here is description from OVF disks in my setup:

$ grep OVF_STORE /rhev/data-center/mnt/nfs1\:_export_1/56ecc03c-4bb5-4792-8971-3c51ea924d2e/images/*/*.meta
/rhev/data-center/mnt/nfs1:_export_1/56ecc03c-4bb5-4792-8971-3c51ea924d2e/images/d6ac0ab5-bd39-4e19-8306-fd3bd36d64ac/adc60bd9-c0f3-4a49-a859-caf6562c79c4.meta:DESCRIPTION={"Updated":true,"Size":23040,"Last Updated":"Wed Jun 17 21:26:32 IDT 2020","Storage Domains":[{"uuid":"56ecc03c-4bb5-4792-8971-3c51ea924d2e"}],"Disk Description":"OVF_STORE"}
/rhev/data-center/mnt/nfs1:_export_1/56ecc03c-4bb5-4792-8971-3c51ea924d2e/images/d8932fa5-d229-44af-826a-19c4989243dd/f3401def-65a5-438f-a3a6-2f40162ae646.meta:DESCRIPTION={"Updated":true,"Size":23040,"Last Updated":"Wed Jun 17 21:26:32 IDT 2020","Storage Domains":[{"uuid":"56ecc03c-4bb5-4792-8971-3c51ea924d2e"}],"Disk Description":"OVF_STORE"}

So we have:

Disk ID: d6ac0ab5-bd39-4e19-8306-fd3bd36d64ac
volume ID: adc60bd9-c0f3-4a49-a859-caf6562c79c4
Updated: true
Size: 23040
Last Updated: Wed Jun 17 21:26:32 IDT 2020

Disk ID: d8932fa5-d229-44af-826a-19c4989243dd
Volume ID: f3401def-65a5-438f-a3a6-2f40162ae646
Updated: true
Size: 23040
Last Updated: Wed Jun 17 21:26:32 IDT 2020

By parsing the disk description, we can tell which disk contains the most 
recent OVF data (both look the same here), and the size of the data, and
use the right size in the request.


So this looks like 2 issues:
- Getting the wrong size of OVF volume
- Not handling expected failures

Comment 13 Nir Soffer 2020-06-17 21:46:11 UTC
Looking in vdsm log I think the issue is in vdsm after all.


1. First uploadImageToStream from ::ffff:10.46.16.247:49588 starting

2019-04-02 02:07:40,730+0300 INFO  (http) [vds.http.Server] Starting request handler for ::ffff:10.46.16.247:49588 (http:294)

2019-04-02 02:07:40,731+0300 INFO  (Thread-7) [vds.http.Server] Request handler for ::ffff:10.46.16.247:49588 started (http:304)

2019-04-02 02:07:40,733+0300 INFO  (Thread-7) [vdsm.api] START uploadImageToStream(methodArgs={'length': 20480, 'fileObj': <socket._fileobject object at 0x7f78a43b9d50>}, ca
llback=<function setCallback at 0x7f78c41082a8>, startEvent=<threading._Event object at 0x7f78c417d1d0>, spUUID='b780e05d-13a5-4b34-8ca6-dcd1e3db6e79', sdUUID='04be3824-bc78
-4be0-bcdb-c9397e054cc0', imgUUID='5cdbfac5-25c7-4b3b-a57b-141fee6d728b', volUUID='b45c1a18-2701-4c0a-b1b3-9552792c9309') from=internal, task_id=2b23acec-57ac-4058-82fb-efb75524ad49 (api:48)


2. This request starts task 2b23acec-57ac-4058-82fb-efb75524ad49, running in thread tasks/4

2019-04-02 02:07:40,908+0300 INFO  (tasks/4) [storage.ThreadPool.WorkerThread] START task 2b23acec-57ac-4058-82fb-efb75524ad49 (cmd=<bound method Task.commit of <vdsm.storage.task.Task instance at 0x7f78a41a9518>>, args=None) (threadPool:208)


3. One  second later, another uploadImageFromStream from ::ffff:10.46.16.247:35434

2019-04-02 02:07:41,414+0300 INFO  (http) [vds.http.Server] Starting request handler for ::ffff:10.46.16.247:35434 (http:294)

2019-04-02 02:07:41,425+0300 INFO  (Thread-8) [vdsm.api] START uploadImageToStream(methodArgs={'length': 20480, 'fileObj': <socket._fileobject object at 0x7f78a43b9a50>}, callback=<function setCallback at 0x7f78c41080c8>, startEvent=<threading._Event object at 0x7f78a428d8d0>, spUUID='b780e05d-13a5-4b34-8ca6-dcd1e3db6e79', sdUUID='04be3824-bc78-4be0-bcdb-c9397e054cc0', imgUUID='5cdbfac5-25c7-4b3b-a57b-141fee6d728b', volUUID='b45c1a18-2701-4c0a-b1b3-9552792c9309') from=internal, task_id=4b79d567-f2e6-4d26-9c35-c7f9579e54fc (api:48)


4. This request starts task 4b79d567-f2e6-4d26-9c35-c7f9579e54fc, running in thread tasks/0

2019-04-02 02:07:41,493+0300 INFO  (tasks/0) [storage.ThreadPool.WorkerThread] START task 4b79d567-f2e6-4d26-9c35-c7f9579e54fc (cmd=<bound method Task.commit of <vdsm.storage.task.Task instance at 0x7f78a423cfc8>>, args=None) (threadPool:208)

Both tasks are reading the volume b45c1a18-2701-4c0a-b1b3-9552792c9309


5. tasks/4 activates the volume

2019-04-02 02:07:41,164+0300 INFO  (tasks/4) [storage.LVM] Refreshing active lvs: vg=04be3824-bc78-4be0-bcdb-c9397e054cc0 lvs=['b45c1a18-2701-4c0a-b1b3-9552792c9309'] (lvm:1
410)


6. tasks/4 finish reading the image, so it deactivates the volume

2019-04-02 02:07:41,658+0300 INFO  (tasks/4) [storage.LVM] Deactivating lvs: vg=04be3824-bc78-4be0-bcdb-c9397e054cc0 lvs=['b45c1a18-2701-4c0a-b1b3-9552792c9309'] (lvm:1422)


7. tasks/0 found the the volume was active so it refresh the volume

2019-04-02 02:07:41,723+0300 INFO  (tasks/0) [storage.LVM] Refreshing active lvs: vg=04be3824-bc78-4be0-bcdb-c9397e054cc0 lvs=['b45c1a18-2701-4c0a-b1b3-9552792c9309'] (lvm:1

Because this happens 70 milliseconds after tasks/4 deactivated the volume,
tasks/0 does not see the deactivate lv. Running lvm command takes more than
70 milliseconds.

So at this point the lv was deactivated by thread tasks/4...


8. tasks/0 fail to read from non-existing device

2019-04-02 02:07:41,945+0300 ERROR (tasks/0) [storage.ImageSharing] partial data 0 from 20480 (imageSharing:122)


The issue is bad locking.

uploadImageToStream and downloadImageFromStream must take a lock on the volume
for the entire operation, so one task cannot deactivate a volume use by another task.

Looking in the code, uploadImageFromStream and downloadImageFromStream do use
a lock on the image:

1742     def uploadImageToStream(self, methodArgs, callback, startEvent, sdUUID,
1743                             imgUUID, volUUID=None):
1744         """
1745         Retrieves an image from to a given file the specified method
1746         and methodArgs.
1747         """
1748         while not startEvent.is_set():
1749             startEvent.wait()
1750
1751         img_ns = rm.getNamespace(sc.IMAGE_NAMESPACE, sdUUID)
1752         with rm.acquireResource(img_ns, imgUUID, rm.SHARED):
1753             try:
1754                 img = image.Image(self.poolPath)
1755                 return img.copyFromImage(methodArgs, sdUUID, imgUUID, volUUID)
1756             finally:
1757                 callback()
1758
1759     def downloadImageFromStream(self, methodArgs, callback, sdUUID, imgUUID,
1760                                 volUUID=None):
1761         """
1762         Download an image from a stream.
1763         """
1764         img_ns = rm.getNamespace(sc.IMAGE_NAMESPACE, sdUUID)
1765         with rm.acquireResource(img_ns, imgUUID, rm.EXCLUSIVE):
1766             try:
1767                 img = image.Image(self.poolPath)
1768                 return img.copyToImage(methodArgs, sdUUID, imgUUID, volUUID)
1769             finally:
1770                 callback()

downloadImageFromStream takes exclusive lock, so there is no race between upload
and download, but uploadImageToStream takes a shared lock, so we can have multiple
readers.

The issue is that Image.copyFromImage is activating the volume before the copy
and deactivating the volume after the copy.

 985     def copyFromImage(self, methodArgs, sdUUID, imgUUID, volUUID):
 986         domain = sdCache.produce(sdUUID)
 987 
 988         vol = self._activateVolumeForImportExport(domain, imgUUID, volUUID)
 989         try:
 990             imageSharing.copyFromImage(vol.getVolumePath(), methodArgs)
 991         finally:
 992             domain.deactivateImage(imgUUID)

So while 2 tasks are holding a shared lock, they activate and deactivate the same volume,
so one task can deactivate a volume just before the other task try to read it.

Looks like we can allow only single reader for uploadImageToStream. If we want to
allow more than one reader we need to implement reference counting so avoid
uncontrolled deactivation.

Comment 14 Nir Soffer 2020-06-17 21:47:32 UTC
Moving to 4.4.2 since this is an easy fix.

Comment 15 Nikolai Sednev 2020-07-14 08:59:01 UTC
Works for me on latest 
ovirt-engine-4.4.1.8-0.7.el8ev.noarch

Reported issue no longer exists.

Comment 16 Sandro Bonazzola 2020-08-05 06:25:19 UTC
This bugzilla is included in oVirt 4.4.1 release, published on July 8th 2020.

Since the problem described in this bug report should be resolved in oVirt 4.4.1 release, it has been closed with a resolution of CURRENT RELEASE.

If the solution does not work for you, please open a new bug report.


Note You need to log in before you can comment on or make changes to this bug.