Пример #1
0
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
}
Пример #2
0
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
}
Пример #3
0
// 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
}
Пример #4
0
// 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
}
Пример #5
0
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
}