Commit 9893f57e authored by Jean Bez's avatar Jean Bez
Browse files

Include I/O forwarding tests

parent a1a3a04d
Loading
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -48,6 +48,13 @@ gkfs_add_python_test(
    SOURCE shell/
)

gkfs_add_python_test(
    NAME forwarding
    PYTHON_VERSION 3.6
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration
    SOURCE forwarding/
)

if(GKFS_INSTALL_TESTS)
    install(DIRECTORY harness
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration
@@ -90,7 +97,6 @@ if(GKFS_INSTALL_TESTS)
            PATTERN ".pytest_cache" EXCLUDE
    )


    install(DIRECTORY shell
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration
        FILES_MATCHING
@@ -98,4 +104,12 @@ if(GKFS_INSTALL_TESTS)
            PATTERN "__pycache__" EXCLUDE
            PATTERN ".pytest_cache" EXCLUDE
    )

    install(DIRECTORY forwarding
        DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration
        FILES_MATCHING
            REGEX ".*\\.py"
            PATTERN "__pycache__" EXCLUDE
            PATTERN ".pytest_cache" EXCLUDE
    )
endif()
+23 −11
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ from harness.logger import logger, initialize_logging, finalize_logging
from harness.cli import add_cli_options, set_default_log_formatter
from harness.workspace import Workspace, FileCreator
from harness.gkfs import Daemon, Client, ShellClient, FwdDaemon, FwdClient, ShellFwdClient
from harness.factory import FwdDaemonCreator, FwdClientCreator
from harness.reporter import report_test_status, report_test_headline, report_assertion_pass

def pytest_configure(config):
@@ -86,10 +87,6 @@ def gkfs_daemon(test_workspace, request):
    """

    interface = request.config.getoption('--interface')

    if request.config.getoption('--forwarding') == 'ON':
        daemon = FwdDaemon(interface, test_workspace)
    else:
    daemon = Daemon(interface, test_workspace)

    yield daemon.run()
@@ -105,9 +102,6 @@ def gkfs_client(test_workspace, request):

    interface = request.config.getoption('--interface')

    if request.config.getoption('--forwarding') == 'ON':
        return FwdClient(test_workspace)

    return Client(test_workspace)

@pytest.fixture
@@ -119,9 +113,6 @@ def gkfs_shell(test_workspace, request):

    interface = request.config.getoption('--interface')

    if request.config.getoption('--forwarding') == 'ON':
        return ShellFwdClient(test_workspace)

    return ShellClient(test_workspace)

@pytest.fixture
@@ -132,3 +123,24 @@ def file_factory(test_workspace):
    """

    return FileCreator(test_workspace)

@pytest.fixture
def gkfwd_daemon_factory(test_workspace, request):
    """
    Returns a factory that can create forwarding daemons
    in the test workspace.
    """

    interface = request.config.getoption('--interface')

    return FwdDaemonCreator(interface, test_workspace)

@pytest.fixture
def gkfwd_client_factory(test_workspace, request):
    """
    Sets up a gekkofs client environment so that
    operations (system calls, library calls, ...) can
    be requested from a co-running daemon.
    """

    return FwdClientCreator(test_workspace)
+60 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ from pathlib import Path
import errno
import stat
import os
import time
import ctypes
import sh
import sys
@@ -25,12 +26,52 @@ from harness.logger import logger
nonexisting = "nonexisting"


