Skip to content

Commit 300c50b

Browse files
committed
[wip] use new ClusterManager in nbextension
allows listing/starting/stopping clusters not started by the notebook server
1 parent 242903a commit 300c50b

File tree

5 files changed

+117
-249
lines changed

5 files changed

+117
-249
lines changed

ipyparallel/cluster/cluster.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,7 @@ def _cls_str(cls):
326326
"class": _cls_str(self.controller_launcher_class),
327327
"state": None,
328328
}
329-
if self.controller:
330-
d["controller"]["state"] = self.controller.to_dict()
329+
d["controller"]["state"] = self.controller.to_dict()
331330

332331
d["engines"] = {
333332
"class": _cls_str(self.engine_launcher_class),
@@ -791,15 +790,16 @@ def list_clusters(self):
791790
# TODO: what should we return?
792791
# just cluster ids or the full dict?
793792
# just cluster ids for now
794-
return sorted(self._clusters)
793+
return self._clusters.items()
795794

796795
def new_cluster(self, **kwargs):
797796
"""Create a new cluster"""
798797
cluster = Cluster(parent=self, **kwargs)
799-
if cluster.cluster_id in self._clusters:
800-
raise KeyError(f"Cluster {cluster.cluster_id} already exists!")
801-
self._clusters[cluster.cluster_id] = cluster
802-
return cluster
798+
cluster_key = self._cluster_key(cluster)
799+
if cluster_key in self._clusters:
800+
raise KeyError(f"Cluster {cluster_key} already exists!")
801+
self._clusters[cluster_key] = cluster
802+
return cluster_key, cluster
803803

804804
def get_cluster(self, cluster_id):
805805
"""Get a Cluster object by id"""

ipyparallel/nbextension/clustermanager.py

Lines changed: 0 additions & 185 deletions
This file was deleted.

ipyparallel/nbextension/handlers.py

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,91 +5,116 @@
55
import os
66
import sys
77

8-
from notebook.base.handlers import IPythonHandler
9-
from notebook.nbextensions import install_nbextension
8+
from notebook.base.handlers import APIHandler
109
from notebook.utils import url_path_join as ujoin
1110
from tornado import web
1211

13-
from .clustermanager import ClusterManager
12+
from ..cluster import ClusterManager
13+
from ..util import abbreviate_profile_dir
1414

1515

1616
static = os.path.join(os.path.dirname(__file__), 'static')
1717

1818

19-
class ClusterHandler(IPythonHandler):
19+
class ClusterHandler(APIHandler):
2020
@property
2121
def cluster_manager(self):
2222
return self.settings['cluster_manager']
2323

24+
def cluster_model(self, cluster):
25+
"""Create a JSONable cluster model"""
26+
d = cluster.to_dict()
27+
profile_dir = d['cluster']['profile_dir']
28+
# provide abbreviated profile info
29+
d['cluster']['profile'] = abbreviate_profile_dir(profile_dir)
30+
return d
31+
32+
33+
class ClusterListHandler(ClusterHandler):
34+
"""List and create new clusters
35+
36+
GET /clusters : list current clusters
37+
POST / clusters (JSON body) : create a new cluster"""
2438

25-
class MainClusterHandler(ClusterHandler):
2639
@web.authenticated
2740
def get(self):
28-
self.finish(json.dumps(self.cluster_manager.list_profiles()))
29-
41+
# currently reloads everything from disk. Is that what we want?
42+
clusters = self.cluster_manager.load_clusters()
43+
self.finish(
44+
{key: self.cluster_model(cluster) for key, cluster in clusters.items()}
45+
)
3046

31-
class ClusterProfileHandler(ClusterHandler):
3247
@web.authenticated
33-
def get(self, profile):
34-
self.finish(json.dumps(self.cluster_manager.profile_info(profile)))
48+
def post(self):
49+
body = self.get_json_body() or {}
50+
# profile
51+
# cluster_id
52+
cluster_id, cluster = self.cluster_manager.new_cluster(**body)
53+
self.write(json.dumps({}))
3554

3655

3756
class ClusterActionHandler(ClusterHandler):
57+
"""Actions on a single cluster
58+
59+
GET: read single cluster model
60+
POST: start
61+
PATCH: engines?
62+
DELETE: stop
63+
"""
64+
65+
def get_cluster(self, cluster_key):
66+
try:
67+
return self.cluster_manager.get_cluster(cluster_key)
68+
except KeyError:
69+
raise web.HTTPError(404, f"No such cluster: {cluster_key}")
70+
71+
@web.authenticated
72+
async def post(self, cluster_key):
73+
cluster = self.get_cluster(cluster_key)
74+
n = self.get_argument('n', default=None)
75+
await cluster.start_cluster(n=n)
76+
self.write(json.dumps(self.cluster_model(cluster)))
77+
3878
@web.authenticated
39-
def post(self, profile, action):
40-
cm = self.cluster_manager
41-
if action == 'start':
42-
n = self.get_argument('n', default=None)
43-
if not n:
44-
data = cm.start_cluster(profile)
45-
else:
46-
data = cm.start_cluster(profile, int(n))
47-
if action == 'stop':
48-
data = cm.stop_cluster(profile)
49-
self.finish(json.dumps(data))
79+
async def get(self, cluster_key):
80+
cluster = self.get_cluster(cluster_key)
81+
self.write(json.dumps(self.cluster_model(cluster)))
82+
83+
@web.authenticated
84+
async def delete(self, cluster_key):
85+
cluster = self.get_cluster(cluster_key)
86+
await cluster.stop_cluster()
87+
self.cluster_manager.remove_cluster(cluster_key)
88+
self.write(json.dumps(self.cluster_model(cluster)))
5089

5190

5291
# -----------------------------------------------------------------------------
5392
# URL to handler mappings
5493
# -----------------------------------------------------------------------------
5594

5695

57-
_cluster_action_regex = r"(?P<action>start|stop)"
58-
_profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
96+
_cluster_action_regex = r"(?P<action>start|stop|create)"
97+
_cluster_key_regex = (
98+
r"(?P<cluster_key>[^\/]+)" # there is almost no text that is invalid
99+
)
59100

60101
default_handlers = [
61-
(r"/clusters", MainClusterHandler),
102+
(r"/clusters", ClusterListHandler),
62103
(
63-
r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex),
104+
rf"/clusters/{_cluster_key_regex}",
64105
ClusterActionHandler,
65106
),
66-
(r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
67107
]
68108

69109

70110
def load_jupyter_server_extension(nbapp):
71111
"""Load the nbserver extension"""
72-
from distutils.version import LooseVersion as V
73112
import notebook
74113

75114
nbapp.log.info("Loading IPython parallel extension")
76115
webapp = nbapp.web_app
77116
webapp.settings['cluster_manager'] = ClusterManager(parent=nbapp)
78117

79-
if V(notebook.__version__) < V('4.2'):
80-
windows = sys.platform.startswith('win')
81-
install_nbextension(
82-
static, destination='ipyparallel', symlink=not windows, user=True
83-
)
84-
cfgm = nbapp.config_manager
85-
cfgm.update(
86-
'tree',
87-
{
88-
'load_extensions': {
89-
'ipyparallel/main': True,
90-
}
91-
},
92-
)
93118
base_url = webapp.settings['base_url']
94119
webapp.add_handlers(
95120
".*$", [(ujoin(base_url, pat), handler) for pat, handler in default_handlers]

0 commit comments

Comments
 (0)