compute/cmd/qemu/metrics/server.go

151 lines
3.8 KiB
Go

package metrics
import (
"net/http"
"fmt"
"log"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gopkg.in/ini.v1"
"libvirt.org/go/libvirt"
)
var (
Version = ""
libvirtUpDesc = prometheus.NewDesc(
prometheus.BuildFQName("libvirt", "", "up"),
"Whether scraping libvirt's metrics was successful.",
nil,
nil)
libvirtVersionsInfoDesc = prometheus.NewDesc(
prometheus.BuildFQName("libvirt", "", "versions_info"),
"Versions of virtualization components",
[]string{"hypervisor_running", "libvirtd_running", "libvirt_library"},
nil)
errorsMap map[string]struct{}
)
func Config() (*ini.File, error) {
return ini.Load("/etc/deevirt/config.ini")
}
// WriteErrorOnce writes message to stdout only once
// for the error
// "err" - an error message
// "name" - name of an error, to count it
func WriteErrorOnce(err string, name string) {
if _, ok := errorsMap[name]; !ok {
log.Printf("%s", err)
errorsMap[name] = struct{}{}
}
}
// CollectFromLibvirt obtains Prometheus metrics from all domains in a
// libvirt setup.
func CollectFromLibvirt(ch chan<- prometheus.Metric, uri string) error {
conn, err := libvirt.NewConnect(uri)
if err != nil {
return err
}
defer conn.Close()
hostname, err := conn.GetHostname()
if err != nil {
return err
}
hypervisorVersionNum, err := conn.GetVersion() // virConnectGetVersion, hypervisor running, e.g. QEMU
if err != nil {
return err
}
hypervisorVersion := fmt.Sprintf("%d.%d.%d", hypervisorVersionNum/1000000%1000, hypervisorVersionNum/1000%1000, hypervisorVersionNum%1000)
libvirtdVersionNum, err := conn.GetLibVersion() // virConnectGetLibVersion, libvirt daemon running
if err != nil {
return err
}
libvirtdVersion := fmt.Sprintf("%d.%d.%d", libvirtdVersionNum/1000000%1000, libvirtdVersionNum/1000%1000, libvirtdVersionNum%1000)
libraryVersionNum, err := libvirt.GetVersion() // virGetVersion, version of libvirt (dynamic) library used by this binary (exporter), not the daemon version
if err != nil {
return err
}
libraryVersion := fmt.Sprintf("%d.%d.%d", libraryVersionNum/1000000%1000, libraryVersionNum/1000%1000, libraryVersionNum%1000)
ch <- prometheus.MustNewConstMetric(
libvirtVersionsInfoDesc,
prometheus.GaugeValue,
1.0,
hypervisorVersion,
libvirtdVersion,
libraryVersion)
CollectNode(conn, ch, hostname)
CollectDomains(conn, ch, hostname)
return nil
}
// LibvirtExporter implements a Prometheus exporter for libvirt state.
type LibvirtExporter struct {
uri string
}
// NewLibvirtExporter creates a new Prometheus exporter for libvirt.
func NewLibvirtExporter(uri string) (*LibvirtExporter, error) {
return &LibvirtExporter{
uri: uri,
}, nil
}
// Describe returns metadata for all Prometheus metrics that may be exported.
func (e *LibvirtExporter) Describe(ch chan<- *prometheus.Desc) {
// Status and versions
ch <- libvirtUpDesc
e.DescribeNode(ch)
e.DescribeDomains(ch)
}
// Collect scrapes Prometheus metrics from libvirt.
func (e *LibvirtExporter) Collect(ch chan<- prometheus.Metric) {
err := CollectFromLibvirt(ch, e.uri)
if err == nil {
ch <- prometheus.MustNewConstMetric(
libvirtUpDesc,
prometheus.GaugeValue,
1.0)
} else {
log.Printf("Failed to scrape metrics: %s", err)
ch <- prometheus.MustNewConstMetric(
libvirtUpDesc,
prometheus.GaugeValue,
0.0)
}
}
func Server() {
exporter, err := NewLibvirtExporter("qemu:///system")
if err != nil {
panic(err)
}
prometheus.MustRegister(exporter)
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`
<html>
<head><title>Qemu Exporter by Deevirt</title></head>
<body>
<h1>Qemu Exporter by Deevirt</h1>
<p><a href='/metrics'>Metrics</a></p>
</body>
</html>`))
})
log.Fatal(http.ListenAndServe(":9177", nil))
}