|
- /*
- Copyright 2021 The KubeEdge 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 trigger
-
- import (
- "fmt"
- "math"
- "reflect"
- "strconv"
- "strings"
- "time"
- )
-
- type Base interface {
- Trigger(stats map[string]interface{}) bool
- }
-
- type BinaryTrigger struct {
- Operator string
- Metric string
- Threshold float64
- }
-
- func convertFloat(v interface{}) (float64, error) {
- var realValue float64
- var err error
- switch v := v.(type) {
- case int:
- realValue = float64(v)
- case float32:
- realValue = float64(v)
- case float64:
- realValue = v
- case string:
- realValue, err = strconv.ParseFloat(v, 64)
- }
- return realValue, err
- }
-
- func (bt *BinaryTrigger) Trigger(stats map[string]interface{}) bool {
- var value interface{}
- var ok bool
- left := strings.Index(bt.Metric, "[")
- if left == -1 {
- value, ok = stats[bt.Metric]
- if !ok {
- return false
- }
- } else {
- // e.g. metric = precisions[3]
- right := strings.LastIndex(bt.Metric, "]")
- metric := strings.TrimSpace(bt.Metric[:left])
- topValue, ok := stats[metric]
- if !ok {
- return false
- }
-
- // only support the integer index
- subMetric := bt.Metric[left+1 : right]
- idx, err := strconv.Atoi(subMetric)
- if err != nil {
- return false
- }
-
- s := reflect.ValueOf(topValue)
- switch s.Kind() {
- case reflect.Slice, reflect.Array:
- default:
- return false
- }
-
- if !ok {
- return false
- }
-
- if idx >= s.Len() {
- return false
- }
- value = s.Index(idx).Interface()
- }
- realValue, err := convertFloat(value)
- if err != nil {
- return false
- }
-
- isEqual := math.Abs(realValue-bt.Threshold) < 1e-6
-
- switch bt.Operator {
- case "gt", ">":
- return !isEqual && realValue > bt.Threshold
- case "ge", ">=":
- return isEqual || realValue >= bt.Threshold
- case "eq", "=", "==":
- return isEqual
- case "ne", "!=":
- return !isEqual
- case "le", "<=":
- return isEqual || realValue <= bt.Threshold
- case "lt", "<":
- return !isEqual && realValue < bt.Threshold
- default:
- return false
- }
- }
-
- type TimerRangeTrigger struct {
- Start string
- End string
- Type string
- }
-
- func (tt *TimerRangeTrigger) Trigger(stats map[string]interface{}) bool {
- now := time.Now()
- start := tt.Start
- end := tt.End
- // now only support the 'daily' type
- var format string
- switch tt.Type {
- case "daily":
- default:
- format = "15:04"
- }
-
- v := now.Format(format)
- if start > end {
- // for daily type: [23:00, 01:00]
- return start <= v || v <= end
- }
-
- // for daily type: [01:00, 02:00]
- return start <= v && v <= end
- }
-
- type AndTrigger struct {
- Triggers []Base
- }
-
- func (at *AndTrigger) Trigger(stats map[string]interface{}) bool {
- for _, t := range at.Triggers {
- if !t.Trigger(stats) {
- return false
- }
- }
- return true
- }
-
- func newAndTrigger(triggers ...Base) *AndTrigger {
- var valid []Base
- for _, t := range triggers {
- if t != nil {
- valid = append(valid, t)
- }
- }
- return &AndTrigger{
- Triggers: valid,
- }
- }
-
- func NewTrigger(trigger map[string]interface{}) (Base, error) {
- var err error
- checkPeriodSeconds, ok := trigger["checkPeriodSeconds"]
- if !ok {
- checkPeriodSeconds = 60
- }
- _ = checkPeriodSeconds
- condVal, ok := trigger["condition"]
- var conditionTrigger Base
- if ok {
- cond, ok := condVal.(map[string]interface{})
- if !ok {
- return nil, fmt.Errorf("invalid condition value:%v",
- condVal)
- }
-
- threshold, err := convertFloat(cond["threshold"])
-
- if err != nil {
- return nil, fmt.Errorf("invalid threshold value:%v", cond["threshold"])
- }
- conditionTrigger = &BinaryTrigger{
- Operator: cond["operator"].(string),
- Metric: cond["metric"].(string),
- Threshold: threshold,
- }
- }
- var timerTrigger Base
- _, ok = trigger["timer"]
- if ok {
- timer := make(map[string]string)
- switch t := trigger["timer"].(type) {
- case map[string]interface{}:
- for k, v := range t {
- timer[k] = v.(string)
- }
- case map[string]string:
- for k, v := range t {
- timer[k] = v
- }
- default:
- err = fmt.Errorf("invalid timer %v", trigger["timer"])
- }
- timerTrigger = &TimerRangeTrigger{
- Start: timer["start"],
- End: timer["end"],
- Type: timer["type"],
- }
- }
- if err != nil {
- return nil, err
- }
- return newAndTrigger(timerTrigger, conditionTrigger), err
- }
|