Skip to content

Commit 130328b

Browse files
Add head comments to functions and add num_workers to data frame
1 parent b4a1583 commit 130328b

File tree

6 files changed

+57
-26
lines changed

6 files changed

+57
-26
lines changed

demo-notebooks/guided-demos/3_widget_example.ipynb

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
"name": "python",
117117
"nbconvert_exporter": "python",
118118
"pygments_lexer": "ipython3",
119-
"version": "3.9.19"
119+
"version": "3.9.18"
120120
},
121121
"vscode": {
122122
"interpreter": {

src/codeflare_sdk/cluster/cluster.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ def _map_to_ray_cluster(rc) -> Optional[RayCluster]:
863863
name=rc["metadata"]["name"],
864864
status=status,
865865
# for now we are not using autoscaling so same replicas is fine
866-
workers=rc["spec"]["workerGroupSpecs"][0]["replicas"],
866+
num_workers=rc["spec"]["workerGroupSpecs"][0]["replicas"],
867867
worker_mem_limits=rc["spec"]["workerGroupSpecs"][0]["template"]["spec"][
868868
"containers"
869869
][0]["resources"]["limits"]["memory"],
@@ -909,7 +909,7 @@ def _copy_to_ray(cluster: Cluster) -> RayCluster:
909909
ray = RayCluster(
910910
name=cluster.config.name,
911911
status=cluster.status(print_to_console=False)[0],
912-
workers=cluster.config.num_workers,
912+
num_workers=cluster.config.num_workers,
913913
worker_mem_requests=cluster.config.worker_memory_requests,
914914
worker_mem_limits=cluster.config.worker_memory_limits,
915915
worker_cpu_requests=cluster.config.worker_cpu_requests,

src/codeflare_sdk/cluster/model.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class RayCluster:
7878
head_cpu_limits: int
7979
head_mem_requests: str
8080
head_mem_limits: str
81-
workers: int
81+
num_workers: int
8282
worker_mem_requests: str
8383
worker_mem_limits: str
8484
worker_cpu_requests: Union[int, str]

src/codeflare_sdk/cluster/widgets.py

+49-18
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import contextlib
1919
import io
2020
import os
21-
from time import sleep
21+
import warnings
2222
import time
2323
import codeflare_sdk
2424
from kubernetes import client
@@ -103,7 +103,13 @@ def is_notebook() -> bool:
103103

104104

105105
def view_clusters(namespace: str = None):
106-
"""view_clusters function will display existing clusters with their specs, and handle user interactions."""
106+
"""
107+
view_clusters function will display existing clusters with their specs, and handle user interactions.
108+
"""
109+
if not is_notebook():
110+
warnings.warn("view_clusters can only be used in a Jupyter Notebook environment.")
111+
return # Exit function if not in Jupyter Notebook
112+
107113
from .cluster import get_current_namespace
108114
if not namespace:
109115
namespace = get_current_namespace()
@@ -112,13 +118,13 @@ def view_clusters(namespace: str = None):
112118
raycluster_data_output = widgets.Output()
113119
url_output = widgets.Output()
114120

115-
df = _fetch_cluster_data(namespace)
116-
if df.empty:
121+
ray_clusters_df = _fetch_cluster_data(namespace)
122+
if ray_clusters_df.empty:
117123
print(f"No clusters found in the {namespace} namespace.")
118124
return
119125

120126
classification_widget = widgets.ToggleButtons(
121-
options=df["Name"].tolist(), value=df["Name"].tolist()[0],
127+
options=ray_clusters_df["Name"].tolist(), value=ray_clusters_df["Name"].tolist()[0],
122128
description='Select an existing cluster:',
123129
)
124130
# Setting the initial value to trigger the event handler to display the cluster details.
@@ -132,39 +138,45 @@ def view_clusters(namespace: str = None):
132138
icon='trash',
133139
tooltip="Delete the selected cluster"
134140
)
135-
delete_button.on_click(lambda b: _on_delete_button_click(b, classification_widget, df, raycluster_data_output, user_output, delete_button, list_jobs_button, ray_dashboard_button))
141+
delete_button.on_click(lambda b: _on_delete_button_click(b, classification_widget, ray_clusters_df, raycluster_data_output, user_output, delete_button, list_jobs_button, ray_dashboard_button))
136142

137143
list_jobs_button = widgets.Button(
138144
description='View Jobs',
139145
icon='suitcase',
140146
tooltip="Open the Ray Job Dashboard"
141147
)
142-
list_jobs_button.on_click(lambda b: _on_list_jobs_button_click(b, classification_widget, df, user_output, url_output))
148+
list_jobs_button.on_click(lambda b: _on_list_jobs_button_click(b, classification_widget, ray_clusters_df, user_output, url_output))
143149

144150
ray_dashboard_button = widgets.Button(
145151
description='Open Ray Dashboard',
146152
icon='dashboard',
147153
tooltip="Open the Ray Dashboard in a new tab",
148154
layout=widgets.Layout(width='auto'),
149155
)
150-
ray_dashboard_button.on_click(lambda b: _on_ray_dashboard_button_click(b, classification_widget, df, user_output, url_output))
156+
ray_dashboard_button.on_click(lambda b: _on_ray_dashboard_button_click(b, classification_widget, ray_clusters_df, user_output, url_output))
151157

152158
display(widgets.VBox([classification_widget, raycluster_data_output]))
153159
display(widgets.HBox([delete_button, list_jobs_button, ray_dashboard_button]), url_output, user_output)
154160

155-
# Handles the event when a cluster is selected from the toggle buttons, updating the output with cluster details.
161+
156162
def _on_cluster_click(selection_change, raycluster_data_output: widgets.Output, namespace: str, classification_widget: widgets.ToggleButtons):
163+
"""
164+
_on_cluster_click handles the event when a cluster is selected from the toggle buttons, updating the output with cluster details.
165+
"""
157166
new_value = selection_change["new"]
158167
raycluster_data_output.clear_output()
159-
df = _fetch_cluster_data(namespace)
160-
classification_widget.options = df["Name"].tolist()
168+
ray_clusters_df = _fetch_cluster_data(namespace)
169+
classification_widget.options = ray_clusters_df["Name"].tolist()
161170
with raycluster_data_output:
162-
display(HTML(df[df["Name"]==new_value][["Name", "Namespace", "Head GPUs", "Head CPU Req~Lim", "Head Memory Req~Lim", "Worker GPUs", "Worker CPU Req~Lim", "Worker Memory Req~Lim", "status"]].to_html(escape=False, index=False, border=2)))
171+
display(HTML(ray_clusters_df[ray_clusters_df["Name"]==new_value][["Name", "Namespace", "Num Workers", "Head GPUs", "Head CPU Req~Lim", "Head Memory Req~Lim", "Worker GPUs", "Worker CPU Req~Lim", "Worker Memory Req~Lim", "status"]].to_html(escape=False, index=False, border=2)))
163172

164173

165-
def _on_delete_button_click(b, classification_widget: widgets.ToggleButtons, df: pd.DataFrame, raycluster_data_output: widgets.Output, user_output: widgets.Output, delete_button: widgets.Button, list_jobs_button: widgets.Button, ray_dashboard_button: widgets.Button):
174+
def _on_delete_button_click(b, classification_widget: widgets.ToggleButtons, ray_clusters_df: pd.DataFrame, raycluster_data_output: widgets.Output, user_output: widgets.Output, delete_button: widgets.Button, list_jobs_button: widgets.Button, ray_dashboard_button: widgets.Button):
175+
"""
176+
_on_delete_button_click handles the event when the Delete Button is clicked, deleting the selected cluster.
177+
"""
166178
cluster_name = classification_widget.value
167-
namespace = df[df["Name"]==classification_widget.value]["Namespace"].values[0]
179+
namespace = ray_clusters_df[ray_clusters_df["Name"]==classification_widget.value]["Namespace"].values[0]
168180

169181
_delete_cluster(cluster_name, namespace)
170182

@@ -185,10 +197,13 @@ def _on_delete_button_click(b, classification_widget: widgets.ToggleButtons, df:
185197
else:
186198
classification_widget.options = new_df["Name"].tolist()
187199

188-
def _on_ray_dashboard_button_click(b, classification_widget: widgets.ToggleButtons, df: pd.DataFrame, user_output: widgets.Output, url_output: widgets.Output):
200+
def _on_ray_dashboard_button_click(b, classification_widget: widgets.ToggleButtons, ray_clusters_df: pd.DataFrame, user_output: widgets.Output, url_output: widgets.Output):
201+
"""
202+
_on_ray_dashboard_button_click handles the event when the Open Ray Dashboard button is clicked, opening the Ray Dashboard in a new tab
203+
"""
189204
from codeflare_sdk.cluster import Cluster
190205
cluster_name = classification_widget.value
191-
namespace = df[df["Name"]==classification_widget.value]["Namespace"].values[0]
206+
namespace = ray_clusters_df[ray_clusters_df["Name"]==classification_widget.value]["Namespace"].values[0]
192207

193208
# Suppress from Cluster Object initialisation widgets and outputs
194209
with widgets.Output(), contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
@@ -201,10 +216,13 @@ def _on_ray_dashboard_button_click(b, classification_widget: widgets.ToggleButto
201216
with url_output:
202217
display(Javascript(f'window.open("{dashboard_url}", "_blank");'))
203218

204-
def _on_list_jobs_button_click(b, classification_widget: widgets.ToggleButtons, df: pd.DataFrame, user_output: widgets.Output, url_output: widgets.Output):
219+
def _on_list_jobs_button_click(b, classification_widget: widgets.ToggleButtons, ray_clusters_df: pd.DataFrame, user_output: widgets.Output, url_output: widgets.Output):
220+
"""
221+
_on_list_jobs_button_click handles the event when the View Jobs button is clicked, opening the Ray Jobs Dashboard in a new tab
222+
"""
205223
from codeflare_sdk.cluster import Cluster
206224
cluster_name = classification_widget.value
207-
namespace = df[df["Name"]==classification_widget.value]["Namespace"].values[0]
225+
namespace = ray_clusters_df[ray_clusters_df["Name"]==classification_widget.value]["Namespace"].values[0]
208226

209227
# Suppress from Cluster Object initialisation widgets and outputs
210228
with widgets.Output(), contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
@@ -217,12 +235,17 @@ def _on_list_jobs_button_click(b, classification_widget: widgets.ToggleButtons,
217235
with url_output:
218236
display(Javascript(f'window.open("{dashboard_url}/#/jobs", "_blank");'))
219237

238+
220239
def _delete_cluster(
221240
cluster_name: str,
222241
namespace: str,
223242
timeout: int = 5,
224243
interval: int = 1,
225244
):
245+
"""
246+
_delete_cluster function deletes the cluster with the given name and namespace.
247+
It optionally waits for the cluster to be deleted.
248+
"""
226249
from .cluster import _check_aw_exists
227250

228251
try:
@@ -276,12 +299,16 @@ def _delete_cluster(
276299

277300

278301
def _fetch_cluster_data(namespace):
302+
"""
303+
_fetch_cluster_data function fetches all clusters and their spec in a given namespace and returns a DataFrame.
304+
"""
279305
from .cluster import list_all_clusters
280306
rayclusters = list_all_clusters(namespace, False)
281307
if not rayclusters:
282308
return pd.DataFrame()
283309
names = [item.name for item in rayclusters]
284310
namespaces = [item.namespace for item in rayclusters]
311+
num_workers = [item.num_workers for item in rayclusters]
285312
head_extended_resources = [
286313
f"{list(item.head_extended_resources.keys())[0]}: {list(item.head_extended_resources.values())[0]}"
287314
if item.head_extended_resources else "0"
@@ -311,6 +338,7 @@ def _fetch_cluster_data(namespace):
311338
data = {
312339
"Name": names,
313340
"Namespace": namespaces,
341+
"Num Workers": num_workers,
314342
"Head GPUs": head_extended_resources,
315343
"Worker GPUs": worker_extended_resources,
316344
"Head CPU Req~Lim": head_cpu_rl,
@@ -323,6 +351,9 @@ def _fetch_cluster_data(namespace):
323351

324352

325353
def _format_status(status):
354+
"""
355+
_format_status function formats the status enum.
356+
"""
326357
status_map = {
327358
RayClusterStatus.READY: '<span style="color: green;">Ready ✓</span>',
328359
RayClusterStatus.SUSPENDED: '<span style="color: #007BFF;">Suspended ❄️</span>',

src/codeflare_sdk/utils/pretty_print.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def print_clusters(clusters: List[RayCluster]):
135135
)
136136
name = cluster.name
137137
dashboard = cluster.dashboard
138-
workers = str(cluster.workers)
138+
workers = str(cluster.num_workers)
139139
memory = f"{cluster.worker_mem_requests}~{cluster.worker_mem_limits}"
140140
cpu = f"{cluster.worker_cpu_requests}~{cluster.worker_cpu_limits}"
141141
gpu = str(cluster.worker_extended_resources.get("nvidia.com/gpu", 0))

tests/unit_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ def test_ray_details(mocker, capsys):
940940
ray1 = RayCluster(
941941
name="raytest1",
942942
status=RayClusterStatus.READY,
943-
workers=1,
943+
num_workers=1,
944944
worker_mem_requests="2G",
945945
worker_mem_limits="2G",
946946
worker_cpu_requests=1,
@@ -979,7 +979,7 @@ def test_ray_details(mocker, capsys):
979979
assert details == ray2
980980
assert ray2.name == "raytest2"
981981
assert ray1.namespace == ray2.namespace
982-
assert ray1.workers == ray2.workers
982+
assert ray1.num_workers == ray2.num_workers
983983
assert ray1.worker_mem_requests == ray2.worker_mem_requests
984984
assert ray1.worker_mem_limits == ray2.worker_mem_limits
985985
assert ray1.worker_cpu_requests == ray2.worker_cpu_requests
@@ -2358,7 +2358,7 @@ def test_cluster_status(mocker):
23582358
fake_ray = RayCluster(
23592359
name="test",
23602360
status=RayClusterStatus.UNKNOWN,
2361-
workers=1,
2361+
num_workers=1,
23622362
worker_mem_requests=2,
23632363
worker_mem_limits=2,
23642364
worker_cpu_requests=1,

0 commit comments

Comments
 (0)