Commit 1e5b0c88 authored by Leigh B Stoller's avatar Leigh B Stoller

Several changes:

* Add a "lockout" checkbox to the red-dot instance view. When checked it
  prevents all future extension requests (no pad time granted either). This
  is an active checkbox; backend ajax call when you check/uncheck.

* Add Created to the instance view panel.

* Most of the snapshot node changes, but hidden for now until I get more
  testing done. This includes the modal and ajax changes, but not the
  backend changes (still in my devel tree).

* Minor changes to date formatting.

* More details in the extension email.
parent 7390f752
...@@ -32,6 +32,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -32,6 +32,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
var statusTemplate = _.template(statusString); var statusTemplate = _.template(statusString);
var terminateTemplate = _.template(terminateString); var terminateTemplate = _.template(terminateString);
var lastStatus = ""; var lastStatus = "";
var lockout = 0;
var lockdown = 0; var lockdown = 0;
var lockdown_code = ""; var lockdown_code = "";
var EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1"; var EMULAB_NS = "http://www.protogeni.net/resources/rspec/ext/emulab/1";
...@@ -47,6 +48,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -47,6 +48,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
dossh = window.APT_OPTIONS.dossh; dossh = window.APT_OPTIONS.dossh;
extend = window.APT_OPTIONS.extend || null; extend = window.APT_OPTIONS.extend || null;
profile_uuid = window.APT_OPTIONS.profileUUID; profile_uuid = window.APT_OPTIONS.profileUUID;
lockout = window.APT_OPTIONS.lockout;
lockdown = window.APT_OPTIONS.lockdown; lockdown = window.APT_OPTIONS.lockdown;
lockdown_code= uuid.substr(2, 5); lockdown_code= uuid.substr(2, 5);
var instanceStatus = window.APT_OPTIONS.instanceStatus; var instanceStatus = window.APT_OPTIONS.instanceStatus;
...@@ -65,11 +67,13 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -65,11 +67,13 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
sliceURN: window.APT_OPTIONS.sliceURN, sliceURN: window.APT_OPTIONS.sliceURN,
sliceExpires: window.APT_OPTIONS.sliceExpires, sliceExpires: window.APT_OPTIONS.sliceExpires,
sliceExpiresText: window.APT_OPTIONS.sliceExpiresText, sliceExpiresText: window.APT_OPTIONS.sliceExpiresText,
sliceCreated: window.APT_OPTIONS.sliceCreated,
creatorUid: window.APT_OPTIONS.creatorUid, creatorUid: window.APT_OPTIONS.creatorUid,
creatorEmail: window.APT_OPTIONS.creatorEmail, creatorEmail: window.APT_OPTIONS.creatorEmail,
registered: window.APT_OPTIONS.registered, registered: window.APT_OPTIONS.registered,
isadmin: window.APT_OPTIONS.isadmin, isadmin: window.APT_OPTIONS.isadmin,
errorURL: errorURL, errorURL: errorURL,
lockout: lockout,
lockdown: lockdown, lockdown: lockdown,
lockdown_code: lockdown_code, lockdown_code: lockdown_code,
// The status panel starts out collapsed. // The status panel starts out collapsed.
...@@ -84,6 +88,14 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -84,6 +88,14 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
$('#oneonly_div').html(oneonlyString); $('#oneonly_div').html(oneonlyString);
$('#approval_div').html(approvalString); $('#approval_div').html(approvalString);
// Format dates with moment before display.
$('.format-date').each(function() {
var date = $.trim($(this).html());
if (date != "") {
$(this).html(moment($(this).html()).format("lll"));
}
});
// //
// Look at initial status to determine if we show the progress bar. // Look at initial status to determine if we show the progress bar.
// //
...@@ -155,13 +167,6 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -155,13 +167,6 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
'&snapuuid=' + uuid); '&snapuuid=' + uuid);
}); });
// Handler for the Snapshot confirm button.
$('button#snapshot_confirm').click(function (event) {
event.preventDefault();
sup.HideModal('#snapshot_modal');
StartSnapshot();
});
// If we got a publicURL, set the href and show the button. // If we got a publicURL, set the href and show the button.
if (window.APT_OPTIONS.publicURL) { if (window.APT_OPTIONS.publicURL) {
ShowSliverInfo(window.APT_OPTIONS.publicURL); ShowSliverInfo(window.APT_OPTIONS.publicURL);
...@@ -217,6 +222,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -217,6 +222,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
clearTimeout(popover_timer); clearTimeout(popover_timer);
}).click(function(){ }).click(function(){
clearTimeout(popover_timer); clearTimeout(popover_timer);
DoSnapshotNode();
}); });
// Terminate an experiment. // Terminate an experiment.
...@@ -256,6 +262,11 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -256,6 +262,11 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
xmlthing.done(callback); xmlthing.done(callback);
}); });
// lockout change event handler.
$('#lockout_checkbox').change(function() {
DoLockout($(this).is(":checked"));
});
/* /*
* Attach an event handler to the profile status collapse. * Attach an event handler to the profile status collapse.
* We want to change the text inside the collapsed view * We want to change the text inside the collapsed view
...@@ -514,7 +525,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -514,7 +525,7 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
// Reformat in local time and show the user. // Reformat in local time and show the user.
var local_date = new Date(when); var local_date = new Date(when);
$("#quickvm_expires").html(moment(when).calendar()); $("#quickvm_expires").html(moment(when).format('lll'));
// Countdown also based on local time. // Countdown also based on local time.
target_date = local_date.getTime(); target_date = local_date.getTime();
...@@ -599,12 +610,31 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -599,12 +610,31 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
sup.SpitOops("oops", message); sup.SpitOops("oops", message);
return; return;
} }
$("#quickvm_expires").html(moment(json.value).calendar()); $("#quickvm_expires").html(moment(json.value).format('lll'));
// Reset the countdown clock. // Reset the countdown clock.
StartCountdownClock.reset = json.value; StartCountdownClock.reset = json.value;
} }
//
// Request lockout set/clear.
//
function DoLockout(lockout)
{
lockout = (lockout ? 1 : 0);
var callback = function(json) {
if (json.code) {
alert("Failed to change lockout: " + json.value);
return;
}
}
var xmlthing = sup.CallServerMethod(ajaxurl, "status", "Lockout",
{"uuid" : uuid,
"lockout" : lockout});
xmlthing.done(callback);
}
// //
// Request a refresh from the backend cluster, to see if the sliverstatus // Request a refresh from the backend cluster, to see if the sliverstatus
// has changed. // has changed.
...@@ -884,6 +914,13 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -884,6 +914,13 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
} }
DoReload(client_id); DoReload(client_id);
} }
else if ($(e.target).text() == "Snapshot") {
if (isguest) {
alert("Only registered users can snapshot nodes");
return;
}
DoSnapshotNode(client_id);
}
$('#context').contextmenu('destroy'); $('#context').contextmenu('destroy');
} }
}) })
...@@ -912,11 +949,12 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -912,11 +949,12 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
" data-toggle='dropdown'> " + " data-toggle='dropdown'> " +
" <span class='glyphicon glyphicon-cog'></span> " + " <span class='glyphicon glyphicon-cog'></span> " +
" </button> " + " </button> " +
" <ul class='dropdown-menu' role='menu'> " + " <ul class='dropdown-menu text-left' role='menu'> " +
" <li><a href='#' name='shell'>Shell</a></li> " + " <li><a href='#' name='shell'>Shell</a></li> " +
" <li><a href='#' name='console'>Console</a></li> " + " <li><a href='#' name='console'>Console</a></li> " +
" <li><a href='#' name='reboot'>Reboot</a></li> " + " <li><a href='#' name='reboot'>Reboot</a></li> " +
" <li><a href='#' name='reload'>Reload</a></li> " + " <li><a href='#' name='reload'>Reload</a></li> " +
" <li class=hidden><a href='#' name='snapshot'>Snapshot</a></li> " +
" </ul>" + " </ul>" +
" </div>" + " </div>" +
" </td>" + " </td>" +
...@@ -1063,6 +1101,20 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -1063,6 +1101,20 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
return false; return false;
}); });
//
// And a handler for the snapshot action.
//
$('#listview-row-' + node + ' [name=snapshot]')
.click(function (e) {
e.preventDefault();
if (isguest) {
alert("Only registered users can snapshot nodes");
return false;
}
DoSnapshotNode(node);
return false;
});
nodecount++; nodecount++;
}); });
...@@ -1165,8 +1217,11 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -1165,8 +1217,11 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
// //
// Request to start a snapshot. This assumes a single node of course. // Request to start a snapshot. This assumes a single node of course.
// //
function StartSnapshot() function StartSnapshot(node_id, update_profile)
{ {
if (node_id === undefined) {
node_id = "";
}
sup.ShowModal('#waitwait-modal'); sup.ShowModal('#waitwait-modal');
var callback = function(json) { var callback = function(json) {
...@@ -1180,13 +1235,56 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal, ...@@ -1180,13 +1235,56 @@ function (_, sup, moment, marked, UriTemplate, ShowImagingModal,
} }
ShowProgressModal(); ShowProgressModal();
} }
var xmlthing = sup.CallServerMethod(ajaxurl, var xmlthing =
"status", sup.CallServerMethod(ajaxurl, "status", "SnapShot",
"SnapShot", {"uuid" : uuid,
{"uuid" : uuid}); "node_id" : node_id,
"update_profile" : update_profile});
xmlthing.done(callback); xmlthing.done(callback);
} }
//
// This is for snapshot of a single node profile, or a specific
// node in a multi-node profile.
//
function DoSnapshotNode(node_id)
{
// Default to update unless checkbox says otherwise.
var update_profile = 1;
//
// Snapshot specific node from the context menu. We give the
// the user some extra options in confirm modal.
//
if (node_id) {
// Default to checked any time we show the modal.
$('#snapshot_update_profile').prop("checked", true);
$('#snapshot_update_profile_div').removeClass("hidden");
}
else {
$('#snapshot_update_profile_div').addClass("hidden");
}
sup.ShowModal('#snapshot_modal');
// Handler for the Snapshot confirm button.
$('button#snapshot_confirm').bind("click.snapshot", function (event) {
event.preventDefault();
$('button#snapshot_confirm').unbind("click.snapshot");
if (node_id && $('#snapshot_update_profile').is(':checked')) {
update_profile = 1;
}
sup.HideModal('#snapshot_modal');
StartSnapshot(node_id, update_profile);
});
// Handler for hide modal to unbind the click handler.
$('#snapshot_modal').on('hidden.bs.modal', function (event) {
$(this).unbind(event);
$('button#snapshot_confirm').unbind("click.snapshot");
});
}
// //
// User clicked on a node, so we want to create a tab to hold // User clicked on a node, so we want to create a tab to hold
// the ssh tab with a panel in it, and then call StartSSH above // the ssh tab with a panel in it, and then call StartSSH above
......
...@@ -303,10 +303,20 @@ function Do_RequestExtension() ...@@ -303,10 +303,20 @@ function Do_RequestExtension()
else { else {
$diff = $expires_time - time(); $diff = $expires_time - time();
$cdiff = time() - $created_time; $cdiff = time() - $created_time;
#
# If admin lockout, we are refusing any more free time.
#
if ($instance->extension_lockout()) {
$needapproval = 1;
$granted = 0;
$message = "because you are not allowed any more ".
"free extensions";
}
# #
# After a month, all extension requests require admin approval. # After a month, all extension requests require admin approval.
# #
if ($cdiff > (3600 * 24 * $autoextend_maxage)) { elseif ($cdiff > (3600 * 24 * $autoextend_maxage)) {
$needapproval = 1; $needapproval = 1;
$granted = 2; $granted = 2;
$message = "because it was started more then ". $message = "because it was started more then ".
...@@ -369,7 +379,9 @@ function Do_RequestExtension() ...@@ -369,7 +379,9 @@ function Do_RequestExtension()
} }
# Refresh. # Refresh.
$slice = GeniSlice::Lookup("sa", $instance->slice_uuid()); $slice = GeniSlice::Lookup("sa", $instance->slice_uuid());
$new_expires = gmdate("Y-m-d\TH:i:s\Z", strtotime($slice->expires())); $new_expires = date("Y-m-d H:i:s T", strtotime($slice->expires()));
$created = date("Y-m-d H:i:s T", strtotime($instance->created()));
list($cluster) = Instance::ParseURN($instance->aggregate_urn());
SPITAJAX_RESPONSE($new_expires); SPITAJAX_RESPONSE($new_expires);
...@@ -378,7 +390,9 @@ function Do_RequestExtension() ...@@ -378,7 +390,9 @@ function Do_RequestExtension()
"A request to extend your experiment was made and ". "A request to extend your experiment was made and ".
"granted.\n". "granted.\n".
"Your reason was:\n\n". $reason . "\n\n". "Your reason was:\n\n". $reason . "\n\n".
"Your experiment will now expire at $new_expires.\n\n\n". "Your experiment was started on $created\n".
"Your experiment will now expire at $new_expires\n".
"It is running on $cluster\n\n\n".
"$APTBASE/status.php?uuid=$uuid\n", "$APTBASE/status.php?uuid=$uuid\n",
"From: $APTMAIL\n" . "From: $APTMAIL\n" .
"BCC: $TBMAIL_OPS"); "BCC: $TBMAIL_OPS");
...@@ -420,9 +434,10 @@ function needAdminApproval($wanted, $granted, $reason, $message) ...@@ -420,9 +434,10 @@ function needAdminApproval($wanted, $granted, $reason, $message)
# Refresh. # Refresh.
$slice = GeniSlice::Lookup("sa", $instance->slice_uuid()); $slice = GeniSlice::Lookup("sa", $instance->slice_uuid());
$new_expires = date("Y-m-d H:i:s", $new_expires = date("Y-m-d H:i:s T",
strtotime($slice->expires()) + ($howlong * 3600 * 24)); strtotime($slice->expires()) + ($howlong * 3600 * 24));
$created = date("Y-m-d H:i:s", strtotime($instance->created())); $created = date("Y-m-d H:i:s T", strtotime($instance->created()));
list($cluster) = Instance::ParseURN($instance->aggregate_urn());
$instance->SendEmail($APTMAIL, $instance->SendEmail($APTMAIL,
"Experiment Extension Request: $uuid", "Experiment Extension Request: $uuid",
...@@ -434,7 +449,7 @@ function needAdminApproval($wanted, $granted, $reason, $message) ...@@ -434,7 +449,7 @@ function needAdminApproval($wanted, $granted, $reason, $message)
$reason . "\n\n". $reason . "\n\n".
"This experiment was started on $created\n". "This experiment was started on $created\n".
"Granting the request would set the expiration to $new_expires\n". "Granting the request would set the expiration to $new_expires\n".
"It is running on ". $instance->aggregate_urn() . "\n". "It is running on $cluster\n".
"\n\n". $url . "\n\n", "\n\n". $url . "\n\n",
"From: " . $creator->email()); "From: " . $creator->email());
...@@ -443,8 +458,8 @@ function needAdminApproval($wanted, $granted, $reason, $message) ...@@ -443,8 +458,8 @@ function needAdminApproval($wanted, $granted, $reason, $message)
# XXX # XXX
SPITAJAX_ERROR(2, "Your request requires admininstrator approval". SPITAJAX_ERROR(2, "Your request requires admininstrator approval".
($message ? " because $message" : "") . ". " . ($message ? " because $message" : "") . ". " .
"You will receive email when your ". "You will receive email if/when your ".
"request is granted. Thanks!"); "request is granted (or denied). Thanks!");
return; return;
} }
...@@ -540,13 +555,28 @@ function Do_Snapshot() ...@@ -540,13 +555,28 @@ function Do_Snapshot()
SPITAJAX_ERROR(1, "Not your profile to change. Clone first!"); SPITAJAX_ERROR(1, "Not your profile to change. Clone first!");
return; return;
} }
$optargs = "";
if (isset($ajax_args["node_id"])) {
$node_id = $ajax_args["node_id"];
if (!TBvalid_vnode_id($node_id)) {
SPITAJAX_ERROR(1, "Bad node id");
return;
}
$optargs .= " -s $node_id ";
if (isset($ajax_args["update_profile"]) &&
$ajax_args["update_profile"]) {
$optargs .= " -u all ";
}
}
# #
# Call out to the backend. # Call out to the backend.
# #
$webtask_id = WebTask::GenerateID(); $webtask_id = WebTask::GenerateID();
$retval = SUEXEC($this_user->uid(), "nobody", $retval = SUEXEC($this_user->uid(), "nobody",
"webmanage_instance -t $webtask_id -- snapshot $uuid", "webmanage_instance -t $webtask_id -- ".
" snapshot $optargs $uuid",
SUEXEC_ACTION_IGNORE); SUEXEC_ACTION_IGNORE);
$webtask = WebTask::Lookup($webtask_id); $webtask = WebTask::Lookup($webtask_id);
...@@ -768,6 +798,43 @@ function Do_Reload() ...@@ -768,6 +798,43 @@ function Do_Reload()
Do_RebootOrReload("reload"); Do_RebootOrReload("reload");
} }
#
# Set or clear the lockout flag
#
function Do_Lockout()
{
global $this_user;
global $ajax_args;
$this_idx = $this_user->uid_idx();
if (!isset($ajax_args["uuid"])) {
SPITAJAX_ERROR(1, "Missing profile uuid");
return;
}
if (!isset($ajax_args["lockout"])) {
SPITAJAX_ERROR(1, "Missing lockout value");
return;
}
$uuid = $ajax_args["uuid"];
$instance = Instance::Lookup($uuid);
if (!$instance) {
SPITAJAX_ERROR(1, "Unknown instance uuid");
return;
}
if (!ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission.");
return;
}
$lockout = ($ajax_args["lockout"] ? 1 : 0);
if (!DBQueryWarn("update apt_instances set extension_adminonly='$lockout' ".
"where uuid='$uuid'")) {
SPITAJAX_ERROR(1, "Database failure.");
return;
}
SPITAJAX_RESPONSE("Success");
}
# Local Variables: # Local Variables:
# mode:php # mode:php
# End: # End:
......
...@@ -139,11 +139,13 @@ if ($slice) { ...@@ -139,11 +139,13 @@ if ($slice) {
$slice_urn = $slice->urn(); $slice_urn = $slice->urn();
$slice_expires = DateStringGMT($slice->expires()); $slice_expires = DateStringGMT($slice->expires());
$slice_expires_text = gmdate("m-d\TH:i\Z", strtotime($slice->expires())); $slice_expires_text = gmdate("m-d\TH:i\Z", strtotime($slice->expires()));
$slice_created = DateStringGMT($instance->created());
} }
else { else {
$slice_urn = ""; $slice_urn = "";
$slice_expires = ""; $slice_expires = "";
$slice_expires_text = ""; $slice_expires_text = "";
$slice_created = "";
} }
$registered = (isset($this_user) ? "true" : "false"); $registered = (isset($this_user) ? "true" : "false");
$snapping = 0; $snapping = 0;
...@@ -153,6 +155,7 @@ $lockdown = ($instance->admin_lockdown() || ...@@ -153,6 +155,7 @@ $lockdown = ($instance->admin_lockdown() ||
$instance->user_lockdown() ? 1 : 0); $instance->user_lockdown() ? 1 : 0);
$extension_reason= ($instance->extension_reason() ? $extension_reason= ($instance->extension_reason() ?
CleanString($instance->extension_reason()) : ""); CleanString($instance->extension_reason()) : "");
$lockout = $instance->extension_lockout();
# #
# We give ssh to the creator (real user or guest user). # We give ssh to the creator (real user or guest user).
...@@ -193,6 +196,7 @@ echo " window.APT_OPTIONS.profilePublic = " . $profile_public . ";\n"; ...@@ -193,6 +196,7 @@ echo " window.APT_OPTIONS.profilePublic = " . $profile_public . ";\n";
echo " window.APT_OPTIONS.sliceURN = '" . $slice_urn . "';\n"; echo " window.APT_OPTIONS.sliceURN = '" . $slice_urn . "';\n";
echo " window.APT_OPTIONS.sliceExpires = '" . $slice_expires . "';\n"; echo " window.APT_OPTIONS.sliceExpires = '" . $slice_expires . "';\n";
echo " window.APT_OPTIONS.sliceExpiresText = '" . $slice_expires_text . "';\n"; echo " window.APT_OPTIONS.sliceExpiresText = '" . $slice_expires_text . "';\n";
echo " window.APT_OPTIONS.sliceCreated = '" . $slice_created . "';\n";
echo " window.APT_OPTIONS.creatorUid = '" . $creator_uid . "';\n"; echo " window.APT_OPTIONS.creatorUid = '" . $creator_uid . "';\n";
echo " window.APT_OPTIONS.creatorEmail = '" . $creator_email . "';\n"; echo " window.APT_OPTIONS.creatorEmail = '" . $creator_email . "';\n";
echo " window.APT_OPTIONS.registered = $registered;\n"; echo " window.APT_OPTIONS.registered = $registered;\n";
...@@ -204,6 +208,7 @@ echo " window.APT_OPTIONS.oneonly = $oneonly;\n"; ...@@ -204,6 +208,7 @@ echo " window.APT_OPTIONS.oneonly = $oneonly;\n";
echo " window.APT_OPTIONS.dossh = $dossh;\n"; echo " window.APT_OPTIONS.dossh = $dossh;\n";
echo " window.APT_OPTIONS.publicURL = $public_url;\n"; echo " window.APT_OPTIONS.publicURL = $public_url;\n";
echo " window.APT_OPTIONS.lockdown = $lockdown;\n"; echo " window.APT_OPTIONS.lockdown = $lockdown;\n";
echo " window.APT_OPTIONS.lockout = $lockout;\n";
echo " window.APT_OPTIONS.AJAXURL = 'server-ajax.php';\n"; echo " window.APT_OPTIONS.AJAXURL = 'server-ajax.php';\n";
if (isset($extend) && $extend != "") { if (isset($extend) && $extend != "") {
echo " window.APT_OPTIONS.extend = $extend;\n"; echo " window.APT_OPTIONS.extend = $extend;\n";
......
...@@ -51,11 +51,16 @@ ...@@ -51,11 +51,16 @@
<%- profileName %></a></td> <%- profileName %></a></td>
</tr> </tr>
<% } %> <% } %>
<tr>
<td class='border-none'>Created:</td>
<td class='border-none format-date'><%- sliceCreated %></td>
</tr>
<tr> <tr>
<td class='border-none'>Expires:</td> <td class='border-none'>Expires:</td>
<td class='border-none'> <td class='border-none'>
<span id='instance_expiration'> <span id='instance_expiration'>
<span id='quickvm_expires'><%- sliceExpiresText %></span> <span id='quickvm_expires' class='format-date'>
<%- sliceExpires %></span>
(<span id='quickvm_countdown'></span>) (<span id='quickvm_countdown'></span>)
</span> </span>
<% if (isadmin && lockdown) { %> <% if (isadmin && lockdown) { %>
...@@ -72,14 +77,20 @@ ...@@ -72,14 +77,20 @@
type='button'>Sliver type='button'>Sliver
</a> </a>
</div> </div>
<% if (isadmin) { %>
<div class='pull-left'>
<label class="checkbox-inline" style='margin-right: 10px;'>
<input type="checkbox" id="lockout_checkbox"
<% if (lockout) { %>checked<% } %> >Locked</label>
</div>
<% } %>
<div class='pull-right'> <div class='pull-right'>
<% if (registered) { %> <% if (registered) { %>
<button class='btn btn-xs btn-primary hidden' disabled <button class='btn btn-xs btn-primary hidden' disabled
id='clone_button' type=button> id='clone_button' type=button>
Clone</button> Clone</button>
<button class='btn btn-xs btn-primary hidden' disabled <button class='btn btn-xs btn-primary hidden' disabled
id='snapshot_button' type=button id='snapshot_button' type=button>
data-toggle='modal' data-target='#snapshot_modal'>
Snapshot</button> Snapshot</button>
<% if (profileUUID && profileUUID != "") { %> <% if (profileUUID && profileUUID != "") { %>
<a class='btn btn-xs btn-primary' <a class='btn btn-xs btn-primary'
...@@ -223,14 +234,22 @@ ...@@ -223,14 +234,22 @@
<div class='modal-dialog'> <div class='modal-dialog'>
<div class='modal-content'> <div class='modal-content'>
<div class='modal-header'> <div class='modal-header'>
<button type='button' class='close' data-dismiss='modal'
aria-hidden='true'>&times;</button>
<center><h3>Confirm to Snapshot</h3> <center><h3>Confirm to Snapshot</h3>
</div> </div>
<div class='modal-body'> <div class='modal-body'>
Performing a snapshot will create a new disk image and <div id='snapshot_update_profile_div' class='hidden'>
modify the profile. This is okay, but please confirm. <center>
<b>Update Profile?</b>
<br>
<input type=checkbox
id='snapshot_update_profile' checked value=yes>
</center>
Check this box if you want us to update your profile to use the
new disk image. All nodes running the same image will be
updated. If you uncheck the box, you will need to modify the
profile source code yourself.
<br> <br>
</div>
<div id='wholedisk_div' class='hidden'> <div id='wholedisk_div' class='hidden'>
<br> <br>
<center> <center>
...@@ -241,11 +260,16 @@ ...@@ -241,11 +260,16 @@
need to do this if you put data into one of the unused need to do this if you put data into one of the unused
partitions on the local disk. <b><em>Do not check this box if you partitions on the local disk. <b><em>Do not check this box if you
do not know what this means!</em></b> do not know what this means!</em></b>
<br>
</div> </div>
<br> <center>
<button class='btn btn-danger btn-sm align-center' type='button' <button style='margin-right: 20px;'
id='snapshot_confirm'>Confirm</button></center> class='btn btn-primary btn-sm'
data-dismiss='modal' aria-hidden='true'>
Cancel</button>
<button class='btn btn-danger btn-sm'; type='button'
id='snapshot_confirm'>
Confirm</button>
</center>
</div> </div>
</div> </div>
<