forked from bluecmd/spectrum_virtualize_exporter
-
Notifications
You must be signed in to change notification settings - Fork 1
/
spectrum_virtualize_exporter.go
153 lines (136 loc) · 4.62 KB
/
spectrum_virtualize_exporter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Server executable of spectrum_virtualize_exporter
//
// Copyright (C) 2020 Christian Svensson
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gopkg.in/yaml.v2"
)
var (
authMapFile = flag.String("auth-file", "spectrum-monitor.yaml", "file containing the authentication map to use when connecting to a Spectrum Virtualize device")
listen = flag.String("listen", ":9747", "address to listen on")
timeoutSeconds = flag.Int("scrape-timeout", 30, "max seconds to allow a scrape to take")
insecure = flag.Bool("insecure", false, "Allow insecure certificates")
extraCAs = flag.String("extra-ca-cert", "", "file containing extra PEMs to add to the CA trust store")
authMap = map[string]Auth{}
)
type Auth struct {
User string
Password string
}
type SpectrumHTTP interface {
Get(path string, query string, obj interface{}) error
}
func newSpectrumClient(ctx context.Context, tgt url.URL, hc *http.Client) (SpectrumHTTP, error) {
auth, ok := authMap[tgt.String()]
if !ok {
return nil, fmt.Errorf("no api authentication registered for %q", tgt.String())
}
if auth.User != "" && auth.Password != "" {
c, err := newSpectrumPasswordClient(ctx, tgt, hc, auth.User, auth.Password)
if err != nil {
return nil, err
}
return c, nil
}
return nil, fmt.Errorf("invalid authentication data for %q", tgt.String())
}
func probeHandler(w http.ResponseWriter, r *http.Request, tr *http.Transport) {
params := r.URL.Query()
target := params.Get("target")
if target == "" {
http.Error(w, "Target parameter missing or empty", http.StatusBadRequest)
return
}
probeSuccessGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "probe_success",
Help: "Whether or not the probe succeeded",
})
probeDurationGauge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "probe_duration_seconds",
Help: "How many seconds the probe took to complete",
})
ctx, cancel := context.WithTimeout(r.Context(), time.Duration(*timeoutSeconds)*time.Second)
defer cancel()
registry := prometheus.NewRegistry()
registry.MustRegister(probeSuccessGauge)
registry.MustRegister(probeDurationGauge)
start := time.Now()
success, err := probe(ctx, target, registry, &http.Client{Transport: tr})
if err != nil {
log.Printf("Probe request rejected; error is: %v", err)
http.Error(w, fmt.Sprintf("probe: %v", err), http.StatusBadRequest)
return
}
duration := time.Since(start).Seconds()
probeDurationGauge.Set(duration)
if success {
probeSuccessGauge.Set(1)
log.Printf("Probe of %q succeeded, took %.3f seconds", target, duration)
} else {
// probeSuccessGauge default is 0
log.Printf("Probe of %q failed, took %.3f seconds", target, duration)
}
h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{})
h.ServeHTTP(w, r)
}
func main() {
flag.Parse()
af, err := os.ReadFile(*authMapFile)
if err != nil {
log.Fatalf("Failed to read API authentication map file: %v", err)
}
if err := yaml.Unmarshal(af, &authMap); err != nil {
log.Fatalf("Failed to parse API authentication map file: %v", err)
}
roots, err := x509.SystemCertPool()
if err != nil {
log.Fatalf("Unable to fetch system CA store: %v", err)
}
if *extraCAs != "" {
certs, err := os.ReadFile(*extraCAs)
if err != nil {
log.Fatalf("Failed to read extra CA file: %v", err)
}
if ok := roots.AppendCertsFromPEM(certs); !ok {
log.Fatalf("Failed to append certs from PEM, unknown error")
}
}
tc := &tls.Config{RootCAs: roots}
if *insecure {
tc.InsecureSkipVerify = true
}
tr := &http.Transport{TLSClientConfig: tc}
log.Printf("Loaded %d API credentials", len(authMap))
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/probe", func(w http.ResponseWriter, r *http.Request) {
probeHandler(w, r, tr)
})
go http.ListenAndServe(*listen, nil)
log.Printf("Spectrum Virtualize exporter running, listening on %q", *listen)
select {}
}