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 }