mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-05 00:15:44 +00:00
Merge pull request #28712 from elezar/info-cdi-devices
info: report configured and discovered CDI devices
This commit is contained in:
commit
cefaa0ee44
5 changed files with 131 additions and 56 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
@ -72,6 +74,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"`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -130,6 +131,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 {
|
||||
|
|
@ -177,6 +179,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{}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -47,22 +47,16 @@ var _ = Describe("Podman Info", func() {
|
|||
})
|
||||
|
||||
It("podman info --format GO template", func() {
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Store.GraphRoot}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
podmanTest.PodmanExitCleanly("info", "--format", "{{.Store.GraphRoot}}")
|
||||
})
|
||||
|
||||
It("podman info --format GO template", func() {
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Registries}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Registries}}")
|
||||
Expect(session.OutputToString()).To(ContainSubstring("registry"))
|
||||
})
|
||||
|
||||
It("podman info --format GO template plugins", func() {
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Plugins}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Plugins}}")
|
||||
Expect(session.OutputToString()).To(ContainSubstring("local"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("journald"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("bridge"))
|
||||
|
|
@ -72,10 +66,7 @@ var _ = Describe("Podman Info", func() {
|
|||
SkipIfNotRootless("test of rootless_storage_path is only meaningful as rootless")
|
||||
SkipIfRemote("Only tests storage on local client")
|
||||
configPath := filepath.Join(podmanTest.TempDir, ".config", "containers", "storage.conf")
|
||||
os.Setenv("CONTAINERS_STORAGE_CONF", configPath)
|
||||
defer func() {
|
||||
os.Unsetenv("CONTAINERS_STORAGE_CONF")
|
||||
}()
|
||||
GinkgoT().Setenv("CONTAINERS_STORAGE_CONF", configPath)
|
||||
err := os.RemoveAll(filepath.Dir(configPath))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
|
|
@ -104,9 +95,7 @@ var _ = Describe("Podman Info", func() {
|
|||
})
|
||||
|
||||
It("check RemoteSocket ", func() {
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.RemoteSocket.Path}}")
|
||||
switch podmanTest.RemoteSocketScheme {
|
||||
case "unix":
|
||||
Expect(session.OutputToString()).To(MatchRegexp("/run/.*podman.*sock"))
|
||||
|
|
@ -114,9 +103,7 @@ var _ = Describe("Podman Info", func() {
|
|||
Expect(session.OutputToString()).To(MatchRegexp("tcp://127.0.0.1:.*"))
|
||||
}
|
||||
|
||||
session = podmanTest.Podman([]string{"info", "--format", "{{.Host.ServiceIsRemote}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
session = podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.ServiceIsRemote}}")
|
||||
if podmanTest.RemoteTest {
|
||||
Expect(session.OutputToString()).To(Equal("true"))
|
||||
} else {
|
||||
|
|
@ -124,18 +111,14 @@ var _ = Describe("Podman Info", func() {
|
|||
}
|
||||
|
||||
if IsRemote() {
|
||||
session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
session = podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.RemoteSocket.Exists}}")
|
||||
Expect(session.OutputToString()).To(Equal("true"))
|
||||
}
|
||||
})
|
||||
|
||||
It("Podman info must contain cgroupControllers with RelevantControllers", func() {
|
||||
SkipIfRootless("Hard to tell which controllers are going to be enabled for rootless")
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.CgroupControllers}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.CgroupControllers}}")
|
||||
Expect(session.OutputToString()).To(ContainSubstring("memory"))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("pids"))
|
||||
})
|
||||
|
|
@ -149,28 +132,22 @@ var _ = Describe("Podman Info", func() {
|
|||
}
|
||||
Fail("CIRRUS_CI is set, but CI_DESIRED_RUNTIME is not! See #14912")
|
||||
}
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.OCIRuntime.Name}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.OCIRuntime.Name}}")
|
||||
Expect(session.OutputToString()).To(Equal(want))
|
||||
})
|
||||
|
||||
It("Podman info: check desired network backend", func() {
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.NetworkBackend}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.NetworkBackend}}")
|
||||
Expect(session.OutputToString()).To(Equal("netavark"))
|
||||
|
||||
session = podmanTest.Podman([]string{"info", "--format", "{{.Host.NetworkBackendInfo.Backend}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session = podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.NetworkBackendInfo.Backend}}")
|
||||
Expect(session.OutputToString()).To(Equal("netavark"))
|
||||
})
|
||||
|
||||
It("Podman info: check default network from configuration", func() {
|
||||
configPath := filepath.Join(podmanTest.TempDir, "containers.conf")
|
||||
|
||||
os.Setenv("CONTAINERS_CONF_OVERRIDE", configPath)
|
||||
GinkgoT().Setenv("CONTAINERS_CONF_OVERRIDE", configPath)
|
||||
|
||||
customNetName := "my-custom-test-network"
|
||||
configContent := fmt.Sprintf("[network]\ndefault_network=%q\n", customNetName)
|
||||
|
|
@ -179,12 +156,71 @@ var _ = Describe("Podman Info", func() {
|
|||
Expect(err).ToNot(HaveOccurred())
|
||||
podmanTest.RestartRemoteService()
|
||||
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Host.NetworkBackendInfo.DefaultNetwork}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.NetworkBackendInfo.DefaultNetwork}}")
|
||||
Expect(session.OutputToString()).To(Equal(customNetName))
|
||||
})
|
||||
|
||||
It("Podman info: check CDI spec dirs and devices from configuration", func() {
|
||||
cdiDir := filepath.Join(podmanTest.TempDir, "cdi")
|
||||
Expect(os.MkdirAll(cdiDir, os.ModePerm)).To(Succeed())
|
||||
|
||||
cdiSpec := []byte(`{
|
||||
"cdiVersion": "0.3.0",
|
||||
"kind": "vendor.com/device",
|
||||
"devices": [
|
||||
{
|
||||
"name": "myKmsg",
|
||||
"containerEdits": {
|
||||
"env": ["PODMAN_CDI_INFO_TEST=1"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
Expect(os.WriteFile(filepath.Join(cdiDir, "device.json"), cdiSpec, os.ModePerm)).To(Succeed())
|
||||
|
||||
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())
|
||||
|
||||
GinkgoT().Setenv("CONTAINERS_CONF_OVERRIDE", configPath)
|
||||
podmanTest.RestartRemoteService()
|
||||
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Host.CDISpecDirs}} {{.Host.DiscoveredDevices}}")
|
||||
|
||||
Expect(session.OutputToString()).To(ContainSubstring(cdiDir))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("vendor.com/device=myKmsg"))
|
||||
})
|
||||
|
||||
It("Podman info: check CDI spec dirs and devices with --cdi-spec-dir", func() {
|
||||
SkipIfRemote("The --cdi-spec-dir flag is not supported for remote")
|
||||
|
||||
cdiDir := filepath.Join(podmanTest.TempDir, "cdi")
|
||||
Expect(os.MkdirAll(cdiDir, os.ModePerm)).To(Succeed())
|
||||
|
||||
cdiSpec := []byte(`{
|
||||
"cdiVersion": "0.3.0",
|
||||
"kind": "vendor.com/device",
|
||||
"devices": [
|
||||
{
|
||||
"name": "myKmsg",
|
||||
"containerEdits": {
|
||||
"env": ["PODMAN_CDI_INFO_TEST=1"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
Expect(os.WriteFile(filepath.Join(cdiDir, "device.json"), cdiSpec, os.ModePerm)).To(Succeed())
|
||||
|
||||
session := podmanTest.PodmanExitCleanly("--cdi-spec-dir", cdiDir, "info", "--format", "{{.Host.CDISpecDirs}} {{.Host.DiscoveredDevices}}")
|
||||
Expect(session.OutputToString()).To(ContainSubstring(cdiDir))
|
||||
Expect(session.OutputToString()).To(ContainSubstring("vendor.com/device=myKmsg"))
|
||||
|
||||
session = podmanTest.PodmanExitCleanly("--cdi-spec-dir", cdiDir, "system", "info", "--format", "{{.Host.CDISpecDirs}} {{.Host.DiscoveredDevices}}")
|
||||
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")
|
||||
|
|
@ -194,9 +230,7 @@ var _ = Describe("Podman Info", func() {
|
|||
}
|
||||
Fail("CIRRUS_CI is set, but CI_DESIRED_STORAGE is not! See #20161")
|
||||
}
|
||||
session := podmanTest.Podman([]string{"info", "--format", "{{.Store.GraphDriverName}}"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session := podmanTest.PodmanExitCleanly("info", "--format", "{{.Store.GraphDriverName}}")
|
||||
Expect(session.OutputToString()).To(Equal(want), ".Store.GraphDriverName from podman info")
|
||||
|
||||
// Confirm desired setting of composefs
|
||||
|
|
@ -205,9 +239,7 @@ var _ = Describe("Podman Info", func() {
|
|||
if os.Getenv("CI_DESIRED_COMPOSEFS") != "" {
|
||||
expect = "true"
|
||||
}
|
||||
session = podmanTest.Podman([]string{"info", "--format", `{{index .Store.GraphOptions "overlay.use_composefs"}}`})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).To(ExitCleanly())
|
||||
session = podmanTest.PodmanExitCleanly("info", "--format", `{{index .Store.GraphOptions "overlay.use_composefs"}}`)
|
||||
Expect(session.OutputToString()).To(Equal(expect), ".Store.GraphOptions -> overlay.use_composefs")
|
||||
}
|
||||
})
|
||||
|
|
@ -216,19 +248,13 @@ var _ = Describe("Podman Info", func() {
|
|||
// This should not run on architectures and OSes that use the file locks backend.
|
||||
// Which, for now, is Linux + RISCV and FreeBSD, neither of which are in CI - so
|
||||
// no skips.
|
||||
info1 := podmanTest.Podman([]string{"info", "--format", "{{ .Host.FreeLocks }}"})
|
||||
info1.WaitWithDefaultTimeout()
|
||||
Expect(info1).To(ExitCleanly())
|
||||
info1 := podmanTest.PodmanExitCleanly("info", "--format", "{{ .Host.FreeLocks }}")
|
||||
free1, err := strconv.Atoi(info1.OutputToString())
|
||||
Expect(err).To(Not(HaveOccurred()))
|
||||
|
||||
ctr := podmanTest.Podman([]string{"create", ALPINE, "top"})
|
||||
ctr.WaitWithDefaultTimeout()
|
||||
Expect(ctr).To(ExitCleanly())
|
||||
podmanTest.PodmanExitCleanly("create", ALPINE, "top")
|
||||
|
||||
info2 := podmanTest.Podman([]string{"info", "--format", "{{ .Host.FreeLocks }}"})
|
||||
info2.WaitWithDefaultTimeout()
|
||||
Expect(info2).To(ExitCleanly())
|
||||
info2 := podmanTest.PodmanExitCleanly("info", "--format", "{{ .Host.FreeLocks }}")
|
||||
free2, err := strconv.Atoi(info2.OutputToString())
|
||||
Expect(err).To(Not(HaveOccurred()))
|
||||
|
||||
|
|
@ -251,9 +277,7 @@ var _ = Describe("Podman Info", func() {
|
|||
})
|
||||
|
||||
It("Podman info: check client information", func() {
|
||||
info := podmanTest.Podman([]string{"info", "--format", "{{ .Client }}"})
|
||||
info.WaitWithDefaultTimeout()
|
||||
Expect(info).To(ExitCleanly())
|
||||
info := podmanTest.PodmanExitCleanly("info", "--format", "{{ .Client }}")
|
||||
// client info should only appear when using the remote client
|
||||
if IsRemote() {
|
||||
Expect(info.OutputToString()).ToNot(Equal("<nil>"))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue