From ed167d8b38b2dea734a2cb44dee9e30ef46f28fc Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Thu, 13 Mar 2025 16:48:32 +0100 Subject: [PATCH 01/13] Add support for GKFS proxy in integration tests and client setup - Introduced `gkfs_daemon_proxy` and `gkfs_proxy_client` fixtures for proxy testing. - Implemented `Proxy` class to manage GKFS proxy lifecycle. - Updated `Daemon` and `Client` classes to support proxy functionality. - Added tests for reading and writing files through the GKFS proxy. --- tests/integration/conftest.py | 25 ++- tests/integration/conftest.template | 10 ++ tests/integration/harness/gkfs.py | 153 +++++++++++++++++- .../operations/test_read_operations.py | 30 ++++ 4 files changed, 215 insertions(+), 3 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 55c5614a4..225e244ff 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -34,7 +34,7 @@ from pathlib import Path 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, FwdDaemonCreator, FwdClientCreator +from harness.gkfs import Daemon, Client, Proxy, ShellClient, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator from harness.reporter import report_test_status, report_test_headline, report_assertion_pass def pytest_configure(config): @@ -123,6 +123,19 @@ def gkfs_daemon(request): return request.getfixturevalue(request.param) +def gkfs_daemon_proxy(request): + interface = request.config.getoption('--interface') + daemon = Daemon(interface, "rocksdb", test_workspace, True) + + yield daemon.run() + daemon.shutdown() + +def gkfs_proxy(test_workspace): + """ + Sets up a gekkofs proxy environment. + """ + return Proxy(test_workspace) + @pytest.fixture def gkfs_client(test_workspace): """ @@ -133,6 +146,16 @@ def gkfs_client(test_workspace): return Client(test_workspace) +@pytest.fixture +def gkfs_proxy_client(test_workspace): + """ + Sets up a gekkofs client environment so that + operations (system calls, library calls, ...) can + be requested from a co-running daemon. + """ + + return Client(test_workspace, True) + @pytest.fixture def gkfs_shell(test_workspace): """ diff --git a/tests/integration/conftest.template b/tests/integration/conftest.template index 55c5614a4..937de04c7 100644 --- a/tests/integration/conftest.template +++ b/tests/integration/conftest.template @@ -133,6 +133,16 @@ def gkfs_client(test_workspace): return Client(test_workspace) +@pytest.fixture +def gkfs_proxy_client(test_workspace): + """ + Sets up a gekkofs client environment so that + operations (system calls, library calls, ...) can + be requested from a co-running daemon. + """ + + return ProxyClient(test_workspace) + @pytest.fixture def gkfs_shell(test_workspace): """ diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 67846ec83..4cca2e851 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -48,6 +48,12 @@ gkfs_client_log_level = 'all' gkfs_client_log_syscall_filter = 'epoll_wait,epoll_create' gkfs_daemon_active_log_pattern = r'Startup successful. Daemon is ready.' +gkfs_proxy_cmd = 'gkfs_proxy' +gkfs_proxy_pid = 'gkfs_proxy.pid' +gkfs_proxy_log_file = 'gkfs_proxy.log' +gkfs_proxy_log_level = 'all' +gkfs_proxy_active_log_pattern = r'Initializing environment' + gkfwd_daemon_cmd = 'gkfs_daemon' gkfwd_client_cmd = 'gkfs.io' gkfwd_client_lib_file = 'libgkfs_intercept.so' @@ -225,7 +231,7 @@ class FwdClientCreator: class Daemon: - def __init__(self, interface, database, workspace): + def __init__(self, interface, database, workspace, proxy = False): self._address = get_ephemeral_address(interface) self._workspace = workspace @@ -233,6 +239,8 @@ class Daemon: self._cmd = sh.Command(gkfs_daemon_cmd, self._workspace.bindirs) self._env = os.environ.copy() self._metadir = self.rootdir + self._proxy = proxy + libdirs = ':'.join( filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + [str(p) for p in self._workspace.libdirs])) @@ -257,6 +265,8 @@ class Daemon: '--enable-chunkstats'] if self._database == "parallaxdb" : args.append('--clean-rootdir-finish') + if self._proxy == True: + args.append('--proxy-protocol ofi+sockets') logger.debug(f"spawning daemon") logger.debug(f"cmdline: {self._cmd} " + " ".join(map(str, args))) @@ -367,6 +377,141 @@ class Daemon: def interface(self): return self._interface +class Proxy: + def __init__(self, workspace): + self._parser = IOParser() + self._workspace = workspace + self._cmd = sh.Command(gkfs_proxy_cmd, self._workspace.bindirs) + self._env = os.environ.copy() + + libdirs = ':'.join( + filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + + [str(p) for p in self._workspace.libdirs])) + + self._patched_env = { + 'LD_LIBRARY_PATH' : libdirs, + 'GKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file), + 'GKFS_PROXY_LOG_PATH' : str(self.logdir / gkfs_proxy_log_file), + 'GKFS_PROXY_LOG_LEVEL': gkfs_proxy_log_level + } + self._env.update(self._patched_env) + + def run(self): + + args = ['--pid-path', gkfs_proxy_pid, + '-p', 'ofi+sockets'] + + + logger.debug(f"spawning proxy") + logger.debug(f"cmdline: {self._cmd} " + " ".join(map(str, args))) + logger.debug(f"patched env:\n{pformat(self._patched_env)}") + + self._proc = self._cmd( + args, + _env=self._env, +# _out=sys.stdout, +# _err=sys.stderr, + _bg=True, + ) + + logger.debug(f"proxy process spawned (PID={self._proc.pid})") + logger.debug("waiting for proxy to be ready") + + try: + self.wait_until_active(self._proc.pid, 720.0) + except Exception as ex: + logger.error(f"proxy initialization failed: {ex}") + + # if the daemon initialized correctly but took longer than + # `timeout`, we may be leaving running processes behind + if _process_exists(self._proc.pid): + self.shutdown() + + logger.critical(f"proxy was shut down, what is ex? {ex.__repr__}?") + raise ex + + logger.debug("proxy is ready") + + return self + + + + def wait_until_active(self, pid, timeout, max_lines=50): + """ + Waits until a GKFS daemon is active or until a certain timeout + has expired. Checks if the daemon is running by searching its + log for a pre-defined readiness message. + + Parameters + ---------- + pid: `int` + The PID of the daemon process to wait for. + + timeout: `number` + The number of seconds to wait for + + max_lines: `int` + The maximum number of log lines to check for a match. + """ + + + + init_time = perf_counter() + + while perf_counter() - init_time < timeout: + try: + # logger.debug(f"checking log file") + with open(self.logdir / gkfs_proxy_log_file) as log: + for line in islice(log, max_lines): + if re.search(gkfs_proxy_active_log_pattern, line) is not None: + return + except FileNotFoundError: + # Log is missing, the daemon might have crashed... + logger.debug(f"proxy log file missing, checking if daemon is alive...") + + pid=self._proc.pid + + if not _process_exists(pid): + raise RuntimeError(f"process {pid} is not running") + + # ... or it might just be lazy. let's give it some more time + logger.debug(f"proxy {pid} found, retrying...") + time.sleep(1) + raise RuntimeError("initialization timeout exceeded") + + def shutdown(self): + logger.debug(f"terminating proxy") + + try: + self._proc.terminate() + err = self._proc.wait() + except sh.SignalException_SIGTERM: + pass + except Exception: + raise + + + @property + def cwd(self): + return self._workspace.twd + + @property + def rootdir(self): + return self._workspace.rootdir + + @property + def mountdir(self): + return self._workspace.mountdir + + @property + def logdir(self): + return self._workspace.logdir + + @property + def interface(self): + return self._interface + + class _proxy_exec(): def __init__(self, client, name): self._client = client @@ -383,11 +528,12 @@ class Client: 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, proxy = False): self._parser = IOParser() self._workspace = workspace self._cmd = sh.Command(gkfs_client_cmd, self._workspace.bindirs) self._env = os.environ.copy() + self._proxy = proxy libdirs = ':'.join( filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + @@ -424,6 +570,9 @@ class Client: 'LIBGKFS_LOG_SYSCALL_FILTER': gkfs_client_log_syscall_filter } + if (self._proxy == True): + self._patched_env['LIBGKFS_PROXY_PID_FILE'] = gkfs_proxy_pid + self._env.update(self._patched_env) @property diff --git a/tests/integration/operations/test_read_operations.py b/tests/integration/operations/test_read_operations.py index d3635465e..bb568c234 100644 --- a/tests/integration/operations/test_read_operations.py +++ b/tests/integration/operations/test_read_operations.py @@ -70,6 +70,36 @@ def test_read(gkfs_daemon, gkfs_client): assert ret.buf == buf assert ret.retval == len(buf) # Return the number of read bytes + +def test_read_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): + file = gkfs_daemon_proxy.mountdir / "file" + + # create a file in gekkofs + ret = gkfs_client_proxy.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write a buffer we know + buf = b'42' + ret = gkfs_client_proxy.write(file, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + # open the file to read + ret = gkfs_client_proxy.open(file, + os.O_RDONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # read the file + ret = gkfs_client_proxy.read(file, len(buf)) + + assert ret.buf == buf + assert ret.retval == len(buf) # Return the number of read bytes + def test_pread(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" -- GitLab From edbc5825857528f6b02eaa947c7a0c8d591bda7e Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Thu, 13 Mar 2025 17:57:40 +0100 Subject: [PATCH 02/13] Add GKFS proxy fixtures for integration tests --- tests/integration/conftest.py | 7 ++++--- tests/integration/conftest.template | 20 +++++++++++++++++--- tests/integration/harness/gkfs.py | 3 ++- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 225e244ff..d1b68f580 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -122,14 +122,15 @@ def gkfs_daemon_parallaxdb(test_workspace, request): def gkfs_daemon(request): return request.getfixturevalue(request.param) - -def gkfs_daemon_proxy(request): +@pytest.fixture +def gkfs_daemon_proxy(test_workspace, request): interface = request.config.getoption('--interface') daemon = Daemon(interface, "rocksdb", test_workspace, True) yield daemon.run() daemon.shutdown() +@pytest.fixture def gkfs_proxy(test_workspace): """ Sets up a gekkofs proxy environment. @@ -147,7 +148,7 @@ def gkfs_client(test_workspace): return Client(test_workspace) @pytest.fixture -def gkfs_proxy_client(test_workspace): +def gkfs_client_proxy(test_workspace): """ Sets up a gekkofs client environment so that operations (system calls, library calls, ...) can diff --git a/tests/integration/conftest.template b/tests/integration/conftest.template index 937de04c7..d1b68f580 100644 --- a/tests/integration/conftest.template +++ b/tests/integration/conftest.template @@ -34,7 +34,7 @@ from pathlib import Path 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, FwdDaemonCreator, FwdClientCreator +from harness.gkfs import Daemon, Client, Proxy, ShellClient, FwdDaemon, FwdClient, ShellFwdClient, FwdDaemonCreator, FwdClientCreator from harness.reporter import report_test_status, report_test_headline, report_assertion_pass def pytest_configure(config): @@ -122,6 +122,20 @@ def gkfs_daemon_parallaxdb(test_workspace, request): def gkfs_daemon(request): return request.getfixturevalue(request.param) +@pytest.fixture +def gkfs_daemon_proxy(test_workspace, request): + interface = request.config.getoption('--interface') + daemon = Daemon(interface, "rocksdb", test_workspace, True) + + yield daemon.run() + daemon.shutdown() + +@pytest.fixture +def gkfs_proxy(test_workspace): + """ + Sets up a gekkofs proxy environment. + """ + return Proxy(test_workspace) @pytest.fixture def gkfs_client(test_workspace): @@ -134,14 +148,14 @@ def gkfs_client(test_workspace): return Client(test_workspace) @pytest.fixture -def gkfs_proxy_client(test_workspace): +def gkfs_client_proxy(test_workspace): """ Sets up a gekkofs client environment so that operations (system calls, library calls, ...) can be requested from a co-running daemon. """ - return ProxyClient(test_workspace) + return Client(test_workspace, True) @pytest.fixture def gkfs_shell(test_workspace): diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 4cca2e851..2b2d4b232 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -266,7 +266,8 @@ class Daemon: if self._database == "parallaxdb" : args.append('--clean-rootdir-finish') if self._proxy == True: - args.append('--proxy-protocol ofi+sockets') + args.append('--proxy-protocol') + args.append('ofi+sockets') logger.debug(f"spawning daemon") logger.debug(f"cmdline: {self._cmd} " + " ".join(map(str, args))) -- GitLab From 9614b0dc4f29856ab6db66f35e3d90f8a0c27837 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 11:22:22 +0100 Subject: [PATCH 03/13] Add proxy support tests for unlink and write operations --- .../operations/test_unlink_operations.py | 60 +++++++++++++++++++ .../operations/test_write_operations.py | 45 ++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/tests/integration/operations/test_unlink_operations.py b/tests/integration/operations/test_unlink_operations.py index 22cff1bc4..f9729e6aa 100644 --- a/tests/integration/operations/test_unlink_operations.py +++ b/tests/integration/operations/test_unlink_operations.py @@ -97,3 +97,63 @@ def test_unlink(gkfs_daemon, gkfs_client): ret = gkfs_client.unlink(file) # Remove renamed file (extra chunks, success) assert ret.retval == 0 + + + +def test_unlink_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): + + file = gkfs_daemon_proxy.mountdir / "file" + dir = gkfs_daemon_proxy.mountdir / "dir" + + + # Delete an unexistent file + ret = gkfs_client_proxy.unlink(file) + assert ret.retval == -1 + assert ret.errno == errno.ENOENT + + + + + + # create a file in gekkofs + ret = gkfs_client_proxy.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write a buffer we know + buf = b'42' + ret = gkfs_client_proxy.write(file, buf, len(buf)) + assert ret.retval == len(buf) # Return the number of written bytes + + + + ret = gkfs_client_proxy.unlink(file) # Remove renamed file (success) + assert ret.retval == 0 + + ret = gkfs_client_proxy.stat(file) # file does not exist + assert ret.retval != 0 + assert ret.errno == errno.ENOENT + + ret = gkfs_client_proxy.mkdir(dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) # Create a directory + assert ret.retval == 0 + + ret = gkfs_client_proxy.unlink(dir) + assert ret.retval == -1 + assert ret.errno == errno.EISDIR + + + # create a file in gekkofs + ret = gkfs_client_proxy.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # > 4 chunks + ret = gkfs_client_proxy.write_validate(file, 2097153) + assert ret.retval == 1 + + ret = gkfs_client_proxy.unlink(file) # Remove renamed file (extra chunks, success) + assert ret.retval == 0 \ No newline at end of file diff --git a/tests/integration/operations/test_write_operations.py b/tests/integration/operations/test_write_operations.py index 42c62c28d..11e31d775 100644 --- a/tests/integration/operations/test_write_operations.py +++ b/tests/integration/operations/test_write_operations.py @@ -85,6 +85,51 @@ def test_write(gkfs_daemon, gkfs_client): assert ret.retval == 0 assert ret.statbuf.st_size == (len(str1) + len(str2) + len(str3)) +def test_write_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): + + file = gkfs_daemon_proxy.mountdir / "file" + + ret = gkfs_client_proxy.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + buf = b'42' + ret = gkfs_client_proxy.write(file, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + file_append = gkfs_daemon_proxy.mountdir / "file_append" + + ret = gkfs_client_proxy.open(file_append, + os.O_CREAT | os.O_WRONLY | os.O_APPEND, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + str1 = b'Hello' + str2 = b', World!' + str3 = b' This is a test.\n' + + ret = gkfs_client_proxy.write(file_append, str1, len(str1), True) + assert ret.retval == len(str1) + ret = gkfs_client_proxy.stat(file_append) + assert ret.retval == 0 + assert ret.statbuf.st_size == len(str1) + + ret = gkfs_client_proxy.write(file_append, str2, len(str2), True) + assert ret.retval == len(str2) + ret = gkfs_client_proxy.stat(file_append) + assert ret.retval == 0 + assert ret.statbuf.st_size == (len(str1) + len(str2)) + + ret = gkfs_client_proxy.write(file_append, str3, len(str3), True) + assert ret.retval == len(str3) + ret = gkfs_client_proxy.stat(file_append) + assert ret.retval == 0 + assert ret.statbuf.st_size == (len(str1) + len(str2) + len(str3)) + def test_pwrite(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" -- GitLab From 989448d9ec49bff7697ea4e80d05b0e6ebba0731 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 11:50:21 +0100 Subject: [PATCH 04/13] Refactor gkfs_proxy fixture to ensure proper startup and shutdown of the proxy environment --- tests/integration/conftest.py | 4 +++- tests/integration/conftest.template | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index d1b68f580..0e8017893 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -135,7 +135,9 @@ def gkfs_proxy(test_workspace): """ Sets up a gekkofs proxy environment. """ - return Proxy(test_workspace) + proxy = Proxy(test_workspace) + yield proxy.run() + proxy.shutdown() @pytest.fixture def gkfs_client(test_workspace): diff --git a/tests/integration/conftest.template b/tests/integration/conftest.template index d1b68f580..0e8017893 100644 --- a/tests/integration/conftest.template +++ b/tests/integration/conftest.template @@ -135,7 +135,9 @@ def gkfs_proxy(test_workspace): """ Sets up a gekkofs proxy environment. """ - return Proxy(test_workspace) + proxy = Proxy(test_workspace) + yield proxy.run() + proxy.shutdown() @pytest.fixture def gkfs_client(test_workspace): -- GitLab From c75c57c3d71431e6e506f6ce05d0a0a42280ed6c Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 12:53:42 +0100 Subject: [PATCH 05/13] Update GKFS proxy log level and adjust environment variables for improved logging --- tests/integration/harness/gkfs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 2b2d4b232..b6469540d 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -51,7 +51,7 @@ gkfs_daemon_active_log_pattern = r'Startup successful. Daemon is ready.' gkfs_proxy_cmd = 'gkfs_proxy' gkfs_proxy_pid = 'gkfs_proxy.pid' gkfs_proxy_log_file = 'gkfs_proxy.log' -gkfs_proxy_log_level = 'all' +gkfs_proxy_log_level = '100' gkfs_proxy_active_log_pattern = r'Initializing environment' gkfwd_daemon_cmd = 'gkfs_daemon' @@ -391,7 +391,6 @@ class Proxy: self._patched_env = { 'LD_LIBRARY_PATH' : libdirs, - 'GKFS_HOSTS_FILE' : str(self.cwd / gkfs_hosts_file), 'GKFS_PROXY_LOG_PATH' : str(self.logdir / gkfs_proxy_log_file), 'GKFS_PROXY_LOG_LEVEL': gkfs_proxy_log_level } @@ -399,7 +398,8 @@ class Proxy: def run(self): - args = ['--pid-path', gkfs_proxy_pid, + args = ['-H', str(self.cwd / gkfs_hosts_file), + '--pid-path', gkfs_proxy_pid, '-p', 'ofi+sockets'] -- GitLab From e8455bafa8589adeb77dbe08c1285e312df4dc25 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 14:15:21 +0100 Subject: [PATCH 06/13] Refactor proxy tests and update file handling for improved clarity and consistency --- tests/integration/harness/gkfs.py | 17 ++++++----------- .../operations/test_read_operations.py | 7 +++---- .../operations/test_unlink_operations.py | 9 +++------ .../operations/test_write_operations.py | 10 +++------- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index b6469540d..a0cc80ab1 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -384,7 +384,7 @@ class Proxy: self._workspace = workspace self._cmd = sh.Command(gkfs_proxy_cmd, self._workspace.bindirs) self._env = os.environ.copy() - + libdirs = ':'.join( filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + [str(p) for p in self._workspace.libdirs])) @@ -395,11 +395,12 @@ class Proxy: 'GKFS_PROXY_LOG_LEVEL': gkfs_proxy_log_level } self._env.update(self._patched_env) + def run(self): args = ['-H', str(self.cwd / gkfs_hosts_file), - '--pid-path', gkfs_proxy_pid, + '--pid-path', str(self.cwd / gkfs_proxy_pid), '-p', 'ofi+sockets'] @@ -410,8 +411,8 @@ class Proxy: self._proc = self._cmd( args, _env=self._env, -# _out=sys.stdout, -# _err=sys.stderr, + _out=sys.stdout, + _err=sys.stderr, _bg=True, ) @@ -432,7 +433,6 @@ class Proxy: raise ex logger.debug("proxy is ready") - return self @@ -482,7 +482,6 @@ class Proxy: def shutdown(self): logger.debug(f"terminating proxy") - try: self._proc.terminate() err = self._proc.wait() @@ -500,10 +499,6 @@ class Proxy: def rootdir(self): return self._workspace.rootdir - @property - def mountdir(self): - return self._workspace.mountdir - @property def logdir(self): return self._workspace.logdir @@ -572,7 +567,7 @@ class Client: } if (self._proxy == True): - self._patched_env['LIBGKFS_PROXY_PID_FILE'] = gkfs_proxy_pid + self._patched_env['LIBGKFS_PROXY_PID_FILE'] = str(self.cwd / gkfs_proxy_pid) self._env.update(self._patched_env) diff --git a/tests/integration/operations/test_read_operations.py b/tests/integration/operations/test_read_operations.py index bb568c234..cdc670da6 100644 --- a/tests/integration/operations/test_read_operations.py +++ b/tests/integration/operations/test_read_operations.py @@ -39,7 +39,6 @@ from harness.logger import logger nonexisting = "nonexisting" - def test_read(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" @@ -71,9 +70,9 @@ def test_read(gkfs_daemon, gkfs_client): assert ret.retval == len(buf) # Return the number of read bytes -def test_read_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): - file = gkfs_daemon_proxy.mountdir / "file" - +def test_read_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): + file = gkfs_daemon_proxy.mountdir / "file_read_proxy" + # create a file in gekkofs ret = gkfs_client_proxy.open(file, os.O_CREAT | os.O_WRONLY, diff --git a/tests/integration/operations/test_unlink_operations.py b/tests/integration/operations/test_unlink_operations.py index f9729e6aa..f9ea326d7 100644 --- a/tests/integration/operations/test_unlink_operations.py +++ b/tests/integration/operations/test_unlink_operations.py @@ -38,8 +38,6 @@ from harness.logger import logger nonexisting = "nonexisting" - - def test_unlink(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" @@ -99,11 +97,10 @@ def test_unlink(gkfs_daemon, gkfs_client): assert ret.retval == 0 +def test_unlink_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): -def test_unlink_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): - - file = gkfs_daemon_proxy.mountdir / "file" - dir = gkfs_daemon_proxy.mountdir / "dir" + file = gkfs_daemon_proxy.mountdir / "file_proxy" + dir = gkfs_daemon_proxy.mountdir / "dir_proxy" # Delete an unexistent file diff --git a/tests/integration/operations/test_write_operations.py b/tests/integration/operations/test_write_operations.py index 11e31d775..49799ce55 100644 --- a/tests/integration/operations/test_write_operations.py +++ b/tests/integration/operations/test_write_operations.py @@ -39,10 +39,9 @@ from harness.logger import logger nonexisting = "nonexisting" - def test_write(gkfs_daemon, gkfs_client): - file = gkfs_daemon.mountdir / "file" + file = gkfs_daemon.mountdir / "file_write" ret = gkfs_client.open(file, os.O_CREAT | os.O_WRONLY, @@ -85,9 +84,9 @@ def test_write(gkfs_daemon, gkfs_client): assert ret.retval == 0 assert ret.statbuf.st_size == (len(str1) + len(str2) + len(str3)) -def test_write_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): +def test_write_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): - file = gkfs_daemon_proxy.mountdir / "file" + file = gkfs_daemon_proxy.mountdir / "file_write_proxy" ret = gkfs_client_proxy.open(file, os.O_CREAT | os.O_WRONLY, @@ -130,7 +129,6 @@ def test_write_proxy(gkfs_daemon_proxy, gkfs_client_proxy, gkfs_proxy): assert ret.retval == 0 assert ret.statbuf.st_size == (len(str1) + len(str2) + len(str3)) - def test_pwrite(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" @@ -146,7 +144,6 @@ def test_pwrite(gkfs_daemon, gkfs_client): assert ret.retval == len(buf) # Return the number of written bytes - def test_writev(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" @@ -162,7 +159,6 @@ def test_writev(gkfs_daemon, gkfs_client): assert ret.retval == len(buf_0) + len(buf_1) # Return the number of written bytes - def test_pwritev(gkfs_daemon, gkfs_client): file = gkfs_daemon.mountdir / "file" -- GitLab From f0300672d2bc38e6baf9a0cd09dc8f21abd6fca9 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 15:08:00 +0100 Subject: [PATCH 07/13] Fix typo in log message for auto_sm detection in hostfile --- src/proxy/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxy/util.cpp b/src/proxy/util.cpp index 81c3ddba6..ac963ba8c 100644 --- a/src/proxy/util.cpp +++ b/src/proxy/util.cpp @@ -83,7 +83,7 @@ load_hostfile(const std::string& lfpath) { uri.find("na+sm") != std::string::npos) { PROXY_DATA->use_auto_sm(true); PROXY_DATA->log()->info( - "{}() auto_sm detected in daemon hosefile. Enabling it on proxy ...", + "{}() auto_sm detected in daemon hostfile. Enabling it on proxy ...", __func__); } -- GitLab From 57fa32f93b39c298ed3a42554c132f684f5152f3 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 17 Mar 2025 15:20:59 +0100 Subject: [PATCH 08/13] Add proxy tests for truncate and directory scanning edge cases --- tests/integration/data/test_truncate.py | 88 +++++++++++++++++++ .../directories/test_directories.py | 30 +++++++ 2 files changed, 118 insertions(+) diff --git a/tests/integration/data/test_truncate.py b/tests/integration/data/test_truncate.py index b05ca8abf..377a818b8 100644 --- a/tests/integration/data/test_truncate.py +++ b/tests/integration/data/test_truncate.py @@ -158,3 +158,91 @@ def test_fail_truncate(gkfs_daemon, gkfs_client): assert ret.statbuf.st_size == buf_length+1 +def test_truncate_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): + """Testing truncate: + 1. create a large file over multiple chunks + 2. truncate it in the middle and compare it with a fresh file with equal contents (exactly at chunk border) + 3. truncate it again so that in truncates in the middle of the chunk and compare with fresh file + TODO chunksize needs to be respected to make sure chunk border and in the middle of chunk truncates are honored + """ + truncfile = gkfs_daemon_proxy.mountdir / "trunc_file" + + # open and create test file + ret = gkfs_client_proxy.open(truncfile, os.O_CREAT | os.O_WRONLY, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write a multi MB file (16mb) + buf_length = 16777216 + ret = gkfs_client_proxy.write_random(truncfile, buf_length) + + assert ret.retval == buf_length + + ret = gkfs_client_proxy.stat(truncfile) + + assert ret.statbuf.st_size == buf_length + + # truncate it + # split exactly in the middle + trunc_size = buf_length // 2 + ret = gkfs_client_proxy.truncate(truncfile, trunc_size) + + assert ret.retval == 0 + # check file length + ret = gkfs_client_proxy.stat(truncfile) + + assert ret.statbuf.st_size == trunc_size + + # verify contents by writing a new file (random content is seeded) and checksum both + truncfile_verify = gkfs_daemon_proxy.mountdir / "trunc_file_verify" + + # open and create test file + ret = gkfs_client_proxy.open(truncfile_verify, os.O_CREAT | os.O_WRONLY, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write trunc_size of data to new file + ret = gkfs_client_proxy.write_random(truncfile_verify, trunc_size) + + assert ret.retval == trunc_size + + ret = gkfs_client_proxy.stat(truncfile_verify) + + assert ret.statbuf.st_size == trunc_size + + ret = gkfs_client_proxy.file_compare(truncfile, truncfile_verify, trunc_size) + + assert ret.retval == 0 + + # trunc at byte 712345 (middle of chunk) + # TODO feed chunksize into test to make sure it is always in the middle of the chunk + trunc_size = 712345 + ret = gkfs_client_proxy.truncate(truncfile, trunc_size) + + assert ret.retval == 0 + + # check file length + ret = gkfs_client_proxy.stat(truncfile) + + assert ret.statbuf.st_size == trunc_size + + # verify contents by writing a new file (random content is seeded) and checksum both + truncfile_verify_2 = gkfs_daemon_proxy.mountdir / "trunc_file_verify_2" + + # open and create test file + ret = gkfs_client_proxy.open(truncfile_verify_2, os.O_CREAT | os.O_WRONLY, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write trunc_size of data to new file + ret = gkfs_client_proxy.write_random(truncfile_verify_2, trunc_size) + + assert ret.retval == trunc_size + + ret = gkfs_client_proxy.stat(truncfile_verify_2) + + assert ret.statbuf.st_size == trunc_size + + ret = gkfs_client_proxy.file_compare(truncfile, truncfile_verify_2, trunc_size) + + assert ret.retval == 0 \ No newline at end of file diff --git a/tests/integration/directories/test_directories.py b/tests/integration/directories/test_directories.py index a5148c5e5..3c8480cba 100644 --- a/tests/integration/directories/test_directories.py +++ b/tests/integration/directories/test_directories.py @@ -299,3 +299,33 @@ def test_opendir(gkfs_daemon, gkfs_client, directory_path): assert ret.dirp is None assert ret.errno == errno.ENOENT +def test_finedir_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): + """Tests several corner cases for directories scan""" + + topdir = gkfs_daemon_proxy.mountdir / "finetop" + file_a = topdir / "file_" + + + # create topdir + ret = gkfs_client_proxy.mkdir( + topdir, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 0 + + ret = gkfs_client_proxy.readdir(topdir) + + # XXX: This might change in the future if we add '.' and '..' + assert len(ret.dirents) == 0 + + # populate top directory + + for files in range (1,4): + ret = gkfs_client_proxy.directory_validate( + topdir, 1) + assert ret.retval == files + + + ret = gkfs_client_proxy.directory_validate( + topdir, 1000) + assert ret.retval == 1000+3 \ No newline at end of file -- GitLab From e3e27b3b698293a27fbe94d31318f7a241452b3c Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 18 Mar 2025 16:55:51 +0100 Subject: [PATCH 09/13] Add user library integration tests and update CMake configuration --- tests/integration/CMakeLists.txt | 2 + .../integration/syscalls/test_userlibrary.py | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/integration/syscalls/test_userlibrary.py diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 3b1db61be..1fe2fde4a 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -61,6 +61,8 @@ gkfs_enable_python_testing( ${CMAKE_BINARY_DIR}/src/client/ ${CMAKE_BINARY_DIR}/tests/integration/harness/ ${CMAKE_BINARY_DIR}/examples/gfind/ + ${CMAKE_BINARY_DIR}/examples/user_library/ + ${CMAKE_BINARY_DIR}/src/proxy/ LIBRARY_PREFIX_DIRECTORIES ${CMAKE_PREFIX_PATH} ) diff --git a/tests/integration/syscalls/test_userlibrary.py b/tests/integration/syscalls/test_userlibrary.py new file mode 100644 index 000000000..dd291a5d4 --- /dev/null +++ b/tests/integration/syscalls/test_userlibrary.py @@ -0,0 +1,46 @@ +################################################################################ +# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany # +# # +# This software was partially supported by the # +# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # +# # +# This software was partially supported by the # +# ADA-FS project under the SPPEXA project funded by the DFG. # +# # +# This file is part of GekkoFS. # +# # +# GekkoFS is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# GekkoFS is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with GekkoFS. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sh +import sys +import pytest +from harness.logger import logger + +nonexisting = "nonexisting" + +def test_user(gkfs_daemon, gkfs_shell): + + file = gkfs_daemon.mountdir / "file" + + cmd = gkfs_shell.gkfs_lib_example() \ No newline at end of file -- GitLab From c3549f1d940d32b0ac6dbd92106d0fe6f62cf3f7 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 18 Mar 2025 17:22:57 +0100 Subject: [PATCH 10/13] Add gkfs_shell_proxy fixture and extend directory tests for enhanced functionality --- tests/integration/conftest.py | 9 +++ tests/integration/conftest.template | 9 +++ .../directories/test_directories.py | 58 ++++++++++++++++++- tests/integration/harness/gkfs.py | 6 +- 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 0e8017893..f1a86324c 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -168,6 +168,15 @@ def gkfs_shell(test_workspace): return ShellClient(test_workspace) +@pytest.fixture +def gkfs_shell_proxy(test_workspace): + """ + Sets up a gekkofs environment so that shell commands + (stat, ls, mkdir, etc.) can be issued to a co-running daemon. + """ + + return ShellClient(test_workspace,True) + @pytest.fixture def file_factory(test_workspace): """ diff --git a/tests/integration/conftest.template b/tests/integration/conftest.template index 0e8017893..019cb5cc2 100644 --- a/tests/integration/conftest.template +++ b/tests/integration/conftest.template @@ -167,6 +167,15 @@ def gkfs_shell(test_workspace): """ return ShellClient(test_workspace) + +@pytest.fixture +def gkfs_shell_proxy(test_workspace): + """ + Sets up a gekkofs environment so that shell commands + (stat, ls, mkdir, etc.) can be issued to a co-running daemon. + """ + + return ShellClient(test_workspace,True) @pytest.fixture def file_factory(test_workspace): diff --git a/tests/integration/directories/test_directories.py b/tests/integration/directories/test_directories.py index 3c8480cba..c6c08b541 100644 --- a/tests/integration/directories/test_directories.py +++ b/tests/integration/directories/test_directories.py @@ -328,4 +328,60 @@ def test_finedir_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_client_proxy): ret = gkfs_client_proxy.directory_validate( topdir, 1000) - assert ret.retval == 1000+3 \ No newline at end of file + assert ret.retval == 1000+3 + + +def test_extended_proxy(gkfs_daemon_proxy, gkfs_proxy, gkfs_shell_proxy, gkfs_client_proxy): + topdir = gkfs_daemon_proxy.mountdir / "test_extended" + dir_a = topdir / "dir_a" + dir_b = topdir / "dir_b" + file_a = topdir / "file_a" + subdir_a = dir_a / "subdir_a" + + # create topdir + ret = gkfs_client_proxy.mkdir( + topdir, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 0 + + ret = gkfs_client_proxy.mkdir( + dir_a, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 0 + + ret = gkfs_client_proxy.mkdir( + dir_b, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 0 + + ret = gkfs_client_proxy.mkdir( + subdir_a, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 0 + + ret = gkfs_client_proxy.open(file_a, + os.O_CREAT, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + assert ret.retval != -1 + + buf = b'42' + ret = gkfs_client_proxy.write(file_a, buf, 1) + + assert ret.retval == 1 + + cmd = gkfs_shell_proxy.sfind( + topdir, + '-M', + gkfs_daemon_proxy.mountdir, + '-S', + 1, + '-name', + '*_k*' + ) + + assert cmd.exit_code == 0 + assert cmd.stdout.decode() == "MATCHED 0/4\n" \ No newline at end of file diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index a0cc80ab1..07e35dbbf 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -634,10 +634,11 @@ class ShellClient: on a GekkoFS instance. """ - def __init__(self, workspace): + def __init__(self, workspace, proxy = False): self._workspace = workspace self._search_paths = _find_search_paths(self._workspace.bindirs) self._env = os.environ.copy() + self._proxy = proxy libdirs = ':'.join( filter(None, [os.environ.get('LD_LIBRARY_PATH', '')] + @@ -670,6 +671,9 @@ class ShellClient: 'LIBGKFS_LOG_SYSCALL_FILTER': gkfs_client_log_syscall_filter } + if (self._proxy == True): + self._patched_env['LIBGKFS_PROXY_PID_FILE'] = str(self.cwd / gkfs_proxy_pid) + self._env.update(self._patched_env) @property -- GitLab From 9378d1e9e5059b766ab367b8fefcae4547757c4d Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 18 Mar 2025 18:38:20 +0100 Subject: [PATCH 11/13] Add support for building tools and enhance integration tests with malleability checks --- CMakePresets.json | 3 +- tests/integration/CMakeLists.txt | 1 + tests/integration/conftest.py | 2 +- tests/integration/harness/gkfs.py | 5 +- .../integration/syscalls/test_malleability.py | 61 +++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/integration/syscalls/test_malleability.py diff --git a/CMakePresets.json b/CMakePresets.json index 9e61710fa..f01b9ef0c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -72,6 +72,7 @@ "CMAKE_INSTALL_PREFIX": "${sourceDir}/gkfs/install", "GKFS_USE_GUIDED_DISTRIBUTION": true, "GKFS_ENABLE_PARALLAX": false, + "GKFS_BUILD_TOOLS": true, "GKFS_BUILD_TESTS": true, "GKFS_INSTALL_TESTS": true, "GKFS_CHUNK_STATS": true, @@ -142,4 +143,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 1fe2fde4a..1bb3bdf81 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -62,6 +62,7 @@ gkfs_enable_python_testing( ${CMAKE_BINARY_DIR}/tests/integration/harness/ ${CMAKE_BINARY_DIR}/examples/gfind/ ${CMAKE_BINARY_DIR}/examples/user_library/ + ${CMAKE_BINARY_DIR}/tools/ ${CMAKE_BINARY_DIR}/src/proxy/ LIBRARY_PREFIX_DIRECTORIES ${CMAKE_PREFIX_PATH} ) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index f1a86324c..019cb5cc2 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -167,7 +167,7 @@ def gkfs_shell(test_workspace): """ return ShellClient(test_workspace) - + @pytest.fixture def gkfs_shell_proxy(test_workspace): """ diff --git a/tests/integration/harness/gkfs.py b/tests/integration/harness/gkfs.py index 07e35dbbf..7a5460679 100644 --- a/tests/integration/harness/gkfs.py +++ b/tests/integration/harness/gkfs.py @@ -862,7 +862,7 @@ class FwdDaemon: self._address = get_ephemeral_address(interface) self._workspace = workspace - + self._hostfile = str(self.cwd / gkfwd_hosts_file) self._cmd = sh.Command(gkfwd_daemon_cmd, self._workspace.bindirs) self._env = os.environ.copy() @@ -997,6 +997,9 @@ class FwdDaemon: def interface(self): return self._interface + @property + def hostfile(self): + return self._hostfile class FwdClient: """ diff --git a/tests/integration/syscalls/test_malleability.py b/tests/integration/syscalls/test_malleability.py new file mode 100644 index 000000000..7fb1ed458 --- /dev/null +++ b/tests/integration/syscalls/test_malleability.py @@ -0,0 +1,61 @@ +################################################################################ +# Copyright 2018-2025, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2025, Johannes Gutenberg Universitaet Mainz, Germany # +# # +# This software was partially supported by the # +# EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # +# # +# This software was partially supported by the # +# ADA-FS project under the SPPEXA project funded by the DFG. # +# # +# This file is part of GekkoFS. # +# # +# GekkoFS is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# GekkoFS is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with GekkoFS. If not, see . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sh +import sys +import pytest +from harness.logger import logger + +nonexisting = "nonexisting" + +def test_malleability(gkfwd_daemon_factory, gkfs_shell): + + d00 = gkfwd_daemon_factory.create() + # Add "#FS_INSTANCE_END" in the file with name d00.hostfile + with open(d00.hostfile, 'a') as f: + f.write("#FS_INSTANCE_END\n") + + d01 = gkfwd_daemon_factory.create() + + cmd = gkfs_shell.gkfs_malleability('expand','status') + + cmd = gkfs_shell.gkfs_malleability('expand','start') + + import time + time.sleep(10) + + cmd = gkfs_shell.gkfs_malleability('expand','status') + + cmd = gkfs_shell.gkfs_malleability('expand','finalize') + \ No newline at end of file -- GitLab From 4a6dd8bcc4c83a3eafef8d0afa802c40413edc67 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 18 Mar 2025 19:05:05 +0100 Subject: [PATCH 12/13] Enhance malleability test by adding file creation and writing iterations --- .../integration/syscalls/test_malleability.py | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/integration/syscalls/test_malleability.py b/tests/integration/syscalls/test_malleability.py index 7fb1ed458..e5122eac9 100644 --- a/tests/integration/syscalls/test_malleability.py +++ b/tests/integration/syscalls/test_malleability.py @@ -39,13 +39,37 @@ from harness.logger import logger nonexisting = "nonexisting" -def test_malleability(gkfwd_daemon_factory, gkfs_shell): +def test_malleability(gkfwd_daemon_factory, gkfs_client, gkfs_shell): d00 = gkfwd_daemon_factory.create() # Add "#FS_INSTANCE_END" in the file with name d00.hostfile + + + with open(d00.hostfile, 'a') as f: f.write("#FS_INSTANCE_END\n") + # loop 10 times, and create a file in each iteration + + + + for i in range(10): + file = d00.mountdir / f"file{i}" + # create a file in gekkofs + ret = gkfs_client.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval != -1 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(file, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + # Create content + d01 = gkfwd_daemon_factory.create() cmd = gkfs_shell.gkfs_malleability('expand','status') -- GitLab From d7f7004ff3b8bb9ff540387e72fb82a143af882e Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 18 Mar 2025 20:28:28 +0100 Subject: [PATCH 13/13] Update CHANGELOG and enhance malleability tests with additional proxy checks and shutdown procedures --- CHANGELOG.md | 3 +- tests/integration/forwarding/test_map.py | 10 ++++- .../integration/syscalls/test_malleability.py | 41 +++++++++++-------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8f49b8d..ce2b80ba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased ### New - Added cppcheck code checking capabilities ([!214](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/214)) - + - Tests to cover proxy and malleability ([!222](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/222)) + ### Changed - Tests check ret for -1 instead of 10000 fd ([!320](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/320)) - Allow some more script tests to run as pthread_at_fork solved some issues. diff --git a/tests/integration/forwarding/test_map.py b/tests/integration/forwarding/test_map.py index 92eae8fa5..0ba66184e 100644 --- a/tests/integration/forwarding/test_map.py +++ b/tests/integration/forwarding/test_map.py @@ -138,6 +138,10 @@ def test_two_io_nodes(gkfwd_daemon_factory, gkfwd_client_factory): assert ion == '1' + d00.shutdown() + d01.shutdown() + + def test_two_io_nodes_remap(gkfwd_daemon_factory, gkfwd_client_factory): """Write files from two clients using two daemons""" @@ -201,6 +205,8 @@ def test_two_io_nodes_remap(gkfwd_daemon_factory, gkfwd_client_factory): ion = line.split()[-1] assert ion == '1' + d00.shutdown() + d01.shutdown() def test_two_io_nodes_operations(gkfwd_daemon_factory, gkfwd_client_factory): """Write files from one client and read in the other using two daemons""" @@ -284,4 +290,6 @@ def test_two_io_nodes_operations(gkfwd_daemon_factory, gkfwd_client_factory): if 'Forward to' in line: ion = line.split()[-1] - assert ion == '1' \ No newline at end of file + assert ion == '1' + d00.shutdown() + d01.shutdown() \ No newline at end of file diff --git a/tests/integration/syscalls/test_malleability.py b/tests/integration/syscalls/test_malleability.py index e5122eac9..ce3336013 100644 --- a/tests/integration/syscalls/test_malleability.py +++ b/tests/integration/syscalls/test_malleability.py @@ -40,20 +40,18 @@ from harness.logger import logger nonexisting = "nonexisting" def test_malleability(gkfwd_daemon_factory, gkfs_client, gkfs_shell): - + import time d00 = gkfwd_daemon_factory.create() # Add "#FS_INSTANCE_END" in the file with name d00.hostfile + d01 = gkfwd_daemon_factory.create() - - + time.sleep(10) with open(d00.hostfile, 'a') as f: f.write("#FS_INSTANCE_END\n") # loop 10 times, and create a file in each iteration - - - for i in range(10): + for i in range(3): file = d00.mountdir / f"file{i}" # create a file in gekkofs ret = gkfs_client.open(file, @@ -62,24 +60,35 @@ def test_malleability(gkfwd_daemon_factory, gkfs_client, gkfs_shell): assert ret.retval != -1 - # write a buffer we know - buf = b'42' - ret = gkfs_client.write(file, buf, len(buf)) - - assert ret.retval == len(buf) # Return the number of written bytes + ret = gkfs_client.write_validate(file, 64096) + assert ret.retval == 1 # Create content - d01 = gkfwd_daemon_factory.create() + d02 = gkfwd_daemon_factory.create() + d03 = gkfwd_daemon_factory.create() cmd = gkfs_shell.gkfs_malleability('expand','status') + assert cmd.exit_code == 0 + assert cmd.stdout.decode() == "No expansion running/finished.\n" cmd = gkfs_shell.gkfs_malleability('expand','start') + assert cmd.stdout.decode() == "Expansion process from 2 nodes to 4 nodes launched...\n" + assert cmd.exit_code == 0 + + while True: + cmd = gkfs_shell.gkfs_malleability('expand','status') + if cmd.stdout.decode() == "No expansion running/finished.\n": + break + time.sleep(1) - import time - time.sleep(10) + cmd = gkfs_shell.gkfs_malleability('expand','finalize') + assert cmd.stdout.decode() == "Expand finalize 0\n" + assert cmd.exit_code == 0 - cmd = gkfs_shell.gkfs_malleability('expand','status') - cmd = gkfs_shell.gkfs_malleability('expand','finalize') + d00.shutdown() + d01.shutdown() + d02.shutdown() + d03.shutdown() \ No newline at end of file -- GitLab