You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@trafficcontrol.apache.org by GitBox <gi...@apache.org> on 2022/06/14 20:10:00 UTC

[GitHub] [trafficcontrol] ocket8888 commented on a diff in pull request #6510: Port TO check scripts to Go

ocket8888 commented on code in PR #6510:
URL: https://github.com/apache/trafficcontrol/pull/6510#discussion_r897273321


##########
go.mod:
##########
@@ -55,47 +55,8 @@ require (
 )
 
 require (
-	github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
 	github.com/basho/backoff v0.0.0-20150307023525-2ff7c4694083 // indirect
-	github.com/cenkalti/backoff v2.2.1+incompatible // indirect
-	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
-	github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
-	github.com/goccy/go-json v0.8.1 // indirect
-	github.com/golang/protobuf v1.5.2 // indirect
-	github.com/hashicorp/errwrap v1.0.0 // indirect
-	github.com/hashicorp/go-multierror v1.1.0 // indirect
-	github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
-	github.com/lestrrat-go/blackmagic v1.0.0 // indirect
-	github.com/lestrrat-go/httpcc v1.0.0 // indirect
-	github.com/lestrrat-go/iter v1.0.1 // indirect
-	github.com/lestrrat-go/option v1.0.0 // indirect
-	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
-	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/nxadm/tail v1.4.8 // indirect
-	go.uber.org/atomic v1.6.0 // indirect
-	google.golang.org/protobuf v1.27.1 // indirect
-	gopkg.in/square/go-jose.v2 v2.5.1 // indirect
-	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
+	github.com/google/gopacket v1.1.19
 )
 
-require (
-	github.com/Shopify/sarama v1.27.2
-	github.com/klauspost/compress v1.13.6 // indirect
-)
-
-require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/eapache/go-resiliency v1.2.0 // indirect
-	github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
-	github.com/eapache/queue v1.1.0 // indirect
-	github.com/golang/snappy v0.0.4 // indirect
-	github.com/hashicorp/go-uuid v1.0.2 // indirect
-	github.com/jcmturner/gofork v1.0.0 // indirect
-	github.com/pierrec/lz4 v2.5.2+incompatible // indirect
-	github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
-	golang.org/x/text v0.3.7 // indirect
-	gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
-	gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
-	gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
-	gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
-)
+require github.com/Shopify/sarama v1.27.2

Review Comment:
   This dependency looks unused



