nfree.in 8.52 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!/usr/bin/perl

#
# nfree - Takes pysical node names, and frees them from the experiment they
# are allocated to. If nodes are ommited, frees up all nodes in the given
# experiment. Looks in the scheduled_reloads and next_reserve tables to see
# if this node should be re-reserved into another experiment and/or reloaded,
# rather than being put back into the pool of free nodes
#
Mac Newbold's avatar
Mac Newbold committed
10

11
12
13
#
# Configure variables
#
14
my $TB       = "@prefix@";
15
16
use lib '@prefix@/lib';
use libdb;
17

18
19
20
21
if (@ARGV < 2) {
    die("Usage: nfree <pid> <eid> [<node> <node> <...>]\n".
	"Releases all nodes in the specified experiment. If nodes are listed,\n".
	"nfree releases only the listed nodes.\n");
22
}
Mac Newbold's avatar
Mac Newbold committed
23

24
my $error = 0;
mac's avatar
mac committed
25
my $consetup="$TB/libexec/console_setup";
26
my $exportsetup="$TB/sbin/exports_setup";
27
my $os_load = "$TB/bin/os_load -r ";
28
my $reloadpid="emulab-ops";
29
30
my $reload_pendingeid="reloadpending";
my $reloadeid="reloading";
31

32
my @node_names=();
Mac Newbold's avatar
Mac Newbold committed
33
my @freed_nodes=();
Mac Newbold's avatar
Mac Newbold committed
34

35
36
37
38
my $pid = shift;
my $eid = shift;

use strict;
39
40
41
42
43
44
use English;

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

# Make sure that the experiment actually exists
# NOTE: project permissions checking is done later, on an individual
# node basis.
if (!ExpState($pid,$eid)) {
    die("There is no experiment '$eid' in project '$pid'.\n");
51
52
}

53
54
55
56
57
58
59
#
# Make sure the user has the ability to modify this experiment
#
if (!TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_MODIFY)) {
    die("You don't have sufficient access to modify '$eid' in project '$pid'.\n");
}

60
61
62
63
64
65
66
67
######################################################################
# Step 1 - Free nodes
#
# Find nodes that can be freed at this time, and do so. Nodes which
# are awaiting reloads and which have been scheduled to be reserved
# to another experiment, are put into lists so that they can be
# handled later
######################################################################
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# Make a list of nodes given on the command line
foreach my $n (@ARGV) {
    # Shark hack
    if ($n =~ /(sh\d+)/ ) {
	#
	# It's a shark - do the whole shelf if its not done already.
	#
	my $shelf = $1;
	if ( ! (join(",",@node_names) =~ /,$shelf-\d,/)) {
	    # Shelf hasn't been done yet...
	    foreach my $n ( 1 .. 8 ) {
		push(@node_names,"$shelf-$n");
	    }
	}
	# End shark hack
84

85
86
87
88
    } else {
	# its not a shark - just add it in...
	push(@node_names,"$n");
    }
89
90
}
 
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#
# Lock all of the tables we'll be reading, so that we get a consistent
# view of the current state
#
DBQueryFatal("lock tables nodes write, node_types read, " .
	     "scheduled_reloads read, interfaces write, delays write, " .
	     "reserved write, last_reservation write, current_reloads write, " .
	     "next_reserve read");

#
# If no nodes were given on the command line, fill the list with all nodes
# in the experiment.
#
if (@node_names == 0) {
    print "Releasing all nodes from experiment '$eid' in project '$pid'...\n";
    push @node_names,ExpNodes($pid,$eid);
107
108
}

