@@ -40,8 +40,8 @@ use crate::common::arraydef::IntArray;
4040use crate :: common:: utils:: str:: { pluralize, select_plural, truncate_middle} ;
4141use crate :: worker:: start:: WORKER_EXTRA_PROCESS_PID ;
4242use anyhow:: Error ;
43- use colored:: Color as Colorization ;
4443use colored:: Colorize ;
44+ use colored:: { Color as Colorization , ColoredString } ;
4545use std:: collections:: BTreeSet ;
4646use std:: fs:: File ;
4747use tako:: gateway:: {
@@ -281,7 +281,7 @@ impl Output for CliOutput {
281281
282282 let manager_info = configuration. get_manager_info ( ) ;
283283 let mut rows = vec ! [
284- vec![ "Worker ID " . cell( ) . bold( true ) , id. cell( ) ] ,
284+ vec![ "Worker" . cell( ) . bold( true ) , id. cell( ) ] ,
285285 vec![ "State" . cell( ) . bold( true ) , state] ,
286286 vec![ "Hostname" . cell( ) . bold( true ) , configuration. hostname. cell( ) ] ,
287287 vec![ "Started" . cell( ) . bold( true ) , format_datetime( started) . cell( ) ] ,
@@ -1046,97 +1046,176 @@ impl Output for CliOutput {
10461046 ) ;
10471047 }
10481048
1049- fn print_explanation (
1050- & self ,
1051- task_id : TaskId ,
1052- worker_id : WorkerId ,
1053- explanation : & TaskExplanation ,
1054- ) {
1055- let all = explanation. variants . len ( ) ;
1056- let count: usize = explanation
1057- . variants
1058- . iter ( )
1059- . map ( |e| if e. is_empty ( ) { 1 } else { 0 } )
1060- . sum ( ) ;
1061- if count > 0 {
1062- if all == 1 {
1063- println ! (
1064- "Task {task_id} {} on worker {worker_id}." ,
1065- "can run" . color( colored:: Color :: Green )
1066- ) ;
1067- } else {
1068- println ! (
1069- "Task {task_id} {} on worker {worker_id}, ({count}/{all} variants)" ,
1070- "can run" . color( colored:: Color :: Green )
1071- ) ;
1072- }
1073- if explanation. n_task_deps == 0 {
1074- println ! ( "Task is directly executable because it does not have any dependencies." , ) ;
1075- } else if explanation. n_waiting_deps == explanation. n_task_deps {
1076- println ! (
1077- "Task is directly executable because all dependencies ({}) are already finished." ,
1078- explanation. n_task_deps
1079- ) ;
1080- } else {
1081- println ! (
1082- "Task is not directly executable because {} dependencies are not yet finished." ,
1083- format!( "{}/{}" , explanation. n_waiting_deps, explanation. n_task_deps)
1084- . color( colored:: Color :: Red )
1085- ) ;
1086- }
1087- } else if all == 1 {
1049+ fn print_explanation ( & self , task_id : TaskId , explanation : & TaskExplanation ) {
1050+ if explanation. n_task_deps == 0 {
10881051 println ! (
1089- "Task {task_id} {} on worker {worker_id}." ,
1090- "cannot run" . color( colored:: Color :: Red )
1091- ) ;
1052+ "Task {} is {} to run because it has no dependencies." ,
1053+ task_id. to_string( ) . color( colored:: Color :: Cyan ) ,
1054+ "ready" . color( colored:: Color :: Yellow ) ,
1055+ )
1056+ } else if explanation. n_waiting_deps == explanation. n_task_deps {
1057+ println ! (
1058+ "Task {} is {} to run because its all {} dependencies are finished." ,
1059+ task_id. to_string( ) . color( colored:: Color :: Cyan ) ,
1060+ "ready" . color( colored:: Color :: Yellow ) ,
1061+ explanation
1062+ . n_task_deps
1063+ . to_string( )
1064+ . color( colored:: Color :: Cyan ) ,
1065+ )
10921066 } else {
10931067 println ! (
1094- "Task {task_id} {} on worker {worker_id}, none of {all} variants." ,
1095- "cannot run" . color( colored:: Color :: Red )
1096- ) ;
1068+ "Task {} is {} to run, because {} dependencies are not finished." ,
1069+ task_id. to_string( ) . color( colored:: Color :: Cyan ) ,
1070+ "not ready" . color( colored:: Color :: Green ) ,
1071+ format!( "{}/{}" , explanation. n_waiting_deps, explanation. n_task_deps)
1072+ . to_string( )
1073+ . color( colored:: Color :: Red )
1074+ )
10971075 }
1098- for ( i, explanations) in explanation. variants . iter ( ) . enumerate ( ) {
1099- if all > 1 && !explanations. is_empty ( ) {
1100- println ! ( "Resource variant {i}:" )
1101- }
1102- for expl in explanations {
1103- match expl {
1104- TaskExplainItem :: Time {
1105- min_time,
1106- remaining_time,
1107- } => {
1108- println ! (
1109- "* Task requests at least {} of running time, but worker remaining time is: {}." ,
1110- human_duration( chrono:: Duration :: from_std( * min_time) . unwrap( ) )
1111- . color( colored:: Color :: Cyan ) ,
1112- human_duration( chrono:: Duration :: from_std( * remaining_time) . unwrap( ) )
1113- . color( colored:: Color :: Red ) ,
1114- )
1115- }
1116- TaskExplainItem :: Resources {
1117- resource,
1118- request_amount,
1119- worker_amount,
1120- } => {
1121- println ! (
1122- "* Task requests at least {}, but worker provides {}." ,
1123- format!( "{} {}" , request_amount, resource) . color( colored:: Color :: Cyan ) ,
1124- format!( "{} {}" , worker_amount, resource) . color( colored:: Color :: Red ) ,
1125- )
1126- }
1127- TaskExplainItem :: WorkerGroup {
1128- n_nodes,
1129- group_size,
1130- } => {
1131- println ! (
1132- "* Task requests at least {} nodes, but worker is in group of size {}." ,
1133- format!( "{}" , n_nodes) . color( colored:: Color :: Cyan ) ,
1134- format!( "{}" , group_size) . color( colored:: Color :: Red ) ,
1135- )
1076+ let mut rows = Vec :: new ( ) ;
1077+ for w in & explanation. workers {
1078+ let all_varints = w. variants . len ( ) ;
1079+ let runnable_variants: usize = w
1080+ . variants
1081+ . iter ( )
1082+ . map ( |e| if e. is_empty ( ) { 1 } else { 0 } )
1083+ . sum ( ) ;
1084+ let can_run = if runnable_variants == 0 {
1085+ "No" . cell ( ) . foreground_color ( Some ( Color :: Red ) )
1086+ } else if all_varints == 1 {
1087+ "Yes" . cell ( ) . foreground_color ( Some ( Color :: Green ) )
1088+ } else {
1089+ format ! (
1090+ "{} ({}/{})" ,
1091+ "Yes" . color( colored:: Color :: Green ) ,
1092+ runnable_variants. to_string( ) . color( colored:: Color :: Green ) ,
1093+ all_varints
1094+ )
1095+ . cell ( )
1096+ } ;
1097+ let mut header = vec ! [ w. worker_id. cell( ) , can_run] ;
1098+ for ( i, variant) in w. variants . iter ( ) . enumerate ( ) {
1099+ if !variant. is_empty ( ) {
1100+ let ( mut rtype, mut provs, mut rqs) = ( Vec :: new ( ) , Vec :: new ( ) , Vec :: new ( ) ) ;
1101+ variant. iter ( ) . for_each ( |v| {
1102+ let ( rt, rq, pr) = explanation_item_to_strings ( v) ;
1103+ rtype. push ( rt. to_string ( ) ) ;
1104+ provs. push ( rq. to_string ( ) ) ;
1105+ rqs. push ( pr. to_string ( ) ) ;
1106+ } ) ;
1107+ if header. is_empty ( ) {
1108+ header. push ( "" . cell ( ) ) ;
1109+ header. push ( "" . cell ( ) ) ;
11361110 }
1111+ header. push ( i. cell ( ) ) ;
1112+ header. push ( rtype. join ( "\n " ) . cell ( ) ) ;
1113+ header. push ( provs. join ( "\n " ) . cell ( ) ) ;
1114+ header. push ( rqs. join ( "\n " ) . cell ( ) ) ;
1115+ rows. push ( std:: mem:: take ( & mut header) ) ;
1116+ }
1117+ }
1118+ if !header. is_empty ( ) {
1119+ for _ in 0 ..5 {
1120+ header. push ( "" . cell ( ) ) ;
11371121 }
1122+ rows. push ( header) ;
11381123 }
11391124 }
1125+ let header = vec ! [
1126+ "Worker Id" . cell( ) ,
1127+ "Runnable" . cell( ) ,
1128+ "Variant" . cell( ) ,
1129+ "Type" . cell( ) ,
1130+ "Provides" . cell( ) ,
1131+ "Request" . cell( ) ,
1132+ ] ;
1133+ self . print_horizontal_table ( rows, header) ;
1134+ // let all = explanation.variants.len();
1135+ // let count: usize = explanation
1136+ // .variants
1137+ // .iter()
1138+ // .map(|e| if e.is_empty() { 1 } else { 0 })
1139+ // .sum();
1140+ // if count > 0 {
1141+ // if all == 1 {
1142+ // println!(
1143+ // "Task {task_id} {} on worker {worker_id}.",
1144+ // "can run".color(colored::Color::Green)
1145+ // );
1146+ // } else {
1147+ // println!(
1148+ // "Task {task_id} {} on worker {worker_id}, ({count}/{all} variants)",
1149+ // "can run".color(colored::Color::Green)
1150+ // );
1151+ // }
1152+ // if explanation.n_task_deps == 0 {
1153+ // println!("Task is directly executable because it does not have any dependencies.",);
1154+ // } else if explanation.n_waiting_deps == explanation.n_task_deps {
1155+ // println!(
1156+ // "Task is directly executable because all dependencies ({}) are already finished.",
1157+ // explanation.n_task_deps
1158+ // );
1159+ // } else {
1160+ // println!(
1161+ // "Task is not directly executable because {} dependencies are not yet finished.",
1162+ // format!("{}/{}", explanation.n_waiting_deps, explanation.n_task_deps)
1163+ // .color(colored::Color::Red)
1164+ // );
1165+ // }
1166+ // } else if all == 1 {
1167+ // println!(
1168+ // "Task {task_id} {} on worker {worker_id}.",
1169+ // "cannot run".color(colored::Color::Red)
1170+ // );
1171+ // } else {
1172+ // println!(
1173+ // "Task {task_id} {} on worker {worker_id}, none of {all} variants.",
1174+ // "cannot run".color(colored::Color::Red)
1175+ // );
1176+ // }
1177+ // for (i, explanations) in explanation.variants.iter().enumerate() {
1178+ // if all > 1 && !explanations.is_empty() {
1179+ // println!("Resource variant {i}:")
1180+ // }
1181+ // for expl in explanations {
1182+ // match expl {
1183+ // TaskExplainItem::Time {
1184+ // min_time,
1185+ // remaining_time,
1186+ // } => {
1187+ // println!(
1188+ // "* Task requests at least {} of running time, but worker remaining time is: {}.",
1189+ // human_duration(chrono::Duration::from_std(*min_time).unwrap())
1190+ // .color(colored::Color::Cyan),
1191+ // human_duration(chrono::Duration::from_std(*remaining_time).unwrap())
1192+ // .color(colored::Color::Red),
1193+ // )
1194+ // }
1195+ // TaskExplainItem::Resources {
1196+ // resource,
1197+ // request_amount,
1198+ // worker_amount,
1199+ // } => {
1200+ // println!(
1201+ // "* Task requests at least {}, but worker provides {}.",
1202+ // format!("{} {}", request_amount, resource).color(colored::Color::Cyan),
1203+ // format!("{} {}", worker_amount, resource).color(colored::Color::Red),
1204+ // )
1205+ // }
1206+ // TaskExplainItem::WorkerGroup {
1207+ // n_nodes,
1208+ // group_size,
1209+ // } => {
1210+ // println!(
1211+ // "* Task requests at least {} nodes, but worker is in group of size {}.",
1212+ // format!("{}", n_nodes).color(colored::Color::Cyan),
1213+ // format!("{}", group_size).color(colored::Color::Red),
1214+ // )
1215+ // }
1216+ // }
1217+ // }
1218+ // }
11401219 }
11411220}
11421221
@@ -1651,6 +1730,42 @@ fn resources_summary(resources: &ResourceDescriptor, multiline: bool) -> String
16511730 result
16521731}
16531732
1733+ fn explanation_item_to_strings (
1734+ item : & TaskExplainItem ,
1735+ ) -> ( ColoredString , ColoredString , ColoredString ) {
1736+ match item {
1737+ TaskExplainItem :: Time {
1738+ min_time,
1739+ remaining_time,
1740+ } => (
1741+ "time" . color ( colored:: Color :: Magenta ) ,
1742+ human_duration ( chrono:: Duration :: from_std ( * remaining_time) . unwrap ( ) )
1743+ . to_string ( )
1744+ . color ( colored:: Color :: Yellow ) ,
1745+ human_duration ( chrono:: Duration :: from_std ( * min_time) . unwrap ( ) )
1746+ . to_string ( )
1747+ . color ( colored:: Color :: Red ) ,
1748+ ) ,
1749+ TaskExplainItem :: Resources {
1750+ resource,
1751+ request_amount,
1752+ worker_amount,
1753+ } => (
1754+ resource. color ( colored:: Color :: Cyan ) ,
1755+ format ! ( "{}" , worker_amount) . color ( colored:: Color :: Yellow ) ,
1756+ format ! ( "{}" , request_amount) . color ( colored:: Color :: Red ) ,
1757+ ) ,
1758+ TaskExplainItem :: WorkerGroup {
1759+ n_nodes,
1760+ group_size,
1761+ } => (
1762+ "nodes" . color ( colored:: Color :: Magenta ) ,
1763+ format ! ( "group size {}" , group_size) . color ( colored:: Color :: Yellow ) ,
1764+ format ! ( "{}" , n_nodes) . color ( colored:: Color :: Red ) ,
1765+ ) ,
1766+ }
1767+ }
1768+
16541769#[ cfg( test) ]
16551770mod tests {
16561771 use crate :: client:: output:: cli:: { resources_full_describe, resources_summary} ;
0 commit comments