Mtr exporter

From UVOO Tech Wiki
Revision as of 14:08, 14 June 2024 by Busk (talk | contribs)
Jump to navigation Jump to search

mtr_probe.go

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "os/exec"

    "github.com/gorilla/mux"
)

type MTRResult struct {
    // Define the structure to match MTR JSON output
}

func runMTR(target string) (string, error) {
    cmd := exec.Command("mtr", "--report", "--json", target)
    out, err := cmd.Output()
    if err != nil {
        return "", err
    }
    return string(out), nil
}

func probeHandler(w http.ResponseWriter, r *http.Request) {
    target := r.URL.Query().Get("target")
    if target == "" {
        target = "8.8.8.8"
    }

    result, err := runMTR(target)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(result))
}

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/probe", probeHandler).Methods("GET")
    http.Handle("/", r)
    log.Println("Starting server on :9115")
    log.Fatal(http.ListenAndServe(":9115", nil))
}


Dockerfile

# Start from the official Go image
FROM golang:1.18 as builder

# Set the Current Working Directory inside the container
WORKDIR /app

# Copy go.mod and go.sum files
COPY go.mod go.sum ./

# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
RUN go mod download

# Copy the source from the current directory to the Working Directory inside the container
COPY . .

# Build the Go app
RUN go build -o mtr_probe .

# Use a minimal image to run the application
FROM debian:bullseye-slim

# Install MTR
RUN apt-get update && apt-get install -y mtr-tiny

# Copy the pre-built binary file from the previous stage
COPY --from=builder /app/mtr_probe /usr/local/bin/mtr_probe

# Expose port 9115 to the outside world
EXPOSE 9115

# Command to run the executable
CMD ["mtr_probe"]

go.mod

module mtr_probe

go 1.18

require github.com/gorilla/mux v1.8.0

sh

# Build the Docker image
docker build -t mtr-probe .

# Run the Docker container
docker run -d -p 9115:9115 mtr-probe


scrape_configs:
  - job_name: 'blackbox'
    metrics_path: /probe
    params:
      module: [mtr]  # Look for an 'mtr' module in blackbox exporter config.
    static_configs:
      - targets:
        - 'example.com'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115  # MTR prober address

More

# Stage 1: Build the Go application
FROM golang:1.18 as builder

# Set the working directory inside the container
WORKDIR /app

# Copy go.mod and go.sum files
COPY go.mod go.sum ./

# Download all dependencies
RUN go mod download

# Copy the source code
COPY . .

# Build the Go application
RUN go build -o mtr_probe .

# Stage 2: Build the final image
FROM debian:bullseye-slim

# Install MTR and necessary utilities
RUN apt-get update && apt-get install -y mtr-tiny wget ca-certificates

# Install Blackbox Exporter
RUN wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.23.0/blackbox_exporter-0.23.0.linux-amd64.tar.gz \
    && tar -xzf blackbox_exporter-0.23.0.linux-amd64.tar.gz \
    && mv blackbox_exporter-0.23.0.linux-amd64/blackbox_exporter /usr/local/bin/ \
    && rm -rf blackbox_exporter-0.23.0.linux-amd64*

# Copy the Go application binary
COPY --from=builder /app/mtr_probe /usr/local/bin/mtr_probe

# Create a blackbox exporter config
COPY blackbox.yml /etc/blackbox_exporter/config.yml

# Expose ports for both services
EXPOSE 9115 9116

# Start both the Go application and Blackbox Exporter
CMD ["sh", "-c", "mtr_probe & blackbox_exporter --config.file=/etc/blackbox_exporter/config.yml"]

blacbox.yaml

modules:
  http_2xx:
    prober: http
    timeout: 5s
    http:
      valid_http_versions: ["HTTP/1.1", "HTTP/2"]
      valid_status_codes: []  # Defaults to 2xx
      method: GET
  mtr:
    prober: mtr
    timeout: 30s
    mtr:
      targets:
        - 8.8.8.8  # Example target

docker run -d -p 9115:9115 -p 9116:9116 mtr-blackbox-exporter

more2

```package main

import (

   "encoding/json"
   "log"
   "net/http"
   "os/exec"
   "strconv"
   "strings"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"

)

// Define the MTRResult struct to match MTR JSON output type MTRResult struct {

   Report struct {
       Hubs []struct {
           Count int     json:"count"
           Loss  float64 json:"Loss%"
           Snt   int     json:"Snt"
           Last  float64 json:"Last"
           Avg   float64 json:"Avg"
           Best  float64 json:"Best"
           Wrst  float64 json:"Wrst"
           StDev float64 json:"StDev"
           Host  string  json:"Host"
       } json:"hubs"
   } json:"report"

}

var (

   // Define Prometheus metrics
   mtrLoss = prometheus.NewGaugeVec(prometheus.GaugeOpts{
       Name: "mtr_loss_percentage",
       Help: "Packet loss percentage reported by MTR",
   }, []string{"host", "target"})
mtrAvg = prometheus.NewGaugeVec(prometheus.GaugeOpts{
    Name: "mtr_avg_latency",
    Help: "Average latency reported by MTR",
}, []string{"host", "target"})

mtrBest = prometheus.NewGaugeVec(prometheus.GaugeOpts{
    Name: "mtr_best_latency",
    Help: "Best latency reported by MTR",
}, []string{"host", "target"})

mtrWrst = prometheus.NewGaugeVec(prometheus.GaugeOpts{
    Name: "mtr_wrst_latency",
    Help: "Worst latency reported by MTR",
}, []string{"host", "target"})

)

func init() {

   // Register Prometheus metrics
   prometheus.MustRegister(mtrLoss)
   prometheus.MustRegister(mtrAvg)
   prometheus.MustRegister(mtrBest)
   prometheus.MustRegister(mtrWrst)

}

func runMTR(target string) (MTRResult, error) {

   cmd := exec.Command("mtr", "--report", "--json", target)
   out, err := cmd.Output()
   if err != nil {
       return MTRResult{}, err
   }
var result MTRResult
err = json.Unmarshal(out, &result)
if err != nil {
    return MTRResult{}, err
}
return result, nil

}

func probeHandler(w http.ResponseWriter, r *http.Request) {

   target := r.URL.Query().Get("target")
   if target == "" {
       target = "8.8.8.8"
   }
result, err := runMTR(target)
if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

for _, hub := range result.Report.Hubs {
    host := strings.TrimSpace(hub.Host)
    mtrLoss.WithLabelValues(host, target).Set(hub.Loss)
    mtrAvg.WithLabelValues(host, target).Set(hub.Avg)
    mtrBest.WithLabelValues(host, target).Set(hub.Best)
    mtrWrst.WithLabelValues(host, target).Set(hub.Wrst)
}

w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)

}

func main() {

   r := mux.NewRouter()
   r.HandleFunc("/probe", probeHandler).Methods("GET")
   r.Handle("/metrics", promhttp.Handler())
log.Println("Starting server on :9115")
log.Fatal(http.ListenAndServe(":9115", r))

}

```