Commit 8e88917e authored by Leigh B Stoller's avatar Leigh B Stoller

Separate user vs admin lockdown, previously they were intertwined.

User lockdown is as before, user can override that on the terminate
page. Admin lockdown is like Classic lockdown; the flag must be cleared
before the experiment can be terminated, there is no override on the
termination page.

UI changes on the status and admin extend page for the additional
flag (instead of a single lockdown, there are now two).
parent 9c70c5ed
......@@ -1732,7 +1732,7 @@ sub ExtendInternal($$$$)
}
# Lockdown.
if ($lockdown) {
if (DoLockdownInternal("set", "admin")) {
if (DoLockdownInternal("set", "user")) {
SENDMAIL($TBOPS,
"Failed to lock down APT Instance",
"Failed to lock down $instance\n".
......@@ -2195,7 +2195,7 @@ sub DoExtendOld()
}
# Lockdown.
if ($lockdown) {
if (DoLockdownInternal("set", "admin")) {
if (DoLockdownInternal("set", "user")) {
SENDMAIL($TBOPS,
"Failed to lock down APT Instance",
"Failed to lock down $instance\n".
......@@ -3082,7 +3082,7 @@ sub DoPanic()
fatal("Experiment is busy, cannot lock it. Please try again later");
}
#
# And tell the backend clusters to lockdown the slice.
# And tell the backend clusters to Panic the slice.
#
my $coderef = sub {
my ($sliver) = @_;
......@@ -3114,7 +3114,7 @@ sub DoPanic()
if (ParRun({"maxwaittime" => 99999,
"maxchildren" => scalar(@agglist)},
\@return_codes, $coderef, @agglist)) {
$emsg = "Internal error calling Lockdown()";
$emsg = "Internal error calling Panic()";
goto bad;
}
#
......@@ -3233,7 +3233,7 @@ sub DoLinktest()
if (ParRun({"maxwaittime" => 99999,
"maxchildren" => scalar(@agglist)},
\@return_codes, $coderef, @agglist)) {
$errmsg = "Internal error calling Lockdown()";
$errmsg = "Internal error calling LinkTest()";
goto bad;
}
#
......@@ -3778,6 +3778,11 @@ sub DoSchedTerminate()
print STDERR "$errmsg\n";
exit($errcode);
}
if ($instance->admin_lockdown()) {
$errmsg = "Must clear the admin lockdown first.";
$errcode = -1;
goto bad;
}
if (defined($reason) &&
!TBcheck_dbslot($reason, "default", "fulltext",
TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) {
......@@ -3814,8 +3819,8 @@ sub DoSchedTerminate()
$slice->SetExpiration(time() + ($days * 3600 * 24));
}
# Now we can clear this.
if ($instance->admin_lockdown()) {
if (DoLockdownInternal("clear", "all")) {
if ($instance->user_lockdown()) {
if (DoLockdownInternal("clear", "user")) {
SENDMAIL($TBOPS,
"Failed to clear lock down on APT Instance",
"Failed to clear lock down $instance\n".
......
......@@ -148,16 +148,18 @@ $(function ()
function (json) {
console.info(json);
if (json.code == 0) {
var html = firstrowTemplate({"expinfo": json.value,
"uuid" : window.UUID,
"uid" : window.CREATOR,
"pid" : window.PID});
var html = firstrowTemplate(
{"expinfo" : json.value,
"uuid" : window.UUID,
"uid" : window.CREATOR,
"pid" : window.PID}
);
$("#firstrow").html(html);
$('.format-date').each(function() {
var date = $.trim($(this).html());
if (date != "") {
$(this).html(moment($(this).html())
.format("MMM D h:mm A"));
.format("MMM D h:mm A"));
}
});
// lockout change event handler.
......@@ -165,14 +167,26 @@ $(function ()
DoLockout($(this).is(":checked"));
});
// lockdown change event handler.
$('#lockdown-checkbox').change(function() {
DoLockdown($(this).is(":checked"));
$('#user-lockdown-checkbox')
.change(function() {
DoLockdown("user",
$(this).is(":checked"));
});
$('#admin-lockdown-checkbox')
.change(function() {
DoLockdown("admin",
$(this).is(":checked"));
});
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
placement: 'auto',
});
// No termination.
if (json.value.admin_lockdown) {
$('#terminate-button')
.attr("disabled", "disabled");
}
// Update the Max Extension
DoMaxExtension();
}
......@@ -245,9 +259,9 @@ $(function ()
//
// Request lockdown set/clear.
//
function DoLockdown(lockdown)
function DoLockdown(which, lockdown)
{
lockdown = (lockdown ? 1 : 0);
var action = (lockdown ? "set" : "clear");
var callback = function(json) {
sup.HideModal("#waitwait-modal");
......@@ -255,11 +269,20 @@ $(function ()
alert("Failed to change lockdown: " + json.value);
return;
}
if (which == "admin") {
if (lockdown) {
$('#terminate-button').attr("disabled", "disabled");
}
else {
$('#terminate-button').removeAttr("disabled");
}
}
}
sup.ShowModal("#waitwait-modal");
var xmlthing = sup.CallServerMethod(null, "status", "Lockdown",
{"uuid" : window.UUID,
"lockdown" : lockdown});
{"uuid" : window.UUID,
"which" : which,
"action" : action});
xmlthing.done(callback);
}
......@@ -274,7 +297,7 @@ $(function ()
return;
}
$('#max-extension').html(moment(json.value)
.format("LLL"));
.format("MMM D, YYYY h:mm A"));
/*
* Look to see if the number of days requested is going to be
......
......@@ -37,7 +37,8 @@ $(function ()
var lastStatus = "";
var paniced = 0;
var lockout = 0;
var lockdown = 0;
var admin_lockdown = 0;
var user_lockdown = 0;
var lockdown_code = "";
var consolenodes = {};
var showlinktest = false;
......@@ -62,8 +63,9 @@ $(function ()
profile_uuid = window.APT_OPTIONS.profileUUID;
paniced = window.APT_OPTIONS.paniced;
lockout = window.APT_OPTIONS.lockout;
lockdown = window.APT_OPTIONS.lockdown;
user_lockdown= window.APT_OPTIONS.user_lockdown;
lockdown_code= uuid.substr(2, 5);
admin_lockdown = window.APT_OPTIONS.admin_lockdown;
instanceStatus = window.APT_OPTIONS.instanceStatus;
hidelinktest = window.APT_OPTIONS.hidelinktest;
var errorURL = window.HELPFORUM;
......@@ -95,7 +97,8 @@ $(function ()
project: window.APT_OPTIONS.project,
group: window.APT_OPTIONS.group,
lockout: lockout,
lockdown: lockdown,
admin_lockdown: admin_lockdown,
user_lockdown: user_lockdown,
lockdown_code: lockdown_code,
// The status panel starts out collapsed.
status_panel_show: (instanceStatus == "ready" ? false : true),
......@@ -255,7 +258,7 @@ $(function ()
event.preventDefault();
sup.HideModal('#terminate_modal');
if (lockdown) {
if (user_lockdown) {
if (lockdown_code != $('#terminate_lockdown_code').val()) {
sup.SpitOops("oops", "Refusing to terminate; wrong code");
return;
......@@ -294,8 +297,11 @@ $(function ()
DoLockout($(this).is(":checked"));
});
// lockdown change event handler.
$('#lockdown_checkbox').change(function() {
DoLockdown($(this).is(":checked"));
$('#user_lockdown_checkbox').change(function() {
DoLockdown("user", $(this).is(":checked"));
});
$('#admin_lockdown_checkbox').change(function() {
DoLockdown("admin", $(this).is(":checked"));
});
// Quarantine change event handler.
$('#quarantine_checkbox').change(function() {
......@@ -631,8 +637,13 @@ $(function ()
}
function ButtonState(button, enable)
{
if (button == "terminate")
if (button == "terminate") {
button = "#terminate_button";
// When admin lockdown is set, we never enable this button.
if (admin_lockdown) {
enable = 0;
}
}
else if (button == "extend")
button = "#extend_button";
else if (button == "refresh")
......@@ -793,15 +804,16 @@ $(function ()
//
// Request lockout set/clear.
//
function DoLockout(lockout)
function DoLockout(enable)
{
lockout = (lockout ? 1 : 0);
enable = (enable ? 1 : 0);
var callback = function(json) {
if (json.code) {
alert("Failed to change lockout: " + json.value);
return;
}
lockout = enable;
}
var xmlthing = sup.CallServerMethod(ajaxurl, "status", "Lockout",
{"uuid" : uuid,
......@@ -812,9 +824,9 @@ $(function ()
//
// Request lockdown set/clear.
//
function DoLockdown(lockdown)
function DoLockdown(which, lockdown)
{
lockdown = (lockdown ? 1 : 0);
var action = (lockdown ? "set" : "clear");
var callback = function(json) {
sup.HideModal("#waitwait-modal");
......@@ -822,11 +834,24 @@ $(function ()
alert("Failed to change lockdown: " + json.value);
return;
}
if (which == "user") {
user_lockdown = lockdown;
}
else if (which == "admin") {
admin_lockdown = lockdown;
if (lockdown) {
DisableButton("terminate");
}
else {
EnableButton("terminate");
}
}
}
sup.ShowModal("#waitwait-modal");
var xmlthing = sup.CallServerMethod(ajaxurl, "status", "Lockdown",
{"uuid" : uuid,
"lockdown" : lockdown});
{"uuid" : uuid,
"which" : which,
"action" : action});
xmlthing.done(callback);
}
......@@ -844,6 +869,7 @@ $(function ()
"Failed to change Quarantine mode: " + json.value);
return;
}
paniced = mode;
}
sup.ShowModal('#waitwait-modal');
var xmlthing = sup.CallServerMethod(ajaxurl, "status", "Quarantine",
......
......@@ -197,11 +197,18 @@ function Do_TerminateInstance()
$webtask = WebTask::CreateAnonymous();
$webtask_id = $webtask->task_id();
if ($instance->admin_lockdown() || $instance->user_lockdown()) {
if ($instance->admin_lockdown()) {
SPITAJAX_ERROR(1, "This experiment has been locked down by an ".
"administrator. The lock must be cleared before ".
"it can terminated.");
return;
}
if ($instance->user_lockdown()) {
if (! (isset($this_user) &&
($instance->creator() == $this_user->uid() || ISADMIN()))) {
SPITAJAX_ERROR(1, "Not enough permission to terminate; ".
"experiment is locked down");
"experiment is locked down.");
return;
}
$override = substr($uuid, 2, 5);
......@@ -214,7 +221,7 @@ function Do_TerminateInstance()
}
$retval = SUEXEC($this_user->uid(), "nobody",
"webmanage_instance -t $webtask_id -- ".
" lockdown $uuid clear all",
" lockdown $uuid clear user",
SUEXEC_ACTION_CONTINUE);
if ($retval) {
$webtask->Refresh();
......@@ -447,6 +454,13 @@ function Do_DenyOrMoreinfo($action)
SPITAJAX_ERROR(1, "You do not have permission to do this.");
goto bad;
}
if ($instance->admin_lockdown()) {
SPITAJAX_ERROR(1, "This experiment has been locked down by an ".
"administrator. The lock must be cleared before ".
"it can terminated.");
return;
}
if (isset($ajax_args["reason"]) && $ajax_args["reason"] != "") {
$reason = $ajax_args["reason"];
......@@ -1403,17 +1417,39 @@ function Do_Lockdown()
SPITAJAX_ERROR(1, "Missing profile uuid");
return;
}
if (!isset($ajax_args["lockdown"])) {
SPITAJAX_ERROR(1, "Missing lockdown value");
if (!isset($ajax_args["action"])) {
SPITAJAX_ERROR(1, "Missing action");
return;
}
$action = $ajax_args["action"];
if ($action != "set" && $action != "clear") {
SPITAJAX_ERROR(1, "Bad action");
return;
}
if (!isset($ajax_args["which"])) {
SPITAJAX_ERROR(1, "Missing which");
return;
}
$which = $ajax_args["which"];
if ($which != "user" && $which != "admin") {
SPITAJAX_ERROR(1, "Bad which");
return;
}
if ($which == "admin" && !ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission.");
return;
}
$uuid = $ajax_args["uuid"];
$instance = Instance::Lookup($uuid);
if (!$instance) {
SPITAJAX_ERROR(1, "Unknown instance uuid");
return;
}
$action = ($ajax_args["lockdown"] ? "set" : "clear");
if ($which == "user" &&
!($this_idx == $instance->creator_idx() || ISADMIN())) {
SPITAJAX_ERROR(1, "Not enough permission.");
return;
}
#
# Call out to the backend.
......@@ -1421,7 +1457,7 @@ function Do_Lockdown()
$webtask = WebTask::CreateAnonymous();
$retval = SUEXEC("nobody", "nobody",
"webmanage_instance -t " . $webtask->task_id() .
" lockdown $uuid $action admin ",
" lockdown $uuid $action $which ",
SUEXEC_ACTION_IGNORE);
$webtask->Refresh();
......@@ -1661,8 +1697,10 @@ function Do_ExpInfo()
$blob["profile_name"] = "unknown";
$blob["profile_uuid"] = null;
}
$blob["lockdown"] = $instance->admin_lockdown() ? true : false;
$blob["lockout"] = $instance->extension_lockout() ? true : false;
$blob["user_lockdown"] = $instance->user_lockdown() ? true : false;
$blob["admin_lockdown"] = $instance->admin_lockdown() ? true : false;
$blob["paniced"] = $instance->paniced() ? true : false;
$blob["lockout"] = $instance->extension_lockout() ? true : false;
SPITAJAX_RESPONSE($blob);
}
......
......@@ -170,8 +170,8 @@ $registered = (isset($this_user) ? "true" : "false");
$snapping = 0;
$oneonly = (isset($oneonly) && $oneonly ? 1 : 0);
$isadmin = (ISADMIN() ? 1 : 0);
$lockdown = ($instance->admin_lockdown() ||
$instance->user_lockdown() ? 1 : 0);
$user_lockdown = ($instance->user_lockdown() ? 1 : 0);
$admin_lockdown = ($instance->admin_lockdown() ? 1 : 0);
$extension_reason= ($instance->extension_reason() ?
CleanString($instance->extension_reason()) : "");
$extension_denied_reason= ($instance->extension_denied_reason() ?
......@@ -250,7 +250,8 @@ echo " window.APT_OPTIONS.oneonly = $oneonly;\n";
echo " window.APT_OPTIONS.dossh = $dossh;\n";
echo " window.APT_OPTIONS.ispprofile = $ispprofile;\n";
echo " window.APT_OPTIONS.publicURL = $public_url;\n";
echo " window.APT_OPTIONS.lockdown = $lockdown;\n";
echo " window.APT_OPTIONS.user_lockdown = $user_lockdown;\n";
echo " window.APT_OPTIONS.admin_lockdown = $admin_lockdown;\n";
echo " window.APT_OPTIONS.lockout = $lockout;\n";
echo " window.APT_OPTIONS.isopenstack = $isopenstack;\n";
echo " window.APT_OPTIONS.paniced = $paniced;\n";
......
<div class='col-sm-8 col-sm-offset-2'>
<div class='col-sm-7 col-sm-offset-1'>
<div class='panel panel-default'>
<div class="panel-heading text-center">
Experiment info for <%= expinfo.name %>
......@@ -32,27 +32,8 @@
<td class="format-date"><%- expinfo.expires %></td>
</tr>
<tr>
<td>
<span data-toggle='popover'
data-delay='{"hide":1000, "show":500}'
data-content="When checked, only administrator can extend
this experiment. No free time is granted to
user at all.">Lockout:</span>
<input type="checkbox" id="lockout-checkbox"
<% if (expinfo.lockout) { %>checked<% } %> >
</td>
<td>
<span data-toggle='popover'
data-delay='{"hide":1000, "show":500}'
data-content="When checked, the experiment
cannot be terminated until the lockdown bit
is cleared by an admininstrator, or if the
user verifies
the lockdown code when clicking the Terminate
button.">Lockdown:</span>
<input type="checkbox" id="lockdown-checkbox"
<% if (expinfo.lockdown) { %>checked<% } %> >
</td>
<td></td>
<td></td>
<td>Max Ext:</td>
<td id="max-extension">...</td>
<td></td>
......@@ -61,3 +42,80 @@
</div>
</div>
</div>
<div class='col-sm-3'>
<div class='panel panel-default'>
<div class="panel-heading text-center">
Flags
</div>
<div class="panel-body">
<table class="table table-condensed">
<tbody>
<tr>
<td>
<span data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, only administrator can
extend this experiment. No free time
is granted to user at all.">
Lockout:</span>
</td>
<td>
<input type="checkbox" id="lockout-checkbox"
<% if (expinfo.lockout) { %>checked<% } %> >
</td>
</tr>
<tr>
<td>
<span data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment
cannot be terminated until the admin
lockdown bit is cleared by an
admininstrator. The user cannot override
this lockdown.">
Admin Lockdown:</span>
</td>
<td>
<input type="checkbox" id="admin-lockdown-checkbox"
<% if (expinfo.admin_lockdown) { %>checked<% } %> >
</td>
</tr>
<tr>
<td>
<span data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment
cannot be terminated by the user
unless the user verifies
the lockdown code when clicking the
Terminate button.
This prevents accidental termination,
and is automatically set whenever an
experiment is extended for more then two
weeks. Use the admin lockdown flag to
fully prevent termination.">
User Lockdown:</span>
</td>
<td>
<input type="checkbox" id="user-lockdown-checkbox"
<% if (expinfo.user_lockdown) { %>checked<% } %> >
</td>
</tr>
<tr>
<td>
<span data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment is put into
Quarantine (emulab panic) mode.">
Quarantine:</span>
</td>
<td>
<input type="checkbox" id="quarantine-checkbox"
<% if (expinfo.paniced) { %>checked<% } %> >
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
......@@ -55,10 +55,16 @@ pre {
class='form-control'
rows=5></textarea>
<div style="margin-top: 10px;">
<button class='btn btn-primary btn-sm'
style='margin-right: 20px;'
data-toggle='modal' data-target='#confirm-terminate-modal'
type='button' name='terminate'>Terminate</button>
<span data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content='Schedule termination for the future (in days)
or right away (leave days zero).'>
<button class='btn btn-primary btn-sm'
style='margin-right: 20px;'
id="terminate-button"
data-toggle='modal' data-target='#confirm-terminate-modal'
type='button' name='terminate'>Schedule Termination</button>
</span>
<button class='btn btn-danger btn-sm'
style='margin-right: 20px;'
id='deny-extension'
......
......@@ -180,42 +180,6 @@ pre {
target='_blank'
type='button'>Logfile</a>
</div>
<% if (isadmin) { %>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":1000, "show":500}'
data-content="When checked, only administrator can extend
this experiment. No free time is granted to
user at all.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="lockout_checkbox"
<% if (lockout) { %>checked<% } %> >Lockout</label>
</div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":1000, "show":500}'
data-content="When checked, the experiment
cannot be terminated until the lockdown bit
is cleared by an admininstrator, or if the user
verifies
the lockdown code when clicking the Terminate
button.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="lockdown_checkbox"
<% if (lockdown) { %>checked<% } %> >Lockdown</label>
</div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":1000, "show":500}'
data-content="When checked, the experiment is put into
Quarantine (emulab panic) mode.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="quarantine_checkbox"
<% if (paniced) { %>checked<% } %> >
<span <% if (paniced) { %>class="text-danger"<% } %> >
Quarantined</span></label>
</div>
<% } %>
<div class='pull-right'>
<% if (registered && !isfadmin) { %>
<span>
......@@ -259,6 +223,60 @@ pre {
Terminate</button>
<% } %>
</div>
<% if (isadmin) { %>
<div class='clearfix'></div>
<div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, only administrator can extend
this experiment. No free time is granted to
user at all.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="lockout_checkbox"
<% if (lockout) { %>checked<% } %> >Lockout</label>
</div>
</div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment
cannot be terminated by the user unless the
user verifies
the lockdown code when clicking the Terminate
button. This prevents accidental termination,
and is automatically set whenever an experiment
is extended for more then two weeks.
Use the admin lockdown flag to fully prevent
termination.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="user_lockdown_checkbox"
<% if (user_lockdown) { %>checked<% } %> >
User Lockdown</label>
</div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment
cannot be terminated by the user. Only an
admin can do it.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="admin_lockdown_checkbox"
<% if (admin_lockdown) { %>checked<% } %> >
Admin Lockdown</label>
</div>
<div class='pull-left'
data-toggle='popover'
data-delay='{"hide":500, "show":500}'
data-content="When checked, the experiment is put into
Quarantine (emulab panic) mode.">
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="quarantine_checkbox"
<% if (paniced) { %>checked<% } %> >
<span <% if (paniced) { %>class="text-danger"<% } %> >
Quarantined</span></label>
</div>
<% } %>
</div>
</div>
</div>
......
......@@ -8,7 +8,7 @@
<p>Are you sure you want to terminate this experiment?
Click on the button below if you are really sure.
</p>
<% if (lockdown) { %>
<% if (user_lockdown) { %>
<p class='text-warning'>
This experiment has been flagged as important, possibly because
it was granted an extension by an administrator; please confirm
......
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