Commit e181d3bf authored by Leigh Stoller's avatar Leigh Stoller

Another checkpoint; web UI working now.

parent 9e9db2ea
......@@ -90,6 +90,7 @@ sub DoRefreshInternal($$);
sub DoModify();
sub DoExtend();
sub DoSnapshot();
sub DoSnapShotInternal($$$$$);
#
# Parse command arguments. Once we return from getopts, all that should be
......@@ -177,8 +178,10 @@ sub DoCreate()
my $fstype;
my $read_access;
my $write_access;
# imdataset snapshot info.
my ($instance,$nodeid,$bsname);
my $optlist = "ds:t:e:f:w:p:R:W:a:";
my $optlist = "ds:t:e:f:w:p:R:W:I:i:";
my %options = ();
if (! getopts($optlist, \%options)) {
&$usage();
......@@ -192,13 +195,26 @@ sub DoCreate()
if (! ($type eq "stdataset" || $type eq "ltdataset" ||
$type eq "imdataset"));
}
# We ignore the aggregate urn unless its an imdataset.
if ($type eq "imdataset") {
if (!exists($options{"a"})) {
print STDERR "Must provide -a opton for imdatasets\n";
if (!exists($options{"i"})) {
print STDERR "Must provide -i opton for imdatasets\n";
&$usage();
}
if (!exists($options{"I"})) {
print STDERR "Must provide -I opton for imdatasets\n";
&$usage();
}
$instance = APT_Instance->Lookup($options{"i"});
if (!defined($instance)) {
fatal("Instance does not exist!");
}
$aggregate_urn = $instance->aggregate_urn();
($nodeid,$bsname) = split(",", $options{"I"});
if (! (defined($nodeid) && defined($bsname))) {
print STDERR "Improper -I opton for imdatasets\n";
&$usage();
}
$aggregate_urn = $options{"a"};
}
if (defined($options{"f"})) {
$fstype = $options{"f"};
......@@ -287,7 +303,14 @@ sub DoCreate()
if (!defined($dataset)) {
fatal("Internal error creating dataset object");
}
# new dataset is returned locked.
# new dataset is returned locked. If we have instance, try to lock
# that now, else its a failure.
if ($type eq "imdataset" && defined($instance)) {
if ($instance->Lock()) {
$errmsg = "Instance is busy, cannot snapshot data";
goto failed;
}
}
#
# Ask the aggregate to create the dataset.
......@@ -295,11 +318,14 @@ sub DoCreate()
my $response = $dataset->CreateDataset();
if ($response->code() != GENIRESPONSE_SUCCESS) {
$errmsg = "CreateDataset failed: ". $response->output() . "\n";
$instance->Unlock()
if (defined($instance));
goto failed;
}
$blob = $response->value();
$dataset->Update({"remote_uuid" => $blob->{"uuid"},
"remote_urn" => $blob->{"urn"}});
#
# Okay, this is silly; there is no distinct state for resource allocation.
# It is unapproved and locked. The other side tells us its locked in the
......@@ -311,9 +337,21 @@ sub DoCreate()
}
else {
$dataset->Update({"state" => $blob->{"state"}});
$dataset->Unlock();
return 0;
if ($type ne "imdataset" || !defined($instance)) {
$dataset->Unlock();
return 0;
}
}
#
# Handoff to snapshot if an imdataset.
#
if ($type eq "imdataset" &&
DoSnapShotInternal($dataset, $instance,
$bsname, $nodeid, \$errmsg)) {
$instance->Unlock();
goto failed;
}
#
# If busy, then allocation is in progress. We leave it locked and
# poll in the background for a while, hoping for it to eventually
......@@ -351,7 +389,9 @@ sub DoCreate()
last;
}
}
done:
$dataset->Unlock();
$instance->Unlock() if (defined($instance));
unlink($logfile)
if (defined($logfile));
return 0;
......@@ -451,9 +491,9 @@ sub DoRefreshInternal($$)
}
else {
$dataset->Update({"state" => $blob->{"state"}});
}
if ($dataset->type() eq "imdataset") {
$dataset->Update({"size" => $blob->{"size"}});
if ($dataset->type() eq "imdataset") {
$dataset->Update({"size" => $blob->{"size"}});
}
}
return 0;
}
......@@ -516,7 +556,9 @@ sub DoModify()
goto failed;
}
$blob = $response->value();
$dataset->Update({"expires" => TBDateStringLocal($blob->{"expires"})});
if ($dataset->type() ne "imdataset") {
$dataset->Update({"expires" => TBDateStringLocal($blob->{"expires"})});
}
$dataset->Unlock();
return 0;
......@@ -599,17 +641,15 @@ sub DoSnapshot()
if (!defined($dataset)) {
fatal("No such dataset");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
}
if ($dataset->type() ne "imdataset") {
$errmsg = "Only image backed datasets supported";
goto failed;
fatal("Only image backed datasets supported");
}
my $instance = APT_Instance->Lookup($options{"i"});
if (!defined($instance)) {
$errmsg = "No such instance";
goto failed;
fatal("No such instance");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
}
if ($instance->Lock()) {
# undef so we do not try to unlock it below.
......@@ -617,6 +657,25 @@ sub DoSnapshot()
$errmsg = "instance is busy, cannot lock it";
goto failed
}
if (DoSnapShotInternal($dataset, $instance, $bsname, $nodeid, \$errmsg)) {
goto failed;
}
$instance->Unlock();
$dataset->Unlock();
return 0;
failed:
$instance->Unlock() if (defined($instance));
$dataset->Unlock();
# This will set the webtask, see below.
fatal($errmsg);
}
sub DoSnapShotInternal($$$$$)
{
my ($dataset, $instance, $bsname, $nodeid, $perrmsg) = @_;
my $errmsg;
my $manifest = GeniXML::Parse($instance->manifest());
if (! defined($manifest)) {
$errmsg = "Could not parse manifest";
......@@ -659,17 +718,12 @@ sub DoSnapshot()
goto failed;
}
$dataset->Update({"state" => "busy"});
$instance->Unlock();
$dataset->Unlock();
return 0;
failed:
$instance->Unlock() if (defined($instance));
$dataset->Unlock();
# This will set the webtask, see below.
fatal($errmsg);
$$perrmsg = $errmsg;
return -1;
}
sub fatal($)
{
my ($mesg) = @_;
......
......@@ -3526,6 +3526,7 @@ sub ModifyDataset($)
return $group
if (GeniResponse::IsResponse($group));
my $image;
my $lease = Lease->Lookup($group->pid(), $group->gid(), $dataset);
if (defined($lease)) {
#
......@@ -3538,7 +3539,7 @@ sub ModifyDataset($)
$islease = 1;
}
else {
my $image = Image->Lookup($group->pid(), $dataset);
$image = Image->Lookup($group->pid(), $dataset);
if (!defined($image)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED);
}
......@@ -3600,12 +3601,13 @@ sub ModifyDataset($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef, $output);
}
done:
$lease->Refresh();
my $blob = {};
$blob->{'expires'} = emutil::TBDateStringGMT($lease->lease_end());
$blob->{'state'} = $lease->state();
if ($islease) {
$lease->Refresh();
$blob->{'expires'} = emutil::TBDateStringGMT($lease->lease_end());
$blob->{'state'} = $lease->state();
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
sub DescribeDataset($)
......
......@@ -25,6 +25,7 @@ chdir("..");
include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
include("instance_defs.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Create Dataset";
......@@ -47,7 +48,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
#
function SPITFORM($formfields, $errors)
{
global $this_user, $projlist, $embedded;
global $this_user, $projlist, $embedded, $this_idx;
$button_label = "Create";
$title = "Create Dataset";
......@@ -78,13 +79,21 @@ function SPITFORM($formfields, $errors)
echo "</script>\n";
if (!$embedded) {
$am_array = Instance::DefaultAggregateList();
$amlist = array();
while (list($am) = each($am_array)) {
$amlist[] = $am;
$query_result =
DBQueryFatal("select uuid from apt_instances as a ".
"where creator_idx='$this_idx'");
$instance_array = array();
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$instance = Instance::Lookup($uuid);
$profile = Profile::Lookup($instance->profile_id(),
$instance->profile_version());
$instance_array[] =
array("uuid" => $uuid, "name" => $profile->name());
}
echo "<script type='text/plain' id='amlist-json'>\n";
echo htmlentities(json_encode($amlist));
echo "<script type='text/plain' id='instances-json'>\n";
echo htmlentities(json_encode($instance_array));
echo "</script>\n";
}
......
This diff is collapsed.
......@@ -28,6 +28,8 @@ include("imageid_defs.php");
chdir("apt");
include("quickvm_sup.php");
include("dataset_defs.php");
include("instance_defs.php");
include("profile_defs.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Modify Dataset";
......@@ -69,7 +71,7 @@ if (!$dataset->AccessCheck($this_user, $LEASE_ACCESS_MODIFY)) {
#
function SPITFORM($formfields, $errors)
{
global $this_user, $projlist, $embedded;
global $this_user, $projlist, $embedded, $this_idx;
$button_label = "Save";
$title = "Modify Dataset";
$isadmin = (ISADMIN() ? "true" : "false");
......@@ -86,6 +88,25 @@ function SPITFORM($formfields, $errors)
echo "<script type='text/plain' id='error-json'>\n";
echo htmlentities(json_encode($errors));
echo "</script>\n";
if (!$embedded) {
$query_result =
DBQueryFatal("select uuid from apt_instances as a ".
"where creator_idx='$this_idx'");
$instance_array = array();
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$instance = Instance::Lookup($uuid);
$profile = Profile::Lookup($instance->profile_id(),
$instance->profile_version());
$instance_array[] =
array("uuid" => $uuid, "name" => $profile->name());
}
echo "<script type='text/plain' id='instances-json'>\n";
echo htmlentities(json_encode($instance_array));
echo "</script>\n";
}
echo "<link rel='stylesheet'
href='css/jquery-ui.min.css'>\n";
......
......@@ -11,7 +11,7 @@ function (_, sup, moment, mainString, helpString)
var fields = null;
var fstypes = null;
var projlist = null;
var amlist = null;
var instances = null;
var editing = false;
var isadmin = false;
var embedded = 0;
......@@ -29,8 +29,9 @@ function (_, sup, moment, mainString, helpString)
projlist =
JSON.parse(_.unescape($('#projects-json')[0].textContent));
}
if (! (editing || embedded)) {
amlist = JSON.parse(_.unescape($('#amlist-json')[0].textContent));
if (!embedded) {
instances =
JSON.parse(_.unescape($('#instances-json')[0].textContent));
}
GeneratePageBody(fields, null);
}
......@@ -46,7 +47,7 @@ function (_, sup, moment, mainString, helpString)
formfields: formfields,
fstypes: fstypes,
projects: projlist,
amlist: amlist,
instancelist: instances,
title: window.TITLE,
embedded: window.EMBEDDED,
editing: editing,
......@@ -138,6 +139,15 @@ function (_, sup, moment, mainString, helpString)
.html("project " + $('#dataset_pid option:selected').val());
}
}
// Handler for instance change.
if (instances) {
$('#dataset_instance').change(function (event) {
$("#dataset_instance option:selected" ).each(function() {
HandleInstanceChange($(this).val());
return;
});
});
}
//
// Handle submit button.
......@@ -260,6 +270,40 @@ function (_, sup, moment, mainString, helpString)
xmlthing.done(callback);
}
/*
* When instance changes, need to get the manifest and find the
* a node with a blockstore to offer the user.
*/
function HandleInstanceChange(uuid)
{
var EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1";
var callback = function(json) {
var xmlDoc = $.parseXML(json.value);
var xml = $(xmlDoc);
$(xml).find("node").each(function() {
var node = $(this).attr("client_id");
var bsref = this.getElementsByTagNameNS(EMULAB_NS,'blockstore');
if (bsref.length) {
var bsname = $(bsref).attr("name");
var bsclass = $(bsref).attr("class");
if (bsclass == "local") {
$('#dataset_node').val(node);
$('#dataset_bsname').val(bsname);
return;
}
}
});
};
var xmlthing = sup.CallServerMethod(null, "status",
"GetInstanceManifest",
{"uuid" : uuid});
xmlthing.done(callback);
}
$(document).ready(initialize);
});
......
......@@ -78,7 +78,42 @@ function (_, sup, moment, mainString)
event.preventDefault();
ExtendDataset();
});
/*
* If the state is busy, then lets poll watching for it to
* go valid.
*/
if (fields.dataset_state == "busy" ||
fields.dataset_state == "allocating") {
StateWatch();
}
}
// Periodically ask the server for the status.
function StateWatch()
{
var callback = function(json) {
console.info(json);
if (json.code) {
sup.SpitOops("oops", json.value);
return;
}
if (! (json.value.state == "busy" ||
json.value.state == "allocating")) {
window.location.reload(true);
return;
}
if (json.size) {
$('#dataset_size').html(json.size);
}
setTimeout(function f() { StateWatch() }, 5000);
}
var xmlthing = sup.CallServerMethod(null, "dataset",
"getinfo",
{"uuid" : dataset_uuid});
xmlthing.done(callback);
}
//
// Delete dataset.
//
......
......@@ -113,7 +113,9 @@ $routing = array("myprofiles" =>
"approve" =>
"Do_ApproveDataset",
"extend" =>
"Do_ExtendDataset")),
"Do_ExtendDataset",
"getinfo" =>
"Do_GetInfo")),
"ssh-keys" =>
array("file" => "ssh-keys.ajax",
"guest" => false,
......
......@@ -106,31 +106,32 @@
<div id='dataset_imageonly_div'
<% if (formfields.dataset_type != "imdataset") {
%> class='hidden' <% } %> >
<% if (!editing && amlist) { %>
<select name="dataset_am"
id="dataset_am"
<% if (instancelist) { %>
<select name="dataset_instance"
id="dataset_instance"
class='form-control format-me'
data-key="dataset_am"
data-label="Aggregate"
data-key="dataset_instance"
data-label="Instance"
placeholder='Please Select'>
<% _.each(amlist, function(name) { %>
<option value=''>Please Select</option>
<% _.each(instancelist, function(instance) { %>
<option
<% if (name == formfields.dataset_am) { %>
<% if (instance.uuid == formfields.dataset_instance) { %>
selected
<% } %>
value='<%= name %>'><%= name %>
value='<%= instance.uuid %>'><%= instance.name %>
</option>
<% }); %>
</select>
<% } %>
<input name="dataset_node"
<input name="dataset_node" id='dataset_node'
value="<%- formfields.dataset_node %>"
class="form-control format-me"
data-key="dataset_node"
data-label="Node"
placeholder="Node to snapshot dataset from"
type="text">
<input name="dataset_bsname"
<input name="dataset_bsname" id='dataset_bsname'
value="<%- formfields.dataset_bsname %>"
class="form-control format-me"
data-key="dataset_bsname"
......
......@@ -33,7 +33,8 @@
</tr>
<tr>
<td>Size</td>
<td><%- formfields.dataset_size %>MiB</td>
<td><span id='dataset_size'>
<%- formfields.dataset_size %></span>MiB</td>
</tr>
<tr>
<td>Type</td>
......@@ -130,12 +131,14 @@
href='edit-dataset.php?uuid=<%- formfields.dataset_uuid %>'
type='button'>Modify
</a>
<% if (formfields.dataset_type != "imdataset") { %>
<a class='btn btn-primary btn-sm'
id='dataset_extend_button'
style='margin-right: 10px;'
data-toggle='modal' data-target='#extend_modal'
type='button'>Extend
</a>
<% } %>
<% } %>
<a type=button class='btn btn-info btn-sm'
id='embedded-anchors'
......
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