package messageprocessor import ( "errors" "fmt" lp2 "github.com/ClusterCockpit/cc-energy-manager/pkg/cc-message" cclog "github.com/ClusterCockpit/cc-metric-collector/pkg/ccLogger" units "github.com/ClusterCockpit/cc-units" "github.com/expr-lang/expr" "github.com/expr-lang/expr/vm" ) type MessageLocation int const ( MESSAGE_LOCATION_TAGS MessageLocation = iota MESSAGE_LOCATION_META MESSAGE_LOCATION_FIELDS ) // Abstract function to move entries from one location to another func moveInMessage(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, from, to MessageLocation) (bool, error) { for d, data := range *checks { value, err := expr.Run(d, *params) if err != nil { return false, fmt.Errorf("failed to evaluate: %v", err.Error()) } cclog.ComponentDebug("MessageProcessor", "Move from", from, "to", to) if value.(bool) { var v string var ok bool = false switch from { case MESSAGE_LOCATION_TAGS: cclog.ComponentDebug("MessageProcessor", "Getting tag key", data.Key) v, ok = message.GetTag(data.Key) case MESSAGE_LOCATION_META: cclog.ComponentDebug("MessageProcessor", "Getting meta key", data.Key) cclog.ComponentDebug("MessageProcessor", message.Meta()) v, ok = message.GetMeta(data.Key) case MESSAGE_LOCATION_FIELDS: var x interface{} cclog.ComponentDebug("MessageProcessor", "Getting field key", data.Key) x, ok = message.GetField(data.Key) v = fmt.Sprintf("%v", x) } if ok { switch from { case MESSAGE_LOCATION_TAGS: cclog.ComponentDebug("MessageProcessor", "Removing tag key", data.Key) message.RemoveTag(data.Key) case MESSAGE_LOCATION_META: cclog.ComponentDebug("MessageProcessor", "Removing meta key", data.Key) message.RemoveMeta(data.Key) case MESSAGE_LOCATION_FIELDS: cclog.ComponentDebug("MessageProcessor", "Removing field key", data.Key) message.RemoveField(data.Key) } switch to { case MESSAGE_LOCATION_TAGS: cclog.ComponentDebug("MessageProcessor", "Adding tag", data.Value, "->", v) message.AddTag(data.Value, v) case MESSAGE_LOCATION_META: cclog.ComponentDebug("MessageProcessor", "Adding meta", data.Value, "->", v) message.AddMeta(data.Value, v) case MESSAGE_LOCATION_FIELDS: cclog.ComponentDebug("MessageProcessor", "Adding field", data.Value, "->", v) message.AddField(data.Value, v) } } else { return false, fmt.Errorf("failed to get message entry: %s", data.Key) } } } return false, nil } func deleteIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, location MessageLocation) (bool, error) { for d, data := range *checks { value, err := expr.Run(d, *params) if err != nil { return true, fmt.Errorf("failed to evaluate: %v", err.Error()) } if value.(bool) { switch location { case MESSAGE_LOCATION_FIELDS: switch data.Key { case "value", "event", "log", "control": return false, errors.New("cannot delete protected fields") default: cclog.ComponentDebug("MessageProcessor", "Removing field for", data.Key) message.RemoveField(data.Key) } case MESSAGE_LOCATION_TAGS: cclog.ComponentDebug("MessageProcessor", "Removing tag for", data.Key) message.RemoveTag(data.Key) case MESSAGE_LOCATION_META: cclog.ComponentDebug("MessageProcessor", "Removing meta for", data.Key) message.RemoveMeta(data.Key) } } } return false, nil } func addIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig, location MessageLocation) (bool, error) { for d, data := range *checks { value, err := expr.Run(d, *params) if err != nil { return true, fmt.Errorf("failed to evaluate: %v", err.Error()) } if value.(bool) { switch location { case MESSAGE_LOCATION_FIELDS: cclog.ComponentDebug("MessageProcessor", "Adding field", data.Value, "->", data.Value) message.AddField(data.Key, data.Value) case MESSAGE_LOCATION_TAGS: cclog.ComponentDebug("MessageProcessor", "Adding tag", data.Value, "->", data.Value) message.AddTag(data.Key, data.Value) case MESSAGE_LOCATION_META: cclog.ComponentDebug("MessageProcessor", "Adding meta", data.Value, "->", data.Value) message.AddMeta(data.Key, data.Value) } } } return false, nil } func deleteTagIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return deleteIf(message, params, checks, MESSAGE_LOCATION_TAGS) } func addTagIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return addIf(message, params, checks, MESSAGE_LOCATION_TAGS) } func moveTagToMeta(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_TAGS, MESSAGE_LOCATION_META) } func moveTagToField(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_TAGS, MESSAGE_LOCATION_FIELDS) } func deleteMetaIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return deleteIf(message, params, checks, MESSAGE_LOCATION_META) } func addMetaIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return addIf(message, params, checks, MESSAGE_LOCATION_META) } func moveMetaToTag(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_META, MESSAGE_LOCATION_TAGS) } func moveMetaToField(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_META, MESSAGE_LOCATION_FIELDS) } func deleteFieldIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return deleteIf(message, params, checks, MESSAGE_LOCATION_FIELDS) } func addFieldIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return addIf(message, params, checks, MESSAGE_LOCATION_FIELDS) } func moveFieldToTag(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_FIELDS, MESSAGE_LOCATION_TAGS) } func moveFieldToMeta(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]messageProcessorTagConfig) (bool, error) { return moveInMessage(message, params, checks, MESSAGE_LOCATION_FIELDS, MESSAGE_LOCATION_META) } func dropMessagesIf(params *map[string]interface{}, checks *map[*vm.Program]struct{}) (bool, error) { for d := range *checks { value, err := expr.Run(d, *params) if err != nil { return false, fmt.Errorf("failed to evaluate: %v", err.Error()) } if value.(bool) { return true, nil } } return false, nil } func normalizeUnits(message lp2.CCMessage) (bool, error) { if in_unit, ok := message.GetMeta("unit"); ok { u := units.NewUnit(in_unit) if u.Valid() { cclog.ComponentDebug("MessageProcessor", "Update unit with", u.Short()) message.AddMeta("unit", u.Short()) } } else if in_unit, ok := message.GetTag("unit"); ok { u := units.NewUnit(in_unit) if u.Valid() { cclog.ComponentDebug("MessageProcessor", "Update unit with", u.Short()) message.AddTag("unit", u.Short()) } } return false, nil } func changeUnitPrefix(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]string) (bool, error) { for r, n := range *checks { value, err := expr.Run(r, *params) if err != nil { return false, fmt.Errorf("failed to evaluate: %v", err.Error()) } if value.(bool) { newPrefix := units.NewPrefix(n) cclog.ComponentDebug("MessageProcessor", "Condition matches, change to prefix", newPrefix.String()) if in_unit, ok := message.GetMeta("unit"); ok && newPrefix != units.InvalidPrefix { u := units.NewUnit(in_unit) if u.Valid() { cclog.ComponentDebug("MessageProcessor", "Input unit", u.Short()) conv, out_unit := units.GetUnitPrefixFactor(u, newPrefix) if conv != nil && out_unit.Valid() { if val, ok := message.GetField("value"); ok { cclog.ComponentDebug("MessageProcessor", "Update unit with", out_unit.Short()) message.AddField("value", conv(val)) message.AddMeta("unit", out_unit.Short()) } } } } else if in_unit, ok := message.GetTag("unit"); ok && newPrefix != units.InvalidPrefix { u := units.NewUnit(in_unit) if u.Valid() { cclog.ComponentDebug("MessageProcessor", "Input unit", u.Short()) conv, out_unit := units.GetUnitPrefixFactor(u, newPrefix) if conv != nil && out_unit.Valid() { if val, ok := message.GetField("value"); ok { cclog.ComponentDebug("MessageProcessor", "Update unit with", out_unit.Short()) message.AddField("value", conv(val)) message.AddTag("unit", out_unit.Short()) } } } } } } return false, nil } func renameMessagesIf(message lp2.CCMessage, params *map[string]interface{}, checks *map[*vm.Program]string) (bool, error) { for d, n := range *checks { value, err := expr.Run(d, *params) if err != nil { return true, fmt.Errorf("failed to evaluate: %v", err.Error()) } if value.(bool) { old := message.Name() cclog.ComponentDebug("MessageProcessor", "Rename to", n) message.SetName(n) cclog.ComponentDebug("MessageProcessor", "Add old name as 'oldname' to meta", old) message.AddMeta("oldname", old) } } return false, nil }