exports_setup.in 6.16 KB
Newer Older
1 2 3 4 5 6 7 8 9
#!/usr/bin/perl -wT
use English;
use Fcntl ':flock';

#
# Create an /etc/exports.tail file based on current reserved table and project
# members. Fire that tail over to the fileserver where it is concatenated with
# the head file to become the new /etc/exports
#
10 11
# This script always does the right thing, so it does not matter who calls it. 
#
12 13 14 15 16 17 18 19 20 21 22 23 24 25
# usage: exports_setup
#

#
# Configure variables
#
my $TB		= "@prefix@";
my $TBOPS       = "@TBOPSEMAIL@";

# Note no -n option. We redirect stdin from the new exports file below.
my $SSH		= "sshtb -q -l root fs.emulab.net";
my $PROG	= "/usr/testbed/sbin/exports_setup.proxy";
my $exportstail = "/var/tmp/exports.tail";
my $lockfile    = "/var/tmp/testbed_exports_lockfile";
26
my $projdir     = "/q/proj";
27 28 29 30 31 32 33 34 35 36 37
my $usersdir    = "/users";
my $dbg		= 0;
my @row;

#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("Must be root! Maybe its a development version?");
}
# XXX Hacky!
38 39 40
#if ($TB ne "/usr/testbed") {
#    die("Wrong version. Maybe its a development version?");
#}
41 42 43 44 45

# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

46 47 48 49
#
# Turn off line buffering on output
#
$| = 1;
50

51
#
52
# Testbed Support libraries
53 54 55
# 
push(@INC, "$TB/lib");
require libtestbed;
56
require libdb;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

#
# We need to serialize this script to avoid a trashed map file. Use
# a dummy file in /var/tmp, opened for writing and flock'ed. 
#
open(LOCK, ">>$lockfile") || fatal("Couldn't open $lockfile\n");
$count = 0;
while (flock(LOCK, LOCK_EX|LOCK_NB) == 0) {
    print "Another /etc/exports update in progress. Waiting a moment ...\n";
    if ($count++ > 20) {
	fatal("Could not get the lock after a long time!\n");
    }
    sleep(1);
}

#
# We stick the new map entries into the tail file. First zero it out.
#
open(MAP, ">$exportstail") || fatal("Couldn't open $exportstail\n");
print MAP "\n";
print MAP "#\n";
print MAP "# DO NOT EDIT below this point. Auto generated entries!\n";
print MAP "#\n";
print MAP "\n";

#
# We export to particular nodes, based on the experiment that is allocated
# to the node. Since we want the exports file to be based on IP numbers,
# we need this crazy join to find out the type of the node, so we can find
# the control network card number, so we can find the IP address for that
# card.
#
$db_result =
90 91 92 93 94 95 96 97
    DBQueryFatal("select reserved.node_id,reserved.pid,interfaces.IP ".
		 "from reserved ".
		 "left join nodes on reserved.node_id=nodes.node_id ".
		 "left join node_types on node_types.type=nodes.type ".
		 "left join interfaces on reserved.node_id=interfaces.node_id".
		 " and interfaces.card=node_types.control_net ".
		 "where interfaces.IP!='NULL' ".
		 "order by reserved.pid,interfaces.IP");
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 126 127 128 129 130 131 132 133 134 135

#
# Generate a per project directory line, listing all of the nodes for
# that project. We could do it one per line, but too messy.
#
if ($db_result->numrows > 0) {
    my $lastpid  = 0;
    my @hostlist = ();
    
    print MAP "#\n";
    print MAP "# Export Project directories\n";
    print MAP "#\n";

    while (@row = $db_result->fetchrow_array) {
	my $node_id = $row[0];
	my $pid     = $row[1];
	my $ip      = $row[2];

	if (!$lastpid) {
	    $lastpid = $pid;
	    push(@hostlist, $ip);
	    next;
	}

	if ($pid ne $lastpid) {
	    print MAP "$projdir/$lastpid -maproot=root @hostlist\n";
	    @hostlist = ();
	    $lastpid = $pid;
	}
	push(@hostlist, $ip);
    }
    print MAP "$projdir/$lastpid -maproot=root @hostlist\n";
}

