Commit 7c38809d authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

rspecs are so passe ...

Redo the rspectogenilib converter with the goal of supporting both
translation *and* regression testing. A new library is responsible for
taking the output of libXML and creating a data structure representing
the rspec. While this is being done, we look for any constructs or
attributes we cannot handle with geni-lib and report that as an error;
we are not going to convert an rspec unless we can do it correctly. and
completely.

Regression testing is done with another part of this library, that knows
how to compare each element of two rspecs. Basically start at the root
and compare all the way down, failing if the two "parses" of the XML are
not equal at any level.

rspectogenilib now has an option that does regression testing by running
the new geni-lib and comparing the resulting rspec against the original.

On the UI side, there is a new button on existing rspec based
profiles (currently only for admin and studs) called "Convert to
geni-lib" that runs the converter to convert the profile to geni-lib.
The user does not have to accept the new script of course.

However, a converted profile is marked in the database, and the user can
still use Jacks on it, we just run rspectogenilib geni-lib again on the
new rspec. If the user edits the geni-lib, we switch back to normal
geni-lib (clear the flag) when the new version is saved, and Jacks is
once again read-only. This is explained in the UI, and is one of the
things people need to give comment on.

There is also a mode on the Create Profile page for converting new rspec
based profiles to geni-lib, but that is fully turned off for now, we can
get to that later.
parent 57c22f07
......@@ -468,6 +468,11 @@ sub Create($$$$$$)
$vquery .= ",repohash=" . DBQuoteSpecial($argref->{'repohash'});
$vquery .= ",repokey=" . DBQuoteSpecial($argref->{'repokey'});
}
if (exists($argref->{'portal_converted'}) &&
$argref->{'portal_converted'} ne "") {
$vquery .= ",portal_converted=" .
DBQuoteSpecial($argref->{'portal_converted'});
}
# Back to the main table.
$cquery .= ",uuid='$puuid'";
......
This diff is collapsed.
......@@ -37,7 +37,7 @@ BIN_SCRIPTS = manage_profile manage_instance manage_dataset \
SBIN_SCRIPTS = apt_daemon aptevent_daemon portal_xmlrpc apt_checkup \
portal_monitor
LIB_SCRIPTS = APT_Profile.pm APT_Instance.pm APT_Dataset.pm APT_Geni.pm \
APT_Aggregate.pm APT_Utility.pm
APT_Aggregate.pm APT_Utility.pm APT_Rspec.pm
WEB_BIN_SCRIPTS = webmanage_profile webmanage_instance webmanage_dataset \
webcreate_instance webrungenilib webns2rspec webns2genilib \
webrspec2genilib webmanage_reservations webmanage_gitrepo \
......
......@@ -187,6 +187,7 @@ my %xmlfields =
"rspec" => ["rspec", $SLOT_REQUIRED|$SLOT_UPDATE],
"script" => ["script", $SLOT_OPTIONAL|$SLOT_UPDATE],
"repourl" => ["repourl", $SLOT_OPTIONAL],
"portal_converted" => ["portal_converted", $SLOT_OPTIONAL|$SLOT_UPDATE],
);
if ($action eq "update") {
......@@ -660,12 +661,14 @@ sub ModifyProfileInternal($$$)
$webtask->newProfile($profile->uuid())
if (defined($webtask));
}
foreach my $key ("rspec", "script", "paramdefs", "repohash") {
foreach my $key ("rspec", "script", "paramdefs",
"repohash", "portal_converted") {
$profile->UpdateVersion({$key => $update_args{$key}})
if (exists($update_args{$key}));
}
}
foreach my $key ("rspec", "script", "paramdefs", "repohash") {
foreach my $key ("rspec", "script", "paramdefs",
"repohash", "portal_converted") {
delete($update_args{$key})
if (exists($update_args{$key}));
}
......@@ -780,7 +783,6 @@ sub VerifyXML($$)
$modifiers{$dbslot} = $value;
next;
}
# Now check that the value is legal.
if (! TBcheck_dbslot($value, "apt_profiles",
$dbslot, TBDB_CHECKDBSLOT_ERROR)) {
......
This diff is collapsed.
......@@ -35,7 +35,8 @@ $(function ()
var isppprofile = false;
var isadmin = 0;
var multisite = 0;
var APT_NS = "http://www.protogeni.net/resources/rspec/ext/apt-tour/1";
var APT_NS = "http://www.protogeni.net/resources/rspec/ext/apt-tour/1";
var EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1";
var manageTemplate = _.template(manageString);
var waitwaitTemplate = _.template(waitwaitString);
var rendererTemplate = _.template(rendererString);
......@@ -47,6 +48,7 @@ $(function ()
var gitrepoTemplate = _.template(gitrepoString);
var plistTemplate = _.template(plistString);
var stepsInitialized = false;
var portal_converted = false;
var pythonRe = /^import/m;
var tclRe = /^source tb_compat/m;
......@@ -84,6 +86,10 @@ $(function ()
// Ditto a script.
if (_.has(fields, "profile_script") && fields["profile_script"] != "") {
gotscript = 1;
if (_.has(fields, "portal_converted") &&
fields["portal_converted"] == "yes") {
portal_converted = 1;
}
}
// Ditto a repourl
if (_.has(fields, "profile_repourl") &&
......@@ -124,6 +130,7 @@ $(function ()
notifyupdate: window.UPDATED,
viewing: window.VIEWING,
gotrspec: gotrspec,
gotscript: gotscript,
action: window.ACTION,
button_label: window.BUTTONLABEL,
version_uuid: window.VERSION_UUID,
......@@ -134,6 +141,7 @@ $(function ()
canmodify: window.CANMODIFY,
canpublish: window.CANPUBLISH,
isadmin: window.ISADMIN,
isstud: window.ISSTUD,
history: window.HISTORY,
activity: window.ACTIVITY,
manual: window.MANUAL,
......@@ -158,9 +166,6 @@ $(function ()
$('#waitwait_div').html(waitwait_html);
var showtopo_html = showtopoTemplate({});
$('#showtopomodal_div').html(showtopo_html);
var isViewer = gotscript;
editor = new JacksEditor($('#editmodal_div'),
isViewer, false, false, false, !multisite);
var renderer_html = rendererTemplate({});
$('#renderer_div').html(renderer_html);
var oops_html = oopsTemplate({});
......@@ -199,6 +204,44 @@ $(function ()
placement: 'auto',
container: 'body',
});
// But the repo push URL is handled differently.
var urlstring =
"<div style='width 100%'> "+
" <input readonly type=text id='push-url-input' " +
" style='display:inline; width: 93%; padding: 2px;' " +
" class='form-control input-sm' "+
" value='" + fields.profile_repopushurl + "'>" +
" <a href='#' class='btn btn-xs' id='push-url-copy' " +
" style='padding: 0px'>" +
" <span class='glyphicon glyphicon-copy'></span></a></div>";
$('#push-url').click(function (e) {
console.info("push-url click");
if ($('#push-url-input').length == 0) {
$('#push-url').popover({
html: true,
content: urlstring,
trigger: 'manual',
placement:'auto',
container:'body',
});
$('#push-url').popover('show');
$('#push-url-copy').click(function (e) {
e.preventDefault();
$('#push-url-input').select();
document.execCommand("copy");
$('#push-url').popover('destroy');
});
$('#push-url-input').click(function (e) {
e.preventDefault();
$('#push-url').popover('destroy');
});
}
else {
$('#push-url').popover('destroy');
}
});
// Format dates with moment before display.
$('.format-date').each(function() {
var date = $.trim($(this).html());
......@@ -243,7 +286,17 @@ $(function ()
$('#edit_topo_modal_button').click(function (event) {
event.preventDefault();
editor.show($('#profile_rspec_textarea').val(), changeRspec);
editor.show($('#profile_rspec_textarea').val(),
function (newrspec) {
// Only for a new profile or profile converted
if (!fromrepo && portal_converted) {
ConvertToGenilib(newrspec);
}
else {
// Plain old rspec. SAD!
changeRspec(newrspec);
}
});
});
// The Show Source button.
$('#show_source_modal_button').click(function (event) {
......@@ -251,7 +304,7 @@ $(function ()
// The "source" is either the script or the XML if there
// is no script.
//
var source = $.trim($('#profile_script_textarea').val());
var source = $('#profile_script_textarea').val();
var type = "source";
if (source.length > 0 &&
(window.ACTION === 'edit' ||
......@@ -259,7 +312,7 @@ $(function ()
openEditor(source);
} else {
if (source.length === 0) {
source = $.trim($('#profile_rspec_textarea').val());
source = $('#profile_rspec_textarea').val();
type = "rspec";
}
if (profile_uuid) {
......@@ -415,6 +468,14 @@ $(function ()
event.preventDefault();
HandleGitRepoUpdate();
});
// Convert rspec profile to geni-lib
$('#profile-convert-confirm').click(function (event) {
event.preventDefault();
sup.HideModal('#profile-convert-modal',
function () {
ConvertToGenilib();
});
});
//
// Perform actions on the rspec before submit.
......@@ -547,6 +608,8 @@ $(function ()
$('#profile_instructions').prop("disabled", true);
$('#profile_description').prop("disabled", true);
}
CreateJacksEditor();
//
// Show/Hide the Update Successful animation.
//
......@@ -638,7 +701,30 @@ $(function ()
// the server to be "run", which returns XML.
//
if (newRspec != $('#profile_script_textarea').val()) {
checkScript(newRspec, repoupdate_callback);
console.info("geni-lib code has changed");
if (portal_converted) {
/*
* User might not want to proceed down this path,
* will not be able to use Jacks.
*/
$('#edit-genilib-continue').click(function(event) {
sup.HideModal('#edit-genilib-warning-modal',
function () {
MarkPortalConverted(false);
CreateJacksEditor();
checkScript(newRspec,
repoupdate_callback);
});
});
sup.ShowModal('#edit-genilib-warning-modal',
function () {
$('#edit-genilib-continue').off("click");
});
return;
}
else {
checkScript(newRspec, repoupdate_callback);
}
}
else if (repoupdate_callback !== undefined) {
repoupdate_callback(false /* unmodified. */);
......@@ -919,6 +1005,7 @@ $(function ()
if (gotscript) {
$('#profile_instructions').prop("readonly", true);
$('#profile_description').prop("readonly", true);
$('.geni-lib-warning').removeClass("hidden");
}
else {
// Allow editing the boxes now that we have an rspec.
......@@ -988,6 +1075,7 @@ $(function ()
var text = $(this).text();
$('#profile_instructions').val(text);
});
//
// First time we see the XML, grab step data out of it. But after
// that the steps table is authoritative, and so we sync the table
......@@ -1245,14 +1333,14 @@ $(function ()
if (json.value.rspec != "") {
gotscript = 1;
NewRspecHandler(json.value.rspec);
if (repoupdate_callback !== undefined) {
repoupdate_callback(true /* modified */);
}
// Force this; the script is obviously different, but the
// the XML might be exactly same. Still want to save it.
if (!fromrepo || window.ACTION == "create") {
ProfileModified();
}
if (repoupdate_callback !== undefined) {
repoupdate_callback(true /* modified */);
}
// Show the XML source button.
$('#show_xml_modal_button').removeClass("hidden");
}
......@@ -1272,7 +1360,7 @@ $(function ()
// Pass along uuid as a flag to update repo.
args["repoupdate"] = version_uuid;
}
WaitWait("We are converting your geni-lib script to an rspec");
WaitWait("We are converting your geni-lib script to XML");
var xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile",
"CheckScript", args);
......@@ -1459,10 +1547,69 @@ $(function ()
{
if (source !== null)
{
changeRspec(source);
changeRspec(source);
}
}
function ConvertToGenilib(rspec)
{
var converting = false;
// Coming out of Jacks, otherwise a conversion.
if (rspec !== undefined) {
changeRspec(rspec);
}
else {
rspec = $.trim($('#profile_rspec_textarea').val());
converting = true;
}
/*
* Convert rspec to geni-lib
*/
var callback = function(json) {
sup.HideWaitWait();
console.info(json.value);
if (json.code) {
$('#profile-conversion-failure-message').html(json.value);
sup.ShowModal('#profile-convert-failed-modal');
return;
}
gotscript = 1;
$('#profile_script_textarea').val(json.value.script);
NewRspecHandler(json.value.rspec);
ProfileModified();
// Show the XML source button.
$('#show_xml_modal_button').removeClass("hidden");
// A conversion, throw up post conversion modal
if (converting) {
MarkPortalConverted(true);
// Hide the conversion button.
$('#profile_convert_button').addClass("hidden");
// Bind function to switch to the editor.
$('#profile-converted-viewscript').click(function(event) {
sup.HideModal('#profile-converted-modal',
function () {
openEditor(json.value.script);
});
});
sup.ShowModal('#profile-converted-modal');
}
};
if (converting) {
WaitWait("Please wait while we convert your rspec to geni-lib");
}
else {
WaitWait();
}
var xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile",
"ConvertRspec",
{"rspec" : rspec});
xmlthing.done(callback);
}
function ShowDeletionWarning(images)
{
/*
......@@ -1531,5 +1678,23 @@ $(function ()
xmlthing.done(callback);
}
function CreateJacksEditor()
{
var isViewer = gotscript && !portal_converted;
if (editor) {
$('#editmodal_div').empty();
}
editor = new JacksEditor($('#editmodal_div'),
isViewer, false, false, false, !multisite);
}
function MarkPortalConverted(converted)
{
portal_converted = converted;
// Mark the form as containing a converted script.
$('#quickvm_create_profile_form ' +
'[name=portal_converted]').val(converted ? "yes" : "no");
}
$(document).ready(initialize);
});
......@@ -220,6 +220,12 @@ function Do_Create()
htmlspecialchars($formfields["profile_script"]) .
"</value>");
fwrite($fp, "</attribute>\n");
if (isset($formfields["portal_converted"]) &&
$formfields["portal_converted"] == "yes") {
fwrite($fp, "<attribute name='portal_converted'>");
fwrite($fp, " <value>1</value>");
fwrite($fp, "</attribute>\n");
}
}
if (isset($formfields["profile_repourl"]) &&
$formfields["profile_repourl"] != "") {
......@@ -1138,6 +1144,55 @@ function Do_ConvertClassic()
unlink($outfname);
}
#
# Convert rspec to geni-lib script.
#
function Do_ConvertRspec()
{
global $this_user, $suexec_output;
global $ajax_args;
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
if (!isset($ajax_args["rspec"])) {
SPITAJAX_ERROR(1, "Missing rspec");
return;
}
$infname = tempnam("/tmp", "convertin");
$outfname = tempnam("/tmp", "convertout");
$rspecfname = tempnam("/tmp", "convertoutrspec");
$fp = fopen($infname, "w");
fwrite($fp, $ajax_args["rspec"]);
fclose($fp);
chmod($infname, 0666);
chmod($outfname, 0666);
chmod($rspecfname, 0666);
#
# Invoke the backend.
#
$retval = SUEXEC($this_uid, "nobody",
"webrspec2genilib -r -s $rspecfname -o $outfname $infname",
SUEXEC_ACTION_IGNORE);
if ($retval != 0) {
#
# All errors go to the user for now.
#
SPITAJAX_ERROR($retval, $suexec_output);
}
else {
$script = file_get_contents($outfname);
SPITAJAX_RESPONSE(array("script" => $script,
"rspec" => file_get_contents($rspecfname)));
}
unlink($infname);
unlink($outfname);
unlink($rspecfname);
}
#
# Clone a repository and send back the script or rspec.
#
......
......@@ -71,6 +71,7 @@ function SPITFORM($formfields, $errors)
$activity = 0;
$ispp = 0;
$isadmin = (ISADMIN() ? 1 : 0);
$isstud = (STUDLY() ? 1 : 0);
$canrepo = (ISADMIN() || STUDLY() ? 1 : 0);
$multisite = 1;
$cloning = 0;
......@@ -193,6 +194,7 @@ function SPITFORM($formfields, $errors)
echo " window.CANPUBLISH= $canpublish;\n";
echo " window.DISABLED= $disabled;\n";
echo " window.ISADMIN = $isadmin;\n";
echo " window.ISSTUD = $isstud;\n";
echo " window.MULTISITE = $multisite;\n";
echo " window.HISTORY = $history;\n";
echo " window.CLONING = $cloning;\n";
......@@ -368,6 +370,8 @@ if (! isset($create)) {
if ($profile->script() && $profile->script() != "") {
$defaults["profile_script"] = $profile->script();
}
$defaults["portal_converted"]
= ($profile->portal_converted() == 1 ? "yes" : "no");
# Default the project if in only one project.
if (count($projlist) == 1) {
list($project) = each($projlist);
......@@ -383,6 +387,8 @@ if (! isset($create)) {
if ($profile->script() && $profile->script() != "") {
$defaults["profile_script"] = $profile->script();
}
$defaults["portal_converted"]
= ($profile->portal_converted() == 1 ? "yes" : "no");
if ($profile->repourl() && $profile->repourl() != "") {
$defaults["profile_repourl"] = $profile->repourl();
# Need this so JS code knows when HEAD changes.
......
......@@ -138,6 +138,7 @@ class Profile
function parent_profileid() { return $this->field('parent_profileid'); }
function parent_version() { return $this->field('parent_version'); }
function profile_nodelete() { return $this->field('profile_nodelete'); }
function portal_converted() { return $this->field('portal_converted'); }
# Private means only in the same project.
function IsPrivate() {
......
......@@ -122,6 +122,8 @@ $routing = array("myprofiles" =>
"Do_BindParameters",
"ConvertClassic" =>
"Do_ConvertClassic",
"ConvertRspec" =>
"Do_ConvertRspec",
"UpdateRepository" =>
"Do_UpdateRepository",
"GetRepository" =>
......
This diff is collapsed.
Supports Markdown
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