Files
kubesphere/pkg/models/metrics/util.go
2020-01-14 23:24:53 +08:00

294 lines
7.6 KiB
Go

/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics
import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2"
"kubesphere.io/kubesphere/pkg/informers"
"math"
"sort"
"strconv"
"runtime/debug"
)
const (
DefaultPageLimit = 5
DefaultPage = 1
ResultTypeVector = "vector"
ResultTypeMatrix = "matrix"
MetricStatusSuccess = "success"
ResultItemMetricResourceName = "resource_name"
ResultSortTypeDesc = "desc"
ResultSortTypeAsc = "asc"
)
type FormatedMetricDataWrapper struct {
fmtMetricData v1alpha2.QueryResult
by func(p, q *v1alpha2.QueryValue) bool
}
func (wrapper FormatedMetricDataWrapper) Len() int {
return len(wrapper.fmtMetricData.Result)
}
func (wrapper FormatedMetricDataWrapper) Less(i, j int) bool {
return wrapper.by(&wrapper.fmtMetricData.Result[i], &wrapper.fmtMetricData.Result[j])
}
func (wrapper FormatedMetricDataWrapper) Swap(i, j int) {
wrapper.fmtMetricData.Result[i], wrapper.fmtMetricData.Result[j] = wrapper.fmtMetricData.Result[j], wrapper.fmtMetricData.Result[i]
}
// sorted metric by ascending or descending order
func (rawMetrics *Response) SortBy(sortMetricName string, sortType string) (*Response, int) {
defer func() {
if err := recover(); err != nil {
klog.Errorln(err)
debug.PrintStack()
}
}()
if sortMetricName == "" || rawMetrics == nil {
return rawMetrics, -1
}
// default sort type is descending order
if sortType == "" {
sortType = ResultSortTypeDesc
}
var currentResourceMap = make(map[string]int)
// {<Resource Name>: <Ordering>}
var indexMap = make(map[string]int)
i := 0
// each metricItem is the result for a specific metric name
// so we find the metricItem with sortMetricName, and sort it
for _, metricItem := range rawMetrics.Results {
// only vector type result can be sorted
if metricItem.Data.ResultType == ResultTypeVector && metricItem.Status == MetricStatusSuccess {
if metricItem.MetricName == sortMetricName {
if sortType == ResultSortTypeAsc {
// asc
sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *v1alpha2.QueryValue) bool {
value1 := p.Value
value2 := q.Value
v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64)
v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64)
if v1 == v2 {
resourceName1 := p.Metric[ResultItemMetricResourceName]
resourceName2 := q.Metric[ResultItemMetricResourceName]
return resourceName1 < resourceName2
}
return v1 < v2
}})
} else {
// desc
sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *v1alpha2.QueryValue) bool {
value1 := p.Value
value2 := q.Value
v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64)
v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64)
if v1 == v2 {
resourceName1 := p.Metric[ResultItemMetricResourceName]
resourceName2 := q.Metric[ResultItemMetricResourceName]
return resourceName1 > resourceName2
}
return v1 > v2
}})
}
for _, r := range metricItem.Data.Result {
// record the ordering of resource_name to indexMap
// example: {"metric":{ResultItemMetricResourceName: "Deployment:xxx"},"value":[1541142931.731,"3"]}
resourceName, exist := r.Metric[ResultItemMetricResourceName]
if exist && resourceName != "" {
if _, exist := indexMap[resourceName]; !exist {
indexMap[resourceName] = i
i = i + 1
}
}
}
}
// iterator all metric to find max metricItems length
for _, r := range metricItem.Data.Result {
k, ok := r.Metric[ResultItemMetricResourceName]
if ok && k != "" {
currentResourceMap[k] = 1
}
}
}
}
var keys []string
for k := range currentResourceMap {
keys = append(keys, k)
}
sort.Strings(keys)
for _, resource := range keys {
if _, exist := indexMap[resource]; !exist {
indexMap[resource] = i
i = i + 1
}
}
// sort other metric
for i := 0; i < len(rawMetrics.Results); i++ {
re := rawMetrics.Results[i]
if re.Data.ResultType == ResultTypeVector && re.Status == MetricStatusSuccess {
sortedMetric := make([]v1alpha2.QueryValue, len(indexMap))
for j := 0; j < len(re.Data.Result); j++ {
r := re.Data.Result[j]
k, exist := r.Metric[ResultItemMetricResourceName]
if exist && k != "" {
index, exist := indexMap[k]
if exist {
sortedMetric[index] = r
}
}
}
rawMetrics.Results[i].Data.Result = sortedMetric
}
}
return rawMetrics, len(indexMap)
}
func (fmtLevelMetric *Response) Page(pageNum string, limitNum string, maxLength int) *Response {
if maxLength <= 0 {
return fmtLevelMetric
}
// matrix type can not be sorted
for _, metricItem := range fmtLevelMetric.Results {
// if metric reterieved field, resultType: ""
if metricItem.Data.ResultType == ResultTypeMatrix {
return fmtLevelMetric
}
}
var page = DefaultPage
if pageNum != "" {
p, err := strconv.Atoi(pageNum)
if err != nil {
klog.Errorln(err)
} else {
if p > 0 {
page = p
}
}
} else {
// the default mode is none paging
return fmtLevelMetric
}
var limit = DefaultPageLimit
if limitNum != "" {
l, err := strconv.Atoi(limitNum)
if err != nil {
klog.Errorln(err)
} else {
if l > 0 {
limit = l
}
}
}
// the i page: [(page-1) * limit, (page) * limit - 1]
start := (page - 1) * limit
end := (page)*limit - 1
for i := 0; i < len(fmtLevelMetric.Results); i++ {
// only pageing when result type is `vector` and result status is `success`
if fmtLevelMetric.Results[i].Data.ResultType != ResultTypeVector || fmtLevelMetric.Results[i].Status != MetricStatusSuccess {
continue
}
resultLen := len(fmtLevelMetric.Results[i].Data.Result)
if start >= resultLen {
fmtLevelMetric.Results[i].Data.Result = nil
continue
}
if end >= resultLen {
end = resultLen - 1
}
slice := fmtLevelMetric.Results[i].Data.Result[start : end+1]
fmtLevelMetric.Results[i].Data.Result = slice
}
allPage := int(math.Ceil(float64(maxLength) / float64(limit)))
// add page fields
fmtLevelMetric.CurrentPage = page
fmtLevelMetric.TotalItem = maxLength
fmtLevelMetric.TotalPage = allPage
return fmtLevelMetric
}
func getNodeAddressAndRole(nodeName string) (string, string) {
nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister()
node, err := nodeLister.Get(nodeName)
if err != nil {
return "", ""
}
var addr string
for _, address := range node.Status.Addresses {
if address.Type == "InternalIP" {
addr = address.Address
break
}
}
role := "node"
_, exists := node.Labels["node-role.kubernetes.io/master"]
if exists {
role = "master"
}
return addr, role
}
func getNodeName(nodeIp string) string {
nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister()
nodes, _ := nodeLister.List(labels.Everything())
for _, node := range nodes {
for _, address := range node.Status.Addresses {
if address.Type == "InternalIP" && address.Address == nodeIp {
return node.Name
}
}
}
return ""
}