github.com/go-graphite/carbonapi@v0.17.0/expr/functions/aggregateWithWildcards/function.go (about) 1 package aggregateWithWildcards 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/go-graphite/carbonapi/expr/consolidations" 9 "github.com/go-graphite/carbonapi/expr/helper" 10 "github.com/go-graphite/carbonapi/expr/interfaces" 11 "github.com/go-graphite/carbonapi/expr/types" 12 "github.com/go-graphite/carbonapi/pkg/parser" 13 ) 14 15 type aggregateWithWildcards struct{} 16 17 func GetOrder() interfaces.Order { 18 return interfaces.Any 19 } 20 21 func New(configFile string) []interfaces.FunctionMetadata { 22 f := &aggregateWithWildcards{} 23 res := make([]interfaces.FunctionMetadata, 0) 24 for _, n := range []string{"multiplySeriesWithWildcards", "averageSeriesWithWildcards", "aggregateWithWildcards", "sumSeriesWithWildcards"} { 25 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 26 } 27 return res 28 } 29 30 func (f *aggregateWithWildcards) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 31 if e.ArgsLen() < 2 { 32 return nil, parser.ErrMissingArgument 33 } 34 35 args, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 36 if err != nil { 37 return nil, err 38 } 39 40 var callback string 41 fieldsArgsIx := 1 42 switch e.Target() { 43 case "aggregateWithWildcards": 44 callback, err = e.GetStringArg(1) 45 if err != nil { 46 return nil, err 47 } 48 fieldsArgsIx = 2 49 case "sumSeriesWithWildcards": 50 callback = "sum" 51 case "averageSeriesWithWildcards": 52 callback = "average" 53 case "multiplySeriesWithWildcards": 54 callback = "multiply" 55 } 56 57 var fields []int 58 fields, err = e.GetIntArgs(fieldsArgsIx) 59 if err != nil { 60 return nil, err 61 } 62 63 aggFunc, ok := consolidations.ConsolidationToFunc[callback] 64 if !ok { 65 return nil, fmt.Errorf("unsupported consolidation function %s", callback) 66 } 67 groups := make(map[string][]*types.MetricData) 68 nodeList := make([]string, 0, 256) 69 70 for _, a := range args { 71 metric := types.ExtractNameTag(a.Name) 72 nodes := strings.Split(metric, ".") 73 s := make([]string, 0, len(nodes)) 74 // Yes, this is O(n^2), but len(nodes) < 10 and len(fields) < 3 75 // Iterating an int slice is faster than a map for n ~ 30 76 // http://www.antoine.im/posts/someone_is_wrong_on_the_internet 77 for i, n := range nodes { 78 if !helper.Contains(fields, i) { 79 s = append(s, n) 80 } 81 } 82 83 node := strings.Join(s, ".") 84 85 if len(groups[node]) == 0 { 86 nodeList = append(nodeList, node) 87 } 88 89 groups[node] = append(groups[node], a) 90 } 91 92 results := make([]*types.MetricData, 0, len(groups)) 93 94 for _, node := range nodeList { 95 // Pass in -1 for xFilesFactor because aggregateWithWildcards doesn't support xFilesFactor 96 res, err := helper.AggregateSeries(e, groups[node], aggFunc, -1, false) 97 if err != nil { 98 return nil, err 99 } 100 res[0].Name = node 101 res[0].Tags["aggregatedBy"] = callback 102 103 results = append(results, res...) 104 } 105 106 return results, nil 107 } 108 109 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 110 func (f *aggregateWithWildcards) Description() map[string]types.FunctionDescription { 111 return map[string]types.FunctionDescription{ 112 "aggregateWithWildcards": { 113 Name: "aggregateWithWildcards", 114 Function: "aggregateWithWildcards(seriesList, func, *positions)", 115 Description: "Call aggregator after inserting wildcards at the given position(s).\n\nExample:\n\n.. code-block:: none\n\n &target=aggregateWithWildcards(host.cpu-[0-7].cpu-{user,system}.value, 'sum', 1)", 116 Module: "graphite.render.functions", 117 Group: "Calculate", 118 Params: []types.FunctionParam{ 119 { 120 Name: "seriesList", 121 Type: types.SeriesList, 122 Required: true, 123 }, 124 { 125 Name: "func", 126 Type: types.AggFunc, 127 Required: true, 128 Options: types.StringsToSuggestionList(consolidations.AvailableConsolidationFuncs()), 129 }, 130 { 131 Name: "positions", 132 Type: types.Node, 133 Required: true, 134 }, 135 }, 136 }, 137 "averageSeriesWithWildcards": { 138 Description: "Call averageSeries after inserting wildcards at the given position(s).\n\nExample:\n\n.. code-block:: none\n\n &target=averageSeriesWithWildcards(host.cpu-[0-7}.cpu-{user,system}.value, 1)\n\nThis would be the equivalent of\n\n.. code-block:: none\n\n &target=averageSeries(host.*.cpu-user.value)&target=averageSeries(host.*.cpu-system.value)\n\nThis is an alias for :py:func:`aggregateWithWildcards <aggregateWithWildcards>` with aggregation ``average``.", 139 Function: "averageSeriesWithWildcards(seriesList, *position)", 140 Group: "Combine", 141 Module: "graphite.render.functions", 142 Name: "averageSeriesWithWildcards", 143 Params: []types.FunctionParam{ 144 { 145 Name: "seriesList", 146 Required: true, 147 Type: types.SeriesList, 148 }, 149 { 150 Multiple: true, 151 Name: "position", 152 Type: types.Node, 153 }, 154 }, 155 SeriesChange: true, // function aggregate metrics or change series items count 156 NameChange: true, // name changed 157 TagsChange: true, // name tag changed 158 ValuesChange: true, // values changed 159 }, 160 "multiplySeriesWithWildcards": { 161 Description: "Call multiplySeries after inserting wildcards at the given position(s).\n\nExample:\n\n.. code-block:: none\n\n &target=multiplySeriesWithWildcards(web.host-[0-7}.{avg-response,total-request}.value, 2)\n\nThis would be the equivalent of\n\n.. code-block:: none\n\n &target=multiplySeries(web.host-0.{avg-response,total-request}.value)&target=multiplySeries(web.host-1.{avg-response,total-request}.value)...\n\nThis is an alias for :py:func:`aggregateWithWildcards <aggregateWithWildcards>` with aggregation ``multiply``.", 162 Function: "multiplySeriesWithWildcards(seriesList, *position)", 163 Group: "Combine", 164 Module: "graphite.render.functions", 165 Name: "multiplySeriesWithWildcards", 166 Params: []types.FunctionParam{ 167 { 168 Name: "seriesList", 169 Required: true, 170 Type: types.SeriesList, 171 }, 172 { 173 Multiple: true, 174 Name: "position", 175 Type: types.Node, 176 }, 177 }, 178 SeriesChange: true, // function aggregate metrics or change series items count 179 NameChange: true, // name changed 180 TagsChange: true, // name tag changed 181 ValuesChange: true, // values changed 182 }, 183 "sumSeriesWithWildcards": { 184 Description: "Call sumSeries after inserting wildcards at the given position(s).\n\nExample:\n\n.. code-block:: none\n\n &target=sumSeriesWithWildcards(host.cpu-[0-7}.cpu-{user,system}.value, 1)\n\nThis would be the equivalent of\n\n.. code-block:: none\n\n &target=sumSeries(host.cpu-[0-7}.cpu-user.value)&target=sumSeries(host.cpu-[0-7}.cpu-system.value)\n\nThis is an alias for :py:func:`aggregateWithWildcards <aggregateWithWildcards>` with aggregation ``sum``.", 185 Function: "sumSeriesWithWildcards(seriesList, *position)", 186 Group: "Combine", 187 Module: "graphite.render.functions", 188 Name: "sumSeriesWithWildcards", 189 Params: []types.FunctionParam{ 190 { 191 Name: "seriesList", 192 Required: true, 193 Type: types.SeriesList, 194 }, 195 { 196 Multiple: true, 197 Name: "position", 198 Type: types.Node, 199 }, 200 }, 201 SeriesChange: true, // function aggregate metrics or change series items count 202 NameChange: true, // name changed 203 TagsChange: true, // name tag changed 204 ValuesChange: true, // values changed 205 }, 206 } 207 }