Commit 108d3d6f authored by Leigh Stoller's avatar Leigh Stoller

New page and support to display a historical experiment using the

manifest we have store in the DB. Basically a highly stripped down
version of the status page.
parent 2102dbce
...@@ -821,6 +821,133 @@ class Instance ...@@ -821,6 +821,133 @@ class Instance
} }
} }
class InstanceHistory
{
var $record;
var $slivers;
#
# Constructor by lookup on unique index.
#
function InstanceHistory($uuid) {
$safe_uuid = addslashes($uuid);
$query_result =
DBQueryWarn("select h.*,f.exitmessage,f.exitcode ".
" from apt_instance_history as h ".
"left join apt_instance_failures as f ".
" on f.uuid=h.uuid ".
"where h.uuid='$safe_uuid'");
if (!$query_result || !mysql_num_rows($query_result)) {
$this->record = null;
return;
}
$this->record = mysql_fetch_array($query_result);
#
# Get the list of aggregate records. Early records do not have one.
#
$query_result =
DBQueryWarn("select * from apt_instance_aggregate_history ".
"where uuid='$uuid'");
if (!$query_result) {
$this->record = null;
return;
}
if (!mysql_num_rows($query_result)) {
$this->slivers = array(
array("uuid" => $this->record["uuid"],
"name" => $this->record["name"],
"aggregate_urn" => $this->record["aggregate_urn"],
"status" => $this->record["status"],
"public_url" => $this->record["public_url"],
"manifest" => $this->record["manifest"],
));
}
else {
$this->slivers = array();
while ($row = mysql_fetch_array($query_result)) {
$this->slivers[] = $row;
}
}
}
# accessors
function slivers() { return $this->slivers; }
function field($name) {
return (is_null($this->record) ? -1 : $this->record[$name]);
}
function uuid() { return $this->field('uuid'); }
function name() { return $this->field('name'); }
function profile_id() { return $this->field('profile_id'); }
function profile_version() { return $this->field('profile_version'); }
function slice_uuid() { return $this->field('slice_uuid'); }
function creator() { return $this->field('creator'); }
function creator_idx() { return $this->field('creator_idx'); }
function creator_uuid() { return $this->field('creator_uuid'); }
function pid() { return $this->field('pid'); }
function pid_idx() { return $this->field('pid_idx'); }
function gid() { return $this->field('gid'); }
function gid_idx() { return $this->field('gid_idx'); }
function aggregate_urn(){ return $this->field('aggregate_urn'); }
function public_url() { return $this->field('public_url'); }
function logfileid() { return $this->field('logfileid'); }
function created() { return $this->field('created'); }
function destroyed() { return $this->field('destroyed'); }
function expired() { return $this->field('expired'); }
function extension_count() { return $this->field('extension_count'); }
function extension_days() { return $this->field('extension_days'); }
function extension_hours() { return $this->field('extension_hours'); }
function physnode_count() { return $this->field('physnode_count'); }
function virtnode_count() { return $this->field('virtnode_count'); }
function servername() { return $this->field('servername'); }
function repourl() { return $this->field('repourl'); }
function reporef() { return $this->field('reporef'); }
function repohash() { return $this->field('repohash'); }
function rspec() { return $this->field('rspec'); }
function script() { return $this->field('script'); }
function params() { return $this->field('params'); }
function manifest() { return $this->field('manifest'); }
function IsAPT() {
return preg_match('/aptlab/', $this->servername());
}
function IsCloud() {
return preg_match('/cloudlab/', $this->servername());
}
function IsPNet() {
return preg_match('/phantomnet/', $this->servername());
}
# Hmm, how does one cause an error in a php constructor?
function IsValid() {
return !is_null($this->record);
}
# Lookup up an instance by uuid
function Lookup($uuid) {
$foo = new InstanceHistory($uuid);
if ($foo->IsValid()) {
# Insert into cache.
return $foo;
}
return null;
}
function LookupBySlice($slice_uuid)
{
$safe_uuid = addslashes($slice_uuid);
$query_result =
DBQueryWarn("select uuid from apt_instance_history ".
"where slice_uuid='$safe_uuid'");
if (!$query_result || !mysql_num_rows($query_result)) {
return null;
}
$row = mysql_fetch_array($query_result);
return InstanceHistory::Lookup($row[0]);
}
}
class InstanceSliver class InstanceSliver
{ {
var $sliver; var $sliver;
......
This diff is collapsed.
<?php
#
# Copyright (c) 2000-2018 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_once("webtask.php");
include_once("geni_defs.php");
chdir("apt");
include_once("profile_defs.php");
include_once("instance_defs.php");
#
# Return info about a previous experiment.
#
function Do_HistoryRecord()
{
global $this_user, $ajax_args;
if (!isset($ajax_args["uuid"])) {
SPITAJAX_ERROR(1, "Missing uuid");
return;
}
$uuid = $ajax_args["uuid"];
if (!IsValidUUID($uuid)) {
SPITAJAX_ERROR(1, "Not a valid UUID: $uuid");
return;
}
$record = InstanceHistory::Lookup($uuid);
if (!$record) {
SPITAJAX_ERROR(1, "No such record: $uuid");
return;
}
if (! (ISADMIN() || ISFOREIGN_ADMIN() || $record->CanView($this_user))) {
SPITAJAX_ERROR(1, "You do not have permission to view this page!");
return;
}
$blob = $record->record;
$blob["slivers"] = $record->slivers();
#
# Need to add a few things.
#
$blob["created"] = DateStringGMT($blob["created"]);
$blob["destroyed"] = DateStringGMT($blob["destroyed"]);
$blob["profile_uuid"] = null;
$blob["profile_name"] = null;
if ($profile = Profile::Lookup($blob["profile_id"],
$blob["profile_version"])) {
$blob["profile_name"] = $profile->name() . ":" . $profile->version();
$blob["profile_uuid"] = $profile->uuid();
}
# Need to munge the urls since these slices are history.
foreach ($blob["slivers"] as &$sliver) {
$url = $sliver["public_url"];
if ($url && preg_match("/publicid=\w*/", $url)) {
$url = "https://" . parse_url($url, PHP_URL_HOST) .
"/showslicelogs.php?slice_uuid=" . $record->slice_uuid();
$sliver["public_url"] = $url;
}
}
SPITAJAX_RESPONSE($blob);
}
# Local Variables:
# mode:php
# End:
?>
<?php
#
# Copyright (c) 2000-2018 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_once("geni_defs.php");
chdir("apt");
include("quickvm_sup.php");
$page_title = "Experiment Record";
#
# Get current user.
#
$this_user = CheckLogin($check_status);
if (isset($this_user)) {
CheckLoginOrDie();
}
else {
RedirectLoginPage();
}
#
# We do not set the isfadmin flag if the user has normal permission
# to see this experiment, since that would change what the user sees.
# Okay for real admins, but not for foreign admins.
#
$isfadmin = (ISFOREIGN_ADMIN() ? 1 : 0);
$isadmin = (ISADMIN() ? 1 : 0);
#
# Verify page arguments.
#
$reqargs = OptionalPageArguments("slice_uuid", PAGEARG_UUID,
"uuid", PAGEARG_UUID);
if (! (isset($slice_uuid) || isset($uuid))) {
SPITHEADER(1);
echo "<div class='align-center'>
<p class='lead text-center'>
What experiment record would you like to look at?
</p>
</div>\n";
SPITNULLREQUIRE();
SPITFOOTER();
return;
}
#
# See if the record exists.
#
if (isset($uuid)) {
$record = InstanceHistory::Lookup($uuid);
}
else {
$record = InstanceHistory::LookupbySlice($slice_uuid);
}
if (!$record) {
SPITHEADER(1);
echo "<div class='align-center'>
<p class='lead text-center'>
Experiment record does not exist.
</p>
</div>\n";
SPITNULLREQUIRE();
SPITFOOTER();
return;
}
$uuid = $record->uuid();
if (! (ISADMIN() || ISFOREIGN_ADMIN() || $record->CanView($this_user))) {
PAGEERROR("You do not have permission to look at this experiment!");
}
SPITHEADER(1);
# Place to hang the toplevel template.
echo "<div id='page-body'></div>\n";
#
# Build up a blob of aggregates info used by this experiment.
#
$blob = array();
foreach ($record->slivers as $sliver) {
$aggregate_urn = $sliver["aggregate_urn"];
$aggregate = Aggregate::Lookup($aggregate_urn);
$weburl = $aggregate->weburl();
$blob[$aggregate_urn] = array("weburl" => $weburl,
"name" => $aggregate->name());
}
echo "<script type='text/plain' id='amlist-json'>\n";
echo json_encode($blob, JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP);
echo "</script>\n";
echo "<script type='text/javascript'>\n";
echo " window.uuid = '" . $uuid . "';\n";
echo " window.isadmin = $isadmin;\n";
echo " window.isfadmin = $isfadmin;\n";
echo "</script>\n";
echo "<script src='js/lib/d3.v3.js'></script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/jquery-ui.js'></script>\n";
echo "<script src='js/lib/codemirror-min.js'></script>\n";
REQUIRE_UNDERSCORE();
REQUIRE_SUP();
REQUIRE_MOMENT();
REQUIRE_MARKED();
REQUIRE_URITEMPLATE();
SPITREQUIRE("js/memlane.js");
echo "<link rel='stylesheet'
href='css/jquery-ui-1.10.4.custom.min.css'>\n";
echo "<link rel='stylesheet' href='css/codemirror.css'>\n";
AddTemplateList(array("memlane", "waitwait-modal", "oops-modal"));
SPITFOOTER();
?>
...@@ -120,18 +120,6 @@ foreach (array($query1_result, $query2_result) as $query_result) { ...@@ -120,18 +120,6 @@ foreach (array($query1_result, $query2_result) as $query_result) {
if (isset($email)) { if (isset($email)) {
$creator = $email; $creator = $email;
} }
#
# If the slice is gone, the public url needs to be replaced.
#
$tmp = array();
foreach (preg_split("/,/", $public_urls) as $url) {
if ($destroyed != "" && preg_match("/publicid=\w*/", $url)) {
$url = "https://" . parse_url($url, PHP_URL_HOST) .
"/showslicelogs.php?slice_uuid=" . $slice_uuid;
}
$tmp[] = $url;
}
$public_urls = implode(",", $tmp);
$instance = array(); $instance = array();
$instance["active"] = intval($active); $instance["active"] = intval($active);
$instance["uuid"] = $uuid; $instance["uuid"] = $uuid;
...@@ -141,9 +129,6 @@ foreach (array($query1_result, $query2_result) as $query_result) { ...@@ -141,9 +129,6 @@ foreach (array($query1_result, $query2_result) as $query_result) {
$instance["created"] = $created; $instance["created"] = $created;
$instance["destroyed"] = $destroyed; $instance["destroyed"] = $destroyed;
$instance["clusters"] = ($clusters ? $clusters : "n/a"); $instance["clusters"] = ($clusters ? $clusters : "n/a");
if (ISADMIN()) {
$instance["public_urls"] = $public_urls;
}
if (isset($exitcode)) { if (isset($exitcode)) {
$instance["iserror"] = 1; $instance["iserror"] = 1;
......
...@@ -418,7 +418,11 @@ $routing = array("geni-login" => ...@@ -418,7 +418,11 @@ $routing = array("geni-login" =>
"Do_GetHealthStatus", "Do_GetHealthStatus",
"GetWirelessStatus" => "GetWirelessStatus" =>
"Do_GetWirelessStatus")), "Do_GetWirelessStatus")),
"memlane" =>
array("file" => "memlane.ajax",
"guest" => false,
"methods" => array("HistoryRecord" =>
"Do_HistoryRecord")),
); );
# #
......
<style>
pre {
white-space: pre-wrap;
}
.table-condensed > thead > tr > th,
.table-condensed > tbody > tr > th,
.table-condensed > tfoot > tr > th,
.table-condensed > thead > tr > td,
.table-condensed > tbody > tr > td,
.table-condensed > tfoot > tr > td {
padding: 3px;
}
</style>
<div class="row">
<div class='col-lg-8 col-lg-offset-2
col-md-8 col-md-offset-2
col-sm-10 col-sm-offset-1
col-xs-12 col-xs-offset-0'>
<div class='panel panel-default' id='info-panel'>
<div class='panel-body'>
<table class='table table-condensed nospaceafter border-none'>
<tr>
<td class='border-none'>Name:</td>
<td class='border-none'><%- record.name %></td>
</tr>
<% if (record.profile_uuid) { %>
<tr>
<td class='border-none'>Profile:</td>
<td class='border-none'>
<a target=_blank
href='show-profile.php?uuid=<%- record.profile_uuid %>'>
<%- record.profile_name %></a></td>
</tr>
<% } %>
<% if (record.reporef) { %>
<tr>
<td class='border-none'>RefSpec:</td>
<td class='border-none'>
<span><%= record.reporef %></span>
(<%= record.repohash %>)
</td>
</tr>
<% } %>
<tr>
<td class='border-none'>Creator:</td>
<td class='border-none'>
<a href="user-dashboard.php?user=<%- record.creator_idx %>">
<%- record.creator %></a></td>
</tr>
<% if (record.pid) { %>
<tr>
<% if (record.pid == record.gid) { %>
<td class='border-none'>Project:</td>
<td class='border-none'>
<a href="show-project.php?pid=<%- record.pid %>">
<%- record.pid %></a></td>
<% } else { %>
<td class='border-none'>Group:</td>
<td class='border-none'>
<a href="show-group.php?pid=<%- record.pid %>&gid=<%- record.gid %>">
<%- record.pid %>:<%- record.gid %></a></td>
<% } %>
</tr>
<% } %>
<tr>
<td class='border-none'>Created:</td>
<td class='border-none format-date'><%- record.created %></td>
</tr>
<tr>
<td class='border-none'>Destroyed:</td>
<td class='border-none'>
<span class='format-date'><%- record.destroyed %></span>
</td>
</tr>
</table>
<div class='pull-left'>
<a class='btn btn-info btn-xs pull-left hidden'
style='margin-right: 10px;'
id='sliverinfo_button'
href='#'
target='_blank'
type='button'>Logs</a>
<div id='sliverinfo_dropdown' class='hidden'>
<select style='margin-right: 0px;'>
<option value='selectme'>Logs</option>
</select>
<a class='btn btn-info btn-tiny'
style='margin-right: 10px;'
href='#'
target='_blank'
type='button'>Go</a>
</div>
</div>
<div class='pull-left'>
<a class='hidden'
style='margin-right: 10px;'
id='logfile_button'
href='#'
target='_blank'
type='button'>Stitcher</a>
</div>
</div>
</div>
</div>
</div>
<div class='row'>
<!-- Add a hidden div for errors returned by the instantiation. -->
<div class='row hidden' id='error_panel'>
<div class='col-lg-8 col-lg-offset-2
col-md-8 col-md-offset-2
col-sm-10 col-sm-offset-1
col-xs-12 col-xs-offset-0'>
<div class="panel panel-danger">
<div class="panel-heading">
<center><h4 class="panel-title">Error Message</h4></center>
</div>
<div class="panel-body"><code style="white-space: pre-wrap"
id="error_panel_text"></code>
</div>
</div>
</div>
</div>
</div>
<div class='row'>
<div class='col-lg-12 col-lg-offset-0
col-md-12 col-md-offset-0
col-sm-12 col-sm-offset-0
col-xs-12 col-xs-offset-0'>
<div class='panel panel-default invisible' id='showtopo_container'>
<div class='panel-body'>
<div id='quicktabs_div'>
<ul id='quicktabs_ul' class='nav nav-tabs'>
<li class='active'>
<a href='#topology' data-toggle='tab'>Topology View</a>
</li>
<li>
<a href='#listview' data-toggle='tab'>List View</a>
</li>
<li>
<a href='#rspec' id='show_rspec_tab'
data-toggle='tab'>Rspec</a>
</li>
<li>
<a href='#manifest' id='show_manifest_tab'
data-toggle='tab'>Manifest</a>
</li>
</ul>
<div id='quicktabs_content' class='tab-content'>
<div class='tab-pane active' id='topology'>
<div id='showtopo_statuspage' class='jacks'></div>
</div>
<div class='tab-pane' id='listview'>
<div id='showtopo_listview'>
<table class='table table-striped table-hover table-condensed'
id='listview_table'>
<thead>
<tr>
<th>ID</th>
<th>Node</th>
<th>Type</th>
<th>Image</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class='tab-pane' id='rspec'>
<div id='rspec_textarea' style='width: 100%;'></div>
</div>
<div class='tab-pane' id='manifest'>
<div id='manifest_textarea' style='width: 100%;'></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class='row'>
<div id='waitwait_div'></div>
<div id='oops_div'></div>
</div>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<th>Clusters</th> <th>Clusters</th>
<th>Error</th> <th>Error</th>
<% if (window.ISADMIN) { %> <% if (window.ISADMIN) { %>
<th>Logs</th> <th>View</th>
<% } %> <% } %>
</tr> </tr>
</thead> </thead>
...@@ -26,12 +26,7 @@ ...@@ -26,12 +26,7 @@
</td> </td>
<td><%- instance.creator %></td> <td><%- instance.creator %></td>
<td><%- instance.created %></td> <td><%- instance.created %></td>
<% if (instance.active) { %> <td><%- instance.destroyed %></td>
<td><a href='status.php?uuid=<%- instance.uuid %>' target="_blank">
<span class="glyphicon glyphicon-eye-open"></span></a></td>
<% } else { %>
<td><%- instance.destroyed %></td>
<% } %>
<td><%- instance.clusters %></td> <td><%- instance.clusters %></td>
<td class="text-danger"> <td class="text-danger">
<% if (_.has(instance, "iserror")) { %> <% if (_.has(instance, "iserror")) { %>
...@@ -48,14 +43,15 @@ ...@@ -48,14 +43,15 @@
<% } %> <% } %>
<% } %> <% } %>
</td> </td>
<% if (window.ISADMIN) { %> <td>
<td> <% if (instance.active) { %>
<% _.each(instance.public_urls.split(","), function(url) { %> <a href='status.php?uuid=<%- instance.uuid %>' target="_blank">
<a href='<%- url %>' target="_blank"> <span class="glyphicon glyphicon-eye-open"></span></a>
<span class="glyphicon glyphicon-link"></span></a> <% } else { %>
<% }); %> <a href='memlane.php?uuid=<%- instance.uuid %>' target="_blank">
</td> <span class="glyphicon glyphicon-eye-open"></span></a>
<% } %> <% } %>
</td>
</tr> </tr>
<% }); %> <% }); %>
</tbody> </tbody>
......
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