From 795218e40728f61732aed7ac64bc65ee010d4576 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Wed, 19 Aug 2020 07:35:42 -0400 Subject: [PATCH 1/9] Disk usage alert draft --- 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 ++ .../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 +- .../monitoring/server/alerts/alerts_common.ts | 55 +++ .../server/alerts/alerts_factory.ts | 3 + .../server/alerts/disk_usage_alert.ts | 372 ++++++++++++++++++ .../plugins/monitoring/server/alerts/index.ts | 1 + .../monitoring/server/alerts/types.d.ts | 18 +- .../lib/alerts/fetch_disk_usage_node_stats.ts | 120 ++++++ .../translations/translations/ja-JP.json | 4 +- .../translations/translations/zh-CN.json | 4 +- 18 files changed, 639 insertions(+), 25 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.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 2c714080969e4..d8c060230f367 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 56cba83813a63..9e90aa3ecec42 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..add4651976321 --- /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: false, + }; +} 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 34e995510cf72..8c1c791c68e50 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 @@ -39,6 +39,7 @@ import { ALERT_LICENSE_EXPIRATION, ALERT_CLUSTER_HEALTH, ALERT_CPU_USAGE, + ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, } from '../../../../common/constants'; @@ -156,6 +157,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 88b36b9572fc7..e4fbd5c6a1ede 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -23,6 +23,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; @@ -66,6 +67,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 d75f63d883315..db15e7a9129db 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 @@ -16,7 +16,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'); @@ -65,7 +69,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 f6f7a01690529..dbd3c2fb5a1f0 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, @@ -50,7 +54,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/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts new file mode 100644 index 0000000000000..f64345d049974 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -0,0 +1,55 @@ +/* + * 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'; + +export class AlertingDefaults { + public static readonly ACTION_VARIABLES = { + 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.', + }), + }, + 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.', + }), + }, + }, + state: { + resolved: i18n.translate('xpack.monitoring.alerts.state.resolved', { + defaultMessage: 'resolved', + }), + firing: i18n.translate('xpack.monitoring.alerts.state.firing', { + defaultMessage: 'firing', + }), + }, + }; +} 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/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts new file mode 100644 index 0000000000000..7b0f96d735e78 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -0,0 +1,372 @@ +/* + * 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, + AlertMessageDocLinkToken, +} 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 } from './alerts_common'; + +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 readonly TYPE = ALERT_DISK_USAGE; + + public static readonly LABEL = i18n.translate('xpack.monitoring.alerts.diskUsage.label', { + defaultMessage: 'Disk Usage', + }); + + public type = DiskUsageAlert.TYPE; + public paramDetails = DiskUsageAlert.PARAM_DETAILS; + public label = DiskUsageAlert.LABEL; + + protected defaultParams = { + threshold: 90, + duration: '5m', + }; + + protected actionVariables = [...Object.values(AlertingDefaults.ACTION_VARIABLES.context)]; + + protected async fetchData( + params: CommonAlertParams, + callCluster: any, + clusters: AlertCluster[], + uiSettings: IUiSettingsClient, + availableCcs: string[] + ): Promise { + let esIndexPattern = 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 diskUsed = 100 - stat.diskAvailable; + return { + instanceKey: `${stat.clusterUuid}:${stat.nodeId}`, + clusterUuid: stat.clusterUuid, + shouldFire: diskUsed > threshold, + severity: AlertSeverity.Danger, + meta: stat, + ccs: stat.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.toFixed(2), + }, + }), + nextSteps: [ + { + text: i18n.translate('xpack.monitoring.alerts.diskUsage.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.diskUsage.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, + ], + }, + ], + 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 + ) { + if (!alertStates.length) { + return; + } + + const ccs = alertStates.reduce((accum: string, state): string => { + if (state.ccs) { + return state.ccs; + } + return accum; + }, ''); + + const firingCount = alertStates.filter((alertState) => alertState.ui.isFiring).length; + + if (firingCount > 0) { + const shortActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.shortAction', { + defaultMessage: 'Verify disk levels across affected nodes.', + }); + const fullActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.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 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.ACTION_VARIABLES.state.firing, + nodes: (alertStates as AlertDiskUsageState[]) + .filter((state: AlertDiskUsageState) => state.ui.isFiring) + .map((state: AlertDiskUsageState) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) + .join(','), + count: firingCount, + clusterName: cluster.clusterName, + action, + actionPlain: shortActionText, + }); + } else { + const resolvedCount = alertStates.filter((alertState) => !alertState.ui.isFiring).length; + const resolvedNodes = (alertStates as AlertDiskUsageState[]) + .filter((state) => !state.ui.isFiring) + .map((state) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) + .join(','); + 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.ACTION_VARIABLES.state.resolved, + nodes: resolvedNodes, + count: resolvedCount, + clusterName: cluster.clusterName, + }); + } + } + } + + // TODO: + protected processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices, + logger: Logger + ) { + 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 AlertDiskUsageState; + if (node.shouldFire) { + list.push(stat.nodeId); + } + return list; + }, [] as string[]); + firingNodeUuids.sort(); // It doesn't matter how we sort, but keep the order consistent + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; + const instance = services.alertInstanceFactory(instanceId); + const state = (instance.getState() as unknown) as AlertInstanceState; + const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; + let shouldExecuteActions = false; + for (const node of nodes) { + const stat = node.meta as AlertDiskUsageState; + let nodeState: AlertDiskUsageState; + const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { + const nodeAlertState = alertState as AlertDiskUsageState; + return ( + nodeAlertState.cluster.clusterUuid === cluster.clusterUuid && + nodeAlertState.nodeId === (node.meta as AlertDiskUsageState).nodeId + ); + }); + if (indexInState > -1) { + nodeState = alertInstanceState.alertStates[indexInState] as AlertDiskUsageState; + } else { + 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 = new Date().valueOf(); + nodeState.ui.isFiring = true; + nodeState.ui.message = this.getUiMessage(nodeState, node); + nodeState.ui.severity = node.severity; + nodeState.ui.resolvedMS = 0; + shouldExecuteActions = true; + } else if (!node.shouldFire && nodeState.ui.isFiring) { + nodeState.ui.isFiring = false; + nodeState.ui.resolvedMS = new Date().valueOf(); + nodeState.ui.message = this.getUiMessage(nodeState, node); + shouldExecuteActions = true; + } + + if (indexInState === -1) { + alertInstanceState.alertStates.push(nodeState); + } else { + alertInstanceState.alertStates = [ + ...alertInstanceState.alertStates.slice(0, indexInState), + nodeState, + ...alertInstanceState.alertStates.slice(indexInState + 1), + ]; + } + } + + instance.replaceState(alertInstanceState); + if (shouldExecuteActions) { + this.executeActions(instance, alertInstanceState, null, cluster); + } + } + } +} 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/types.d.ts b/x-pack/plugins/monitoring/server/alerts/types.d.ts index b6c8427375841..2b51a59ad4274 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -11,8 +11,8 @@ export interface AlertEnableAction { config: { [key: string]: any }; } -export interface AlertInstanceState extends BaseAlertInstanceState { - alertStates: AlertState[]; +export interface AlertInstanceState { + alertStates: Array; } export interface AlertState { @@ -27,6 +27,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; @@ -78,6 +84,14 @@ export interface AlertCpuUsageNodeStats { ccs: string | null; } +export interface AlertDiskUsageNodeStats { + clusterUuid: string; + nodeId: string; + nodeName: string; + diskAvailable: number; + ccs: string | null; +} + export interface AlertData { instanceKey: string; clusterUuid: string; 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..d8e65357d6c47 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts @@ -0,0 +1,120 @@ +/* + * 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', + }, + }, + free_ratio_percentile: { + bucket_script: { + buckets_path: { + available_in_bytes: 'available_in_bytes', + total_in_bytes: 'total_in_bytes', + }, + script: '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', ''); + stats.push({ + clusterUuid: clusterBucket.key, + nodeId: node.key, + nodeName: get(node, 'name.buckets[0].key'), + diskAvailable: get(node, 'free_ratio_percentile.value'), + ccs: indexName.includes(':') ? indexName.split(':')[0] : null, + }); + } + } + return stats; +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c958215a2677e..de296e6c7e6ac 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12687,8 +12687,8 @@ "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.validation.duration": "有効な期間が必要です。", + "xpack.monitoring.alerts.validation.threshold": "有効な数字が必要です。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "このアラートに対する推奨されるアクション。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterHealth": "このクラスターを実行しているElasticsearchのバージョン。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a06e5813796f4..0490ca690f1e2 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12693,8 +12693,8 @@ "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.validation.duration": "必须指定有效的持续时间。", + "xpack.monitoring.alerts.validation.threshold": "必须指定有效数字。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "此告警的建议操作。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.clusterHealth": "在此集群中运行的 Elasticsearch 版本。", From 98ad58d238e3c9378fbbcb3f734bdcf08866ad19 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Wed, 2 Sep 2020 08:53:51 -0400 Subject: [PATCH 2/9] Fixed typings and defaults --- .../monitoring/server/alerts/alerts_common.ts | 24 +++-- .../server/alerts/cluster_health_alert.ts | 59 +---------- .../server/alerts/cpu_usage_alert.ts | 55 +---------- .../server/alerts/disk_usage_alert.ts | 99 +++++++++---------- .../elasticsearch_version_mismatch_alert.ts | 67 +------------ .../alerts/kibana_version_mismatch_alert.ts | 62 ++---------- .../server/alerts/license_expiration_alert.ts | 63 ++---------- .../alerts/logstash_version_mismatch_alert.ts | 67 +------------ .../server/alerts/nodes_changed_alert.ts | 61 +----------- .../monitoring/server/alerts/types.d.ts | 11 ++- .../lib/alerts/fetch_disk_usage_node_stats.ts | 11 ++- .../server/lib/alerts/fetch_status.test.ts | 2 - .../translations/translations/ja-JP.json | 54 ---------- .../translations/translations/zh-CN.json | 54 ---------- 14 files changed, 112 insertions(+), 577 deletions(-) diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts index f64345d049974..f5d7dc50736d5 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -7,7 +7,15 @@ import { i18n } from '@kbn/i18n'; export class AlertingDefaults { - public static readonly ACTION_VARIABLES = { + 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', @@ -18,6 +26,12 @@ export class AlertingDefaults { } ), }, + clusterName: { + name: 'clusterName', + description: i18n.translate('xpack.monitoring.alerts.actionVariables.clusterName', { + defaultMessage: 'The cluster to which the nodes belong.', + }), + }, internalFullMessage: { name: 'internalFullMessage', description: i18n.translate('xpack.monitoring.alerts.actionVariables.internalFullMessage', { @@ -43,13 +57,5 @@ export class AlertingDefaults { }), }, }, - state: { - resolved: i18n.translate('xpack.monitoring.alerts.state.resolved', { - defaultMessage: 'resolved', - }), - firing: i18n.translate('xpack.monitoring.alerts.state.firing', { - defaultMessage: 'firing', - }), - }, }; } 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..48001ff761872 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,7 @@ 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.', - }), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'clusterHealth', description: i18n.translate( @@ -77,30 +55,6 @@ 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.', - } - ), - }, ]; 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, }); @@ -260,9 +211,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.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index afe5abcf1ebd7..595673f1945be 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -31,13 +31,7 @@ import { CommonAlertParams, CommonAlertParamDetail, } from '../../common/types'; - -const RESOLVED = i18n.translate('xpack.monitoring.alerts.cpuUsage.resolved', { - defaultMessage: 'resolved', -}); -const FIRING = i18n.translate('xpack.monitoring.alerts.cpuUsage.firing', { - defaultMessage: 'firing', -}); +import { AlertingDefaults } from './alerts_common'; const DEFAULT_THRESHOLD = 85; const DEFAULT_DURATION = '5m'; @@ -74,30 +68,7 @@ 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.', - }), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'nodes', description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.nodes', { @@ -110,24 +81,6 @@ 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.', - }), - }, ]; protected async fetchData( @@ -347,7 +300,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, @@ -387,7 +340,7 @@ export class CpuUsageAlert extends BaseAlert { }, } ), - state: RESOLVED, + state: AlertingDefaults.ALERT_STATE.resolved, nodes: resolvedNodes, count: resolvedCount, clusterName: cluster.clusterName, diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 7b0f96d735e78..752aa00b836b6 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_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 { @@ -24,7 +24,6 @@ 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 } from './alerts_common'; interface ParamDetails { @@ -46,15 +45,12 @@ export class DiskUsageAlert extends BaseAlert { 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 paramDetails = DiskUsageAlert.PARAM_DETAILS; public label = DiskUsageAlert.LABEL; protected defaultParams = { @@ -62,7 +58,21 @@ export class DiskUsageAlert extends BaseAlert { duration: '5m', }; - protected actionVariables = [...Object.values(AlertingDefaults.ACTION_VARIABLES.context)]; + protected actionVariables = [ + ...Object.values(AlertingDefaults.ALERT_TYPE.context), + { + 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.', + }), + }, + ]; protected async fetchData( params: CommonAlertParams, @@ -86,14 +96,14 @@ export class DiskUsageAlert extends BaseAlert { ); return stats.map((stat) => { - const diskUsed = 100 - stat.diskAvailable; + const { clusterUuid, nodeId, diskUsage, ccs } = stat; return { - instanceKey: `${stat.clusterUuid}:${stat.nodeId}`, - clusterUuid: stat.clusterUuid, - shouldFire: diskUsed > threshold, + instanceKey: `${clusterUuid}:${nodeId}`, + shouldFire: diskUsage > threshold, severity: AlertSeverity.Danger, meta: stat, - ccs: stat.ccs, + clusterUuid, + ccs, }; }); } @@ -202,12 +212,7 @@ export class DiskUsageAlert extends BaseAlert { return; } - const ccs = alertStates.reduce((accum: string, state): string => { - if (state.ccs) { - return state.ccs; - } - return accum; - }, ''); + const ccs = alertStates.find((state) => state.ccs)?.ccs; const firingCount = alertStates.filter((alertState) => alertState.ui.isFiring).length; @@ -252,7 +257,7 @@ export class DiskUsageAlert extends BaseAlert { instance.scheduleActions('default', { internalShortMessage, internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, - state: AlertingDefaults.ACTION_VARIABLES.state.firing, + state: AlertingDefaults.ALERT_STATE.firing, nodes: (alertStates as AlertDiskUsageState[]) .filter((state: AlertDiskUsageState) => state.ui.isFiring) .map((state: AlertDiskUsageState) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) @@ -283,7 +288,7 @@ export class DiskUsageAlert extends BaseAlert { instance.scheduleActions('default', { internalShortMessage: internalMessage, internalFullMessage: internalMessage, - state: AlertingDefaults.ACTION_VARIABLES.state.resolved, + state: AlertingDefaults.ALERT_STATE.resolved, nodes: resolvedNodes, count: resolvedCount, clusterName: cluster.clusterName, @@ -292,74 +297,64 @@ export class DiskUsageAlert extends BaseAlert { } } - // TODO: - protected processData( - data: AlertData[], - clusters: AlertCluster[], - services: AlertServices, - logger: Logger - ) { + protected processData(data: AlertData[], clusters: AlertCluster[], services: AlertServices) { + const currentUTC = +new Date(); for (const cluster of clusters) { const nodes = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); - if (nodes.length === 0) { + if (!nodes.length) { continue; } const firingNodeUuids = nodes.reduce((list: string[], node) => { - const stat = node.meta as AlertDiskUsageState; + const stat = node.meta; if (node.shouldFire) { list.push(stat.nodeId); } return list; - }, [] as string[]); - firingNodeUuids.sort(); // It doesn't matter how we sort, but keep the order consistent + }, []); + + firingNodeUuids.sort(); + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; const instance = services.alertInstanceFactory(instanceId); - const state = (instance.getState() as unknown) as AlertInstanceState; + const state = instance.getState() as AlertInstanceState; const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; + let shouldExecuteActions = false; for (const node of nodes) { const stat = node.meta as AlertDiskUsageState; - let nodeState: AlertDiskUsageState; + const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { const nodeAlertState = alertState as AlertDiskUsageState; return ( nodeAlertState.cluster.clusterUuid === cluster.clusterUuid && - nodeAlertState.nodeId === (node.meta as AlertDiskUsageState).nodeId + nodeAlertState.nodeId === node.meta.nodeId ); }); - if (indexInState > -1) { - nodeState = alertInstanceState.alertStates[indexInState] as AlertDiskUsageState; - } else { - nodeState = this.getDefaultAlertState(cluster, node) as AlertDiskUsageState; - } + const nodeState = (indexInState > -1 + ? alertInstanceState.alertStates[indexInState] + : this.getDefaultAlertState(cluster, node)) as AlertDiskUsageState; nodeState.diskUsage = stat.diskUsage; nodeState.nodeId = stat.nodeId; nodeState.nodeName = stat.nodeName; + nodeState.ui.message = this.getUiMessage(nodeState, node); + shouldExecuteActions = shouldExecuteActions || node.shouldFire || nodeState.ui.isFiring; if (node.shouldFire) { - nodeState.ui.triggeredMS = new Date().valueOf(); + nodeState.ui.triggeredMS = currentUTC; nodeState.ui.isFiring = true; - nodeState.ui.message = this.getUiMessage(nodeState, node); nodeState.ui.severity = node.severity; nodeState.ui.resolvedMS = 0; - shouldExecuteActions = true; - } else if (!node.shouldFire && nodeState.ui.isFiring) { + } else if (nodeState.ui.isFiring) { nodeState.ui.isFiring = false; - nodeState.ui.resolvedMS = new Date().valueOf(); - nodeState.ui.message = this.getUiMessage(nodeState, node); - shouldExecuteActions = true; + nodeState.ui.resolvedMS = currentUTC; } - if (indexInState === -1) { + if (indexInState < 0) { alertInstanceState.alertStates.push(nodeState); } else { - alertInstanceState.alertStates = [ - ...alertInstanceState.alertStates.slice(0, indexInState), - nodeState, - ...alertInstanceState.alertStates.slice(indexInState + 1), - ]; + alertInstanceState.alertStates.splice(indexInState, 0, nodeState); } } 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..a79deefe57be7 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,7 @@ 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.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'versionList', description: i18n.translate( @@ -73,33 +42,6 @@ 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.', - } - ), - }, ]; 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 { @@ -252,7 +193,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/kibana_version_mismatch_alert.ts b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.ts index 80e8701933f56..afae48cf87e8b 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,11 @@ 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.', - } - ), - }, + 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, { name: 'versionList', description: i18n.translate( @@ -82,24 +55,6 @@ 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.', - } - ), - }, ]; 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 { @@ -242,7 +196,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.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts index 7a249db28d2db..ce7d4f9b12642 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,11 @@ 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.', - } - ), - }, + 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, { name: 'expiredDate', description: i18n.translate( @@ -90,24 +62,6 @@ 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.', - } - ), - }, ]; 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, }); @@ -251,7 +204,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.ts b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.ts index f996e54de28ef..a6b998fc808c2 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,7 @@ 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.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'versionList', description: i18n.translate( @@ -73,33 +42,6 @@ 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.', - } - ), - }, ]; 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 { @@ -246,7 +187,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.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts index 73f3ee055c928..2e7cb35c7c96c 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,7 @@ 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.', - } - ), - }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'added', description: i18n.translate('xpack.monitoring.alerts.nodesChanged.actionVariables.added', { @@ -92,21 +55,6 @@ 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.', - } - ), - }, ]; 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 { @@ -280,7 +227,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 2b51a59ad4274..b685dcaed790f 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -13,11 +13,12 @@ export interface AlertEnableAction { export interface AlertInstanceState { alertStates: Array; + [x: string]: unknown; } export interface AlertState { cluster: AlertCluster; - ccs: string | null; + ccs?: string; ui: AlertUiState; } @@ -81,21 +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; - diskAvailable: number; - ccs: string | null; + 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.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.ts index d8e65357d6c47..6201204ebebe0 100644 --- 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 @@ -73,13 +73,14 @@ export async function fetchDiskUsageNodeStats( field: 'node_stats.fs.total.available_in_bytes', }, }, - free_ratio_percentile: { + usage_ratio_percentile: { bucket_script: { buckets_path: { available_in_bytes: 'available_in_bytes', total_in_bytes: 'total_in_bytes', }, - script: 'Math.floor((params.available_in_bytes / params.total_in_bytes) * 100)', + script: + '100 - Math.floor((params.available_in_bytes / params.total_in_bytes) * 100)', }, }, name: { @@ -107,11 +108,15 @@ export async function fetchDiskUsageNodeStats( 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'), - diskAvailable: get(node, 'free_ratio_percentile.value'), ccs: indexName.includes(':') ? indexName.split(':')[0] : null, }); } 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 7c25952256ec9..02a22cf94f94a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12550,40 +12550,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レベルを検証します。", @@ -12593,19 +12577,11 @@ "xpack.monitoring.alerts.cpuUsage.ui.resolvedMessage": "ノード{nodeName}でのCPU使用状況は現在しきい値を下回っています。現在、#resolved時点で、{cpuUsage}%と報告されています。", "xpack.monitoring.alerts.validation.duration": "有効な期間が必要です。", "xpack.monitoring.alerts.validation.threshold": "有効な数字が必要です。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "このアラートに対する推奨されるアクション。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "このアラートに推奨されるアクション(Markdownなし)。", "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": "すべてのノードのバージョンが同じことを確認してください。", @@ -12615,19 +12591,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": "すべてのインスタンスのバージョンが同じことを確認してください。", @@ -12635,56 +12604,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 7c243369319c6..7dc041199cc5a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12555,41 +12555,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 级别。", @@ -12599,19 +12583,11 @@ "xpack.monitoring.alerts.cpuUsage.ui.resolvedMessage": "节点 {nodeName} 上的 cpu 使用率现在低于阈值,当前报告截止到 #resolved 为 {cpuUsage}%", "xpack.monitoring.alerts.validation.duration": "必须指定有效的持续时间。", "xpack.monitoring.alerts.validation.threshold": "必须指定有效数字。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.action": "此告警的建议操作。", - "xpack.monitoring.alerts.elasticsearchVersionMismatch.actionVariables.actionPlain": "此告警的建议操作,无任何 Markdown。", "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": "确认所有节点具有相同的版本。", @@ -12621,19 +12597,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": "确认所有实例具有相同的版本。", @@ -12641,56 +12610,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": "确认您已添加、移除或重新启动节点。", From 26e1f84c1fb863d94d8319d9d36c9ee03d74b91e Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Wed, 2 Sep 2020 09:40:29 -0400 Subject: [PATCH 3/9] Fixed tests --- .../monitoring/server/alerts/alerts_common.ts | 12 +++---- .../server/alerts/alerts_factory.test.ts | 2 +- .../alerts/cluster_health_alert.test.ts | 8 ++--- .../server/alerts/cluster_health_alert.ts | 2 +- .../server/alerts/cpu_usage_alert.test.ts | 4 +-- .../server/alerts/cpu_usage_alert.ts | 2 +- .../server/alerts/disk_usage_alert.ts | 2 +- ...asticsearch_version_mismatch_alert.test.ts | 14 ++++---- .../elasticsearch_version_mismatch_alert.ts | 2 +- .../kibana_version_mismatch_alert.test.ts | 24 +++++++------- .../alerts/kibana_version_mismatch_alert.ts | 10 +++--- .../alerts/license_expiration_alert.test.ts | 11 +++---- .../server/alerts/license_expiration_alert.ts | 10 +++--- .../logstash_version_mismatch_alert.test.ts | 14 ++++---- .../alerts/logstash_version_mismatch_alert.ts | 2 +- .../server/alerts/nodes_changed_alert.test.ts | 14 ++++---- .../server/alerts/nodes_changed_alert.ts | 32 +++++++++---------- 17 files changed, 82 insertions(+), 83 deletions(-) diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts index f5d7dc50736d5..538f5a4a041b4 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -26,12 +26,6 @@ export class AlertingDefaults { } ), }, - clusterName: { - name: 'clusterName', - description: i18n.translate('xpack.monitoring.alerts.actionVariables.clusterName', { - defaultMessage: 'The cluster to which the nodes belong.', - }), - }, internalFullMessage: { name: 'internalFullMessage', description: i18n.translate('xpack.monitoring.alerts.actionVariables.internalFullMessage', { @@ -44,6 +38,12 @@ export class AlertingDefaults { 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', { 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/cluster_health_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts index 4b083787f58cb..0aa7ee8d297a9 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.' }, { @@ -125,7 +125,7 @@ describe('ClusterHealthAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -206,7 +206,7 @@ describe('ClusterHealthAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -238,7 +238,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 48001ff761872..b0f9d9d7e952e 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts @@ -45,7 +45,6 @@ export class ClusterHealthAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'clusterHealth', description: i18n.translate( @@ -55,6 +54,7 @@ export class ClusterHealthAlert extends BaseAlert { } ), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( 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 c330e977e53d8..581f78ae78cc2 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.' }, { 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 595673f1945be..4bbb709b86e1f 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -68,7 +68,6 @@ export class CpuUsageAlert extends BaseAlert { }; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'nodes', description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.nodes', { @@ -81,6 +80,7 @@ export class CpuUsageAlert extends BaseAlert { defaultMessage: 'The number of nodes reporting high cpu usage.', }), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 752aa00b836b6..64d985bf1e1c8 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -59,7 +59,6 @@ export class DiskUsageAlert extends BaseAlert { }; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'nodes', description: i18n.translate('xpack.monitoring.alerts.diskUsage.actionVariables.nodes', { @@ -72,6 +71,7 @@ export class DiskUsageAlert extends BaseAlert { defaultMessage: 'The number of nodes reporting high disk usage.', }), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( 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 ed300c211215b..585d2dde89930 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.' }, { @@ -128,7 +128,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -197,7 +197,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -229,7 +229,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 a79deefe57be7..a82d69372f754 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 @@ -32,7 +32,6 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'versionList', description: i18n.translate( @@ -42,6 +41,7 @@ export class ElasticsearchVersionMismatchAlert extends BaseAlert { } ), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( 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 dd3b37b5755e7..6e446eb4f8dfa 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', @@ -51,7 +51,7 @@ describe('KibanaVersionMismatchAlert', () => { }); describe('execute', () => { - function FakeDate() {} + function FakeDate() { } FakeDate.prototype.valueOf = () => 1; const clusterUuid = 'abc123'; @@ -131,7 +131,7 @@ describe('KibanaVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -199,7 +199,7 @@ describe('KibanaVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -231,7 +231,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 afae48cf87e8b..c56d33b5bc142 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 @@ -32,11 +32,6 @@ export class KibanaVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - 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, { name: 'versionList', description: i18n.translate( @@ -55,6 +50,11 @@ export class KibanaVersionMismatchAlert extends BaseAlert { } ), }, + 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( 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 e2f21b34efe21..35b542496fbb7 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', @@ -135,7 +134,7 @@ describe('LicenseExpirationAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -226,7 +225,7 @@ describe('LicenseExpirationAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -258,7 +257,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 ce7d4f9b12642..b1e3d53ab9e2d 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts @@ -39,11 +39,6 @@ export class LicenseExpirationAlert extends BaseAlert { }); public isLegacy = true; protected actionVariables = [ - 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, { name: 'expiredDate', description: i18n.translate( @@ -62,6 +57,11 @@ export class LicenseExpirationAlert extends BaseAlert { } ), }, + 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( 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 fbb4a01d5b4ed..b2997e50cb04a 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.' }, { @@ -128,7 +128,7 @@ describe('LogstashVersionMismatchAlert', () => { alertStates: [ { cluster: { clusterUuid: 'abc123', clusterName: 'testCluster' }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -196,7 +196,7 @@ describe('LogstashVersionMismatchAlert', () => { clusterUuid, clusterName, }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: null, @@ -228,7 +228,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 a6b998fc808c2..f69f42a1c91b5 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 @@ -32,7 +32,6 @@ export class LogstashVersionMismatchAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'versionList', description: i18n.translate( @@ -42,6 +41,7 @@ export class LogstashVersionMismatchAlert extends BaseAlert { } ), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; protected async fetchData( 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 4b3e3d2d6cb6d..1df20ec82a326 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', @@ -54,7 +54,7 @@ describe('NodesChangedAlert', () => { }); describe('execute', () => { - function FakeDate() {} + function FakeDate() { } FakeDate.prototype.valueOf = () => 1; const clusterUuid = 'abc123'; @@ -141,7 +141,7 @@ describe('NodesChangedAlert', () => { alertStates: [ { cluster: { clusterUuid, clusterName }, - ccs: null, + ccs: undefined, ui: { isFiring: true, message: { @@ -208,7 +208,7 @@ describe('NodesChangedAlert', () => { // clusterUuid, // clusterName, // }, - // ccs: null, + // ccs: undefined, // ui: { // isFiring: true, // message: null, @@ -239,7 +239,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 2e7cb35c7c96c..49ce6b5b8a632 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -33,7 +33,6 @@ export class NodesChangedAlert extends BaseAlert { public isLegacy = true; protected actionVariables = [ - ...Object.values(AlertingDefaults.ALERT_TYPE.context), { name: 'added', description: i18n.translate('xpack.monitoring.alerts.nodesChanged.actionVariables.added', { @@ -55,6 +54,7 @@ export class NodesChangedAlert extends BaseAlert { } ), }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), ]; private getNodeStates(legacyAlert: LegacyAlert): LegacyAlertNodesChangedList | undefined { @@ -120,29 +120,29 @@ export class NodesChangedAlert extends BaseAlert { const addedText = Object.values(states.added).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.addedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{added}' added to this cluster.`, - values: { - added: Object.values(states.added).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{added}' added to this cluster.`, + values: { + added: Object.values(states.added).join(','), + }, + }) : null; const removedText = Object.values(states.removed).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.removedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{removed}' removed from this cluster.`, - values: { - removed: Object.values(states.removed).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{removed}' removed from this cluster.`, + values: { + removed: Object.values(states.removed).join(','), + }, + }) : null; const restartedText = Object.values(states.restarted).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.restartedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{restarted}' restarted in this cluster.`, - values: { - restarted: Object.values(states.restarted).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{restarted}' restarted in this cluster.`, + values: { + restarted: Object.values(states.restarted).join(','), + }, + }) : null; return { From d7bca1c4506f4d1310a3f578727874e21e3549bd Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Wed, 2 Sep 2020 09:40:47 -0400 Subject: [PATCH 4/9] Fixed tests --- .../kibana_version_mismatch_alert.test.ts | 2 +- .../server/alerts/nodes_changed_alert.test.ts | 2 +- .../server/alerts/nodes_changed_alert.ts | 30 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) 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 6e446eb4f8dfa..ef5abb42a8e37 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 @@ -51,7 +51,7 @@ describe('KibanaVersionMismatchAlert', () => { }); describe('execute', () => { - function FakeDate() { } + function FakeDate() {} FakeDate.prototype.valueOf = () => 1; const clusterUuid = 'abc123'; 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 1df20ec82a326..6ee432141e42f 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 @@ -54,7 +54,7 @@ describe('NodesChangedAlert', () => { }); describe('execute', () => { - function FakeDate() { } + function FakeDate() {} FakeDate.prototype.valueOf = () => 1; const clusterUuid = 'abc123'; 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 49ce6b5b8a632..cade069d9f1ee 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -120,29 +120,29 @@ export class NodesChangedAlert extends BaseAlert { const addedText = Object.values(states.added).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.addedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{added}' added to this cluster.`, - values: { - added: Object.values(states.added).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{added}' added to this cluster.`, + values: { + added: Object.values(states.added).join(','), + }, + }) : null; const removedText = Object.values(states.removed).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.removedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{removed}' removed from this cluster.`, - values: { - removed: Object.values(states.removed).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{removed}' removed from this cluster.`, + values: { + removed: Object.values(states.removed).join(','), + }, + }) : null; const restartedText = Object.values(states.restarted).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.restartedFiringMessage', { - defaultMessage: `Elasticsearch nodes '{restarted}' restarted in this cluster.`, - values: { - restarted: Object.values(states.restarted).join(','), - }, - }) + defaultMessage: `Elasticsearch nodes '{restarted}' restarted in this cluster.`, + values: { + restarted: Object.values(states.restarted).join(','), + }, + }) : null; return { From add24ff595dd696506ce6906f54aec1df53d542c Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Fri, 4 Sep 2020 10:01:16 -0400 Subject: [PATCH 5/9] Addressed code feedback --- .../monitoring/server/alerts/alerts_common.ts | 18 +++++++ .../server/alerts/disk_usage_alert.ts | 54 +++++++++---------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts index 538f5a4a041b4..c284f9108bac3 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -5,6 +5,8 @@ */ import { i18n } from '@kbn/i18n'; +import { AlertMessageDocLinkToken } from './types'; +import { AlertMessageTokenType } from '../../common/enums'; export class AlertingDefaults { public static readonly ALERT_STATE = { @@ -59,3 +61,19 @@ export class AlertingDefaults { }, }; } + +export const createDocLink = (translateStr: string, linkText: string, linkURL: string) => { + return { + text: i18n.translate(translateStr, { + defaultMessage: `#start_link${linkText}#end_link`, + }), + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertMessageTokenType.DocLink, + partialUrl: linkURL, + } as AlertMessageDocLinkToken, + ], + }; +}; diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 64d985bf1e1c8..889f2e4ba3167 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -24,7 +24,7 @@ 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 } from './alerts_common'; +import { AlertingDefaults, createDocLink } from './alerts_common'; interface ParamDetails { [key: string]: CommonAlertParamDetail; @@ -110,7 +110,7 @@ export class DiskUsageAlert extends BaseAlert { protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) { const alertInstanceStates = alertInstance.state?.alertStates as AlertDiskUsageState[]; - const nodeUuid = filters.find((filter) => filter.nodeUuid); + const nodeUuid = filters?.find((filter) => filter.nodeUuid); if (!filters || !filters.length || !alertInstanceStates?.length || !nodeUuid) { return true; @@ -153,36 +153,30 @@ export class DiskUsageAlert extends BaseAlert { defaultMessage: `Node #start_link{nodeName}#end_link is reporting disk usage of {diskUsage}% at #absolute`, values: { nodeName: stat.nodeName, - diskUsage: stat.diskUsage.toFixed(2), + diskUsage: stat.diskUsage, }, }), nextSteps: [ - { - text: i18n.translate('xpack.monitoring.alerts.diskUsage.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.diskUsage.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, - ], - }, + createDocLink( + 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', + 'Tune for disk usage', + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tune-for-disk-usage.html` + ), + createDocLink( + 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.ilmPolicies', + 'Implement ILM policies', + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/index-lifecycle-management.html` + ), + createDocLink( + 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.addMoreNodes', + 'Add more data nodes', + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/add-elasticsearch-nodes.html` + ), + createDocLink( + 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.resizeYourDeployment', + 'Resize your deployment (ECE)', + `{elasticWebsiteUrl}/guide/en/cloud-enterprise/{docLinkVersion}/ece-resize-deployment.html` + ), ], tokens: [ { @@ -354,7 +348,7 @@ export class DiskUsageAlert extends BaseAlert { if (indexInState < 0) { alertInstanceState.alertStates.push(nodeState); } else { - alertInstanceState.alertStates.splice(indexInState, 0, nodeState); + alertInstanceState.alertStates[indexInState] = nodeState; } } From 7bb5c0664f4e402ff4c20997b79337412c2fd9c8 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Thu, 17 Sep 2020 12:37:31 -0400 Subject: [PATCH 6/9] Fixed disk and cpu usage states --- .../public/alerts/disk_usage_alert/index.tsx | 2 +- .../public/alerts/lib/replace_tokens.tsx | 4 +- .../public/views/elasticsearch/nodes/index.js | 3 +- .../monitoring/server/alerts/alerts_common.ts | 10 +- .../server/alerts/cpu_usage_alert.ts | 125 ++-- .../server/alerts/disk_usage_alert.test.ts | 537 ++++++++++++++++++ .../server/alerts/disk_usage_alert.ts | 103 ++-- .../fetch_disk_usage_node_stats.test.ts | 161 ++++++ 8 files changed, 795 insertions(+), 150 deletions(-) create mode 100644 x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts 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 index add4651976321..c2abb35612b38 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -24,6 +24,6 @@ export function createDiskUsageAlertType(): AlertTypeModel { ), validate, defaultActionMessage: '{{context.internalFullMessage}}', - requiresAppContext: false, + 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..79ae82010ced4 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx +++ b/x-pack/plugins/monitoring/public/alerts/lib/replace_tokens.tsx @@ -83,7 +83,9 @@ export function replaceTokens(alertMessage: AlertMessage): JSX.Element | string element = ( {preString} - {linkPart[1]} + + {linkPart[1]} + {postString} ); 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 152ccf440cd54..690dda51d3c9e 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', { @@ -83,7 +84,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 index c284f9108bac3..e9bfc6633327e 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -62,7 +62,12 @@ export class AlertingDefaults { }; } -export const createDocLink = (translateStr: string, linkText: string, linkURL: string) => { +export const createLink = ( + translateStr: string, + linkText: string, + linkURL: string, + type: AlertMessageTokenType = AlertMessageTokenType.DocLink +) => { return { text: i18n.translate(translateStr, { defaultMessage: `#start_link${linkText}#end_link`, @@ -71,8 +76,9 @@ export const createDocLink = (translateStr: string, linkText: string, linkURL: s { startToken: '#start_link', endToken: '#end_link', - type: AlertMessageTokenType.DocLink, partialUrl: linkURL, + url: linkURL, + type, } as AlertMessageDocLinkToken, ], }; 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 7e53a28bfa18d..400528815127a 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,7 +30,7 @@ import { CommonAlertParams, CommonAlertParamDetail, } from '../../common/types'; -import { AlertingDefaults } from './alerts_common'; +import { AlertingDefaults, createLink } from './alerts_common'; import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; const DEFAULT_THRESHOLD = 85; @@ -190,32 +189,16 @@ export class CpuUsageAlert extends BaseAlert { }, }), nextSteps: [ - { - text: 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`, - }), - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: AlertMessageTokenType.DocLink, - partialUrl: `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html`, - } as AlertMessageDocLinkToken, - ], - }, + createLink( + 'xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads', + 'Check hot threads', + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html` + ), + createLink( + 'xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks', + 'Check long running tasks', + `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html` + ), ], tokens: [ { @@ -350,79 +333,49 @@ export class CpuUsageAlert extends BaseAlert { } } - protected processData( - data: AlertData[], - clusters: AlertCluster[], - services: AlertServices, - logger: Logger - ) { + protected processData(data: AlertData[], clusters: AlertCluster[], services: AlertServices) { + const currentUTC = +new Date(); for (const cluster of clusters) { - const nodes = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); - if (nodes.length === 0) { + const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); + if (!nodes.length) { continue; } - const firingNodeUuids = nodes.reduce((list: string[], node) => { - const stat = node.meta as AlertCpuUsageNodeStats; - if (node.shouldFire) { - list.push(stat.nodeId); - } - return list; - }, [] as string[]); - firingNodeUuids.sort(); // It doesn't matter how we sort, but keep the order consistent - const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; + const instanceId = `.monitoring:${this.type}:${cluster.clusterUuid}`; const instance = services.alertInstanceFactory(instanceId); - const state = (instance.getState() as unknown) as AlertInstanceState; - const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; - let shouldExecuteActions = false; - for (const node of nodes) { - const stat = node.meta as AlertCpuUsageNodeStats; - let nodeState: AlertCpuUsageState; - const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { - const nodeAlertState = alertState as AlertCpuUsageState; - return ( - nodeAlertState.cluster.clusterUuid === cluster.clusterUuid && - nodeAlertState.nodeId === (node.meta as AlertCpuUsageNodeStats).nodeId - ); - }); - if (indexInState > -1) { - nodeState = alertInstanceState.alertStates[indexInState] as AlertCpuUsageState; - } else { - nodeState = this.getDefaultAlertState(cluster, node) as AlertCpuUsageState; - } + const state = instance.getState() as AlertInstanceState; + const newAlertStates: AlertInstanceState['alertStates'] = []; + const oldAlertStates = (state?.alertStates || []) as AlertCpuUsageState[]; + for (const node of nodes) { + const stat = node.meta as AlertCpuUsageState; + const 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(); + nodeState.ui.triggeredMS = currentUTC; nodeState.ui.isFiring = true; - nodeState.ui.message = this.getUiMessage(nodeState, node); nodeState.ui.severity = node.severity; - nodeState.ui.resolvedMS = 0; - shouldExecuteActions = true; - } else if (!node.shouldFire && nodeState.ui.isFiring) { - nodeState.ui.isFiring = false; - nodeState.ui.resolvedMS = new Date().valueOf(); - nodeState.ui.message = this.getUiMessage(nodeState, node); - shouldExecuteActions = true; - } - - if (indexInState === -1) { - alertInstanceState.alertStates.push(nodeState); + newAlertStates.push(nodeState); } else { - alertInstanceState.alertStates = [ - ...alertInstanceState.alertStates.slice(0, indexInState), - nodeState, - ...alertInstanceState.alertStates.slice(indexInState + 1), - ]; + const lastNodeState = oldAlertStates.find( + (oldNodeState) => nodeState.nodeId === oldNodeState.nodeId + ); + if (lastNodeState?.ui.isFiring) { + nodeState.ui.resolvedMS = currentUTC; + newAlertStates.push(nodeState); + } } - } - instance.replaceState(alertInstanceState); - if (shouldExecuteActions) { - this.executeActions(instance, alertInstanceState, null, cluster); + 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); + } } } } 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..832b4d40fe0c7 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts @@ -0,0 +1,537 @@ +/* + * 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: 85, 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 replaceState = jest.fn(); + const scheduleActions = jest.fn(); + const getState = jest.fn(); + const executorOptions = { + services: { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn().mockImplementation(() => { + return { + 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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + internalShortMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + action: `[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + actionPlain: 'Verify disk usage levels across affected nodes.', + clusterName, + count, + nodes: `${nodeName}:${diskUsage.toFixed(2)}`, + state: 'firing', + }); + }); + + it('should not fire actions if under threshold', async () => { + (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { + return [ + { + ...stat, + diskUsage: 1, + }, + ]; + }); + 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); + expect(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + ccs: undefined, + cluster: { + clusterUuid, + clusterName, + }, + diskUsage: 1, + nodeId, + nodeName, + ui: { + isFiring: false, + lastCheckedMS: 0, + message: null, + resolvedMS: 0, + severity: 'danger', + triggeredMS: 0, + }, + }, + ], + }); + expect(scheduleActions).not.toHaveBeenCalled(); + }); + + it('should resolve with a resolved message', async () => { + (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { + return [ + { + ...stat, + diskUsage: 1, + }, + ]; + }); + (getState as jest.Mock).mockImplementation(() => { + return { + alertStates: [ + { + cluster: { + clusterUuid, + clusterName, + }, + ccs: null, + diskUsage: 91, + nodeId, + nodeName, + ui: { + isFiring: true, + message: null, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }; + }); + 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(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + cluster: { clusterUuid, clusterName }, + ccs: null, + diskUsage: 1, + nodeId, + nodeName, + ui: { + isFiring: false, + message: { + text: + 'The disk usage on node myNodeName is now under the threshold, currently reporting at 1.00% as of #resolved', + tokens: [ + { + startToken: '#resolved', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + ], + }, + severity: 'danger', + resolvedMS: 1, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }); + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `Disk usage alert is resolved for ${count} node(s) in cluster: ${clusterName}.`, + internalShortMessage: `Disk usage alert is resolved for ${count} node(s) in cluster: ${clusterName}.`, + clusterName, + count, + nodes: `${nodeName}:1.00`, + state: 'resolved', + }); + }); + + 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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + internalShortMessage: `Disk usage alert is firing for ${count} node(s) in cluster: ${clusterName}. Verify disk usage levels across affected nodes.`, + action: `[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + actionPlain: 'Verify disk usage levels across affected nodes.', + clusterName, + count, + nodes: `${nodeName}:${diskUsage.toFixed(2)}`, + state: 'firing', + }); + }); + + it('should show proper counts for resolved and firing nodes', async () => { + (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { + return [ + { + ...stat, + diskUsage: 1, + }, + { + ...stat, + nodeId: 'anotherNode', + nodeName: 'anotherNode', + diskUsage: 99, + }, + ]; + }); + (getState as jest.Mock).mockImplementation(() => { + return { + alertStates: [ + { + cluster: { + clusterUuid, + clusterName, + }, + ccs: null, + diskUsage: 91, + nodeId, + nodeName, + ui: { + isFiring: true, + message: null, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + { + cluster: { + clusterUuid, + clusterName, + }, + ccs: null, + diskUsage: 100, + nodeId: 'anotherNode', + nodeName: 'anotherNode', + ui: { + isFiring: true, + message: null, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }; + }); + 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(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + cluster: { clusterUuid, clusterName }, + ccs: null, + diskUsage: 1, + nodeId, + nodeName, + ui: { + isFiring: false, + message: { + text: + 'The disk usage on node myNodeName is now under the threshold, currently reporting at 1.00% as of #resolved', + tokens: [ + { + startToken: '#resolved', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + ], + }, + severity: 'danger', + resolvedMS: 1, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + { + ccs: null, + cluster: { clusterUuid, clusterName }, + diskUsage: 99, + nodeId: 'anotherNode', + nodeName: 'anotherNode', + ui: { + isFiring: true, + message: { + text: + 'Node #start_linkanotherNode#end_link is reporting disk usage of 99.00% at #absolute', + nextSteps: [ + { + text: '#start_linkCheck hot threads#end_link', + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: 'docLink', + partialUrl: + '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', + }, + ], + }, + { + text: '#start_linkCheck long running tasks#end_link', + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: 'docLink', + partialUrl: + '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', + }, + ], + }, + ], + tokens: [ + { + startToken: '#absolute', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + { + startToken: '#start_link', + endToken: '#end_link', + type: 'link', + url: 'elasticsearch/nodes/anotherNode', + }, + ], + }, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }); + expect(scheduleActions).toHaveBeenCalledTimes(1); + expect(scheduleActions.mock.calls[0]).toEqual([ + 'default', + { + action: + '[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + actionPlain: 'Verify disk usage levels across affected nodes.', + internalFullMessage: + 'Disk usage alert is firing for 1 node(s) in cluster: testCluster. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', + internalShortMessage: + 'Disk usage alert is firing for 1 node(s) in cluster: testCluster. Verify disk usage levels across affected nodes.', + nodes: 'anotherNode:99.00', + clusterName, + count, + 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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + 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 index 889f2e4ba3167..014d1e3ea62f6 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -15,7 +15,6 @@ import { AlertMessageTimeToken, AlertMessageLinkToken, AlertInstanceState, - AlertMessageDocLinkToken, } from './types'; import { AlertInstance, AlertServices } from '../../../alerts/server'; import { INDEX_PATTERN_ELASTICSEARCH, ALERT_DISK_USAGE } from '../../common/constants'; @@ -24,7 +23,8 @@ 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, createDocLink } from './alerts_common'; +import { AlertingDefaults, createLink } from './alerts_common'; +import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; interface ParamDetails { [key: string]: CommonAlertParamDetail; @@ -81,11 +81,10 @@ export class DiskUsageAlert extends BaseAlert { uiSettings: IUiSettingsClient, availableCcs: string[] ): Promise { - let esIndexPattern = INDEX_PATTERN_ELASTICSEARCH; + let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); if (availableCcs) { esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); } - const { duration, threshold } = params; const stats = await fetchDiskUsageNodeStats( callCluster, @@ -157,22 +156,28 @@ export class DiskUsageAlert extends BaseAlert { }, }), nextSteps: [ - createDocLink( + createLink( 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', 'Tune for disk usage', `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tune-for-disk-usage.html` ), - createDocLink( + createLink( + 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', + 'Identify large indices', + `elasticsearch/indices`, + AlertMessageTokenType.Link + ), + createLink( 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.ilmPolicies', 'Implement ILM policies', `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/index-lifecycle-management.html` ), - createDocLink( + createLink( 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.addMoreNodes', 'Add more data nodes', `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/add-elasticsearch-nodes.html` ), - createDocLink( + createLink( 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.resizeYourDeployment', 'Resize your deployment (ECE)', `{elasticWebsiteUrl}/guide/en/cloud-enterprise/{docLinkVersion}/ece-resize-deployment.html` @@ -207,12 +212,14 @@ export class DiskUsageAlert extends BaseAlert { } const ccs = alertStates.find((state) => state.ccs)?.ccs; - - const firingCount = alertStates.filter((alertState) => alertState.ui.isFiring).length; + 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 levels across affected nodes.', + defaultMessage: 'Verify disk usage levels across affected nodes.', }); const fullActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.fullAction', { defaultMessage: 'View nodes', @@ -252,9 +259,8 @@ export class DiskUsageAlert extends BaseAlert { internalShortMessage, internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, state: AlertingDefaults.ALERT_STATE.firing, - nodes: (alertStates as AlertDiskUsageState[]) - .filter((state: AlertDiskUsageState) => state.ui.isFiring) - .map((state: AlertDiskUsageState) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) + nodes: firingNodes + .map((state) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) .join(','), count: firingCount, clusterName: cluster.clusterName, @@ -262,11 +268,11 @@ export class DiskUsageAlert extends BaseAlert { actionPlain: shortActionText, }); } else { - const resolvedCount = alertStates.filter((alertState) => !alertState.ui.isFiring).length; const resolvedNodes = (alertStates as AlertDiskUsageState[]) .filter((state) => !state.ui.isFiring) - .map((state) => `${state.nodeName}:${state.diskUsage.toFixed(2)}`) - .join(','); + .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', @@ -283,7 +289,7 @@ export class DiskUsageAlert extends BaseAlert { internalShortMessage: internalMessage, internalFullMessage: internalMessage, state: AlertingDefaults.ALERT_STATE.resolved, - nodes: resolvedNodes, + nodes: resolvedNodes.join(','), count: resolvedCount, clusterName: cluster.clusterName, }); @@ -294,67 +300,46 @@ export class DiskUsageAlert extends BaseAlert { protected processData(data: AlertData[], clusters: AlertCluster[], services: AlertServices) { const currentUTC = +new Date(); for (const cluster of clusters) { - const nodes = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); + const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); if (!nodes.length) { continue; } - const firingNodeUuids = nodes.reduce((list: string[], node) => { - const stat = node.meta; - if (node.shouldFire) { - list.push(stat.nodeId); - } - return list; - }, []); - - firingNodeUuids.sort(); - - const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; + const instanceId = `.monitoring:${this.type}:${cluster.clusterUuid}`; const instance = services.alertInstanceFactory(instanceId); const state = instance.getState() as AlertInstanceState; - const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; + const newAlertStates: AlertInstanceState['alertStates'] = []; + const oldAlertStates = (state?.alertStates || []) as AlertDiskUsageState[]; - let shouldExecuteActions = false; for (const node of nodes) { const stat = node.meta as AlertDiskUsageState; - - const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { - const nodeAlertState = alertState as AlertDiskUsageState; - return ( - nodeAlertState.cluster.clusterUuid === cluster.clusterUuid && - nodeAlertState.nodeId === node.meta.nodeId - ); - }); - const nodeState = (indexInState > -1 - ? alertInstanceState.alertStates[indexInState] - : this.getDefaultAlertState(cluster, node)) as AlertDiskUsageState; - + const nodeState = this.getDefaultAlertState(cluster, node) as AlertDiskUsageState; nodeState.diskUsage = stat.diskUsage; nodeState.nodeId = stat.nodeId; nodeState.nodeName = stat.nodeName; - nodeState.ui.message = this.getUiMessage(nodeState, node); - shouldExecuteActions = shouldExecuteActions || node.shouldFire || nodeState.ui.isFiring; if (node.shouldFire) { nodeState.ui.triggeredMS = currentUTC; nodeState.ui.isFiring = true; nodeState.ui.severity = node.severity; - nodeState.ui.resolvedMS = 0; - } else if (nodeState.ui.isFiring) { - nodeState.ui.isFiring = false; - nodeState.ui.resolvedMS = currentUTC; - } - - if (indexInState < 0) { - alertInstanceState.alertStates.push(nodeState); + newAlertStates.push(nodeState); } else { - alertInstanceState.alertStates[indexInState] = nodeState; + const lastNodeState = oldAlertStates.find( + (oldNodeState) => nodeState.nodeId === oldNodeState.nodeId + ); + if (lastNodeState?.ui.isFiring) { + nodeState.ui.resolvedMS = currentUTC; + newAlertStates.push(nodeState); + } } - } - instance.replaceState(alertInstanceState); - if (shouldExecuteActions) { - this.executeActions(instance, alertInstanceState, null, cluster); + 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); + } } } } 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..cfe8d962517ae --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_disk_usage_node_stats.test.ts @@ -0,0 +1,161 @@ +/* + * 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, + }, + ]); + }); + + it('fetch properly return ccs', async () => { + callCluster = jest.fn().mockImplementation(() => { + return { + aggregations: { + clusters: { + buckets: [ + { + key: clusters[0].clusterUuid, + nodes: { + buckets: [ + { + key: 'theNodeId', + index: { + buckets: [ + { + key: 'ccs:.monitoring-es-*', + }, + ], + }, + name: { + buckets: [ + { + key: 'theNodeName', + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }; + }); + const result = await fetchDiskUsageNodeStats(callCluster, clusters, index, duration, size); + expect(result[0].ccs).toBe('ccs'); + }); + + it('should use consistent params', async () => { + let params = null; + callCluster = jest.fn().mockImplementation((...args) => { + params = args[1]; + }); + await fetchDiskUsageNodeStats(callCluster, clusters, index, duration, size); + expect(params).toStrictEqual({ + index, + filterPath: ['aggregations'], + body: { + size: 0, + query: { + bool: { + filter: [ + { terms: { cluster_uuid: clusters.map((cluster) => cluster.clusterUuid) } }, + { term: { type: 'node_stats' } }, + { + range: { + timestamp: { + gte: `now-${duration}`, + }, + }, + }, + ], + }, + }, + aggs: { + clusters: { + terms: { + field: 'cluster_uuid', + size, + include: clusters.map((cluster) => cluster.clusterUuid), + }, + aggs: { + nodes: { + terms: { field: 'node_stats.node_id', size }, + aggs: { + index: { terms: { field: '_index', size: 1 } }, + usage_ratio_percentile: { + avg: { field: 'nodes.buckets.usage_ratio_percentile.value' }, + }, + name: { terms: { field: 'source_node.name', size: 1 } }, + }, + }, + }, + }, + }, + }, + }); + }); +}); From b32226a6667f6bf386aff7e7dc41013dc4f18394 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Tue, 22 Sep 2020 06:54:08 -0400 Subject: [PATCH 7/9] Fixed resolve state and throttle --- .../monitoring/server/alerts/base_alert.ts | 9 +- .../server/alerts/cpu_usage_alert.ts | 6 +- .../server/alerts/disk_usage_alert.ts | 87 +++++++++++++++---- 3 files changed, 82 insertions(+), 20 deletions(-) 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/cpu_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index 400528815127a..c923da35d8517 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -333,7 +333,11 @@ export class CpuUsageAlert extends BaseAlert { } } - protected processData(data: AlertData[], clusters: AlertCluster[], services: AlertServices) { + protected async processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices + ) { const currentUTC = +new Date(); for (const cluster of clusters) { const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 014d1e3ea62f6..9e25665ddfc2f 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_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 } from 'kibana/server'; +import { IUiSettingsClient, Logger } from 'kibana/server'; import { i18n } from '@kbn/i18n'; import { BaseAlert } from './base_alert'; import { @@ -207,10 +207,6 @@ export class DiskUsageAlert extends BaseAlert { item: AlertData | null, cluster: AlertCluster ) { - if (!alertStates.length) { - return; - } - const ccs = alertStates.find((state) => state.ccs)?.ccs; const firingNodes = alertStates.filter( (alertState) => alertState.ui.isFiring @@ -297,8 +293,58 @@ export class DiskUsageAlert extends BaseAlert { } } - protected processData(data: AlertData[], clusters: AlertCluster[], services: AlertServices) { - const currentUTC = +new Date(); + private executeDeltas( + services: AlertServices, + cluster: AlertCluster, + newAlertStates: AlertDiskUsageState[], + oldAlertStates: AlertDiskUsageState[] + ) { + const deltaFiringStates = []; + const deltaResolvedStates = []; + const deltaInstanceIdPrefix: string = `.monitoring:${this.type}:${ + cluster.clusterUuid + }:${Date.now()}:`; + + for (const newAlertState of newAlertStates) { + const relatedOldState = oldAlertStates.find( + (oldState) => + oldState.nodeId === newAlertState.nodeId && + oldState.ui.isFiring !== newAlertState.ui.isFiring && + oldState.ui.resolvedMS !== newAlertState.ui.resolvedMS + ); + if (!relatedOldState) { + if (newAlertState.ui.isFiring) { + deltaFiringStates.push(newAlertState); + } else if (newAlertState.ui.resolvedMS) { + deltaResolvedStates.push(newAlertState); + } + } + } + + if (deltaFiringStates.length + deltaResolvedStates.length === newAlertStates.length) { + /** No delta changes, so we do nothing */ + return; + } + + if (deltaFiringStates.length) { + const instance = services.alertInstanceFactory(`${deltaInstanceIdPrefix}:firing`); + this.executeActions(instance, { alertStates: deltaFiringStates }, null, cluster); + } + + if (deltaResolvedStates.length) { + const instance = services.alertInstanceFactory(`${deltaInstanceIdPrefix}:resolved`); + this.executeActions(instance, { alertStates: deltaResolvedStates }, null, cluster); + } + } + + protected async processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices, + logger: Logger, + state: any + ) { + const currentUTC = Date.now(); for (const cluster of clusters) { const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); if (!nodes.length) { @@ -307,9 +353,9 @@ export class DiskUsageAlert extends BaseAlert { const instanceId = `.monitoring:${this.type}:${cluster.clusterUuid}`; const instance = services.alertInstanceFactory(instanceId); - const state = instance.getState() as AlertInstanceState; - const newAlertStates: AlertInstanceState['alertStates'] = []; - const oldAlertStates = (state?.alertStates || []) as AlertDiskUsageState[]; + const instanceState = instance.getState() as AlertInstanceState; + const newAlertStates: AlertDiskUsageState[] = []; + const oldAlertStates = (instanceState?.alertStates || []) as AlertDiskUsageState[]; for (const node of nodes) { const stat = node.meta as AlertDiskUsageState; @@ -334,13 +380,24 @@ export class DiskUsageAlert extends BaseAlert { } 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); - } + /** + * Addresses lost delta triggers if executed between throttle states, context: + * https://github.com/elastic/kibana/pull/75419#discussion_r490497639. This is + * a temporary solution until: https://github.com/elastic/kibana/issues/49405 is implemented + */ + this.executeDeltas(services, cluster, newAlertStates, oldAlertStates); + + 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; } } From 47711dba300c718b38bc33c7ef43b2f20ffebfc4 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Mon, 28 Sep 2020 23:14:53 -0400 Subject: [PATCH 8/9] CR feedback --- .../monitoring/server/alerts/alerts_common.ts | 9 +- .../server/alerts/cpu_usage_alert.test.ts | 11 +- .../server/alerts/cpu_usage_alert.ts | 89 +++-- .../server/alerts/disk_usage_alert.test.ts | 322 +----------------- .../server/alerts/disk_usage_alert.ts | 99 ++---- .../fetch_disk_usage_node_stats.test.ts | 90 ----- 6 files changed, 93 insertions(+), 527 deletions(-) diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts index e9bfc6633327e..30c7fa78cb2b8 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -63,21 +63,18 @@ export class AlertingDefaults { } export const createLink = ( - translateStr: string, linkText: string, linkURL: string, type: AlertMessageTokenType = AlertMessageTokenType.DocLink ) => { + const link = type === AlertMessageTokenType.DocLink ? { url: linkURL } : { partialUrl: linkURL }; return { - text: i18n.translate(translateStr, { - defaultMessage: `#start_link${linkText}#end_link`, - }), + text: linkText, tokens: [ { + ...link, startToken: '#start_link', endToken: '#end_link', - partialUrl: linkURL, - url: linkURL, type, } as AlertMessageDocLinkToken, ], 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 10d1e965a76a9..baab68b86f847 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 @@ -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, @@ -150,7 +153,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - partialUrl: + url: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', }, ], @@ -162,7 +165,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - partialUrl: + url: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', }, ], @@ -502,7 +505,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - partialUrl: + url: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', }, ], @@ -514,7 +517,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - partialUrl: + url: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', }, ], 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 c923da35d8517..bf8b7049bcb03 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -190,13 +190,15 @@ export class CpuUsageAlert extends BaseAlert { }), nextSteps: [ createLink( - 'xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads', - 'Check hot threads', + i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.hotThreads', { + defaultMessage: '#start_linkCheck hot threads#end_link', + }), `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html` ), createLink( - 'xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks', - 'Check long running tasks', + i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.nextSteps.runningTasks', { + defaultMessage: '#start_linkCheck long running tasks#end_link', + }), `{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html` ), ], @@ -338,49 +340,72 @@ export class CpuUsageAlert extends BaseAlert { clusters: AlertCluster[], services: AlertServices ) { - const currentUTC = +new Date(); for (const cluster of clusters) { - const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); - if (!nodes.length) { + const nodes = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); + if (nodes.length === 0) { continue; } - - const instanceId = `.monitoring:${this.type}:${cluster.clusterUuid}`; + const firingNodeUuids = nodes.reduce((list: string[], node) => { + const stat = node.meta as AlertCpuUsageNodeStats; + if (node.shouldFire) { + list.push(stat.nodeId); + } + return list; + }, [] as string[]); + firingNodeUuids.sort(); // It doesn't matter how we sort, but keep the order consistent + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; const instance = services.alertInstanceFactory(instanceId); - const state = instance.getState() as AlertInstanceState; - const newAlertStates: AlertInstanceState['alertStates'] = []; - const oldAlertStates = (state?.alertStates || []) as AlertCpuUsageState[]; - + const state = (instance.getState() as unknown) as AlertInstanceState; + const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; + let shouldExecuteActions = false; for (const node of nodes) { - const stat = node.meta as AlertCpuUsageState; - const nodeState = this.getDefaultAlertState(cluster, node) as AlertCpuUsageState; + const stat = node.meta as AlertCpuUsageNodeStats; + let nodeState: AlertCpuUsageState; + const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { + const nodeAlertState = alertState as AlertCpuUsageState; + return ( + nodeAlertState.cluster.clusterUuid === cluster.clusterUuid && + nodeAlertState.nodeId === (node.meta as AlertCpuUsageNodeStats).nodeId + ); + }); + if (indexInState > -1) { + nodeState = alertInstanceState.alertStates[indexInState] as AlertCpuUsageState; + } 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 = currentUTC; + 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; - newAlertStates.push(nodeState); - } else { - const lastNodeState = oldAlertStates.find( - (oldNodeState) => nodeState.nodeId === oldNodeState.nodeId - ); - if (lastNodeState?.ui.isFiring) { - nodeState.ui.resolvedMS = currentUTC; - newAlertStates.push(nodeState); - } + nodeState.ui.resolvedMS = 0; + shouldExecuteActions = true; + } else if (!node.shouldFire && nodeState.ui.isFiring) { + nodeState.ui.isFiring = false; + nodeState.ui.resolvedMS = new Date().valueOf(); + nodeState.ui.message = this.getUiMessage(nodeState, node); + shouldExecuteActions = true; } - - 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); + if (indexInState === -1) { + alertInstanceState.alertStates.push(nodeState); + } else { + alertInstanceState.alertStates = [ + ...alertInstanceState.alertStates.slice(0, indexInState), + nodeState, + ...alertInstanceState.alertStates.slice(indexInState + 1), + ]; } } + 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 index 832b4d40fe0c7..7efdfb58d54c3 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts @@ -36,7 +36,7 @@ describe('DiskUsageAlert', () => { expect(alert.type).toBe(ALERT_DISK_USAGE); expect(alert.label).toBe('Disk Usage'); expect(alert.defaultThrottle).toBe('1d'); - expect(alert.defaultParams).toStrictEqual({ threshold: 85, duration: '5m' }); + 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.' }, @@ -89,6 +89,7 @@ describe('DiskUsageAlert', () => { }; const kibanaUrl = 'http://localhost:5601'; + const hasScheduledActions = jest.fn(); const replaceState = jest.fn(); const scheduleActions = jest.fn(); const getState = jest.fn(); @@ -97,6 +98,7 @@ describe('DiskUsageAlert', () => { callCluster: jest.fn(), alertInstanceFactory: jest.fn().mockImplementation(() => { return { + hasScheduledActions, replaceState, scheduleActions, getState, @@ -151,143 +153,6 @@ describe('DiskUsageAlert', () => { }); }); - it('should not fire actions if under threshold', async () => { - (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - diskUsage: 1, - }, - ]; - }); - 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); - expect(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - ccs: undefined, - cluster: { - clusterUuid, - clusterName, - }, - diskUsage: 1, - nodeId, - nodeName, - ui: { - isFiring: false, - lastCheckedMS: 0, - message: null, - resolvedMS: 0, - severity: 'danger', - triggeredMS: 0, - }, - }, - ], - }); - expect(scheduleActions).not.toHaveBeenCalled(); - }); - - it('should resolve with a resolved message', async () => { - (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - diskUsage: 1, - }, - ]; - }); - (getState as jest.Mock).mockImplementation(() => { - return { - alertStates: [ - { - cluster: { - clusterUuid, - clusterName, - }, - ccs: null, - diskUsage: 91, - nodeId, - nodeName, - ui: { - isFiring: true, - message: null, - severity: 'danger', - resolvedMS: 0, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }; - }); - 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(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - cluster: { clusterUuid, clusterName }, - ccs: null, - diskUsage: 1, - nodeId, - nodeName, - ui: { - isFiring: false, - message: { - text: - 'The disk usage on node myNodeName is now under the threshold, currently reporting at 1.00% as of #resolved', - tokens: [ - { - startToken: '#resolved', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'danger', - resolvedMS: 1, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `Disk usage alert is resolved for ${count} node(s) in cluster: ${clusterName}.`, - internalShortMessage: `Disk usage alert is resolved for ${count} node(s) in cluster: ${clusterName}.`, - clusterName, - count, - nodes: `${nodeName}:1.00`, - state: 'resolved', - }); - }); - it('should handle ccs', async () => { const ccs = 'testCluster'; (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { @@ -325,187 +190,6 @@ describe('DiskUsageAlert', () => { }); }); - it('should show proper counts for resolved and firing nodes', async () => { - (fetchDiskUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - diskUsage: 1, - }, - { - ...stat, - nodeId: 'anotherNode', - nodeName: 'anotherNode', - diskUsage: 99, - }, - ]; - }); - (getState as jest.Mock).mockImplementation(() => { - return { - alertStates: [ - { - cluster: { - clusterUuid, - clusterName, - }, - ccs: null, - diskUsage: 91, - nodeId, - nodeName, - ui: { - isFiring: true, - message: null, - severity: 'danger', - resolvedMS: 0, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - { - cluster: { - clusterUuid, - clusterName, - }, - ccs: null, - diskUsage: 100, - nodeId: 'anotherNode', - nodeName: 'anotherNode', - ui: { - isFiring: true, - message: null, - severity: 'danger', - resolvedMS: 0, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }; - }); - 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(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - cluster: { clusterUuid, clusterName }, - ccs: null, - diskUsage: 1, - nodeId, - nodeName, - ui: { - isFiring: false, - message: { - text: - 'The disk usage on node myNodeName is now under the threshold, currently reporting at 1.00% as of #resolved', - tokens: [ - { - startToken: '#resolved', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'danger', - resolvedMS: 1, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - { - ccs: null, - cluster: { clusterUuid, clusterName }, - diskUsage: 99, - nodeId: 'anotherNode', - nodeName: 'anotherNode', - ui: { - isFiring: true, - message: { - text: - 'Node #start_linkanotherNode#end_link is reporting disk usage of 99.00% at #absolute', - nextSteps: [ - { - text: '#start_linkCheck hot threads#end_link', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'docLink', - partialUrl: - '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', - }, - ], - }, - { - text: '#start_linkCheck long running tasks#end_link', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'docLink', - partialUrl: - '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', - }, - ], - }, - ], - tokens: [ - { - startToken: '#absolute', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/anotherNode', - }, - ], - }, - severity: 'danger', - resolvedMS: 0, - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledTimes(1); - expect(scheduleActions.mock.calls[0]).toEqual([ - 'default', - { - action: - '[View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', - actionPlain: 'Verify disk usage levels across affected nodes.', - internalFullMessage: - 'Disk usage alert is firing for 1 node(s) in cluster: testCluster. [View nodes](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:abc123))', - internalShortMessage: - 'Disk usage alert is firing for 1 node(s) in cluster: testCluster. Verify disk usage levels across affected nodes.', - nodes: 'anotherNode:99.00', - clusterName, - count, - state: 'firing', - }, - ]); - }); - it('should fire with different messaging for cloud', async () => { const alert = new DiskUsageAlert() as IDiskUsageAlertMock; alert.initializeAlertType( diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 9e25665ddfc2f..688385019306a 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -157,30 +157,35 @@ export class DiskUsageAlert extends BaseAlert { }), nextSteps: [ createLink( - 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', - 'Tune for disk usage', + 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( - 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.tuneDisk', - 'Identify large indices', - `elasticsearch/indices`, + i18n.translate('xpack.monitoring.alerts.diskUsage.ui.nextSteps.identifyIndices', { + defaultMessage: '#start_linkIdentify large indices#end_link', + }), + 'elasticsearch/indices', AlertMessageTokenType.Link ), createLink( - 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.ilmPolicies', - 'Implement ILM policies', + 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( - 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.addMoreNodes', - 'Add more data nodes', + 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( - 'xpack.monitoring.alerts.diskUsage.ui.nextSteps.resizeYourDeployment', - 'Resize your deployment (ECE)', - `{elasticWebsiteUrl}/guide/en/cloud-enterprise/{docLinkVersion}/ece-resize-deployment.html` + 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: [ @@ -293,50 +298,6 @@ export class DiskUsageAlert extends BaseAlert { } } - private executeDeltas( - services: AlertServices, - cluster: AlertCluster, - newAlertStates: AlertDiskUsageState[], - oldAlertStates: AlertDiskUsageState[] - ) { - const deltaFiringStates = []; - const deltaResolvedStates = []; - const deltaInstanceIdPrefix: string = `.monitoring:${this.type}:${ - cluster.clusterUuid - }:${Date.now()}:`; - - for (const newAlertState of newAlertStates) { - const relatedOldState = oldAlertStates.find( - (oldState) => - oldState.nodeId === newAlertState.nodeId && - oldState.ui.isFiring !== newAlertState.ui.isFiring && - oldState.ui.resolvedMS !== newAlertState.ui.resolvedMS - ); - if (!relatedOldState) { - if (newAlertState.ui.isFiring) { - deltaFiringStates.push(newAlertState); - } else if (newAlertState.ui.resolvedMS) { - deltaResolvedStates.push(newAlertState); - } - } - } - - if (deltaFiringStates.length + deltaResolvedStates.length === newAlertStates.length) { - /** No delta changes, so we do nothing */ - return; - } - - if (deltaFiringStates.length) { - const instance = services.alertInstanceFactory(`${deltaInstanceIdPrefix}:firing`); - this.executeActions(instance, { alertStates: deltaFiringStates }, null, cluster); - } - - if (deltaResolvedStates.length) { - const instance = services.alertInstanceFactory(`${deltaInstanceIdPrefix}:resolved`); - this.executeActions(instance, { alertStates: deltaResolvedStates }, null, cluster); - } - } - protected async processData( data: AlertData[], clusters: AlertCluster[], @@ -344,18 +305,20 @@ export class DiskUsageAlert extends BaseAlert { logger: Logger, state: any ) { - const currentUTC = Date.now(); + const currentUTC = +new Date(); for (const cluster of clusters) { const nodes = data.filter((node) => node.clusterUuid === cluster.clusterUuid); if (!nodes.length) { continue; } - const instanceId = `.monitoring:${this.type}:${cluster.clusterUuid}`; + 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 instanceState = instance.getState() as AlertInstanceState; const newAlertStates: AlertDiskUsageState[] = []; - const oldAlertStates = (instanceState?.alertStates || []) as AlertDiskUsageState[]; for (const node of nodes) { const stat = node.meta as AlertDiskUsageState; @@ -369,26 +332,10 @@ export class DiskUsageAlert extends BaseAlert { nodeState.ui.isFiring = true; nodeState.ui.severity = node.severity; newAlertStates.push(nodeState); - } else { - const lastNodeState = oldAlertStates.find( - (oldNodeState) => nodeState.nodeId === oldNodeState.nodeId - ); - if (lastNodeState?.ui.isFiring) { - nodeState.ui.resolvedMS = currentUTC; - newAlertStates.push(nodeState); - } } - nodeState.ui.message = this.getUiMessage(nodeState, node); } - /** - * Addresses lost delta triggers if executed between throttle states, context: - * https://github.com/elastic/kibana/pull/75419#discussion_r490497639. This is - * a temporary solution until: https://github.com/elastic/kibana/issues/49405 is implemented - */ - this.executeDeltas(services, cluster, newAlertStates, oldAlertStates); - const alertInstanceState = { alertStates: newAlertStates }; instance.replaceState(alertInstanceState); if (newAlertStates.length && !instance.hasScheduledActions()) { 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 index cfe8d962517ae..4e221d3ebb35a 100644 --- 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 @@ -68,94 +68,4 @@ describe('fetchDiskUsageNodeStats', () => { }, ]); }); - - it('fetch properly return ccs', async () => { - callCluster = jest.fn().mockImplementation(() => { - return { - aggregations: { - clusters: { - buckets: [ - { - key: clusters[0].clusterUuid, - nodes: { - buckets: [ - { - key: 'theNodeId', - index: { - buckets: [ - { - key: 'ccs:.monitoring-es-*', - }, - ], - }, - name: { - buckets: [ - { - key: 'theNodeName', - }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - }; - }); - const result = await fetchDiskUsageNodeStats(callCluster, clusters, index, duration, size); - expect(result[0].ccs).toBe('ccs'); - }); - - it('should use consistent params', async () => { - let params = null; - callCluster = jest.fn().mockImplementation((...args) => { - params = args[1]; - }); - await fetchDiskUsageNodeStats(callCluster, clusters, index, duration, size); - expect(params).toStrictEqual({ - index, - filterPath: ['aggregations'], - body: { - size: 0, - query: { - bool: { - filter: [ - { terms: { cluster_uuid: clusters.map((cluster) => cluster.clusterUuid) } }, - { term: { type: 'node_stats' } }, - { - range: { - timestamp: { - gte: `now-${duration}`, - }, - }, - }, - ], - }, - }, - aggs: { - clusters: { - terms: { - field: 'cluster_uuid', - size, - include: clusters.map((cluster) => cluster.clusterUuid), - }, - aggs: { - nodes: { - terms: { field: 'node_stats.node_id', size }, - aggs: { - index: { terms: { field: '_index', size: 1 } }, - usage_ratio_percentile: { - avg: { field: 'nodes.buckets.usage_ratio_percentile.value' }, - }, - name: { terms: { field: 'source_node.name', size: 1 } }, - }, - }, - }, - }, - }, - }, - }); - }); }); From 8693ed72daeee7e3e5aa5b771d6751b95abaa0e4 Mon Sep 17 00:00:00 2001 From: Igor Zaytsev Date: Wed, 30 Sep 2020 00:01:46 -0400 Subject: [PATCH 9/9] Fixed links --- .../public/alerts/lib/replace_tokens.tsx | 4 +++- .../monitoring/server/alerts/alerts_common.ts | 2 +- .../alerts/cluster_health_alert.test.ts | 5 ++-- .../server/alerts/cluster_health_alert.ts | 10 ++------ .../server/alerts/cpu_usage_alert.test.ts | 23 +++++++++---------- .../server/alerts/cpu_usage_alert.ts | 16 +------------ .../server/alerts/disk_usage_alert.test.ts | 10 ++++---- .../server/alerts/disk_usage_alert.ts | 11 ++------- ...asticsearch_version_mismatch_alert.test.ts | 5 ++-- .../elasticsearch_version_mismatch_alert.ts | 9 +------- .../kibana_version_mismatch_alert.test.ts | 5 ++-- .../alerts/kibana_version_mismatch_alert.ts | 7 +----- .../alerts/license_expiration_alert.test.ts | 5 ++-- .../server/alerts/license_expiration_alert.ts | 9 +------- .../logstash_version_mismatch_alert.test.ts | 5 ++-- .../alerts/logstash_version_mismatch_alert.ts | 7 +----- .../server/alerts/nodes_changed_alert.test.ts | 5 ++-- .../server/alerts/nodes_changed_alert.ts | 9 +------- 18 files changed, 42 insertions(+), 105 deletions(-) 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 79ae82010ced4..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} ); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts index 30c7fa78cb2b8..41c8bba17df0a 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_common.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_common.ts @@ -67,7 +67,7 @@ export const createLink = ( linkURL: string, type: AlertMessageTokenType = AlertMessageTokenType.DocLink ) => { - const link = type === AlertMessageTokenType.DocLink ? { url: linkURL } : { partialUrl: linkURL }; + const link = type === AlertMessageTokenType.DocLink ? { partialUrl: linkURL } : { url: linkURL }; return { text: linkText, tokens: [ 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 b70ec295151b7..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 @@ -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, 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 b0f9d9d7e952e..427dd2f86de00 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.ts @@ -180,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', 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 baab68b86f847..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 @@ -153,7 +153,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - url: + partialUrl: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', }, ], @@ -165,7 +165,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - url: + partialUrl: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', }, ], @@ -196,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, @@ -373,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, @@ -505,7 +505,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - url: + partialUrl: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', }, ], @@ -517,7 +517,7 @@ describe('CpuUsageAlert', () => { startToken: '#start_link', endToken: '#end_link', type: 'docLink', - url: + partialUrl: '{elasticWebsiteUrl}/guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', }, ], @@ -562,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', @@ -597,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 bf8b7049bcb03..4228354f52748 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -230,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 @@ -253,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', { 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 index 7efdfb58d54c3..546399f666b6c 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.test.ts @@ -142,9 +142,9 @@ describe('DiskUsageAlert', () => { } as any); const count = 1; expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `Disk 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: `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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify disk usage levels across affected nodes.', clusterName, count, @@ -179,9 +179,9 @@ describe('DiskUsageAlert', () => { } as any); const count = 1; expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `Disk 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: `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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify disk usage levels across affected nodes.', clusterName, count, @@ -209,7 +209,7 @@ describe('DiskUsageAlert', () => { 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](http://localhost:5601/app/monitoring#elasticsearch/nodes?_g=(cluster_uuid:${clusterUuid}))`, + action: `[View nodes](elasticsearch/nodes)`, actionPlain: 'Verify disk usage levels across affected nodes.', clusterName, count, diff --git a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts index 688385019306a..e43dca3ce87b1 100644 --- a/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/disk_usage_alert.ts @@ -212,7 +212,6 @@ export class DiskUsageAlert extends BaseAlert { item: AlertData | null, cluster: AlertCluster ) { - const ccs = alertStates.find((state) => state.ccs)?.ccs; const firingNodes = alertStates.filter( (alertState) => alertState.ui.isFiring ) as AlertDiskUsageState[]; @@ -225,14 +224,8 @@ export class DiskUsageAlert extends BaseAlert { const fullActionText = i18n.translate('xpack.monitoring.alerts.diskUsage.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.diskUsage.firing.internalShortMessage', { 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 55fedeace65a8..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 @@ -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]', 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 a82d69372f754..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 @@ -163,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', 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 e7be89113f335..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 @@ -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]', 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 c56d33b5bc142..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 @@ -168,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', 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 91c375e4ddec7..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 @@ -176,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, 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 b1e3d53ab9e2d..f1412ff0fc91a 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.ts @@ -172,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( 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 d960e8f37ab15..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 @@ -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]', 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 f69f42a1c91b5..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 @@ -159,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', 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 8093d8ec5b8c1..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 @@ -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: '', 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 cade069d9f1ee..e03e6ea53ab4e 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -191,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(',');