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

Add reservation metics and exportType for costs #87

Merged
merged 42 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
131c72e
Added data in ISO format as label
jkroepke Oct 23, 2023
3e6a82c
update deps
jkroepke Oct 23, 2023
3b98fe5
Merge pull request #1 from PaulPowershell/date-iso
PaulPowershell Nov 20, 2023
e9e405a
Merge pull request #2 from PaulPowershell/deps
PaulPowershell Nov 20, 2023
d9e2a54
Update metrics_azurerm_costs.go
kevindelmont Jan 5, 2024
1346d42
Update config_cost.go
kevindelmont Jan 5, 2024
e5738f0
Update example.yaml
kevindelmont Jan 5, 2024
3127cd0
Merge pull request #3 from PaulPowershell/feat/ExportType
kevindelmont Jan 5, 2024
a84dab9
push fix to kevin PR
PaulPowershell Jan 5, 2024
5d81d56
fix error L190
PaulPowershell Jan 8, 2024
a036bc4
Merge branch 'main' into main
PaulPowershell Jan 9, 2024
70111d4
commit
PaulPowershell Jan 9, 2024
66e2d1b
add reservation
PaulPowershell Jan 12, 2024
5eb00b5
fix appel func reservation
PaulPowershell Jan 12, 2024
4018575
delete old comment
PaulPowershell Jan 12, 2024
00eb8a5
push change
PaulPowershell Jan 12, 2024
10c8941
remove subscription foreach
PaulPowershell Jan 15, 2024
ec3dcf7
add more properties & yaml
PaulPowershell Jan 15, 2024
ba9245f
syntaxe
PaulPowershell Jan 15, 2024
7e91573
fix utilization
PaulPowershell Jan 15, 2024
c27eb0f
add last change
PaulPowershell Jan 15, 2024
0da8ebf
rollback
PaulPowershell Jan 15, 2024
78a20a9
change value
PaulPowershell Jan 15, 2024
1c4f1bb
fix date
PaulPowershell Jan 16, 2024
41b93bb
add metrics
PaulPowershell Jan 16, 2024
e32b4cc
rollback
PaulPowershell Jan 16, 2024
a6e889c
decimal 3 to 2
PaulPowershell Jan 16, 2024
d93ce05
add customDays
PaulPowershell Jan 16, 2024
ebaa793
change enddate
PaulPowershell Jan 17, 2024
8f293e5
commit
PaulPowershell Jan 22, 2024
dc36847
change authorizer
PaulPowershell Jan 23, 2024
4a9f71b
fix S1025
PaulPowershell Jan 23, 2024
db1175a
trad comment in english
PaulPowershell Jan 23, 2024
56cbd82
Merge pull request #4 from PaulPowershell/add-reservation
PaulPowershell Jan 23, 2024
aee531b
Merge branch 'main' into main
PaulPowershell Jan 23, 2024
febb17b
fix module go armcompute
PaulPowershell Jan 29, 2024
c7a70ee
revert example.yaml
mblaschke Feb 10, 2024
d226258
lowercase collector name
mblaschke Feb 10, 2024
dc219ce
remove custom backoff for reservations
mblaschke Feb 10, 2024
7069695
refactoring
mblaschke Feb 10, 2024
ad4ed84
Update metrics_azurerm_resources.go
mblaschke Feb 24, 2024
59d6d91
fix build
mblaschke Feb 24, 2024
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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/vendor/
/azure-resourcemanager-exporter*
/release-assets
/.vscode
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type (
Iam CollectorBase `yaml:"iam"`
Graph CollectorGraph `yaml:"graph"`
Costs CollectorCosts `yaml:"costs"`
Reservation CollectorReservation `yaml:"reservation"`
Portscan CollectorPortscan `yaml:"portscan"`
} `yaml:"collectors"`
}
Expand Down
4 changes: 4 additions & 0 deletions config/config_cost.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type (
Subscriptions *[]string `yaml:"subscriptions"`
TimeFrames []string `yaml:"timeFrames"`
Dimensions []string `yaml:"dimensions"`
ExportType string `yaml:"exportType"`
Granularity string `yaml:"granularity"`
ValueField string `yaml:"valueField"`
Labels map[string]string `yaml:"labels"`
Expand All @@ -38,6 +39,7 @@ type (

configCollectorCostsQueryConfig struct {
Dimensions []configCollectorCostsQueryConfigDimension
ExportType string // Ajout du champ ExportType
}

configCollectorCostsQueryConfigDimension struct {
Expand Down Expand Up @@ -81,6 +83,8 @@ func (q *CollectorCostsQuery) GetConfig() *configCollectorCostsQueryConfig {
Label: labelName,
},
)

q.config.ExportType = q.ExportType
}
}

