Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[infra] hosts table metrics #173708

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5c78e31
initial impl
klacabane Dec 20, 2023
65326dc
add host.name to synthtrace host documents
klacabane Dec 21, 2023
d7c0a84
add more system metricsets to synthtrace
klacabane Dec 21, 2023
d6be806
load 50/500 hosts
klacabane Dec 22, 2023
b40969e
use querystring to set host limit
klacabane Dec 22, 2023
d754285
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Dec 22, 2023
799788d
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Dec 26, 2023
866b033
infra kibana client to install system package
klacabane Dec 26, 2023
f173687
add diskio metricset + dimension field
klacabane Dec 26, 2023
ecd2ab9
only run host view step with 500 hosts
klacabane Dec 26, 2023
9de1787
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Dec 26, 2023
399c5b4
add mock fn
klacabane Dec 26, 2023
e315868
fix tests
klacabane Dec 26, 2023
4f157da
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Dec 27, 2023
066ae0b
update hosts table wait for render, add charts rendering wait in journey
dmlemeshko Dec 28, 2023
12e9220
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Jan 8, 2024
7ed6817
use correct dataset
klacabane Jan 8, 2024
2344594
track total render time
klacabane Jan 9, 2024
954b1e9
Revert "track total render time"
klacabane Jan 9, 2024
c6d80a1
record infra source loading
klacabane Jan 10, 2024
43e2f73
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Jan 11, 2024
2bee796
extract kibana headers
klacabane Jan 11, 2024
a6ac829
use same timestamp for synthtrace documents
klacabane Jan 11, 2024
95b0b2d
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Jan 12, 2024
ede719b
Merge branch 'main' into 172475-performance-metrics-hosts-view
klacabane Jan 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .buildkite/ftr_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ enabled:
- x-pack/performance/journeys/tags_listing_page.ts
- x-pack/performance/journeys/cloud_security_dashboard.ts
- x-pack/performance/journeys/apm_service_inventory.ts
- x-pack/performance/journeys/infra_hosts_view.ts
- x-pack/test/custom_branding/config.ts
- x-pack/test/profiling_api_integration/cloud/config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/workflows/configs/serverless.config.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface ContainerDocument extends Fields {
'container.id': string;
'kubernetes.pod.uid': string;
'kubernetes.node.name': string;
'metricset.name'?: string;
}

