Show More
@@ -0,0 +1,90 b'' | |||
|
1 | """Manage IPython.parallel clusters in the notebook. | |
|
2 | ||
|
3 | Authors: | |
|
4 | ||
|
5 | * Brian Granger | |
|
6 | """ | |
|
7 | ||
|
8 | #----------------------------------------------------------------------------- | |
|
9 | # Copyright (C) 2008-2011 The IPython Development Team | |
|
10 | # | |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
|
12 | # the file COPYING, distributed as part of this software. | |
|
13 | #----------------------------------------------------------------------------- | |
|
14 | ||
|
15 | #----------------------------------------------------------------------------- | |
|
16 | # Imports | |
|
17 | #----------------------------------------------------------------------------- | |
|
18 | ||
|
19 | import datetime | |
|
20 | import os | |
|
21 | import uuid | |
|
22 | import glob | |
|
23 | ||
|
24 | from tornado import web | |
|
25 | from zmq.eventloop import ioloop | |
|
26 | ||
|
27 | from IPython.config.configurable import LoggingConfigurable | |
|
28 | from IPython.utils.traitlets import Unicode, List, Dict, Bool | |
|
29 | from IPython.parallel.apps.launcher import IPClusterLauncher | |
|
30 | from IPython.core.profileapp import list_profiles_in, list_bundled_profiles | |
|
31 | from IPython.utils.path import get_ipython_dir, get_ipython_package_dir | |
|
32 | ||
|
33 | #----------------------------------------------------------------------------- | |
|
34 | # Classes | |
|
35 | #----------------------------------------------------------------------------- | |
|
36 | ||
|
37 | class ClusterManager(LoggingConfigurable): | |
|
38 | ||
|
39 | profiles = Dict() | |
|
40 | ||
|
41 | ||
|
42 | def list_profile_names(self): | |
|
43 | """List all profiles in the ipython_dir and cwd. | |
|
44 | """ | |
|
45 | profiles = list_profiles_in(get_ipython_dir()) | |
|
46 | profiles += list_profiles_in(os.getcwdu()) | |
|
47 | return profiles | |
|
48 | ||
|
49 | ||
|
50 | def list_profiles(self): | |
|
51 | profiles = self.list_profile_names() | |
|
52 | result = [self.profile_info(p) for p in profiles] | |
|
53 | return result | |
|
54 | ||
|
55 | ||
|
56 | def profile_info(self, profile): | |
|
57 | if profile not in self.list_profile_names(): | |
|
58 | raise web.HTTPError(404, u'profile not found') | |
|
59 | result = dict(profile=profile) | |
|
60 | data = self.profiles.get(profile) | |
|
61 | if data is None: | |
|
62 | result['status'] = 'stopped' | |
|
63 | else: | |
|
64 | result['status'] = 'running' | |
|
65 | result['n'] = data['n'] | |
|
66 | return result | |
|
67 | ||
|
68 | def start_cluster(self, profile, n=4): | |
|
69 | """Start a cluster for a given profile.""" | |
|
70 | if profile not in self.list_profile_names(): | |
|
71 | raise web.HTTPError(404, u'profile not found') | |
|
72 | if profile in self.profiles: | |
|
73 | raise web.HTTPError(409, u'cluster already running') | |
|
74 | launcher = IPClusterLauncher(ipcluster_profile=profile, ipcluster_n=n) | |
|
75 | launcher.start() | |
|
76 | self.profiles[profile] = { | |
|
77 | 'launcher': launcher, | |
|
78 | 'n': n | |
|
79 | } | |
|
80 | ||
|
81 | def stop_cluster(self, profile): | |
|
82 | """Stop a cluster for a given profile.""" | |
|
83 | if profile not in self.profiles: | |
|
84 | raise web.HTTPError(409, u'cluster not running') | |
|
85 | launcher = self.profiles.pop(profile)['launcher'] | |
|
86 | launcher.stop() | |
|
87 | ||
|
88 | def stop_all_clusters(self): | |
|
89 | for p in self.profiles.values(): | |
|
90 | p['launcher'].stop() |
@@ -92,6 +92,29 b' ipython profile list -h # show the help string for the list subcommand' | |||
|
92 | 92 | #----------------------------------------------------------------------------- |
|
93 | 93 | |
|
94 | 94 | |
|
95 | def list_profiles_in(path): | |
|
96 | """list profiles in a given root directory""" | |
|
97 | files = os.listdir(path) | |
|
98 | profiles = [] | |
|
99 | for f in files: | |
|
100 | full_path = os.path.join(path, f) | |
|
101 | if os.path.isdir(full_path) and f.startswith('profile_'): | |
|
102 | profiles.append(f.split('_',1)[-1]) | |
|
103 | return profiles | |
|
104 | ||
|
105 | ||
|
106 | def list_bundled_profiles(): | |
|
107 | """list profiles that are bundled with IPython.""" | |
|
108 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') | |
|
109 | files = os.listdir(path) | |
|
110 | profiles = [] | |
|
111 | for profile in files: | |
|
112 | full_path = os.path.join(path, profile) | |
|
113 | if os.path.isdir(full_path): | |
|
114 | profiles.append(profile) | |
|
115 | return profiles | |
|
116 | ||
|
117 | ||
|
95 | 118 | class ProfileList(Application): |
|
96 | 119 | name = u'ipython-profile' |
|
97 | 120 | description = list_help |
@@ -115,35 +138,15 b' class ProfileList(Application):' | |||
|
115 | 138 | the environment variable IPYTHON_DIR. |
|
116 | 139 | """ |
|
117 | 140 | ) |
|
118 | ||
|
119 | def _list_profiles_in(self, path): | |
|
120 | """list profiles in a given root directory""" | |
|
121 | files = os.listdir(path) | |
|
122 | profiles = [] | |
|
123 | for f in files: | |
|
124 | full_path = os.path.join(path, f) | |
|
125 | if os.path.isdir(full_path) and f.startswith('profile_'): | |
|
126 | profiles.append(f.split('_',1)[-1]) | |
|
127 | return profiles | |
|
128 | ||
|
129 | def _list_bundled_profiles(self): | |
|
130 | """list profiles in a given root directory""" | |
|
131 | path = os.path.join(get_ipython_package_dir(), u'config', u'profile') | |
|
132 | files = os.listdir(path) | |
|
133 | profiles = [] | |
|
134 | for profile in files: | |
|
135 | full_path = os.path.join(path, profile) | |
|
136 | if os.path.isdir(full_path): | |
|
137 | profiles.append(profile) | |
|
138 | return profiles | |
|
139 | ||
|
141 | ||
|
142 | ||
|
140 | 143 | def _print_profiles(self, profiles): |
|
141 | 144 | """print list of profiles, indented.""" |
|
142 | 145 | for profile in profiles: |
|
143 | 146 | print ' %s' % profile |
|
144 | ||
|
147 | ||
|
145 | 148 | def list_profile_dirs(self): |
|
146 |
profiles = |
|
|
149 | profiles = list_bundled_profiles() | |
|
147 | 150 | if profiles: |
|
148 | 151 | |
|
149 | 152 | print "Available profiles in IPython:" |
@@ -153,13 +156,13 b' class ProfileList(Application):' | |||
|
153 | 156 | print " into your IPython directory (%s)," % self.ipython_dir |
|
154 | 157 | print " where you can customize it." |
|
155 | 158 | |
|
156 |
profiles = |
|
|
159 | profiles = list_profiles_in(self.ipython_dir) | |
|
157 | 160 | if profiles: |
|
158 | 161 | |
|
159 | 162 | print "Available profiles in %s:" % self.ipython_dir |
|
160 | 163 | self._print_profiles(profiles) |
|
161 | 164 | |
|
162 |
profiles = |
|
|
165 | profiles = list_profiles_in(os.getcwdu()) | |
|
163 | 166 | if profiles: |
|
164 | 167 | |
|
165 | 168 | print "Available profiles in current directory (%s):" % os.getcwdu() |
@@ -587,7 +587,6 b' class NotebookRootHandler(AuthenticatedHandler):' | |||
|
587 | 587 | |
|
588 | 588 | @authenticate_unless_readonly |
|
589 | 589 | def get(self): |
|
590 | ||
|
591 | 590 | nbm = self.application.notebook_manager |
|
592 | 591 | files = nbm.list_notebooks() |
|
593 | 592 | self.finish(jsonapi.dumps(files)) |
@@ -661,6 +660,41 b' class NotebookCopyHandler(AuthenticatedHandler):' | |||
|
661 | 660 | mathjax_url=self.application.ipython_app.mathjax_url, |
|
662 | 661 | ) |
|
663 | 662 | |
|
663 | ||
|
664 | #----------------------------------------------------------------------------- | |
|
665 | # Cluster handlers | |
|
666 | #----------------------------------------------------------------------------- | |
|
667 | ||
|
668 | ||
|
669 | class MainClusterHandler(AuthenticatedHandler): | |
|
670 | ||
|
671 | @web.authenticated | |
|
672 | def get(self): | |
|
673 | cm = self.application.cluster_manager | |
|
674 | self.finish(jsonapi.dumps(cm.list_profiles())) | |
|
675 | ||
|
676 | ||
|
677 | class ClusterProfileHandler(AuthenticatedHandler): | |
|
678 | ||
|
679 | @web.authenticated | |
|
680 | def get(self, profile): | |
|
681 | cm = self.application.cluster_manager | |
|
682 | self.finish(jsonapi.dumps(cm.profile_info(profile))) | |
|
683 | ||
|
684 | ||
|
685 | class ClusterActionHandler(AuthenticatedHandler): | |
|
686 | ||
|
687 | @web.authenticated | |
|
688 | def post(self, profile, action): | |
|
689 | cm = self.application.cluster_manager | |
|
690 | if action == 'start': | |
|
691 | n = int(self.get_argument('n', default=4)) | |
|
692 | cm.start_cluster(profile, n) | |
|
693 | if action == 'stop': | |
|
694 | cm.stop_cluster(profile) | |
|
695 | self.finish() | |
|
696 | ||
|
697 | ||
|
664 | 698 | #----------------------------------------------------------------------------- |
|
665 | 699 | # RST web service handlers |
|
666 | 700 | #----------------------------------------------------------------------------- |
@@ -49,9 +49,11 b' from .handlers import (LoginHandler, LogoutHandler,' | |||
|
49 | 49 | ProjectDashboardHandler, NewHandler, NamedNotebookHandler, |
|
50 | 50 | MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler, |
|
51 | 51 | ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler, |
|
52 | RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler | |
|
52 | RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler, | |
|
53 | MainClusterHandler, ClusterProfileHandler, ClusterActionHandler | |
|
53 | 54 | ) |
|
54 | 55 | from .notebookmanager import NotebookManager |
|
56 | from .clustermanager import ClusterManager | |
|
55 | 57 | |
|
56 | 58 | from IPython.config.application import catch_config_error, boolean_flag |
|
57 | 59 | from IPython.core.application import BaseIPythonApplication |
@@ -74,6 +76,9 b' from IPython.utils import py3compat' | |||
|
74 | 76 | _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)" |
|
75 | 77 | _kernel_action_regex = r"(?P<action>restart|interrupt)" |
|
76 | 78 | _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)" |
|
79 | _profile_regex = r"(?P<profile>[a-zA-Z0-9]+)" | |
|
80 | _cluster_action_regex = r"(?P<action>start|stop)" | |
|
81 | ||
|
77 | 82 | |
|
78 | 83 | LOCALHOST = '127.0.0.1' |
|
79 | 84 | |
@@ -101,7 +106,8 b' def url_path_join(a,b):' | |||
|
101 | 106 | |
|
102 | 107 | class NotebookWebApplication(web.Application): |
|
103 | 108 | |
|
104 |
def __init__(self, ipython_app, kernel_manager, notebook_manager, |
|
|
109 | def __init__(self, ipython_app, kernel_manager, notebook_manager, | |
|
110 | cluster_manager, log, | |
|
105 | 111 | base_project_url, settings_overrides): |
|
106 | 112 | handlers = [ |
|
107 | 113 | (r"/", ProjectDashboardHandler), |
@@ -120,6 +126,9 b' class NotebookWebApplication(web.Application):' | |||
|
120 | 126 | (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler), |
|
121 | 127 | (r"/rstservice/render", RSTHandler), |
|
122 | 128 | (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}), |
|
129 | (r"/clusters", MainClusterHandler), | |
|
130 | (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler), | |
|
131 | (r"/clusters/%s" % _profile_regex, ClusterProfileHandler), | |
|
123 | 132 | ] |
|
124 | 133 | settings = dict( |
|
125 | 134 | template_path=os.path.join(os.path.dirname(__file__), "templates"), |
@@ -151,10 +160,11 b' class NotebookWebApplication(web.Application):' | |||
|
151 | 160 | super(NotebookWebApplication, self).__init__(new_handlers, **settings) |
|
152 | 161 | |
|
153 | 162 | self.kernel_manager = kernel_manager |
|
154 | self.log = log | |
|
155 | 163 | self.notebook_manager = notebook_manager |
|
164 | self.cluster_manager = cluster_manager | |
|
156 | 165 | self.ipython_app = ipython_app |
|
157 | 166 | self.read_only = self.ipython_app.read_only |
|
167 | self.log = log | |
|
158 | 168 | |
|
159 | 169 | |
|
160 | 170 | #----------------------------------------------------------------------------- |
@@ -395,6 +405,7 b' class NotebookApp(BaseIPythonApplication):' | |||
|
395 | 405 | ) |
|
396 | 406 | self.notebook_manager = NotebookManager(config=self.config, log=self.log) |
|
397 | 407 | self.notebook_manager.list_notebooks() |
|
408 | self.cluster_manager = ClusterManager(config=self.config, log=self.log) | |
|
398 | 409 | |
|
399 | 410 | def init_logging(self): |
|
400 | 411 | super(NotebookApp, self).init_logging() |
@@ -406,7 +417,8 b' class NotebookApp(BaseIPythonApplication):' | |||
|
406 | 417 | def init_webapp(self): |
|
407 | 418 | """initialize tornado webapp and httpserver""" |
|
408 | 419 | self.web_app = NotebookWebApplication( |
|
409 |
self, self.kernel_manager, self.notebook_manager, |
|
|
420 | self, self.kernel_manager, self.notebook_manager, | |
|
421 | self.cluster_manager, self.log, | |
|
410 | 422 | self.base_project_url, self.webapp_settings |
|
411 | 423 | ) |
|
412 | 424 | if self.certfile: |
@@ -1164,14 +1164,16 b' class IPClusterLauncher(LocalProcessLauncher):' | |||
|
1164 | 1164 | ipcluster_cmd = List(ipcluster_cmd_argv, config=True, |
|
1165 | 1165 | help="Popen command for ipcluster") |
|
1166 | 1166 | ipcluster_args = List( |
|
1167 | ['--clean-logs', '--log-to-file', '--log-level=%i'%logging.INFO], config=True, | |
|
1167 | ['--clean-logs=True', '--log-to-file', '--log-level=%i'%logging.INFO], config=True, | |
|
1168 | 1168 | help="Command line arguments to pass to ipcluster.") |
|
1169 | 1169 | ipcluster_subcommand = Unicode('start') |
|
1170 | ipcluster_profile = Unicode('default') | |
|
1170 | 1171 | ipcluster_n = Integer(2) |
|
1171 | 1172 | |
|
1172 | 1173 | def find_args(self): |
|
1173 | 1174 | return self.ipcluster_cmd + [self.ipcluster_subcommand] + \ |
|
1174 |
['--n=%i'%self.ipcluster_n] + |
|
|
1175 | ['--n=%i'%self.ipcluster_n, '--profile=%s'%self.ipcluster_profile] + \ | |
|
1176 | self.ipcluster_args | |
|
1175 | 1177 | |
|
1176 | 1178 | def start(self): |
|
1177 | 1179 | return super(IPClusterLauncher, self).start() |
General Comments 0
You need to be logged in to leave comments.
Login now