2
0

networkcosts.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. // NetworkUsageData contains the network usage values for egress network traffic and nat gateway
  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. NetworkNatGatewayEgress []*util.Vector
  17. NetworkNatGatewayIngress []*util.Vector
  18. }
  19. // NetworkUsageVector contains a network usage vector for egress network traffic
  20. type NetworkUsageVector struct {
  21. ClusterID string
  22. PodName string
  23. Namespace string
  24. Values []*util.Vector
  25. }
  26. // GetNetworkUsageData performs a join of the the results of zone, region, and internet usage queries to return a single
  27. // map containing network costs for each namespace+pod
  28. func GetNetworkUsageData(
  29. zr []*source.NetZoneGiBResult,
  30. rr []*source.NetRegionGiBResult,
  31. ir []*source.NetInternetGiBResult,
  32. nge []*source.NetNatGatewayGiBResult,
  33. ngi []*source.NetNatGatewayIngressGiBResult,
  34. defaultClusterID string,
  35. ) (map[string]*NetworkUsageData, error) {
  36. zoneNetworkMap, err := getNetworkUsage(zr, defaultClusterID)
  37. if err != nil {
  38. return nil, err
  39. }
  40. regionNetworkMap, err := getNetworkUsage(rr, defaultClusterID)
  41. if err != nil {
  42. return nil, err
  43. }
  44. internetNetworkMap, err := getNetworkUsage(ir, defaultClusterID)
  45. if err != nil {
  46. return nil, err
  47. }
  48. natGatewayEgressNetMap, err := getNetworkUsage(nge, defaultClusterID)
  49. if err != nil {
  50. return nil, err
  51. }
  52. natGatewayIngressNetMap, err := getNetworkUsage(ngi, defaultClusterID)
  53. if err != nil {
  54. return nil, err
  55. }
  56. usageData := make(map[string]*NetworkUsageData)
  57. for k, v := range zoneNetworkMap {
  58. existing, ok := usageData[k]
  59. if !ok {
  60. usageData[k] = &NetworkUsageData{
  61. ClusterID: v.ClusterID,
  62. PodName: v.PodName,
  63. Namespace: v.Namespace,
  64. NetworkZoneEgress: v.Values,
  65. }
  66. continue
  67. }
  68. existing.NetworkZoneEgress = v.Values
  69. }
  70. for k, v := range regionNetworkMap {
  71. existing, ok := usageData[k]
  72. if !ok {
  73. usageData[k] = &NetworkUsageData{
  74. ClusterID: v.ClusterID,
  75. PodName: v.PodName,
  76. Namespace: v.Namespace,
  77. NetworkRegionEgress: v.Values,
  78. }
  79. continue
  80. }
  81. existing.NetworkRegionEgress = v.Values
  82. }
  83. for k, v := range internetNetworkMap {
  84. existing, ok := usageData[k]
  85. if !ok {
  86. usageData[k] = &NetworkUsageData{
  87. ClusterID: v.ClusterID,
  88. PodName: v.PodName,
  89. Namespace: v.Namespace,
  90. NetworkInternetEgress: v.Values,
  91. }
  92. continue
  93. }
  94. existing.NetworkInternetEgress = v.Values
  95. }
  96. for k, v := range natGatewayEgressNetMap {
  97. existing, ok := usageData[k]
  98. if !ok {
  99. usageData[k] = &NetworkUsageData{
  100. ClusterID: v.ClusterID,
  101. PodName: v.PodName,
  102. Namespace: v.Namespace,
  103. NetworkNatGatewayEgress: v.Values,
  104. }
  105. continue
  106. }
  107. existing.NetworkNatGatewayEgress = v.Values
  108. }
  109. for k, v := range natGatewayIngressNetMap {
  110. existing, ok := usageData[k]
  111. if !ok {
  112. usageData[k] = &NetworkUsageData{
  113. ClusterID: v.ClusterID,
  114. PodName: v.PodName,
  115. Namespace: v.Namespace,
  116. NetworkNatGatewayIngress: v.Values,
  117. }
  118. continue
  119. }
  120. existing.NetworkNatGatewayIngress = v.Values
  121. }
  122. return usageData, nil
  123. }
  124. // GetNetworkCost computes the actual cost for NetworkUsageData based on data provided by the Provider.
  125. func GetNetworkCost(usage *NetworkUsageData, cloud costAnalyzerCloud.Provider) ([]*util.Vector, error) {
  126. var results []*util.Vector
  127. pricing, err := cloud.NetworkPricing()
  128. if err != nil {
  129. return nil, err
  130. }
  131. zoneCost := pricing.ZoneNetworkEgressCost
  132. regionCost := pricing.RegionNetworkEgressCost
  133. internetCost := pricing.InternetNetworkEgressCost
  134. natGatewayEgressCost := pricing.NatGatewayEgressCost
  135. natGatewayIngressCost := pricing.NatGatewayIngressCost
  136. zlen := len(usage.NetworkZoneEgress)
  137. rlen := len(usage.NetworkRegionEgress)
  138. ilen := len(usage.NetworkInternetEgress)
  139. ngelen := len(usage.NetworkNatGatewayEgress)
  140. ngilen := len(usage.NetworkNatGatewayIngress)
  141. l := max(zlen, rlen, ilen, ngelen, ngilen)
  142. for i := 0; i < l; i++ {
  143. var cost float64 = 0
  144. var timestamp float64
  145. if i < zlen {
  146. cost += usage.NetworkZoneEgress[i].Value * zoneCost
  147. timestamp = usage.NetworkZoneEgress[i].Timestamp
  148. }
  149. if i < rlen {
  150. cost += usage.NetworkRegionEgress[i].Value * regionCost
  151. timestamp = usage.NetworkRegionEgress[i].Timestamp
  152. }
  153. if i < ilen {
  154. cost += usage.NetworkInternetEgress[i].Value * internetCost
  155. timestamp = usage.NetworkInternetEgress[i].Timestamp
  156. }
  157. if i < ngelen {
  158. cost += usage.NetworkNatGatewayEgress[i].Value * natGatewayEgressCost
  159. timestamp = usage.NetworkNatGatewayEgress[i].Timestamp
  160. }
  161. if i < ngilen {
  162. cost += usage.NetworkNatGatewayIngress[i].Value * natGatewayIngressCost
  163. timestamp = usage.NetworkNatGatewayIngress[i].Timestamp
  164. }
  165. results = append(results, &util.Vector{
  166. Value: cost,
  167. Timestamp: timestamp,
  168. })
  169. }
  170. return results, nil
  171. }
  172. func getNetworkUsage(qrs []*source.NetworkGiBResult, defaultClusterID string) (map[string]*NetworkUsageVector, error) {
  173. ncdmap := make(map[string]*NetworkUsageVector)
  174. for _, val := range qrs {
  175. podName := val.Pod
  176. if podName == "" {
  177. return nil, fmt.Errorf("network vector does not contain 'pod' or 'pod_name' field")
  178. }
  179. namespace := val.Namespace
  180. if namespace == "" {
  181. return nil, fmt.Errorf("network vector does not contain 'namespace' field")
  182. }
  183. clusterID := val.Cluster
  184. if clusterID == "" {
  185. clusterID = defaultClusterID
  186. }
  187. key := namespace + "," + podName + "," + clusterID
  188. ncdmap[key] = &NetworkUsageVector{
  189. ClusterID: clusterID,
  190. Namespace: namespace,
  191. PodName: podName,
  192. Values: val.Data,
  193. }
  194. }
  195. return ncdmap, nil
  196. }
  197. func max(x int, rest ...int) int {
  198. curr := x
  199. for _, v := range rest {
  200. if v > curr {
  201. curr = v
  202. }
  203. }
  204. return curr
  205. }