From c49d5464a2cfeb9171f8e20a76134de2851e9542 Mon Sep 17 00:00:00 2001 From: igoristic Date: Wed, 30 Sep 2020 12:38:52 -0400 Subject: [PATCH] [Monitoring] Disk usage alerting (#75419) * Disk usage alert draft * Fixed typings and defaults * Fixed tests * Fixed tests * Addressed code feedback * Fixed disk and cpu usage states * Fixed resolve state and throttle * CR feedback * Fixed links --- x-pack/plugins/monitoring/common/constants.ts | 2 + x-pack/plugins/monitoring/common/types.ts | 5 +- .../duration}/expression.tsx | 8 +- .../duration}/validation.tsx | 17 +- .../cpu_usage_alert/cpu_usage_alert.tsx | 6 +- .../public/alerts/disk_usage_alert/index.tsx | 29 ++ .../public/alerts/lib/replace_tokens.tsx | 8 +- .../cluster/overview/elasticsearch_panel.js | 2 + x-pack/plugins/monitoring/public/plugin.ts | 2 + .../elasticsearch/node/advanced/index.js | 8 +- .../public/views/elasticsearch/node/index.js | 8 +- .../public/views/elasticsearch/nodes/index.js | 3 +- .../monitoring/server/alerts/alerts_common.ts | 82 +++++ .../server/alerts/alerts_factory.test.ts | 2 +- .../server/alerts/alerts_factory.ts | 3 + .../monitoring/server/alerts/base_alert.ts | 9 +- .../alerts/cluster_health_alert.test.ts | 13 +- .../server/alerts/cluster_health_alert.ts | 69 +--- .../server/alerts/cpu_usage_alert.test.ts | 22 +- .../server/alerts/cpu_usage_alert.ts | 121 ++---- .../server/alerts/disk_usage_alert.test.ts | 221 +++++++++++ .../server/alerts/disk_usage_alert.ts | 343 ++++++++++++++++++ ...asticsearch_version_mismatch_alert.test.ts | 19 +- .../elasticsearch_version_mismatch_alert.ts | 76 +--- .../plugins/monitoring/server/alerts/index.ts | 1 + .../kibana_version_mismatch_alert.test.ts | 27 +- .../alerts/kibana_version_mismatch_alert.ts | 69 +--- .../alerts/license_expiration_alert.test.ts | 16 +- .../server/alerts/license_expiration_alert.ts | 72 +--- .../logstash_version_mismatch_alert.test.ts | 19 +- .../alerts/logstash_version_mismatch_alert.ts | 74 +--- .../server/alerts/nodes_changed_alert.test.ts | 17 +- .../server/alerts/nodes_changed_alert.ts | 70 +--- .../monitoring/server/alerts/types.d.ts | 25 +- .../fetch_disk_usage_node_stats.test.ts | 71 ++++ .../lib/alerts/fetch_disk_usage_node_stats.ts | 125 +++++++ .../server/lib/alerts/fetch_status.test.ts | 2 - .../translations/translations/ja-JP.json | 58 +-- .../translations/translations/zh-CN.json | 58 +-- 39 files changed, 1076 insertions(+), 706 deletions(-) rename x-pack/plugins/monitoring/public/alerts/{cpu_usage_alert => components/duration}/expression.tsx (85%) rename x-pack/plugins/monitoring/public/alerts/{cpu_usage_alert => components/duration}/validation.tsx (62%) create mode 100644 x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx create mode 100644 x-pack/plugins/monitoring/server/alerts/alerts_common.ts create mode 100644 x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts create mode 100644 x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts index 8be0eb0b06823..6eb0d6e93d58a 100644 --- a/x-pack/plugins/monitoring/common/constants.ts +++ b/x-pack/plugins/monitoring/common/constants.ts @@ -231,6 +231,7 @@ export const ALERT_PREFIX = 'monitoring_'; export const ALERT_LICENSE_EXPIRATION = `${ALERT_PREFIX}alert_license_expiration`; export const ALERT_CLUSTER_HEALTH = `${ALERT_PREFIX}alert_cluster_health`; export const ALERT_CPU_USAGE = `${ALERT_PREFIX}alert_cpu_usage`; +export const ALERT_DISK_USAGE = `${ALERT_PREFIX}alert_disk_usage`; export const ALERT_NODES_CHANGED = `${ALERT_PREFIX}alert_nodes_changed`; export const ALERT_ELASTICSEARCH_VERSION_MISMATCH = `${ALERT_PREFIX}alert_elasticsearch_version_mismatch`; export const ALERT_KIBANA_VERSION_MISMATCH = `${ALERT_PREFIX}alert_kibana_version_mismatch`; @@ -243,6 +244,7 @@ export const ALERTS = [ ALERT_LICENSE_EXPIRATION, ALERT_CLUSTER_HEALTH, ALERT_CPU_USAGE, + ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, ALERT_KIBANA_VERSION_MISMATCH, diff --git a/x-pack/plugins/monitoring/common/types.ts b/x-pack/plugins/monitoring/common/types.ts index f5dc85dce32e1..4216a046fef9f 100644 --- a/x-pack/plugins/monitoring/common/types.ts +++ b/x-pack/plugins/monitoring/common/types.ts @@ -27,8 +27,9 @@ export interface CommonAlertState { meta: any; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface CommonAlertFilter {} +export interface CommonAlertFilter { + nodeUuid?: string; +} export interface CommonAlertCpuUsageFilter extends CommonAlertFilter { nodeUuid: string; diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/expression.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx similarity index 85% rename from x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/expression.tsx rename to x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx index 7dc6155de529e..2df7169efc675 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/expression.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/expression.tsx @@ -6,10 +6,10 @@ import React, { Fragment } from 'react'; import { EuiForm, EuiSpacer } from '@elastic/eui'; -import { CommonAlertParamDetails } from '../../../common/types'; -import { AlertParamDuration } from '../flyout_expressions/alert_param_duration'; -import { AlertParamType } from '../../../common/enums'; -import { AlertParamPercentage } from '../flyout_expressions/alert_param_percentage'; +import { CommonAlertParamDetails } from '../../../../common/types'; +import { AlertParamDuration } from '../../flyout_expressions/alert_param_duration'; +import { AlertParamType } from '../../../../common/enums'; +import { AlertParamPercentage } from '../../flyout_expressions/alert_param_percentage'; export interface Props { alertParams: { [property: string]: any }; diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/validation.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx similarity index 62% rename from x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/validation.tsx rename to x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx index 577ec12e634ed..892ee0926ca38 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/validation.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx @@ -6,25 +6,30 @@ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ValidationResult } from '../../../../triggers_actions_ui/public/types'; +import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; -export function validate(opts: any): ValidationResult { +interface ValidateOptions { + duration: string; + threshold: number; +} + +export function validate(inputValues: ValidateOptions): ValidationResult { const validationResult = { errors: {} }; const errors: { [key: string]: string[] } = { duration: [], threshold: [], }; - if (!opts.duration) { + if (!inputValues.duration) { errors.duration.push( - i18n.translate('xpack.monitoring.alerts.cpuUsage.validation.duration', { + i18n.translate('xpack.monitoring.alerts.validation.duration', { defaultMessage: 'A valid duration is required.', }) ); } - if (isNaN(opts.threshold)) { + if (isNaN(inputValues.threshold)) { errors.threshold.push( - i18n.translate('xpack.monitoring.alerts.cpuUsage.validation.threshold', { + i18n.translate('xpack.monitoring.alerts.validation.threshold', { defaultMessage: 'A valid number is required.', }) ); diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index c9f82eb521433..fb4ecacf57fd6 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -6,9 +6,9 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; -import { validate } from './validation'; import { ALERT_CPU_USAGE } from '../../../common/constants'; -import { Expression } from './expression'; +import { validate } from '../components/duration/validation'; +import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { CpuUsageAlert } from '../../../server/alerts'; @@ -18,7 +18,7 @@ export function createCpuUsageAlertType(): AlertTypeModel { id: ALERT_CPU_USAGE, name: alert.label, iconClass: 'bell', - alertParamsExpression: (props: any) => ( + alertParamsExpression: (props: Props) => ( ), validate, diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx new file mode 100644 index 0000000000000..c2abb35612b38 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { validate } from '../components/duration/validation'; +import { Expression, Props } from '../components/duration/expression'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { DiskUsageAlert } from '../../../server/alerts'; + +export function createDiskUsageAlertType(): AlertTypeModel { + return { + id: DiskUsageAlert.TYPE, + name: DiskUsageAlert.LABEL, + iconClass: 'bell', + alertParamsExpression: (props: Props) => ( + + ), + validate, + defaultActionMessage: '{{context.internalFullMessage}}', + requiresAppContext: true, + }; +} diff --git a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx index 29e0822ad684d..02f5703f66382 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx +++ b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx @@ -17,6 +17,7 @@ import { formatTimestampToDuration } from '../../../common'; import { CALCULATE_DURATION_UNTIL } from '../../../common/constants'; import { AlertMessageTokenType } from '../../../common/enums'; import { Legacy } from '../../legacy_shims'; +import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; export function replaceTokens(alertMessage: AlertMessage): JSX.Element | string | null { if (!alertMessage) { @@ -58,10 +59,11 @@ export function replaceTokens(alertMessage: AlertMessage): JSX.Element | string const index = text.indexOf(linkPart[0]); const preString = text.substring(0, index); const postString = text.substring(index + linkPart[0].length); + const safeLink = getSafeForExternalLink(`#/${linkToken.url}`); element = ( {preString} - {linkPart[1]} + {linkPart[1]} {postString} ); @@ -83,7 +85,9 @@ export function replaceTokens(alertMessage: AlertMessage): JSX.Element | string element = ( {preString} - {linkPart[1]} + + {linkPart[1]} + {postString} ); diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js index 91752be83eb4f..61a24f31ca89a 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js @@ -40,6 +40,7 @@ import { ALERT_LICENSE_EXPIRATION, ALERT_CLUSTER_HEALTH, ALERT_CPU_USAGE, + ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, } from '../../../../common/constants'; @@ -157,6 +158,7 @@ const OVERVIEW_PANEL_ALERTS = [ALERT_CLUSTER_HEALTH, ALERT_LICENSE_EXPIRATION]; const NODES_PANEL_ALERTS = [ ALERT_CPU_USAGE, + ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, ]; diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index 087e7acc4c703..a9c26ca7103a2 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -24,6 +24,7 @@ import { MonitoringStartPluginDependencies, MonitoringConfig } from './types'; import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; import { createCpuUsageAlertType } from './alerts/cpu_usage_alert'; import { createLegacyAlertTypes } from './alerts/legacy_alert'; +import { createDiskUsageAlertType } from './alerts/disk_usage_alert'; interface MonitoringSetupPluginDependencies { home?: HomePublicPluginSetup; @@ -71,6 +72,7 @@ export class MonitoringPlugin } plugins.triggers_actions_ui.alertTypeRegistry.register(createCpuUsageAlertType()); + plugins.triggers_actions_ui.alertTypeRegistry.register(createDiskUsageAlertType()); const legacyAlertTypes = createLegacyAlertTypes(); for (const legacyAlertType of legacyAlertTypes) { plugins.triggers_actions_ui.alertTypeRegistry.register(legacyAlertType); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js index 5c4b4d28b93cb..8c30e4a2c1b07 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js @@ -17,7 +17,11 @@ import template from './index.html'; import { Legacy } from '../../../../legacy_shims'; import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced'; import { MonitoringViewBaseController } from '../../../base_controller'; -import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE } from '../../../../../common/constants'; +import { + CODE_PATH_ELASTICSEARCH, + ALERT_CPU_USAGE, + ALERT_DISK_USAGE, +} from '../../../../../common/constants'; function getPageData($injector) { const $http = $injector.get('$http'); @@ -67,7 +71,7 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], filters: [ { nodeUuid: nodeName, diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js index b4b3c7ca55303..ed2603e6dfff3 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js @@ -18,7 +18,11 @@ import { Node } from '../../../components/elasticsearch/node/node'; import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices'; import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE } from '../../../../common/constants'; +import { + CODE_PATH_ELASTICSEARCH, + ALERT_CPU_USAGE, + ALERT_DISK_USAGE, +} from '../../../../common/constants'; uiRoutes.when('/elasticsearch/nodes/:node', { template, @@ -51,7 +55,7 @@ uiRoutes.when('/elasticsearch/nodes/:node', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], filters: [ { nodeUuid: nodeName, diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js index 33584f802a56e..66fcac43e4fc5 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js @@ -19,6 +19,7 @@ import { ELASTICSEARCH_SYSTEM_ID, CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_DISK_USAGE, } from '../../../../common/constants'; uiRoutes.when('/elasticsearch/nodes', { @@ -86,7 +87,7 @@ uiRoutes.when('/elasticsearch/nodes', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], }, }, }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts new file mode 100644 index 0000000000000..41c8bba17df0a --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { AlertMessageDocLinkToken } from './types'; +import { AlertMessageTokenType } from '../../common/enums'; + +export class AlertingDefaults { + public static readonly ALERT_STATE = { + resolved: i18n.translate('xpack.monitoring.alerts.state.resolved', { + defaultMessage: 'resolved', + }), + firing: i18n.translate('xpack.monitoring.alerts.state.firing', { + defaultMessage: 'firing', + }), + }; + public static readonly ALERT_TYPE = { + context: { + internalShortMessage: { + name: 'internalShortMessage', + description: i18n.translate( + 'xpack.monitoring.alerts.actionVariables.internalShortMessage', + { + defaultMessage: 'The short internal message generated by Elastic.', + } + ), + }, + internalFullMessage: { + name: 'internalFullMessage', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.internalFullMessage', { + defaultMessage: 'The full internal message generated by Elastic.', + }), + }, + state: { + name: 'state', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.state', { + defaultMessage: 'The current state of the alert.', + }), + }, + clusterName: { + name: 'clusterName', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.clusterName', { + defaultMessage: 'The cluster to which the nodes belong.', + }), + }, + action: { + name: 'action', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.action', { + defaultMessage: 'The recommended action for this alert.', + }), + }, + actionPlain: { + name: 'actionPlain', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.actionPlain', { + defaultMessage: 'The recommended action for this alert, without any markdown.', + }), + }, + }, + }; +} + +export const createLink = ( + linkText: string, + linkURL: string, + type: AlertMessageTokenType = AlertMessageTokenType.DocLink +) => { + const link = type === AlertMessageTokenType.DocLink ? { partialUrl: linkURL } : { url: linkURL }; + return { + text: linkText, + tokens: [ + { + ...link, + startToken: '#start_link', + endToken: '#end_link', + type, + } as AlertMessageDocLinkToken, + ], + }; +}; diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts index d8fa703c7f785..60693eb42a30e 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts @@ -63,6 +63,6 @@ describe('AlertsFactory', () => { it('should get all', () => { const alerts = AlertsFactory.getAll(); - expect(alerts.length).toBe(7); + expect(alerts.length).toBe(8); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts index b91eab05cf912..6b1c0d5fffe18 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts @@ -6,6 +6,7 @@ import { CpuUsageAlert, + DiskUsageAlert, NodesChangedAlert, ClusterHealthAlert, LicenseExpirationAlert, @@ -18,6 +19,7 @@ import { ALERT_CLUSTER_HEALTH, ALERT_LICENSE_EXPIRATION, ALERT_CPU_USAGE, + ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_KIBANA_VERSION_MISMATCH, @@ -29,6 +31,7 @@ export const BY_TYPE = { [ALERT_CLUSTER_HEALTH]: ClusterHealthAlert, [ALERT_LICENSE_EXPIRATION]: LicenseExpirationAlert, [ALERT_CPU_USAGE]: CpuUsageAlert, + [ALERT_DISK_USAGE]: DiskUsageAlert, [ALERT_NODES_CHANGED]: NodesChangedAlert, [ALERT_LOGSTASH_VERSION_MISMATCH]: LogstashVersionMismatchAlert, [ALERT_KIBANA_VERSION_MISMATCH]: KibanaVersionMismatchAlert, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index f583b4882f83c..aff84710d27ad 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -238,7 +238,7 @@ export class BaseAlert { ); const data = await this.fetchData(params, callCluster, clusters, uiSettings, availableCcs); - this.processData(data, clusters, services, logger); + return await this.processData(data, clusters, services, logger, state); } protected async fetchData( @@ -252,12 +252,13 @@ export class BaseAlert { throw new Error('Child classes must implement `fetchData`'); } - protected processData( + protected async processData( data: AlertData[], clusters: AlertCluster[], services: AlertServices, - logger: Logger - ) { + logger: Logger, + instanceState: unknown + ): Promise> { for (const item of data) { const cluster = clusters.find((c: AlertCluster) => c.clusterUuid === item.clusterUuid); if (!cluster) { diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts index 66085b53516a2..22b6c6607016f 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts @@ -25,6 +25,7 @@ describe('ClusterHealthAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { name: 'clusterHealth', description: 'The health of the cluster.' }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -34,7 +35,6 @@ describe('ClusterHealthAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { name: 'clusterHealth', description: 'The health of the cluster.' }, { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, { name: 'action', description: 'The recommended action for this alert.' }, { @@ -129,7 +129,7 @@ describe('ClusterHealthAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -157,11 +157,10 @@ describe('ClusterHealthAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[Allocate missing replica shards.](http://localhost:5601/app/monitoring#elasticsearch/indices?_g=(cluster_uuid:abc123))', + action: '[Allocate missing replica shards.](elasticsearch/indices)', actionPlain: 'Allocate missing replica shards.', internalFullMessage: - 'Cluster health alert is firing for testCluster. Current health is yellow. [Allocate missing replica shards.](http://localhost:5601/app/monitoring#elasticsearch/indices?_g=(cluster_uuid:abc123))', + 'Cluster health alert is firing for testCluster. Current health is yellow. [Allocate missing replica shards.](elasticsearch/indices)', internalShortMessage: 'Cluster health alert is firing for testCluster. Current health is yellow. Allocate missing replica shards.', clusterName, @@ -210,7 +209,7 @@ describe('ClusterHealthAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -242,7 +241,7 @@ describe('ClusterHealthAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: false, message: { diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts index bb6c471591417..427dd2f86de00 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts @@ -22,6 +22,7 @@ import { AlertMessageTokenType, AlertClusterHealthType } from '../../common/enum import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; import { CommonAlertParams } from '../../common/types'; +import { AlertingDefaults } from './alerts_common'; const RED_STATUS_MESSAGE = i18n.translate('xpack.monitoring.alerts.clusterHealth.redMessage', { defaultMessage: 'Allocate missing primary and replica shards', @@ -44,30 +45,6 @@ export class ClusterHealthAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.clusterHealth.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.clusterHealth.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate('xpack.monitoring.alerts.clusterHealth.actionVariables.state', { - defaultMessage: 'The current state of the alert.', - }), - }, { name: 'clusterHealth', description: i18n.translate( @@ -77,30 +54,7 @@ export class ClusterHealthAlert extends BaseAlert { } ), }, - { - name: 'clusterName', - description: i18n.translate( - 'xpack.monitoring.alerts.clusterHealth.actionVariables.clusterName', - { - defaultMessage: 'The cluster to which the nodes belong.', - } - ), - }, - { - name: 'action', - description: i18n.translate('xpack.monitoring.alerts.clusterHealth.actionVariables.action', { - defaultMessage: 'The recommended action for this alert.', - }), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.clusterHealth.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( @@ -128,7 +82,6 @@ export class ClusterHealthAlert extends BaseAlert { shouldFire: !legacyAlert.resolved_timestamp, severity: mapLegacySeverity(legacyAlert.metadata.severity), meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -214,9 +167,7 @@ export class ClusterHealthAlert extends BaseAlert { }, } ), - state: i18n.translate('xpack.monitoring.alerts.clusterHealth.resolved', { - defaultMessage: `resolved`, - }), + state: AlertingDefaults.ALERT_STATE.resolved, clusterHealth: health, clusterName: cluster.clusterName, }); @@ -229,14 +180,8 @@ export class ClusterHealthAlert extends BaseAlert { : i18n.translate('xpack.monitoring.alerts.clusterHealth.action.warning', { defaultMessage: `Allocate missing replica shards.`, }); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#elasticsearch/indices?_g=(${globalState.join( - ',' - )})`; - const action = `[${actionText}](${url})`; + + const action = `[${actionText}](elasticsearch/indices)`; instance.scheduleActions('default', { internalShortMessage: i18n.translate( 'xpack.monitoring.alerts.clusterHealth.firing.internalShortMessage', @@ -260,9 +205,7 @@ export class ClusterHealthAlert extends BaseAlert { }, } ), - state: i18n.translate('xpack.monitoring.alerts.clusterHealth.firing', { - defaultMessage: `firing`, - }), + state: AlertingDefaults.ALERT_STATE.firing, clusterHealth: health, clusterName: cluster.clusterName, action, diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts index 2705a77e0fce4..495fe993cca1b 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts @@ -27,6 +27,8 @@ describe('CpuUsageAlert', () => { expect(alert.defaultParams).toStrictEqual({ threshold: 85, duration: '5m' }); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { name: 'nodes', description: 'The list of nodes reporting high cpu usage.' }, + { name: 'count', description: 'The number of nodes reporting high cpu usage.' }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -36,8 +38,6 @@ describe('CpuUsageAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { name: 'nodes', description: 'The list of nodes reporting high cpu usage.' }, - { name: 'count', description: 'The number of nodes reporting high cpu usage.' }, { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, { name: 'action', description: 'The recommended action for this alert.' }, { @@ -78,6 +78,7 @@ describe('CpuUsageAlert', () => { }; const kibanaUrl = 'http://localhost:5601'; + const hasScheduledActions = jest.fn(); const replaceState = jest.fn(); const scheduleActions = jest.fn(); const getState = jest.fn(); @@ -86,6 +87,7 @@ describe('CpuUsageAlert', () => { callCluster: jest.fn(), alertInstanceFactory: jest.fn().mockImplementation(() => { return { + hasScheduledActions, replaceState, scheduleActions, getState, @@ -133,6 +135,7 @@ describe('CpuUsageAlert', () => { expect(replaceState).toHaveBeenCalledWith({ alertStates: [ { + ccs: undefined, cluster: { clusterUuid, clusterName }, cpuUsage, nodeId, @@ -193,9 +196,9 @@ describe('CpuUsageAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + internalFullMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](elasticsearch/nodes)`, internalShortMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify CPU levels across affected nodes.`, - action: `[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify CPU levels across affected nodes.', clusterName, count, @@ -370,9 +373,9 @@ describe('CpuUsageAlert', () => { } as any); const count = 1; expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + internalFullMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](elasticsearch/nodes)`, internalShortMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify CPU levels across affected nodes.`, - action: `[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify CPU levels across affected nodes.', clusterName, count, @@ -559,11 +562,10 @@ describe('CpuUsageAlert', () => { expect(scheduleActions.mock.calls[0]).toEqual([ 'default', { - action: - '[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + action: '[View nodes](elasticsearch/nodes)', actionPlain: 'Verify CPU levels across affected nodes.', internalFullMessage: - 'CPU usage alert is firing for 1 node(s) in cluster: testCluster. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + 'CPU usage alert is firing for 1 node(s) in cluster: testCluster. [View nodes](elasticsearch/nodes)', internalShortMessage: 'CPU usage alert is firing for 1 node(s) in cluster: testCluster. Verify CPU levels across affected nodes.', nodes: 'anotherNode:99.00', @@ -594,7 +596,7 @@ describe('CpuUsageAlert', () => { expect(scheduleActions).toHaveBeenCalledWith('default', { internalFullMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify CPU levels across affected nodes.`, internalShortMessage: `CPU usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify CPU levels across affected nodes.`, - action: `[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify CPU levels across affected nodes.', clusterName, count, diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index 5bca84e33da3c..4228354f52748 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IUiSettingsClient, Logger } from 'kibana/server'; +import { IUiSettingsClient } from 'kibana/server'; import { i18n } from '@kbn/i18n'; import { BaseAlert } from './base_alert'; import { @@ -16,7 +16,6 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertInstanceState, - AlertMessageDocLinkToken, } from './types'; import { AlertInstance, AlertServices } from '../../../alerts/server'; import { INDEX_PATTERN_ELASTICSEARCH, ALERT_CPU_USAGE } from '../../common/constants'; @@ -31,15 +30,9 @@ import { CommonAlertParams, CommonAlertParamDetail, } from '../../common/types'; +import { AlertingDefaults, createLink } from './alerts_common'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; -const RESOLVED = i18n.translate('xpack.monitoring.alerts.cpuUsage.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.cpuUsage.firing', { - defaultMessage: 'firing', -}); - const DEFAULT_THRESHOLD = 85; const DEFAULT_DURATION = '5m'; @@ -75,30 +68,6 @@ export class CpuUsageAlert extends BaseAlert { }; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.cpuUsage.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.cpuUsage.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.state', { - defaultMessage: 'The current state of the alert.', - }), - }, { name: 'nodes', description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.nodes', { @@ -111,24 +80,7 @@ export class CpuUsageAlert extends BaseAlert { defaultMessage: 'The number of nodes reporting high cpu usage.', }), }, - { - name: 'clusterName', - description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.clusterName', { - defaultMessage: 'The cluster to which the nodes belong.', - }), - }, - { - name: 'action', - description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.action', { - defaultMessage: 'The recommended action for this alert.', - }), - }, - { - name: 'actionPlain', - description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.actionPlain', { - defaultMessage: 'The recommended action for this alert, without any markdown.', - }), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( @@ -237,32 +189,18 @@ export class CpuUsageAlert extends BaseAlert { }, }), nextSteps: [ - { - text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads', { - defaultMessage: `#start_linkCheck hot threads#end_link`, + createLink( + i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads', { + defaultMessage: '#start_linkCheck hot threads#end_link', }), - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: AlertMessageTokenType.DocLink, - partialUrl: `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html`, - } as AlertMessageDocLinkToken, - ], - }, - { - text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks', { - defaultMessage: `#start_linkCheck long running tasks#end_link`, + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html` + ), + createLink( + i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks', { + defaultMessage: '#start_linkCheck long running tasks#end_link', }), - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: AlertMessageTokenType.DocLink, - partialUrl: `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html`, - } as AlertMessageDocLinkToken, - ], - }, + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html` + ), ], tokens: [ { @@ -292,13 +230,6 @@ export class CpuUsageAlert extends BaseAlert { return; } - const ccs = instanceState.alertStates.reduce((accum: string, state): string => { - if (state.ccs) { - return state.ccs; - } - return accum; - }, ''); - const firingCount = instanceState.alertStates.filter((alertState) => alertState.ui.isFiring) .length; const firingNodes = instanceState.alertStates @@ -315,14 +246,7 @@ export class CpuUsageAlert extends BaseAlert { const fullActionText = i18n.translate('xpack.monitoring.alerts.cpuUsage.fullAction', { defaultMessage: 'View nodes', }); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (ccs) { - globalState.push(`ccs:${ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#elasticsearch/nodes?_g=(${globalState.join( - ',' - )})`; - const action = `[${fullActionText}](${url})`; + const action = `[${fullActionText}](elasticsearch/nodes)`; const internalShortMessage = i18n.translate( 'xpack.monitoring.alerts.cpuUsage.firing.internalShortMessage', { @@ -348,7 +272,7 @@ export class CpuUsageAlert extends BaseAlert { instance.scheduleActions('default', { internalShortMessage, internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, nodes: firingNodes, count: firingCount, clusterName: cluster.clusterName, @@ -388,7 +312,7 @@ export class CpuUsageAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, nodes: resolvedNodes, count: resolvedCount, clusterName: cluster.clusterName, @@ -397,18 +321,16 @@ export class CpuUsageAlert extends BaseAlert { } } - protected processData( + protected async processData( data: AlertData[], clusters: AlertCluster[], - services: AlertServices, - logger: Logger + services: AlertServices ) { for (const cluster of clusters) { const nodes = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); if (nodes.length === 0) { continue; } - const firingNodeUuids = nodes.reduce((list: string[], node) => { const stat = node.meta as AlertCpuUsageNodeStats; if (node.shouldFire) { @@ -437,13 +359,14 @@ export class CpuUsageAlert extends BaseAlert { } else { nodeState = this.getDefaultAlertState(cluster, node) as AlertCpuUsageState; } - nodeState.cpuUsage = stat.cpuUsage; nodeState.nodeId = stat.nodeId; nodeState.nodeName = stat.nodeName; if (node.shouldFire) { - nodeState.ui.triggeredMS = new Date().valueOf(); + if (!nodeState.ui.isFiring) { + nodeState.ui.triggeredMS = new Date().valueOf(); + } nodeState.ui.isFiring = true; nodeState.ui.message = this.getUiMessage(nodeState, node); nodeState.ui.severity = node.severity; @@ -455,7 +378,6 @@ export class CpuUsageAlert extends BaseAlert { nodeState.ui.message = this.getUiMessage(nodeState, node); shouldExecuteActions = true; } - if (indexInState === -1) { alertInstanceState.alertStates.push(nodeState); } else { @@ -466,7 +388,6 @@ export class CpuUsageAlert extends BaseAlert { ]; } } - instance.replaceState(alertInstanceState); if (shouldExecuteActions) { this.executeActions(instance, alertInstanceState, null, cluster); diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts new file mode 100644 index 0000000000000..546399f666b6c --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DiskUsageAlert } from './disk_usage_alert'; +import { ALERT_DISK_USAGE } from '../../common/constants'; +import { fetchDiskUsageNodeStats } from '../lib/alerts/fetch_disk_usage_node_stats'; +import { fetchClusters } from '../lib/alerts/fetch_clusters'; + +type IDiskUsageAlertMock = DiskUsageAlert & { + defaultParams: { + threshold: number; + duration: string; + }; +} & { + actionVariables: Array<{ + name: string; + description: string; + }>; +}; + +const RealDate = Date; + +jest.mock('../lib/alerts/fetch_disk_usage_node_stats', () => ({ + fetchDiskUsageNodeStats: jest.fn(), +})); +jest.mock('../lib/alerts/fetch_clusters', () => ({ + fetchClusters: jest.fn(), +})); + +describe('DiskUsageAlert', () => { + it('should have defaults', () => { + const alert = new DiskUsageAlert() as IDiskUsageAlertMock; + expect(alert.type).toBe(ALERT_DISK_USAGE); + expect(alert.label).toBe('Disk Usage'); + expect(alert.defaultThrottle).toBe('1d'); + expect(alert.defaultParams).toStrictEqual({ threshold: 90, duration: '5m' }); + expect(alert.actionVariables).toStrictEqual([ + { name: 'nodes', description: 'The list of nodes reporting high disk usage.' }, + { name: 'count', description: 'The number of nodes reporting high disk usage.' }, + { + name: 'internalShortMessage', + description: 'The short internal message generated by Elastic.', + }, + { + name: 'internalFullMessage', + description: 'The full internal message generated by Elastic.', + }, + { name: 'state', description: 'The current state of the alert.' }, + { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, + { name: 'action', description: 'The recommended action for this alert.' }, + { + name: 'actionPlain', + description: 'The recommended action for this alert, without any markdown.', + }, + ]); + }); + + describe('execute', () => { + const FakeDate = function () {}; + FakeDate.prototype.valueOf = () => 1; + + const clusterUuid = 'abc123'; + const clusterName = 'testCluster'; + const nodeId = 'myNodeId'; + const nodeName = 'myNodeName'; + const diskUsage = 91; + const stat = { + clusterUuid, + nodeId, + nodeName, + diskUsage, + }; + const getUiSettingsService = () => ({ + asScopedToClient: jest.fn(), + }); + const getLogger = () => ({ + debug: jest.fn(), + }); + const monitoringCluster = null; + const config = { + ui: { + ccs: { enabled: true }, + container: { elasticsearch: { enabled: false } }, + metricbeat: { index: 'metricbeat-*' }, + }, + }; + const kibanaUrl = 'http://localhost:5601'; + + const hasScheduledActions = jest.fn(); + const replaceState = jest.fn(); + const scheduleActions = jest.fn(); + const getState = jest.fn(); + const executorOptions = { + services: { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn().mockImplementation(() => { + return { + hasScheduledActions, + replaceState, + scheduleActions, + getState, + }; + }), + }, + state: {}, + }; + + beforeEach(() => { + Date = FakeDate as DateConstructor; + (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { + return [stat]; + }); + (fetchClusters as jest.Mock).mockImplementation(() => { + return [{ clusterUuid, clusterName }]; + }); + }); + + afterEach(() => { + Date = RealDate; + replaceState.mockReset(); + scheduleActions.mockReset(); + getState.mockReset(); + }); + + it('should fire actions', async () => { + const alert = new DiskUsageAlert() as IDiskUsageAlertMock; + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + params: alert.defaultParams, + } as any); + const count = 1; + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](elasticsearch/nodes)`, + internalShortMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + action: `[View nodes](elasticsearch/nodes)`, + actionPlain: 'Verify disk usage levels across affected nodes.', + clusterName, + count, + nodes: `${nodeName}:${diskUsage.toFixed(2)}`, + state: 'firing', + }); + }); + + it('should handle ccs', async () => { + const ccs = 'testCluster'; + (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { + return [ + { + ...stat, + ccs, + }, + ]; + }); + const alert = new DiskUsageAlert() as IDiskUsageAlertMock; + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + params: alert.defaultParams, + } as any); + const count = 1; + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. [View nodes](elasticsearch/nodes)`, + internalShortMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + action: `[View nodes](elasticsearch/nodes)`, + actionPlain: 'Verify disk usage levels across affected nodes.', + clusterName, + count, + nodes: `${nodeName}:${diskUsage.toFixed(2)}`, + state: 'firing', + }); + }); + + it('should fire with different messaging for cloud', async () => { + const alert = new DiskUsageAlert() as IDiskUsageAlertMock; + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + true + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + params: alert.defaultParams, + } as any); + const count = 1; + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + internalShortMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + action: `[View nodes](elasticsearch/nodes)`, + actionPlain: 'Verify disk usage levels across affected nodes.', + clusterName, + count, + nodes: `${nodeName}:${diskUsage.toFixed(2)}`, + state: 'firing', + }); + }); + }); +}); diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts new file mode 100644 index 0000000000000..e43dca3ce87b1 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -0,0 +1,343 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IUiSettingsClient, Logger } from 'kibana/server'; +import { i18n } from '@kbn/i18n'; +import { BaseAlert } from './base_alert'; +import { + AlertData, + AlertCluster, + AlertState, + AlertMessage, + AlertDiskUsageState, + AlertMessageTimeToken, + AlertMessageLinkToken, + AlertInstanceState, +} from './types'; +import { AlertInstance, AlertServices } from '../../../alerts/server'; +import { INDEX_PATTERN_ELASTICSEARCH, ALERT_DISK_USAGE } from '../../common/constants'; +import { fetchDiskUsageNodeStats } from '../lib/alerts/fetch_disk_usage_node_stats'; +import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; +import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { RawAlertInstance } from '../../../alerts/common'; +import { CommonAlertFilter, CommonAlertParams, CommonAlertParamDetail } from '../../common/types'; +import { AlertingDefaults, createLink } from './alerts_common'; +import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; + +interface ParamDetails { + [key: string]: CommonAlertParamDetail; +} + +export class DiskUsageAlert extends BaseAlert { + public static readonly PARAM_DETAILS: ParamDetails = { + threshold: { + label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.threshold.label', { + defaultMessage: `Notify when disk capacity is over`, + }), + type: AlertParamType.Percentage, + }, + duration: { + label: i18n.translate('xpack.monitoring.alerts.diskUsage.paramDetails.duration.label', { + defaultMessage: `Look at the average over`, + }), + type: AlertParamType.Duration, + }, + }; + public static paramDetails = DiskUsageAlert.PARAM_DETAILS; + public static readonly TYPE = ALERT_DISK_USAGE; + public static readonly LABEL = i18n.translate('xpack.monitoring.alerts.diskUsage.label', { + defaultMessage: 'Disk Usage', + }); + public type = DiskUsageAlert.TYPE; + public label = DiskUsageAlert.LABEL; + + protected defaultParams = { + threshold: 90, + duration: '5m', + }; + + protected actionVariables = [ + { + name: 'nodes', + description: i18n.translate('xpack.monitoring.alerts.diskUsage.actionVariables.nodes', { + defaultMessage: 'The list of nodes reporting high disk usage.', + }), + }, + { + name: 'count', + description: i18n.translate('xpack.monitoring.alerts.diskUsage.actionVariables.count', { + defaultMessage: 'The number of nodes reporting high disk usage.', + }), + }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), + ]; + + protected async fetchData( + params: CommonAlertParams, + callCluster: any, + clusters: AlertCluster[], + uiSettings: IUiSettingsClient, + availableCcs: string[] + ): Promise { + let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); + if (availableCcs) { + esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); + } + const { duration, threshold } = params; + const stats = await fetchDiskUsageNodeStats( + callCluster, + clusters, + esIndexPattern, + duration as string, + this.config.ui.max_bucket_size + ); + + return stats.map((stat) => { + const { clusterUuid, nodeId, diskUsage, ccs } = stat; + return { + instanceKey: `${clusterUuid}:${nodeId}`, + shouldFire: diskUsage > threshold, + severity: AlertSeverity.Danger, + meta: stat, + clusterUuid, + ccs, + }; + }); + } + + protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) { + const alertInstanceStates = alertInstance.state?.alertStates as AlertDiskUsageState[]; + const nodeUuid = filters?.find((filter) => filter.nodeUuid); + + if (!filters || !filters.length || !alertInstanceStates?.length || !nodeUuid) { + return true; + } + + const nodeAlerts = alertInstanceStates.filter(({ nodeId }) => nodeId === nodeUuid); + return Boolean(nodeAlerts.length); + } + + protected getDefaultAlertState(cluster: AlertCluster, item: AlertData): AlertState { + const currentState = super.getDefaultAlertState(cluster, item); + currentState.ui.severity = AlertSeverity.Warning; + return currentState; + } + + protected getUiMessage(alertState: AlertState, item: AlertData): AlertMessage { + const stat = item.meta as AlertDiskUsageState; + if (!alertState.ui.isFiring) { + return { + text: i18n.translate('xpack.monitoring.alerts.diskUsage.ui.resolvedMessage', { + defaultMessage: `The disk usage on node {nodeName} is now under the threshold, currently reporting at {diskUsage}% as of #resolved`, + values: { + nodeName: stat.nodeName, + diskUsage: stat.diskUsage.toFixed(2), + }, + }), + tokens: [ + { + startToken: '#resolved', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.resolvedMS, + } as AlertMessageTimeToken, + ], + }; + } + return { + text: i18n.translate('xpack.monitoring.alerts.diskUsage.ui.firingMessage', { + defaultMessage: `Node #start_link{nodeName}#end_link is reporting disk usage of {diskUsage}% at #absolute`, + values: { + nodeName: stat.nodeName, + diskUsage: stat.diskUsage, + }, + }), + nextSteps: [ + createLink( + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', { + defaultMessage: '#start_linkTune for disk usage#end_link', + }), + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tune-for-disk-usage.html` + ), + createLink( + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.identifyIndices', { + defaultMessage: '#start_linkIdentify large indices#end_link', + }), + 'elasticsearch/indices', + AlertMessageTokenType.Link + ), + createLink( + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.ilmPolicies', { + defaultMessage: '#start_linkImplement ILM policies#end_link', + }), + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/index-lifecycle-management.html` + ), + createLink( + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.addMoreNodes', { + defaultMessage: '#start_linkAdd more data nodes#end_link', + }), + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/add-elasticsearch-nodes.html` + ), + createLink( + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.resizeYourDeployment', { + defaultMessage: '#start_linkResize your deployment (ECE)#end_link', + }), + `{elasticWebsiteUrl}/guide/en/cloud-enterprise/current/ece-resize-deployment.html` + ), + ], + tokens: [ + { + startToken: '#absolute', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.triggeredMS, + } as AlertMessageTimeToken, + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertMessageTokenType.Link, + url: `elasticsearch/nodes/${stat.nodeId}`, + } as AlertMessageLinkToken, + ], + }; + } + + protected executeActions( + instance: AlertInstance, + { alertStates }: AlertInstanceState, + item: AlertData | null, + cluster: AlertCluster + ) { + const firingNodes = alertStates.filter( + (alertState) => alertState.ui.isFiring + ) as AlertDiskUsageState[]; + const firingCount = firingNodes.length; + + if (firingCount > 0) { + const shortActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.shortAction', { + defaultMessage: 'Verify disk usage levels across affected nodes.', + }); + const fullActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.fullAction', { + defaultMessage: 'View nodes', + }); + + const action = `[${fullActionText}](elasticsearch/nodes)`; + const internalShortMessage = i18n.translate( + 'xpack.monitoring.alerts.diskUsage.firing.internalShortMessage', + { + defaultMessage: `Disk usage alert is firing for {count} node(s) in cluster: {clusterName}. {shortActionText}`, + values: { + count: firingCount, + clusterName: cluster.clusterName, + shortActionText, + }, + } + ); + const internalFullMessage = i18n.translate( + 'xpack.monitoring.alerts.diskUsage.firing.internalFullMessage', + { + defaultMessage: `Disk usage alert is firing for {count} node(s) in cluster: {clusterName}. {action}`, + values: { + count: firingCount, + clusterName: cluster.clusterName, + action, + }, + } + ); + + instance.scheduleActions('default', { + internalShortMessage, + internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, + state: AlertingDefaults.ALERT_STATE.firing, + nodes: firingNodes + .map((state) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) + .join(','), + count: firingCount, + clusterName: cluster.clusterName, + action, + actionPlain: shortActionText, + }); + } else { + const resolvedNodes = (alertStates as AlertDiskUsageState[]) + .filter((state) => !state.ui.isFiring) + .map((state) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`); + const resolvedCount = resolvedNodes.length; + + if (resolvedCount > 0) { + const internalMessage = i18n.translate( + 'xpack.monitoring.alerts.diskUsage.resolved.internalMessage', + { + defaultMessage: `Disk usage alert is resolved for {count} node(s) in cluster: {clusterName}.`, + values: { + count: resolvedCount, + clusterName: cluster.clusterName, + }, + } + ); + + instance.scheduleActions('default', { + internalShortMessage: internalMessage, + internalFullMessage: internalMessage, + state: AlertingDefaults.ALERT_STATE.resolved, + nodes: resolvedNodes.join(','), + count: resolvedCount, + clusterName: cluster.clusterName, + }); + } + } + } + + protected async processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices, + logger: Logger, + state: any + ) { + const currentUTC = +new Date(); + for (const cluster of clusters) { + const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); + if (!nodes.length) { + continue; + } + + const firingNodeUuids = nodes + .filter((node) => node.shouldFire) + .map((node) => node.meta.nodeId) + .join(','); + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids}`; + const instance = services.alertInstanceFactory(instanceId); + const newAlertStates: AlertDiskUsageState[] = []; + + for (const node of nodes) { + const stat = node.meta as AlertDiskUsageState; + const nodeState = this.getDefaultAlertState(cluster, node) as AlertDiskUsageState; + nodeState.diskUsage = stat.diskUsage; + nodeState.nodeId = stat.nodeId; + nodeState.nodeName = stat.nodeName; + + if (node.shouldFire) { + nodeState.ui.triggeredMS = currentUTC; + nodeState.ui.isFiring = true; + nodeState.ui.severity = node.severity; + newAlertStates.push(nodeState); + } + nodeState.ui.message = this.getUiMessage(nodeState, node); + } + + const alertInstanceState = { alertStates: newAlertStates }; + instance.replaceState(alertInstanceState); + if (newAlertStates.length && !instance.hasScheduledActions()) { + this.executeActions(instance, alertInstanceState, null, cluster); + state.lastExecutedAction = currentUTC; + } + } + + state.lastChecked = currentUTC; + return state; + } +} diff --git a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts index 1db85f915d794..3422e8a7c78ad 100644 --- a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts @@ -25,6 +25,10 @@ describe('ElasticsearchVersionMismatchAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { + name: 'versionList', + description: 'The versions of Elasticsearch running in this cluster.', + }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -34,10 +38,6 @@ describe('ElasticsearchVersionMismatchAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { - name: 'versionList', - description: 'The versions of Elasticsearch running in this cluster.', - }, { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, { name: 'action', description: 'The recommended action for this alert.' }, { @@ -132,7 +132,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -148,11 +148,10 @@ describe('ElasticsearchVersionMismatchAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + action: '[View nodes](elasticsearch/nodes)', actionPlain: 'Verify you have the same version across all nodes.', internalFullMessage: - 'Elasticsearch version mismatch alert is firing for testCluster. Elasticsearch is running [8.0.0, 7.2.1]. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + 'Elasticsearch version mismatch alert is firing for testCluster. Elasticsearch is running [8.0.0, 7.2.1]. [View nodes](elasticsearch/nodes)', internalShortMessage: 'Elasticsearch version mismatch alert is firing for testCluster. Verify you have the same version across all nodes.', versionList: '[8.0.0, 7.2.1]', @@ -201,7 +200,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -233,7 +232,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: false, message: { diff --git a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts index e3b952fbbe5d3..f26b21f0c64c5 100644 --- a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.ts @@ -20,14 +20,9 @@ import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; +import { AlertingDefaults } from './alerts_common'; const WATCH_NAME = 'elasticsearch_version_mismatch'; -const RESOLVED = i18n.translate('xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.elasticsearchVersionMismatch.firing', { - defaultMessage: 'firing', -}); export class ElasticsearchVersionMismatchAlert extends BaseAlert { public type = ALERT_ELASTICSEARCH_VERSION_MISMATCH; @@ -37,33 +32,6 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.state', - { - defaultMessage: 'The current state of the alert.', - } - ), - }, { name: 'versionList', description: i18n.translate( @@ -73,33 +41,7 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { } ), }, - { - name: 'clusterName', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterName', - { - defaultMessage: 'The cluster to which the nodes belong.', - } - ), - }, - { - name: 'action', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action', - { - defaultMessage: 'The recommended action for this alert.', - } - ), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( @@ -131,7 +73,6 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { shouldFire: !legacyAlert.resolved_timestamp, severity, meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -206,7 +147,7 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, clusterName: cluster.clusterName, }); } else { @@ -222,14 +163,7 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { defaultMessage: 'View nodes', } ); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#elasticsearch/nodes?_g=(${globalState.join( - ',' - )})`; - const action = `[${fullActionText}](${url})`; + const action = `[${fullActionText}](elasticsearch/nodes)`; instance.scheduleActions('default', { internalShortMessage: i18n.translate( 'xpack.monitoring.alerts.elasticsearchVersionMismatch.firing.internalShortMessage', @@ -252,7 +186,7 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { }, } ), - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, clusterName: cluster.clusterName, versionList: versions, action, diff --git a/x-pack/plugins/monitoring/server/alerts/index.ts b/x-pack/plugins/monitoring/server/alerts/index.ts index 048e703d2222c..8fdac65514477 100644 --- a/x-pack/plugins/monitoring/server/alerts/index.ts +++ b/x-pack/plugins/monitoring/server/alerts/index.ts @@ -6,6 +6,7 @@ export { BaseAlert } from './base_alert'; export { CpuUsageAlert } from './cpu_usage_alert'; +export { DiskUsageAlert } from './disk_usage_alert'; export { ClusterHealthAlert } from './cluster_health_alert'; export { LicenseExpirationAlert } from './license_expiration_alert'; export { NodesChangedAlert } from './nodes_changed_alert'; diff --git a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts index 362532a995f2d..1082e9f6311a4 100644 --- a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts @@ -25,6 +25,14 @@ describe('KibanaVersionMismatchAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { + name: 'versionList', + description: 'The versions of Kibana running in this cluster.', + }, + { + name: 'clusterName', + description: 'The cluster to which the instances belong.', + }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -34,14 +42,6 @@ describe('KibanaVersionMismatchAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { - name: 'versionList', - description: 'The versions of Kibana running in this cluster.', - }, - { - name: 'clusterName', - description: 'The cluster to which the instances belong.', - }, { name: 'action', description: 'The recommended action for this alert.' }, { name: 'actionPlain', @@ -135,7 +135,7 @@ describe('KibanaVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -150,11 +150,10 @@ describe('KibanaVersionMismatchAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[View instances](http://localhost:5601/app/monitoring#kibana/instances?_g=(cluster_uuid:abc123))', + action: '[View instances](kibana/instances)', actionPlain: 'Verify you have the same version across all instances.', internalFullMessage: - 'Kibana version mismatch alert is firing for testCluster. Kibana is running [8.0.0, 7.2.1]. [View instances](http://localhost:5601/app/monitoring#kibana/instances?_g=(cluster_uuid:abc123))', + 'Kibana version mismatch alert is firing for testCluster. Kibana is running [8.0.0, 7.2.1]. [View instances](kibana/instances)', internalShortMessage: 'Kibana version mismatch alert is firing for testCluster. Verify you have the same version across all instances.', versionList: '[8.0.0, 7.2.1]', @@ -203,7 +202,7 @@ describe('KibanaVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -235,7 +234,7 @@ describe('KibanaVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: false, message: { diff --git a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts index 80e8701933f56..316f305603964 100644 --- a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts @@ -20,14 +20,9 @@ import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; +import { AlertingDefaults } from './alerts_common'; const WATCH_NAME = 'kibana_version_mismatch'; -const RESOLVED = i18n.translate('xpack.monitoring.alerts.kibanaVersionMismatch.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.kibanaVersionMismatch.firing', { - defaultMessage: 'firing', -}); export class KibanaVersionMismatchAlert extends BaseAlert { public type = ALERT_KIBANA_VERSION_MISMATCH; @@ -37,33 +32,6 @@ export class KibanaVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate( - 'xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.state', - { - defaultMessage: 'The current state of the alert.', - } - ), - }, { name: 'versionList', description: i18n.translate( @@ -82,24 +50,11 @@ export class KibanaVersionMismatchAlert extends BaseAlert { } ), }, - { - name: 'action', - description: i18n.translate( - 'xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.action', - { - defaultMessage: 'The recommended action for this alert.', - } - ), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + AlertingDefaults.ALERT_TYPE.context.internalShortMessage, + AlertingDefaults.ALERT_TYPE.context.internalFullMessage, + AlertingDefaults.ALERT_TYPE.context.state, + AlertingDefaults.ALERT_TYPE.context.action, + AlertingDefaults.ALERT_TYPE.context.actionPlain, ]; protected async fetchData( @@ -129,7 +84,6 @@ export class KibanaVersionMismatchAlert extends BaseAlert { shouldFire: !legacyAlert.resolved_timestamp, severity, meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -198,7 +152,7 @@ export class KibanaVersionMismatchAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, clusterName: cluster.clusterName, }); } else { @@ -214,12 +168,7 @@ export class KibanaVersionMismatchAlert extends BaseAlert { defaultMessage: 'View instances', } ); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#kibana/instances?_g=(${globalState.join(',')})`; - const action = `[${fullActionText}](${url})`; + const action = `[${fullActionText}](kibana/instances)`; instance.scheduleActions('default', { internalShortMessage: i18n.translate( 'xpack.monitoring.alerts.kibanaVersionMismatch.firing.internalShortMessage', @@ -242,7 +191,7 @@ export class KibanaVersionMismatchAlert extends BaseAlert { }, } ), - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, clusterName: cluster.clusterName, versionList: versions, action, diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts index da94e4af83802..74c300d971898 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts @@ -32,6 +32,8 @@ describe('LicenseExpirationAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { name: 'expiredDate', description: 'The date when the license expires.' }, + { name: 'clusterName', description: 'The cluster to which the license belong.' }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -41,9 +43,6 @@ describe('LicenseExpirationAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { name: 'expiredDate', description: 'The date when the license expires.' }, - - { name: 'clusterName', description: 'The cluster to which the license belong.' }, { name: 'action', description: 'The recommended action for this alert.' }, { name: 'actionPlain', @@ -139,7 +138,7 @@ describe('LicenseExpirationAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -177,11 +176,10 @@ describe('LicenseExpirationAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[Please update your license.](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + action: '[Please update your license.](elasticsearch/nodes)', actionPlain: 'Please update your license.', internalFullMessage: - 'License expiration alert is firing for testCluster. Your license expires in THE_DATE. [Please update your license.](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + 'License expiration alert is firing for testCluster. Your license expires in THE_DATE. [Please update your license.](elasticsearch/nodes)', internalShortMessage: 'License expiration alert is firing for testCluster. Your license expires in THE_DATE. Please update your license.', clusterName, @@ -230,7 +228,7 @@ describe('LicenseExpirationAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -262,7 +260,7 @@ describe('LicenseExpirationAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: false, message: { diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts index 7a249db28d2db..f1412ff0fc91a 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts @@ -28,13 +28,7 @@ import { AlertMessageTokenType } from '../../common/enums'; import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; - -const RESOLVED = i18n.translate('xpack.monitoring.alerts.licenseExpiration.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.licenseExpiration.firing', { - defaultMessage: 'firing', -}); +import { AlertingDefaults } from './alerts_common'; const WATCH_NAME = 'xpack_license_expiration'; @@ -45,33 +39,6 @@ export class LicenseExpirationAlert extends BaseAlert { }); public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.licenseExpiration.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.licenseExpiration.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate( - 'xpack.monitoring.alerts.licenseExpiration.actionVariables.state', - { - defaultMessage: 'The current state of the alert.', - } - ), - }, { name: 'expiredDate', description: i18n.translate( @@ -90,24 +57,11 @@ export class LicenseExpirationAlert extends BaseAlert { } ), }, - { - name: 'action', - description: i18n.translate( - 'xpack.monitoring.alerts.licenseExpiration.actionVariables.action', - { - defaultMessage: 'The recommended action for this alert.', - } - ), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.licenseExpiration.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + AlertingDefaults.ALERT_TYPE.context.internalShortMessage, + AlertingDefaults.ALERT_TYPE.context.internalFullMessage, + AlertingDefaults.ALERT_TYPE.context.state, + AlertingDefaults.ALERT_TYPE.context.action, + AlertingDefaults.ALERT_TYPE.context.actionPlain, ]; protected async fetchData( @@ -135,7 +89,6 @@ export class LicenseExpirationAlert extends BaseAlert { shouldFire: !legacyAlert.resolved_timestamp, severity: mapLegacySeverity(legacyAlert.metadata.severity), meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -211,7 +164,7 @@ export class LicenseExpirationAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, expiredDate: $expiry.format(FORMAT_DURATION_TEMPLATE_SHORT).trim(), clusterName: cluster.clusterName, }); @@ -219,14 +172,7 @@ export class LicenseExpirationAlert extends BaseAlert { const actionText = i18n.translate('xpack.monitoring.alerts.licenseExpiration.action', { defaultMessage: 'Please update your license.', }); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#elasticsearch/nodes?_g=(${globalState.join( - ',' - )})`; - const action = `[${actionText}](${url})`; + const action = `[${actionText}](elasticsearch/nodes)`; const expiredDate = $expiry.format(FORMAT_DURATION_TEMPLATE_SHORT).trim(); instance.scheduleActions('default', { internalShortMessage: i18n.translate( @@ -251,7 +197,7 @@ export class LicenseExpirationAlert extends BaseAlert { }, } ), - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, expiredDate, clusterName: cluster.clusterName, action, diff --git a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts index 5ed189014cc6e..d3729660040d8 100644 --- a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts @@ -25,6 +25,10 @@ describe('LogstashVersionMismatchAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { + name: 'versionList', + description: 'The versions of Logstash running in this cluster.', + }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -34,10 +38,6 @@ describe('LogstashVersionMismatchAlert', () => { description: 'The full internal message generated by Elastic.', }, { name: 'state', description: 'The current state of the alert.' }, - { - name: 'versionList', - description: 'The versions of Logstash running in this cluster.', - }, { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, { name: 'action', description: 'The recommended action for this alert.' }, { @@ -132,7 +132,7 @@ describe('LogstashVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -147,11 +147,10 @@ describe('LogstashVersionMismatchAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[View nodes](http://localhost:5601/app/monitoring#logstash/nodes?_g=(cluster_uuid:abc123))', + action: '[View nodes](logstash/nodes)', actionPlain: 'Verify you have the same version across all nodes.', internalFullMessage: - 'Logstash version mismatch alert is firing for testCluster. Logstash is running [8.0.0, 7.2.1]. [View nodes](http://localhost:5601/app/monitoring#logstash/nodes?_g=(cluster_uuid:abc123))', + 'Logstash version mismatch alert is firing for testCluster. Logstash is running [8.0.0, 7.2.1]. [View nodes](logstash/nodes)', internalShortMessage: 'Logstash version mismatch alert is firing for testCluster. Verify you have the same version across all nodes.', versionList: '[8.0.0, 7.2.1]', @@ -200,7 +199,7 @@ describe('LogstashVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -232,7 +231,7 @@ describe('LogstashVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: false, message: { diff --git a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts index f996e54de28ef..37515e32e591a 100644 --- a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts @@ -20,14 +20,9 @@ import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { AlertSeverity } from '../../common/enums'; import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; +import { AlertingDefaults } from './alerts_common'; const WATCH_NAME = 'logstash_version_mismatch'; -const RESOLVED = i18n.translate('xpack.monitoring.alerts.logstashVersionMismatch.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.logstashVersionMismatch.firing', { - defaultMessage: 'firing', -}); export class LogstashVersionMismatchAlert extends BaseAlert { public type = ALERT_LOGSTASH_VERSION_MISMATCH; @@ -37,33 +32,6 @@ export class LogstashVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.state', - { - defaultMessage: 'The current state of the alert.', - } - ), - }, { name: 'versionList', description: i18n.translate( @@ -73,33 +41,7 @@ export class LogstashVersionMismatchAlert extends BaseAlert { } ), }, - { - name: 'clusterName', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.clusterName', - { - defaultMessage: 'The cluster to which the nodes belong.', - } - ), - }, - { - name: 'action', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.action', - { - defaultMessage: 'The recommended action for this alert.', - } - ), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( @@ -130,7 +72,6 @@ export class LogstashVersionMismatchAlert extends BaseAlert { shouldFire: !legacyAlert.resolved_timestamp, severity, meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -202,7 +143,7 @@ export class LogstashVersionMismatchAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, clusterName: cluster.clusterName, }); } else { @@ -218,12 +159,7 @@ export class LogstashVersionMismatchAlert extends BaseAlert { defaultMessage: 'View nodes', } ); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#logstash/nodes?_g=(${globalState.join(',')})`; - const action = `[${fullActionText}](${url})`; + const action = `[${fullActionText}](logstash/nodes)`; instance.scheduleActions('default', { internalShortMessage: i18n.translate( 'xpack.monitoring.alerts.logstashVersionMismatch.firing.internalShortMessage', @@ -246,7 +182,7 @@ export class LogstashVersionMismatchAlert extends BaseAlert { }, } ), - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, clusterName: cluster.clusterName, versionList: versions, action, diff --git a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts index ec2b19eb5dfae..63b061649027a 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts @@ -32,6 +32,9 @@ describe('NodesChangedAlert', () => { expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ + { name: 'added', description: 'The list of nodes added to the cluster.' }, + { name: 'removed', description: 'The list of nodes removed from the cluster.' }, + { name: 'restarted', description: 'The list of nodes restarted in the cluster.' }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -42,9 +45,6 @@ describe('NodesChangedAlert', () => { }, { name: 'state', description: 'The current state of the alert.' }, { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, - { name: 'added', description: 'The list of nodes added to the cluster.' }, - { name: 'removed', description: 'The list of nodes removed from the cluster.' }, - { name: 'restarted', description: 'The list of nodes restarted in the cluster.' }, { name: 'action', description: 'The recommended action for this alert.' }, { name: 'actionPlain', @@ -145,7 +145,7 @@ describe('NodesChangedAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -160,11 +160,10 @@ describe('NodesChangedAlert', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - action: - '[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + action: '[View nodes](elasticsearch/nodes)', actionPlain: 'Verify that you added, removed, or restarted nodes.', internalFullMessage: - 'Nodes changed alert is firing for testCluster. The following Elasticsearch nodes have been added: removed: restarted:test. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + 'Nodes changed alert is firing for testCluster. The following Elasticsearch nodes have been added: removed: restarted:test. [View nodes](elasticsearch/nodes)', internalShortMessage: 'Nodes changed alert is firing for testCluster. Verify that you added, removed, or restarted nodes.', added: '', @@ -212,7 +211,7 @@ describe('NodesChangedAlert', () => { // clusterUuid, // clusterName, // }, - // ccs: null, + // ccs: undefined, // ui: { // isFiring: true, // message: null, @@ -243,7 +242,7 @@ describe('NodesChangedAlert', () => { // alertStates: [ // { // cluster: { clusterUuid, clusterName }, - // ccs: null, + // ccs: undefined, // ui: { // isFiring: false, // message: { diff --git a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts index 73f3ee055c928..e03e6ea53ab4e 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -21,14 +21,9 @@ import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { CommonAlertParams } from '../../common/types'; import { fetchLegacyAlerts } from '../lib/alerts/fetch_legacy_alerts'; import { mapLegacySeverity } from '../lib/alerts/map_legacy_severity'; +import { AlertingDefaults } from './alerts_common'; const WATCH_NAME = 'elasticsearch_nodes'; -const RESOLVED = i18n.translate('xpack.monitoring.alerts.nodesChanged.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.nodesChanged.firing', { - defaultMessage: 'firing', -}); export class NodesChangedAlert extends BaseAlert { public type = ALERT_NODES_CHANGED; @@ -38,39 +33,6 @@ export class NodesChangedAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - { - name: 'internalShortMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.nodesChanged.actionVariables.internalShortMessage', - { - defaultMessage: 'The short internal message generated by Elastic.', - } - ), - }, - { - name: 'internalFullMessage', - description: i18n.translate( - 'xpack.monitoring.alerts.nodesChanged.actionVariables.internalFullMessage', - { - defaultMessage: 'The full internal message generated by Elastic.', - } - ), - }, - { - name: 'state', - description: i18n.translate('xpack.monitoring.alerts.nodesChanged.actionVariables.state', { - defaultMessage: 'The current state of the alert.', - }), - }, - { - name: 'clusterName', - description: i18n.translate( - 'xpack.monitoring.alerts.nodesChanged.actionVariables.clusterName', - { - defaultMessage: 'The cluster to which the nodes belong.', - } - ), - }, { name: 'added', description: i18n.translate('xpack.monitoring.alerts.nodesChanged.actionVariables.added', { @@ -92,21 +54,7 @@ export class NodesChangedAlert extends BaseAlert { } ), }, - { - name: 'action', - description: i18n.translate('xpack.monitoring.alerts.nodesChanged.actionVariables.action', { - defaultMessage: 'The recommended action for this alert.', - }), - }, - { - name: 'actionPlain', - description: i18n.translate( - 'xpack.monitoring.alerts.nodesChanged.actionVariables.actionPlain', - { - defaultMessage: 'The recommended action for this alert, without any markdown.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; private getNodeStates(legacyAlert: LegacyAlert): LegacyAlertNodesChangedList | undefined { @@ -138,7 +86,6 @@ export class NodesChangedAlert extends BaseAlert { shouldFire: true, // This alert always has a resolved timestamp severity: mapLegacySeverity(legacyAlert.metadata.severity), meta: legacyAlert, - ccs: null, }); return accum; }, []); @@ -234,7 +181,7 @@ export class NodesChangedAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, clusterName: cluster.clusterName, }); } else { @@ -244,14 +191,7 @@ export class NodesChangedAlert extends BaseAlert { const fullActionText = i18n.translate('xpack.monitoring.alerts.nodesChanged.fullAction', { defaultMessage: 'View nodes', }); - const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; - if (alertState.ccs) { - globalState.push(`ccs:${alertState.ccs}`); - } - const url = `${this.kibanaUrl}/app/monitoring#elasticsearch/nodes?_g=(${globalState.join( - ',' - )})`; - const action = `[${fullActionText}](${url})`; + const action = `[${fullActionText}](elasticsearch/nodes)`; const states = this.getNodeStates(legacyAlert) || { added: {}, removed: {}, restarted: {} }; const added = Object.values(states.added).join(','); const removed = Object.values(states.removed).join(','); @@ -280,7 +220,7 @@ export class NodesChangedAlert extends BaseAlert { }, } ), - state: FIRING, + state: AlertingDefaults.ALERT_STATE.firing, clusterName: cluster.clusterName, added, removed, diff --git a/x-pack/plugins/monitoring/server/alerts/types.d.ts b/x-pack/plugins/monitoring/server/alerts/types.d.ts index b6c8427375841..b685dcaed790f 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -11,13 +11,14 @@ export interface AlertEnableAction { config: { [key: string]: any }; } -export interface AlertInstanceState extends BaseAlertInstanceState { - alertStates: AlertState[]; +export interface AlertInstanceState { + alertStates: Array; + [x: string]: unknown; } export interface AlertState { cluster: AlertCluster; - ccs: string | null; + ccs?: string; ui: AlertUiState; } @@ -27,6 +28,12 @@ export interface AlertCpuUsageState extends AlertState { nodeName: string; } +export interface AlertDiskUsageState extends AlertState { + diskUsage: number; + nodeId: string; + nodeName?: string; +} + export interface AlertUiState { isFiring: boolean; severity: AlertSeverity; @@ -75,13 +82,21 @@ export interface AlertCpuUsageNodeStats { containerUsage: number; containerPeriods: number; containerQuota: number; - ccs: string | null; + ccs?: string; +} + +export interface AlertDiskUsageNodeStats { + clusterUuid: string; + nodeId: string; + nodeName: string; + diskUsage: number; + ccs?: string; } export interface AlertData { instanceKey: string; clusterUuid: string; - ccs: string | null; + ccs?: string; shouldFire: boolean; severity: AlertSeverity; meta: any; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts new file mode 100644 index 0000000000000..4e221d3ebb35a --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { fetchDiskUsageNodeStats } from './fetch_disk_usage_node_stats'; + +describe('fetchDiskUsageNodeStats', () => { + let callCluster = jest.fn(); + const clusters = [ + { + clusterUuid: 'cluster123', + clusterName: 'test-cluster', + }, + ]; + const index = '.monitoring-es-*'; + const duration = '5m'; + const size = 10; + + it('fetch normal stats', async () => { + callCluster = jest.fn().mockImplementation(() => { + return { + aggregations: { + clusters: { + buckets: [ + { + key: clusters[0].clusterUuid, + nodes: { + buckets: [ + { + key: 'theNodeId', + index: { + buckets: [ + { + key: '.monitoring-es-*', + }, + ], + }, + name: { + buckets: [ + { + key: 'theNodeName', + }, + ], + }, + usage_ratio_percentile: { + value: 10, + }, + }, + ], + }, + }, + ], + }, + }, + }; + }); + + const result = await fetchDiskUsageNodeStats(callCluster, clusters, index, duration, size); + expect(result).toEqual([ + { + clusterUuid: clusters[0].clusterUuid, + nodeName: 'theNodeName', + nodeId: 'theNodeId', + diskUsage: 10, + ccs: null, + }, + ]); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts new file mode 100644 index 0000000000000..6201204ebebe0 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { get } from 'lodash'; +import { AlertCluster, AlertDiskUsageNodeStats } from '../../alerts/types'; + +export async function fetchDiskUsageNodeStats( + callCluster: any, + clusters: AlertCluster[], + index: string, + duration: string, + size: number +): Promise { + const clustersIds = clusters.map((cluster) => cluster.clusterUuid); + const params = { + index, + filterPath: ['aggregations'], + body: { + size: 0, + query: { + bool: { + filter: [ + { + terms: { + cluster_uuid: clustersIds, + }, + }, + { + term: { + type: 'node_stats', + }, + }, + { + range: { + timestamp: { + gte: `now-${duration}`, + }, + }, + }, + ], + }, + }, + aggs: { + clusters: { + terms: { + field: 'cluster_uuid', + size, + include: clustersIds, + }, + aggs: { + nodes: { + terms: { + field: 'node_stats.node_id', + size, + }, + aggs: { + index: { + terms: { + field: '_index', + size: 1, + }, + }, + total_in_bytes: { + max: { + field: 'node_stats.fs.total.total_in_bytes', + }, + }, + available_in_bytes: { + max: { + field: 'node_stats.fs.total.available_in_bytes', + }, + }, + usage_ratio_percentile: { + bucket_script: { + buckets_path: { + available_in_bytes: 'available_in_bytes', + total_in_bytes: 'total_in_bytes', + }, + script: + '100 - Math.floor((params.available_in_bytes / params.total_in_bytes) * 100)', + }, + }, + name: { + terms: { + field: 'source_node.name', + size: 1, + }, + }, + }, + }, + }, + }, + }, + }, + }; + + const response = await callCluster('search', params); + const stats: AlertDiskUsageNodeStats[] = []; + const { buckets: clusterBuckets = [] } = response.aggregations.clusters; + + if (!clusterBuckets.length) { + return stats; + } + + for (const clusterBucket of clusterBuckets) { + for (const node of clusterBucket.nodes.buckets) { + const indexName = get(node, 'index.buckets[0].key', ''); + const diskUsage = Number(get(node, 'usage_ratio_percentile.value')); + if (isNaN(diskUsage) || diskUsage === undefined || diskUsage === null) { + continue; + } + stats.push({ + diskUsage, + clusterUuid: clusterBucket.key, + nodeId: node.key, + nodeName: get(node, 'name.buckets[0].key'), + ccs: indexName.includes(':') ? indexName.split(':')[0] : null, + }); + } + } + return stats; +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts index ff674195f0730..fdd7253550624 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts @@ -85,7 +85,6 @@ describe('fetchStatus', () => { alertStates = [ { cluster: defaultClusterState, - ccs: null, ui: { ...defaultUiState, isFiring: true, @@ -111,7 +110,6 @@ describe('fetchStatus', () => { alertStates = [ { cluster: defaultClusterState, - ccs: null, ui: { ...defaultUiState, resolvedMS: 1500, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 550cf35b1e3bf..0b2c3b1231e4b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12237,40 +12237,24 @@ "xpack.monitoring.alerts.callout.warningLabel": "警告アラート", "xpack.monitoring.alerts.clusterHealth.action.danger": "見つからないプライマリおよびレプリカシャードを割り当てます。", "xpack.monitoring.alerts.clusterHealth.action.warning": "見つからないレプリカシャードを割り当てます。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.clusterHealth.actionVariables.clusterHealth": "クラスターの正常性。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.clusterName": "ノードが属しているクラスター。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.clusterHealth.firing": "実行中", "xpack.monitoring.alerts.clusterHealth.firing.internalFullMessage": "クラスター正常性アラートが{clusterName}に対して作動しています。現在の正常性は{health}です。{action}", "xpack.monitoring.alerts.clusterHealth.firing.internalShortMessage": "クラスター正常性アラートが{clusterName}に対して作動しています。現在の正常性は{health}です。{actionText}", "xpack.monitoring.alerts.clusterHealth.label": "クラスターの正常性", "xpack.monitoring.alerts.clusterHealth.redMessage": "見つからないプライマリおよびレプリカシャードを割り当て", - "xpack.monitoring.alerts.clusterHealth.resolved": "解決済み", "xpack.monitoring.alerts.clusterHealth.resolved.internalFullMessage": "クラスター正常性アラートが{clusterName}に対して作動しています。", "xpack.monitoring.alerts.clusterHealth.resolved.internalShortMessage": "クラスター正常性アラートが{clusterName}に対して作動しています。", "xpack.monitoring.alerts.clusterHealth.ui.firingMessage": "Elasticsearchクラスターの正常性は{health}です。", "xpack.monitoring.alerts.clusterHealth.ui.nextSteps.message1": "{message}. #start_linkView now#end_link", "xpack.monitoring.alerts.clusterHealth.yellowMessage": "見つからないレプリカシャードを割り当て", - "xpack.monitoring.alerts.cpuUsage.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.clusterName": "ノードが属しているクラスター。", "xpack.monitoring.alerts.cpuUsage.actionVariables.count": "高CPU使用率を報告しているノード数。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", "xpack.monitoring.alerts.cpuUsage.actionVariables.nodes": "高CPU使用率を報告しているノードのリスト。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.cpuUsage.firing": "実行中", "xpack.monitoring.alerts.cpuUsage.firing.internalFullMessage": "CPU使用状況アラートはCPU使用状況アラートは、クラスター{clusterName}の{count}個のノードで実行されています。{action}", "xpack.monitoring.alerts.cpuUsage.firing.internalShortMessage": "CPU使用状況アラートは、クラスター{clusterName}の{count}個のノードで実行されています。{shortActionText}", "xpack.monitoring.alerts.cpuUsage.fullAction": "ノードの表示", "xpack.monitoring.alerts.cpuUsage.label": "CPU使用状況", "xpack.monitoring.alerts.cpuUsage.paramDetails.duration.label": "平均を確認", "xpack.monitoring.alerts.cpuUsage.paramDetails.threshold.label": "CPUが終了したときに通知", - "xpack.monitoring.alerts.cpuUsage.resolved": "解決済み", "xpack.monitoring.alerts.cpuUsage.resolved.internalFullMessage": "CPU使用状況アラートは、クラスター{clusterName}の{count}個のノードで解決されました。", "xpack.monitoring.alerts.cpuUsage.resolved.internalShortMessage": "CPU使用状況アラートは、クラスター{clusterName}の{count}個のノードで解決されました。", "xpack.monitoring.alerts.cpuUsage.shortAction": "影響を受けるノード全体のCPUレベルを検証します。", @@ -12278,21 +12262,13 @@ "xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads": "#start_linkCheck hot threads#end_link", "xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks": "#start_linkCheck long running tasks#end_link", "xpack.monitoring.alerts.cpuUsage.ui.resolvedMessage": "ノード{nodeName}でのCPU使用状況は現在しきい値を下回っています。現在、#resolved時点で、{cpuUsage}%と報告されています。", - "xpack.monitoring.alerts.cpuUsage.validation.duration": "有効な期間が必要です。", - "xpack.monitoring.alerts.cpuUsage.validation.threshold": "有効な数字が必要です。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", + "xpack.monitoring.alerts.validation.duration": "有効な期間が必要です。", + "xpack.monitoring.alerts.validation.threshold": "有効な数字が必要です。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterHealth": "このクラスターを実行しているElasticsearchのバージョン。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterName": "ノードが属しているクラスター。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing": "実行中", "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing.internalFullMessage": "{clusterName}に対してElasticsearchバージョン不一致アラートが実行されています。Elasticsearchは{versions}を実行しています。{action}", "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing.internalShortMessage": "{clusterName}に対してElasticsearchバージョン不一致アラートが実行されています。{shortActionText}", "xpack.monitoring.alerts.elasticsearchVersionMismatch.fullAction": "ノードの表示", "xpack.monitoring.alerts.elasticsearchVersionMismatch.label": "Elasticsearchバージョン不一致", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved": "解決済み", "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved.internalFullMessage": "{clusterName}のElasticsearchバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved.internalShortMessage": "{clusterName}のElasticsearchバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.shortAction": "すべてのノードのバージョンが同じことを確認してください。", @@ -12302,19 +12278,12 @@ "xpack.monitoring.alerts.flyoutExpressions.timeUnits.hourLabel": "{timeValue, plural, one {時間} other {時間}}", "xpack.monitoring.alerts.flyoutExpressions.timeUnits.minuteLabel": "{timeValue, plural, one {分} other {分}}", "xpack.monitoring.alerts.flyoutExpressions.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterHealth": "このクラスターを実行しているKibanaのバージョン。", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterName": "インスタンスが属しているクラスター。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.kibanaVersionMismatch.firing": "実行中", "xpack.monitoring.alerts.kibanaVersionMismatch.firing.internalFullMessage": "{clusterName}に対してKibanaバージョン不一致アラートが実行されています。Kibanaは{versions}を実行しています。{action}", "xpack.monitoring.alerts.kibanaVersionMismatch.firing.internalShortMessage": "{clusterName}に対してKibanaバージョン不一致アラートが実行されています。{shortActionText}", "xpack.monitoring.alerts.kibanaVersionMismatch.fullAction": "インスタンスを表示", "xpack.monitoring.alerts.kibanaVersionMismatch.label": "Kibanaバージョン不一致", - "xpack.monitoring.alerts.kibanaVersionMismatch.resolved": "解決済み", "xpack.monitoring.alerts.kibanaVersionMismatch.resolved.internalFullMessage": "{clusterName}のKibanaバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.kibanaVersionMismatch.resolved.internalShortMessage": "{clusterName}のKibanaバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.kibanaVersionMismatch.shortAction": "すべてのインスタンスのバージョンが同じことを確認してください。", @@ -12322,56 +12291,33 @@ "xpack.monitoring.alerts.kibanaVersionMismatch.ui.resolvedMessage": "このクラスターではすべてのKibanaのバージョンが同じです。", "xpack.monitoring.alerts.legacyAlert.expressionText": "構成するものがありません。", "xpack.monitoring.alerts.licenseExpiration.action": "ライセンスを更新してください。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.licenseExpiration.actionVariables.clusterName": "ライセンスが属しているクラスター。", "xpack.monitoring.alerts.licenseExpiration.actionVariables.expiredDate": "ライセンスの有効期限。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.licenseExpiration.firing": "実行中", "xpack.monitoring.alerts.licenseExpiration.firing.internalFullMessage": "ライセンス有効期限アラートが{clusterName}に対して実行されています。ライセンスは{expiredDate}に期限切れになります。{action}", "xpack.monitoring.alerts.licenseExpiration.firing.internalShortMessage": "{clusterName}に対してライセンス有効期限アラートが実行されています。ライセンスは{expiredDate}に期限切れになります。{actionText}", "xpack.monitoring.alerts.licenseExpiration.label": "ライセンス期限", - "xpack.monitoring.alerts.licenseExpiration.resolved": "解決済み", "xpack.monitoring.alerts.licenseExpiration.resolved.internalFullMessage": "{clusterName}のライセンス有効期限アラートが解決されました。", "xpack.monitoring.alerts.licenseExpiration.resolved.internalShortMessage": "{clusterName}のライセンス有効期限アラートが解決されました。", "xpack.monitoring.alerts.licenseExpiration.ui.firingMessage": "このクラスターのライセンスは#absoluteの#relativeに期限切れになります。#start_linkライセンスを更新してください。#end_link", "xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage": "このクラスターのライセンスは有効です。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.clusterHealth": "このクラスターを実行しているLogstashのバージョン。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.clusterName": "ノードが属しているクラスター。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.logstashVersionMismatch.firing": "実行中", "xpack.monitoring.alerts.logstashVersionMismatch.firing.internalFullMessage": "{clusterName}に対してLogstashバージョン不一致アラートが実行されています。Logstashは{versions}を実行しています。{action}", "xpack.monitoring.alerts.logstashVersionMismatch.firing.internalShortMessage": "{clusterName}に対してLogstashバージョン不一致アラートが実行されています。{shortActionText}", "xpack.monitoring.alerts.logstashVersionMismatch.fullAction": "ノードの表示", "xpack.monitoring.alerts.logstashVersionMismatch.label": "Logstashバージョン不一致", - "xpack.monitoring.alerts.logstashVersionMismatch.resolved": "解決済み", "xpack.monitoring.alerts.logstashVersionMismatch.resolved.internalFullMessage": "{clusterName}のLogstashバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.logstashVersionMismatch.resolved.internalShortMessage": "{clusterName}のLogstashバージョン不一致アラートが解決されました。", "xpack.monitoring.alerts.logstashVersionMismatch.shortAction": "すべてのノードのバージョンが同じことを確認してください。", "xpack.monitoring.alerts.logstashVersionMismatch.ui.firingMessage": "このクラスターでは、複数のバージョンのLogstash({versions})が実行されています。", "xpack.monitoring.alerts.logstashVersionMismatch.ui.resolvedMessage": "このクラスターではすべてのLogstashのバージョンが同じです。", "xpack.monitoring.alerts.migrate.manageAction.requiredFieldError": "{field} は必須フィールドです。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.nodesChanged.actionVariables.added": "ノードのリストがクラスターに追加されました。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.clusterName": "ノードが属しているクラスター。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.internalFullMessage": "詳細な内部メッセージはElasticで生成されました。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.internalShortMessage": "内部メッセージ(省略あり)はElasticで生成されました。", "xpack.monitoring.alerts.nodesChanged.actionVariables.removed": "ノードのリストがクラスターから削除されました。", "xpack.monitoring.alerts.nodesChanged.actionVariables.restarted": "ノードのリストがクラスターで再起動しました。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.state": "現在のアラートの状態。", - "xpack.monitoring.alerts.nodesChanged.firing": "実行中", "xpack.monitoring.alerts.nodesChanged.firing.internalFullMessage": "{clusterName}に対してノード変更アラートが実行されています。次のElasticsearchノードが追加されました:{added}、削除:{removed}、再起動:{restarted}。{action}", "xpack.monitoring.alerts.nodesChanged.firing.internalShortMessage": "{clusterName}に対してノード変更アラートが実行されています。{shortActionText}", "xpack.monitoring.alerts.nodesChanged.fullAction": "ノードの表示", "xpack.monitoring.alerts.nodesChanged.label": "ノードが変更されました", - "xpack.monitoring.alerts.nodesChanged.resolved": "解決済み", "xpack.monitoring.alerts.nodesChanged.resolved.internalFullMessage": "{clusterName}のElasticsearchノード変更アラートが解決されました。", "xpack.monitoring.alerts.nodesChanged.resolved.internalShortMessage": "{clusterName}のElasticsearchノード変更アラートが解決されました。", "xpack.monitoring.alerts.nodesChanged.shortAction": "ノードを追加、削除、または再起動したことを確認してください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5f50eb831022b..4d753b26fd7e3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12245,41 +12245,25 @@ "xpack.monitoring.alerts.callout.warningLabel": "警告告警", "xpack.monitoring.alerts.clusterHealth.action.danger": "分配缺失的主分片和副本分片。", "xpack.monitoring.alerts.clusterHealth.action.warning": "分配缺失的副本分片。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.clusterHealth.actionVariables.clusterHealth": "集群的运行状况。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.clusterName": "节点所属的集群。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", - "xpack.monitoring.alerts.clusterHealth.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.clusterHealth.firing": "触发", "xpack.monitoring.alerts.clusterHealth.firing.internalFullMessage": "为 {clusterName} 触发了集群运行状况告警。当前运行状况为 {health}。{action}", "xpack.monitoring.alerts.clusterHealth.firing.internalShortMessage": "为 {clusterName} 触发了集群运行状况告警。当前运行状况为 {health}。{actionText}", "xpack.monitoring.alerts.clusterHealth.label": "集群运行状况", "xpack.monitoring.alerts.clusterHealth.redMessage": "分配缺失的主分片和副本分片", - "xpack.monitoring.alerts.clusterHealth.resolved": "已解决", "xpack.monitoring.alerts.clusterHealth.resolved.internalFullMessage": "已为 {clusterName} 解决集群运行状况告警。", "xpack.monitoring.alerts.clusterHealth.resolved.internalShortMessage": "已为 {clusterName} 解决集群运行状况告警。", "xpack.monitoring.alerts.clusterHealth.ui.firingMessage": "Elasticsearch 集群运行状况为 {health}。", "xpack.monitoring.alerts.clusterHealth.ui.nextSteps.message1": "{message}。#start_linkView now#end_link", "xpack.monitoring.alerts.clusterHealth.ui.resolvedMessage": "Elasticsearch 集群运行状况为绿色。", "xpack.monitoring.alerts.clusterHealth.yellowMessage": "分配缺失的副本分片", - "xpack.monitoring.alerts.cpuUsage.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.clusterName": "节点所属的集群。", "xpack.monitoring.alerts.cpuUsage.actionVariables.count": "报告高 CPU 使用率的节点数目。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", "xpack.monitoring.alerts.cpuUsage.actionVariables.nodes": "报告高 CPU 使用率的节点列表。", - "xpack.monitoring.alerts.cpuUsage.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.cpuUsage.firing": "触发", "xpack.monitoring.alerts.cpuUsage.firing.internalFullMessage": "为集群 {clusterName} 中 {count} 个节点触发了 CPU 使用率告警。{action}", "xpack.monitoring.alerts.cpuUsage.firing.internalShortMessage": "为集群 {clusterName} 中 {count} 个节点触发了 CPU 使用率告警。{shortActionText}", "xpack.monitoring.alerts.cpuUsage.fullAction": "查看节点", "xpack.monitoring.alerts.cpuUsage.label": "CPU 使用率", "xpack.monitoring.alerts.cpuUsage.paramDetails.duration.label": "查看以下范围的平均值:", "xpack.monitoring.alerts.cpuUsage.paramDetails.threshold.label": "CPU 超过以下值时通知:", - "xpack.monitoring.alerts.cpuUsage.resolved": "已解决", "xpack.monitoring.alerts.cpuUsage.resolved.internalFullMessage": "已为集群 {clusterName} 中的 {count} 个节点解决 CPU 使用率告警。", "xpack.monitoring.alerts.cpuUsage.resolved.internalShortMessage": "已为集群 {clusterName} 中的 {count} 个节点解决 CPU 使用率告警。", "xpack.monitoring.alerts.cpuUsage.shortAction": "跨受影响节点验证 CPU 级别。", @@ -12287,21 +12271,13 @@ "xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads": "#start_link检查热线程#end_link", "xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks": "#start_link检查长时间运行的任务#end_link", "xpack.monitoring.alerts.cpuUsage.ui.resolvedMessage": "节点 {nodeName} 上的 cpu 使用率现在低于阈值,当前报告截止到 #resolved 为 {cpuUsage}%", - "xpack.monitoring.alerts.cpuUsage.validation.duration": "必须指定有效的持续时间。", - "xpack.monitoring.alerts.cpuUsage.validation.threshold": "必须指定有效数字。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", + "xpack.monitoring.alerts.validation.duration": "必须指定有效的持续时间。", + "xpack.monitoring.alerts.validation.threshold": "必须指定有效数字。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterHealth": "在此集群中运行的 Elasticsearch 版本。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterName": "节点所属的集群。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing": "触发", "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing.internalFullMessage": "为 {clusterName} 触发了 Elasticsearch 版本不匹配告警。Elasticsearch 正在运行 {versions}。{action}", "xpack.monitoring.alerts.elasticsearchVersionMismatch.firing.internalShortMessage": "为 {clusterName} 触发了 Elasticsearch 版本不匹配告警。{shortActionText}", "xpack.monitoring.alerts.elasticsearchVersionMismatch.fullAction": "查看节点", "xpack.monitoring.alerts.elasticsearchVersionMismatch.label": "Elasticsearch 版本不匹配", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved": "已解决", "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved.internalFullMessage": "为 {clusterName} 解决了 Elasticsearch 版本不匹配告警。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.resolved.internalShortMessage": "为 {clusterName} 解决了 Elasticsearch 版本不匹配告警。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.shortAction": "确认所有节点具有相同的版本。", @@ -12311,19 +12287,12 @@ "xpack.monitoring.alerts.flyoutExpressions.timeUnits.hourLabel": "{timeValue, plural, one {小时} other {小时}}", "xpack.monitoring.alerts.flyoutExpressions.timeUnits.minuteLabel": "{timeValue, plural, one {分钟} other {分钟}}", "xpack.monitoring.alerts.flyoutExpressions.timeUnits.secondLabel": "{timeValue, plural, one {秒} other {秒}}", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterHealth": "此集群中运行的 Kibana 版本。", "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.clusterName": "实例所属的集群。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", - "xpack.monitoring.alerts.kibanaVersionMismatch.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.kibanaVersionMismatch.firing": "触发", "xpack.monitoring.alerts.kibanaVersionMismatch.firing.internalFullMessage": "为 {clusterName} 触发了 Kibana 版本不匹配告警。Kibana 正在运行 {versions}。{action}", "xpack.monitoring.alerts.kibanaVersionMismatch.firing.internalShortMessage": "为 {clusterName} 触发了 Kibana 版本不匹配告警。{shortActionText}", "xpack.monitoring.alerts.kibanaVersionMismatch.fullAction": "查看实例", "xpack.monitoring.alerts.kibanaVersionMismatch.label": "Kibana 版本不匹配", - "xpack.monitoring.alerts.kibanaVersionMismatch.resolved": "已解决", "xpack.monitoring.alerts.kibanaVersionMismatch.resolved.internalFullMessage": "为 {clusterName} 解决了 Kibana 版本不匹配告警。", "xpack.monitoring.alerts.kibanaVersionMismatch.resolved.internalShortMessage": "为 {clusterName} 解决了 Kibana 版本不匹配告警。", "xpack.monitoring.alerts.kibanaVersionMismatch.shortAction": "确认所有实例具有相同的版本。", @@ -12331,56 +12300,33 @@ "xpack.monitoring.alerts.kibanaVersionMismatch.ui.resolvedMessage": "在此集群中所有 Kibana 版本都相同。", "xpack.monitoring.alerts.legacyAlert.expressionText": "没有可配置的内容。", "xpack.monitoring.alerts.licenseExpiration.action": "请更新您的许可证。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.licenseExpiration.actionVariables.clusterName": "许可证所属的集群。", "xpack.monitoring.alerts.licenseExpiration.actionVariables.expiredDate": "许可证过期日期。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", - "xpack.monitoring.alerts.licenseExpiration.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.licenseExpiration.firing": "触发", "xpack.monitoring.alerts.licenseExpiration.firing.internalFullMessage": "为 {clusterName} 触发了许可证到期告警。您的许可证将于 {expiredDate}到期。{action}", "xpack.monitoring.alerts.licenseExpiration.firing.internalShortMessage": "为 {clusterName} 触发了许可证到期告警。您的许可证将于 {expiredDate}到期。{actionText}", "xpack.monitoring.alerts.licenseExpiration.label": "许可证到期", - "xpack.monitoring.alerts.licenseExpiration.resolved": "已解决", "xpack.monitoring.alerts.licenseExpiration.resolved.internalFullMessage": "为 {clusterName} 解决了许可证到期告警。", "xpack.monitoring.alerts.licenseExpiration.resolved.internalShortMessage": "为 {clusterName} 解决了许可证到期告警。", "xpack.monitoring.alerts.licenseExpiration.ui.firingMessage": "此集群的许可证将于 #relative后,即 #absolute到期。 #start_link请更新您的许可证。#end_link", "xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage": "此集群的许可证处于活动状态。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.clusterHealth": "此集群中运行的 Logstash 版本。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.clusterName": "节点所属的集群。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", - "xpack.monitoring.alerts.logstashVersionMismatch.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.logstashVersionMismatch.firing": "触发", "xpack.monitoring.alerts.logstashVersionMismatch.firing.internalFullMessage": "为 {clusterName} 触发了 Logstash 版本不匹配告警。Logstash 正在运行 {versions}。{action}", "xpack.monitoring.alerts.logstashVersionMismatch.firing.internalShortMessage": "为 {clusterName} 触发了 Logstash 版本不匹配告警。{shortActionText}", "xpack.monitoring.alerts.logstashVersionMismatch.fullAction": "查看节点", "xpack.monitoring.alerts.logstashVersionMismatch.label": "Logstash 版本不匹配", - "xpack.monitoring.alerts.logstashVersionMismatch.resolved": "已解决", "xpack.monitoring.alerts.logstashVersionMismatch.resolved.internalFullMessage": "为 {clusterName} 解决了 Logstash 版本不匹配告警。", "xpack.monitoring.alerts.logstashVersionMismatch.resolved.internalShortMessage": "为 {clusterName} 解决了 Logstash 版本不匹配告警。", "xpack.monitoring.alerts.logstashVersionMismatch.shortAction": "确认所有节点具有相同的版本。", "xpack.monitoring.alerts.logstashVersionMismatch.ui.firingMessage": "在此集群中运行着多个 Logstash ({versions}) 版本。", "xpack.monitoring.alerts.logstashVersionMismatch.ui.resolvedMessage": "在此集群中所有 Logstash 版本都相同。", "xpack.monitoring.alerts.migrate.manageAction.requiredFieldError": "{field} 是必填字段。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.nodesChanged.actionVariables.added": "添加到集群的节点列表。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.clusterName": "节点所属的集群。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.internalFullMessage": "Elastic 生成的完整内部消息。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.internalShortMessage": "Elastic 生成的简短内部消息。", "xpack.monitoring.alerts.nodesChanged.actionVariables.removed": "从集群中移除的节点列表。", "xpack.monitoring.alerts.nodesChanged.actionVariables.restarted": "在集群中重新启动的节点列表。", - "xpack.monitoring.alerts.nodesChanged.actionVariables.state": "告警的当前状态。", - "xpack.monitoring.alerts.nodesChanged.firing": "触发", "xpack.monitoring.alerts.nodesChanged.firing.internalFullMessage": "为 {clusterName} 触发了节点已更改告警。以下 Elasticsearch 节点已添加:{added},以下已移除:{removed},以下已重新启动:{restarted}。{action}", "xpack.monitoring.alerts.nodesChanged.firing.internalShortMessage": "为 {clusterName} 触发了节点已更改告警。{shortActionText}", "xpack.monitoring.alerts.nodesChanged.fullAction": "查看节点", "xpack.monitoring.alerts.nodesChanged.label": "已更改节点", - "xpack.monitoring.alerts.nodesChanged.resolved": "已解决", "xpack.monitoring.alerts.nodesChanged.resolved.internalFullMessage": "已为 {clusterName} 解决 Elasticsearch 节点已更改告警。", "xpack.monitoring.alerts.nodesChanged.resolved.internalShortMessage": "已为 {clusterName} 解决 Elasticsearch 节点已更改告警。", "xpack.monitoring.alerts.nodesChanged.shortAction": "确认您已添加、移除或重新启动节点。",