VictoriaMetrics/lib/promscrape/discovery/openstack/instance.go

141 lines
3.9 KiB
Go
Raw Normal View History

package openstack
import (
"encoding/json"
"fmt"
"path"
"sort"
"strconv"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
// See https://docs.openstack.org/api-ref/compute/#list-servers
type serversDetail struct {
Servers []server `json:"servers"`
Links []struct {
HREF string `json:"href"`
Rel string `json:"rel"`
} `json:"servers_links,omitempty"`
}
type server struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
Name string `json:"name"`
HostID string `json:"hostid"`
Status string `json:"status"`
Addresses map[string][]struct {
Address string `json:"addr"`
Version int `json:"version"`
Type string `json:"OS-EXT-IPS:type"`
} `json:"addresses"`
Metadata map[string]string `json:"metadata,omitempty"`
Flavor struct {
ID string `json:"id"`
} `json:"flavor"`
}
func parseServersDetail(data []byte) (*serversDetail, error) {
var srvd serversDetail
if err := json.Unmarshal(data, &srvd); err != nil {
return nil, fmt.Errorf("cannot parse serversDetail: %w", err)
}
return &srvd, nil
}
func addInstanceLabels(servers []server, port int) []*promutils.Labels {
var ms []*promutils.Labels
for _, server := range servers {
commonLabels := promutils.NewLabels(16)
commonLabels.Add("__meta_openstack_instance_id", server.ID)
commonLabels.Add("__meta_openstack_instance_status", server.Status)
commonLabels.Add("__meta_openstack_instance_name", server.Name)
commonLabels.Add("__meta_openstack_project_id", server.TenantID)
commonLabels.Add("__meta_openstack_user_id", server.UserID)
commonLabels.Add("__meta_openstack_instance_flavor", server.Flavor.ID)
for k, v := range server.Metadata {
commonLabels.Add(discoveryutils.SanitizeLabelName("__meta_openstack_tag_"+k), v)
}
// Traverse server.Addresses in alphabetical order of pool name
// in order to return targets in deterministic order.
sortedPools := make([]string, 0, len(server.Addresses))
for pool := range server.Addresses {
sortedPools = append(sortedPools, pool)
}
sort.Strings(sortedPools)
for _, pool := range sortedPools {
addresses := server.Addresses[pool]
if len(addresses) == 0 {
// skip pool with zero addresses
continue
}
var publicIP string
// its possible to have only one floating ip per pool
for _, ip := range addresses {
if ip.Type != "floating" {
continue
}
publicIP = ip.Address
break
}
for _, ip := range addresses {
// fast return
if len(ip.Address) == 0 || ip.Type == "floating" {
continue
}
// copy labels
m := promutils.NewLabels(20)
m.AddFrom(commonLabels)
m.Add("__meta_openstack_address_pool", pool)
m.Add("__meta_openstack_private_ip", ip.Address)
if len(publicIP) > 0 {
m.Add("__meta_openstack_public_ip", publicIP)
}
m.Add("__address__", discoveryutils.JoinHostPort(ip.Address, port))
ms = append(ms, m)
}
}
}
return ms
}
func (cfg *apiConfig) getServers() ([]server, error) {
creds, err := cfg.getFreshAPICredentials()
if err != nil {
return nil, err
}
computeURL := *creds.computeURL
computeURL.Path = path.Join(computeURL.Path, "servers", "detail")
q := computeURL.Query()
q.Set("all_tenants", strconv.FormatBool(cfg.allTenants))
computeURL.RawQuery = q.Encode()
nextLink := computeURL.String()
var servers []server
for {
resp, err := getAPIResponse(nextLink, cfg)
if err != nil {
return nil, err
}
serversDetail, err := parseServersDetail(resp)
if err != nil {
return nil, err
}
servers = append(servers, serversDetail.Servers...)
if len(serversDetail.Links) == 0 {
return servers, nil
}
nextLink = serversDetail.Links[0].HREF
}
}
func getInstancesLabels(cfg *apiConfig) ([]*promutils.Labels, error) {
srv, err := cfg.getServers()
if err != nil {
return nil, err
}
return addInstanceLabels(srv, cfg.port), nil
}