Skip to content

Commit

Permalink
feat(metrics): dedicated server subscription (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
maxime1907 authored Dec 30, 2024
1 parent 8448b17 commit ac00a6d
Show file tree
Hide file tree
Showing 8 changed files with 432 additions and 81 deletions.
3 changes: 3 additions & 0 deletions pkg/credentials/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ var apiKeyRights = []APIKeyRight{
{Method: "GET", Endpoint: "/cloud/project/*/flavor/*"},
{Method: "GET", Endpoint: "/cloud/project/*/instance"},
{Method: "GET", Endpoint: "/cloud/project/*/instance/*"},
{Method: "GET", Endpoint: "/dedicated/server"},
{Method: "GET", Endpoint: "/dedicated/server/*"},
{Method: "GET", Endpoint: "/dedicated/server/*/serviceInfos"},
}

func generateURL(baseURL string, apiKeyRights []APIKeyRight) string {
Expand Down
88 changes: 7 additions & 81 deletions pkg/network/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ import (
"net/http"
"os"
"strconv"
"strings"
"time"

"github.com/ovh/go-ovh/ovh"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog"
"github.com/urfave/cli"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/api"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/models"
)

var ServeCommand = cli.Command{
Expand All @@ -25,92 +22,22 @@ var ServeCommand = cli.Command{

var logger = zerolog.New(os.Stdout).With().Timestamp().Logger()

var cloudProjectInstanceBilling = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "ovh_exporter_cloud_project_instance_billing",
Help: "Tracks the billing for OVH cloud project instances.",
},
[]string{"project_id", "instance_id", "instance_name", "billing_type", "billing_code", "billing_monthly_date", "billing_monthly_status"},
)

func pingHandler(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "pong")
}

func initializeMetrics() {
prometheus.MustRegister(cloudProjectInstanceBilling)
prometheus.MustRegister(dedicatedServerSubscription)
}

func setCloudProjectInstanceBilling(projectID string, instanceID string, instanceName string, billingType string, billingCode string, billingMonthlyDate time.Time, billingMonthlyStatus string, amount float64) {
billingMonthlyDateFormatted := billingMonthlyDate.Format("2006-01-02 15:04:05")

cloudProjectInstanceBilling.With(prometheus.Labels{
"project_id": projectID,
"instance_id": instanceID,
"instance_name": instanceName,
"billing_type": billingType,
"billing_code": billingCode,
"billing_monthly_date": billingMonthlyDateFormatted,
"billing_monthly_status": billingMonthlyStatus,
}).Set(amount)
}

func updateCloudProviderInstanceBillingPerInstance(projectID string, instance models.InstanceSummary, flavors []models.Flavor) {
logger.Info().Msgf("updating cloud provider instance billing for instance %s", instance.ID)

flavor := api.FindFlavorByID(flavors, instance.FlavorID)
planType := "undefined"
if instance.PlanCode != nil && flavor.PlanCodes.Hourly != nil && flavor.PlanCodes.Monthly != nil {
switch {
case *instance.PlanCode == *flavor.PlanCodes.Hourly:
planType = "hourly"
case *instance.PlanCode == *flavor.PlanCodes.Monthly:
planType = "monthly"
}
}
instancePlanCode := "undefined"
if instance.PlanCode != nil {
instancePlanCode = *instance.PlanCode
}
instanceMonthlyBillingSince := time.Unix(0, 0)
instanceMonthlyBillingStatus := "undefined"
if instance.MonthlyBilling != nil {
instanceMonthlyBillingSince = instance.MonthlyBilling.Since
instanceMonthlyBillingStatus = instance.MonthlyBilling.Status
}

setCloudProjectInstanceBilling(projectID, instance.ID, instance.Name, planType, instancePlanCode, instanceMonthlyBillingSince, instanceMonthlyBillingStatus, 1)
}

func updateCloudProviderInstanceBillingPerProjectID(ovhClient *ovh.Client, projectID string) {
logger.Info().Msgf("updating cloud provider instance billing for project %s", projectID)

projectInstances, err := api.GetCloudProjectInstances(ovhClient, projectID)
if err != nil {
logger.Error().Msgf("Failed to retrieve instances: %v", err)
return
}

flavors, err := api.GetCloudProjectFlavorsPerInstances(ovhClient, projectID, projectInstances)
if err != nil {
logger.Error().Msgf("Failed to retrieve flavors: %v", err)
return
}

for _, instance := range projectInstances {
updateCloudProviderInstanceBillingPerInstance(projectID, instance, flavors)
}
}

func updateCloudProviderInstanceBilling(ovhClient *ovh.Client) {
projectIDs := os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_BILLING_PROJECT_IDS")
func updateMetrics(ovhClient *ovh.Client) {
cloudProjectInstanceBilling.Reset()
updateCloudProviderInstanceBilling(ovhClient)

projectIDList := strings.Split(projectIDs, ",")

for _, projectID := range projectIDList {
updateCloudProviderInstanceBillingPerProjectID(ovhClient, projectID)
}
dedicatedServerSubscription.Reset()
updateDedicatedServersSubscription(ovhClient)
}

func setupCacheUpdater(ovhClient *ovh.Client) {
Expand All @@ -127,8 +54,7 @@ func setupCacheUpdater(ovhClient *ovh.Client) {
defer ticker.Stop()

for {
cloudProjectInstanceBilling.Reset()
updateCloudProviderInstanceBilling(ovhClient)
updateMetrics(ovhClient)

<-ticker.C
}
Expand Down
91 changes: 91 additions & 0 deletions pkg/network/serve_cloud_project_instance_billing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package network

import (
"os"
"strings"
"time"

"github.com/ovh/go-ovh/ovh"
"github.com/prometheus/client_golang/prometheus"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/api"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/models"
)

var cloudProjectInstanceBilling = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "ovh_exporter_cloud_project_instance_billing",
Help: "Tracks the billing for OVH cloud project instances.",
},
[]string{"project_id", "instance_id", "instance_name", "billing_type", "billing_code", "billing_monthly_date", "billing_monthly_status"},
)

func setCloudProjectInstanceBilling(projectID string, instanceID string, instanceName string, billingType string, billingCode string, billingMonthlyDate time.Time, billingMonthlyStatus string, amount float64) {
billingMonthlyDateFormatted := billingMonthlyDate.Format("2006-01-02 15:04:05")

cloudProjectInstanceBilling.With(prometheus.Labels{
"project_id": projectID,
"instance_id": instanceID,
"instance_name": instanceName,
"billing_type": billingType,
"billing_code": billingCode,
"billing_monthly_date": billingMonthlyDateFormatted,
"billing_monthly_status": billingMonthlyStatus,
}).Set(amount)
}

func updateCloudProviderInstanceBillingPerInstance(projectID string, instance models.InstanceSummary, flavors []models.Flavor) {
logger.Info().Msgf("updating cloud provider instance billing for instance %s", instance.ID)

flavor := api.FindFlavorByID(flavors, instance.FlavorID)
planType := "undefined"
if instance.PlanCode != nil && flavor.PlanCodes.Hourly != nil && flavor.PlanCodes.Monthly != nil {
switch {
case *instance.PlanCode == *flavor.PlanCodes.Hourly:
planType = "hourly"
case *instance.PlanCode == *flavor.PlanCodes.Monthly:
planType = "monthly"
}
}
instancePlanCode := "undefined"
if instance.PlanCode != nil {
instancePlanCode = *instance.PlanCode
}
instanceMonthlyBillingSince := time.Unix(0, 0)
instanceMonthlyBillingStatus := "undefined"
if instance.MonthlyBilling != nil {
instanceMonthlyBillingSince = instance.MonthlyBilling.Since
instanceMonthlyBillingStatus = instance.MonthlyBilling.Status
}

setCloudProjectInstanceBilling(projectID, instance.ID, instance.Name, planType, instancePlanCode, instanceMonthlyBillingSince, instanceMonthlyBillingStatus, 1)
}

func updateCloudProviderInstanceBillingPerProjectID(ovhClient *ovh.Client, projectID string) {
logger.Info().Msgf("updating cloud provider instance billing for project %s", projectID)

projectInstances, err := api.GetCloudProjectInstances(ovhClient, projectID)
if err != nil {
logger.Error().Msgf("Failed to retrieve instances: %v", err)
return
}

flavors, err := api.GetCloudProjectFlavorsPerInstances(ovhClient, projectID, projectInstances)
if err != nil {
logger.Error().Msgf("Failed to retrieve flavors: %v", err)
return
}

for _, instance := range projectInstances {
updateCloudProviderInstanceBillingPerInstance(projectID, instance, flavors)
}
}

func updateCloudProviderInstanceBilling(ovhClient *ovh.Client) {
projectIDs := os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_BILLING_PROJECT_IDS")

projectIDList := strings.Split(projectIDs, ",")

for _, projectID := range projectIDList {
updateCloudProviderInstanceBillingPerProjectID(ovhClient, projectID)
}
}
91 changes: 91 additions & 0 deletions pkg/network/serve_dedicated_server_subscription.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package network

import (
"strconv"
"time"

"github.com/ovh/go-ovh/ovh"
"github.com/prometheus/client_golang/prometheus"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/api"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/models"
)

var dedicatedServerSubscription = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "ovh_exporter_dedicated_server_subscription",
Help: "Tracks the subscription for OVH dedicated servers.",
},
[]string{"server_id", "status", "creation", "expiration", "engaged_up_to", "renewal_type", "renew_automatic", "renew_period", "renew_manual_payment", "renew_forced", "renew_delete_at_expiration"},
)

