Commit 645dccd7 authored by Leigh B Stoller's avatar Leigh B Stoller

The Web UI part of repository-based profiles.

parent 92d64446
......@@ -131,6 +131,9 @@ class Instance
function servername() { return $this->field('servername'); }
function aggregate_urn(){ return $this->field('aggregate_urn'); }
function private_key() { return $this->field('privkey'); }
function repourl() { return $this->field('repourl'); }
function reporef() { return $this->field('reporef'); }
function repohash() { return $this->field('repohash'); }
function isopenstack() { return $this->field('isopenstack'); }
function openstack_utilization() {
return $this->field('openstack_utilization');
......
......@@ -103,11 +103,22 @@ function Do_GetParameters()
SPITAJAX_ERROR(1, "Not enough permission to instantiate profile");
return;
}
if (!$profile->isParameterized()) {
#
# For a repo-based profile, the paramdefs will be passed in.
# Note that different refspecs might be parameterized, but others not.
#
if ($profile->repourl() &&
isset($ajax_args["paramdefs"]) && $ajax_args["paramdefs"] != "") {
$paramdefs = $ajax_args["paramdefs"];
}
elseif (!$profile->isParameterized()) {
SPITAJAX_ERROR(1, "Not a parameterized profile");
return;
}
list ($formfrag, $defaults) = $profile->GenerateFormFragment();
else {
$paramdefs = $profile->paramdefs();
}
list ($formfrag, $defaults) = $profile->GenerateFormFragment($paramdefs);
SPITAJAX_RESPONSE(array("formfrag" => htmlentities($formfrag),
"defaults" => $defaults));
}
......@@ -820,8 +831,29 @@ function Do_Submit()
# we could get an rspec.
#
if ($profile->isParameterized() && $this_user &&
isset($formfields["pp_rspec"]) && $formfields["pp_rspec"] != "") {
$args["rspec"] = $formfields["pp_rspec"];
isset($formfields["rspec"]) && $formfields["rspec"] != "") {
$args["rspec"] = $formfields["rspec"];
}
#
# For a repo-based profile, we might get an rspec and/or a script.
# Need to deal with the paramdefs block too.
#
if ($profile->repourl() && $this_user) {
if (isset($formfields["script"]) && $formfields["script"] != "") {
$args["script"] = $formfields["script"];
}
if (isset($formfields["rspec"]) && $formfields["rspec"] != "") {
$args["rspec"] = $formfields["rspec"];
}
# If the user is instantiating the profile version, we will not
# get these.
if (isset($formfields["reporef"]) && $formfields["reporef"] != "") {
$args["reporef"] = $formfields["reporef"];
}
if (isset($formfields["repohash"]) && $formfields["repohash"] != "") {
$args["repohash"] = $formfields["repohash"];
}
}
$aggregate_urn = "";
......
......@@ -58,6 +58,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
"asguest", PAGEARG_BOOLEAN,
"default", PAGEARG_STRING,
"from", PAGEARG_STRING,
"refspec", PAGEARG_STRING,
"formfields", PAGEARG_ARRAY);
if ($ISAPT && !$this_user) {
......@@ -317,6 +318,7 @@ function SPITFORM($formfields, $newuser, $errors)
global $TBBASE, $APTMAIL, $ISAPT, $ISCLOUD, $ISPNET, $PORTAL_NAME;
global $profile_array, $this_user, $profilename, $profile;
global $projlist, $skipfirststep, $TBMAINSITE;
global $refspec;
$showabout = ($ISAPT && !$this_user ? 1 : 0);
$registered = (isset($this_user) ? "true" : "false");
......@@ -400,6 +402,15 @@ function SPITFORM($formfields, $newuser, $errors)
echo " window.SKIPFIRSTSTEP = " . ($skipfirststep ? "true" : "false") . ";\n";
echo " window.PORTAL_NAME = '$PORTAL_NAME';\n";
echo " window.USERNAME = '" . $formfields["username"] . "';\n";
if (isset($profile) && $profile->repourl()) {
echo " window.FROMREPO = true;\n";
if (isset($refspec)) {
echo " window.REFSPEC = '$refspec';\n";
}
}
else {
echo " window.FROMREPO = false;\n";
}
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
......@@ -411,8 +422,10 @@ function SPITFORM($formfields, $newuser, $errors)
REQUIRE_FORMHELPERS();
REQUIRE_FILESTYLE();
REQUIRE_MARKED();
REQUIRE_MOMENT();
REQUIRE_JACKS();
REQUIRE_JQUERY_STEPS();
AddLibrary("js/gitrepo.js");
SPITREQUIRE("js/instantiate-new.js");
}
......
......@@ -63,6 +63,10 @@ $(function () {
if (_.has(item.dataset, "colsize")) {
colsize = item.dataset['colsize'];;
}
// Override wide setting per field
if (_.has(item.dataset, "wide")) {
wide = item.dataset['wide'];;
}
/*
* Wrap in a div we can name. We assume the form
......
......@@ -412,7 +412,9 @@ $(function ()
disabled: true,
versions: [],
withpublishing: false,
genilib_editor: true
genilib_editor: true,
canrepo: false,
fromrepo: false
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide": false });
......@@ -464,7 +466,9 @@ $(function ()
disabled: true,
versions: [],
withpublishing: false,
genilib_editor: true
genilib_editor: true,
canrepo: false,
fromrepo: false
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{'wide': false });
......
$(function () {
window.gitrepo = (function () {
var repoString = APT_OPTIONS.fetchTemplate('gitrepo-picker');
var repoTemplate = _.template(repoString);
var branchlist = null;
var taglist = null;
/*
* Get the branches and tags for a profile, and draw the picker.
*/
function InitRepoPicker(uuid, change_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
console.info(json.value);
return;
}
branchlist = json.value.branchlist;
taglist = json.value.taglist;
ShowRepoPicker(uuid, change_callback);
}
// Visible cue that something is happening
$('#gitpicker-div table').css("opacity", 0.4);
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetBranchList",
{"uuid" : uuid});
xmlthing.done(callback);
}
function ShowRepoPicker(uuid, change_callback)
{
var html = repoTemplate({"branches" : branchlist,
"tags" : taglist,
"uuid" : uuid});
$('#gitpicker-div').removeClass("hidden");
$('#gitpicker-div table').css("opacity", '');
$('#gitrepo-picker').html(html);
if (change_callback !== undefined) {
$('.branch-button').click(function (event) {
event.preventDefault();
change_callback($(this).data("which"));
});
}
}
/*
* Get source code for a branch or tag and send it back to caller.
* Also update the info panel as on the manage/show page.
*/
function GetRepoSource(uuid, refspec, caller_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
sup.HideWaitWait();
sup.SpitOops(json.value);
caller_callback(null);
return;
}
sup.HideWaitWait(function() {
caller_callback(json.value.script, json.value.hash);
});
GetCommitInfo(uuid, refspec);
}
sup.ShowWaitWait("We are getting the source code from the " +
"repository. Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetRepoSource",
{"uuid" : uuid,
"refspec" : refspec});
xmlthing.done(callback);
}
/*
* The manage/show page both have a panel for the currently
* loaded commit. Update that panel when we get new source.
*/
function UpdateInfoPanel(blob)
{
$('#repoinfo-panel .commit-hash').text(blob.hash);
$('#repoinfo-panel .commit-author').html(blob.author);
$('#repoinfo-panel .commit-refspec').html(blob.refspec);
$('#repoinfo-panel .commit-date')
.html(moment(blob.date).format("lll"));
var log = blob.log;
if (log.length <= 20) {
$('#repoinfo-panel .commit-log-start').html(log);
$('#repoinfo-panel .commit-log .log').addClass("hidden");
}
else {
$('#repoinfo-panel .commit-log-start').html(log.substr(0,20));
$('#repoinfo-panel .commit-log .log').popover('destroy');
// DUMB! destroy is async.
setTimeout(function () {
$('#repoinfo-panel .commit-log .log').popover({
trigger: 'hover',
placement: 'auto',
container: 'body',
content: "<pre>" + log + "</pre>",
});}, 200);
$('#repoinfo-panel .commit-log .log').removeClass("hidden");
}
$('#repoinfo-panel .panel-body').css("opacity", '');
$('#repoinfo-panel').removeClass("hidden");
}
/*
* Ask for commit info, then update the info panel.
*/
function GetCommitInfo(uuid, refspec)
{
if (refspec === undefined) {
refspec = "refs/heads/master";
}
var callback = function(json) {
console.info(json);
if (json.code) {
console.info("GetCommitInfo", json.value);
return;
}
json.value.refspec = refspec;
UpdateInfoPanel(json.value);
}
// Visible cue that something is happening
$('#repoinfo-panel .panel-body').css("opacity", 0.4);
var args = {"uuid" : uuid, "refspec" : refspec}
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetCommitInfo", args);
xmlthing.done(callback);
}
/*
* Update from repo, possibly getting a new script or rspec.
*/
function UpdateRepo(uuid, caller_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
sup.HideWaitWait();
sup.SpitOops(json.value);
caller_callback(null);
return;
}
sup.HideWaitWait(function() {
caller_callback(json.value);
});
}
sup.ShowWaitWait("We are attempting to pull from your repository. "+
"Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"UpdateRepository",
{uuid : uuid});
xmlthing.done(callback);
}
// Exports from this module for use elsewhere
return {
InitRepoPicker: InitRepoPicker,
GetRepoSource: GetRepoSource,
UpdateRepo: UpdateRepo,
GetCommitInfo: GetCommitInfo,
};
})();
});
......@@ -31,6 +31,7 @@ $(function ()
var amValueToKey = {};
var showpicker = 0;
var portal = null;
var fromrepo = false;
var registered = false;
var JACKS_NS = "http://www.protogeni.net/resources/rspec/ext/jacks/1";
var jacks = {
......@@ -59,6 +60,7 @@ $(function ()
multisite = window.MULTISITE;
portal = window.PORTAL;
ajaxurl = window.AJAXURL;
fromrepo = window.FROMREPO;
doconstraints = window.DOCONSTRAINTS;
showpicker = window.SHOWPICKER;
......@@ -1172,8 +1174,87 @@ $(function ()
var $xmlthing = sup.CallServerMethod(ajaxurl,
"instantiate", "GetProfile",
{"uuid" : profile});
/*
* If a repo-based and we got a specific branch/tag, we have to
* get the source for that, since it will be different then what
* is stored in the profile descriptor.
*/
if (fromrepo && window.REFSPEC !== undefined) {
var which = window.REFSPEC;
$xmlthing.done(function(json) {
gitrepo.GetRepoSource(profile, which, function(source, hash) {
var pythonRe = /^import/m;
$('#repohash').val(hash);
$('#reporef').val(which);
if (pythonRe.test(source)) {
ConvertScript(source, function(rspec, paramdefs) {
// Need to pass these along at submit.
$('#rspec_textarea').val(rspec);
$('#script_textarea').val(source);
json.value.rspec = rspec;
json.value.isscript = true;
//
// We can get a parameterized profile, or not.
//
if (paramdefs === undefined) {
json.value.ispprofile = false;
}
else {
$('#paramdefs').val(paramdefs);
json.value.ispprofile = true;
}
callback(json);
});
}
else {
// New rspec, proceed
json.value.rspec = source;
// Need to pass this along at submit.
$('#rspec_textarea').val(source);
callback(json);
}
});
});
}
else {
$xmlthing.done(callback);
}
}
//
// Pass a geni-lib script to the server to run (convert to XML).
// We use this on repo-based profiles, where we have to get the
// source code from the repo, and convert to an rspec.
//
function ConvertScript(script, continuation)
{
var callback = function(json) {
sup.HideWaitWait();
if (json.code) {
sup.SpitOops("oops",
"<pre><code>" +
$('<div/>').text(json.value).html() +
"</code></pre>");
return;
}
if (json.value.rspec != "") {
continuation(json.value.rspec, json.value.paramdefs);
}
}
sup.ShowWaitWait("We are converting the geni-lib script to an rspec. " +
"Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"CheckScript",
{"script" : script,
"getparams" : true});
xmlthing.done(callback);
}
/*
* Callback from the PP configurator. Stash rspec into the form.
......@@ -1182,7 +1263,7 @@ $(function ()
// If not a registered user, we do not get an rspec back, since
// the user is not allowed to change the configuration.
if (newRspec) {
$('#pp_rspec_textarea').val(newRspec);
$('#rspec_textarea').val(newRspec);
selected_rspec = newRspec;
CreateAggregateSelectors(newRspec);
}
......
......@@ -24,6 +24,7 @@ $(function ()
var doconstraints = 0;
var amValueToKey = {};
var showpicker = 0;
var fromrepo = false;
var portal = null;
var registered = false;
var JACKS_NS = "http://www.protogeni.net/resources/rspec/ext/jacks/1";
......@@ -53,6 +54,7 @@ $(function ()
multisite = window.MULTISITE;
portal = window.PORTAL;
ajaxurl = window.AJAXURL;
fromrepo = window.FROMREPO;
doconstraints = window.DOCONSTRAINTS;
showpicker = window.SHOWPICKER;
......@@ -860,7 +862,7 @@ $(function ()
var xml = $(xmlDoc);
/*
* We now use the desciption from inside the rspec, unless there
* We now use the description from inside the rspec, unless there
* is none, in which case look to see if the we got one in the
* rpc reply, which we will until all profiles converted over to
* new format rspecs.
......@@ -882,8 +884,29 @@ $(function ()
var $xmlthing = sup.CallServerMethod(ajaxurl,
"instantiate", "GetProfile",
{"uuid" : profile});
/*
* If a repo-based and we got a specific branch/tag, we have to
* get the source for that, since it will be different then what
* is stored in the profile descriptor.
*/
console.info("fee", fromrepo, window.BRANCH, window.TAG);
if (fromrepo &&
(window.BRANCH !== undefined || window.TAG !== undefined)) {
var which =
(window.BRANCH !== undefined ? window.BRANCH : window.TAG);
$xmlthing.done(function(json) {
gitrepo.GetRepoSource(uuid, which, function(source) {
console.info("foo");
callback(json);
});
});
}
else {
$xmlthing.done(callback);
}
}
/*
* Callback from the PP configurator. Stash rspec into the form.
......@@ -892,7 +915,7 @@ $(function ()
// If not a registered user, we do not get an rspec back, since
// the user is not allowed to change the configuration.
if (newRspec) {
$('#pp_rspec_textarea').val(newRspec);
$('#rspec_textarea').val(newRspec);
selected_rspec = newRspec;
CreateAggregateSelectors(newRspec);
}
......
......@@ -2,7 +2,7 @@ $(function ()
{
'use strict';
var templates = APT_OPTIONS.fetchTemplateList(['manage-profile', 'waitwait-modal', 'renderer-modal', 'showtopo-modal', 'oops-modal', 'rspectextview-modal', 'guest-instantiate', 'publish-modal', 'instantiate-modal', 'share-modal']);
var templates = APT_OPTIONS.fetchTemplateList(['manage-profile', 'waitwait-modal', 'renderer-modal', 'showtopo-modal', 'oops-modal', 'rspectextview-modal', 'guest-instantiate', 'publish-modal', 'instantiate-modal', 'share-modal', 'gitrepo-picker']);
var manageString = templates['manage-profile'];
var waitwaitString = templates['waitwait-modal'];
var rendererString = templates['renderer-modal'];
......@@ -13,7 +13,7 @@ $(function ()
var publishString = templates['publish-modal'];
var instantiateString = templates['instantiate-modal'];
var shareString = templates['share-modal'];
var gitrepoString = templates['gitrepo-picker'];
var profile_uuid = null;
var profile_name = '';
......@@ -23,6 +23,8 @@ $(function ()
var snapping = 0;
var gotrspec = 0;
var gotscript = 0;
var fromrepo = 0;
var repohash = null;
var ajaxurl = "";
var amlist = null;
var modified = false;
......@@ -41,6 +43,7 @@ $(function ()
var guestInstTemplate = _.template(guestInstantiateString);
var InstTemplate = _.template(instantiateString);
var shareTemplate = _.template(shareString);
var gitrepoTemplate = _.template(gitrepoString);
var stepsInitialized = false;
var pythonRe = /^import/m;
......@@ -75,6 +78,12 @@ $(function ()
if (_.has(fields, "profile_script") && fields["profile_script"] != "") {
gotscript = 1;
}
// Ditto a repourl
if (_.has(fields, "profile_repourl") &&
fields["profile_repourl"] != "") {
fromrepo = 1;
repohash = fields["profile_repohash"];
}
// If this is an existing profile, stash the name/project
if (_.has(fields, "profile_name")) {
......@@ -127,7 +136,9 @@ $(function ()
disabled: window.DISABLED,
versions: versions,
withpublishing: window.WITHPUBLISHING,
genilib_editor: false
genilib_editor: false,
canrepo: window.CANREPO,
fromrepo: fromrepo
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide" : true});
......@@ -155,6 +166,11 @@ $(function ()
$('#rspectext_div').html(rspectext_html);
$('#share_div').html(shareTemplate({formfields: fields}))
// Fireoff repo stuff now.
if (fromrepo) {
SetupRepo();
}
//
// Fix for filestyle problem; not a real class I guess, it
// runs at page load, and so the filestyle'd button in the
......@@ -214,21 +230,6 @@ $(function ()
reader.readAsText(this.files[0]);
});
// Handler for all paths to rspec change (file upload, jacks, edit).
function changeRspec(newRspec)
{
if (pythonRe.test(newRspec) || tclRe.test(newRspec)) {
//
// A geni-lib script. We are going to pass the script to
// the server to be "run", which returns XML.
//
if (newRspec != $('#profile_script_textarea').val()) {
checkScript(newRspec);
}
return;
}
NewRspecHandler(newRspec);
}
$('#edit_topo_modal_button').click(function (event) {
event.preventDefault();
editor.show($('#profile_rspec_textarea').val(), changeRspec);
......@@ -242,7 +243,7 @@ $(function ()
var source = $.trim($('#profile_script_textarea').val());
var type = "source";
if (source.length > 0 && window.ACTION === 'edit') {
if (source.length > 0 && window.ACTION === 'edit' && !fromrepo) {
openEditor();
} else {
if (source.length === 0) {
......@@ -262,10 +263,19 @@ $(function ()
sup.ClearDownloadOnClick($('#rspec_modal_download_button'));
// $('#rspec_modal_download_button').addClass("hidden");
}
if (!fromrepo) {
$('#rspec_modal_upload_span').removeClass("hidden");
$('#rspec_modal_editbuttons').removeClass("hidden");
$('#rspec_modal_viewbuttons').addClass("hidden");
$('#modal_profile_rspec_textarea').prop("readonly", false);
}
else {
// No editing repo-based profiles
$('#rspec_modal_upload_span').addClass("hidden");
$('#rspec_modal_editbuttons').addClass("hidden");
$('#rspec_modal_viewbuttons').removeClass("hidden");
$('#modal_profile_rspec_textarea').prop("readonly", true);
}
$('#modal_profile_rspec_textarea').val(source);
$('#rspec_modal').modal({'backdrop':'static','keyboard':false});
$('#rspec_modal').modal('show');
......@@ -309,7 +319,7 @@ $(function ()
$('#rspec_modal').on('shown.bs.modal', function() {
var source = $('#modal_profile_rspec_textarea').val();
var mode = "text/xml";
var readonly = window.CLONING ||
var readonly = window.CLONING || fromrepo ||
$('#modal_profile_rspec_textarea').prop("readonly");
// Need to determine the mode.
......@@ -327,7 +337,7 @@ $(function ()
$('#modal_profile_rspec_div').prepend(elt);
}, {
value: source,