export class Container extends Entity<ContainerDocument> {
Expand Down
93 changes: 90 additions & 3 deletions packages/kbn-apm-synthtrace-client/src/lib/infra/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,79 @@ import { Serializable } from '../serializable';
import { pod } from './pod';

interface HostDocument extends Fields {
'agent.id': string;
'host.hostname': string;
'host.name': string;
'metricset.name'?: string;
}

class Host extends Entity<HostDocument> {
metrics() {
cpu() {
return new HostMetrics({
...this.fields,
'system.cpu.total.norm.pct': 46,
'system.cpu.total.norm.pct': 0.094,
'system.cpu.user.pct': 0.805,
'system.cpu.system.pct': 0.704,
'system.cpu.cores': 16,
'metricset.period': 10000,
'metricset.name': 'cpu',
});
}

memory() {
return new HostMetrics({
...this.fields,
'system.memory.actual.free': 44704067584,
'system.memory.actual.used.bytes': 24015409152,
'system.memory.actual.used.pct': 0.35,
'system.memory.total': 68719476736,
'system.memory.used.bytes': 39964708864,
'system.memory.used.pct': 0.582,
'metricset.period': 10000,
'metricset.name': 'memory',
});
}

network() {
return new HostMetrics({
...this.fields,
'host.network.ingress.bytes': 2871285,
'host.network.egress.bytes': 2904987,
'metricset.period': 10000,
'metricset.name': 'network',
});
}

load() {
return new HostMetrics({
...this.fields,
'system.load': {
1: 3,
cores: 16,
},
'metricset.period': 10000,
'metricset.name': 'load',
});
}

filesystem() {
return new HostMetrics({
...this.fields,
'system.filesystem.used.pct': 12.23,
'metricset.period': 10000,
'metricset.name': 'filesystem',
});
}

diskio() {
return new HostMetrics({
...this.fields,
'system.diskio.read.count': 3538413,
'system.diskio.write.count': 4694333,
'system.diskio.read.bytes': 33147297792,
'system.diskio.write.bytes': 48595652608,
'metricset.period': 10000,
'metricset.name': 'diskio',
});
}

Expand All @@ -39,13 +104,35 @@ class Host extends Entity<HostDocument> {
}

export interface HostMetricsDocument extends HostDocument {
'system.cpu.total.norm.pct': number;
'agent.id': string;
'metricset.period'?: number;
'metricset.name'?: string;
'system.cpu.total.norm.pct'?: number;
'system.cpu.user.pct'?: number;
'system.cpu.system.pct'?: number;
'system.cpu.cores'?: number;
'system.diskio.read.count'?: number;
'system.diskio.write.count'?: number;
'system.diskio.read.bytes'?: number;
'system.diskio.write.bytes'?: number;
'system.filesystem.used.pct'?: number;
'system.memory.actual.used.pct'?: number;
'system.memory.total'?: number;
'system.memory.actual.used.bytes'?: number;
'system.memory.actual.free'?: number;
'system.memory.used.bytes'?: number;
'system.memory.used.pct'?: number;
'system.load'?: { 1: number; cores: number };
'host.network.ingress.bytes'?: number;
'host.network.egress.bytes'?: number;
}

class HostMetrics extends Serializable<HostMetricsDocument> {}

export function host(name: string): Host {
return new Host({
'agent.id': 'synthtrace',
'host.hostname': name,
'host.name': name,
});
}
1 change: 1 addition & 0 deletions packages/kbn-apm-synthtrace-client/src/lib/infra/pod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { container } from './container';
interface PodDocument extends Fields {
'kubernetes.pod.uid': string;
'kubernetes.node.name': string;
'metricset.name'?: string;
}

export class Pod extends Entity<PodDocument> {
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-apm-synthtrace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { ApmSynthtraceEsClient } from './src/lib/apm/client/apm_synthtrace_es_cl
export { ApmSynthtraceKibanaClient } from './src/lib/apm/client/apm_synthtrace_kibana_client';

export { InfraSynthtraceEsClient } from './src/lib/infra/infra_synthtrace_es_client';
export { InfraSynthtraceKibanaClient } from './src/lib/infra/infra_synthtrace_kibana_client';

export { AssetsSynthtraceEsClient } from './src/lib/assets/assets_synthtrace_es_client';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import fetch from 'node-fetch';
import pRetry from 'p-retry';
import { Logger } from '../../utils/create_logger';
import { kibanaHeaders } from '../../shared/client_headers';

export class ApmSynthtraceKibanaClient {
private readonly logger: Logger;
Expand Down Expand Up @@ -63,12 +64,3 @@ export class ApmSynthtraceKibanaClient {
this.logger.info(`Installed APM package ${packageVersion}`);
}
}

function kibanaHeaders() {
return {
Accept: 'application/json',
'Content-Type': 'application/json',
'kbn-xsrf': 'kibana',
'elastic-api-version': '2023-10-31',
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,20 @@ function getRoutingTransform() {
return new Transform({
objectMode: true,
transform(document: ESDocumentWithOperation<InfraDocument>, encoding, callback) {
if ('host.hostname' in document) {
const metricset = document['metricset.name'];

if (metricset === 'cpu') {
document._index = 'metrics-system.cpu-default';
} else if (metricset === 'memory') {
document._index = 'metrics-system.memory-default';
} else if (metricset === 'network') {
document._index = 'metrics-system.network-default';
} else if (metricset === 'load') {
document._index = 'metrics-system.load-default';
} else if (metricset === 'filesystem') {
document._index = 'metrics-system.filesystem-default';
} else if (metricset === 'diskio') {
document._index = 'metrics-system.diskio-default';
} else if ('container.id' in document) {
document._index = 'metrics-kubernetes.container-default';
} else if ('kubernetes.pod.uid' in document) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { join } from 'path';
import fetch from 'node-fetch';
import pRetry from 'p-retry';
import { Logger } from '../utils/create_logger';
import { kibanaHeaders } from '../shared/client_headers';

export class InfraSynthtraceKibanaClient {
private readonly logger: Logger;
private target: string;

constructor(options: { logger: Logger; target: string; username: string; password: string }) {
this.logger = options.logger;
const url = new URL(options.target);
url.username = options.username;
url.password = options.password;
this.target = url.toString();
}

async fetchLatestSystemPackageVersion() {
const fleetPackageApiUrl = join(this.target, '/api/fleet/epm/packages/system?prerelease=true');
this.logger.debug(`Fetching latest System package version from ${fleetPackageApiUrl}`);
const response = await fetch(fleetPackageApiUrl, {
method: 'GET',
headers: kibanaHeaders(),
});

const responseJson = await response.json();

if (response.status !== 200) {
throw new Error(
`Failed to fetch latest System package version, received HTTP ${response.status} and message: ${responseJson.message}`
);
}

const { latestVersion } = responseJson.item;

return latestVersion as string;
}

async installSystemPackage(packageVersion: string) {
this.logger.debug(`Installing System package ${packageVersion}`);

const url = join(this.target, `/api/fleet/epm/packages/system/${packageVersion}`);
const response = await pRetry(() => {
return fetch(url, {
method: 'POST',
headers: kibanaHeaders(),
body: '{"force":true}',
});
});

const responseJson = await response.json();

if (!responseJson.items) {
throw new Error(
`Failed to install System package version ${packageVersion}, received HTTP ${response.status} and message: ${responseJson.message} for url ${url}`
);
}

this.logger.info(`Installed System package ${packageVersion}`);
}
}
16 changes: 16 additions & 0 deletions packages/kbn-apm-synthtrace/src/lib/shared/client_headers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export function kibanaHeaders() {
return {
Accept: 'application/json',
'Content-Type': 'application/json',
'kbn-xsrf': 'kibana',
'elastic-api-version': '2023-10-31',
};
}
86 changes: 86 additions & 0 deletions x-pack/performance/journeys/infra_hosts_view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 { Journey } from '@kbn/journeys';
import {
createLogger,
InfraSynthtraceEsClient,
LogLevel,
InfraSynthtraceKibanaClient,
} from '@kbn/apm-synthtrace';
import { infra, timerange } from '@kbn/apm-synthtrace-client';
import { subj } from '@kbn/test-subj-selector';

export const journey = new Journey({
beforeSteps: async ({ kbnUrl, auth, es }) => {
const logger = createLogger(LogLevel.debug);
const synthKibanaClient = new InfraSynthtraceKibanaClient({
logger,
target: kbnUrl.get(),
username: auth.getUsername(),
password: auth.getPassword(),
});

const pkgVersion = await synthKibanaClient.fetchLatestSystemPackageVersion();
await synthKibanaClient.installSystemPackage(pkgVersion);

const synthEsClient = new InfraSynthtraceEsClient({
logger,
client: es,
refreshAfterIndex: true,
});

const start = Date.now() - 1000 * 60 * 10;
await synthEsClient.index(
generateHostsData({
from: new Date(start).toISOString(),
to: new Date().toISOString(),
count: 1000,
})
);
},
}).step('Navigate to Hosts view and load 500 hosts', async ({ page, kbnUrl, kibanaPage }) => {
await page.goto(
kbnUrl.get(
`app/metrics/hosts?_a=(dateRange:(from:now-15m,to:now),filters:!(),limit:500,panelFilters:!(),query:(language:kuery,query:''))`
)
);
// wait for table to be loaded
await page.waitForSelector(subj('hostsView-table-loaded'));
// wait for metric charts to be loaded
await kibanaPage.waitForCharts({ count: 5, timeout: 60000 });
});

export function generateHostsData({
from,
to,
count = 1,
}: {
from: string;
to: string;
count: number;
}) {
const range = timerange(from, to);

const hosts = Array(count)
.fill(0)
.map((_, idx) => infra.host(`my-host-${idx}`));

return range
.interval('30s')
.rate(1)
.generator((timestamp, index) =>
hosts.flatMap((host) => [
host.cpu().timestamp(timestamp),
host.memory().timestamp(timestamp),
host.network().timestamp(timestamp),
host.load().timestamp(timestamp),
host.filesystem().timestamp(timestamp),
host.diskio().timestamp(timestamp),
])
);
}
Loading