From 78c437ba136ba4056cfe0ff49061f95b2e69c92e Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Tue, 12 May 2026 14:20:19 +0200 Subject: [PATCH] info: report configured and discovered CDI devices Add CDI information to podman info and podman system info. The host info now includes the configured CDI spec directories and the currently discovered CDI devices. The devices are resolved when the info endpoint is called and there is no need to refresh these in the background. Also map the same data into the Docker-compatible /info response as CDISpecDirs and DiscoveredDevices. Signed-off-by: Evan Lezar --- docs/source/markdown/podman-info.1.md | 1 + libpod/define/info.go | 8 ++++++ libpod/info.go | 29 ++++++++++++++++++++++ pkg/api/handlers/compat/info.go | 13 ++++++++++ test/e2e/info_test.go | 35 +++++++++++++++++++++++++++ test/system/005-info.bats | 31 ++++++++++++++++++++++++ 6 files changed, 117 insertions(+) diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md index f9a784b2c63..da6cb7da812 100644 --- a/docs/source/markdown/podman-info.1.md +++ b/docs/source/markdown/podman-info.1.md @@ -11,6 +11,7 @@ podman\-info - Display Podman related system information ## DESCRIPTION Displays information pertinent to the host, current storage stats, configured container registries, and build of podman. +Host information includes configured CDI spec directories and resolved CDI devices when present. ## OPTIONS diff --git a/libpod/define/info.go b/libpod/define/info.go index e03c39289f6..b12681c0769 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -33,6 +33,8 @@ type HostInfo struct { CgroupManager string `json:"cgroupManager"` CgroupsVersion string `json:"cgroupVersion"` CgroupControllers []string `json:"cgroupControllers"` + CDISpecDirs []string `json:"cdiSpecDirs"` + DiscoveredDevices []DeviceInfo `json:"discoveredDevices,omitempty"` Conmon *ConmonInfo `json:"conmon"` CPUs int `json:"cpus"` CPUUtilization *CPUUsage `json:"cpuUtilization"` @@ -69,6 +71,12 @@ type HostInfo struct { EmulatedArchitectures []string `json:"emulatedArchitectures,omitempty"` } +// DeviceInfo describes a device discovered by a device source. +type DeviceInfo struct { + Source string `json:"source"` + ID string `json:"id"` +} + // RemoteSocket describes information about the API socket type RemoteSocket struct { Path string `json:"path,omitempty"` diff --git a/libpod/info.go b/libpod/info.go index 8140fb12e08..01e3f65e461 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -23,6 +23,7 @@ import ( "go.podman.io/podman/v6/libpod/define" "go.podman.io/podman/v6/libpod/linkmode" "go.podman.io/storage/pkg/system" + "tags.cncf.io/container-device-interface/pkg/cdi" ) // Info returns the store and host information @@ -129,6 +130,7 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) { SwapFree: mi.SwapFree, SwapTotal: mi.SwapTotal, } + info.CDISpecDirs, info.DiscoveredDevices = r.cdiInfo() platform := parse.DefaultPlatform() pArr := strings.Split(platform, "/") if len(pArr) == 3 { @@ -176,6 +178,33 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) { return &info, nil } +func (r *Runtime) cdiInfo() ([]string, []define.DeviceInfo) { + registry, err := cdi.NewCache( + cdi.WithSpecDirs(r.config.Engine.CdiSpecDirs.Get()...), + cdi.WithAutoRefresh(false), + ) + if err != nil { + logrus.Debugf("Creating CDI registry for info: %v", err) + return r.config.Engine.CdiSpecDirs.Get(), nil + } + if err := registry.Refresh(); err != nil { + logrus.Debugf("The following error was triggered when refreshing the CDI registry for info: %v", err) + } + + return registry.GetSpecDirectories(), cdiDeviceInfo(registry.ListDevices()) +} + +func cdiDeviceInfo(deviceNames []string) []define.DeviceInfo { + devices := make([]define.DeviceInfo, 0, len(deviceNames)) + for _, device := range deviceNames { + devices = append(devices, define.DeviceInfo{ + Source: "cdi", + ID: device, + }) + } + return devices +} + func (r *Runtime) getContainerStoreInfo() (define.ContainerStore, error) { var paused, running, stopped int cs := define.ContainerStore{} diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index 5ef43badd5e..44bf6a1341b 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -62,6 +62,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { CPUSet: sysInfo.Cpuset, CPUShares: sysInfo.CPUShares, CgroupDriver: configInfo.Engine.CgroupManager, + CDISpecDirs: infoData.Host.CDISpecDirs, ContainerdCommit: dockerSystem.Commit{}, Containers: infoData.Store.ContainerStore.Number, ContainersPaused: stateInfo[define.ContainerStatePaused], @@ -70,6 +71,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { Debug: log.IsLevelEnabled(log.DebugLevel), DefaultAddressPools: getDefaultAddressPools(configInfo), DefaultRuntime: configInfo.Engine.OCIRuntime, + DiscoveredDevices: getDiscoveredDevices(infoData.Host.DiscoveredDevices), DockerRootDir: infoData.Store.GraphRoot, Driver: infoData.Store.GraphDriverName, DriverStatus: getGraphStatus(infoData.Store.GraphStatus), @@ -132,6 +134,17 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, info) } +func getDiscoveredDevices(discoveredDevices []define.DeviceInfo) []dockerSystem.DeviceInfo { + devices := make([]dockerSystem.DeviceInfo, 0, len(discoveredDevices)) + for _, device := range discoveredDevices { + devices = append(devices, dockerSystem.DeviceInfo{ + Source: device.Source, + ID: device.ID, + }) + } + return devices +} + func getServiceConfig(runtime *libpod.Runtime) *registry.ServiceConfig { var indexConfs map[string]*registry.IndexInfo diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go index f0f42555089..e5ee7dc3b9b 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -185,6 +185,41 @@ var _ = Describe("Podman Info", func() { Expect(session.OutputToString()).To(Equal(customNetName)) }) + It("Podman info: check CDI spec dirs and devices from configuration", func() { + cdiDir := filepath.Join(podmanTest.TempDir, "cdi") + err := os.MkdirAll(cdiDir, os.ModePerm) + Expect(err).ToNot(HaveOccurred()) + + cdiSpec := []byte(`{ + "cdiVersion": "0.3.0", + "kind": "vendor.com/device", + "devices": [ + { + "name": "myKmsg", + "containerEdits": { + "env": ["PODMAN_CDI_INFO_TEST=1"] + } + } + ] +}`) + err = os.WriteFile(filepath.Join(cdiDir, "device.json"), cdiSpec, os.ModePerm) + Expect(err).ToNot(HaveOccurred()) + + configPath := filepath.Join(podmanTest.TempDir, "containers.conf") + configContent := fmt.Sprintf("[engine]\ncdi_spec_dirs = [%q]\n", cdiDir) + err = os.WriteFile(configPath, []byte(configContent), os.ModePerm) + Expect(err).ToNot(HaveOccurred()) + + os.Setenv("CONTAINERS_CONF_OVERRIDE", configPath) + podmanTest.RestartRemoteService() + + session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CDISpecDirs}} {{.Host.DiscoveredDevices}}"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitCleanly()) + Expect(session.OutputToString()).To(ContainSubstring(cdiDir)) + Expect(session.OutputToString()).To(ContainSubstring("vendor.com/device=myKmsg")) + }) + It("Podman info: check desired storage driver", func() { // defined in .cirrus.yml want := os.Getenv("CI_DESIRED_STORAGE") diff --git a/test/system/005-info.bats b/test/system/005-info.bats index e56dcfce2c3..7a8a37e3148 100644 --- a/test/system/005-info.bats +++ b/test/system/005-info.bats @@ -62,6 +62,37 @@ store.imageStore.number | 1 done < <(parse_table "$tests") } +@test "podman info - CDI spec dirs and devices" { + skip_if_remote "--cdi-spec-dir flag is not supported for remote" + + cdi_dir=$PODMAN_TMPDIR/cdi + mkdir -p "$cdi_dir" + cat >"$cdi_dir/device.json" <