Commit e7871305 authored by Kevin Atkinson's avatar Kevin Atkinson

Implement frontend and middleend support for loading multiple images

at once with Frisbee (excludes the actual MFS changes).

Os_load now takes take a list of comma serrated image names for the
"-i" and "-m" options.  The default OS is the OS for the last image
specified in the list.  I also changed the "-p" option of osload to
search both the project specified and emulab-ops for the image rather
than just the project specified in order to simplify specifying
multiple images (and because I personally found that behavior annoying
when using osload).

I modified the current_reloads table to be able to specify more than one
image for a node by adding an "idx" column which controls the order of
the reloads.  I also added a "prepare" column to the table (explained
below)

I modified tmcd to basically loop over the entries in the table and
create a multiline LOADINFO responsive, and modified rc.frisbee to
handle the multiline response and load each image in turn.

I modified os_load to take a new option "-P" which will tell rc.frisbee
to zap the superblocks even if a whole disk image is not specified.
To do this I set the prepare entry for the first image in the
current_reloads table to true.  Tmcd than passes this into to
rc.frisbee in the LOADINFO line.  When rc.frisbee sees this it will
make sure to zap the superblock before loading that image.

To support having multiple images as the default, "default_imageid"
can now be a comma separated list.  I implemented a hack to be able to
set multiple imageids via editnodetype.php3.  Basically the form
splits default_imageid into default_imageid_0, default_imageid_1, etc
and than adds an empty default_imageid_# slot to allow adding an
imageid.  Multiple images can be added by adding one image, than
submitting the form, and than adding another into the empty slot.  Not
the best, but I don't thing this will be a very common operation.
When the form is submitted it will than combine all default_imageid_#
into a comma separated list ignoring any that are deleted or set to
"No ImageID" (ie 0).

