networkcosts.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package costmodel
  2. import (
  3. costAnalyzerCloud "github.com/kubecost/cost-model/pkg/cloud"
  4. "github.com/kubecost/cost-model/pkg/log"
  5. "github.com/kubecost/cost-model/pkg/prom"
  6. "github.com/kubecost/cost-model/pkg/util"
  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 interface{}, rr interface{}, ir interface{}, 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(qr interface{}, defaultClusterID string) (map[string]*NetworkUsageVector, error) {
  118. ncdmap := make(map[string]*NetworkUsageVector)
  119. // TODO: Pass actual query instead of NetworkUsage
  120. result, err := prom.NewQueryResults("NetworkUsage", qr)
  121. if err != nil {
  122. return nil, err
  123. }
  124. for _, val := range result.Results {
  125. podName, err := val.GetString("pod_name")
  126. if err != nil {
  127. return nil, err
  128. }
  129. namespace, err := val.GetString("namespace")
  130. if err != nil {
  131. return nil, err
  132. }
  133. clusterID, err := val.GetString("cluster_id")
  134. if clusterID == "" {
  135. log.Debugf("Prometheus vector does not have cluster id")
  136. clusterID = defaultClusterID
  137. }
  138. key := namespace + "," + podName + "," + clusterID
  139. ncdmap[key] = &NetworkUsageVector{
  140. ClusterID: clusterID,
  141. Namespace: namespace,
  142. PodName: podName,
  143. Values: val.Values,
  144. }
  145. }
  146. return ncdmap, nil
  147. }
  148. func max(x int, rest ...int) int {
  149. curr := x
  150. for _, v := range rest {
  151. if v > curr {
  152. curr = v
  153. }
  154. }
  155. return curr
  156. }