##// END OF EJS Templates
manual rebase services/kernels/
Zachary Sailer -
Show More
@@ -1,182 +1,187 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import logging
19 import logging
20 from tornado import web
20 from tornado import web
21
21
22 from zmq.utils import jsonapi
22 from zmq.utils import jsonapi
23
23
24 from IPython.utils.jsonutil import date_default
24 from IPython.utils.jsonutil import date_default
25
25
26 from ...base.handlers import IPythonHandler
26 from ...base.handlers import IPythonHandler
27 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
27 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Kernel handlers
30 # Kernel handlers
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33
33
34 class MainKernelHandler(IPythonHandler):
34 class MainKernelHandler(IPythonHandler):
35
35
36 @web.authenticated
36 @web.authenticated
37 def get(self):
37 def get(self):
38 km = self.kernel_manager
38 km = self.kernel_manager
39 self.finish(jsonapi.dumps(km.list_kernel_ids()))
39 self.finish(jsonapi.dumps(km.list_kernels()))
40
40
41 @web.authenticated
41 @web.authenticated
42 def post(self):
42 def post(self):
43 km = self.kernel_manager
43 km = self.kernel_manager
44 nbm = self.notebook_manager
44 nbm = self.notebook_manager
45 notebook_id = self.get_argument('notebook', default=None)
45 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
46 kernel_id = km.start_kernel(notebook_id, cwd=nbm.notebook_dir)
46 model = km.kernel_model(kernel_id, self.ws_url)
47 data = {'ws_url':self.ws_url,'kernel_id':kernel_id}
48 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
47 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
49 self.finish(jsonapi.dumps(data))
48 self.finish(jsonapi.dumps(model))
50
49
51
50
52 class KernelHandler(IPythonHandler):
51 class KernelHandler(IPythonHandler):
53
52
54 SUPPORTED_METHODS = ('DELETE')
53 SUPPORTED_METHODS = ('DELETE', 'GET')
54
55 @web.authenticated
56 def get(self, kernel_id):
57 km = self.kernel_manager
58 model = km.kernel_model(kernel_id,self.ws_url)
59 self.finish(jsonapi.dumps(model))
55
60
56 @web.authenticated
61 @web.authenticated
57 def delete(self, kernel_id):
62 def delete(self, kernel_id):
58 km = self.kernel_manager
63 km = self.kernel_manager
59 km.shutdown_kernel(kernel_id)
64 km.shutdown_kernel(kernel_id)
60 self.set_status(204)
65 self.set_status(204)
61 self.finish()
66 self.finish()
62
67
63
68
64 class KernelActionHandler(IPythonHandler):
69 class KernelActionHandler(IPythonHandler):
65
70
66 @web.authenticated
71 @web.authenticated
67 def post(self, kernel_id, action):
72 def post(self, kernel_id, action):
68 km = self.kernel_manager
73 km = self.kernel_manager
69 if action == 'interrupt':
74 if action == 'interrupt':
70 km.interrupt_kernel(kernel_id)
75 km.interrupt_kernel(kernel_id)
71 self.set_status(204)
76 self.set_status(204)
72 if action == 'restart':
77 if action == 'restart':
73 km.restart_kernel(kernel_id)
78 km.restart_kernel(kernel_id)
74 data = {'ws_url':self.ws_url, 'kernel_id':kernel_id}
79 model = km.kernel_model(kernel_id,self.ws_url)
75 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
80 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
76 self.write(jsonapi.dumps(data))
81 self.write(jsonapi.dumps(model))
77 self.finish()
82 self.finish()
78
83
79
84
80 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
85 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
81
86
82 def create_stream(self):
87 def create_stream(self):
83 km = self.kernel_manager
88 km = self.kernel_manager
84 meth = getattr(km, 'connect_%s' % self.channel)
89 meth = getattr(km, 'connect_%s' % self.channel)
85 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
90 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
86
91
87 def initialize(self, *args, **kwargs):
92 def initialize(self, *args, **kwargs):
88 self.zmq_stream = None
93 self.zmq_stream = None
89
94
90 def on_first_message(self, msg):
95 def on_first_message(self, msg):
91 try:
96 try:
92 super(ZMQChannelHandler, self).on_first_message(msg)
97 super(ZMQChannelHandler, self).on_first_message(msg)
93 except web.HTTPError:
98 except web.HTTPError:
94 self.close()
99 self.close()
95 return
100 return
96 try:
101 try:
97 self.create_stream()
102 self.create_stream()
98 except web.HTTPError:
103 except web.HTTPError:
99 # WebSockets don't response to traditional error codes so we
104 # WebSockets don't response to traditional error codes so we
100 # close the connection.
105 # close the connection.
101 if not self.stream.closed():
106 if not self.stream.closed():
102 self.stream.close()
107 self.stream.close()
103 self.close()
108 self.close()
104 else:
109 else:
105 self.zmq_stream.on_recv(self._on_zmq_reply)
110 self.zmq_stream.on_recv(self._on_zmq_reply)
106
111
107 def on_message(self, msg):
112 def on_message(self, msg):
108 msg = jsonapi.loads(msg)
113 msg = jsonapi.loads(msg)
109 self.session.send(self.zmq_stream, msg)
114 self.session.send(self.zmq_stream, msg)
110
115
111 def on_close(self):
116 def on_close(self):
112 # This method can be called twice, once by self.kernel_died and once
117 # This method can be called twice, once by self.kernel_died and once
113 # from the WebSocket close event. If the WebSocket connection is
118 # from the WebSocket close event. If the WebSocket connection is
114 # closed before the ZMQ streams are setup, they could be None.
119 # closed before the ZMQ streams are setup, they could be None.
115 if self.zmq_stream is not None and not self.zmq_stream.closed():
120 if self.zmq_stream is not None and not self.zmq_stream.closed():
116 self.zmq_stream.on_recv(None)
121 self.zmq_stream.on_recv(None)
117 self.zmq_stream.close()
122 self.zmq_stream.close()
118
123
119
124
120 class IOPubHandler(ZMQChannelHandler):
125 class IOPubHandler(ZMQChannelHandler):
121 channel = 'iopub'
126 channel = 'iopub'
122
127
123 def create_stream(self):
128 def create_stream(self):
124 super(IOPubHandler, self).create_stream()
129 super(IOPubHandler, self).create_stream()
125 km = self.kernel_manager
130 km = self.kernel_manager
126 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
131 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
127 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
132 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
128
133
129 def on_close(self):
134 def on_close(self):
130 km = self.kernel_manager
135 km = self.kernel_manager
131 if self.kernel_id in km:
136 if self.kernel_id in km:
132 km.remove_restart_callback(
137 km.remove_restart_callback(
133 self.kernel_id, self.on_kernel_restarted,
138 self.kernel_id, self.on_kernel_restarted,
134 )
139 )
135 km.remove_restart_callback(
140 km.remove_restart_callback(
136 self.kernel_id, self.on_restart_failed, 'dead',
141 self.kernel_id, self.on_restart_failed, 'dead',
137 )
142 )
138 super(IOPubHandler, self).on_close()
143 super(IOPubHandler, self).on_close()
139
144
140 def _send_status_message(self, status):
145 def _send_status_message(self, status):
141 msg = self.session.msg("status",
146 msg = self.session.msg("status",
142 {'execution_state': status}
147 {'execution_state': status}
143 )
148 )
144 self.write_message(jsonapi.dumps(msg, default=date_default))
149 self.write_message(jsonapi.dumps(msg, default=date_default))
145
150
146 def on_kernel_restarted(self):
151 def on_kernel_restarted(self):
147 logging.warn("kernel %s restarted", self.kernel_id)
152 logging.warn("kernel %s restarted", self.kernel_id)
148 self._send_status_message('restarting')
153 self._send_status_message('restarting')
149
154
150 def on_restart_failed(self):
155 def on_restart_failed(self):
151 logging.error("kernel %s restarted failed!", self.kernel_id)
156 logging.error("kernel %s restarted failed!", self.kernel_id)
152 self._send_status_message('dead')
157 self._send_status_message('dead')
153
158
154 def on_message(self, msg):
159 def on_message(self, msg):
155 """IOPub messages make no sense"""
160 """IOPub messages make no sense"""
156 pass
161 pass
157
162
158
163
159 class ShellHandler(ZMQChannelHandler):
164 class ShellHandler(ZMQChannelHandler):
160 channel = 'shell'
165 channel = 'shell'
161
166
162
167
163 class StdinHandler(ZMQChannelHandler):
168 class StdinHandler(ZMQChannelHandler):
164 channel = 'stdin'
169 channel = 'stdin'
165
170
166
171
167 #-----------------------------------------------------------------------------
172 #-----------------------------------------------------------------------------
168 # URL to handler mappings
173 # URL to handler mappings
169 #-----------------------------------------------------------------------------
174 #-----------------------------------------------------------------------------
170
175
171
176
172 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
177 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
173 _kernel_action_regex = r"(?P<action>restart|interrupt)"
178 _kernel_action_regex = r"(?P<action>restart|interrupt)"
174
179
175 default_handlers = [
180 default_handlers = [
176 (r"/kernels", MainKernelHandler),
181 (r"/api/kernels", MainKernelHandler),
177 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
182 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
178 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
183 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
179 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
184 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
180 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
185 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
181 (r"/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
186 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
182 ]
187 ]
@@ -1,110 +1,96 b''
1 """A kernel manager relating notebooks and kernels
1 """A kernel manager relating notebooks and kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from tornado import web
19 from tornado import web
20
20
21 from IPython.kernel.multikernelmanager import MultiKernelManager
21 from IPython.kernel.multikernelmanager import MultiKernelManager
22 from IPython.utils.traitlets import (
22 from IPython.utils.traitlets import (
23 Dict, List, Unicode,
23 Dict, List, Unicode,
24 )
24 )
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31 class MappingKernelManager(MultiKernelManager):
31 class MappingKernelManager(MultiKernelManager):
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
33
33
34 def _kernel_manager_class_default(self):
34 def _kernel_manager_class_default(self):
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
36
36
37 kernel_argv = List(Unicode)
37 kernel_argv = List(Unicode)
38
38 kernels = []
39 _notebook_mapping = Dict()
40
39
41 #-------------------------------------------------------------------------
40 #-------------------------------------------------------------------------
42 # Methods for managing kernels and sessions
41 # Methods for managing kernels and sessions
43 #-------------------------------------------------------------------------
42 #-------------------------------------------------------------------------
44
43
45 def kernel_for_notebook(self, notebook_id):
46 """Return the kernel_id for a notebook_id or None."""
47 return self._notebook_mapping.get(notebook_id)
48
49 def set_kernel_for_notebook(self, notebook_id, kernel_id):
50 """Associate a notebook with a kernel."""
51 if notebook_id is not None:
52 self._notebook_mapping[notebook_id] = kernel_id
53
54 def notebook_for_kernel(self, kernel_id):
55 """Return the notebook_id for a kernel_id or None."""
56 for notebook_id, kid in self._notebook_mapping.iteritems():
57 if kernel_id == kid:
58 return notebook_id
59 return None
60
61 def delete_mapping_for_kernel(self, kernel_id):
62 """Remove the kernel/notebook mapping for kernel_id."""
63 notebook_id = self.notebook_for_kernel(kernel_id)
64 if notebook_id is not None:
65 del self._notebook_mapping[notebook_id]
66
67 def _handle_kernel_died(self, kernel_id):
44 def _handle_kernel_died(self, kernel_id):
68 """notice that a kernel died"""
45 """notice that a kernel died"""
69 self.log.warn("Kernel %s died, removing from map.", kernel_id)
46 self.log.warn("Kernel %s died, removing from map.", kernel_id)
70 self.delete_mapping_for_kernel(kernel_id)
71 self.remove_kernel(kernel_id)
47 self.remove_kernel(kernel_id)
72
48
73 def start_kernel(self, notebook_id=None, **kwargs):
49 def start_kernel(self, **kwargs):
74 """Start a kernel for a notebook an return its kernel_id.
50 """Start a kernel for a session an return its kernel_id.
75
51
76 Parameters
52 Parameters
77 ----------
53 ----------
78 notebook_id : uuid
54 session_id : uuid
79 The uuid of the notebook to associate the new kernel with. If this
55 The uuid of the session to associate the new kernel with. If this
80 is not None, this kernel will be persistent whenever the notebook
56 is not None, this kernel will be persistent whenever the session
81 requests a kernel.
57 requests a kernel.
82 """
58 """
83 kernel_id = self.kernel_for_notebook(notebook_id)
59 kernel_id = None
84 if kernel_id is None:
60 if kernel_id is None:
85 kwargs['extra_arguments'] = self.kernel_argv
61 kwargs['extra_arguments'] = self.kernel_argv
86 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
62 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
87 self.set_kernel_for_notebook(notebook_id, kernel_id)
88 self.log.info("Kernel started: %s" % kernel_id)
63 self.log.info("Kernel started: %s" % kernel_id)
89 self.log.debug("Kernel args: %r" % kwargs)
64 self.log.debug("Kernel args: %r" % kwargs)
90 # register callback for failed auto-restart
65 # register callback for failed auto-restart
91 self.add_restart_callback(kernel_id,
66 self.add_restart_callback(kernel_id,
92 lambda : self._handle_kernel_died(kernel_id),
67 lambda : self._handle_kernel_died(kernel_id),
93 'dead',
68 'dead',
94 )
69 )
95 else:
70 else:
96 self.log.info("Using existing kernel: %s" % kernel_id)
71 self.log.info("Using existing kernel: %s" % kernel_id)
97
72
98 return kernel_id
73 return kernel_id
99
74
100 def shutdown_kernel(self, kernel_id, now=False):
75 def shutdown_kernel(self, kernel_id, now=False):
101 """Shutdown a kernel by kernel_id"""
76 """Shutdown a kernel by kernel_id"""
77 i = 0
78 for kernel in self.kernels:
79 if kernel['kernel_id'] == kernel_id:
80 del self.kernels[i]
81 i = i+1
102 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
82 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
103 self.delete_mapping_for_kernel(kernel_id)
83
84 def kernel_model(self, kernel_id, ws_url):
85 model = {"kernel_id":kernel_id, "ws_url": ws_url}
86 self.kernels.append(model)
87 return model
88
89 def list_kernels(self):
90 return self.kernels
104
91
105 # override _check_kernel_id to raise 404 instead of KeyError
92 # override _check_kernel_id to raise 404 instead of KeyError
106 def _check_kernel_id(self, kernel_id):
93 def _check_kernel_id(self, kernel_id):
107 """Check a that a kernel_id exists and raise 404 if not."""
94 """Check a that a kernel_id exists and raise 404 if not."""
108 if kernel_id not in self:
95 if kernel_id not in self:
109 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
96 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
110
General Comments 0
You need to be logged in to leave comments. Login now