usageapiintegration.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package oracle
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "time"
  7. "github.com/opencost/opencost/core/pkg/opencost"
  8. "github.com/opencost/opencost/pkg/cloud"
  9. "github.com/oracle/oci-go-sdk/v65/common"
  10. "github.com/oracle/oci-go-sdk/v65/example/helpers"
  11. "github.com/oracle/oci-go-sdk/v65/usageapi"
  12. )
  13. type UsageApiIntegration struct {
  14. UsageApiConfiguration
  15. ConnectionStatus cloud.ConnectionStatus
  16. }
  17. func (uai *UsageApiIntegration) GetCloudCost(start time.Time, end time.Time) (*opencost.CloudCostSetRange, error) {
  18. client, err := uai.GetUsageApiClient()
  19. if err != nil {
  20. return nil, fmt.Errorf("getting oracle usage api client: %s", err.Error())
  21. }
  22. req := usageapi.RequestSummarizedUsagesRequest{
  23. RequestSummarizedUsagesDetails: usageapi.RequestSummarizedUsagesDetails{
  24. Granularity: usageapi.RequestSummarizedUsagesDetailsGranularityDaily,
  25. GroupBy: []string{"resourceId", "service", "subscriptionId", "tenantName"},
  26. IsAggregateByTime: common.Bool(false),
  27. TimeUsageStarted: &common.SDKTime{Time: start},
  28. TimeUsageEnded: &common.SDKTime{Time: end},
  29. QueryType: usageapi.RequestSummarizedUsagesDetailsQueryTypeCost,
  30. TenantId: common.String(uai.TenancyID),
  31. },
  32. Limit: common.Int(500),
  33. }
  34. resp, err := client.RequestSummarizedUsages(context.Background(), req)
  35. helpers.FatalIfError(err)
  36. ccsr, err := opencost.NewCloudCostSetRange(start, end, opencost.AccumulateOptionDay, uai.Key())
  37. if err != nil {
  38. return nil, err
  39. }
  40. for _, item := range resp.Items {
  41. resourceId := ""
  42. if item.ResourceId != nil {
  43. resourceId = *item.ResourceId
  44. }
  45. tenantName := ""
  46. if item.TenantName != nil {
  47. tenantName = *item.TenantName
  48. }
  49. subscriptionId := ""
  50. if item.SubscriptionId != nil {
  51. subscriptionId = *item.SubscriptionId
  52. }
  53. service := ""
  54. if item.Service != nil {
  55. service = *item.Service
  56. }
  57. category := SelectOCICategory(service)
  58. // Iterate through the slice of tags, assigning
  59. // keys and values to the map of labels
  60. labels := opencost.CloudCostLabels{}
  61. for _, tag := range item.Tags {
  62. if tag.Key == nil || tag.Value == nil {
  63. continue
  64. }
  65. labels[*tag.Key] = *tag.Value
  66. }
  67. properties := &opencost.CloudCostProperties{
  68. ProviderID: resourceId,
  69. Provider: opencost.OracleProvider,
  70. AccountID: uai.TenancyID,
  71. AccountName: tenantName,
  72. InvoiceEntityID: subscriptionId,
  73. RegionID: uai.Region,
  74. Service: service,
  75. Category: category,
  76. Labels: labels,
  77. }
  78. winStart := item.TimeUsageStarted.Time
  79. winEnd := start.AddDate(0, 0, 1)
  80. listRate := 0.0
  81. if item.ListRate != nil {
  82. listRate = float64(*item.ListRate)
  83. }
  84. attrCostToParse := ""
  85. if item.AttributedCost != nil {
  86. attrCostToParse = *item.AttributedCost
  87. }
  88. attrCost, err := strconv.ParseFloat(attrCostToParse, 64)
  89. if err != nil {
  90. return nil, fmt.Errorf("unable to parse float '%s': %s", attrCostToParse, err.Error())
  91. }
  92. computedAmt := 0.0
  93. if item.ComputedAmount != nil {
  94. computedAmt = float64(*item.ComputedAmount)
  95. }
  96. cc := &opencost.CloudCost{
  97. Properties: properties,
  98. Window: opencost.NewWindow(&winStart, &winEnd),
  99. //todo: which returned costs go where?
  100. ListCost: opencost.CostMetric{
  101. Cost: listRate,
  102. },
  103. NetCost: opencost.CostMetric{
  104. Cost: computedAmt,
  105. },
  106. AmortizedNetCost: opencost.CostMetric{
  107. Cost: attrCost,
  108. },
  109. AmortizedCost: opencost.CostMetric{
  110. Cost: attrCost,
  111. },
  112. InvoicedCost: opencost.CostMetric{
  113. Cost: computedAmt,
  114. },
  115. }
  116. ccsr.LoadCloudCost(cc)
  117. }
  118. return ccsr, nil
  119. }
  120. func (uai *UsageApiIntegration) GetStatus() cloud.ConnectionStatus {
  121. // initialize status if it has not done so; this can happen if the integration is inactive
  122. if uai.ConnectionStatus.String() == "" {
  123. uai.ConnectionStatus = cloud.InitialStatus
  124. }
  125. return uai.ConnectionStatus
  126. }
  127. func SelectOCICategory(service string) string {
  128. if service == "Compute" {
  129. return opencost.ComputeCategory
  130. } else if service == "Block Storage" || service == "Object Storage" {
  131. return opencost.StorageCategory
  132. } else if service == "Load Balancer" || service == "Virtual Cloud Network" {
  133. return opencost.NetworkCategory
  134. } else {
  135. return opencost.OtherCategory
  136. }
  137. }