Commit e181d3bf authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

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";
}
......
......@@ -130,64 +130,72 @@ function Do_CreateDataSet()
}
else {
#
# Node and BS name are optional, but the aggregate is not.
# Not going to allow descriptor only image datasets, must
# provide the node/bsname, and the instance if on the APT path.
#
if (! (isset($formfields["dataset_am"]) &&
$formfields["dataset_am"] != "")) {
$errors["dataset_am"] = "Must select an aggregate";
if (! (isset($formfields["dataset_node"]) &&
$formfields["dataset_node"] != "")) {
$errors["dataset_node"] = "Must provide a node";
}
$am_array = Instance::DefaultAggregateList();
if (array_key_exists($formfields["dataset_am"], $am_array)) {
$command .= " -a " . $am_array[$formfields["dataset_am"]];
}
else {
$errors["dataset_am"] = "Invalid aggregate";
}
if ((isset($formfields["dataset_node"]) &&
$formfields["dataset_node"] != "") ||
(isset($formfields["dataset_bsname"]) &&
$formfields["dataset_bsname"] != "")) {
if (! (isset($formfields["dataset_node"]) &&
$formfields["dataset_node"] != "")) {
$errors["dataset_node"] = "Must also provide a node";
elseif (! (isset($formfields["dataset_bsname"]) &&
$formfields["dataset_bsname"] != "")) {
$errors["dataset_bsname"] = "Must provide bsname";
}
else {
$nodeid = $formfields["dataset_node"];
$bsname = $formfields["dataset_bsname"];
if (!TBvalid_node_id($nodeid)) {
$errors["dataset_node"] = TBFieldErrorString();
}
elseif (! (isset($formfields["dataset_bsname"]) &&
$formfields["dataset_bsname"] != "")) {
$errors["dataset_bsname"] = "Must also provide bsname";
elseif (!TBvalid_vnode_id($bsname)) {
$errors["dataset_bsname"] = TBFieldErrorString();
}
else {
$nodeid = $formfields["dataset_node"];
$bsname = $formfields["dataset_bsname"];
if (!TBvalid_node_id($nodeid)) {
$errors["dataset_node"] = TBFieldErrorString();
}
elseif (!TBvalid_vnode_id($bsname)) {
$errors["dataset_bsname"] = TBFieldErrorString();
}
elseif ($node = Node::Lookup($nodeid)) {
$reservation = $node->Reservation();
if (!$reservation ||
!$reservation->pid() == $pid) {
$errors["dataset_node"] =
"Node not reserved to an experiment in the same ".
"project as your dataset";
}
else {
$blockstore = $reservation->LookupBlockstore($bsname);
if (!$blockstore) {
$errors["dataset_bsname"] = "No such blockstore";
if (!count($errors)) {
if ($embedded) {
if ($node = Node::Lookup($nodeid)) {
$reservation = $node->Reservation();
if (!$reservation ||
!$reservation->pid() == $pid) {
$errors["dataset_node"] =
"Node not reserved to an experiment in the ".
"same project as your dataset";
}
elseif ($blockstore['fixed'] != $node->VirtName()) {
$errors["dataset_bsname"] =
"Blockstore does not exist on node $nodeid";
else {
$blockstore =
$reservation->LookupBlockstore($bsname);
if (!$blockstore) {
$errors["dataset_bsname"] =
"No such blockstore";
}
elseif ($blockstore['fixed'] != $node->VirtName()) {
$errors["dataset_bsname"] =
"Blockstore does not exist on node $nodeid";
}
}
}
$command .= " -I ${nodeid},${bsname}";
else {
$errors["dataset_node"] = "No such node";
}
}
else {
$errors["dataset_node"] = "No such node";
if (! (isset($formfields["dataset_instance"]) &&
$formfields["dataset_instance"] != "")) {
$errors["dataset_instance"] = "Must provide instance";
}
elseif (!preg_match("/^\w+\-\w+\-\w+\-\w+\-\w+$/",
$formfields["dataset_instance"])) {
$errors["dataset_instance"] = "Invalid instance uuid";
}
elseif ($instance =
Instance::Lookup($formfields["dataset_instance"])){
$command .= " -i " . $instance->uuid();
}
else {
$errors["dataset_instance"] = "No such instance";
}
}
$command .= " -I ${nodeid},${bsname}";
}
}
}
......@@ -290,9 +298,6 @@ function Do_ModifyDataSet()
SPITAJAX_ERROR(1, "Not enough permission");
return;
}
#
# All we can do on this path is change the permission bits,
#
if (isset($formfields["dataset_read"])) {
if ($formfields["dataset_read"] != "global" &&
$formfields["dataset_read"] != "project") {
......@@ -332,25 +337,47 @@ function Do_ModifyDataSet()
elseif (!TBvalid_vnode_id($bsname)) {
$errors["dataset_bsname"] = TBFieldErrorString();
}
elseif ($node = Node::Lookup($nodeid)) {
$reservation = $node->Reservation();
if (!$reservation ||
$reservation->pid() != $dataset->pid()) {
$errors["dataset_node"] =
"Node not reserved to an experiment in the same ".
"project as your dataset";
}
if (!count($errors)) {
if ($embedded) {
if ($node = Node::Lookup($nodeid)) {
$reservation = $node->Reservation();
if (!$reservation ||
$reservation->pid() != $dataset->pid()) {
$errors["dataset_node"] =
"Node not reserved to an experiment in the ".
"same project as your dataset";
}
else {
$blockstore =
$reservation->LookupBlockstore($bsname);
if (!$blockstore ||
$blockstore['fixed'] != $node->VirtName()) {
$errors["dataset_bsname"] =
"Blockstore does not exist on node $nodeid";
}
}
}
else {
$blockstore = $reservation->LookupBlockstore($bsname);
if (!$blockstore ||
$blockstore['fixed'] != $node->VirtName()) {
$errors["dataset_bsname"] =
"Blockstore does not exist on node $nodeid";
}
$errors["dataset_node"] = "No such node";
}
}
else {
$errors["dataset_node"] = "No such node";
if (! (isset($formfields["dataset_instance"]) &&
$formfields["dataset_instance"] != "")) {
$errors["dataset_instance"] = "Must provide instance";
}
elseif (!preg_match("/^\w+\-\w+\-\w+\-\w+\-\w+$/",
$formfields["dataset_instance"])) {
$errors["dataset_instance"] = "Invalid instance uuid";
}
elseif ($instance =
Instance::Lookup($formfields["dataset_instance"])){
$instance_uuid = $instance->uuid();
}
else {
$errors["dataset_instance"] = "No such instance";
}
}
}
}
......@@ -391,11 +418,19 @@ function Do_ModifyDataSet()
$safe_bsname = escapeshellarg($bsname);
$dname = $dataset->id();
$pid = $dataset->pid();
$retval = SUEXEC($this_uid, $dataset->pid(),
"webcreate_image -b $safe_bsname -p $pid ".
"$dname $safe_nodeid",
SUEXEC_ACTION_CONTINUE);
if ($embedded) {
$retval = SUEXEC($this_uid, $dataset->pid(),
"webcreate_image -b $safe_bsname -p $pid ".
"$dname $safe_nodeid",
SUEXEC_ACTION_CONTINUE);
}
else {
$retval = SUEXEC($this_uid, $dataset->pid(),
"webmanage_dataset snapshot -i $instance_uuid ".
" -b $safe_bsname $pid/$dname $safe_nodeid",
SUEXEC_ACTION_CONTINUE);
}
if ($retval) {
SPITAJAX_ERROR(1, $suexec_output);
return;
......@@ -599,6 +634,48 @@ function Do_ExtendDataset()
SPITAJAX_RESPONSE("show-dataset.php?uuid=$dataset_uuid");
}
function Do_GetInfo()
{
global $this_user;
global $ajax_args;
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
$islease = 1;
if (!isset($ajax_args["uuid"])) {
SPITAJAX_ERROR(1, "Missing uuid");
return;
}
#
# Either a local lease or a remote dataset.
#
$dataset = Lease::Lookup($ajax_args["uuid"]);
if (!$dataset) {
$dataset = Dataset::Lookup($ajax_args["uuid"]);
$islease = 0;
}
if (!$dataset) {
SPITAJAX_ERROR(1, "Unknown dataset");
return;
}
if ($this_uid != $dataset->owner_uid() && !ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission");
return;
}
$blob = array();
if ($dataset->state() == "busy" ||
($dataset->state() == "unapproved" && $dataset->locked())) {
$blob["state"] = "allocating";
}
else {
$blob["state"] = $dataset->state();
}
$blob["size"] = $dataset->size() ? $dataset->size() : "0";
SPITAJAX_RESPONSE($blob);
}
# Local Variables:
# mode:php
# End:
......
......@@ -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)