Skip to content

Commit fceccb5

Browse files
author
Mattia Moretti
authored
Auto Interval and metricdatapoints error handling cdscrs 35197 (#263)
* auto works * datapoints error handling * capturing interval undefined * some cleaning * wait till region is populated * cleanup * interval as template var * cleanup * template variable for interval * fix template var * fine tune interval defaults * interpolateProps verbessern * some more controls * golang verbesserungen * minor golang warn fixed * ready
1 parent 318ba9c commit fceccb5

File tree

10 files changed

+96
-63
lines changed

10 files changed

+96
-63
lines changed
83.9 KB
Loading

docs/using.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ The final list of variables should look like this (**Plugin v5** and above):
191191

192192
![Metrics dashboard variables screenshot](images/template-multi.png)
193193

194+
### Template variable for interval
195+
196+
For intervals, you can use a custom or constant variable. To create a custom, select the variable type as custom.
197+
Label(appears as the display name of the variable selected).
198+
199+
Custom variables provide the convenience of using custom intervals for your dashboard. Please note the accepted format for intervals are within squared brackets and must include the time unit (for example [5m] to specify 5 minutes or [2h] for two hours). Following is an example of interval template variable definition:
200+
![Template variable for interval](images/Interval-Template-Var.png)
201+
202+
194203
### Windows and Resolution
195204

196205
For windows and resolution, you can use a custom or constant variable. To create a custom, select the variable type as custom.

pkg/plugin/metrics_functions.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (o *OCIDatasource) TestConnectivity(ctx context.Context) error {
3636
return fmt.Errorf("TestConnectivity failed: cannot read o.tenancyAccess")
3737
}
3838

39-
for key, _ := range o.tenancyAccess {
39+
for key := range o.tenancyAccess {
4040
testResult = false
4141

4242
if tmode == "multitenancy" && tenv == "OCI Instance" {
@@ -118,7 +118,7 @@ func (o *OCIDatasource) GetTenancies(ctx context.Context) []models.OCIResource {
118118
backend.Logger.Error("client", "GetTenancies", "fetching the tenancies")
119119

120120
tenancyList := []models.OCIResource{}
121-
for key, _ := range o.tenancyAccess {
121+
for key := range o.tenancyAccess {
122122
// frame.AppendRow(*(common.String(key)))
123123

124124
tenancyList = append(tenancyList, models.OCIResource{
@@ -421,7 +421,7 @@ func (o *OCIDatasource) GetNamespaceWithMetricNames(
421421
// Links:
422422
// https://docs.oracle.com/en-us/iaas/Content/Identity/Reference/monitoringpolicyreference.htm
423423
// https://docs.oracle.com/en-us/iaas/api/#/en/monitoring/20180401/MetricData/SummarizeMetricsData
424-
func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams models.MetricsDataRequest, tenancyOCID string) ([]time.Time, []models.OCIMetricDataPoints) {
424+
func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams models.MetricsDataRequest, tenancyOCID string) ([]time.Time, []models.OCIMetricDataPoints, error) {
425425
backend.Logger.Error("client", "GetMetricDataPoints", "fetching the metrics datapoints under compartment '"+requestParams.CompartmentOCID+"' for query '"+requestParams.QueryText+"'")
426426

427427
times := []time.Time{}
@@ -441,7 +441,7 @@ func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams m
441441

442442
if len(takey) == 0 {
443443
backend.Logger.Warn("client", "GetMetricDataPoints", "invalid takey")
444-
return nil, nil
444+
return nil, nil, errors.New("Datasource not configured (invalid takey)")
445445
}
446446

447447
metricsDataRequest := monitoring.SummarizeMetricsDataRequest{
@@ -481,17 +481,18 @@ func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams m
481481

482482
// fetching the metrics data for specified region in parallel
483483
var wg sync.WaitGroup
484+
errCh := make(chan error)
484485
for _, subscribedRegion := range subscribedRegions {
485486
if subscribedRegion != constants.ALL_REGION {
486487
wg.Add(1)
487-
go func(mc monitoring.MonitoringClient, sRegion string) {
488+
go func(mc monitoring.MonitoringClient, sRegion string, errCh chan error) {
488489
defer wg.Done()
489490

490491
mc.SetRegion(sRegion)
491492
resp, err := mc.SummarizeMetricsData(ctx, metricsDataRequest)
492493
if err != nil {
493494
backend.Logger.Error("client", "GetMetricDataPoints", err)
494-
return
495+
errCh <- err
495496
}
496497

497498
if len(resp.Items) > 0 {
@@ -516,7 +517,14 @@ func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams m
516517
resourceLabels: rl,
517518
})
518519
}
519-
}(o.tenancyAccess[takey].monitoringClient, subscribedRegion)
520+
errCh <- nil
521+
}(o.tenancyAccess[takey].monitoringClient, subscribedRegion, errCh)
522+
// Receive on the error channel
523+
err := <-errCh
524+
if err != nil {
525+
backend.Logger.Error("client", "GetMetricDataPoints", err)
526+
return nil, nil, err
527+
}
520528
}
521529
}
522530
wg.Wait()
@@ -686,7 +694,7 @@ func (o *OCIDatasource) GetMetricDataPoints(ctx context.Context, requestParams m
686694
dataPoints = append(dataPoints, dp)
687695
}
688696

689-
return times, dataPoints
697+
return times, dataPoints, nil
690698
}
691699

692700
// fetchFromCache will fetch value from cache and if it not present it will fetch via api and store to cache and return
@@ -731,15 +739,15 @@ func (o *OCIDatasource) GetTags(
731739
}
732740
}
733741

734-
compartments := []models.OCIResource{}
735-
if len(compartmentOCID) == 0 {
736-
compartments = append(compartments, o.GetCompartments(ctx, tenancyOCID)...)
737-
} else {
738-
compartments = append(compartments, models.OCIResource{
739-
Name: compartmentName,
740-
OCID: compartmentOCID,
741-
})
742-
}
742+
// compartments := []models.OCIResource{}
743+
// if len(compartmentOCID) == 0 {
744+
// compartments = append(compartments, o.GetCompartments(ctx, tenancyOCID)...)
745+
// } else {
746+
// compartments = append(compartments, models.OCIResource{
747+
// Name: compartmentName,
748+
// OCID: compartmentOCID,
749+
// })
750+
// }
743751

744752
// var ccc core.ComputeClient
745753
// //var vcc core.VirtualNetworkClient

pkg/plugin/plugin.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ import (
99
"fmt"
1010
"net/http"
1111
"reflect"
12-
"regexp"
1312
"strconv"
1413
"strings"
15-
"time"
1614

1715
"github.com/pkg/errors"
1816

@@ -37,22 +35,17 @@ const NoTenancy = "NoTenancy"
3735
var EmptyString string = ""
3836
var EmptyKeyPass *string = &EmptyString
3937

40-
var (
41-
cacheRefreshTime = time.Minute // how often to refresh our compartmentID cache
42-
re = regexp.MustCompile(`(?m)\w+Name`)
43-
)
44-
4538
type TenancyAccess struct {
4639
monitoringClient monitoring.MonitoringClient
4740
identityClient identity.IdentityClient
4841
config common.ConfigurationProvider
4942
}
5043

5144
type OCIDatasource struct {
52-
tenancyAccess map[string]*TenancyAccess
53-
logger log.Logger
54-
nameToOCID map[string]string
55-
timeCacheUpdated time.Time
45+
tenancyAccess map[string]*TenancyAccess
46+
logger log.Logger
47+
nameToOCID map[string]string
48+
// timeCacheUpdated time.Time
5649
backend.CallResourceHandler
5750
// clients *client.OCIClients
5851
settings *models.OCIDatasourceSettings
@@ -301,7 +294,7 @@ func (o *OCIDatasource) getConfigProvider(environment string, tenancymode string
301294
if err != nil {
302295
return errors.New("Error Loading config settings")
303296
}
304-
for key, _ := range q.tenancyocid {
297+
for key := range q.tenancyocid {
305298
var configProvider common.ConfigurationProvider
306299
if tenancymode != "multitenancy" {
307300
if key != "DEFAULT" {

pkg/plugin/query.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package plugin
55

66
import (
77
"context"
8+
"time"
89

910
"github.com/grafana/grafana-plugin-sdk-go/backend"
1011
"github.com/grafana/grafana-plugin-sdk-go/data"
@@ -57,8 +58,19 @@ func (ocidx *OCIDatasource) query(ctx context.Context, pCtx backend.PluginContex
5758

5859
// create data frame response
5960
frame := data.NewFrame("response").SetMeta(&data.FrameMeta{ExecutedQueryString: qm.QueryText})
60-
61-
times, metricDataValues := ocidx.GetMetricDataPoints(ctx, metricsDataRequest, qm.TenancyOCID)
61+
var err error
62+
var metricDataValues []models.OCIMetricDataPoints
63+
var times []time.Time
64+
65+
if (qm.Region != "" && qm.Region != "select region") &&
66+
(qm.CompartmentOCID != "" && qm.CompartmentOCID != "select compartment") &&
67+
(qm.Namespace != "" && qm.Namespace != "select namespace") {
68+
times, metricDataValues, err = ocidx.GetMetricDataPoints(ctx, metricsDataRequest, qm.TenancyOCID)
69+
}
70+
if err != nil {
71+
response.Error = err
72+
return response
73+
}
6274

6375
// plotting the x axis with time as unit
6476
frame.Fields = append(frame.Fields, data.NewField("time", nil, times))
@@ -88,10 +100,8 @@ func (ocidx *OCIDatasource) query(ctx context.Context, pCtx backend.PluginContex
88100

89101
// Create a new slice for each key in the map
90102
var values []string
103+
values = append(values, dimension.Values...)
91104

92-
for _, vall := range dimension.Values {
93-
values = append(values, vall)
94-
}
95105
// Assign the values slice to the map key
96106
OriginalDimensionMap[key] = values
97107
}

pkg/plugin/utils.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func listMetricsMetadataFromAllRegion(
8686
go func(mc monitoring.MonitoringClient, sRegion string) {
8787
defer wg.Done()
8888

89-
newCacheKey := strings.ReplaceAll(cacheKey, constants.ALL_REGION, subscribedRegion)
89+
newCacheKey := strings.ReplaceAll(cacheKey, constants.ALL_REGION, sRegion)
9090
metadata := listMetricsMetadataPerRegion(ctx, ci, newCacheKey, fetchFor, mc, req)
9191

9292
if len(metadata) > 0 {
@@ -627,7 +627,7 @@ func (o *OCIDatasource) generateCustomMetricLabel(legendFormat string, metricNam
627627
}
628628

629629
for _, placeholderStr := range rePlaceholderLabel.FindAllString(metricLabel, -1) {
630-
if rePlaceholderLabel.Match([]byte(placeholderStr)) == true {
630+
if rePlaceholderLabel.Match([]byte(placeholderStr)) {
631631
matches := rePlaceholderLabel.FindStringSubmatch(placeholderStr)
632632
labelIndex := rePlaceholderLabel.SubexpIndex("label")
633633

src/QueryEditor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export const QueryEditor: React.FC<Props> = (props) => {
267267

268268
const getIntervalOptions = async () => {
269269
let options: Array<SelectableValue<string>> = [];
270+
options = addTemplateVariablesToOptions(options);
270271
IntervalOptions.forEach((item: any) => {
271272
const sv: SelectableValue<any> = {
272273
label: item.label,

src/datasource.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,20 @@ export class OCIDataSource extends DataSourceWithBackend<OCIQuery, OCIDataSource
5151
return true;
5252
}
5353

54+
SetAutoInterval(timestamp1: number, timestamp2: number): string {
55+
const differenceInMs = timestamp2 - timestamp1;
56+
const differenceInHours = differenceInMs / (1000 * 60 * 60);
57+
58+
// use limits and defaults specified here: https://docs.oracle.com/en-us/iaas/Content/Monitoring/Reference/mql.htm#Interval
59+
if (differenceInHours <= 6) {
60+
return "[1m]"; // Equal or Less than 6 hours, set to 1 minute interval
61+
} else if (differenceInHours < 36) {
62+
return "[5m]"; // Between 6 and 36 hours, set to 5 minute interval
63+
} else {
64+
return "[1h]"; // More than 36 hours, set to 1 hour interval
65+
}
66+
}
67+
5468
compartmentFormatter = (value: string): string => {
5569
// if (typeof value === 'string') {
5670
// return value;
@@ -71,6 +85,14 @@ export class OCIDataSource extends DataSourceWithBackend<OCIQuery, OCIDataSource
7185
applyTemplateVariables(query: OCIQuery, scopedVars: ScopedVars) {
7286
const templateSrv = getTemplateSrv();
7387

88+
const TimeStart = parseInt(getTemplateSrv().replace("${__from}"), 10)
89+
const TimeEnd = parseInt(getTemplateSrv().replace("${__to}"), 10)
90+
if (this.isVariable(query.interval)) {
91+
query.interval = templateSrv.replace(query.interval, scopedVars);
92+
}
93+
if (query.interval === QueryPlaceholder.Interval || query.interval === "auto" || query.interval === undefined){
94+
query.interval = this.SetAutoInterval(TimeStart, TimeEnd);
95+
}
7496
query.region = templateSrv.replace(query.region, scopedVars);
7597
query.tenancy = templateSrv.replace(query.tenancy, scopedVars);
7698
query.compartment = templateSrv.replace(query.compartment, scopedVars, this.compartmentFormatter);
@@ -110,20 +132,15 @@ export class OCIDataSource extends DataSourceWithBackend<OCIQuery, OCIDataSource
110132

111133
interpolateProps<T extends Record<string, any>>(object: T, scopedVars: ScopedVars = {}): T {
112134
const templateSrv = getTemplateSrv();
113-
return Object.entries(object).reduce((acc, [key, value]) => {
114-
if (key === "compartment"){
115-
return {
116-
...acc,
117-
[key]: value && isString(value) ? templateSrv.replace(value, scopedVars, this.compartmentFormatter) : value,
118-
};
135+
return Object.entries(object).reduce((acc: any, [key, value]) => {
136+
if (value && isString(value)) {
137+
const formatter = key === "compartment" ? this.compartmentFormatter : undefined;
138+
acc[key] = templateSrv.replace(value, scopedVars, formatter);
119139
} else {
120-
return {
121-
...acc,
122-
[key]: value && isString(value) ? templateSrv.replace(value, scopedVars) : value,
123-
};
140+
acc[key] = value;
124141
}
125-
126-
}, {} as T);
142+
return acc as T;
143+
}, {});
127144
}
128145

129146
// // **************************** Template variable helpers ****************************
@@ -133,7 +150,6 @@ export class OCIDataSource extends DataSourceWithBackend<OCIQuery, OCIDataSource
133150
// * Example:
134151
// * template variable with the query "regions()" will be matched with the regionsQueryRegex and list of available regions will be returned.
135152
// */
136-
// metricFindQuery?(query: any, options?: any): Promise<MetricFindValue[]> {
137153

138154
async metricFindQuery?(query: any, options?: any): Promise<MetricFindValue[]> {
139155
const templateSrv = getTemplateSrv();

src/query_model.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
44
*/
55

6-
import { OCIQuery, QueryPlaceholder, AggregationOptions, IntervalOptions } from './types';
6+
import { OCIQuery, QueryPlaceholder, AggregationOptions } from './types';
77
import { ScopedVars } from '@grafana/data';
88
import { TemplateSrv } from '@grafana/runtime';
99

@@ -64,50 +64,45 @@ export default class QueryModel {
6464

6565
return true;
6666
}
67-
6867

69-
buildQuery(queryText: string) {
7068

69+
buildQuery(queryText: string) {
70+
//check if a raw query is being used
7171
if (this.target.queryTextRaw !== '' && this.target.rawQuery === false) {
7272
queryText = String(this.target.queryTextRaw);
7373
} else {
74-
// for default interval
75-
if (this.target.interval === QueryPlaceholder.Interval) {
76-
this.target.interval = IntervalOptions[0].value;
77-
}
74+
// if builder mode is used then:
75+
76+
// add interval
7877
queryText += this.target.interval;
7978

80-
// for dimensions
79+
// add dimensions
8180
let dimensionParams = '{';
8281
let noOfDimensions = this.target.dimensionValues?.length ?? 0;
8382
if (noOfDimensions !== 0) {
8483
this.target.dimensionValues?.forEach((dv) => {
8584
dimensionParams += dv;
8685
noOfDimensions--;
87-
8886
if (noOfDimensions !== 0) {
8987
dimensionParams += ',';
9088
}
9189
});
9290
dimensionParams += '}';
93-
9491
queryText += dimensionParams;
9592
}
9693

97-
// for groupBy option
94+
// add groupBy option
9895
if (this.target.groupBy !== QueryPlaceholder.GroupBy) {
9996
queryText += '.groupBy(' + this.target.groupBy + ')';
10097
}
10198

102-
// for default statistics
99+
// add default statistics
103100
if (this.target.statistic === QueryPlaceholder.Aggregation) {
104101
this.target.statistic = AggregationOptions[0].value;
105102
}
106-
107103
queryText += '.' + this.target.statistic;
108104
}
109105

110-
111106
return queryText;
112107
}
113108
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const IntervalOptions = [
5959
{ label: '5 minutes', value: '[5m]', description: 'Maximum time range supported: 30 days' },
6060
{ label: '1 hour', value: '[1h]', description: 'Maximum time range supported: 90 days' },
6161
{ label: '1 day', value: '[1d]', description: 'Maximum time range supported: 90 days' },
62+
{ label: 'Auto', value: 'auto', description: 'Automatic selection of interval accordingly to OCI default' },
6263
];
6364

6465
export const AggregationOptions = [

0 commit comments

Comments
 (0)