Skip to content

Commit

Permalink
add VM disks metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
czerwonk committed Aug 5, 2019
1 parent a2fc594 commit 8c0570d
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 28 deletions.
23 changes: 23 additions & 0 deletions disk/disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package disk

import "github.com/czerwonk/ovirt_exporter/storagedomain"

// Disk represents the disk resource
type Disk struct {
ID string `xml:"id,attr"`
Name string `xml:"name,omitempty"`
Alias string `xml:"alias,omitempty"`
ProvisionedSize uint64 `xml:"provisioned_size,omitempty"`
ActualSize uint64 `xml:"actual_size,omitempty"`
TotalSize uint64 `xml:"total_size,omitempty"`
StorageDomains *storagedomain.StorageDomains `xml:"storage_domains,omitempty"`
}

// StorageDomainName returns the name of the storage domain of the disk
func (d *Disk) StorageDomainName() string {
if len(d.StorageDomains.Domains) == 0 {
return ""
}

return d.StorageDomains.Domains[0].Name
}
28 changes: 28 additions & 0 deletions disk/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package disk

import (
"fmt"

"github.com/czerwonk/ovirt_api/api"
"github.com/czerwonk/ovirt_exporter/storagedomain"
)

// Get retrieves disk information
func Get(id string, client *api.Client) (*Disk, error) {
path := fmt.Sprintf("disks/%s", id)

d := &Disk{}
err := client.GetAndParse(path, &d)
if err != nil {
return nil, err
}

for i, dom := range d.StorageDomains.Domains {
d.StorageDomains.Domains[i] = storagedomain.StorageDomain{
ID: dom.ID,
Name: storagedomain.Name(dom.ID, client),
}
}

return d, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/czerwonk/ovirt_exporter

require (
github.com/czerwonk/ovirt_api v0.0.0-20180321161247-63e3f014686c
github.com/pkg/errors v0.8.0
github.com/prometheus/client_golang v0.9.4
github.com/prometheus/common v0.4.1
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down
2 changes: 2 additions & 0 deletions host/host.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package host

// Hosts is a collection of Host
type Hosts struct {
Hosts []Host `xml:"host"`
}

// Host represents the host resource
type Host struct {
ID string `xml:"id,attr"`
Name string `xml:"name"`
Expand Down
27 changes: 21 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@ import (
"github.com/czerwonk/ovirt_exporter/host"
"github.com/czerwonk/ovirt_exporter/storagedomain"
"github.com/czerwonk/ovirt_exporter/vm"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/prometheus/common/log"
)

const version string = "0.8.6"
const version string = "0.9.0"

var (
showVersion = flag.Bool("version", false, "Print version information.")
listenAddress = flag.String("web.listen-address", ":9325", "Address on which to expose metrics and web interface.")
metricsPath = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
apiURL = flag.String("api.url", "https://localhost/ovirt-engine/api/", "API REST Endpoint")
apiUser = flag.String("api.username", "user@internal", "API username")
apiPass = flag.String("api.password", "", "API password")
apiPassFile = flag.String("api.password-file", "", "File containing the API password")
apiInsecureCert = flag.Bool("api.insecure-cert", false, "Skip verification for untrusted SSL/TLS certificates")
withSnapshots = flag.Bool("with-snapshots", true, "Collect snapshot metrics (can be time consuming in some cases)")
withNetwork = flag.Bool("with-network", true, "Collect network metrics (can be time consuming in some cases)")
withDisks = flag.Bool("with-disks", true, "Collect disk metrics (can be time consuming in some cases)")
debug = flag.Bool("debug", false, "Show verbose output (e.g. body of each response received from API)")

collectorDuration = prometheus.NewHistogramVec(
Expand Down Expand Up @@ -112,23 +115,35 @@ func connectAPI() (*api.Client, error) {
opts = append(opts, api.WithInsecure())
}

b, err := ioutil.ReadFile(*apiPassFile)
pass, err := apiPassword()
if err != nil {
return nil, err
return nil, errors.Wrap(err, "error while reading password file")
}
apiPass := strings.Trim(string(b), "\n")

client, err := api.NewClient(*apiURL, *apiUser, apiPass, opts...)
client, err := api.NewClient(*apiURL, *apiUser, pass, opts...)
if err != nil {
return nil, err
}

return client, err
}

func apiPassword() (string, error) {
if *apiPassFile == "" {
return *apiPass, nil
}

b, err := ioutil.ReadFile(*apiPassFile)
if err != nil {
return "", err
}

return strings.Trim(string(b), "\n"), nil
}

func handleMetricsRequest(w http.ResponseWriter, r *http.Request, client *api.Client, appReg *prometheus.Registry) {
reg := prometheus.NewRegistry()
reg.MustRegister(vm.NewCollector(client, *withSnapshots, *withNetwork, collectorDuration.WithLabelValues("vm")))
reg.MustRegister(vm.NewCollector(client, *withSnapshots, *withNetwork, *withDisks, collectorDuration.WithLabelValues("vm")))
reg.MustRegister(host.NewCollector(client, *withNetwork, collectorDuration.WithLabelValues("host")))
reg.MustRegister(storagedomain.NewCollector(client, collectorDuration.WithLabelValues("storage")))

Expand Down
1 change: 1 addition & 0 deletions metric/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package metric

import "github.com/prometheus/client_golang/prometheus"

// MustCreate creates a new prometheus metric
func MustCreate(desc *prometheus.Desc, v float64, labelValues []string) prometheus.Metric {
return prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, float64(v), labelValues...)
}
2 changes: 2 additions & 0 deletions network/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

// CollectMetricsForHost collects net metrics for a specific Host
func CollectMetricsForHost(path, prefix string, labelNames, labelValues []string, client *api.Client, ch chan<- prometheus.Metric) error {
nics := &HostNics{}
err := client.GetAndParse(path, nics)
Expand All @@ -20,6 +21,7 @@ func CollectMetricsForHost(path, prefix string, labelNames, labelValues []string
return collectForNics(nics.Nics, path, prefix, labelNames, labelValues, client, ch)
}

// CollectMetricsForVM collects net metrics for a specific VM
func CollectMetricsForVM(path, prefix string, labelNames, labelValues []string, client *api.Client, ch chan<- prometheus.Metric) error {
nics := &VMNics{}
err := client.GetAndParse(path, nics)
Expand Down
3 changes: 3 additions & 0 deletions network/nic.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package network

// HostNics is a collection of NICs of a host
type HostNics struct {
Nics []Nic `xml:"host_nic"`
}

// VMNics is a collection of NICs of a VM
type VMNics struct {
Nics []Nic `xml:"nic"`
}

// Nic represents the network interface card resource
type Nic struct {
ID string `xml:"id,attr"`
Name string `xml:"name"`
Expand Down
2 changes: 2 additions & 0 deletions statistic/staticstic.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package statistic

// Statistics is a collection of Statistics
type Statistics struct {
Statistic []Statistic `xml:"statistic"`
}

// Statistic represents the statistic resource
type Statistic struct {
Name string `xml:"name"`
Description string `xml:"description"`
Expand Down
52 changes: 52 additions & 0 deletions storagedomain/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package storagedomain

import (
"sync"

"fmt"

"github.com/czerwonk/ovirt_api/api"
"github.com/prometheus/common/log"
)

var (
cacheMutex = sync.Mutex{}
nameCache = make(map[string]string)
)

// Get retrieves domain information
func Get(id string, client *api.Client) (*StorageDomain, error) {
path := fmt.Sprintf("storagedomains/%s", id)

d := StorageDomain{}
err := client.GetAndParse(path, &d)
if err != nil {
return nil, err
}

return &d, nil
}

// Name retrieves domain name
func Name(id string, client *api.Client) string {
cacheMutex.Lock()
defer cacheMutex.Unlock()

if n, found := nameCache[id]; found {
return n
}

d, err := Get(id, client)
if err != nil {
log.Error(err)
return ""
}

if (d == nil) {
log.Errorf("could not find name for storage domain with ID %s", id)
return ""
}

nameCache[id] = d.Name
return d.Name
}
22 changes: 12 additions & 10 deletions storagedomain/storage_domain.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package storagedomain

// StorageDomains is a collection of storage domains
type StorageDomains struct {
Domains []StorageDomain `xml:"storage_domain"`
}

// StorageDomain represents the storage domain resource
type StorageDomain struct {
ID string `xml:"id,attr"`
Name string `xml:"name"`
Name string `xml:"name,omitempty"`
Storage struct {
Path string `xml:"path"`
Type string `xml:"type"`
Path string `xml:"path,omitempty"`
Type string `xml:"type,omitempty"`
} `xml:"storage,omitempty"`
Type string `xml:"type"`
Available float64 ` xml:"available"`
Committed float64 `xml:"committed"`
Used float64 `xml:"used"`
ExternalStatus string `xml:"external_status"`
Master bool `xml:"master"`
Type string `xml:"type,omitempty"`
Available float64 ` xml:"available,omitempty"`
Committed float64 `xml:"committed,omitempty"`
Used float64 `xml:"used,omitempty"`
ExternalStatus string `xml:"external_status,omitempty"`
Master bool `xml:"master,omitempty"`
DataCenters struct {
DataCenter struct {
ID string `xml:"id,attr"`
} `xml:"data_center"`
} `xml:"data_centers"`
} `xml:"data_centers,omitempty"`
}
14 changes: 14 additions & 0 deletions vm/disk_attachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package vm

import "github.com/czerwonk/ovirt_exporter/disk"

// DiskAttachments is a collection of diskattachments
type DiskAttachments struct {
Attachment []DiskAttachment `xml:"disk_attachment"`
}

// DiskAttachment represents the diskattachment resource
type DiskAttachment struct {
LogicalName string `xml:"logical_name"`
Disk disk.Disk `xml:"disk"`
}
4 changes: 3 additions & 1 deletion vm/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package vm

import "time"

// Snapshots is a collection of snapshots
type Snapshots struct {
Snapshot []Snapshot `xml:"snapshot"`
}

// Snapshot repesents the snapshot resource
type Snapshot struct {
Id string `xml:"id,attr"`
ID string `xml:"id,attr"`
Description string `xml:"description"`
Date time.Time `xml:"date"`
PersistMemorystate bool `xml:"persist_memorystate"`
Expand Down
2 changes: 2 additions & 0 deletions vm/vm.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package vm

// VMs is a collection of virtual machines
type VMs struct {
VMs []VM `xml:"vm"`
}

// VM represents the virutal machine resource
type VM struct {
ID string `xml:"id,attr"`
Name string `xml:"name"`
Expand Down
Loading

0 comments on commit 8c0570d

Please sign in to comment.