func setDedicatedServerSubscription(server models.Server, serviceinfos models.ServiceInfo, amount float64) {
renewAutomatic := false
if serviceinfos.Renew != nil {
renewAutomatic = serviceinfos.Renew.Automatic
}

renewDeleteAtExpiration := false
if serviceinfos.Renew != nil {
renewDeleteAtExpiration = serviceinfos.Renew.DeleteAtExpiration
}

renewForced := false
if serviceinfos.Renew != nil {
renewForced = serviceinfos.Renew.Forced
}

renewManualPayment := false
if serviceinfos.Renew != nil && serviceinfos.Renew.ManualPayment != nil {
renewManualPayment = *serviceinfos.Renew.ManualPayment
}

renewPeriod := "-1"
if serviceinfos.Renew != nil && serviceinfos.Renew.Period != nil {
renewPeriod = strconv.Itoa(*serviceinfos.Renew.Period)
}

engagedUpTo := time.Unix(0, 0)
if serviceinfos.EngagedUpTo != nil {
engagedUpTo = *serviceinfos.EngagedUpTo
}

dedicatedServerSubscription.With(prometheus.Labels{
"server_id": server.ID,
"status": serviceinfos.Status.String(),
"creation": serviceinfos.Creation.Format("2006-01-02 15:04:05"),
"expiration": serviceinfos.Expiration.Format("2006-01-02 15:04:05"),
"engaged_up_to": engagedUpTo.Format("2006-01-02 15:04:05"),
"renewal_type": serviceinfos.RenewalType.String(),
"renew_automatic": strconv.FormatBool(renewAutomatic),
"renew_period": renewPeriod,
"renew_manual_payment": strconv.FormatBool(renewManualPayment),
"renew_forced": strconv.FormatBool(renewForced),
"renew_delete_at_expiration": strconv.FormatBool(renewDeleteAtExpiration),
}).Set(amount)
}

