Change metering exported format to csv.

Signed-off-by: Rao Yunkun <yunkunrao@yunify.com>
This commit is contained in:
Rao Yunkun
2021-03-02 10:27:53 +08:00
parent e9073f0486
commit 5bddda51e7
7 changed files with 133 additions and 28 deletions

View File

@@ -19,8 +19,8 @@ package v1alpha3
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/jszwec/csvutil"
"io"
"strconv"
"strings"
@@ -388,7 +388,7 @@ func ExportMetrics(resp *restful.Response, metrics model.Metrics) {
}
}
resBytes, err := json.MarshalIndent(metrics, "", " ")
resBytes, err := csvutil.Marshal(metrics.Results)
if err != nil {
api.HandleBadRequest(resp, nil, err)
return

View File

@@ -64,16 +64,17 @@ func (h *tenantHandler) QueryMeteringsHierarchy(req *restful.Request, resp *rest
func (h *tenantHandler) HandlePriceInfoQuery(req *restful.Request, resp *restful.Response) {
var priceInfoResponse metering.PriceInfo
priceInfoResponse.Init()
meterConfig, err := monitoring.LoadYaml()
if err != nil {
klog.Error(err)
klog.Warning(err)
resp.WriteAsJson(priceInfoResponse)
return
}
priceInfo := meterConfig.GetPriceInfo()
priceInfoResponse.Currency = "CNY"
priceInfoResponse.Currency = priceInfo.CurrencyUnit
priceInfoResponse.CpuPerCorePerHour = priceInfo.CpuPerCorePerHour
priceInfoResponse.MemPerGigabytesPerHour = priceInfo.MemPerGigabytesPerHour
priceInfoResponse.IngressNetworkTrafficPerGiagabytesPerHour = priceInfo.IngressNetworkTrafficPerGiagabytesPerHour

View File

@@ -9,6 +9,16 @@ type PriceInfo struct {
PvcPerGigabytesPerHour float64 `json:"pvc_per_gigabytes_per_hour,omitempty" description:"pvc price"`
}
// currently init method fill illegal value to hint that metering config file was not mounted yet
func (p *PriceInfo) Init() {
p.Currency = ""
p.CpuPerCorePerHour = -1
p.MemPerGigabytesPerHour = -1
p.IngressNetworkTrafficPerGiagabytesPerHour = -1
p.EgressNetworkTrafficPerGiagabytesPerHour = -1
p.PvcPerGigabytesPerHour = -1
}
type PodStatistic struct {
CPUUsage float64 `json:"cpu_usage" description:"cpu_usage"`
MemoryUsageWoCache float64 `json:"memory_usage_wo_cache" description:"memory_usage_wo_cache"`

View File

@@ -19,6 +19,14 @@ const (
meteringConfig = "/etc/kubesphere/metering/ks-metering.yaml"
)
var meterResourceUnitMap = map[int]string{
METER_RESOURCE_TYPE_CPU: "cores",
METER_RESOURCE_TYPE_MEM: "bytes",
METER_RESOURCE_TYPE_NET_INGRESS: "bytes",
METER_RESOURCE_TYPE_NET_EGRESS: "bytes",
METER_RESOURCE_TYPE_PVC: "bytes",
}
var MeterResourceMap = map[string]int{
"meter_cluster_cpu_usage": METER_RESOURCE_TYPE_CPU,
"meter_cluster_memory_usage": METER_RESOURCE_TYPE_MEM,
@@ -67,6 +75,7 @@ type PriceInfo struct {
IngressNetworkTrafficPerGiagabytesPerHour float64 `json:"ingressNetworkTrafficPerGiagabytesPerHour" yaml:"ingressNetworkTrafficPerGiagabytesPerHour"`
EgressNetworkTrafficPerGigabytesPerHour float64 `json:"egressNetworkTrafficPerGigabytesPerHour" yaml:"egressNetworkTrafficPerGigabytesPerHour"`
PvcPerGigabytesPerHour float64 `json:"pvcPerGigabytesPerHour" yaml:"pvcPerGigabytesPerHour"`
CurrencyUnit string `json:"currencyUnit" yaml:"currencyUnit"`
}
type Billing struct {
@@ -143,6 +152,25 @@ func getAvgPointValue(points []monitoring.Point) float64 {
return getSumPointValue(points) / float64(len(points))
}
func getCurrencyUnit() string {
meterConfig, err := LoadYaml()
if err != nil {
klog.Error(err)
return ""
}
return meterConfig.GetPriceInfo().CurrencyUnit
}
func getResourceUnit(meterName string) string {
if resourceType, ok := MeterResourceMap[meterName]; !ok {
klog.Errorf("invlaid meter %v", meterName)
return ""
} else {
return meterResourceUnitMap[resourceType]
}
}
func getFeeWithMeterName(meterName string, sum float64) float64 {
meterConfig, err := LoadYaml()
@@ -216,6 +244,8 @@ func updateMetricStatData(metric monitoring.Metric, scalingMap map[string]float6
metricData.MetricValues[index].SumValue = sum
metricData.MetricValues[index].Fee = getFeeWithMeterName(metricName, sum)
}
metricData.MetricValues[index].CurrencyUnit = getCurrencyUnit()
metricData.MetricValues[index].ResourceUnit = getResourceUnit(metricName)
}
return metricData

View File

@@ -20,7 +20,9 @@ import (
"errors"
"fmt"
"github.com/json-iterator/go"
"github.com/jszwec/csvutil"
"strconv"
"strings"
"time"
)
@@ -36,14 +38,46 @@ type Metadata struct {
}
type Metric struct {
MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum"`
MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum" csv:"metric_name"`
MetricData `json:"data,omitempty" description:"actual metric result"`
Error string `json:"error,omitempty"`
Error string `json:"error,omitempty" csv:"-"`
}
type MetricValues []MetricValue
func (m MetricValues) MarshalCSV() ([]byte, error) {
var ret []string
for _, v := range m {
tmp, err := v.MarshalCSV()
if err != nil {
return nil, err
}
ret = append(ret, string(tmp))
}
return []byte(strings.Join(ret, "||")), nil
}
type MetricData struct {
MetricType string `json:"resultType,omitempty" description:"result type, one of matrix, vector"`
MetricValues []MetricValue `json:"result,omitempty" description:"metric data including labels, time series and values"`
MetricType string `json:"resultType,omitempty" description:"result type, one of matrix, vector" csv:"metric_type"`
MetricValues `json:"result,omitempty" description:"metric data including labels, time series and values" csv:"metric_values"`
}
func (m MetricData) MarshalCSV() ([]byte, error) {
var ret []byte
for _, v := range m.MetricValues {
tmp, err := csvutil.Marshal(&v)
if err != nil {
return nil, err
}
ret = append(ret, tmp...)
}
return ret, nil
}
// The first element is the timestamp, the second is the metric value.
@@ -60,11 +94,48 @@ type MetricValue struct {
ExportSample *ExportPoint `json:"exported_value,omitempty" description:"exported time series, values of vector type"`
ExportedSeries []ExportPoint `json:"exported_values,omitempty" description:"exported time series, values of matrix type"`
MinValue float64 `json:"min_value" description:"minimum value from monitor points"`
MaxValue float64 `json:"max_value" description:"maximum value from monitor points"`
AvgValue float64 `json:"avg_value" description:"average value from monitor points"`
SumValue float64 `json:"sum_value" description:"sum value from monitor points"`
Fee float64 `json:"fee" description:"resource fee"`
MinValue float64 `json:"min_value" description:"minimum value from monitor points"`
MaxValue float64 `json:"max_value" description:"maximum value from monitor points"`
AvgValue float64 `json:"avg_value" description:"average value from monitor points"`
SumValue float64 `json:"sum_value" description:"sum value from monitor points"`
Fee float64 `json:"fee" description:"resource fee"`
ResourceUnit string `json:"resource_unit"`
CurrencyUnit string `json:"currency_unit"`
}
func (mv MetricValue) MarshalCSV() ([]byte, error) {
// metric value format:
// target,stats value(include fees),exported_value,exported_values
// for example:
// {workspace:demo-ws},,2021-02-23 01:00:00 AM 0|2021-02-23 02:00:00 AM 0|...
var metricValueCSVTemplate = "{%s},unit:%s|min:%.3f|max:%.3f|avg:%.3f|sum:%.3f|fee:%.2f %s,%s,%s"
var targetList []string
for k, v := range mv.Metadata {
targetList = append(targetList, fmt.Sprintf("%s=%s", k, v))
}
exportedSampleStr := ""
if mv.ExportSample != nil {
exportedSampleStr = mv.ExportSample.Format()
}
exportedSeriesStrList := []string{}
for _, v := range mv.ExportedSeries {
exportedSeriesStrList = append(exportedSeriesStrList, v.Format())
}
return []byte(fmt.Sprintf(metricValueCSVTemplate,
strings.Join(targetList, "|"),
mv.ResourceUnit,
mv.MinValue,
mv.MaxValue,
mv.AvgValue,
mv.SumValue,
mv.Fee,
mv.CurrencyUnit,
exportedSampleStr,
exportedSeriesStrList)), nil
}
func (mv *MetricValue) TransferToExportedMetricValue() {
@@ -152,16 +223,6 @@ func (p ExportPoint) Value() float64 {
return p[1]
}
// MarshalJSON implements json.Marshaler. It will be called when writing JSON to HTTP response
// Inspired by prometheus/client_golang
func (p ExportPoint) MarshalJSON() ([]byte, error) {
t, err := jsoniter.Marshal(p.Timestamp())
if err != nil {
return nil, err
}
v, err := jsoniter.Marshal(strconv.FormatFloat(p.Value(), 'f', -1, 64))
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
func (p ExportPoint) Format() string {
return p.Timestamp() + " " + strconv.FormatFloat(p.Value(), 'f', -1, 64)
}