mirror of
https://github.com/grafana/grafana.git
synced 2025-09-22 21:23:05 +08:00

Co-authored-by: Jean-Philippe Quéméner <JohnnyQQQQ@users.noreply.github.com> Co-authored-by: gotjosh <josue.abreu@gmail.com> Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>
285 lines
9.3 KiB
TypeScript
285 lines
9.3 KiB
TypeScript
import { css, cx } from '@emotion/css';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
|
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
|
import {
|
|
Alert,
|
|
Button,
|
|
ConfirmModal,
|
|
Field,
|
|
HorizontalGroup,
|
|
Icon,
|
|
RadioButtonGroup,
|
|
Tooltip,
|
|
useStyles2,
|
|
useTheme2,
|
|
} from '@grafana/ui';
|
|
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
|
import { loadDataSources } from 'app/features/datasources/state/actions';
|
|
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
|
|
import { StoreState } from 'app/types/store';
|
|
|
|
import { useExternalAmSelector, useExternalDataSourceAlertmanagers } from '../../hooks/useExternalAmSelector';
|
|
import {
|
|
addExternalAlertmanagersAction,
|
|
fetchExternalAlertmanagersAction,
|
|
fetchExternalAlertmanagersConfigAction,
|
|
} from '../../state/actions';
|
|
|
|
import { AddAlertManagerModal } from './AddAlertManagerModal';
|
|
import { ExternalAlertmanagerDataSources } from './ExternalAlertmanagerDataSources';
|
|
|
|
const alertmanagerChoices: Array<SelectableValue<AlertmanagerChoice>> = [
|
|
{ value: AlertmanagerChoice.Internal, label: 'Only Internal' },
|
|
{ value: AlertmanagerChoice.External, label: 'Only External' },
|
|
{ value: AlertmanagerChoice.All, label: 'Both internal and external' },
|
|
];
|
|
|
|
export const ExternalAlertmanagers = () => {
|
|
const styles = useStyles2(getStyles);
|
|
const dispatch = useDispatch();
|
|
const [modalState, setModalState] = useState({ open: false, payload: [{ url: '' }] });
|
|
const [deleteModalState, setDeleteModalState] = useState({ open: false, index: 0 });
|
|
|
|
const externalAlertManagers = useExternalAmSelector();
|
|
const externalDsAlertManagers = useExternalDataSourceAlertmanagers();
|
|
|
|
const alertmanagersChoice = useSelector(
|
|
(state: StoreState) => state.unifiedAlerting.externalAlertmanagers.alertmanagerConfig.result?.alertmanagersChoice
|
|
);
|
|
const theme = useTheme2();
|
|
|
|
useEffect(() => {
|
|
dispatch(fetchExternalAlertmanagersAction());
|
|
dispatch(fetchExternalAlertmanagersConfigAction());
|
|
dispatch(loadDataSources());
|
|
const interval = setInterval(() => dispatch(fetchExternalAlertmanagersAction()), 5000);
|
|
|
|
return () => {
|
|
clearInterval(interval);
|
|
};
|
|
}, [dispatch]);
|
|
|
|
const onDelete = useCallback(
|
|
(index: number) => {
|
|
// to delete we need to filter the alertmanager from the list and repost
|
|
const newList = (externalAlertManagers ?? [])
|
|
.filter((am, i) => i !== index)
|
|
.map((am) => {
|
|
return am.url;
|
|
});
|
|
dispatch(
|
|
addExternalAlertmanagersAction({
|
|
alertmanagers: newList,
|
|
alertmanagersChoice: alertmanagersChoice ?? AlertmanagerChoice.All,
|
|
})
|
|
);
|
|
setDeleteModalState({ open: false, index: 0 });
|
|
},
|
|
[externalAlertManagers, dispatch, alertmanagersChoice]
|
|
);
|
|
|
|
const onEdit = useCallback(() => {
|
|
const ams = externalAlertManagers ? [...externalAlertManagers] : [{ url: '' }];
|
|
setModalState((state) => ({
|
|
...state,
|
|
open: true,
|
|
payload: ams,
|
|
}));
|
|
}, [setModalState, externalAlertManagers]);
|
|
|
|
const onOpenModal = useCallback(() => {
|
|
setModalState((state) => {
|
|
const ams = externalAlertManagers ? [...externalAlertManagers, { url: '' }] : [{ url: '' }];
|
|
return {
|
|
...state,
|
|
open: true,
|
|
payload: ams,
|
|
};
|
|
});
|
|
}, [externalAlertManagers]);
|
|
|
|
const onCloseModal = useCallback(() => {
|
|
setModalState((state) => ({
|
|
...state,
|
|
open: false,
|
|
}));
|
|
}, [setModalState]);
|
|
|
|
const onChangeAlertmanagerChoice = (alertmanagersChoice: AlertmanagerChoice) => {
|
|
dispatch(
|
|
addExternalAlertmanagersAction({ alertmanagers: externalAlertManagers.map((am) => am.url), alertmanagersChoice })
|
|
);
|
|
};
|
|
|
|
const onChangeAlertmanagers = (alertmanagers: string[]) => {
|
|
dispatch(
|
|
addExternalAlertmanagersAction({
|
|
alertmanagers,
|
|
alertmanagersChoice: alertmanagersChoice ?? AlertmanagerChoice.All,
|
|
})
|
|
);
|
|
};
|
|
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
case 'active':
|
|
return theme.colors.success.main;
|
|
|
|
case 'pending':
|
|
return theme.colors.warning.main;
|
|
|
|
default:
|
|
return theme.colors.error.main;
|
|
}
|
|
};
|
|
|
|
const noAlertmanagers = externalAlertManagers?.length === 0;
|
|
const noDsAlertmanagers = externalDsAlertManagers?.length === 0;
|
|
const hasExternalAlertmanagers = !(noAlertmanagers && noDsAlertmanagers);
|
|
|
|
return (
|
|
<div>
|
|
<h4>External Alertmanagers</h4>
|
|
<Alert title="External Alertmanager changes" severity="info">
|
|
The way you configure external Alertmanagers has changed.
|
|
<br />
|
|
You can now use configured Alertmanager data sources as receivers of your Grafana-managed alerts.
|
|
<br />
|
|
For more information, refer to our documentation.
|
|
</Alert>
|
|
|
|
<ExternalAlertmanagerDataSources
|
|
alertmanagers={externalDsAlertManagers}
|
|
inactive={alertmanagersChoice === AlertmanagerChoice.Internal}
|
|
/>
|
|
|
|
{hasExternalAlertmanagers && (
|
|
<div className={styles.amChoice}>
|
|
<Field
|
|
label="Send alerts to"
|
|
description="Configures how the Grafana alert rule evaluation engine Alertmanager handles your alerts. Internal (Grafana built-in Alertmanager), External (All Alertmanagers configured above), or both."
|
|
>
|
|
<RadioButtonGroup
|
|
options={alertmanagerChoices}
|
|
value={alertmanagersChoice}
|
|
onChange={(value) => onChangeAlertmanagerChoice(value!)}
|
|
/>
|
|
</Field>
|
|
</div>
|
|
)}
|
|
|
|
<h5>Alertmanagers by URL</h5>
|
|
<Alert severity="warning" title="Deprecation Notice">
|
|
The URL-based configuration of Alertmanagers is deprecated and will be removed in Grafana 9.2.0.
|
|
<br />
|
|
Use Alertmanager data sources to configure your external Alertmanagers.
|
|
</Alert>
|
|
|
|
<div className={styles.muted}>
|
|
You can have your Grafana managed alerts be delivered to one or many external Alertmanager(s) in addition to the
|
|
internal Alertmanager by specifying their URLs below.
|
|
</div>
|
|
<div className={styles.actions}>
|
|
{!noAlertmanagers && (
|
|
<Button type="button" onClick={onOpenModal}>
|
|
Add Alertmanager
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{noAlertmanagers ? (
|
|
<EmptyListCTA
|
|
title="You have not added any external alertmanagers"
|
|
onClick={onOpenModal}
|
|
buttonTitle="Add Alertmanager"
|
|
buttonIcon="bell-slash"
|
|
/>
|
|
) : (
|
|
<>
|
|
<table className={cx('filter-table form-inline filter-table--hover', styles.table)}>
|
|
<thead>
|
|
<tr>
|
|
<th>Url</th>
|
|
<th>Status</th>
|
|
<th style={{ width: '2%' }}>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{externalAlertManagers?.map((am, index) => {
|
|
return (
|
|
<tr key={index}>
|
|
<td>
|
|
<span className={styles.url}>{am.url}</span>
|
|
{am.actualUrl ? (
|
|
<Tooltip content={`Discovered ${am.actualUrl} from ${am.url}`} theme="info">
|
|
<Icon name="info-circle" />
|
|
</Tooltip>
|
|
) : null}
|
|
</td>
|
|
<td>
|
|
<Icon name="heart" style={{ color: getStatusColor(am.status) }} title={am.status} />
|
|
</td>
|
|
<td>
|
|
<HorizontalGroup>
|
|
<Button variant="secondary" type="button" onClick={onEdit} aria-label="Edit alertmanager">
|
|
<Icon name="pen" />
|
|
</Button>
|
|
<Button
|
|
variant="destructive"
|
|
aria-label="Remove alertmanager"
|
|
type="button"
|
|
onClick={() => setDeleteModalState({ open: true, index })}
|
|
>
|
|
<Icon name="trash-alt" />
|
|
</Button>
|
|
</HorizontalGroup>
|
|
</td>
|
|
</tr>
|
|
);
|
|
})}
|
|
</tbody>
|
|
</table>
|
|
</>
|
|
)}
|
|
|
|
<ConfirmModal
|
|
isOpen={deleteModalState.open}
|
|
title="Remove Alertmanager"
|
|
body="Are you sure you want to remove this Alertmanager"
|
|
confirmText="Remove"
|
|
onConfirm={() => onDelete(deleteModalState.index)}
|
|
onDismiss={() => setDeleteModalState({ open: false, index: 0 })}
|
|
/>
|
|
{modalState.open && (
|
|
<AddAlertManagerModal
|
|
onClose={onCloseModal}
|
|
alertmanagers={modalState.payload}
|
|
onChangeAlertmanagerConfig={onChangeAlertmanagers}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const getStyles = (theme: GrafanaTheme2) => ({
|
|
url: css`
|
|
margin-right: ${theme.spacing(1)};
|
|
`,
|
|
muted: css`
|
|
color: ${theme.colors.text.secondary};
|
|
`,
|
|
actions: css`
|
|
margin-top: ${theme.spacing(2)};
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
`,
|
|
table: css`
|
|
margin-bottom: ${theme.spacing(2)};
|
|
`,
|
|
amChoice: css`
|
|
margin-bottom: ${theme.spacing(4)};
|
|
`,
|
|
});
|