mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
改进:增加schedpolicy和dynamicschedtag支持
This commit is contained in:
@@ -0,0 +1,574 @@
|
||||
package conditionparser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"fmt"
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidOp = errors.New("invalid operation")
|
||||
ErrFieldNotFound = errors.New("field not found")
|
||||
ErrOutOfIndex = errors.New("out of index")
|
||||
)
|
||||
|
||||
func IsValid(exprStr string) bool {
|
||||
_, err := parser.ParseExpr(exprStr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Eval(exprStr string, input interface{}) (bool, error) {
|
||||
expr, err := parser.ParseExpr(exprStr)
|
||||
if err != nil {
|
||||
log.Errorf("parse expr %s error %s", exprStr, err)
|
||||
return false, err
|
||||
}
|
||||
result, err := eval(expr, input)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch result.(type) {
|
||||
case bool, *jsonutils.JSONBool:
|
||||
return getBool(result), nil
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrX := getArray(result)
|
||||
for i := 0; i < len(arrX); i += 1 {
|
||||
val := getBool(arrX[i])
|
||||
if val {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func eval(expr ast.Expr, input interface{}) (interface{}, error) {
|
||||
switch expr.(type) {
|
||||
case *ast.BinaryExpr:
|
||||
return evalBinary(expr.(*ast.BinaryExpr), input)
|
||||
case *ast.UnaryExpr:
|
||||
return evalUnary(expr.(*ast.UnaryExpr), input)
|
||||
case *ast.SelectorExpr:
|
||||
return evalSelector(expr.(*ast.SelectorExpr), input)
|
||||
case *ast.Ident:
|
||||
return evalIdent(expr.(*ast.Ident), input)
|
||||
case *ast.ParenExpr:
|
||||
return evalParen(expr.(*ast.ParenExpr), input)
|
||||
case *ast.CallExpr:
|
||||
return evalCall(expr.(*ast.CallExpr), input)
|
||||
case *ast.IndexExpr:
|
||||
return evalIndex(expr.(*ast.IndexExpr), input)
|
||||
case *ast.BasicLit:
|
||||
return evalBasicLit(expr.(*ast.BasicLit), input)
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalBasicLit(expr *ast.BasicLit, input interface{}) (interface{}, error) {
|
||||
switch expr.Kind {
|
||||
case token.IDENT:
|
||||
switch input.(type) {
|
||||
case *jsonutils.JSONDict:
|
||||
jsonX := input.(*jsonutils.JSONDict)
|
||||
return jsonX.Get(expr.Value)
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case token.INT:
|
||||
return strconv.Atoi(expr.Value)
|
||||
case token.FLOAT:
|
||||
return strconv.ParseFloat(expr.Value, 64)
|
||||
case token.CHAR:
|
||||
return expr.Value[1 : len(expr.Value)-1][0], nil
|
||||
case token.STRING:
|
||||
return expr.Value[1 : len(expr.Value)-1], nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalIndex(expr *ast.IndexExpr, input interface{}) (interface{}, error) {
|
||||
X, err := eval(expr.X, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexV, err := eval(expr.Index, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var indexI int64
|
||||
switch indexV.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
|
||||
indexI = reflect.ValueOf(indexV).Int()
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
switch X.(type) {
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrX := getArray(X)
|
||||
if indexI >= 0 && indexI < int64(len(arrX)) {
|
||||
return arrX[indexI], nil
|
||||
} else {
|
||||
return nil, ErrOutOfIndex
|
||||
}
|
||||
case string:
|
||||
strX := X.(string)
|
||||
if indexI >= 0 && indexI < int64(len(strX)) {
|
||||
return strX[indexI], nil
|
||||
} else {
|
||||
return nil, ErrOutOfIndex
|
||||
}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func args2Strings(args []interface{}) ([]string, error) {
|
||||
strs := make([]string, len(args))
|
||||
for i := 0; i < len(args); i += 1 {
|
||||
var ok bool
|
||||
strs[i], ok = args[i].(string)
|
||||
if !ok {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
return strs, nil
|
||||
}
|
||||
|
||||
func evalCallInternal(funcV interface{}, args []interface{}) (interface{}, error) {
|
||||
switch funcV.(type) {
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrFuncV := getArray(funcV)
|
||||
ret := make([]interface{}, len(arrFuncV))
|
||||
for i := 0; i < len(arrFuncV); i += 1 {
|
||||
reti, err := evalCallInternal(arrFuncV[i], args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = reti
|
||||
}
|
||||
return ret, nil
|
||||
case *funcCaller:
|
||||
caller := funcV.(*funcCaller)
|
||||
switch caller.caller.(type) {
|
||||
case string:
|
||||
strX := caller.caller.(string)
|
||||
strArgs, err := args2Strings(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch caller.method {
|
||||
case "startswith":
|
||||
if len(strArgs) != 1 {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
return strings.HasPrefix(strX, strArgs[0]), nil
|
||||
case "endswith":
|
||||
if len(strArgs) != 1 {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
return strings.HasSuffix(strX, strArgs[0]), nil
|
||||
case "contains":
|
||||
if len(strArgs) != 1 {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
return strings.Contains(strX, strArgs[0]), nil
|
||||
case "in":
|
||||
return utils.IsInStringArray(strX, strArgs), nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalCall(expr *ast.CallExpr, input interface{}) (interface{}, error) {
|
||||
funcV, err := eval(expr.Fun, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args := make([]interface{}, len(expr.Args))
|
||||
for i := 0; i < len(expr.Args); i += 1 {
|
||||
args[i], err = eval(expr.Args[i], input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return evalCallInternal(funcV, args)
|
||||
}
|
||||
|
||||
func evalParen(expr *ast.ParenExpr, input interface{}) (interface{}, error) {
|
||||
return eval(expr.X, input)
|
||||
}
|
||||
|
||||
func getJSONProperty(json *jsonutils.JSONDict, identStr string) (jsonutils.JSONObject, error) {
|
||||
if json.Contains(identStr) {
|
||||
return json.Get(identStr)
|
||||
} else if json.Contains(fmt.Sprintf("%s.0", identStr)) {
|
||||
idx := 0
|
||||
jsonArray := jsonutils.NewArray()
|
||||
for {
|
||||
obj, _ := json.Get(fmt.Sprintf("%s.%d", identStr, idx))
|
||||
if obj != nil {
|
||||
jsonArray.Add(obj)
|
||||
idx += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return jsonArray, nil
|
||||
} else {
|
||||
return nil, ErrFieldNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func evalIdent(expr *ast.Ident, input interface{}) (interface{}, error) {
|
||||
if expr.Obj == nil || input == nil {
|
||||
return expr.Name, nil
|
||||
} else {
|
||||
switch input.(type) {
|
||||
case *jsonutils.JSONDict:
|
||||
json := input.(*jsonutils.JSONDict)
|
||||
return getJSONProperty(json, expr.Name)
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type funcCaller struct {
|
||||
caller interface{}
|
||||
method string
|
||||
}
|
||||
|
||||
func getArray(X interface{}) []interface{} {
|
||||
switch X.(type) {
|
||||
case *jsonutils.JSONArray:
|
||||
arr := X.(*jsonutils.JSONArray)
|
||||
ret := make([]interface{}, arr.Size())
|
||||
for i := 0; i < arr.Size(); i += 1 {
|
||||
ret[i], _ = arr.GetAt(i)
|
||||
}
|
||||
return ret
|
||||
default:
|
||||
return X.([]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
func evalSelectorInternal(X interface{}, identStr string) (interface{}, error) {
|
||||
switch X.(type) {
|
||||
case *jsonutils.JSONDict:
|
||||
json := X.(*jsonutils.JSONDict)
|
||||
return getJSONProperty(json, identStr)
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrX := getArray(X)
|
||||
ret := make([]interface{}, len(arrX))
|
||||
for i := 0; i < len(arrX); i += 1 {
|
||||
reti, err := evalSelectorInternal(arrX[i], identStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = reti
|
||||
}
|
||||
return ret, nil
|
||||
case string, *jsonutils.JSONString:
|
||||
return &funcCaller{caller: getString(X), method: identStr}, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalSelector(expr *ast.SelectorExpr, input interface{}) (interface{}, error) {
|
||||
X, err := eval(expr.X, input)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
ident, err := evalIdent(expr.Sel, nil)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
identStr := ident.(string)
|
||||
|
||||
return evalSelectorInternal(X, identStr)
|
||||
}
|
||||
|
||||
func evalUnaryInternal(X interface{}, op token.Token) (interface{}, error) {
|
||||
switch X.(type) {
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrX := getArray(X)
|
||||
ret := make([]interface{}, len(arrX))
|
||||
for i := 0; i < len(arrX); i += 1 {
|
||||
reti, err := evalUnaryInternal(arrX[i], op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = reti
|
||||
}
|
||||
return ret, nil
|
||||
case bool, *jsonutils.JSONBool:
|
||||
boolX := getBool(X)
|
||||
switch op {
|
||||
case token.NOT:
|
||||
return !boolX, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
||||
intX := getInt(X)
|
||||
switch op {
|
||||
case token.SUB:
|
||||
return -intX, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case float32, float64, *jsonutils.JSONFloat:
|
||||
floatX := getFloat(X)
|
||||
switch op {
|
||||
case token.SUB:
|
||||
return -floatX, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalUnary(expr *ast.UnaryExpr, input interface{}) (interface{}, error) {
|
||||
X, err := eval(expr.X, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
op := expr.Op
|
||||
return evalUnaryInternal(X, op)
|
||||
}
|
||||
|
||||
func getString(val interface{}) string {
|
||||
switch val.(type) {
|
||||
case *jsonutils.JSONString:
|
||||
jsonVal, _ := val.(*jsonutils.JSONString).GetString()
|
||||
return jsonVal
|
||||
default:
|
||||
return val.(string)
|
||||
}
|
||||
}
|
||||
|
||||
func getBool(val interface{}) bool {
|
||||
switch val.(type) {
|
||||
case *jsonutils.JSONBool:
|
||||
jsonVal, _ := val.(*jsonutils.JSONBool).Bool()
|
||||
return jsonVal
|
||||
default:
|
||||
return val.(bool)
|
||||
}
|
||||
}
|
||||
|
||||
func getInt(val interface{}) int64 {
|
||||
switch val.(type) {
|
||||
case *jsonutils.JSONInt:
|
||||
jsonVal, _ := val.(*jsonutils.JSONInt).Int()
|
||||
return jsonVal
|
||||
default:
|
||||
return reflect.ValueOf(val).Int()
|
||||
}
|
||||
}
|
||||
|
||||
func getFloat(val interface{}) float64 {
|
||||
switch val.(type) {
|
||||
case *jsonutils.JSONFloat:
|
||||
jsonVal, _ := val.(*jsonutils.JSONFloat).Float()
|
||||
return jsonVal
|
||||
default:
|
||||
return reflect.ValueOf(val).Float()
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryInternal(X, Y interface{}, op token.Token) (interface{}, error) {
|
||||
switch X.(type) {
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrX := getArray(X)
|
||||
ret := make([]interface{}, len(arrX))
|
||||
for i := 0; i < len(arrX); i += 1 {
|
||||
reti, err := evalBinaryInternal(arrX[i], Y, op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = reti
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
switch Y.(type) {
|
||||
case []interface{}, *jsonutils.JSONArray:
|
||||
arrY := getArray(Y)
|
||||
ret := make([]interface{}, len(arrY))
|
||||
for i := 0; i < len(arrY); i += 1 {
|
||||
reti, err := evalBinaryInternal(X, arrY[i], op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret[i] = reti
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
switch X.(type) {
|
||||
case bool, *jsonutils.JSONBool:
|
||||
switch Y.(type) {
|
||||
case bool, *jsonutils.JSONBool:
|
||||
boolX := getBool(X)
|
||||
boolY := getBool(Y)
|
||||
switch op {
|
||||
case token.LAND:
|
||||
return boolX && boolY, nil
|
||||
case token.LOR:
|
||||
return boolX || boolY, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case string, *jsonutils.JSONString:
|
||||
switch Y.(type) {
|
||||
case string, *jsonutils.JSONString:
|
||||
strX := getString(X)
|
||||
strY := getString(Y)
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return strX + strY, nil
|
||||
case token.EQL:
|
||||
return strX == strY, nil
|
||||
case token.NEQ:
|
||||
return strX != strY, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
||||
intX := getInt(X)
|
||||
switch Y.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
||||
intY := getInt(Y)
|
||||
return evalIntegerOp(intX, intY, op)
|
||||
case float32, float64, jsonutils.JSONFloat:
|
||||
floatX := float64(intX)
|
||||
floatY := getFloat(Y)
|
||||
return evalFloatOp(floatX, floatY, op)
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
case float32, float64, *jsonutils.JSONFloat:
|
||||
floatX := getFloat(X)
|
||||
switch Y.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, *jsonutils.JSONInt:
|
||||
floatY := float64(getInt(Y))
|
||||
return evalFloatOp(floatX, floatY, op)
|
||||
case float32, float64, *jsonutils.JSONFloat:
|
||||
floatY := getFloat(Y)
|
||||
return evalFloatOp(floatX, floatY, op)
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinary(bExpr *ast.BinaryExpr, input interface{}) (interface{}, error) {
|
||||
X, err := eval(bExpr.X, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Y, err := eval(bExpr.Y, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return evalBinaryInternal(X, Y, bExpr.Op)
|
||||
}
|
||||
|
||||
func evalIntegerOp(X, Y int64, op token.Token) (interface{}, error) {
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return X + Y, nil
|
||||
case token.SUB:
|
||||
return X - Y, nil
|
||||
case token.MUL:
|
||||
return X * Y, nil
|
||||
case token.QUO:
|
||||
return X / Y, nil
|
||||
case token.REM:
|
||||
return X % Y, nil
|
||||
case token.AND:
|
||||
return X & Y, nil
|
||||
case token.OR:
|
||||
return X | Y, nil
|
||||
case token.XOR:
|
||||
return X ^ Y, nil
|
||||
case token.SHL:
|
||||
return X << uint64(Y), nil
|
||||
case token.SHR:
|
||||
return X >> uint64(Y), nil
|
||||
case token.AND_NOT:
|
||||
return X &^ Y, nil
|
||||
case token.EQL:
|
||||
return X == Y, nil
|
||||
case token.LSS:
|
||||
return X < Y, nil
|
||||
case token.GTR:
|
||||
return X > Y, nil
|
||||
case token.NEQ:
|
||||
return X != Y, nil
|
||||
case token.LEQ:
|
||||
return X <= Y, nil
|
||||
case token.GEQ:
|
||||
return X >= Y, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
|
||||
func evalFloatOp(X, Y float64, op token.Token) (interface{}, error) {
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return X + Y, nil
|
||||
case token.SUB:
|
||||
return X - Y, nil
|
||||
case token.MUL:
|
||||
return X * Y, nil
|
||||
case token.QUO:
|
||||
return X / Y, nil
|
||||
case token.EQL:
|
||||
return X == Y, nil
|
||||
case token.LSS:
|
||||
return X < Y, nil
|
||||
case token.GTR:
|
||||
return X > Y, nil
|
||||
case token.NEQ:
|
||||
return X != Y, nil
|
||||
case token.LEQ:
|
||||
return X <= Y, nil
|
||||
case token.GEQ:
|
||||
return X >= Y, nil
|
||||
default:
|
||||
return nil, ErrInvalidOp
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user