Commit 517699a9 authored by Leigh Stoller's avatar Leigh Stoller

Bulk of changes for deconfusing the image creation process (issue #318).

parent fe5ee658
......@@ -297,11 +297,15 @@ sub DoSnapshot()
my $update_profile;
my $copyback_uuid;
my $copyback_urn;
my $swebtask;
my $nosnapshot = 0;
my $mustnotexist = 0;
my $update_prepare = 0;
my $doversions = 0;
my $usetracker = 0;
my $operation = "image-only"; # Default to just snapshot.
my $optlist = "n:i:u:Uc:";
my $optlist = "n:i:u:Uc:O:Ss";
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
......@@ -324,6 +328,23 @@ sub DoSnapshot()
if (defined($options{"U"})) {
$update_prepare = 1;
}
if (defined($options{"s"})) {
$nosnapshot = 1;
}
if (defined($options{"S"})) {
$nosnapshot = 1;
$mustnotexist = 1;
}
if (defined($options{"O"})) {
$operation = $options{"O"};
if ($operation !~
/^(update-profile|copy-profile|new-profile|image-only)$/) {
usage();
}
if ($operation eq "update-profile") {
$update_profile = "node";
}
}
if (defined($cloneprofile) && defined($update_profile)) {
fatal("Not allowed to update profile when cloning a profile");
}
......@@ -444,12 +465,12 @@ sub DoSnapshot()
#
if (! TBcheck_dbslot($imagename, "images",
"imagename", TBDB_CHECKDBSLOT_ERROR)) {
$imagename = $profile->profileid();
$imagename .= "." . $node_id
if (defined($node_id));
$errmsg = "Invalid imagename: " . TBFieldErrorString() . "\n";
$errcode = 1;
goto uerror;
}
#
#
# Instruct the remote cluster to copy the image back to its origin,
# but we need to ask the IMS for uuid of the image that is running,
# so we can tell the cluster, which then tells the origin cluster.
......@@ -535,23 +556,27 @@ sub DoSnapshot()
}
$needunlock = 1;
#
# Grab the webtask, but only after we have it locked, since we are
# going to modify it. This is a different webtask then the one we
# got on the command line. The command line webtask is for reporting
# he results of the command, while the instance webtask is used to
# report ongoing status of the imaging operation (to the web UI).
#
my $swebtask = $instance->webtask();
# We reuse this so clear it.
$swebtask->Reset();
$swebtask->AutoStore(1);
# This is convenience for the web server.
$swebtask->aggregate_urn($aggregate->aggregate_urn());
$swebtask->client_id($node_id);
$instance->SetStatus("imaging");
$aggregate->SetStatus("imaging");
if (!$nosnapshot) {
#
# Grab the webtask, but only after we have it locked, since we are
# going to modify it. This is a different webtask then the one we
# got on the command line. The command line webtask is for reporting
# he results of the command, while the instance webtask is used to
# report ongoing status of the imaging operation (to the web UI).
#
$swebtask = $instance->webtask();
# We reuse this so clear it.
$swebtask->Reset();
$swebtask->AutoStore(1);
# These are for the web server (imaging status).
$swebtask->aggregate_urn($aggregate->aggregate_urn());
$swebtask->client_id($node_id);
$swebtask->operation($operation) if (defined($operation));
$swebtask->imagename($imagename);
$instance->SetStatus("imaging");
$aggregate->SetStatus("imaging");
}
#
# This returns pretty fast, and then the imaging takes place in
......@@ -559,11 +584,14 @@ sub DoSnapshot()
#
my $response =
$aggregate->CreateImage($sliver_urn, $imagename,
$update_prepare, $copyback_uuid);
$update_prepare, $copyback_uuid,
undef, $nosnapshot, $mustnotexist);
if (!defined($response)) {
$errmsg = "Internal error creating image";
$instance->SetStatus($old_status);
$aggregate->SetStatus($old_status);
if (!$nosnapshot) {
$instance->SetStatus($old_status);
$aggregate->SetStatus($old_status);
}
goto uerror;
}
if ($response->code() != GENIRESPONSE_SUCCESS) {
......@@ -572,36 +600,59 @@ sub DoSnapshot()
if ($response->code() == GENIRESPONSE_BUSY ||
$response->code() == GENIRESPONSE_SERVER_UNAVAILABLE ||
$response->code() == GENIRESPONSE_FORBIDDEN);
# Important to tell web interface about these.
$errcode = GENIRESPONSE_NOSPACE
if ($response->code() == GENIRESPONSE_NOSPACE);
$instance->SetStatus($old_status);
$aggregate->SetStatus($old_status);
$errcode = GENIRESPONSE_ALREADYEXISTS
if ($response->code() == GENIRESPONSE_ALREADYEXISTS);
if (!$nosnapshot) {
$instance->SetStatus($old_status);
$aggregate->SetStatus($old_status);
}
goto uerror;
}
my ($image_urn, $image_url,
$version_urn, $version_url) = @{ $response->value() };
if (!defined($version_urn)) {
#
# For version zero, kill the version number.
#
if (!defined($version_urn) || $version_urn =~ /:0$/) {
$version_urn = $image_urn;
$version_url = $image_url
}
if (defined($swebtask)) {
$swebtask->image_urn($version_urn);
$swebtask->image_url($version_url);
my $image_name;
my $image_name;
if ($usetracker) {
# DoImageTrackerStuff determined that we use whatever the cluster
# tells us, cause it is the home of the image.
if (!defined($copyback_urn)) {
$image_name = $version_urn;
}
else {
$image_name = $copyback_urn;
}
# When using the image server we use URNs.
if ($usetracker) {
# DoImageTrackerStuff determined that we use whatever the cluster
# tells us, cause it is the home of the image.
if (!defined($copyback_urn)) {
$image_name = $version_urn;
}
else {
$image_name = $version_url;
$image_name = $copyback_urn;
}
}
elsif ($aggregate->OnLocalCluster()) {
$image_name = $version_urn;
}
else {
$image_name = $version_url;
}
if ($nosnapshot) {
if (defined($webtask)) {
$webtask->image_urn($version_urn);
$webtask->image_url($version_url);
$webtask->image_name($image_name);
}
print "$version_urn,$version_url,$image_name\n";
$slice->UnLock();
exit(0);
}
if (defined($swebtask)) {
$swebtask->image_urn($version_urn);
$swebtask->image_url($version_url);
$swebtask->image_name($image_name);
# We tell the web interface that the image has to be copied
......@@ -609,11 +660,8 @@ sub DoSnapshot()
if (defined($copyback_uuid)) {
$swebtask->copyback_uuid($copyback_uuid);
}
print "$version_urn,$version_url,$image_name\n";
}
else {
print "$image_urn,$image_url\n";
}
print "$version_urn,$version_url,$image_name\n";
#
# Exit and leave child to poll.
......
......@@ -184,7 +184,7 @@ my %xmlfields =
$SLOT_UPDATE|$SLOT_ADMINONLY],
"profile_nodelete_all"=> ["nodelete_all", $SLOT_OPTIONAL|$SLOT_MODIFIER,
$SLOT_UPDATE|$SLOT_ADMINONLY],
"rspec" => ["rspec", $SLOT_REQUIRED|$SLOT_UPDATE],
"rspec" => ["rspec", $SLOT_OPTIONAL|$SLOT_UPDATE],
"script" => ["script", $SLOT_OPTIONAL|$SLOT_UPDATE],
"repourl" => ["repourl", $SLOT_OPTIONAL],
"portal_converted" => ["portal_converted", $SLOT_OPTIONAL|$SLOT_UPDATE],
......@@ -223,7 +223,7 @@ else {
#
sub CreateProfile()
{
my $optlist = "s:c:U";
my $optlist = "s:c:Un:";
my $snap = 0;
my $copy = 0;
my $prepare = 0;
......@@ -246,6 +246,9 @@ sub CreateProfile()
if (defined($options{"U"})) {
$prepare = 1;
}
if (defined($options{"n"})) {
$node_id = $options{"n"};
}
}
if (defined($options{"c"})) {
$copy = 1;
......@@ -321,7 +324,7 @@ sub CreateProfile()
}
#
# Are we going to snapshot a node in an experiment? If so we
# sanity check to make sure there is just one node.
# sanity check to make sure that node is in the experiment.
#
if ($snap) {
$instance = APT_Instance->Lookup($copyuuid);
......@@ -331,24 +334,31 @@ sub CreateProfile()
if ($instance->status() ne "ready") {
UserError("Instance must be in the ready state for cloning");
}
if ($instance->AggregateList() != 1) {
UserError("Must be only one aggregate to snapshot");
}
($aggregate) = $instance->AggregateList();
my $manifest = GeniXML::Parse($aggregate->manifest());
if (! defined($manifest)) {
fatal("Could not parse manifest");
}
my @nodes = GeniXML::FindNodes("n:node", $manifest)->get_nodelist();
if (@nodes != 1) {
UserError("Too many nodes (> 1) to snapshot");
foreach my $agg ($instance->AggregateList()) {
my $manifest = GeniXML::Parse($agg->manifest());
if (! defined($manifest)) {
fatal("Could not parse manifest");
}
foreach my $ref (GeniXML::FindNodes("n:node",
$manifest)->get_nodelist()) {
my $client_id = GeniXML::GetVirtualId($ref);
my $manager_urn = GetManagerId($ref);
my $urn = GeniXML::GetSliverId($ref);
# No sliver urn or a different aggregate.
next
if (! (defined($urn) &&
defined($manager_urn) &&
$manager_urn eq $agg->aggregate_urn()));
if ($node_id eq $client_id) {
$aggregate = $agg;
last;
}
}
}
my $sliver_urn = GeniXML::GetSliverId($nodes[0]);
my $manager_urn= GeniXML::GetManagerId($nodes[0]);
$node_id = GeniXML::GetVirtualId($nodes[0]);
if (! (defined($sliver_urn) &&
$manager_urn eq $aggregate->aggregate_urn())) {
UserError("$node_id is not at " . $aggregate->aggregate_urn());
if (!defined($aggregate)) {
UserError("$node_id is not in any of the manifests!");
}
$parent_profile = $instance->Profile();
}
......@@ -430,6 +440,9 @@ sub CreateProfile()
$this_user, $project)) {
$newimage = $iwebtask->image_urn();
}
elsif ($aggregate->OnLocalCluster()) {
$newimage = $iwebtask->image_urn();
}
else {
$newimage = $iwebtask->image_url();
}
......@@ -441,7 +454,8 @@ sub CreateProfile()
# We cannot change a geni-lib script profile, so no need to do this.
# But we can change a portal converted profile (or rspec).
#
if (!defined($profile->script()) || $profile->portal_converted()) {
if (defined($profile->rspec()) &&
(!defined($profile->script()) || $profile->portal_converted())) {
#
# See if anything is actually going to change, since the cluster
# might not be doing image versioning.
......@@ -529,6 +543,31 @@ sub CreateProfile()
# figure out what to tell the user.
$pwebtask->cloning(0);
$profile->Unlock();
#
# Send email to user when this is a new profile with no rspec, to let
# them know they can now fill in a topology.
#
# URL to profile page.
# URN of new image.
#
my $profile_name = $profile->name();
my $profile_pid = $profile->pid();
my $creator = User->Lookup($profile->creator_idx());
my $url = $project->Brand()->wwwBase() .
"/manage_profile.php?action=edit&uuid=$new_uuid";
my $message =
"Your disk image for profile '$profile_name' is now ready for use.\n".
"The URN for your new disk image is:\n\n".
"\t" . $newimage . "\n\n".
"The URL for your profile is:\n\n".
"\t" . $url . "\n\n".
"Click on the link above to edit your new profile.\n";
$project->SendEmail($creator->email(),
"Disk image for profile '$profile_name' is ready",
$message,
$project->Brand()->OpsEmailAddress());
}
else {
$profile->InsertImageRecords();
......
......@@ -160,7 +160,7 @@ window.ShowImagingModal = (function()
$xmlthing.done(callback);
}
return function(s_callback, c_callback)
return function(s_callback, c_callback, nokeyboard)
{
status_callback = s_callback;
completion_callback = c_callback;
......@@ -175,7 +175,8 @@ window.ShowImagingModal = (function()
console.log(json);
var imaging_html = imagingTemplate({
needcopy : _.has(json.value, "copyback_uuid")});
"needcopy" : _.has(json.value, "copyback_uuid"),
"nokeyboard" : nokeyboard});
$('#imaging_div').html(imaging_html);
imaging_modal_display = true;
......
......@@ -148,6 +148,7 @@ $(function ()
manual: window.MANUAL,
copyuuid: (window.COPYUUID || null),
snapuuid: (window.SNAPUUID || null),
snapnode_id: (window.SNAPNODE_ID || null),
general_error: (errors.error || ''),
isapt: window.ISAPT,
disabled: window.DISABLED,
......@@ -1088,10 +1089,8 @@ $(function ()
$('#profile_instructions .textarea').addClass("hidden");
}
// Creating new profile, now we can show the tour/metadata fields.
if (!window.VIEWING) {
$('#tour-text-boxes').removeClass("hidden");
$('#metadata-fields').removeClass("hidden");
}
$('#tour-text-boxes').removeClass("hidden");
$('#metadata-fields').removeClass("hidden");
//
// First time we see the XML, grab step data out of it. But after
......@@ -1184,8 +1183,10 @@ $(function ()
}
else {
EnableButtons();
DisableButton("profile_submit_button");
}
});
},
true);
}
//
......
This diff is collapsed.
......@@ -47,10 +47,14 @@ function Do_Create()
}
$formfields = $ajax_args["formfields"];
# Allow for form precheck only. So JS code knows it will be fast.
$checkonly = isset($ajax_args["checkonly"]) && $ajax_args["checkonly"];
$checkonly = (isset($ajax_args["checkonly"]) &&
$ajax_args["checkonly"] == 1 ? 1 : 0);
if (isset($formfields["snapuuid"]) && $formfields["snapuuid"] != "") {
$snapuuid = $formfields["snapuuid"];
}
if (isset($formfields["snapnode_id"]) && $formfields["snapnode_id"] != "") {
$snapnode_id = $formfields["snapnode_id"];
}
if (isset($formfields["copyuuid"]) && $formfields["copyuuid"] != "") {
$copyuuid = $formfields["copyuuid"];
}
......@@ -103,19 +107,43 @@ function Do_Create()
$errors["profile_${key}"] = TBFieldErrorString();
}
}
if (isset($formfields["profile_rspec"]) &&
$formfields["profile_rspec"] != "") {
if (! TBvalid_rspec($formfields["profile_rspec"])) {
$errors["profile_rspec"] = TBFieldErrorString();
if (isset($formfields["copy-profile"]) &&
$formfields["copy-profile"] != "") {
// We get here via image creation on the status page.
$profile = Profile::Lookup($formfields["copy-profile"]);
if (!$profile) {
$errors["errors"] = "Cannot find source profile";
goto bad;
}
$rspec = $profile->rspec();
if ($profile->script() && $profile->script() != "") {
$script = $profile->script();
}
else {
$rspec = $formfields["profile_rspec"];
if ($profile->portal_converted()) {
$portal_converted = 1;
}
}
else {
# Best place to put the error.
$errors["sourcefile"] = "Missing Field";
if (isset($formfields["profile_rspec"]) &&
$formfields["profile_rspec"] != "") {
if (! TBvalid_rspec($formfields["profile_rspec"])) {
$errors["profile_rspec"] = TBFieldErrorString();
}
else {
$rspec = $formfields["profile_rspec"];
}
}
if (isset($formfields["profile_script"]) &&
$formfields["profile_script"] != "") {
$script = $formfields["profile_script"];
}
if (isset($formfields["portal_converted"]) &&
$formfields["portal_converted"] != "") {
$portal_converted = ($formfields["portal_converted"] == "yes"
? 1 : 0);
}
}
# Present these errors before we call out to do anything else.
if (count($errors)) {
goto bad;
......@@ -174,6 +202,9 @@ function Do_Create()
$instance->profile_version())) {
$errors["error"] = "Cannot load profile for instance!";
}
else if (isset($snapnode_id) && !TBvalid_vnode_id($snapnode_id)) {
$errors["error"] = "Invalid node_id for snapshot!";
}
}
bad:
if (count($errors)) {
......@@ -210,19 +241,18 @@ function Do_Create()
fwrite($fp, " <value>" .
htmlspecialchars($formfields["profile_name"]) . "</value>");
fwrite($fp, "</attribute>\n");
fwrite($fp, "<attribute name='rspec'>");
fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>");
fwrite($fp, "</attribute>\n");
if (isset($formfields["profile_script"]) &&
$formfields["profile_script"] != "") {
if (isset($rspec)) {
fwrite($fp, "<attribute name='rspec'>");
fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>");
fwrite($fp, "</attribute>\n");
}
if (isset($script)) {
fwrite($fp, "<attribute name='script'>");
fwrite($fp, " <value>" .
htmlspecialchars($formfields["profile_script"]) .
"</value>");
htmlspecialchars($script) . "</value>");
fwrite($fp, "</attribute>\n");
if (isset($formfields["portal_converted"]) &&
$formfields["portal_converted"] != "") {
$pcon = ($formfields["portal_converted"] == "yes" ? 1 : 0);
if (isset($portal_converted)) {
$pcon = ($portal_converted ? 1 : 0);
fwrite($fp, "<attribute name='portal_converted'>");
fwrite($fp, " <value>$pcon</value>");
fwrite($fp, "</attribute>\n");
......@@ -333,6 +363,9 @@ function Do_Create()
$formfields["update_prepare"]) {
$command .= " -U ";
}
if (isset($snapnode_id)) {
$command .= " -n " . escapeshellarg($snapnode_id);
}
}
}
$command .= " $xmlname";
......
......@@ -47,9 +47,10 @@ $this_idx = $this_user->uid_idx();
$optargs = OptionalPageArguments("create", PAGEARG_STRING,
"action", PAGEARG_STRING,
"uuid", PAGEARG_STRING,
"fromexp", PAGEARG_STRING,
"fromexp", PAGEARG_STRING,
"copyuuid", PAGEARG_STRING,
"snapuuid", PAGEARG_STRING,
"snapnode_id", PAGEARG_NODEID,
"finished", PAGEARG_BOOLEAN,
"formfields", PAGEARG_ARRAY);
......@@ -59,7 +60,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
function SPITFORM($formfields, $errors)
{
global $this_user, $projlist, $action, $profile, $DEFAULT_AGGREGATE;
global $notifyupdate, $notifyclone, $copyuuid, $snapuuid;
global $notifyupdate, $notifyclone, $copyuuid, $snapuuid, $snapnode_id;
global $ISCLOUD, $fromexp;
global $version_array, $WITHPUBLISHING;
$viewing = 0;
......@@ -213,6 +214,9 @@ function SPITFORM($formfields, $errors)
}
elseif (isset($snapuuid)) {
echo " window.SNAPUUID = '$snapuuid';\n";
if (isset($snapnode_id)) {
echo " window.SNAPNODE_ID = '$snapnode_id';\n";
}
}
if (isset($fromexp)) {
echo " window.EXPUUID = '$fromexp';\n";
......@@ -334,29 +338,35 @@ if (! isset($create)) {
if (! isset($action) || $action == "") {
$action = "create";
}
if (! (isset($projlist) && count($projlist))) {
$errors["error"] =
"You do not appear to be a member of any projects in which ".
"you have permission to create new profiles";
SPITUSERERROR("You do not appear to be a member of any projects in ".
"which you have permission to create new profiles");
}
if (isset($snapuuid)) {
if (!IsValidUUID($snapuuid)) {
SPITUSERERROR("Not a valid UUID for clone");
}
else {
$instance = Instance::Lookup($snapuuid);
if (!$instance) {
SPITUSERERROR("No such instance to clone!");
}
else if ($this_idx != $instance->creator_idx() && !ISADMIN()) {
SPITUSERERROR("Not enough permission!");
}
else if ($instance->status() != "ready") {
SPITUSERERROR("Instance is busy, cannot clone it. " .
"Please try again later.");
}
}
}
if ($action == "edit" || $action == "clone" || $action == "copy") {
if ($action == "clone" || $action == "copy") {
if ($action == "clone") {
if (! (isset($snapuuid) && IsValidUUID($snapuuid))) {
$errors["error"] = "No experiment specified for clone!";
}
$instance = Instance::Lookup($snapuuid);
if (!$instance) {
SPITUSERERROR("No such instance to clone!");
if (! isset($instance)) {
SPITUSERERROR("No experiment specified for clone!");
}
else if ($this_idx != $instance->creator_idx() && !ISADMIN()) {
SPITUSERERROR("Not enough permission!");
}
else if ($instance->status() != "ready") {
SPITUSERERROR("Instance is busy, cannot clone it. " .
"Please try again later.");
}
$profile = Profile::Lookup($instance->profile_id(),
$instance->profile_version());
if (!$profile) {
......@@ -370,25 +380,37 @@ if (! isset($create)) {
# Pass this along through the new create page.
$copyuuid = $profile->uuid();
}
$defaults["profile_rspec"] = $profile->rspec();
$defaults["profile_who"] = "private";
if ($profile->rspec() && $profile->rspec() != "") {
$defaults["profile_rspec"] = $profile->rspec();
}
if ($profile->script() && $profile->script() != "") {
$defaults["profile_script"] = $profile->script();
}
$defaults["portal_converted"]
= ($profile->portal_converted() == 1 ? "yes" : "no");
# Default the project if in only one project.
if (count($projlist) == 1) {
list($project) = each($projlist);
reset($projlist);
$defaults["profile_pid"] = $project;
}
elseif (array_key_exists($profile->pid(), $projlist)) {
#
# Default to same project as the original, *if* the user
# is a member of that project. Convenient.
#
$defaults["profile_pid"] = $profile->pid();
}
}
else {
$defaults["profile_pid"] = $profile->pid();
$defaults["profile_name"] = $profile->name();
$defaults["profile_version"] = $profile->version();
$defaults["profile_rspec"] = $profile->rspec();
if ($profile->rspec() && $profile->rspec() != "") {
$defaults["profile_rspec"] = $profile->rspec();
}
if ($profile->script() && $profile->script() != "") {
$defaults["profile_script"] = $profile->script();
}
......@@ -449,6 +471,14 @@ if (! isset($create)) {
reset($projlist);
$defaults["profile_pid"] = $project;
}
elseif (isset($instance) &&
array_key_exists($instance->pid(), $projlist)) {
#
# Default to same project as the original, *if* the user
# is a member of that project. Convenient.
#
$defaults["profile_pid"] = $instance->pid();
}
$defaults["profile_who"] = "private";
#
......
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -87,7 +87,6 @@ $disabled = ($profile->isDisabled() ? 1 : 0);
$defaults = array();
$defaults["profile_name"] = $profile->name();
$defaults["profile_rspec"] = $profile->rspec();
$defaults["profile_version"] = $profile->version();
$defaults["profile_creator"] = $profile->creator();
$defaults["profile_updater"] = $profile->updater();
......@@ -96,6 +95,9 @@ $defaults["profile_created"] = DateStringGMT($profile->created());
$defaults["profile_published"] = DateStringGMT($profile->published());
$defaults["profile_version_url"] = $profile->URL();
$defaults["profile_profile_url"] = $profile->ProfileURL();
if ($profile->rspec() && $profile->rspec() != "") {
$defaults["profile_rspec"] = $profile->rspec();
}
if ($profile->script() && $profile->script() != "") {
$defaults["profile_script"] = $profile->script();
}
......
......@@ -939,6 +939,19 @@ function Do_Snapshot()
SPITAJAX_ERROR(1, "Experiment is currently busy");
return;
}
if (!isset($ajax_args["operation"]) || $ajax_args["operation"] == "") {
SPITAJAX_ERROR(-1, "Must supply operation!");
return;
}
$operation = $ajax_args["operation"];
if (! ($operation == "update-profile" ||
$operation == "copy-profile" ||
$operation == "new-profile" ||
$operation == "image-only")) {
SPITAJAX_ERROR(-1, "Invalid operation!");
return;
}
#
# The profile also has to belong to the user, since it is
# going to be modified to use the new image.
......@@ -961,22 +974,14 @@ function Do_Snapshot()
return;
}
$optargs .= " -n $node_id ";
if (isset($ajax_args["update_profile"]) &&
$ajax_args["update_profile"]) {
$optargs .= " -u all ";
}
}
else {
#
# Single node profile snapshot, we always update the profile.
#
$optargs .= " -u node ";
}
}
if (isset($ajax_args["update_prepare"]) &&
$ajax_args["update_prepare"]) {
$optargs .= " -U ";
}
if (isset($ajax_args["nosnapshot"]) && $ajax_args["nosnapshot"]) {
$optargs .= " -S ";
}
if (isset($ajax_args["imagename"]) && $ajax_args["imagename"] != "") {
if (!TBvalid_imagename($ajax_args["imagename"])) {
SPITAJAX_ERROR(1, "Not a valid imagename, alphanumeric only");
......@@ -984,7 +989,8 @@ function Do_Snapshot()
}
$optargs .= " -i " . escapeshellarg($ajax_args["imagename"]);
}