networkcosts.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package costmodel
  2. import (
  3. "fmt"
  4. "github.com/opencost/opencost/core/pkg/source"
  5. "github.com/opencost/opencost/core/pkg/util"
  6. costAnalyzerCloud "github.com/opencost/opencost/pkg/cloud/models"
  7. )
  8. // NetworkUsageVNetworkUsageDataector contains the network usage values for egress network traffic
  9. type NetworkUsageData struct {
  10. ClusterID string
  11. PodName string
  12. Namespace string
  13. NetworkZoneEgress []*util.Vector
  14. NetworkRegionEgress []*util.Vector
  15. NetworkInternetEgress []*util.Vector
  16. }
  17. // NetworkUsageVector contains a network usage vector for egress network traffic
  18. type NetworkUsageVector struct {
  19. ClusterID string
  20. PodName string
  21. Namespace string
  22. Values []*util.Vector
  23. }
  24. // GetNetworkUsageData performs a join of the the results of zone, region, and internet usage queries to return a single
  25. // map containing network costs for each namespace+pod
  26. func GetNetworkUsageData(zr []*source.NetZoneGiBResult, rr []*source.NetRegionGiBResult, ir []*source.NetInternetGiBResult, defaultClusterID string) (map[string]*NetworkUsageData, error) {
  27. zoneNetworkMap, err := getNetworkUsage(zr, defaultClusterID)
  28. if err != nil {
  29. return nil, err
  30. }
  31. regionNetworkMap, err := getNetworkUsage(rr, defaultClusterID)
  32. if err != nil {
  33. return nil, err
  34. }
  35. internetNetworkMap, err := getNetworkUsage(ir, defaultClusterID)
  36. if err != nil {
  37. return nil, err
  38. }
  39. usageData := make(map[string]*NetworkUsageData)
  40. for k, v := range zoneNetworkMap {
  41. existing, ok := usageData[k]
  42. if !ok {
  43. usageData[k] = &NetworkUsageData{
  44. ClusterID: v.ClusterID,
  45. PodName: v.PodName,
  46. Namespace: v.Namespace,
  47. NetworkZoneEgress: v.Values,
  48. }
  49. continue
  50. }
  51. existing.NetworkZoneEgress = v.Values
  52. }
  53. for k, v := range regionNetworkMap {
  54. existing, ok := usageData[k]
  55. if !ok {
  56. usageData[k] = &NetworkUsageData{
  57. ClusterID: v.ClusterID,
  58. PodName: v.PodName,
  59. Namespace: v.Namespace,
  60. NetworkRegionEgress: v.Values,
  61. }
  62. continue
  63. }
  64. existing.NetworkRegionEgress = v.Values
  65. }
  66. for k, v := range internetNetworkMap {
  67. existing, ok := usageData[k]
  68. if !ok {
  69. usageData[k] = &NetworkUsageData{
  70. ClusterID: v.ClusterID,
  71. PodName: v.PodName,
  72. Namespace: v.Namespace,
  73. NetworkInternetEgress: v.Values,
  74. }
  75. continue
  76. }
  77. existing.NetworkInternetEgress = v.Values
  78. }
  79. return usageData, nil
  80. }
  81. // GetNetworkCost computes the actual cost for NetworkUsageData based on data provided by the Provider.
  82. func GetNetworkCost(usage *NetworkUsageData, cloud costAnalyzerCloud.Provider) ([]*util.Vector, error) {
  83. var results []*util.Vector
  84. pricing, err := cloud.NetworkPricing()
  85. if err != nil {
  86. return nil, err
  87. }
  88. zoneCost := pricing.ZoneNetworkEgressCost
  89. regionCost := pricing.RegionNetworkEgressCost
  90. internetCost := pricing.InternetNetworkEgressCost
  91. zlen := len(usage.NetworkZoneEgress)
  92. rlen := len(usage.NetworkRegionEgress)
  93. ilen := len(usage.NetworkInternetEgress)
  94. l := max(zlen, rlen, ilen)
  95. for i := 0; i < l; i++ {
  96. var cost float64 = 0
  97. var timestamp float64
  98. if i < zlen {
  99. cost += usage.NetworkZoneEgress[i].Value * zoneCost
  100. timestamp = usage.NetworkZoneEgress[i].Timestamp
  101. }
  102. if i < rlen {
  103. cost += usage.NetworkRegionEgress[i].Value * regionCost
  104. timestamp = usage.NetworkRegionEgress[i].Timestamp
  105. }
  106. if i < ilen {
  107. cost += usage.NetworkInternetEgress[i].Value * internetCost
  108. timestamp = usage.NetworkInternetEgress[i].Timestamp
  109. }
  110. results = append(results, &util.Vector{
  111. Value: cost,
  112. Timestamp: timestamp,
  113. })
  114. }
  115. return results, nil
  116. }
  117. func getNetworkUsage(qrs []*source.NetworkGiBResult, defaultClusterID string) (map[string]*NetworkUsageVector, error) {
  118. ncdmap := make(map[string]*NetworkUsageVector)
  119. for _, val := range qrs {
  120. podName := val.Pod
  121. if podName == "" {
  122. return nil, fmt.Errorf("network vector does not contain 'pod' or 'pod_name' field")
  123. }
  124. namespace := val.Namespace
  125. if namespace == "" {
  126. return nil, fmt.Errorf("network vector does not contain 'namespace' field")
  127. }
  128. clusterID := val.Cluster
  129. if clusterID == "" {
  130. clusterID = defaultClusterID
  131. }
  132. key := namespace + "," + podName + "," + clusterID
  133. ncdmap[key] = &NetworkUsageVector{
  134. ClusterID: clusterID,
  135. Namespace: namespace,
  136. PodName: podName,
  137. Values: val.Data,
  138. }
  139. }
  140. return ncdmap, nil
  141. }
  142. func max(x int, rest ...int) int {
  143. curr := x
  144. for _, v := range rest {
  145. if v > curr {
  146. curr = v
  147. }
  148. }
  149. return curr
  150. }