github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/api/handlers/compat/containers_stats.go (about)

     1  package compat
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/containers/libpod/libpod"
     9  	"github.com/containers/libpod/libpod/define"
    10  	"github.com/containers/libpod/pkg/api/handlers/utils"
    11  	"github.com/containers/libpod/pkg/cgroups"
    12  	docker "github.com/docker/docker/api/types"
    13  	"github.com/gorilla/schema"
    14  	"github.com/pkg/errors"
    15  	"github.com/sirupsen/logrus"
    16  )
    17  
    18  const DefaultStatsPeriod = 5 * time.Second
    19  
    20  func StatsContainer(w http.ResponseWriter, r *http.Request) {
    21  	runtime := r.Context().Value("runtime").(*libpod.Runtime)
    22  	decoder := r.Context().Value("decoder").(*schema.Decoder)
    23  
    24  	query := struct {
    25  		Stream bool `schema:"stream"`
    26  	}{
    27  		Stream: true,
    28  	}
    29  	if err := decoder.Decode(&query, r.URL.Query()); err != nil {
    30  		utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "Failed to parse parameters for %s", r.URL.String()))
    31  		return
    32  	}
    33  
    34  	name := utils.GetName(r)
    35  	ctnr, err := runtime.LookupContainer(name)
    36  	if err != nil {
    37  		utils.ContainerNotFound(w, name, err)
    38  		return
    39  	}
    40  
    41  	// If the container isn't running, then let's not bother and return
    42  	// immediately.
    43  	state, err := ctnr.State()
    44  	if err != nil {
    45  		utils.InternalServerError(w, err)
    46  		return
    47  	}
    48  	if state != define.ContainerStateRunning && !query.Stream {
    49  		utils.InternalServerError(w, define.ErrCtrStateInvalid)
    50  		return
    51  	}
    52  
    53  	stats, err := ctnr.GetContainerStats(&libpod.ContainerStats{})
    54  	if err != nil {
    55  		utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain Container %s stats", name))
    56  		return
    57  	}
    58  
    59  	var preRead time.Time
    60  	var preCPUStats CPUStats
    61  	if query.Stream {
    62  		preRead = time.Now()
    63  		systemUsage, _ := cgroups.GetSystemCPUUsage()
    64  		preCPUStats = CPUStats{
    65  			CPUUsage: docker.CPUUsage{
    66  				TotalUsage:        stats.CPUNano,
    67  				PercpuUsage:       stats.PerCPU,
    68  				UsageInKernelmode: stats.CPUSystemNano,
    69  				UsageInUsermode:   stats.CPUNano - stats.CPUSystemNano,
    70  			},
    71  			CPU:            stats.CPU,
    72  			SystemUsage:    systemUsage,
    73  			OnlineCPUs:     0,
    74  			ThrottlingData: docker.ThrottlingData{},
    75  		}
    76  	}
    77  
    78  	for ok := true; ok; ok = query.Stream {
    79  		// Container stats
    80  		stats, err := ctnr.GetContainerStats(stats)
    81  		if err != nil {
    82  			utils.InternalServerError(w, err)
    83  			return
    84  		}
    85  		inspect, err := ctnr.Inspect(false)
    86  		if err != nil {
    87  			utils.InternalServerError(w, err)
    88  			return
    89  		}
    90  		// Cgroup stats
    91  		cgroupPath, err := ctnr.CGroupPath()
    92  		if err != nil {
    93  			utils.InternalServerError(w, err)
    94  			return
    95  		}
    96  		cgroup, err := cgroups.Load(cgroupPath)
    97  		if err != nil {
    98  			utils.InternalServerError(w, err)
    99  			return
   100  		}
   101  		cgroupStat, err := cgroup.Stat()
   102  		if err != nil {
   103  			utils.InternalServerError(w, err)
   104  			return
   105  		}
   106  
   107  		// FIXME: network inspection does not yet work entirely
   108  		net := make(map[string]docker.NetworkStats)
   109  		networkName := inspect.NetworkSettings.EndpointID
   110  		if networkName == "" {
   111  			networkName = "network"
   112  		}
   113  		net[networkName] = docker.NetworkStats{
   114  			RxBytes:    stats.NetInput,
   115  			RxPackets:  0,
   116  			RxErrors:   0,
   117  			RxDropped:  0,
   118  			TxBytes:    stats.NetOutput,
   119  			TxPackets:  0,
   120  			TxErrors:   0,
   121  			TxDropped:  0,
   122  			EndpointID: inspect.NetworkSettings.EndpointID,
   123  			InstanceID: "",
   124  		}
   125  
   126  		systemUsage, _ := cgroups.GetSystemCPUUsage()
   127  		s := StatsJSON{
   128  			Stats: Stats{
   129  				Read:    time.Now(),
   130  				PreRead: preRead,
   131  				PidsStats: docker.PidsStats{
   132  					Current: cgroupStat.Pids.Current,
   133  					Limit:   0,
   134  				},
   135  				BlkioStats: docker.BlkioStats{
   136  					IoServiceBytesRecursive: toBlkioStatEntry(cgroupStat.Blkio.IoServiceBytesRecursive),
   137  					IoServicedRecursive:     nil,
   138  					IoQueuedRecursive:       nil,
   139  					IoServiceTimeRecursive:  nil,
   140  					IoWaitTimeRecursive:     nil,
   141  					IoMergedRecursive:       nil,
   142  					IoTimeRecursive:         nil,
   143  					SectorsRecursive:        nil,
   144  				},
   145  				CPUStats: CPUStats{
   146  					CPUUsage: docker.CPUUsage{
   147  						TotalUsage:        cgroupStat.CPU.Usage.Total,
   148  						PercpuUsage:       cgroupStat.CPU.Usage.PerCPU,
   149  						UsageInKernelmode: cgroupStat.CPU.Usage.Kernel,
   150  						UsageInUsermode:   cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel,
   151  					},
   152  					CPU:         stats.CPU,
   153  					SystemUsage: systemUsage,
   154  					OnlineCPUs:  uint32(len(cgroupStat.CPU.Usage.PerCPU)),
   155  					ThrottlingData: docker.ThrottlingData{
   156  						Periods:          0,
   157  						ThrottledPeriods: 0,
   158  						ThrottledTime:    0,
   159  					},
   160  				},
   161  				PreCPUStats: preCPUStats,
   162  				MemoryStats: docker.MemoryStats{
   163  					Usage:             cgroupStat.Memory.Usage.Usage,
   164  					MaxUsage:          cgroupStat.Memory.Usage.Limit,
   165  					Stats:             nil,
   166  					Failcnt:           0,
   167  					Limit:             cgroupStat.Memory.Usage.Limit,
   168  					Commit:            0,
   169  					CommitPeak:        0,
   170  					PrivateWorkingSet: 0,
   171  				},
   172  			},
   173  			Name:     stats.Name,
   174  			ID:       stats.ContainerID,
   175  			Networks: net,
   176  		}
   177  
   178  		utils.WriteJSON(w, http.StatusOK, s)
   179  		if flusher, ok := w.(http.Flusher); ok {
   180  			flusher.Flush()
   181  		}
   182  
   183  		preRead = s.Read
   184  		bits, err := json.Marshal(s.CPUStats)
   185  		if err != nil {
   186  			logrus.Errorf("Unable to marshal cpu stats: %q", err)
   187  		}
   188  		if err := json.Unmarshal(bits, &preCPUStats); err != nil {
   189  			logrus.Errorf("Unable to unmarshal previous stats: %q", err)
   190  		}
   191  
   192  		// Only sleep when we're streaming.
   193  		if query.Stream {
   194  			time.Sleep(DefaultStatsPeriod)
   195  		}
   196  	}
   197  }
   198  
   199  func toBlkioStatEntry(entries []cgroups.BlkIOEntry) []docker.BlkioStatEntry {
   200  	results := make([]docker.BlkioStatEntry, len(entries))
   201  	for i, e := range entries {
   202  		bits, err := json.Marshal(e)
   203  		if err != nil {
   204  			logrus.Errorf("unable to marshal blkio stats: %q", err)
   205  		}
   206  		if err := json.Unmarshal(bits, &results[i]); err != nil {
   207  			logrus.Errorf("unable to unmarshal blkio stats: %q", err)
   208  		}
   209  	}
   210  	return results
   211  }