Commit ddffa2e9 authored by Leigh B Stoller's avatar Leigh B Stoller

Various little tweaks for making reservations available to mere users.

parent 798097e5
......@@ -73,6 +73,7 @@ use lib "@prefix@/lib";
use EmulabConstants;
use emdb;
use emutil;
use Brand;
use User;
use Project;
use Reservation;
......@@ -166,9 +167,11 @@ sub DoReserve()
#
# We allow for a user or project argument.
#
my $optlist = "t:s:e:nN:u:";
my $optlist = "t:s:e:nN:u:p:";
my ($start, $end, $type, $reason, $update);
my $checkonly = 0;
my $portal;
my $brand;
my %rpcargs = ();
my $context;
my ($credential,$speaksfor);
......@@ -194,6 +197,9 @@ sub DoReserve()
if (defined($options{"u"})) {
$update = $options{"u"};
}
if (defined($options{"p"})) {
$portal = $options{"p"};
}
if (defined($options{"N"})) {
$reason = readfile($options{"N"});
}
......@@ -202,7 +208,11 @@ sub DoReserve()
}
usage()
if (!defined($project));
# Need brand for email. No portal argument means emulab brand.
$brand = Brand->Create($portal);
if (!defined($brand)) {
fatal("Bad branding");
}
if (defined($update)) {
usage()
if (! (defined($count) || defined($start) || defined($end)));
......@@ -267,7 +277,37 @@ sub DoReserve()
fatal($response->output());
}
print Dumper($response);
exit(0);
#
# Exit with different status if the reservation is feasible but
# needs to be approved. The value is a boolean indicating
# approved. Note that this applies even in "check" mode; the
# approval code indicates if the request is feasible and will be
# immediately approved.
#
if ($response->value()) {
# approved right away
exit(0);
}
#
# Needs to be approved, exit with special status.
#
# If this was not $checkonly, then we want to send email locally
# since the email was generated to the tbops but not the portal
# email lists.
#
if (!$checkonly) {
my $this_uid = $this_user->uid();
my $this_email = $this_user->email();
my $url = $brand->wwwBase() . "/list-reservations.php";
$brand->SendEmail($brand->ExtensionsEmailAddress(),
"Pending reservation request needs approval",
"A reservation request was made by $this_uid, but it needs approval.".
"\n\n".
"See: $url", $this_email);
}
exit(2);
}
#
......
......@@ -28,15 +28,16 @@ $(function ()
*/
function LoadData()
{
var count = Object.keys(amlist).length;
var amcount = Object.keys(amlist).length;
var rescount = 0;
_.each(amlist, function(urn, name) {
var callback = function(json) {
console.log(json);
// Kill the spinner.
count--;
if (count <= 0) {
amcount--;
if (amcount <= 0) {
$('#spinner').addClass("hidden");
}
if (json.code) {
......@@ -45,8 +46,15 @@ $(function ()
return;
}
var reservations = json.value;
if (reservations.length == 0)
rescount += reservations.length;
if (reservations.length == 0) {
if (amcount == 0 && rescount == 0) {
// No reservations at all, show the message.
$('#noreservations').removeClass("hidden");
}
return;
}
// Generate the main template.
var html = listTemplate({
......
......@@ -283,6 +283,7 @@ $(function ()
function ValidateReservation()
{
var callback = function(json) {
// Three indicates success but needs admin approval.
if (json.code) {
if (json.code != 2) {
sup.SpitOops("oops", json.value);
......@@ -293,6 +294,14 @@ $(function ()
ToggleSubmit(true, "submit");
// Make sure we still warn about an unsaved form.
aptforms.MarkFormUnsaved();
if (json.value == 3) {
$('#confirm-reservation .needs-approval')
.removeClass("hidden");
}
else {
$('#confirm-reservation .needs-approval')
.addClass("hidden");
}
sup.ShowModal('#confirm-reservation');
};
aptforms.SubmitForm('#reserve-request-form', "reserve",
......@@ -311,7 +320,7 @@ $(function ()
sup.SpitOops("oops", json.value);
return;
}
window.location.replace(json.value);
window.location.replace("list-reservations.php");
};
aptforms.SubmitForm('#reserve-request-form', "reserve",
"Reserve", reserve_callback,
......@@ -422,7 +431,7 @@ $(function ()
// Toggle the button between check and submit.
function ToggleSubmit(enable, which) {
if (which == "submit") {
$('#reserve-submit-button').text("Reserve");
$('#reserve-submit-button').text("Submit");
$('#reserve-submit-button').addClass("btn-success");
$('#reserve-submit-button').removeClass("btn-primary");
}
......
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -76,6 +76,10 @@ echo "<div id='main-body'>
<div id='spinner'>
<center id='spinner'><img src='images/spinner.gif' /></center><br>
</div>
<div id='noreservations' class=hidden>
You do not have any reservations. Would you like to
<a href='reserve.php'>create</a> one?
</div>
</div>\n";
# Place to hang the modals for now
......
......@@ -46,7 +46,7 @@ function Do_Reserve()
{
global $this_user;
global $ajax_args;
global $suexec_output;
global $suexec_output, $TB_PROJECT_CREATEEXPT;
if (!isset($ajax_args["formfields"])) {
SPITAJAX_ERROR(-1, "Missing formfields");
......@@ -159,8 +159,11 @@ function Do_Reserve()
"webmanage_reservations -t " . $webtask->task_id() . " ".
"-a $urn reserve -n $args", SUEXEC_ACTION_IGNORE);
$webtask->Refresh();
if ($retval) {
#
# Exit code "2" indicates OK, but will need approval.
#
if ($retval && $retval != 2) {
#
# Positive value means the reservation is not possible. Treat
# as a form error for the caller.
......@@ -179,7 +182,7 @@ function Do_Reserve()
}
# Return if that is all the caller wanted,
if (isset($ajax_args["validate"]) && $ajax_args["validate"]) {
SPITAJAX_RESPONSE(0);
SPITAJAX_RESPONSE($retval == 2 ? 3 : 0);
$webtask->Delete();
return;
}
......@@ -205,7 +208,10 @@ function Do_Reserve()
"-a $urn reserve $args", SUEXEC_ACTION_IGNORE);
$webtask->Refresh();
if ($retval) {
#
# Exit code "2" indicates OK, but will need approval.
#
if ($retval && $retval != 2) {
#
# Positive value means the reservation is not possible. Treat
# as a form error for the caller.
......@@ -229,7 +235,7 @@ function Do_Reserve()
unlink($reasonfile);
}
$webtask->Delete();
SPITAJAX_RESPONSE("list-reservations.php");
SPITAJAX_RESPONSE($retval == 2 ? 3 : 0);
}
#
......
......@@ -38,11 +38,6 @@ $this_user = CheckLoginOrRedirect();
$isadmin = (ISADMIN() ? 1 : 0);
$isfadmin = (ISFOREIGN_ADMIN() ? 1 : 0);
if (!ISADMIN()) {
SPITUSERERROR("You do not have permission to view this page");
exit();
}
#
# Verify page arguments. Cluster is a domain that we turn into a URN.
#
......
......@@ -5,7 +5,7 @@
<div class='modal-body'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h3>Confirm to Delete</h3>
<center><h4>Confirm to Delete</h4>
<button class='btn btn-danger btn-sm'
id='confirm_delete'>Confirm</a></center>
</div>
......
......@@ -38,7 +38,7 @@
<th>Count</th>
<th>Starts</th>
<th>Ends</th>
<th>Pending</th>
<th>Status</th>
</tr>
</thead>
<tbody>
......@@ -85,9 +85,9 @@
<td class="format-date"><%- value.start %></td>
<td class="format-date"><%- value.end %></td>
<% if (value.approved) { %>
<td>No</td>
<td>Approved</td>
<% } else {%>
<td><span class="text-danger">Yes</span></td>
<td><span class="text-danger">Pending</span></td>
<% } %>
</tr>
<% }); %>
......
......@@ -5,6 +5,11 @@
<div class='panel-heading'>
<h3 class='panel-title'>
<% if (editing) { %>Modify <% } %>Reservation Request
<a href='#' class='btn btn-xs'
data-toggle='modal'
data-target="#reservation-help-modal">
<span style='margin-bottom: 4px; margin-left: 0px;'
class='glyphicon glyphicon-question-sign'></span></a>
<% if (editing) { %>
<span class="hidden pull-right text-danger small"
id="unapproved-warning">
......@@ -274,14 +279,78 @@
<div class='modal-body'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h3>Would you like to Commit this reservation?</h3>
<center>
<h4>Would you like to submit this reservation request?</h4>
<button style='margin-right: 20px;'
class='btn btn-primary btn-sm'
data-dismiss='modal' aria-hidden='true'>Cancel</button>
<button class='btn btn-success btn-sm'
id='commit-reservation'
data-dismiss='modal' aria-hidden='true'>Reserve</button>
data-dismiss='modal' aria-hidden='true'>Submit</button>
</center>
<div style="margin-top: 5px;" class="needs-approval hidden">
NOTE: This reservation request can be accomodated, but the
size and/or duration means that the reservation needs to
be approved by an administrator before it is fully active.
The administrator has been been notified and will be acting
on this request shortly.</div>
</div>
</div>
</div>
</div>
<!-- Help -->
<div id='reservation-help-modal' class='modal fade'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-body'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<p>
Use this form to request that resources be set aside for
your experiments at a future time. A reservation request
consists of these specific elements:
<ul>
<li> Project; what project your experiment is going to be
run in. If you are member of only one project, then do
not worry about this.</li>
<li> Count; How many nodes you need for your experiment. </li>
<li> Start date/time; when you would like the resources to
be available for you to use.</li>
<li> End date/time; when you expect to be finished. This is
a <em>hard</em> deadline; if the resources have been
promised to another user you will not be allowed to keep
them past the end of the reservation (requests to extend
your experiment will be denied).</li>
<li> Cluster; at what cluster you want resources. If you
need resources at multiple clusters, you need to submit a
request for each cluster.</li>
<li> Node type; reservations are for specific node types. You
might not care what node type you get, but the reservation
system requires it.</li>
<li> Description; a reasonable explanation of why you need
these resources. Most reservations need administrator
approval, so the more details you provide, the better
the chances your request will be approved.
</li>
</ul>
<p>
When you click on <b>Check</b> a preliminary query is made
to see if the request is <em>feasible</em> given the
current set of reservations at the cluster. If not, you can
modify your request and check again. Once you get to a
feasible reservation request, you will then want to <b>Submit</b>
it. The request might still fail, say if someone else got
in and captured those resources before you, but we expect
that to be very rare.
</p>
<p>
If your request is unusually large or long in duration, it
is possible that the reservation will be recorded in a
<em>pending</em> state; an administrator will need to
review the request and approve it. Your list of
reservations will indicate which requests are pending
approval.
</p>
</div>
</div>
</div>
......
Markdown is supported
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