more accurate resource pool data in aggregation reports
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1067,7 +1067,7 @@ func normalizeCellValue(value interface{}) interface{} {
|
||||
|
||||
type totalsPoint struct {
|
||||
Label string
|
||||
VmCount int64
|
||||
VmCount float64
|
||||
VcpuTotal float64
|
||||
RamTotal float64
|
||||
PresenceRatio float64
|
||||
@@ -1078,7 +1078,18 @@ type totalsPoint struct {
|
||||
}
|
||||
|
||||
func buildHourlyTotals(ctx context.Context, dbConn *sqlx.DB, records []SnapshotRecord) ([]totalsPoint, error) {
|
||||
points := make([]totalsPoint, 0, len(records))
|
||||
type hourBucket struct {
|
||||
samples int
|
||||
vmSum float64
|
||||
vcpuSum float64
|
||||
ramSum float64
|
||||
tinSum float64
|
||||
bronzeSum float64
|
||||
silverSum float64
|
||||
goldSum float64
|
||||
}
|
||||
buckets := make(map[int64]*hourBucket)
|
||||
|
||||
for _, record := range records {
|
||||
if err := db.ValidateTableName(record.TableName); err != nil {
|
||||
return nil, err
|
||||
@@ -1112,17 +1123,46 @@ WHERE %s
|
||||
if err := dbConn.GetContext(ctx, &row, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourKey := record.SnapshotTime.Local().Truncate(time.Hour).Unix()
|
||||
bucket := buckets[hourKey]
|
||||
if bucket == nil {
|
||||
bucket = &hourBucket{}
|
||||
buckets[hourKey] = bucket
|
||||
}
|
||||
bucket.samples++
|
||||
bucket.vmSum += float64(row.VmCount)
|
||||
bucket.vcpuSum += float64(row.VcpuTotal)
|
||||
bucket.ramSum += float64(row.RamTotal)
|
||||
bucket.tinSum += row.TinTotal
|
||||
bucket.bronzeSum += row.BronzeTotal
|
||||
bucket.silverSum += row.SilverTotal
|
||||
bucket.goldSum += row.GoldTotal
|
||||
}
|
||||
|
||||
keys := make([]int64, 0, len(buckets))
|
||||
for key := range buckets {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
|
||||
|
||||
points := make([]totalsPoint, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
bucket := buckets[key]
|
||||
if bucket.samples == 0 {
|
||||
continue
|
||||
}
|
||||
denom := float64(bucket.samples)
|
||||
vmAvg := bucket.vmSum / denom
|
||||
points = append(points, totalsPoint{
|
||||
Label: record.SnapshotTime.Local().Format("2006-01-02 15:04"),
|
||||
VmCount: row.VmCount,
|
||||
VcpuTotal: float64(row.VcpuTotal),
|
||||
RamTotal: float64(row.RamTotal),
|
||||
// For hourly snapshots, prorated VM count equals VM count (no finer granularity).
|
||||
PresenceRatio: float64(row.VmCount),
|
||||
TinTotal: row.TinTotal,
|
||||
BronzeTotal: row.BronzeTotal,
|
||||
SilverTotal: row.SilverTotal,
|
||||
GoldTotal: row.GoldTotal,
|
||||
Label: time.Unix(key, 0).Local().Format("2006-01-02 15:00"),
|
||||
VmCount: vmAvg,
|
||||
VcpuTotal: bucket.vcpuSum / denom,
|
||||
RamTotal: bucket.ramSum / denom,
|
||||
PresenceRatio: vmAvg,
|
||||
TinTotal: bucket.tinSum / denom,
|
||||
BronzeTotal: bucket.bronzeSum / denom,
|
||||
SilverTotal: bucket.silverSum / denom,
|
||||
GoldTotal: bucket.goldSum / denom,
|
||||
})
|
||||
}
|
||||
return points, nil
|
||||
@@ -1175,10 +1215,10 @@ WHERE %s
|
||||
}
|
||||
points = append(points, totalsPoint{
|
||||
Label: record.SnapshotTime.Local().Format("2006-01-02"),
|
||||
VmCount: row.VmCount,
|
||||
VmCount: float64(row.VmCount),
|
||||
VcpuTotal: row.VcpuTotal,
|
||||
RamTotal: row.RamTotal,
|
||||
PresenceRatio: computeProratedVmCount(row.PresenceRatio, row.VmCount, prorateByAvg),
|
||||
PresenceRatio: computeProratedVmCount(row.PresenceRatio, float64(row.VmCount), prorateByAvg),
|
||||
TinTotal: row.TinTotal,
|
||||
BronzeTotal: row.BronzeTotal,
|
||||
SilverTotal: row.SilverTotal,
|
||||
@@ -1188,11 +1228,11 @@ WHERE %s
|
||||
return points, nil
|
||||
}
|
||||
|
||||
func computeProratedVmCount(presenceRatio float64, vmCount int64, prorate bool) float64 {
|
||||
func computeProratedVmCount(presenceRatio float64, vmCount float64, prorate bool) float64 {
|
||||
if !prorate {
|
||||
return float64(vmCount)
|
||||
return vmCount
|
||||
}
|
||||
return presenceRatio * float64(vmCount)
|
||||
return presenceRatio * vmCount
|
||||
}
|
||||
|
||||
func writeTotalsChart(logger *slog.Logger, xlsx *excelize.File, sheetName string, points []totalsPoint) {
|
||||
@@ -1224,6 +1264,18 @@ func writeTotalsChart(logger *slog.Logger, xlsx *excelize.File, sheetName string
|
||||
xlsx.SetCellValue(sheetName, fmt.Sprintf("I%d", row), point.GoldTotal)
|
||||
}
|
||||
|
||||
if lastRow := len(points) + 1; lastRow >= 2 {
|
||||
numFmt := "0.00000000"
|
||||
styleID, err := xlsx.NewStyle(&excelize.Style{CustomNumFmt: &numFmt})
|
||||
if err == nil {
|
||||
if err := xlsx.SetCellStyle(sheetName, "E2", fmt.Sprintf("I%d", lastRow), styleID); err != nil {
|
||||
logger.Error("Error setting totals number format", "error", err)
|
||||
}
|
||||
} else {
|
||||
logger.Error("Error creating totals number format", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
if endCell, err := excelize.CoordinatesToCellName(len(headers), 1); err == nil {
|
||||
filterRange := "A1:" + endCell
|
||||
if err := xlsx.AutoFilter(sheetName, filterRange, nil); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user