| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- package costmodel
- import (
- "fmt"
- "github.com/opencost/opencost/core/pkg/source"
- "github.com/opencost/opencost/core/pkg/util"
- costAnalyzerCloud "github.com/opencost/opencost/pkg/cloud/models"
- )
- // NetworkUsageData contains the network usage values for egress network traffic and nat gateway
- type NetworkUsageData struct {
- ClusterID string
- PodName string
- Namespace string
- NetworkZoneEgress []*util.Vector
- NetworkRegionEgress []*util.Vector
- NetworkInternetEgress []*util.Vector
- NetworkNatGatewayEgress []*util.Vector
- NetworkNatGatewayIngress []*util.Vector
- }
- // NetworkUsageVector contains a network usage vector for egress network traffic
- type NetworkUsageVector struct {
- ClusterID string
- PodName string
- Namespace string
- Values []*util.Vector
- }
- // GetNetworkUsageData performs a join of the the results of zone, region, and internet usage queries to return a single
- // map containing network costs for each namespace+pod
- func GetNetworkUsageData(
- zr []*source.NetZoneGiBResult,
- rr []*source.NetRegionGiBResult,
- ir []*source.NetInternetGiBResult,
- nge []*source.NetNatGatewayGiBResult,
- ngi []*source.NetNatGatewayIngressGiBResult,
- defaultClusterID string,
- ) (map[string]*NetworkUsageData, error) {
- zoneNetworkMap, err := getNetworkUsage(zr, defaultClusterID)
- if err != nil {
- return nil, err
- }
- regionNetworkMap, err := getNetworkUsage(rr, defaultClusterID)
- if err != nil {
- return nil, err
- }
- internetNetworkMap, err := getNetworkUsage(ir, defaultClusterID)
- if err != nil {
- return nil, err
- }
- natGatewayEgressNetMap, err := getNetworkUsage(nge, defaultClusterID)
- if err != nil {
- return nil, err
- }
- natGatewayIngressNetMap, err := getNetworkUsage(ngi, defaultClusterID)
- if err != nil {
- return nil, err
- }
- usageData := make(map[string]*NetworkUsageData)
- for k, v := range zoneNetworkMap {
- existing, ok := usageData[k]
- if !ok {
- usageData[k] = &NetworkUsageData{
- ClusterID: v.ClusterID,
- PodName: v.PodName,
- Namespace: v.Namespace,
- NetworkZoneEgress: v.Values,
- }
- continue
- }
- existing.NetworkZoneEgress = v.Values
- }
- for k, v := range regionNetworkMap {
- existing, ok := usageData[k]
- if !ok {
- usageData[k] = &NetworkUsageData{
- ClusterID: v.ClusterID,
- PodName: v.PodName,
- Namespace: v.Namespace,
- NetworkRegionEgress: v.Values,
- }
- continue
- }
- existing.NetworkRegionEgress = v.Values
- }
- for k, v := range internetNetworkMap {
- existing, ok := usageData[k]
- if !ok {
- usageData[k] = &NetworkUsageData{
- ClusterID: v.ClusterID,
- PodName: v.PodName,
- Namespace: v.Namespace,
- NetworkInternetEgress: v.Values,
- }
- continue
- }
- existing.NetworkInternetEgress = v.Values
- }
- for k, v := range natGatewayEgressNetMap {
- existing, ok := usageData[k]
- if !ok {
- usageData[k] = &NetworkUsageData{
- ClusterID: v.ClusterID,
- PodName: v.PodName,
- Namespace: v.Namespace,
- NetworkNatGatewayEgress: v.Values,
- }
- continue
- }
- existing.NetworkNatGatewayEgress = v.Values
- }
- for k, v := range natGatewayIngressNetMap {
- existing, ok := usageData[k]
- if !ok {
- usageData[k] = &NetworkUsageData{
- ClusterID: v.ClusterID,
- PodName: v.PodName,
- Namespace: v.Namespace,
- NetworkNatGatewayIngress: v.Values,
- }
- continue
- }
- existing.NetworkNatGatewayIngress = v.Values
- }
- return usageData, nil
- }
- // GetNetworkCost computes the actual cost for NetworkUsageData based on data provided by the Provider.
- func GetNetworkCost(usage *NetworkUsageData, cloud costAnalyzerCloud.Provider) ([]*util.Vector, error) {
- var results []*util.Vector
- pricing, err := cloud.NetworkPricing()
- if err != nil {
- return nil, err
- }
- zoneCost := pricing.ZoneNetworkEgressCost
- regionCost := pricing.RegionNetworkEgressCost
- internetCost := pricing.InternetNetworkEgressCost
- natGatewayEgressCost := pricing.NatGatewayEgressCost
- natGatewayIngressCost := pricing.NatGatewayIngressCost
- zlen := len(usage.NetworkZoneEgress)
- rlen := len(usage.NetworkRegionEgress)
- ilen := len(usage.NetworkInternetEgress)
- ngelen := len(usage.NetworkNatGatewayEgress)
- ngilen := len(usage.NetworkNatGatewayIngress)
- l := max(zlen, rlen, ilen, ngelen, ngilen)
- for i := 0; i < l; i++ {
- var cost float64 = 0
- var timestamp float64
- if i < zlen {
- cost += usage.NetworkZoneEgress[i].Value * zoneCost
- timestamp = usage.NetworkZoneEgress[i].Timestamp
- }
- if i < rlen {
- cost += usage.NetworkRegionEgress[i].Value * regionCost
- timestamp = usage.NetworkRegionEgress[i].Timestamp
- }
- if i < ilen {
- cost += usage.NetworkInternetEgress[i].Value * internetCost
- timestamp = usage.NetworkInternetEgress[i].Timestamp
- }
- if i < ngelen {
- cost += usage.NetworkNatGatewayEgress[i].Value * natGatewayEgressCost
- timestamp = usage.NetworkNatGatewayEgress[i].Timestamp
- }
- if i < ngilen {
- cost += usage.NetworkNatGatewayIngress[i].Value * natGatewayIngressCost
- timestamp = usage.NetworkNatGatewayIngress[i].Timestamp
- }
- results = append(results, &util.Vector{
- Value: cost,
- Timestamp: timestamp,
- })
- }
- return results, nil
- }
- func getNetworkUsage(qrs []*source.NetworkGiBResult, defaultClusterID string) (map[string]*NetworkUsageVector, error) {
- ncdmap := make(map[string]*NetworkUsageVector)
- for _, val := range qrs {
- podName := val.Pod
- if podName == "" {
- return nil, fmt.Errorf("network vector does not contain 'pod' or 'pod_name' field")
- }
- namespace := val.Namespace
- if namespace == "" {
- return nil, fmt.Errorf("network vector does not contain 'namespace' field")
- }
- clusterID := val.Cluster
- if clusterID == "" {
- clusterID = defaultClusterID
- }
- key := namespace + "," + podName + "," + clusterID
- ncdmap[key] = &NetworkUsageVector{
- ClusterID: clusterID,
- Namespace: namespace,
- PodName: podName,
- Values: val.Data,
- }
- }
- return ncdmap, nil
- }
- func max(x int, rest ...int) int {
- curr := x
- for _, v := range rest {
- if v > curr {
- curr = v
- }
- }
- return curr
- }
|