github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/container/stats_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package container
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  	"strings"
    24  	"time"
    25  
    26  	v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
    27  	v2 "github.com/containerd/cgroups/v3/cgroup2/stats"
    28  	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
    29  	"github.com/containerd/nerdctl/v2/pkg/statsutil"
    30  	"github.com/vishvananda/netlink"
    31  	"github.com/vishvananda/netns"
    32  )
    33  
    34  //nolint:nakedret
    35  func setContainerStatsAndRenderStatsEntry(previousStats *statsutil.ContainerStats, firstSet bool, anydata interface{}, pid int, interfaces []native.NetInterface) (statsEntry statsutil.StatsEntry, err error) {
    36  
    37  	var (
    38  		data  *v1.Metrics
    39  		data2 *v2.Metrics
    40  	)
    41  
    42  	switch v := anydata.(type) {
    43  	case *v1.Metrics:
    44  		data = v
    45  	case *v2.Metrics:
    46  		data2 = v
    47  	default:
    48  		err = errors.New("cannot convert metric data to cgroups.Metrics")
    49  		return
    50  	}
    51  
    52  	var nlinks []netlink.Link
    53  
    54  	if !firstSet {
    55  		var (
    56  			nlink    netlink.Link
    57  			nlHandle *netlink.Handle
    58  			ns       netns.NsHandle
    59  		)
    60  
    61  		ns, err = netns.GetFromPid(pid)
    62  		if err != nil {
    63  			err = fmt.Errorf("failed to retrieve the statistics in netns %s: %v", ns, err)
    64  			return
    65  		}
    66  		defer func() {
    67  			err = ns.Close()
    68  		}()
    69  
    70  		nlHandle, err = netlink.NewHandleAt(ns)
    71  		if err != nil {
    72  			err = fmt.Errorf("failed to retrieve the statistics in netns %s: %v", ns, err)
    73  			return
    74  		}
    75  		defer nlHandle.Close()
    76  
    77  		for _, v := range interfaces {
    78  			nlink, err = nlHandle.LinkByIndex(v.Index)
    79  			if err != nil {
    80  				err = fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", v.Name, ns, err)
    81  				return
    82  			}
    83  			//exclude inactive interface
    84  			if nlink.Attrs().Flags&net.FlagUp != 0 {
    85  
    86  				//exclude loopback interface
    87  				if nlink.Attrs().Flags&net.FlagLoopback != 0 || strings.HasPrefix(nlink.Attrs().Name, "lo") {
    88  					continue
    89  				}
    90  				nlinks = append(nlinks, nlink)
    91  			}
    92  		}
    93  	}
    94  
    95  	if data != nil {
    96  		if !firstSet {
    97  			statsEntry, err = statsutil.SetCgroupStatsFields(previousStats, data, nlinks)
    98  		}
    99  		previousStats.CgroupCPU = data.CPU.Usage.Total
   100  		previousStats.CgroupSystem = data.CPU.Usage.Kernel
   101  		if err != nil {
   102  			return
   103  		}
   104  	} else if data2 != nil {
   105  		if !firstSet {
   106  			statsEntry, err = statsutil.SetCgroup2StatsFields(previousStats, data2, nlinks)
   107  		}
   108  		previousStats.Cgroup2CPU = data2.CPU.UsageUsec * 1000
   109  		previousStats.Cgroup2System = data2.CPU.SystemUsec * 1000
   110  		if err != nil {
   111  			return
   112  		}
   113  	}
   114  	previousStats.Time = time.Now()
   115  
   116  	return
   117  }