Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add version metric #31

Merged
merged 3 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Metrics are retrieved using the [Proxmox Backup Server API](https://pbs.proxmox.
| Metric | Meaning | Labels |
| ------------------------------ | ------------------------------------------------------- | -------------------------------------------- |
| pbs_up | Was the last query of Proxmox Backup Server successful? | |
| pbs_version | Version of Proxmox Backup Server | `version`, `repoid`, `release` |
| pbs_available | The available bytes of the underlying storage. | `datastore` |
| pbs_size | The size of the underlying storage in bytes. | `datastore` |
| pbs_used | The used bytes of the underlying storage. | `datastore` |
Expand Down
79 changes: 78 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
)

const promNamespace = "pbs"
const versionApi = "/api2/json/version"
const datastoreUsageApi = "/api2/json/status/datastore-usage"
const datastoreApi = "/api2/json/admin/datastore"
const nodeApi = "/api2/json/nodes"
Expand Down Expand Up @@ -65,6 +66,11 @@ var (
"Was the last query of PBS successful.",
nil, nil,
)
version = prometheus.NewDesc(
prometheus.BuildFQName(promNamespace, "", "version"),
"Version of the PBS installation.",
[]string{"version", "repoid", "release"}, nil,
)
available = prometheus.NewDesc(
prometheus.BuildFQName(promNamespace, "", "available"),
"The available bytes of the underlying storage.",
Expand Down Expand Up @@ -177,6 +183,14 @@ var (
)
)

type VersionResponse struct {
Data struct {
Release string `json:"release"`
Repoid string `json:"repoid"`
Version string `json:"version"`
} `json:"data"`
}

type DatastoreResponse struct {
Data []struct {
Avail int64 `json:"avail"`
Expand Down Expand Up @@ -268,6 +282,7 @@ func NewExporter(endpoint string, username string, apitoken string, apitokenname

func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- up
ch <- version
ch <- available
ch <- size
ch <- used
Expand Down Expand Up @@ -308,6 +323,13 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
}

func (e *Exporter) collectFromAPI(ch chan<- prometheus.Metric) error {

// get version
err := e.getVersion(ch)
if err != nil {
return err
}

// get datastores
req, err := http.NewRequest("GET", e.endpoint+datastoreUsageApi, nil)
if err != nil {
Expand All @@ -320,7 +342,7 @@ func (e *Exporter) collectFromAPI(ch chan<- prometheus.Metric) error {
// debug
if *loglevel == "debug" {
log.Printf("DEBUG: Request URL: %s", req.URL)
//log.Printf("DEBUG: Request Header: %s", vmID)
// log.Printf("DEBUG: Request Header: %s", vmID)
}

// make request and show output
Expand Down Expand Up @@ -372,6 +394,61 @@ func (e *Exporter) collectFromAPI(ch chan<- prometheus.Metric) error {
return nil
}

func (e *Exporter) getVersion(ch chan<- prometheus.Metric) error {
// get version
req, err := http.NewRequest("GET", e.endpoint+versionApi, nil)
if err != nil {
return err
}

// add Authorization header
req.Header.Set("Authorization", e.authorizationHeader)

// debug
if *loglevel == "debug" {
log.Printf("DEBUG: Request URL: %s", req.URL)
// log.Printf("DEBUG: Request Header: %s", vmID)
}

// make request and show output
resp, err := client.Do(req)
if err != nil {
return err
}

body, err := io.ReadAll(resp.Body)
if err := resp.Body.Close(); err != nil {
log.Printf("Error closing response body: %v", err)
}
if err != nil {
return err
}

// debug
if *loglevel == "debug" {
log.Printf("DEBUG: Status code %d returned from endpoint: %s", resp.StatusCode, e.endpoint)
//log.Printf("DEBUG: Response body: %s", string(body))
}

// check if status code is 200
if resp.StatusCode != 200 {
return fmt.Errorf("ERROR: Status code %d returned from endpoint: %s", resp.StatusCode, e.endpoint)
}

// parse json
var response VersionResponse
err = json.Unmarshal(body, &response)
if err != nil {
return err
}

ch <- prometheus.MustNewConstMetric(
version, prometheus.GaugeValue, 1, response.Data.Version, response.Data.Repoid, response.Data.Release,
)

return nil
}

func (e *Exporter) getNodeMetrics(ch chan<- prometheus.Metric) error {
// NOTE: According to the api documentation, we have to provide the node name (won't work with the node ip),
// but it seems to work with any name, so we just use "localhost" here.
Expand Down