123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805 |
- /*
- * ECAL
- *
- * Copyright 2020 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- package engine
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "regexp"
- "sort"
- "strings"
- "sync"
- "devt.de/krotik/common/errorutil"
- "devt.de/krotik/common/sortutil"
- )
- /*
- Rule models a matching rule for event receivers (actions). A rule has 3 possible
- matching criteria:
- - Match on event kinds: A list of strings in dot notation which describes event kinds. May
- contain '*' characters as wildcards (e.g. core.tests.*).
- - Match on event cascade scope: A list of strings in dot notation which describe the
- required scopes of an event cascade.
- - Match on event state: A simple list of required key / value states in the event
- state. Nil values can be used as wildcards (i.e. match is only on key).
- Rules have priorities (0 being the highest) and may suppress each other.
- */
- type Rule struct {
- Name string // Name of the rule
- Desc string // Description of the rule (optional)
- KindMatch []string // Match on event kinds
- ScopeMatch []string // Match on event cascade scope
- StateMatch map[string]interface{} // Match on event state
- Priority int // Priority of the rule
- SuppressionList []string // List of suppressed rules by this rule
- Action RuleAction // Action of the rule
- }
- /*
- CopyAs returns a shallow copy of this rule with a new name.
- */
- func (r *Rule) CopyAs(newName string) *Rule {
- return &Rule{
- Name: newName,
- Desc: r.Desc,
- KindMatch: r.KindMatch,
- ScopeMatch: r.ScopeMatch,
- StateMatch: r.StateMatch,
- Priority: r.Priority,
- SuppressionList: r.SuppressionList,
- Action: r.Action,
- }
- }
- func (r *Rule) String() string {
- sm, _ := json.Marshal(r.StateMatch)
- return fmt.Sprintf("Rule:%s [%s] (Priority:%v Kind:%v Scope:%v StateMatch:%s Suppress:%v)",
- r.Name, strings.TrimSpace(r.Desc), r.Priority, r.KindMatch, r.ScopeMatch, sm, r.SuppressionList)
- }
- /*
- RuleAction is an action which is executed by a matching rule. The action gets
- a unique thread ID from the executing thread.
- */
- type RuleAction func(p Processor, m Monitor, e *Event, tid uint64) error
- /*
- RuleIndex is an index for rules. It takes the form of a tree structure in which
- incoming events are matched level by level (e.g. event of kind core.task1.step1
- is first matched by kind "core" then "task1" and then "step1". At the leaf of
- the index tree it may then be matched on a state condition).
- */
- type RuleIndex interface {
- /*
- AddRule adds a new rule to the index.
- */
- AddRule(rule *Rule) error
- /*
- IsTriggering checks if a given event triggers a rule in this index.
- */
- IsTriggering(event *Event) bool
- /*
- Match returns all rules in this index which match a given event. This
- method does a full matching check including state matching.
- */
- Match(event *Event) []*Rule
- /*
- String returns a string representation of this rule index and all subindexes.
- */
- String() string
- /*
- Rules returns all rules with the given prefix in the name. Use the empty
- string to return all rules.
- */
- Rules() map[string]*Rule
- }
- /*
- ruleSubIndex is a sub index used by a rule index.
- */
- type ruleSubIndex interface {
- /*
- type returns the type of the rule sub index.
- */
- Type() string
- /*
- addRuleAtLevel adds a new rule to the index at a specific level. The
- level is described by a part of the rule kind match.
- */
- addRuleAtLevel(rule *Rule, kindMatchLevel []string)
- /*
- isTriggeringAtLevel checks if a given event triggers a rule at the given
- level of the index.
- */
- isTriggeringAtLevel(event *Event, level int) bool
- /*
- matchAtLevel returns all rules in this index which match a given event
- at the given level. This method does a full matching check including
- state matching.
- */
- matchAtLevel(event *Event, level int) []*Rule
- /*
- stringIndent returns a string representation with a given indentation of this
- rule index and all subindexes.
- */
- stringIndent(indent string) string
- }
- /*
- ruleIndexRoot models the index root node.
- */
- type ruleIndexRoot struct {
- *RuleIndexKind
- rules map[string]*Rule
- }
- /*
- AddRule adds a new rule to the index.
- */
- func (r *ruleIndexRoot) AddRule(rule *Rule) error {
- if _, ok := r.rules[rule.Name]; ok {
- return fmt.Errorf("Cannot add rule %v twice", rule.Name)
- }
- r.rules[rule.Name] = rule
- return r.RuleIndexKind.AddRule(rule)
- }
- /*
- Rules returns all rules with the given prefix in the name. Use the empty
- string to return all rules.
- */
- func (r *ruleIndexRoot) Rules() map[string]*Rule {
- return r.rules
- }
- /*
- NewRuleIndex creates a new rule container for efficient event matching.
- */
- func NewRuleIndex() RuleIndex {
- return &ruleIndexRoot{newRuleIndexKind(), make(map[string]*Rule)}
- }
- /*
- Rule index types
- */
- const (
- typeRuleIndexKind = "RuleIndexKind"
- typeRuleIndexState = "RuleIndexState"
- typeRuleIndexAll = "RuleIndexAll"
- )
- // Rule Index Kind
- // ===============
- /*
- RuleIndexKind data structure.
- */
- type RuleIndexKind struct {
- id uint64 // Id of this rule index
- kindAllMatch []ruleSubIndex // Rules with target all events of a specific category
- kindSingleMatch map[string][]ruleSubIndex // Rules which target specific event kinds
- count int // Number of loaded rules
- }
- /*
- newRuleIndexKind creates a new rule index matching on event kind.
- */
- func newRuleIndexKind() *RuleIndexKind {
- return &RuleIndexKind{
- newRuleIndexID(),
- make([]ruleSubIndex, 0),
- make(map[string][]ruleSubIndex),
- 0,
- }
- }
- /*
- Type returns the type of the rule sub index.
- */
- func (ri *RuleIndexKind) Type() string {
- return typeRuleIndexKind
- }
- /*
- AddRule adds a new rule to the index.
- */
- func (ri *RuleIndexKind) AddRule(rule *Rule) error {
- // Check essential rule attributes
- if rule.KindMatch == nil || len(rule.KindMatch) == 0 {
- return fmt.Errorf("Cannot add rule without a kind match: %v", rule.Name)
- } else if rule.ScopeMatch == nil {
- return fmt.Errorf("Cannot add rule without a scope match: %v", rule.Name)
- }
- // Add rule to the index for all kind matches
- for _, kindMatch := range rule.KindMatch {
- ri.addRuleAtLevel(rule, strings.Split(kindMatch, RuleKindSeparator))
- ri.count++
- }
- return nil
- }
- /*
- addRuleAtLevel adds a new rule to the index at a specific level. The
- level is described by a part of the rule kind match.
- */
- func (ri *RuleIndexKind) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
- var indexType string
- var index ruleSubIndex
- var ruleSubIndexList []ruleSubIndex
- var ok bool
- // Pick the right index type
- if len(kindMatchLevel) == 1 {
- if rule.StateMatch != nil {
- indexType = typeRuleIndexState
- } else {
- indexType = typeRuleIndexAll
- }
- } else {
- indexType = typeRuleIndexKind
- }
- // Get (create when necessary) a sub index of a specific type for the
- // match item of this level
- matchItem := kindMatchLevel[0]
- // Select the correct ruleSubIndexList
- if matchItem == RuleKindWildcard {
- ruleSubIndexList = ri.kindAllMatch
- } else {
- if ruleSubIndexList, ok = ri.kindSingleMatch[matchItem]; !ok {
- ruleSubIndexList = make([]ruleSubIndex, 0)
- ri.kindSingleMatch[matchItem] = ruleSubIndexList
- }
- }
- // Check if the required index is already existing
- for _, item := range ruleSubIndexList {
- if item.Type() == indexType {
- index = item
- break
- }
- }
- // Create a new index if no index was found
- if index == nil {
- switch indexType {
- case typeRuleIndexState:
- index = newRuleIndexState()
- case typeRuleIndexAll:
- index = newRuleIndexAll()
- case typeRuleIndexKind:
- index = newRuleIndexKind()
- }
- // Add the new index to the correct list
- if matchItem == RuleKindWildcard {
- ri.kindAllMatch = append(ruleSubIndexList, index)
- } else {
- ri.kindSingleMatch[matchItem] = append(ruleSubIndexList, index)
- }
- }
- // Recurse into the next level
- index.addRuleAtLevel(rule, kindMatchLevel[1:])
- }
- /*
- IsTriggering checks if a given event triggers a rule in this index.
- */
- func (ri *RuleIndexKind) IsTriggering(event *Event) bool {
- return ri.isTriggeringAtLevel(event, 0)
- }
- /*
- isTriggeringAtLevel checks if a given event triggers a rule at the given
- level of the index.
- */
- func (ri *RuleIndexKind) isTriggeringAtLevel(event *Event, level int) bool {
- // Check if the event kind is too general (e.g. rule is defined as a.b.c
- // and the event kind is a.b)
- if len(event.kind) <= level {
- return false
- }
- levelKind := event.kind[level]
- nextLevel := level + 1
- // Check rules targeting all events
- for _, index := range ri.kindAllMatch {
- if index.isTriggeringAtLevel(event, nextLevel) {
- return true
- }
- }
- // Check rules targeting specific events
- if ruleSubIndexList, ok := ri.kindSingleMatch[levelKind]; ok {
- for _, index := range ruleSubIndexList {
- if index.isTriggeringAtLevel(event, nextLevel) {
- return true
- }
- }
- }
- return false
- }
- /*
- Match returns all rules in this index which match a given event. This method
- does a full matching check including state matching.
- */
- func (ri *RuleIndexKind) Match(event *Event) []*Rule {
- return ri.matchAtLevel(event, 0)
- }
- /*
- matchAtLevel returns all rules in this index which match a given event
- at the given level. This method does a full matching check including
- state matching.
- */
- func (ri *RuleIndexKind) matchAtLevel(event *Event, level int) []*Rule {
- // Check if the event kind is too general (e.g. rule is defined as a.b.c
- // and the event kind is a.b)
- if len(event.kind) <= level {
- return nil
- }
- var ret []*Rule
- levelKind := event.kind[level]
- nextLevel := level + 1
- // Check rules targeting all events
- for _, index := range ri.kindAllMatch {
- ret = append(ret, index.matchAtLevel(event, nextLevel)...)
- }
- // Check rules targeting specific events
- if ruleSubIndexList, ok := ri.kindSingleMatch[levelKind]; ok {
- for _, index := range ruleSubIndexList {
- ret = append(ret, index.matchAtLevel(event, nextLevel)...)
- }
- }
- return ret
- }
- /*
- String returns a string representation of this rule index and all subindexes.
- */
- func (ri *RuleIndexKind) String() string {
- return ri.stringIndent("")
- }
- /*
- stringIndent returns a string representation with a given indentation of this
- rule index and all subindexes.
- */
- func (ri *RuleIndexKind) stringIndent(indent string) string {
- var buf bytes.Buffer
- newIndent := indent + " "
- writeIndexList := func(name string, indexList []ruleSubIndex) {
- if len(indexList) > 0 {
- buf.WriteString(fmt.Sprint(indent, name))
- buf.WriteString(fmt.Sprintf(" - %v (%v)\n", ri.Type(), ri.id))
- for _, index := range indexList {
- buf.WriteString(index.stringIndent(newIndent))
- }
- }
- }
- writeIndexList("*", ri.kindAllMatch)
- var keys []string
- for k := range ri.kindSingleMatch {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, key := range keys {
- indexList := ri.kindSingleMatch[key]
- writeIndexList(key, indexList)
- }
- return buf.String()
- }
- // Rule Index State
- // ================
- /*
- RuleMatcherKey is used for pure key - value state matches.
- */
- type RuleMatcherKey struct {
- bits uint64
- bitsAny uint64
- bitsValue map[interface{}]uint64
- bitsRegexes map[uint64]*regexp.Regexp
- }
- /*
- addRule adds a new rule to this key matcher.
- */
- func (rm *RuleMatcherKey) addRule(num uint, bit uint64, key string, value interface{}) {
- // Register rule bit
- rm.bits |= bit
- if value == nil {
- rm.bitsAny |= bit
- } else if regex, ok := value.(*regexp.Regexp); ok {
- // For regex match we add a bit to the any mask so the presence of
- // the key is checked before the actual regex is checked
- rm.bitsAny |= bit
- rm.bitsRegexes[bit] = regex
- } else {
- rm.bitsValue[value] |= bit
- }
- }
- /*
- match adds matching rules to a given bit mask.
- */
- func (rm *RuleMatcherKey) match(bits uint64, value interface{}) uint64 {
- toRemove := rm.bitsAny ^ rm.bits
- if value != nil {
- if additionalBits, ok := rm.bitsValue[value]; ok {
- toRemove = rm.bitsAny | additionalBits ^ rm.bits
- }
- }
- keyMatchedBits := bits ^ (bits & toRemove)
- for bm, r := range rm.bitsRegexes {
- if keyMatchedBits&bm > 0 && !r.MatchString(fmt.Sprint(value)) {
- // Regex does not match remove the bit
- keyMatchedBits ^= keyMatchedBits & bm
- }
- }
- return keyMatchedBits
- }
- /*
- unmatch removes all registered rules in this
- */
- func (rm *RuleMatcherKey) unmatch(bits uint64) uint64 {
- return bits ^ (bits & rm.bits)
- }
- /*
- String returns a string representation of this key matcher.
- */
- func (rm *RuleMatcherKey) String() string {
- var buf bytes.Buffer
- buf.WriteString(fmt.Sprintf("%08X *:%08X", rm.bits, rm.bitsAny))
- buf.WriteString(" [")
- var keys []interface{}
- for k := range rm.bitsValue {
- keys = append(keys, k)
- }
- sortutil.InterfaceStrings(keys)
- for _, k := range keys {
- m := rm.bitsValue[k]
- buf.WriteString(fmt.Sprintf("%v:%08X ", k, m))
- }
- buf.WriteString("] [")
- var rkeys []uint64
- for k := range rm.bitsRegexes {
- rkeys = append(rkeys, k)
- }
- sortutil.UInt64s(rkeys)
- for _, k := range rkeys {
- r := rm.bitsRegexes[k]
- buf.WriteString(fmt.Sprintf("%08X:%v ", k, r))
- }
- buf.WriteString("]")
- return buf.String()
- }
- /*
- RuleIndexState data structure
- */
- type RuleIndexState struct {
- id uint64 // Id of this rule index
- rules []*Rule // All rules stored in this index
- keyMap map[string]*RuleMatcherKey // Map of keys (key or key and value) to KeyMatcher
- }
- /*
- newRuleIndexState creates a new rule index matching on event state.
- */
- func newRuleIndexState() *RuleIndexState {
- return &RuleIndexState{newRuleIndexID(), make([]*Rule, 0),
- make(map[string]*RuleMatcherKey)}
- }
- /*
- Type returns the type of the rule sub index.
- */
- func (ri *RuleIndexState) Type() string {
- return typeRuleIndexState
- }
- /*
- addRuleAtLevel adds a new rule to the index at a specific level. The
- level is described by a part of the rule kind match.
- */
- func (ri *RuleIndexState) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
- errorutil.AssertTrue(len(kindMatchLevel) == 0,
- fmt.Sprint("RuleIndexState must be a leaf - level is:", kindMatchLevel))
- num := uint(len(ri.rules))
- var bit uint64 = 1 << num
- ri.rules = append(ri.rules, rule)
- for k, v := range rule.StateMatch {
- var ok bool
- var keyMatcher *RuleMatcherKey
- if keyMatcher, ok = ri.keyMap[k]; !ok {
- keyMatcher = &RuleMatcherKey{0, 0, make(map[interface{}]uint64), make(map[uint64]*regexp.Regexp)}
- ri.keyMap[k] = keyMatcher
- }
- keyMatcher.addRule(num, bit, k, v)
- }
- }
- /*
- isTriggeringAtLevel checks if a given event triggers a rule at the given
- level of the index.
- */
- func (ri *RuleIndexState) isTriggeringAtLevel(event *Event, level int) bool {
- return len(event.kind) == level
- }
- /*
- matchAtLevel returns all rules in this index which match a given event
- at the given level. This method does a full matching check including
- state matching.
- */
- func (ri *RuleIndexState) matchAtLevel(event *Event, level int) []*Rule {
- if len(event.kind) != level {
- return nil
- }
- // Assume all rules match and remove the ones with don't
- var matchBits uint64 = (1 << uint(len(ri.rules))) - 1
- // Match key and values
- for key, matcher := range ri.keyMap {
- if val, ok := event.state[key]; ok {
- // Key is present in event
- matchBits = matcher.match(matchBits, val)
- } else {
- // Key is not present in event - remove all rules which require the key
- matchBits = matcher.unmatch(matchBits)
- }
- if matchBits == 0 {
- // All rules have been excluded
- return nil
- }
- }
- var ret []*Rule
- var collectionBits uint64 = 1
- // Collect matched rules
- for i := 0; collectionBits <= matchBits; i++ {
- if matchBits&collectionBits > 0 {
- ret = append(ret, ri.rules[i])
- }
- collectionBits <<= 1
- }
- return ret
- }
- /*
- stringIndent returns a string representation with a given indentation of this
- rule index and all subindexes.
- */
- func (ri *RuleIndexState) stringIndent(indent string) string {
- var buf bytes.Buffer
- buf.WriteString(fmt.Sprintf("%v%v (%v) ", indent, ri.Type(), ri.id))
- buf.WriteString("[")
- for _, r := range ri.rules {
- buf.WriteString(fmt.Sprintf("%v ", r.Name))
- }
- buf.WriteString("]\n")
- newIndent := indent + " "
- var keys []string
- for k := range ri.keyMap {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- m := ri.keyMap[k]
- buf.WriteString(fmt.Sprintf("%v%v - %v\n", newIndent, k, m))
- }
- return buf.String()
- }
- // Rule Index All
- // ==============
- /*
- RuleIndexAll data structure.
- */
- type RuleIndexAll struct {
- id uint64 // Id of this rule index
- rules []*Rule // Rules with target all events of a specific category
- }
- /*
- newRuleIndexAll creates a new leaf rule index matching on all events.
- */
- func newRuleIndexAll() *RuleIndexAll {
- return &RuleIndexAll{newRuleIndexID(), make([]*Rule, 0)}
- }
- /*
- Type returns the type of the rule sub index.
- */
- func (ri *RuleIndexAll) Type() string {
- return typeRuleIndexAll
- }
- /*
- addRuleAtLevel adds a new rule to the index at a specific level. The
- level is described by a part of the rule kind match.
- */
- func (ri *RuleIndexAll) addRuleAtLevel(rule *Rule, kindMatchLevel []string) {
- ri.rules = append(ri.rules, rule)
- }
- /*
- isTriggeringAtLevel checks if a given event triggers a rule at the given
- level of the index.
- */
- func (ri *RuleIndexAll) isTriggeringAtLevel(event *Event, level int) bool {
- return len(event.kind) == level
- }
- /*
- matchAtLevel returns all rules in this index which match a given event
- at the given level. This method does a full matching check including
- state matching.
- */
- func (ri *RuleIndexAll) matchAtLevel(event *Event, level int) []*Rule {
- if len(event.kind) != level {
- return nil
- }
- return ri.rules
- }
- /*
- stringIndent returns a string representation with a given indentation of this
- rule index and all subindexes.
- */
- func (ri *RuleIndexAll) stringIndent(indent string) string {
- var buf bytes.Buffer
- buf.WriteString(fmt.Sprintf("%v%v (%v)\n", indent, ri.Type(), ri.id))
- newIndent := indent + " "
- for _, rule := range ri.rules {
- buf.WriteString(fmt.Sprintf("%v%v\n", newIndent, rule))
- }
- return buf.String()
- }
- // Unique id creation
- // ==================
- var ruleindexidcounter uint64 = 1
- var ruleindexidcounterLock = &sync.Mutex{}
- /*
- newId returns a new unique id.
- */
- func newRuleIndexID() uint64 {
- ruleindexidcounterLock.Lock()
- defer ruleindexidcounterLock.Unlock()
- ret := ruleindexidcounter
- ruleindexidcounter++
- return ret
- }
|