mirror of
https://github.com/samba-team/samba.git
synced 2025-03-27 22:50:26 +03:00
selftest: Add linux namespace support (USE_NAMESPACES=1)
This hooks up the selftest/ns/* scripts added earlier with the selftest system, so developers can optionally run a testenv or test using linux namespaces instead of socket-wrapper. The idea is this is experimental functionality that we can extend further in future, in order to make testing Samba more versatile. + The top-level WAF script now does an 'unshare' to create a new top-level 'selftest' namespace in which to create the testenv(s). + selftest.pl creates a common 'selftest0' bridge to connect together the individual DCs. + Update Samba.pm so it can use real IPs instead of loopback addresses. In fork_and_exec(), we add a couple of hooks so that the binary gets started in a different namespace (using unshare/start_in_ns.sh), and the parent process connects the new child namespace up to the common selftest0 bridge (using add_bridge_iface.sh). Signed-off-by: Tim Beale <timbeale@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
c9e6251382
commit
067b4fc03f
@ -424,6 +424,16 @@ if ($opt_libuid_wrapper_so_path) {
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($ENV{USE_NAMESPACES})) {
|
||||
print "Using linux containerization for selftest testenv(s)...\n";
|
||||
|
||||
# Create a common bridge to connect up the testenv namespaces. We give
|
||||
# it the client's IP address, as this is where the tests will run from
|
||||
my $ipv4_addr = Samba::get_ipv4_addr("client");
|
||||
my $ipv6_addr = Samba::get_ipv6_addr("client");
|
||||
system "$ENV{SRCDIR_ABS}/selftest/ns/create_bridge.sh selftest0 $ipv4_addr $ipv6_addr";
|
||||
}
|
||||
|
||||
$ENV{LD_PRELOAD} = $ld_preload;
|
||||
print "LD_PRELOAD=$ENV{LD_PRELOAD}\n";
|
||||
|
||||
|
@ -516,7 +516,13 @@ sub get_ipv4_addr
|
||||
$swiface += $iface_num;
|
||||
}
|
||||
|
||||
return "127.0.0.$swiface";
|
||||
if (use_namespaces()) {
|
||||
# use real IPs if selftest is running in its own network namespace
|
||||
return "10.0.0.$swiface";
|
||||
} else {
|
||||
# use loopback IPs with socket-wrapper
|
||||
return "127.0.0.$swiface";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_ipv6_addr
|
||||
@ -541,7 +547,12 @@ sub get_interfaces_config
|
||||
}
|
||||
for (my $i = 0; $i < $num_ips; $i++) {
|
||||
my $ipv4_addr = Samba::get_ipv4_addr($hostname, $i);
|
||||
$interfaces .= "$ipv4_addr/8 ";
|
||||
if (use_namespaces()) {
|
||||
# use a /24 subnet with network namespaces
|
||||
$interfaces .= "$ipv4_addr/24 ";
|
||||
} else {
|
||||
$interfaces .= "$ipv4_addr/8 ";
|
||||
}
|
||||
}
|
||||
|
||||
my $ipv6_addr = Samba::get_ipv6_addr($hostname);
|
||||
@ -627,10 +638,13 @@ sub fork_and_exec
|
||||
unlink($daemon_ctx->{LOG_FILE});
|
||||
print "STARTING $daemon_ctx->{NAME} for $ENV{ENVNAME}...";
|
||||
|
||||
my $parent_pid = $$;
|
||||
my $pid = fork();
|
||||
|
||||
# exec the daemon in the child process
|
||||
if ($pid == 0) {
|
||||
my @preargs = ();
|
||||
|
||||
# redirect the daemon's stdout/stderr to a log file
|
||||
if (defined($daemon_ctx->{TEE_STDOUT})) {
|
||||
# in some cases, we want out from samba to go to the log file,
|
||||
@ -671,12 +685,27 @@ sub fork_and_exec
|
||||
close($env_vars->{STDIN_PIPE});
|
||||
open STDIN, ">&", $STDIN_READER or die "can't dup STDIN_READER to STDIN: $!";
|
||||
|
||||
# if using kernel namespaces, prepend the command so the process runs in
|
||||
# its own namespace
|
||||
if (Samba::use_namespaces()) {
|
||||
@preargs = ns_exec_preargs($parent_pid, $env_vars);
|
||||
}
|
||||
|
||||
# the command args are stored as an array reference (because...Perl),
|
||||
# so convert the reference back to an array
|
||||
my @full_cmd = @{ $daemon_ctx->{FULL_CMD} };
|
||||
exec(@full_cmd) or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
|
||||
|
||||
exec(@preargs, @full_cmd) or die("Unable to start $ENV{MAKE_TEST_BINARY}: $!");
|
||||
}
|
||||
|
||||
print "DONE ($pid)\n";
|
||||
|
||||
# if using kernel namespaces, we now establish a connection between the
|
||||
# main selftest namespace (i.e. this process) and the new child namespace
|
||||
if (use_namespaces()) {
|
||||
ns_child_forked($pid, $env_vars);
|
||||
}
|
||||
|
||||
return $pid;
|
||||
}
|
||||
|
||||
@ -797,4 +826,78 @@ sub export_envvars_to_file
|
||||
close(FILE);
|
||||
}
|
||||
|
||||
# Returns true if kernel namespaces are being used instead of socket-wrapper.
|
||||
# The default is false.
|
||||
sub use_namespaces
|
||||
{
|
||||
return defined($ENV{USE_NAMESPACES});
|
||||
}
|
||||
|
||||
# returns a given testenv's interface-name (only when USE_NAMESPACES=1)
|
||||
sub ns_interface_name
|
||||
{
|
||||
my ($hostname) = @_;
|
||||
|
||||
# when using namespaces, each testenv has its own vethX interface,
|
||||
# where X = Samba::get_interface(testenv_name)
|
||||
my $iface = get_interface($hostname);
|
||||
return "veth$iface";
|
||||
}
|
||||
|
||||
# Called after a new child namespace has been forked
|
||||
sub ns_child_forked
|
||||
{
|
||||
my ($child_pid, $env_vars) = @_;
|
||||
|
||||
# we only need to do this for the first child forked for this testenv
|
||||
if (defined($env_vars->{NS_PID})) {
|
||||
return;
|
||||
}
|
||||
|
||||
# store the child PID. It's the only way the main (selftest) namespace can
|
||||
# access the new child (testenv) namespace.
|
||||
$env_vars->{NS_PID} = $child_pid;
|
||||
|
||||
# Add the new child namespace's interface to the main selftest bridge.
|
||||
# This connects together the various testenvs so that selftest can talk to
|
||||
# them all
|
||||
my $iface = ns_interface_name($env_vars->{NETBIOSNAME});
|
||||
system "$ENV{SRCDIR}/selftest/ns/add_bridge_iface.sh $iface-br selftest0";
|
||||
}
|
||||
|
||||
# returns args to prepend to a command in order to execute it the correct
|
||||
# namespace for the testenv (creating a new namespace if needed).
|
||||
# This should only used when USE_NAMESPACES=1 is set.
|
||||
sub ns_exec_preargs
|
||||
{
|
||||
my ($parent_pid, $env_vars) = @_;
|
||||
|
||||
# NS_PID stores the pid of the first child daemon run in this namespace
|
||||
if (defined($env_vars->{NS_PID})) {
|
||||
|
||||
# the namespace has already been created previously. So we use nsenter
|
||||
# to execute the command in the given testenv's namespace. We need to
|
||||
# use the NS_PID to identify this particular namespace
|
||||
return ("nsenter", "-t", "$env_vars->{NS_PID}", "--net");
|
||||
} else {
|
||||
|
||||
# We need to create a new namespace for this daemon (i.e. we're
|
||||
# setting up a new testenv). First, write the environment variables to
|
||||
# an exports.sh file for this testenv (for convenient access by the
|
||||
# namespace scripts).
|
||||
my $exports_file = "$env_vars->{TESTENV_DIR}/exports.sh";
|
||||
export_envvars_to_file($exports_file, $env_vars);
|
||||
|
||||
# when using namespaces, each testenv has its own veth interface
|
||||
my $interface = ns_interface_name($env_vars->{NETBIOSNAME});
|
||||
|
||||
# we use unshare to create a new network namespace. The start_in_ns.sh
|
||||
# helper script gets run first to setup the new namespace's interfaces.
|
||||
# (This all gets prepended around the actual command to run in the new
|
||||
# namespace)
|
||||
return ("unshare", "--net", "$ENV{SRCDIR}/selftest/ns/start_in_ns.sh",
|
||||
$interface, $exports_file, $parent_pid);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -468,6 +468,9 @@ sub get_cmd_env_vars
|
||||
return $cmd_env;
|
||||
}
|
||||
|
||||
# Sets up a forest trust namespace.
|
||||
# (Note this is different to kernel namespaces, setup by the
|
||||
# USE_NAMESPACES=1 option)
|
||||
sub setup_namespaces($$:$$)
|
||||
{
|
||||
my ($self, $localenv, $upn_array, $spn_array) = @_;
|
||||
|
@ -246,9 +246,12 @@ def cmd_testonly(opt):
|
||||
|
||||
env.OPTIONS += " --nss_wrapper_so_path=" + CONFIG_GET(opt, 'LIBNSS_WRAPPER_SO_PATH')
|
||||
env.OPTIONS += " --resolv_wrapper_so_path=" + CONFIG_GET(opt, 'LIBRESOLV_WRAPPER_SO_PATH')
|
||||
env.OPTIONS += " --socket_wrapper_so_path=" + CONFIG_GET(opt, 'LIBSOCKET_WRAPPER_SO_PATH')
|
||||
env.OPTIONS += " --uid_wrapper_so_path=" + CONFIG_GET(opt, 'LIBUID_WRAPPER_SO_PATH')
|
||||
|
||||
# selftest can optionally use kernel namespaces instead of socket-wrapper
|
||||
if os.environ.get('USE_NAMESPACES') is None:
|
||||
env.OPTIONS += " --socket_wrapper_so_path=" + CONFIG_GET(opt, 'LIBSOCKET_WRAPPER_SO_PATH')
|
||||
|
||||
#if unversioned_sys_platform in ('freebsd', 'netbsd', 'openbsd', 'sunos'):
|
||||
# env.OPTIONS += " --use-dns-faking"
|
||||
|
||||
@ -277,6 +280,13 @@ def cmd_testonly(opt):
|
||||
# We use the full path rather than relative path to avoid problems on some platforms (ie. solaris 8).
|
||||
env.CORE_COMMAND = '${PERL} ${srcdir}/selftest/selftest.pl --target=${SELFTEST_TARGET} --prefix=${SELFTEST_PREFIX} --srcdir=${srcdir} --exclude=${srcdir}/selftest/skip ${TESTLISTS} ${OPTIONS} ${TESTS}'
|
||||
|
||||
# If using namespaces (rather than socket-wrapper), run the selftest script
|
||||
# in its own network namespace (by doing an 'unshare'). (To create a new
|
||||
# namespace as a non-root user, we have to also unshare the current user
|
||||
# namespace, and remap ourself as root in the namespace created)
|
||||
if os.environ.get('USE_NAMESPACES') is not None:
|
||||
env.CORE_COMMAND = 'unshare --net --user --map-root-user ' + env.CORE_COMMAND
|
||||
|
||||
if env.ADDRESS_SANITIZER:
|
||||
# For now we cannot run with leak detection
|
||||
no_leak_check = "ASAN_OPTIONS=detect_leaks=0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user