exports_setup.in 6.42 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
# usage: exports_setup
#

#
# Configure variables
#
my $TB		= "@prefix@";
my $TBOPS       = "@TBOPSEMAIL@";
20
my $TESTMODE    = @TESTMODE@;
21
22

# Note no -n option. We redirect stdin from the new exports file below.
Robert Ricci's avatar
Robert Ricci committed
23
my $SSH		= "$TB/bin/sshtb -l root fs.emulab.net";
24
25
26
my $PROG	= "/usr/testbed/sbin/exports_setup.proxy";
my $exportstail = "/var/tmp/exports.tail";
my $lockfile    = "/var/tmp/testbed_exports_lockfile";
27
my $projdir     = "/q/proj";
28
29
30
31
32
33
34
35
36
37
38
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!
39
40
41
#if ($TB ne "/usr/testbed") {
#    die("Wrong version. Maybe its a development version?");
#}
42
43
44
45
46

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

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

52
#
53
# Testbed Support libraries
54
# 
55
56
57
use lib "@prefix@/lib";
use libdb;
use libtestbed;
58
59
60
61
62

#
# 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. 
#
63
64
65
66
if (!$TESTMODE) {
  open(LOCK, ">>$lockfile") || fatal("Couldn't open $lockfile\n");
  $count = 0;
  while (flock(LOCK, LOCK_EX|LOCK_NB) == 0) {
67
68
    print "Another /etc/exports update in progress. Waiting a moment ...\n";
    if ($count++ > 20) {
69
      fatal("Could not get the lock after a long time!\n");
70
71
    }
    sleep(1);
72
  }
73
74
75
76
77
}

#
# We stick the new map entries into the tail file. First zero it out.
#
78
79
80
81
82
if (!$TESTMODE) {
  open(MAP, ">$exportstail") || fatal("Couldn't open $exportstail\n");
} else {
  open(MAP, ">/dev/null") || fatal("Couldn't open /dev/null\n");
}
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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 =
97
98
99
100
101
102
103
104
    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");
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

#
# 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) {
130
131
	    print MAP "$projdir/$lastpid $projdir/$lastpid/images ".
		      "-maproot=root @hostlist\n";
132
133
134
135
136
	    @hostlist = ();
	    $lastpid = $pid;
	}
	push(@hostlist, $ip);
    }
137
138
    print MAP "$projdir/$lastpid $projdir/$lastpid/images ".
	      "-maproot=root @hostlist\n";
139
140
141
142
143
144
}

#
# 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
145
146
147
148
149
150
151
152
# 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
153
154
#
$db_result =
155
156
157
158
159
160
161
162
163
   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");
164
165
166
167
168
169

#
# 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) {
170
171
172
    my $lastpid = 0;
    my %hostlist = ();
    my %userlist = ();
173
174
175
176
177
178
    
    print MAP "#\n";
    print MAP "# Export User directories\n";
    print MAP "#\n";

    while (@row = $db_result->fetchrow_array) {
179
180
181
182
	my $node     = $row[0];
	my $pid      = $row[1];
	my $uid      = $row[2];
	my $ip       = $row[3];
183

184
185
186
187
	if (!$lastpid) {
	    $lastpid         = $pid;
	    $hostlist{$ip}   = $ip;
	    $userlist{$uid}  = $uid;
188
189
190
	    next;
	}

191
192
193
194
195
196
197
198
199
200
201
202
203
204
	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;
205
	}
206
207
208
209
210
211
212
213
214
215
216
217
218
	$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 ";
219
    }
220
    print MAP "\n";
221
222
223
224
225
226
227
228
229
}

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

#
# Fire the new tail file over to the fileserver to finish. We cat the file
# right into it.
#
230
231
232
if (!$TESTMODE) {
  $UID = 0;
  system("$SSH $PROG < $exportstail") == 0 or
233
    fatal("Failed: $SSH $PROG < $exportstail: $?");
234
  unlink("$exportstail");
235

236
237
238
239
240
  #
  # Close the lock file. Exiting releases it, but might as well.
  #
  close(LOCK);
}
241
242
243
244
245
246

exit(0);

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

247
    SENDMAIL($TBOPS, "TESTBED: Exports Setup Failed", $msg);
248
249
    die($msg);
}