From 187a8703c8521e5caab5504f095c396541b4c104 Mon Sep 17 00:00:00 2001 From: Nathan Rodman Date: Thu, 16 Dec 2021 10:17:24 -0800 Subject: [PATCH] Alerting: Add filters for AlertList panel (#43130) * add state filters for prom alerts * combine state filters * add datasource filter for panel * remove duplicate state check * show only prom, loki, and grafana datasources --- .../alerting/unified/utils/datasource.ts | 1 + .../panel/alertlist/AlertInstances.tsx | 22 ++++----- .../panel/alertlist/UnifiedAlertList.tsx | 27 ++++++++++- public/app/plugins/panel/alertlist/module.tsx | 48 +++++++++++-------- public/app/plugins/panel/alertlist/types.ts | 19 ++++---- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/public/app/features/alerting/unified/utils/datasource.ts b/public/app/features/alerting/unified/utils/datasource.ts index 186770bd77a..99bb03c9f2d 100644 --- a/public/app/features/alerting/unified/utils/datasource.ts +++ b/public/app/features/alerting/unified/utils/datasource.ts @@ -4,6 +4,7 @@ import { RulesSource } from 'app/types/unified-alerting'; import { getAllDataSources } from './config'; export const GRAFANA_RULES_SOURCE_NAME = 'grafana'; +export const GRAFANA_DATASOURCE_NAME = '-- Grafana --'; export enum DataSourceType { Alertmanager = 'alertmanager', diff --git a/public/app/plugins/panel/alertlist/AlertInstances.tsx b/public/app/plugins/panel/alertlist/AlertInstances.tsx index 01a6f8f494f..1b5a049b1d7 100644 --- a/public/app/plugins/panel/alertlist/AlertInstances.tsx +++ b/public/app/plugins/panel/alertlist/AlertInstances.tsx @@ -8,7 +8,6 @@ import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-aler import { UnifiedAlertListOptions } from './types'; import { AlertInstancesTable } from 'app/features/alerting/unified/components/rules/AlertInstancesTable'; import { sortAlerts } from 'app/features/alerting/unified/utils/misc'; -import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager'; interface Props { ruleWithLocation: PromRuleWithLocation; @@ -44,23 +43,22 @@ export const AlertInstances = ({ ruleWithLocation, options }: Props) => { }; function filterAlerts(options: PanelProps['options'], alerts: Alert[]): Alert[] { + const hasAlertState = Object.values(options.stateFilter).some((value) => value); let filteredAlerts = [...alerts]; - if (options.alertInstanceLabelFilter) { - const matchers = parseMatchers(options.alertInstanceLabelFilter || ''); - filteredAlerts = filteredAlerts.filter(({ labels }) => labelsMatchMatchers(labels, matchers)); - } - if (Object.values(options.alertInstanceStateFilter).some((value) => value)) { + if (hasAlertState) { filteredAlerts = filteredAlerts.filter((alert) => { return ( - (options.alertInstanceStateFilter.Alerting && alert.state === GrafanaAlertState.Alerting) || - (options.alertInstanceStateFilter.Pending && alert.state === GrafanaAlertState.Pending) || - (options.alertInstanceStateFilter.NoData && alert.state === GrafanaAlertState.NoData) || - (options.alertInstanceStateFilter.Normal && alert.state === GrafanaAlertState.Normal) || - (options.alertInstanceStateFilter.Error && alert.state === GrafanaAlertState.Error) + (options.stateFilter.firing && + (alert.state === GrafanaAlertState.Alerting || alert.state === PromAlertingRuleState.Firing)) || + (options.stateFilter.pending && + (alert.state === GrafanaAlertState.Pending || alert.state === PromAlertingRuleState.Pending)) || + (options.stateFilter.noData && alert.state === GrafanaAlertState.NoData) || + (options.stateFilter.normal && alert.state === GrafanaAlertState.Normal) || + (options.stateFilter.error && alert.state === GrafanaAlertState.Error) || + (options.stateFilter.inactive && alert.state === PromAlertingRuleState.Inactive) ); }); } - return filteredAlerts; } diff --git a/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx b/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx index 9f23e309470..e532878c896 100644 --- a/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx +++ b/public/app/plugins/panel/alertlist/UnifiedAlertList.tsx @@ -13,10 +13,15 @@ import { flattenRules, alertStateToState, getFirstActiveAt } from 'app/features/ import { PromRuleWithLocation } from 'app/types/unified-alerting'; import { fetchAllPromRulesAction } from 'app/features/alerting/unified/state/actions'; import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector'; -import { getAllRulesSourceNames } from 'app/features/alerting/unified/utils/datasource'; +import { + getAllRulesSourceNames, + GRAFANA_DATASOURCE_NAME, + GRAFANA_RULES_SOURCE_NAME, +} from 'app/features/alerting/unified/utils/datasource'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { Annotation, RULE_LIST_POLL_INTERVAL_MS } from 'app/features/alerting/unified/utils/constants'; import { PromAlertingRuleState } from 'app/types/unified-alerting-dto'; +import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager'; export function UnifiedAlertList(props: PanelProps) { const dispatch = useDispatch(); @@ -151,11 +156,31 @@ function filterRules(options: PanelProps['options'], ru ); }); } + if (options.alertInstanceLabelFilter) { + const matchers = parseMatchers(options.alertInstanceLabelFilter); + // Reduce rules and instances to only those that match + filteredRules = filteredRules.reduce((rules, rule) => { + const filteredAlerts = rule.rule.alerts.filter(({ labels }) => labelsMatchMatchers(labels, matchers)); + if (filteredAlerts.length) { + rules.push({ ...rule, rule: { ...rule.rule, alerts: filteredAlerts } }); + } + return rules; + }, [] as PromRuleWithLocation[]); + } if (options.folder) { filteredRules = filteredRules.filter((rule) => { return rule.namespaceName === options.folder.title; }); } + if (options.datasource) { + const isGrafanaDS = options.datasource === GRAFANA_DATASOURCE_NAME; + + filteredRules = filteredRules.filter( + isGrafanaDS + ? ({ dataSourceName }) => dataSourceName === GRAFANA_RULES_SOURCE_NAME + : ({ dataSourceName }) => dataSourceName === options.datasource + ); + } return filteredRules; } diff --git a/public/app/plugins/panel/alertlist/module.tsx b/public/app/plugins/panel/alertlist/module.tsx index e52d9a145ca..823495fd48e 100644 --- a/public/app/plugins/panel/alertlist/module.tsx +++ b/public/app/plugins/panel/alertlist/module.tsx @@ -5,7 +5,7 @@ import { AlertList } from './AlertList'; import { UnifiedAlertList } from './UnifiedAlertList'; import { AlertListOptions, ShowOption, SortOrder, UnifiedAlertListOptions } from './types'; import { alertListPanelMigrationHandler } from './AlertListMigrationHandler'; -import { config } from '@grafana/runtime'; +import { config, DataSourcePicker } from '@grafana/runtime'; import { RuleFolderPicker } from 'app/features/alerting/unified/components/rule-editor/RuleFolderPicker'; import { ALL_FOLDER, @@ -221,9 +221,29 @@ const unifiedAlertList = new PanelPlugin(UnifiedAlertLi }, category: ['Filter'], }) + .addCustomEditor({ + path: 'datasource', + name: 'Datasource', + description: 'Filter alerts from selected datasource', + id: 'datasource', + defaultValue: null, + editor: function RenderDatasourcePicker(props) { + return ( + props.onChange(ds.name)} + onClear={() => props.onChange('')} + /> + ); + }, + category: ['Filter'], + }) .addBooleanSwitch({ path: 'stateFilter.firing', - name: 'Alerting', + name: 'Alerting / Firing', defaultValue: true, category: ['Alert state filter'], }) @@ -240,34 +260,22 @@ const unifiedAlertList = new PanelPlugin(UnifiedAlertLi category: ['Alert state filter'], }) .addBooleanSwitch({ - path: 'alertInstanceStateFilter.Alerting', - name: 'Alerting', - defaultValue: true, - category: ['Alert instance state filter'], - }) - .addBooleanSwitch({ - path: 'alertInstanceStateFilter.Pending', - name: 'Pending', - defaultValue: true, - category: ['Alert instance state filter'], - }) - .addBooleanSwitch({ - path: 'alertInstanceStateFilter.NoData', + path: 'stateFilter.noData', name: 'No Data', defaultValue: false, - category: ['Alert instance state filter'], + category: ['Alert state filter'], }) .addBooleanSwitch({ - path: 'alertInstanceStateFilter.Normal', + path: 'stateFilter.normal', name: 'Normal', defaultValue: false, - category: ['Alert instance state filter'], + category: ['Alert state filter'], }) .addBooleanSwitch({ - path: 'alertInstanceStateFilter.Error', + path: 'stateFilter.error', name: 'Error', defaultValue: true, - category: ['Alert instance state filter'], + category: ['Alert state filter'], }); }); diff --git a/public/app/plugins/panel/alertlist/types.ts b/public/app/plugins/panel/alertlist/types.ts index 1721c578405..4b2d313bcb3 100644 --- a/public/app/plugins/panel/alertlist/types.ts +++ b/public/app/plugins/panel/alertlist/types.ts @@ -1,5 +1,3 @@ -import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto'; - export enum SortOrder { AlphaAsc = 1, AlphaDesc, @@ -32,6 +30,15 @@ export interface AlertListOptions { folderId: number; } +interface StateFilter { + firing: boolean; + pending: boolean; + inactive: boolean; + noData: boolean; + normal: boolean; + error: boolean; +} + export interface UnifiedAlertListOptions { maxItems: number; sortOrder: SortOrder; @@ -39,11 +46,7 @@ export interface UnifiedAlertListOptions { alertName: string; showInstances: boolean; folder: { id: number; title: string }; - stateFilter: { - [K in PromAlertingRuleState]: boolean; - }; + stateFilter: StateFilter; alertInstanceLabelFilter: string; - alertInstanceStateFilter: { - [K in GrafanaAlertState]: boolean; - }; + datasource: string; }