This plugin simulates errors in pread, pwrite, and extents. This is
useful for testing error handling in NBD clients, and understanding how
plugin exceptions are reported to the NBD client.
---
plugins/python/Makefile.am | 1 +
plugins/python/examples/error.py | 77 ++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)
create mode 100644 plugins/python/examples/error.py
diff --git a/plugins/python/Makefile.am b/plugins/python/Makefile.am
index eecd7e89..e6e6c9e6 100644
--- a/plugins/python/Makefile.am
+++ b/plugins/python/Makefile.am
@@ -27,20 +27,21 @@
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
include $(top_srcdir)/common-rules.mk
EXTRA_DIST = \
nbdkit-python-plugin.pod \
examples/file.py \
+ examples/error.py \
examples/imageio.py \
examples/ramdisk.py \
examples/url.py \
$(NULL)
if HAVE_PYTHON
plugin_LTLIBRARIES = nbdkit-python-plugin.la
nbdkit_python_plugin_la_SOURCES = \
diff --git a/plugins/python/examples/error.py b/plugins/python/examples/error.py
new file mode 100644
index 00000000..0331a83e
--- /dev/null
+++ b/plugins/python/examples/error.py
@@ -0,0 +1,77 @@
+# Example Python plugin.
+#
+# This plugin simulates errors for testing NBD client error hanlding.
+# Every odd call will fail, and every even call will succeed, unless
+# there a real error accesing the specified file.
+#
+# This example can be freely used for any purpose.
+
+# Run it from the build directory like this:
+#
+# ./nbdkit -f -v python ./plugins/python/examples/error.py file=test.img
+#
+# Or run it after installing nbdkit like this:
+#
+# nbdkit -f -v python ./plugins/python/examples/error.py file=test.img
+#
+# The -f -v arguments are optional. They cause the server to stay in
+# the foreground and print debugging, which is useful when testing.
+
+import os
+
+API_VERSION = 2
+
+filename = None
+calls = 0
+
+
+def config(key, value):
+ global filename
+ assert key == "file"
+ filename = value
+
+
+def open(readonly):
+ flags = os.O_RDONLY if readonly else os.O_RDWR
+ return {"fd": os.open(filename, flags)}
+
+
+def can_extents(h):
+ return True
+
+
+def get_size(h):
+ return os.stat(h["fd"]).st_size
+
+
+def extents(h, count, offset, flags):
+ global calls
+ calls += 1
+ if calls % 2:
+ raise RuntimeError(f"extents error offset={offset} count={count}")
+
+ # We don't really support extents, so we report the entire file as
+ # data.
+ return [(offset, count, 0)]
+
+
+def pread(h, buf, offset, flags):
+ global calls
+ calls += 1
+ if calls % 2:
+ raise RuntimeError(f"pread error offset={offset} count={len(buf)}")
+
+ os.lseek(h["fd"], offset, os.SEEK_SET)
+ n = os.readv(h['fd'], [buf])
+ assert n == len(buf)
+
+
+def pwrite(h, buf, offset, flags):
+ global calls
+ calls += 1
+ if calls % 2:
+ raise RuntimeError(f"pwrite error offset={offset} count={len(buf)}")
+
+ os.lseek(h["fd"], offset, os.SEEK_SET)
+ n = os.writev(h['fd'], [buf])
+ assert n == len(buf)
--
2.34.1