mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00

In case os.Open[File], os.Mkdir[All], ioutil.ReadFile and the like fails, the error message already contains the file name and the operation that fails, so there is no need to wrap the error with something like "open %s failed". While at it - replace a few places with os.Open, ioutil.ReadAll with ioutil.ReadFile. - replace errors.Wrapf with errors.Wrap for cases where there are no %-style arguments. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
162 lines
3.4 KiB
Go
162 lines
3.4 KiB
Go
package cgroups
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type cpuHandler struct {
|
|
}
|
|
|
|
func getCPUHandler() *cpuHandler {
|
|
return &cpuHandler{}
|
|
}
|
|
|
|
func cleanString(s string) string {
|
|
return strings.Trim(s, "\n")
|
|
}
|
|
|
|
func readAcct(ctr *CgroupControl, name string) (uint64, error) {
|
|
p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
|
|
return readFileAsUint64(p)
|
|
}
|
|
|
|
func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) {
|
|
p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
|
|
data, err := ioutil.ReadFile(p)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "reading %s", p)
|
|
}
|
|
r := []uint64{}
|
|
for _, s := range strings.Split(string(data), " ") {
|
|
s = cleanString(s)
|
|
if s == "" {
|
|
break
|
|
}
|
|
v, err := strconv.ParseUint(s, 10, 0)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "parsing %s", s)
|
|
}
|
|
r = append(r, v)
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// Apply set the specified constraints
|
|
func (c *cpuHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
|
|
if res.CPU == nil {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("cpu apply not implemented yet")
|
|
}
|
|
|
|
// Create the cgroup
|
|
func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) {
|
|
if ctr.cgroup2 {
|
|
return false, nil
|
|
}
|
|
return ctr.createCgroupDirectory(CPU)
|
|
}
|
|
|
|
// Destroy the cgroup
|
|
func (c *cpuHandler) Destroy(ctr *CgroupControl) error {
|
|
return rmDirRecursively(ctr.getCgroupv1Path(CPU))
|
|
}
|
|
|
|
// Stat fills a metrics structure with usage stats for the controller
|
|
func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error {
|
|
var err error
|
|
usage := CPUUsage{}
|
|
if ctr.cgroup2 {
|
|
values, err := readCgroup2MapFile(ctr, "cpu.stat")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if val, found := values["usage_usec"]; found {
|
|
usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
usage.Kernel *= 1000
|
|
}
|
|
if val, found := values["system_usec"]; found {
|
|
usage.Kernel, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
usage.Total *= 1000
|
|
}
|
|
// FIXME: How to read usage.PerCPU?
|
|
} else {
|
|
usage.Total, err = readAcct(ctr, "cpuacct.usage")
|
|
if err != nil {
|
|
if !os.IsNotExist(errors.Cause(err)) {
|
|
return err
|
|
}
|
|
usage.Total = 0
|
|
}
|
|
usage.Kernel, err = readAcct(ctr, "cpuacct.usage_sys")
|
|
if err != nil {
|
|
if !os.IsNotExist(errors.Cause(err)) {
|
|
return err
|
|
}
|
|
usage.Kernel = 0
|
|
}
|
|
usage.PerCPU, err = readAcctList(ctr, "cpuacct.usage_percpu")
|
|
if err != nil {
|
|
if !os.IsNotExist(errors.Cause(err)) {
|
|
return err
|
|
}
|
|
usage.PerCPU = nil
|
|
}
|
|
}
|
|
m.CPU = CPUMetrics{Usage: usage}
|
|
return nil
|
|
}
|
|
|
|
// GetSystemCPUUsage returns the system usage for all the cgroups
|
|
func GetSystemCPUUsage() (uint64, error) {
|
|
cgroupv2, err := IsCgroup2UnifiedMode()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if !cgroupv2 {
|
|
p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage")
|
|
return readFileAsUint64(p)
|
|
}
|
|
|
|
files, err := ioutil.ReadDir(cgroupRoot)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var total uint64
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
continue
|
|
}
|
|
p := filepath.Join(cgroupRoot, file.Name(), "cpu.stat")
|
|
|
|
values, err := readCgroup2MapPath(p)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if val, found := values["usage_usec"]; found {
|
|
v, err := strconv.ParseUint(cleanString(val[0]), 10, 0)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
total += v * 1000
|
|
}
|
|
|
|
}
|
|
return total, nil
|
|
}
|