node.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package kubemodel
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. // Node represents a Kubernetes node with capacity-based resource tracking.
  7. // All resource measures (CPU, RAM) represent node capacity, not requests or limits.
  8. // This aligns with the principle that cost allocation should be based on provisioned capacity.
  9. type Node struct {
  10. UID string `json:"uid"`
  11. ProviderResourceUID string `json:"providerResourceUid"`
  12. Name string `json:"name"`
  13. Labels map[string]string `json:"labels,omitempty"`
  14. Annotations map[string]string `json:"annotations,omitempty"`
  15. DurationSeconds Measurement `json:"durationSeconds"`
  16. CpuMillicoreSeconds Measurement `json:"cpuMillicoreSeconds"` // Node CPU capacity in millicore-seconds
  17. RAMByteSeconds Measurement `json:"ramByteSeconds"` // Node RAM capacity in Byte-seconds
  18. AttachedVolumes map[string]*NodeVolumeUsage `json:"attachedVolumes,omitempty"`
  19. CpuMillicoreUsageMax Measurement `json:"cpuMillicoreUsageMax"` // Peak CPU usage observed
  20. RAMByteUsageMax Measurement `json:"ramByteUsageMax"` // Peak RAM usage observed
  21. Start time.Time `json:"start,omitempty"` // Node creation/start timestamp
  22. End time.Time `json:"end,omitempty"` // Node deletion/end timestamp (nil if still running)
  23. }
  24. // NodeVolumeUsage tracks storage usage for a disk volume attached to a node.
  25. // Used for cost allocation of cloud storage resources (e.g., AWS EBS volumes).
  26. type NodeVolumeUsage struct {
  27. VolumeUID string `json:"volumeUid"` // "root" for primary disk, or actual volume UID for additional volumes
  28. CapacityBytes Measurement `json:"capacityBytes"` // Total capacity of the volume in bytes
  29. UsageByteSeconds Measurement `json:"usageByteSeconds"` // Cumulative usage (Byte × seconds) over measurement window
  30. VolumeType string `json:"volumeType"` // "root" for primary disk, "persistent" for additional PVs
  31. ProviderID string `json:"providerId"` // Cloud provider volume ID (e.g., "vol-xxxxx" for AWS EBS)
  32. DurationSeconds Measurement `json:"durationSeconds"` // Duration the volume was attached during measurement window in seconds
  33. }
  34. // CpuMillicoreUsageAverage calculates the average CPU usage in millicores over the uptime period.
  35. // Returns 0 if uptime is 0 to avoid division by zero.
  36. func (n *Node) CpuMillicoreUsageAverage() Measurement {
  37. if n.DurationSeconds == 0 {
  38. return 0
  39. }
  40. return n.CpuMillicoreSeconds / n.DurationSeconds
  41. }
  42. // RAMByteUsageAverage calculates the average RAM usage in bytes over the uptime period.
  43. // Returns 0 if uptime is 0 to avoid division by zero.
  44. func (n *Node) RAMByteUsageAverage() Measurement {
  45. if n.DurationSeconds == 0 {
  46. return 0
  47. }
  48. return n.RAMByteSeconds / n.DurationSeconds
  49. }
  50. // TotalVolumeUsageByteSeconds returns the sum of all volume usage Byte-seconds across all attached volumes.
  51. func (n *Node) TotalVolumeUsageByteSeconds() Measurement {
  52. var total Measurement
  53. for _, volume := range n.AttachedVolumes {
  54. total += volume.UsageByteSeconds
  55. }
  56. return total
  57. }
  58. // TotalVolumeCapacityBytes returns the sum of all volume capacities across all attached volumes.
  59. func (n *Node) TotalVolumeCapacityBytes() Measurement {
  60. var total Measurement
  61. for _, volume := range n.AttachedVolumes {
  62. total += volume.CapacityBytes
  63. }
  64. return total
  65. }
  66. // GetVolumeUsageAverage calculates the average storage usage in bytes for a specific volume over the uptime period.
  67. // Returns 0 if uptime is 0 or volume doesn't exist.
  68. func (n *Node) GetVolumeUsageAverage(volumeUID string) Measurement {
  69. volume, exists := n.AttachedVolumes[volumeUID]
  70. if !exists || n.DurationSeconds == 0 {
  71. return 0
  72. }
  73. return volume.UsageByteSeconds / n.DurationSeconds
  74. }
  75. func (kms *KubeModelSet) RegisterNode(uid, name string) error {
  76. if uid == "" {
  77. err := fmt.Errorf("UID is nil for Node '%s'", name)
  78. kms.Error(err)
  79. return err
  80. }
  81. if _, ok := kms.Nodes[uid]; !ok {
  82. if kms.Cluster == nil {
  83. kms.Warnf("RegisterNode(%s, %s): Cluster is nil", uid, name)
  84. }
  85. kms.Nodes[uid] = &Node{
  86. UID: uid,
  87. Name: name,
  88. AttachedVolumes: make(map[string]*NodeVolumeUsage),
  89. }
  90. kms.Metadata.ObjectCount++
  91. }
  92. return nil
  93. }