func kairosdbExtractPlots(query *plot.Query, kairosdbSeries map[string]map[string]kairosdbSeriesEntry, kairosdbPlots []metricQueryResponse) ([]*plot.Series, error) { var results []*plot.Series for _, kairosdbPlot := range kairosdbPlots { target := "" for _, series := range query.Series { entry := kairosdbSeries[series.Source][series.Metric] m := kairosdbPlot.Results[0].Name if _, ok := kairosdbPlot.Results[0].Tags[entry.tag]; !ok { continue } s := kairosdbPlot.Results[0].Tags[entry.tag][0] if s == series.Source && m == series.Metric { if target == "" { target = series.Name } else { return nil, fmt.Errorf("ambiguity during plot target retrieval") } } } // Skip series if target not found if target == "" { continue } series := &plot.Series{ Name: target, } for _, plotPoint := range kairosdbPlot.Results[0].Values { series.Plots = append(series.Plots, plot.Plot{ Time: time.Unix(int64(plotPoint[0]/1000), 0), Value: plot.Value(plotPoint[1]), }) } results = append(results, series) } return results, nil }
func graphiteExtractResult(plots []graphitePlot) ([]*plot.Series, error) { var results []*plot.Series for _, p := range plots { series := &plot.Series{ Name: p.Target, } for _, d := range p.Datapoints { series.Plots = append(series.Plots, plot.Plot{ Time: time.Unix(int64(d[1]), 0), Value: plot.Value(d[0]), }) } results = append(results, series) } return results, nil }
// GetPlots retrieves time series data from origin based on a query and a time interval. func (c *RRDConnector) GetPlots(query *plot.Query) ([]*plot.Series, error) { var ( results []*plot.Series xport *rrd.Exporter ) if len(query.Series) == 0 { return nil, fmt.Errorf("rrd[%s]: requested series list is empty", c.name) } xport = rrd.NewExporter() if c.daemon != "" { xport.SetDaemon(c.daemon) } step := time.Duration(0) for _, s := range query.Series { if _, ok := c.metrics[s.Source]; !ok { return nil, fmt.Errorf("rrd[%s]: unknown source `%s'", c.name, s.Source) } else if _, ok := c.metrics[s.Source][s.Metric]; !ok { return nil, fmt.Errorf("rrd[%s]: unknown metric `%s' for source `%s'", c.name, s.Metric, s.Source) } filePath := strings.Replace(c.metrics[s.Source][s.Metric].FilePath, ":", "\\:", -1) // Set plots request xport.Def(s.Name+"-def0", filePath, c.metrics[s.Source][s.Metric].Dataset, c.metrics[s.Source][s.Metric].Cf) xport.CDef(s.Name, s.Name+"-def0") xport.XportDef(s.Name, s.Name) if c.metrics[s.Source][s.Metric].Step > step { step = c.metrics[s.Source][s.Metric].Step } } // Get plots if step == 0 { step = query.EndTime.Sub(query.StartTime) / time.Duration(config.DefaultPlotSample) } data := rrd.XportResult{} data, err := xport.Xport(query.StartTime, query.EndTime, step) if err != nil { return nil, err } for idx, name := range data.Legends { series := &plot.Series{ Name: name, } // FIXME: skip last garbage entry (see https://github.com/ziutek/rrd/pull/13) for i := 0; i < data.RowCnt-1; i++ { series.Plots = append(series.Plots, plot.Plot{ Time: query.StartTime.Add(data.Step * time.Duration(i)), Value: plot.Value(data.ValueAt(idx, i)), }) } results = append(results, series) } data.FreeValues() return results, nil }
// GetPlots retrieves time series data from provider based on a query and a time interval. func (connector *InfluxDBConnector) GetPlots(query *plot.Query) ([]*plot.Series, error) { l := len(query.Series) if l == 0 { return nil, fmt.Errorf("influxdb[%s]: requested series list is empty", connector.name) } metrics := make([]string, l) columns := make([]string, l) for i, series := range query.Series { metrics[i] = strconv.Quote(connector.series[series.Source][series.Metric][0]) columns[i] = strconv.Quote(connector.series[series.Source][series.Metric][1]) } influxdbQuery := fmt.Sprintf( "select %s from %s where time > %ds and time < %ds order asc", strings.Join(columns, ","), strings.Join(metrics, ","), query.StartTime.Unix(), query.EndTime.Unix(), ) q, err := connector.client.Query(influxdbQuery, "s") if err != nil { return nil, fmt.Errorf("influxdb[%s]: unable to perform query: %s", connector.name, err) } results := make([]*plot.Series, 0) seriesMap := make(map[string]*plot.Series) step := int(query.EndTime.Sub(query.StartTime) / time.Duration(query.Sample)) for _, r := range q { name := r.GetName() columns := r.GetColumns()[2:] for i := range columns { seriesMap[name+"\x1e"+columns[i]] = &plot.Series{ Step: step, } } for _, point := range r.GetPoints() { for i := 0; i < len(columns); i++ { key := name + "\x1e" + columns[i] seriesMap[key].Plots = append(seriesMap[key].Plots, plot.Plot{ Time: time.Unix(int64(point[0].(float64)), 0), Value: plot.Value(point[i+2].(float64)), }) } } } for _, s := range query.Series { key := connector.series[s.Source][s.Metric][0] + "\x1e" + connector.series[s.Source][s.Metric][1] if _, ok := seriesMap[key]; !ok { continue } entry := seriesMap[key] entry.Name = s.Name results = append(results, entry) } return results, nil }
func makePlotsResponse(plotSeries map[string][]plot.Series, plotReq *PlotRequest, graph *library.Graph) (*PlotResponse, error) { response := &PlotResponse{ ID: graph.ID, Start: plotReq.startTime.Format(time.RFC3339), End: plotReq.endTime.Format(time.RFC3339), Name: graph.Name, Description: graph.Description, Title: graph.Title, Type: graph.Type, StackMode: graph.StackMode, UnitType: graph.UnitType, UnitLegend: graph.UnitLegend, Modified: graph.Modified, } for _, groupItem := range graph.Groups { var ( groupConsolidate int groupSeries []plot.Series err error ) seriesOptions := make(map[string]map[string]interface{}) for _, seriesItem := range groupItem.Series { if _, ok := plotSeries[seriesItem.Name]; !ok { return nil, fmt.Errorf("unable to find plots for `%s' series", seriesItem.Name) } for _, plotItem := range plotSeries[seriesItem.Name] { var optionKey string // Apply series scale if any if scale, _ := config.GetFloat(seriesItem.Options, "scale", false); scale != 0 { plotItem.Scale(plot.Value(scale)) } // Merge options from group and series if groupItem.Type == plot.OperTypeAverage || groupItem.Type == plot.OperTypeSum { optionKey = groupItem.Name } else { optionKey = seriesItem.Name } seriesOptions[optionKey] = make(map[string]interface{}) for key, value := range groupItem.Options { seriesOptions[optionKey][key] = value } if groupItem.Type != plot.OperTypeAverage && groupItem.Type != plot.OperTypeSum { for key, value := range seriesItem.Options { seriesOptions[optionKey][key] = value } } groupSeries = append(groupSeries, plotItem) } } if len(groupSeries) == 0 { continue } // Normalize all series plots on the same time step groupConsolidate, err = config.GetInt(groupItem.Options, "consolidate", true) if err != nil { groupConsolidate = plot.ConsolidateAverage } groupSeries, err = plot.Normalize( groupSeries, plotReq.startTime, plotReq.endTime, plotReq.Sample, groupConsolidate, ) if err != nil { return nil, fmt.Errorf("unable to consolidate series: %s", err) } // Perform requested series operations if groupItem.Type == plot.OperTypeAverage || groupItem.Type == plot.OperTypeSum { var ( operSeries plot.Series err error ) if groupItem.Type == plot.OperTypeAverage { operSeries, err = plot.AverageSeries(groupSeries) if err != nil { return nil, fmt.Errorf("unable to average series: %s", err) } } else { operSeries, err = plot.SumSeries(groupSeries) if err != nil { return nil, fmt.Errorf("unable to sum series: %s", err) } } operSeries.Name = groupItem.Name groupSeries = []plot.Series{operSeries} } // Apply group scale if any if scale, _ := config.GetFloat(groupItem.Options, "scale", false); scale != 0 { for _, seriesItem := range groupSeries { seriesItem.Scale(plot.Value(scale)) } } for _, seriesItem := range groupSeries { // Summarize each series (compute min/max/avg/last values) seriesItem.Summarize(plotReq.Percentiles) response.Series = append(response.Series, &SeriesResponse{ Name: seriesItem.Name, StackID: groupItem.StackID, Plots: seriesItem.Plots, Summary: seriesItem.Summary, Options: seriesOptions[seriesItem.Name], }) } } return response, nil }