mirror of https://github.com/k3s-io/k3s
281 lines
8.0 KiB
Go
281 lines
8.0 KiB
Go
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package v2
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/cadvisor/info/v1"
|
|
"k8s.io/klog"
|
|
)
|
|
|
|
func machineFsStatsFromV1(fsStats []v1.FsStats) []MachineFsStats {
|
|
var result []MachineFsStats
|
|
for i := range fsStats {
|
|
stat := fsStats[i]
|
|
readDuration := time.Millisecond * time.Duration(stat.ReadTime)
|
|
writeDuration := time.Millisecond * time.Duration(stat.WriteTime)
|
|
ioDuration := time.Millisecond * time.Duration(stat.IoTime)
|
|
weightedDuration := time.Millisecond * time.Duration(stat.WeightedIoTime)
|
|
machineFsStat := MachineFsStats{
|
|
Device: stat.Device,
|
|
Type: stat.Type,
|
|
Capacity: &stat.Limit,
|
|
Usage: &stat.Usage,
|
|
Available: &stat.Available,
|
|
DiskStats: DiskStats{
|
|
ReadsCompleted: &stat.ReadsCompleted,
|
|
ReadsMerged: &stat.ReadsMerged,
|
|
SectorsRead: &stat.SectorsRead,
|
|
ReadDuration: &readDuration,
|
|
WritesCompleted: &stat.WritesCompleted,
|
|
WritesMerged: &stat.WritesMerged,
|
|
SectorsWritten: &stat.SectorsWritten,
|
|
WriteDuration: &writeDuration,
|
|
IoInProgress: &stat.IoInProgress,
|
|
IoDuration: &ioDuration,
|
|
WeightedIoDuration: &weightedDuration,
|
|
},
|
|
}
|
|
if stat.HasInodes {
|
|
machineFsStat.InodesFree = &stat.InodesFree
|
|
}
|
|
result = append(result, machineFsStat)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func MachineStatsFromV1(cont *v1.ContainerInfo) []MachineStats {
|
|
var stats []MachineStats
|
|
var last *v1.ContainerStats
|
|
for i := range cont.Stats {
|
|
val := cont.Stats[i]
|
|
stat := MachineStats{
|
|
Timestamp: val.Timestamp,
|
|
}
|
|
if cont.Spec.HasCpu {
|
|
stat.Cpu = &val.Cpu
|
|
cpuInst, err := InstCpuStats(last, val)
|
|
if err != nil {
|
|
klog.Warningf("Could not get instant cpu stats: %v", err)
|
|
} else {
|
|
stat.CpuInst = cpuInst
|
|
}
|
|
last = val
|
|
}
|
|
if cont.Spec.HasMemory {
|
|
stat.Memory = &val.Memory
|
|
}
|
|
if cont.Spec.HasNetwork {
|
|
stat.Network = &NetworkStats{
|
|
// FIXME: Use reflection instead.
|
|
Tcp: TcpStat(val.Network.Tcp),
|
|
Tcp6: TcpStat(val.Network.Tcp6),
|
|
Interfaces: val.Network.Interfaces,
|
|
}
|
|
}
|
|
if cont.Spec.HasFilesystem {
|
|
stat.Filesystem = machineFsStatsFromV1(val.Filesystem)
|
|
}
|
|
// TODO(rjnagal): Handle load stats.
|
|
stats = append(stats, stat)
|
|
}
|
|
return stats
|
|
}
|
|
|
|
func ContainerStatsFromV1(containerName string, spec *v1.ContainerSpec, stats []*v1.ContainerStats) []*ContainerStats {
|
|
newStats := make([]*ContainerStats, 0, len(stats))
|
|
var last *v1.ContainerStats
|
|
for _, val := range stats {
|
|
stat := &ContainerStats{
|
|
Timestamp: val.Timestamp,
|
|
}
|
|
if spec.HasCpu {
|
|
stat.Cpu = &val.Cpu
|
|
cpuInst, err := InstCpuStats(last, val)
|
|
if err != nil {
|
|
klog.Warningf("Could not get instant cpu stats: %v", err)
|
|
} else {
|
|
stat.CpuInst = cpuInst
|
|
}
|
|
last = val
|
|
}
|
|
if spec.HasMemory {
|
|
stat.Memory = &val.Memory
|
|
}
|
|
if spec.HasNetwork {
|
|
// TODO: Handle TcpStats
|
|
stat.Network = &NetworkStats{
|
|
Tcp: TcpStat(val.Network.Tcp),
|
|
Tcp6: TcpStat(val.Network.Tcp6),
|
|
Interfaces: val.Network.Interfaces,
|
|
}
|
|
}
|
|
if spec.HasFilesystem {
|
|
if len(val.Filesystem) == 1 {
|
|
stat.Filesystem = &FilesystemStats{
|
|
TotalUsageBytes: &val.Filesystem[0].Usage,
|
|
BaseUsageBytes: &val.Filesystem[0].BaseUsage,
|
|
InodeUsage: &val.Filesystem[0].Inodes,
|
|
}
|
|
} else if len(val.Filesystem) > 1 && containerName != "/" {
|
|
// Cannot handle multiple devices per container.
|
|
klog.V(4).Infof("failed to handle multiple devices for container %s. Skipping Filesystem stats", containerName)
|
|
}
|
|
}
|
|
if spec.HasDiskIo {
|
|
stat.DiskIo = &val.DiskIo
|
|
}
|
|
if spec.HasCustomMetrics {
|
|
stat.CustomMetrics = val.CustomMetrics
|
|
}
|
|
if len(val.Accelerators) > 0 {
|
|
stat.Accelerators = val.Accelerators
|
|
}
|
|
// TODO(rjnagal): Handle load stats.
|
|
newStats = append(newStats, stat)
|
|
}
|
|
return newStats
|
|
}
|
|
|
|
func DeprecatedStatsFromV1(cont *v1.ContainerInfo) []DeprecatedContainerStats {
|
|
stats := make([]DeprecatedContainerStats, 0, len(cont.Stats))
|
|
var last *v1.ContainerStats
|
|
for _, val := range cont.Stats {
|
|
stat := DeprecatedContainerStats{
|
|
Timestamp: val.Timestamp,
|
|
HasCpu: cont.Spec.HasCpu,
|
|
HasMemory: cont.Spec.HasMemory,
|
|
HasNetwork: cont.Spec.HasNetwork,
|
|
HasFilesystem: cont.Spec.HasFilesystem,
|
|
HasDiskIo: cont.Spec.HasDiskIo,
|
|
HasCustomMetrics: cont.Spec.HasCustomMetrics,
|
|
}
|
|
if stat.HasCpu {
|
|
stat.Cpu = val.Cpu
|
|
cpuInst, err := InstCpuStats(last, val)
|
|
if err != nil {
|
|
klog.Warningf("Could not get instant cpu stats: %v", err)
|
|
} else {
|
|
stat.CpuInst = cpuInst
|
|
}
|
|
last = val
|
|
}
|
|
if stat.HasMemory {
|
|
stat.Memory = val.Memory
|
|
}
|
|
if stat.HasNetwork {
|
|
stat.Network.Interfaces = val.Network.Interfaces
|
|
}
|
|
if stat.HasFilesystem {
|
|
stat.Filesystem = val.Filesystem
|
|
}
|
|
if stat.HasDiskIo {
|
|
stat.DiskIo = val.DiskIo
|
|
}
|
|
if stat.HasCustomMetrics {
|
|
stat.CustomMetrics = val.CustomMetrics
|
|
}
|
|
// TODO(rjnagal): Handle load stats.
|
|
stats = append(stats, stat)
|
|
}
|
|
return stats
|
|
}
|
|
|
|
func InstCpuStats(last, cur *v1.ContainerStats) (*CpuInstStats, error) {
|
|
if last == nil {
|
|
return nil, nil
|
|
}
|
|
if !cur.Timestamp.After(last.Timestamp) {
|
|
return nil, fmt.Errorf("container stats move backwards in time")
|
|
}
|
|
if len(last.Cpu.Usage.PerCpu) != len(cur.Cpu.Usage.PerCpu) {
|
|
return nil, fmt.Errorf("different number of cpus")
|
|
}
|
|
timeDelta := cur.Timestamp.Sub(last.Timestamp)
|
|
// Nanoseconds to gain precision and avoid having zero seconds if the
|
|
// difference between the timestamps is just under a second
|
|
timeDeltaNs := uint64(timeDelta.Nanoseconds())
|
|
convertToRate := func(lastValue, curValue uint64) (uint64, error) {
|
|
if curValue < lastValue {
|
|
return 0, fmt.Errorf("cumulative stats decrease")
|
|
}
|
|
valueDelta := curValue - lastValue
|
|
// Use float64 to keep precision
|
|
return uint64(float64(valueDelta) / float64(timeDeltaNs) * 1e9), nil
|
|
}
|
|
total, err := convertToRate(last.Cpu.Usage.Total, cur.Cpu.Usage.Total)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
percpu := make([]uint64, len(last.Cpu.Usage.PerCpu))
|
|
for i := range percpu {
|
|
var err error
|
|
percpu[i], err = convertToRate(last.Cpu.Usage.PerCpu[i], cur.Cpu.Usage.PerCpu[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
user, err := convertToRate(last.Cpu.Usage.User, cur.Cpu.Usage.User)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
system, err := convertToRate(last.Cpu.Usage.System, cur.Cpu.Usage.System)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CpuInstStats{
|
|
Usage: CpuInstUsage{
|
|
Total: total,
|
|
PerCpu: percpu,
|
|
User: user,
|
|
System: system,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Get V2 container spec from v1 container info.
|
|
func ContainerSpecFromV1(specV1 *v1.ContainerSpec, aliases []string, namespace string) ContainerSpec {
|
|
specV2 := ContainerSpec{
|
|
CreationTime: specV1.CreationTime,
|
|
HasCpu: specV1.HasCpu,
|
|
HasMemory: specV1.HasMemory,
|
|
HasFilesystem: specV1.HasFilesystem,
|
|
HasNetwork: specV1.HasNetwork,
|
|
HasDiskIo: specV1.HasDiskIo,
|
|
HasCustomMetrics: specV1.HasCustomMetrics,
|
|
Image: specV1.Image,
|
|
Labels: specV1.Labels,
|
|
Envs: specV1.Envs,
|
|
}
|
|
if specV1.HasCpu {
|
|
specV2.Cpu.Limit = specV1.Cpu.Limit
|
|
specV2.Cpu.MaxLimit = specV1.Cpu.MaxLimit
|
|
specV2.Cpu.Mask = specV1.Cpu.Mask
|
|
}
|
|
if specV1.HasMemory {
|
|
specV2.Memory.Limit = specV1.Memory.Limit
|
|
specV2.Memory.Reservation = specV1.Memory.Reservation
|
|
specV2.Memory.SwapLimit = specV1.Memory.SwapLimit
|
|
}
|
|
if specV1.HasCustomMetrics {
|
|
specV2.CustomMetrics = specV1.CustomMetrics
|
|
}
|
|
specV2.Aliases = aliases
|
|
specV2.Namespace = namespace
|
|
return specV2
|
|
}
|