Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add prototype shutdown button to Notebook dashboard #1343

Closed
wants to merge 6 commits into from

6 participants

@takluyver
Owner

This adds a shutdown button on the notebook dashboard, which is a prerequisite to be able to run it without a terminal to Ctrl-C it.

It's pretty rough, and there's still quite a bit of work to be done. I thought I'd get it in so people could start telling me better ways to do things.

@takluyver
Owner

In particular, what should we do with the browser? A bit of searching suggests that we can't close a tab that we didn't open in Javascript. Should we render a simple page "Thanks for using IPython Notebook. You can now close this tab."? (It's like Windows 95 all over again!)

@fperez
Owner

Great start! Some thoughts:

  • It needs a confirmation dialog, given how destructive it is.

  • I also think this shutdown action should be available from the file menu in each notebook. Eventually we should be able to type

ipython notebook foo.ipynb

and be taken directly to that notebook page, without the dashboard being opened explicitly (it's always available though, just not shown). I suspect that use pattern would feel very natural to people, since it would be almost like editing any other local file with a regular application.

  • Yes, the static shutdown page at the end should provide a message along the lines you indicate. Perhaps with a logo that links to the main ipython.org page? Just so it doesn't look so bland :)
@takluyver
Owner

Done the first of those things - I'll get to the others tomorrow. When the action can be invoked from either the dashboard or the notebook view, where's the best place to put the JS function for it?

@fperez
Owner

Since this is still about managing the whole server, I think it should go into the dashboard code. It's just that the notebooks have a few things that are in the dashboard view. We have precedent: the new notebook action is also normally started from the dashboard, but we also expose it in each nb for convenience. I think the shutdown falls in this same category.

@minrk
Owner

(no direct bearing on this particular PR, but responding to above comment:)

ipython notebook foo.ipynb

Ultimately, when we decouple the notebook server from a single project directory, this command should actually just open the notebook window within an existing notebook server (launching it only if one is not running). The normal local use case should be that a user only ever runs a single notebook server, and everything they ever do goes through there.

@fperez
Owner

Completely agreed, @minrk.

@takluyver
Owner

Ok, we've got a confirmation dialog, a shutdown page, a menu option in the notebook view. And a merge conflict. I'll rebase it.

I added the dashboard JS file to the notebook, but doesn't that mean that the onready code from the dashboard is being invoked on the notebook page as well? Will that cause any problems?

@takluyver
Owner

Rebased.

@ellisonbg
Owner

I am -1 on having this in the notebook server. The notebook is quickly moving in the direction of being a full multi-user web application. In that context, it absolutely does not make sense to allow one user to shutdown the entire server. I would even say this does not make sense for the existing single user app, given that multiple users can still connect. It is a brutal and final action that will not only cause data loss for the user that clicks this button w/o understanding what it really does (what is the difference between killing a notebook and killing the server) but also for for every user.

We need to have some big design discussions at PyCon on how we are going to support both of the following usage cases:

  • Notebook as single user app that is lightweight.
  • Fullblown multiuser web site.

Until we do that I don't want to add things that wed us to the single user model.

@takluyver
Owner

On the other hand, I do think we need a way to stop the server besides having a terminal to hit Ctrl-C in. I don't see any way to do that without it being in the server itself.

It seems like we need some sort of 'admin' level of control in addition to the basic 'read-only'/interactive distinction we have at present. Starting the notebook locally would give you all rights. In a multiuser environment, an admin would also be able to manage users.

@ellisonbg
Owner
@minrk
Owner

If we run the notebook as a daemon (e.g. ipython notebook /path/to/stuff opens up a project/notebook page and returns immediately), then we would be able to have things like ipython notebook stop. We need to implement all of the stuff required for a single notebook server instance to fulfill a user's needs:

  • specify kernel launch arguments from the web interface (most commonly pylab/not)
  • different arguments per kernel
  • navigate filesystem to some degree
  • decouple notebook server from single project so it can serve multiple projects at once
  • reliable URLs from file paths

But I agree that since this is the direction we are going, shutdown from the web interface (other than possible admin panel) is probably not a good idea.

@takluyver
Owner

OK, closing this since we're not going to do it this way.

@takluyver takluyver closed this
@fperez
Owner

Mmh, I'd still like to think about this for a minute. The current situation is really highly sub-optimal, and I actually think that a web shutdown with confirmation dialog is better than the Ctrl-C in a terminal.

Brian mentions it's such a brutal action that he wants people to have to go to a terminal to do it, but the problem is that you can do that accidentally. It's easy to have the wrong window focused and hit Ctrl-C, killing every single kernel you have open, whereas a button that triggers a confirmation dialog before shutdown is actually safer. This form of accidental shutdown has only happened once to me so far ever and it was a trivial test notebook, so I didn't care. But I can see a user easily thinking they're doing Ctrl-C for 'copy' and accidentally shutting down a notebook with a ton of important kernels open. Keep in mind that Ctrl-C in a terminal offers no confirmation or way to back off the action, so the current situation is actually worse and less safe than what this PR offers.

Furthermore, having this shutdown button in the main page makes it possible to have a sensible GUI terminal-less launcher, which right now can't really be done because there's no way to sensible shut down the process started by the GUI launcher. But with this, we could include for Windows users an 'IPython notebook' Start menu entry, and Mac users could use one of the AppleScript examples that are floating on the net for IPython Qt console starting.

I'm all for a long-term elegant solution with a full admin panel, but I still think this fixes a serious, important limitation we have right now, and doesn't introduce any problems whatsoever. So I'd like to see this moving forward...

@takluyver takluyver reopened this
@takluyver
Owner

OK, reopened. I did put a confirmation dialog in, so feel free to check this out and test. I guess we'll just need to find a way to disable/hide the button under certain conditions.

@ellisonbg
Owner

I am still strongly -1 on this. Let continue the discussion at PyCon/PyData.

@fperez
Owner

Sure. I really think what we have is effectively broken and that this is at least a first step in the right direction, but I'd love to understand your reasons.

@ellisonbg
Owner

We never got to discussing this at PyCon, so let's continue here.

Fernando you argue for this capability using the current fact that using Ctrl-C it is currently too easy to accidentally kill the notebook and all its kernels. These are two completely separate issues. We should fix the Ctrl-C issue by allowing the notebook to run as a daemon as Min suggests earlier in this thread. But that has no bearing on whether or not we allow users to kill everything from the browser. Running the notebook as a daemon will also allow us to build GUI launchers and the "ipython notebook mynb.ipynb" syntax without worrying about adding the kill button in the notebook web UI.

Here is my thinking about this:

  • Whether we like it or not, the notebook is a multiuser server not a single user application.
  • Multiuser servers should only be started and stopped by a very special person that has appropriate access/privs.
  • Until we can distinguish who has such access/privs, we should not expose this capability in the browser.
  • The eventual implementation will have to be tightly integrated with how access/privs are exposed in the notebook web app.
@fperez
Owner
@takluyver
Owner
@ellisonbg
Owner

I am in favor of closing this PR. Maybe @fperez and I can arm wrestle over it...or hash it out on Skype sometime...

@fperez
Owner

Well, there are two questions: (i) do we want the feature at all, and (ii) is this PR the right implementation?

I still hold firm on my comment above: we're doing a huge disservice to our users by not providing a feature that:

  • would help things tremendously in usability today

  • is tiny and can easily be changed later if the overall design goes in a different direction.

Telling users "things will be different sometime in the future, so deal with a sucky UI until we figure that out" isn't a good solution.

I simply don't see what the problem is: low cost, huge benefit, easy to change later... Practicality has to beat purity sometimes...

@ellisonbg
Owner
@Carreau
Owner

How would you like to make the notebook multi-user aware ? If you spawn a process with the logged user ID that act as the webserver only for this user, then it makes sens to shutdown this webserver, the Master Server that deal with redirecting each user would only be controllable by admins.

@minrk
Owner

@ellisonbg I don't really see how having this shutdown available in the current UI impedes developing the multi-user model with an admin panel. That development should just take place as if this functionality simply doesn't exist, and then once it's in place, this should just be deleted.

@Carreau - there will not be several servers, one for each user. There will be one multi-user notebook server, and the user would own their notebooks and kernels. Thus, only admins would be able to shutdown the notebook server.

@ellisonbg
Owner

Let me approach this from a different perspective.

Imagine that I own a house. It is within my right to destroy me house and there are many valid reasons why I might want to do that (remodel, termite infested, asbestos, etc.). But, destroying my house is somewhat difficult. I have to hire contractors or rent equipment, I have to make plans to move all of my stuff out of the house, I have to find somewhere else to live in the meantime. Now imagine if I decided to put a big red button by the front door that said "DESTROY THIS HOUSE" and I wired up the button to explosives that would instantly level the house. You would say I say I was crazy. You would say that destroying a house should be difficult. You would say that it was really dangerous, that accidents happen. What if someone bumped it. What if a young child pushed the button. What if it was pushed by accident when there was a party going on - many people would be killed and I would loose all my belonging. Even if I put safety measures in place (made it difficult to push, asked for user confirmation) you would still say the button makes to too easy to destroy the house.

The shutdown button, even with safety measures put into place, is analogous to this. The entire conceptual foundation of the button (make it easy to shutdown the notebook server) is problematic. I can think of multiple scenarios where such a button could be pushed by accident (new user who is confused, young children pushing buttons on the computer randomely, etc.). If the notebook were purely a single user application, then it is not a big deal. An accidental shutdown affects just that user and is no different from killing it at the terminal by mistake. But when there are multiple users in the picture, there is the risk of serious problems. What if I was using that notebook server for a big presentation at the time you killed it? What if a number of users had unsaved work or had performed a time consuming computation?

I simple don't understand why would would want to open the door for these possibilities.

@ellisonbg
Owner

@minrk You are right that the shutdown button doesn't preclude the multiuser notebook having an admin panel. But that is my point - the shutdown button requires that the multiuser notebook have an admin panel, which is not something I want to commit to starting out.

@takluyver
Owner
@fperez
Owner
@ellisonbg
Owner
@ellisonbg
Owner
@fperez
Owner
@takluyver
Owner
@ellisonbg
Owner

@fperez has greatly clarified his thinking in his last post. Let me summarize. In our current "trusted single user mode" we already have a big "DESTROY THIS HOUSE" button on the wall, namely that any user has the full privs of the user that started the notebook. The argument is that adding a shutdown button to the notebook UI doesn't introduce any new risk that wasn't already there. I am buying this argument at the 85% level.

Here is my 15% reservation: my 6 year old regularly comes over to my laptop and clicks on things. If a notebook is open, he could easily click on a shutdown button and confirm. He would, on the other hand, have a very difficult time typing "!rm -rf ~/* into a cell and running that cell. I realize this is not too likely, which is why this is only a 15% effect for me.

I am OK going ahead with a shutdown button (we will have to finish reviewing this particular implementation) under two conditions:

  • If we ever find that the "shutdown button clicker" has more power than can be wielded by simply running code in notebook cells, the button has to go. That will happen in a full multiuser setting.
  • If the code to support the shutdown button in other contexts gets overly complex, we reconsider it.
@fperez
Owner
@ellisonbg
Owner
@fperez
Owner
@Carreau
Owner
  • We should no longer prompt the user if the kernel dies. We should just restart it a notify the user.

Then if a user have a "forgotten" notebook opened on a remote computer (let's say at home) and wan't to shut it down from the dashboard (from work) he can't ... the kernel won't stop restarting. That would be weird.

  • The individual notebook pages should not have a "stop" button.

That makes sens, we could even have the dashboard button active only if all the kernels are stopped.

@ellisonbg
Owner
@Carreau
Owner
  • We should no longer prompt the user if the kernel dies. We should just restart it a notify the user.

Then if a user have a "forgotten" notebook opened on a remote computer (let's say at home) and wan't to shut it down from the dashboard (from work) it can't ... the kernel won't stop restarting. That would be weird.

I don't follow. The heartbeating that detects a dead kernel and
triggers the kernel restart logic is handled on the server side. This
logic is disabled when a kernel is shutdown cleanly (like the
dashboard button does). This means that if you shutdown a kernel from
the dashboard while the notebook is running somewhere else, that other
notebook will simply stop being able to run code. We should probably
make sure a message is sent to that other notebook (it might be
already) and that the user is at least notified "someone else shutdown
the kernel for this notebook"

Ok, my bad, I thought the "The kernel has died, would you like to restart it?" message was done by polling in JS.
If it's pushed by the webserver then it's ok for me.
We should then push 2 kinds of messages :

  • kernel has died and been automatically restarted
  • kernel has been automatically shutdown
@bfroehle

I haven't managed to read the entire thread here, but a shutdown button seems appropriate enough. However, this pull request will need to be rebased before it can be merged.

@Carreau
Owner

Do we revive this or move close and open an issue for it ?

@Carreau
Owner

ping again.

@Carreau
Owner

Hi,
This PR is without activities for 2 month now,
I'm going to close it and open an issue to reference it.
This does not mean than we refuse the code that is here, but that we try to keep the number of opened Pull request as low as possible to have better bird view of active code.

Fell free to reopen this PR whenever you want or contact us if you have any questions.

Thanks for contributing.

@Carreau Carreau closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
5 IPython/frontend/html/notebook/handlers.py
@@ -312,6 +312,11 @@ def get(self, notebook_id):
mathjax_url=self.application.ipython_app.mathjax_url,
)
+class ShutdownHandler(AuthenticatedHandler):
+ def get(self):
+ self.render('shutdown.html', logged_in=False, login_available=False)
+ self.application.ipython_app.ioloop.stop()
+
#-----------------------------------------------------------------------------
# Kernel handlers
#-----------------------------------------------------------------------------
View
7 IPython/frontend/html/notebook/notebookapp.py
@@ -49,7 +49,7 @@
ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
- RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
+ RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler, ShutdownHandler
)
from .notebookmanager import NotebookManager
@@ -106,6 +106,7 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
(r"/", ProjectDashboardHandler),
(r"/login", LoginHandler),
(r"/logout", LogoutHandler),
+ (r"/shutdown", ShutdownHandler),
(r"/new", NewHandler),
(r"/%s" % _notebook_id_regex, NamedNotebookHandler),
(r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
@@ -419,8 +420,10 @@ def start(self):
b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
new=2)
threading.Thread(target=b).start()
+
+ self.ioloop = ioloop.IOLoop.instance()
try:
- ioloop.IOLoop.instance().start()
+ self.ioloop.start()
except KeyboardInterrupt:
info("Interrupted...")
finally:
View
6 IPython/frontend/html/notebook/static/css/projectdashboard.css
@@ -49,10 +49,14 @@ body {
float: left;
}
-#notebooks_buttons {
+#notebooks_buttons, #control_buttons {
float: right;
}
+#control_toolbar {
+ padding-top: 0.5em;
+}
+
#project_name {
height: 25px;
line-height: 25px;
View
1  IPython/frontend/html/notebook/static/js/menubar.js
@@ -67,6 +67,7 @@ var IPython = (function (IPython) {
this.element.find('button#print_notebook').click(function () {
IPython.print_widget.print_notebook();
});
+ this.element.find('#shutdown_server').click(confirm_shutdown);
// Edit
this.element.find('#cut_cell').click(function () {
IPython.notebook.cut_cell();
View
27 IPython/frontend/html/notebook/static/js/projectdashboardmain.js
@@ -10,6 +10,30 @@
//============================================================================
+confirm_shutdown = function () {
+ var dialog = $('<div/>');
+ dialog.html('Do you want to shut down the notebook server? ' +
+ 'You will lose any unsaved work and all running kernels.');
+ $(document).append(dialog);
+ dialog.dialog({
+ resizable: false,
+ modal: true,
+ title: "Shutdown IPython Notebook?",
+ closeText: '',
+ buttons : {
+ "Shutdown": function () {
+ window.location.href = $('body').data('baseProjectUrl')+'shutdown';
+ $(this).dialog('close');
+ },
+ "Continue running": function () {
+ $(this).dialog('close');
+ }
+ }
+ });
+
+};
+
+
$(document).ready(function () {
$('div#header').addClass('border-box-sizing');
@@ -37,6 +61,9 @@ $(document).ready(function () {
$('div#header').css('display','block');
$('div#main_app').css('display','block');
+ $('#shutdown').button().click(function(e) {
+ confirm_shutdown();
+ });
});
View
3  IPython/frontend/html/notebook/templates/notebook.html
@@ -77,6 +77,8 @@
</li>
<hr/>
<li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
+ <hr/>
+ <li id="shutdown_server"><a href="#">Shutdown Notebooks</a></li>
</ul>
</li>
<li><a href="#">Edit</a>
@@ -221,6 +223,7 @@
<script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
+<script src="{{ static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
</body>
View
6 IPython/frontend/html/notebook/templates/projectdashboard.html
@@ -35,6 +35,12 @@
<div id="notebook_list">
<div id="project_name"><h2>{{project}}</h2></div>
</div>
+
+ <div id="control_toolbar">
+ <span id="control_buttons">
+ <button id="shutdown">Shutdown Notebook Server</button>
+ </span>
+ </div>
{% end %}
{% block script %}
View
39 IPython/frontend/html/notebook/templates/shutdown.html
@@ -0,0 +1,39 @@
+{% extends layout.html %}
+
+{% block stylesheet %}
+<style type="text/css">
+#app_hbox {
+ width: 100%
+}
+
+#content_panel {
+ width: 800px;
+ margin-left: auto;
+ margin-right: auto;
+ padding-top: 1em;
+}
+
+h1 {
+ font-size: 2em;
+ padding-bottom: 1em;
+}
+
+p {
+ padding-bottom: 1em;
+}
+
+</style>
+{% end %}
+
+{% block title %}
+(Shutdown) IPython Notebook
+{% end %}
+
+{% block content_panel %}
+<h1>Notebook Shutdown</h1>
+
+<p>The IPython Notebook server has been stopped. You can close this tab.</p>
+
+<p><a href="http://ipython.org/">IPython homepage</a></p>
+
+{% end %}
Something went wrong with that request. Please try again.