mirror of
https://github.com/grafana/grafana.git
synced 2025-01-28 17:24:59 -06:00
Alerting: Add basic support for active_time_intervals (#91710)
This commit is contained in:
parent
5487ea444a
commit
add99fb3d0
@ -1803,25 +1803,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/Policy.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "5"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "15"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/notification-policies/PromDurationDocs.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||
|
@ -644,7 +644,7 @@ describe('NotificationPolicies', () => {
|
||||
renderNotificationPolicies(dataSources.promAlertManager.name);
|
||||
const rootRouteContainer = await ui.rootRouteContainer.find();
|
||||
await waitFor(() =>
|
||||
expect(within(rootRouteContainer).getByTestId('matching-instances')).toHaveTextContent('0instances')
|
||||
expect(within(rootRouteContainer).getByTestId('matching-instances')).toHaveTextContent('0instance')
|
||||
);
|
||||
|
||||
expect(ui.editButton.query(rootRouteContainer)).not.toBeInTheDocument();
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from 'app/features/alerting/unified/openapi/timeIntervalsApi.gen';
|
||||
import { deleteMuteTimingAction, updateAlertManagerConfigAction } from 'app/features/alerting/unified/state/actions';
|
||||
import { BaseAlertmanagerArgs } from 'app/features/alerting/unified/types/hooks';
|
||||
import { renameMuteTimings } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { renameTimeInterval } from 'app/features/alerting/unified/utils/alertmanager';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||
import { PROVENANCE_ANNOTATION, PROVENANCE_NONE } from 'app/features/alerting/unified/utils/k8s/constants';
|
||||
import { getK8sNamespace, shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils';
|
||||
@ -261,7 +261,7 @@ export const useUpdateMuteTiming = ({ alertmanager }: BaseAlertmanagerArgs) => {
|
||||
}
|
||||
|
||||
if (nameHasChanged && draft.alertmanager_config.route) {
|
||||
draft.alertmanager_config.route = renameMuteTimings(
|
||||
draft.alertmanager_config.route = renameTimeInterval(
|
||||
timeInterval.name,
|
||||
originalName,
|
||||
draft.alertmanager_config.route
|
||||
|
@ -100,8 +100,9 @@ describe('Policy', () => {
|
||||
// for grouping
|
||||
expect(within(defaultPolicy).getByTestId('grouping')).toHaveTextContent('grafana_folder, alertname');
|
||||
|
||||
// no mute timings
|
||||
// no timings
|
||||
expect(within(defaultPolicy).queryByTestId('mute-timings')).not.toBeInTheDocument();
|
||||
expect(within(defaultPolicy).queryByTestId('active-timings')).not.toBeInTheDocument();
|
||||
|
||||
// for timing options
|
||||
expect(within(defaultPolicy).getByTestId('timing-options')).toHaveTextContent(
|
||||
@ -132,6 +133,7 @@ describe('Policy', () => {
|
||||
// expect(within(firstPolicy).getByTestId('matching-instances')).toHaveTextContent('0instances');
|
||||
expect(within(firstPolicy).getByTestId('contact-point')).toHaveTextContent('provisioned-contact-point');
|
||||
expect(within(firstPolicy).getByTestId('mute-timings')).toHaveTextContent('Muted whenmt-1');
|
||||
expect(within(firstPolicy).getByTestId('active-timings')).toHaveTextContent('Active whenmt-2');
|
||||
expect(within(firstPolicy).getByTestId('inherited-properties')).toHaveTextContent('Inherited2 properties');
|
||||
|
||||
// second custom policy should be correct
|
||||
@ -139,6 +141,7 @@ describe('Policy', () => {
|
||||
expect(within(secondPolicy).getByTestId('label-matchers')).toHaveTextContent(/^region \= EMEA$/);
|
||||
expect(within(secondPolicy).queryByTestId('continue-matching')).not.toBeInTheDocument();
|
||||
expect(within(secondPolicy).queryByTestId('mute-timings')).not.toBeInTheDocument();
|
||||
expect(within(secondPolicy).queryByTestId('active-timings')).not.toBeInTheDocument();
|
||||
expect(within(secondPolicy).getByTestId('inherited-properties')).toHaveTextContent('Inherited3 properties');
|
||||
|
||||
// third custom policy should be correct
|
||||
@ -360,6 +363,7 @@ const mockRoutes: RouteWithID = {
|
||||
receiver: 'provisioned-contact-point',
|
||||
object_matchers: [['team', eq, 'operations']],
|
||||
mute_time_intervals: ['mt-1'],
|
||||
active_time_intervals: ['mt-2'],
|
||||
continue: true,
|
||||
routes: [
|
||||
{
|
||||
|
@ -163,6 +163,7 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
|
||||
const groupBy = currentRoute.group_by;
|
||||
const muteTimings = currentRoute.mute_time_intervals ?? [];
|
||||
const activeTimings = currentRoute.active_time_intervals ?? [];
|
||||
|
||||
const timingOptions: TimingOptions = {
|
||||
group_wait: currentRoute.group_wait,
|
||||
@ -245,7 +246,9 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
) : hasMatchers ? (
|
||||
<Matchers matchers={matchers ?? []} formatter={getAmMatcherFormatter(alertManagerSourceName)} />
|
||||
) : (
|
||||
<span className={styles.metadata}>No matchers</span>
|
||||
<span className={styles.metadata}>
|
||||
<Trans i18nKey="alerting.policies.no-matchers">No matchers</Trans>
|
||||
</span>
|
||||
)}
|
||||
<Spacer />
|
||||
{/* TODO maybe we should move errors to the gutter instead? */}
|
||||
@ -264,7 +267,7 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
type="button"
|
||||
onClick={() => onAddPolicy(currentRoute, 'child')}
|
||||
>
|
||||
New child policy
|
||||
<Trans i18nKey="alerting.policies.new-child">New child policy</Trans>
|
||||
</Button>
|
||||
) : (
|
||||
<Dropdown
|
||||
@ -296,7 +299,7 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
icon="angle-down"
|
||||
type="button"
|
||||
>
|
||||
Add new policy
|
||||
<Trans i18nKey="alerting.policies.new-policy">Add new policy</Trans>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)}
|
||||
@ -326,6 +329,7 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
contactPoint={contactPoint ?? undefined}
|
||||
groupBy={groupBy}
|
||||
muteTimings={muteTimings}
|
||||
activeTimings={activeTimings}
|
||||
timingOptions={timingOptions}
|
||||
inheritedProperties={inheritedProperties}
|
||||
alertManagerSourceName={alertManagerSourceName}
|
||||
@ -379,7 +383,9 @@ const Policy = (props: PolicyComponentProps) => {
|
||||
className={styles.moreButtons}
|
||||
onClick={() => setVisibleChildPolicies(visibleChildPolicies + POLICIES_PER_PAGE)}
|
||||
>
|
||||
{moreCount} additional {pluralize('policy', moreCount)}
|
||||
<Trans i18nKey="alerting.policies.n-more-policies" count={moreCount}>
|
||||
{{ count: moreCount }} additional policies
|
||||
</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
@ -397,6 +403,7 @@ interface MetadataRowProps {
|
||||
contactPoint?: string;
|
||||
groupBy?: string[];
|
||||
muteTimings?: string[];
|
||||
activeTimings?: string[];
|
||||
timingOptions?: TimingOptions;
|
||||
inheritedProperties?: Partial<InheritableProperties>;
|
||||
alertManagerSourceName: string;
|
||||
@ -417,6 +424,7 @@ function MetadataRow({
|
||||
timingOptions,
|
||||
groupBy,
|
||||
muteTimings = [],
|
||||
activeTimings = [],
|
||||
matchingInstancesPreview,
|
||||
inheritedProperties,
|
||||
matchingAlertGroups,
|
||||
@ -436,6 +444,7 @@ function MetadataRow({
|
||||
const singleGroup = isDefaultPolicy && isArray(groupBy) && groupBy.length === 0;
|
||||
|
||||
const hasMuteTimings = Boolean(muteTimings.length);
|
||||
const hasActiveTimings = Boolean(activeTimings.length);
|
||||
|
||||
return (
|
||||
<div className={styles.metadataRow}>
|
||||
@ -450,12 +459,18 @@ function MetadataRow({
|
||||
data-testid="matching-instances"
|
||||
>
|
||||
<Text color="primary">{numberOfAlertInstances ?? '-'}</Text>
|
||||
<span>{pluralize('instance', numberOfAlertInstances)}</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.n-instances" count={numberOfAlertInstances ?? 0}>
|
||||
instance
|
||||
</Trans>
|
||||
</span>
|
||||
</MetaText>
|
||||
)}
|
||||
{contactPoint && (
|
||||
<MetaText icon="at" data-testid="contact-point">
|
||||
<span>Delivered to</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.delivered-to">Delivered to</Trans>
|
||||
</span>
|
||||
<ContactPointsHoverDetails
|
||||
alertManagerSourceName={alertManagerSourceName}
|
||||
receivers={receivers}
|
||||
@ -467,26 +482,42 @@ function MetadataRow({
|
||||
<>
|
||||
{customGrouping && (
|
||||
<MetaText icon="layer-group" data-testid="grouping">
|
||||
<span>Grouped by</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.grouped-by">Grouped by</Trans>
|
||||
</span>
|
||||
<Text color="primary">{groupBy.join(', ')}</Text>
|
||||
</MetaText>
|
||||
)}
|
||||
{singleGroup && (
|
||||
<MetaText icon="layer-group">
|
||||
<span>Single group</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.grouping.single-group">Single group</Trans>
|
||||
</span>
|
||||
</MetaText>
|
||||
)}
|
||||
{noGrouping && (
|
||||
<MetaText icon="layer-group">
|
||||
<span>Not grouping</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.grouping.none">Not grouping</Trans>
|
||||
</span>
|
||||
</MetaText>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{hasMuteTimings && (
|
||||
<MetaText icon="calendar-slash" data-testid="mute-timings">
|
||||
<span>Muted when</span>
|
||||
<MuteTimings timings={muteTimings} alertManagerSourceName={alertManagerSourceName} />
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.mute-time">Muted when</Trans>
|
||||
</span>
|
||||
<TimeIntervals timings={muteTimings} alertManagerSourceName={alertManagerSourceName} />
|
||||
</MetaText>
|
||||
)}
|
||||
{hasActiveTimings && (
|
||||
<MetaText icon="calendar-alt" data-testid="active-timings">
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.active-time">Active when</Trans>
|
||||
</span>
|
||||
<TimeIntervals timings={activeTimings} alertManagerSourceName={alertManagerSourceName} />
|
||||
</MetaText>
|
||||
)}
|
||||
{timingOptions && (
|
||||
@ -498,7 +529,9 @@ function MetadataRow({
|
||||
{hasInheritedProperties && (
|
||||
<>
|
||||
<MetaText icon="corner-down-right-alt" data-testid="inherited-properties">
|
||||
<span>Inherited</span>
|
||||
<span>
|
||||
<Trans i18nKey="alerting.policies.metadata.inherited">Inherited</Trans>
|
||||
</span>
|
||||
<InheritedProperties properties={inheritedProperties} />
|
||||
</MetaText>
|
||||
</>
|
||||
@ -514,7 +547,7 @@ export const useCreateDropdownMenuActions = (
|
||||
provisioned: boolean,
|
||||
onEditPolicy: (route: RouteWithID, isDefault?: boolean, readOnly?: boolean) => void,
|
||||
currentRoute: RouteWithID,
|
||||
toggleShowExportDrawer: (nextValue?: any) => void,
|
||||
toggleShowExportDrawer: () => void,
|
||||
onDeletePolicy: (route: RouteWithID) => void
|
||||
) => {
|
||||
const [
|
||||
@ -644,10 +677,12 @@ function DefaultPolicyIndicator() {
|
||||
return (
|
||||
<>
|
||||
<Text element="h2" variant="body" weight="medium">
|
||||
Default policy
|
||||
<Trans i18nKey="alerting.policies.default-policy.title">Default policy</Trans>
|
||||
</Text>
|
||||
<span className={styles.metadata}>
|
||||
All alert instances will be handled by the default policy if no other matching policies are found.
|
||||
<Trans i18nKey="alerting.policies.default-policy.description">
|
||||
All alert instances will be handled by the default policy if no other matching policies are found.
|
||||
</Trans>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
@ -656,7 +691,7 @@ function DefaultPolicyIndicator() {
|
||||
function AutogeneratedRootIndicator() {
|
||||
return (
|
||||
<Text element="h3" variant="body" weight="medium">
|
||||
Auto-generated policies
|
||||
<Trans i18nKey="alerting.policies.generated-policies">Auto-generated policies</Trans>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@ -683,7 +718,7 @@ const InheritedProperties: FC<{ properties: InheritableProperties }> = ({ proper
|
||||
</HoverCard>
|
||||
);
|
||||
|
||||
const MuteTimings: FC<{ timings: string[]; alertManagerSourceName: string }> = ({
|
||||
const TimeIntervals: FC<{ timings: string[]; alertManagerSourceName: string }> = ({
|
||||
timings,
|
||||
alertManagerSourceName,
|
||||
}) => {
|
||||
@ -852,7 +887,9 @@ const ContactPointsHoverDetails: FC<ContactPointDetailsProps> = ({
|
||||
placement="top"
|
||||
header={
|
||||
<MetaText icon="at">
|
||||
<div>Contact Point</div>
|
||||
<div>
|
||||
<Trans i18nKey="alerting.contact-point">Contact Point</Trans>
|
||||
</div>
|
||||
<Text color="primary">{contactPoint}</Text>
|
||||
</MetaText>
|
||||
}
|
||||
@ -930,7 +967,7 @@ const routePropertyToValue = (
|
||||
if (isNotGrouping) {
|
||||
return (
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
Not grouping
|
||||
<Trans i18nKey="alerting.policies.metadata.grouping.none">Not grouping</Trans>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@ -938,7 +975,7 @@ const routePropertyToValue = (
|
||||
if (isSingleGroup) {
|
||||
return (
|
||||
<Text variant="bodySmall" color="secondary">
|
||||
Single group
|
||||
<Trans i18nKey="alerting.policies.metadata.grouping.single-group">Single group</Trans>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ import { discoverFeatures } from '../api/buildInfo';
|
||||
import { FetchPromRulesFilter, fetchRules } from '../api/prometheus';
|
||||
import { FetchRulerRulesFilter, deleteRulerRulesGroup, fetchRulerRules, setRulerRuleGroup } from '../api/ruler';
|
||||
import { RuleFormValues } from '../types/rule-form';
|
||||
import { addDefaultsToAlertmanagerConfig, removeMuteTimingFromRoute } from '../utils/alertmanager';
|
||||
import { addDefaultsToAlertmanagerConfig, removeTimeIntervalFromRoute } from '../utils/alertmanager';
|
||||
import {
|
||||
GRAFANA_RULES_SOURCE_NAME,
|
||||
getAllRulesSourceNames,
|
||||
@ -590,7 +590,7 @@ export const deleteMuteTimingAction = (alertManagerSourceName: string, muteTimin
|
||||
alertmanager_config: {
|
||||
...configWithoutMuteTimings,
|
||||
route: config.alertmanager_config.route
|
||||
? removeMuteTimingFromRoute(muteTimingName, config.alertmanager_config?.route)
|
||||
? removeTimeIntervalFromRoute(muteTimingName, config.alertmanager_config?.route)
|
||||
: undefined,
|
||||
...time_intervals_without_mute_to_save,
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Matcher, MatcherOperator, Route } from 'app/plugins/datasource/alertmanager/types';
|
||||
import { Labels } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { labelsMatchMatchers, removeMuteTimingFromRoute, matchersToString } from './alertmanager';
|
||||
import { labelsMatchMatchers, removeTimeIntervalFromRoute, matchersToString } from './alertmanager';
|
||||
import { parseMatcher, parsePromQLStyleMatcherLooseSafe } from './matchers';
|
||||
|
||||
describe('Alertmanager utils', () => {
|
||||
@ -104,6 +104,7 @@ describe('Alertmanager utils', () => {
|
||||
receiver: 'slack',
|
||||
object_matchers: [['env', MatcherOperator.equal, 'prod']],
|
||||
mute_time_intervals: ['test2'],
|
||||
active_time_intervals: ['test1'],
|
||||
},
|
||||
{
|
||||
receiver: 'pagerduty',
|
||||
@ -113,13 +114,15 @@ describe('Alertmanager utils', () => {
|
||||
],
|
||||
};
|
||||
|
||||
it('should remove mute timings from routes', () => {
|
||||
expect(removeMuteTimingFromRoute('test1', route)).toEqual({
|
||||
it('should remove time interval from routes', () => {
|
||||
expect(removeTimeIntervalFromRoute('test1', route)).toEqual({
|
||||
mute_time_intervals: ['test2'],
|
||||
active_time_intervals: [],
|
||||
object_matchers: [['foo', '=', 'bar']],
|
||||
receiver: 'gmail',
|
||||
routes: [
|
||||
{
|
||||
active_time_intervals: [],
|
||||
mute_time_intervals: ['test2'],
|
||||
object_matchers: [['env', '=', 'prod']],
|
||||
receiver: 'slack',
|
||||
@ -127,6 +130,7 @@ describe('Alertmanager utils', () => {
|
||||
},
|
||||
{
|
||||
mute_time_intervals: [],
|
||||
active_time_intervals: [],
|
||||
object_matchers: [['env', '=', 'eu']],
|
||||
receiver: 'pagerduty',
|
||||
routes: undefined,
|
||||
|
@ -35,22 +35,22 @@ export function addDefaultsToAlertmanagerConfig(config: AlertManagerCortexConfig
|
||||
return config;
|
||||
}
|
||||
|
||||
export function removeMuteTimingFromRoute(muteTiming: string, route: Route): Route {
|
||||
export function removeTimeIntervalFromRoute(muteTiming: string, route: Route): Route {
|
||||
const newRoute: Route = {
|
||||
...route,
|
||||
mute_time_intervals: route.mute_time_intervals?.filter((muteName) => muteName !== muteTiming) ?? [],
|
||||
routes: route.routes?.map((subRoute) => removeMuteTimingFromRoute(muteTiming, subRoute)),
|
||||
active_time_intervals: route.active_time_intervals?.filter((muteName) => muteName !== muteTiming) ?? [],
|
||||
routes: route.routes?.map((subRoute) => removeTimeIntervalFromRoute(muteTiming, subRoute)),
|
||||
};
|
||||
return newRoute;
|
||||
}
|
||||
|
||||
export function renameMuteTimings(newMuteTimingName: string, oldMuteTimingName: string, route: Route): Route {
|
||||
export function renameTimeInterval(newName: string, oldName: string, route: Route): Route {
|
||||
return {
|
||||
...route,
|
||||
mute_time_intervals: route.mute_time_intervals?.map((name) =>
|
||||
name === oldMuteTimingName ? newMuteTimingName : name
|
||||
),
|
||||
routes: route.routes?.map((subRoute) => renameMuteTimings(newMuteTimingName, oldMuteTimingName, subRoute)),
|
||||
mute_time_intervals: route.mute_time_intervals?.map((name) => (name === oldName ? newName : name)),
|
||||
active_time_intervals: route.active_time_intervals?.map((name) => (name === oldName ? newName : name)),
|
||||
routes: route.routes?.map((subRoute) => renameTimeInterval(newName, oldName, subRoute)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,10 @@ export type Route = {
|
||||
group_interval?: string;
|
||||
repeat_interval?: string;
|
||||
routes?: Route[];
|
||||
/** Times when the route should be muted. */
|
||||
mute_time_intervals?: string[];
|
||||
/** Times when the route should be active. This is the opposite of `mute_time_intervals` */
|
||||
active_time_intervals?: string[];
|
||||
/** only the root policy might have a provenance field defined */
|
||||
provenance?: string;
|
||||
};
|
||||
|
@ -106,6 +106,7 @@
|
||||
"export-all": "Export all",
|
||||
"view": "View"
|
||||
},
|
||||
"contact-point": "Contact Point",
|
||||
"contact-points": {
|
||||
"delivery-duration": "Last delivery took <1></1>",
|
||||
"last-delivery-attempt": "Last delivery attempt",
|
||||
@ -136,7 +137,23 @@
|
||||
"saving": "Saving mute timing"
|
||||
},
|
||||
"policies": {
|
||||
"default-policy": {
|
||||
"description": "All alert instances will be handled by the default policy if no other matching policies are found.",
|
||||
"title": "Default policy"
|
||||
},
|
||||
"generated-policies": "Auto-generated policies",
|
||||
"metadata": {
|
||||
"active-time": "Active when",
|
||||
"delivered-to": "Delivered to",
|
||||
"grouped-by": "Grouped by",
|
||||
"grouping": {
|
||||
"none": "Not grouping",
|
||||
"single-group": "Single group"
|
||||
},
|
||||
"inherited": "Inherited",
|
||||
"mute-time": "Muted when",
|
||||
"n-instances_one": "instance",
|
||||
"n-instances_other": "instances",
|
||||
"timingOptions": {
|
||||
"groupInterval": {
|
||||
"description": "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent.",
|
||||
@ -151,7 +168,12 @@
|
||||
"label": "Repeated every <1></1>"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"n-more-policies_one": "{{count}} additional policy",
|
||||
"n-more-policies_other": "{{count}} additional policies",
|
||||
"new-child": "New child policy",
|
||||
"new-policy": "Add new policy",
|
||||
"no-matchers": "No matchers"
|
||||
},
|
||||
"provisioning": {
|
||||
"badge-tooltip-provenance": "This resource has been provisioned via {{provenance}} and cannot be edited through the UI",
|
||||
|
@ -106,6 +106,7 @@
|
||||
"export-all": "Ēχpőřŧ äľľ",
|
||||
"view": "Vįęŵ"
|
||||
},
|
||||
"contact-point": "Cőʼnŧäčŧ Pőįʼnŧ",
|
||||
"contact-points": {
|
||||
"delivery-duration": "Ŀäşŧ đęľįvęřy ŧőőĸ <1></1>",
|
||||
"last-delivery-attempt": "Ŀäşŧ đęľįvęřy äŧŧęmpŧ",
|
||||
@ -136,7 +137,23 @@
|
||||
"saving": "Ŝävįʼnģ mūŧę ŧįmįʼnģ"
|
||||
},
|
||||
"policies": {
|
||||
"default-policy": {
|
||||
"description": "Åľľ äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę ĥäʼnđľęđ þy ŧĥę đęƒäūľŧ pőľįčy įƒ ʼnő őŧĥęř mäŧčĥįʼnģ pőľįčįęş äřę ƒőūʼnđ.",
|
||||
"title": "Đęƒäūľŧ pőľįčy"
|
||||
},
|
||||
"generated-policies": "Åūŧő-ģęʼnęřäŧęđ pőľįčįęş",
|
||||
"metadata": {
|
||||
"active-time": "Åčŧįvę ŵĥęʼn",
|
||||
"delivered-to": "Đęľįvęřęđ ŧő",
|
||||
"grouped-by": "Ğřőūpęđ þy",
|
||||
"grouping": {
|
||||
"none": "Ńőŧ ģřőūpįʼnģ",
|
||||
"single-group": "Ŝįʼnģľę ģřőūp"
|
||||
},
|
||||
"inherited": "Ĩʼnĥęřįŧęđ",
|
||||
"mute-time": "Mūŧęđ ŵĥęʼn",
|
||||
"n-instances_one": "įʼnşŧäʼnčę",
|
||||
"n-instances_other": "įʼnşŧäʼnčęş",
|
||||
"timingOptions": {
|
||||
"groupInterval": {
|
||||
"description": "Ħőŵ ľőʼnģ ŧő ŵäįŧ þęƒőřę şęʼnđįʼnģ ä ʼnőŧįƒįčäŧįőʼn äþőūŧ ʼnęŵ äľęřŧş ŧĥäŧ äřę äđđęđ ŧő ä ģřőūp őƒ äľęřŧş ƒőř ŵĥįčĥ äʼn įʼnįŧįäľ ʼnőŧįƒįčäŧįőʼn ĥäş äľřęäđy þęęʼn şęʼnŧ.",
|
||||
@ -151,7 +168,12 @@
|
||||
"label": "Ŗępęäŧęđ ęvęřy <1></1>"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"n-more-policies_one": "{{count}} äđđįŧįőʼnäľ pőľįčy",
|
||||
"n-more-policies_other": "{{count}} äđđįŧįőʼnäľ pőľįčįęş",
|
||||
"new-child": "Ńęŵ čĥįľđ pőľįčy",
|
||||
"new-policy": "Åđđ ʼnęŵ pőľįčy",
|
||||
"no-matchers": "Ńő mäŧčĥęřş"
|
||||
},
|
||||
"provisioning": {
|
||||
"badge-tooltip-provenance": "Ŧĥįş řęşőūřčę ĥäş þęęʼn přővįşįőʼnęđ vįä {{provenance}} äʼnđ čäʼnʼnőŧ þę ęđįŧęđ ŧĥřőūģĥ ŧĥę ŮĨ",
|
||||
|
Loading…
Reference in New Issue
Block a user