-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic clobberd functionality #8
Open
ethanf108
wants to merge
16
commits into
WillNilges:main
Choose a base branch
from
ethanf108:clobberd
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
cd3bb36
Basic clobberd functionality
ethanf108 155f87a
Basic socket functionality
ethanf108 af40591
Added some logging
ethanf108 94e2259
Added basic socket comm support
ethanf108 3abbe43
Basic Systemd service
ethanf108 c3a6138
Added lots of functionality, moved away from threaded approach
ethanf108 b2cc1ef
Fixed bug where containers were immediately marked as finished
ethanf108 c68d218
Added read and write timeout
ethanf108 0453836
Added full Pings support
ethanf108 af24a7c
Better error message for read timeout
ethanf108 9eac509
Added mny commands to clobber
ethanf108 2e4e5be
Removed dead code, implemented username feature (oops)
ethanf108 a2c5b12
Added Jobs command
ethanf108 f7c2203
Added My Jobs command
ethanf108 bf72b3f
Added setuid (maybe a bad idea)
ethanf108 9c96ddb
Added Status as default command
ethanf108 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[Unit] | ||
Description=Clobberd (@wilnil, @ethanf108). Beats u up if u use GPU when its not ur turn :) | ||
|
||
[Service] | ||
Type=simple | ||
ExecStart=/usr/local/sbin/clobberd | ||
|
||
[Install] | ||
WantedBy=default.target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
use clap::{Parser, Subcommand}; | ||
use colored::Colorize; | ||
use pod::*; | ||
use std::os::unix::net::UnixStream; | ||
use users::get_user_by_uid; | ||
|
||
mod comms; | ||
mod pod; | ||
|
||
#[derive(Parser, Debug)] | ||
#[command(author, version, about, long_about = None)] | ||
struct Args { | ||
#[command(subcommand)] | ||
command: Option<Commands>, | ||
} | ||
|
||
#[derive(Subcommand, Debug)] | ||
enum Commands { | ||
Status, | ||
Kill, | ||
Watch { | ||
#[arg(short, long, required = true)] | ||
device: u8, | ||
}, | ||
Unwatch { | ||
#[arg(short, long, required = true)] | ||
device: u8, | ||
}, | ||
Queue { | ||
#[arg(short, long, required = true)] | ||
image: String, | ||
#[arg(short, long, required = true)] | ||
gpus: Vec<u32>, | ||
}, | ||
Jobs { | ||
#[arg(short, long)] | ||
active: bool, | ||
}, | ||
} | ||
|
||
#[link(name = "c")] | ||
extern "C" { | ||
fn getuid() -> u32; | ||
} | ||
|
||
fn print_response(response: comms::Response) { | ||
use comms::Response::*; | ||
match response { | ||
Success => {} | ||
Error(e) => eprintln!("Error: {}", e.red()), | ||
GPUStatus { locks } => { | ||
for (index, gpu) in locks.iter().enumerate() { | ||
if let Some(user) = gpu { | ||
println!( | ||
"{} {} {} {}", | ||
"GPU".yellow(), | ||
index.to_string().yellow().bold(), | ||
"is being used by".red(), | ||
user.name.yellow().bold() | ||
); | ||
} else { | ||
println!( | ||
"{} {} {}", | ||
"GPU".yellow(), | ||
index.to_string().yellow().bold(), | ||
"is not being used".green() | ||
); | ||
} | ||
} | ||
} | ||
ActiveJobs(jobs) => { | ||
for job in jobs { | ||
println!( | ||
"{} {} {} {} {}", | ||
"Job".yellow(), | ||
job.id.to_string().yellow().bold(), | ||
format!("({})", job.owner.name).green().bold(), | ||
"is using GPU(s)".yellow(), | ||
job.requested_gpus | ||
.iter() | ||
.map(|g| g.to_string()) | ||
.reduce(|a, b| { | ||
let mut str = a.to_owned(); | ||
str.push_str(&b.to_owned()); | ||
str.push_str(", "); | ||
str | ||
}) | ||
.unwrap_or("NONE".into()) | ||
.green() | ||
); | ||
} | ||
} | ||
MyJobs(jobs) => { | ||
for (index, job) in jobs { | ||
println!( | ||
"{} {} {} {}", | ||
"Job".yellow(), | ||
job.id.to_string().yellow().bold(), | ||
"is in queue position".yellow(), | ||
(index + 1).to_string().yellow().bold() | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn send_command(command: comms::Command) { | ||
use std::io::{Read, Write}; | ||
let mut sock = match UnixStream::connect("/run/clobberd.sock") { | ||
Ok(s) => s, | ||
Err(e) => { | ||
eprintln!("Error connecting to socket. Is clobberd running?\n{}", e); | ||
return; | ||
} | ||
}; | ||
|
||
let json = match serde_json::to_string(&command) { | ||
Ok(j) => j, | ||
Err(e) => { | ||
eprintln!("Error serializing to JSON? somehow: {}", e); | ||
return; | ||
} | ||
}; | ||
|
||
if let Err(e) = sock.write(json.as_bytes()) { | ||
eprintln!("Error writing to socket: {}", e); | ||
} | ||
|
||
sock.flush().unwrap(); | ||
sock.shutdown(std::net::Shutdown::Write).unwrap(); | ||
|
||
let mut buf = String::with_capacity(10 * 1024); | ||
match sock.read_to_string(&mut buf) { | ||
Ok(_size) => match serde_json::from_str::<comms::Response>(&buf) { | ||
Ok(response) => print_response(response), | ||
Err(e) => eprintln!("Error parsing response: {}", e), | ||
}, | ||
Err(e) => { | ||
eprintln!("Error reading from socket: {}", e); | ||
} | ||
} | ||
} | ||
|
||
async fn find_image(uid: u32, image: String) -> Result<Option<String>, String> { | ||
let pod = Pod::new(uid); | ||
if let Err(e) = pod.ping().await { | ||
eprintln!( | ||
"Error connecting to podman: {}\n\nMaybe try {}", | ||
e, | ||
"systemctl enable --user --now podman.socket".green().bold() | ||
); | ||
return Err("".to_string()); | ||
} | ||
pod.image_exists(image.clone()) | ||
.await | ||
.map(|e| if e { Some(image) } else { None }) | ||
.map_err(|e| e.to_string()) | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
let args = Args::parse(); | ||
|
||
let (uid, is_root) = unsafe { (getuid(), getuid() == 0) }; | ||
|
||
let username = match get_user_by_uid(uid) { | ||
Some(user) => user.name().to_string_lossy().to_string(), | ||
None => { | ||
eprintln!("Error obtaining username."); | ||
return; | ||
} | ||
}; | ||
|
||
if let Some(command) = args.command { | ||
use Commands::*; | ||
match command { | ||
Status => send_command(comms::Command::Status), | ||
Kill => { | ||
if !is_root { | ||
eprintln!("Permission denied."); | ||
return; | ||
} | ||
send_command(comms::Command::Kill); | ||
} | ||
Watch { device } => { | ||
if !is_root { | ||
eprintln!("Permission denied."); | ||
return; | ||
} | ||
send_command(comms::Command::SetWatch { | ||
device_number: device as u32, | ||
watching: true, | ||
}); | ||
} | ||
Unwatch { device } => { | ||
if !is_root { | ||
eprintln!("Permission denied."); | ||
return; | ||
} | ||
send_command(comms::Command::SetWatch { | ||
device_number: device as u32, | ||
watching: false, | ||
}); | ||
} | ||
Queue { image, gpus } => match find_image(uid, image).await { | ||
Ok(Some(image)) => send_command(comms::Command::QueueJob { | ||
user: comms::User { | ||
uid: uid as usize, | ||
name: username.to_string(), | ||
}, | ||
image_id: image, | ||
gpus: gpus, | ||
}), | ||
|
||
Ok(None) => eprintln!("Cannot find image"), | ||
Err(e) => eprintln!("Error finding image: {}", e), | ||
}, | ||
Jobs { active } => { | ||
if active { | ||
send_command(comms::Command::ActiveJobs); | ||
} else { | ||
send_command(comms::Command::MyJobs { uid: uid }); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clobber should probably go in
/usr/local/bin