##########
traffic_ops/app/bin/checks/ToPingCheck.go:
##########
@@ -0,0 +1,350 @@
+/*
+   Licensed 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.
+*/
+
+/* ToPingCheck.go
+   Used for checking ILO ping,  MTU test, 10G (IPv4), and 10G6 (IPv6) pings.
+*/
+
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-log"
+	tc "github.com/apache/trafficcontrol/lib/go-tc"
+	toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
+)
+
+// Traffic Ops connection params
+const AllowInsecureConnections = false
+const UserAgent = "go/tc-ping-monitor"
+const UseClientCache = false
+const TrafficOpsRequestTimeout = time.Second * time.Duration(10)
+
+var confForce *bool
+
+type Config struct {
+	URL    string `json:"to_url"`
+	User   string `json:"user"`
+	Passwd string `json:"passwd"`
+}
+
+type Server struct {
+	id        int
+	cdn       string
+	name      string
+	fqdn      string
+	ilo       string
+	ip4       string
+	ip6       string
+	mtu       int
+	failcount int
+	status    string
+	failaddr  string
+}
+
+func NewServer(id int, name string, status string, f int) Server {
+	server := Server{}
+	server.id = id
+	server.name = name
+	server.status = status
+	server.failcount = f
+	return server
+}
+
+func LoadConfig(file string) (Config, error) {
+	var config Config
+	configFile, err := os.Open(file)
+	defer configFile.Close()
+	if err != nil {
+		return config, err
+	}
+	jsonParser := json.NewDecoder(configFile)
+	jsonParser.Decode(&config)
+	return config, err
+}
+
+func (s *Server) ping(name string) bool {
+	// I haven't found a good way to do this natively with go yet, which makes me sad
+	s.failcount = 0
+	size := 30 //default
+	ok := true
+	var addr string
+	switch name {
+	case "IPv4", "10G":
+		log.Infof("IPv4")
+		if s.ip4 != "" {
+			ok, addr = ping4(size, s.ip4)
+			if ok == false {
+				s.failaddr = addr
+			}
+		}
+	case "IPv6", "10G6":
+		log.Infof("IPv6")
+		if s.ip6 != "" {
+			ok, addr = ping6(size, s.ip6)
+			if ok == false {
+				s.failaddr = addr
+			}
+		}
+	case "ILO":
+		log.Infof("ILO")
+		if s.ilo != "" {
+			match, err := regexp.MatchString(":", s.ilo)
+			if err != nil {
+				log.Errorf("Match error:", err)
+				os.Exit(1)
+			}
+			if match {
+				ok, addr = ping6(size, s.ilo)
+			} else {
+				ok, addr = ping4(size, s.ilo)
+				if ok == false {
+					s.failaddr = addr
+				}
+			}
+		}
+	case "MTU":
+		log.Infof("MTU")
+		ok4 := true
+		if s.ip4 != "" {
+			// subtract protocol headers from MTU to get payload size
+			size = s.mtu - 28
+			ok4, addr = ping4(size, s.ip4)
+		} else {
+			log.Warnf("no IPv4 address detected (skipping)")
+		}
+		if ok4 == false {
+			s.failaddr = addr
+		}
+		ok6 := true
+		if s.ip6 != "" {
+			// subtract protocol headers from MTU to get payload size
+			size = s.mtu - 48
+			ok6, addr = ping6(size, s.ip6)
+		} else {
+			log.Warnf("no IPv6 address detected (skipping)")
+		}
+		if ok6 == false {
+			if len(s.failaddr) > 0 {
+				s.failaddr = s.failaddr + "," + addr
+			} else {
+				s.failaddr = addr
+			}
+		}
+		if !(ok4 && ok6) {
+			ok = false
+		}
+	}
+	return ok
+}
+
+func ping4(size int, addr string) (bool, string) {
+	log.Infof("size: ", strconv.Itoa(size))
+	out, err := exec.Command("/bin/ping", "-M", "do", "-s", strconv.Itoa(size), "-c", "2", addr).Output()
+	if err != nil {
+		log.Warnf("ping failed for %s: %s", addr, err.Error())
+		return false, addr
+	}
+	log.Debugf("ping output:\n%v", out)
+	return true, addr
+}
+
+func ping6(size int, addr string) (bool, string) {
+	log.Infof("size: ", strconv.Itoa(size))
+	out, err := exec.Command("/bin/ping6", "-M", "do", "-s", strconv.Itoa(size), "-c", "2", addr).Output()
+	if err != nil {
+		log.Warnf("ping failed for %s: %s", addr, err.Error())
+		return false, addr
+	}
+	log.Debugf("ping output:\n%v", out)
+	return true, addr
+}
+
+func main() {
+	var cpath_new string
+	var ok bool
+
+	jobStart := time.Now()
+
+	// define default config file path
+	cpath, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	if err != nil {
+		log.Errorf("Config error:", err)
+		os.Exit(1)
+	}
+	cpath_new = strings.Replace(cpath, "/bin/checks", "/conf/check-config.json", 1)
+
+	// command-line flags
+	confPtr := flag.String("conf", cpath_new, "Config file path")
+	confName := flag.String("name", "undef", "'10G|IPv4', '10G6|IPv6', 'ILO', 'MTU'")
+	confInclude := flag.String("host", "undef", "Specific host or regex to include (optional)")
+	confCdn := flag.String("cdn", "all", "Check specific CDN by name")
+	confExclude := flag.String("exclude", "undef", "Hostname regex to exclude")
+	confReset := flag.Bool("reset", false, "Reset check values in TO to 'blank' state")
+	confQuiet := flag.Bool("q", false, "Do not send updates to TO")
+	confForce = flag.Bool("f", false, "Force a failure result")
+	flag.Parse()
+
+	reName, err := regexp.Compile("^(10G|10G6|IPv4|IPv6|ILO|MTU)$")
+	if err != nil {
+		log.Errorf("supplied exclusion regex does not compile:", err)
+		os.Exit(1)
+	}
+	if !(reName.Match([]byte(*confName))) {
+		log.Errorf("Check name must be one of the following:")
+		log.Errorf("'10G' (legacy) or 'IPv4' (new) for IPv4 interface check")
+		log.Errorf("'10G6' (legacy) or 'IPv6' (new) for IPv6 interface check")
+		log.Errorf("'ILO' out-of-band mgmt interface check")
+		log.Errorf("'MTU' uses the MTU value for the server in TO to check MTU (checks both v4 and v6, if available)")
+		os.Exit(1)
+	}
+
+	// load config json
+	config, err := LoadConfig(*confPtr)
+	if err != nil {
+		log.Errorf("Error loading config:", err)
+		os.Exit(1)
+	}
+
+	// connect to TO
+	session, _, err := toclient.LoginWithAgent(
+		config.URL,
+		config.User,
+		config.Passwd,
+		AllowInsecureConnections,
+		UserAgent,
+		UseClientCache,
+		TrafficOpsRequestTimeout)
+	if err != nil {
+		log.Errorf("An error occurred while logging in: %v\n", err)
+		os.Exit(1)
+	}
+
+	// Make TO API call for server details
+	var servers tc.ServersV3Response
+	servers, _, err = session.GetServersWithHdr(nil, nil)
+	if err != nil {
+		log.Errorf("An error occurred while getting servers: %v\n", err)
+		os.Exit(1)
+	}
+
+	for _, server := range servers.Response {
+		re, err := regexp.Compile("^(MID|EDGE).*")
+		if err != nil {
+			log.Errorf("supplied exclusion regex does not compile:", err)
+			os.Exit(1)
+		}
+		if re.Match([]byte(server.Type)) {
+			serverStart := time.Now()
+			if *confInclude != "undef" {
+				re_inc, err := regexp.Compile(*confInclude)
+				if err != nil {
+					log.Errorf("supplied exclusion regex does not compile:", err)
+					os.Exit(1)
+				}
+				if !re_inc.MatchString(*server.HostName) {
+					log.Debugf("%s does not match the provided include regex, skipping", server.HostName)
+					continue
+				}
+			}
+			if *confCdn != "all" && *confCdn != *server.CDNName {
+				log.Debugf("%s is not assinged to the specified CDN '%s', skipping", server.HostName, *confCdn)
+				continue
+			}
+			if *confExclude != "undef" {
+				re, err := regexp.Compile(*confExclude)
+				if err != nil {
+					log.Errorf("supplied exclusion regex does not compile:", err)
+					os.Exit(1)
+				}
+				if re.MatchString(*server.HostName) {
+					log.Debugf("%s matches the provided exclude regex, skipping", server.HostName)
+					continue
+				}
+			}
+			s := NewServer(*server.ID, *server.HostName, *server.Status, -1)
+			defaulStatusValue := -1
+			var statusData tc.ServercheckRequestNullable
+			statusData.ID = &s.id
+			statusData.Name = confName
+			statusData.HostName = &s.name
+			statusData.Value = &defaulStatusValue
+			s.fqdn = s.name + "." + *server.DomainName
+			for _, interf := range server.Interfaces {
+				for _, addr := range interf.IPAddresses {
+					if s.ip4 == "" && strings.Count(addr.Address, ":") == 0 {
+						s.ip4 = strings.Split(addr.Address, "/")[0]
+					}
+					if s.ip6 == "" && strings.Count(addr.Address, ":") > 0 {
+						s.ip6 = strings.Split(addr.Address, "/")[0]
+					}
+				}
+			}
+			if server.ILOIPAddress == nil {
+				s.ilo = ""
+			} else {
+				if strings.Contains(*server.ILOIPAddress, "/") {
+					s.ilo = strings.Split(*server.ILOIPAddress, "/")[0]
+				} else {
+					s.ilo = *server.ILOIPAddress
+				}
+			}
+			s.cdn = *server.CDNName
+			s.mtu = int(*server.Interfaces[0].MTU)
+			log.Infof("Next server=%s status=%s", s.fqdn, s.status)
+			if (s.status == "REPORTED" || s.status == "ADMIN_DOWN") && *confReset != true {
+				ok = s.ping(*confName)
+				log.Infof("ok: %v", ok)
+				if ok == false {
+					s.failcount = 1
+				}
+			}
+
+			// send status update to TO
+			if s.failcount == -1 {
+				// server not checked
+				*statusData.Value = -1
+			} else if s.failcount > 0 {
+				// server had failures
+				log.Infof("result=failure server=%s status=%s check=%s addr=%s", s.fqdn, s.status, *confName, s.failaddr)
+				*statusData.Value = 0
+			} else {
+				// server looks OK
+				log.Infof("result=success server=%s status=%s", s.fqdn, s.status)
+				*statusData.Value = 1
+			}
+			serverElapsed := time.Since(serverStart)
+			log.Infof("Finished checking server=%s result=%d cdn=%s elapsed=%s", s.fqdn, *statusData.Value, s.cdn, serverElapsed)
+			if *confQuiet == false {
+				log.Debugf("Sending update to TO")
+				_, _, err := session.InsertServerCheckStatus(statusData)
+				if err != nil {
+					log.Errorf("Error updating server check status with TO:", err)
+				}
+			} else {
+				log.Debugf("Skipping update to TO")
+			}
+		}
+	}
+	jobElapsed := time.Since(jobStart)
+	log.Infof("Job complete totaltime=%s", jobElapsed)
+	os.Exit(0)
+}

