| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- package oracle
- import (
- "context"
- "fmt"
- "strconv"
- "time"
- "github.com/opencost/opencost/core/pkg/log"
- "github.com/opencost/opencost/core/pkg/opencost"
- "github.com/opencost/opencost/pkg/cloud"
- "github.com/oracle/oci-go-sdk/v65/common"
- "github.com/oracle/oci-go-sdk/v65/usageapi"
- )
- type UsageApiIntegration struct {
- UsageApiConfiguration
- ConnectionStatus cloud.ConnectionStatus
- }
- func (uai *UsageApiIntegration) GetCloudCost(start time.Time, end time.Time) (*opencost.CloudCostSetRange, error) {
- client, err := uai.GetUsageApiClient()
- if err != nil {
- uai.ConnectionStatus = cloud.FailedConnection
- return nil, fmt.Errorf("getting oracle usage api client: %s", err.Error())
- }
- req := usageapi.RequestSummarizedUsagesRequest{
- RequestSummarizedUsagesDetails: usageapi.RequestSummarizedUsagesDetails{
- Granularity: usageapi.RequestSummarizedUsagesDetailsGranularityDaily,
- GroupBy: []string{"resourceId", "service", "subscriptionId", "tenantName"},
- IsAggregateByTime: common.Bool(false),
- TimeUsageStarted: &common.SDKTime{Time: start},
- TimeUsageEnded: &common.SDKTime{Time: end},
- QueryType: usageapi.RequestSummarizedUsagesDetailsQueryTypeCost,
- TenantId: common.String(uai.TenancyID),
- },
- Limit: common.Int(500),
- }
- resp, err := client.RequestSummarizedUsages(context.Background(), req)
- if err != nil {
- uai.ConnectionStatus = cloud.FailedConnection
- return nil, fmt.Errorf("failed to query usage: %w", err)
- }
- ccsr, err := opencost.NewCloudCostSetRange(start, end, opencost.AccumulateOptionDay, uai.Key())
- if err != nil {
- return nil, err
- }
- // Set status to missing data if query comes back empty and the status isn't already successful
- if len(resp.Items) == 0 && uai.ConnectionStatus != cloud.SuccessfulConnection {
- uai.ConnectionStatus = cloud.MissingData
- return ccsr, nil
- }
- for _, item := range resp.Items {
- resourceId := ""
- if item.ResourceId != nil {
- resourceId = *item.ResourceId
- }
- tenantName := ""
- if item.TenantName != nil {
- tenantName = *item.TenantName
- }
- subscriptionId := ""
- if item.SubscriptionId != nil {
- subscriptionId = *item.SubscriptionId
- }
- service := ""
- if item.Service != nil {
- service = *item.Service
- }
- category := SelectOCICategory(service)
- // Iterate through the slice of tags, assigning
- // keys and values to the map of labels
- labels := opencost.CloudCostLabels{}
- for _, tag := range item.Tags {
- if tag.Key == nil || tag.Value == nil {
- continue
- }
- labels[*tag.Key] = *tag.Value
- }
- properties := &opencost.CloudCostProperties{
- ProviderID: resourceId,
- Provider: opencost.OracleProvider,
- AccountID: uai.TenancyID,
- AccountName: tenantName,
- InvoiceEntityID: subscriptionId,
- RegionID: uai.Region,
- Service: service,
- Category: category,
- Labels: labels,
- }
- winStart := item.TimeUsageStarted.Time
- winEnd := start.AddDate(0, 0, 1)
- listRate := 0.0
- if item.ListRate != nil {
- listRate = float64(*item.ListRate)
- }
- attrCostToParse := ""
- if item.AttributedCost != nil {
- attrCostToParse = *item.AttributedCost
- }
- attrCost, err := strconv.ParseFloat(attrCostToParse, 64)
- if err != nil {
- return nil, fmt.Errorf("unable to parse float '%s': %s", attrCostToParse, err.Error())
- }
- computedAmt := 0.0
- if item.ComputedAmount != nil {
- computedAmt = float64(*item.ComputedAmount)
- }
- cc := &opencost.CloudCost{
- Properties: properties,
- Window: opencost.NewWindow(&winStart, &winEnd),
- //todo: which returned costs go where?
- ListCost: opencost.CostMetric{
- Cost: listRate,
- },
- NetCost: opencost.CostMetric{
- Cost: computedAmt,
- },
- AmortizedNetCost: opencost.CostMetric{
- Cost: attrCost,
- },
- AmortizedCost: opencost.CostMetric{
- Cost: attrCost,
- },
- InvoicedCost: opencost.CostMetric{
- Cost: computedAmt,
- },
- }
- ccsr.LoadCloudCost(cc)
- }
- uai.ConnectionStatus = cloud.SuccessfulConnection
- return ccsr, nil
- }
- func (uai *UsageApiIntegration) GetStatus() cloud.ConnectionStatus {
- // initialize status if it has not done so; this can happen if the integration is inactive
- if uai.ConnectionStatus.String() == "" {
- uai.ConnectionStatus = cloud.InitialStatus
- }
- return uai.ConnectionStatus
- }
- func (uai *UsageApiIntegration) RefreshStatus() cloud.ConnectionStatus {
- log.Warn("status refresh is not supported for the Oracle provider")
- return uai.ConnectionStatus
- }
- func SelectOCICategory(service string) string {
- if service == "Compute" {
- return opencost.ComputeCategory
- } else if service == "Block Storage" || service == "Object Storage" {
- return opencost.StorageCategory
- } else if service == "Load Balancer" || service == "Virtual Cloud Network" {
- return opencost.NetworkCategory
- } else {
- return opencost.OtherCategory
- }
- }
|