VictoriaMetrics/lib/promscrape/discovery/ovhcloud/dedicated_server.go
Zhu Jiekun d1d59d6348
feature: [vmagent] Add service discovery support for OVH Cloud VPS and dedicated server (#6160)
### Describe Your Changes
related issue:
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6071

#### Added
- Added service discovery support for OVH Cloud:
    - VPS.
    - Dedicated server.

#### Docs
- `CHANGELOG.md`, `sd_configs.md`, `vmagent.md` are updated.

#### Note
- Useful links: 
    - OVH Cloud VPS API: https://eu.api.ovh.com/console/#/vps~GET
- OVH Cloud Dedicated server API:
https://eu.api.ovh.com/console/#/dedicated/server~GET
    - OVH Cloud SDK: https://github.com/ovh/go-ovh
- Prometheus SD:
https://prometheus.io/docs/prometheus/latest/configuration/configuration/#ovhcloud_sd_config

Tested on OVH Cloud VPS and dedicated server.
<img width="1722" alt="image"
src="https://github.com/VictoriaMetrics/VictoriaMetrics/assets/30280396/d3f0adc8-b0ef-423e-9379-8a9b9b0792ee">

<img width="1724" alt="image"
src="https://github.com/VictoriaMetrics/VictoriaMetrics/assets/30280396/18b5b730-3512-4fc0-8b2c-f2450ac550fd">

---
Signed-off-by: Jiekun <jiekun@victoriametrics.com>
Co-authored-by: hagen1778 <roman@victoriametrics.com>
2024-09-30 15:06:14 +02:00

153 lines
5.4 KiB
Go

package ovhcloud
import (
"encoding/json"
"fmt"
"net/http"
"net/netip"
"net/url"
"path"
"strconv"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
// dedicatedServer struct from API.
// IP addresses are fetched independently.
// See: https://eu.api.ovh.com/console/#/dedicated/server/%7BserviceName%7D~GET and getDedicatedServerDetails
type dedicatedServer struct {
State string `json:"state"`
IPs []netip.Addr
CommercialRange string `json:"commercialRange"`
LinkSpeed int `json:"linkSpeed"`
Rack string `json:"rack"`
NoIntervention bool `json:"noIntervention"`
Os string `json:"os"`
SupportLevel string `json:"supportLevel"`
ServerID int64 `json:"serverId"`
Reverse string `json:"reverse"`
Datacenter string `json:"datacenter"`
Name string `json:"name"`
}
// getDedicatedServerLabels get labels for dedicated servers.
func getDedicatedServerLabels(cfg *apiConfig) ([]*promutils.Labels, error) {
dedicatedServerList, err := getDedicatedServerList(cfg)
if err != nil {
return nil, err
}
// Attach properties to each VPS and compose vpsDetailedList
dedicatedServerDetailList := make([]dedicatedServer, 0, len(dedicatedServerList))
for _, dedicatedServerName := range dedicatedServerList {
dedicatedServer, err := getDedicatedServerDetails(cfg, dedicatedServerName)
if err != nil {
logger.Errorf("getDedicatedServerDetails for %s failed, err: %v", dedicatedServerName, err)
continue
}
dedicatedServerDetailList = append(dedicatedServerDetailList, *dedicatedServer)
}
ms := make([]*promutils.Labels, 0, len(dedicatedServerDetailList))
for _, server := range dedicatedServerDetailList {
// convert IPs into string and select default IP.
var ipv4, ipv6 string
for _, ip := range server.IPs {
if ip.Is4() {
ipv4 = ip.String()
}
if ip.Is6() {
ipv6 = ip.String()
}
}
defaultIP := ipv4
if defaultIP == "" {
defaultIP = ipv6
}
m := promutils.NewLabels(15)
m.Add("__address__", defaultIP)
m.Add("instance", server.Name)
m.Add("__meta_ovhcloud_dedicated_server_state", server.State)
m.Add("__meta_ovhcloud_dedicated_server_commercial_range", server.CommercialRange)
m.Add("__meta_ovhcloud_dedicated_server_link_speed", fmt.Sprintf("%d", server.LinkSpeed))
m.Add("__meta_ovhcloud_dedicated_server_rack", server.Rack)
m.Add("__meta_ovhcloud_dedicated_server_no_intervention", strconv.FormatBool(server.NoIntervention))
m.Add("__meta_ovhcloud_dedicated_server_os", server.Os)
m.Add("__meta_ovhcloud_dedicated_server_support_level", server.SupportLevel)
m.Add("__meta_ovhcloud_dedicated_server_server_id", fmt.Sprintf("%d", server.ServerID))
m.Add("__meta_ovhcloud_dedicated_server_reverse", server.Reverse)
m.Add("__meta_ovhcloud_dedicated_server_datacenter", server.Datacenter)
m.Add("__meta_ovhcloud_dedicated_server_name", server.Name)
m.Add("__meta_ovhcloud_dedicated_server_ipv4", ipv4)
m.Add("__meta_ovhcloud_dedicated_server_ipv6", ipv6)
ms = append(ms, m)
}
return ms, nil
}
// getVPSDetails get properties of a dedicated server.
// Also see: https://eu.api.ovh.com/console/#/dedicated/server/%7BserviceName%7D~GET
func getDedicatedServerDetails(cfg *apiConfig, dedicatedServerName string) (*dedicatedServer, error) {
// get properties.
reqPath := path.Join("/dedicated/server", url.QueryEscape(dedicatedServerName))
resp, err := cfg.client.GetAPIResponseWithReqParams(reqPath, func(request *http.Request) {
request.Header, _ = getAuthHeaders(cfg, request.Header, cfg.client.APIServer(), reqPath)
})
if err != nil {
return nil, fmt.Errorf("request %s error: %v", reqPath, err)
}
var dedicatedServerDetails dedicatedServer
if err = json.Unmarshal(resp, &dedicatedServerDetails); err != nil {
return nil, fmt.Errorf("cannot unmarshal %s response: %v", reqPath, err)
}
// get IPs for this dedicated server.
// e.g. ["139.99.154.111","2402:1f00:8100:401::bb6"]
// Also see: https://eu.api.ovh.com/console/#/dedicated/server/%7BserviceName%7D/ips~GET
reqPath = path.Join(reqPath, "ips")
resp, err = cfg.client.GetAPIResponseWithReqParams(reqPath, func(request *http.Request) {
request.Header, _ = getAuthHeaders(cfg, request.Header, cfg.client.APIServer(), reqPath)
})
if err != nil {
return nil, fmt.Errorf("request %s error: %v", reqPath, err)
}
var ips []string
if err = json.Unmarshal(resp, &ips); err != nil {
return nil, fmt.Errorf("cannot unmarshal %s response: %v", reqPath, err)
}
// handle different IP formats
parsedIPs, err := parseIPList(ips)
if err != nil {
return nil, err
}
dedicatedServerDetails.IPs = parsedIPs
return &dedicatedServerDetails, nil
}
// getDedicatedServerList list available services.
// example: ["ns0000000.ip-00-00-000.eu"]
// Also see: https://eu.api.ovh.com/console/#/dedicated/server~GET
func getDedicatedServerList(cfg *apiConfig) ([]string, error) {
var dedicatedServerList []string
reqPath := "/dedicated/server"
resp, err := cfg.client.GetAPIResponseWithReqParams(reqPath, func(request *http.Request) {
request.Header, _ = getAuthHeaders(cfg, request.Header, cfg.client.APIServer(), reqPath)
})
if err != nil {
return nil, fmt.Errorf("request %s error: %v", reqPath, err)
}
if err = json.Unmarshal(resp, &dedicatedServerList); err != nil {
return nil, fmt.Errorf("cannot unmarshal %s response: %v", reqPath, err)
}
return dedicatedServerList, nil
}