use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum SshTransport {
Native,
NvidiaSmi,
RocmSmi,
#[default]
Unsupported,
}
impl SshTransport {
pub fn chip_label(self) -> &'static str {
match self {
Self::Native => "native",
Self::NvidiaSmi => "nvidia-smi",
Self::RocmSmi => "rocm-smi",
Self::Unsupported => "unsupported",
}
}
}
impl fmt::Display for SshTransport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.chip_label())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct SshFallbackPolicy {
pub try_nvidia_smi: bool,
pub try_rocm_smi: bool,
}
impl SshFallbackPolicy {
pub fn from_cli(raw: Option<&str>) -> Result<Self, SshFallbackPolicyError> {
let Some(s) = raw else {
return Ok(Self::default_enabled());
};
let s = s.trim();
if s.is_empty() {
return Ok(Self::default_enabled());
}
let mut policy = Self::default();
let mut saw_explicit_none = false;
for token in s.split(',') {
let token = token.trim().to_ascii_lowercase();
match token.as_str() {
"" => continue,
"none" | "off" | "disabled" => {
saw_explicit_none = true;
}
"nvidia-smi" | "nvidia_smi" | "nvidia" => policy.try_nvidia_smi = true,
"rocm-smi" | "rocm_smi" | "rocm" | "amd" => policy.try_rocm_smi = true,
other => {
return Err(SshFallbackPolicyError::Unknown(other.to_string()));
}
}
}
if saw_explicit_none {
return Ok(Self::default());
}
Ok(policy)
}
fn default_enabled() -> Self {
Self {
try_nvidia_smi: true,
try_rocm_smi: true,
}
}
#[allow(dead_code)]
pub fn any_enabled(&self) -> bool {
self.try_nvidia_smi || self.try_rocm_smi
}
}
#[derive(Debug, thiserror::Error)]
pub enum SshFallbackPolicyError {
#[error("unknown fallback kind `{0}` (valid: nvidia-smi, rocm-smi, none)")]
Unknown(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StrictHostKey {
#[default]
Yes,
AcceptNew,
No,
}
impl StrictHostKey {
pub fn from_cli(raw: &str) -> Result<Self, StrictHostKeyError> {
match raw.trim().to_ascii_lowercase().as_str() {
"yes" | "true" | "strict" => Ok(Self::Yes),
"accept-new" | "acceptnew" | "accept_new" => Ok(Self::AcceptNew),
"no" | "false" | "off" => Ok(Self::No),
other => Err(StrictHostKeyError::Unknown(other.to_string())),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum StrictHostKeyError {
#[error("unknown --ssh-strict-host-key value `{0}` (valid: yes, accept-new, no)")]
Unknown(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transport_chip_label_is_stable() {
assert_eq!(SshTransport::Native.chip_label(), "native");
assert_eq!(SshTransport::NvidiaSmi.chip_label(), "nvidia-smi");
assert_eq!(SshTransport::RocmSmi.chip_label(), "rocm-smi");
assert_eq!(SshTransport::Unsupported.chip_label(), "unsupported");
}
#[test]
fn policy_default_enables_both_shims() {
let p = SshFallbackPolicy::from_cli(None).unwrap();
assert!(p.try_nvidia_smi);
assert!(p.try_rocm_smi);
}
#[test]
fn policy_none_disables_both() {
let p = SshFallbackPolicy::from_cli(Some("none")).unwrap();
assert!(!p.try_nvidia_smi);
assert!(!p.try_rocm_smi);
assert!(!p.any_enabled());
}
#[test]
fn policy_only_nvidia() {
let p = SshFallbackPolicy::from_cli(Some("nvidia-smi")).unwrap();
assert!(p.try_nvidia_smi);
assert!(!p.try_rocm_smi);
}
#[test]
fn policy_both_explicitly() {
let p = SshFallbackPolicy::from_cli(Some("nvidia-smi,rocm-smi")).unwrap();
assert!(p.try_nvidia_smi);
assert!(p.try_rocm_smi);
}
#[test]
fn policy_none_wins_over_other_tokens() {
let p = SshFallbackPolicy::from_cli(Some("nvidia-smi,none")).unwrap();
assert!(!p.any_enabled());
}
#[test]
fn policy_rejects_unknown_token() {
let e = SshFallbackPolicy::from_cli(Some("gpumonitor")).unwrap_err();
assert!(matches!(e, SshFallbackPolicyError::Unknown(_)));
}
#[test]
fn strict_host_key_parses_all_variants() {
assert_eq!(StrictHostKey::from_cli("yes").unwrap(), StrictHostKey::Yes);
assert_eq!(
StrictHostKey::from_cli("accept-new").unwrap(),
StrictHostKey::AcceptNew
);
assert_eq!(StrictHostKey::from_cli("no").unwrap(), StrictHostKey::No);
}
#[test]
fn strict_host_key_rejects_unknown() {
let e = StrictHostKey::from_cli("sometimes").unwrap_err();
assert!(matches!(e, StrictHostKeyError::Unknown(_)));
}
}