github.com/mrgossett/heapster@v0.18.2/model/util.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 model
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"k8s.io/heapster/store/daystore"
    23  	"k8s.io/heapster/store/statstore"
    24  )
    25  
    26  // latestTimestamp returns its largest time.Time argument
    27  func latestTimestamp(first time.Time, second time.Time) time.Time {
    28  	if first.After(second) {
    29  		return first
    30  	}
    31  	return second
    32  }
    33  
    34  // newInfoType is an InfoType Constructor, which returns a new InfoType.
    35  // Initial fields for the new InfoType can be provided as arguments.
    36  // A nil argument results in a newly-allocated map for that field.
    37  func newInfoType(metrics map[string]*daystore.DayStore, labels map[string]string, context map[string]*statstore.TimePoint) InfoType {
    38  	if metrics == nil {
    39  		metrics = make(map[string]*daystore.DayStore)
    40  	}
    41  	if labels == nil {
    42  		labels = make(map[string]string)
    43  	}
    44  	if context == nil {
    45  		context = make(map[string]*statstore.TimePoint)
    46  	}
    47  	return InfoType{
    48  		Creation: time.Time{},
    49  		Metrics:  metrics,
    50  		Labels:   labels,
    51  		Context:  context,
    52  	}
    53  }
    54  
    55  // addContainerToMap creates or finds a ContainerInfo element under a map[string]*ContainerInfo
    56  func addContainerToMap(container_name string, dict map[string]*ContainerInfo) *ContainerInfo {
    57  	var container_ptr *ContainerInfo
    58  
    59  	if val, ok := dict[container_name]; ok {
    60  		// A container already exists under that name, return the address
    61  		container_ptr = val
    62  	} else {
    63  		container_ptr = &ContainerInfo{
    64  			InfoType: newInfoType(nil, nil, nil),
    65  		}
    66  		dict[container_name] = container_ptr
    67  	}
    68  	return container_ptr
    69  }
    70  
    71  // addTimePoints adds the values of two TimePoints as uint64.
    72  // addTimePoints returns a new TimePoint with the added Value fields
    73  // and the Timestamp of the first TimePoint.
    74  func addTimePoints(tp1 statstore.TimePoint, tp2 statstore.TimePoint) statstore.TimePoint {
    75  	maxTS := tp1.Timestamp
    76  	if maxTS.Before(tp2.Timestamp) {
    77  		maxTS = tp2.Timestamp
    78  	}
    79  	return statstore.TimePoint{
    80  		Timestamp: maxTS,
    81  		Value:     tp1.Value + tp2.Value,
    82  	}
    83  }
    84  
    85  // popTPSlice pops the first element of a TimePoint Slice, removing it from the slice.
    86  // popTPSlice receives a *[]TimePoint and returns its first element.
    87  func popTPSlice(tps_ptr *[]statstore.TimePoint) *statstore.TimePoint {
    88  	if tps_ptr == nil {
    89  		return nil
    90  	}
    91  	tps := *tps_ptr
    92  	if len(tps) == 0 {
    93  		return nil
    94  	}
    95  	res := tps[0]
    96  	if len(tps) == 1 {
    97  		(*tps_ptr) = tps[0:0]
    98  	}
    99  	(*tps_ptr) = tps[1:]
   100  	return &res
   101  }
   102  
   103  // addMatchingTimeseries performs addition over two timeseries with unique timestamps.
   104  // addMatchingTimeseries returns a []TimePoint of the resulting aggregated timeseries.
   105  // Assumes time-descending order of both []TimePoint parameters and the return slice.
   106  func addMatchingTimeseries(left []statstore.TimePoint, right []statstore.TimePoint) []statstore.TimePoint {
   107  	var cur_left *statstore.TimePoint
   108  	var cur_right *statstore.TimePoint
   109  	result := []statstore.TimePoint{}
   110  
   111  	// Merge timeseries into result until either one is empty
   112  	cur_left = popTPSlice(&left)
   113  	cur_right = popTPSlice(&right)
   114  	for cur_left != nil && cur_right != nil {
   115  		result = append(result, addTimePoints(*cur_left, *cur_right))
   116  		if cur_left.Timestamp.Equal(cur_right.Timestamp) {
   117  			cur_left = popTPSlice(&left)
   118  			cur_right = popTPSlice(&right)
   119  		} else if cur_left.Timestamp.After(cur_right.Timestamp) {
   120  			cur_left = popTPSlice(&left)
   121  		} else {
   122  			cur_right = popTPSlice(&right)
   123  		}
   124  	}
   125  	if cur_left == nil && cur_right != nil {
   126  		result = append(result, *cur_right)
   127  	} else if cur_left != nil && cur_right == nil {
   128  		result = append(result, *cur_left)
   129  	}
   130  
   131  	// Append leftover elements from non-empty timeseries
   132  	if len(left) > 0 {
   133  		result = append(result, left...)
   134  	} else if len(right) > 0 {
   135  		result = append(result, right...)
   136  	}
   137  
   138  	return result
   139  }
   140  
   141  // instantFromCumulativeMetric calculates the value of an instantaneous metric from two
   142  // points of a cumulative metric, such as cpu/usage.
   143  // The inputs are the value and timestamp of the newer cumulative datapoint,
   144  // and a pointer to a TimePoint holding the previous cumulative datapoint.
   145  func instantFromCumulativeMetric(value uint64, stamp time.Time, prev *statstore.TimePoint) (uint64, error) {
   146  	if prev == nil {
   147  		return uint64(0), fmt.Errorf("unable to calculate instant metric with nil previous TimePoint")
   148  	}
   149  	if !stamp.After(prev.Timestamp) {
   150  		return uint64(0), fmt.Errorf("the previous TimePoint is not earlier in time than the newer one")
   151  	}
   152  	tdelta := uint64(stamp.Sub(prev.Timestamp).Nanoseconds())
   153  	// Divide metric by nanoseconds that have elapsed, multiply by 1000 to get an unsigned metric
   154  	if value < prev.Value {
   155  		return uint64(0), fmt.Errorf("the provided value %d is less than the previous one %d", value, prev.Value)
   156  	}
   157  	// Divide metric by nanoseconds that have elapsed, multiply by 1000 to get an unsigned metric
   158  	vdelta := (value - prev.Value) * 1000
   159  
   160  	instaVal := vdelta / tdelta
   161  	prev.Value = value
   162  	prev.Timestamp = stamp
   163  	return instaVal, nil
   164  }
   165  
   166  // getStats extracts derived stats from an InfoType.
   167  func getStats(info InfoType) map[string]StatBundle {
   168  	res := make(map[string]StatBundle)
   169  	for key, ds := range info.Metrics {
   170  		last, lastMax, _ := ds.Hour.Last()
   171  		minAvg := last.Value
   172  		minPct := lastMax
   173  		minMax := lastMax
   174  		hourAvg, _ := ds.Hour.Average()
   175  		hourPct, _ := ds.Hour.Percentile(0.95)
   176  		hourMax, _ := ds.Hour.Max()
   177  		dayAvg, _ := ds.Average()
   178  		dayPct, _ := ds.NinetyFifth()
   179  		dayMax, _ := ds.Max()
   180  
   181  		res[key] = StatBundle{
   182  			Minute: Stats{
   183  				Average:     minAvg,
   184  				NinetyFifth: minPct,
   185  				Max:         minMax,
   186  			},
   187  			Hour: Stats{
   188  				Average:     hourAvg,
   189  				NinetyFifth: hourPct,
   190  				Max:         hourMax,
   191  			},
   192  			Day: Stats{
   193  				Average:     dayAvg,
   194  				NinetyFifth: dayPct,
   195  				Max:         dayMax,
   196  			},
   197  		}
   198  	}
   199  	return res
   200  }
   201  
   202  func epsilonFromMetric(metric string) uint64 {
   203  	switch metric {
   204  	case cpuLimit:
   205  		return cpuLimitEpsilon
   206  	case cpuUsage:
   207  		return cpuUsageEpsilon
   208  	case memLimit:
   209  		return memLimitEpsilon
   210  	case memUsage:
   211  		return memUsageEpsilon
   212  	case memWorking:
   213  		return memWorkingEpsilon
   214  	default:
   215  		if strings.Contains(metric, fsLimit) {
   216  			return fsLimitEpsilon
   217  		}
   218  
   219  		if strings.Contains(metric, fsUsage) {
   220  			return fsUsageEpsilon
   221  		}
   222  
   223  		return defaultEpsilon
   224  	}
   225  }