assets.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package costmodel
  2. import (
  3. "fmt"
  4. "time"
  5. "github.com/opencost/opencost/core/pkg/log"
  6. "github.com/opencost/opencost/core/pkg/opencost"
  7. )
  8. func (cm *CostModel) ComputeAssets(start, end time.Time) (*opencost.AssetSet, error) {
  9. assetSet := opencost.NewAssetSet(start, end)
  10. nodeMap, err := cm.ClusterNodes(start, end)
  11. if err != nil {
  12. return nil, fmt.Errorf("error computing node assets for %s: %w", opencost.NewClosedWindow(start, end), err)
  13. }
  14. lbMap, err := cm.ClusterLoadBalancers(start, end)
  15. if err != nil {
  16. return nil, fmt.Errorf("error computing load balancer assets for %s: %w", opencost.NewClosedWindow(start, end), err)
  17. }
  18. diskMap, err := cm.ClusterDisks(start, end)
  19. if err != nil {
  20. return nil, fmt.Errorf("error computing disk assets for %s: %w", opencost.NewClosedWindow(start, end), err)
  21. }
  22. clusterManagement, err := cm.ClusterManagement(start, end)
  23. if err != nil {
  24. return nil, fmt.Errorf("error computing cluster management assets for %s: %w", opencost.NewClosedWindow(start, end), err)
  25. }
  26. for _, d := range diskMap {
  27. s := d.Start
  28. if s.Before(start) || s.After(end) {
  29. log.Debugf("CostModel.ComputeAssets: disk '%s' start outside window: %s not in [%s, %s]", d.Name, s.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  30. s = start
  31. }
  32. e := d.End
  33. if e.Before(start) || e.After(end) {
  34. log.Debugf("CostModel.ComputeAssets: disk '%s' end outside window: %s not in [%s, %s]", d.Name, e.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  35. e = end
  36. }
  37. hours := e.Sub(s).Hours()
  38. disk := opencost.NewDisk(d.Name, d.Cluster, d.ProviderID, s, e, opencost.NewWindow(&start, &end))
  39. cm.PropertiesFromCluster(disk.Properties)
  40. disk.Cost = d.Cost
  41. disk.ByteHours = d.Bytes * hours
  42. if d.BytesUsedAvgPtr != nil {
  43. byteHours := *d.BytesUsedAvgPtr * hours
  44. disk.ByteHoursUsed = &byteHours
  45. }
  46. if d.BytesUsedMaxPtr != nil {
  47. usageMax := *d.BytesUsedMaxPtr
  48. disk.ByteUsageMax = &usageMax
  49. }
  50. if d.Local {
  51. disk.Local = 1.0
  52. }
  53. disk.Breakdown = &opencost.Breakdown{
  54. Idle: d.Breakdown.Idle,
  55. System: d.Breakdown.System,
  56. User: d.Breakdown.User,
  57. Other: d.Breakdown.Other,
  58. }
  59. disk.StorageClass = d.StorageClass
  60. disk.VolumeName = d.VolumeName
  61. disk.ClaimName = d.ClaimName
  62. disk.ClaimNamespace = d.ClaimNamespace
  63. assetSet.Insert(disk, nil)
  64. }
  65. for _, lb := range lbMap {
  66. s := lb.Start
  67. if s.Before(start) || s.After(end) {
  68. log.Debugf("CostModel.ComputeAssets: load balancer '%s' start outside window: %s not in [%s, %s]", lb.Name, s.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  69. s = start
  70. }
  71. e := lb.End
  72. if e.Before(start) || e.After(end) {
  73. log.Debugf("CostModel.ComputeAssets: load balancer '%s' end outside window: %s not in [%s, %s]", lb.Name, e.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  74. e = end
  75. }
  76. loadBalancer := opencost.NewLoadBalancer(lb.Name, lb.Cluster, lb.ProviderID, s, e, opencost.NewWindow(&start, &end), lb.Private, lb.Ip)
  77. cm.PropertiesFromCluster(loadBalancer.Properties)
  78. loadBalancer.Cost = lb.Cost
  79. assetSet.Insert(loadBalancer, nil)
  80. }
  81. for _, cman := range clusterManagement {
  82. cmAsset := opencost.NewClusterManagement(cman.Provisioner, cman.Cluster, opencost.NewClosedWindow(start, end))
  83. cm.PropertiesFromCluster(cmAsset.Properties)
  84. cmAsset.Cost = cman.Cost
  85. assetSet.Insert(cmAsset, nil)
  86. }
  87. for _, n := range nodeMap {
  88. // check label, to see if node from fargate, if so ignore.
  89. if n.Labels != nil {
  90. if value, ok := n.Labels["label_eks_amazonaws_com_compute_type"]; ok && value == "fargate" {
  91. continue
  92. }
  93. }
  94. s := n.Start
  95. if s.Before(start) || s.After(end) {
  96. log.Debugf("CostModel.ComputeAssets: node '%s' start outside window: %s not in [%s, %s]", n.Name, s.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  97. s = start
  98. }
  99. e := n.End
  100. if e.Before(start) || e.After(end) {
  101. log.Debugf("CostModel.ComputeAssets: node '%s' end outside window: %s not in [%s, %s]", n.Name, e.Format("2006-01-02T15:04:05"), start.Format("2006-01-02T15:04:05"), end.Format("2006-01-02T15:04:05"))
  102. e = end
  103. }
  104. hours := e.Sub(s).Hours()
  105. node := opencost.NewNode(n.Name, n.Cluster, n.ProviderID, s, e, opencost.NewWindow(&start, &end))
  106. cm.PropertiesFromCluster(node.Properties)
  107. node.NodeType = n.NodeType
  108. node.CPUCoreHours = n.CPUCores * hours
  109. node.RAMByteHours = n.RAMBytes * hours
  110. node.GPUHours = n.GPUCount * hours
  111. node.CPUBreakdown = &opencost.Breakdown{
  112. Idle: n.CPUBreakdown.Idle,
  113. System: n.CPUBreakdown.System,
  114. User: n.CPUBreakdown.User,
  115. Other: n.CPUBreakdown.Other,
  116. }
  117. node.RAMBreakdown = &opencost.Breakdown{
  118. Idle: n.RAMBreakdown.Idle,
  119. System: n.RAMBreakdown.System,
  120. User: n.RAMBreakdown.User,
  121. Other: n.RAMBreakdown.Other,
  122. }
  123. node.CPUCost = n.CPUCost
  124. node.GPUCost = n.GPUCost
  125. node.GPUCount = n.GPUCount
  126. node.RAMCost = n.RAMCost
  127. if n.Overhead != nil {
  128. node.Overhead = &opencost.NodeOverhead{
  129. RamOverheadFraction: n.Overhead.RamOverheadFraction,
  130. CpuOverheadFraction: n.Overhead.CpuOverheadFraction,
  131. OverheadCostFraction: ((n.Overhead.CpuOverheadFraction * n.CPUCost) +
  132. (n.Overhead.RamOverheadFraction * n.RAMCost)) / node.TotalCost(),
  133. }
  134. } else {
  135. node.Overhead = &opencost.NodeOverhead{}
  136. }
  137. node.Discount = n.Discount
  138. if n.Preemptible {
  139. node.Preemptible = 1.0
  140. }
  141. node.SetLabels(opencost.AssetLabels(n.Labels))
  142. assetSet.Insert(node, nil)
  143. }
  144. return assetSet, nil
  145. }
  146. func (cm *CostModel) ClusterDisks(start, end time.Time) (map[DiskIdentifier]*Disk, error) {
  147. return ClusterDisks(cm.DataSource, cm.Provider, start, end)
  148. }
  149. func (cm *CostModel) ClusterLoadBalancers(start, end time.Time) (map[LoadBalancerIdentifier]*LoadBalancer, error) {
  150. return ClusterLoadBalancers(cm.DataSource, start, end)
  151. }
  152. func (cm *CostModel) ClusterNodes(start, end time.Time) (map[NodeIdentifier]*Node, error) {
  153. return ClusterNodes(cm.DataSource, cm.Provider, start, end)
  154. }
  155. func (cm *CostModel) ClusterManagement(start, end time.Time) (map[ClusterManagementIdentifier]*ClusterManagementCost, error) {
  156. return ClusterManagement(cm.DataSource, start, end)
  157. }
  158. // propertiesFromCluster populates static cluster properties to individual asset properties
  159. func (cm *CostModel) PropertiesFromCluster(props *opencost.AssetProperties) {
  160. // If properties does not have cluster value, do nothing
  161. if props.Cluster == "" {
  162. return
  163. }
  164. clusterMap := cm.ClusterMap.AsMap()
  165. ci, ok := clusterMap[props.Cluster]
  166. if !ok {
  167. log.Debugf("CostMode.PropertiesFromCluster: cluster '%s' was not found in ClusterMap", props.Cluster)
  168. return
  169. }
  170. props.Project = ci.Project
  171. props.Account = ci.Account
  172. props.Provider = ci.Provider
  173. }