### Complete Hello Plugin Example with Options Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Implements a greeting plugin that adds a --greet option to srun. It registers the option, stores its value, and displays a greeting before tasks run. ```rust use eyre::WrapErr; use slurm_spank::{ spank_log_user, Context, Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN, }; use std::error::Error; use tracing::info; // Export plugin for Slurm loader SPANK_PLUGIN!(b"hello", SLURM_VERSION_NUMBER, SpankHello); #[derive(Default)] struct SpankHello { greet: Option, } unsafe impl Plugin for SpankHello { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Register --greet=name option in local and remote contexts match spank.context()? { Context::Local | Context::Remote => { spank .register_option( SpankOption::new("greet") .takes_value("name") .usage("Greet [name] before running tasks"), ) .wrap_err("Failed to register greet option")?; } _ => {} } Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Store option value after processing self.greet = spank .get_option_value("greet") .wrap_err("Failed to read --greet option")?; if let Some(name) = &self.greet { info!("User opted to greet {name}"); } Ok(()) } fn user_init(&mut self, _spank: &mut SpankHandle) -> Result<(), Box> { // Display greeting when tasks start if let Some(name) = &self.greet { spank_log_user!("Hello {name}!"); } Ok(()) } } // Cargo.toml for this plugin: // [package] // name = "slurm-spank-hello" // version = "0.1.0" // edition = "2021" // // [lib] // crate-type = ["cdylib"] // // [dependencies] // eyre = "0.6.8" // slurm-spank = "0.4" // tracing = "0.1" ``` -------------------------------- ### Implement Plugin with Error Handling Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt This example shows how to implement the `Plugin` trait and handle `SpankError` variants. It demonstrates getting environment variables and includes a placeholder for custom error reporting. ```rust use slurm_spank::{Plugin, SpankError, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"error_handler", SLURM_VERSION_NUMBER, ErrorHandlerPlugin); #[derive(Default)] struct ErrorHandlerPlugin; unsafe impl Plugin for ErrorHandlerPlugin { fn task_post_fork(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // SpankError variants: // - SpankAPI(function_name, SpankApiError) - SPANK API call failed // - Utf8Error(string) - Invalid UTF-8 in string // - CStringError(string) - String contains null bytes // - EnvExists(name) - Environment variable exists (overwrite=false) // - PidNotFound(pid) - PID not found in job // - IdNotFound(id) - Task ID not found // - Overflow(value) - Integer overflow // Example: handle specific errors match spank.getenv("SOME_VAR") { Ok(Some(value)) => println!("Value: {}", value), Ok(None) => println!("Variable not set"), Err(e) => { println!("Error getting env var: {}", e); // Can check error type: // if matches!(e, SpankError::Utf8Error(_)) { ... } } } Ok(()) } // Custom error reporting (overrides default) fn report_error(&self, _spank: &mut SpankHandle, error: &dyn Error) { // Build error chain let mut report = error.to_string(); let mut current = error; while let Some(source) = current.source() { report.push_str(&format!( ``` -------------------------------- ### SpankHandle Environment Methods Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Methods to get, set, and unset environment variables in the job's environment. These can be used in remote contexts to modify the environment seen by job tasks. Job control environment methods work in local/allocator contexts. ```APIDOC ## SpankHandle Environment Methods ### Description Methods to get, set, and unset environment variables in the job's environment. Use these in remote context to modify the environment seen by job tasks. Job control environment methods work in local/allocator context. ### Methods - **`getenv(key: &str)`**: Gets an environment variable. Returns `Option`. - **`getenv_lossy(key: &str)`**: Gets an environment variable with lossy UTF-8 conversion. Returns `Option`. - **`getenv_os(key: &str)`**: Gets an environment variable as an `OsString`. Returns `Option`. - **`setenv(key: &str, value: &str, overwrite: bool)`**: Sets an environment variable. `overwrite` determines if existing variables are replaced. - **`unsetenv(key: &str)`**: Unsets an environment variable. - **`job_env()`**: Gets the entire job environment as a `Vec<&str>`. - **`job_argv()`**: Gets the job command arguments as a `Vec`. - **`job_control_getenv(key: &str)`**: Gets an environment variable from the job control environment (local/allocator context). Returns `Option`. - **`job_control_setenv(key: &str, value: &str, overwrite: bool)`**: Sets an environment variable in the job control environment. - **`job_control_unsetenv(key: &str)`**: Unsets an environment variable from the job control environment. ### Example Usage (Rust) ```rust use slurm_spank::{Context, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"env_manager", SLURM_VERSION_NUMBER, EnvManagerPlugin); #[derive(Default)] struct EnvManagerPlugin; unsafe impl Plugin for EnvManagerPlugin { fn user_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Get environment variable if let Some(value) = spank.getenv("MY_VAR")? { println!("MY_VAR is set to: {}", value); } // Get with lossy UTF-8 conversion if let Some(value) = spank.getenv_lossy("SOME_VAR")? { println!("SOME_VAR: {}", value); } // Get as OsString if let Some(value) = spank.getenv_os("BINARY_VAR")? { println!("BINARY_VAR: {:?}", value); } // Set environment variable (overwrite=true) spank.setenv("PLUGIN_ENABLED", "1", true)?; // Set environment variable (overwrite=false) spank.setenv("DEFAULT_VALUE", "default", false)?; // Unset environment variable spank.unsetenv("UNWANTED_VAR")?; // Get entire job environment let env_vars = spank.job_env()?; for var in env_vars { println!("ENV: {}", var); } // Get job command arguments let argv = spank.job_argv()?; println!("Command: {:?}", argv); Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Job control environment (for local/allocator context) if spank.context()? == Context::Local { // Get from job control environment if let Some(val) = spank.job_control_getenv("SLURM_JOB_ID")? { println!("Job ID: {}", val); } // Set in job control environment spank.job_control_setenv("MY_SETTING", "value", true)?; // Unset from job control environment spank.job_control_unsetenv("OLD_SETTING")?; } Ok(()) } } ``` ``` -------------------------------- ### Prepend Arguments to Task Execution in Rust Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Use `prepend_task_argv` to add arguments to the beginning of a task's command line before it executes. This is useful for wrapping commands, for example, with debugging tools like `strace`. This method is only valid in `task_init` or `task_init_privileged` callbacks. ```rust use slurm_spank::{Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"wrapper", SLURM_VERSION_NUMBER, WrapperPlugin); #[derive(Default)] struct WrapperPlugin { use_strace: bool, } unsafe impl Plugin for WrapperPlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { spank.register_option( SpankOption::new("strace") .usage("Run tasks under strace for debugging"), )?; Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { self.use_strace = spank.is_option_set("strace"); Ok(()) } fn task_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { if self.use_strace { // Prepend strace to the command // If original command was: ./myapp --arg1 // It becomes: strace -o /tmp/trace.out ./myapp --arg1 spank.prepend_task_argv(vec!["strace", "-o", "/tmp/trace.out"])?; } Ok(()) } } ``` -------------------------------- ### Manage Job Environment Variables in Rust Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Use these methods to get, set, and unset environment variables within a Slurm job's environment. These are useful for configuring tasks remotely. Ensure correct context for job control methods. ```rust use slurm_spank::{Context, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"env_manager", SLURM_VERSION_NUMBER, EnvManagerPlugin); #[derive(Default)] struct EnvManagerPlugin; unsafe impl Plugin for EnvManagerPlugin { fn user_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Get environment variable (returns Option) if let Some(value) = spank.getenv("MY_VAR")? { println!("MY_VAR is set to: {}", value); } // Get with lossy UTF-8 conversion (replaces invalid chars with U+FFFD) if let Some(value) = spank.getenv_lossy("SOME_VAR")? { println!("SOME_VAR: {}", value); } // Get as OsString (for non-UTF8 values) if let Some(value) = spank.getenv_os("BINARY_VAR")? { println!("BINARY_VAR: {:?}", value); } // Set environment variable (overwrite=true replaces existing) spank.setenv("PLUGIN_ENABLED", "1", true)?; // Set without overwriting existing value spank.setenv("DEFAULT_VALUE", "default", false)?; // Unset environment variable spank.unsetenv("UNWANTED_VAR")?; // Get entire job environment as Vec<&str> let env_vars = spank.job_env()?; for var in env_vars { println!("ENV: {}", var); } // Get job command arguments let argv = spank.job_argv()?; println!("Command: {:?}", argv); Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Job control environment (for local/allocator context) if spank.context()? == Context::Local { // Get from job control environment if let Some(val) = spank.job_control_getenv("SLURM_JOB_ID")? { println!("Job ID: {}", val); } // Set in job control environment spank.job_control_setenv("MY_SETTING", "value", true)?; // Unset from job control environment spank.job_control_unsetenv("OLD_SETTING")?; } Ok(()) } } ``` -------------------------------- ### Parse Plugin Arguments with SpankHandle::plugin_argv() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Reads plugin-specific configuration options from the plugstack.conf file. Use this to parse arguments like min_prio and log_dir. ```rust use slurm_spank::{Context, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"configurable", SLURM_VERSION_NUMBER, ConfigurablePlugin); #[derive(Default)] struct ConfigurablePlugin { min_priority: i32, log_dir: String, } unsafe impl Plugin for ConfigurablePlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Parse plugin arguments from plugstack.conf // Example line: required /path/to/plugin.so min_prio=-10 log_dir=/var/log/spank if spank.context()? == Context::Remote { for arg in spank.plugin_argv()? { if let Some(value) = arg.strip_prefix("min_prio=") { self.min_priority = value.parse()?; } else if let Some(value) = arg.strip_prefix("log_dir=") { self.log_dir = value.to_string(); } else { return Err(format!("Unknown plugin argument: {}", arg).into()); } } } println!("Config: min_prio={}, log_dir={}", self.min_priority, self.log_dir); Ok(()) } } ``` -------------------------------- ### Define and Initialize Slurm Spank Plugin Source: https://github.com/fdiakh/slurm-spank-rs/blob/main/src/hello.md This code defines the main structure for the Spank plugin and implements the `init` method to register a custom option. The `SPANK_PLUGIN` macro is essential for Slurm to load the plugin. ```rust use eyre::WrapErr; use slurm_spank::{ spank_log_user, Context, Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN, }; use std::error::Error; use tracing::info; // All spank plugins must define this macro for the // Slurm plugin loader. SPANK_PLUGIN!(b"hello", SLURM_VERSION_NUMBER, SpankHello); #[derive(Default)] struct SpankHello { greet: Option, } unsafe impl Plugin for SpankHello { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Register the --greet=name option match spank.context()? { Context::Local | Context::Remote => { spank .register_option( SpankOption::new("greet") .takes_value("name") .usage("Greet [name] before running tasks"), ) .wrap_err("Failed to register greet option")?; } _ => {} } Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Check if the option was set self.greet = spank .get_option_value("greet") .wrap_err("Failed to read --greet option")? .map(|s| s.to_string()); if let Some(name) = &self.greet { info!("User opted to greet {name}"); } Ok(()) } fn user_init(&mut self, _spank: &mut SpankHandle) -> Result<(), Box> { // Greet as requested if let Some(name) = &self.greet { spank_log_user!("Hello {name}!"); } Ok(()) } } ``` -------------------------------- ### Determine Plugin Context with SpankHandle::context() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Use `spank.context()?` within the `init()` callback to determine the current execution context (Local, Remote, Allocator, Slurmd, JobScript) and conditionally execute code. ```rust use slurm_spank::{Context, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"context_aware", SLURM_VERSION_NUMBER, ContextAwarePlugin); #[derive(Default)] struct ContextAwarePlugin; unsafe impl Plugin for ContextAwarePlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { match spank.context()? { Context::Local => { // Running in srun on submit host println!("Running in local context (srun)"); } Context::Remote => { // Running on compute node in slurmstepd println!("Running in remote context (compute node)"); } Context::Allocator => { // Running in salloc or sbatch println!("Running in allocator context"); } Context::Slurmd => { // Running in slurmd daemon println!("Running in slurmd context"); } Context::JobScript => { // Running in job prolog/epilog script println!("Running in job script context"); } } Ok(()) } } ``` -------------------------------- ### Register Command-Line Options with SpankHandle::register_option() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Register custom command-line options during the `init()` callback using `SpankOption::new()` and `spank.register_option()`. Options can take values or act as flags. Retrieve their values in `init_post_opt()`. ```rust use slurm_spank::{Context, Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"custom_opts", SLURM_VERSION_NUMBER, CustomOptsPlugin); #[derive(Default)] struct CustomOptsPlugin { custom_value: Option, flag_enabled: bool, } unsafe impl Plugin for CustomOptsPlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Only register in contexts where options are processed match spank.context()? { Context::Local | Context::Remote | Context::Allocator => { // Register option that takes a value: --custom-opt=VALUE spank.register_option( SpankOption::new("custom-opt") .takes_value("VALUE") .usage("Set a custom value for the job"), )?; // Register a flag option (no value): --enable-feature spank.register_option( SpankOption::new("enable-feature") .usage("Enable special feature for this job"), )?; } _ => {} } Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Retrieve option value after processing self.custom_value = spank .get_option_value("custom-opt")? .map(|s| s.to_string()); // Check if flag was set self.flag_enabled = spank.is_option_set("enable-feature"); if let Some(val) = &self.custom_value { println!("Custom option set to: {}", val); } if self.flag_enabled { println!("Feature enabled"); } Ok(()) } } ``` -------------------------------- ### Exporting a Slurm SPANK Plugin with SPANK_PLUGIN! Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Use the SPANK_PLUGIN! macro to export your plugin struct, making it loadable by Slurm. Provide the plugin name, Slurm version, and your plugin struct type. ```rust use slurm_spank::{Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; // Export the plugin for Slurm to load SPANK_PLUGIN!(b"myplugin", SLURM_VERSION_NUMBER, MyPlugin); #[derive(Default)] struct MyPlugin; unsafe impl Plugin for MyPlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Plugin initialization code Ok(()) } } ``` -------------------------------- ### SpankHandle::register_option() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Registers a command-line option that users can pass to srun, sbatch, or salloc. Options must be registered during the `init()` callback. The `SpankOption` builder is used to configure the option's name, argument information, and usage text. ```APIDOC ## SpankHandle::register_option() ### Description Registers a command-line option that users can pass to srun, sbatch, or salloc. Options must be registered during the `init()` callback. Use `SpankOption` builder to configure the option name, argument info, and usage text. ### Method `register_option(option: SpankOption) -> Result<(), Box>` ### Parameters - **option** (`SpankOption`): An instance of `SpankOption` configured with the desired properties of the command-line option. ### Usage Options should be registered within the `init()` or `init_post_opt()` callbacks of a `Plugin` implementation. ### Example ```rust use slurm_spank::{Context, Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"custom_opts", SLURM_VERSION_NUMBER, CustomOptsPlugin); #[derive(Default)] struct CustomOptsPlugin { custom_value: Option, flag_enabled: bool, } unsafe impl Plugin for CustomOptsPlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Only register in contexts where options are processed match spank.context()? { Context::Local | Context::Remote | Context::Allocator => { // Register option that takes a value: --custom-opt=VALUE spank.register_option( SpankOption::new("custom-opt") .takes_value("VALUE") .usage("Set a custom value for the job"), )?; // Register a flag option (no value): --enable-feature spank.register_option( SpankOption::new("enable-feature") .usage("Enable special feature for this job"), )?; } _ => {} } Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Retrieve option value after processing self.custom_value = spank .get_option_value("custom-opt")? .map(|s| s.to_string()); // Check if flag was set self.flag_enabled = spank.is_option_set("enable-feature"); if let Some(val) = &self.custom_value { println!("Custom option set to: {}", val); } if self.flag_enabled { println!("Feature enabled"); } Ok(()) } } ``` ``` -------------------------------- ### SpankHandle::context() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Retrieves the current execution context of the SPANK plugin. This allows for conditional logic based on whether the plugin is running in local (srun), remote (slurmd), allocator (salloc/sbatch), slurmd daemon, or job_script context. ```APIDOC ## SpankHandle::context() ### Description Returns the context in which the plugin is currently running. Use this to conditionally execute code based on whether you're in local (srun), remote (slurmd), allocator (salloc/sbatch), slurmd, or job_script context. ### Method `context()` ### Return Value A `Context` enum value representing the current execution environment. ### Example ```rust use slurm_spank::{Context, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"context_aware", SLURM_VERSION_NUMBER, ContextAwarePlugin); #[derive(Default)] struct ContextAwarePlugin; unsafe impl Plugin for ContextAwarePlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { match spank.context()? { Context::Local => { // Running in srun on submit host println!("Running in local context (srun)"); } Context::Remote => { // Running on compute node in slurmstepd println!("Running in remote context (compute node)"); } Context::Allocator => { // Running in salloc or sbatch println!("Running in allocator context"); } Context::Slurmd => { // Running in slurmd daemon println!("Running in slurmd context"); } Context::JobScript => { // Running in job prolog/epilog script println!("Running in job script context"); } } Ok(()) } } ``` ``` -------------------------------- ### Slurm Logging Functions and Macros Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Routes messages through Slurm's logging infrastructure using spank_log() or convenience macros. Messages are logged according to Slurm's configured settings. ```rust use slurm_spank::{ spank_log, spank_log_debug, spank_log_error, spank_log_info, spank_log_user, spank_log_verbose, LogLevel, Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN, }; use std::error::Error; SPANK_PLUGIN!(b"logger", SLURM_VERSION_NUMBER, LoggerPlugin); #[derive(Default)] struct LoggerPlugin; unsafe impl Plugin for LoggerPlugin { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Direct logging function with explicit level spank_log(LogLevel::Info, "Plugin initializing"); spank_log(LogLevel::Debug, "Debug message"); // Convenience macros (support format strings) spank_log_error!("Error: something went wrong with code {}", 42); spank_log_info!("Info: job {} starting", spank.job_id().unwrap_or(0)); spank_log_verbose!("Verbose: detailed information"); spank_log_debug!("Debug: debugging info"); // Log directly to user without "error:" prefix spank_log_user!("Hello from the SPANK plugin!"); Ok(()) } } ``` -------------------------------- ### Retrieve Job and Task Information in Rust Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Use SpankHandle methods within appropriate contexts (e.g., task_post_fork, task_exit) to access job ID, step ID, user IDs, node counts, task counts, and resource allocations. Ensure necessary imports and plugin registration. ```rust use slurm_spank::{Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"job_info", SLURM_VERSION_NUMBER, JobInfoPlugin); #[derive(Default)] struct JobInfoPlugin; unsafe impl Plugin for JobInfoPlugin { fn task_post_fork(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Job identification let job_id = spank.job_id()?; let step_id = spank.job_stepid()?; let array_id = spank.job_array_id()?; let array_task_id = spank.job_array_task_id()?; // User information let uid = spank.job_uid()?; let gid = spank.job_gid()?; let supplementary_gids = spank.job_supplementary_gids()?; // Node and task counts let total_nodes = spank.job_nnodes()?; let node_id = spank.job_nodeid()?; let local_tasks = spank.job_local_task_count()?; let total_tasks = spank.job_total_task_count()?; // Current task information let task_id = spank.task_id()?; let global_task_id = spank.task_global_id()?; let task_pid = spank.task_pid()?; // Resource allocation let ncpus = spank.job_ncpus()?; let cpus_per_task = spank.step_cpus_per_task()?; let alloc_cores = spank.job_alloc_cores()?; let alloc_mem = spank.job_alloc_mem()?; let step_cores = spank.step_alloc_cores()?; let step_mem = spank.step_alloc_mem()?; // Slurm version let version = spank.slurm_version()?; let restart_count = spank.slurm_restart_count()?; // Task ID conversions let global_from_local = spank.local_to_global_id(task_id as u32)?; let local_from_global = spank.global_to_local_id(global_task_id)?; let global_from_pid = spank.pid_to_global_id(task_pid)?; let local_from_pid = spank.pid_to_local_id(task_pid)?; println!( "Job {} step {} task {}/{} (pid {}) on node {}/{}", job_id, step_id, global_task_id, total_tasks, task_pid, node_id, total_nodes ); Ok(()) } fn task_exit(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Available when task exits let exit_status = spank.task_exit_status()?; let task_id = spank.task_global_id()?; println!("Task {} exited with status {}", task_id, exit_status); Ok(()) } } ``` -------------------------------- ### Complete Renice Plugin Implementation in Rust Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt This Rust code implements a SLURM SPANK plugin that allows users to adjust task priority via --renice option or SLURM_RENICE environment variable, with administrator-configurable minimum priority limits. It registers the option, parses configuration, and applies priority changes. ```rust use eyre::{eyre, Report, WrapErr}; use libc::{setpriority, PRIO_PROCESS}; use slurm_spank::{Context, Plugin, SpankHandle, SpankOption, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; use tracing::{error, info}; const MIN_PRIO: i32 = -20; const PRIO_ENV_VAR: &str = "SLURM_RENICE"; SPANK_PLUGIN!(b"renice", SLURM_VERSION_NUMBER, SpankRenice); struct SpankRenice { min_prio: i32, // Admin-configurable minimum priority prio: Option, } impl Default for SpankRenice { fn default() -> Self { Self { min_prio: MIN_PRIO, prio: None, } } } unsafe impl Plugin for SpankRenice { fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Skip in irrelevant contexts if spank.context()? == Context::Allocator || spank.context()? == Context::Slurmd { return Ok(()); } // Parse admin configuration from plugstack.conf if spank.context()? == Context::Remote { for arg in spank.plugin_argv().wrap_err("Invalid plugin argument")? { match arg.strip_prefix("min_prio=") { Some(value) => { self.min_prio = parse_prio(value).wrap_err("Invalid min_prio")? } None => return Err(eyre!("Invalid plugin argument: {}", arg).into()), } } } // Register --renice option spank.register_option( SpankOption::new("renice") .takes_value("prio") .usage("Re-nice job tasks to priority [prio]"), ).wrap_err("Failed to register renice option")?; Ok(()) } fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { match spank.context()? { Context::Local | Context::Remote => (), _ => return Ok(()), } if let Some(prio) = spank.get_option_value("renice")? { self.set_prio(&prio, "--renice")?; } Ok(()) } fn task_post_fork(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { // Check environment variable if option not set if self.prio.is_none() { if let Some(prio) = spank.getenv(PRIO_ENV_VAR)? { self.set_prio(&prio, PRIO_ENV_VAR)?; } } // Apply priority change if let Some(prio) = self.prio { let task_id = spank.task_global_id()?; let pid = spank.task_pid()?; info!("re-nicing task{} pid {} to {}", task_id, pid, prio); if unsafe { setpriority(PRIO_PROCESS, pid as u32, prio) } < 0 { return Err(Report::new(std::io::Error::last_os_error()) .wrap_err("setpriority") .into()); } } Ok(()) } } impl SpankRenice { fn set_prio(&mut self, prio: &str, opt_name: &str) -> Result<(), Report> { let prio = parse_prio(prio)?; self.prio = if prio >= self.min_prio { Some(prio) } else { error!("{}={} not allowed, using min_prio ({})", opt_name, prio, self.min_prio); Some(self.min_prio) }; Ok(()) } } fn parse_prio(value: &str) -> Result { let value: i32 = value.parse()?; match value { -20..=19 => Ok(value), _ => Err(eyre!("Priority must be between -20 and 19")), } } ``` -------------------------------- ### SpankHandle::prepend_task_argv() Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Prepends arguments to the task's argument vector before execution. This can be used to wrap commands with additional tools or modify how tasks are launched. Only valid in task_init or task_init_privileged callbacks. ```APIDOC ## SpankHandle::prepend_task_argv() ### Description Prepends arguments to the task's argument vector before execution. This can be used to wrap commands with additional tools or modify how tasks are launched. Only valid in `task_init` or `task_init_privileged` callbacks. ### Method `prepend_task_argv(args: Vec<&str>)` ### Endpoint N/A (This is a method call within a SPANK plugin) ### Parameters #### Request Body - **args** (Vec<&str>) - Required - A vector of string slices representing the arguments to prepend. ### Request Example ```rust // Inside a task_init or task_init_privileged callback: // If original command was: ./myapp --arg1 // It becomes: strace -o /tmp/trace.out ./myapp --arg1 spank.prepend_task_argv(vec!["strace", "-o", "/tmp/trace.out"])?; ``` ### Response N/A (This method modifies the task's argument vector in place and does not return a value directly, but can return a `Result<(), Box>`) #### Success Response - **None** #### Response Example N/A ``` -------------------------------- ### Implementing the Plugin Trait for Job Lifecycle Events Source: https://context7.com/fdiakh/slurm-spank-rs/llms.txt Implement the Plugin trait on your struct to handle job lifecycle events. All callbacks have default no-op implementations, allowing you to override only the necessary ones. ```rust use slurm_spank::{Plugin, SpankHandle, SLURM_VERSION_NUMBER, SPANK_PLUGIN}; use std::error::Error; SPANK_PLUGIN!(b"lifecycle", SLURM_VERSION_NUMBER, LifecyclePlugin); #[derive(Default)] struct LifecyclePlugin; unsafe impl Plugin for LifecyclePlugin { // Called just after plugins are loaded, before option processing fn init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called after all user options have been processed fn init_post_opt(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called at the same time as job prolog fn job_prolog(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called in local (srun) context after options processed, before task launch fn local_user_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called after privileges are temporarily dropped (remote context only) fn user_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called for each task after fork, before privileges dropped (remote context) fn task_init_privileged(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called for each task just before execve (remote context) fn task_init(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called for each task from parent after fork is complete (remote context) fn task_post_fork(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called for each task as exit status is collected (remote context) fn task_exit(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called at the same time as job epilog fn job_epilog(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called when slurmd daemon shuts down fn slurmd_exit(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } // Called just before slurmstepd/srun exits fn exit(&mut self, spank: &mut SpankHandle) -> Result<(), Box> { Ok(()) } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.