mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
570 lines
18 KiB
TypeScript
570 lines
18 KiB
TypeScript
import { defaultsDeep, find, map, isString } from 'lodash';
|
||
import { QueryCtrl } from 'app/plugins/sdk';
|
||
import TimegrainConverter from './time_grain_converter';
|
||
import './editor/editor_component';
|
||
|
||
import { TemplateSrv } from '@grafana/runtime';
|
||
import { auto } from 'angular';
|
||
import { DataFrame, PanelEvents } from '@grafana/data';
|
||
import { AzureQueryType, AzureMetricQuery, AzureMonitorQuery } from './types';
|
||
import { convertTimeGrainsToMs } from './utils/common';
|
||
import Datasource from './datasource';
|
||
|
||
export interface ResultFormat {
|
||
text: string;
|
||
value: string;
|
||
}
|
||
|
||
export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||
static templateUrl = 'partials/query.editor.html';
|
||
|
||
defaultDropdownValue = 'select';
|
||
|
||
dummyDiminsionString = '+';
|
||
|
||
queryQueryTypeOptions = [
|
||
{ id: AzureQueryType.AzureMonitor, label: 'Metrics' },
|
||
{ id: AzureQueryType.LogAnalytics, label: 'Logs' },
|
||
{ id: AzureQueryType.ApplicationInsights, label: 'Application Insights' },
|
||
{ id: AzureQueryType.InsightsAnalytics, label: 'Insights Analytics' },
|
||
{ id: AzureQueryType.AzureResourceGraph, label: 'Azure Resource Graph' },
|
||
];
|
||
|
||
// Query types that have been migrated to React
|
||
reactQueryEditors = [
|
||
AzureQueryType.AzureMonitor,
|
||
AzureQueryType.LogAnalytics,
|
||
AzureQueryType.ApplicationInsights,
|
||
AzureQueryType.InsightsAnalytics,
|
||
AzureQueryType.AzureResourceGraph,
|
||
];
|
||
|
||
// target: AzureMonitorQuery;
|
||
|
||
declare target: {
|
||
// should be: AzureMonitorQuery
|
||
refId: string;
|
||
queryType: AzureQueryType;
|
||
subscription: string;
|
||
subscriptions: string[];
|
||
azureMonitor: AzureMetricQuery;
|
||
azureLogAnalytics: {
|
||
query: string;
|
||
resultFormat: string;
|
||
workspace: string;
|
||
};
|
||
azureResourceGraph: {
|
||
query: string;
|
||
resultFormat: string;
|
||
};
|
||
appInsights: {
|
||
// metric style query when rawQuery == false
|
||
metricName: string;
|
||
dimension: any;
|
||
dimensionFilter: string;
|
||
dimensions: string[];
|
||
|
||
aggOptions: string[];
|
||
aggregation: string;
|
||
|
||
timeGrainType: string;
|
||
timeGrainCount: string;
|
||
timeGrainUnit: string;
|
||
timeGrain: string;
|
||
timeGrains: Array<{ text: string; value: string }>;
|
||
allowedTimeGrainsMs: number[];
|
||
};
|
||
insightsAnalytics: {
|
||
query: any;
|
||
resultFormat: string;
|
||
};
|
||
};
|
||
|
||
defaults = {
|
||
queryType: 'Azure Monitor',
|
||
azureMonitor: {
|
||
resourceGroup: undefined,
|
||
metricDefinition: undefined,
|
||
resourceName: undefined,
|
||
metricNamespace: undefined,
|
||
metricName: undefined,
|
||
dimensionFilter: '*',
|
||
timeGrain: 'auto',
|
||
top: '10',
|
||
aggOptions: [] as string[],
|
||
timeGrains: [] as string[],
|
||
},
|
||
azureLogAnalytics: {
|
||
query: [
|
||
'//change this example to create your own time series query',
|
||
'<table name> ' +
|
||
'//the table to query (e.g. Usage, Heartbeat, Perf)',
|
||
'| where $__timeFilter(TimeGenerated) ' +
|
||
'//this is a macro used to show the full chart’s time range, choose the datetime column here',
|
||
'| summarize count() by <group by column>, bin(TimeGenerated, $__interval) ' +
|
||
'//change “group by column” to a column in your table, such as “Computer”. ' +
|
||
'The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.',
|
||
'| order by TimeGenerated asc',
|
||
].join('\n'),
|
||
resultFormat: 'time_series',
|
||
workspace:
|
||
this.datasource && this.datasource.azureLogAnalyticsDatasource
|
||
? this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace
|
||
: '',
|
||
},
|
||
azureResourceGraph: {
|
||
resultFormat: 'table',
|
||
},
|
||
appInsights: {
|
||
metricName: this.defaultDropdownValue,
|
||
// dimension: [],
|
||
timeGrain: 'auto',
|
||
},
|
||
insightsAnalytics: {
|
||
query: '',
|
||
resultFormat: 'time_series',
|
||
},
|
||
};
|
||
|
||
resultFormats: ResultFormat[];
|
||
workspaces: any[] = [];
|
||
showHelp = false;
|
||
showLastQuery = false;
|
||
lastQuery = '';
|
||
lastQueryError?: string;
|
||
subscriptions: Array<{ text: string; value: string }> = [];
|
||
|
||
/** @ngInject */
|
||
constructor($scope: any, $injector: auto.IInjectorService, private templateSrv: TemplateSrv) {
|
||
super($scope, $injector);
|
||
|
||
defaultsDeep(this.target, this.defaults);
|
||
|
||
this.migrateTimeGrains();
|
||
|
||
this.migrateToFromTimes();
|
||
|
||
this.migrateToDefaultNamespace();
|
||
|
||
this.migrateApplicationInsightsKeys();
|
||
|
||
this.migrateApplicationInsightsDimensions();
|
||
|
||
migrateMetricsDimensionFilters(this.target.azureMonitor);
|
||
|
||
this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope);
|
||
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
|
||
this.resultFormats = [
|
||
{ text: 'Time series', value: 'time_series' },
|
||
{ text: 'Table', value: 'table' },
|
||
];
|
||
this.getSubscriptions();
|
||
if (this.target.queryType === 'Azure Log Analytics') {
|
||
this.getWorkspaces();
|
||
}
|
||
}
|
||
|
||
onDataReceived(dataList: DataFrame[]) {
|
||
this.lastQueryError = undefined;
|
||
this.lastQuery = '';
|
||
|
||
const anySeriesFromQuery: any = find(dataList, { refId: this.target.refId });
|
||
if (anySeriesFromQuery && anySeriesFromQuery.meta) {
|
||
this.lastQuery = anySeriesFromQuery.meta.query;
|
||
}
|
||
}
|
||
|
||
onDataError(err: any) {
|
||
this.handleQueryCtrlError(err);
|
||
}
|
||
|
||
handleQueryCtrlError(err: any) {
|
||
if (err.query && err.query.refId && err.query.refId !== this.target.refId) {
|
||
return;
|
||
}
|
||
|
||
if (err.error && err.error.data && err.error.data.error && err.error.data.error.innererror) {
|
||
if (err.error.data.error.innererror.innererror) {
|
||
this.lastQueryError = err.error.data.error.innererror.innererror.message;
|
||
} else {
|
||
this.lastQueryError = err.error.data.error.innererror.message;
|
||
}
|
||
} else if (err.error && err.error.data && err.error.data.error) {
|
||
this.lastQueryError = err.error.data.error.message;
|
||
} else if (err.error && err.error.data) {
|
||
this.lastQueryError = err.error.data.message;
|
||
} else if (err.data && err.data.error) {
|
||
this.lastQueryError = err.data.error.message;
|
||
} else if (err.data && err.data.message) {
|
||
this.lastQueryError = err.data.message;
|
||
} else {
|
||
this.lastQueryError = err;
|
||
}
|
||
}
|
||
|
||
migrateTimeGrains() {
|
||
if (this.target.azureMonitor.timeGrainUnit) {
|
||
if (this.target.azureMonitor.timeGrain !== 'auto') {
|
||
this.target.azureMonitor.timeGrain = TimegrainConverter.createISO8601Duration(
|
||
this.target.azureMonitor.timeGrain ?? 'auto',
|
||
this.target.azureMonitor.timeGrainUnit
|
||
);
|
||
}
|
||
|
||
delete this.target.azureMonitor.timeGrainUnit;
|
||
}
|
||
|
||
if (this.target.appInsights.timeGrainUnit) {
|
||
if (this.target.appInsights.timeGrain !== 'auto') {
|
||
if (this.target.appInsights.timeGrainCount) {
|
||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||
this.target.appInsights.timeGrainCount,
|
||
this.target.appInsights.timeGrainUnit
|
||
);
|
||
} else {
|
||
this.target.appInsights.timeGrainCount = this.target.appInsights.timeGrain;
|
||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||
this.target.appInsights.timeGrain,
|
||
this.target.appInsights.timeGrainUnit
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
const oldAzureTimeGrains = (this.target.azureMonitor as any).timeGrains;
|
||
if (
|
||
oldAzureTimeGrains &&
|
||
oldAzureTimeGrains.length > 0 &&
|
||
(!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
|
||
) {
|
||
this.target.azureMonitor.allowedTimeGrainsMs = convertTimeGrainsToMs(oldAzureTimeGrains);
|
||
}
|
||
|
||
if (
|
||
this.target.appInsights.timeGrains &&
|
||
this.target.appInsights.timeGrains.length > 0 &&
|
||
(!this.target.appInsights.allowedTimeGrainsMs || this.target.appInsights.allowedTimeGrainsMs.length === 0)
|
||
) {
|
||
this.target.appInsights.allowedTimeGrainsMs = convertTimeGrainsToMs(this.target.appInsights.timeGrains);
|
||
}
|
||
}
|
||
|
||
migrateToFromTimes() {
|
||
this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__from\s/gi, '$__timeFrom() ');
|
||
this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() ');
|
||
}
|
||
|
||
async migrateToDefaultNamespace() {
|
||
if (
|
||
this.target.azureMonitor.metricNamespace &&
|
||
this.target.azureMonitor.metricNamespace !== this.defaultDropdownValue &&
|
||
this.target.azureMonitor.metricDefinition
|
||
) {
|
||
return;
|
||
}
|
||
|
||
this.target.azureMonitor.metricNamespace = this.target.azureMonitor.metricDefinition;
|
||
}
|
||
|
||
migrateApplicationInsightsKeys(): void {
|
||
const appInsights = this.target.appInsights as any;
|
||
|
||
// Migrate old app insights data keys to match other datasources
|
||
const mappings = {
|
||
xaxis: 'timeColumn',
|
||
yaxis: 'valueColumn',
|
||
spliton: 'segmentColumn',
|
||
groupBy: 'dimension',
|
||
groupByOptions: 'dimensions',
|
||
filter: 'dimensionFilter',
|
||
} as { [old: string]: string };
|
||
|
||
for (const old in mappings) {
|
||
if (appInsights[old]) {
|
||
appInsights[mappings[old]] = appInsights[old];
|
||
delete appInsights[old];
|
||
}
|
||
}
|
||
}
|
||
|
||
migrateApplicationInsightsDimensions() {
|
||
const { appInsights } = this.target;
|
||
|
||
if (!appInsights.dimension) {
|
||
appInsights.dimension = [];
|
||
}
|
||
|
||
if (isString(appInsights.dimension)) {
|
||
appInsights.dimension = [appInsights.dimension as string];
|
||
}
|
||
}
|
||
|
||
replace = (variable: string) => {
|
||
return this.templateSrv.replace(variable, this.panelCtrl.panel.scopedVars);
|
||
};
|
||
|
||
onQueryTypeChange() {
|
||
if (this.target.queryType === 'Azure Log Analytics') {
|
||
return this.getWorkspaces();
|
||
}
|
||
}
|
||
|
||
getSubscriptions() {
|
||
if (!this.datasource.azureMonitorDatasource.isConfigured()) {
|
||
return;
|
||
}
|
||
|
||
// assert the type
|
||
if (!(this.datasource instanceof Datasource)) {
|
||
return;
|
||
}
|
||
|
||
return this.datasource.azureMonitorDatasource.getSubscriptions().then((subscriptions) => {
|
||
// We changed the format in the datasource for the new react stuff, so here we change it back
|
||
const subs = subscriptions.map((v) => ({
|
||
text: `${v.text} - ${v.value}`,
|
||
value: v.value,
|
||
}));
|
||
|
||
this.subscriptions = subs;
|
||
if (!this.target.subscription && this.target.queryType === 'Azure Monitor') {
|
||
this.target.subscription = this.datasource.azureMonitorDatasource.subscriptionId;
|
||
} else if (!this.target.subscription && this.target.queryType === 'Azure Log Analytics') {
|
||
this.target.subscription = this.datasource.azureLogAnalyticsDatasource.subscriptionId;
|
||
}
|
||
|
||
if (!this.target.subscription && this.subscriptions.length > 0) {
|
||
this.target.subscription = this.subscriptions[0].value;
|
||
}
|
||
|
||
if (!this.target.subscriptions) {
|
||
this.target.subscriptions = subscriptions.map((sub) => sub.value);
|
||
}
|
||
|
||
return this.subscriptions;
|
||
});
|
||
}
|
||
|
||
onSubscriptionChange() {
|
||
if (this.target.queryType === 'Azure Log Analytics') {
|
||
return this.getWorkspaces();
|
||
}
|
||
}
|
||
|
||
generateAutoUnits(timeGrain: string, timeGrains: Array<{ value: string }>) {
|
||
if (timeGrain === 'auto') {
|
||
return TimegrainConverter.findClosestTimeGrain(
|
||
'1m',
|
||
map(timeGrains, (o) => TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)) || [
|
||
'1m',
|
||
'5m',
|
||
'15m',
|
||
'30m',
|
||
'1h',
|
||
'6h',
|
||
'12h',
|
||
'1d',
|
||
]
|
||
);
|
||
}
|
||
|
||
return '';
|
||
}
|
||
|
||
getAzureMonitorAutoInterval() {
|
||
return this.generateAutoUnits(
|
||
this.target.azureMonitor.timeGrain ?? 'auto',
|
||
(this.target.azureMonitor as any).timeGrains
|
||
);
|
||
}
|
||
|
||
getApplicationInsightAutoInterval() {
|
||
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
|
||
}
|
||
|
||
azureMonitorAddDimensionFilter() {
|
||
this.target.azureMonitor = this.target.azureMonitor ?? {};
|
||
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
|
||
|
||
this.target.azureMonitor.dimensionFilters.push({
|
||
dimension: '',
|
||
operator: 'eq',
|
||
filter: '',
|
||
});
|
||
}
|
||
|
||
azureMonitorRemoveDimensionFilter(index: number) {
|
||
this.target.azureMonitor = this.target.azureMonitor ?? {};
|
||
this.target.azureMonitor.dimensionFilters = this.target.azureMonitor.dimensionFilters ?? [];
|
||
|
||
this.target.azureMonitor.dimensionFilters.splice(index, 1);
|
||
this.refresh();
|
||
}
|
||
|
||
/* Azure Log Analytics */
|
||
|
||
getWorkspaces = () => {
|
||
return this.datasource.azureLogAnalyticsDatasource
|
||
.getWorkspaces(this.target.subscription)
|
||
.then((list: any[]) => {
|
||
this.workspaces = list;
|
||
|
||
if (list.length > 0 && !this.target.azureLogAnalytics.workspace) {
|
||
if (this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace) {
|
||
this.target.azureLogAnalytics.workspace = this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace;
|
||
}
|
||
|
||
if (!this.target.azureLogAnalytics.workspace) {
|
||
this.target.azureLogAnalytics.workspace = list[0].value;
|
||
}
|
||
}
|
||
|
||
return this.workspaces;
|
||
})
|
||
.catch(this.handleQueryCtrlError.bind(this));
|
||
};
|
||
|
||
getAzureLogAnalyticsSchema = () => {
|
||
return this.getWorkspaces()
|
||
.then(() => {
|
||
return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace);
|
||
})
|
||
.catch(this.handleQueryCtrlError.bind(this));
|
||
};
|
||
|
||
onLogAnalyticsQueryChange = (nextQuery: string) => {
|
||
this.target.azureLogAnalytics.query = nextQuery;
|
||
};
|
||
|
||
onLogAnalyticsQueryExecute = () => {
|
||
this.panelCtrl.refresh();
|
||
};
|
||
|
||
get templateVariables() {
|
||
return this.templateSrv.getVariables().map((t) => '$' + t.name);
|
||
}
|
||
|
||
getAppInsightsMetricNames() {
|
||
if (!this.datasource.appInsightsDatasource.isConfigured()) {
|
||
return;
|
||
}
|
||
|
||
return this.datasource.getAppInsightsMetricNames().catch(this.handleQueryCtrlError.bind(this));
|
||
}
|
||
|
||
getAppInsightsColumns() {
|
||
return this.datasource.getAppInsightsColumns(this.target.refId);
|
||
}
|
||
|
||
onAppInsightsColumnChange() {
|
||
return this.refresh();
|
||
}
|
||
|
||
onAppInsightsMetricNameChange() {
|
||
if (!this.target.appInsights.metricName || this.target.appInsights.metricName === this.defaultDropdownValue) {
|
||
return;
|
||
}
|
||
|
||
return this.datasource
|
||
.getAppInsightsMetricMetadata(this.replace(this.target.appInsights.metricName))
|
||
.then((aggData: { supportedAggTypes: string[]; supportedGroupBy: string[]; primaryAggType: string }) => {
|
||
this.target.appInsights.aggOptions = aggData.supportedAggTypes;
|
||
this.target.appInsights.dimensions = aggData.supportedGroupBy;
|
||
this.target.appInsights.aggregation = aggData.primaryAggType;
|
||
return this.refresh();
|
||
})
|
||
.catch(this.handleQueryCtrlError.bind(this));
|
||
}
|
||
|
||
onInsightsAnalyticsQueryChange = (nextQuery: string) => {
|
||
this.target.insightsAnalytics.query = nextQuery;
|
||
};
|
||
|
||
onQueryExecute = () => {
|
||
return this.refresh();
|
||
};
|
||
|
||
getAppInsightsQuerySchema = () => {
|
||
return this.datasource.appInsightsDatasource.getQuerySchema().catch(this.handleQueryCtrlError.bind(this));
|
||
};
|
||
|
||
removeGroupBy = (index: number) => {
|
||
const { appInsights } = this.target;
|
||
appInsights.dimension.splice(index, 1);
|
||
this.refresh();
|
||
};
|
||
|
||
getAppInsightsGroupBySegments(query: any) {
|
||
const { appInsights } = this.target;
|
||
|
||
// HACK alert... there must be a better way!
|
||
if (this.dummyDiminsionString && this.dummyDiminsionString.length && '+' !== this.dummyDiminsionString) {
|
||
if (!appInsights.dimension) {
|
||
appInsights.dimension = [];
|
||
}
|
||
appInsights.dimension.push(this.dummyDiminsionString);
|
||
this.dummyDiminsionString = '+';
|
||
this.refresh();
|
||
}
|
||
|
||
// Return the list of dimensions stored on the query object from the last request :(
|
||
return map(appInsights.dimensions, (option: string) => {
|
||
return { text: option, value: option };
|
||
});
|
||
}
|
||
|
||
resetAppInsightsGroupBy() {
|
||
this.target.appInsights.dimension = 'none';
|
||
this.refresh();
|
||
}
|
||
|
||
updateTimeGrainType() {
|
||
if (this.target.appInsights.timeGrainType === 'specific') {
|
||
this.target.appInsights.timeGrainCount = '1';
|
||
this.target.appInsights.timeGrainUnit = 'minute';
|
||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||
this.target.appInsights.timeGrainCount,
|
||
this.target.appInsights.timeGrainUnit
|
||
);
|
||
} else {
|
||
this.target.appInsights.timeGrainCount = '';
|
||
this.target.appInsights.timeGrainUnit = '';
|
||
}
|
||
}
|
||
|
||
updateAppInsightsTimeGrain() {
|
||
if (this.target.appInsights.timeGrainUnit && this.target.appInsights.timeGrainCount) {
|
||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||
this.target.appInsights.timeGrainCount,
|
||
this.target.appInsights.timeGrainUnit
|
||
);
|
||
}
|
||
this.refresh();
|
||
}
|
||
|
||
/**
|
||
* Receives a full new query object from React and updates it into the Angular controller
|
||
*/
|
||
handleNewQuery = (newQuery: AzureMonitorQuery) => {
|
||
Object.assign(this.target, newQuery);
|
||
this.refresh();
|
||
};
|
||
}
|
||
|
||
// Modifies the actual query object
|
||
export function migrateMetricsDimensionFilters(item: AzureMetricQuery) {
|
||
if (!item.dimensionFilters) {
|
||
item.dimensionFilters = [];
|
||
}
|
||
const oldDimension = (item as any).dimension;
|
||
if (oldDimension && oldDimension !== 'None') {
|
||
item.dimensionFilters.push({
|
||
dimension: oldDimension,
|
||
operator: 'eq',
|
||
filter: (item as any).dimensionFilter,
|
||
});
|
||
delete (item as any).dimension;
|
||
delete (item as any).dimensionFilter;
|
||
}
|
||
}
|