mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-17 09:15:59 +08:00

Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
202 lines
5.2 KiB
Go
202 lines
5.2 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.
|
|
|
|
// Utility methods to calculate percentiles.
|
|
|
|
package summary
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
|
|
info "github.com/google/cadvisor/info/v2"
|
|
)
|
|
|
|
const secondsToMilliSeconds = 1000
|
|
const milliSecondsToNanoSeconds = 1000000
|
|
const secondsToNanoSeconds = secondsToMilliSeconds * milliSecondsToNanoSeconds
|
|
|
|
type Uint64Slice []uint64
|
|
|
|
func (s Uint64Slice) Len() int { return len(s) }
|
|
func (s Uint64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
func (s Uint64Slice) Less(i, j int) bool { return s[i] < s[j] }
|
|
|
|
// Get percentile of the provided samples. Round to integer.
|
|
func (s Uint64Slice) GetPercentile(d float64) uint64 {
|
|
if d < 0.0 || d > 1.0 {
|
|
return 0
|
|
}
|
|
count := s.Len()
|
|
if count == 0 {
|
|
return 0
|
|
}
|
|
sort.Sort(s)
|
|
n := float64(d * (float64(count) + 1))
|
|
idx, frac := math.Modf(n)
|
|
index := int(idx)
|
|
percentile := float64(s[index-1])
|
|
if index > 1 && index < count {
|
|
percentile += frac * float64(s[index]-s[index-1])
|
|
}
|
|
return uint64(percentile)
|
|
}
|
|
|
|
type mean struct {
|
|
// current count.
|
|
count uint64
|
|
// current mean.
|
|
Mean float64
|
|
}
|
|
|
|
func (m *mean) Add(value uint64) {
|
|
m.count++
|
|
if m.count == 1 {
|
|
m.Mean = float64(value)
|
|
return
|
|
}
|
|
c := float64(m.count)
|
|
v := float64(value)
|
|
m.Mean = (m.Mean*(c-1) + v) / c
|
|
}
|
|
|
|
type Percentile interface {
|
|
Add(info.Percentiles)
|
|
AddSample(uint64)
|
|
GetAllPercentiles() info.Percentiles
|
|
}
|
|
|
|
type resource struct {
|
|
// list of samples being tracked.
|
|
samples Uint64Slice
|
|
// average from existing samples.
|
|
mean mean
|
|
// maximum value seen so far in the added samples.
|
|
max uint64
|
|
}
|
|
|
|
// Adds a new percentile sample.
|
|
func (r *resource) Add(p info.Percentiles) {
|
|
if !p.Present {
|
|
return
|
|
}
|
|
if p.Max > r.max {
|
|
r.max = p.Max
|
|
}
|
|
r.mean.Add(p.Mean)
|
|
// Selecting 90p of 90p :(
|
|
r.samples = append(r.samples, p.Ninety)
|
|
}
|
|
|
|
// Add a single sample. Internally, we convert it to a fake percentile sample.
|
|
func (r *resource) AddSample(val uint64) {
|
|
sample := info.Percentiles{
|
|
Present: true,
|
|
Mean: val,
|
|
Max: val,
|
|
Fifty: val,
|
|
Ninety: val,
|
|
NinetyFive: val,
|
|
}
|
|
r.Add(sample)
|
|
}
|
|
|
|
// Get max, average, and 90p from existing samples.
|
|
func (r *resource) GetAllPercentiles() info.Percentiles {
|
|
p := info.Percentiles{}
|
|
p.Mean = uint64(r.mean.Mean)
|
|
p.Max = r.max
|
|
p.Fifty = r.samples.GetPercentile(0.5)
|
|
p.Ninety = r.samples.GetPercentile(0.9)
|
|
p.NinetyFive = r.samples.GetPercentile(0.95)
|
|
p.Present = true
|
|
return p
|
|
}
|
|
|
|
func NewResource(size int) Percentile {
|
|
return &resource{
|
|
samples: make(Uint64Slice, 0, size),
|
|
mean: mean{count: 0, Mean: 0},
|
|
}
|
|
}
|
|
|
|
// Return aggregated percentiles from the provided percentile samples.
|
|
func GetDerivedPercentiles(stats []*info.Usage) info.Usage {
|
|
cpu := NewResource(len(stats))
|
|
memory := NewResource(len(stats))
|
|
for _, stat := range stats {
|
|
cpu.Add(stat.Cpu)
|
|
memory.Add(stat.Memory)
|
|
}
|
|
usage := info.Usage{}
|
|
usage.Cpu = cpu.GetAllPercentiles()
|
|
usage.Memory = memory.GetAllPercentiles()
|
|
return usage
|
|
}
|
|
|
|
// Calculate part of a minute this sample set represent.
|
|
func getPercentComplete(stats []*secondSample) (percent int32) {
|
|
numSamples := len(stats)
|
|
if numSamples > 1 {
|
|
percent = 100
|
|
timeRange := stats[numSamples-1].Timestamp.Sub(stats[0].Timestamp).Nanoseconds()
|
|
// allow some slack
|
|
if timeRange < 58*secondsToNanoSeconds {
|
|
percent = int32((timeRange * 100) / 60 * secondsToNanoSeconds)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Calculate cpurate from two consecutive total cpu usage samples.
|
|
func getCPURate(latest, previous secondSample) (uint64, error) {
|
|
elapsed := latest.Timestamp.Sub(previous.Timestamp).Nanoseconds()
|
|
if elapsed < 10*milliSecondsToNanoSeconds {
|
|
return 0, fmt.Errorf("elapsed time too small: %d ns: time now %s last %s", elapsed, latest.Timestamp.String(), previous.Timestamp.String())
|
|
}
|
|
if latest.Cpu < previous.Cpu {
|
|
return 0, fmt.Errorf("bad sample: cumulative cpu usage dropped from %d to %d", latest.Cpu, previous.Cpu)
|
|
}
|
|
// Cpurate is calculated in cpu-milliseconds per second.
|
|
cpuRate := (latest.Cpu - previous.Cpu) * secondsToMilliSeconds / uint64(elapsed)
|
|
return cpuRate, nil
|
|
}
|
|
|
|
// Returns a percentile sample for a minute by aggregating seconds samples.
|
|
func GetMinutePercentiles(stats []*secondSample) info.Usage {
|
|
lastSample := secondSample{}
|
|
cpu := NewResource(len(stats))
|
|
memory := NewResource(len(stats))
|
|
for _, stat := range stats {
|
|
if !lastSample.Timestamp.IsZero() {
|
|
cpuRate, err := getCPURate(*stat, lastSample)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
cpu.AddSample(cpuRate)
|
|
memory.AddSample(stat.Memory)
|
|
} else {
|
|
memory.AddSample(stat.Memory)
|
|
}
|
|
lastSample = *stat
|
|
}
|
|
percent := getPercentComplete(stats)
|
|
return info.Usage{
|
|
PercentComplete: percent,
|
|
Cpu: cpu.GetAllPercentiles(),
|
|
Memory: memory.GetAllPercentiles(),
|
|
}
|
|
}
|