You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by el...@apache.org on 2017/01/17 20:05:09 UTC
[2/3] incubator-trafficcontrol git commit: Add TM2 StatSummary
Add TM2 StatSummary
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/578d33be
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/578d33be
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/578d33be
Branch: refs/heads/master
Commit: 578d33be372609a6b746fe72cf02fa8c25dc9eff
Parents: 2480241
Author: Robert Butts <ro...@gmail.com>
Authored: Thu Jan 12 16:24:43 2017 -0700
Committer: Jeff Elsloo <je...@cable.comcast.com>
Committed: Tue Jan 17 13:04:16 2017 -0700
----------------------------------------------------------------------
.../experimental/traffic_monitor/cache/data.go | 1 +
.../experimental/traffic_monitor/index.html | 2 +-
.../traffic_monitor/manager/datarequest.go | 139 +++++++++++++++++--
3 files changed, 133 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/578d33be/traffic_monitor/experimental/traffic_monitor/cache/data.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/cache/data.go b/traffic_monitor/experimental/traffic_monitor/cache/data.go
index 087995a..b7871a5 100644
--- a/traffic_monitor/experimental/traffic_monitor/cache/data.go
+++ b/traffic_monitor/experimental/traffic_monitor/cache/data.go
@@ -93,6 +93,7 @@ type ResultStatVal struct {
}
// TM1Time provides a custom MarshalJSON func to serialise a time.Time into milliseconds since the epoch, as served in Traffic Monitor 1.x APIs
+// TODO move somewhere more generic (enum?)
type TM1Time time.Time
func (t *TM1Time) MarshalJSON() ([]byte, error) {
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/578d33be/traffic_monitor/experimental/traffic_monitor/index.html
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/index.html b/traffic_monitor/experimental/traffic_monitor/index.html
index 847982e..5c02902 100644
--- a/traffic_monitor/experimental/traffic_monitor/index.html
+++ b/traffic_monitor/experimental/traffic_monitor/index.html
@@ -442,7 +442,7 @@ under the License.
<li class="endpoint"><a href="/publish/CrConfig">CrConfig (json)</a></li>
<li class="endpoint"><a href="/publish/PeerStates">PeerStates</a></li>
<li class="endpoint"><a href="/publish/Stats">Stats</a></li>
- <li class="endpoint"><a href="/publish/StatsSummary">StatsSummary</a></li>
+ <li class="endpoint"><a href="/publish/StatSummary">StatSummary</a></li>
<li class="endpoint"><a href="/publish/ConfigDoc">ConfigDoc</a></li>
</ul>
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/578d33be/traffic_monitor/experimental/traffic_monitor/manager/datarequest.go
----------------------------------------------------------------------
diff --git a/traffic_monitor/experimental/traffic_monitor/manager/datarequest.go b/traffic_monitor/experimental/traffic_monitor/manager/datarequest.go
index ddf8e6b..1117248 100644
--- a/traffic_monitor/experimental/traffic_monitor/manager/datarequest.go
+++ b/traffic_monitor/experimental/traffic_monitor/manager/datarequest.go
@@ -135,7 +135,14 @@ func (f *CacheStatFilter) WithinStatHistoryMax(n int) bool {
// If `wildcard` is empty, `stats` is considered exact.
// If `type` is empty, all cache types are returned.
func NewCacheStatFilter(path string, params url.Values, cacheTypes map[enum.CacheName]enum.CacheType) (cache.Filter, error) {
- validParams := map[string]struct{}{"hc": struct{}{}, "stats": struct{}{}, "wildcard": struct{}{}, "type": struct{}{}, "hosts": struct{}{}}
+ validParams := map[string]struct{}{
+ "hc": struct{}{},
+ "stats": struct{}{},
+ "wildcard": struct{}{},
+ "type": struct{}{},
+ "hosts": struct{}{},
+ "cache": struct{}{},
+ }
if len(params) > len(validParams) {
return nil, fmt.Errorf("invalid query parameters")
}
@@ -181,6 +188,12 @@ func NewCacheStatFilter(path string, params url.Values, cacheTypes map[enum.Cach
hosts[enum.CacheName(host)] = struct{}{}
}
}
+ if paramHosts, exists := params["cache"]; exists && len(paramHosts) > 0 {
+ commaHosts := strings.Split(paramHosts[0], ",")
+ for _, host := range commaHosts {
+ hosts[enum.CacheName(host)] = struct{}{}
+ }
+ }
pathArgument := getPathArgument(path)
if pathArgument != "" {
@@ -603,10 +616,6 @@ func srvPeerStates(params url.Values, errorCount threadsafe.Uint, path string, t
return WrapErrCode(errorCount, path, bytes, err)
}
-func srvStatSummary() ([]byte, int) {
- return nil, http.StatusNotImplemented
-}
-
func srvStats(staticAppData StaticAppData, healthPollInterval time.Duration, lastHealthDurations DurationMapThreadsafe, fetchCount threadsafe.Uint, healthIteration threadsafe.Uint, errorCount threadsafe.Uint) ([]byte, error) {
return getStats(staticAppData, healthPollInterval, lastHealthDurations.Get(), fetchCount.Get(), healthIteration.Get(), errorCount.Get())
}
@@ -744,15 +753,15 @@ func MakeDispatchMap(
"/publish/PeerStates": wrap(WrapParams(func(params url.Values, path string) ([]byte, int) {
return srvPeerStates(params, errorCount, path, toData, peerStates)
}, ContentTypeJSON)),
- "/publish/StatSummary": wrap(WrapParams(func(params url.Values, path string) ([]byte, int) {
- return srvStatSummary()
- }, ContentTypeJSON)),
"/publish/Stats": wrap(WrapErr(errorCount, func() ([]byte, error) {
return srvStats(staticAppData, healthPollInterval, lastHealthDurations, fetchCount, healthIteration, errorCount)
}, ContentTypeJSON)),
"/publish/ConfigDoc": wrap(WrapErr(errorCount, func() ([]byte, error) {
return srvConfigDoc(opsConfig)
}, ContentTypeJSON)),
+ "/publish/StatSummary": wrap(WrapParams(func(params url.Values, path string) ([]byte, int) {
+ return srvStatSummary(params, errorCount, path, toData, statResultHistory)
+ }, ContentTypeJSON)),
"/api/cache-count": wrap(WrapBytes(func() []byte {
return srvAPICacheCount(localStates)
}, ContentTypeJSON)),
@@ -1119,3 +1128,117 @@ func getStats(staticAppData StaticAppData, pollingInterval time.Duration, lastHe
return json.Marshal(s)
}
+
+type StatSummary struct {
+ Caches map[enum.CacheName]map[string]StatSummaryStat `json:"caches"`
+ srvhttp.CommonAPIData
+}
+
+type StatSummaryStat struct {
+ DataPointCount int64 `json:"dpCount"`
+ Start float64 `json:"start"`
+ End float64 `json:"end"`
+ High float64 `json:"high"`
+ Low float64 `json:"low"`
+ Average float64 `json:"average"`
+ StartTime int64 `json:"startTime"`
+ EndTime int64 `json:"endTime"`
+}
+
+// toNumeric returns a float for any numeric type, and false if the interface does not hold a numeric type.
+// This allows converting unknown numeric types (for example, from JSON) in a single line
+func toNumeric(v interface{}) (float64, bool) {
+ switch i := v.(type) {
+ case uint8:
+ return float64(i), true
+ case uint16:
+ return float64(i), true
+ case uint32:
+ return float64(i), true
+ case uint64:
+ return float64(i), true
+ case int8:
+ return float64(i), true
+ case int16:
+ return float64(i), true
+ case int32:
+ return float64(i), true
+ case int64:
+ return float64(i), true
+ case float32:
+ return float64(i), true
+ case float64:
+ return float64(i), true
+ case int:
+ return float64(i), true
+ case uint:
+ return float64(i), true
+ default:
+ return 0.0, false
+ }
+}
+
+func createStatSummary(statResultHistory cache.ResultStatHistory, filter cache.Filter, params url.Values) StatSummary {
+ statPrefix := "ats."
+ ss := StatSummary{
+ Caches: map[enum.CacheName]map[string]StatSummaryStat{},
+ CommonAPIData: srvhttp.GetCommonAPIData(params, time.Now()),
+ }
+ for cache, stats := range statResultHistory {
+ if !filter.UseCache(cache) {
+ continue
+ }
+ ssStats := map[string]StatSummaryStat{}
+ for statName, statHistory := range stats {
+ if !filter.UseStat(statName) {
+ continue
+ }
+ if len(statHistory) == 0 {
+ continue
+ }
+ ssStat := StatSummaryStat{}
+ msPerNs := int64(1000000)
+ ssStat.StartTime = time.Time(statHistory[len(statHistory)-1].Time).UnixNano() / msPerNs
+ ssStat.EndTime = time.Time(statHistory[0].Time).UnixNano() / msPerNs
+ oldestVal, isOldestValNumeric := toNumeric(statHistory[len(statHistory)-1].Val)
+ newestVal, isNewestValNumeric := toNumeric(statHistory[0].Val)
+ if !isOldestValNumeric || !isNewestValNumeric {
+ continue // skip non-numeric stats
+ }
+ ssStat.Start = oldestVal
+ ssStat.End = newestVal
+ ssStat.High = newestVal
+ ssStat.Low = newestVal
+ for _, val := range statHistory {
+ fVal, ok := toNumeric(val.Val)
+ if !ok {
+ continue // skip non-numeric stats. TODO warn about stat history containing different types?
+ }
+ for i := uint64(0); i < val.Span; i++ {
+ ssStat.DataPointCount++
+ ssStat.Average -= ssStat.Average / float64(ssStat.DataPointCount)
+ ssStat.Average += fVal / float64(ssStat.DataPointCount)
+ }
+ if fVal < ssStat.Low {
+ ssStat.Low = fVal
+ }
+ if fVal > ssStat.High {
+ ssStat.High = fVal
+ }
+ }
+ ssStats[statPrefix+statName] = ssStat
+ }
+ ss.Caches[cache] = ssStats
+ }
+ return ss
+}
+
+func srvStatSummary(params url.Values, errorCount threadsafe.Uint, path string, toData todata.TODataThreadsafe, statResultHistory threadsafe.ResultStatHistory) ([]byte, int) {
+ filter, err := NewCacheStatFilter(path, params, toData.Get().ServerTypes)
+ if err != nil {
+ HandleErr(errorCount, path, err)
+ return []byte(err.Error()), http.StatusBadRequest
+ }
+ bytes, err := json.Marshal(createStatSummary(statResultHistory.Get(), filter, params))
+ return WrapErrCode(errorCount, path, bytes, err)
+}