123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- import {
- DataQuery,
- DataSourceRef,
- getDefaultRelativeTimeRange,
- IntervalValues,
- rangeUtil,
- RelativeTimeRange,
- ScopedVars,
- TimeRange,
- } from '@grafana/data';
- import { getDataSourceSrv } from '@grafana/runtime';
- import { ExpressionDatasourceRef } from '@grafana/runtime/src/utils/DataSourceWithBackend';
- import { getNextRefIdChar } from 'app/core/utils/query';
- import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
- import { ExpressionDatasourceUID } from 'app/features/expressions/ExpressionDatasource';
- import { ExpressionQuery, ExpressionQueryType } from 'app/features/expressions/types';
- import { RuleWithLocation } from 'app/types/unified-alerting';
- import {
- AlertQuery,
- Annotations,
- GrafanaAlertStateDecision,
- Labels,
- PostableRuleGrafanaRuleDTO,
- RulerRuleDTO,
- } from 'app/types/unified-alerting-dto';
- import { EvalFunction } from '../../state/alertDef';
- import { RuleFormType, RuleFormValues } from '../types/rule-form';
- import { getRulesAccess } from './access-control';
- import { Annotation } from './constants';
- import { getDefaultOrFirstCompatibleDataSource, isGrafanaRulesSource } from './datasource';
- import { arrayToRecord, recordToArray } from './misc';
- import { isAlertingRulerRule, isGrafanaRulerRule, isRecordingRulerRule } from './rules';
- import { parseInterval } from './time';
- export const getDefaultFormValues = (): RuleFormValues => {
- const { canCreateGrafanaRules, canCreateCloudRules } = getRulesAccess();
- return Object.freeze({
- name: '',
- labels: [{ key: '', value: '' }],
- annotations: [
- { key: Annotation.summary, value: '' },
- { key: Annotation.description, value: '' },
- { key: Annotation.runbookURL, value: '' },
- ],
- dataSourceName: null,
- type: canCreateGrafanaRules ? RuleFormType.grafana : canCreateCloudRules ? RuleFormType.cloudAlerting : undefined, // viewers can't create prom alerts
- group: '',
- // grafana
- folder: null,
- queries: [],
- condition: '',
- noDataState: GrafanaAlertStateDecision.NoData,
- execErrState: GrafanaAlertStateDecision.Alerting,
- evaluateEvery: '1m',
- evaluateFor: '5m',
- // cortex / loki
- namespace: '',
- expression: '',
- forTime: 1,
- forTimeUnit: 'm',
- });
- };
- export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO {
- const { name, expression, forTime, forTimeUnit, type } = values;
- if (type === RuleFormType.cloudAlerting) {
- return {
- alert: name,
- for: `${forTime}${forTimeUnit}`,
- annotations: arrayToRecord(values.annotations || []),
- labels: arrayToRecord(values.labels || []),
- expr: expression,
- };
- } else if (type === RuleFormType.cloudRecording) {
- return {
- record: name,
- labels: arrayToRecord(values.labels || []),
- expr: expression,
- };
- }
- throw new Error(`unexpected rule type: ${type}`);
- }
- function listifyLabelsOrAnnotations(item: Labels | Annotations | undefined): Array<{ key: string; value: string }> {
- return [...recordToArray(item || {}), { key: '', value: '' }];
- }
- export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): PostableRuleGrafanaRuleDTO {
- const { name, condition, noDataState, execErrState, evaluateFor, queries } = values;
- if (condition) {
- return {
- grafana_alert: {
- title: name,
- condition,
- no_data_state: noDataState,
- exec_err_state: execErrState,
- data: queries,
- },
- for: evaluateFor,
- annotations: arrayToRecord(values.annotations || []),
- labels: arrayToRecord(values.labels || []),
- };
- }
- throw new Error('Cannot create rule without specifying alert condition');
- }
- export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleFormValues {
- const { ruleSourceName, namespace, group, rule } = ruleWithLocation;
- const defaultFormValues = getDefaultFormValues();
- if (isGrafanaRulesSource(ruleSourceName)) {
- if (isGrafanaRulerRule(rule)) {
- const ga = rule.grafana_alert;
- return {
- ...defaultFormValues,
- name: ga.title,
- type: RuleFormType.grafana,
- group: group.name,
- evaluateFor: rule.for || '0',
- evaluateEvery: group.interval || defaultFormValues.evaluateEvery,
- noDataState: ga.no_data_state,
- execErrState: ga.exec_err_state,
- queries: ga.data,
- condition: ga.condition,
- annotations: listifyLabelsOrAnnotations(rule.annotations),
- labels: listifyLabelsOrAnnotations(rule.labels),
- folder: { title: namespace, id: ga.namespace_id },
- };
- } else {
- throw new Error('Unexpected type of rule for grafana rules source');
- }
- } else {
- if (isAlertingRulerRule(rule)) {
- const [forTime, forTimeUnit] = rule.for
- ? parseInterval(rule.for)
- : [defaultFormValues.forTime, defaultFormValues.forTimeUnit];
- return {
- ...defaultFormValues,
- name: rule.alert,
- type: RuleFormType.cloudAlerting,
- dataSourceName: ruleSourceName,
- namespace,
- group: group.name,
- expression: rule.expr,
- forTime,
- forTimeUnit,
- annotations: listifyLabelsOrAnnotations(rule.annotations),
- labels: listifyLabelsOrAnnotations(rule.labels),
- };
- } else if (isRecordingRulerRule(rule)) {
- return {
- ...defaultFormValues,
- name: rule.record,
- type: RuleFormType.cloudRecording,
- dataSourceName: ruleSourceName,
- namespace,
- group: group.name,
- expression: rule.expr,
- labels: listifyLabelsOrAnnotations(rule.labels),
- };
- } else {
- throw new Error('Unexpected type of rule for cloud rules source');
- }
- }
- }
- export const getDefaultQueries = (): AlertQuery[] => {
- const dataSource = getDefaultOrFirstCompatibleDataSource();
- if (!dataSource) {
- return [getDefaultExpression('A')];
- }
- const relativeTimeRange = getDefaultRelativeTimeRange();
- return [
- {
- refId: 'A',
- datasourceUid: dataSource.uid,
- queryType: '',
- relativeTimeRange,
- model: {
- refId: 'A',
- hide: false,
- },
- },
- getDefaultExpression('B'),
- ];
- };
- const getDefaultExpression = (refId: string): AlertQuery => {
- const model: ExpressionQuery = {
- refId,
- hide: false,
- type: ExpressionQueryType.classic,
- datasource: {
- uid: ExpressionDatasourceUID,
- type: ExpressionDatasourceRef.type,
- },
- conditions: [
- {
- type: 'query',
- evaluator: {
- params: [3],
- type: EvalFunction.IsAbove,
- },
- operator: {
- type: 'and',
- },
- query: {
- params: ['A'],
- },
- reducer: {
- params: [],
- type: 'last',
- },
- },
- ],
- };
- return {
- refId,
- datasourceUid: ExpressionDatasourceUID,
- queryType: '',
- model,
- };
- };
- const dataQueriesToGrafanaQueries = async (
- queries: DataQuery[],
- relativeTimeRange: RelativeTimeRange,
- scopedVars: ScopedVars | {},
- panelDataSourceRef?: DataSourceRef,
- maxDataPoints?: number,
- minInterval?: string
- ): Promise<AlertQuery[]> => {
- const result: AlertQuery[] = [];
- for (const target of queries) {
- const datasource = await getDataSourceSrv().get(target.datasource?.uid ? target.datasource : panelDataSourceRef);
- const dsRef = { uid: datasource.uid, type: datasource.type };
- const range = rangeUtil.relativeToTimeRange(relativeTimeRange);
- const { interval, intervalMs } = getIntervals(range, minInterval ?? datasource.interval, maxDataPoints);
- const queryVariables = {
- __interval: { text: interval, value: interval },
- __interval_ms: { text: intervalMs, value: intervalMs },
- ...scopedVars,
- };
- const interpolatedTarget = datasource.interpolateVariablesInQueries
- ? await datasource.interpolateVariablesInQueries([target], queryVariables)[0]
- : target;
- // expressions
- if (dsRef.uid === ExpressionDatasourceUID) {
- const newQuery: AlertQuery = {
- refId: interpolatedTarget.refId,
- queryType: '',
- relativeTimeRange,
- datasourceUid: ExpressionDatasourceUID,
- model: interpolatedTarget,
- };
- result.push(newQuery);
- // queries
- } else {
- const datasourceSettings = getDataSourceSrv().getInstanceSettings(dsRef);
- if (datasourceSettings && datasourceSettings.meta.alerting) {
- const newQuery: AlertQuery = {
- refId: interpolatedTarget.refId,
- queryType: interpolatedTarget.queryType ?? '',
- relativeTimeRange,
- datasourceUid: datasourceSettings.uid,
- model: {
- ...interpolatedTarget,
- maxDataPoints,
- intervalMs,
- },
- };
- result.push(newQuery);
- }
- }
- }
- return result;
- };
- export const panelToRuleFormValues = async (
- panel: PanelModel,
- dashboard: DashboardModel
- ): Promise<Partial<RuleFormValues> | undefined> => {
- const { targets } = panel;
- if (!panel.id || !dashboard.uid) {
- return undefined;
- }
- const relativeTimeRange = rangeUtil.timeRangeToRelative(rangeUtil.convertRawToRange(dashboard.time));
- const queries = await dataQueriesToGrafanaQueries(
- targets,
- relativeTimeRange,
- panel.scopedVars || {},
- panel.datasource ?? undefined,
- panel.maxDataPoints ?? undefined,
- panel.interval ?? undefined
- );
- // if no alerting capable queries are found, can't create a rule
- if (!queries.length || !queries.find((query) => query.datasourceUid !== ExpressionDatasourceUID)) {
- return undefined;
- }
- if (!queries.find((query) => query.datasourceUid === ExpressionDatasourceUID)) {
- queries.push(getDefaultExpression(getNextRefIdChar(queries.map((query) => query.model))));
- }
- const { folderId, folderTitle } = dashboard.meta;
- const formValues = {
- type: RuleFormType.grafana,
- folder:
- folderId && folderTitle
- ? {
- id: folderId,
- title: folderTitle,
- }
- : undefined,
- queries,
- name: panel.title,
- condition: queries[queries.length - 1].refId,
- annotations: [
- {
- key: Annotation.dashboardUID,
- value: dashboard.uid,
- },
- {
- key: Annotation.panelID,
- value: String(panel.id),
- },
- ],
- };
- return formValues;
- };
- export function getIntervals(range: TimeRange, lowLimit?: string, resolution?: number): IntervalValues {
- if (!resolution) {
- if (lowLimit && rangeUtil.intervalToMs(lowLimit) > 1000) {
- return {
- interval: lowLimit,
- intervalMs: rangeUtil.intervalToMs(lowLimit),
- };
- }
- return { interval: '1s', intervalMs: 1000 };
- }
- return rangeUtil.calculateInterval(range, resolution, lowLimit);
- }
|