|
|
@@ -92,6 +92,32 @@ type Allocation struct {
|
|
|
// and appended to an Allocation, and so by default is is nil.
|
|
|
ProportionalAssetResourceCosts ProportionalAssetResourceCosts `json:"proportionalAssetResourceCosts"` //@bingen:field[ignore]
|
|
|
SharedCostBreakdown SharedCostBreakdowns `json:"sharedCostBreakdown"` //@bingen:field[ignore]
|
|
|
+ LoadBalancers LbAllocations `json:"LoadBalancers"` // @bingen:field[version=17]
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+type LbAllocations map[string]*LbAllocation
|
|
|
+
|
|
|
+func (orig LbAllocations) Clone() LbAllocations {
|
|
|
+ if orig == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ newAllocs := LbAllocations{}
|
|
|
+
|
|
|
+ for key, lbAlloc := range orig {
|
|
|
+ newAllocs[key] = &LbAllocation{
|
|
|
+ Service: lbAlloc.Service,
|
|
|
+ Cost: lbAlloc.Cost,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return newAllocs
|
|
|
+}
|
|
|
+
|
|
|
+type LbAllocation struct {
|
|
|
+ Service string `json:"service"`
|
|
|
+ Cost float64 `json:"cost"`
|
|
|
+ Private bool `json:"private"`
|
|
|
}
|
|
|
|
|
|
// RawAllocationOnlyData is information that only belong in "raw" Allocations,
|
|
|
@@ -252,24 +278,29 @@ func (pva *PVAllocation) Equal(that *PVAllocation) bool {
|
|
|
}
|
|
|
|
|
|
type ProportionalAssetResourceCost struct {
|
|
|
- Cluster string `json:"cluster"`
|
|
|
- Node string `json:"node,omitempty"`
|
|
|
- ProviderID string `json:"providerID,omitempty"`
|
|
|
- CPUPercentage float64 `json:"cpuPercentage"`
|
|
|
- GPUPercentage float64 `json:"gpuPercentage"`
|
|
|
- RAMPercentage float64 `json:"ramPercentage"`
|
|
|
- NodeResourceCostPercentage float64 `json:"nodeResourceCostPercentage"`
|
|
|
- GPUTotalCost float64 `json:"-"`
|
|
|
- GPUProportionalCost float64 `json:"-"`
|
|
|
- CPUTotalCost float64 `json:"-"`
|
|
|
- CPUProportionalCost float64 `json:"-"`
|
|
|
- RAMTotalCost float64 `json:"-"`
|
|
|
- RAMProportionalCost float64 `json:"-"`
|
|
|
-}
|
|
|
-
|
|
|
-func (parc ProportionalAssetResourceCost) Key(insertByNode bool) string {
|
|
|
- if insertByNode {
|
|
|
- return parc.Cluster + "," + parc.Node
|
|
|
+ Cluster string `json:"cluster"`
|
|
|
+ Name string `json:"name,omitempty"`
|
|
|
+ Type string `json:"name,omitempty"`
|
|
|
+ ProviderID string `json:"providerID,omitempty"`
|
|
|
+ CPUPercentage float64 `json:"cpuPercentage"`
|
|
|
+ GPUPercentage float64 `json:"gpuPercentage"`
|
|
|
+ RAMPercentage float64 `json:"ramPercentage"`
|
|
|
+ LoadBalancerPercentage float64 `json:"loadBalancerPercentage"`
|
|
|
+
|
|
|
+ NodeResourceCostPercentage float64 `json:"nodeResourceCostPercentage"`
|
|
|
+ GPUTotalCost float64 `json:"-"`
|
|
|
+ GPUProportionalCost float64 `json:"-"`
|
|
|
+ CPUTotalCost float64 `json:"-"`
|
|
|
+ CPUProportionalCost float64 `json:"-"`
|
|
|
+ RAMTotalCost float64 `json:"-"`
|
|
|
+ RAMProportionalCost float64 `json:"-"`
|
|
|
+ LoadBalancerProportionalCost float64 `json:"-"`
|
|
|
+ LoadBalancerTotalCost float64 `json:"-"`
|
|
|
+}
|
|
|
+
|
|
|
+func (parc ProportionalAssetResourceCost) Key(insertByName bool) string {
|
|
|
+ if insertByName {
|
|
|
+ return parc.Cluster + "," + parc.Name
|
|
|
} else {
|
|
|
return parc.Cluster
|
|
|
}
|
|
|
@@ -287,36 +318,36 @@ func (parcs ProportionalAssetResourceCosts) Clone() ProportionalAssetResourceCos
|
|
|
return cloned
|
|
|
}
|
|
|
|
|
|
-func (parcs ProportionalAssetResourceCosts) Insert(parc ProportionalAssetResourceCost, insertByNode bool) {
|
|
|
- if !insertByNode {
|
|
|
- parc.Node = ""
|
|
|
+func (parcs ProportionalAssetResourceCosts) Insert(parc ProportionalAssetResourceCost, insertByName bool) {
|
|
|
+ if !insertByName {
|
|
|
+ parc.Name = ""
|
|
|
+ parc.Type = ""
|
|
|
parc.ProviderID = ""
|
|
|
}
|
|
|
- if curr, ok := parcs[parc.Key(insertByNode)]; ok {
|
|
|
+ if curr, ok := parcs[parc.Key(insertByName)]; ok {
|
|
|
|
|
|
toInsert := ProportionalAssetResourceCost{
|
|
|
- Node: curr.Node,
|
|
|
- Cluster: curr.Cluster,
|
|
|
- ProviderID: curr.ProviderID,
|
|
|
- CPUTotalCost: curr.CPUTotalCost + parc.CPUTotalCost,
|
|
|
- CPUProportionalCost: curr.CPUProportionalCost + parc.CPUProportionalCost,
|
|
|
- RAMTotalCost: curr.RAMTotalCost + parc.RAMTotalCost,
|
|
|
- RAMProportionalCost: curr.RAMProportionalCost + parc.RAMProportionalCost,
|
|
|
- GPUProportionalCost: curr.GPUProportionalCost + parc.GPUProportionalCost,
|
|
|
- GPUTotalCost: curr.GPUTotalCost + parc.GPUTotalCost,
|
|
|
- }
|
|
|
-
|
|
|
- computePercentages(&toInsert)
|
|
|
- parcs[parc.Key(insertByNode)] = toInsert
|
|
|
+ Name: curr.Name,
|
|
|
+ Type: curr.Type,
|
|
|
+ Cluster: curr.Cluster,
|
|
|
+ ProviderID: curr.ProviderID,
|
|
|
+ CPUProportionalCost: curr.CPUProportionalCost + parc.CPUProportionalCost,
|
|
|
+ RAMProportionalCost: curr.RAMProportionalCost + parc.RAMProportionalCost,
|
|
|
+ GPUProportionalCost: curr.GPUProportionalCost + parc.GPUProportionalCost,
|
|
|
+ LoadBalancerProportionalCost: curr.LoadBalancerProportionalCost + parc.LoadBalancerProportionalCost,
|
|
|
+ }
|
|
|
+
|
|
|
+ ComputePercentages(&toInsert)
|
|
|
+ parcs[parc.Key(insertByName)] = toInsert
|
|
|
} else {
|
|
|
- computePercentages(&parc)
|
|
|
- parcs[parc.Key(insertByNode)] = parc
|
|
|
+ ComputePercentages(&parc)
|
|
|
+ parcs[parc.Key(insertByName)] = parc
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func computePercentages(toInsert *ProportionalAssetResourceCost) {
|
|
|
+func ComputePercentages(toInsert *ProportionalAssetResourceCost) {
|
|
|
// compute percentages
|
|
|
- totalCost := toInsert.RAMTotalCost + toInsert.CPUTotalCost + toInsert.GPUTotalCost
|
|
|
+ totalNodeCost := toInsert.RAMTotalCost + toInsert.CPUTotalCost + toInsert.GPUTotalCost
|
|
|
|
|
|
if toInsert.CPUTotalCost > 0 {
|
|
|
toInsert.CPUPercentage = toInsert.CPUProportionalCost / toInsert.CPUTotalCost
|
|
|
@@ -326,21 +357,25 @@ func computePercentages(toInsert *ProportionalAssetResourceCost) {
|
|
|
toInsert.GPUPercentage = toInsert.GPUProportionalCost / toInsert.GPUTotalCost
|
|
|
}
|
|
|
|
|
|
+ if toInsert.LoadBalancerTotalCost > 0 {
|
|
|
+ toInsert.LoadBalancerPercentage = toInsert.LoadBalancerProportionalCost / toInsert.LoadBalancerTotalCost
|
|
|
+ }
|
|
|
+
|
|
|
if toInsert.RAMTotalCost > 0 {
|
|
|
toInsert.RAMPercentage = toInsert.RAMProportionalCost / toInsert.RAMTotalCost
|
|
|
}
|
|
|
|
|
|
- ramFraction := toInsert.RAMTotalCost / totalCost
|
|
|
+ ramFraction := toInsert.RAMTotalCost / totalNodeCost
|
|
|
if ramFraction != ramFraction || ramFraction < 0 {
|
|
|
ramFraction = 0
|
|
|
}
|
|
|
|
|
|
- cpuFraction := toInsert.CPUTotalCost / totalCost
|
|
|
+ cpuFraction := toInsert.CPUTotalCost / totalNodeCost
|
|
|
if cpuFraction != cpuFraction || cpuFraction < 0 {
|
|
|
cpuFraction = 0
|
|
|
}
|
|
|
|
|
|
- gpuFraction := toInsert.GPUTotalCost / totalCost
|
|
|
+ gpuFraction := toInsert.GPUTotalCost / totalNodeCost
|
|
|
if gpuFraction != gpuFraction || gpuFraction < 0 {
|
|
|
gpuFraction = 0
|
|
|
}
|
|
|
@@ -352,12 +387,12 @@ func computePercentages(toInsert *ProportionalAssetResourceCost) {
|
|
|
func (parcs ProportionalAssetResourceCosts) Add(that ProportionalAssetResourceCosts) {
|
|
|
|
|
|
for _, parc := range that {
|
|
|
- // if node field is empty, we know this is a cluster level PARC aggregation
|
|
|
- insertByNode := true
|
|
|
- if parc.Node == "" {
|
|
|
- insertByNode = false
|
|
|
+ // if name field is empty, we know this is a cluster level PARC aggregation
|
|
|
+ insertByName := true
|
|
|
+ if parc.Name == "" {
|
|
|
+ insertByName = false
|
|
|
}
|
|
|
- parcs.Insert(parc, insertByNode)
|
|
|
+ parcs.Insert(parc, insertByName)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -477,6 +512,7 @@ func (a *Allocation) Clone() *Allocation {
|
|
|
RawAllocationOnly: a.RawAllocationOnly.Clone(),
|
|
|
ProportionalAssetResourceCosts: a.ProportionalAssetResourceCosts.Clone(),
|
|
|
SharedCostBreakdown: a.SharedCostBreakdown.Clone(),
|
|
|
+ LoadBalancers: a.LoadBalancers.Clone(),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -999,11 +1035,38 @@ func (a *Allocation) add(that *Allocation) {
|
|
|
a.NetworkCostAdjustment += that.NetworkCostAdjustment
|
|
|
a.LoadBalancerCostAdjustment += that.LoadBalancerCostAdjustment
|
|
|
|
|
|
+ // Sum LoadBalancer Allocations
|
|
|
+ a.LoadBalancers = a.LoadBalancers.Add(that.LoadBalancers)
|
|
|
+
|
|
|
// Any data that is in a "raw allocation only" is not valid in any
|
|
|
// sort of cumulative Allocation (like one that is added).
|
|
|
a.RawAllocationOnly = nil
|
|
|
}
|
|
|
|
|
|
+func (thisLbAllocs LbAllocations) Add(thatLbAllocs LbAllocations) LbAllocations {
|
|
|
+ // loop through both sets of LB allocations, building a new LBAllocations that has the summed set
|
|
|
+ mergedLbAllocs := thisLbAllocs.Clone()
|
|
|
+ if thatLbAllocs != nil {
|
|
|
+ if mergedLbAllocs == nil {
|
|
|
+ mergedLbAllocs = LbAllocations{}
|
|
|
+ }
|
|
|
+ for lbKey, thatlbAlloc := range thatLbAllocs {
|
|
|
+ thisLbAlloc, ok := mergedLbAllocs[lbKey]
|
|
|
+ if !ok {
|
|
|
+ thisLbAlloc = &LbAllocation{
|
|
|
+ Service: thatlbAlloc.Service,
|
|
|
+ Cost: thatlbAlloc.Cost,
|
|
|
+ }
|
|
|
+ mergedLbAllocs[lbKey] = thisLbAlloc
|
|
|
+ } else {
|
|
|
+ thisLbAlloc.Cost += thatlbAlloc.Cost
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return mergedLbAllocs
|
|
|
+}
|
|
|
+
|
|
|
// AllocationSet stores a set of Allocations, each with a unique name, that share
|
|
|
// a window. An AllocationSet is mutable, so treat it like a threadsafe map.
|
|
|
type AllocationSet struct {
|
|
|
@@ -1195,6 +1258,12 @@ func (as *AllocationSet) AggregateBy(aggregateBy []string, options *AllocationAg
|
|
|
for _, alloc := range as.Allocations {
|
|
|
|
|
|
alloc.Properties.AggregatedMetadata = options.IncludeAggregatedMetadata
|
|
|
+ // build a parallel set of allocations to only be used
|
|
|
+ // for computing PARCs
|
|
|
+ if options.IncludeProportionalAssetResourceCosts {
|
|
|
+ parcSet.Insert(alloc.Clone())
|
|
|
+ }
|
|
|
+
|
|
|
// External allocations get aggregated post-hoc (see step 6) and do
|
|
|
// not necessarily contain complete sets of properties, so they are
|
|
|
// moved to a separate AllocationSet.
|
|
|
@@ -1218,12 +1287,6 @@ func (as *AllocationSet) AggregateBy(aggregateBy []string, options *AllocationAg
|
|
|
aggSet.Insert(alloc)
|
|
|
}
|
|
|
|
|
|
- // build a parallel set of allocations to only be used
|
|
|
- // for computing PARCs
|
|
|
- if options.IncludeProportionalAssetResourceCosts {
|
|
|
- parcSet.Insert(alloc.Clone())
|
|
|
- }
|
|
|
-
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
@@ -1294,7 +1357,7 @@ func (as *AllocationSet) AggregateBy(aggregateBy []string, options *AllocationAg
|
|
|
// (2b) If proportional asset resource costs are to be included, compute them
|
|
|
// and add them to the allocations.
|
|
|
if options.IncludeProportionalAssetResourceCosts {
|
|
|
- err := deriveProportionalAssetResourceCosts(options, as, shareSet)
|
|
|
+ err := deriveProportionalAssetResourceCosts(options, as, shareSet, parcSet)
|
|
|
if err != nil {
|
|
|
log.Debugf("AggregateBy: failed to derive proportional asset resource costs from idle coefficients: %s", err)
|
|
|
return fmt.Errorf("AggregateBy: failed to derive proportional asset resource costs from idle coefficients: %s", err)
|
|
|
@@ -1895,7 +1958,7 @@ func computeIdleCoeffs(options *AllocationAggregationOptions, as *AllocationSet,
|
|
|
return coeffs, totals, nil
|
|
|
}
|
|
|
|
|
|
-func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions, as *AllocationSet, shareSet *AllocationSet) error {
|
|
|
+func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions, as *AllocationSet, shareSet, parcsSet *AllocationSet) error {
|
|
|
|
|
|
// Compute idle coefficients, then save them in AllocationAggregationOptions
|
|
|
// [idle_id][allocation name][resource] = [coeff]
|
|
|
@@ -1906,11 +1969,7 @@ func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions,
|
|
|
totals := map[string]map[string]float64{}
|
|
|
|
|
|
// Record allocation values first, then normalize by totals to get percentages
|
|
|
- for _, alloc := range as.Allocations {
|
|
|
- if alloc.IsIdle() {
|
|
|
- // Skip idle allocations in coefficient calculation
|
|
|
- continue
|
|
|
- }
|
|
|
+ for _, alloc := range parcsSet.Allocations {
|
|
|
|
|
|
idleId, err := alloc.getIdleId(options)
|
|
|
if err != nil {
|
|
|
@@ -1931,6 +1990,22 @@ func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions,
|
|
|
if _, ok := coeffs[idleId][name]; !ok {
|
|
|
coeffs[idleId][name] = map[string]float64{}
|
|
|
}
|
|
|
+ // idle IDs for load balancers are their services
|
|
|
+ for key := range alloc.LoadBalancers {
|
|
|
+ if _, ok := totals[key]; !ok {
|
|
|
+ totals[key] = map[string]float64{}
|
|
|
+ }
|
|
|
+
|
|
|
+ if _, ok := coeffs[key]; !ok {
|
|
|
+ coeffs[key] = map[string]map[string]float64{}
|
|
|
+ }
|
|
|
+ if _, ok := coeffs[key][name]; !ok {
|
|
|
+ coeffs[key][name] = map[string]float64{}
|
|
|
+ }
|
|
|
+
|
|
|
+ coeffs[key][name]["loadbalancer"] += alloc.LoadBalancerTotalCost()
|
|
|
+ totals[key]["loadbalancer"] += alloc.LoadBalancerTotalCost()
|
|
|
+ }
|
|
|
|
|
|
coeffs[idleId][name]["cpu"] += alloc.CPUTotalCost()
|
|
|
coeffs[idleId][name]["gpu"] += alloc.GPUTotalCost()
|
|
|
@@ -1976,6 +2051,23 @@ func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions,
|
|
|
totals[idleId]["cpu"] += alloc.CPUTotalCost()
|
|
|
totals[idleId]["gpu"] += alloc.GPUTotalCost()
|
|
|
totals[idleId]["ram"] += alloc.RAMTotalCost()
|
|
|
+
|
|
|
+ // idle IDs for load balancers are their services
|
|
|
+ for key := range alloc.LoadBalancers {
|
|
|
+ if _, ok := totals[key]; !ok {
|
|
|
+ totals[key] = map[string]float64{}
|
|
|
+ }
|
|
|
+
|
|
|
+ if _, ok := coeffs[key]; !ok {
|
|
|
+ coeffs[key] = map[string]map[string]float64{}
|
|
|
+ }
|
|
|
+ if _, ok := coeffs[key][name]; !ok {
|
|
|
+ coeffs[key][name] = map[string]float64{}
|
|
|
+ }
|
|
|
+ coeffs[key][name]["loadbalancer"] += alloc.LoadBalancerTotalCost()
|
|
|
+ totals[key]["loadbalancer"] += alloc.LoadBalancerTotalCost()
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// after totals are computed, loop through and set parcs on allocations
|
|
|
@@ -1988,15 +2080,26 @@ func deriveProportionalAssetResourceCosts(options *AllocationAggregationOptions,
|
|
|
alloc.ProportionalAssetResourceCosts = ProportionalAssetResourceCosts{}
|
|
|
alloc.ProportionalAssetResourceCosts.Insert(ProportionalAssetResourceCost{
|
|
|
Cluster: alloc.Properties.Cluster,
|
|
|
- Node: alloc.Properties.Node,
|
|
|
+ Name: alloc.Properties.Node,
|
|
|
+ Type: "Node",
|
|
|
ProviderID: alloc.Properties.ProviderID,
|
|
|
- GPUTotalCost: totals[idleId]["gpu"],
|
|
|
- CPUTotalCost: totals[idleId]["cpu"],
|
|
|
- RAMTotalCost: totals[idleId]["ram"],
|
|
|
GPUProportionalCost: coeffs[idleId][alloc.Name]["gpu"],
|
|
|
CPUProportionalCost: coeffs[idleId][alloc.Name]["cpu"],
|
|
|
RAMProportionalCost: coeffs[idleId][alloc.Name]["ram"],
|
|
|
}, options.IdleByNode)
|
|
|
+ // insert a separate PARC for the load balancer
|
|
|
+ if alloc.LoadBalancerCost != 0 {
|
|
|
+ for key, svc := range alloc.LoadBalancers {
|
|
|
+
|
|
|
+ alloc.ProportionalAssetResourceCosts.Insert(ProportionalAssetResourceCost{
|
|
|
+ Cluster: alloc.Properties.Cluster,
|
|
|
+ Name: svc.Service,
|
|
|
+ Type: "LoadBalancer",
|
|
|
+ LoadBalancerProportionalCost: coeffs[key][alloc.Name]["loadbalancer"],
|
|
|
+ }, options.IdleByNode)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
return nil
|