109
my @reloads = ();
110
my %reserves= ();
Mac Newbold's avatar
Mac Newbold committed
111
foreach my $n (@node_names) { 
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
    #
    # Check to make sure they have acutally reserved the nodes.
    #
    my $result = DBQueryFatal("select * from reserved where node_id='$n' ".
	"and eid='$eid' and pid='$pid'");
    if ($result->numrows == 0) {
	print "Node '$n' is not reserved by your experiment.\n";
	$error++;
	next;
    } else {
	push(@freed_nodes,$n);
    }

    #
    # This little sillyness is for disk reloading. Kill the last reservation.
    #
    DBQueryWarn("delete from last_reservation where node_id='$n'") || $error++;

    #
    # If the node has a reloads entry, change the reservation so that the
    # reload_daemon will pick it up.
    #
    $result = DBQueryFatal("select node_id,image_id from scheduled_reloads " .
			  "where node_id='$n'");
136
    if ( $result->numrows() > 0 ) {
137
138

	my @row = $result->fetchrow();
139
140
	my $image_id = $row[1];
	my $reload_type = $row[2];
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

	print "Adding scheduled reload for $n to the list.\n";
	push(@reloads,$n);

    } else {
	#
	# If the node has a next_reserve entry, change the reservation
	#
	my $result = DBQueryFatal("select node_id,pid,eid from next_reserve ".
				  "where node_id='$n'");

	if ( $result->num_rows() > 0 ) {
	    # 
	    # Add the reservation to a list to be taken care of later
	    #
	    my ($node, $next_pid, $next_eid) = $result->fetchrow_array();
	    $reserves{$node} = "$next_pid:$next_eid";
158
	} else {
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
	    #
	    # No reloads or reservation changes, so really free the node
	    #
	    # This little sillyness is for disk reloading. Remember the last
	    # project a node was reserved into.
	    #
	    DBQuery("insert into last_reservation values ('$n', '$pid')");

	    print "Releasing node '$n'...";
	    if (DBQueryWarn("delete from reserved " .
			    "where node_id='$n' and eid='$eid'")) {
		"Succeeded.\n";
	    } else {
		$error++;
	    }
174
	}
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
    }

    #
    # Find the control net interface for this node type, as well as some
    # of the default values for its node type
    #
    $result = DBQueryFatal("select control_net,osid,node_types.pxe_boot_path " .
	"from node_types " .
	"left join nodes on nodes.type=node_types.type " .
	"where node_id='$n'");
    my ($control, $osid, $pxe_boot_path) = $result->fetchrow_array();
    if (! ($n =~ /sh\d+/)) { # If its not a shark
	# Clean out all IPs except the control net
	DBQueryWarn("update interfaces set IP='' " .
	    "where node_id='$n' and card!='$control'") || $error++;
190
    } else {
191
192
193
194
195
	# Shark hack
	# it is a shark, so clear out the alias(es)
	DBQueryWarn("update interfaces set IPalias='' ".
		    "where node_id='$n'") || $error++;
	# End shark hack
196
    }
197

198
199
200
201
202
203
204
205
206
207
208
209
    #
    # Clean out all delays
    #
    DBQueryWarn("delete from delays where node_id='$n'") || $error++;

    #
    # And clean out various tidbits from the nodes table.
    #
    DBQueryWarn("update nodes set def_boot_osid='$osid', def_boot_cmd_line='',".
	"def_boot_path='',startupcmd='',rpms='',deltas='',tarballs='',".
	"pxe_boot_path='$pxe_boot_path', next_pxe_boot_path='' ".
	"where node_id='$n'") || $error++;
210

211
212
213
214
    #
    # Clean out the current_reloads table
    #
    DBQueryWarn("delete from current_reloads where node_id='$n'") || $error++;
215
216
}

217
DBQueryFatal("unlock tables");
mac's avatar
mac committed
218

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
######################################################################
# Step 2 - Set up reserves and reloads
#
# If any nodes were awaiting reloads, put them in the proper
# experiment, and issue an os_load, if appropriate. If any nodes were
# set up for simple reservations, we take care of that in this stage
# too.
######################################################################
 
if ((@reloads > 0) || (keys %reserves > 0)) {
    #
    # Lock tables to maintain consistency
    #
    print "Locking tables.\n";
    DBQueryFatal("lock tables nodes read, node_types read, scheduled_reloads read, ".
		 "interfaces write, reserved write, next_reserve write");
235
  
236
237
    #
    # Take care of reloads by putting them into a special experiment,
238
    # which is processed by the reload_daemon
239
240
241
242
243
244
245
246
247
248
    #
    foreach my $n ( @reloads ) {
	#
	# Change reservation (don't delete or we'll get races)
	#
	print "Changing reservation for $n to $reloadpid/$reload_pendingeid...\n";
	DBQueryWarn("update reserved set ".
	    "pid='$reloadpid',eid='$reload_pendingeid',vname=NULL ".
	    "where node_id='$n'") || $error++;
    }
249
  
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
    #
    # Handle scheduled reservations
    #
    foreach my $n ( keys %reserves ) {
	my ($next_pid,$next_eid) = split (":",$reserves{$n});
	#
	# Change reservation (don't delete or we'll get races)
	#
	print "Changing reservation for $n to $next_pid/$next_eid...\n";
	DBQueryWarn("update reserved set pid='$next_pid',eid='$next_eid'," .
		    "vname=NULL where node_id='$n'") || $error++;
	DBQueryWarn("delete from next_reserve where node_id='$n'") || $error++;
    }

    #
    # Done, so we can now unlock tables
    #
    print "Unlocking tables.\n";
    DBQueryFatal("unlock tables");
269

270
271
}

272
273
274
275
276
277
278
######################################################################
# Step 3 - Set up console for freed nodes.
#
# Using a list of freed nodes build eariler, run consetup to reset
# their serial consoles.
######################################################################

279
if (@freed_nodes) {
280
281
    my @conlist=();
    my @sharks=();
282
283
284

    foreach my $n ( @freed_nodes ) {
	# Shark hack
285
286
287
288
289
290
291
292
	if ($n =~ /(sh\d+)/) {
	    # Its a shark - do the shelf if it hasn't been done yet
	    my $shelf = $1;
	    if (!(join(",",@sharks) =~ /\b$shelf\b/)) {
		push(@sharks,$shelf);
		push(@conlist,$shelf);
	    }
	}
293
	# End shark hack
294
295
296
	else {
	    push(@conlist,$n);
	}
297
    }
298
299
300
301

    #
    # Finally, run the actual command
    #
302
    system("$consetup @conlist") == 0 or
303
    print STDERR "WARNING: $consetup @conlist failed!\n";
Mac Newbold's avatar
Mac Newbold committed
304
305
}

306
exit($error);
307