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