Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TO Go cdns/health and cdns/{name}/health #2305

Merged
merged 1 commit into from
Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/go-tc/traffic_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,15 @@ func TrafficMonitorTransformToMap(tmConfig *TrafficMonitorConfig) (*TrafficMonit

return &tm, nil
}

type HealthData struct {
TotalOffline uint64 `json:"totalOffline"`
TotalOnline uint64 `json:"totalOnline"`
CacheGroups []HealthDataCacheGroup `json:"cachegroups"`
}

type HealthDataCacheGroup struct {
Offline int64 `json:"offline"`
Online int64 `json:"online"`
Name CacheGroupName `json:"name"`
}
9 changes: 9 additions & 0 deletions lib/go-util/num.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,12 @@ func HashInts(ints []int, sortIntsBeforeHashing bool) []byte {
bts := sha512.Sum512(buf)
return bts[:]
}

// IntSliceToMap creates an int set from an array.
func IntSliceToMap(s []int) map[int]struct{} {
m := map[int]struct{}{}
for _, v := range s {
m[v] = struct{}{}
}
return m
}
51 changes: 5 additions & 46 deletions traffic_ops/traffic_ops_golang/cachesstats/cachesstats.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,18 @@ package cachesstats
*/

import (
"crypto/tls"
"database/sql"
"encoding/json"
"errors"
"net/http"
"net/url"
"strconv"
"time"

"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/monitorhlp"
)

