mirror of
https://github.com/grafana/grafana.git
synced 2024-11-24 09:50:29 -06:00
AzureMonitor: Add deep links to Azure Portal for Metrics (#32273)
* Add Explore in azure portal functionality to Grafana AM data sources for Metrics * fix url * fix build * Fix the URL with metric and aggregation * user new version of metric blade * use lookup object instead of enum for converting aggregation type Co-authored-by: joshhunt <josh@trtr.co>
This commit is contained in:
parent
532b8d4bc2
commit
ad6010a7b3
@ -10,12 +10,34 @@ import {
|
||||
AzureMonitorResourceGroupsResponse,
|
||||
AzureQueryType,
|
||||
AzureMonitorMetricsMetadataResponse,
|
||||
AzureMetricQuery,
|
||||
} from '../types';
|
||||
import { DataSourceInstanceSettings, ScopedVars, MetricFindValue } from '@grafana/data';
|
||||
import {
|
||||
DataSourceInstanceSettings,
|
||||
ScopedVars,
|
||||
MetricFindValue,
|
||||
DataQueryResponse,
|
||||
DataQueryRequest,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { getBackendSrv, DataSourceWithBackend, getTemplateSrv, FetchResponse } from '@grafana/runtime';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { mergeMap } from 'rxjs/operators';
|
||||
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
|
||||
const defaultDropdownValue = 'select';
|
||||
|
||||
// Used to convert our aggregation value to the Azure enum for deep linking
|
||||
const aggregationTypeMap: Record<string, number> = {
|
||||
None: 0,
|
||||
Total: 1,
|
||||
Minimum: 2,
|
||||
Maximum: 3,
|
||||
Average: 4,
|
||||
Count: 7,
|
||||
};
|
||||
|
||||
export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureMonitorQuery, AzureDataSourceJsonData> {
|
||||
apiVersion = '2018-01-01';
|
||||
apiPreviewVersion = '2017-12-01-preview';
|
||||
@ -26,10 +48,12 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
url: string;
|
||||
cloudName: string;
|
||||
supportedMetricNamespaces: string[] = [];
|
||||
timeSrv: TimeSrv;
|
||||
|
||||
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.timeSrv = getTimeSrv();
|
||||
this.subscriptionId = instanceSettings.jsonData.subscriptionId;
|
||||
this.cloudName = instanceSettings.jsonData.cloudName || 'azuremonitor';
|
||||
this.baseUrl = `/${this.cloudName}/subscriptions`;
|
||||
@ -55,6 +79,83 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
);
|
||||
}
|
||||
|
||||
query(request: DataQueryRequest<AzureMonitorQuery>): Observable<DataQueryResponse> {
|
||||
const metricQueries = request.targets.reduce((prev: Record<string, AzureMonitorQuery>, cur) => {
|
||||
prev[cur.refId] = cur;
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
return super.query(request).pipe(
|
||||
mergeMap((res: DataQueryResponse) => {
|
||||
return from(this.processResponse(res, metricQueries));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async processResponse(
|
||||
res: DataQueryResponse,
|
||||
metricQueries: Record<string, AzureMonitorQuery>
|
||||
): Promise<DataQueryResponse> {
|
||||
if (res.data) {
|
||||
for (const df of res.data) {
|
||||
const metricQuery = metricQueries[df.refId]?.azureMonitor;
|
||||
if (metricQuery) {
|
||||
const url = this.buildAzurePortalUrl(metricQuery, this.subscriptionId, this.timeSrv.timeRange());
|
||||
|
||||
for (const field of df.fields) {
|
||||
field.config.links = [
|
||||
{
|
||||
url: url,
|
||||
title: 'View in Azure Portal',
|
||||
targetBlank: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
stringifyAzurePortalUrlParam(value: string | object): string {
|
||||
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
||||
return encodeURIComponent(stringValue);
|
||||
}
|
||||
|
||||
buildAzurePortalUrl(metricQuery: AzureMetricQuery, subscriptionId: string, timeRange: TimeRange) {
|
||||
const aggregationType = aggregationTypeMap[metricQuery.aggregation] ?? aggregationTypeMap.Average;
|
||||
|
||||
const chartDef = this.stringifyAzurePortalUrlParam({
|
||||
v2charts: [
|
||||
{
|
||||
metrics: [
|
||||
{
|
||||
resourceMetadata: {
|
||||
id: `/subscriptions/${subscriptionId}/resourceGroups/${metricQuery.resourceGroup}/providers/${metricQuery.metricDefinition}/${metricQuery.resourceName}`,
|
||||
},
|
||||
name: metricQuery.metricName,
|
||||
aggregationType: aggregationType,
|
||||
namespace: metricQuery.metricNamespace,
|
||||
metricVisualization: {
|
||||
displayName: metricQuery.metricName,
|
||||
resourceDisplayName: metricQuery.resourceName,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const timeContext = this.stringifyAzurePortalUrlParam({
|
||||
absolute: {
|
||||
startTime: timeRange.from,
|
||||
endTime: timeRange.to,
|
||||
},
|
||||
});
|
||||
|
||||
return `https://portal.azure.com/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/${timeContext}/ChartDefinition/${chartDef}`;
|
||||
}
|
||||
|
||||
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
|
||||
const item = target.azureMonitor;
|
||||
|
||||
|
@ -68,7 +68,7 @@ describe('Azure Monitor QueryEditor', () => {
|
||||
metricNamespace: undefined,
|
||||
resourceName: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: '',
|
||||
aggregation: 'None',
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ const NamespaceField: React.FC<AzureQueryEditorFieldProps> = ({
|
||||
resourceName: undefined,
|
||||
metricNamespace: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: '',
|
||||
aggregation: 'None',
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
},
|
||||
|
@ -47,7 +47,7 @@ const ResourceGroupsField: React.FC<AzureQueryEditorFieldProps> = ({
|
||||
resourceName: undefined,
|
||||
metricNamespace: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: '',
|
||||
aggregation: 'None',
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ const ResourceNameField: React.FC<AzureQueryEditorFieldProps> = ({
|
||||
|
||||
metricNamespace: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: '',
|
||||
aggregation: 'None',
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
},
|
||||
|
@ -84,7 +84,7 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
metricNamespace: undefined,
|
||||
resourceName: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: '',
|
||||
aggregation: 'None',
|
||||
timeGrain: '',
|
||||
dimensionFilters: [],
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user