diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts index 2a4e9b5e21eae..e4621f63793c2 100644 --- a/x-pack/plugins/infra/common/constants.ts +++ b/x-pack/plugins/infra/common/constants.ts @@ -11,6 +11,7 @@ export const METRICS_APP = 'metrics'; export const LOGS_APP = 'logs'; export const METRICS_FEATURE_ID = 'infrastructure'; +export const INFRA_ALERT_FEATURE_ID = 'infrastructure'; export const LOGS_FEATURE_ID = 'logs'; export type InfraFeatureId = typeof METRICS_FEATURE_ID | typeof LOGS_FEATURE_ID; diff --git a/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts b/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts index f0b0f4eb7f0ab..1c6076b277a55 100644 --- a/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts +++ b/x-pack/plugins/infra/common/http_api/infra/get_infra_metrics.ts @@ -53,11 +53,16 @@ export const GetInfraMetricsRequestBodyPayloadRT = rt.intersection([ }), ]); -export const InfraAssetMetricsItemRT = rt.type({ - name: rt.string, - metrics: rt.array(InfraAssetMetricsRT), - metadata: rt.array(InfraAssetMetadataRT), -}); +export const InfraAssetMetricsItemRT = rt.intersection([ + rt.type({ + name: rt.string, + metrics: rt.array(InfraAssetMetricsRT), + metadata: rt.array(InfraAssetMetadataRT), + }), + rt.partial({ + alertsCount: rt.number, + }), +]); export const GetInfraMetricsResponsePayloadRT = rt.type({ type: rt.literal('host'), diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/config.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/config.ts index cf4374cae94fa..253bb59f79d66 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/config.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/config.ts @@ -11,5 +11,4 @@ import type { ValidFeatureId } from '@kbn/rule-data-utils'; export const ALERTS_PER_PAGE = 10; export const ALERTS_TABLE_ID = 'xpack.infra.hosts.alerts.table'; -export const INFRA_ALERT_FEATURE_ID = 'infrastructure'; export const infraAlertFeatureIds: ValidFeatureId[] = [AlertConsumers.INFRASTRUCTURE]; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index 1d7ea3bd82538..9534406d5dcfa 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -72,6 +72,7 @@ const mockHostNode: InfraAssetMetricsItem[] = [ { name: 'cloud.provider', value: 'aws' }, ], name: 'host-0', + alertsCount: 0, }, { metrics: [ @@ -109,6 +110,7 @@ const mockHostNode: InfraAssetMetricsItem[] = [ { name: 'host.ip', value: '243.86.94.22' }, ], name: 'host-1', + alertsCount: 0, }, ]; @@ -161,6 +163,7 @@ describe('useHostTable hook', () => { diskSpaceUsage: 0.2040001, memoryFree: 34359.738368, normalizedLoad1m: 239.2040001, + alertsCount: 0, }, { name: 'host-1', @@ -178,6 +181,7 @@ describe('useHostTable hook', () => { diskSpaceUsage: 0.5400000214576721, memoryFree: 9.194304, normalizedLoad1m: 100, + alertsCount: 0, }, ]; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 446a0c577bca9..c54877bd11a20 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -13,6 +13,8 @@ import { isEqual } from 'lodash'; import { isNumber } from 'lodash/fp'; import { CloudProvider } from '@kbn/custom-icons'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import { EuiToolTip } from '@elastic/eui'; +import { EuiBadge } from '@elastic/eui'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; import { EntryTitle } from '../components/table/entry_title'; @@ -44,6 +46,7 @@ interface HostMetadata { export type HostNodeRow = HostMetadata & HostMetrics & { name: string; + alertsCount?: number; }; /** @@ -54,7 +57,7 @@ const formatMetric = (type: InfraAssetMetricType, value: number | undefined | nu }; const buildItemsList = (nodes: InfraAssetMetricsItem[]): HostNodeRow[] => { - return nodes.map(({ metrics, metadata, name }) => { + return nodes.map(({ metrics, metadata, name, alertsCount }) => { const metadataKeyValue = metadata.reduce( (acc, curr) => ({ ...acc, @@ -79,12 +82,14 @@ const buildItemsList = (nodes: InfraAssetMetricsItem[]): HostNodeRow[] => { }), {} as HostMetrics ), + + alertsCount: alertsCount ?? 0, }; }); }; -const isTitleColumn = (cell: any): cell is HostNodeRow['title'] => { - return typeof cell === 'object' && cell && 'name' in cell; +const isTitleColumn = (cell: HostNodeRow[keyof HostNodeRow]): cell is HostNodeRow['title'] => { + return cell !== null && typeof cell === 'object' && cell && 'name' in cell; }; const sortValues = (aValue: any, bValue: any, { direction }: Sorting) => { @@ -124,6 +129,8 @@ export const useHostsTable = () => { const [selectedItems, setSelectedItems] = useState([]); const { hostNodes } = useHostsViewContext(); + const displayAlerts = hostNodes.some((item) => 'alertsCount' in item); + const { value: formulas } = useAsync(() => inventoryModel.metrics.getFormulas()); const [{ detailsItemId, pagination, sorting }, setProperties] = useHostsTableUrlState(); @@ -221,6 +228,39 @@ export const useHostsTable = () => { }, ], }, + ...(displayAlerts + ? [ + { + name: TABLE_COLUMN_LABEL.alertsCount, + field: 'alertsCount', + sortable: true, + 'data-test-subj': 'hostsView-tableRow-alertsCount', + render: (alertsCount: HostNodeRow['alertsCount'], row: HostNodeRow) => { + if (!alertsCount) { + return null; + } + return ( + + { + setProperties({ detailsItemId: row.id === detailsItemId ? null : row.id }); + }} + onClickAriaLabel={TABLE_COLUMN_LABEL.alertsCount} + iconOnClick={() => { + setProperties({ detailsItemId: row.id === detailsItemId ? null : row.id }); + }} + iconOnClickAriaLabel={TABLE_COLUMN_LABEL.alertsCount} + > + {alertsCount} + + + ); + }, + }, + ] + : []), { name: TABLE_COLUMN_LABEL.title, field: 'title', @@ -315,7 +355,6 @@ export const useHostsTable = () => { 'data-test-subj': 'hostsView-tableRow-rx', render: (avg: number) => formatMetric('rx', avg), align: 'right', - width: '120px', }, { name: ( @@ -330,7 +369,6 @@ export const useHostsTable = () => { 'data-test-subj': 'hostsView-tableRow-tx', render: (avg: number) => formatMetric('tx', avg), align: 'right', - width: '120px', }, ], [ @@ -344,6 +382,7 @@ export const useHostsTable = () => { formulas?.tx.value, reportHostEntryClick, setProperties, + displayAlerts, ] ); diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts index c1dcafefaccc3..dd53bd96dc185 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts @@ -18,8 +18,8 @@ import { DEFAULT_PAGE_SIZE, LOCAL_STORAGE_PAGE_SIZE_KEY } from '../constants'; export const GET_DEFAULT_TABLE_PROPERTIES: TableProperties = { detailsItemId: null, sorting: { - direction: 'asc', - field: 'name', + direction: 'desc', + field: 'alertsCount', }, pagination: { pageIndex: 0, diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/translations.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/translations.ts index 304b1980d965c..9be99e1d70a8b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/translations.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/translations.ts @@ -8,6 +8,10 @@ import { i18n } from '@kbn/i18n'; export const TABLE_COLUMN_LABEL = { + alertsCount: i18n.translate('xpack.infra.hostsViewPage.table.alertsColumnHeader', { + defaultMessage: 'Active alerts', + }), + title: i18n.translate('xpack.infra.hostsViewPage.table.nameColumnHeader', { defaultMessage: 'Name', }), diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 56b8fe29c85d6..398eaa0ccf3b8 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -22,7 +22,10 @@ import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin import { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; import { PluginSetupContract as AlertingPluginContract } from '@kbn/alerting-plugin/server'; import { MlPluginSetup } from '@kbn/ml-plugin/server'; -import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; +import { + RuleRegistryPluginSetupContract, + RuleRegistryPluginStartContract, +} from '@kbn/rule-registry-plugin/server'; import { ObservabilityPluginSetup } from '@kbn/observability-plugin/server'; import { LogsSharedPluginSetup, LogsSharedPluginStart } from '@kbn/logs-shared-plugin/server'; import { VersionedRouteConfig } from '@kbn/core-http-server'; @@ -59,6 +62,7 @@ export interface InfraServerPluginStartDeps { dataViews: DataViewsPluginStart; logsShared: LogsSharedPluginStart; profilingDataAccess?: ProfilingDataAccessPluginStart; + ruleRegistry: RuleRegistryPluginStartContract; apmDataAccess: ApmDataAccessPluginStart; } diff --git a/x-pack/plugins/infra/server/routes/infra/index.ts b/x-pack/plugins/infra/server/routes/infra/index.ts index d98e8034e6207..4d8fd931082c1 100644 --- a/x-pack/plugins/infra/server/routes/infra/index.ts +++ b/x-pack/plugins/infra/server/routes/infra/index.ts @@ -14,6 +14,7 @@ import { GetInfraMetricsResponsePayloadRT, } from '../../../common/http_api/infra'; import { InfraBackendLibs } from '../../lib/infra_types'; +import { getInfraAlertsClient } from './lib/helpers/get_infra_alerts_client'; import { getHosts } from './lib/host/get_hosts'; export const initInfraMetricsRoute = (libs: InfraBackendLibs) => { @@ -35,10 +36,20 @@ export const initInfraMetricsRoute = (libs: InfraBackendLibs) => { try { const searchClient = data.search.asScoped(request); + const alertsClient = await getInfraAlertsClient({ + getStartServices: libs.getStartServices, + request, + }); const soClient = savedObjects.getScopedClient(request); const source = await libs.sources.getSourceConfiguration(soClient, params.sourceId); - const hosts = await getHosts({ searchClient, sourceConfig: source.configuration, params }); + const hosts = await getHosts({ + searchClient, + alertsClient, + sourceConfig: source.configuration, + params, + }); + return response.ok({ body: GetInfraMetricsResponsePayloadRT.encode(hosts), }); diff --git a/x-pack/plugins/infra/server/routes/infra/lib/helpers/get_infra_alerts_client.ts b/x-pack/plugins/infra/server/routes/infra/lib/helpers/get_infra_alerts_client.ts new file mode 100644 index 0000000000000..7110a822fb8a4 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/helpers/get_infra_alerts_client.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { isEmpty } from 'lodash'; +import { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types'; +import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import { KibanaRequest } from '@kbn/core/server'; +import type { InfraPluginStartServicesAccessor } from '../../../../types'; + +type RequiredParams = ESSearchRequest & { + size: number; + track_total_hits: boolean | number; +}; + +export type InfraAlertsClient = Awaited>; + +export async function getInfraAlertsClient({ + getStartServices, + request, +}: { + getStartServices: InfraPluginStartServicesAccessor; + request: KibanaRequest; +}) { + const [, { ruleRegistry }] = await getStartServices(); + const alertsClient = await ruleRegistry.getRacClientWithRequest(request); + const infraAlertsIndices = await alertsClient.getAuthorizedAlertsIndices(['infrastructure']); + + if (!infraAlertsIndices || isEmpty(infraAlertsIndices)) { + throw Error('No alert indices exist for "infrastrucuture"'); + } + + return { + search( + searchParams: TParams + ): Promise> { + return alertsClient.find({ + ...searchParams, + index: infraAlertsIndices.join(','), + }) as Promise; + }, + }; +} diff --git a/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts index 6d44224661750..6eab207f7fbae 100644 --- a/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts +++ b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts.ts @@ -11,6 +11,7 @@ import { mapToApiResponse } from '../mapper'; import { hasFilters } from '../utils'; import { GetHostsArgs } from '../types'; import { getAllHosts } from './get_all_hosts'; +import { getHostsAlertsCount } from './get_hosts_alerts_count'; export const getHosts = async (args: GetHostsArgs): Promise => { const runFilterQuery = hasFilters(args.params.query); @@ -23,8 +24,23 @@ export const getHosts = async (args: GetHostsArgs): Promise { diff --git a/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts_alerts_count.ts b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts_alerts_count.ts new file mode 100644 index 0000000000000..d1e17e8d133ee --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra/lib/host/get_hosts_alerts_count.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kqlQuery, termQuery, termsQuery } from '@kbn/observability-plugin/server'; +import { + ALERT_RULE_PRODUCER, + ALERT_STATUS, + ALERT_STATUS_ACTIVE, + ALERT_UUID, +} from '@kbn/rule-data-utils'; +import { INFRA_ALERT_FEATURE_ID } from '../../../../../common/constants'; +import { BUCKET_KEY, MAX_SIZE } from '../constants'; +import { InfraAlertsClient } from '../helpers/get_infra_alerts_client'; + +export type HostAlertsResponse = Array<{ + name: string; + alertsCount: number; +}>; + +export async function getHostsAlertsCount({ + alertsClient, + hostNamesShortList, + kuery, + from, + to, + maxNumHosts = MAX_SIZE, +}: { + alertsClient: InfraAlertsClient; + hostNamesShortList: string[]; + kuery?: string; + from: string; + to: string; + maxNumHosts?: number; +}): Promise { + const rangeQuery = [ + { + range: { + 'kibana.alert.time_range': { + gte: from, + lte: to, + format: 'strict_date_optional_time', + }, + }, + }, + ]; + const params = { + size: 0, + track_total_hits: false, + query: { + bool: { + filter: [ + ...termQuery(ALERT_RULE_PRODUCER, INFRA_ALERT_FEATURE_ID), + ...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), + ...termsQuery(BUCKET_KEY, ...hostNamesShortList), + ...rangeQuery, + ...kqlQuery(kuery), + ], + }, + }, + aggs: { + hosts: { + terms: { + field: BUCKET_KEY, + size: maxNumHosts, + }, + aggs: { + alerts_count: { + cardinality: { + field: ALERT_UUID, + }, + }, + }, + }, + }, + }; + + const result = await alertsClient.search(params); + + const filterAggBuckets = result.aggregations?.hosts.buckets ?? []; + + return filterAggBuckets.map((bucket) => ({ + name: bucket.key as string, + alertsCount: bucket.alerts_count.value, + })); +} diff --git a/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts b/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts index 89d2e71b364f9..cc922d42c8991 100644 --- a/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts +++ b/x-pack/plugins/infra/server/routes/infra/lib/mapper.ts @@ -20,10 +20,12 @@ import { HostsMetricsSearchValueRT, } from './types'; import { METADATA_AGGREGATION_NAME } from './constants'; +import { HostAlertsResponse } from './host/get_hosts_alerts_count'; export const mapToApiResponse = ( params: GetInfraMetricsRequestBodyPayload, - buckets?: HostsMetricsSearchBucket[] | undefined + buckets?: HostsMetricsSearchBucket[] | undefined, + alertsCountResponse?: HostAlertsResponse ): GetInfraMetricsResponsePayload => { if (!buckets) { return { @@ -32,12 +34,20 @@ export const mapToApiResponse = ( }; } - const hosts = buckets.map((bucket) => { - const metrics = convertMetricBucket(params, bucket); - const metadata = convertMetadataBucket(bucket); + const hosts = buckets + .map((bucket) => { + const metrics = convertMetricBucket(params, bucket); + const metadata = convertMetadataBucket(bucket); - return { name: bucket.key as string, metrics, metadata }; - }); + const cpuValue = metrics.find((metric) => metric.name === 'cpu')?.value ?? 0; + const alerts = alertsCountResponse?.find((item) => item.name === bucket.key); + + return { name: bucket.key as string, metrics, metadata, cpuValue, ...alerts }; + }) + .sort((a, b) => { + return b.cpuValue - a.cpuValue; + }) + .map(({ cpuValue, ...rest }) => rest); return { type: params.type, diff --git a/x-pack/plugins/infra/server/routes/infra/lib/types.ts b/x-pack/plugins/infra/server/routes/infra/lib/types.ts index d9800112e7dfe..ab097e91b2b09 100644 --- a/x-pack/plugins/infra/server/routes/infra/lib/types.ts +++ b/x-pack/plugins/infra/server/routes/infra/lib/types.ts @@ -12,6 +12,7 @@ import { InfraStaticSourceConfiguration } from '../../../../common/source_config import { GetInfraMetricsRequestBodyPayload } from '../../../../common/http_api/infra'; import { BasicMetricValueRT, TopMetricsTypeRT } from '../../../lib/metrics/types'; +import { InfraAlertsClient } from './helpers/get_infra_alerts_client'; export const FilteredMetricsTypeRT = rt.type({ doc_count: rt.number, @@ -76,6 +77,7 @@ export interface HostsMetricsAggregationQueryConfig { export interface GetHostsArgs { searchClient: ISearchClient; + alertsClient: InfraAlertsClient; sourceConfig: InfraStaticSourceConfiguration; params: GetInfraMetricsRequestBodyPayload; } diff --git a/x-pack/test/api_integration/apis/metrics_ui/infra.ts b/x-pack/test/api_integration/apis/metrics_ui/infra.ts index baa907c25575f..c3465c68ae7fe 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/infra.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/infra.ts @@ -165,8 +165,8 @@ export default function ({ getService }: FtrProviderContext) { const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); expect(names).eql([ - 'gke-observability-8--observability-8--bc1afd95-f0zc', 'gke-observability-8--observability-8--bc1afd95-ngmh', + 'gke-observability-8--observability-8--bc1afd95-f0zc', 'gke-observability-8--observability-8--bc1afd95-nhhw', ]); }); @@ -208,8 +208,9 @@ export default function ({ getService }: FtrProviderContext) { const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); expect(names).eql([ - 'gke-observability-8--observability-8--bc1afd95-f0zc', 'gke-observability-8--observability-8--bc1afd95-ngmh', + 'gke-observability-8--observability-8--bc1afd95-f0zc', + , ]); }); @@ -269,5 +270,38 @@ export default function ({ getService }: FtrProviderContext) { ); }); }); + + describe('Host with active alerts', () => { + before(async () => { + await Promise.all([ + esArchiver.load('x-pack/test/functional/es_archives/infra/alerts'), + esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'), + ]); + }); + + after(async () => { + await Promise.all([ + esArchiver.unload('x-pack/test/functional/es_archives/infra/alerts'), + esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'), + ]); + }); + + describe('fetch hosts', () => { + it('should return metrics for a host with alert count', async () => { + const body: GetInfraMetricsRequestBodyPayload = { + ...basePayload, + range: { + from: '2018-10-17T19:42:21.208Z', + to: '2018-10-17T19:58:03.952Z', + }, + limit: 1, + }; + const response = await makeRequest({ body, expectedHTTPCode: 200 }); + + expect(response.body.nodes).length(1); + expect(response.body.nodes[0].alertsCount).eql(2); + }); + }); + }); }); } diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index 260b48c26f0fc..a185023c9b33d 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -34,6 +34,7 @@ const END_HOST_PROCESSES_DATE = moment.utc(DATES.metricsAndLogs.hosts.processesD const tableEntries = [ { + alertsCount: 2, title: 'demo-stack-apache-01', cpuUsage: '1.2%', normalizedLoad: '0.5%', @@ -44,36 +45,29 @@ const tableEntries = [ tx: '0 bit/s', }, { - title: 'demo-stack-client-01', - cpuUsage: '0.5%', - normalizedLoad: '0.1%', - memoryUsage: '13.8%', - memoryFree: '3.3 GB', - diskSpaceUsage: '16.9%', - rx: '0 bit/s', - tx: '0 bit/s', - }, - { - title: 'demo-stack-haproxy-01', - cpuUsage: '0.8%', + alertsCount: 2, + title: 'demo-stack-mysql-01', + cpuUsage: '0.9%', normalizedLoad: '0%', - memoryUsage: '16.5%', + memoryUsage: '18.2%', memoryFree: '3.2 GB', - diskSpaceUsage: '16.3%', + diskSpaceUsage: '17.8%', rx: '0 bit/s', tx: '0 bit/s', }, { - title: 'demo-stack-mysql-01', - cpuUsage: '0.9%', + alertsCount: 2, + title: 'demo-stack-redis-01', + cpuUsage: '0.8%', normalizedLoad: '0%', - memoryUsage: '18.2%', - memoryFree: '3.2 GB', - diskSpaceUsage: '17.8%', + memoryUsage: '15.9%', + memoryFree: '3.3 GB', + diskSpaceUsage: '16.3%', rx: '0 bit/s', tx: '0 bit/s', }, { + alertsCount: 0, title: 'demo-stack-nginx-01', cpuUsage: '0.8%', normalizedLoad: '1.4%', @@ -84,15 +78,27 @@ const tableEntries = [ tx: '0 bit/s', }, { - title: 'demo-stack-redis-01', + alertsCount: 0, + title: 'demo-stack-haproxy-01', cpuUsage: '0.8%', normalizedLoad: '0%', - memoryUsage: '15.9%', - memoryFree: '3.3 GB', + memoryUsage: '16.5%', + memoryFree: '3.2 GB', diskSpaceUsage: '16.3%', rx: '0 bit/s', tx: '0 bit/s', }, + { + alertsCount: 0, + title: 'demo-stack-client-01', + cpuUsage: '0.5%', + normalizedLoad: '0.1%', + memoryUsage: '13.8%', + memoryFree: '3.3 GB', + diskSpaceUsage: '16.9%', + rx: '0 bit/s', + tx: '0 bit/s', + }, ]; export default ({ getPageObjects, getService }: FtrProviderContext) => { @@ -610,10 +616,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await Promise.all( [ { metric: 'hostsCount', value: '3' }, - { metric: 'cpuUsage', value: '0.8%' }, + { metric: 'cpuUsage', value: '0.9%' }, { metric: 'normalizedLoad1m', value: '0.2%' }, - { metric: 'memoryUsage', value: '16.3%' }, - { metric: 'diskUsage', value: '16.9%' }, + { metric: 'memoryUsage', value: '17.5%' }, + { metric: 'diskUsage', value: '17.2%' }, ].map(async ({ metric, value }) => { await retry.try(async () => { const tileValue = await pageObjects.infraHostsView.getKPITileValue(metric); @@ -626,12 +632,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should update the alerts count on a search submit', async () => { const alertsCount = await pageObjects.infraHostsView.getAlertsCount(); - expect(alertsCount).to.be('2'); + expect(alertsCount).to.be('6'); }); it('should update the alerts table content on a search submit', async () => { - const ACTIVE_ALERTS = 2; - const RECOVERED_ALERTS = 2; + const ACTIVE_ALERTS = 6; + const RECOVERED_ALERTS = 4; const ALL_ALERTS = ACTIVE_ALERTS + RECOVERED_ALERTS; const COLUMNS = 11; @@ -708,7 +714,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHostsView.sortByCpuUsage(); let hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataFirtPage).to.eql(tableEntries[1]); + expect(hostDataFirtPage).to.eql(tableEntries[5]); await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); @@ -725,7 +731,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataLastPage).to.eql(tableEntries[1]); + expect(hostDataLastPage).to.eql(tableEntries[5]); }); it('should sort by text field asc', async () => { @@ -737,14 +743,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataLastPage).to.eql(tableEntries[5]); + expect(hostDataLastPage).to.eql(tableEntries[2]); }); it('should sort by text field desc', async () => { await pageObjects.infraHostsView.sortByTitle(); let hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataFirtPage).to.eql(tableEntries[5]); + expect(hostDataFirtPage).to.eql(tableEntries[2]); await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index 60c4f0727e7c0..31ae75c4000d6 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -66,10 +66,29 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { const cells = await row.findAllByCssSelector('[data-test-subj*="hostsView-tableRow-"]'); // Retrieve content for each cell - const [title, cpuUsage, normalizedLoad, memoryUsage, memoryFree, diskSpaceUsage, rx, tx] = - await Promise.all(cells.map((cell) => this.getHostsCellContent(cell))); - - return { title, cpuUsage, normalizedLoad, memoryUsage, memoryFree, diskSpaceUsage, rx, tx }; + const [ + alertsCount, + title, + cpuUsage, + normalizedLoad, + memoryUsage, + memoryFree, + diskSpaceUsage, + rx, + tx, + ] = await Promise.all(cells.map((cell) => this.getHostsCellContent(cell))); + + return { + alertsCount, + title, + cpuUsage, + normalizedLoad, + memoryUsage, + memoryFree, + diskSpaceUsage, + rx, + tx, + }; }, async getHostsCellContent(cell: WebElementWrapper) { @@ -235,11 +254,11 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { // Sorting getCpuUsageHeader() { - return testSubjects.find('tableHeaderCell_cpu_2'); + return testSubjects.find('tableHeaderCell_cpu_3'); }, getTitleHeader() { - return testSubjects.find('tableHeaderCell_title_1'); + return testSubjects.find('tableHeaderCell_title_2'); }, async sortByCpuUsage() {