package metrics import ( "net/http" "fmt" "log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "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{} ) // 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(` Qemu Exporter by Deevirt

Qemu Exporter by Deevirt

Metrics

`)) }) log.Fatal(http.ListenAndServe(":9177", nil)) }