refactor monitoring (#1751)

Signed-off-by: huanggze <loganhuang@yunify.com>
This commit is contained in:
Guangzhe Huang
2020-03-07 12:34:52 +08:00
committed by GitHub
parent 6c6bfb2677
commit 148a804726
30 changed files with 1606 additions and 2073 deletions

View File

@@ -0,0 +1,69 @@
/*
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 monitoring
import (
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"time"
)
type MonitoringOperator interface {
GetMetrics(stmts []string, time time.Time) (v1alpha2.APIResponse, error)
GetMetricsOverTime(stmts []string, start, end time.Time, step time.Duration) (v1alpha2.APIResponse, error)
GetNamedMetrics(time time.Time, opt monitoring.QueryOption) (v1alpha2.APIResponse, error)
GetNamedMetricsOverTime(start, end time.Time, step time.Duration, opt monitoring.QueryOption) (v1alpha2.APIResponse, error)
SortMetrics(raw v1alpha2.APIResponse, target, order, identifier string) (v1alpha2.APIResponse, int)
PageMetrics(raw v1alpha2.APIResponse, page, limit, rows int) v1alpha2.APIResponse
}
type monitoringOperator struct {
c monitoring.Interface
}
func NewMonitoringOperator(client monitoring.Interface) MonitoringOperator {
return &monitoringOperator{client}
}
// TODO(huanggze): reserve for custom monitoring
func (mo monitoringOperator) GetMetrics(stmts []string, time time.Time) (v1alpha2.APIResponse, error) {
panic("implement me")
}
// TODO(huanggze): reserve for custom monitoring
func (mo monitoringOperator) GetMetricsOverTime(stmts []string, start, end time.Time, step time.Duration) (v1alpha2.APIResponse, error) {
panic("implement me")
}
func (mo monitoringOperator) GetNamedMetrics(time time.Time, opt monitoring.QueryOption) (v1alpha2.APIResponse, error) {
metrics, err := mo.c.GetNamedMetrics(time, opt)
if err != nil {
klog.Error(err)
}
return v1alpha2.APIResponse{Results: metrics}, err
}
func (mo monitoringOperator) GetNamedMetricsOverTime(start, end time.Time, step time.Duration, opt monitoring.QueryOption) (v1alpha2.APIResponse, error) {
metrics, err := mo.c.GetNamedMetricsOverTime(start, end, step, opt)
if err != nil {
klog.Error(err)
}
return v1alpha2.APIResponse{Results: metrics}, err
}

View File

@@ -0,0 +1,64 @@
/*
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 monitoring
import "k8s.io/api/core/v1"
// TODO(wansir): Can we decouple this part from monitoring module, since the project structure has been changed
func GetNamespacesWithMetrics(namespaces []*v1.Namespace) []*v1.Namespace {
// var nsNameList []string
// for i := range namespaces {
// nsNameList = append(nsNameList, namespaces[i].Name)
// }
// nsFilter := "^(" + strings.Join(nsNameList, "|") + ")$"
//
// now := time.Now()
// opt := &monitoring.QueryOptions{
// Level: monitoring.MetricsLevelNamespace,
// ResourcesFilter: nsFilter,
// Start: now,
// End: now,
// MetricsFilter: "namespace_cpu_usage|namespace_memory_usage_wo_cache|namespace_pod_count",
// }
//
// gm, err := monitoring.Get(opt)
// if err != nil {
// klog.Error(err)
// return namespaces
// }
//
// for _, m := range gm.Results {
// for _, v := range m.Data.MetricsValues {
// ns, exist := v.Metadata["namespace"]
// if !exist {
// continue
// }
//
// for _, item := range namespaces {
// if item.Name == ns {
// if item.Annotations == nil {
// item.Annotations = make(map[string]string, 0)
// }
// item.Annotations[m.MetricsName] = strconv.FormatFloat(v.Sample[1], 'f', -1, 64)
// }
// }
// }
// }
//
return namespaces
}

View File

@@ -0,0 +1,204 @@
/*
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 monitoring
import (
"kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2"
"kubesphere.io/kubesphere/pkg/simple/client/monitoring"
"math"
"sort"
)
// TODO(huanggze): the id value is dependent of Prometheus label-value pair (i.e. label_kubesphere_io_workspace). We should regulate the naming convention.
const (
IdentifierNode = "node"
IdentifierWorkspace = "label_kubesphere_io_workspace"
IdentifierNamespace = "namespace"
IdentifierWorkload = "workload"
IdentifierPod = "pod"
IdentifierContainer = "container"
IdentifierPVC = "persistentvolumeclaim"
OrderAscending = "asc"
OrderDescending = "desc"
)
type wrapper struct {
monitoring.MetricData
by func(p, q *monitoring.MetricValue) bool
}
func (w wrapper) Len() int {
return len(w.MetricValues)
}
func (w wrapper) Less(i, j int) bool {
return w.by(&w.MetricValues[i], &w.MetricValues[j])
}
func (w wrapper) Swap(i, j int) {
w.MetricValues[i], w.MetricValues[j] = w.MetricValues[j], w.MetricValues[i]
}
// The sortMetrics sorts a group of resources by a given metric
// Example:
//
// before sorting
// |------| Metric 1 | Metric 2 | Metric 3 |
// | ID a | 1 | XL | |
// | ID b | 1 | S | |
// | ID c | 3 | M | |
//
// sort by metrics_2
// |------| Metric 1 | Metric 2 (asc) | Metric 3 |
// | ID a | 1 | XL | |
// | ID c | 3 | M | |
// | ID b | 1 | S | |
//
// ranking can only be applied to instant query results, not range query
func (mo monitoringOperator) SortMetrics(raw v1alpha2.APIResponse, target, order, identifier string) (v1alpha2.APIResponse, int) {
if target == "" || len(raw.Results) == 0 {
return raw, -1
}
if order == "" {
order = OrderDescending
}
var currentResourceMap = make(map[string]int)
// resource-ordinal map
var indexMap = make(map[string]int)
i := 0
for _, item := range raw.Results {
if item.MetricType == monitoring.MetricTypeVector && item.Status == monitoring.StatusSuccess {
if item.MetricName == target {
if order == OrderAscending {
sort.Sort(wrapper{item.MetricData, func(p, q *monitoring.MetricValue) bool {
if p.Sample[1] == q.Sample[1] {
return p.Metadata[identifier] < q.Metadata[identifier]
}
return p.Sample[1] < q.Sample[1]
}})
} else {
sort.Sort(wrapper{item.MetricData, func(p, q *monitoring.MetricValue) bool {
if p.Sample[1] == q.Sample[1] {
return p.Metadata[identifier] > q.Metadata[identifier]
}
return p.Sample[1] > q.Sample[1]
}})
}
for _, r := range item.MetricValues {
// record the ordinal of resource to indexMap
resourceName, exist := r.Metadata[identifier]
if exist {
if _, exist := indexMap[resourceName]; !exist {
indexMap[resourceName] = i
i = i + 1
}
}
}
}
// get total number of rows
for _, r := range item.MetricValues {
k, ok := r.Metadata[identifier]
if ok {
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 metrics
for i := 0; i < len(raw.Results); i++ {
item := raw.Results[i]
if item.MetricType == monitoring.MetricTypeVector && item.Status == monitoring.StatusSuccess {
sortedMetric := make([]monitoring.MetricValue, len(indexMap))
for j := 0; j < len(item.MetricValues); j++ {
r := item.MetricValues[j]
k, exist := r.Metadata[identifier]
if exist {
index, exist := indexMap[k]
if exist {
sortedMetric[index] = r
}
}
}
raw.Results[i].MetricValues = sortedMetric
}
}
return raw, len(indexMap)
}
func (mo monitoringOperator) PageMetrics(raw v1alpha2.APIResponse, page, limit, rows int) v1alpha2.APIResponse {
if page <= 0 || limit <= 0 || rows <= 0 || len(raw.Results) == 0 {
return raw
}
// matrix type can not be sorted
for _, item := range raw.Results {
if item.MetricType != monitoring.MetricTypeVector {
return raw
}
}
// the i page: [(page-1) * limit, (page) * limit - 1]
start := (page - 1) * limit
end := (page)*limit - 1
for i := 0; i < len(raw.Results); i++ {
if raw.Results[i].MetricType != monitoring.MetricTypeVector || raw.Results[i].Status != monitoring.StatusSuccess {
continue
}
resultLen := len(raw.Results[i].MetricValues)
if start >= resultLen {
raw.Results[i].MetricValues = nil
continue
}
if end >= resultLen {
end = resultLen - 1
}
slice := raw.Results[i].MetricValues[start : end+1]
raw.Results[i].MetricValues = slice
}
raw.CurrentPage = page
raw.TotalPage = int(math.Ceil(float64(rows) / float64(limit)))
raw.TotalItem = rows
return raw
}