Newer
Older
################################################################################
# Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain #
# Copyright 2015-2022, 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 <https://www.gnu.org/licenses/>. #
# #
# SPDX-License-Identifier: GPL-3.0-or-later #
################################################################################
import os, sh, sys, re, pytest, signal
import random, socket, netifaces, time
from pathlib import Path
from itertools import islice
from time import perf_counter
from pprint import pformat
from harness.logger import logger
from harness.io import IOParser
from harness.cmd import CommandParser
### some definitions required to interface with the client/daemon
gkfs_daemon_cmd = 'gkfs_daemon'
gkfs_client_cmd = 'gkfs.io'
gkfs_client_lib_file = 'libgkfs_intercept.so'
gkfs_hosts_file = 'gkfs_hosts.txt'
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']
def get_ephemeral_host():
"""
Returns a random IP in the 127.0.0.0/24. This decreases the likelihood of
races for ports by 255^3.
"""
res = '127.{}.{}.{}'.format(random.randrange(1, 255),
random.randrange(1, 255),
random.randrange(2, 255),)
return res
def get_ephemeral_port(port=0, host=None):
"""
Get an ephemeral socket at random from the kernel.
Parameters
----------
port: `str`
If specified, use this port as a base and the next free port after that
base will be returned.
host: `str`
If specified, use this host. Otherwise it will use a temporary IP in
the 127.0.0.0/24 range
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
Returns
-------
Available port to use
"""
if host is None:
host = get_ephemeral_host()
# Dynamic port-range:
# * cat /proc/sys/net/ipv4/ip_local_port_range
# 32768 61000
if port == 0:
port = random.randrange(1024, 32768)
while True:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
port = s.getsockname()[1]
s.close()
return port
except socket.error:
port = random.randrange(1024, 32768)
def get_ephemeral_address(iface):
"""
Get an ephemeral network address (IPv4:port) from an interface
and a random port.
Parameters
----------
iface: `str`
The interface that will be used to find out the IPv4 address
for the ephemeral address.
A network address formed by iface's IPv4 address and an available
randomly selected port.
"""
return f"{iface}:{get_ephemeral_port(host=get_ip_addr(iface))}"
def _process_exists(pid):
"""
Checks whether a given PID exists in the system
Parameters
----------
pid: `int`
The PID to check for
Returns
-------
True if a process with the provided `pid` exists in the system.
False Otherwise
"""
try:
sh.ps(['-h', '-p', pid])
except Exception:
# sh.raises an Exception if the command doesn't return 0
return False
return True
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def _find_search_paths(additional_paths=None):
"""
Return the entire list of search paths available to the process. If
additional_paths is not provided, $PATH env is returned.
Parameters
----------
additional_paths: `list`
If provided, additional paths that should be used for searching before
falling back to the contents of $PATH.
Returns
-------
A list containing the paths that should be searched for commands.
"""
paths_to_search = []
if isinstance(additional_paths, (tuple, list)):
paths_to_search.extend(additional_paths)
env_paths = os.environ.get("PATH", "").split(os.pathsep)
paths_to_search.extend(env_paths)
return paths_to_search
class FwdDaemonCreator:
"""
Factory that allows tests to create forwarding daemons in a workspace.
"""
def __init__(self, interface, workspace):
self._interface = interface
self._workspace = workspace
def create(self):
"""
Create a forwarding daemon in the tests workspace.
Returns
-------
The `FwdDaemon` object to interact with the daemon.
"""
Loading full blame...