func updateDedicatedServerSubscription(ovhClient *ovh.Client, server models.Server) {
logger.Info().Msgf("updating dedicated server subscription for server %s", server.ID)

serviceInfos, err := api.GetDedicatedServerServiceInfos(ovhClient, server.ID)
if err != nil {
logger.Error().Msgf("Failed to retrieve dedicated server serviceinfos: %v", err)
return
}

setDedicatedServerSubscription(server, serviceInfos, 1)
}

func updateDedicatedServersSubscription(ovhClient *ovh.Client) {
logger.Info().Msgf("updating dedicated server subscription")

servers, err := api.GetDedicatedServers(ovhClient)
if err != nil {
logger.Error().Msgf("Failed to retrieve servers: %v", err)
return
}

for _, server := range servers {
updateDedicatedServerSubscription(ovhClient, server)
}
}
24 changes: 24 additions & 0 deletions pkg/ovhsdk/api/dedicated_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package api

import (
"github.com/ovh/go-ovh/ovh"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/models"
)

func GetDedicatedServers(client *ovh.Client) ([]models.Server, error) {
var serviceNames []string
var servers []models.Server

endpoint := "/dedicated/server"

err := client.Get(endpoint, &serviceNames)
if err != nil {
return servers, err
}

for _, serviceName := range serviceNames {
servers = append(servers, models.Server{ID: serviceName})
}

return servers, nil
}
19 changes: 19 additions & 0 deletions pkg/ovhsdk/api/dedicated_server_serviceinfos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package api

import (
"github.com/ovh/go-ovh/ovh"
"github.com/wiremind/ovh-exporter/pkg/ovhsdk/models"
)

func GetDedicatedServerServiceInfos(client *ovh.Client, serverID string) (models.ServiceInfo, error) {
var serviceinfos models.ServiceInfo

endpoint := "/dedicated/server/" + serverID + "/serviceInfos"

err := client.Get(endpoint, &serviceinfos)
if err != nil {
return serviceinfos, err
}

return serviceinfos, nil
}
5 changes: 5 additions & 0 deletions pkg/ovhsdk/models/dedicated_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package models

type Server struct {
ID string
}
Loading

0 comments on commit ac00a6d

Please sign in to comment.