Commit 3c836228 authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Replace Jon's image constraint code with the real thing. Added the

ajax backend for getting the image constraints, including image permission
checks. Also changes to the cluster dropdowns for the case that no clusters
are allowed. Add double click prevention, this was bothering me every time
I saw someone do it. Add a check to make sure that all clusters dropdowns
are actually selected, else do not allow the submit.
parent 5e36c240
......@@ -180,6 +180,186 @@ function Do_Instantiate()
SPITAJAX_RESPONSE("status.php?uuid=$uuid");
}
#
# Return constraint info for a set of images.
#
function Do_GetImageInfo()
{
global $this_user;
global $ajax_args;
if (!isset($ajax_args["images"])) {
SPITAJAX_ERROR(1, "Missing image list");
return;
}
if ($this_user && !$this_user->webonly()) {
if (!isset($ajax_args["project"])) {
SPITAJAX_ERROR(1, "Missing project selection");
return;
}
$pid = $ajax_args["project"];
if (!preg_match("/^[\w]+$/", $pid)) {
SPITAJAX_ERROR(1, "Illegal project name: $pid");
return;
}
$project = Project::Lookup($pid);
if (!$project) {
SPITAJAX_ERROR(1, "No such project: $pid");
return;
}
$approved = 0;
if (! (ISADMIN() ||
($project->IsMember($this_user, $approved) && $approved))) {
SPITAJAX_ERROR(1, "Not a member of project: $pid");
return;
}
}
$dblink = DBConnect("ims");
$constraints = array();
$images = array();
foreach ($ajax_args["images"] as $urn) {
if (Instance::ValidURN($urn)) {
# This // vs : thing is a pain.
$urn = preg_replace('/\/\//', ":", $urn);
# Need to see if there is a specific version.
list ($auth,$type,$id) = Instance::ParseURN($urn);
list ($proj,$name,$version) = preg_split('/:/', $id);
#
# Lookup is without the version number in the urn.
#
$urn = preg_replace('/:\d+$/', "", $urn);
$safe_urn = addslashes($urn);
#
# Gack, if this is for a system image in emulab-ops, and it is
# not in the database, it is probably cause the urn is referencing
# the wrong domain. If we do not find what we are looking for, then
# we want to try again with "emulab.net" cause that is most likely
# what the user really means.
#
if ($proj == "emulab-ops") {
$checkagain = 1;
}
again:
if (is_null($version)) {
$query_result =
DBQueryWarn("select i.*,v.* from images as i ".
"left join image_versions as v on ".
" v.urn=i.urn ".
"where i.urn='$safe_urn' ".
"order by v.version desc limit 1",
$dblink);
}
else {
$query_result =
DBQueryWarn("select i.*,v.* from image_versions as v ".
"left join images as i on ".
" i.image_uuid=v.image_uuid ".
"where v.urn='$safe_urn' and v.version='$version'",
$dblink);
}
if ($query_result && !mysql_num_rows($query_result) && $checkagain) {
list ($a,$b,$c) = Instance::ParseURN($urn);
$urn = "urn:publicid:IDN+emulab.net+image+${c}";
$safe_urn = addslashes($urn);
$checkagain = 0;
goto again;
}
}
elseif (parse_url($urn)) {
#
# Or it must be a URL.
#
$safe_url = addslashes($urn);
$query_result =
DBQueryWarn("select i.*,v.* from image_versions as v ".
"left join images as i on ".
" i.image_uuid=v.image_uuid ".
"where v.metadata_url='$safe_url'",
$dblink);
}
else {
continue;
}
if (!$query_result || !mysql_num_rows($query_result)) {
# Is this the right thing to do?
continue;
}
$row = mysql_fetch_array($query_result);
#
# Need to look at the privacy setting, and if the image is not
# public, the user has to be in the same project as the image
# (or own the image), or it has to be listed in the permissions.
#
if ($row["visibility"] != "public") {
# This will not be set for guests.
if (!isset($project)) {
continue;
}
#
# Project selection box has to match the project the image
# belongs too. If the user changes the project selection, we
# have to rerun the constraints.
#
list ($auth) = Instance::ParseURN($row["project_urn"]);
list ($domain,$impid) = preg_split('/:/', $auth);
if ($impid && $impid == $pid) {
goto allowed;
}
#
# Check special permissions.
#
$perms_result =
DBQueryWarn("select * from image_permissions ".
"where urn='" . addslashes($row["urn"]) . "'",
$dblink);
while ($prow = mysql_fetch_array($perms_result)) {
$ptype = $prow["permission_type"];
$purn = $prow["permission_urn"];
if ($ptype == "user") {
list ($ign1,$ign2,$id) = Instance::ParseURN($purn);
if ($id && $id == $this_user->uid()) {
goto allowed;
}
}
elseif ($ptype == "project") {
list ($auth) = Instance::ParseURN($purn);
list ($domain,$ppid) = preg_split('/:/', $auth);
if ($ppid && $ppid == $pid) {
goto allowed;
}
}
}
continue;
}
allowed:
$constraints[] =
array("node" =>
array("images" => array($row["urn"]),
"type" => array($row["virtualizaton"])));
$constraints[] =
array("node" =>
array("images" => array($row["urn"]),
"hardware" => array($row["types_known_working"])));
$images[] = array("id" => $urn,
"name" => $row["description"]);
}
$result = array(array("constraints" => $constraints,
"images" => $images));
SPITAJAX_RESPONSE($result);
}
# Local Variables:
# mode:php
# End:
......
......@@ -256,9 +256,35 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
" to enable more capabilities. Thanks!")
return false;
}
// Prevent double click.
if ($(this).data('submitted') === true) {
// Previously submitted - don't submit again
console.info("Ignoring double submit");
event.preventDefault();
return false;
}
else {
// See if all cluster selections have been made. Seems
// to be a common problem.
if (!AllClustersSelected()) {
alert("Please make all your cluster selections!");
event.preventDefault();
return false;
}
// Mark it so that the next submit can be ignored
$(this).data('submitted', true);
}
$("#waitwait-modal").modal('show');
return true;
});
/*
* Need to update image constraints when the project selector
* is changed.
*/
$('#profile_pid').change(function (event) {
UpdateImageConstraints();
return true;
});
$('#profile_copy_button').click(function (event) {
event.preventDefault();
if (!registered) {
......@@ -595,10 +621,13 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
" </label> " +
" <div class='col-sm-6'>" +
" <select name=\"formfields[sites][" + siteid + "]\"" +
" class='form-control'>" + options +
" class='form-control'>" +
" <option value=''>Please Select</option>" + options +
" </select>" +
"</div>" +
"<div class='col-sm-4'></div><div class='col-sm-6 alert alert-warning' id='where-warning' style='display: none; margin-top: 5px; margin-bottom: 5px'>This site only works on some clusters. Incompatible clusters are unselectable.</div>" +
"<div class='col-sm-4'></div>" +
"<div class='col-sm-6 alert alert-warning' id='where-warning' style='display: none; margin-top: 5px; margin-bottom: 5px'>This site only works on some clusters. Incompatible clusters are unselectable.</div>" +
"<div class='col-sm-6 alert alert-danger' id='where-nowhere' style='display: none; margin-top: 5px; margin-bottom: 5px'>This site <b>will not work on any clusters</b>. All clusters are unselectable.</div>" +
"</div></div>";
sitenum++;
});
......@@ -609,6 +638,30 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
updateWhere();
}
/*
* Make sure all clusters slected before submit.
*/
function AllClustersSelected()
{
if (!multisite || Object.keys(sites).length <= 1) {
return $('#nosite_selector').find('select').val() != "";
}
else {
var allgood = 1;
_.each(_.keys(sites), function (siteId) {
$('#site_selector #site' +
siteIdToSiteNum[siteId] + 'cluster')
.find('select').each(function () {
if ($(this).val() == "") {
allgood = 0;
return;
}
});
});
return allgood;
}
}
var constraints;
function contextReady(data)
......@@ -641,52 +694,51 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
});
}
var foundImages = [];
var foundImages = [];
function onFoundImages(images)
{
console.log(images);
if (! _.isEqual(foundImages, images))
function onFoundImages(images)
{
foundImages = images;
// STUB: Replace this with an ajax call to get images
_.delay(imageUpdate, 5000);
if (! _.isEqual(foundImages, images)) {
foundImages = images;
UpdateImageConstraints();
}
return true;
}
/*
* Update the image constraints if anything changes.
*/
function UpdateImageConstraints() {
console.log("UpdateImageConstraints");
console.log(foundImages);
if (!foundImages.length) {
return;
}
var callback = function(json) {
if (json.code) {
alert("Could not get image info: " + json.value);
return;
}
console.log(json.value);
if (1) {
constraints.addPossibles({ images: json.value.images });
constraints.allowAllSets(json.value.constraints);
updateWhere();
}
};
// Must pass the selected project along for constraint checking.
var $xmlthing =
sup.CallServerMethod(ajaxurl,
"instantiate", "GetImageInfo",
{"images" : foundImages,
"project" : $('#project_selector')
.find('select').val()});
$xmlthing.done(callback);
return true;
}
}
function imageUpdate()
{
// STUB: Parse ajax call results and generate new context options and constraints
var newOptions = {
contextOptions: {
images: [
{
"id": "urn:publicid:IDN+emulab.net+image+emulab-ops:SOME_NAME",
"name": "SOME_NAME Blah Blah"
},
]
},
constraints: [
{
"node": {
"aggregates": [
"urn:publicid:IDN+apt.emulab.net+authority+cm"
],
"images": [
"urn:publicid:IDN+emulab.net+image+emulab-ops:SOME_TYPE"
],
"types": [
"raw-pc"
]
}
},
]
};
constraints.addPossibles(newOptions.contextOptions);
constraints.allowAllSets(newOptions.constraints);
console.log('imageUpdate');
updateWhere();
}
function contextFail(fail1, fail2)
{
......@@ -704,27 +756,8 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
}
}
var amValueToKey = {};
/*
'Cloudlab Utah':
"urn:publicid:IDN+utah.cloudlab.us+authority+cm",
'Cloudlab Wisconsin':
"urn:publicid:IDN+wisc.cloudlab.us+authority+cm",
'Cloudlab Clemson':
"urn:publicid:IDN+clemson.cloudlab.us+authority+cm",
'APT Utah':
"urn:publicid:IDN+apt.emulab.net+authority+cm",
'IG UtahDDC':
"urn:publicid:IDN+utahddc.geniracks.net+authority+cm",
'Emulab':
"urn:publicid:IDN+emulab.net+authority+cm"
};
*/
var amValueToKey = {};
function finishUpdateWhere(allNodes, nodesBySite)
{
if (!multisite || Object.keys(sites).length <= 1) {
......@@ -753,16 +786,27 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
var clause = 'aggregates';
allowed = constraints.getValidList(bound, subclause,
clause, rejected);
if (rejected.length > 0)
if (allowed.length == 0)
{
domNode.find('#where-warning').hide();
domNode.find('#where-nowhere').show();
}
else if (rejected.length > 0)
{
domNode.find('#where-warning').show();
domNode.find('#where-nowhere').hide();
}
else
{
domNode.find('#where-warning').hide();
domNode.find('#where-nowhere').hide();
}
domNode.find('select').children().each(function () {
var value = $(this).attr('value');
// Skip the Please Select option
if (value == "") {
return;
}
var key = amValueToKey[value];
var i = 0;
var found = false;
......@@ -777,10 +821,17 @@ function (_, Constraints, sup, ppstart, JacksEditor, aboutaptString, aboutcloudS
if (found)
{
$(this).prop('disabled', false);
if (allowed.length == 1) {
$(this).attr('selected', "selected");
// This does not appear to do anything, at least in Chrome
$(this).prop('selected', true);
}
}
else
{
$(this).prop('disabled', true);
$(this).removeAttr('selected');
// See above comment.
$(this).prop('selected', false);
}
});
......
......@@ -53,7 +53,9 @@ $routing = array("myprofiles" =>
"Instantiate" =>
"Do_Instantiate",
"GetParameters" =>
"Do_GetParameters")),
"Do_GetParameters",
"GetImageInfo" =>
"Do_GetImageInfo")),
"manage_profile" =>
array("file" => "manage_profile.ajax",
"guest" => false,
......
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