os_load.in 5.11 KB
Newer Older
1 2
#!/usr/bin/perl -wT
use English;
3
use Getopt::Std;
4 5 6
   
#
# XXX Paper and plastic IP addresses wired into the kernel choice.
7 8
#     Path to netdisk is wired in (should come from os_info table).
#     wd0 wired in. Should come from node_types table in DB
9 10 11
# 

#
12 13 14
# Load an image onto a disk. The image must be in the DB images table,
# which defines how/where to load, and what partitions are affected.
# The nodes and partitions tables are updated appropriately. 
15
# 
16
# usage: os_load [-s] <imageid> <node> [node ...]
17
#
18 19 20 21 22 23 24
sub usage()
{
    print STDOUT "Usage: os_load [-s] <imageid> <node> [node ...]\n".
	"Use the -s to setup reload only, but do not issue a reboot\n";
    exit(-1);
}
my  $optlist = "s";
25 26 27 28 29

#
# Configure variables
#
my $TB		= "@prefix@";
30 31 32 33 34 35

#
# Load the Testbed support stuff. 
#
push(@INC, "$TB/lib");
require libdb;
36

37
my $NETDISK     = "/tftpboot/netdisk";
38 39
my $PAPERADDR	= "boss.emulab.net";
my $PLASTICADDR	= "users.emulab.net";
40
my $nodereboot	= "$TB/bin/node_reboot";
41
my $dbg		= 0;
42
my @row;
43
my %imageid_row = ();
44
my @nodes       = ();
45
my $mereuser    = 0;
46
my $setuponly   = 0;
47
my $failures    = 0;
48 49 50 51 52 53 54 55

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

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

#
56 57 58 59 60 61 62
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
63
if (@ARGV < 2) {
64 65 66 67 68 69
    usage();
}
if (defined($options{"s"})) {
    $setuponly = 1;
}
my $imageid = shift;
70

71 72 73 74 75 76 77 78
#
# Untaint args.
#
if ($imageid =~ /^([-\@\w.\+]+)$/) {
    $imageid = $1;
}
else {
    die("Bad data in $imageid.");
79
}
80 81 82 83 84 85 86 87 88 89 90

foreach my $node ( @ARGV ) {
    if ($node =~ /^([-\@\w]+)$/) {
	$node = $1;
    }
    else {
	die("Bad node name: $node.");
    }
    
    push(@nodes, $node);
}
91 92 93 94 95 96

#
# Figure out who called us. Root and admin types can do whatever they
# want. Normal users can only change nodes in experiments in their
# own projects.
#
97 98
if ($UID && !TBAdmin($UID)) {
    $mereuser = 1;
99 100
}

101 102 103 104 105 106
#
# Grab the imageid description from the DB. The permission check for this
# is that mere user can load an image from any project he/she is a member of,
# or any image that has a null pid field, since those are defined to be
# open to anyone. 
#
107
$db_result = DBQueryFatal("select * from images where imageid='$imageid'");
108 109 110 111 112 113 114 115 116 117 118
    
if ($db_result->numrows < 1) {
    die("No such imageid $imageid!");
}
%imageid_row = $db_result->fetchhash();

my $imagepid = 0;
if (defined($imageid_row{'pid'})) {
    $imagepid = $imageid_row{'pid'};
}
    
119 120
if ($mereuser && $imagepid && !ProjMember($imagepid)) {
    die("You do not have permission to load imageid $imageid!");
121 122
}

123 124 125 126 127
#
# Check to make sure that mere user is allowed to muck with nodes
#
if ($mereuser) {
    foreach my $node (@nodes) {
128
	if (! NodeAccessCheck(\$node)) {
129
	    die("You do not have permission to load images on $node\n");
130 131 132 133
	}
    }
}

134 135 136 137
my $loadpart  = $imageid_row{'loadpart'};
my $loadlen   = $imageid_row{'loadlength'};
my $imagepath = $imageid_row{'path'};
my $defosid   = $imageid_row{'default_osid'};
138 139

#
140
# 0 means load the entire disk.
141 142
#
my $diskpart = "";
143 144
if ($loadpart) {
    $diskpart = "wd0:s${loadpart}";
145 146 147 148 149 150
}
else {
    $diskpart = "wd0";
}

#
151 152
# For now, all testbed default images from from paper and all pid specific
# images come from plastic:/proj.
153 154
# 
my $cmdline = "";
155
if ($imagepid) {
156 157 158 159
    if (! ($imagepath =~ /^\/proj\//)) {
	die("Your image must reside in /proj\n");
    }
    $cmdline = "${PLASTICADDR}:$imagepath $diskpart";    
160 161
}
else {
162
    $cmdline = "${PAPERADDR}:$imagepath $diskpart";
163 164 165
}

#
166
# Loop for each node.
167 168 169 170
# 
foreach my $node (@nodes) {
    my $pc = $node;
	
171
    print STDOUT "Changing default OS for $pc to $defosid\n";
172 173 174
    DBQueryFatal("update nodes set ".
		 "def_boot_osid='$defosid',def_boot_path='' ".
		 "where node_id='$pc'");
175 176

    #
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    # Assign partition table entries for each partition in the image.
    # This is complicated by the fact that an image that covers only
    # part of the slices, should only change the partition table entries
    # for the subset of slices that are written to disk. In reality, this
    # is silly since there is no telling what the disk is going to end
    # up looking like after a partial image is written, especially if its
    # a user generated image. Not sure how to handle this yet. For now
    # lets just say that a user defined images essentially wipe the disk
    # except for the stuff they write. 
    #
    for ($i = 1; $i <= 4; $i++) {
	my $partname = "part${i}_osid";
	
	if (defined($imageid_row{$partname})) {
	    my $osid = $imageid_row{$partname};

193 194 195
	    DBQueryFatal("replace into partitions ".
			 "(partition, osid, node_id) ".
			 "values('$i', '$osid', '$pc')");
196 197
	}
	else {
198 199
	    DBQueryFatal("delete from partitions ".
			 "where node_id='$pc' and partition='$i'");
200 201
	}
    }
202
    
203
    print STDOUT "Setting up reload for $pc\n";
204 205 206 207
    DBQueryFatal("update nodes set ".
		 "next_boot_path='$NETDISK',".
		 "next_boot_cmd_line='$cmdline' ".
		 "where node_id='$pc'");
208
}
209

210 211 212 213 214 215
#
# Fire off a reboot.
#
if (! $setuponly) {
    system("$nodereboot @nodes");
    $failures = $? >> 8;
216 217 218
}

print STDOUT "OS Reload Done!\n";
219
exit $failures;
220