github.com/google/cadvisor@v0.49.1/info/v2/conversion.go (about) 1 // Copyright 2015 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 package v2 16 17 import ( 18 "fmt" 19 "time" 20 21 "k8s.io/klog/v2" 22 23 v1 "github.com/google/cadvisor/info/v1" 24 ) 25 26 func machineFsStatsFromV1(fsStats []v1.FsStats) []MachineFsStats { 27 var result []MachineFsStats 28 for i := range fsStats { 29 stat := fsStats[i] 30 readDuration := time.Millisecond * time.Duration(stat.ReadTime) 31 writeDuration := time.Millisecond * time.Duration(stat.WriteTime) 32 ioDuration := time.Millisecond * time.Duration(stat.IoTime) 33 weightedDuration := time.Millisecond * time.Duration(stat.WeightedIoTime) 34 machineFsStat := MachineFsStats{ 35 Device: stat.Device, 36 Type: stat.Type, 37 Capacity: &stat.Limit, 38 Usage: &stat.Usage, 39 Available: &stat.Available, 40 DiskStats: DiskStats{ 41 ReadsCompleted: &stat.ReadsCompleted, 42 ReadsMerged: &stat.ReadsMerged, 43 SectorsRead: &stat.SectorsRead, 44 ReadDuration: &readDuration, 45 WritesCompleted: &stat.WritesCompleted, 46 WritesMerged: &stat.WritesMerged, 47 SectorsWritten: &stat.SectorsWritten, 48 WriteDuration: &writeDuration, 49 IoInProgress: &stat.IoInProgress, 50 IoDuration: &ioDuration, 51 WeightedIoDuration: &weightedDuration, 52 }, 53 } 54 if stat.HasInodes { 55 machineFsStat.InodesFree = &stat.InodesFree 56 } 57 result = append(result, machineFsStat) 58 } 59 return result 60 } 61 62 func MachineStatsFromV1(cont *v1.ContainerInfo) []MachineStats { 63 var stats []MachineStats 64 var last *v1.ContainerStats 65 for i := range cont.Stats { 66 val := cont.Stats[i] 67 stat := MachineStats{ 68 Timestamp: val.Timestamp, 69 } 70 if cont.Spec.HasCpu { 71 stat.Cpu = &val.Cpu 72 cpuInst, err := InstCpuStats(last, val) 73 if err != nil { 74 klog.Warningf("Could not get instant cpu stats: %v", err) 75 } else { 76 stat.CpuInst = cpuInst 77 } 78 last = val 79 } 80 if cont.Spec.HasMemory { 81 stat.Memory = &val.Memory 82 } 83 if cont.Spec.HasNetwork { 84 stat.Network = &NetworkStats{ 85 // FIXME: Use reflection instead. 86 Tcp: TcpStat(val.Network.Tcp), 87 Tcp6: TcpStat(val.Network.Tcp6), 88 Interfaces: val.Network.Interfaces, 89 } 90 } 91 if cont.Spec.HasFilesystem { 92 stat.Filesystem = machineFsStatsFromV1(val.Filesystem) 93 } 94 // TODO(rjnagal): Handle load stats. 95 stats = append(stats, stat) 96 } 97 return stats 98 } 99 100 func ContainerStatsFromV1(containerName string, spec *v1.ContainerSpec, stats []*v1.ContainerStats) []*ContainerStats { 101 newStats := make([]*ContainerStats, 0, len(stats)) 102 var last *v1.ContainerStats 103 for _, val := range stats { 104 stat := &ContainerStats{ 105 Timestamp: val.Timestamp, 106 ReferencedMemory: val.ReferencedMemory, 107 } 108 if spec.HasCpu { 109 stat.Cpu = &val.Cpu 110 cpuInst, err := InstCpuStats(last, val) 111 if err != nil { 112 klog.Warningf("Could not get instant cpu stats: %v", err) 113 } else { 114 stat.CpuInst = cpuInst 115 } 116 last = val 117 } 118 if spec.HasMemory { 119 stat.Memory = &val.Memory 120 } 121 if spec.HasHugetlb { 122 stat.Hugetlb = &val.Hugetlb 123 } 124 if spec.HasNetwork { 125 // TODO: Handle TcpStats 126 stat.Network = &NetworkStats{ 127 Tcp: TcpStat(val.Network.Tcp), 128 Tcp6: TcpStat(val.Network.Tcp6), 129 Interfaces: val.Network.Interfaces, 130 } 131 } 132 if spec.HasProcesses { 133 stat.Processes = &val.Processes 134 } 135 if spec.HasFilesystem { 136 if len(val.Filesystem) == 1 { 137 stat.Filesystem = &FilesystemStats{ 138 TotalUsageBytes: &val.Filesystem[0].Usage, 139 BaseUsageBytes: &val.Filesystem[0].BaseUsage, 140 InodeUsage: &val.Filesystem[0].Inodes, 141 } 142 } else if len(val.Filesystem) > 1 && containerName != "/" { 143 // Cannot handle multiple devices per container. 144 klog.V(4).Infof("failed to handle multiple devices for container %s. Skipping Filesystem stats", containerName) 145 } 146 } 147 if spec.HasDiskIo { 148 stat.DiskIo = &val.DiskIo 149 } 150 if spec.HasCustomMetrics { 151 stat.CustomMetrics = val.CustomMetrics 152 } 153 if len(val.Accelerators) > 0 { 154 stat.Accelerators = val.Accelerators 155 } 156 if len(val.PerfStats) > 0 { 157 stat.PerfStats = val.PerfStats 158 } 159 if len(val.PerfUncoreStats) > 0 { 160 stat.PerfUncoreStats = val.PerfUncoreStats 161 } 162 if len(val.Resctrl.MemoryBandwidth) > 0 || len(val.Resctrl.Cache) > 0 { 163 stat.Resctrl = val.Resctrl 164 } 165 // TODO(rjnagal): Handle load stats. 166 newStats = append(newStats, stat) 167 } 168 return newStats 169 } 170 171 func DeprecatedStatsFromV1(cont *v1.ContainerInfo) []DeprecatedContainerStats { 172 stats := make([]DeprecatedContainerStats, 0, len(cont.Stats)) 173 var last *v1.ContainerStats 174 for _, val := range cont.Stats { 175 stat := DeprecatedContainerStats{ 176 Timestamp: val.Timestamp, 177 HasCpu: cont.Spec.HasCpu, 178 HasMemory: cont.Spec.HasMemory, 179 HasHugetlb: cont.Spec.HasHugetlb, 180 HasNetwork: cont.Spec.HasNetwork, 181 HasFilesystem: cont.Spec.HasFilesystem, 182 HasDiskIo: cont.Spec.HasDiskIo, 183 HasCustomMetrics: cont.Spec.HasCustomMetrics, 184 ReferencedMemory: val.ReferencedMemory, 185 } 186 if stat.HasCpu { 187 stat.Cpu = val.Cpu 188 cpuInst, err := InstCpuStats(last, val) 189 if err != nil { 190 klog.Warningf("Could not get instant cpu stats: %v", err) 191 } else { 192 stat.CpuInst = cpuInst 193 } 194 last = val 195 } 196 if stat.HasMemory { 197 stat.Memory = val.Memory 198 } 199 if stat.HasHugetlb { 200 stat.Hugetlb = val.Hugetlb 201 } 202 if stat.HasNetwork { 203 stat.Network.Interfaces = val.Network.Interfaces 204 } 205 if stat.HasProcesses { 206 stat.Processes = val.Processes 207 } 208 if stat.HasFilesystem { 209 stat.Filesystem = val.Filesystem 210 } 211 if stat.HasDiskIo { 212 stat.DiskIo = val.DiskIo 213 } 214 if stat.HasCustomMetrics { 215 stat.CustomMetrics = val.CustomMetrics 216 } 217 if len(val.PerfStats) > 0 { 218 stat.PerfStats = val.PerfStats 219 } 220 if len(val.PerfUncoreStats) > 0 { 221 stat.PerfUncoreStats = val.PerfUncoreStats 222 } 223 if len(val.Resctrl.MemoryBandwidth) > 0 || len(val.Resctrl.Cache) > 0 { 224 stat.Resctrl = val.Resctrl 225 } 226 // TODO(rjnagal): Handle load stats. 227 stats = append(stats, stat) 228 } 229 return stats 230 } 231 232 func InstCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) { 233 if last == nil { 234 return nil, nil 235 } 236 if !cur.Timestamp.After(last.Timestamp) { 237 return nil, fmt.Errorf("container stats move backwards in time") 238 } 239 if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) { 240 return nil, fmt.Errorf("different number of cpus") 241 } 242 timeDelta := cur.Timestamp.Sub(last.Timestamp) 243 // Nanoseconds to gain precision and avoid having zero seconds if the 244 // difference between the timestamps is just under a second 245 timeDeltaNs := uint64(timeDelta.Nanoseconds()) 246 convertToRate := func(lastValue, curValue uint64) (uint64, error) { 247 if curValue < lastValue { 248 return 0, fmt.Errorf("cumulative stats decrease") 249 } 250 valueDelta := curValue - lastValue 251 // Use float64 to keep precision 252 return uint64(float64(valueDelta) / float64(timeDeltaNs) * 1e9), nil 253 } 254 total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total) 255 if err != nil { 256 return nil, err 257 } 258 percpu := make([]uint64, len(last.Cpu.Usage.PerCpu)) 259 for i := range percpu { 260 var err error 261 percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i]) 262 if err != nil { 263 return nil, err 264 } 265 } 266 user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User) 267 if err != nil { 268 return nil, err 269 } 270 system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System) 271 if err != nil { 272 return nil, err 273 } 274 return &CpuInstStats{ 275 Usage: CpuInstUsage{ 276 Total: total, 277 PerCpu: percpu, 278 User: user, 279 System: system, 280 }, 281 }, nil 282 } 283 284 // Get V2 container spec from v1 container info. 285 func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace string) ContainerSpec { 286 specV2 := ContainerSpec{ 287 CreationTime: specV1.CreationTime, 288 HasCpu: specV1.HasCpu, 289 HasMemory: specV1.HasMemory, 290 HasHugetlb: specV1.HasHugetlb, 291 HasFilesystem: specV1.HasFilesystem, 292 HasNetwork: specV1.HasNetwork, 293 HasProcesses: specV1.HasProcesses, 294 HasDiskIo: specV1.HasDiskIo, 295 HasCustomMetrics: specV1.HasCustomMetrics, 296 Image: specV1.Image, 297 Labels: specV1.Labels, 298 Envs: specV1.Envs, 299 } 300 if specV1.HasCpu { 301 specV2.Cpu.Limit = specV1.Cpu.Limit 302 specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit 303 specV2.Cpu.Mask = specV1.Cpu.Mask 304 } 305 if specV1.HasMemory { 306 specV2.Memory.Limit = specV1.Memory.Limit 307 specV2.Memory.Reservation = specV1.Memory.Reservation 308 specV2.Memory.SwapLimit = specV1.Memory.SwapLimit 309 } 310 if specV1.HasCustomMetrics { 311 specV2.CustomMetrics = specV1.CustomMetrics 312 } 313 specV2.Aliases = aliases 314 specV2.Namespace = namespace 315 return specV2 316 }