""" Reprouce hang when using io.FileIO with inaccessible NFS server. Usage: python fileio_nfs_test.py mountpoint/filename server_address Example run: mount -t example.com mnt touch mnt/file python fileio_nfs_test.py mnt/file example.com """ import io import logging import mmap import os import subprocess import sys import threading import time from contextlib import closing filename = sys.argv[1] server = sys.argv[2] logging.basicConfig( level=logging.INFO, format="%(asctime)s - (%(threadName)s) - %(message)s") done = threading.Event() ready = threading.Event() def canary(): logging.info("Blocking access to storage") rule = ["-p", "tcp", "-d", server, "--dport", "2049", "-j", "DROP"] add_rule = ["iptables", "-A", "OUTPUT"] + rule check_rule = ["iptables", "-C", "OUTPUT"] + rule del_rule = ["iptables", "-D", "OUTPUT"] + rule subprocess.check_call(add_rule) logging.info("If this test is hang, please run: %s", " ".join(del_rule)) ready.set() for i in range(30): logging.info("check %d", i) if done.wait(1): break logging.info("Unblocking access to storage") if subprocess.call(check_rule, stderr=subprocess.PIPE) == 0: subprocess.check_call(del_rule) logging.info("pid=%d", os.getpid()) logging.info("Opening %s", filename) fd = os.open(filename, os.O_RDWR | os.O_DIRECT) logging.info("OK fd=%d", fd) logging.info("Starting canary thread") threading.Thread(target=canary, name="Canary").start() ready.wait() # For some reason adding iptables rule is not applied immediatelly. logging.info("Waiting until storage is blocked...") time.sleep(10) logging.info("Opening io.FileIO") with closing(io.FileIO(fd, "r+", closefd=True)) as file: logging.info("OK") logging.info("Creating mmap") with closing(mmap.mmap(-1, 1024 * 16, mmap.MAP_SHARED)) as buf: logging.info("OK") logging.info("Filling mmap") buf.write("x" * len(buf)) logging.info("OK") logging.info("Writing mmap to storage") pos = 0 while pos < len(buf): wbuf = buffer(buf, pos) pos += file.write(wbuf) logging.info("OK") logging.info("Syncing") os.fsync(file.fileno()) logging.info("OK") logging.info("Done") done.set()