Skip to content

Commit 6e45753

Browse files
committed
pretty print list of clusters
1 parent a713b2d commit 6e45753

File tree

6 files changed

+155
-7
lines changed

6 files changed

+155
-7
lines changed

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
openshift-client==1.0.18
2+
rich==12.5.1
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
README.md
12
pyproject.toml
23
src/codeflare_sdk/__init__.py
34
src/codeflare_sdk.egg-info/PKG-INFO
@@ -6,4 +7,6 @@ src/codeflare_sdk.egg-info/dependency_links.txt
67
src/codeflare_sdk.egg-info/top_level.txt
78
src/codeflare_sdk/cluster/__init__.py
89
src/codeflare_sdk/cluster/cluster.py
9-
src/codeflare_sdk/cluster/config.py
10+
src/codeflare_sdk/cluster/config.py
11+
src/codeflare_sdk/utils/__init__.py
12+
src/codeflare_sdk/utils/pretty_print.py

src/codeflare_sdk/cluster/cluster.py

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,50 @@
11
from .config import ClusterConfiguration
2-
def up(config: ClusterConfiguration):
3-
pass
2+
from ..utils.pretty_print import RayCluster
3+
from ..utils import pretty_print
4+
import openshift as oc
5+
from typing import List
46

5-
def down(name):
6-
pass
7+
class Cluster:
8+
def __init__(self, config: ClusterConfiguration):
9+
pass
710

8-
def status(name):
9-
pass
11+
def up(self):
12+
pass
13+
14+
def down(self, name):
15+
pass
16+
17+
def status(self, name):
18+
pass
19+
20+
21+
def list_all_clusters(print_to_console=True):
22+
clusters = _get_ray_clusters()
23+
if print_to_console:
24+
pretty_print.print_clusters(clusters)
25+
return clusters
26+
27+
28+
# private methods
29+
30+
def _get_appwrappers(namespace='default'):
31+
with oc.project(namespace), oc.timeout(10*60):
32+
app_wrappers = oc.selector('appwrappers').qnames()
33+
return app_wrappers
34+
35+
36+
def _get_ray_clusters(namespace='default') -> List[RayCluster]:
37+
list_of_clusters = []
38+
with oc.project(namespace), oc.timeout(10*60):
39+
ray_clusters = oc.selector('rayclusters').objects()
40+
for cluster in ray_clusters:
41+
cluster_model = cluster.model
42+
list_of_clusters.append(RayCluster(
43+
name=cluster.name(), status=cluster_model.status.state,
44+
min_workers=cluster_model.spec.workerGroupSpecs[0].replicas,
45+
max_workers=cluster_model.spec.workerGroupSpecs[0].replicas,
46+
worker_mem_max=cluster_model.spec.workerGroupSpecs[0].template.spec.containers[0].resources.limits.memory,
47+
worker_mem_min=cluster_model.spec.workerGroupSpecs[0].template.spec.containers[0].resources.requests.memory,
48+
worker_cpu=cluster_model.spec.workerGroupSpecs[0].template.spec.containers[0].resources.limits.cpu,
49+
worker_gpu=0))
50+
return list_of_clusters

src/codeflare_sdk/utils/__init__.py

Whitespace-only changes.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from ast import Str
2+
from turtle import st
3+
from rich import print
4+
from rich.table import Table
5+
from rich.console import Console
6+
from rich.layout import Layout
7+
from rich.panel import Panel
8+
from rich import box
9+
from dataclasses import dataclass
10+
from typing import List
11+
12+
@dataclass
13+
class RayCluster:
14+
name: str
15+
status: str
16+
min_workers: int
17+
max_workers: int
18+
worker_mem_min: str
19+
worker_mem_max: str
20+
worker_cpu: int
21+
worker_gpu: int
22+
23+
def print_clusters(clusters:List[RayCluster], verbose=True):
24+
console = Console()
25+
26+
title_printed = False
27+
28+
for cluster in clusters:
29+
status = "Active :white_heavy_check_mark:" if cluster.status.lower() == 'ready' else "InActive :x:"
30+
name = cluster.name
31+
dashboard = f"https://codeflare-raydashboard.research.ibm.com?rayclustername={name}"
32+
mincount = str(cluster.min_workers)
33+
maxcount = str(cluster.max_workers)
34+
memory = cluster.worker_mem_min+"~"+cluster.worker_mem_max
35+
cpu = str(cluster.worker_cpu)
36+
gpu = str(cluster.worker_mem_max)
37+
#owned = bool(cluster["userOwned"])
38+
owned = True
39+
40+
#'table0' to display the cluster name, status, url, and dashboard link
41+
table0 = Table(box=None, show_header=False)
42+
if owned:
43+
table0.add_row("[white on green][bold]Owner")
44+
else:
45+
table0.add_row("")
46+
table0.add_row("[bold underline]"+name,status)
47+
table0.add_row()
48+
table0.add_row(f"[bold]URI:[/bold] ray://{name}-head.codeflare.svc:1001")
49+
table0.add_row()
50+
table0.add_row(f"[link={dashboard} blue underline]Dashboard:link:[/link]")
51+
table0.add_row("") #empty row for spacing
52+
53+
54+
#'table1' to display the worker counts
55+
table1 = Table(box=None)
56+
table1.add_row()
57+
table1.add_column("Min", style="cyan", no_wrap=True)
58+
table1.add_column("Max", style="magenta")
59+
table1.add_row()
60+
table1.add_row(mincount,maxcount)
61+
table1.add_row()
62+
63+
#'table2' to display the worker resources
64+
table2 = Table(box=None)
65+
table2.add_column("Memory", style="cyan", no_wrap=True, min_width=10)
66+
table2.add_column("CPU", style="magenta", min_width=10)
67+
table2.add_column("GPU", style="magenta", min_width=10)
68+
table2.add_row()
69+
table2.add_row(memory, cpu, gpu)
70+
table2.add_row()
71+
72+
#panels to encompass table1 and table2 into separate cards
73+
panel_1 = Panel.fit(table1,title="Workers")
74+
panel_2 = Panel.fit(table2, title="Worker specs(each)")
75+
76+
#table3 to display panel_1 and panel_2 side-by-side in a single row
77+
table3 = Table(box=None, show_header=False, title="Cluster Resources")
78+
table3.add_row(panel_1,panel_2)
79+
80+
#table4 to display table0 and table3, one below the other
81+
table4 = Table(box=None, show_header=False)
82+
table4.add_row(table0)
83+
table4.add_row(table3)
84+
85+
# Encompass all details of the cluster in a single panel
86+
if not title_printed:
87+
#If first cluster in the list, then create a table with title "Codeflare clusters".
88+
#This is done to ensure the title is center aligned on the cluster display tables, rather
89+
#than being center aligned on the console/terminal if we simply use console.print(title)
90+
91+
table5 = Table(box=None, title="[bold] :rocket: List of CodeFlare clusters :rocket:")
92+
table5.add_row(Panel.fit(table4))
93+
console.print(table5)
94+
title_printed = True
95+
else:
96+
console.print(Panel.fit(table4))

tests/test_clusters.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from codeflare_sdk.cluster.cluster import _get_ray_clusters
2+
from codeflare_sdk.utils.pretty_print import print_clusters
3+
def test_list_clusters():
4+
clusters = _get_ray_clusters()
5+
print_clusters(clusters)
6+

0 commit comments

Comments
 (0)