-
Notifications
You must be signed in to change notification settings - Fork 163
DevicePlugin ENV variables #788
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
Changes from all commits
3178213
28f9a07
86fd136
545d33f
22cf634
5712433
16b3639
dc0c4e0
76fabfa
d97380f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| fn transform_resource_name(resource_name: &str) -> String { | ||
| resource_name | ||
| .chars() | ||
| .map(|c| match c { | ||
| '.' | '/' => '_', | ||
| other => other.to_ascii_uppercase(), | ||
| }) | ||
| .collect() | ||
| } | ||
|
|
||
| pub fn to_usb_resource_env_var(resource_name: &str) -> String { | ||
| format!( | ||
| "{}_{}", | ||
| super::USB_RESOURCE_PREFIX, | ||
| transform_resource_name(resource_name) | ||
| ) | ||
| } | ||
|
|
||
| pub fn to_pci_resource_env_var(resource_name: &str) -> String { | ||
| format!( | ||
| "{}_{}", | ||
| super::PCI_RESOURCE_PREFIX, | ||
| transform_resource_name(resource_name) | ||
| ) | ||
| } | ||
|
|
||
| pub fn extract_usb_address(devnode: &str) -> Option<(String, String)> { | ||
| if !devnode.starts_with("/dev/bus/usb/") { | ||
| return None; | ||
| } | ||
|
|
||
| let parts: Vec<&str> = devnode.split('/').collect(); | ||
| if parts.len() >= 2 { | ||
| let bus = parts[parts.len() - 2]; | ||
| let device = parts[parts.len() - 1]; | ||
|
|
||
| if let (Ok(bus_num), Ok(dev_num)) = (bus.parse::<u32>(), device.parse::<u32>()) { | ||
| return Some((bus_num.to_string(), dev_num.to_string())); | ||
| } | ||
| } | ||
|
|
||
| None | ||
| } | ||
|
|
||
| fn is_pci_address(s: &str) -> bool { | ||
| let parts: Vec<&str> = s.splitn(3, ':').collect(); | ||
| if parts.len() != 3 { | ||
| return false; | ||
| } | ||
| let slot_func: Vec<&str> = parts[2].splitn(2, '.').collect(); | ||
| if slot_func.len() != 2 { | ||
| return false; | ||
| } | ||
| [parts[0], parts[1], slot_func[0], slot_func[1]] | ||
| .iter() | ||
| .all(|seg| !seg.is_empty() && seg.chars().all(|c| c.is_ascii_hexdigit())) | ||
| } | ||
|
|
||
| pub fn extract_pci_address(sysfs_path: &str) -> Option<String> { | ||
| sysfs_path | ||
| .split('/') | ||
| .filter(|s| is_pci_address(s)) | ||
| .next_back() | ||
| .map(|s| s.to_string()) | ||
| } | ||
|
|
||
| pub fn read_iommu_group(devpath: &str) -> Option<String> { | ||
| let full_path = format!("/sys{devpath}"); | ||
| let iommu_link = std::path::Path::new(&full_path).join("iommu_group"); | ||
| std::fs::read_link(&iommu_link).ok().and_then(|target| { | ||
| target | ||
| .file_name() | ||
| .and_then(|n| n.to_str()) | ||
| .map(|s| s.to_string()) | ||
| }) | ||
|
Comment on lines
+67
to
+75
|
||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_to_usb_resource_env_var() { | ||
| assert_eq!( | ||
| to_usb_resource_env_var("akri.sh/udev-usb-keyboard"), | ||
| "USB_RESOURCE_AKRI_SH_UDEV-USB-KEYBOARD" | ||
| ); | ||
| assert_eq!( | ||
| to_usb_resource_env_var("akri.sh/udev-usb-mouse"), | ||
| "USB_RESOURCE_AKRI_SH_UDEV-USB-MOUSE" | ||
| ); | ||
| assert_eq!( | ||
| to_usb_resource_env_var("example.com/my-device"), | ||
| "USB_RESOURCE_EXAMPLE_COM_MY-DEVICE" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_to_pci_resource_env_var() { | ||
| assert_eq!( | ||
| to_pci_resource_env_var("example.com/my-gpu"), | ||
| "PCI_RESOURCE_EXAMPLE_COM_MY-GPU" | ||
| ); | ||
| assert_eq!( | ||
| to_pci_resource_env_var("akri.sh/udev-gpu-t400e"), | ||
| "PCI_RESOURCE_AKRI_SH_UDEV-GPU-T400E" | ||
| ); | ||
| assert_eq!( | ||
| to_pci_resource_env_var("vendor.io/device"), | ||
| "PCI_RESOURCE_VENDOR_IO_DEVICE" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_extract_pci_address() { | ||
| assert_eq!( | ||
| extract_pci_address("/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0"), | ||
| Some("0000:01:00.0".to_string()) | ||
| ); | ||
| assert_eq!( | ||
| extract_pci_address("/sys/devices/pci0000:00/0000:03:00.0"), | ||
| Some("0000:03:00.0".to_string()) | ||
| ); | ||
| assert_eq!(extract_pci_address("/dev/bus/usb/001/010"), None); | ||
| assert_eq!(extract_pci_address("/dev/video0"), None); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_extract_usb_address_valid() { | ||
| assert_eq!( | ||
| extract_usb_address("/dev/bus/usb/001/010"), | ||
| Some(("1".to_string(), "10".to_string())) | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| extract_usb_address("/dev/bus/usb/002/005"), | ||
| Some(("2".to_string(), "5".to_string())) | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| extract_usb_address("/dev/bus/usb/003/127"), | ||
| Some(("3".to_string(), "127".to_string())) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_extract_usb_address_invalid() { | ||
| assert_eq!(extract_usb_address("/dev/video0"), None); | ||
| assert_eq!(extract_usb_address("/dev/sda"), None); | ||
| assert_eq!(extract_usb_address("/dev/ttyUSB0"), None); | ||
|
|
||
| assert_eq!(extract_usb_address("/dev/bus/usb/"), None); | ||
| assert_eq!(extract_usb_address("/dev/bus/usb/abc/def"), None); | ||
|
|
||
| assert_eq!(extract_usb_address(""), None); | ||
| assert_eq!(extract_usb_address("/"), None); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_extract_usb_address_edge_cases() { | ||
| assert_eq!( | ||
| extract_usb_address("/dev/bus/usb/1/5"), | ||
| Some(("1".to_string(), "5".to_string())) | ||
| ); | ||
|
|
||
| assert_eq!( | ||
| extract_usb_address("/dev/bus/usb/001/001"), | ||
| Some(("1".to_string(), "1".to_string())) | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -87,6 +87,19 @@ impl DiscoveryHandler for DiscoveryHandlerImpl { | |||||||||||||||||
| let discovery_handler_config: UdevDiscoveryDetails = | ||||||||||||||||||
| deserialize_discovery_details(&discover_request.discovery_details) | ||||||||||||||||||
| .map_err(|e| tonic::Status::new(tonic::Code::InvalidArgument, format!("{e}")))?; | ||||||||||||||||||
| let device_plugin_resource_name: Option<String> = discover_request | ||||||||||||||||||
| .discovery_properties | ||||||||||||||||||
| .get(super::DEVICE_PLUGIN_RESOURCE_PROPERTY_KEY) | ||||||||||||||||||
| .and_then(|b| b.vec.as_ref()) | ||||||||||||||||||
| .and_then(|v| std::str::from_utf8(v).ok()) | ||||||||||||||||||
| .map(|s| s.trim().to_string()); | ||||||||||||||||||
| let vfio_passthrough: bool = discover_request | ||||||||||||||||||
| .discovery_properties | ||||||||||||||||||
| .get(super::VFIO_PASSTHROUGH_PROPERTY_KEY) | ||||||||||||||||||
| .and_then(|b| b.vec.as_ref()) | ||||||||||||||||||
| .and_then(|v| std::str::from_utf8(v).ok()) | ||||||||||||||||||
| .map(|s| s.trim().eq_ignore_ascii_case("true")) | ||||||||||||||||||
| .unwrap_or(false); | ||||||||||||||||||
| let mut previously_discovered_devices: Vec<Device> = Vec::new(); | ||||||||||||||||||
| tokio::spawn(async move { | ||||||||||||||||||
| let udev_rules = discovery_handler_config.udev_rules.clone(); | ||||||||||||||||||
|
|
@@ -130,6 +143,16 @@ impl DiscoveryHandler for DiscoveryHandlerImpl { | |||||||||||||||||
| super::UDEV_DEVNODE_LABEL_ID.to_string() + &property_suffix, | ||||||||||||||||||
| devnode.clone(), | ||||||||||||||||||
| ); | ||||||||||||||||||
|
|
||||||||||||||||||
| if let Some(ref resource_name) = device_plugin_resource_name { | ||||||||||||||||||
| if let Some((bus, device)) = super::device_utils::extract_usb_address(&devnode) { | ||||||||||||||||||
| let env_var = super::device_utils::to_usb_resource_env_var(resource_name); | ||||||||||||||||||
| let value = format!("{bus}:{device}"); | ||||||||||||||||||
| trace!("discover - USB resource: {env_var}={value} path={devnode}"); | ||||||||||||||||||
| properties.insert(env_var + &property_suffix, value); | ||||||||||||||||||
|
||||||||||||||||||
| properties.insert(env_var + &property_suffix, value); | |
| if let Some(existing_value) = properties.get(&env_var) { | |
| trace!( | |
| "discover - USB resource already set: {env_var}={existing_value}, ignoring additional value {value} path={devnode}" | |
| ); | |
| } else { | |
| properties.insert(env_var, value); | |
| } |
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.
The concern is technically valid in the abstract, but USB passthrough always uses groupRecursive: false (the default). In that case property_suffix is an empty string, so the key is exactly USB_RESOURCE_ as KubeVirt expects. The groupRecursive: true mode is intended for multi-node device groups (e.g. a camera with multiple video nodes), which is a different use case and would not apply here.
Uh oh!
There was an error while loading. Please reload this page.