Commit 386c2778 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Support for vnodes, plus some other changes:

1) Merge in the accounts code that I did for ron. Instead of resetting
   the password file on each reboot, look at the node status to
   determine if the password/group file should be reset. If the
   node is free, reset it. Otherwise, we track changes to the password
   and group file now, so that users can change it and not have their
   changes wiped out at each reboot. I had to do this for the ron
   nodes so that the testbed software would not alter or delete
   already existing accounts; I keep a couple of little dbm files
   listing all the accounts/groups added. The only downside right now
   is if a node is reallocated before it is wiped clean; I plan to add
   an os_teardown phase to experiment termination asap.

2) Add tunnels support. New DB table (tunnels) provides information
   for running vtund to link up to remote nodes. Creates a vtund.conf
   file on the fly, and fires them off. The complication is that you
   cannot do the ifconfigs or the routes until the tunnels are
   connected, so that stuff has to be configured within the vtund.conf
   file on a per tunnel basis. vtund.conf has some sections for
   running commands when tunnels are brought up or down.

3) Damage the routing configuration code that Mike did. To support
   tunnels, as noted above, rc,route is no longer a simple list of
   commands, but a program that adds/dels routes based on the netmask,
   with a special "enable" section for the other stuff. This allows me
   to call it from vtund.conf for up/down on each tunnel'ed
   interfaces, as needed. Quite gross, but no way around it.

4) For remote nodes, add a vnodesetup script, invoked from boss when
   experiments are setup/torndown. This gets the tunnel/route/trafgen
   configuration and runs them. It then goes into the background
   waiting for a death signal, at which time it brings them down and
   cleans out the vnode state.