Everything will work fine with old MFSs as long as only one image is
loaded.  If multiple images are loaded with an old MFS, an email will
be sent to testbed-ops.  This works by having tmcd detect old MFS's by
using the version number and setting the state to RELOADOLDMFS.  Stated
will pick up on the and send the email to testbed-ops via a trigger.
parent e1a25db1
......@@ -400,7 +400,11 @@ foreach my $argkey (keys(%editnodetype_args)) {
$attr_name = $attr_type = $argkey;
$attr_name =~ s/^attr_${wordpat}_(.*)$/$1/;
if ($argkey =~ /_(osid|imageid)$/) {
if ($argkey =~ /_default_imageid$/) {
# hack, now a comma seperated list, assume valid
$attr_type = "imageids";
}
elsif ($argkey =~ /_(osid|imageid)$/) {
# Special case: the type is the LAST part of the name for ID attrs.
$attr_type = $1;
}
......@@ -457,6 +461,13 @@ foreach $attr_name (@attr_names) {
if (!exists($imageids->{$attr_value}));
$attr_types{$attr_name} = "integer"; # An idx is an integer attr.
}
elsif ($attr_type eq "imageids") {
foreach my $k (split /,/, $attr_value) {
UserError("NodeType Image ID Attr: $attr_name contains an invalid imageid.")
if (!exists($imageids->{$k}));
}
$attr_types{$attr_name} = "string"; # really a comma seperated list
}
}
exit(0)
......
......@@ -842,6 +842,18 @@ sub stateTransition($$) {
handleCommand($node,$TBPOWERCYCLE);
next;
};
/^RELOADOLDMFS$/ && do {
my $frisbee_osid = TBNodeDiskloadOSID($node);
my $frisbee_name = DBQuerySingleFatal("select osname from os_info where osid=$frisbee_osid");
my $msg =
("Attempted to load multiple images on $node using an old Frisbee MFS.\n".
"To make this work please update the $frisbee_name MFS image.\n");
SENDMAIL($REALTBOPS,
"$frisbee_name Needs Updating",
$msg,
"Stated Daemon <".$TBOPS.">");
next;
};
notify("Unknown trigger '$trig' for $node in $mode/$newstate!\n");
}
# Clear any of the node triggers that we ran.
......
This diff is collapsed.
......@@ -17,9 +17,11 @@ sub usage()
print("Usage: os_load [-s] [[-p <pid>] -i <imageid>] <node> [node ...]\n".
" os_load [-s] [[-p <pid>] -i <imageid>] -e pid,eid\n".
" os_load -l\n".
"Use -i to specify an image ID. Use node default otherwise.\n".
"Use -i to specify a comma seperated list of image IDs.\n".
" Use the node default otherwise.\n".
"Use -p to specify the project ID in which to find the imageid.\n".
"Use -m to specify the internal name if an image ID.\n".
" If the image is not found in <pid> also try ".TB_OPSPID().".\n".
"Use -m to specify the internal name(s) if an image ID.\n".
"Use -s to start reload, but do not wait for it to complete.\n".
"Use -w to wait for the nodes to finish booting.\n".
"Use -r to supress rebooting nodes - you need to to it yourself\n".
......@@ -28,21 +30,23 @@ sub usage()
"Use -z <style> to zero all unallocated blocks on the disk\n".
" style==0: do not zero (same as not using -z)\n".
" style==1: let frisbee do the zeroing\n".
" style==2: zero disk before running frisbee\n");
" style==2: zero disk before running frisbee\n".
"Use -P to prepare the disk as if a whole disk image was loaded\n");
exit(-1);
}
my $optlist = "swldi:e:p:m:rz:";
my $optlist = "swldi:e:p:m:rz:P";
my $waitmode = 1;
my $listonly = 0;
my $debug = 0;
my $noreboot = 0;
my $zerofree = 0;
my $prepare = 0;
my @nodes = ();
my $imagepid;
my $imagename;
my $imageid;
my $pid;
my $eid;
my @imageids = ();
my $imagenames;
# Configure variables
my $TB = "@prefix@";
......@@ -91,6 +95,8 @@ $noreboot = 1
if (defined($options{"r"}));
$zerofree = $options{"z"}
if (defined($options{"z"}));
$prepare = 1
if (defined($options{"P"}));
#
# Figure out which nodes. Choice of nodes on command line, or all nodes in an
......@@ -150,15 +156,6 @@ else {
if (defined($options{"i"})) {
usage()
if (defined($options{"m"}));
$imagename = $options{"i"};
if ($imagename =~ /^([-\w\.\+]+)$/) {
$imagename = $1;
}
else {
die("*** Bad data in imagename: $imagename.\n");
}
if (defined($options{"p"})) {
$imagepid = $options{"p"};
......@@ -170,21 +167,29 @@ if (defined($options{"i"})) {
die("*** Bad data in imagepid: $imagepid.\n");
}
}
$imagenames = $options{"i"};
#
# If -p option given, use that.
# If in experiment mode, then use the pid of the experiment, unless
# a -p option was given.
# Otherwise look in the system project.
#
if (defined($imagepid)) {
if (! ($imageid = TBImageID($imagepid, $imagename))) {
die("*** $0:\n".
" No such image $imagename in project $imagepid!\n");
foreach my $imagename (split /,/, $imagenames) {
if ($imagename =~ /^([-\w\.\+]+)$/) {
$imagename = $1;
}
}
else {
if (defined($pid)) {
else {
die("*** Bad data in imagename: $imagename.\n");
}
#
# If -p option given, use that.
# If in experiment mode, then use the pid of the experiment, unless
# a -p option was given.
# Otherwise look in the system project.
#
my $imageid;
if (defined($imagepid)) {
$imageid = TBImageID($imagepid, $imagename);
}
if (! $imageid && defined($pid)) {
$imageid = TBImageID($pid, $imagename);
}
if (! $imageid) {
......@@ -192,8 +197,9 @@ if (defined($options{"i"})) {
}
if (! $imageid) {
die("*** $0:\n".
" No such image $imagename!\n");
" No such image $imagename!\n");
}
push @imageids, $imageid;
}
}
......@@ -204,13 +210,16 @@ if (defined($options{"m"})) {
usage()
if (defined($options{"i"}));
$imageid = $options{"m"};
if ($imageid =~ /^([-\w\.\+]+)$/) {
$imageid = $1;
}
else {
die("*** Bad data in imageid: $imageid\n");
foreach my $imageid (split /,/, $options{"i"}) {
if ($imageid =~ /^([-\w\.\+]+)$/) {
$imageid = $1;
}
else {
die("*** Bad data in imageid: $imageid\n");
}
push @imageids, $imageid;
}
}
......@@ -229,7 +238,7 @@ foreach my $node ( @nodes ) {
# is an image ID, assume they have made this mistake and stop.
#
my $_pid = defined($imagepid) ? $imagepid : TB_OPSPID();
if ($first && !defined($imagename) && TBImageID($_pid, $nodeid)) {
if ($first && !defined($imagenames) && TBImageID($_pid, $nodeid)) {
print "*** reload: forgot the -i before image name $nodeid?\n";
exit(1);
}
......@@ -259,10 +268,11 @@ $osloadargs{'debug'} = $debug;
$osloadargs{'waitmode'} = $waitmode;
$osloadargs{'noreboot'} = $noreboot;
$osloadargs{'zerofree'} = $zerofree;
$osloadargs{'prepare'} = $prepare;
$osloadargs{'nodelist'} = [ map { $_->node_id() } @nodes ];
# No imageid means to load the default image.
$osloadargs{'imageid'} = $imageid
if (defined($imageid));
$osloadargs{'imageids'} = [ @imageids ]
if (@imageids);
$osloadargs{'swapinfo'} = 1;
exit(osload(\%osloadargs, \%failednodes));
......
......@@ -26,4 +26,4 @@
* NB: See ron/libsetup.pm. That is version 4! I'll merge that in.
*/
#define DEFAULT_VERSION 2
#define CURRENT_VERSION 29
#define CURRENT_VERSION 30
......@@ -3729,24 +3729,25 @@ COMMAND_PROTOTYPE(dorouting)
*/
COMMAND_PROTOTYPE(doloadinfo)
{
MYSQL_RES *res;
MYSQL_ROW row;
MYSQL_RES *res, *res2;
MYSQL_ROW row, row2;
char buf[MYBUFSIZE];
char *bufp = buf, *ebufp = &buf[sizeof(buf)];
char *disktype, *useacpi, *useasf, address[MYBUFSIZE];
char mbrvers[51];
int disknum, zfill;
char *loadpart, *OS, *prepare;
int disknum, nrows, zfill;
/*
* Get the address the node should contact to load its image
*/
res = mydb_query("select load_address,loadpart,OS,frisbee_pid,"
" mustwipe,mbr_version,access_key,imageid"
" mustwipe,mbr_version,access_key,imageid,prepare"
" from current_reloads as r "
"left join images as i on i.imageid = r.image_id "
"left join os_info as o on i.default_osid = o.osid "
"where node_id='%s'",
8, reqp->nodeid);
"where node_id='%s' order by r.idx",
9, reqp->nodeid);
if (!res) {
error("doloadinfo: %s: DB Error getting loading address!\n",
......@@ -3754,111 +3755,156 @@ COMMAND_PROTOTYPE(doloadinfo)
return 1;
}
if ((int)mysql_num_rows(res) == 0) {
if ((nrows = (int)mysql_num_rows(res)) == 0) {
mysql_free_result(res);
return 0;
}
row = mysql_fetch_row(res);
/*
* Remote nodes get a URL for the address.
*/
if (!reqp->islocal) {
if (!row[6] || !row[6][0]) {
error("doloadinfo: %s: "
"No access key associated with imageid %s\n",
reqp->nodeid, row[7]);
mysql_free_result(res);
if (nrows > 1 && vers <= 30) {
bufp += OUTPUT(bufp, ebufp - bufp,
"ADDR=/NEWER-MFS-NEEDED PART=0 PARTOS=Bogus\n");
error("doloadinfo: %s: Old MFS Version found, need version 30\n",
reqp->nodeid);
#ifdef EVENTSYS
address_tuple_t tuple;
/*
* Send the state out via an event
*/
/* XXX: Maybe we don't need to alloc a new tuple every time through */
tuple = address_tuple_alloc();
if (tuple == NULL) {
error("doreset: Unable to allocate address tuple!\n");
return 1;
}
OUTPUT(address, sizeof(address),
"%s/spewimage.php?imageid=%s&access_key=%s",
TBBASE, row[7], row[6]);
}
else {
tuple->host = BOSSNODE;
tuple->objtype = "TBNODESTATE";
tuple->objname = reqp->nodeid;
tuple->eventtype = "RELOADOLDMFS";
if (myevent_send(tuple)) {
error("doloadinfo: %s: Unable to set state to RELOADOLDMFS");
}
address_tuple_free(tuple);
#endif
}
else while (nrows) {
row = mysql_fetch_row(res);
loadpart = row[1];
OS = row[2];
prepare = row[8];
/*
* Simple text string.
* Remote nodes get a URL for the address.
*/
if (! row[0] || !row[0][0]) {
mysql_free_result(res);
return 0;
if (!reqp->islocal) {
if (!row[6] || !row[6][0]) {
error("doloadinfo: %s: "
"No access key associated with imageid %s\n",
reqp->nodeid, row[7]);
mysql_free_result(res);
return 1;
}
OUTPUT(address, sizeof(address),
"%s/spewimage.php?imageid=%s&access_key=%s",
TBBASE, row[7], row[6]);
}
strcpy(address, row[0]);
else {
/*
* Simple text string.
*/
if (! row[0] || !row[0][0]) {
mysql_free_result(res);
return 0;
}
strcpy(address, row[0]);
/*
* Sanity check
*/
if (!row[3] || !row[3][0]) {
error("doloadinfo: %s: "
"No pid associated with address %s\n",
reqp->nodeid, row[0]);
mysql_free_result(res);
return 1;
}
}
bufp += OUTPUT(bufp, ebufp - bufp,
"ADDR=%s PART=%s PARTOS=%s", address, loadpart, OS);
/*
* Sanity check
* Remember zero-fill free space, mbr version fields, and access_key
*/
if (!row[3] || !row[3][0]) {
error("doloadinfo: %s: "
"No pid associated with address %s\n",
reqp->nodeid, row[0]);
mysql_free_result(res);
zfill = 0;
if (row[4] && row[4][0])
zfill = atoi(row[4]);
strcpy(mbrvers,"1");
if (row[5] && row[5][0])
strcpy(mbrvers, row[5]);
/*
* Get disk type and number
*/
disktype = DISKTYPE;
disknum = DISKNUM;
useacpi = "unknown";
useasf = "unknown";
res2 = mydb_query("select attrkey,attrvalue from nodes as n "
"left join node_type_attributes as a on "
" n.type=a.type "
"where (a.attrkey='bootdisk_unit' or "
" a.attrkey='disktype' or "
" a.attrkey='use_acpi' or "
" a.attrkey='use_asf') and "
" n.node_id='%s'", 2, reqp->nodeid);
if (!res2) {
error("doloadinfo: %s: DB Error getting disktype!\n",
reqp->nodeid);
return 1;
}
}
if ((int)mysql_num_rows(res2) > 0) {
int nrows2 = (int)mysql_num_rows(res2);
while (nrows2) {
row2 = mysql_fetch_row(res2);
if (row2[1] && row2[1][0]) {
if (strcmp(row2[0], "bootdisk_unit") == 0) {
disknum = atoi(row2[1]);
}
else if (strcmp(row2[0], "disktype") == 0) {
disktype = row2[1];
}
else if (strcmp(row2[0], "use_acpi") == 0) {
useacpi = row2[1];
}
else if (strcmp(row2[0], "use_asf") == 0) {
useasf = row2[1];
}
}
nrows2--;
}
}
bufp += OUTPUT(bufp, ebufp - bufp,
"ADDR=%s PART=%s PARTOS=%s", address, row[1], row[2]);
mysql_free_result(res2);
/*
* Remember zero-fill free space, mbr version fields, and access_key
*/
zfill = 0;
if (row[4] && row[4][0])
zfill = atoi(row[4]);
strcpy(mbrvers,"1");
if (row[5] && row[5][0])
strcpy(mbrvers, row[5]);
/*
* Get disk type and number
*/
disktype = DISKTYPE;
disknum = DISKNUM;
useacpi = "unknown";
useasf = "unknown";
bufp += OUTPUT(bufp, ebufp - bufp,
" DISK=%s%d ZFILL=%d ACPI=%s MBRVERS=%s ASF=%s PREPARE=%s\n",
disktype, disknum, zfill, useacpi, mbrvers, useasf, prepare);
res = mydb_query("select attrkey,attrvalue from nodes as n "
"left join node_type_attributes as a on "
" n.type=a.type "
"where (a.attrkey='bootdisk_unit' or "
" a.attrkey='disktype' or "
" a.attrkey='use_acpi' or "
" a.attrkey='use_asf') and "
" n.node_id='%s'", 2, reqp->nodeid);
if (!res) {
error("doloadinfo: %s: DB Error getting disktype!\n",
reqp->nodeid);
return 1;
nrows--;
}
if ((int)mysql_num_rows(res) > 0) {
int nrows = (int)mysql_num_rows(res);
while (nrows) {
row = mysql_fetch_row(res);
if (row[1] && row[1][0]) {
if (strcmp(row[0], "bootdisk_unit") == 0) {
disknum = atoi(row[1]);
}
else if (strcmp(row[0], "disktype") == 0) {
disktype = row[1];
}
else if (strcmp(row[0], "use_acpi") == 0) {
useacpi = row[1];
}
else if (strcmp(row[0], "use_asf") == 0) {
useasf = row[1];
}
}
nrows--;
}
}
bufp += OUTPUT(bufp, ebufp - bufp,
" DISK=%s%d ZFILL=%d ACPI=%s MBRVERS=%s ASF=%s\n",
disktype, disknum, zfill, useacpi, mbrvers, useasf);
mysql_free_result(res);
client_writeback(sock, buf, strlen(buf), tcp);
......
......@@ -72,7 +72,7 @@ $initial_attributes = array(
"attrtype" => "integer"),
array("attrkey" => "default_imageid",
"attrvalue" => $default_image->imageid(),
"attrtype" => "integer"),
"attrtype" => "string"),
array("attrkey" => "default_osid", "attrvalue" => $rhl_std->osid(),
"attrtype" => "integer"),
array("attrkey" => "delay_capacity", "attrvalue" => "2",
......@@ -117,6 +117,19 @@ function SPITFORM($node_type, $formfields, $attributes, $deletes, $errors)
global $osid_result, $imageid_result, $mfsosid_result, $new_type;
global $newattribute_name, $newattribute_value, $newattribute_type;
#
# Split default_imageid
#
$default_imageids = preg_split('/,/', $attributes["default_imageid"]);
for ($i = 0; $i != count($default_imageids); $i++) {
$attributes["default_imageid_$i"] = $default_imageids[$i];
}
$last_default_imageid_label = "default_imageid_$i";
$attributes["default_imageid_$i"] = 0;
unset($attributes["default_imageid"]);
ksort($attributes);
#
# Standard Testbed Header
#
......@@ -308,8 +321,13 @@ function SPITFORM($node_type, $formfields, $attributes, $deletes, $errors)
WRITEOSIDMENU($key, "attributes[$key]", $mfsosid_result, $val,
"deletes[$key]", $deletes[$key]);
}
elseif ($key == "default_imageid") {
WRITEIMAGEIDMENU($key, "attributes[$key]", $imageid_result, $val,
elseif ($key == $last_default_imageid_label) {
WRITEIMAGEIDMENU("$key<sup><b>1</b></sup>", "attributes[$key]", $imageid_result, $val,
"deletes[$key]", $deletes[$key]);
}
elseif ($key == "default_imageid" ||
substr($key, 0, strlen("default_imageid_")) == "default_imageid_") {
WRITEIMAGEIDMENU("$key", "attributes[$key]", $imageid_result, $val,
"deletes[$key]", $deletes[$key]);
}
else {
......@@ -375,6 +393,13 @@ function SPITFORM($node_type, $formfields, $attributes, $deletes, $errors)
echo "</form>
</table>\n";
echo "<blockquote>
<ol type=1 start=1>
<li> To add more than one image, add the first image and
submit the form. Than add the next, etc.</li>
</ol>
</blockquote>";
}
if (isset($new_type)) {
......@@ -426,6 +451,7 @@ elseif (isset($node_type)) {
$attribute_types[$row['attrkey']] = $row['attrtype'];
$attribute_deletes[$row['attrkey']] = "";
}
$attribute_types["default_imageid"] = "string";
}
else {
PAGEARGERROR("Must provide one of node_type or new_type");
......@@ -471,6 +497,18 @@ if (!isset($new_type)) {
#
$errors = array();
#
# Combine imageids into a comma seperated list
#
$default_imagesids = array();
for ($i = 0; isset($attributes["default_imageid_$i"]); $i++) {
if (!(isset($deletes["default_imageid_$i"]) && $deletes["default_imageid_$i"] == "checked")
&& $attributes["default_imageid_$i"] != 0)
array_push($default_imagesids, $attributes["default_imageid_$i"]);
unset($attributes["default_imageid_$i"]);
}
$attributes["default_imageid"] = join(',', $default_imagesids);
# Check the attributes.
while (list ($key, $val) = each ($attributes)) {
# Skip checks if scheduled for deletion
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment