All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 108d3d6f authored by Leigh B Stoller's avatar Leigh B 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
}
}
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
{
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) {
if (isset($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["active"] = intval($active);
$instance["uuid"] = $uuid;
......@@ -141,9 +129,6 @@ foreach (array($query1_result, $query2_result) as $query_result) {
$instance["created"] = $created;
$instance["destroyed"] = $destroyed;
$instance["clusters"] = ($clusters ? $clusters : "n/a");
if (ISADMIN()) {
$instance["public_urls"] = $public_urls;
}
if (isset($exitcode)) {
$instance["iserror"] = 1;
......
......@@ -418,7 +418,11 @@ $routing = array("geni-login" =>
"Do_GetHealthStatus",
"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 @@
<th>Clusters</th>
<th>Error</th>
<% if (window.ISADMIN) { %>
<th>Logs</th>
<th>View</th>
<% } %>
</tr>
</thead>
......@@ -26,12 +26,7 @@
</td>
<td><%- instance.creator %></td>
<td><%- instance.created %></td>
<% if (instance.active) { %>
<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.destroyed %></td>
<td><%- instance.clusters %></td>
<td class="text-danger">
<% if (_.has(instance, "iserror")) { %>
......@@ -48,14 +43,15 @@
<% } %>
<% } %>
</td>
<% if (window.ISADMIN) { %>
<td>
<% _.each(instance.public_urls.split(","), function(url) { %>
<a href='<%- url %>' target="_blank">
<span class="glyphicon glyphicon-link"></span></a>
<% }); %>
</td>
<% } %>
<td>
<% if (instance.active) { %>
<a href='status.php?uuid=<%- instance.uuid %>' target="_blank">
<span class="glyphicon glyphicon-eye-open"></span></a>
<% } else { %>
<a href='memlane.php?uuid=<%- instance.uuid %>' target="_blank">
<span class="glyphicon glyphicon-eye-open"></span></a>
<% } %>
</td>
</tr>
<% }); %>
</tbody>
......
Markdown is supported
0% or