github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/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/podman/v2/libpod" 9 "github.com/containers/podman/v2/libpod/define" 10 "github.com/containers/podman/v2/pkg/api/handlers/utils" 11 "github.com/containers/podman/v2/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 { 49 utils.Error(w, "Container not running and streaming requested", http.StatusConflict, define.ErrCtrStateInvalid) 50 return 51 } 52 53 stats, err := ctnr.GetContainerStats(&define.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 // Write header and content type. 79 w.WriteHeader(http.StatusOK) 80 w.Header().Add("Content-Type", "application/json") 81 if flusher, ok := w.(http.Flusher); ok { 82 flusher.Flush() 83 } 84 85 // Setup JSON encoder for streaming. 86 coder := json.NewEncoder(w) 87 coder.SetEscapeHTML(true) 88 89 streamLabel: // A label to flatten the scope 90 select { 91 case <-r.Context().Done(): 92 logrus.Debugf("Client connection (container stats) cancelled") 93 94 default: 95 // Container stats 96 stats, err := ctnr.GetContainerStats(stats) 97 if err != nil { 98 logrus.Errorf("Unable to get container stats: %v", err) 99 return 100 } 101 inspect, err := ctnr.Inspect(false) 102 if err != nil { 103 logrus.Errorf("Unable to inspect container: %v", err) 104 return 105 } 106 // Cgroup stats 107 cgroupPath, err := ctnr.CGroupPath() 108 if err != nil { 109 logrus.Errorf("Unable to get cgroup path of container: %v", err) 110 return 111 } 112 cgroup, err := cgroups.Load(cgroupPath) 113 if err != nil { 114 logrus.Errorf("Unable to load cgroup: %v", err) 115 return 116 } 117 cgroupStat, err := cgroup.Stat() 118 if err != nil { 119 logrus.Errorf("Unable to get cgroup stats: %v", err) 120 return 121 } 122 123 // FIXME: network inspection does not yet work entirely 124 net := make(map[string]docker.NetworkStats) 125 networkName := inspect.NetworkSettings.EndpointID 126 if networkName == "" { 127 networkName = "network" 128 } 129 net[networkName] = docker.NetworkStats{ 130 RxBytes: stats.NetInput, 131 RxPackets: 0, 132 RxErrors: 0, 133 RxDropped: 0, 134 TxBytes: stats.NetOutput, 135 TxPackets: 0, 136 TxErrors: 0, 137 TxDropped: 0, 138 EndpointID: inspect.NetworkSettings.EndpointID, 139 InstanceID: "", 140 } 141 142 systemUsage, _ := cgroups.GetSystemCPUUsage() 143 s := StatsJSON{ 144 Stats: Stats{ 145 Read: time.Now(), 146 PreRead: preRead, 147 PidsStats: docker.PidsStats{ 148 Current: cgroupStat.Pids.Current, 149 Limit: 0, 150 }, 151 BlkioStats: docker.BlkioStats{ 152 IoServiceBytesRecursive: toBlkioStatEntry(cgroupStat.Blkio.IoServiceBytesRecursive), 153 IoServicedRecursive: nil, 154 IoQueuedRecursive: nil, 155 IoServiceTimeRecursive: nil, 156 IoWaitTimeRecursive: nil, 157 IoMergedRecursive: nil, 158 IoTimeRecursive: nil, 159 SectorsRecursive: nil, 160 }, 161 CPUStats: CPUStats{ 162 CPUUsage: docker.CPUUsage{ 163 TotalUsage: cgroupStat.CPU.Usage.Total, 164 PercpuUsage: cgroupStat.CPU.Usage.PerCPU, 165 UsageInKernelmode: cgroupStat.CPU.Usage.Kernel, 166 UsageInUsermode: cgroupStat.CPU.Usage.Total - cgroupStat.CPU.Usage.Kernel, 167 }, 168 CPU: stats.CPU, 169 SystemUsage: systemUsage, 170 OnlineCPUs: uint32(len(cgroupStat.CPU.Usage.PerCPU)), 171 ThrottlingData: docker.ThrottlingData{ 172 Periods: 0, 173 ThrottledPeriods: 0, 174 ThrottledTime: 0, 175 }, 176 }, 177 PreCPUStats: preCPUStats, 178 MemoryStats: docker.MemoryStats{ 179 Usage: cgroupStat.Memory.Usage.Usage, 180 MaxUsage: cgroupStat.Memory.Usage.Limit, 181 Stats: nil, 182 Failcnt: 0, 183 Limit: cgroupStat.Memory.Usage.Limit, 184 Commit: 0, 185 CommitPeak: 0, 186 PrivateWorkingSet: 0, 187 }, 188 }, 189 Name: stats.Name, 190 ID: stats.ContainerID, 191 Networks: net, 192 } 193 194 if err := coder.Encode(s); err != nil { 195 logrus.Errorf("Unable to encode stats: %v", err) 196 return 197 } 198 if flusher, ok := w.(http.Flusher); ok { 199 flusher.Flush() 200 } 201 202 if !query.Stream { 203 return 204 } 205 206 preRead = s.Read 207 bits, err := json.Marshal(s.CPUStats) 208 if err != nil { 209 logrus.Errorf("Unable to marshal cpu stats: %q", err) 210 } 211 if err := json.Unmarshal(bits, &preCPUStats); err != nil { 212 logrus.Errorf("Unable to unmarshal previous stats: %q", err) 213 } 214 215 time.Sleep(DefaultStatsPeriod) 216 goto streamLabel 217 } 218 } 219 220 func toBlkioStatEntry(entries []cgroups.BlkIOEntry) []docker.BlkioStatEntry { 221 results := make([]docker.BlkioStatEntry, len(entries)) 222 for i, e := range entries { 223 bits, err := json.Marshal(e) 224 if err != nil { 225 logrus.Errorf("unable to marshal blkio stats: %q", err) 226 } 227 if err := json.Unmarshal(bits, &results[i]); err != nil { 228 logrus.Errorf("unable to unmarshal blkio stats: %q", err) 229 } 230 } 231 return results 232 }