diff --git a/backend/GNUmakefile.in b/backend/GNUmakefile.in index e5c818c4d1a3e5a624f78671db89385b8466f599..38d744c1349126c82da103e1b873b43e7fb3d992 100644 --- a/backend/GNUmakefile.in +++ b/backend/GNUmakefile.in @@ -13,9 +13,9 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@ include $(OBJDIR)/Makeconf BIN_SCRIPTS = moduserinfo newgroup newmmlist editexp editimageid \ - editnodetype editsitevars + editnodetype editsitevars newimageid WEB_BIN_SCRIPTS = webmoduserinfo webnewgroup webnewmmlist webeditimageid \ - webeditnodetype webeditsitevars + webeditnodetype webeditsitevars webnewimageid WEB_SBIN_SCRIPTS= LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS) diff --git a/backend/newimageid.in b/backend/newimageid.in new file mode 100644 index 0000000000000000000000000000000000000000..6283e3bddf3aa95c50d341e04b716507378b3c24 --- /dev/null +++ b/backend/newimageid.in @@ -0,0 +1,580 @@ +#!/usr/bin/perl -wT +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2007 University of Utah and the Flux Group. +# All rights reserved. +# +use English; +use strict; +use Getopt::Std; +use XML::Simple; +use Data::Dumper; + +# +# Back-end script to create new Image descriptors. +# +sub usage() +{ + print("Usage: newimageid [-v] <xmlfile>\n"); + exit(-1); +} +my $optlist = "dv"; +my $debug = 0; +my $verify = 0; # Check data and return status only. + +# +# Configure variables +# +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAUDIT = "@TBAUDITEMAIL@"; +my $TBGROUP_DIR = "@GROUPSROOT_DIR@"; +my $TBPROJ_DIR = "@PROJROOT_DIR@"; + +# +# Untaint the path +# +$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Load the Testbed support stuff. +# +use lib "@prefix@/lib"; +use libdb; +use libtestbed; +use User; +use Project; +use Image; + +# Protos +sub fatal($); +sub UserError(;$); +sub escapeshellarg($); + +# +# Parse command arguments. Once we return from getopts, all that should be +# left are the required arguments. +# +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"d"})) { + $debug = 1; +} +if (defined($options{"v"})) { + $verify = 1; +} +if (@ARGV != 1) { + usage(); +} +my $xmlfile = shift(@ARGV); + +# +# Map invoking user to object. +# If invoked as "nobody" we are coming from the web interface and the +# current user context is "implied" (see tbauth.php3). +# +my $this_user; + +if (getpwuid($UID) ne "nobody") { + $this_user = User->ThisUser(); + + if (! defined($this_user)) { + fatal("You ($UID) do not exist!"); + } + # You don't need admin privileges to create new Image descriptors. +} +else { + # + # Check the filename when invoked from the web interface; must be a + # file in /tmp. + # + if ($xmlfile =~ /^([-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in pathname: $xmlfile"); + } + + # Use realpath to resolve any symlinks. + my $translated = `realpath $xmlfile`; + if ($translated =~ /^(\/tmp\/[-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in translated pathname: $xmlfile"); + } + + # The web interface (and in the future the xmlrpc interface) sets this. + $this_user = User->ImpliedUser(); + + if (! defined($this_user)) { + fatal("Cannot determine implied user!"); + } +} + +# +# These are the fields that we allow to come in from the XMLfile. +# +my $SLOT_OPTIONAL = 0x1; # The field is not required. +my $SLOT_REQUIRED = 0x2; # The field is required and must be non-null. +my $SLOT_ADMINONLY = 0x4; # Only admins can set this field. +# +# XXX We should encode all of this in the DB so that we can generate the +# forms on the fly, as well as this checking code. +# +my %xmlfields = + # XML Field Name DB slot name Flags Default + ("imagename" => ["imagename" , $SLOT_REQUIRED], + "pid" => ["pid", $SLOT_REQUIRED], + "gid" => ["gid", $SLOT_OPTIONAL], + "description" => ["description", $SLOT_REQUIRED], + "loadpart" => ["loadpart", $SLOT_REQUIRED], + "loadlength" => ["loadlength", $SLOT_REQUIRED], + "part1_osid" => ["part1_osid", $SLOT_OPTIONAL], + "part2_osid" => ["part2_osid", $SLOT_OPTIONAL], + "part3_osid" => ["part3_osid", $SLOT_OPTIONAL], + "part4_osid" => ["part4_osid", $SLOT_OPTIONAL], + "default_osid" => ["default_osid", $SLOT_REQUIRED], + "path" => ["path", $SLOT_OPTIONAL, ""], + "mtype_*" => ["mtype", $SLOT_OPTIONAL], + "node_id" => ["node_id", $SLOT_OPTIONAL, ""], + "shared", => ["shared", $SLOT_OPTIONAL, 0], + "global", => ["global", $SLOT_ADMINONLY, 0], + "makedefault", => ["makedefault", $SLOT_ADMINONLY, 0], +); + +# +# Must wrap the parser in eval since it exits on error. +# +my $xmlparse = eval { XMLin($xmlfile, + VarAttr => 'name', + ContentKey => '-content', + SuppressEmpty => undef); }; +fatal($@) + if ($@); + +# +# Process and dump the errors (formatted for the web interface). +# We should probably XML format the errors instead but not sure I want +# to go there yet. +# +my %errors = (); + +# +# Make sure all the required arguments were provided. +# +my $key; +foreach $key (keys(%xmlfields)) { + my (undef, $required, undef) = @{$xmlfields{$key}}; + + $errors{$key} = "Required value not provided" + if ($required & $SLOT_REQUIRED && + ! exists($xmlparse->{'attribute'}->{"$key"})); +} +UserError() + if (keys(%errors)); + +# +# We build up an array of arguments to pass to Image->Create() as we check +# the attributes. +# +my %newimageid_args = (); + +# +# Wildcard keys have one or more *'s in them like simple glob patterns. +# This allows multiple key instances for categories of attributes, and +# putting a "type signature" in the key for arg checking, as well. +# +# Wildcards are made into regex's by anchoring the ends and changing each * to +# a "word" (group of alphahumeric.) A tail * means "the rest", allowing +# multiple words separated by underscores or dashes. +# +my $wordpat = '[a-zA-Z0-9]+'; +my $tailpat = '[-\w]+'; +my %wildcards; +foreach $key (keys(%xmlfields)) { + if (index($key, "*") >= 0) { + my $regex = '^' . $key . '$'; + $regex =~ s/\*\$$/$tailpat/; + $regex =~ s/\*/$wordpat/g; + $wildcards{$key} = $regex; + } +} +# Key ordering is lost in a hash. +# Put longer matching wildcard keys before their prefix. +my @wildkeys = reverse(sort(keys(%wildcards))); + +foreach $key (keys(%{ $xmlparse->{'attribute'} })) { + my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'}; + + print STDERR "User attribute: '$key' -> '$value'\n" + if ($debug); + + my $field = $key; + my $wild; + if (!exists($xmlfields{$key})) { + + # Not a regular key; look for a wildcard regex match. + foreach my $wildkey (@wildkeys) { + my $regex = $wildcards{$wildkey}; + if ($wild = $key =~ /$regex/) { + $field = $wildkey; + print STDERR "Wildcard: '$key' matches '$wildkey'\n" + if ($debug); + last; # foreach $wildkey + } + } + if (!$wild) { + $errors{$key} = "Unknown attribute"; + next; # foreach $key + } + } + + my ($dbslot, $required, $default) = @{$xmlfields{$field}}; + + if ($required & $SLOT_REQUIRED) { + # A slot that must be provided, so do not allow a null value. + if (!defined($value)) { + $errors{$key} = "Must provide a non-null value"; + next; + } + } + if ($required & $SLOT_OPTIONAL) { + # Optional slot. If value is null skip it. Might not be the correct + # thing to do all the time? + if (!defined($value)) { + next + if (!defined($default)); + $value = $default; + } + } + if ($required & $SLOT_ADMINONLY) { + # Admin implies optional, but thats probably not correct approach. + $errors{$key} = "Administrators only" + if (! $this_user->IsAdmin()); + } + + # Now check that the value is legal. + if (! TBcheck_dbslot($value, "images", $dbslot, TBDB_CHECKDBSLOT_ERROR)) { + $errors{$key} = TBFieldErrorString(); + next; + } + + $newimageid_args{$key} = $value; +} +UserError() + if (keys(%errors)); + +# +# Need a list of node types. We join this over the nodes table so that +# we get a list of just the nodes that are currently in the testbed, not +# just in the node_types table. +# +my $types_result = + DBQueryFatal("select distinct n.type from nodes as n ". + "left join node_type_attributes as a on a.type=n.type ". + "where a.attrkey='imageable' and ". + " a.attrvalue!='0'"); +# Save the valid types in a new array for later. +my @mtypes_array; +while (my ($type) = $types_result->fetchrow_array()) { + push(@mtypes_array, $type); + $xmlfields{"mtype_$type"} = ["mtype", $SLOT_OPTIONAL]; +} +## printf "%s mtypes\n", $#mtypes_array + 1; +## foreach my $x (@mtypes_array) { printf "%s\n", $x; } +## print "\n"; + +# +# Now do special checks. +# + +my $isadmin = $this_user->IsAdmin(); +my $imagename = $newimageid_args{"imagename"}; + +my $project = Project->Lookup($newimageid_args{"pid"}); +if (!defined($project)) { + UserError("Project: No such project"); +} +if (!$project->AccessCheck($this_user, TB_PROJECT_MAKEIMAGEID())) { + UserError("Project: Not enough permission"); +} + +my $group; +if (exists($newimageid_args{"group"})) { + my $gid = $newimageid_args{"group"}; + $group = Group->LookupSubgroupByName($gid); + if (!defined($group)) { + UserError("Group: No such group $gid"); + } +} +else { + $group = $project->GetProjectGroup(); +} + +if ($newimageid_args{"loadpart"} != 0 && $newimageid_args{"loadlength"} != 1) { + UserError("#of Partitions: Only single slices or partial disks are allowed"); +} + +# +# Check sanity of the OSIDs for each slice. Permission checks not needed. +# Store the ones we care about and silently forget about the extraneous OSIDs. +# +my @osid_array; +for (my $i = 1; $i <= 4; $i++) { + my $foo = "part${i}_osid"; + + if ($newimageid_args{"loadpart"} ? + $i == $newimageid_args{"loadpart"} : + $i <= $newimageid_args{"loadlength"}) { + + if (!exists($newimageid_args{$foo})) { + UserError("Partition $i OS: Must select an OS"); + } + else { + my $thisosid = $newimageid_args{$foo}; + if ($thisosid eq "" || $thisosid eq "X") { + UserError("Partition $i OS: Must select an OS"); + } + elsif ($thisosid eq "none") { + # + # Allow admins to specify no OS for a partition. + # + UserError("Partition $i OS: Must select an OS") + if (!$isadmin); + delete($newimageid_args{$foo}); + } + elsif (!OSinfo->Lookup($thisosid)) { + UserError("Partition $i OS: No such OS defined"); + } + else { + push(@osid_array, $thisosid); + } + } + } + else { + delete($newimageid_args{$foo}); + } +} + +# +# Check the boot OS. Must be one of the OSes selected for a partition. +# +if (!exists($newimageid_args{"default_osid"}) || + $newimageid_args{"default_osid"} eq "" || + $newimageid_args{"default_osid"} eq "none") { + UserError("Boot OS: Not Selected"); +} +elsif (!OSinfo->Lookup($newimageid_args{"default_osid"})) { + UserError("Boot OS: No such OS defined"); +} +else { + UserError("Boot OS: Invalid; Must be one of the partitions") + if (!grep($_ eq $newimageid_args{"default_osid"}, @osid_array)); +} + +# +# Only admin types can set the global bit for an image. Ignore silently. +# +my $global = 0; +if ($isadmin && + exists($newimageid_args{"global"}) && + $newimageid_args{"global"} eq "1") { + $global = 1; +} + +my $shared = 0; +if (exists($newimageid_args{"shared"}) && + $newimageid_args{"shared"} eq "1") { + $shared = 1; +} +# Does not make sense to do this. +if ($global && $shared) { + UserError("Global: Image declared both shared and global"); +} + +# +# The path must not contain illegal chars and it must be more than +# the original /proj/$pid we gave the user. We allow admins to specify +# a path outside of /proj though. +# +if (!exists($newimageid_args{"path"}) || + $newimageid_args{"path"} eq "") { + UserError("Path: Missing Field"); +} +elsif (! $isadmin) { + my $pdef = ""; + + if (!$shared && + exists($newimageid_args{"gid"}) && + $newimageid_args{"gid"} ne "" && + $newimageid_args{"gid"} ne $newimageid_args{"pid"}) { + $pdef = "$TBGROUP_DIR/" . + $newimageid_args{"pid"} . "/" . $newimageid_args{"gid"} . "/"; + } + else { + $pdef = "$TBPROJ_DIR/" . $newimageid_args{"pid"} . "/images/"; + } + + if (index($newimageid_args{"path"}, $pdef) < 0) { + UserError("Path: Invalid Path"); + } +} + +# +# See what node types this image will work on. Must be at least one! +# +UserError("Node Types: Must have at least one node type") + if ($#mtypes_array < 0); +my $typeclause = join(" or ", map("type='$_'", @mtypes_array)); + +# Check validity of mtype_* args, since the keys are dynamically generated. +my $node_types_selected = 0; +my @mtype_keys = grep(/^mtype_/, keys(%newimageid_args)); +foreach my $key (@mtype_keys) { + my $value = $newimageid_args{$key}; + print STDERR "mtype: '$key' -> '$value'\n" + if ($debug); + + my $type = $key; + $type =~ s/^mtype_//; + my $match = grep(/^${type}$/, @mtypes_array); + if ($match == 0) { + $errors{$key} = "Illegal node type." + } + elsif ($value eq "1") { + $node_types_selected++; + } +} +UserError("Node Types: Must select at least one node type") + if ($node_types_selected == 0); + +# +# Check sanity of node name and that user can create an image from it. +# +my ($node, $node_id); +if (exists($newimageid_args{"node_id"}) && + $newimageid_args{"node_id"} ne "") { + + if (!($node = Node->Lookup($newimageid_args{"node_id"}))) { + UserError("Node: Invalid node name"); + } + elsif (!$node->AccessCheck($this_user, TB_NODEACCESS_LOADIMAGE())) { + UserError("Node: Not enough permission"); + } + else { + $node_id = $node->node_id(); + } +} + +# +# Mereusers are not allowed to create more than one osid/imageid mapping +# for each machinetype. They cannot actually do that through the EZ form +# since the osid/imageid has to be unique, but it can happen by mixed +# use of the long form and the short form, or with multiple uses of the +# long form. +# +my $osidclause; +foreach my $partn_osid (grep(/^part[1-4]_osid$/, keys(%newimageid_args))) { + $osidclause .= " or " + if (defined($osidclause)); + $osidclause .= "osid='$newimageid_args{$partn_osid}'"; +} + +DBQueryWarn("lock tables images write, os_info write, osidtoimageid write"); +my $query_result = + DBQueryWarn("select osidtoimageid.*,images.pid,images.imagename ". + " from osidtoimageid ". + "left join images on ". + " images.imageid=osidtoimageid.imageid ". + "where ($osidclause) and ($typeclause)"); +DBQueryWarn("unlock tables"); +if ($query_result->numrows) { + + my $msg = + "There are other image descriptors that specify the same OS". + "descriptors for the same node types. There must be a unique". + "mapping of OS descriptor to Image descriptor for each node type!". + "Perhaps you need to delete one of the images below, or create a". + "new OS descriptor to use in this new Image descriptor. \n\n"; + my $fmt = "%-20s %-20s %-20s\n"; + $msg .= sprintf($fmt, "OS ID/name", "Node Type", "Image PID/ID/name"); + $msg .= sprintf($fmt, "==========", "=========", "================="); + while (my ($osid, $type, $imageid, $pid, $imagename) = + $query_result->fetchrow_array()) { + my $osname = OSinfo->Lookup($osid)->osname(); + $msg .= sprintf($fmt, "$osid/$osname",$type,"$pid/$imageid/$imagename"); + } + + UserError("Conflict: Please check the other Image descriptors". + " and make the necessary changes!\n $msg"); +} + +exit(0) + if ($verify); + +# +# Now safe to create new image descriptor. +# +# We pass the imagename along as an argument to Create(), so remove it from +# the argument array. +# +delete($newimageid_args{"imagename"}); + +my $usrerr; +my $new_image = Image->Create($project, $group, $this_user, $imagename, + \%newimageid_args, \$usrerr); +UserError($usrerr) + if (defined($usrerr)); +fatal("Could not create new Image!") + if (!defined($new_image)); + +my $imageid = $new_image->imageid(); + +# The web interface requires this line to be printed. +print "IMAGE $imagename/$imageid has been created\n"; + +exit(0); + +sub fatal($) +{ + my ($mesg) = @_; + + print STDERR "*** $0:\n". + " $mesg\n"; + # Exit with negative status so web interface treats it as system error. + exit(-1); +} + +sub UserError(;$) +{ + my ($mesg) = @_; + + if (keys(%errors)) { + foreach my $key (keys(%errors)) { + my $val = $errors{$key}; + print "${key}: $val\n"; + } + } + print "$mesg\n" + if (defined($mesg)); + + # Exit with positive status so web interface treats it as user error. + exit(1); +} + +sub escapeshellarg($) +{ + my ($str) = @_; + + $str =~ s/[^[:alnum:]]/\\$&/g; + return $str; +} diff --git a/configure b/configure index 3c33e5433dfafb67674339a84aa8cb45952ce521..67d73815f6bc067a2ade8173c5c1d60de3cf4d93 100755 --- a/configure +++ b/configure @@ -2429,7 +2429,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ account/quotamail account/mkusercert account/newproj account/newuser \ backend/GNUmakefile backend/moduserinfo backend/newgroup \ backend/newmmlist backend/editexp backend/editimageid backend/editnodetype \ - backend/editsitevars \ + backend/editsitevars backend/newimageid \ tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \ tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \ tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \ diff --git a/configure.in b/configure.in index a2f954815fb07b639b9dc0cca86a98a99e0ae50d..f71a069e989943df8c89facf51e1a49f6f1da225 100755 --- a/configure.in +++ b/configure.in @@ -809,7 +809,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ account/quotamail account/mkusercert account/newproj account/newuser \ backend/GNUmakefile backend/moduserinfo backend/newgroup \ backend/newmmlist backend/editexp backend/editimageid backend/editnodetype \ - backend/editsitevars \ + backend/editsitevars backend/newimageid \ tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \ tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \ tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \ diff --git a/db/Image.pm.in b/db/Image.pm.in index 5c71683b20f644e53cddca20888d636021db81e7..936d7dcaaedc0b28196ed5976c9af412645cd829 100644 --- a/db/Image.pm.in +++ b/db/Image.pm.in @@ -164,6 +164,163 @@ sub Refresh($) return 0; } +# +# Create a new os_info. This installs the new record in the DB, +# and returns an instance. There is some bookkeeping along the way. +# +sub Create($$$$) +{ + my ($class, $project, $group, $creator, $imagename, $argref, $usrerr_ref) = @_; + my $idx; + my $now = time(); + + return undef + if (ref($class) || !ref($project)); + + my $isadmin = $creator->IsAdmin(); + + # We may ignore particular partN_osid's by deleting them. + my @arg_slots = grep(/^part[1-4]_osid$/, keys(%{$argref})); + + # Pass-through a bunch of required slots, ignoring any extras + # and stuff we handle explicitly. + foreach my $key ("loadpart", "loadlength", "default_osid") { + if (!exists($argref->{$key})) { + $$usrerr_ref = "Error: $key missing in Image->Create!"; + return undef; + } + push(@arg_slots, $key); + } + # Pass-through optional slots, otherwise the DB default is used. + foreach my $key ("path", "shared", "global") { + if (exists($argref->{$key})) { + push(@arg_slots, $key); + } + } + + my $pid = $project->pid(); + my $pid_idx = $project->pid_idx(); + my $gid = $group->gid(); + my $gid_idx = $group->gid_idx(); + my $uid = $creator->uid(); + my $uid_idx = $creator->uid_idx(); + + # + # The pid/imageid has to be unique, so lock the table for the check/insert. + # + DBQueryWarn("lock tables images write, emulab_indicies write") + or return undef; + + my $query_result = + DBQueryWarn("select imagename from images ". + "where pid_idx='$pid_idx' and imagename='$imagename'"); + + if ($query_result->numrows) { + DBQueryWarn("unlock tables"); + $$usrerr_ref = "Error: IMAGE $imagename in project $pid already exists!"; + return undef; + } + + # + # Grab unique ID. Table already locked. + # + my $imageid = TBGetUniqueIndex("next_imageid", undef, 1); + my $uuid = NewUUID(); + my $desc = "''"; + + # Some fields special cause of quoting. + # + if (exists($argref->{'description'})) { + $desc = DBQuoteSpecial($argref->{'description'}); + } + + my $query = "insert into images set ". + join(",", map("$_='" . $argref->{$_} . "'", @arg_slots)); + + # Append the rest + $query .= ",imagename='$imagename'"; + $query .= ",imageid='$imageid'"; + $query .= ",uuid='$uuid'"; + $query .= ",pid='$pid',pid_idx='$pid_idx'"; + $query .= ",gid='$gid',gid_idx='$gid_idx'"; + $query .= ",creator='$uid',creator_idx='$uid_idx'"; + $query .= ",created=now()"; + $query .= ",description=$desc"; + + if (! DBQueryWarn($query)) { + DBQueryWarn("unlock tables"); + tberror("Error inserting new images record for $pid/$imagename!"); + return undef; + } + + my $image = Image->Lookup($imageid); + # Create the osidtoimageid mapping. Admins have an option to do it or not. + my $makedefault = exists($argref->{"makedefault"}) && + $argref->{"makedefault"} eq "1"; + if (!$isadmin || $makedefault) { + + # Lock tables unlocks previous locks as a side-effect. + DBQueryWarn("lock tables osidtoimageid write, images write, ". + "nodes as n write, node_type_attributes as a write"); + # + # Need a list of node types. We join this over the nodes table so that + # we get a list of just the nodes that are currently in the testbed, not + # just in the node_types table. + # + my $types_result = + DBQueryWarn("select distinct n.type from nodes as n ". + "left join node_type_attributes as a on a.type=n.type ". + "where a.attrkey='imageable' and ". + " a.attrvalue!='0'"); + my @mtypes_array; + my @map_updates; + while (my ($type) = $types_result->fetchrow_array()) { + push(@mtypes_array, $type); + + # Remember when we get one of the mtype_$type args. These aren't DB + # columns to update, but instead control re-creating the rows in the + # osidtoimageid table for this imageid, below. + my $mtype = "mtype_$type"; + if (exists($argref->{$mtype})) { + my $value = $argref->{$mtype}; + ##printf "argref->{$mtype} %s\n", $value; + if ($value eq "1") { + push(@map_updates, $type); + } + } + } + + my @osid_array; + for (my $i = 1; $i <= 4; $i++) { + my $foo = $image->field("part${i}_osid"); + if (defined($foo)) { + push(@osid_array, $foo); + } + } + + for (my $i = 0; $i <= $#map_updates; $i++) { + for (my $j = 0; $j <= $#osid_array; $j++) { + my $query = "INSERT INTO osidtoimageid ". + " (osid, type, imageid) ". + "VALUES ('$osid_array[$j]', ". + " '$map_updates[$i]', ". + " '$imageid')"; + ##print "$query\n"; + if (! DBQueryWarn($query)) { + DBQueryWarn("unlock tables"); + tberror("Error inserting new ostoimageid mapping for ". + "$pid/$imagename!"); + return undef; + } + } + } + } + + DBQueryWarn("unlock tables"); + + return $image; +} + # # Worker class method to edit image descriptor. # Assumes most argument checking was done elsewhere. @@ -196,7 +353,7 @@ sub EditImageid($$$$) # just in the node_types table. # my $types_result = - DBQueryFatal("select distinct n.type from nodes as n ". + DBQueryWarn("select distinct n.type from nodes as n ". "left join node_type_attributes as a on a.type=n.type ". "where a.attrkey='imageable' and ". " a.attrvalue!='0'"); @@ -230,13 +387,13 @@ sub EditImageid($$$$) } if (keys %updates || $redo_map) { - DBQueryFatal("lock tables images write, os_info write, ". + DBQueryWarn("lock tables images write, os_info write, ". "osidtoimageid write"); } if (keys %updates) { if ($image->Update(\%updates)) { $$usrerr_ref = "DB: Error updating the images table"; - DBQueryFatal("unlock tables"); + DBQueryWarn("unlock tables"); return undef; } } @@ -245,7 +402,7 @@ sub EditImageid($$$$) # Update the osidtoimageid table too. # # Must delete old entries first. - DBQueryFatal("delete from osidtoimageid ". + DBQueryWarn("delete from osidtoimageid ". "where imageid='$imageid'"); my @osid_array; @@ -263,12 +420,12 @@ sub EditImageid($$$$) " '$map_updates[$i]', ". " '$imageid')"; ##print "$query\n"; - DBQueryFatal($query); + DBQueryWarn($query); } } } if (keys %updates || $redo_map) { - DBQueryFatal("unlock tables"); + DBQueryWarn("unlock tables"); } return 1; diff --git a/sql/database-fill.sql b/sql/database-fill.sql index 63f8f8029285c5315d4bb3833b0ce6dbf77dc0fb..bcb99bcf34e6e1f5713375e7145d01a731337114 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -789,12 +789,24 @@ REPLACE INTO table_regex VALUES ('experiments','elab_in_elab','int','redirect',' REPLACE INTO table_regex VALUES ('experiments','elabinelab_singlenet','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('experiments','elabinelab_cvstag','text','regex','^[-\\w\\@\\/\\.]+$',0,0,NULL); -REPLACE INTO table_regex VALUES ('images','imageid','text','redirect','default:int',0,100000000,NULL); REPLACE INTO table_regex VALUES ('images','imagename','text','regex','^[a-zA-Z0-9][-\\w\\.+]+$',2,30,NULL); +REPLACE INTO table_regex VALUES ('images','imageid','text','redirect','default:int',0,100000000,NULL); +REPLACE INTO table_regex VALUES ('images','pid','text','redirect','projects:pid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','gid','text','redirect','groups:gid',0,0,NULL); REPLACE INTO table_regex VALUES ('images','description','text','regex','^[\\040-\\176\\012\\015\\011]*$',1,256,NULL); +REPLACE INTO table_regex VALUES ('images','loadpart','text','redirect','default:tinyint',0,4,NULL); +REPLACE INTO table_regex VALUES ('images','loadlength','text','redirect','default:tinyint',1,4,NULL); +REPLACE INTO table_regex VALUES ('images','part1_osid','text','redirect','os_info:osid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','part2_osid','text','redirect','os_info:osid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','part3_osid','text','redirect','os_info:osid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','part4_osid','text','redirect','os_info:osid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','default_osid','text','redirect','os_info:osid',0,0,NULL); REPLACE INTO table_regex VALUES ('images','path','text','regex','^[-_\\w\\.\\/:+]*$',1,256,NULL); +REPLACE INTO table_regex VALUES ('images','shared','text','redirect','default:boolean',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','global','text','redirect','default:boolean',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','makedefault','text','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('images','mtype','text','redirect','default:boolean',0,0,NULL); -REPLACE INTO table_regex VALUES ('images','osid','text','redirect','os_info:osid',0,0,NULL); +REPLACE INTO table_regex VALUES ('images','node_id','text','redirect','nodes:node_id',0,0,NULL); REPLACE INTO table_regex VALUES ('images','load_address','text','redirect','default:text',0,0,NULL); REPLACE INTO table_regex VALUES ('images','frisbee_pid','text','redirect','default:int',0,0,NULL); diff --git a/www/imageid_defs.php b/www/imageid_defs.php index 1bfe16f74663568f54c0c0801dcc581812b065a9..7467c29e723f9fa9c5c0760f0a1cc95f941bf0a9 100644 --- a/www/imageid_defs.php +++ b/www/imageid_defs.php @@ -115,7 +115,94 @@ class Image } # - # Class function to edit image descriptor. + # Class function to create a new image descriptor. + # + function NewImageId($imagename, $args, &$errors) { + global $suexec_output, $suexec_output_array; + + # + # Generate a temporary file and write in the XML goo. + # + $xmlname = tempnam("/tmp", "newimageid"); + if (! $xmlname) { + TBERROR("Could not create temporary filename", 0); + $errors[] = "Transient error(1); please try again later."; + return null; + } + if (! ($fp = fopen($xmlname, "w"))) { + TBERROR("Could not open temp file $xmlname", 0); + $errors[] = "Transient error(2); please try again later."; + return null; + } + + # Add these. Maybe caller should do this? + $args["imagename"] = $imagename; + + fwrite($fp, "<image>\n"); + foreach ($args as $name => $value) { + fwrite($fp, "<attribute name=\"$name\">"); + fwrite($fp, " <value>" . htmlspecialchars($value) . "</value>"); + fwrite($fp, "</attribute>\n"); + } + fwrite($fp, "</image>\n"); + fclose($fp); + chmod($xmlname, 0666); + + $retval = SUEXEC("nobody", "nobody", "webnewimageid $xmlname", + SUEXEC_ACTION_IGNORE); + + if ($retval) { + if ($retval < 0) { + $errors[] = "Transient error(3); please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + } + else { + # unlink($xmlname); + if (count($suexec_output_array)) { + for ($i = 0; $i < count($suexec_output_array); $i++) { + $line = $suexec_output_array[$i]; + if (preg_match("/^([-\w]+):\s*(.*)$/", + $line, $matches)) { + $errors[$matches[1]] = $matches[2]; + } + else + $errors[] = $line; + } + } + else + $errors[] = "Transient error(4); please try again later."; + } + return null; + } + + # + # Parse the last line of output. Ick. + # + unset($matches); + + if (!preg_match("/^IMAGE\s+([^\/]+)\/(\d+)\s+/", + $suexec_output_array[count($suexec_output_array)-1], + $matches)) { + $errors[] = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + return null; + } + $image = $matches[2]; + $newimage = image::Lookup($image); + if (! $newimage) { + $errors[] = "Transient error; please try again later."; + TBERROR("Could not lookup new image $image", 0); + return null; + } + + # Unlink this here, so that the file is left behind in case of error. + # We can then create the image by hand from the xmlfile, if desired. + unlink($xmlname); + return $newimage; + } + + # + # Class function to edit an image descriptor. # function EditImageid($image, $args, &$errors) { global $suexec_output, $suexec_output_array; diff --git a/www/newimageid.php3 b/www/newimageid.php3 index d0616aa5ccc58bc2133410dc4dce3e545cebab1c..e95c1a58788b1d2fe821f4a7e1956924c090aaf1 100644 --- a/www/newimageid.php3 +++ b/www/newimageid.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2004, 2006, 2007 University of Utah and the Flux Group. +# Copyright (c) 2000-2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -16,7 +16,7 @@ include("osiddefs.php3"); PAGEHEADER("Create a new Image Descriptor (long form)"); # -# Only known and logged in users! +# Only known and logged in users. # $this_user = CheckLoginOrDie(); $uid = $this_user->uid(); @@ -26,8 +26,8 @@ $isadmin = ISADMIN(); # # Verify page arguments. # -$optargs = OptionalPageArguments("submit", PAGEARG_STRING, - "formfields", PAGEARG_ARRAY); +$optargs = OptionalPageArguments("submit", PAGEARG_STRING, + "formfields", PAGEARG_ARRAY); # # See what projects the uid can do this in. @@ -51,14 +51,14 @@ $types_result = " a.attrvalue!='0' and n.role='testnode'"); # -# Spit the form out using the array of data. -# +# Spit the form out using the array of data. +# function SPITFORM($formfields, $errors) { global $this_user, $projlist, $isadmin, $types_result; global $TBDB_IMAGEID_IMAGENAMELEN, $TBDB_NODEIDLEN; global $TBPROJ_DIR, $TBGROUP_DIR; - + if ($errors) { echo "<table class=nogrid align=center border=0 cellpadding=6 cellspacing=0> @@ -162,14 +162,15 @@ function SPITFORM($formfields, $errors) } </SCRIPT>\n"; + $url = CreateURL("newimageid"); echo "<br> - <table align=center border=1> + <table align=center border=1> <tr> <td align=center colspan=2> <em>(Fields marked with * are required)</em> </td> </tr> - <form action='newimageid.php3' method=post name=idform>\n"; + <form action='$url' method=post name=idform>\n"; # # Select Project @@ -225,7 +226,7 @@ function SPITFORM($formfields, $errors) </tr>\n"; # - # Image Name: + # Image Name # echo "<tr> <td>*Descriptor Name (no blanks):</td> @@ -421,7 +422,7 @@ function SPITFORM($formfields, $errors) echo "<tr> <td align=center colspan=2> - <b><input type=submit name=submit value=Submit></b> + <b><input type=submit name=submit value=Submit></b> </td> </tr>\n"; @@ -472,11 +473,11 @@ if (!isset($submit)) { $defaults["part3_osid"] = ""; $defaults["part4_osid"] = ""; $defaults["default_osid"]= ""; + $defaults["path"] = "$TBPROJ_DIR/"; $defaults["node_id"] = ""; $defaults["shared"] = "No"; $defaults["global"] = "No"; $defaults["makedefault"] = "No"; - $defaults["path"] = "$TBPROJ_DIR/"; # # For users that are in one project and one subgroup, it is usually @@ -485,7 +486,7 @@ if (!isset($submit)) { # if (count($projlist) == 1) { list($project, $grouplist) = each($projlist); - + if (count($grouplist) <= 2) { $defaults["pid"] = $project; if (count($grouplist) == 1 || strcmp($project, $grouplist[0])) @@ -512,203 +513,108 @@ if (!isset($submit)) { # Otherwise, must validate and redisplay if errors # $errors = array(); -$project = null; -$group = null; +# Be friendly about the form field names. if (!isset($formfields["pid"]) || strcmp($formfields["pid"], "") == 0) { - $errors["Project"] = "Not Selected"; -} -elseif (!TBvalid_pid($formfields["pid"])) { - $errors["Project"] = "Invalid project name"; -} -elseif (! ($project = Project::Lookup($formfields["pid"]))) { - $errors["Project"] = "Invalid project name"; -} -elseif (! $project->AccessCheck($this_user, $TB_PROJECT_MAKEIMAGEID)) { - $errors["Project"] = "Not enough permission"; -} - -if (isset($formfields["gid"]) && $formfields["gid"] != "") { - if ($formfields["pid"] == $formfields["gid"] && $project) { - $group = $project->DefaultGroup(); - } - elseif (!TBvalid_gid($formfields["gid"])) { - $errors["Group"] = "Invalid group name"; - } - elseif ($project && - ! ($group = $project->LookupSubgroupByName($formfields["gid"]))) { - $errors["Group"] = "Invalid group name"; - } -} -elseif ($project) { - $group = $project->DefaultGroup(); + $errors["Project"] = "Missing Field"; } if (!isset($formfields["imagename"]) || strcmp($formfields["imagename"], "") == 0) { $errors["Descriptor Name"] = "Missing Field"; } -else { - if (! ereg("^[a-zA-Z0-9][-_a-zA-Z0-9\.\+]+$", $formfields["imagename"])) { - $errors["Descriptor Name"] = - "Must be alphanumeric (includes _, -, +, and .)<br>". - "and must begin with an alphanumeric"; - } - elseif (strlen($formfields["imagename"]) > $TBDB_IMAGEID_IMAGENAMELEN) { - $errors["Descriptor Name"] = - "Too long! ". - "Must be less than or equal to $TBDB_IMAGEID_IMAGENAMELEN"; - } -} - -if (!isset($formfields["description"]) || - strcmp($formfields["description"], "") == 0) { - $errors["Description"] = "Missing Field"; -} if (!isset($formfields["loadpart"]) || - strcmp($formfields["loadpart"], "") == 0 || strcmp($formfields["loadpart"], "X") == 0) { - $errors["Starting Partition"] = "Not Selected"; -} -elseif (! ereg("^[0-9]+$", $formfields["loadpart"]) || - $formfields["loadpart"] < 0 || $formfields["loadpart"] > 4) { - $errors["Starting Partition"] = "Must be 0,1,2,3, or 4!"; + $errors["Starting DOS Partion"] = "Missing Field"; } if (!isset($formfields["loadlength"]) || - strcmp($formfields["loadlength"], "") == 0 || strcmp($formfields["loadlength"], "X") == 0) { - $errors["#of Partitions"] = "Not Selected"; + $errors["Number of DOS Partitions"] = "Missing Field"; } -elseif (! ereg("^[0-9]+$", $formfields["loadlength"]) || - $formfields["loadlength"] < 1 || $formfields["loadlength"] > 4) { - $errors["#of Partitions"] = "Must be 1,2,3, or 4!"; -} -elseif ($formfields["loadpart"] != 0 && $formfields["loadlength"] != 1) { - $errors["#of Partitions"] = - "Only single slices or<br> partial disks are allowed"; + +if (!isset($formfields["default_osid"]) || + strcmp($formfields["default_osid"], "none") == 0) { + $errors["Boot OS"] = "Missing Field"; } # -# Check sanity of the OSIDs for each slice. Permission checks not needed. -# Store the ones we care about and silently forget about the extraneous -# OSIDs by setting the locals to NULL. +# Build up argument array to pass along. # -# XXX This loop creates locals part1_osid, part2_osid, part3_osid, and -# part4_osid on the fly. Look at $$foo. We use them below. -# -$osid_array = array(); +$args = array(); -for ($i = 1; $i <= 4; $i++) { - $foo = "part${i}_osid"; # Local variable dynamically created. - $thisosid = $formfields[$foo]; +if (isset($formfields["pid"]) && $formfields["pid"] != "") { + $args["pid"] = $pid = $formfields["pid"]; +} - if (($formfields["loadpart"] && $i == $formfields["loadpart"]) || - (!$formfields["loadpart"] && $i <= $formfields["loadlength"])) { +if (isset($formfields["gid"]) && $formfields["gid"] != "") { + $args["gid"] = $gid = $formfields["gid"]; +} - if (!isset($thisosid) || - strcmp($thisosid, "") == 0 || - strcmp($thisosid, "X") == 0) { - $errors["Partition $i OS"] = "Must select an OS"; - } - elseif (strcmp($thisosid, "none") == 0) { - # - # Allow admins to specify no OS for a partition. - # - if (!$isadmin) - $errors["Partition $i OS"] = "Must select an OS"; - $$foo = "NULL"; - } - elseif (! TBvalid_osid($thisosid)) { - $errors["Partition $i OS"] = "Invalid characters in OSID"; - } - elseif (!OSinfo::Lookup($thisosid)) { - $errors["Partition $i OS"] = "No such OS defined"; - } - else { - $$foo = "'$thisosid'"; - $osid_array[] = $thisosid; - } - } - else { - $$foo = "NULL"; - } +if (isset($formfields["imagename"]) && $formfields["imagename"] != "") { + $args["imagename"] = $formfields["imagename"]; } -# -# Check the boot OS. Must be one of the OSes selected for a partition. -# -if (!isset($formfields["default_osid"]) || - strcmp($formfields["default_osid"], "") == 0 || - strcmp($formfields["default_osid"], "none") == 0) { - $errors["Boot OS"] = "Not Selected"; +if (isset($formfields["description"]) && $formfields["description"] != "") { + $args["description"] = $formfields["description"]; } -elseif (! TBvalid_osid($formfields["default_osid"])) { - $errors["Boot OS"] = "Invalid characters in OSID"; + +if (isset($formfields["loadpart"]) && + $formfields["loadpart"] != "none" && $formfields["loadpart"] != "") { + $args["loadpart"] = $formfields["loadpart"]; } -elseif (!OSinfo::Lookup($formfields["default_osid"])) { - $errors["Boot OS"] = "No such OS defined"; + +if (isset($formfields["loadlength"]) && + $formfields["loadlength"] != "none" && $formfields["loadlength"] != "") { + $args["loadlength"] = $formfields["loadlength"]; } -else { - for ($i = 0; $i < count($osid_array); $i++) { - if (strcmp($osid_array[$i], $formfields["default_osid"]) == 0) - break; - } - if ($i == count($osid_array)) - $errors["Boot OS"] = "Invalid; Must be one of the partitions"; + +if (isset($formfields["part1_osid"]) && + $formfields["part1_osid"] != "none" && $formfields["part1_osid"] != "") { + $args["part1_osid"] = $formfields["part1_osid"]; } -# -# Only admin types can set the global bit for an image. Ignore silently. -# -$global = 0; -if ($isadmin && - isset($formfields["global"]) && - strcmp($formfields["global"], "Yep") == 0) { - $global = 1; +if (isset($formfields["part2_osid"]) && + $formfields["part2_osid"] != "none" && $formfields["part2_osid"] != "") { + $args["part2_osid"] = $formfields["part2_osid"]; } -$shared = 0; -if (isset($formfields["shared"]) && - strcmp($formfields["shared"], "Yep") == 0) { - $shared = 1; +if (isset($formfields["part3_osid"]) && + $formfields["part3_osid"] != "none" && $formfields["part3_osid"] != "") { + $args["part3_osid"] = $formfields["part3_osid"]; } -# Does not make sense to do this. -if ($global && $shared) { - $errors["Global"] = "Image declared both shared and global"; + +if (isset($formfields["part4_osid"]) && + $formfields["part4_osid"] != "none" && $formfields["part4_osid"] != "") { + $args["part4_osid"] = $formfields["part4_osid"]; } -# -# The path must not contain illegal chars and it must be more than -# the original /proj/$pid we gave the user. We allow admins to specify -# a path outside of /proj though. -# -if (!isset($formfields["path"]) || - strcmp($formfields["path"], "") == 0) { - $errors["Path"] = "Missing Field"; +if (isset($formfields["default_osid"]) && + $formfields["default_osid"] != "none" && $formfields["default_osid"] != "") { + $args["default_osid"] = $formfields["default_osid"]; } -elseif (! ereg("^[-_a-zA-Z0-9\/\.+]+$", $formfields["path"])) { - $errors["Path"] = "Contains invalid characters"; + +if (isset($formfields["path"]) && $formfields["path"] != "") { + $args["path"] = $formfields["path"]; } -elseif (! $isadmin) { - $pdef = ""; - - if (!$shared && - isset($formfields["gid"]) && - strcmp($formfields["gid"], "") && - strcmp($formfields["gid"], $formfields["pid"])) { - $pdef = "$TBGROUP_DIR/" . - $formfields["pid"] . "/" . $formfields["gid"] . "/"; - } - else { - $pdef = "$TBPROJ_DIR/" . $formfields["pid"] . "/images/"; - } - if (strpos($formfields["path"], $pdef) === false) { - $errors["Path"] = "Invalid Path"; - } +if (isset($formfields["node_id"]) && $formfields["node_id"] != "") { + $args["node_id"] = $node_id = $formfields["node_id"]; +} + +# Filter booleans from checkboxes to 0 or 1. +if (isset($formfields["shared"])) { + $args["shared"] = strcmp($formfields["shared"], "Yep") ? 0 : 1; +} +if (isset($formfields["global"])) { + $args["global"] = strcmp($formfields["global"], "Yep") ? 0 : 1; +} +$makedefault = 0; +if (isset($formfields["makedefault"])) { + $args["makedefault"] = $makedefault = + strcmp($formfields["makedefault"], "Yep") ? 0 : 1; } # @@ -716,7 +622,7 @@ elseif (! $isadmin) { # Store the valid types in a new array for simplicity. # $mtypes_array = array(); - +mysql_data_seek($types_result, 0); while ($row = mysql_fetch_array($types_result)) { $type = $row["type"]; @@ -732,37 +638,13 @@ if (! count($mtypes_array)) { $errors["Node Types"] = "Must select at least one type"; } -# -# Check sanity of node name and that user can create an image from it. -# -unset($node); -if (isset($formfields["node_id"]) && - strcmp($formfields["node_id"], "")) { +# The mtype_* checkboxes are dynamically generated. +foreach ($mtypes_array as $type) { - if (!TBvalid_node_id($formfields["node_id"])) { - $errors["Node"] = "Invalid node name"; - } - elseif (! ($node = Node::Lookup($formfields["node_id"]))) { - $errors["Node"] = "Invalid node name"; - } - elseif (!$node->AccessCheck($this_user, $TB_NODEACCESS_LOADIMAGE)) { - $errors["Node"] = "Not enough permission"; - } - else { - $node_id = $node->node_id(); - } -} - -# -# Only admins have this option. Always on for mereusers, but default off -# for admins. -# -$makedefault = 0; - -if (! $isadmin || - (isset($formfields["makedefault"]) && - strcmp($formfields["makedefault"], "Yep") == 0)) { - $makedefault = 1; + # Filter booleans from checkbox values. + $checked = isset($formfields["mtype_$type"]) && + strcmp($formfields["mtype_$type"], "Yep") == 0; + $args["mtype_$type"] = $checked ? "1" : "0"; } # @@ -776,67 +658,47 @@ if (count($errors)) { } # -# For the rest, sanitize and convert to locals to make life easier. -# -$description = addslashes($formfields["description"]); -$pid = $project->pid(); -$gid = $group->gid(); -$pid_idx = $project->pid_idx(); -$gid_idx = $group->gid_idx(); -$imagename = $formfields["imagename"]; -$loadpart = $formfields["loadpart"]; -$loadlength = $formfields["loadlength"]; -$default_osid= $formfields["default_osid"]; -$path = $formfields["path"]; - -# -# Grab unique imageid (before locking tables). -# -$imageid = TBGetUniqueIndex("next_osid"); -$uuid = NewUUID(); - -# -# And insert the record! -# -DBQueryFatal("lock tables images write, osidtoimageid write"); - -# -# Of course, the Image record may not already exist in the DB. -# -if (($image = Image::LookupByName($project, $imagename))) { - DBQueryFatal("unlock tables"); +# Mereusers are not allowed to create more than one osid/imageid mapping +# for each machinetype. They cannot actually do that through the EZ form +# since the osid/imageid has to be unique, but it can happen by mixed +# use of the long form and the short form, or with multiple uses of the +# long form. - $errors["Descriptor Name"] = "Already in use in selected project"; +# Can't check this unless we have at least one mtype! +if (!count($mtypes_array)) { SPITFORM($formfields, $errors); PAGEFOOTER(); return; } - -# -# Mereusers are not allowed to create more than one osid/imageid mapping -# for each machinetype. They cannot actually do that through the EZ form -# since the osid/imageid has to be unique, but it can happen by mixed -# use of the long form and the short form, or with multiple uses of the -# long form. -# + $typeclause = "type=" . "'$mtypes_array[0]'"; - for ($i = 1; $i < count($mtypes_array); $i++) { $typeclause = "$typeclause or type=" . "'$mtypes_array[$i]'"; } -$osidclause = "osid=" . "'$osid_array[0]'"; - -for ($i = 1; $i < count($osid_array); $i++) { - $osidclause = "$osidclause or osid=" . "'$osid_array[$i]'"; +unset($osidclause); +for ($i = 1; $i <= 4; $i++) { + # Local variable dynamically created. + $foo = "part${i}_osid"; + + if (isset($formfields[$foo])) { + if (isset($osidclause)) + $osidclause = "$osidclause or osid='" . $formfields[$foo] . "' "; + else + $osidclause = "osid='" . $formfields[$foo] . "' "; + + $osid_array[] = $formfields[$foo]; + } } +DBQueryFatal("lock tables images write, os_info write, osidtoimageid write"); $query_result = DBQueryFatal("select osidtoimageid.*,images.pid,images.imagename ". " from osidtoimageid ". "left join images on ". " images.imageid=osidtoimageid.imageid ". "where ($osidclause) and ($typeclause)"); +DBQueryFatal("unlock tables"); if (mysql_num_rows($query_result)) { if (!$isadmin || $makedefault) { @@ -855,23 +717,25 @@ if (mysql_num_rows($query_result)) { echo "<table border=1 cellpadding=2 cellspacing=2 align='center'>\n"; echo "<tr> - <td align=center>OSID</td> - <td align=center>Type</td> - <td align=center>ImageID</td> + <td align=center>OS ID/name</td> + <td align=center>Node Type</td> + <td align=center>Image PID/ID/name</td> </tr>\n"; while ($row = mysql_fetch_array($query_result)) { - $imageid = $row['imageid']; - $url = rawurlencode($imageid); $osid = $row["osid"]; + $osinfo = OSinfo::Lookup($osid); + $osname = $osinfo->osname(); $type = $row["type"]; + $imageid = $row['imageid']; + $url = CreateURL("showimageid", URLARG_IMAGEID, $imageid); + $pid = $row['pid']; $imagename = $row["imagename"]; echo "<tr> - <td>$osid</td> + <td>$osid/$osname</td> <td>$type</td> - <td><A href='showimageid.php3?&imageid=$url'> - $imagename</A></td> + <td>$pid/$imageid/<A href='$url'>$imagename</A></td> </tr>\n"; } echo "</table><br><br>\n"; @@ -881,43 +745,24 @@ if (mysql_num_rows($query_result)) { } } -$query_result = - DBQueryFatal("INSERT INTO images ". - "(imagename, imageid, description, loadpart, loadlength, ". - " part1_osid, part2_osid, part3_osid, part4_osid, ". - " default_osid, path, pid, gid, shared, global, ". - " creator, creator_idx, created, pid_idx, gid_idx, uuid) ". - "VALUES ". - " ('$imagename', '$imageid', '$description', $loadpart, ". - " $loadlength, ". - " $part1_osid, $part2_osid, $part3_osid, $part4_osid, ". - " '$default_osid', '$path', '$pid', '$gid', $shared, ". - " $global, '$uid', '$dbid', now(), $pid_idx, $gid_idx, ". - " '$uuid')"); - -if (!$isadmin || $makedefault) { - for ($i = 0; $i < count($mtypes_array); $i++) { - for ($j = 0; $j < count($osid_array); $j++) { - DBQueryFatal("REPLACE INTO osidtoimageid ". - "(osid, type, imageid) ". - "VALUES ('$osid_array[$j]', '$mtypes_array[$i]', ". - " '$imageid')"); - } - } +# Send to the backend for more checking, and eventually, to update the DB. +$imagename = $args["imagename"]; +if (! ($image = Image::NewImageId($imagename, $args, $errors))) { + # Always respit the form so that the form fields are not lost. + # I just hate it when that happens so lets not be guilty of it ourselves. + SPITFORM($formfields, $errors); + PAGEFOOTER(); + return; } -DBQueryFatal("unlock tables"); - -# -# Get the object for rest of the script. -# -if (! ($image = Image::Lookup($imageid))) { - TBERROR("Could not look up object for image $imageid", 1); -} +$pid = $image->pid(); +$gid_idx = $image->gid_idx(); +$group = Group::Lookup($gid_idx); SUBPAGESTART(); SUBMENUSTART("More Options"); -if (! isset($node)) { +if (! isset($node_id)) { + $imageid = $image->imageid(); $fooid = rawurlencode($imageid); WRITESUBMENUBUTTON("Edit this Image Descriptor", "editimageid.php3?imageid=$fooid"); @@ -940,7 +785,7 @@ SUBMENUEND(); $image->Show(); SUBPAGEEND(); -if (isset($node)) { +if (isset($node_id)) { # # Create the image. # @@ -948,6 +793,11 @@ if (isset($node)) { # problem for the descriptor; the script is done with it by the time # it returns. However, if the node is freed up, things are going to go # awry. + # + + $node = Node::Lookup($node_id); # Already been checked. + $node_id = $node->node_id(); # XXX Why? + # # Grab the unix GID for running script. # @@ -955,7 +805,7 @@ if (isset($node)) { $safe_name = escapeshellarg($imagename); echo "<br> - Creating image using node '$node_id' ... + Creating image using node '$node_id'. <br><br>\n"; flush(); @@ -966,8 +816,8 @@ if (isset($node)) { echo "This will take 10 minutes or more; you will receive email notification when the image is complete. In the meantime, <b>PLEASE DO NOT</b> delete the imageid or the experiment - $node is in. In fact, it is best if you do not mess with - the node at all!<br>\n"; + $node_id is in. In fact, it is best if you do not mess with + the node or the experiment at all until you receive email.<br>\n"; } #