Commit 0399cd8e authored by Leigh Stoller's avatar Leigh Stoller

The rest of the snapshot node changes.

parent c1d3f663
...@@ -682,18 +682,91 @@ sub Unlock($) ...@@ -682,18 +682,91 @@ sub Unlock($)
} }
# #
# Update the disk image inside a single node profile. # Update the disk image inside a profile. We update the URL for the
# specified node, and if $all is set, we change all nodes with the
# same original disk image as the specified node.
# #
sub UpdateDiskImage($$) sub UpdateDiskImage($$@)
{ {
my ($self, $image_url) = @_; my ($self, $node_id, $image_url, $all) = @_;
my $rspec = GeniXML::Parse($self->rspec()); my $rspec = GeniXML::Parse($self->rspec());
if (! defined($rspec)) { if (! defined($rspec)) {
print STDERR "UpdateDiskImage: Could not parse rspec\n"; print STDERR "UpdateDiskImage: Could not parse rspec\n";
return -1; return -1;
} }
my ($node) = GeniXML::FindNodes("n:node", $rspec)->get_nodelist(); #
GeniXML::SetDiskImage($node, $image_url); # Find all the nodes we want to update, might be just the one or
# all with the same image.
#
my @nodes = ();
my $node;
# First find the specified node.
foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) {
if (GeniXML::GetVirtualId($ref) eq $node_id) {
$node = $ref;
last;
}
}
if (!defined($node)) {
print STDERR "$node_id not in rspec\n";
return -1;
}
if ($all) {
#
# Pull out the disk url/urn of the specified node.
#
my $diskref = GeniXML::GetDiskImage($node);
my $image_urn;
my $image_url;
if (defined($diskref)) {
$image_url = GeniXML::GetText("url", $diskref);
$image_urn = GeniXML::GetText("name", $diskref);
if (defined($image_url) || defined($image_urn)) {
# Watch for url in the name, flipflop.
if (defined($image_urn) && $image_urn =~ /^http/) {
$image_url = $image_urn;
$image_urn = undef;
}
}
}
#
# Now find all nodes using the same disk urn/url and change.
#
foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) {
my $diskref = GeniXML::GetDiskImage($ref);
next
if (!defined($diskref));
my $this_url = GeniXML::GetText("url", $diskref);
my $this_urn = GeniXML::GetText("name", $diskref);
next
if (!(defined($image_url) || defined($image_urn)));
# Watch for url in the name, flipflop.
if (defined($this_urn) && $this_urn =~ /^http/) {
$this_url = $this_urn;
$this_urn = undef;
}
if (defined($image_url)) {
push(@nodes, $ref)
if (defined($this_url) && $this_url eq $image_url);
}
else {
push(@nodes, $ref)
if (defined($this_urn) && $this_urn eq $image_urn);
}
}
}
else {
@nodes = ($node);
}
if (!@nodes) {
print STDERR "Could not find any nodes to update disk image\n";
return -1;
}
foreach my $node (@nodes) {
GeniXML::SetDiskImage($node, $image_url);
}
if ($self->UpdateVersion({"rspec" => GeniXML::Serialize($rspec)})) { if ($self->UpdateVersion({"rspec" => GeniXML::Serialize($rspec)})) {
print STDERR "UpdateDiskImage: Could not update rspec\n"; print STDERR "UpdateDiskImage: Could not update rspec\n";
return -1; return -1;
......
...@@ -35,7 +35,8 @@ use POSIX qw(setsid close); ...@@ -35,7 +35,8 @@ use POSIX qw(setsid close);
# #
sub usage() sub usage()
{ {
print("Usage: manage_instance snapshot instance [imagename node_id]\n"); print("Usage: manage_instance snapshot ".
"[-s node_id] [-i imagename] [-u node|all] instance\n");
print("Usage: manage_instance consoleurl instance node\n"); print("Usage: manage_instance consoleurl instance node\n");
print("Usage: manage_instance extend instance seconds\n"); print("Usage: manage_instance extend instance seconds\n");
print("Usage: manage_instance terminate instance\n"); print("Usage: manage_instance terminate instance\n");
...@@ -59,7 +60,7 @@ my $TBOPS = "@TBOPSEMAIL@"; ...@@ -59,7 +60,7 @@ my $TBOPS = "@TBOPSEMAIL@";
my $QUICKVM = "$TB/sbin/protogeni/quickvm"; my $QUICKVM = "$TB/sbin/protogeni/quickvm";
# Debugging # Debugging
my $usemydevtree = 0; my $usemydevtree = 1;
# #
# Untaint the path # Untaint the path
...@@ -174,6 +175,27 @@ sub DoSnapshot() ...@@ -174,6 +175,27 @@ sub DoSnapshot()
my $errcode = 1; my $errcode = 1;
my $needunlock = 0; my $needunlock = 0;
my $old_status = $instance->status(); my $old_status = $instance->status();
my $node_id;
my $imagename;
my $update_profile;
my $optlist = "n:i:u:";
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"n"})) {
$node_id = $options{"n"};
}
if (defined($options{"i"})) {
$imagename = $options{"i"};
}
if (defined($options{"u"})) {
$update_profile = $options{"u"};
if ($update_profile !~ /^(node|all)$/) {
usage();
}
}
if ($old_status ne "ready") { if ($old_status ne "ready") {
fatal("Instance must be in the ready state to take a snapshot"); fatal("Instance must be in the ready state to take a snapshot");
...@@ -196,33 +218,27 @@ sub DoSnapshot() ...@@ -196,33 +218,27 @@ sub DoSnapshot()
} }
# #
# If we get an imagename on the command line, the caller is # Might be a clone (manage_profile).
# saying it is responsible (clone). If we do not get one, we
# create the name and update the underlying profile with the new
# image urn.
# #
my $project;
my $imagename;
my $node_id;
my $sliver_urn; my $sliver_urn;
my $update_profile = 0;
my $profile = APT_Profile->Lookup($instance->profile_id()); my $profile = APT_Profile->Lookup($instance->profile_id());
if (!defined($profile)) { if (!defined($profile)) {
fatal("Could not lookup profile for instance"); fatal("Could not lookup profile for instance");
} }
if (@ARGV) { my $project = Project->Lookup($profile->pid_idx());
$imagename = shift(@ARGV); if (!defined($project)) {
if (@ARGV) { fatal("Could not lookup project for profile");
$node_id = shift(@ARGV); }
if (defined($node_id)) {
if (!defined($imagename)) {
$imagename = $profile->name() . "." . $node_id;
} }
} }
else { else {
$imagename = $profile->name(); if (!defined($imagename)) {
$update_profile = 1; $imagename = $profile->name();
$project = Project->Lookup($profile->pid_idx());
if (!defined($project)) {
fatal("Could not lookup project for profile");
} }
} }
...@@ -236,6 +252,7 @@ sub DoSnapshot() ...@@ -236,6 +252,7 @@ sub DoSnapshot()
my $node; my $node;
my @nodes = GeniXML::FindNodes("n:node", $manifest)->get_nodelist(); my @nodes = GeniXML::FindNodes("n:node", $manifest)->get_nodelist();
if (!defined($node_id)) { if (!defined($node_id)) {
# We snapshot the one node in the instance.
if (@nodes != 1) { if (@nodes != 1) {
fatal("Too many nodes (> 1) to snapshot"); fatal("Too many nodes (> 1) to snapshot");
} }
...@@ -243,10 +260,11 @@ sub DoSnapshot() ...@@ -243,10 +260,11 @@ sub DoSnapshot()
$sliver_urn = GeniXML::GetSliverId($node); $sliver_urn = GeniXML::GetSliverId($node);
} }
else { else {
foreach $node (@nodes) { foreach my $ref (@nodes) {
my $client_id = GeniXML::GetVirtualId($node); my $client_id = GeniXML::GetVirtualId($ref);
if ($node_id eq $client_id) { if ($node_id eq $client_id) {
$sliver_urn = GeniXML::GetSliverId($node); $sliver_urn = GeniXML::GetSliverId($ref);
$node = $ref;
last; last;
} }
} }
...@@ -256,12 +274,11 @@ sub DoSnapshot() ...@@ -256,12 +274,11 @@ sub DoSnapshot()
} }
# #
# Really, a snapshot and not a clone. We are not going to allow this # We are not going to allow this if the instance is on a different
# if the instance in on a different cluster then where the image was # cluster then where the image was originally created, since otherwise
# originally created, since otherwise the image provenance will look # the image provenancewill look like spaghetti.
# like spaghetti.
# #
if ($update_profile) { if (defined($update_profile)) {
my $diskref = GeniXML::GetDiskImage($node); my $diskref = GeniXML::GetDiskImage($node);
if (defined($diskref)) { if (defined($diskref)) {
my $image_url = GeniXML::GetText("url", $diskref); my $image_url = GeniXML::GetText("url", $diskref);
...@@ -476,7 +493,7 @@ sub DoSnapshot() ...@@ -476,7 +493,7 @@ sub DoSnapshot()
$errcode = 60; $errcode = 60;
goto bad; goto bad;
} }
elsif ($update_profile) { elsif (defined($update_profile)) {
# #
# If successful, we create a new version of the profile and # If successful, we create a new version of the profile and
# update the rspec to reflect the new image version. Note # update the rspec to reflect the new image version. Note
...@@ -496,7 +513,8 @@ sub DoSnapshot() ...@@ -496,7 +513,8 @@ sub DoSnapshot()
exit(1); exit(1);
} }
} }
$profile->UpdateDiskImage($version_url); $profile->UpdateDiskImage($node_id, $version_url,
($update_profile eq "all" ? 1 : 0));
} }
$instance->SetStatus("ready"); $instance->SetStatus("ready");
# We garbage collect these later, so anyone waiting has a chance # We garbage collect these later, so anyone waiting has a chance
......
...@@ -433,8 +433,8 @@ if (defined($instance)) { ...@@ -433,8 +433,8 @@ if (defined($instance)) {
fatal("Could not lock new profile"); fatal("Could not lock new profile");
} }
my $command = "$MANAGEINSTANCE -t " . $webtask->task_id() . " ". my $command = "$MANAGEINSTANCE -t " . $webtask->task_id() . " -- ".
"snapshot $apt_uuid $imagename $node_id"; "snapshot -s $node_id -i $imagename $apt_uuid";
# #
# This returns pretty fast, and then the imaging takes place in # This returns pretty fast, and then the imaging takes place in
...@@ -456,7 +456,7 @@ if (defined($instance)) { ...@@ -456,7 +456,7 @@ if (defined($instance)) {
$webtask->Refresh(); $webtask->Refresh();
my $image_url = $webtask->image_url(); my $image_url = $webtask->image_url();
if (!defined($image_url) || if (!defined($image_url) ||
$profile->UpdateDiskImage($image_url)) { $profile->UpdateDiskImage($node_id, $image_url, 0)) {
$webtask->Delete() $webtask->Delete()
if (!defined($webtask_id)); if (!defined($webtask_id));
$profile->Delete(1); $profile->Delete(1);
......
...@@ -954,7 +954,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -954,7 +954,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
" <li><a href='#' name='console'>Console</a></li> " + " <li><a href='#' name='console'>Console</a></li> " +
" <li><a href='#' name='reboot'>Reboot</a></li> " + " <li><a href='#' name='reboot'>Reboot</a></li> " +
" <li><a href='#' name='reload'>Reload</a></li> " + " <li><a href='#' name='reload'>Reload</a></li> " +
" <li class=hidden><a href='#' name='snapshot'>Snapshot</a></li> " + " <li><a href='#' name='snapshot'>Snapshot</a></li> " +
" </ul>" + " </ul>" +
" </div>" + " </div>" +
" </td>" + " </td>" +
......
...@@ -35,7 +35,7 @@ $creator = null; ...@@ -35,7 +35,7 @@ $creator = null;
# #
# Locate the objects and check permission. # Locate the objects and check permission.
# #
function StatusSetupAjax() function StatusSetupAjax($needmodify)
{ {
global $this_user, $ajax_args; global $this_user, $ajax_args;
global $instance, $creator; global $instance, $creator;
...@@ -58,17 +58,24 @@ function StatusSetupAjax() ...@@ -58,17 +58,24 @@ function StatusSetupAjax()
SPITAJAX_ERROR(1, "no such instance creator"); SPITAJAX_ERROR(1, "no such instance creator");
return 1; return 1;
} }
# Admin users do whatever the like. # Admin users do whatever they like.
if (isset($this_user) && ISADMIN()) { if (isset($this_user) && ISADMIN()) {
return 0; return 0;
} }
# An experiment created by a real user, can be accessed by that user only. # For a guest user; must be the same guest that created experiment.
# Ditto a guest user; must be the same guest. if (get_class($creator) == "GeniUser") {
if (! ((get_class($creator) == "User" && if (isset($_COOKIE['quickvm_user']) &&
isset($this_user) && $creator->uuid() == $this_user->uuid()) || $_COOKIE['quickvm_user'] == $creator->uuid()) {
(get_class($creator) == "GeniUser" && return 0;
isset($_COOKIE['quickvm_user']) && }
$_COOKIE['quickvm_user'] == $creator->uuid()))) { SPITAJAX_ERROR(1, "You do not have permission!");
return 1;
}
# An experiment created by a real user, can be accessed by other
# members of the project, subject to modify restrictions.
if (! (isset($this_user) && get_class($creator) == "User" &&
$instance->CanView($this_user) &&
(!$needmodify || $instance->CanModify($this_user)))) {
SPITAJAX_ERROR(1, "You do not have permission!"); SPITAJAX_ERROR(1, "You do not have permission!");
return 1; return 1;
} }
...@@ -82,7 +89,7 @@ function Do_GetInstanceStatus() ...@@ -82,7 +89,7 @@ function Do_GetInstanceStatus()
{ {
global $instance, $creator; global $instance, $creator;
if (StatusSetupAjax()) { if (StatusSetupAjax(0)) {
return; return;
} }
$blob = array(); $blob = array();
...@@ -121,7 +128,7 @@ function Do_TerminateInstance() ...@@ -121,7 +128,7 @@ function Do_TerminateInstance()
{ {
global $instance, $creator, $this_user, $ajax_args; global $instance, $creator, $this_user, $ajax_args;
if (StatusSetupAjax()) { if (StatusSetupAjax(1)) {
return; return;
} }
$uuid = $instance->uuid(); $uuid = $instance->uuid();
...@@ -187,7 +194,7 @@ function Do_GetInstanceManifest() ...@@ -187,7 +194,7 @@ function Do_GetInstanceManifest()
{ {
global $instance, $creator; global $instance, $creator;
if (StatusSetupAjax()) { if (StatusSetupAjax(0)) {
return; return;
} }
...@@ -208,7 +215,7 @@ function Do_GetSSHAuthObject() ...@@ -208,7 +215,7 @@ function Do_GetSSHAuthObject()
} }
$hostport = $ajax_args["hostport"]; $hostport = $ajax_args["hostport"];
if (StatusSetupAjax()) { if (StatusSetupAjax(1)) {
return; return;
} }
# #
...@@ -245,7 +252,7 @@ function Do_RequestExtension() ...@@ -245,7 +252,7 @@ function Do_RequestExtension()
$needapproval = 0; $needapproval = 0;
$granted = 0; $granted = 0;
if (StatusSetupAjax()) { if (StatusSetupAjax(1)) {
goto bad; goto bad;
} }
$uuid = $instance->uuid(); $uuid = $instance->uuid();
...@@ -477,7 +484,7 @@ function Do_ConsoleURL() ...@@ -477,7 +484,7 @@ function Do_ConsoleURL()
} }
$node = $ajax_args["node"]; $node = $ajax_args["node"];
if (StatusSetupAjax()) { if (StatusSetupAjax(1)) {
return; return;
} }
$uuid = $instance->uuid(); $uuid = $instance->uuid();
...@@ -562,7 +569,7 @@ function Do_Snapshot() ...@@ -562,7 +569,7 @@ function Do_Snapshot()
SPITAJAX_ERROR(1, "Bad node id"); SPITAJAX_ERROR(1, "Bad node id");
return; return;
} }
$optargs .= " -s $node_id "; $optargs .= " -n $node_id ";
if (isset($ajax_args["update_profile"]) && if (isset($ajax_args["update_profile"]) &&
$ajax_args["update_profile"]) { $ajax_args["update_profile"]) {
...@@ -576,7 +583,7 @@ function Do_Snapshot() ...@@ -576,7 +583,7 @@ function Do_Snapshot()
$webtask_id = WebTask::GenerateID(); $webtask_id = WebTask::GenerateID();
$retval = SUEXEC($this_user->uid(), "nobody", $retval = SUEXEC($this_user->uid(), "nobody",
"webmanage_instance -t $webtask_id -- ". "webmanage_instance -t $webtask_id -- ".
" snapshot $optargs $uuid", " snapshot $uuid $optargs",
SUEXEC_ACTION_IGNORE); SUEXEC_ACTION_IGNORE);
$webtask = WebTask::Lookup($webtask_id); $webtask = WebTask::Lookup($webtask_id);
......
...@@ -97,8 +97,7 @@ if (!$creator) { ...@@ -97,8 +97,7 @@ if (!$creator) {
if (! (isset($this_user) && ISADMIN())) { if (! (isset($this_user) && ISADMIN())) {
# An experiment created by a real user, can be accessed by that user only. # An experiment created by a real user, can be accessed by that user only.
# Ditto a guest user; must be the same guest. # Ditto a guest user; must be the same guest.
if (! ((get_class($creator) == "User" && if (! ((get_class($creator) == "User" && $instance->CanView($this_user)) ||
isset($this_user) && $creator->uuid() == $this_user->uuid()) ||
(get_class($creator) == "GeniUser" && (get_class($creator) == "GeniUser" &&
isset($_COOKIE['quickvm_user']) && isset($_COOKIE['quickvm_user']) &&
$_COOKIE['quickvm_user'] == $creator->uuid()))) { $_COOKIE['quickvm_user'] == $creator->uuid()))) {
......
...@@ -78,7 +78,13 @@ ...@@ -78,7 +78,13 @@
</a> </a>
</div> </div>
<% if (isadmin) { %> <% if (isadmin) { %>
<div class='pull-left'> <div class='pull-left'
data-toggle='popover'
data-delay='{"hide":1500, "show":500}'
data-html='true'
data-content="When checked, only administrator can extend
this experiment. No free time is granted to
user at all">
<label class="checkbox-inline" style='margin-right: 10px;'> <label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="lockout_checkbox" <input type="checkbox" id="lockout_checkbox"
<% if (lockout) { %>checked<% } %> >Lockout</label> <% if (lockout) { %>checked<% } %> >Lockout</label>
...@@ -247,7 +253,8 @@ ...@@ -247,7 +253,8 @@
Check this box if you want us to update your profile to use the Check this box if you want us to update your profile to use the
new disk image. All nodes running the same image will be new disk image. All nodes running the same image will be
updated. If you uncheck the box, you will need to modify the updated. If you uncheck the box, you will need to modify the
profile source code yourself. profile source code yourself (you will receive email with the
name/url of the new image).
<br> <br>
</div> </div>
<div id='wholedisk_div' class='hidden'> <div id='wholedisk_div' class='hidden'>
...@@ -281,7 +288,7 @@ ...@@ -281,7 +288,7 @@
<li><a tabindex="-1">Console</a></li> <li><a tabindex="-1">Console</a></li>
<li><a tabindex="-1">Reboot</a></li> <li><a tabindex="-1">Reboot</a></li>
<li><a tabindex="-1">Reload</a></li> <li><a tabindex="-1">Reload</a></li>
<li class=hidden><a tabindex="-1">Snapshot</a></li> <li><a tabindex="-1">Snapshot</a></li>
</ul> </ul>
</div> </div>
</div> </div>
......
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