func Get(w http.ResponseWriter, r *http.Request) {
Expand All @@ -56,25 +55,9 @@ func getCachesStats(tx *sql.Tx) ([]CacheData, error) {
return nil, errors.New("getting monitors: " + err.Error())
}

monitorForwardProxy, monitorForwardProxyExists, err := getGlobalParam(tx, MonitorProxyParameter)
client, err := monitorhlp.GetClient(tx)
if err != nil {
return nil, errors.New("getting global monitor proxy parameter: " + err.Error())
}

client := &http.Client{Timeout: MonitorRequestTimeout}
if monitorForwardProxyExists {
proxyURI, err := url.Parse(monitorForwardProxy)
if err != nil {
return nil, errors.New("monitor forward proxy '" + monitorForwardProxy + "' in parameter '" + MonitorProxyParameter + "' not a URI: " + err.Error())
}
clientTransport := &http.Transport{Proxy: http.ProxyURL(proxyURI)}
if proxyURI.Scheme == "https" {
// TM does not support HTTP/2 and golang when connecting to https will use HTTP/2 by default causing a conflict
// The result will be an unsupported scheme error
// Setting TLSNextProto to any empty map will disable using HTTP/2 per https://golang.org/src/net/http/doc.go
clientTransport.TLSNextProto = make(map[string]func(authority string, c *tls.Conn) http.RoundTripper)
}
client = &http.Client{Timeout: MonitorRequestTimeout, Transport: clientTransport}
return nil, errors.New("getting monitor client: " + err.Error())
}

cacheData, err := getCacheData(tx)
Expand All @@ -91,7 +74,7 @@ func getCachesStats(tx *sql.Tx) ([]CacheData, error) {
success := false
errs := []error{}
for _, monitorFQDN := range monitorFQDNs {
crStates, err := getCRStates(monitorFQDN, client)
crStates, err := monitorhlp.GetCRStates(monitorFQDN, client)
if err != nil {
errs = append(errs, errors.New("getting CRStates for CDN '"+string(cdn)+"' monitor '"+monitorFQDN+"': "+err.Error()))
continue
Expand Down Expand Up @@ -197,31 +180,7 @@ func addStats(cacheData []CacheData, stats CacheStats) []CacheData {
return cacheData
}

// CRStates contains the Monitor CRStates needed by Cachedata. It is NOT the full object served by the Monitor, but only the data required by the caches stats endpoint.
type CRStates struct {
Caches map[tc.CacheName]Available `json:"caches"`
}

type Available struct {
IsAvailable bool `json:"isAvailable"`
}

func getCRStates(monitorFQDN string, client *http.Client) (CRStates, error) {
path := `/publish/CrStates`
resp, err := client.Get("http://" + monitorFQDN + path)
if err != nil {
return CRStates{}, errors.New("getting CRStates from Monitor '" + monitorFQDN + "': " + err.Error())
}
defer resp.Body.Close()

crs := CRStates{}
if err := json.NewDecoder(resp.Body).Decode(&crs); err != nil {
return CRStates{}, errors.New("decoding CRStates from monitor '" + monitorFQDN + "': " + err.Error())
}
return crs, nil
}

func addHealth(cacheData []CacheData, crStates CRStates) []CacheData {
func addHealth(cacheData []CacheData, crStates tc.CRStates) []CacheData {
if crStates.Caches == nil {
return cacheData // TODO warn?
}
Expand Down
75 changes: 15 additions & 60 deletions traffic_ops/traffic_ops_golang/cdn/capacity.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/monitorhlp"
)

func GetCapacity(w http.ResponseWriter, r *http.Request) {
Expand All @@ -50,27 +51,6 @@ const MonitorProxyParameter = "tm.traffic_mon_fwd_proxy"
const MonitorRequestTimeout = time.Second * 10
const MonitorOnlineStatus = "ONLINE"

// CRStates contains the Monitor CRStates members needed for health. It is NOT the full object served by the Monitor, but only the data required by this endpoint.
type CRStates struct {
Caches map[tc.CacheName]Available `json:"caches"`
}

type Available struct {
IsAvailable bool `json:"isAvailable"`
}

// CRConfig contains the Monitor CRConfig members needed for health. It is NOT the full object served by the Monitor, but only the data required by this endpoint.
type CRConfig struct {
ContentServers map[tc.CacheName]CRConfigServer `json:"contentServers"`
}

type CRConfigServer struct {
CacheGroup tc.CacheGroupName `json:"locationId"`
Status tc.CacheStatus `json:"status"`
Type tc.CacheType `json:"type"`
Profile string `json:"profile"`
}

func getCapacity(tx *sql.Tx) (CapacityResp, error) {
monitors, err := getCDNMonitorFQDNs(tx)
if err != nil {
Expand Down Expand Up @@ -144,15 +124,15 @@ func getCapacityData(monitors map[tc.CDNName][]string, thresholds map[string]flo
for cdn, monitorFQDNs := range monitors {
err := error(nil)
for _, monitorFQDN := range monitorFQDNs {
crStates := CRStates{}
crConfig := CRConfig{}
crStates := tc.CRStates{}
crConfig := tc.CRConfig{}
cacheStats := CacheStats{}
if crStates, err = getCRStates(monitorFQDN, client); err != nil {
if crStates, err = monitorhlp.GetCRStates(monitorFQDN, client); err != nil {
err = errors.New("getting CRStates for CDN '" + string(cdn) + "' monitor '" + monitorFQDN + "': " + err.Error())
log.Warnln("getCapacity failed to get CRStates from cdn '" + string(cdn) + " monitor '" + monitorFQDN + "', trying next monitor: " + err.Error())
continue
}
if crConfig, err = getCRConfig(monitorFQDN, client); err != nil {
if crConfig, err = monitorhlp.GetCRConfig(monitorFQDN, client); err != nil {
err = errors.New("getting CRConfig for CDN '" + string(cdn) + "' monitor '" + monitorFQDN + "': " + err.Error())
log.Warnln("getCapacity failed to get CRConfig from cdn '" + string(cdn) + " monitor '" + monitorFQDN + "', trying next monitor: " + err.Error())
continue
Expand All @@ -172,30 +152,34 @@ func getCapacityData(monitors map[tc.CDNName][]string, thresholds map[string]flo
return cap, nil
}

func addCapacity(cap CapData, cacheStats CacheStats, crStates CRStates, crConfig CRConfig, thresholds map[string]float64) CapData {
func addCapacity(cap CapData, cacheStats CacheStats, crStates tc.CRStates, crConfig tc.CRConfig, thresholds map[string]float64) CapData {
for cacheName, stats := range cacheStats.Caches {
cache, ok := crConfig.ContentServers[cacheName]
cache, ok := crConfig.ContentServers[string(cacheName)]
if !ok {
continue
}
if !strings.HasPrefix(string(cache.Type), string(tc.CacheTypeEdge)) {
if cache.ServerType == nil || cache.ServerStatus == nil || cache.Profile == nil {
log.Warnln("addCapacity got cache with nil values! Skipping!")
continue
}
if !strings.HasPrefix(*cache.ServerType, string(tc.CacheTypeEdge)) {
continue
}
if len(stats.KBPS) < 1 || len(stats.MaxKBPS) < 1 {
continue
}
if cache.Status == "REPORTED" || cache.Status == "ONLINE" {
if string(*cache.ServerStatus) == string(tc.CacheStatusReported) || string(*cache.ServerStatus) == string(tc.CacheStatusOnline) {
if crStates.Caches[cacheName].IsAvailable {
cap.Available += float64(stats.KBPS[0].Value)
} else {
cap.Unavailable += float64(stats.KBPS[0].Value)
}
} else if cache.Status == "ADMIN_DOWN" {
} else if string(*cache.ServerStatus) == string(tc.CacheStatusAdminDown) {
cap.Maintenance += float64(stats.KBPS[0].Value)
} else {
continue // don't add capacity for OFFLINE or other statuses
}
cap.Capacity += float64(stats.MaxKBPS[0].Value) - thresholds[cache.Profile]
cap.Capacity += float64(stats.MaxKBPS[0].Value) - thresholds[*cache.Profile]
}
return cap
}
Expand Down Expand Up @@ -234,35 +218,6 @@ AND pa.name = 'health.threshold.availableBandwidthInKbps'
return profileThresholds, nil
}

func getCRStates(monitorFQDN string, client *http.Client) (CRStates, error) {
path := `/publish/CrStates`
resp, err := client.Get("http://" + monitorFQDN + path)
if err != nil {
return CRStates{}, errors.New("getting CRStates from Monitor '" + monitorFQDN + "': " + err.Error())
}
defer resp.Body.Close()

crs := CRStates{}
if err := json.NewDecoder(resp.Body).Decode(&crs); err != nil {
return CRStates{}, errors.New("decoding CRStates from monitor '" + monitorFQDN + "': " + err.Error())
}
return crs, nil
}

func getCRConfig(monitorFQDN string, client *http.Client) (CRConfig, error) {
path := `/publish/CrConfig`
resp, err := client.Get("http://" + monitorFQDN + path)
if err != nil {
return CRConfig{}, errors.New("getting CRConfig from Monitor '" + monitorFQDN + "': " + err.Error())
}
defer resp.Body.Close()
crs := CRConfig{}
if err := json.NewDecoder(resp.Body).Decode(&crs); err != nil {
return CRConfig{}, errors.New("decoding CRConfig from monitor '" + monitorFQDN + "': " + err.Error())
}
return crs, nil
}

// CacheStats contains the Monitor CacheStats needed by Cachedata. It is NOT the full object served by the Monitor, but only the data required by the caches stats endpoint.
type CacheStats struct {
Caches map[tc.CacheName]CacheStat `json:"caches"`
Expand Down
Loading