Commit 2c7863a2 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint new Profile versioning code. Also new instance history table and

support code.
parent edd5097c
......@@ -287,5 +287,24 @@ sub Profile($)
$self->profile_version());
}
#
# Record a history entry.
# We do this when the instance is terminated.
#
sub RecordHistory($)
{
my ($self) = @_;
my $uuid = $self->uuid();
DBQueryWarn("replace into apt_instance_history ".
"select uuid,profile_id,profile_version,slice_uuid, ".
" creator,creator_idx,creator_uuid,aggregate_urn, ".
" created,now() ".
" from apt_instances where uuid='$uuid'")
or return -1;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -58,6 +58,25 @@ CREATE TABLE `active_checkups` (
-- Table structure for table `apt_instances`
--
DROP TABLE IF EXISTS `apt_instance_history`;
CREATE TABLE `apt_instance_history` (
`uuid` varchar(40) NOT NULL default '',
`profile_id` int(10) unsigned NOT NULL default '0',
`profile_version` int(10) unsigned NOT NULL default '0',
`slice_uuid` varchar(40) NOT NULL default '',
`creator` varchar(8) NOT NULL default '',
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
`creator_uuid` varchar(40) NOT NULL default '',
`aggregate_urn` varchar(128) default NULL,
`created` datetime default NULL,
`destroyed` datetime default NULL,
PRIMARY KEY (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `apt_instances`
--
DROP TABLE IF EXISTS `apt_instances`;
CREATE TABLE `apt_instances` (
`uuid` varchar(40) NOT NULL default '',
......
#
# Add profile versioning.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBTableExists("apt_instance_history")) {
DBQueryFatal("CREATE TABLE `apt_instance_history` ( ".
" `uuid` varchar(40) NOT NULL default '', ".
" `profile_id` int(10) unsigned NOT NULL default '0', ".
" `profile_version` int(10) unsigned NOT NULL default '0', ".
" `slice_uuid` varchar(40) NOT NULL default '', ".
" `creator` varchar(8) NOT NULL default '', ".
" `creator_idx` mediumint(8) unsigned NOT NULL default '0', ".
" `creator_uuid` varchar(40) NOT NULL default '', ".
" `aggregate_urn` varchar(128) default NULL, ".
" `created` datetime default NULL, ".
" `destroyed` datetime default NULL, ".
" PRIMARY KEY (`uuid`) ".
" ) ENGINE=MyISAM DEFAULT CHARSET=latin1");
}
return 0;
}
# Local Variables:
# mode:perl
# End:
<?php
#
# Copyright (c) 2000-2014 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");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
$page_title = "My Profiles";
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLogin($check_status);
if (!$this_user) {
RedirectLoginPage();
exit();
}
SPITHEADER(1);
if (!ISADMIN()) {
SPITUSERERROR("Not enough permission to view this page!");
}
$instances = array();
#
# First existing instances and then the history table.
#
$query1_result =
DBQueryFatal("select i.uuid,i.profile_version,i.created,'' as destroyed, ".
" i.creator,p.uuid as profile_uuid,p.name,p.pid ".
" from apt_instances as i ".
"left join apt_profile_versions as p on ".
" p.profileid=i.profile_id and ".
" p.version=i.profile_version ".
"order by i.created desc");
$query2_result =
DBQueryFatal("select h.uuid,h.profile_version,h.created,h.destroyed, ".
" h.creator,p.uuid as profile_uuid,p.name,p.pid ".
" from apt_instance_history as h ".
"left join apt_profile_versions as p on ".
" p.profileid=h.profile_id and ".
" p.version=h.profile_version ".
"order by h.created desc");
if (mysql_num_rows($query1_result) == 0 &&
mysql_num_rows($query1_result) == 0) {
$message = "<b>Oops, there is no activity to show you.</b><br>";
SPITUSERERROR($message);
exit();
}
foreach (array($query1_result, $query2_result) as $query_result) {
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$pname = $row["name"];
$pproj = $row["pid"];
$puuid = $row["profile_uuid"];
$pversion = $row["profile_version"];
$created = $row["created"];
$destroyed = $row["destroyed"];
$creator = $row["creator"];
$instance = array();
$instance["uuid"] = $uuid;
$instance["p_name"] = $pname;
$instance["p_pid"] = $pproj;
$instance["p_uuid"] = $puuid;
$instance["p_version"] = $pversion;
$instance["creator"] = $creator;
$instance["created"] = $created;
$instance["destroyed"] = $destroyed;
$instances[] = $instance;
}
}
# Place to hang the toplevel template.
echo "<div id='activity-body'></div>\n";
echo "<script type='text/javascript'>\n";
echo " window.AJAXURL = 'server-ajax.php';\n";
echo "</script>\n";
echo "<script type='text/plain' id='instances-json'>\n";
echo json_encode($instances);
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/activity'></script>\n";
SPITFOOTER();
?>
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup',
'js/lib/text!template/activity.html'],
function (_, sup, profileString)
{
'use strict';
var ajaxurl = null;
var profileTemplate = _.template(profileString);
function initialize()
{
window.APT_OPTIONS.initialize(sup);
ajaxurl = window.AJAXURL;
var instances =
JSON.parse(_.unescape($('#instances-json')[0].textContent));
var activity_html = profileTemplate({instances: instances});
$('#activity-body').html(activity_html);
}
$(document).ready(initialize);
});
......@@ -63,6 +63,7 @@ function (_, sup, filesize, ShowImagingModal,
canmodify: window.CANMODIFY,
canpublish: window.CANPUBLISH,
history: window.HISTORY,
activity: window.ACTIVITY,
snapuuid: (window.SNAPUUID || null),
general_error: (errors.error || ''),
});
......
require(window.APT_OPTIONS.configObject,
['underscore', 'js/quickvm_sup',
'js/lib/text!template/profile-activity.html'],
function (_, sup, profileString)
{
'use strict';
var ajaxurl = null;
var profileTemplate = _.template(profileString);
function initialize()
{
window.APT_OPTIONS.initialize(sup);
ajaxurl = window.AJAXURL;
var instances =
JSON.parse(_.unescape($('#instances-json')[0].textContent));
var activity_html = profileTemplate({instances: instances});
$('#activity-body').html(activity_html);
}
$(document).ready(initialize);
});
require(window.APT_OPTIONS.configObject,
['js/quickvm_sup'],
function (sup)
['underscore', 'js/quickvm_sup',
'js/lib/text!template/profile-history.html'],
function (_, sup, profileString)
{
'use strict';
var ajaxurl = null;
var profileTemplate = _.template(profileString);
function initialize()
{
window.APT_OPTIONS.initialize(sup);
ajaxurl = window.AJAXURL;
$('.showtopo_modal_button').click(function (event) {
event.preventDefault();
ShowTopology($(this).data("profile"));
});
}
var profiles = JSON.parse(_.unescape($('#profiles-json')[0].textContent));
var profile_html = profileTemplate({profiles: profiles});
$('#history-body').html(profile_html);
function ShowTopology(profile)
{
var profile;
var index;
var callback = function(json) {
if (json.code) {
alert("Failed to get rspec for topology viewer: " + json.value);
return;
}
sup.ShowModal("#quickvm_topomodal");
$("#quickvm_topomodal").one("shown.bs.modal", function () {
sup.maketopmap('#showtopo_nopicker', json.value.rspec, null);
});
};
var $xmlthing = sup.CallServerMethod(ajaxurl,
"myprofiles",
"GetProfile",
{"uuid" : profile});
$xmlthing.done(callback);
console.info(profiles);
}
$(document).ready(initialize);
});
......@@ -23,6 +23,7 @@
#
chdir("..");
include_once("webtask.php");
include_once("geni_defs.php");
chdir("apt");
include_once("profile_defs.php");
include_once("instance_defs.php");
......@@ -229,7 +230,7 @@ function Do_GuestInstantiate()
# Need to form a guest id. Ideally, lets look for a guest user
# with the same email and use that.
#
$geniuser = GeniUser::LookupByEmail($this_user->email());
$geniuser = GeniUser::LookupByEmail("sa", $this_user->email());
if ($geniuser) {
$guestid = $geniuser->uid();
$token = $geniuser->auth_token();
......
......@@ -71,6 +71,7 @@ function SPITFORM($formfields, $errors)
$canmodify = 0;
$canpublish = 0;
$history = 0;
$activity = 0;
if ($action == "edit") {
$button_label = "Modify";
......@@ -80,6 +81,7 @@ function SPITFORM($formfields, $errors)
$history = ($profile->HasHistory() ? 1 : 0);
$canmodify = ($profile->CanModify() ? 1 : 0);
$canpublish = ($profile->CanPublish() ? 1 : 0);
$activity = ($profile->HasActivity() ? 1 : 0);
if ($canmodify) {
$title = "Modify Profile";
}
......@@ -149,6 +151,7 @@ function SPITFORM($formfields, $errors)
echo " window.CANMODIFY= $canmodify;\n";
echo " window.CANPUBLISH= $canpublish;\n";
echo " window.HISTORY = $history;\n";
echo " window.ACTIVITY = $activity;\n";
echo " window.TITLE = '$title';\n";
echo " window.BUTTONLABEL = '$button_label';\n";
if (isset($snapuuid)) {
......
<?php
#
# Copyright (c) 2000-2014 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");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
$page_title = "My Profiles";
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("uuid", PAGEARG_STRING);
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLogin($check_status);
if (!$this_user) {
RedirectLoginPage();
exit();
}
SPITHEADER(1);
$profile = Profile::Lookup($uuid);
if (!$profile) {
SPITUSERERROR("No such profile!");
}
else if ($this_user->uid_idx() != $profile->creator_idx() && !ISADMIN()) {
SPITUSERERROR("Not enough permission!");
}
$profileid = $profile->profileid();
$instances = array();
#
# First existing instances and then the history table.
#
$query_result =
DBQueryFatal("(select i.uuid,i.profile_version,i.created,'' as destroyed, ".
" i.creator,p.uuid as profile_uuid ".
" from apt_instances as i ".
"left join apt_profile_versions as p on ".
" p.profileid=i.profile_id and ".
" p.version=i.profile_version ".
"where i.profile_id='$profileid') ".
"union ".
"(select h.uuid,h.profile_version,h.created,h.destroyed, ".
" h.creator,p.uuid as profile_uuid ".
" from apt_instance_history as h ".
"left join apt_profile_versions as p on ".
" p.profileid=h.profile_id and ".
" p.version=h.profile_version ".
"where h.profile_id='$profileid') ".
"order by created desc");
if (mysql_num_rows($query_result) == 0) {
$message = "<b>Oops, there is no activity to show you.</b><br>";
SPITUSERERROR($message);
exit();
}
while ($row = mysql_fetch_array($query_result)) {
$uuid = $row["uuid"];
$puuid = $row["profile_uuid"];
$pversion = $row["profile_version"];
$created = $row["created"];
$destroyed = $row["destroyed"];
$creator = $row["creator"];
$instance = array();
$instance["uuid"] = $uuid;
$instance["p_uuid"] = $puuid;
$instance["p_version"] = $pversion;
$instance["creator"] = $creator;
$instance["created"] = $created;
$instance["destroyed"] = $destroyed;
$instances[] = $instance;
}
# Place to hang the toplevel template.
echo "<div id='activity-body'></div>\n";
echo "<script type='text/javascript'>\n";
echo " window.AJAXURL = 'server-ajax.php';\n";
echo "</script>\n";
echo "<script type='text/plain' id='instances-json'>\n";
echo json_encode($instances);
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/profile-activity'></script>\n";
SPITFOOTER();
?>
......@@ -52,6 +52,7 @@ else if ($this_user->uid_idx() != $profile->creator_idx() && !ISADMIN()) {
SPITUSERERROR("Not enough permission!");
}
$profileid = $profile->profileid();
$profiles = array();
$query_result =
DBQueryFatal("select v.*,DATE(v.created) as created ".
......@@ -59,25 +60,6 @@ $query_result =
"where v.profileid='$profileid' ".
"order by v.created desc");
echo "<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'>\n";
echo " <table class='table table-striped table-condensed'>
<thead>
<tr>
<th>Vers</th>
<th>Creator</th>
<th>Description</th>
<th>Created</th>
<th>Published</th>
<th>From</th>
</tr>
</thead>
<tbody>\n";
while ($row = mysql_fetch_array($query_result)) {
$idx = $row["profileid"];
$uuid = $row["uuid"];
......@@ -87,9 +69,6 @@ while ($row = mysql_fetch_array($query_result)) {
$pid = $row["pid"];
$created = $row["created"];
$published = $row["published"];
$public = $row["public"];
$listed = ($row["listed"] ? "Yes" : "No");
$shared = $row["shared"];
$creator = $row["creator"];
$rspec = $row["rspec"];
$desc = '';
......@@ -100,64 +79,33 @@ while ($row = mysql_fetch_array($query_result)) {
if (!$published) {
$published = " ";
}
if ($public)
$privacy = "Public";
elseif ($shared)
$privacy = "Shared";
else
$privacy = "Private";
$parsed_xml = simplexml_load_string($rspec);
if ($parsed_xml &&
$parsed_xml->rspec_tour && $parsed_xml->rspec_tour->description) {
$desc = $parsed_xml->rspec_tour->description;
$desc = (string) $parsed_xml->rspec_tour->description;
}
echo " <tr>
<td>
<a href='manage_profile.php?action=edit&uuid=$uuid'>$version</a>
</td>
<td>$creator</td>
<td>$desc</td>
<td>$created</td>
<td>$published</td>
<td style='text-align:center'>
<a href='manage_profile.php?action=edit&uuid=$uuid'>$pversion</a>
</td>
</tr>\n";
$profile = array();
$profile["uuid"] = $uuid;
$profile["version"] = $version;
$profile["creator"] = $creator;
$profile["description"] = $desc;
$profile["created"] = $created;
$profile["published"] = $published;
$profile["parent_version"] = $pversion;
$profiles[] = $profile;
}
echo " </tbody>
</table>
</div>
</div>\n";
echo "<!-- This is the topology view modal -->
<div id='quickvm_topomodal' class='modal fade'>
<div class='modal-dialog' id='showtopo_dialog'>
<div class='modal-content'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>
&times;</button>
<h3>Topology Viewer</h3>
</div>
<div class='modal-body'>
<!-- This topo diagram goes inside this div -->
<div class='panel panel-default'
id='showtopo_container'>
<div class='panel-body'>
<div id='showtopo_nopicker' class='jacks'></div>
</div>
</div>
</div>
</div>
</div>
</div>\n";
# Place to hang the toplevel template.
echo "<div id='history-body'></div>\n";
echo "<script type='text/javascript'>\n";
echo " window.AJAXURL = 'server-ajax.php';\n";
echo "</script>\n";
echo "<script type='text/plain' id='profiles-json'>\n";
echo json_encode($profiles);
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
echo "<script src='js/lib/bootstrap.js'></script>\n";
echo "<script src='js/lib/require.js' data-main='js/profile-history'></script>\n";
......
......@@ -264,5 +264,39 @@ class Profile
return ($this->version() >= $row[0] ? 1 : 0);
}
#
# Has a profile been instantiated?
#
function HasActivity() {
$profileid = $this->profileid();
$query_result =
DBQueryWarn("select count(h.uuid) from apt_instance_history as h ".
"where h.profile_id='$profileid'");
if (!$query_result) {
return 0;
}
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_row($query_result);
if ($row[0] > 0) {
return 1;
}
}
$query_result =
DBQueryWarn("select count(uuid) from apt_instances ".
"where profile_id='$profileid'");
if (!$query_result) {
return 0;
}
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_row($query_result);
if ($row[0] > 0) {
return 1;
}
}
return 0;
}
}
?>
......@@ -164,9 +164,8 @@ function SPITHEADER($thinheader = 0)
href='instantiate.php'>Home</a></form></li>
<li class='apt-left'><form><a class='btn btn-quickvm-home navbar-btn'
href='http://docs.aptlab.net' target='_blank'>Manual</a></form></li>\n";
if (!$disable_accounts) {
echo " <li id='quickvm_actions_menu' class='dropdown apt-left ".
(!$login_user ? "hidden" : "") . "'>" .
if ($login_user) {
echo " <li id='quickvm_actions_menu' class='dropdown apt-left'> ".
"<a href='#' class='dropdown-toggle' data-toggle='dropdown'>
Actions <b class='caret'></b></a>
<ul class='dropdown-menu'>
......@@ -174,8 +173,12 @@ function SPITHEADER($thinheader = 0)
<li><a href='myexperiments.php'>My Experiments</a></li>
<li><a href='manage_profile.php'>Create Profile</a></li>
<li class='divider'></li>
<li><a href='logout.php'>Logout</a></li>
</ul>
<li><a href='logout.php'>Logout</a></li>";
if (ISADMIN()) {
echo " <li class='divider'></li>
<li><a href='activity.php'>Activity</a></li>";
}
echo " </ul>
</li>\n";
}
echo " </ul>
......
<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'>
<table class='table table-striped table-condensed'>
<thead>
<tr>
<th>Profile</th>
<th>Pid</th>
<th>Vers</th>
<th>Creator</th>
<th>Created</th>
<th>Destroyed</th>
</tr>
</thead>
<tbody>
<% _.each(instances, function(instance) { %>
<tr>
<td><%- instance.p_name %></a></td>
<td><%- instance.p_pid %></a></td>
<td>
<a href='manage_profile.php?action=edit&uuid=<%- instance.p_uuid %>'>
<%- instance.p_version %></a>
</td>
<td><%- instance.creator %></td>
<td><%- instance.created %></td>
<td><%- instance.destroyed %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
</div>
......@@ -43,6 +43,14 @@
type='button'>History
</a>
<% } %>
<% if (activity) { %>
<a class='btn btn-info btn-xs pull-left'
id='profile_activity_button'
style='margin-right: 10px; font-size: 12px'
href='profile-activity.php?uuid=<%= uuid %>'
type='button'>Activity
</a>
<% } %>
</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'>
<table class='table table-striped table-condensed'>
<thead>
<tr>
<th>Vers</th>
<th>Creator</th>
<th>Created</th>
<th>Destroyed</th>
</tr>
</thead>
<tbody>
<% _.each(instances, function(instance) { %>
<tr>
<td>
<a href='manage_profile.php?action=edit&uuid=<%- instance.p_uuid %>'>
<%- instance.p_version %></a>
</td>
<td><%- instance.creator %></td>
<td><%- instance.created %></td>
<td><%- instance.destroyed %></td>
</tr>
<% }); %>
</tbody>
</table>
</div>