Update to new cadvisor v0.48.1

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
This commit is contained in:
Davanum Srinivas
2023-10-31 07:51:09 -04:00
parent 0294521985
commit 8b9fc325e2
36 changed files with 425 additions and 331 deletions

View File

@@ -19,7 +19,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"path"
@@ -147,7 +147,7 @@ func (c *Client) httpGetResponse(postData interface{}, urlPath, infoName string)
return nil, fmt.Errorf("received empty response for %q from %q", infoName, urlPath)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
err = fmt.Errorf("unable to read all %q from %q: %v", infoName, urlPath, err)
return nil, err

View File

@@ -17,7 +17,7 @@ package collector
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"regexp"
"strconv"
@@ -145,7 +145,7 @@ func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (t
defer response.Body.Close()
pageContent, err := ioutil.ReadAll(response.Body)
pageContent, err := io.ReadAll(response.Body)
if err != nil {
return nextCollectionTime, nil, err
}

View File

@@ -21,7 +21,6 @@ package common
import (
"encoding/json"
"flag"
"io/ioutil"
"os"
)
@@ -48,7 +47,7 @@ type networkInterface struct {
}
func GetContainerHintsFromFile(containerHintsFile string) (ContainerHints, error) {
dat, err := ioutil.ReadFile(containerHintsFile)
dat, err := os.ReadFile(containerHintsFile)
if os.IsNotExist(err) {
return ContainerHints{}, nil
}

View File

@@ -16,7 +16,6 @@ package common
import (
"fmt"
"io/ioutil"
"math"
"os"
"path"
@@ -76,7 +75,11 @@ func getSpecInternal(cgroupPaths map[string]string, machineInfoFactory info.Mach
dir, err := os.Stat(cgroupPathDir)
if err == nil && dir.ModTime().Before(lowestTime) {
lowestTime = dir.ModTime()
} else if os.IsNotExist(err) {
// Directory does not exist, skip checking for files within.
continue
}
// The modified time of the cgroup directory sometimes changes whenever a subcontainer is created.
// eg. /docker will have creation time matching the creation of latest docker container.
// Use clone_children/events as a workaround as it isn't usually modified. It is only likely changed
@@ -235,7 +238,7 @@ func readString(dirpath string, file string) string {
cgroupFile := path.Join(dirpath, file)
// Read
out, err := ioutil.ReadFile(cgroupFile)
out, err := os.ReadFile(cgroupFile)
if err != nil {
// Ignore non-existent files
if !os.IsNotExist(err) {
@@ -437,3 +440,20 @@ func (m deviceIdentifierMap) Find(major, minor uint64, namer DeviceNamer) string
m[d] = s
return s
}
// RemoveNetMetrics is used to remove any network metrics from the given MetricSet.
// It returns the original set as is if remove is false, or if there are no metrics
// to remove.
func RemoveNetMetrics(metrics container.MetricSet, remove bool) container.MetricSet {
if !remove {
return metrics
}
// Check if there is anything we can remove, to avoid useless copying.
if !metrics.HasAny(container.AllNetworkMetrics) {
return metrics
}
// A copy of all metrics except for network ones.
return metrics.Difference(container.AllNetworkMetrics)
}

View File

@@ -35,6 +35,7 @@ const (
ContainerTypeCrio
ContainerTypeContainerd
ContainerTypeMesos
ContainerTypePodman
)
// Interface for container operation handlers.

View File

@@ -59,6 +59,7 @@ const (
maxBackoffDelay = 3 * time.Second
baseBackoffDelay = 100 * time.Millisecond
connectionTimeout = 2 * time.Second
maxMsgSize = 16 * 1024 * 1024 // 16MB
)
// Client creates a containerd client
@@ -82,6 +83,7 @@ func Client(address, namespace string) (ContainerdClient, error) {
grpc.WithContextDialer(dialer.ContextDialer),
grpc.WithBlock(),
grpc.WithConnectParams(connParams),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)),
}
unary, stream := newNSInterceptors(namespace)
gopts = append(gopts,

View File

@@ -126,7 +126,14 @@ func newContainerdContainerHandler(
Aliases: []string{id, name},
}
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
// Containers that don't have their own network -- this includes
// containers running in Kubernetes pods that use the network of the
// infrastructure container -- does not need their stats to be
// reported. This stops metrics being reported multiple times for each
// container in a pod.
metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] != "sandbox")
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), metrics)
handler := &containerdContainerHandler{
machineInfoFactory: machineInfoFactory,
@@ -134,7 +141,7 @@ func newContainerdContainerHandler(
fsInfo: fsInfo,
envs: make(map[string]string),
labels: cntr.Labels,
includedMetrics: includedMetrics,
includedMetrics: metrics,
reference: containerReference,
libcontainerHandler: libcontainerHandler,
}
@@ -164,22 +171,12 @@ func (h *containerdContainerHandler) ContainerReference() (info.ContainerReferen
return h.reference, nil
}
func (h *containerdContainerHandler) needNet() bool {
// Since containerd does not handle networking ideally we need to return based
// on includedMetrics list. Here the assumption is the presence of cri-containerd
// label
if h.includedMetrics.Has(container.NetworkUsageMetrics) {
//TODO change it to exported cri-containerd constants
return h.labels["io.cri-containerd.kind"] == "sandbox"
}
return false
}
func (h *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
// TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
// to false. Revisit when we support disk usage stats for containerd
hasFilesystem := false
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
spec.Labels = h.labels
spec.Envs = h.envs
spec.Image = h.image
@@ -204,13 +201,6 @@ func (h *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
if err != nil {
return stats, err
}
// Clean up stats for containers that don't have their own network - this
// includes containers running in Kubernetes pods that use the network of the
// infrastructure container. This stops metrics being reported multiple times
// for each container in a pod.
if !h.needNet() {
stats.Network = info.NetworkStats{}
}
// Get filesystem stats.
err = h.getFsStats(stats)

View File

@@ -17,8 +17,9 @@ package crio
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"io"
"net"
"net/http"
"sync"
@@ -26,6 +27,8 @@ import (
"time"
)
var crioClientTimeout = flag.Duration("crio_client_timeout", time.Duration(0), "CRI-O client timeout. Default is no timeout.")
const (
CrioSocket = "/var/run/crio/crio.sock"
maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
@@ -89,6 +92,7 @@ func Client() (CrioClient, error) {
theClient = &crioClientImpl{
client: &http.Client{
Transport: tr,
Timeout: *crioClientTimeout,
},
}
})
@@ -142,7 +146,7 @@ func (c *crioClientImpl) ContainerInfo(id string) (*ContainerInfo, error) {
// golang's http.Do doesn't return an error if non 200 response code is returned
// handle this case here, rather than failing to decode the body
if resp.StatusCode != http.StatusOK {
respBody, err := ioutil.ReadAll(resp.Body)
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Error finding container %s: Status %d", id, resp.StatusCode)
}

View File

@@ -148,7 +148,15 @@ func newCrioContainerHandler(
Namespace: CrioNamespace,
}
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, includedMetrics)
// Find out if we need network metrics reported for this container.
// Containers that don't have their own network -- this includes
// containers running in Kubernetes pods that use the network of the
// infrastructure container -- does not need their stats to be
// reported. This stops metrics being reported multiple times for each
// container in a pod.
metrics := common.RemoveNetMetrics(includedMetrics, cInfo.Labels["io.kubernetes.container.name"] != "POD")
libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, metrics)
// TODO: extract object mother method
handler := &crioContainerHandler{
@@ -161,7 +169,7 @@ func newCrioContainerHandler(
rootfsStorageDir: rootfsStorageDir,
envs: make(map[string]string),
labels: cInfo.Labels,
includedMetrics: includedMetrics,
includedMetrics: metrics,
reference: containerReference,
libcontainerHandler: libcontainerHandler,
cgroupManager: cgroupManager,
@@ -210,16 +218,10 @@ func (h *crioContainerHandler) ContainerReference() (info.ContainerReference, er
return h.reference, nil
}
func (h *crioContainerHandler) needNet() bool {
if h.includedMetrics.Has(container.NetworkUsageMetrics) {
return h.labels["io.kubernetes.container.name"] == "POD"
}
return false
}
func (h *crioContainerHandler) GetSpec() (info.ContainerSpec, error) {
hasFilesystem := h.includedMetrics.Has(container.DiskUsageMetrics)
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics)
spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem)
spec.Labels = h.labels
spec.Envs = h.envs
@@ -306,13 +308,7 @@ func (h *crioContainerHandler) GetStats() (*info.ContainerStats, error) {
return stats, err
}
if !h.needNet() {
// Clean up stats for containers that don't have their own network - this
// includes containers running in Kubernetes pods that use the network of the
// infrastructure container. This stops metrics being reported multiple times
// for each container in a pod.
stats.Network = info.NetworkStats{}
} else if len(stats.Network.Interfaces) == 0 {
if h.includedMetrics.Has(container.NetworkUsageMetrics) && len(stats.Network.Interfaces) == 0 {
// No network related information indicates that the pid of the
// container is not longer valid and we need to ask crio to
// provide the pid of another container from that pod

View File

@@ -93,6 +93,14 @@ var AllMetrics = MetricSet{
OOMMetrics: struct{}{},
}
// AllNetworkMetrics represents all network metrics that cAdvisor supports.
var AllNetworkMetrics = MetricSet{
NetworkUsageMetrics: struct{}{},
NetworkTcpUsageMetrics: struct{}{},
NetworkAdvancedTcpUsageMetrics: struct{}{},
NetworkUdpUsageMetrics: struct{}{},
}
func (mk MetricKind) String() string {
return string(mk)
}
@@ -104,6 +112,15 @@ func (ms MetricSet) Has(mk MetricKind) bool {
return exists
}
func (ms MetricSet) HasAny(ms1 MetricSet) bool {
for m := range ms1 {
if _, ok := ms[m]; ok {
return true
}
}
return false
}
func (ms MetricSet) add(mk MetricKind) {
ms[mk] = struct{}{}
}
@@ -199,7 +216,9 @@ func InitializePlugins(factory info.MachineInfoFactory, fsInfo fs.FsInfo, includ
for name, plugin := range plugins {
watcher, err := plugin.Register(factory, fsInfo, includedMetrics)
if err != nil {
klog.V(5).Infof("Registration of the %s container factory failed: %v", name, err)
klog.Infof("Registration of the %s container factory failed: %v", name, err)
} else {
klog.Infof("Registration of the %s container factory successfully", name)
}
if watcher != nil {
containerWatchers = append(containerWatchers, watcher)

View File

@@ -21,7 +21,6 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"regexp"
@@ -40,7 +39,6 @@ import (
)
var (
whitelistedUlimits = [...]string{"max_open_files"}
referencedResetInterval = flag.Uint64("referenced_reset_interval", 0,
"Reset interval for referenced bytes (container_referenced_bytes metric), number of measurement cycles after which referenced bytes are cleared, if set to 0 referenced bytes are never cleared (default: 0)")
@@ -206,54 +204,56 @@ func parseUlimit(value string) (int64, error) {
return num, nil
}
func isUlimitWhitelisted(name string) bool {
for _, whitelist := range whitelistedUlimits {
if name == whitelist {
return true
}
}
return false
}
func processLimitsFile(fileData string) []info.UlimitSpec {
const maxOpenFilesLinePrefix = "Max open files"
limits := strings.Split(fileData, "\n")
ulimits := make([]info.UlimitSpec, 0, len(limits))
for _, lim := range limits {
// Skip any headers/footers
if strings.HasPrefix(lim, "Max") {
// Line format: Max open files 16384 16384 files
fields := regexp.MustCompile(`[\s]{2,}`).Split(lim, -1)
name := strings.Replace(strings.ToLower(strings.TrimSpace(fields[0])), " ", "_", -1)
found := isUlimitWhitelisted(name)
if !found {
continue
}
soft := strings.TrimSpace(fields[1])
softNum, softErr := parseUlimit(soft)
hard := strings.TrimSpace(fields[2])
hardNum, hardErr := parseUlimit(hard)
// Omit metric if there were any parsing errors
if softErr == nil && hardErr == nil {
ulimitSpec := info.UlimitSpec{
Name: name,
SoftLimit: int64(softNum),
HardLimit: int64(hardNum),
}
ulimits = append(ulimits, ulimitSpec)
if strings.HasPrefix(lim, "Max open files") {
// Remove line prefix
ulimit, err := processMaxOpenFileLimitLine(
"max_open_files",
lim[len(maxOpenFilesLinePrefix):],
)
if err == nil {
ulimits = append(ulimits, ulimit)
}
}
}
return ulimits
}
// Any caller of processMaxOpenFileLimitLine must ensure that the name prefix is already removed from the limit line.
// with the "Max open files" prefix.
func processMaxOpenFileLimitLine(name, line string) (info.UlimitSpec, error) {
// Remove any leading whitespace
line = strings.TrimSpace(line)
// Split on whitespace
fields := strings.Fields(line)
if len(fields) != 3 {
return info.UlimitSpec{}, fmt.Errorf("unable to parse max open files line: %s", line)
}
// The first field is the soft limit, the second is the hard limit
soft, err := parseUlimit(fields[0])
if err != nil {
return info.UlimitSpec{}, err
}
hard, err := parseUlimit(fields[1])
if err != nil {
return info.UlimitSpec{}, err
}
return info.UlimitSpec{
Name: name,
SoftLimit: soft,
HardLimit: hard,
}, nil
}
func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
filePath := path.Join(rootFs, "/proc", strconv.Itoa(rootPid), "limits")
out, err := ioutil.ReadFile(filePath)
out, err := os.ReadFile(filePath)
if err != nil {
klog.V(4).Infof("error while listing directory %q to read ulimits: %v", filePath, err)
return []info.UlimitSpec{}
@@ -264,14 +264,14 @@ func processRootProcUlimits(rootFs string, rootPid int) []info.UlimitSpec {
func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.ProcessStats, error) {
var fdCount, socketCount uint64
filePath := path.Join(cgroupPath, "cgroup.procs")
out, err := ioutil.ReadFile(filePath)
out, err := os.ReadFile(filePath)
if err != nil {
return info.ProcessStats{}, fmt.Errorf("couldn't open cpu cgroup procs file %v : %v", filePath, err)
}
pids := strings.Split(string(out), "\n")
// EOL is also treated as a new line while reading "cgroup.procs" file with ioutil.ReadFile.
// EOL is also treated as a new line while reading "cgroup.procs" file with os.ReadFile.
// The last value is an empty string "". Ex: pids = ["22", "1223", ""]
// Trim the last value
if len(pids) != 0 && pids[len(pids)-1] == "" {
@@ -280,7 +280,7 @@ func processStatsFromProcs(rootFs string, cgroupPath string, rootPid int) (info.
for _, pid := range pids {
dirPath := path.Join(rootFs, "/proc", pid, "fd")
fds, err := ioutil.ReadDir(dirPath)
fds, err := os.ReadDir(dirPath)
if err != nil {
klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
continue
@@ -324,7 +324,7 @@ func (h *Handler) schedulerStatsFromProcs() (info.CpuSchedstat, error) {
return info.CpuSchedstat{}, fmt.Errorf("couldn't open scheduler statistics for process %d: %v", pid, err)
}
defer f.Close()
contents, err := ioutil.ReadAll(f)
contents, err := io.ReadAll(f)
if err != nil {
return info.CpuSchedstat{}, fmt.Errorf("couldn't read scheduler statistics for process %d: %v", pid, err)
}
@@ -392,7 +392,7 @@ func getReferencedKBytes(pids []int) (uint64, error) {
foundMatch := false
for _, pid := range pids {
smapsFilePath := fmt.Sprintf(smapsFilePathPattern, pid)
smapsContent, err := ioutil.ReadFile(smapsFilePath)
smapsContent, err := os.ReadFile(smapsFilePath)
if err != nil {
klog.V(5).Infof("Cannot read %s file, err: %s", smapsFilePath, err)
if os.IsNotExist(err) {
@@ -468,7 +468,7 @@ func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error)
return ifaceStats, nil
}
var ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
var ignoredDevicePrefixes = []string{"lo", "veth", "docker", "nerdctl"}
func isIgnoredDevice(ifName string) bool {
for _, prefix := range ignoredDevicePrefixes {
@@ -575,7 +575,7 @@ func advancedTCPStatsFromProc(rootFs string, pid int, file1, file2 string) (info
}
func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsFile string) error {
data, err := ioutil.ReadFile(advancedTCPStatsFile)
data, err := os.ReadFile(advancedTCPStatsFile)
if err != nil {
return fmt.Errorf("failure opening %s: %v", advancedTCPStatsFile, err)
}
@@ -631,7 +631,7 @@ func scanAdvancedTCPStats(advancedStats *info.TcpAdvancedStat, advancedTCPStatsF
func scanTCPStats(tcpStatsFile string) (info.TcpStat, error) {
var stats info.TcpStat
data, err := ioutil.ReadFile(tcpStatsFile)
data, err := os.ReadFile(tcpStatsFile)
if err != nil {
return stats, fmt.Errorf("failure opening %s: %v", tcpStatsFile, err)
}
@@ -802,6 +802,7 @@ func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
ret.Memory.Usage = s.MemoryStats.Usage.Usage
ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
ret.Memory.KernelUsage = s.MemoryStats.KernelUsage.Usage
if cgroups.IsCgroup2UnifiedMode() {
ret.Memory.Cache = s.MemoryStats.Stats["file"]

View File

@@ -18,13 +18,13 @@ package raw
import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
inotify "k8s.io/utils/inotify"
"github.com/google/cadvisor/container"
"github.com/google/cadvisor/container/common"
"github.com/google/cadvisor/container/libcontainer"
"github.com/google/cadvisor/watcher"
@@ -43,8 +43,8 @@ type rawContainerWatcher struct {
stopWatcher chan error
}
func NewRawContainerWatcher() (watcher.ContainerWatcher, error) {
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(nil)
func NewRawContainerWatcher(includedMetrics container.MetricSet) (watcher.ContainerWatcher, error) {
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems(includedMetrics)
if err != nil {
return nil, fmt.Errorf("failed to get cgroup subsystems: %v", err)
}
@@ -139,7 +139,7 @@ func (w *rawContainerWatcher) watchDirectory(events chan watcher.ContainerEvent,
// TODO(vmarmol): We should re-do this once we're done to ensure directories were not added in the meantime.
// Watch subdirectories as well.
entries, err := ioutil.ReadDir(dir)
entries, err := os.ReadDir(dir)
if err != nil {
return alreadyWatching, err
}

View File

@@ -21,7 +21,6 @@ package fs
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
@@ -146,7 +145,7 @@ func getFsUUIDToDeviceNameMap() (map[string]string, error) {
return make(map[string]string), nil
}
files, err := ioutil.ReadDir(dir)
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
@@ -387,6 +386,7 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
if err != nil {
return nil, err
}
nfsInfo := make(map[string]Fs, 0)
for device, partition := range i.partitions {
_, hasMount := mountSet[partition.mountpoint]
_, hasDevice := deviceSet[device]
@@ -395,7 +395,11 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
err error
fs Fs
)
switch partition.fsType {
fsType := partition.fsType
if strings.HasPrefix(partition.fsType, "nfs") {
fsType = "nfs"
}
switch fsType {
case DeviceMapper.String():
fs.Capacity, fs.Free, fs.Available, err = getDMStats(device, partition.blockSize)
klog.V(5).Infof("got devicemapper fs capacity stats: capacity: %v free: %v available: %v:", fs.Capacity, fs.Free, fs.Available)
@@ -408,6 +412,22 @@ func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error
}
// if /dev/zfs is not present default to VFS
fallthrough
case NFS.String():
devId := fmt.Sprintf("%d:%d", partition.major, partition.minor)
if v, ok := nfsInfo[devId]; ok {
fs = v
break
}
var inodes, inodesFree uint64
fs.Capacity, fs.Free, fs.Available, inodes, inodesFree, err = getVfsStats(partition.mountpoint)
if err != nil {
klog.V(4).Infof("the file system type is %s, partition mountpoint does not exist: %v, error: %v", partition.fsType, partition.mountpoint, err)
break
}
fs.Inodes = &inodes
fs.InodesFree = &inodesFree
fs.Type = VFS
nfsInfo[devId] = fs
default:
var inodes, inodesFree uint64
if utils.FileExists(partition.mountpoint) {
@@ -616,7 +636,7 @@ func GetDirUsage(dir string) (UsageInfo, error) {
rootStat, ok := rootInfo.Sys().(*syscall.Stat_t)
if !ok {
return usage, fmt.Errorf("unsuported fileinfo for getting inode usage of %q", dir)
return usage, fmt.Errorf("unsupported fileinfo for getting inode usage of %q", dir)
}
rootDevID := rootStat.Dev

View File

@@ -22,6 +22,7 @@ type Context struct {
// docker root directory.
Docker DockerContext
Crio CrioContext
Podman PodmanContext
}
type DockerContext struct {
@@ -30,6 +31,12 @@ type DockerContext struct {
DriverStatus map[string]string
}
type PodmanContext struct {
Root string
Driver string
DriverStatus map[string]string
}
type CrioContext struct {
Root string
}
@@ -50,6 +57,7 @@ const (
ZFS FsType = "zfs"
DeviceMapper FsType = "devicemapper"
VFS FsType = "vfs"
NFS FsType = "nfs"
)
type Fs struct {

View File

@@ -395,6 +395,10 @@ type MemoryStats struct {
Failcnt uint64 `json:"failcnt"`
// Size of kernel memory allocated in bytes.
// Units: Bytes.
KernelUsage uint64 `json:"kernel"`
ContainerData MemoryStatsMemoryData `json:"container_data,omitempty"`
HierarchicalData MemoryStatsMemoryData `json:"hierarchical_data,omitempty"`
}

View File

@@ -25,6 +25,7 @@ import (
const (
TypeName = "name"
TypeDocker = "docker"
TypePodman = "podman"
)
type CpuSpec struct {

View File

@@ -17,7 +17,7 @@ package machine
import (
"bytes"
"flag"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
@@ -45,7 +45,7 @@ func getInfoFromFiles(filePaths string) string {
return ""
}
for _, file := range strings.Split(filePaths, ",") {
id, err := ioutil.ReadFile(file)
id, err := os.ReadFile(file)
if err == nil {
return strings.TrimSpace(string(id))
}
@@ -60,7 +60,7 @@ func Info(sysFs sysfs.SysFs, fsInfo fs.FsInfo, inHostNamespace bool) (*info.Mach
rootFs = "/rootfs"
}
cpuinfo, err := ioutil.ReadFile(filepath.Join(rootFs, "/proc/cpuinfo"))
cpuinfo, err := os.ReadFile(filepath.Join(rootFs, "/proc/cpuinfo"))
if err != nil {
return nil, err
}

View File

@@ -17,7 +17,6 @@ package machine
import (
"fmt"
"io/ioutil"
"os"
"path"
"regexp"
@@ -46,7 +45,7 @@ var (
swapCapacityRegexp = regexp.MustCompile(`SwapTotal:\s*([0-9]+) kB`)
vendorIDRegexp = regexp.MustCompile(`vendor_id\s*:\s*(\w+)`)
cpuBusPath = "/sys/bus/cpu/devices/"
cpuAttributesPath = "/sys/devices/system/cpu/"
isMemoryController = regexp.MustCompile("mc[0-9]+")
isDimm = regexp.MustCompile("dimm[0-9]+")
machineArch = getMachineArch()
@@ -62,7 +61,7 @@ func GetCPUVendorID(procInfo []byte) string {
matches := vendorIDRegexp.FindSubmatch(procInfo)
if len(matches) != 2 {
klog.Warning("Cannot read vendor id correctly, set empty.")
klog.V(4).Info("Cannot read vendor id correctly, set empty.")
return vendorID
}
@@ -77,7 +76,7 @@ func GetPhysicalCores(procInfo []byte) int {
if numCores == 0 {
// read number of cores from /sys/bus/cpu/devices/cpu*/topology/core_id to deal with processors
// for which 'core id' is not available in /proc/cpuinfo
numCores = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUCoreID)
numCores = sysfs.GetUniqueCPUPropertyCount(cpuAttributesPath, sysfs.CPUCoreID)
}
if numCores == 0 {
klog.Errorf("Cannot read number of physical cores correctly, number of cores set to %d", numCores)
@@ -91,7 +90,7 @@ func GetSockets(procInfo []byte) int {
if numSocket == 0 {
// read number of sockets from /sys/bus/cpu/devices/cpu*/topology/physical_package_id to deal with processors
// for which 'physical id' is not available in /proc/cpuinfo
numSocket = sysfs.GetUniqueCPUPropertyCount(cpuBusPath, sysfs.CPUPhysicalPackageID)
numSocket = sysfs.GetUniqueCPUPropertyCount(cpuAttributesPath, sysfs.CPUPhysicalPackageID)
}
if numSocket == 0 {
klog.Errorf("Cannot read number of sockets correctly, number of sockets set to %d", numSocket)
@@ -103,7 +102,7 @@ func GetSockets(procInfo []byte) int {
func GetClockSpeed(procInfo []byte) (uint64, error) {
// First look through sys to find a max supported cpu frequency.
if utils.FileExists(maxFreqFile) {
val, err := ioutil.ReadFile(maxFreqFile)
val, err := os.ReadFile(maxFreqFile)
if err != nil {
return 0, err
}
@@ -136,7 +135,7 @@ func GetClockSpeed(procInfo []byte) (uint64, error) {
// GetMachineMemoryCapacity returns the machine's total memory from /proc/meminfo.
// Returns the total memory capacity as an uint64 (number of bytes).
func GetMachineMemoryCapacity() (uint64, error) {
out, err := ioutil.ReadFile("/proc/meminfo")
out, err := os.ReadFile("/proc/meminfo")
if err != nil {
return 0, err
}
@@ -156,7 +155,7 @@ func GetMachineMemoryCapacity() (uint64, error) {
// (https://github.com/torvalds/linux/blob/v5.5/drivers/edac/edac_mc.c#L198)
func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error) {
memory := map[string]*info.MemoryInfo{}
names, err := ioutil.ReadDir(edacPath)
names, err := os.ReadDir(edacPath)
// On some architectures (such as ARM) memory controller device may not exist.
// If this is the case then we ignore error and return empty slice.
_, ok := err.(*os.PathError)
@@ -170,7 +169,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
if !isMemoryController.MatchString(controller) {
continue
}
dimms, err := ioutil.ReadDir(path.Join(edacPath, controllerDir.Name()))
dimms, err := os.ReadDir(path.Join(edacPath, controllerDir.Name()))
if err != nil {
return map[string]*info.MemoryInfo{}, err
}
@@ -179,7 +178,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
if !isDimm.MatchString(dimm) {
continue
}
memType, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, memTypeFileName))
memType, err := os.ReadFile(path.Join(edacPath, controller, dimm, memTypeFileName))
if err != nil {
return map[string]*info.MemoryInfo{}, err
}
@@ -187,7 +186,7 @@ func GetMachineMemoryByType(edacPath string) (map[string]*info.MemoryInfo, error
if _, exists := memory[readableMemType]; !exists {
memory[readableMemType] = &info.MemoryInfo{}
}
size, err := ioutil.ReadFile(path.Join(edacPath, controller, dimm, sizeFileName))
size, err := os.ReadFile(path.Join(edacPath, controller, dimm, sizeFileName))
if err != nil {
return map[string]*info.MemoryInfo{}, err
}
@@ -210,7 +209,7 @@ func mbToBytes(megabytes int) int {
// GetMachineSwapCapacity returns the machine's total swap from /proc/meminfo.
// Returns the total swap capacity as an uint64 (number of bytes).
func GetMachineSwapCapacity() (uint64, error) {
out, err := ioutil.ReadFile("/proc/meminfo")
out, err := os.ReadFile("/proc/meminfo")
if err != nil {
return 0, err
}

View File

@@ -19,7 +19,6 @@ package machine
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
@@ -39,10 +38,10 @@ func getOperatingSystem() (string, error) {
}
return string(osName), nil
}
bytes, err := ioutil.ReadFile("/etc/os-release")
bytes, err := os.ReadFile("/etc/os-release")
if err != nil && os.IsNotExist(err) {
// /usr/lib/os-release in stateless systems like Clear Linux
bytes, err = ioutil.ReadFile("/usr/lib/os-release")
bytes, err = os.ReadFile("/usr/lib/os-release")
}
if err != nil {
return "", fmt.Errorf("error opening file : %v", err)

View File

@@ -17,9 +17,9 @@ package manager
import (
"flag"
"fmt"
"io/ioutil"
"math"
"math/rand"
"os"
"os/exec"
"path"
"regexp"
@@ -243,7 +243,7 @@ func (cd *containerData) ReadFile(filepath string, inHostNamespace bool) ([]byte
for _, pid := range pids {
filePath := path.Join(rootfs, "/proc", pid, "/root", filepath)
klog.V(3).Infof("Trying path %q", filePath)
data, err := ioutil.ReadFile(filePath)
data, err := os.ReadFile(filePath)
if err == nil {
return data, err
}
@@ -323,7 +323,7 @@ func (cd *containerData) parseProcessList(cadvisorContainer string, inHostNamesp
var fdCount int
dirPath := path.Join(rootfs, "/proc", strconv.Itoa(processInfo.Pid), "fd")
fds, err := ioutil.ReadDir(dirPath)
fds, err := os.ReadDir(dirPath)
if err != nil {
klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
continue
@@ -482,7 +482,7 @@ func (cd *containerData) nextHousekeepingInterval() time.Duration {
stats, err := cd.memoryCache.RecentStats(cd.info.Name, empty, empty, 2)
if err != nil {
if cd.allowErrorLogging() {
klog.Warningf("Failed to get RecentStats(%q) while determining the next housekeeping: %v", cd.info.Name, err)
klog.V(4).Infof("Failed to get RecentStats(%q) while determining the next housekeeping: %v", cd.info.Name, err)
}
} else if len(stats) == 2 {
// TODO(vishnuk): Use no processes as a signal.

View File

@@ -58,10 +58,13 @@ var eventStorageAgeLimit = flag.String("event_storage_age_limit", "default=24h",
var eventStorageEventLimit = flag.String("event_storage_event_limit", "default=100000", "Max number of events to store (per type). Value is a comma separated list of key values, where the keys are event types (e.g.: creation, oom) or \"default\" and the value is an integer. Default is applied to all non-specified event types")
var applicationMetricsCountLimit = flag.Int("application_metrics_count_limit", 100, "Max number of application metrics to store (per container)")
// The namespace under which Docker aliases are unique.
const DockerNamespace = "docker"
// The namespace under which aliases are unique.
const (
DockerNamespace = "docker"
PodmanNamespace = "podman"
)
var HousekeepingConfigFlags = HouskeepingConfig{
var HousekeepingConfigFlags = HousekeepingConfig{
flag.Duration("max_housekeeping_interval", 60*time.Second, "Largest interval to allow between container housekeepings"),
flag.Bool("allow_dynamic_housekeeping", true, "Whether to allow the housekeeping interval to be dynamic"),
}
@@ -136,16 +139,20 @@ type Manager interface {
// Returns debugging information. Map of lines per category.
DebugInfo() map[string][]string
AllPodmanContainers(c *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error)
PodmanContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error)
}
// Housekeeping configuration for the manager
type HouskeepingConfig = struct {
type HousekeepingConfig = struct {
Interval *time.Duration
AllowDynamic *bool
}
// New takes a memory storage and returns a new manager.
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, houskeepingConfig HouskeepingConfig, includedMetricsSet container.MetricSet, collectorHTTPClient *http.Client, rawContainerCgroupPathPrefixWhiteList, containerEnvMetadataWhiteList []string, perfEventsFile string, resctrlInterval time.Duration) (Manager, error) {
func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, HousekeepingConfig HousekeepingConfig, includedMetricsSet container.MetricSet, collectorHTTPClient *http.Client, rawContainerCgroupPathPrefixWhiteList, containerEnvMetadataWhiteList []string, perfEventsFile string, resctrlInterval time.Duration) (Manager, error) {
if memoryCache == nil {
return nil, fmt.Errorf("manager requires memory storage")
}
@@ -192,8 +199,8 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, houskeepingConfig
cadvisorContainer: selfContainer,
inHostNamespace: inHostNamespace,
startupTime: time.Now(),
maxHousekeepingInterval: *houskeepingConfig.Interval,
allowDynamicHousekeeping: *houskeepingConfig.AllowDynamic,
maxHousekeepingInterval: *HousekeepingConfig.Interval,
allowDynamicHousekeeping: *HousekeepingConfig.AllowDynamic,
includedMetrics: includedMetricsSet,
containerWatchers: []watcher.ContainerWatcher{},
eventsChannel: eventsChannel,
@@ -265,6 +272,19 @@ type manager struct {
containerEnvMetadataWhiteList []string
}
func (m *manager) PodmanContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
container, err := m.namespacedContainer(containerName, PodmanNamespace)
if err != nil {
return info.ContainerInfo{}, err
}
inf, err := m.containerDataToContainerInfo(container, query)
if err != nil {
return info.ContainerInfo{}, err
}
return *inf, nil
}
// Start the container manager.
func (m *manager) Start() error {
m.containerWatchers = container.InitializePlugins(m, m.fsInfo, m.includedMetrics)
@@ -274,7 +294,7 @@ func (m *manager) Start() error {
klog.Errorf("Registration of the raw container factory failed: %v", err)
}
rawWatcher, err := raw.NewRawContainerWatcher()
rawWatcher, err := raw.NewRawContainerWatcher(m.includedMetrics)
if err != nil {
return err
}
@@ -581,14 +601,14 @@ func (m *manager) SubcontainersInfo(containerName string, query *info.ContainerI
return m.containerDataSliceToContainerInfoSlice(containers, query)
}
func (m *manager) getAllDockerContainers() map[string]*containerData {
func (m *manager) getAllNamespacedContainers(ns string) map[string]*containerData {
m.containersLock.RLock()
defer m.containersLock.RUnlock()
containers := make(map[string]*containerData, len(m.containers))
// Get containers in the Docker namespace.
// Get containers in a namespace.
for name, cont := range m.containers {
if name.Namespace == DockerNamespace {
if name.Namespace == ns {
containers[cont.info.Name] = cont
}
}
@@ -596,48 +616,34 @@ func (m *manager) getAllDockerContainers() map[string]*containerData {
}
func (m *manager) AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
containers := m.getAllDockerContainers()
output := make(map[string]info.ContainerInfo, len(containers))
for name, cont := range containers {
inf, err := m.containerDataToContainerInfo(cont, query)
if err != nil {
// Ignore the error because of race condition and return best-effort result.
if err == memory.ErrDataNotFound {
klog.Warningf("Error getting data for container %s because of race condition", name)
continue
}
return nil, err
}
output[name] = *inf
}
return output, nil
containers := m.getAllNamespacedContainers(DockerNamespace)
return m.containersInfo(containers, query)
}
func (m *manager) getDockerContainer(containerName string) (*containerData, error) {
func (m *manager) namespacedContainer(containerName string, ns string) (*containerData, error) {
m.containersLock.RLock()
defer m.containersLock.RUnlock()
// Check for the container in the Docker container namespace.
// Check for the container in the namespace.
cont, ok := m.containers[namespacedContainerName{
Namespace: DockerNamespace,
Namespace: ns,
Name: containerName,
}]
// Look for container by short prefix name if no exact match found.
if !ok {
for contName, c := range m.containers {
if contName.Namespace == DockerNamespace && strings.HasPrefix(contName.Name, containerName) {
if contName.Namespace == ns && strings.HasPrefix(contName.Name, containerName) {
if cont == nil {
cont = c
} else {
return nil, fmt.Errorf("unable to find container. Container %q is not unique", containerName)
return nil, fmt.Errorf("unable to find container in %q namespace. Container %q is not unique", ns, containerName)
}
}
}
if cont == nil {
return nil, fmt.Errorf("unable to find Docker container %q", containerName)
return nil, fmt.Errorf("unable to find container %q in %q namespace", containerName, ns)
}
}
@@ -645,7 +651,7 @@ func (m *manager) getDockerContainer(containerName string) (*containerData, erro
}
func (m *manager) DockerContainer(containerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error) {
container, err := m.getDockerContainer(containerName)
container, err := m.namespacedContainer(containerName, DockerNamespace)
if err != nil {
return info.ContainerInfo{}, err
}
@@ -691,7 +697,7 @@ func (m *manager) GetRequestedContainersInfo(containerName string, options v2.Re
info, err := m.containerDataToContainerInfo(data, &query)
if err != nil {
if err == memory.ErrDataNotFound {
klog.Warningf("Error getting data for container %s because of race condition", name)
klog.V(4).Infof("Error getting data for container %s because of race condition", name)
continue
}
errs.append(name, "containerDataToContainerInfo", err)
@@ -717,19 +723,23 @@ func (m *manager) getRequestedContainers(containerName string, options v2.Reques
return containersMap, fmt.Errorf("unknown container: %q", containerName)
}
}
case v2.TypeDocker:
case v2.TypeDocker, v2.TypePodman:
namespace := map[string]string{
v2.TypeDocker: DockerNamespace,
v2.TypePodman: PodmanNamespace,
}[options.IdType]
if !options.Recursive {
containerName = strings.TrimPrefix(containerName, "/")
cont, err := m.getDockerContainer(containerName)
cont, err := m.namespacedContainer(containerName, namespace)
if err != nil {
return containersMap, err
}
containersMap[cont.info.Name] = cont
} else {
if containerName != "/" {
return containersMap, fmt.Errorf("invalid request for docker container %q with subcontainers", containerName)
return containersMap, fmt.Errorf("invalid request for %s container %q with subcontainers", options.IdType, containerName)
}
containersMap = m.getAllDockerContainers()
containersMap = m.getAllNamespacedContainers(namespace)
}
default:
return containersMap, fmt.Errorf("invalid request type %q", options.IdType)
@@ -1357,6 +1367,28 @@ func (m *manager) getFsInfoByDeviceName(deviceName string) (v2.FsInfo, error) {
return v2.FsInfo{}, fmt.Errorf("cannot find filesystem info for device %q", deviceName)
}
func (m *manager) containersInfo(containers map[string]*containerData, query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
output := make(map[string]info.ContainerInfo, len(containers))
for name, cont := range containers {
inf, err := m.containerDataToContainerInfo(cont, query)
if err != nil {
// Ignore the error because of race condition and return best-effort result.
if err == memory.ErrDataNotFound {
klog.V(4).Infof("Error getting data for container %s because of race condition", name)
continue
}
return nil, err
}
output[name] = *inf
}
return output, nil
}
func (m *manager) AllPodmanContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error) {
containers := m.getAllNamespacedContainers(PodmanNamespace)
return m.containersInfo(containers, query)
}
func getVersionInfo() (*info.VersionInfo, error) {
kernelVersion := machine.KernelVersion()

View File

@@ -377,6 +377,13 @@ func NewPrometheusCollector(i infoProvider, f ContainerLabelsFunc, includedMetri
getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(s.Memory.RSS), timestamp: s.Timestamp}}
},
}, {
name: "container_memory_kernel_usage",
help: "Size of kernel memory allocated in bytes.",
valueType: prometheus.GaugeValue,
getValues: func(s *info.ContainerStats) metricValues {
return metricValues{{value: float64(s.Memory.KernelUsage), timestamp: s.Timestamp}}
},
}, {
name: "container_memory_mapped_file",
help: "Size of memory mapped files in bytes.",

View File

@@ -350,10 +350,11 @@ func (p testSubcontainersInfoProvider) GetRequestedContainersInfo(string, v2.Req
Unevictable: map[uint8]uint64{0: 8900, 1: 20000},
},
},
Cache: 14,
RSS: 15,
MappedFile: 16,
Swap: 8192,
Cache: 14,
RSS: 15,
MappedFile: 16,
KernelUsage: 17,
Swap: 8192,
},
Hugetlb: map[string]info.HugetlbStats{
"2Mi": {

View File

@@ -111,6 +111,14 @@ func NewPrometheusMachineCollector(i infoProvider, includedMetrics container.Met
return metricValues{{value: float64(machineInfo.MemoryCapacity), timestamp: machineInfo.Timestamp}}
},
},
{
name: "machine_swap_bytes",
help: "Amount of swap memory available on the machine.",
valueType: prometheus.GaugeValue,
getValues: func(machineInfo *info.MachineInfo) metricValues {
return metricValues{{value: float64(machineInfo.SwapCapacity), timestamp: machineInfo.Timestamp}}
},
},
{
name: "machine_dimm_count",
help: "Number of RAM DIMM (all types memory modules) value labeled by dimm type.",

View File

@@ -48,7 +48,7 @@ func init() {
}
// getAvgPowerBudget retrieves configured power budget
// (in watts) for NVM devices. When libipmct is not available
// (in watts) for NVM devices. When libipmctl is not available
// zero is returned.
func getAvgPowerBudget() (uint, error) {
// Get number of devices on the platform
@@ -61,7 +61,7 @@ func getAvgPowerBudget() (uint, error) {
}
if count == 0 {
klog.Warningf("There are no NVM devices!")
klog.V(4).Infof("There are no NVM devices.")
return uint(0), nil
}

View File

@@ -62,7 +62,7 @@ type group struct {
var (
isLibpfmInitialized = false
libpmfMutex = sync.Mutex{}
libpfmMutex = sync.Mutex{}
)
const (
@@ -70,8 +70,8 @@ const (
)
func init() {
libpmfMutex.Lock()
defer libpmfMutex.Unlock()
libpfmMutex.Lock()
defer libpfmMutex.Unlock()
pErr := C.pfm_initialize()
if pErr != C.PFM_SUCCESS {
klog.Errorf("unable to initialize libpfm: %d", int(pErr))
@@ -266,10 +266,12 @@ func readPerfEventAttr(name string, pfmGetOsEventEncoding func(string, unsafe.Po
func pfmGetOsEventEncoding(name string, perfEventAttrMemory unsafe.Pointer) error {
event := pfmPerfEncodeArgT{}
fstr := C.CString("")
defer C.free(unsafe.Pointer(fstr))
event.fstr = unsafe.Pointer(fstr)
event.attr = perfEventAttrMemory
event.size = C.ulong(unsafe.Sizeof(event))
cSafeName := C.CString(name)
defer C.free(unsafe.Pointer(cSafeName))
pErr := C.pfm_get_os_event_encoding(cSafeName, C.PFM_PLM0|C.PFM_PLM3, C.PFM_OS_PERF_EVENT, unsafe.Pointer(&event))
if pErr != C.PFM_SUCCESS {
return fmt.Errorf("unable to transform event name %s to perf_event_attr: %d", name, int(pErr))
@@ -409,8 +411,8 @@ func (c *collector) Destroy() {
// Finalize terminates libpfm4 to free resources.
func Finalize() {
libpmfMutex.Lock()
defer libpmfMutex.Unlock()
libpfmMutex.Lock()
defer libpfmMutex.Unlock()
klog.V(1).Info("Attempting to terminate libpfm4")
if !isLibpfmInitialized {

View File

@@ -25,7 +25,6 @@ package perf
import "C"
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -70,7 +69,7 @@ func getPMU(pmus uncorePMUs, gotType uint32) (*pmu, error) {
type uncorePMUs map[string]pmu
func readUncorePMU(path string, name string, cpumaskRegexp *regexp.Regexp) (*pmu, error) {
buf, err := ioutil.ReadFile(filepath.Join(path, pmuTypeFilename))
buf, err := os.ReadFile(filepath.Join(path, pmuTypeFilename))
if err != nil {
return nil, err
}
@@ -80,7 +79,7 @@ func readUncorePMU(path string, name string, cpumaskRegexp *regexp.Regexp) (*pmu
return nil, err
}
buf, err = ioutil.ReadFile(filepath.Join(path, pmuCpumaskFilename))
buf, err = os.ReadFile(filepath.Join(path, pmuCpumaskFilename))
if err != nil {
return nil, err
}

View File

@@ -22,7 +22,6 @@ import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -42,6 +41,8 @@ const (
cpusListFileName = "cpus_list"
schemataFileName = "schemata"
tasksFileName = "tasks"
modeFileName = "mode"
sizeFileName = "size"
infoDirName = "info"
monDataDirName = "mon_data"
monGroupsDirName = "mon_groups"
@@ -72,6 +73,8 @@ var (
monGroupsDirName: {},
schemataFileName: {},
tasksFileName: {},
modeFileName: {},
sizeFileName: {},
}
)
@@ -189,7 +192,7 @@ func getPids(containerName string) ([]int, error) {
func getAllProcessThreads(path string) ([]int, error) {
processThreads := make([]int, 0)
threadDirs, err := ioutil.ReadDir(path)
threadDirs, err := os.ReadDir(path)
if err != nil {
return processThreads, err
}
@@ -216,7 +219,7 @@ func findGroup(group string, pids []string, includeGroup bool, exclusive bool) (
availablePaths = append(availablePaths, group)
}
files, err := ioutil.ReadDir(group)
files, err := os.ReadDir(group)
for _, file := range files {
if _, ok := groupDirectories[file.Name()]; !ok {
availablePaths = append(availablePaths, filepath.Join(group, file.Name()))
@@ -296,7 +299,7 @@ func readTasksFile(tasksPath string) (map[string]struct{}, error) {
}
func readStatFrom(path string, vendorID string) (uint64, error) {
context, err := ioutil.ReadFile(path)
context, err := os.ReadFile(path)
if err != nil {
return 0, err
}

View File

@@ -78,7 +78,7 @@ func (s *StatsSummary) AddSample(stat v1.ContainerStats) error {
}
if elapsed > 60*time.Second {
// Make a minute sample. This works with dynamic housekeeping as long
// as we keep max dynamic houskeeping period close to a minute.
// as we keep max dynamic housekeeping period close to a minute.
minuteSample := GetMinutePercentiles(s.secondSamples)
// Clear seconds samples. Keep the latest sample for continuity.
// Copying and resizing helps avoid slice re-allocation.

View File

@@ -15,7 +15,7 @@
package cloudinfo
import (
"io/ioutil"
"os"
"strings"
info "github.com/google/cadvisor/info/v1"
@@ -37,7 +37,7 @@ type provider struct{}
var _ cloudinfo.CloudProvider = provider{}
func (provider) IsActiveProvider() bool {
data, err := ioutil.ReadFile(sysVendorFileName)
data, err := os.ReadFile(sysVendorFileName)
if err != nil {
return false
}
@@ -50,7 +50,7 @@ func (provider) GetInstanceType() info.InstanceType {
}
func (provider) GetInstanceID() info.InstanceID {
data, err := ioutil.ReadFile(biosUUIDFileName)
data, err := os.ReadFile(biosUUIDFileName)
if err != nil {
return info.UnNamedInstance
}

View File

@@ -15,7 +15,7 @@
package gce
import (
"io/ioutil"
"os"
"strings"
info "github.com/google/cadvisor/info/v1"
@@ -39,7 +39,7 @@ type provider struct{}
var _ cloudinfo.CloudProvider = provider{}
func (provider) IsActiveProvider() bool {
data, err := ioutil.ReadFile(gceProductName)
data, err := os.ReadFile(gceProductName)
if err != nil {
klog.V(2).Infof("Error while reading product_name: %v", err)
return false

View File

@@ -17,7 +17,6 @@ package sysfs
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -102,6 +101,9 @@ type SysFs interface {
GetBlockDeviceScheduler(string) (string, error)
// Get device major:minor number string.
GetBlockDeviceNumbers(string) (string, error)
// Is the device "hidden" (meaning will not have a device handle)
// This is the case with native nvme multipathing.
IsBlockDeviceHidden(string) (bool, error)
GetNetworkDevices() ([]os.FileInfo, error)
GetNetworkAddress(string) (string, error)
@@ -124,10 +126,14 @@ type SysFs interface {
IsCPUOnline(dir string) bool
}
type realSysFs struct{}
type realSysFs struct {
cpuPath string
}
func NewRealSysFs() SysFs {
return &realSysFs{}
return &realSysFs{
cpuPath: "/sys/devices/system/cpu",
}
}
func (fs *realSysFs) GetNodesPaths() ([]string, error) {
@@ -142,7 +148,7 @@ func (fs *realSysFs) GetCPUsPaths(cpusPath string) ([]string, error) {
func (fs *realSysFs) GetCoreID(cpuPath string) (string, error) {
coreIDFilePath := fmt.Sprintf("%s%s", cpuPath, coreIDFilePath)
coreID, err := ioutil.ReadFile(coreIDFilePath)
coreID, err := os.ReadFile(coreIDFilePath)
if err != nil {
return "", err
}
@@ -151,7 +157,7 @@ func (fs *realSysFs) GetCoreID(cpuPath string) (string, error) {
func (fs *realSysFs) GetCPUPhysicalPackageID(cpuPath string) (string, error) {
packageIDFilePath := fmt.Sprintf("%s%s", cpuPath, packageIDFilePath)
packageID, err := ioutil.ReadFile(packageIDFilePath)
packageID, err := os.ReadFile(packageIDFilePath)
if err != nil {
return "", err
}
@@ -160,7 +166,7 @@ func (fs *realSysFs) GetCPUPhysicalPackageID(cpuPath string) (string, error) {
func (fs *realSysFs) GetMemInfo(nodePath string) (string, error) {
meminfoPath := fmt.Sprintf("%s/%s", nodePath, meminfoFile)
meminfo, err := ioutil.ReadFile(meminfoPath)
meminfo, err := os.ReadFile(meminfoPath)
if err != nil {
return "", err
}
@@ -169,7 +175,7 @@ func (fs *realSysFs) GetMemInfo(nodePath string) (string, error) {
func (fs *realSysFs) GetDistances(nodePath string) (string, error) {
distancePath := fmt.Sprintf("%s/%s", nodePath, distanceFile)
distance, err := ioutil.ReadFile(distancePath)
distance, err := os.ReadFile(distancePath)
if err != nil {
return "", err
}
@@ -177,12 +183,16 @@ func (fs *realSysFs) GetDistances(nodePath string) (string, error) {
}
func (fs *realSysFs) GetHugePagesInfo(hugePagesDirectory string) ([]os.FileInfo, error) {
return ioutil.ReadDir(hugePagesDirectory)
dirs, err := os.ReadDir(hugePagesDirectory)
if err != nil {
return nil, err
}
return toFileInfo(dirs)
}
func (fs *realSysFs) GetHugePagesNr(hugepagesDirectory string, hugePageName string) (string, error) {
hugePageFilePath := fmt.Sprintf("%s%s/%s", hugepagesDirectory, hugePageName, HugePagesNrFile)
hugePageFile, err := ioutil.ReadFile(hugePageFilePath)
hugePageFile, err := os.ReadFile(hugePageFilePath)
if err != nil {
return "", err
}
@@ -190,19 +200,43 @@ func (fs *realSysFs) GetHugePagesNr(hugepagesDirectory string, hugePageName stri
}
func (fs *realSysFs) GetBlockDevices() ([]os.FileInfo, error) {
return ioutil.ReadDir(blockDir)
dirs, err := os.ReadDir(blockDir)
if err != nil {
return nil, err
}
return toFileInfo(dirs)
}
func (fs *realSysFs) GetBlockDeviceNumbers(name string) (string, error) {
dev, err := ioutil.ReadFile(path.Join(blockDir, name, "/dev"))
dev, err := os.ReadFile(path.Join(blockDir, name, "/dev"))
if err != nil {
return "", err
}
return string(dev), nil
}
func (fs *realSysFs) IsBlockDeviceHidden(name string) (bool, error) {
// See: https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-block
// https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git
// - c8487d854ba5 ("lsblk: Ignore hidden devices")
devHiddenPath := path.Join(blockDir, name, "/hidden")
hidden, err := os.ReadFile(devHiddenPath)
if err != nil && os.IsNotExist(err) {
// older OS may not have /hidden sysfs entry, so for sure
// it is not a hidden device...
return false, nil
}
if err != nil {
return false, fmt.Errorf("failed to read %s: %w", devHiddenPath, err)
}
if string(hidden) == "1" {
return true, nil
}
return false, nil
}
func (fs *realSysFs) GetBlockDeviceScheduler(name string) (string, error) {
sched, err := ioutil.ReadFile(path.Join(blockDir, name, "/queue/scheduler"))
sched, err := os.ReadFile(path.Join(blockDir, name, "/queue/scheduler"))
if err != nil {
return "", err
}
@@ -210,7 +244,7 @@ func (fs *realSysFs) GetBlockDeviceScheduler(name string) (string, error) {
}
func (fs *realSysFs) GetBlockDeviceSize(name string) (string, error) {
size, err := ioutil.ReadFile(path.Join(blockDir, name, "/size"))
size, err := os.ReadFile(path.Join(blockDir, name, "/size"))
if err != nil {
return "", err
}
@@ -218,13 +252,17 @@ func (fs *realSysFs) GetBlockDeviceSize(name string) (string, error) {
}
func (fs *realSysFs) GetNetworkDevices() ([]os.FileInfo, error) {
files, err := ioutil.ReadDir(netDir)
dirs, err := os.ReadDir(netDir)
if err != nil {
return nil, err
}
files, err := toFileInfo(dirs)
if err != nil {
return nil, err
}
// Filter out non-directory & non-symlink files
var dirs []os.FileInfo
filtered := []os.FileInfo{}
for _, f := range files {
if f.Mode()|os.ModeSymlink != 0 {
f, err = os.Stat(path.Join(netDir, f.Name()))
@@ -233,14 +271,14 @@ func (fs *realSysFs) GetNetworkDevices() ([]os.FileInfo, error) {
}
}
if f.IsDir() {
dirs = append(dirs, f)
filtered = append(filtered, f)
}
}
return dirs, nil
return filtered, nil
}
func (fs *realSysFs) GetNetworkAddress(name string) (string, error) {
address, err := ioutil.ReadFile(path.Join(netDir, name, "/address"))
address, err := os.ReadFile(path.Join(netDir, name, "/address"))
if err != nil {
return "", err
}
@@ -248,7 +286,7 @@ func (fs *realSysFs) GetNetworkAddress(name string) (string, error) {
}
func (fs *realSysFs) GetNetworkMtu(name string) (string, error) {
mtu, err := ioutil.ReadFile(path.Join(netDir, name, "/mtu"))
mtu, err := os.ReadFile(path.Join(netDir, name, "/mtu"))
if err != nil {
return "", err
}
@@ -256,7 +294,7 @@ func (fs *realSysFs) GetNetworkMtu(name string) (string, error) {
}
func (fs *realSysFs) GetNetworkSpeed(name string) (string, error) {
speed, err := ioutil.ReadFile(path.Join(netDir, name, "/speed"))
speed, err := os.ReadFile(path.Join(netDir, name, "/speed"))
if err != nil {
return "", err
}
@@ -265,7 +303,7 @@ func (fs *realSysFs) GetNetworkSpeed(name string) (string, error) {
func (fs *realSysFs) GetNetworkStatValue(dev string, stat string) (uint64, error) {
statPath := path.Join(netDir, dev, "/statistics", stat)
out, err := ioutil.ReadFile(statPath)
out, err := os.ReadFile(statPath)
if err != nil {
return 0, fmt.Errorf("failed to read stat from %q for device %q", statPath, dev)
}
@@ -279,7 +317,23 @@ func (fs *realSysFs) GetNetworkStatValue(dev string, stat string) (uint64, error
func (fs *realSysFs) GetCaches(id int) ([]os.FileInfo, error) {
cpuPath := fmt.Sprintf("%s%d/cache", cacheDir, id)
return ioutil.ReadDir(cpuPath)
dir, err := os.ReadDir(cpuPath)
if err != nil {
return nil, err
}
return toFileInfo(dir)
}
func toFileInfo(dirs []os.DirEntry) ([]os.FileInfo, error) {
info := []os.FileInfo{}
for _, dir := range dirs {
fI, err := dir.Info()
if err != nil {
return nil, err
}
info = append(info, fI)
}
return info, nil
}
func bitCount(i uint64) (count int) {
@@ -293,7 +347,7 @@ func bitCount(i uint64) (count int) {
}
func getCPUCount(cache string) (count int, err error) {
out, err := ioutil.ReadFile(path.Join(cache, "/shared_cpu_map"))
out, err := os.ReadFile(path.Join(cache, "/shared_cpu_map"))
if err != nil {
return 0, err
}
@@ -311,7 +365,7 @@ func getCPUCount(cache string) (count int, err error) {
func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
cachePath := fmt.Sprintf("%s%d/cache/%s", cacheDir, cpu, name)
out, err := ioutil.ReadFile(path.Join(cachePath, "/id"))
out, err := os.ReadFile(path.Join(cachePath, "/id"))
if err != nil {
return CacheInfo{}, err
}
@@ -321,7 +375,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
return CacheInfo{}, err
}
out, err = ioutil.ReadFile(path.Join(cachePath, "/size"))
out, err = os.ReadFile(path.Join(cachePath, "/size"))
if err != nil {
return CacheInfo{}, err
}
@@ -332,7 +386,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
}
// convert to bytes
size = size * 1024
out, err = ioutil.ReadFile(path.Join(cachePath, "/level"))
out, err = os.ReadFile(path.Join(cachePath, "/level"))
if err != nil {
return CacheInfo{}, err
}
@@ -342,7 +396,7 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
return CacheInfo{}, err
}
out, err = ioutil.ReadFile(path.Join(cachePath, "/type"))
out, err = os.ReadFile(path.Join(cachePath, "/type"))
if err != nil {
return CacheInfo{}, err
}
@@ -361,13 +415,13 @@ func (fs *realSysFs) GetCacheInfo(cpu int, name string) (CacheInfo, error) {
}
func (fs *realSysFs) GetSystemUUID() (string, error) {
if id, err := ioutil.ReadFile(path.Join(dmiDir, "id", "product_uuid")); err == nil {
if id, err := os.ReadFile(path.Join(dmiDir, "id", "product_uuid")); err == nil {
return strings.TrimSpace(string(id)), nil
} else if id, err = ioutil.ReadFile(path.Join(ppcDevTree, "system-id")); err == nil {
} else if id, err = os.ReadFile(path.Join(ppcDevTree, "system-id")); err == nil {
return strings.TrimSpace(strings.TrimRight(string(id), "\000")), nil
} else if id, err = ioutil.ReadFile(path.Join(ppcDevTree, "vm,uuid")); err == nil {
} else if id, err = os.ReadFile(path.Join(ppcDevTree, "vm,uuid")); err == nil {
return strings.TrimSpace(strings.TrimRight(string(id), "\000")), nil
} else if id, err = ioutil.ReadFile(path.Join(s390xDevTree, "machine-id")); err == nil {
} else if id, err = os.ReadFile(path.Join(s390xDevTree, "machine-id")); err == nil {
return strings.TrimSpace(string(id)), nil
} else {
return "", err
@@ -375,19 +429,19 @@ func (fs *realSysFs) GetSystemUUID() (string, error) {
}
func (fs *realSysFs) IsCPUOnline(cpuPath string) bool {
onlinePath, err := filepath.Abs(cpuPath + "/../online")
cpuOnlinePath, err := filepath.Abs(fs.cpuPath + "/online")
if err != nil {
klog.V(1).Infof("Unable to get absolute path for %s", cpuPath)
return false
}
// Quick check to determine if file exists: if it does not then kernel CPU hotplug is disabled and all CPUs are online.
_, err = os.Stat(onlinePath)
_, err = os.Stat(cpuOnlinePath)
if err != nil && os.IsNotExist(err) {
return true
}
if err != nil {
klog.V(1).Infof("Unable to stat %s: %s", onlinePath, err)
klog.V(1).Infof("Unable to stat %s: %s", cpuOnlinePath, err)
}
cpuID, err := getCPUID(cpuPath)
@@ -396,7 +450,7 @@ func (fs *realSysFs) IsCPUOnline(cpuPath string) bool {
return false
}
isOnline, err := isCPUOnline(onlinePath, cpuID)
isOnline, err := isCPUOnline(cpuOnlinePath, cpuID)
if err != nil {
klog.V(1).Infof("Unable to get online CPUs list: %s", err)
return false
@@ -422,7 +476,7 @@ func getCPUID(dir string) (uint16, error) {
// It parses CPU list (such as: 0,3-5,10) into a struct that allows to determine quickly if CPU or particular ID is online.
// see: https://github.com/opencontainers/runc/blob/ab27e12cebf148aa5d1ee3ad13d9fc7ae12bf0b6/libcontainer/cgroups/fs/cpuset.go#L45
func isCPUOnline(path string, cpuID uint16) (bool, error) {
fileContent, err := ioutil.ReadFile(path)
fileContent, err := os.ReadFile(path)
if err != nil {
return false, err
}
@@ -448,10 +502,9 @@ func isCPUOnline(path string, cpuID uint16) (bool, error) {
if min > max {
return false, fmt.Errorf("invalid values in %s", path)
}
for i := min; i <= max; i++ {
if uint16(i) == cpuID {
return true, nil
}
// Return true, if the CPU under consideration is in the range of online CPUs.
if cpuID >= uint16(min) && cpuID <= uint16(max) {
return true, nil
}
case 1:
value, err := strconv.ParseUint(s, 10, 16)
@@ -469,21 +522,21 @@ func isCPUOnline(path string, cpuID uint16) (bool, error) {
// Looks for sysfs cpu path containing given CPU property, e.g. core_id or physical_package_id
// and returns number of unique values of given property, exemplary usage: getting number of CPU physical cores
func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
absCPUBusPath, err := filepath.Abs(cpuBusPath)
func GetUniqueCPUPropertyCount(cpuAttributesPath string, propertyName string) int {
absCPUAttributesPath, err := filepath.Abs(cpuAttributesPath)
if err != nil {
klog.Errorf("Cannot make %s absolute", cpuBusPath)
klog.Errorf("Cannot make %s absolute", cpuAttributesPath)
return 0
}
pathPattern := absCPUBusPath + "/cpu*[0-9]"
pathPattern := absCPUAttributesPath + "/cpu*[0-9]"
sysCPUPaths, err := filepath.Glob(pathPattern)
if err != nil {
klog.Errorf("Cannot find files matching pattern (pathPattern: %s), number of unique %s set to 0", pathPattern, propertyName)
return 0
}
onlinePath, err := filepath.Abs(cpuBusPath + "/online")
cpuOnlinePath, err := filepath.Abs(cpuAttributesPath + "/online")
if err != nil {
klog.V(1).Infof("Unable to get absolute path for %s", cpuBusPath+"/../online")
klog.V(1).Infof("Unable to get absolute path for %s", cpuAttributesPath+"/../online")
return 0
}
@@ -498,7 +551,7 @@ func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
klog.V(1).Infof("Unable to get CPU ID from path %s: %s", sysCPUPath, err)
return 0
}
isOnline, err := isCPUOnline(onlinePath, cpuID)
isOnline, err := isCPUOnline(cpuOnlinePath, cpuID)
if err != nil && !os.IsNotExist(err) {
klog.V(1).Infof("Unable to determine CPU online state: %s", err)
continue
@@ -507,13 +560,13 @@ func GetUniqueCPUPropertyCount(cpuBusPath string, propertyName string) int {
continue
}
propertyPath := filepath.Join(sysCPUPath, sysFsCPUTopology, propertyName)
propertyVal, err := ioutil.ReadFile(propertyPath)
propertyVal, err := os.ReadFile(propertyPath)
if err != nil {
klog.Warningf("Cannot open %s, assuming 0 for %s of CPU %d", propertyPath, propertyName, cpuID)
propertyVal = []byte("0")
}
packagePath := filepath.Join(sysCPUPath, sysFsCPUTopology, CPUPhysicalPackageID)
packageVal, err := ioutil.ReadFile(packagePath)
packageVal, err := os.ReadFile(packagePath)
if err != nil {
klog.Warningf("Cannot open %s, assuming 0 %s of CPU %d", packagePath, CPUPhysicalPackageID, cpuID)
packageVal = []byte("0")

View File

@@ -57,6 +57,16 @@ func GetBlockDeviceInfo(sysfs sysfs.SysFs) (map[string]info.DiskInfo, error) {
if strings.HasPrefix(name, "loop") || strings.HasPrefix(name, "ram") || strings.HasPrefix(name, "sr") {
continue
}
// Ignore "hidden" devices (i.e. nvme path device sysfs entries).
// These devices are in the form of /dev/nvme$Xc$Yn$Z and will
// not have a device handle (i.e. "hidden")
isHidden, err := sysfs.IsBlockDeviceHidden(name)
if err != nil {
return nil, err
}
if isHidden {
continue
}
diskInfo := info.DiskInfo{
Name: name,
}
@@ -104,7 +114,7 @@ func GetNetworkDevices(sysfs sysfs.SysFs) ([]info.NetInfo, error) {
for _, dev := range devs {
name := dev.Name()
// Ignore docker, loopback, and veth devices.
ignoredDevices := []string{"lo", "veth", "docker"}
ignoredDevices := []string{"lo", "veth", "docker", "nerdctl"}
ignored := false
for _, prefix := range ignoredDevices {
if strings.HasPrefix(name, prefix) {
@@ -200,7 +210,7 @@ func GetNodesInfo(sysFs sysfs.SysFs) ([]info.Node, int, error) {
}
if len(nodesDirs) == 0 {
klog.Warningf("Nodes topology is not available, providing CPU topology")
klog.V(4).Info("Nodes topology is not available, providing CPU topology")
return getCPUTopology(sysFs)
}