Commit b4481cbd authored by Leigh Stoller's avatar Leigh Stoller

Sweeping (massive, huge, bigly?) changes to Classic path image pages.

The high level goal; redo all the classic image pages within the Portal
UI such that mere users never see the old crusty stale rotting pages.
Old fart admins (know any?) can still access the old pages, but I am
hoping that won't actually happen (hint, hint).

Another goal; allow editing of Geni created images (local images
only). It has always bothered me that Portal users have no way to get
info or do minor editing on Portal created images. Especially since
images are so central to everything we do. Only local images can be
edited, but in the Emulab portal, that is all images.

Mothership only for now. Lets see how much whining we get, might have to
adjust the interface a bit since Classic users are going to find
themselves in unfamiliar territory.
parent a5be936d
......@@ -4472,6 +4472,7 @@ sub ListImages($)
my $blob = {"urn" => $image_urn . ":" . $image->version(),
"url" => $image_url,
"uuid" => $image->uuid(),
"created" => emutil::TBDateStringGMT($image->created()),
"updated" => emutil::TBDateStringGMT($image->updated()),
"format" => $image->format(),
......
......@@ -59,7 +59,7 @@ sub usage()
" -w Wait for image to be created\n");
exit(-1);
}
my $optlist = "densg:wFr:b:UB:";
my $optlist = "densg:wFr:b:UB:t:";
my $debug = 0;
my $wholedisk = 0;
my $impotent = 0;
......@@ -74,6 +74,8 @@ my $origin_uuid;
my $bsname;
my $base_image;
my $image;
my $webtask_id;
my $webtask;
#
# Configure variables
......@@ -109,6 +111,7 @@ use Project;
use OSImage;
use Image; # For datasets
use Node;
use WebTask;
use EmulabFeatures;
# Protos
......@@ -163,6 +166,14 @@ if (defined($options{"b"})) {
fatal("Bad data in $bsname.");
}
}
if (defined($options{"t"})) {
$webtask_id = $options{"t"};
$webtask = WebTask->Lookup($webtask_id);
if (!defined($webtask)) {
fatal("No such webtask");
}
$webtask->AutoStore(1);
}
usage()
if (@ARGV != 2);
......@@ -393,9 +404,13 @@ if (defined($image)) {
if ($nosnapshot) {
print "Not taking a snapshot, as directed\n"
if ($debug);
if (defined($webtask)) {
$webtask->Exited(0);
}
exit(0);
}
my $opts = "-p $pid ";
$opts .= "-t $webtask_id " if (defined($webtask));
$opts .= "-w " if ($waitmode);
$opts .= "-F " if ($nodelta);
$opts .= "-U " if ($update_prepare);
......@@ -675,6 +690,9 @@ if ($debug) {
if ($nosnapshot) {
print "Not taking a snapshot, as directed\n"
if ($debug);
if (defined($webtask)) {
$webtask->Exited(0);
}
exit(0);
}
......@@ -685,6 +703,7 @@ if ($nosnapshot) {
$nodelta = 1;
my $opts = "-p $pid ";
$opts .= "-t $webtask_id " if (defined($webtask));
$opts .= "-w " if ($waitmode);
$opts .= "-F " if ($nodelta);
$opts .= "-U " if ($update_prepare);
......@@ -702,6 +721,17 @@ sub fatal($)
{
my ($mesg) = @_;
if (defined($webtask)) {
#
# If we threw an error in create_image, we do not want to
# overwrite that image.
#
$webtask->Refresh();
if (!$webtask->HasExited()) {
$webtask->output($mesg);
$webtask->Exited(1);
}
}
die("*** $0:\n".
" $mesg\n");
}
#!/usr/bin/perl -w
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -1731,6 +1731,7 @@ sub fatal($)
$webtask->Refresh();
$webtask->status("failed");
$webtask->imagesize(0);
$webtask->output($mesg);
$webtask->Exited(1);
}
$image->Unlock()
......
<?php
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -562,9 +562,7 @@ function ClassicImageList($target)
$blob["creator_idx"] = $row["creator_idx"];
$blob["format"] = $row["format"];
$blob["urn"] = $urn;
$blob["url"] = $TBBASE . "/" .
CreateURL("showimageid",
URLARG_IMAGEID, $imageid);
$blob["url"] = "show-image.php?imageid=$imageid";
$results[] = $blob;
}
return $results;
......
<?php
#
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include("defs.php3");
include("imageid_defs.php");
include("osiddefs.php3");
include("node_defs.php");
chdir("apt");
include("quickvm_sup.php");
include_once("profile_defs.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Clone Image";
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLoginOrRedirect();
if (NOPROJECTMEMBERSHIP()) {
return NoProjectMembershipError($this_user);
}
$this_idx = $this_user->uid_idx();
$isadmin = (ISADMIN() ? "true" : "false");
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("node", PAGEARG_NODE,
"baseimage", PAGEARG_IMAGE);
SPITHEADER(1);
#
# If starting from a specific node we can derive baseimage from it.
#
if (isset($node)) {
$baseimage = $node->def_boot_image();
if (!isset($baseimage)) {
USERERROR("Cannot determine base image from node", 1);
}
if (!$node->AccessCheck($this_user, $TB_NODEACCESS_LOADIMAGE)) {
USERERROR("No permission to clone this node", 1);
}
}
elseif (!isset($baseimage)) {
USERERROR("Must supply an image or a node to clone", 1);
}
$baseimage_uuid = $baseimage->uuid();
$baseimage_name = $baseimage->imagename();
$baseimage_version = $baseimage->version();
# Must be allowed to read the image.
if (! $baseimage->AccessCheck($this_user, $TB_IMAGEID_READINFO)) {
$errors["error"] = "Not enough permission to clone image";
}
# Place to hang the toplevel template.
echo "<div id='main-body'></div>\n";
#
# See what projects the user can do this in.
#
$projlist = $this_user->ProjectAccessList($TB_PROJECT_MAKEIMAGEID);
echo "<script type='text/plain' id='projects-json'>\n";
echo htmlentities(json_encode($projlist));
echo "</script>\n";
#
# Need a list of node types. We join this over the nodes table so that
# we get a list of just the nodes that currently in the testbed, not
# just in the node_types table.
#
$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'");
$alltypes = array();
while ($row = mysql_fetch_array($types_result)) {
$alltypes[] = $row["type"];
}
echo "<script type='text/plain' id='alltypes-json'>\n";
echo htmlentities(json_encode($alltypes));
echo "</script>\n";
echo "<script type='text/plain' id='oslist-json'>\n";
echo htmlentities(json_encode($osid_oslist));
echo "</script>\n";
echo "<script type='text/plain' id='osfeatures-json'>\n";
echo htmlentities(json_encode($osid_featurelist));
echo "</script>\n";
echo "<link rel='stylesheet'
href='css/jquery-ui.min.css'>\n";
echo "<script type='text/javascript'>\n";
echo " window.ISADMIN = $isadmin;\n";
echo " window.BASEIMAGE_UUID = '$baseimage_uuid';\n";
echo " window.BASEIMAGE_NAME = '$baseimage_name';\n";
echo " window.BASEIMAGE_VERSION = $baseimage_version;\n";
if (isset($node)) {
$node_id = $node->node_id();
echo " window.NODE = '$node_id';\n";
}
echo "</script>\n";
REQUIRE_UNDERSCORE();
REQUIRE_SUP();
REQUIRE_MOMENT();
REQUIRE_APTFORMS();
REQUIRE_IMAGE();
SPITREQUIRE("js/clone-image.js");
AddTemplateList(array("clone-image",
"oops-modal", "waitwait-modal"));
SPITFOOTER();
?>
<?php
#
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include("defs.php3");
include("imageid_defs.php");
chdir("apt");
include("quickvm_sup.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Show Image";
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLoginOrRedirect();
$this_idx = $this_user->uid_idx();
$isadmin = (ISADMIN() ? "true" : "false");
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("image", PAGEARG_IMAGE);
$optargs = OptionalPageArguments("edit", PAGEARG_BOOLEAN,
"formfields", PAGEARG_ARRAY);
if (!$image) {
SPITUSERERROR("No such image!");
}
if (!$image->AccessCheck($this_user, $TB_IMAGEID_MODIFYINFO)) {
SPITUSERERROR("Not enough permission!");
}
$uuid = $image->uuid();
if ($edit) {
$action = "edit";
}
else {
$action = "view";
}
SPITHEADER(1);
# Place to hang the toplevel template.
echo "<div id='main-body'></div>\n";
echo "<link rel='stylesheet'
href='css/jquery-ui.min.css'>\n";
echo "<script type='text/javascript'>\n";
echo " window.UUID = '$uuid';\n";
echo " window.ACTION = '$action';\n";
echo " window.ISADMIN = $isadmin;\n";
echo "</script>\n";
REQUIRE_UNDERSCORE();
REQUIRE_SUP();
REQUIRE_MOMENT();
REQUIRE_APTFORMS();
SPITREQUIRE("js/create-image.js");
AddTemplateList(array("create-image",
"oops-modal", "waitwait-modal"));
SPITFOOTER();
?>
This diff is collapsed.
<?php
#
# Copyright (c) 2000-2018 University of Utah and the Flux Group.
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -151,7 +151,7 @@ if ($ISCLOUD) {
#
list ($imagedomain,$type,$id) = Instance::ParseURN($row["urn"]);
if ($imagedomain == $domain) {
$url = "$TBBASE/showimageid.php3?imageid=" . $row["image_uuid"];
$url = "$APTBASE/show-image.php?imageid=" . $row["image_uuid"];
}
else {
if (array_key_exists($imagedomain, $aggregates)) {
......@@ -179,7 +179,7 @@ if ($ISCLOUD) {
$blob["project_urn"] = $row["project_urn"];
$blob["format"] = $row["format"];
$blob["urn"] = $row["urn"];
if (isset($url)) {
if (ISADMIN() && isset($url)) {
$blob["url"] = $url;
}
$tmp[] = $blob;
......@@ -246,9 +246,7 @@ else {
$blob["creator_idx"] = $row["creator_idx"];
$blob["format"] = $row["format"];
$blob["urn"] = $urn;
$blob["url"] = $TBBASE . "/" .
CreateURL("showimageid",
URLARG_IMAGEID, $imageid);
$blob["url"] = "show-image.php?imageid=$imageid";
$tmp[] = $blob;
}
}
......@@ -298,10 +296,6 @@ foreach ($tmp as $blob) {
$blob["filter"] = implode(",", $filters);
$images[] = $blob;
}
echo "<script type='text/plain' id='images-json'>\n";
echo htmlentities(json_encode($images)) . "\n";
echo "</script>\n";
echo "<script type='text/javascript'>\n";
$isadmin = (isset($this_user) && ISADMIN() ? 1 : 0);
echo " window.ISADMIN = $isadmin;\n";
......@@ -311,6 +305,10 @@ echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/jquery.tablesorter.min.js'></script>\n";
echo "<script src='js/lib/jquery.tablesorter.widgets.min.js'></script>\n";
echo "<script type='text/plain' id='images-json'>\n";
echo htmlentities(json_encode($images)) . "\n";
echo "</script>\n";
REQUIRE_UNDERSCORE();
REQUIRE_SUP();
REQUIRE_MOMENT();
......
......@@ -98,6 +98,7 @@ $(function () {
if (_.has(item.dataset, "help")) {
label_text = label_text +
"<a href='#' class='btn btn-xs' " +
" style='padding-right: 0px;' " +
" data-toggle='popover' " +
" data-html='true' " +
" data-delay='{\"hide\":1000}' " +
......
$(function ()
{
'use strict';
var templates = APT_OPTIONS.fetchTemplateList(['clone-image',
'oops-modal',
'waitwait-modal']);
var mainString = templates['clone-image'];
var mainTemplate = _.template(mainString);
var formfields = {};
var projlist = null;
var oslist = null;
var osfeatures = null;
var alltypes = null;
var isadmin = false;
function JsonParse(id)
{
return JSON.parse(_.unescape($(id)[0].textContent));
}
function initialize()
{
window.APT_OPTIONS.initialize(sup);
isadmin = window.ISADMIN;
projlist = JsonParse('#projects-json');
oslist = JsonParse('#oslist-json');
osfeatures = JsonParse('#osfeatures-json');
alltypes = JsonParse('#alltypes-json');
if (window.BASEIMAGE_UUID === undefined) {
GeneratePageBody(formfields);
}
else {
sup.CallServerMethod(null, "image", "GetInfo",
{"uuid" : window.BASEIMAGE_UUID},
function(json) {
console.info("info", json);
if (json.code) {
alert("Could not get image info " +
"from server: " + json.value);
return;
}
GeneratePageBody(json.value);
});
}
}
//
// Moved into a separate function since we want to regen the form
// after each submit, which happens via ajax on this page.
//
function GeneratePageBody(formfields)
{
var title;
// Generate the template.
var html = mainTemplate({
formfields: formfields,
projects: projlist,
isadmin: isadmin,
alltypes: alltypes,
oslist: oslist,
osfeatures: osfeatures,
});
html = aptforms.FormatFormFieldsHorizontal(html);
$('#main-body').html(html);
// Now we can do this.
$('#oops_div').html(templates['oops-modal']);
$('#waitwait_div').html(templates['waitwait-modal']);
// Set the correct shared/global radio.
if (formfields["shared"]) {
$("#shared-global-radio-shared").prop("checked", "checked");
}
else if (formfields["global"]) {
$("#shared-global-radio-global").prop("checked", "checked");
}
else {
$("#shared-global-radio-neither").prop("checked", "checked");
}
// Project change handler, change group list
$('#image_pid').change(function (event) {
UpdateGroupSelector();
});
// This activates the tooltip subsystem.
$('[data-toggle="tooltip"]').tooltip({
trigger: 'hover',
});
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
container: 'body'
});
aptforms.EnableUnsavedWarning('#clone-image-form');
//
// Handle submit button.
//
$('#clone-image-button').click(function (event) {
event.preventDefault();
SubmitForm();
});
}
/*
* When the project is changed, change group selector.
*/
function UpdateGroupSelector()
{
var pid = $('#image_pid').val();
var glist = projlist[pid];
console.info(pid, glist);
if (glist.length == 1) {
var gid = glist[0];
// Readonly form control.
$('#image_gid').html(
"<input name=image_gid readonly " +
" class='form-control' value='" + gid + "'>");
return;
}
var html = "";
_.each(glist, function(gid) {
var selected = "";
// Select the project group by default.
if (gid == pid) {
selected = "selected";
}
html = html +
"<option " + selected + " value=" + gid + ">" +
gid + "</option>";
});
$('#image_gid').html(
"<select name=image_gid class='form-control'>" +
html + "</select>");
}
//
// Submit the form.
//
function SubmitForm()
{
var submit_callback = function(json) {
console.info(json);
if (json.code) {
sup.SpitOops("oops", json.value);
return;
}
var url = json.value;
console.info(url);
window.location.replace(url);
};
var checkonly_callback = function(json) {
if (json.code) {
if (json.code != 2) {
sup.SpitOops("oops", json.value);
}
return;
}
aptforms.SubmitForm('#clone-image-form', "image", "Clone",
submit_callback,
"This will take a few minutes; " +
"please be patient!");
};
aptforms.CheckForm('#clone-image-form', "image", "Clone",
checkonly_callback);
}
$(document).ready(initialize);
});
This diff is collapsed.
$(function ()
{
'use strict';
var lastIndex = 0;
/*
* This code witten by Jon; ../../fetchlogfile.html
*/
function initialize()
{
// The URL refers to the old php script that spews the file.
var url = window.SPEWURL + '&isajax=1';
// Fetch spewlogfile via AJAX call
var xhr = new XMLHttpRequest();
// Every time new data comes in or the state variable changes,
// this function is invoked.
xhr.onreadystatechange = function ()
{
// xhr.responseText contains all data received so far from
// spewlogfile
if (xhr.responseText)
{
// Append only new text
var newText = xhr.responseText.substr(lastIndex);
lastIndex = xhr.responseText.length;
var scrollHeight = $('body')[0].scrollHeight;
var scrollTop = $(window).scrollTop();
var innerHeight = window.innerHeight;
var shouldScroll = scrollHeight - innerHeight === scrollTop;
$('pre').append(_.escape(newText));
if (shouldScroll) {
$(window).scrollTop(1000000);
}
}
//
// Request is done, we got everything.
//
if (xhr.readyState == 4) {
//
// This will clear the busy indicators in the outer page,
// if there are any.
//
if (typeof(parent.loadFinished) == "function") {
parent.loadFinished();
}
}
};
// Invoke the AJAX
xhr.open('get', url, true);
xhr.send();
}
$(document).ready(initialize);
});
......@@ -390,6 +390,27 @@ $routing = array("geni-login" =>
"Do_DeleteImage",
"ClassicImages" =>
"Do_ClassicImageList")),
"image" =>
array("file" => "image.ajax",
"guest" => false,
"methods" => array("GetInfo" =>
"Do_GetInfo",
"SaveAdminNotes" =>
"Do_SaveAdminNotes",
"Delete" =>
"Do_Delete",
"SetSharing" =>
"Do_SetSharing",
"SetTypes" =>
"Do_SetTypes",
"Clone" =>
"Do_Clone",
"Snapshot" =>
"Do_Snapshot",
"SnapshotStatus" =>
"Do_SnapshotStatus",
"Modify" =>
"Do_Modify")),
"news" =>
array("file" => "news.ajax",
"guest" => false,
......
<?php
#
# Copyright (c) 2000-2019 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include("defs.php3");
include("imageid_defs.php");
include("node_defs.php");
chdir("apt");
include("quickvm_sup.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Show Image";
#
# Get current user.
#
RedirectSecure();
$this_user = C