Expand Down
11 changes: 11 additions & 0 deletions config/config_reservation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package config

type (
CollectorReservation struct {
CollectorBase `yaml:",inline"`

ResourceScope string `yaml:"resourceScope"`
Granularity string `yaml:"granularity"`
FromDays int `yaml:"FromDays"`
}
)
2 changes: 2 additions & 0 deletions default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ collectors:

costs: {}

reservation: {}

portscan:
scanner:
parallel: 2
Expand Down
10 changes: 4 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/advisor/armadvisor v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0
Expand Down Expand Up @@ -58,6 +58,7 @@ require (
github.com/microsoft/kiota-http-go v1.1.1 // indirect
github.com/microsoft/kiota-serialization-form-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-json-go v1.0.5 // indirect
github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect
github.com/microsoft/kiota-serialization-text-go v1.0.0 // indirect
github.com/microsoftgraph/msgraph-sdk-go v1.29.0
github.com/microsoftgraph/msgraph-sdk-go-core v1.0.1
Expand All @@ -69,6 +70,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/robfig/cron v1.2.0 // indirect
github.com/std-uritemplate/std-uritemplate/go v0.0.50 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/webdevops/go-common v0.0.0-20240114160106-0d99a9593911
go.opentelemetry.io/otel v1.21.0 // indirect
Expand All @@ -83,7 +85,6 @@ require (
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.29.0 // indirect
k8s.io/apimachinery v0.29.0 // indirect
Expand All @@ -96,7 +97,4 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)

require (
github.com/microsoft/kiota-serialization-multipart-go v1.0.0 // indirect
github.com/std-uritemplate/std-uritemplate/go v0.0.50 // indirect
)
require gopkg.in/yaml.v2 v2.4.0
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/advisor/armadvisor v1.2.0 h1:3ddjPq/3A/oB2u7LdohEr900EGP5l1MnAiNc3EbY1E4=
Expand Down
15 changes: 15 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,21 @@ func initMetricCollector() {
logger.With(zap.String("collector", collectorName)).Infof("collector disabled")
}

collectorName = "reservation"
if Config.Collectors.Reservation.IsEnabled() {
c := collector.New(collectorName, &MetricsCollectorAzureRmReservation{}, logger)
c.SetScapeTime(*Config.Collectors.Reservation.ScrapeTime)
c.SetCache(
Opts.GetCachePath(collectorName+".json"),
collector.BuildCacheTag(cacheTag, Config.Azure, Config.Collectors.Reservation),
)
if err := c.Start(); err != nil {
logger.Fatal(err.Error())
}
} else {
logger.With(zap.String("collector", collectorName)).Infof("collector disabled")
}

collectorName = "defender"
if Config.Collectors.Defender.IsEnabled() {
c := collector.New(collectorName, &MetricsCollectorAzureRmDefender{}, logger)
Expand Down
14 changes: 10 additions & 4 deletions metrics_azurerm_costs.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,13 @@ func (m *MetricsCollectorAzureRmCosts) Collect(callback chan<- func()) {
// run cost queries
for _, row := range Config.Collectors.Costs.Queries {
query := row
m.collectRunCostQuery(&query, callback)

exportType := armcostmanagement.ExportTypeActualCost
if strings.EqualFold(query.ExportType, "AmortizedCost") {
exportType = armcostmanagement.ExportTypeAmortizedCost
}

m.collectRunCostQuery(&query, exportType, callback)
}

// run budget collection
Expand All @@ -202,7 +208,7 @@ func (m *MetricsCollectorAzureRmCosts) Collect(callback chan<- func()) {
}
}

func (m *MetricsCollectorAzureRmCosts) collectRunCostQuery(query *config.CollectorCostsQuery, callback chan<- func()) {
func (m *MetricsCollectorAzureRmCosts) collectRunCostQuery(query *config.CollectorCostsQuery, exportType armcostmanagement.ExportType, callback chan<- func()) {
queryLogger := logger.With(zap.String("query", query.Name))
for _, timeframe := range query.TimeFrames {
timeframeLogger := queryLogger.With(zap.String("timeframe", timeframe))
Expand All @@ -213,7 +219,7 @@ func (m *MetricsCollectorAzureRmCosts) collectRunCostQuery(query *config.Collect
timeframeLogger,
m.Collector.GetMetricList(fmt.Sprintf(`query:%v`, query.Name)),
scope,
armcostmanagement.ExportTypeActualCost,
exportType,
query,
timeframe,
nil,
Expand All @@ -232,7 +238,7 @@ func (m *MetricsCollectorAzureRmCosts) collectRunCostQuery(query *config.Collect
subscriptionLogger,
m.Collector.GetMetricList(fmt.Sprintf(`query:%v`, query.Name)),
*subscription.ID,
armcostmanagement.ExportTypeActualCost,
exportType,
query,
timeframe,
subscription,
Expand Down
216 changes: 216 additions & 0 deletions metrics_azurerm_reservation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package main

import (
"context"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/consumption/armconsumption"
"github.com/prometheus/client_golang/prometheus"
"github.com/webdevops/go-common/prometheus/collector"
"github.com/webdevops/go-common/utils/to"
"go.uber.org/zap"
)

// Define MetricsCollectorAzureRmReservation struct
type MetricsCollectorAzureRmReservation struct {
collector.Processor

prometheus struct {
reservationInfo *prometheus.GaugeVec
reservationUsage *prometheus.GaugeVec
reservationMinUsage *prometheus.GaugeVec
reservationMaxUsage *prometheus.GaugeVec
reservationUsedHours *prometheus.GaugeVec
reservationReservedHours *prometheus.GaugeVec
reservationTotalReservedQuantity *prometheus.GaugeVec
}
}

// Setup method to initialize Prometheus metrics
func (m *MetricsCollectorAzureRmReservation) Setup(collector *collector.Collector) {
m.Processor.Setup(collector)

m.prometheus.reservationInfo = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_info",
Help: "Azure ResourceManager Reservation Information",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationUsage = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_utilization",
Help: "Azure ResourceManager Reservation Utilization",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationMinUsage = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_utilization_min",
Help: "Azure ResourceManager Reservation Min Utilization",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationMaxUsage = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_utilization_max",
Help: "Azure ResourceManager Reservation Max Utilization",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationUsedHours = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_used_hours",
Help: "Azure ResourceManager Reservation Used Hours",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationReservedHours = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_reserved_hours",
Help: "Azure ResourceManager Reservation Reserved Hours",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.prometheus.reservationTotalReservedQuantity = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "azurerm_reservation_total_reserved_quantity",
Help: "Azure ResourceManager Reservation Total Reserved Quantity",
},
[]string{
"reservationOrderID",
"reservationID",
"skuName",
"kind",
"usageDate",
},
)

m.Collector.RegisterMetricList("reservationInfo", m.prometheus.reservationInfo, true)
m.Collector.RegisterMetricList("reservationUsage", m.prometheus.reservationUsage, true)
m.Collector.RegisterMetricList("reservationMinUsage", m.prometheus.reservationMinUsage, true)
m.Collector.RegisterMetricList("reservationMaxUsage", m.prometheus.reservationMaxUsage, true)
m.Collector.RegisterMetricList("reservationUsedHours", m.prometheus.reservationUsedHours, true)
m.Collector.RegisterMetricList("reservationReservedHours", m.prometheus.reservationReservedHours, true)
m.Collector.RegisterMetricList("reservationTotalReservedQuantity", m.prometheus.reservationTotalReservedQuantity, true)
}

func (m *MetricsCollectorAzureRmReservation) Reset() {}

func (m *MetricsCollectorAzureRmReservation) Collect(callback chan<- func()) {
m.collectReservationUsage(logger, callback)
}

func (m *MetricsCollectorAzureRmReservation) collectReservationUsage(logger *zap.SugaredLogger, callback chan<- func()) {
reservationInfo := m.Collector.GetMetricList("reservationInfo")
reservationUsage := m.Collector.GetMetricList("reservationUsage")
reservationMinUsage := m.Collector.GetMetricList("reservationMinUsage")
reservationMaxUsage := m.Collector.GetMetricList("reservationMaxUsage")
reservationUsedHours := m.Collector.GetMetricList("reservationUsedHours")
reservationReservedHours := m.Collector.GetMetricList("reservationReservedHours")
reservationTotalReservedQuantity := m.Collector.GetMetricList("reservationTotalReservedQuantity")

ctx := context.Background()
days := Config.Collectors.Reservation.FromDays
resourceScope := Config.Collectors.Reservation.ResourceScope
granularity := Config.Collectors.Reservation.Granularity

now := time.Now()
formattedDate := now.AddDate(0, 0, -days).Format("2006-01-02")
startDate := formattedDate
endDate := time.Now().Format("2006-01-02")

clientFactory, err := armconsumption.NewClientFactory("<subscription-id>", AzureClient.GetCred(), AzureClient.NewArmClientOptions())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't <subscription-id> not set to the subscription id? and loop trough every subscription id in the collect function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanx for your return,
It is surprising, but in fact the value should not be defined because this metrics_azurerm_reservation.go is intended to be used from an MCA tenant (which operates by billing profile rather than by subscription). Therefore, leaving "" works well. We could rename the field but not set it to nil.

if err != nil {
logger.Panic(err)
}

// "Create a pager to retrieve daily booking summaries
pager := clientFactory.NewReservationsSummariesClient().NewListPager(resourceScope, armconsumption.Datagrain(granularity), &armconsumption.ReservationsSummariesClientListOptions{
StartDate: to.Ptr(startDate),
EndDate: to.Ptr(endDate),
Filter: nil,
ReservationID: nil,
ReservationOrderID: nil,
})

// Collect and export metrics
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
logger.Panic(err)
}

for _, reservationProperties := range page.Value {
reservationOrderID := reservationProperties.Properties.ReservationOrderID
reservationID := reservationProperties.Properties.ReservationID
skuName := reservationProperties.Properties.SKUName
kind := reservationProperties.Properties.Kind
reservedHours := reservationProperties.Properties.ReservedHours
usageDate := reservationProperties.Properties.UsageDate.String()
usedHours := reservationProperties.Properties.UsedHours
minUtilizationPercentage := reservationProperties.Properties.MinUtilizationPercentage
avgUtilizationPercentage := reservationProperties.Properties.AvgUtilizationPercentage
maxUtilizationPercentage := reservationProperties.Properties.MaxUtilizationPercentage
totalReservedQuantity := reservationProperties.Properties.TotalReservedQuantity

labels := prometheus.Labels{
"reservationOrderID": to.String(reservationOrderID),
"reservationID": to.String(reservationID),
"skuName": to.String(skuName),
"kind": to.String(kind),
"usageDate": usageDate,
}

reservationInfo.AddInfo(labels)
reservationUsage.AddIfNotNil(labels, avgUtilizationPercentage)
reservationMinUsage.AddIfNotNil(labels, minUtilizationPercentage)
reservationMaxUsage.AddIfNotNil(labels, maxUtilizationPercentage)
reservationUsedHours.AddIfNotNil(labels, usedHours)
reservationReservedHours.AddIfNotNil(labels, reservedHours)
reservationTotalReservedQuantity.AddIfNotNil(labels, totalReservedQuantity)
}
}
}
Loading