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  }