node_update.in 6.53 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3 4

#
# EMULAB-COPYRIGHT
5
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
6 7 8
# All rights reserved.
#

9 10 11 12
use English;
use Getopt::Std;

#
13 14 15
# Mark nodes for update. At the moment all kinds of things will get
# updated (mounts, accounts, tarballs, rpms). At some point these
# should be split up.
16 17 18
#
# XXX There is an inherent race condition with using this script. What if
# nodes are released while it is running?
19
#
20 21
sub usage()
{
22
    print STDOUT "Usage: node_update [-b] pid eid [node ...]\n".
23 24
	"Update user accounts and NFS mounts on nodes in your project.\n".
	"Use -b to use batch operation (place in background, send email).\n";
25 26
    exit(-1);
}
27 28 29 30 31 32 33 34 35 36 37 38 39
my  $optlist = "b";

#
# Exit codes are important; they tell the web page what has happened so
# it can say something useful to the user. Fatal errors are mostly done
# with die(), but expected errors use this routine. At some point we will
# use the DB to communicate the actual error.
#
# $status < 0 - Fatal error. Something went wrong we did not expect.
# $status = 0 - Proceeding in the background. Notified later.
# $status > 0 - Expected error. User not allowed for some reason. 
#

40 41 42 43 44 45 46 47 48 49
#
# Configure variables
#
my $TB		= "@prefix@";
my $TESTMODE    = @TESTMODE@;
my $TBOPS       = "@TBOPSEMAIL@";
my $TBLOGS      = "@TBLOGSEMAIL@";

my $expsetup    = "$TB/sbin/exports_setup";
my $batchmode   = 0;
50 51 52 53 54 55 56
my @nodes       = ();
my $user_name;
my $user_email;
my $logname;
my $failed	= 0;
my $dbuid;
my $estate;
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

#
# Load the Testbed support stuff. 
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;

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

# Turn off line buffering on output
$| = 1; 

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"b"})) {
    $batchmode = 1;
}
83 84 85 86 87
if (@ARGV < 2) {
    usage();
}
my $pid   = shift(@ARGV);
my $eid   = shift(@ARGV);
88 89 90 91 92 93 94

#
# Untaint the arguments.
#
if ($pid =~ /^([-\@\w]+)$/) {
    $pid = $1;
}
95 96 97
else {
    die("*** Bad data in pid: $pid\n");
}	
98 99 100
if ($eid =~ /^([-\@\w]+)$/) {
    $eid = $1;
}
101 102
else {
    die("*** Bad data in eid: $eid\n");
103
}
104 105

#
106
# If more args, they are node names.
107
#
108 109 110 111 112 113 114 115 116 117 118 119
if (@ARGV) {
    # Untaint the nodes.
    foreach my $node ( @ARGV ) {
	if ($node =~ /^([-\w]+)$/) {
	    $node = $1;
	}
	else {
	    die("*** Bad node name: $node.");
	}

	push(@nodes, $node);
    }
120 121 122 123 124 125
}

#
# Verify actual user and get his DB uid.
#
if (! UNIX2DBUID($UID, \$dbuid)) {
126 127
    die("*** $0:\n".
	"    You do not exist in the Emulab Database.\n");
128 129 130
}

if (! UserDBInfo($dbuid, \$user_name, \$user_email)) {
131 132
    die("*** $0:\n".
        "    Cannot determine your name and email address.\n");
133 134 135
}

#
136
# Verify that this person is allowed to do this.
137
#
138 139 140 141
if (!TBAdmin() &&
    ! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_UPDATE)) {
    die("*** $0:\n".
	"    You not have permission to update nodes in $pid/$eid!\n");
142 143
}

