blob: 211b390718d54ccb03ce122ee0cc53b6c8917023 [file] [log] [blame]
package cache
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import (
"io"
"time"
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_monitor/todata"
)
// Handler is a cache handler, which fulfills the common/handler `Handler` interface.
type Handler struct {
resultChan chan Result
ToData *todata.TODataThreadsafe
}
func (h Handler) ResultChan() <-chan Result {
return h.resultChan
}
// NewHandler returns a new cache handler. Note this handler does NOT precompute stat data before calling ResultChan, and Result.Precomputed will be nil.
func NewHandler() Handler {
return Handler{resultChan: make(chan Result)}
}
// NewPrecomputeHandler constructs a new cache Handler, which precomputes stat data and populates result.Precomputed before passing to ResultChan.
func NewPrecomputeHandler(toData todata.TODataThreadsafe) Handler {
return Handler{resultChan: make(chan Result), ToData: &toData}
}
// Precompute returns whether this handler precomputes data before passing the result to the ResultChan
func (handler Handler) Precompute() bool {
return handler.ToData != nil
}
// PrecomputedData represents data parsed and pre-computed from the Result.
type PrecomputedData struct {
DeliveryServiceStats map[string]*DSStat
// This is the total bytes transmitted by all interfaces on the Cache
// Server.
OutBytes uint64
// MaxKbps is the maximum bandwidth of all interfaces on the Cache Server,
// each one calculated as the speed of the interface in Kbps.
MaxKbps int64
Errors []error
Reporting bool
Time time.Time
}
// Result is a result of polling a cache server for statistics.
type Result struct {
// Available indicates whether or not the cache server should be considered
// "available" based on its status as configured in Traffic Ops, the cache
// server's own reported availability (if applicable), and the polled
// vitals and statistics as compared to threshold values.
Available bool
// Error holds what error - if any - caused the statistic polling to fail.
Error error
// ID is the fully qualified domain name of the cache server being polled.
// (This is assumed to be unique even though that isn't necessarily true)
ID string
// Miscellaneous contains the stats that were not directly gathered into
// Statistics, but were still found in the stats polling payload. Their
// contents are NOT guaranteed in ANY way.
Miscellaneous map[string]interface{}
// PollFinished is a channel to which data should be sent to indicate that
// polling has been completed and a Result has been produced.
PollFinished chan<- uint64
// PollID is a unique identifier for the specific polling instance that
// produced this Result.
PollID uint64
PrecomputedData PrecomputedData
// RequestTime holds the elapsed duration between making a statistics
// polling request and either receiving a result or giving up.
RequestTime time.Duration
// Statistics holds the parsed statistic data returned by the cache server.
Statistics Statistics
// Time is the time at which the result has been obtained.
Time time.Time
// UsingIPv4 indicates whether IPv4 can/should be/was used by the polling
// instance that produced this Result. If ``false'', it may be assumed that
// IPv6 was used instead.
UsingIPv4 bool
// Vitals holds the parsed health information returned by the cache server.
Vitals Vitals
// InterfaceVitals holds the parsed health information returned by the cache server per interface.
InterfaceVitals map[string]Vitals
}
// HasStat returns whether the given stat is in the Result.
func (result *Result) HasStat(stat string) bool {
computedStats := ComputedStats()
if _, ok := computedStats[stat]; ok {
return true // health poll has all computed stats
}
if _, ok := result.Miscellaneous[stat]; ok {
return true
}
return false
}
// InterfacesNames returns the names of all network interfaces used by the cache
// server that was monitored to obtain the result.
func (result *Result) InterfacesNames() []string {
interfaceNames := make([]string, 0, len(result.Statistics.Interfaces))
for name, _ := range result.Statistics.Interfaces {
interfaceNames = append(interfaceNames, name)
}
return interfaceNames
}
// Interfaces returns the interfaces assigned to this result.
func (result *Result) Interfaces() map[string]Interface {
return result.Statistics.Interfaces
}
// Vitals is the vitals data returned from a cache.
type Vitals struct {
// LoadAvg is the one-minute "loadavg" of the cache server.
LoadAvg float64
BytesOut uint64
BytesIn uint64
KbpsOut int64
MaxKbpsOut int64
}
// Stat is a generic stat, including the untyped value and the time the stat was
// taken.
type Stat struct {
Time int64 `json:"time"`
Value interface{} `json:"value"`
}
// Filter filters whether stats and caches should be returned from a data set.
type Filter interface {
UseCache(tc.CacheName) bool
UseInterfaceStat(string) bool
UseStat(string) bool
WithinStatHistoryMax(uint64) bool
}
const nsPerMs = 1000000
// StatComputeFunc functions calculate a specific statistic given a set of
// polling results, server and profile information, whether or not the server
// is available, and the name of the specific network interface for which stats
// will be computed.
type StatComputeFunc func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{}
// ComputedStats returns a map of cache stats which are computed by Traffic
// Monitor (rather than returned literally from ATS), mapped to the function to
// compute them.
func ComputedStats() map[string]StatComputeFunc {
return map[string]StatComputeFunc{
"availableBandwidthInKbps": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.Vitals.MaxKbpsOut - info.Vitals.KbpsOut
},
"availableBandwidthInMbps": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return (info.Vitals.MaxKbpsOut - info.Vitals.KbpsOut) / 1000.0
},
tc.StatNameBandwidth: func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.Vitals.KbpsOut
},
tc.StatNameKBPS: func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.Vitals.KbpsOut
},
"gbps": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return float64(info.Vitals.KbpsOut) / 1000000.0
},
tc.StatNameMaxKBPS: func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.Vitals.MaxKbpsOut
},
"loadavg": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.Vitals.LoadAvg
},
"queryTime": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return info.RequestTime.Nanoseconds() / nsPerMs
},
"stateUrl": func(_ ResultInfo, _ tc.TrafficServer, serverProfile tc.TMProfile, _ tc.IsAvailable) interface{} {
return serverProfile.Parameters.HealthPollingURL
},
"status": func(_ ResultInfo, serverInfo tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
return serverInfo.ServerStatus
},
"error-string": func(info ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, _ tc.IsAvailable) interface{} {
if info.Error != nil {
return info.Error.Error()
}
return "false"
},
"isAvailable": func(_ ResultInfo, _ tc.TrafficServer, _ tc.TMProfile, combinedState tc.IsAvailable) interface{} {
return combinedState // if the cache is missing, default to false
},
"isHealthy": func(_ ResultInfo, serverInfo tc.TrafficServer, _ tc.TMProfile, combinedState tc.IsAvailable) interface{} {
if tc.CacheStatusFromString(serverInfo.ServerStatus) == tc.CacheStatusAdminDown {
return true
}
return combinedState.IsAvailable
},
// These are back-up values for when a statistics format doesn't
// support reporting these stats - which would make sense because five
// of them are pre-parsed in Statistics structures already, and I'm not
// sure what the rest of them are even for. None of these are
// documented anywhere. The values in comments are the ones that astats
// parsers will give back (because it won't get this far).
"system.astatsLoad": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.AstatsLoad
return float64(0)
},
"system.configReloadRequests": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.ConfigLoadRequest
return float64(0)
},
"system.configReloads": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.ConfigReloads
return float64(0)
},
"system.inf.name": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.InfName
return ""
},
"system.inf.speed": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.InfSpeed
return float64(0)
},
"system.lastReload": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.LastReload
return float64(0)
},
"system.lastReloadRequest": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.LastReloadRequest
return ""
},
"system.notAvailable": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.NotAvailable
return ""
},
"system.proc.loadavg": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.ProcLoadavg
return float64(0)
},
"system.proc.net.dev": func(ResultInfo, tc.TrafficServer, tc.TMProfile, tc.IsAvailable) interface{} {
// return info.System.ProcNetDev
return float64(0)
},
}
}
// Handle handles results fetched from a cache, parsing the raw Reader data and passing it along to a chan for further processing.
func (handler Handler) Handle(id string, rdr io.Reader, format string, reqTime time.Duration, reqEnd time.Time, reqErr error, pollID uint64, usingIPv4 bool, pollCtx interface{}, pollFinished chan<- uint64) {
log.Debugf("poll %v %v (format '%v') handle start\n", pollID, time.Now(), format)
result := Result{
ID: id,
Time: reqEnd,
RequestTime: reqTime,
PollID: pollID,
UsingIPv4: usingIPv4,
PollFinished: pollFinished,
}
if reqErr != nil {
log.Warnf("%s handler given error: %s", id, reqErr.Error()) // error here, in case the thing that called Handle didn't error
result.Error = reqErr
handler.resultChan <- result
return
}
decoder, err := GetDecoder(format)
if err != nil {
log.Errorln(err.Error())
result.Error = err
handler.resultChan <- result
return
}
stats, miscStats, err := decoder.Parse(result.ID, rdr, pollCtx)
if err != nil {
log.Warnf("%s decode error '%v'", id, err)
result.Error = err
handler.resultChan <- result
return
}
result.Statistics = stats
result.Miscellaneous = miscStats
result.Available = true
if handler.Precompute() {
result.PrecomputedData = decoder.Precompute(result.ID, handler.ToData.Get(), result.Statistics, result.Miscellaneous)
}
result.PrecomputedData.Reporting = true
result.PrecomputedData.Time = result.Time
handler.resultChan <- result
}