33from typing import Dict
44import click
55from tabulate import tabulate
6- from centml .sdk import DeploymentType , DeploymentStatus , ServiceStatus , ApiException , HardwareInstanceResponse
6+ from centml .sdk import (
7+ DeploymentType ,
8+ DeploymentStatus ,
9+ ServiceStatus ,
10+ RolloutStatus ,
11+ ApiException ,
12+ HardwareInstanceResponse ,
13+ )
714from centml .sdk .api import get_centml_client
815
916# convert deployment type enum to a user friendly name
2734 "compute" : DeploymentType .COMPUTE_V2 ,
2835 "rag" : DeploymentType .RAG ,
2936}
37+ rollout_status_to_service_status_map = {
38+ RolloutStatus .HEALTHY : ServiceStatus .HEALTHY ,
39+ RolloutStatus .MISSING : ServiceStatus .MISSING ,
40+ RolloutStatus .PROGRESSING : ServiceStatus .INITIALIZING ,
41+ RolloutStatus .DEGRADED : ServiceStatus .ERROR ,
42+ }
3043
3144
3245def handle_exception (func ):
@@ -75,18 +88,18 @@ def _get_replica_info(deployment):
7588 return {"min" : "N/A" , "max" : "N/A" }
7689
7790
78- def _get_ready_status (cclient , deployment ):
91+ def _get_ready_status (deployment , service_status ):
7992 api_status = deployment .status
80- service_status = (
81- cclient .get_status (deployment .id ).service_status if deployment .status == DeploymentStatus .ACTIVE else None
82- )
8393
8494 status_styles = {
8595 (DeploymentStatus .PAUSED , None ): ("paused" , "yellow" , "black" ),
8696 (DeploymentStatus .DELETED , None ): ("deleted" , "white" , "black" ),
8797 (DeploymentStatus .ACTIVE , ServiceStatus .HEALTHY ): ("ready" , "green" , "black" ),
98+ (DeploymentStatus .ACTIVE , ServiceStatus .SCALINGUP ): ("starting" , "black" , "white" ),
99+ (DeploymentStatus .ACTIVE , ServiceStatus .PULLING ): ("starting" , "black" , "white" ),
88100 (DeploymentStatus .ACTIVE , ServiceStatus .INITIALIZING ): ("starting" , "black" , "white" ),
89101 (DeploymentStatus .ACTIVE , ServiceStatus .MISSING ): ("starting" , "black" , "white" ),
102+ (DeploymentStatus .ACTIVE , ServiceStatus .NOTREADY ): ("starting" , "black" , "white" ),
90103 (DeploymentStatus .ACTIVE , ServiceStatus .ERROR ): ("error" , "red" , "black" ),
91104 (DeploymentStatus .ACTIVE , ServiceStatus .CREATECONTAINERCONFIGERROR ): (
92105 "createContainerConfigError" ,
@@ -103,6 +116,62 @@ def _get_ready_status(cclient, deployment):
103116 return click .style (style [0 ], fg = style [1 ], bg = style [2 ])
104117
105118
119+ def _get_service_status (status_response , revision_number ):
120+ if status_response is None :
121+ return None
122+
123+ service_status = getattr (status_response , "service_status" , None )
124+ if service_status is not None :
125+ return service_status
126+
127+ revision_pod_details_list = getattr (status_response , "revision_pod_details_list" , None ) or []
128+ current_revision = next (
129+ (
130+ revision
131+ for revision in revision_pod_details_list
132+ if getattr (revision , "revision_number" , None ) == revision_number
133+ ),
134+ (
135+ revision_pod_details_list [0 ]
136+ if revision_pod_details_list and getattr (revision_pod_details_list [0 ], "revision_number" ) is None
137+ else None
138+ ),
139+ )
140+ revision_status = getattr (current_revision , "revision_status" , None )
141+
142+ return revision_status or rollout_status_to_service_status_map .get (getattr (status_response , "rollout_status" , None ))
143+
144+
145+ def _append_status_error_message (messages , seen_messages , label , error_message ):
146+ if not error_message or error_message in seen_messages :
147+ return
148+
149+ seen_messages .add (error_message )
150+ messages .append (f"{ label } : { error_message } " )
151+
152+
153+ def _get_status_error_messages (status_response ):
154+ if status_response is None :
155+ return []
156+
157+ error_message = getattr (status_response , "error_message" , None )
158+ if error_message :
159+ return [error_message ]
160+
161+ messages = []
162+ seen_messages = set ()
163+
164+ for revision in getattr (status_response , "revision_pod_details_list" , None ) or []:
165+ revision_label = f"revision { revision .revision_number } " if revision .revision_number is not None else "revision"
166+ _append_status_error_message (messages , seen_messages , revision_label , revision .error_message )
167+
168+ for pod in getattr (revision , "pod_details_list" , None ) or []:
169+ pod_label = pod .name or "pod"
170+ _append_status_error_message (messages , seen_messages , f"{ revision_label } / { pod_label } " , pod .error_message )
171+
172+ return messages
173+
174+
106175@click .command (help = "List all deployments" )
107176@click .argument ("type" , type = click .Choice (list (depl_name_to_type_map .keys ())), required = False , default = None )
108177def ls (type ):
@@ -149,24 +218,27 @@ def get(type, id):
149218 else :
150219 sys .exit ("Please enter correct deployment type" )
151220
152- ready_status = _get_ready_status (cclient , deployment )
221+ deployment_status = cclient .get_status (deployment .id ) if deployment .status == DeploymentStatus .ACTIVE else None
222+ revision_number = getattr (deployment , "revision_number" , None )
223+ service_status = _get_service_status (deployment_status , revision_number )
224+ ready_status = _get_ready_status (deployment , service_status )
225+ status_error_messages = _get_status_error_messages (deployment_status )
153226 _ , id_to_hw_map = _get_hw_to_id_map (cclient , deployment .cluster_id )
154227 hw = id_to_hw_map [deployment .hardware_instance_id ]
155-
156- click .echo (
157- tabulate (
158- [
159- ("Name" , deployment .name ),
160- ("Status" , ready_status ),
161- ("Endpoint" , deployment .endpoint_url ),
162- ("Created at" , deployment .created_at .strftime ("%Y-%m-%d %H:%M:%S" )),
163- ("Hardware" , f"{ hw .name } ({ hw .num_gpu } x { hw .gpu_type } )" ),
164- ("Cost" , f"{ hw .cost_per_hr / 100 } credits/hr" ),
165- ],
166- tablefmt = "rounded_outline" ,
167- disable_numparse = True ,
168- )
169- )
228+ detail_rows = [
229+ ("Name" , deployment .name ),
230+ ("Status" , ready_status ),
231+ ("Endpoint" , deployment .endpoint_url ),
232+ ("Created at" , deployment .created_at .strftime ("%Y-%m-%d %H:%M:%S" )),
233+ ("Hardware" , f"{ hw .name } ({ hw .num_gpu } x { hw .gpu_type } )" ),
234+ ("Cost" , f"{ hw .cost_per_hr / 100 } credits/hr" ),
235+ ]
236+
237+ click .echo (tabulate (detail_rows , tablefmt = "rounded_outline" , disable_numparse = True ))
238+ if status_error_messages :
239+ click .echo ("\n Status errors:" )
240+ for message in status_error_messages :
241+ click .echo (f"- { message } " )
170242
171243 click .echo ("Additional deployment configurations:" )
172244 if depl_type in [DeploymentType .INFERENCE_V2 , DeploymentType .INFERENCE_V3 ]:
0 commit comments