Review Comment:
   Invalid text file: missing terminating newline



##########
traffic_ops/app/bin/checks/ToDSCPCheck.go:
##########
@@ -0,0 +1,646 @@
+package main
+
+import (
+	"context"
+	"crypto/tls"
+	"encoding/json"
+	"flag"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-log"
+	tc "github.com/apache/trafficcontrol/lib/go-tc"
+	toclient "github.com/apache/trafficcontrol/traffic_ops/v3-client"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/google/gopacket/pcap"
+)
+
+// Traffic Ops connection params
+const AllowInsecureConnections = false
+const UserAgent = "go/tc-dscp-monitor"
+const UseClientCache = false
+const TrafficOpsRequestTimeout = time.Second * time.Duration(10)
+
+var (
+	protocol     int
+	sslflag      bool
+	host_header  string
+	confInt      *string
+	http4        *http.Transport
+	http6        *http.Transport
+	https4       *http.Transport
+	https6       *http.Transport
+	httpClient4  *http.Client
+	httpClient6  *http.Client
+	httpsClient4 *http.Client
+	httpsClient6 *http.Client
+	http_port    string
+	https_port   string
+	eth_layer    layers.Ethernet
+	ip4_layer    layers.IPv4
+	ip6_layer    layers.IPv6
+	tcp_layer    layers.TCP
+	tls_layer    layers.TLS
+	payload      gopacket.Payload
+)
+
+var connect_timeout = 2000 * time.Millisecond
+var http_timeout = 1500 * time.Millisecond
+var pcap_timeout = 250 * time.Millisecond
+
+var parser *gopacket.DecodingLayerParser = gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth_layer, &tcp_layer, &tls_layer, &ip4_layer, &ip6_layer, &payload)
+
+type Config struct {
+	URL    string `json:"to_url"`
+	User   string `json:"user"`
+	Passwd string `json:"passwd"`
+}
+
+type Server struct {
+	id        int
+	cdn       string
+	name      string
+	fqdn      string
+	ip4       string
+	ip6       string
+	httpPort  string
+	httpsPort string
+	failcount int
+	status    string
+}
+
+func NewServer(id int, name string, status string, f int) Server {
+	server := Server{}
+	server.id = id
+	server.name = name
+	server.status = status
+	server.failcount = f
+	return server
+}
+
+func LoadConfig(file string) (Config, error) {
+	var config Config
+	configFile, err := os.Open(file)
+	defer configFile.Close()
+	if err != nil {
+		return config, err
+	}
+	jsonParser := json.NewDecoder(configFile)
+	jsonParser.Decode(&config)
+	return config, err
+}
+
+func capture(ctx context.Context, s Server, iface *string, ch_dscp chan uint8, ch_ready chan uint8, ip string, ssl bool) {
+	var pcap_port string
+	if ssl {
+		pcap_port = s.httpsPort
+	} else {
+		pcap_port = s.httpPort
+	}
+
+	pcap_filter := "tcp and src " + ip + " and port " + pcap_port + " and (tcp[tcpflags] & tcp-push != 0 or ip6[53] & 8 != 0)"
+	log.Debugf("capture() filter='%s'", pcap_filter)
+	if handle, err := pcap.OpenLive(*iface, 1400, false, pcap_timeout); err != nil {
+		log.Errorf("capture() pcap.OpenLive() error:", err)
+	} else if err := handle.SetBPFFilter(pcap_filter); err != nil {
+		log.Errorf("capture() handle.SetBPFFilter() error:", err)
+	} else if err := handle.SetDirection(pcap.DirectionIn); err != nil {
+		log.Errorf("capture() handle.SetDirection() error:", err)
+	} else {
+		defer handle.Close()
+		decodedLayers := []gopacket.LayerType{}
+		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
+		timer := time.NewTimer(2000 * time.Millisecond)
+		ch_ready <- 1 // signal that capture is ready to proceed
+		pktCount := 0
+		for {
+			select {
+			case <-ctx.Done():
+				handle.Close() // without this, serious fh leak
+				log.Debugf("capture() context cancelled")
+				return
+			case <-timer.C:
+				log.Debugf("capture() timed out before packets received")
+				ch_dscp <- 254
+				return
+			default:
+				pktCount++
+				packet, err := packetSource.NextPacket()
+				if err == io.EOF {
+					break
+				} else if err != nil {
+					log.Errorf("capture() Error:", err)
+					continue
+				}
+				if sslflag == true && pktCount < 6 {
+					// skip the TLS handshake packets - they may not provide real DSCP value
+					log.Debugf("Packet #%d: %s", pktCount, packet)
+					continue
+				}
+				log.Debugf("Packet #%d: %s", pktCount, packet)
+				err = parser.DecodeLayers(packet.Data(), &decodedLayers)
+				if err != nil {
+					log.Warnf("%s", err)
+				}
+				for _, typ := range decodedLayers {
+					switch typ {
+					case layers.LayerTypeIPv4:
+						ch_dscp <- ip4_layer.TOS
+						return
+					case layers.LayerTypeIPv6:
+						ch_dscp <- ip6_layer.TrafficClass
+						return
+					}
+				}
+			}
+		}
+	}
+}
+
+func protocol_picker(s Server, check_ip string, host_header string, check_path string, v6flag bool) (cap_dscp string) {
+	if protocol == 0 || protocol == 2 {
+		// if prot is HTTP and HTTPS, just check HTTP - both is overkill
+		// do HTTP stuff
+		sslflag = false
+	}
+	if protocol == 1 || protocol == 3 {
+		// if a DS is HTTPS *only*...
+		// do HTTPS stuff
+		sslflag = true
+	}
+	log.Debugf("protocol_picker() ssl=%t", sslflag)
+	cap_dscp = request(confInt, s, host_header, check_ip, check_path, v6flag, sslflag)
+	return
+}
+
+func request(iface *string, s Server, host_header string, ip string, check_path string, v6 bool, ssl bool) string {
+	var cap_dscp uint8
+	var cap_dscp2 string
+	var url string
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel() // make sure all paths cancel the context to avoid context leak
+	ch_dscp := make(chan uint8)
+	ch_ready := make(chan uint8)
+	go capture(ctx, s, iface, ch_dscp, ch_ready, ip, ssl)
+	if ssl {
+		url = "https://" + host_header + check_path
+	} else {
+		url = "http://" + host_header + check_path
+	}
+	log.Infof("request() url=%s ip=%s", url, ip)
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		log.Errorf("request() http.NewRequest() error:", err)
+	}
+	req.Close = true
+	req.Header.Add("Cache-Control", "only-if-cached")
+	req.Header.Add("User-Agent", UserAgent)
+	ready := <-ch_ready
+	if ready == 1 {
+		log.Debugf("request() received go signal from capture()")
+	}
+	if v6 && ssl == false {
+		resp, err := httpClient6.Do(req)
+		if err != nil {
+			log.Errorf("request() httpClient6.Do() error:", err)
+		} else {
+			defer resp.Body.Close()
+			io.Copy(ioutil.Discard, resp.Body)
+		}
+	} else if v6 == false && ssl == false {
+		resp, err := httpClient4.Do(req)
+		if err != nil {
+			log.Errorf("request() httpClient4.Do() error:", err)
+		} else {
+			defer resp.Body.Close()
+			io.Copy(ioutil.Discard, resp.Body)
+		}
+	} else if v6 && ssl {
+		resp, err := httpsClient6.Do(req)
+		if err != nil {
+			log.Errorf("request() httpClient6.Do() error:", err)
+		} else {
+			defer resp.Body.Close()
+			io.Copy(ioutil.Discard, resp.Body)
+		}
+	} else {
+		resp, err := httpsClient4.Do(req)
+		if err != nil {
+			log.Errorf("request() httpClient4.Do() error:", err)
+		} else {
+			defer resp.Body.Close()
+			io.Copy(ioutil.Discard, resp.Body)
+		}
+	}
+	cap_tos, more := <-ch_dscp
+	if more {
+		log.Debugf("request() received tos=%d", cap_tos)
+		cancel() // cancel context to prevent goroutine leak!
+	} else {
+		log.Debugf("request() received all dscp values")
+		cancel() // cancel context to prevent goroutine leak!
+	}
+	if cap_tos == 254 {
+		log.Errorf("request() no valid DSCP mark received")
+		cap_dscp2 = "-1"
+	} else {
+		cap_dscp = cap_tos >> 2
+		cap_dscp2 = strconv.Itoa(int(cap_dscp))
+		log.Debugf("request() received ipv6=%t dscp=%s", v6, cap_dscp2)
+	}
+	return cap_dscp2
+}
+
+func check_result(want string, have string) bool {
+	if want == have {
+		log.Debugf("check_result() success want=%s got=%s", want, have)
+		return true
+	} else if have == "-1" {
+		log.Debugf("check_result() undetermined (ignoring) want=%s got=CAPTURE_TIMEOUT", want)
+		return true
+	} else {
+		log.Debugf("check_result() failure want=%s got=%s", want, have)
+		return false
+	}
+}
+
+func main() {
+	var (
+		cpath_new  string
+		cap_dscp   string
+		conf_dscp  string
+		xmlID      string
+		check_ip4  string
+		check_ip6  string
+		dialer_ip4 string
+		dialer_ip6 string
+	)
+
+	jobStart := time.Now()
+
+	// define default config file path
+	cpath, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	if err != nil {
+		log.Errorf("Config error:", err)
+		os.Exit(1)
+	}
+	cpath_new = strings.Replace(cpath, "/bin/checks", "/conf/check-config.json", 1)
+
+	// command-line flags
+	confPtr := flag.String("conf", cpath_new, "Config file path")
+	confInt = flag.String("iface", "undef", "Network interface for packet capture")
+	confName := flag.String("name", "DSCP", "Check name to pass to TO, e.g. 'DSCP'")
+	confInclude := flag.String("host", "undef", "Specific host or regex to include (optional)")
+	confCdn := flag.String("cdn", "all", "Check specific CDN by name")
+	confExclude := flag.String("exclude", "undef", "Hostname regex to exclude")
+	confReset := flag.Bool("reset", false, "Reset check values in TO to 'blank' state")
+	confQuiet := flag.Bool("q", false, "Do not send updates to TO")
+	flag.Parse()
+
+	if *confInt == "undef" {
+		log.Errorf("Must specify network interface for packet capture")
+		os.Exit(1)
+	}
+	if *confName == "undef" {
+		log.Errorf("Must specify check name for update to send to TO")
+		os.Exit(1)
+	}
+
+	// load config json
+	config, err := LoadConfig(*confPtr)
+	if err != nil {
+		log.Errorf("Error loading config:", err)
+		os.Exit(1)
+	}
+
+	// connect to TO
+	session, _, err := toclient.LoginWithAgent(
+		config.URL,
+		config.User,
+		config.Passwd,
+		AllowInsecureConnections,
+		UserAgent,
+		UseClientCache,
+		TrafficOpsRequestTimeout)
+	if err != nil {
+		log.Errorf("An error occurred while logging in: %v\n", err)
+		os.Exit(1)
+	}
+
+	// Make TO API call for server details
+	var servers tc.ServersV3Response
+	servers, _, err = session.GetServersWithHdr(nil, nil)
+	if err != nil {
+		log.Errorf("An error occurred while getting servers: %v\n", err)
+		os.Exit(1)
+	}
+
+	// Make TO API call for delivery service details
+	var deliveryservices []tc.DeliveryServiceNullableV30
+	deliveryservices, _, err = session.GetDeliveryServicesV30WithHdr(nil, nil)
+	if err != nil {
+		log.Errorf("An error occurred while getting delivery services: %v\n", err)
+		os.Exit(1)
+	}
+
+	// Make TO API call for cdn details
+	var cdns []tc.CDN
+	cdns, _, err = session.GetCDNs()
+	if err != nil {
+		log.Errorf("An error occurred while getting cdns: %v\n", err)
+		os.Exit(1)
+	}
+
+	// map cdn to domain name
+	cdn_map := make(map[string]string)
+	for _, cdn := range cdns {
+		cdn_map[cdn.Name] = cdn.DomainName
+	}
+
+	// map ds info
+	ds_matchlist := make(map[string][]tc.DeliveryServiceMatch)
+	ds_types := make(map[string]tc.DSType)
+	for _, ds := range deliveryservices {
+		ds_matchlist[*ds.XMLID] = *ds.MatchList
+		ds_types[*ds.XMLID] = *ds.Type
+	}
+
+	for _, server := range servers.Response {
+		re, err := regexp.Compile("^EDGE.*")
+		if err != nil {
+			log.Errorf("supplied exclusion regex does not compile:", err)
+			os.Exit(1)
+		}
+		if re.Match([]byte(server.Type)) {
+			serverStart := time.Now()
+			if *confInclude != "undef" {
+				re_inc, err := regexp.Compile(*confInclude)
+				if err != nil {
+					log.Errorf("supplied exclusion regex does not compile:", err)
+					os.Exit(1)
+				}
+				if !re_inc.MatchString(*server.HostName) {
+					log.Debugf("%s does not match the provided include regex, skipping", server.HostName)
+					continue
+				}
+			}
+			if *confCdn != "all" && *confCdn != *server.CDNName {
+				log.Debugf("%s is not assigned to the specified CDN '%s', skipping", server.HostName, *confCdn)
+				continue
+			}
+			if *confExclude != "undef" {
+				re, err := regexp.Compile(*confExclude)
+				if err != nil {
+					log.Errorf("supplied exclusion regex does not compile:", err)
+					os.Exit(1)
+				}
+				if re.MatchString(*server.HostName) {
+					log.Debugf("%s matches the provided exclude regex, skipping", server.HostName)
+					continue
+				}
+			}
+			s := NewServer(*server.ID, *server.HostName, *server.Status, -1)
+			doV4 := false //default
+			doV6 := false //default
+			defaulStatusValue := -1
+			var statusData tc.ServercheckRequestNullable
+			statusData.ID = &s.id
+			statusData.Name = confName
+			statusData.HostName = &s.name
+			statusData.Value = &defaulStatusValue
+			s.fqdn = s.name + "." + *server.DomainName
+			log.Infof("Next server=%s status=%s", s.fqdn, s.status)
+			if (s.status == "REPORTED" || s.status == "ADMIN_DOWN") && *confReset != true {
+				s.failcount = 0
+				s.cdn = *server.CDNName
+				s.httpPort = strconv.Itoa(*server.TCPPort)
+				s.httpsPort = strconv.Itoa(*server.HTTPSPort)
+				for _, interf := range server.Interfaces {
+					for _, addr := range interf.IPAddresses {
+						if s.ip4 == "" && strings.Count(addr.Address, ":") == 0 {
+							s.ip4 = strings.Split(addr.Address, "/")[0]
+						}
+						if s.ip6 == "" && strings.Count(addr.Address, ":") > 0 {
+							s.ip6 = strings.Split(addr.Address, "/")[0]
+						}
+					}
+				}
+				log.Debugf("Ports for %s: http=%s https=%s", s.name, http_port, https_port)
+				services, _, err := session.GetDeliveryServicesByServerV30WithHdr(s.id, nil)
+				if err != nil {
+					log.Errorf("Error getting delivery services from TO:", err)
+					os.Exit(1)
+				}
+
+				dialer := &net.Dialer{
+					Timeout:       connect_timeout,
+					KeepAlive:     -1,
+					DualStack:     false,
+					FallbackDelay: -1,
+				}
+				if s.ip4 != "" {
+					doV4 = true
+					check_ip4 = s.ip4
+					dialer_ip4 = s.ip4
+				}
+				if s.ip6 != "" {
+					doV6 = true
+					check_ip6 = s.ip6
+					dialer_ip6 = "[" + s.ip6 + "]"
+				}
+
+				// it is necessary to define custom Transports in order to support
+				// TLS SNI in go. Otherwise, we could have just had the http client
+				// connect to the IPv4 or IPv6 address, and set a custom Host header
+				// to ID the test target. SNI must be done this way, however, so in
+				// order to be consistent, just set up Transports for all protocol
+				// combinations.
+				http4 = &http.Transport{
+					DisableKeepAlives: true,
+					TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
+					DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+						addr = dialer_ip4 + ":" + s.httpPort
+						return dialer.DialContext(ctx, network, addr)
+					},
+				}
+				http6 = &http.Transport{
+					DisableKeepAlives: true,
+					TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
+					DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+						addr = dialer_ip6 + ":" + s.httpPort
+						return dialer.DialContext(ctx, network, addr)
+					},
+				}
+				https4 = &http.Transport{
+					DisableKeepAlives: true,
+					TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
+					DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+						addr = dialer_ip4 + ":" + s.httpsPort
+						return dialer.DialContext(ctx, network, addr)
+					},
+				}
+				https6 = &http.Transport{
+					DisableKeepAlives: true,
+					TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
+					DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+						addr = dialer_ip6 + ":" + s.httpsPort
+						return dialer.DialContext(ctx, network, addr)
+					},
+				}
+
+				// The Client isntances are tied to each Transport instance, and are also
+				// configured to prevent following any HTTP redirects.
+				httpClient4 = &http.Client{
+					Timeout:   http_timeout,
+					Transport: http4,
+					CheckRedirect: func(req *http.Request, via []*http.Request) error {
+						return http.ErrUseLastResponse
+					},
+				}
+				httpClient6 = &http.Client{
+					Timeout:   http_timeout,
+					Transport: http6,
+					CheckRedirect: func(req *http.Request, via []*http.Request) error {
+						return http.ErrUseLastResponse
+					},
+				}
+				httpsClient4 = &http.Client{
+					Timeout:   http_timeout,
+					Transport: https4,
+					CheckRedirect: func(req *http.Request, via []*http.Request) error {
+						return http.ErrUseLastResponse
+					},
+				}
+				httpsClient6 = &http.Client{
+					Timeout:   http_timeout,
+					Transport: https6,
+					CheckRedirect: func(req *http.Request, via []*http.Request) error {
+						return http.ErrUseLastResponse
+					},
+				}
+
+				for _, service := range services {
+					xmlID = *service.XMLID
+					if service.Active == nil || *service.Active == false {
+						log.Infof("Skipping ds=%s active=false", xmlID)
+						continue
+					} else if service.DSCP == nil || *service.DSCP == 0 {
+						// routers may override with default mark in this case
+						log.Infof("Skipping ds=%s dscp=0", xmlID)
+						continue
+					} else if service.CheckPath == nil || *service.CheckPath == "" {
+						log.Infof("Skipping ds=%s no check path set", xmlID)
+						continue
+					}
+					if matched, _ := regexp.Match(`^/`, []byte(*service.CheckPath)); matched == false {
+						//prepend leading slash if missing
+						*service.CheckPath = "/" + *service.CheckPath
+					}
+					protocol = *service.Protocol
+					conf_dscp = strconv.Itoa(*service.DSCP)
+					check_path := service.CheckPath
+					routing_name := service.RoutingName
+					log.Infof("checking ds=%s server=%s cdn=%s dscp=%s", xmlID, s.fqdn, s.cdn, conf_dscp)
+					for _, match := range ds_matchlist[xmlID] {
+						if match.Type == "HOST_REGEXP" {
+							if matched, err := regexp.MatchString(`\*`, match.Pattern); err != nil {
+								log.Errorf("%s", err)
+							} else if matched == true {
+								re := regexp.MustCompile(`(\\|\.\*)`)
+								host_header = re.ReplaceAllString(match.Pattern, "")
+								matched, err = regexp.MatchString(`^DNS.*`, string(ds_types[xmlID]))
+								if err != nil {
+									log.Errorf("%s", err)
+								}
+								if matched == true {
+									host_header = *routing_name + host_header + cdn_map[s.cdn]
+								} else {
+									host_header = s.name + host_header + cdn_map[s.cdn]
+								}
+							} else {
+								host_header = match.Pattern
+							}
+						}
+					}
+					var v6flag bool
+					if doV4 {
+						// do IPv4 stuff
+						v6flag = false
+						cap_dscp = protocol_picker(s, check_ip4, host_header, *check_path, v6flag)
+						success := check_result(conf_dscp, cap_dscp)
+						if success == false {
+							// retry to be sure - something like out-of-order packets may have been an issue
+							log.Infof("first IPv4 check failed - retrying")
+							cap_dscp = protocol_picker(s, check_ip4, host_header, *check_path, v6flag)
+							success = check_result(conf_dscp, cap_dscp)
+						}
+						if success == false {
+							log.Infof("result=failure type=ipv4 server=%s cdn=%s ds=%s ip=%s want=%s got=%s", s.fqdn, s.cdn, xmlID, check_ip4, conf_dscp, cap_dscp)
+							s.failcount++
+						} else {
+							if cap_dscp == "-1" {
+								cap_dscp = "CAPTURE_TIMEOUT (IGNORING)"
+							}
+							log.Infof("result=success type=ipv4 server=%s cdn=%s ds=%s ip=%s want=%s got=%s", s.fqdn, s.cdn, xmlID, check_ip4, conf_dscp, cap_dscp)
+						}
+					}
+					if doV6 {
+						// do IPv6 stuff
+						v6flag = true
+						cap_dscp = protocol_picker(s, check_ip6, host_header, *check_path, v6flag)
+						success := check_result(conf_dscp, cap_dscp)
+						if success == false {
+							// retry to be sure - something like out-of-order packets may have been an issue
+							log.Infof("first IPv6 check failed - retrying")
+							cap_dscp = protocol_picker(s, check_ip6, host_header, *check_path, v6flag)
+							success = check_result(conf_dscp, cap_dscp)
+						}
+						if success == false {
+							log.Infof("result=failure type=ipv6 server=%s cdn=%s ds=%s ip=%s want=%s got=%s", s.fqdn, s.cdn, xmlID, check_ip6, conf_dscp, cap_dscp)
+							s.failcount++
+						} else {
+							if cap_dscp == "-1" {
+								cap_dscp = "CAPTURE_TIMEOUT (IGNORING)"
+							}
+							log.Infof("result=success type=ipv6 server=%s cdn=%s ds=%s ip=%s want=%s got=%s", s.fqdn, s.cdn, xmlID, check_ip6, conf_dscp, cap_dscp)
+						}
+					}
+				}
+			}
+			// send status update to TO
+			if s.failcount == -1 {
+				// server not checked
+				*statusData.Value = -1
+			} else if s.failcount > 0 {
+				// server had failures
+				*statusData.Value = 0
+			} else {
+				// server looks OK
+				*statusData.Value = 1
+			}
+			serverElapsed := time.Since(serverStart)
+			log.Infof("Finished checking server=%s result=%d cdn=%s elapsed=%s", s.fqdn, *statusData.Value, s.cdn, serverElapsed)
+			if *confQuiet == false {
+				log.Debugf("Sending update to TO")
+				_, _, err := session.InsertServerCheckStatus(statusData)
+				if err != nil {
+					log.Errorf("Error updating server check status with TO:", err)
+				}
+			} else {
+				log.Debugf("Skipping update to TO")
+			}
+		}
+	}
+	jobElapsed := time.Since(jobStart)
+	log.Infof("Job complete", jobElapsed)
+	os.Exit(0)
+}

Review Comment:
   Invalid text file: missing terminating newline



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@trafficcontrol.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org