def test_read(gkfs_daemon, gkfs_client):
def test_write_two_io_nodes(gkfwd_daemon_factory, gkfwd_client_factory):
    """Write files from two clients using two daemons"""

    file = gkfs_daemon.mountdir / "file"
    d00 = gkfwd_daemon_factory.create()
    d01 = gkfwd_daemon_factory.create()

    c00 = gkfwd_client_factory.create('c-0')
    c01 = gkfwd_client_factory.create('c-1')

    file = d00.mountdir / "file-c00"

    # create a file in gekkofs
    ret = c00.open(file,
                           os.O_CREAT | os.O_WRONLY,
                           stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

    assert ret.retval == 10000
    assert ret.errno == 115 #FIXME: Should be 0!

    # write a buffer we know
    buf = b'42'
    ret = c00.write(file, buf, len(buf))

    assert ret.retval == len(buf) # Return the number of written bytes
    assert ret.errno == 115 #FIXME: Should be 0!

    # open the file to read
    ret = c00.open(file,
                           os.O_RDONLY,
                           stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

    assert ret.retval == 10000
    assert ret.errno == 115 #FIXME: Should be 0!

    # read the file
    ret = c00.read(file, len(buf))

    assert ret.buf == buf
    assert ret.retval == len(buf) # Return the number of read bytes
    assert ret.errno == 115 #FIXME: Should be 0!


    file = d01.mountdir / "file-c01"

    # create a file in gekkofs
    ret = gkfs_client.open(file,
    ret = c01.open(file,
                           os.O_CREAT | os.O_WRONLY,
                           stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

@@ -39,13 +80,13 @@ def test_read(gkfs_daemon, gkfs_client):

    # write a buffer we know
    buf = b'42'
    ret = gkfs_client.write(file, buf, len(buf))
    ret = c01.write(file, buf, len(buf))

    assert ret.retval == len(buf) # Return the number of written bytes
    assert ret.errno == 115 #FIXME: Should be 0!

    # open the file to read
    ret = gkfs_client.open(file,
    ret = c01.open(file,
                           os.O_RDONLY,
                           stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)

@@ -53,8 +94,21 @@ def test_read(gkfs_daemon, gkfs_client):
    assert ret.errno == 115 #FIXME: Should be 0!

    # read the file
    ret = gkfs_client.read(file, len(buf))
    ret = c01.read(file, len(buf))

    assert ret.buf == buf
    assert ret.retval == len(buf) # Return the number of read bytes
    assert ret.errno == 115 #FIXME: Should be 0!

    # both files should be there and accessible by the two clients
    ret = c00.readdir(d00.mountdir)

    assert len(ret.dirents) == 2

    assert ret.dirents[0].d_name == 'file-c00'
    assert ret.dirents[0].d_type == 8 # DT_REG
    assert ret.errno == 115 #FIXME: Should be 0!

    assert ret.dirents[1].d_name == 'file-c01'
    assert ret.dirents[1].d_type == 8 # DT_REG
    assert ret.errno == 115 #FIXME: Should be 0!
 No newline at end of file
+0 −8
Original line number Diff line number Diff line
@@ -33,14 +33,6 @@ def add_cli_options(parser):
            help="network interface used for communications (default: 'lo')."
        )

        parser.addoption(
            '--forwarding',
            action='store',
            type=str,
            default='ON',
            help="enable the forwarding mode (default: 'OFF')."
        )

        parser.addoption(
            "--bin-dir",
            action='append',
+37 −22
Original line number Diff line number Diff line
@@ -26,13 +26,24 @@ gkfs_daemon_cmd = 'gkfs_daemon'
gkfs_client_cmd = 'gkfs.io'
gkfs_client_lib_file = 'libgkfs_intercept.so'
gkfs_hosts_file = 'gkfs_hosts.txt'
gkfs_forwarding_map_file = 'gkfs_forwarding.map'
gkfs_daemon_log_file = 'gkfs_daemon.log'
gkfs_daemon_log_level = '100'
gkfs_client_log_file = 'gkfs_client.log'
gkfs_client_log_level = 'all'
gkfs_daemon_active_log_pattern = r'Startup successful. Daemon is ready.'

gkfwd_daemon_cmd = 'gkfwd_daemon'
gkfwd_client_cmd = 'gkfs.io'
gkfwd_client_lib_file = 'libgkfwd_intercept.so'
gkfwd_hosts_file = 'gkfs_hosts.txt'
gkfwd_forwarding_map_file = 'gkfs_forwarding.map'
gkfwd_daemon_log_file = 'gkfwd_daemon.log'
gkfwd_daemon_log_level = '100'
gkfwd_client_log_file = 'gkfwd_client.log'
gkfwd_client_log_level = 'all'
gkfwd_daemon_active_log_pattern = r'Startup successful. Daemon is ready.'


def get_ip_addr(iface):
    return netifaces.ifaddresses(iface)[netifaces.AF_INET][0]['addr']

@@ -590,7 +601,7 @@ class FwdDaemon:
        self._address = get_ephemeral_address(interface)
        self._workspace = workspace

        self._cmd = sh.Command(gkfs_daemon_cmd, self._workspace.bindirs)
        self._cmd = sh.Command(gkfwd_daemon_cmd, self._workspace.bindirs)
        self._env = os.environ.copy()

        libdirs = ':'.join(
@@ -599,9 +610,9 @@ class FwdDaemon:

        self._patched_env = {
            'LD_LIBRARY_PATH'      : libdirs,
            'GKFS_HOSTS_FILE'      : self.cwd / gkfs_hosts_file,
            'GKFS_DAEMON_LOG_PATH' : self.logdir / gkfs_daemon_log_file,
            'GKFS_LOG_LEVEL'       : gkfs_daemon_log_level
            'GKFS_HOSTS_FILE'      : self.cwd / gkfwd_hosts_file,
            'GKFS_DAEMON_LOG_PATH' : self.logdir / gkfwd_daemon_log_file,
            'GKFS_LOG_LEVEL'       : gkfwd_daemon_log_level
        }
        self._env.update(self._patched_env)

@@ -668,9 +679,9 @@ class FwdDaemon:
        while perf_counter() - init_time < timeout:
            try:
                logger.debug(f"checking log file")
                with open(self.logdir / gkfs_daemon_log_file) as log:
                with open(self.logdir / gkfwd_daemon_log_file) as log:
                    for line in islice(log, max_lines):
                        if re.search(gkfs_daemon_active_log_pattern, line) is not None:
                        if re.search(gkfwd_daemon_active_log_pattern, line) is not None:
                            return
            except FileNotFoundError:
                # Log is missing, the daemon might have crashed...
@@ -718,6 +729,7 @@ class FwdDaemon:
    def interface(self):
        return self._interface


class FwdClient:
    """
    A class to represent a GekkoFS client process with a patched LD_PRELOAD.
@@ -725,15 +737,18 @@ class FwdClient:
    function calls, be them system calls (e.g. read()) or glibc I/O functions
    (e.g. opendir()).
    """
    def __init__(self, workspace):
    def __init__(self, workspace, identifier):
        self._parser = IOParser()
        self._workspace = workspace
        self._cmd = sh.Command(gkfs_client_cmd, self._workspace.bindirs)
        self._identifier = identifier
        self._cmd = sh.Command(gkfwd_client_cmd, self._workspace.bindirs)
        self._env = os.environ.copy()

        gkfwd_forwarding_map_file_local = '{}-{}'.format(identifier, gkfwd_forwarding_map_file)

        # create the forwarding map file
        fwd_map_file = open(self.cwd / gkfs_forwarding_map_file, 'w')
        fwd_map_file.write('{} {}\n'.format(socket.gethostname(), 0))
        fwd_map_file = open(self.cwd / gkfwd_forwarding_map_file_local, 'w')
        fwd_map_file.write('{} {}\n'.format(socket.gethostname(), int(identifier.split('-')[1])))
        fwd_map_file.close()

        libdirs = ':'.join(
@@ -745,7 +760,7 @@ class FwdClient:
        # it must be found in one (and only one) of the workspace's bindirs
        preloads = []
        for d in self._workspace.bindirs:
            search_path = Path(d) / gkfs_client_lib_file
            search_path = Path(d) / gkfwd_client_lib_file
            if search_path.exists():
                preloads.append(search_path)

@@ -765,10 +780,10 @@ class FwdClient:
        self._patched_env = {
            'LD_LIBRARY_PATH'               : libdirs,
            'LD_PRELOAD'                    : self._preload_library,
            'LIBGKFS_HOSTS_FILE'            : self.cwd / gkfs_hosts_file,
            'LIBGKFS_FORWARDING_MAP_FILE'   : self.cwd / gkfs_forwarding_map_file,
            'LIBGKFS_HOSTS_FILE'            : self.cwd / gkfwd_hosts_file,
            'LIBGKFS_FORWARDING_MAP_FILE'   : self.cwd / gkfwd_forwarding_map_file_local,
            'LIBGKFS_LOG'                   : gkfs_client_log_level,
            'LIBGKFS_LOG_OUTPUT'            : self._workspace.logdir / gkfs_client_log_file
            'LIBGKFS_LOG_OUTPUT'            : self._workspace.logdir / gkfwd_client_log_file
        }

        self._env.update(self._patched_env)
@@ -817,7 +832,7 @@ class ShellFwdClient:
        self._env = os.environ.copy()

        # create the forwarding map file
        fwd_map_file = open(self.cwd / gkfs_forwarding_map_file, 'w')
        fwd_map_file = open(self.cwd / gkfwd_forwarding_map_file, 'w')
        fwd_map_file.write('{} {}\n'.format(socket.gethostname(), 0))
        fwd_map_file.close()

@@ -830,7 +845,7 @@ class ShellFwdClient:
        # it must be found in one (and only one) of the workspace's bindirs
        preloads = []
        for d in self._workspace.bindirs:
            search_path = Path(d) / gkfs_client_lib_file
            search_path = Path(d) / gkfwd_client_lib_file
            if search_path.exists():
                preloads.append(search_path)

@@ -846,10 +861,10 @@ class ShellFwdClient:
        self._patched_env = {
            'LD_LIBRARY_PATH'               : libdirs,
            'LD_PRELOAD'                    : self._preload_library,
            'LIBGKFS_HOSTS_FILE'            : self.cwd / gkfs_hosts_file,
            'LIBGKFS_FORWARDING_MAP_FILE'   : self.cwd / gkfs_forwarding_map_file,
            'LIBGKFS_LOG'                   : gkfs_client_log_level,
            'LIBGKFS_LOG_OUTPUT'            : self._workspace.logdir / gkfs_client_log_file
            'LIBGKFS_HOSTS_FILE'            : self.cwd / gkfwd_hosts_file,
            'LIBGKFS_FORWARDING_MAP_FILE'   : self.cwd / gkfwd_forwarding_map_file,
            'LIBGKFS_LOG'                   : gkfwd_client_log_level,
            'LIBGKFS_LOG_OUTPUT'            : self._workspace.logdir / gkfwd_client_log_file
        }

        self._env.update(self._patched_env)
@@ -904,7 +919,7 @@ class ShellFwdClient:

        intercept_shell: `bool`
            Controls whether the shell executing the script should be
            executed with LD_PRELOAD=libgkfs_intercept.so (default: True).
            executed with LD_PRELOAD=libgkfwd_intercept.so (default: True).

        timeout: `int`
            How much time, in seconds, we should give the process to complete.