#
# Now we need the users list for exporting from /users. We need to export
# all of the project members to all of the machines in that projects
# experiments. Also, since we again want the IPs, we need all of that crazy
136 137 138 139 140 141 142 143
# join again. Even worse, we cannot list an IP address more than once, so
# we end up with mulitple exports for any particular user (cause they can be
# in multiple experiments withing a project), but with non overlapping
# sets of IP addresses. For example:
#
#	/users/stoller /users/mike 101.101.101.100 101.101.101.101
#	/users/stoller             101.101.101.102
#       /users/mike                101.101.101.103
144 145
#
$db_result =
146 147 148 149 150 151 152 153 154
   DBQueryFatal("select reserved.node_id,reserved.pid,".
		"proj_memb.uid,interfaces.IP from reserved ".
		"left join proj_memb on proj_memb.pid=reserved.pid ".
		"left join nodes on reserved.node_id=nodes.node_id ".
		"left join node_types on node_types.type=nodes.type ".
		"left join interfaces on reserved.node_id=interfaces.node_id ".
		"and interfaces.card=node_types.control_net ".
		"where interfaces.IP!='NULL' and proj_memb.trust!='none' ".
		"order by reserved.pid,reserved.node_id");
155 156 157 158 159 160

#
# Generate a per user directory line, listing all of the nodes for
# that user. We could do it one per line, but too messy.
#
if ($db_result->numrows > 0) {
161 162 163
    my $lastpid = 0;
    my %hostlist = ();
    my %userlist = ();
164 165 166 167 168 169
    
    print MAP "#\n";
    print MAP "# Export User directories\n";
    print MAP "#\n";

    while (@row = $db_result->fetchrow_array) {
170 171 172 173
	my $node     = $row[0];
	my $pid      = $row[1];
	my $uid      = $row[2];
	my $ip       = $row[3];
174

175 176 177 178
	if (!$lastpid) {
	    $lastpid         = $pid;
	    $hostlist{$ip}   = $ip;
	    $userlist{$uid}  = $uid;
179 180 181
	    next;
	}

182 183 184 185 186 187 188 189 190 191 192 193 194 195
	if ($pid ne $lastpid) {
	    foreach my $user (keys(%userlist)) {
		print MAP "$usersdir/$user ";
	    }
	    print MAP "-maproot=root ";
	    
	    foreach my $host (keys(%hostlist)) {
		print MAP "$host ";
	    }
	    print MAP "\n";

	    %hostlist = ();
	    %userlist = ();
	    $lastpid = $pid;
196
	}
197 198 199 200 201 202 203 204 205 206 207 208 209
	$hostlist{$ip}   = $ip;
	$userlist{$uid}  = $uid
    }
    #
    # Last project set
    # 
    foreach my $user (keys(%userlist)) {
	print MAP "$usersdir/$user ";
    }
    print MAP "-maproot=root ";
	    
    foreach my $host (keys(%hostlist)) {
	print MAP "$host ";
210
    }
211
    print MAP "\n";
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
}

print MAP "\n";
close(MAP);

#
# Fire the new tail file over to the fileserver to finish. We cat the file
# right into it.
#
$UID = 0;
system("$SSH $PROG < $exportstail") == 0 or
    fatal("Failed: $SSH $PROG < $exportstail: $?");

unlink("$exportstail");

#
# Close the lock file. Exiting releases it, but might as well.
#
close(LOCK);
exit(0);

sub fatal {
    local($msg) = $_[0];

236
    SENDMAIL($TBOPS, "TESTBED: Exports Setup Failed", $msg);
237 238
    die($msg);
}