diff --git a/vrtManager/connection.py b/vrtManager/connection.py
index 85fae4f1..56ece172 100644
--- a/vrtManager/connection.py
+++ b/vrtManager/connection.py
@@ -1039,17 +1039,20 @@ class wvmConnect(object):
"""A new raw volume in pool holding data, written through a libvirt
stream, which every connection type carries. A failed write leaves
no volume."""
+ stream = self.wvm.newStream(0)
vol = pool.createXML(
f"{util.xml_escape(name)}{len(data)}"
"",
0,
)
try:
- stream = self.wvm.newStream(0)
vol.upload(stream, 0, len(data), 0)
stream.sendAll(lambda _stream, size, buffer: buffer.read(size), io.BytesIO(data))
stream.finish()
except Exception:
+ # libvirt: a stream that did not finish is aborted before it is freed
+ with contextlib.suppress(libvirtError):
+ stream.abort()
vol.delete(0)
raise
return vol
diff --git a/vrtManager/instance.py b/vrtManager/instance.py
index 543c711d..dae75caf 100644
--- a/vrtManager/instance.py
+++ b/vrtManager/instance.py
@@ -427,9 +427,14 @@ class wvmInstances(wvmConnect):
return None
chunks = []
stream = conn.wvm.newStream(0)
- vol.download(stream, 0, 0, 0)
- stream.recvAll(lambda _stream, data, out: out.append(data), chunks)
- stream.finish()
+ try:
+ vol.download(stream, 0, 0, 0)
+ stream.recvAll(lambda _stream, data, out: out.append(data), chunks)
+ stream.finish()
+ except Exception:
+ with contextlib.suppress(libvirtError):
+ stream.abort()
+ raise
self.upload_volume(target, name, b"".join(chunks))
return name
diff --git a/vrtManager/tests/test_cloud_init_seed.py b/vrtManager/tests/test_cloud_init_seed.py
index 13fe68ba..6717655b 100644
--- a/vrtManager/tests/test_cloud_init_seed.py
+++ b/vrtManager/tests/test_cloud_init_seed.py
@@ -78,6 +78,7 @@ class UploadVolumeTestCase(unittest.TestCase):
with self.assertRaises(RuntimeError):
conn.upload_volume(pool, "a.iso", b"data")
pool.createXML.return_value.delete.assert_called_once_with(0)
+ conn.wvm.newStream.return_value.abort.assert_called_once_with()
class DeleteWithDisksTestCase(unittest.TestCase):
diff --git a/vrtManager/tests/test_migration.py b/vrtManager/tests/test_migration.py
index 13f2999a..0656533d 100644
--- a/vrtManager/tests/test_migration.py
+++ b/vrtManager/tests/test_migration.py
@@ -465,6 +465,14 @@ class NvramCopyTestCase(unittest.TestCase):
copy.upload.assert_called_once_with(self.dest.wvm.newStream.return_value, 0, 4, 0)
self.assertEqual(self.host.defined, ["vm"])
+ def test_a_failed_download_aborts_its_stream(self):
+ stream = self.source.wvm.newStream.return_value
+ stream.recvAll.side_effect = libvirt.libvirtError("download failed")
+ with self.assertRaises(libvirt.libvirtError):
+ self.move()
+ stream.abort.assert_called_once_with()
+ self.dest_pool.createXML.assert_not_called()
+
def test_a_shared_directory_is_not_copied(self):
self.shared.stop()
with mock.patch.object(wvmInstances, "same_directory", return_value=True):