github.com/google/cadvisor@v0.49.1/container/raw/handler.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Handler for "raw" containers.
    16  package raw
    17  
    18  import (
    19  	"fmt"
    20  
    21  	"github.com/google/cadvisor/container"
    22  	"github.com/google/cadvisor/container/common"
    23  	"github.com/google/cadvisor/container/libcontainer"
    24  	"github.com/google/cadvisor/fs"
    25  	info "github.com/google/cadvisor/info/v1"
    26  	"github.com/google/cadvisor/machine"
    27  	"github.com/opencontainers/runc/libcontainer/cgroups"
    28  
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  type rawContainerHandler struct {
    33  	// Name of the container for this handler.
    34  	name               string
    35  	machineInfoFactory info.MachineInfoFactory
    36  
    37  	// Absolute path to the cgroup hierarchies of this container.
    38  	// (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
    39  	cgroupPaths map[string]string
    40  
    41  	fsInfo          fs.FsInfo
    42  	externalMounts  []common.Mount
    43  	includedMetrics container.MetricSet
    44  
    45  	libcontainerHandler *libcontainer.Handler
    46  }
    47  
    48  func isRootCgroup(name string) bool {
    49  	return name == "/"
    50  }
    51  
    52  func newRawContainerHandler(name string, cgroupSubsystems map[string]string, machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo, watcher *common.InotifyWatcher, rootFs string, includedMetrics container.MetricSet) (container.ContainerHandler, error) {
    53  	cHints, err := common.GetContainerHintsFromFile(*common.ArgContainerHints)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
    59  
    60  	cgroupManager, err := libcontainer.NewCgroupManager(name, cgroupPaths)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	var externalMounts []common.Mount
    66  	for _, container := range cHints.AllHosts {
    67  		if name == container.FullName {
    68  			externalMounts = container.Mounts
    69  			break
    70  		}
    71  	}
    72  
    73  	pid := 0
    74  	if isRootCgroup(name) {
    75  		pid = 1
    76  
    77  		// delete pids from cgroup paths because /sys/fs/cgroup/pids/pids.current not exist
    78  		delete(cgroupPaths, "pids")
    79  	}
    80  
    81  	handler := libcontainer.NewHandler(cgroupManager, rootFs, pid, includedMetrics)
    82  
    83  	return &rawContainerHandler{
    84  		name:                name,
    85  		machineInfoFactory:  machineInfoFactory,
    86  		cgroupPaths:         cgroupPaths,
    87  		fsInfo:              fsInfo,
    88  		externalMounts:      externalMounts,
    89  		includedMetrics:     includedMetrics,
    90  		libcontainerHandler: handler,
    91  	}, nil
    92  }
    93  
    94  func (h *rawContainerHandler) ContainerReference() (info.ContainerReference, error) {
    95  	// We only know the container by its one name.
    96  	return info.ContainerReference{
    97  		Name: h.name,
    98  	}, nil
    99  }
   100  
   101  func (h *rawContainerHandler) GetRootNetworkDevices() ([]info.NetInfo, error) {
   102  	nd := []info.NetInfo{}
   103  	if isRootCgroup(h.name) {
   104  		mi, err := h.machineInfoFactory.GetMachineInfo()
   105  		if err != nil {
   106  			return nd, err
   107  		}
   108  		return mi.NetworkDevices, nil
   109  	}
   110  	return nd, nil
   111  }
   112  
   113  // Nothing to start up.
   114  func (h *rawContainerHandler) Start() {}
   115  
   116  // Nothing to clean up.
   117  func (h *rawContainerHandler) Cleanup() {}
   118  
   119  func (h *rawContainerHandler) GetSpec() (info.ContainerSpec, error) {
   120  	const hasNetwork = false
   121  	hasFilesystem := isRootCgroup(h.name) || len(h.externalMounts) > 0
   122  	spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNetwork, hasFilesystem)
   123  	if err != nil {
   124  		return spec, err
   125  	}
   126  
   127  	if isRootCgroup(h.name) {
   128  		// Check physical network devices for root container.
   129  		nd, err := h.GetRootNetworkDevices()
   130  		if err != nil {
   131  			return spec, err
   132  		}
   133  		spec.HasNetwork = spec.HasNetwork || len(nd) != 0
   134  
   135  		// Get memory and swap limits of the running machine
   136  		memLimit, err := machine.GetMachineMemoryCapacity()
   137  		if err != nil {
   138  			klog.Warningf("failed to obtain memory limit for machine container")
   139  			spec.HasMemory = false
   140  		} else {
   141  			spec.Memory.Limit = uint64(memLimit)
   142  			// Spec is marked to have memory only if the memory limit is set
   143  			spec.HasMemory = true
   144  		}
   145  
   146  		swapLimit, err := machine.GetMachineSwapCapacity()
   147  		if err != nil {
   148  			klog.Warningf("failed to obtain swap limit for machine container")
   149  		} else {
   150  			spec.Memory.SwapLimit = uint64(swapLimit)
   151  		}
   152  	}
   153  
   154  	return spec, nil
   155  }
   156  
   157  func fsToFsStats(fs *fs.Fs) info.FsStats {
   158  	inodes := uint64(0)
   159  	inodesFree := uint64(0)
   160  	hasInodes := fs.InodesFree != nil
   161  	if hasInodes {
   162  		inodes = *fs.Inodes
   163  		inodesFree = *fs.InodesFree
   164  	}
   165  	return info.FsStats{
   166  		Device:          fs.Device,
   167  		Type:            fs.Type.String(),
   168  		Limit:           fs.Capacity,
   169  		Usage:           fs.Capacity - fs.Free,
   170  		HasInodes:       hasInodes,
   171  		Inodes:          inodes,
   172  		InodesFree:      inodesFree,
   173  		Available:       fs.Available,
   174  		ReadsCompleted:  fs.DiskStats.ReadsCompleted,
   175  		ReadsMerged:     fs.DiskStats.ReadsMerged,
   176  		SectorsRead:     fs.DiskStats.SectorsRead,
   177  		ReadTime:        fs.DiskStats.ReadTime,
   178  		WritesCompleted: fs.DiskStats.WritesCompleted,
   179  		WritesMerged:    fs.DiskStats.WritesMerged,
   180  		SectorsWritten:  fs.DiskStats.SectorsWritten,
   181  		WriteTime:       fs.DiskStats.WriteTime,
   182  		IoInProgress:    fs.DiskStats.IoInProgress,
   183  		IoTime:          fs.DiskStats.IoTime,
   184  		WeightedIoTime:  fs.DiskStats.WeightedIoTime,
   185  	}
   186  }
   187  
   188  func (h *rawContainerHandler) getFsStats(stats *info.ContainerStats) error {
   189  	var filesystems []fs.Fs
   190  	var err error
   191  	// Early exist if no disk metrics are to be collected.
   192  	if !h.includedMetrics.Has(container.DiskUsageMetrics) && !h.includedMetrics.Has(container.DiskIOMetrics) {
   193  		return nil
   194  	}
   195  
   196  	// Get Filesystem information only for the root cgroup.
   197  	if isRootCgroup(h.name) {
   198  		filesystems, err = h.fsInfo.GetGlobalFsInfo()
   199  		if err != nil {
   200  			return err
   201  		}
   202  	} else {
   203  		if len(h.externalMounts) > 0 {
   204  			mountSet := make(map[string]struct{})
   205  			for _, mount := range h.externalMounts {
   206  				mountSet[mount.HostDir] = struct{}{}
   207  			}
   208  			filesystems, err = h.fsInfo.GetFsInfoForPath(mountSet)
   209  			if err != nil {
   210  				return err
   211  			}
   212  		}
   213  	}
   214  
   215  	if h.includedMetrics.Has(container.DiskUsageMetrics) {
   216  		for i := range filesystems {
   217  			fs := filesystems[i]
   218  			stats.Filesystem = append(stats.Filesystem, fsToFsStats(&fs))
   219  		}
   220  	}
   221  
   222  	if h.includedMetrics.Has(container.DiskIOMetrics) {
   223  		common.AssignDeviceNamesToDiskStats(&fsNamer{fs: filesystems, factory: h.machineInfoFactory}, &stats.DiskIo)
   224  
   225  	}
   226  	return nil
   227  }
   228  
   229  func (h *rawContainerHandler) GetStats() (*info.ContainerStats, error) {
   230  	if *disableRootCgroupStats && isRootCgroup(h.name) {
   231  		return nil, nil
   232  	}
   233  	stats, err := h.libcontainerHandler.GetStats()
   234  	if err != nil {
   235  		return stats, err
   236  	}
   237  
   238  	// Get filesystem stats.
   239  	err = h.getFsStats(stats)
   240  	if err != nil {
   241  		return stats, err
   242  	}
   243  
   244  	return stats, nil
   245  }
   246  
   247  func (h *rawContainerHandler) GetCgroupPath(resource string) (string, error) {
   248  	var res string
   249  	if !cgroups.IsCgroup2UnifiedMode() {
   250  		res = resource
   251  	}
   252  	path, ok := h.cgroupPaths[res]
   253  	if !ok {
   254  		return "", fmt.Errorf("could not find path for resource %q for container %q", resource, h.name)
   255  	}
   256  	return path, nil
   257  }
   258  
   259  func (h *rawContainerHandler) GetContainerLabels() map[string]string {
   260  	return map[string]string{}
   261  }
   262  
   263  func (h *rawContainerHandler) GetContainerIPAddress() string {
   264  	// the IP address for the raw container corresponds to the system ip address.
   265  	return "127.0.0.1"
   266  }
   267  
   268  func (h *rawContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
   269  	return common.ListContainers(h.name, h.cgroupPaths, listType)
   270  }
   271  
   272  func (h *rawContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
   273  	return h.libcontainerHandler.GetProcesses()
   274  }
   275  
   276  func (h *rawContainerHandler) Exists() bool {
   277  	return common.CgroupExists(h.cgroupPaths)
   278  }
   279  
   280  func (h *rawContainerHandler) Type() container.ContainerType {
   281  	return container.ContainerTypeRaw
   282  }
   283  
   284  type fsNamer struct {
   285  	fs      []fs.Fs
   286  	factory info.MachineInfoFactory
   287  	info    common.DeviceNamer
   288  }
   289  
   290  func (n *fsNamer) DeviceName(major, minor uint64) (string, bool) {
   291  	for _, info := range n.fs {
   292  		if uint64(info.Major) == major && uint64(info.Minor) == minor {
   293  			return info.Device, true
   294  		}
   295  	}
   296  	if n.info == nil {
   297  		mi, err := n.factory.GetMachineInfo()
   298  		if err != nil {
   299  			return "", false
   300  		}
   301  		n.info = (*common.MachineInfoNamer)(mi)
   302  	}
   303  	return n.info.DeviceName(major, minor)
   304  }