On Mon, May 22, 2017 at 12:57:15PM +0200, Michal Privoznik wrote:
Sparse streams are not that straight forward to use for the very
first time. Especially the sparseRecvAll() and sparseSendAll()
methods which expects callbacks. What we can do to make it easier
for developers is to have an example where they can take an
inspiration from.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
examples/sparsestream.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
create mode 100755 examples/sparsestream.py
diff --git a/examples/sparsestream.py b/examples/sparsestream.py
new file mode 100755
index 0000000..b572a31
--- /dev/null
+++ b/examples/sparsestream.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# Example of sparse streams usage
+#
+# Authors:
+# Michal Privoznik <mprivozn(a)redhat.com>
+
Don't worry, we can handle Unicode.
+import libvirt, sys, os
+
+def bytesWriteHandler(stream, buf, opaque):
+ fd = opaque
Kind of pointless line, but I guess you wanted to show a point.
+ return os.write(fd, buf)
+
+def bytesReadHandler(stream, nbytes, opaque):
+ fd = opaque
+ return os.read(fd, nbytes)
+
+def recvSkipHandler(stream, length, opaque):
+ fd = opaque
+ cur = os.lseek(fd, length, os.SEEK_CUR)
+ return os.ftruncate(fd, cur)
+
+def sendSkipHandler(stream, length, opaque):
+ fd = opaque
+ return os.lseek(fd, length, os.SEEK_CUR)
+
+def holeHandler(stream, opaque):
+ fd = opaque
+ cur = os.lseek(fd, 0, os.SEEK_CUR)
+
+ try:
+ try:
+ data = os.lseek(fd, cur, os.SEEK_DATA)
The only way I saw this raise an exception is when you are exactly at
the end of the file. That's something that can be checked for *and* it
raises precisely OSError(6).
+ # There are three options:
+ # 1) data == cur; @cur is in data
+ # 2) data > cur; @cur is in a hole, next data at @data
+ # 3) data < 0; either @cur is in trailing hole, or @cur is beyond EOF.
+ except:
+ # case 3
+ inData = False
+ eof = os.lseek(fd, 0, os.SEEK_END)
+ if (eof < cur):
+ raise RuntimeError("Current position in file after EOF: %d" %
cur)
+ sectionLen = eof - cur
+ else:
+ if (data > cur):
+ # case 2
+ inData = False
+ sectionLen = data - cur
+ else:
+ # case 1
+ inData = True
+
+ # We don't know where does the next hole start. Let's find out.
+ # Here we get the same options as above
+ try:
+ hole = os.lseek(fd, data, os.SEEK_HOLE)
+ except:
+ # case 3. But wait a second. There is always a trailing hole.
+ # Do the best what we can here
+ raise RuntimeError("No trailing hole")
+
+ if (hole == data):
+ # case 1. Again, this is suspicious. The reason we are here is
+ # because we are in data. But at the same time we are in a
+ # hole. WAT?
+ raise RuntimeError("Impossible happened")
+ else:
+ # case 2
+ sectionLen = hole - data
+ finally:
+ os.lseek(fd, cur, os.SEEK_SET)
I would drop the exception handlers (maybe keep the one for that one
os.lseek) above there. What is the point in seeking to the previous
position when something is wrong and the program is ending anyway.
+ return [inData, sectionLen]
+
+def download(vol, st, filename):
+ offset = 0
+ length = 0
+
+ try:
+ fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, mode=0o0660)
+ vol.download(st, offset, length, libvirt.VIR_STORAGE_VOL_DOWNLOAD_SPARSE_STREAM)
+ st.sparseRecvAll(bytesWriteHandler, recvSkipHandler, fd)
+ except OSError:
+ print(sys.exc_info()[0])
Again, 'OSError as e', but I would not except such program to cleanup
after itself (e.g. I would like to, for example, kill it and see the
part that was output in the file, like you would do with 'cp', 'cat',
etc.) Not only will it clean up the code, but it will also behave as
other programs (i.e. as expected).
+ os.unlink(filename)
+
+ os.close(fd)
+
What does closing after unlink do on windows? :D
Other than that it looks okay.