networkcosts.go 4.5 KB

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