moby/pkg/sysinfo/sysinfo_linux.go
Doug Davis ff42a2eb41 Only show global warnings once
Upon each container create I'm seeing these warning **every** time in the
daemon output:
```
WARN[0002] Your kernel does not support swap memory limit
WARN[0002] Your kernel does not support cgroup rt period
WARN[0002] Your kernel does not support cgroup rt runtime
```
Showing them for each container.create() fills up the logs and encourages
people to ignore the output being generated - which means its less likely
they'll see real issues when they happen.  In short, I don't think we
need to show these warnings more than once, so let's only show these
warnings at daemon start-up time.

Signed-off-by: Doug Davis <dug@us.ibm.com>
2016-11-30 10:11:42 -08:00

259 lines
7.8 KiB
Go

package sysinfo
import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"syscall"
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/cgroups"
)
const (
// SeccompModeFilter refers to the syscall argument SECCOMP_MODE_FILTER.
SeccompModeFilter = uintptr(2)
)
func findCgroupMountpoints() (map[string]string, error) {
cgMounts, err := cgroups.GetCgroupMounts(false)
if err != nil {
return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
}
mps := make(map[string]string)
for _, m := range cgMounts {
for _, ss := range m.Subsystems {
mps[ss] = m.Mountpoint
}
}
return mps, nil
}
// New returns a new SysInfo, using the filesystem to detect which features
// the kernel supports. If `quiet` is `false` warnings are printed in logs
// whenever an error occurs or misconfigurations are present.
func New(quiet bool) *SysInfo {
sysInfo := &SysInfo{}
cgMounts, err := findCgroupMountpoints()
if err != nil {
logrus.Warnf("Failed to parse cgroup information: %v", err)
} else {
sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
sysInfo.cgroupPids = checkCgroupPids(quiet)
}
_, ok := cgMounts["devices"]
sysInfo.CgroupDevicesEnabled = ok
sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
// Check if AppArmor is supported.
if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
sysInfo.AppArmor = true
}
// Check if Seccomp is supported, via CONFIG_SECCOMP.
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_GET_SECCOMP, 0, 0); err != syscall.EINVAL {
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_SECCOMP, SeccompModeFilter, 0); err != syscall.EINVAL {
sysInfo.Seccomp = true
}
}
return sysInfo
}
// checkCgroupMem reads the memory information from the memory cgroup mount point.
func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
mountPoint, ok := cgMounts["memory"]
if !ok {
if !quiet {
logrus.Warn("Your kernel does not support cgroup memory limit")
}
return cgroupMemInfo{}
}
swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
if !quiet && !swapLimit {
logrus.Warn("Your kernel does not support swap memory limit")
}
memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
if !quiet && !memoryReservation {
logrus.Warn("Your kernel does not support memory reservation")
}
oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
if !quiet && !oomKillDisable {
logrus.Warn("Your kernel does not support oom control")
}
memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
if !quiet && !memorySwappiness {
logrus.Warn("Your kernel does not support memory swappiness")
}
kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
if !quiet && !kernelMemory {
logrus.Warn("Your kernel does not support kernel memory limit")
}
return cgroupMemInfo{
MemoryLimit: true,
SwapLimit: swapLimit,
MemoryReservation: memoryReservation,
OomKillDisable: oomKillDisable,
MemorySwappiness: memorySwappiness,
KernelMemory: kernelMemory,
}
}
// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
mountPoint, ok := cgMounts["cpu"]
if !ok {
if !quiet {
logrus.Warn("Unable to find cpu cgroup in mounts")
}
return cgroupCPUInfo{}
}
cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
if !quiet && !cpuShares {
logrus.Warn("Your kernel does not support cgroup cpu shares")
}
cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
if !quiet && !cpuCfsPeriod {
logrus.Warn("Your kernel does not support cgroup cfs period")
}
cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
if !quiet && !cpuCfsQuota {
logrus.Warn("Your kernel does not support cgroup cfs quotas")
}
cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
if !quiet && !cpuRealtimePeriod {
logrus.Warn("Your kernel does not support cgroup rt period")
}
cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
if !quiet && !cpuRealtimeRuntime {
logrus.Warn("Your kernel does not support cgroup rt runtime")
}
return cgroupCPUInfo{
CPUShares: cpuShares,
CPUCfsPeriod: cpuCfsPeriod,
CPUCfsQuota: cpuCfsQuota,
CPURealtimePeriod: cpuRealtimePeriod,
CPURealtimeRuntime: cpuRealtimeRuntime,
}
}
// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
mountPoint, ok := cgMounts["blkio"]
if !ok {
if !quiet {
logrus.Warn("Unable to find blkio cgroup in mounts")
}
return cgroupBlkioInfo{}
}
weight := cgroupEnabled(mountPoint, "blkio.weight")
if !quiet && !weight {
logrus.Warn("Your kernel does not support cgroup blkio weight")
}
weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
if !quiet && !weightDevice {
logrus.Warn("Your kernel does not support cgroup blkio weight_device")
}
readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
if !quiet && !readBpsDevice {
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
}
writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
if !quiet && !writeBpsDevice {
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
}
readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
if !quiet && !readIOpsDevice {
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
}
writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
if !quiet && !writeIOpsDevice {
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
}
return cgroupBlkioInfo{
BlkioWeight: weight,
BlkioWeightDevice: weightDevice,
BlkioReadBpsDevice: readBpsDevice,
BlkioWriteBpsDevice: writeBpsDevice,
BlkioReadIOpsDevice: readIOpsDevice,
BlkioWriteIOpsDevice: writeIOpsDevice,
}
}
// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
mountPoint, ok := cgMounts["cpuset"]
if !ok {
if !quiet {
logrus.Warn("Unable to find cpuset cgroup in mounts")
}
return cgroupCpusetInfo{}
}
cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
if err != nil {
return cgroupCpusetInfo{}
}
mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
if err != nil {
return cgroupCpusetInfo{}
}
return cgroupCpusetInfo{
Cpuset: true,
Cpus: strings.TrimSpace(string(cpus)),
Mems: strings.TrimSpace(string(mems)),
}
}
// checkCgroupPids reads the pids information from the pids cgroup mount point.
func checkCgroupPids(quiet bool) cgroupPids {
_, err := cgroups.FindCgroupMountpoint("pids")
if err != nil {
if !quiet {
logrus.Warn(err)
}
return cgroupPids{}
}
return cgroupPids{
PidsLimit: true,
}
}
func cgroupEnabled(mountPoint, name string) bool {
_, err := os.Stat(path.Join(mountPoint, name))
return err == nil
}
func readProcBool(path string) bool {
val, err := ioutil.ReadFile(path)
if err != nil {
return false
}
return strings.TrimSpace(string(val)) == "1"
}