parent bfad5e85
This diff is collapsed.
......@@ -11,7 +11,8 @@ use Exporter;
os_cleanup_node os_ifconfig_line os_etchosts_line
os_setup os_groupadd os_useradd os_userdel os_usermod os_mkdir
os_rpminstall_line update_delays
os_routing_enable_forward os_routing_enable_gated os_routing_add_manual
os_routing_enable_forward os_routing_enable_gated
os_routing_add_manual os_routing_del_manual os_homedirdel
);
# Must come after package declaration!
......@@ -66,10 +67,16 @@ my $TMCCCMD_DELAY = "delay";
#
# OS dependent part of cleanup node state.
#
sub os_cleanup_node () {
sub os_cleanup_node ($) {
my ($scrub) = @_;
unlink $TMDELAY;
unlink $TMDELMAP;
if (! $scrub) {
return 0;
}
printf STDOUT "Resetting passwd and group files\n";
if (system("$CP -f $TMGROUP /etc/group") != 0) {
print STDERR "Could not copy default group file into place: $!\n";
......@@ -207,6 +214,14 @@ sub os_useradd($$$$$$$$)
return 0;
}
#
# Remove a homedir. Might someday archive and ship back.
#
sub os_homedirdel($$)
{
return 0;
}
#
# Generate and return an RPM install line that is approriate for putting
# into a shell script (invoked at bootup).
......@@ -557,4 +572,21 @@ sub os_routing_add_manual($$$$$)
return $cmd;
}
sub os_routing_del_manual($$$$$)
{
my ($routetype, $destip, $destmask, $gate, $cost) = @_;
my $cmd;
if ($routetype eq "host") {
$cmd = "$ROUTE delete -host $destip";
} elsif ($routetype eq "net") {
$cmd = "$ROUTE delete -net $destip $gate $destmask";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
$cmd = "";
}
return $cmd;
}
1;
......@@ -37,7 +37,7 @@ $| = 1;
#
# First clean up the node as it would be if free.
#
cleanup_node();
cleanup_node(1);
print "Removing old sup checkouts ...\n";
if (-e $SUPCHECKDIR) {
......
......@@ -18,6 +18,11 @@ if [ -x /etc/testbed/setipod ]; then
/etc/testbed/setipod
fi
if [ -x /etc/testbed/rc.tunnel ]; then
echo "Setting up Testbed tunnels ..."
/etc/testbed/rc.tunnel
fi
if [ -x /etc/testbed/rc.ifc ]; then
echo "Setting up Testbed interfaces ..."
/etc/testbed/rc.ifc
......@@ -29,7 +34,7 @@ if [ -x /etc/testbed/rc.delay ]; then
/etc/testbed/rc.delay
elif [ -x /etc/testbed/rc.route ]; then
echo "Setting up Testbed interface routing ..."
/etc/testbed/rc.route
/etc/testbed/rc.route enable
fi
if [ -x /etc/testbed/rc.rpm ]; then
......
This diff is collapsed.
......@@ -11,7 +11,8 @@ use Exporter;
os_cleanup_node os_ifconfig_line os_etchosts_line
os_setup os_groupadd os_useradd os_userdel os_usermod os_mkdir
os_rpminstall_line enable_ipod
os_routing_enable_forward os_routing_enable_gated os_routing_add_manual
os_routing_enable_forward os_routing_enable_gated
os_routing_add_manual os_routing_del_manual os_homedirdel
);
# Must come after package declaration!
......@@ -56,9 +57,15 @@ my $ROUTE = "/sbin/route";
#
# OS dependent part of cleanup node state.
#
sub os_cleanup_node () {
sub os_cleanup_node ($) {
my ($scrub) = @_;
unlink @LOCKFILES;
if (! $scrub) {
return 0;
}
printf STDOUT "Resetting passwd and group files\n";
if (system("$CP -f $TMGROUP $TMPASSWD /etc") != 0) {
print STDERR "Could not copy default group file into place: $!\n";
......@@ -194,6 +201,14 @@ sub os_useradd($$$$$$$$)
return 0;
}
#
# Remove a homedir. Might someday archive and ship back.
#
sub os_homedirdel($$)
{
return 0;
}
#
# Generate and return an RPM install line that is approriate for putting
# into a shell script (invoked at bootup).
......@@ -297,4 +312,21 @@ sub os_routing_add_manual($$$$$)
return $cmd;
}
sub os_routing_del_manual($$$$$)
{
my ($routetype, $destip, $destmask, $gate, $cost) = @_;
my $cmd;
if ($routetype eq "host") {
$cmd = "$ROUTE delete -host $destip";
} elsif ($routetype eq "net") {
$cmd = "$ROUTE delete -net $destip netmask $destmask gw $gate";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
$cmd = "";
}
return $cmd;
}
1;
......@@ -36,7 +36,7 @@ $| = 1;
#
# First clean up the node as it would be if free.
#
cleanup_node();
cleanup_node(1);
print "Removing old sup checkouts ...\n";
if (-e $SUPCHECKDIR) {
......
......@@ -13,6 +13,11 @@ if [ -x /etc/rc.d/testbed/setup ]; then
/etc/rc.d/testbed/setup
fi
if [ -x /etc/testbed/rc.tunnel ]; then
echo "Setting up Testbed tunnels ..."
/etc/testbed/rc.tunnel
fi
if [ -x /etc/rc.d/testbed/rc.ifc ]; then
echo "Setting up Testbed Interfaces ..."
/etc/rc.d/testbed/rc.ifc
......@@ -20,7 +25,7 @@ fi
if [ -x /etc/rc.d/testbed/rc.route ]; then
echo "Setting up Testbed interface routing ..."
/etc/rc.d/testbed/rc.route
/etc/rc.d/testbed/rc.route enable
fi
if [ -x /etc/rc.d/testbed/rc.rpm ]; then
......
......@@ -16,7 +16,8 @@ all: bossnode
include $(TESTBED_SRCDIR)/GNUmakerules
INSTALL_DIR = /etc/local/etc/testbed
DESTDIR =
INSTALL_DIR = $(DESTDIR)/etc/local/etc/testbed
INSTALL = /usr/bin/install -c
INSTALL_PROG = /usr/bin/install -c -m 755
......@@ -24,6 +25,7 @@ install: misc-install script-install bin-install
dir-install:
-mkdir -p $(INSTALL_DIR)
cp /dev/null $(INSTALL_DIR)/isrem
misc-install: dir-install bossnode
$(INSTALL) bossnode $(INSTALL_DIR)/bossnode
......@@ -33,10 +35,12 @@ bin-install: dir-install
script-install: dir-install
$(INSTALL_PROG) $(SRCDIR)/update $(INSTALL_DIR)/update
$(INSTALL_PROG) $(SRCDIR)/libsetup.pm $(INSTALL_DIR)/libsetup.pm
$(INSTALL_PROG) $(SRCDIR)/vnodesetup $(INSTALL_DIR)/vnodesetup
$(INSTALL_PROG) $(SRCDIR)/../libsetup.pm $(INSTALL_DIR)/libsetup.pm
$(INSTALL_PROG) $(SRCDIR)/liblocsetup.pm $(INSTALL_DIR)/liblocsetup.pm
$(INSTALL_PROG) $(SRCDIR)/rc.testbed $(INSTALL_DIR)/rc.testbed
$(INSTALL_PROG) $(SRCDIR)/mkemuman.sh $(INSTALL_DIR)/mkemuman.sh
$(INSTALL_PROG) $(SRCDIR)/upemuman.sh $(INSTALL_DIR)/upemuman.sh
post-install:
chown root $(INSTALL_DIR)/update
......
......@@ -9,7 +9,9 @@ use Exporter;
@EXPORT =
qw ( $CP $EGREP
os_groupadd os_useradd os_userdel os_usermod os_mkdir
os_groupdel os_cleanup_node
os_groupdel os_cleanup_node os_homedirdel
os_routing_enable_forward os_routing_enable_gated
os_routing_add_manual os_routing_del_manual
);
# Must come after package declaration!
......@@ -38,11 +40,13 @@ my $GROUPDEL = "/usr/sbin/pw groupdel";
my $CHPASS = "/usr/bin/chpass -p";
my $MKDB = "/usr/sbin/pwd_mkdb -p";
my $MKDIR = "/bin/mkdir";
my $ROUTE = "/sbin/route";
#
# OS dependent part of cleanup node state.
#
sub os_cleanup_node () {
sub os_cleanup_node ($) {
my ($scrub) = @_;
return 0;
}
......@@ -94,6 +98,17 @@ sub os_usermod($$$$)
return system("$USERMOD $login -g $gid $glist");
}
#
# Remove a homedir. Might someday archive and ship back.
#
sub os_homedirdel($$)
{
my ($login, $homedir) = @_;
print "Removing home directory: $homedir\n";
return(system("rm -rf $homedir"));
}
#
# Add a user.
#
......@@ -121,4 +136,52 @@ sub os_useradd($$$$$$$$)
return 0;
}
#
# OS dependent, routing-related commands
#
sub os_routing_enable_forward()
{
return "echo 'IP forwarding not turned on!'";
}
sub os_routing_enable_gated()
{
return "echo 'GATED IS NOT ALLOWED!'";
}
sub os_routing_add_manual($$$$$)
{
my ($routetype, $destip, $destmask, $gate, $cost) = @_;
my $cmd;
if ($routetype eq "host") {
$cmd = "$ROUTE add -host $destip $gate";
} elsif ($routetype eq "net") {
$cmd = "$ROUTE add -net $destip $gate $destmask";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
$cmd = "";
}
return $cmd;
}
sub os_routing_del_manual($$$$$)
{
my ($routetype, $destip, $destmask, $gate, $cost) = @_;
my $cmd;
if ($routetype eq "host") {
$cmd = "$ROUTE delete -host $destip";
} elsif ($routetype eq "net") {
$cmd = "$ROUTE delete -net $destip $gate $destmask";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
$cmd = "";
}
return $cmd;
}
1;
#!/usr/bin/perl -wT
#
# XXX: The user homedir from boss is ignored; whatever local default is.
# Not removing homedirs of deleted accounts.
# Not overwriting existing ssh authorized_keys with new key.
# Bossinfo comes from /usr/local/etc/testbed/bossnod.
# Remote nodes use sshremote (instead of sshtb).
# A node is remote if its class is pcremote (libdb.pm).
# sshremote requires "emulabman" user on remote node with root key.
#
# On eureka:
# Install /usr/local/etc/testbed dir as root.
# Create /etc/pw.conf pointing default homedir to /z.
# Need to allow setui perl scripts (chown/chmod /usr/bin/suidperl).
#
#
# Common routines and constants for the client bootime setup stuff.
#
package libsetup;
use Exporter;
@ISA = "Exporter";
@EXPORT =
qw ( libsetup_init doaccounts nodeupdate TBBackGround
);
# Must come after package declaration!
use English;
#
# This is the home of the setup library on the client machine. The including
# program has to tell us this by calling the init routine below.
#
my $SETUPDIR;
sub libsetup_init($)
{
my($path) = @_;
$SETUPDIR = $path;
}
#
# This "local" library provides the OS dependent part. Must load this after
# defining the above function cause the local library invokes it to set the
# $SETUPDIR
#
use liblocsetup;
#
# These are the paths of various files and scripts that are part of the
# setup library.
#
sub TMCC() { "$SETUPDIR/tmcc"; }
sub TMPASSDB() { "$SETUPDIR/passdb"; }
sub TMGROUPDB() { "$SETUPDIR/groupdb"; }
#
# This is the VERSION. We send it through to tmcd so it knows what version
# responses this file is expecting.
#
# BE SURE TO BUMP THIS AS INCOMPATIBILE CHANGES TO TMCD ARE MADE!
#
sub TMCD_VERSION() { 4; };
#
# These are the TMCC commands.
#
sub TMCCCMD_ACCT() { "accounts"; }
#
# This is a debugging thing for my home network.
#
#my $NODE = "REDIRECT=155.101.132.101";
$NODE = "";
#
# Open a TMCC connection and return the "stream pointer". Caller is
# responsible for closing the stream and checking return value.
#
# usage: OPENTMCC(char *command, char *args)
#
sub OPENTMCC($;$)
{
my($cmd, $args) = @_;
local *TM;
if (!defined($args)) {
$args = "";
}
my $foo = sprintf("%s -v %d %s %s %s |",
TMCC, TMCD_VERSION, $NODE, $cmd, $args);
open(TM, $foo)
or die "Cannot start $TMCC: $!";
return (*TM);
}
#
# Run a TMCC command with the provided arguments.
#
# usage: RUNTMCC(char *command, char *args)
#
sub RUNTMCC($;$)
{
my($cmd, $args) = @_;
my($TM);
if (!defined($args)) {
$args = "";
}
$TM = OPENTMCC($cmd, $args);
close($TM)
or die $? ? "TMCC exited with status $?" : "Error closing pipe: $!";
return 0;
}
sub doaccounts ()
{
my %newaccounts = ();
my %newgroups = ();
my %deletes = ();
my %PWDDB;
my %GRPDB;
my $TM = OPENTMCC(TMCCCMD_ACCT);
#
# The strategy is to keep a record of all the groups and accounts
# added by the testbed system so that we know what to remove. We
# use a vanilla perl dbm for that, one for the groups and one for
# accounts.
#
# First just get the current set of groups/accounts from tmcd.
#
while (<$TM>) {
if ($_ =~ /^ADDGROUP NAME=([-\@\w.]+) GID=([0-9]+)/) {
#
# Group info goes in the hash table.
#
$newgroups{"emu-$1"} = $2
}
elsif ($_ =~ /^ADDUSER LOGIN=([0-9a-z]+)/) {
#
# Account info goes in the hash table.
#
$newaccounts{$1} = $_;
next;
}
else {
warn "*** WARNING: Bad accounts line: $_\n";
}
}
close($TM);
dbmopen(%PWDDB, TMPASSDB, 0660) or
die("Cannot open " . TMPASSDB . ": $!\n");
dbmopen(%GRPDB, TMGROUPDB, 0660) or
die("Cannot open " . TMGROUPDB . ": $!\n");
#
# First create any groups that do not currently exist. Add each to the
# DB as we create it.
#
while (($group, $gid) = each %newgroups) {
my ($exists,undef,$curgid) = getgrnam($group);
if ($exists) {
if ($gid != $curgid) {
warn "*** WARNING: $group/$gid mismatch with existing group\n";
}
next;
}
print "Adding group: $group/$gid\n";
if (os_groupadd($group, $gid)) {
warn "*** WARNING: Error adding new group $group/$gid\n";
next;
}
# Add to DB only if successful.
$GRPDB{$group} = $gid;
}
#
# Now remove the ones that we created previously, but are now no longer
# in the group set (as told to us by the TMCD). Note, we cannot delete
# them directly from the hash since that would mess up the foreach loop,
# so just stick them in temp and postpass it.
#
while (($group, $gid) = each %GRPDB) {
if (defined($newgroups{$group})) {
next;
}
print "Removing group: $group/$gid\n";
if (os_groupdel($group)) {
warn "*** WARNING: Error removing group $group/$gid\n";
next;
}
# Delete from DB only if successful.
$deletes{$group} = $gid;
}
while (($group, $gid) = each %deletes) {
delete($GRPDB{$group});
}
%deletes = ();
# Write the DB back out!
dbmclose(%GRPDB);
#
# Repeat the same sequence for accounts, except we remove old accounts
# first.
#
while (($login, $uid) = each %PWDDB) {
if (defined($newaccounts{$login})) {
next;
}
my ($exists,undef,$curuid,undef,
undef,undef,undef,$homedir) = getpwnam($login);
#
# If the account is gone, someone removed it by hand. Remove it
# from the DB so we do not keep trying.
#
if (! defined($exists)) {
warn "*** WARNING: Account for $login was already removed!\n";
$deletes{$login} = $login;
next;
}
#
# Check for mismatch, just in case. If there is a mismatch remove it
# from the DB so we do not keep trying.
#
if ($uid != $curuid) {
warn "*** WARNING: ".
"Account uid for $login has changed ($uid/$curuid)!\n";
$deletes{$login} = $login;
next;
}
print "Removing user: $login\n";
if (os_userdel($login) != 0) {
warn "*** WARNING: Error removing user $login\n";
next;
}
#
# Remove the home dir.
#
# Must ask for the current home dir since we rely on pw.conf.
#
if (defined($homedir) &&
index($homedir, "/${login}")) {
print "Removing home directory: $homedir\n";
if (system("rm -rf $homedir")) {
warn "*** WARNING: Could not remove homedir $homedir.\n";
}
}
# Delete from DB only if successful.
$deletes{$login} = $login;
}
while (($login, $foo) = each %deletes) {
delete($PWDDB{$login});
}
my $pat = q(ADDUSER LOGIN=([0-9a-z]+) PSWD=([^:]+) UID=(\d+) GID=(.*) );
$pat .= q(ROOT=(\d) NAME="(.*)" HOMEDIR=(.*) GLIST="(.*)" );
$pat .= q(EMULABPUBKEY="(.*)" HOMEPUBKEY="(.*)");
while (($login, $info) = each %newaccounts) {
if ($info =~ /$pat/) {
$pswd = $2;
$uid = $3;
$gid = $4;
$root = $5;
$name = $6;
$hdir = $7;
$glist = $8;
$ekey = $9;
$hkey = $10;
if ( $name =~ /^(([^:]+$|^))$/ ) {
$name = $1;
}
print "User: $login/$uid/$gid/$root/$name/$hdir/$glist\n";
my ($exists,undef,$curuid) = getpwnam($login);
if ($exists) {
if (!defined($PWDDB{$login})) {
warn "*** WARNING: ".
"Skipping since $login existed before EmulabMan!\n";
next;
}
if ($curuid != $uid) {
warn "*** WARNING: ".
"$login/$uid uid mismatch with existing login.\n";
next;
}
print "Updating $login login info.\n";
os_usermod($login, $gid, "$glist", $root);
next;
}
print "Adding $login account.\n";
if (os_useradd($login, $uid, $gid, $pswd,
"$glist", $hdir, $name, $root)) {
warn "*** WARNING: Error adding new user $login\n";
next;
}
# Add to DB only if successful.
$PWDDB{$login} = $uid;
#
# Create .ssh dir and populate it with an authkeys file.
# Must ask for the current home dir since we rely on pw.conf.
#
my (undef,undef,undef,undef,
undef,undef,undef,$homedir) = getpwuid($uid);
my $sshdir = "$homedir/.ssh";
if (! -e $sshdir && ($ekey ne "" || $hkey ne "")) {
if (! mkdir($sshdir, 0700)) {
warn("*** WARNING: Could not mkdir $sshdir: $!\n");
next;