Add TM2 validator for DsStats delivery services
Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/095def7c Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/095def7c Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/095def7c Branch: refs/heads/master Commit: 095def7c03974f74b08e180476528ebbb6b32a90 Parents: 0dd73c1 Author: Robert Butts <[email protected]> Authored: Thu Mar 30 10:45:50 2017 -0600 Committer: Dave Neuman <[email protected]> Committed: Wed Apr 12 15:43:31 2017 -0600 ---------------------------------------------------------------------- .../traffic_monitor/tmcheck/dsstats.go | 137 +++++++++++++++++++ .../traffic_monitor/tmcheck/tmcheck.go | 20 +++ .../tools/nagios-validate-deliveryservices.go | 69 ++++++++++ .../traffic_monitor/tools/validator-service.go | 11 +- 4 files changed, 235 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/095def7c/traffic_monitor_golang/traffic_monitor/tmcheck/dsstats.go ---------------------------------------------------------------------- diff --git a/traffic_monitor_golang/traffic_monitor/tmcheck/dsstats.go b/traffic_monitor_golang/traffic_monitor/tmcheck/dsstats.go new file mode 100644 index 0000000..a35a3a7 --- /dev/null +++ b/traffic_monitor_golang/traffic_monitor/tmcheck/dsstats.go @@ -0,0 +1,137 @@ +/* + * 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. + */ + +package tmcheck + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/crconfig" + dsdata "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/deliveryservicedata" + "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/enum" + to "github.com/apache/incubator-trafficcontrol/traffic_ops/client" +) + +// ValidateDSStates validates that all Delivery Services in the CRConfig exist in given Traffic Monitor's DSStats. +// Existence in DSStats is useful to verify, because "Available: false" in CRStates +func ValidateDSStats(tmURI string, toClient *to.Session) error { + cdn, err := GetCDN(tmURI) + if err != nil { + return fmt.Errorf("getting CDN from Traffic Monitor: %v", err) + } + return ValidateDSStatsWithCDN(tmURI, cdn, toClient) +} + +// ValidateOfflineStatesWithCDN validates per ValidateOfflineStates, but saves an additional query if the Traffic Monitor's CDN is known. +func ValidateDSStatsWithCDN(tmURI string, tmCDN string, toClient *to.Session) error { + crConfigBytes, err := toClient.CRConfigRaw(tmCDN) + if err != nil { + return fmt.Errorf("getting CRConfig: %v", err) + } + + crConfig := crconfig.CRConfig{} + if err := json.Unmarshal(crConfigBytes, &crConfig); err != nil { + return fmt.Errorf("unmarshalling CRConfig JSON: %v", err) + } + + return ValidateDSStatsWithCRConfig(tmURI, &crConfig, toClient) +} + +// ValidateOfflineStatesWithCRConfig validates per ValidateOfflineStates, but saves querying the CRconfig if it's already fetched. +func ValidateDSStatsWithCRConfig(tmURI string, crConfig *crconfig.CRConfig, toClient *to.Session) error { + dsStats, err := GetDSStats(tmURI + TrafficMonitorDSStatsPath) + if err != nil { + return fmt.Errorf("getting DSStats: %v", err) + } + + return ValidateDSStatsData(dsStats, crConfig) +} + +func hasCaches(dsName string, crconfig *crconfig.CRConfig) bool { + for _, server := range crconfig.ContentServers { + if _, ok := server.DeliveryServices[dsName]; ok { + return true + } + } + return false +} + +// ValidateDSStatsData validates that all delivery services in the given CRConfig with caches assigned exist in the given DSStats. +func ValidateDSStatsData(dsStats *dsdata.StatsOld, crconfig *crconfig.CRConfig) error { + for dsName, _ := range crconfig.DeliveryServices { + if !hasCaches(dsName, crconfig) { + continue + } + if _, ok := dsStats.DeliveryService[enum.DeliveryServiceName(dsName)]; !ok { + return fmt.Errorf("Delivery Service %v in CRConfig but not DSStats", dsName) + } + } + return nil +} + +// DSStatsValidator is designed to be run as a goroutine, and does not return. It continously validates every `interval`, and calls `onErr` on failure, `onResumeSuccess` when a failure ceases, and `onCheck` on every poll. +func DSStatsValidator( + tmURI string, + toClient *to.Session, + interval time.Duration, + grace time.Duration, + onErr func(error), + onResumeSuccess func(), + onCheck func(error), +) { + Validator(tmURI, toClient, interval, grace, onErr, onResumeSuccess, onCheck, ValidateDSStats) +} + +// AllMonitorsDSStatsValidator is designed to be run as a goroutine, and does not return. It continously validates every `interval`, and calls `onErr` on failure, `onResumeSuccess` when a failure ceases, and `onCheck` on every poll. Note the error passed to `onErr` may be a general validation error not associated with any monitor, in which case the passed `enum.TrafficMonitorName` will be empty. +func AllMonitorsDSStatsValidator( + toClient *to.Session, + interval time.Duration, + includeOffline bool, + grace time.Duration, + onErr func(enum.TrafficMonitorName, error), + onResumeSuccess func(enum.TrafficMonitorName), + onCheck func(enum.TrafficMonitorName, error), +) { + AllValidator(toClient, interval, includeOffline, grace, onErr, onResumeSuccess, onCheck, ValidateAllMonitorsDSStats) +} + +// ValidateAllMonitorDSStats validates, for all monitors in the given Traffic Ops, DSStats contains all Delivery Services in the CRConfig. +func ValidateAllMonitorsDSStats(toClient *to.Session, includeOffline bool) (map[enum.TrafficMonitorName]error, error) { + servers, err := GetMonitors(toClient, includeOffline) + if err != nil { + return nil, err + } + + crConfigs := GetCRConfigs(GetCDNs(servers), toClient) + + errs := map[enum.TrafficMonitorName]error{} + for _, server := range servers { + crConfig := crConfigs[enum.CDNName(server.CDNName)] + if err := crConfig.Err; err != nil { + errs[enum.TrafficMonitorName(server.HostName)] = fmt.Errorf("getting CRConfig: %v", err) + continue + } + + uri := fmt.Sprintf("http://%s.%s", server.HostName, server.DomainName) + errs[enum.TrafficMonitorName(server.HostName)] = ValidateDSStatsWithCRConfig(uri, crConfig.CRConfig, toClient) + } + return errs, nil +} http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/095def7c/traffic_monitor_golang/traffic_monitor/tmcheck/tmcheck.go ---------------------------------------------------------------------- diff --git a/traffic_monitor_golang/traffic_monitor/tmcheck/tmcheck.go b/traffic_monitor_golang/traffic_monitor/tmcheck/tmcheck.go index 9c6016b..1ef2532 100644 --- a/traffic_monitor_golang/traffic_monitor/tmcheck/tmcheck.go +++ b/traffic_monitor_golang/traffic_monitor/tmcheck/tmcheck.go @@ -29,6 +29,7 @@ import ( "time" "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/crconfig" + dsdata "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/deliveryservicedata" "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/enum" "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/peer" to "github.com/apache/incubator-trafficcontrol/traffic_ops/client" @@ -37,6 +38,7 @@ import ( const RequestTimeout = time.Second * time.Duration(30) const TrafficMonitorCRStatesPath = "/publish/CrStates" +const TrafficMonitorDSStatsPath = "/publish/DsStats" const TrafficMonitorConfigDocPath = "/publish/ConfigDoc" func getClient() *http.Client { @@ -87,6 +89,24 @@ func GetCRStates(uri string) (*peer.Crstates, error) { return &states, nil } +// GetCRStates gets the CRStates from the given Traffic Monitor. +func GetDSStats(uri string) (*dsdata.StatsOld, error) { + resp, err := getClient().Get(uri) + if err != nil { + return nil, fmt.Errorf("reading reply from %v: %v\n", uri, err) + } + respBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("reading reply from %v: %v\n", uri, err) + } + + dsStats := dsdata.StatsOld{} + if err := json.Unmarshal(respBytes, &dsStats); err != nil { + return nil, fmt.Errorf("unmarshalling: %v", err) + } + return &dsStats, nil +} + type ValidatorFunc func( tmURI string, toClient *to.Session, http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/095def7c/traffic_monitor_golang/traffic_monitor/tools/nagios-validate-deliveryservices.go ---------------------------------------------------------------------- diff --git a/traffic_monitor_golang/traffic_monitor/tools/nagios-validate-deliveryservices.go b/traffic_monitor_golang/traffic_monitor/tools/nagios-validate-deliveryservices.go new file mode 100644 index 0000000..3afdb72 --- /dev/null +++ b/traffic_monitor_golang/traffic_monitor/tools/nagios-validate-deliveryservices.go @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package main + +import ( + "flag" + "fmt" + "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/nagios" + "github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/traffic_monitor/tmcheck" + to "github.com/apache/incubator-trafficcontrol/traffic_ops/client" +) + +const UserAgent = "tm-offline-validator/0.1" + +func main() { + toURI := flag.String("to", "", "The Traffic Ops URI, whose CRConfig to validate") + toUser := flag.String("touser", "", "The Traffic Ops user") + toPass := flag.String("topass", "", "The Traffic Ops password") + includeOffline := flag.Bool("includeOffline", false, "Whether to include Offline Monitors") + help := flag.Bool("help", false, "Usage info") + helpBrief := flag.Bool("h", false, "Usage info") + flag.Parse() + if *help || *helpBrief || *toURI == "" { + fmt.Printf("Usage: ./nagios-validate-offline -to https://traffic-ops.example.net -touser bill -topass thelizard -includeOffline true\n") + return + } + + toClient, err := to.LoginWithAgent(*toURI, *toUser, *toPass, true, UserAgent, false, tmcheck.RequestTimeout) + if err != nil { + fmt.Printf("Error logging in to Traffic Ops: %v\n", err) + return + } + + monitorErrs, err := tmcheck.ValidateAllMonitorsDSStats(toClient, *includeOffline) + + if err != nil { + nagios.Exit(nagios.Critical, fmt.Sprintf("Error validating monitor delivery services: %v", err)) + } + + errStr := "" + for monitor, err := range monitorErrs { + if err != nil { + errStr += fmt.Sprintf("error validating delivery services for monitor %v : %v\n", monitor, err.Error()) + } + } + + if errStr != "" { + nagios.Exit(nagios.Critical, errStr) + } + + nagios.Exit(nagios.Ok, "") +} http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/095def7c/traffic_monitor_golang/traffic_monitor/tools/validator-service.go ---------------------------------------------------------------------- diff --git a/traffic_monitor_golang/traffic_monitor/tools/validator-service.go b/traffic_monitor_golang/traffic_monitor/tools/validator-service.go index 22295dd..5dc1e04 100644 --- a/traffic_monitor_golang/traffic_monitor/tools/validator-service.go +++ b/traffic_monitor_golang/traffic_monitor/tools/validator-service.go @@ -157,8 +157,9 @@ func main() { crStatesOfflineLogs := startValidator(tmcheck.AllMonitorsCRStatesOfflineValidator, toClient, *interval, *includeOffline, *grace) peerPollerLogs := startValidator(tmcheck.PeerPollersAllValidator, toClient, *interval, *includeOffline, *grace) + dsStatsLogs := startValidator(tmcheck.AllMonitorsDSStatsValidator, toClient, *interval, *includeOffline, *grace) - if err := serve(*toURI, crStatesOfflineLogs, peerPollerLogs); err != nil { + if err := serve(*toURI, crStatesOfflineLogs, peerPollerLogs, dsStatsLogs); err != nil { fmt.Printf("Serve error: %v\n", err) } } @@ -198,7 +199,7 @@ func printLogs(logs Logs, w io.Writer) { fmt.Fprintf(w, `</table>`) } -func serve(toURI string, crStatesOfflineLogs Logs, peerPollerLogs Logs) error { +func serve(toURI string, crStatesOfflineLogs Logs, peerPollerLogs Logs, dsStatsLogs Logs) error { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Content-Type", "text/html") @@ -212,13 +213,19 @@ func serve(toURI string, crStatesOfflineLogs Logs, peerPollerLogs Logs) error { fmt.Fprintf(w, `<h1>Traffic Monitor Validator</h1>`) fmt.Fprintf(w, `<p>%s`, toURI) + fmt.Fprintf(w, `<p>%s`, time.Now()) fmt.Fprintf(w, `<h2>CRStates Offline</h2>`) + fmt.Fprintf(w, `<h3>validates all OFFLINE and ADMIN_DOWN caches in the CRConfig are Unavailable</h3>`) printLogs(crStatesOfflineLogs, w) fmt.Fprintf(w, `<h2>Peer Poller</h2>`) + fmt.Fprintf(w, `<h3>validates all peers in the CRConfig have been polled within the last %v</h3>`, tmcheck.PeerPollMax) printLogs(peerPollerLogs, w) + fmt.Fprintf(w, `<h2>Delivery Services</h2>`) + fmt.Fprintf(w, `<h3>validates all Delivery Services in the CRConfig exist in DsStats</h3>`) + printLogs(dsStatsLogs, w) }) return http.ListenAndServe(":80", nil) }