144
#
145
# Check state. Only ACTIVE experiments. 
146
#
147 148 149
$estate = ExpState($pid, $eid);
if ($estate ne EXPTSTATE_ACTIVE) {
    print STDERR "Experiment $pid/$eid is not ACTIVE!\n";
150 151
    # For web page.
    exit(1);
152 153
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
#
# Get the list of nodes that need to be "updated." We already have the
# exerpiment locked, so if we have a problem, must unlock it.
#
if (! scalar(@nodes)) {
    @nodes = ExpNodes($pid, $eid);
}
else {
    #
    # Must verify that user supplied node names are really in the
    # experiment.
    #
    my @allnodes = ExpNodes($pid, $eid);

    foreach my $n1 (@nodes) {
	if (! grep {$_ eq $n1} @allnodes) {
	    die("*** $0:\n".
		"    Node $n1 is not allocated to $pid/$eid!\n");
	}
    }
}
if (! scalar(@nodes)) {
    print STDERR "There are no nodes allocated to experiment $pid/$eid\n";
    # For web page.
    exit(1);
}

181 182 183 184
#
# Batchmode (as from the web interface) goes to background and reports
# later via email.
# 
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
if ($batchmode) {
    #
    # Create a temporary name for a log file.
    #
    $logname = `mktemp /tmp/node_update-$pid-$eid.XXXXXX`;
    chop($logname);
    
    if (TBBackGround($logname)) {
	#
	# Parent exits normally
	#
	print STDOUT
	    "Node Update for $pid/$eid is now in progress.\n".
	    "You will be notified via email when the is complete.\n";
	exit(0);
    }
}

#
204
# Currently, we just need to update the mount points.
205 206 207 208 209 210 211
#
if (system("$expsetup")) {
    fatal("Exports Setup Failed");
}
# Give ops a chance to react.
sleep(2);

212
#
213 214
# Mark the nodes for auto update. Nodes may not respect this field
# (old local images), but its harmless. 
215 216
#
foreach my $node ( @nodes ) {
217 218 219 220
    if (! DBQueryWarn("update nodes set ".
		      "update_accounts=GREATEST(update_accounts,1) ".
		      "where node_id='$node'")) {
	fatal("DB Error!");
221 222 223
    }
}

224 225 226
print STDOUT "Waiting a while for nodes to auto update ...\n";
for (my $i = 0; $i < 10; $i++) {
    sleep(30);
227

228 229
    my $firstnode = pop(@nodes);
    my $querystr  = "node_id='$firstnode' ";
230

231 232
    while (@nodes) {
	my $node = pop(@nodes);
233

234
	$querystr .= "or node_id='$node' ";
235
    }
236 237 238 239
	
    my $query_result =
	DBQueryFatal("select node_id,update_accounts from nodes ".
		     "where ($querystr)");
240

241 242 243
    while (my ($node,$notdone) = $query_result->fetchrow_array) {
	if ($notdone) {
	    push(@nodes, $node);
244
	}
245 246
	else {
	    print STDOUT "$node updated.\n";
247
	}
248
    }
249 250 251
    last
	if (! @nodes);
    print STDOUT "Still waiting for nodes to auto update ...\n";
252
}
253
foreach my $node ( @nodes ) {
254 255 256
    print STDOUT "Node update failed on $node.\n";
    $failed++;
}
257

Robert Ricci's avatar
Robert Ricci committed
258
TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING());
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
NotifyUser("Node Update Complete", $failed);
if (defined($logname)) {
    unlink($logname);
}
exit($failed);

sub NotifyUser($$)
{
    my($mesg, $iserr) = @_;
    my($subject, $from, $to, $hdrs);

    print STDOUT "$mesg\n";

    if (! $batchmode) {
	return;
    }

    if ($iserr) {
277
	$subject = "Node Update Failed $pid/$eid";
278 279
    }
    else {
280
	$subject = "Node Update Success $pid/$eid";
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
    }
    $from  = $TBOPS;
    $hdrs  = "Reply-To: $TBOPS";
    
    #
    # Message goes to user. If a failure, TBOPS also gets it, otherwise
    # it goes into the logs.
    #
    $to    = "$user_name <$user_email>";    
    
    if ($iserr) {
	$hdrs = "Cc: $TBOPS\n".
	        "$hdrs";
    }
    else {
	$hdrs = "Bcc: $TBLOGS\n".
	        "$hdrs";
    }

300 301 302 303
    #
    # Send a message to the testbed list. Append the logfile.
    #
    SENDMAIL($to, $subject, $mesg, $from, $hdrs, ($logname));
304 305 306 307 308
}

sub fatal($) {
    my($mesg) = @_;

309
    TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING());
310 311 312 313 314 315 316
    NotifyUser($mesg, 1);
    if (defined($logname)) {
	unlink($logname);
    }
    exit(1);
}