use std::{str::FromStr, thread, time::Duration};
use chrono::{NaiveTime, Timelike};
use crossbeam::{
channel::{Receiver, Sender},
select,
};
use crate::app::{AppMessage, JobControlMessage};
use anyhow::Result;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct SlurmJob {
pub job_id: String,
pub job_name: String,
pub partition: String,
pub account: String,
pub state: String,
pub start: String,
pub submit: String,
pub end: String,
pub reason: String,
pub work_dir: String,
pub time_limit: String,
pub elapsed_time: String,
pub stdout: Option<String>,
pub stderr: Option<String>,
pub node_list: String,
pub job_script: Vec<String>,
}
impl SlurmJob {
pub fn new(
job_id: String,
job_name: String,
partition: String,
account: String,
state: String,
start: String,
submit: String,
end: String,
reason: String,
work_dir: String,
time_limit: String,
elapsed_time: String,
stdout: Option<String>,
stderr: Option<String>,
node_list: String,
job_script: Vec<String>,
) -> SlurmJob {
SlurmJob {
job_id,
job_name,
partition,
account,
state,
start,
submit,
end,
reason,
work_dir,
time_limit,
elapsed_time,
stdout,
stderr,
node_list,
job_script,
}
}
}
impl SlurmJob {
pub fn cancel(&self) {
let cmd = format!("scancel {}", self.job_id);
std::process::Command::new("bash")
.arg("-c")
.arg(cmd)
.output()
.expect("failed to execute process");
}
pub fn restart(&self) {
let cmd = format!("scontrol requeue {}", self.job_id);
std::process::Command::new("bash")
.arg("-c")
.arg(cmd)
.output()
.expect("failed to execute process");
}
pub fn get_percent_completed(&self) -> u16 {
let elapsed = NaiveTime::from_str(&self.elapsed_time);
let wall_time = NaiveTime::from_str(&self.time_limit);
let elapsed_time = match elapsed {
Ok(elapsed) => {
match wall_time {
Ok(wall_time) => {
let percent_complete = (elapsed.num_seconds_from_midnight() as f32
/ wall_time.num_seconds_from_midnight() as f32)
* 100.;
percent_complete as u16
}
Err(_) => 0.0 as u16,
}
}
Err(_) => 0.0 as u16,
};
if elapsed_time > 100 {
100
} else {
elapsed_time
}
}
}
#[derive(Debug)]
pub struct SlurmJobControl {
send: Sender<AppMessage>,
recv: Receiver<JobControlMessage>,
}
impl SlurmJobControl {
pub fn new(send: Sender<AppMessage>, recv: Receiver<JobControlMessage>) -> Self {
Self { send, recv }
}
fn cancel_job(&self, job_id: &str) -> Result<()> {
let cmd = format!("scancel {}", job_id);
std::process::Command::new("bash")
.arg("-c")
.arg(cmd)
.output()
.expect("failed to execute process");
Ok(())
}
fn requeue_job(&self, job: &SlurmJob) -> Result<()> {
let cmd = format!("sbatch {}", job.job_name);
let _ = std::process::Command::new("bash")
.arg("-c")
.arg(cmd)
.current_dir(&job.work_dir)
.output()
.expect("failed to execute process");
Ok(())
}
fn run(&mut self) {
loop {
select! {
recv(self.recv) -> msg => {
match msg {
Ok(msg) => {
match msg {
JobControlMessage::CancelJob(job_id) => {
let rtn = self.cancel_job(&job_id);
self.send.send(AppMessage::JobCancelled(rtn)).unwrap();
}
JobControlMessage::RequeueJob(slurm_job) => {
let rtn = self.requeue_job(&slurm_job);
self.send.send(AppMessage::JobRequeued(rtn)).unwrap();
}
}
}
Err(_) => {
break;
}
}
}
}
}
}
}
#[derive(Debug)]
pub struct SlurmJobControlHandle {}
impl SlurmJobControlHandle {
pub fn new(send: Sender<AppMessage>, recv: Receiver<JobControlMessage>) -> Self {
let mut actor = SlurmJobControl::new(send, recv);
thread::spawn(move || actor.run());
Self {}
}
}