From 10194df11270d961d97d3d084afdda2ce669c835 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 8 Feb 2019 18:15:17 +0100 Subject: [PATCH] azuremonitor: simple alerting for Azure Monitor API Lots of edge cases and functionality left to implement but a simple query works for alerting now. --- pkg/tsdb/azuremonitor/azuremonitor.go | 39 +++++++++++---- pkg/tsdb/azuremonitor/azuremonitor_test.go | 34 ++++++++++++++ .../test-data/1-azure-monitor-response.json | 47 +++++++++++++++++++ pkg/tsdb/azuremonitor/types.go | 11 +++-- .../plugin.json | 3 +- 5 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response.json diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go index 93fd8ed8110..8ef959bed9c 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor.go +++ b/pkg/tsdb/azuremonitor/azuremonitor.go @@ -11,8 +11,8 @@ import ( "path" "time" - // "github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/api/pluginproxy" + "github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/models" @@ -113,10 +113,12 @@ func (e *AzureMonitorExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Azure azureMonitorTarget := query.Model.Get("azureMonitor").MustMap() - resourceGroup := azureMonitorTarget["resourceGroup"].(string) - metricDefinition := azureMonitorTarget["metricDefinition"].(string) - resourceName := azureMonitorTarget["resourceName"].(string) - azureURL := fmt.Sprintf("resourceGroups/%s/providers/%s/%s/providers/microsoft.insights/metrics", resourceGroup, metricDefinition, resourceName) + urlComponents := make(map[string]string) + urlComponents["resourceGroup"] = azureMonitorTarget["resourceGroup"].(string) + urlComponents["metricDefinition"] = azureMonitorTarget["metricDefinition"].(string) + urlComponents["resourceName"] = azureMonitorTarget["resourceName"].(string) + + azureURL := fmt.Sprintf("resourceGroups/%s/providers/%s/%s/providers/microsoft.insights/metrics", urlComponents["resourceGroup"], urlComponents["metricDefinition"], urlComponents["resourceName"]) alias := azureMonitorTarget["alias"].(string) @@ -133,11 +135,12 @@ func (e *AzureMonitorExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Azure } azureMonitorQueries = append(azureMonitorQueries, &AzureMonitorQuery{ - URL: azureURL, - Target: target, - Params: params, - RefID: query.RefId, - Alias: alias, + URL: azureURL, + UrlComponents: urlComponents, + Target: target, + Params: params, + RefID: query.RefId, + Alias: alias, }) } @@ -247,5 +250,21 @@ func (e *AzureMonitorExecutor) unmarshalResponse(res *http.Response) (AzureMonit func (e *AzureMonitorExecutor) parseResponse(queryRes *tsdb.QueryResult, data AzureMonitorResponse, query *AzureMonitorQuery) error { slog.Info("AzureMonitor", "Response", data) + for _, series := range data.Value { + points := make([]tsdb.TimePoint, 0) + + defaultMetricName := fmt.Sprintf("%s.%s", query.UrlComponents["resourceName"], series.Name.LocalizedValue) + + for _, point := range series.Timeseries[0].Data { + value := point.Average + points = append(points, tsdb.NewTimePoint(null.FloatFrom(value), float64((point.TimeStamp).Unix())*1000)) + } + + queryRes.Series = append(queryRes.Series, &tsdb.TimeSeries{ + Name: defaultMetricName, + Points: points, + }) + } + return nil } diff --git a/pkg/tsdb/azuremonitor/azuremonitor_test.go b/pkg/tsdb/azuremonitor/azuremonitor_test.go index 787e0ae1586..1b8f69aa64a 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor_test.go @@ -1,7 +1,9 @@ package azuremonitor import ( + "encoding/json" "fmt" + "io/ioutil" "testing" "time" @@ -57,5 +59,37 @@ func TestAzureMonitor(t *testing.T) { So(queries[0].Alias, ShouldEqual, "testalias") }) }) + + Convey("Parse AzureMonitor API response in the time series format", func() { + Convey("when data from query aggregated to one time series", func() { + data, err := loadTestFile("./test-data/1-azure-monitor-response.json") + So(err, ShouldBeNil) + So(data.Interval, ShouldEqual, "PT1M") + + res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "A"} + query := &AzureMonitorQuery{ + UrlComponents: map[string]string{ + "resourceName": "grafana", + }, + } + err = executor.parseResponse(res, data, query) + So(err, ShouldBeNil) + + So(len(res.Series), ShouldEqual, 1) + So(res.Series[0].Name, ShouldEqual, "grafana.Percentage CPU") + So(len(res.Series[0].Points), ShouldEqual, 5) + }) + }) }) } + +func loadTestFile(path string) (AzureMonitorResponse, error) { + var data AzureMonitorResponse + + jsonBody, err := ioutil.ReadFile(path) + if err != nil { + return data, err + } + err = json.Unmarshal(jsonBody, &data) + return data, err +} diff --git a/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response.json b/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response.json new file mode 100644 index 00000000000..febb47f2047 --- /dev/null +++ b/pkg/tsdb/azuremonitor/test-data/1-azure-monitor-response.json @@ -0,0 +1,47 @@ +{ + "cost": 0, + "timespan": "2019-02-08T10:13:50Z\/2019-02-08T16:13:50Z", + "interval": "PT1M", + "value": [ + { + "id": "\/subscriptions\/44693801-6ee6-49de-9b2d-9106972f9572\/resourceGroups\/grafanastaging\/providers\/Microsoft.Compute\/virtualMachines\/grafana\/providers\/Microsoft.Insights\/metrics\/Percentage CPU", + "type": "Microsoft.Insights\/metrics", + "name": { + "value": "Percentage CPU", + "localizedValue": "Percentage CPU" + }, + "unit": "Percent", + "timeseries": [ + { + "metadatavalues": [ + + ], + "data": [ + { + "timeStamp": "2019-02-08T10:13:00Z", + "average": 2.0875 + }, + { + "timeStamp": "2019-02-08T10:14:00Z", + "average": 2.1525 + }, + { + "timeStamp": "2019-02-08T10:15:00Z", + "average": 2.155 + }, + { + "timeStamp": "2019-02-08T10:16:00Z", + "average": 3.6925 + }, + { + "timeStamp": "2019-02-08T10:17:00Z", + "average": 2.44 + } + ] + } + ] + } + ], + "namespace": "Microsoft.Compute\/virtualMachines", + "resourceregion": "westeurope" +} diff --git a/pkg/tsdb/azuremonitor/types.go b/pkg/tsdb/azuremonitor/types.go index fc99ede6512..5b1b7255d62 100644 --- a/pkg/tsdb/azuremonitor/types.go +++ b/pkg/tsdb/azuremonitor/types.go @@ -8,11 +8,12 @@ import ( // AzureMonitorQuery is the query for all the services as they have similar queries // with a url, a querystring and an alias field type AzureMonitorQuery struct { - URL string - Target string - Params url.Values - RefID string - Alias string + URL string + UrlComponents map[string]string + Target string + Params url.Values + RefID string + Alias string } // AzureMonitorResponse is the json response from the Azure Monitor API diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json b/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json index 76a56f2baaa..e4f48c581e3 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/plugin.json @@ -158,5 +158,6 @@ }, "metrics": true, - "annotations": true + "annotations": true, + "alerting": true }